It seems we don't need to do the cleanup in i386-builtins.cc anymore, so
I removed it.
David: Is it possible that your recent fixes for the GC within libgccjit
also fixed the issue here?
Here's the updated patch and answers below.
(GitHub link if you find it easier for review:
https://github.com/antoyo/libgccjit/pull/5)
Thanks.
Le 2024-06-26 à 18 h 49, David Malcolm a écrit :
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.
Done
[...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?
It is also used in jit-playback.cc: does that mean it needs a GTY marker?
+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.
I added a comment.
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...
Indeed, this is used as a global cache, as you noticed below.
+
/* 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?
While it is a timebomb, we cannot error out here as this process all the
builtins, so any unsupported type needs to be processed.
The current way allows to use most builtins and usage of a few builtins
will have incorrect type checking.
[...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?
Done.
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.
Done as well.
Thanks again for the patch.
Dave
From 801caae52908c6e2680aaad674108cbfa3925e17 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
gcc/jit/ChangeLog:
PR jit/108762
* docs/topics/compatibility.rst (LIBGCCJIT_ABI_26): New ABI tag.
* docs/topics/functions.rst: Add documentation for the function
gcc_jit_context_get_target_builtin_function.
* dummy-frontend.cc: Include headers target.h, jit-recording.h,
print-tree.h, unordered_map and string, new variables (target_builtins,
target_function_types, and target_builtins_ctxt), new function
(tree_type_to_jit_type).
* jit-builtins.cc: Specify that the function types are not from
target builtins.
* jit-playback.cc: New argument is_target_builtin to new_function.
* jit-playback.h: New argument is_target_builtin to
new_function.
* jit-recording.cc: New argument is_target_builtin to
new_function_type, function_type constructor and function
constructor, new function
(get_target_builtin_function).
* jit-recording.h: Include headers string and unordered_map, new
variable target_function_types, new argument is_target_builtin
to new_function_type, function_type and function, new functions
(get_target_builtin_function, copy).
* libgccjit.cc: New function
(gcc_jit_context_get_target_builtin_function).
* libgccjit.h: New function
(gcc_jit_context_get_target_builtin_function).
* libgccjit.map: New functions
(gcc_jit_context_get_target_builtin_function).
gcc/testsuite:
PR jit/108762
* jit.dg/all-non-failing-tests.h: New test test-target-builtins.c.
* jit.dg/test-target-builtins.c: New test.
---
gcc/jit/docs/topics/compatibility.rst | 9 +
gcc/jit/docs/topics/functions.rst | 19 ++
gcc/jit/dummy-frontend.cc | 206 ++++++++++++++++++-
gcc/jit/jit-builtins.cc | 6 +-
gcc/jit/jit-playback.cc | 12 +-
gcc/jit/jit-playback.h | 5 +-
gcc/jit/jit-recording.cc | 76 ++++++-
gcc/jit/jit-recording.h | 110 +++++++++-
gcc/jit/libgccjit.cc | 18 ++
gcc/jit/libgccjit.h | 13 ++
gcc/jit/libgccjit.map | 5 +
gcc/testsuite/jit.dg/all-non-failing-tests.h | 3 +
gcc/testsuite/jit.dg/test-target-builtins.c | 77 +++++++
13 files changed, 539 insertions(+), 20 deletions(-)
create mode 100644 gcc/testsuite/jit.dg/test-target-builtins.c
diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst
index 92c3ed24c89..1d65e6f4b54 100644
--- a/gcc/jit/docs/topics/compatibility.rst
+++ b/gcc/jit/docs/topics/compatibility.rst
@@ -404,3 +404,12 @@ on functions and variables:
--------------------
``LIBGCCJIT_ABI_28`` covers the addition of
:func:`gcc_jit_context_new_alignof`
+
+.. _LIBGCCJIT_ABI_29:
+
+``LIBGCCJIT_ABI_29``
+--------------------
+
+``LIBGCCJIT_ABI_29`` 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 804605ea939..16e82a34c21 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 (sometimes called
+ intrinsic functions) with the given name. For example:
+
+ .. code-block:: c
+
+ gcc_jit_function *fn
+ = gcc_jit_context_get_target_builtin_function (ctxt, "__builtin_ia32_pmuldq512_mask");
+
+ .. note:: Due to technical limitations with how libgccjit interacts with
+ the insides of GCC, not all built-in functions are supported. More
+ precisely, not all types are supported for parameters of built-in
+ functions from libgccjit. Attempts to get a built-in function that
+ uses such a parameter will lead to an error being emitted within
+ the context.
+
.. function:: gcc_jit_object *\
gcc_jit_function_as_object (gcc_jit_function *func)
diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
index 17f37a3f405..c4097150171 100644
--- a/gcc/jit/dummy-frontend.cc
+++ b/gcc/jit/dummy-frontend.cc
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see
#include "config.h"
#include "system.h"
#include "coretypes.h"
+#include "target.h"
#include "jit-playback.h"
#include "stor-layout.h"
#include "debug.h"
@@ -32,8 +33,13 @@ along with GCC; see the file COPYING3. If not see
#include "cgraph.h"
#include "target.h"
#include "diagnostic-format-text.h"
+#include "jit-recording.h"
+#include "print-tree.h"
#include <mpfr.h>
+#include <unordered_map>
+
+using namespace gcc::jit;
/* Attribute handling. */
@@ -138,6 +144,14 @@ static const struct attribute_spec::exclusions attr_target_exclusions[] =
ATTR_EXCL (NULL, false, false, false),
};
+/* These variables act as a cache for the target builtins. This is needed in
+ order to be able to type-check the calls since we can only get those types
+ in the playback phase while we need them in the recording phase. */
+hash_map<nofree_string_hash, tree> target_builtins{};
+std::unordered_map<std::string, recording::function_type*> target_function_types
+{};
+recording::context target_builtins_ctxt{NULL};
+
/* Table of machine-independent attributes supported in libgccjit. */
static const attribute_spec jit_gnu_attributes[] =
{
@@ -1032,6 +1046,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
@@ -1039,6 +1054,8 @@ jit_langhook_init (void)
eventually be controllable by a command line option. */
mpfr_set_default_prec (256);
+ targetm.init_builtins ();
+
return true;
}
@@ -1106,11 +1123,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);
+ if (TREE_CODE (type) == RECORD_TYPE)
+ // For __builtin_sysv_va_copy.
+ // FIXME: wrong type.
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_VOID);
+ /* TODO: Remove when we add support for sized floating-point types. */
+ for (int i = 0; i < NUM_FLOATN_NX_TYPES; i++)
+ if (type == FLOATN_NX_TYPE_NODE (i))
+ // FIXME: wrong type.
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_VOID);
+ if (type == void_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_VOID);
+ else if (type == ptr_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_VOID_PTR);
+ else if (type == const_ptr_type_node)
+ {
+ // Void const ptr.
+ recording::type* result =
+ new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_VOID_PTR);
+ return new recording::memento_of_get_const (result);
+ }
+ else if (type == unsigned_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_UNSIGNED_INT);
+ else if (type == long_unsigned_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_UNSIGNED_LONG);
+ else if (type == integer_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_INT);
+ else if (type == long_integer_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_LONG);
+ else if (type == long_long_integer_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_LONG_LONG);
+ else if (type == signed_char_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_SIGNED_CHAR);
+ else if (type == char_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_CHAR);
+ else if (type == unsigned_intQI_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_UINT8_T);
+ else if (type == short_integer_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_SHORT);
+ else if (type == short_unsigned_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_UNSIGNED_SHORT);
+ else if (type == complex_float_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_COMPLEX_FLOAT);
+ else if (type == complex_double_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_COMPLEX_DOUBLE);
+ else if (type == complex_long_double_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE);
+ else if (type == float_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_FLOAT);
+ else if (type == double_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_DOUBLE);
+ else if (type == long_double_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_LONG_DOUBLE);
+ else if (type == bfloat16_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_BFLOAT16);
+ else if (type == dfloat128_type_node)
+ // FIXME: wrong type.
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_VOID);
+ else if (type == long_long_unsigned_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_UNSIGNED_LONG_LONG);
+ else if (type == boolean_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_BOOL);
+ else if (type == size_type_node)
+ return new recording::memento_of_get_type (&target_builtins_ctxt,
+ GCC_JIT_TYPE_SIZE_T);
+ else if (TREE_CODE (type) == POINTER_TYPE)
+ {
+ tree inner_type = TREE_TYPE (type);
+ recording::type* element_type = tree_type_to_jit_type (inner_type);
+ return element_type->get_pointer ();
+ }
+ else
+ {
+ // Attempt to find an unqualified type when the current type has qualifiers.
+ tree tp = TYPE_MAIN_VARIANT (type);
+ for ( ; tp != NULL ; tp = TYPE_NEXT_VARIANT (tp))
+ {
+ if (TYPE_QUALS (tp) == 0 && type != tp)
+ {
+ recording::type* result = tree_type_to_jit_type (tp);
+ if (result != NULL)
+ {
+ if (TYPE_READONLY (tp))
+ result = new recording::memento_of_get_const (result);
+ if (TYPE_VOLATILE (tp))
+ result = new recording::memento_of_get_volatile (result);
+ return result;
+ }
+ }
+ }
+
+ fprintf (stderr, "Unknown type:\n");
+ debug_tree (type);
+ abort ();
+ }
+
+ return NULL;
+}
+
+/* Record a builtin function. We save their types to be able to check types
+ in recording and for reflection. */
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;
}
diff --git a/gcc/jit/jit-builtins.cc b/gcc/jit/jit-builtins.cc
index e0bb24738dd..b9f52db15e7 100644
--- a/gcc/jit/jit-builtins.cc
+++ b/gcc/jit/jit-builtins.cc
@@ -215,7 +215,8 @@ builtins_manager::make_builtin_function (enum built_in_function builtin_id)
param_types.length (),
params,
func_type->is_variadic (),
- builtin_id);
+ builtin_id,
+ false);
delete[] params;
/* PR/64020 - If the client code is using builtin cos or sin,
@@ -582,7 +583,8 @@ builtins_manager::make_fn_type (enum jit_builtin_type,
result = m_ctxt->new_function_type (return_type,
num_args,
param_types,
- is_variadic);
+ is_variadic,
+ false);
error:
delete[] param_types;
diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc
index 583956fb463..ac8d7681837 100644
--- a/gcc/jit/jit-playback.cc
+++ b/gcc/jit/jit-playback.cc
@@ -570,7 +570,8 @@ new_function (location *loc,
std::string>> &string_attributes,
const std::vector<std::pair<gcc_jit_fn_attribute,
std::vector<int>>>
- &int_array_attributes)
+ &int_array_attributes,
+ bool is_target_builtin)
{
int i;
param *param;
@@ -606,6 +607,15 @@ new_function (location *loc,
tree fn_attributes = NULL_TREE;
+ 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);
+ }
+
if (builtin_id)
{
gcc_assert (loc == NULL);
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index 6e97b389cbb..23f686a6300 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -121,7 +121,8 @@ public:
std::string>> &string_attributes,
const std::vector<std::pair<gcc_jit_fn_attribute,
std::vector<int>>>
- &int_array_attributes);
+ &int_array_attributes,
+ bool is_target_builtin);
lvalue *
new_global (location *loc,
@@ -851,4 +852,6 @@ extern playback::context *active_playback_ctxt;
} // namespace gcc
+extern hash_map<nofree_string_hash, tree> target_builtins;
+
#endif /* JIT_PLAYBACK_H */
diff --git a/gcc/jit/jit-recording.cc b/gcc/jit/jit-recording.cc
index cc7f529c9e8..0dedc3acf57 100644
--- a/gcc/jit/jit-recording.cc
+++ b/gcc/jit/jit-recording.cc
@@ -934,14 +934,16 @@ recording::function_type *
recording::context::new_function_type (recording::type *return_type,
int num_params,
recording::type **param_types,
- int is_variadic)
+ int is_variadic,
+ bool is_target_builtin)
{
recording::function_type *fn_type
= new function_type (this,
return_type,
num_params,
param_types,
- is_variadic);
+ is_variadic,
+ is_target_builtin);
record (fn_type);
return fn_type;
}
@@ -963,7 +965,8 @@ recording::context::new_function_ptr_type (recording::location *, /* unused loc
= new_function_type (return_type,
num_params,
param_types,
- is_variadic);
+ is_variadic,
+ false);
/* Return a pointer-type to the function type. */
return fn_type->get_pointer ();
@@ -1006,7 +1009,7 @@ recording::context::new_function (recording::location *loc,
loc, kind, return_type,
new_string (name),
num_params, params, is_variadic,
- builtin_id);
+ builtin_id, false);
record (result);
m_functions.safe_push (result);
@@ -1045,6 +1048,53 @@ recording::context::get_builtin_function (const char *name)
return bm->get_builtin_function (name);
}
+/* Create a recording::function instance for a target-specific builtin.
+
+ Implements the post-error-checking part of
+ gcc_jit_context_get_target_builtin_function. */
+
+recording::function *
+recording::context::get_target_builtin_function (const char *name)
+{
+ const char *asm_name = name;
+ if (target_function_types.count (name) == 0)
+ {
+ fprintf (stderr, "Cannot find target builtin %s\n", name);
+ return NULL;
+ }
+
+ recording::function_type* func_type = target_function_types[name]
+ ->copy (this)->dyn_cast_function_type ();
+ const vec<type *>& param_types = func_type->get_param_types ();
+ recording::param **params = new recording::param *[param_types.length ()];
+
+ int i;
+ recording::type *param_type;
+ FOR_EACH_VEC_ELT (param_types, i, param_type)
+ {
+ char buf[16];
+ snprintf (buf, 16, "arg%d", i);
+ params[i] = new_param (NULL,
+ param_type,
+ buf);
+ }
+
+ recording::function *result =
+ new recording::function (this,
+ NULL,
+ GCC_JIT_FUNCTION_IMPORTED,
+ func_type->get_return_type (),
+ new_string (asm_name),
+ param_types.length (),
+ params,
+ func_type->is_variadic (),
+ BUILT_IN_NONE,
+ true);
+ record (result);
+
+ return result;
+}
+
/* Create a recording::global instance and add it to this context's list
of mementos.
@@ -3191,11 +3241,13 @@ recording::function_type::function_type (context *ctxt,
type *return_type,
int num_params,
type **param_types,
- int is_variadic)
+ int is_variadic,
+ bool is_target_builtin)
: type (ctxt),
m_return_type (return_type),
m_param_types (),
- m_is_variadic (is_variadic)
+ m_is_variadic (is_variadic),
+ m_is_target_builtin (is_target_builtin)
{
for (int i = 0; i< num_params; i++)
m_param_types.safe_push (param_types[i]);
@@ -4144,7 +4196,8 @@ recording::function::function (context *ctxt,
int num_params,
recording::param **params,
int is_variadic,
- enum built_in_function builtin_id)
+ enum built_in_function builtin_id,
+ bool is_target_builtin)
: memento (ctxt),
m_loc (loc),
m_kind (kind),
@@ -4158,7 +4211,8 @@ recording::function::function (context *ctxt,
m_fn_ptr_type (NULL),
m_attributes (),
m_string_attributes (),
- m_int_array_attributes ()
+ m_int_array_attributes (),
+ m_is_target_builtin (is_target_builtin)
{
for (int i = 0; i< num_params; i++)
{
@@ -4220,7 +4274,8 @@ recording::function::replay_into (replayer *r)
m_builtin_id,
m_attributes,
m_string_attributes,
- m_int_array_attributes));
+ m_int_array_attributes,
+ m_is_target_builtin));
}
/* Create a recording::local instance and add it to
@@ -4487,7 +4542,8 @@ recording::function::get_address (recording::location *loc)
= m_ctxt->new_function_type (m_return_type,
m_params.length (),
param_types.address (),
- m_is_variadic);
+ m_is_variadic,
+ m_is_target_builtin);
m_fn_ptr_type = fn_type->get_pointer ();
}
gcc_assert (m_fn_ptr_type);
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index abd4f6f8bb3..8ba9ebdf01c 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -28,12 +28,16 @@ along with GCC; see the file COPYING3. If not see
#include <string>
#include <vector>
+#include <unordered_map>
+
class timer;
+extern std::unordered_map<std::string, gcc::jit::recording::function_type*>
+ target_function_types;
+
namespace gcc {
namespace jit {
-
extern const char * const unary_op_reproducer_strings[];
extern const char * const binary_op_reproducer_strings[];
@@ -125,7 +129,8 @@ public:
new_function_type (type *return_type,
int num_params,
type **param_types,
- int is_variadic);
+ int is_variadic,
+ bool is_target_builtin);
type *
new_function_ptr_type (location *loc,
@@ -152,6 +157,9 @@ public:
function *
get_builtin_function (const char *name);
+ function *
+ get_target_builtin_function (const char *name);
+
lvalue *
new_global (location *loc,
enum gcc_jit_global_kind kind,
@@ -555,6 +563,8 @@ public:
these types. */
virtual size_t get_size () { gcc_unreachable (); }
+ virtual type* copy (context* ctxt) = 0;
+
/* Dynamic casts. */
virtual function_type *dyn_cast_function_type () { return NULL; }
virtual function_type *as_a_function_type() { gcc_unreachable (); return NULL; }
@@ -632,6 +642,11 @@ public:
size_t get_size () final override;
+ type* copy (context* ctxt) final override
+ {
+ return ctxt->get_type (m_kind);
+ }
+
bool accepts_writes_from (type *rtype) final override
{
if (m_kind == GCC_JIT_TYPE_VOID_PTR)
@@ -683,6 +698,13 @@ public:
type *dereference () final override { return m_other_type; }
+ type* copy (context* ctxt) final override
+ {
+ type* result = new memento_of_get_pointer (m_other_type->copy (ctxt));
+ ctxt->record (result);
+ return result;
+ }
+
size_t get_size () final override;
bool accepts_writes_from (type *rtype) final override;
@@ -746,6 +768,13 @@ public:
return false;
}
+ type* copy (context* ctxt) final override
+ {
+ type* result = new memento_of_get_const (m_other_type->copy (ctxt));
+ ctxt->record (result);
+ return result;
+ }
+
/* Strip off the "const", giving the underlying type. */
type *unqualified () final override { return m_other_type; }
@@ -779,6 +808,13 @@ public:
return m_other_type->is_same_type_as (other->is_volatile ());
}
+ type* copy (context* ctxt) final override
+ {
+ type* result = new memento_of_get_volatile (m_other_type->copy (ctxt));
+ ctxt->record (result);
+ return result;
+ }
+
/* Strip off the "volatile", giving the underlying type. */
type *unqualified () final override { return m_other_type; }
@@ -805,6 +841,13 @@ public:
return m_other_type->is_same_type_as (other->is_restrict ());
}
+ type* copy (context* ctxt) final override
+ {
+ type* result = new memento_of_get_restrict (m_other_type->copy (ctxt));
+ ctxt->record (result);
+ return result;
+ }
+
/* Strip off the "restrict", giving the underlying type. */
type *unqualified () final override { return m_other_type; }
@@ -825,6 +868,14 @@ public:
: decorated_type (other_type),
m_alignment_in_bytes (alignment_in_bytes) {}
+ type* copy (context* ctxt) final override
+ {
+ type* result = new memento_of_get_aligned (m_other_type->copy (ctxt),
+ m_alignment_in_bytes);
+ ctxt->record (result);
+ return result;
+ }
+
/* Strip off the alignment, giving the underlying type. */
type *unqualified () final override { return m_other_type; }
@@ -859,6 +910,13 @@ public:
return true;
}
+ type* copy (context* ctxt) final override
+ {
+ type* result = new vector_type (m_other_type->copy (ctxt), m_num_units);
+ ctxt->record (result);
+ return result;
+ }
+
size_t get_num_units () const { return m_num_units; }
vector_type *dyn_cast_vector_type () final override { return this; }
@@ -912,6 +970,14 @@ class array_type : public type
array_type *dyn_cast_array_type () final override { return this; }
+ type* copy (context* ctxt) final override
+ {
+ type* result = new array_type (ctxt, m_loc, m_element_type->copy (ctxt),
+ m_num_elements);
+ ctxt->record (result);
+ return result;
+ }
+
bool is_int () const final override { return false; }
bool is_float () const final override { return false; }
bool is_bool () const final override { return false; }
@@ -939,7 +1005,8 @@ public:
type *return_type,
int num_params,
type **param_types,
- int is_variadic);
+ int is_variadic,
+ bool is_target_builtin);
type *dereference () final override;
function_type *dyn_cast_function_type () final override { return this; }
@@ -947,6 +1014,20 @@ public:
bool is_same_type_as (type *other) final override;
+ type* copy (context* ctxt) final override
+ {
+ auto_vec<type *> new_params{};
+ for (size_t i = 0; i < m_param_types.length (); i++)
+ new_params.safe_push (m_param_types[i]->copy (ctxt));
+
+ type* result = new function_type (ctxt, m_return_type->copy (ctxt),
+ m_param_types.length (),
+ new_params.address (),
+ m_is_variadic, m_is_target_builtin);
+ ctxt->record (result);
+ return result;
+ }
+
bool is_int () const final override { return false; }
bool is_float () const final override { return false; }
bool is_bool () const final override { return false; }
@@ -975,6 +1056,7 @@ private:
type *m_return_type;
auto_vec<type *> m_param_types;
int m_is_variadic;
+ bool m_is_target_builtin;
};
class field : public memento
@@ -1076,9 +1158,11 @@ public:
return static_cast <playback::compound_type *> (m_playback_obj);
}
-private:
+protected:
location *m_loc;
string *m_name;
+
+private:
fields *m_fields;
};
@@ -1091,6 +1175,13 @@ public:
struct_ *dyn_cast_struct () final override { return this; }
+ type* copy (context* ctxt) final override
+ {
+ type* result = new struct_ (ctxt, m_loc, m_name);
+ ctxt->record (result);
+ return result;
+ }
+
type *
as_type () { return this; }
@@ -1138,6 +1229,13 @@ public:
void replay_into (replayer *r) final override;
+ type* copy (context* ctxt) final override
+ {
+ type* result = new union_ (ctxt, m_loc, m_name);
+ ctxt->record (result);
+ return result;
+ }
+
bool is_union () const final override { return true; }
private:
@@ -1353,7 +1451,8 @@ public:
int num_params,
param **params,
int is_variadic,
- enum built_in_function builtin_id);
+ enum built_in_function builtin_id,
+ bool is_target_builtin);
void replay_into (replayer *r) final override;
@@ -1415,6 +1514,7 @@ private:
std::vector<gcc_jit_fn_attribute> m_attributes;
std::vector<std::pair<gcc_jit_fn_attribute, std::string>> m_string_attributes;
std::vector<std::pair<gcc_jit_fn_attribute, std::vector<int>>> m_int_array_attributes;
+ bool m_is_target_builtin;
};
class block : public memento
diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc
index eb38da91df1..d7476fc8129 100644
--- a/gcc/jit/libgccjit.cc
+++ b/gcc/jit/libgccjit.cc
@@ -1760,6 +1760,24 @@ gcc_jit_context_new_array_constructor (gcc_jit_context *ctxt,
reinterpret_cast<gcc::jit::recording::rvalue**>(values));
}
+/* Public entrypoint. See description in libgccjit.h.
+
+ After error-checking, the real work is done by the
+ gcc::jit::recording::context::get_target_builtin_function method, in
+ jit-recording.c. */
+
+gcc_jit_function *
+gcc_jit_context_get_target_builtin_function (gcc_jit_context *ctxt,
+ const char *name)
+{
+ RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+ JIT_LOG_FUNC (ctxt->get_logger ());
+ RETURN_NULL_IF_FAIL (name, ctxt, NULL, "NULL name");
+
+ return static_cast <gcc_jit_function *> (
+ ctxt->get_target_builtin_function (name));
+}
+
/* Public entrypoint. See description in libgccjit.h. */
extern gcc_jit_lvalue *
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 03bfc0f58a5..eddf52c9e7e 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -1037,6 +1037,19 @@ extern gcc_jit_lvalue *
gcc_jit_global_set_initializer_rvalue (gcc_jit_lvalue *global,
gcc_jit_rvalue *init_value);
+#define LIBGCCJIT_HAVE_gcc_jit_context_get_target_builtin_function
+
+/* Create a reference to a machine-specific builtin function (sometimes called
+ intrinsic functions).
+
+ This API entrypoint was added in LIBGCCJIT_ABI_29; you can test for its
+ presence using
+ #ifdef LIBGCCJIT_HAVE_gcc_jit_context_get_target_builtin_function
+*/
+extern gcc_jit_function *
+gcc_jit_context_get_target_builtin_function (gcc_jit_context *ctxt,
+ const char *name);
+
#define LIBGCCJIT_HAVE_gcc_jit_global_set_initializer
/* Set an initial value for a global, which must be an array of
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index b02783ebfb2..d26b90b0e1c 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -294,3 +294,8 @@ LIBGCCJIT_ABI_28 {
global:
gcc_jit_context_new_alignof;
} LIBGCCJIT_ABI_27;
+
+LIBGCCJIT_ABI_29 {
+ global:
+ gcc_jit_context_get_target_builtin_function;
+} LIBGCCJIT_ABI_28;
diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h
index 75721329ab5..3609318369f 100644
--- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
+++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
@@ -370,6 +370,9 @@
#undef create_code
#undef verify_code
+/* test-target-builtins.c: This can't be in the testcases array as it
+ is target-specific. */
+
/* test-string-literal.c */
#define create_code create_code_string_literal
#define verify_code verify_code_string_literal
diff --git a/gcc/testsuite/jit.dg/test-target-builtins.c b/gcc/testsuite/jit.dg/test-target-builtins.c
new file mode 100644
index 00000000000..0ffb48e97f6
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-target-builtins.c
@@ -0,0 +1,77 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_PROVIDES_MAIN
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+ CHECK_NON_NULL (gcc_jit_context_get_target_builtin_function (ctxt, "__builtin_ia32_xgetbv"));
+ gcc_jit_function *builtin_eh_pointer = gcc_jit_context_get_target_builtin_function (ctxt, "__builtin_eh_pointer");
+ CHECK_NON_NULL (builtin_eh_pointer);
+
+ gcc_jit_type *int_type =
+ gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+ gcc_jit_type *void_ptr =
+ gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID_PTR);
+ gcc_jit_function *func_main =
+ gcc_jit_context_new_function (ctxt, NULL,
+ GCC_JIT_FUNCTION_EXPORTED,
+ int_type,
+ "main",
+ 0, NULL,
+ 0);
+ gcc_jit_rvalue *zero = gcc_jit_context_zero (ctxt, int_type);
+ gcc_jit_block *block = gcc_jit_function_new_block (func_main, NULL);
+ gcc_jit_lvalue *variable = gcc_jit_function_new_local(func_main, NULL, void_ptr, "variable");
+ gcc_jit_block_add_assignment (block, NULL, variable,
+ gcc_jit_context_new_call (ctxt, NULL, builtin_eh_pointer, 1, &zero));
+ gcc_jit_block_end_with_return (block, NULL, zero);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+ CHECK_NON_NULL (result);
+}
+
+int
+main (int argc, char **argv)
+{
+ /* This is the same as the main provided by harness.h, but it first create a dummy context and compile
+ in order to add the target builtins to libgccjit's internal state. */
+ gcc_jit_context *ctxt;
+ ctxt = gcc_jit_context_acquire ();
+ if (!ctxt)
+ {
+ fail ("gcc_jit_context_acquire failed");
+ return -1;
+ }
+ gcc_jit_result *result;
+ result = gcc_jit_context_compile (ctxt);
+ gcc_jit_result_release (result);
+ gcc_jit_context_release (ctxt);
+
+ int i;
+
+ for (i = 1; i <= 5; i++)
+ {
+ snprintf (test, sizeof (test),
+ "%s iteration %d of %d",
+ extract_progname (argv[0]),
+ i, 5);
+
+ //printf ("ITERATION %d\n", i);
+ test_jit (argv[0], NULL);
+ //printf ("\n");
+ }
+
+ totals ();
+
+ return 0;
+}
--
2.47.0