On Thu, 2023-11-23 at 17:17 -0500, Antoni Boucher wrote:
> Hi.
> I did split the patch and sent one for the bfloat16 support and
> another
> one for the vector support.
> 
> Here's the updated patch for the machine-dependent builtins.
> 

Thanks for the patch; sorry about the long delay in reviewing it.

CCing Jan and Uros re the i386 part of that patch; for reference the
patch being discussed is here:
  https://gcc.gnu.org/pipermail/gcc-patches/2023-November/638027.html

> From e025f95f4790ae861e709caf23cbc0723c1a3804 Mon Sep 17 00:00:00 2001
> From: Antoni Boucher <boua...@zoho.com>
> Date: Mon, 23 Jan 2023 17:21:15 -0500
> Subject: [PATCH] libgccjit: Add support for machine-dependent builtins

[...snip...]

> diff --git a/gcc/config/i386/i386-builtins.cc 
> b/gcc/config/i386/i386-builtins.cc
> index 42fc3751676..5cc1d6f4d2e 100644
> --- a/gcc/config/i386/i386-builtins.cc
> +++ b/gcc/config/i386/i386-builtins.cc
> @@ -225,6 +225,22 @@ static GTY(()) tree ix86_builtins[(int) 
> IX86_BUILTIN_MAX];
>  
>  struct builtin_isa ix86_builtins_isa[(int) IX86_BUILTIN_MAX];
>  
> +static void
> +clear_builtin_types (void)
> +{
> +  for (int i = 0 ; i < IX86_BT_LAST_CPTR + 1 ; i++)
> +    ix86_builtin_type_tab[i] = NULL;
> +
> +  for (int i = 0 ; i < IX86_BUILTIN_MAX ; i++)
> +  {
> +    ix86_builtins[i] = NULL;
> +    ix86_builtins_isa[i].set_and_not_built_p = true;
> +  }
> +
> +  for (int i = 0 ; i < IX86_BT_LAST_ALIAS + 1 ; i++)
> +    ix86_builtin_func_type_tab[i] = NULL;
> +}
> +
>  tree get_ix86_builtin (enum ix86_builtins c)
>  {
>    return ix86_builtins[c];
> @@ -1483,6 +1499,8 @@ ix86_init_builtins (void)
>  {
>    tree ftype, decl;
>  
> +  clear_builtin_types ();
> +
>    ix86_init_builtin_types ();
>  
>    /* Builtins to get CPU type and features. */

Please can one of the i386 maintainers check this?
(CCing Jan and Uros: this is for the case where the compiler code runs
multiple times in-process due to being linked into libgccjit.so.  We
want to restore state within i386-builtins.cc to an initial state, and
ensure that no GC-managed objects persist from previous in-memory
compiles).

> diff --git a/gcc/jit/docs/topics/compatibility.rst
b/gcc/jit/docs/topics/compatibility.rst
> index ebede440ee4..764de23341e 100644
> --- a/gcc/jit/docs/topics/compatibility.rst
> +++ b/gcc/jit/docs/topics/compatibility.rst
> @@ -378,3 +378,12 @@ alignment of a variable:
>  --------------------
>  ``LIBGCCJIT_ABI_25`` covers the addition of
>  :func:`gcc_jit_type_get_restrict`
> +
> +.. _LIBGCCJIT_ABI_26:
> +
> +``LIBGCCJIT_ABI_26``
> +--------------------
> +
> +``LIBGCCJIT_ABI_26`` covers the addition of a function to get target 
> builtins:
> +
> +  * :func:`gcc_jit_context_get_target_builtin_function`
> diff --git a/gcc/jit/docs/topics/functions.rst 
> b/gcc/jit/docs/topics/functions.rst
> index cf5cb716daf..e9b77fdb892 100644
> --- a/gcc/jit/docs/topics/functions.rst
> +++ b/gcc/jit/docs/topics/functions.rst
> @@ -140,6 +140,25 @@ Functions
>        uses such a parameter will lead to an error being emitted within
>        the context.
>  
> +.. function::  gcc_jit_function *\
> +               gcc_jit_context_get_target_builtin_function (gcc_jit_context 
> *ctxt,\
> +                                                            const char *name)
> +
> +   Get the :type:`gcc_jit_function` for the built-in function with the
> +   given name.  For example:

Might be nice to add the "(sometimes called intrinsic functions)" text
you have in the header here.

[...snip....]

> diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
> index a729086bafb..3ca9702d429 100644
> --- a/gcc/jit/dummy-frontend.cc
> +++ b/gcc/jit/dummy-frontend.cc

[...]

> @@ -29,8 +30,14 @@ along with GCC; see the file COPYING3.  If not see
>  #include "options.h"
>  #include "stringpool.h"
>  #include "attribs.h"
> +#include "jit-recording.h"
> +#include "print-tree.h"
>  
>  #include <mpfr.h>
> +#include <unordered_map>
> +#include <string>
> +
> +using namespace gcc::jit;
>  
>  /* Attribute handling.  */
>  
> @@ -86,6 +93,11 @@ static const struct attribute_spec::exclusions 
> attr_const_pure_exclusions[] =
>    ATTR_EXCL (NULL, false, false, false)
>  };
>  
> +hash_map<nofree_string_hash, tree> target_builtins{};

I was wondering if this needs a GTY marker, but I don't think it does:
presumably it's only used within jit_langhook_parse_file where no GC
can happen - unless jit_langhook_write_globals makes use of it?

> +std::unordered_map<std::string, recording::function_type*>
target_function_types
> +{};
> +recording::context target_builtins_ctxt{NULL};

Please add a comment to target_builtins_ctxt saying what it's for.  As
far as I can tell, it's for getting at recording::types from
tree_type_to_jit_type; we then use a new "copy" mechanism to copy
objects from target_builtins_ctxt for use with the real
recording::context.

This feels ugly, but maybe it's the only way to make it work.

Could tree_type_to_jit_type take a recording::context as a param?  The
only non-recursive uses of tree_type_to_jit_type seem to be in
jit_langhook_builtin_function.  Could that use the recording::context
of the current playback::context?  You can get the current
playback::context from gcc::jit::active_playback_ctxt and then access
that playback::context's m_recording_ctxt.

Or is there some need to have this work as a global cache? (which is
what the target_builtins_ctxt effectively does).
It looks like target_function_types might be a cache...

> +
>  /* Table of machine-independent attributes supported in libgccjit.  */
>  const struct attribute_spec jit_attribute_table[] =
>  {
> @@ -594,6 +606,7 @@ jit_langhook_init (void)
>  
>    build_common_tree_nodes (false);
>  
> +  target_builtins.empty ();
>    build_common_builtin_nodes ();
>  
>    /* The default precision for floating point numbers.  This is used
> @@ -601,6 +614,8 @@ jit_langhook_init (void)
>       eventually be controllable by a command line option.  */
>    mpfr_set_default_prec (256);
>  
> +  targetm.init_builtins ();
> +
>    return true;
>  }
>  
> @@ -668,11 +683,198 @@ jit_langhook_type_for_mode (machine_mode mode, int 
> unsignedp)
>    return NULL;
>  }
>  
> -/* Record a builtin function.  We just ignore builtin functions.  */
> +recording::type* tree_type_to_jit_type (tree type)
> +{
> +  if (TREE_CODE (type) == VECTOR_TYPE)
> +  {
> +    tree inner_type = TREE_TYPE (type);
> +    recording::type* element_type = tree_type_to_jit_type (inner_type);
> +    poly_uint64 size = TYPE_VECTOR_SUBPARTS (type);
> +    long constant_size = size.to_constant ();
> +    if (element_type != NULL)
> +      return element_type->get_vector (constant_size);
> +    return NULL;
> +  }
> +  if (TREE_CODE (type) == REFERENCE_TYPE)
> +    // For __builtin_ms_va_start.
> +    // FIXME: wrong type.
> +    return new recording::memento_of_get_type (&target_builtins_ctxt,
> +                                            GCC_JIT_TYPE_VOID);

The various "// FIXME: wrong type" cases in this function feel like a
timebomb; could we instead fail hard on them, rather than potentially
silently generate bad code?

[...snip...]

> +/* Record a builtin function.  We save their types to be able to check types
> +   in recording and for reflection.  */

Aha!  This comment makes it clearer that the stuff above is a cache, so
maybe it has to be done the way you have it above (with the recursive
copying to the appropriate recording::context).

>  static tree
>  jit_langhook_builtin_function (tree decl)
>  {
> +  if (TREE_CODE (decl) == FUNCTION_DECL)
> +  {
> +    const char* name = IDENTIFIER_POINTER (DECL_NAME (decl));
> +    target_builtins.put (name, decl);
> +
> +    std::string string_name (name);
> +    if (target_function_types.count (string_name) == 0)
> +    {
> +      tree function_type = TREE_TYPE (decl);
> +      tree arg = TYPE_ARG_TYPES (function_type);
> +      bool is_variadic = false;
> +
> +      auto_vec <recording::type *> param_types;
> +
> +      while (arg != void_list_node)
> +      {
> +     if (arg == NULL)
> +     {
> +       is_variadic = true;
> +       break;
> +     }
> +     if (arg != void_list_node)
> +     {
> +       recording::type* arg_type = tree_type_to_jit_type (TREE_VALUE (arg));
> +       if (arg_type == NULL)
> +         return decl;
> +       param_types.safe_push (arg_type);
> +     }
> +     arg = TREE_CHAIN (arg);
> +      }
> +
> +      tree result_type = TREE_TYPE (function_type);
> +      recording::type* return_type = tree_type_to_jit_type (result_type);
> +
> +      if (return_type == NULL)
> +     return decl;
> +
> +      recording::function_type* func_type =
> +     new recording::function_type (&target_builtins_ctxt, return_type,
> +                                   param_types.length (),
> +                                   param_types.address (), is_variadic,
> +                                   false);
> +
> +      target_function_types[string_name] = func_type;
> +    }
> +  }
>    return decl;
>  }
>

[...snip...]

> diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc
> index 18cc4da25b8..d71ee2b61a7 100644
> --- a/gcc/jit/jit-playback.cc
> +++ b/gcc/jit/jit-playback.cc
> @@ -509,7 +509,8 @@ new_function (location *loc,
>             const char *name,
>             const auto_vec<param *> *params,
>             int is_variadic,
> -           enum built_in_function builtin_id)
> +           enum built_in_function builtin_id,
> +           int is_target_builtin)
>  {
>    int i;
>    param *param;
> @@ -543,6 +544,14 @@ new_function (location *loc,
>    DECL_RESULT (fndecl) = resdecl;
>    DECL_CONTEXT (resdecl) = fndecl;
>  
> +  if (is_target_builtin)
> +  {
> +    tree *decl = target_builtins.get (name);
> +    if (decl != NULL)
> +      fndecl = *decl;
> +    else
> +      add_error (loc, "cannot find target builtin %s", name);
> +  }

It looks like is_target_builtin is only ever used as a flag, so for
clarity's sake, can it and the various m_is_target_builtin be a bool
rather than an int?


>    if (builtin_id)
>      {
>        gcc_assert (loc == NULL);
> diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
> index f9e29d0baec..06128b4d640 100644
> --- a/gcc/jit/jit-playback.h
> +++ b/gcc/jit/jit-playback.h
> @@ -104,7 +104,8 @@ public:
>               const char *name,
>               const auto_vec<param *> *params,
>               int is_variadic,
> -             enum built_in_function builtin_id);
> +             enum built_in_function builtin_id,
> +             int is_target_builtin);
>  
>    lvalue *
>    new_global (location *loc,
> @@ -818,4 +819,6 @@ extern playback::context *active_playback_ctxt;
>  
>  } // namespace gcc
>  
> +extern hash_map<nofree_string_hash, tree> target_builtins;
> +
>  #endif /* JIT_PLAYBACK_H */
  
[...snip...]

Assuming that the i386 maintainers are OK with the change to i386-
builtins.cc, this is OK for trunk if you fix the various nitpicks I
mention above (and you'll probably need to do the usual refreshing of
the ABI version)

On Thu, 2023-11-23 at 17:21 -0500, Antoni Boucher wrote:
> I will need to not forget to update the function
> tree_type_to_jit_type
> in dummy-frontend.cc to add back the support for bfloat16 when the
> patch for it is merged.

Adding this here as a reminder.

Thanks again for the patch.
Dave

Reply via email to