From dc27f66396ba9d05bc8007d8509620e5ae14d834 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 10 Jan 2024 15:23:37 +0100 Subject: [PATCH] [PATCH] libgccjit: Add support for function attributes and variable attributes. gcc/jit/ChangeLog: * dummy-frontend.cc (handle_alias_attribute): New function. (handle_always_inline_attribute): New function. (handle_cold_attribute): New function. (handle_fnspec_attribute): New function. (handle_format_arg_attribute): New function. (handle_format_attribute): New function. (handle_noinline_attribute): New function. (handle_target_attribute): New function. (handle_used_attribute): New function. (handle_visibility_attribute): New function. (handle_weak_attribute): New function. (handle_alias_ifunc_attribute): New function. * jit-playback.cc (fn_attribute_to_string): New function. (variable_attribute_to_string): New function. (global_new_decl): Add attributes support. (set_variable_attribute): New function. (new_global): Add attributes support. (new_global_initialized): Add attributes support. (new_local): Add attributes support. * jit-playback.h (fn_attribute_to_string): New function. (set_variable_attribute): New function. * jit-recording.cc (recording::lvalue::add_attribute): New function. (recording::function::function): New function. (recording::function::write_to_dump): Add attributes support. (recording::function::add_attribute): New function. (recording::function::add_string_attribute): New function. (recording::function::add_integer_array_attribute): New function. (recording::global::replay_into): Add attributes support. (recording::local::replay_into): Add attributes support. * libgccjit.cc (gcc_jit_function_add_attribute): New function. (gcc_jit_function_add_string_attribute): New function. (gcc_jit_function_add_integer_array_attribute): New function. (gcc_jit_lvalue_add_attribute): New function. * libgccjit.h (enum gcc_jit_fn_attribute): New enum. (gcc_jit_function_add_attribute): New function. (gcc_jit_function_add_string_attribute): New function. (gcc_jit_function_add_integer_array_attribute): New function. (enum gcc_jit_variable_attribute): New function. (gcc_jit_lvalue_add_string_attribute): New function. * libgccjit.map: Declare new functions. gcc/testsuite/ChangeLog: * jit.dg/all-non-failing-tests.h: Add new attributes tests. * jit.dg/jit.exp: Add `jit-verify-assembler-output-not` test command. * jit.dg/test-restrict.c: New test. * jit.dg/test-restrict-attribute.c: New test. * jit.dg/test-alias-attribute.c: New test. * jit.dg/test-always_inline-attribute.c: New test. * jit.dg/test-cold-attribute.c: New test. * jit.dg/test-const-attribute.c: New test. * jit.dg/test-noinline-attribute.c: New test. * jit.dg/test-nonnull-attribute.c: New test. * jit.dg/test-pure-attribute.c: New test. * jit.dg/test-used-attribute.c: New test. * jit.dg/test-variable-attribute.c: New test. * jit.dg/test-weak-attribute.c: New test. gcc/jit/ChangeLog: * docs/topics/compatibility.rst: Add documentation for LIBGCCJIT_ABI_26. * docs/topics/types.rst: Add documentation for new functions. Co-authored-by: Antoni Boucher Signed-off-by: Guillaume Gomez --- gcc/jit/docs/topics/compatibility.rst | 12 + gcc/jit/docs/topics/types.rst | 77 +++ gcc/jit/dummy-frontend.cc | 512 ++++++++++++++++-- gcc/jit/jit-playback.cc | 169 +++++- gcc/jit/jit-playback.h | 37 +- gcc/jit/jit-recording.cc | 166 +++++- gcc/jit/jit-recording.h | 22 +- gcc/jit/libgccjit.cc | 67 +++ gcc/jit/libgccjit.h | 55 ++ gcc/jit/libgccjit.map | 8 + gcc/testsuite/jit.dg/all-non-failing-tests.h | 32 +- gcc/testsuite/jit.dg/jit.exp | 33 ++ gcc/testsuite/jit.dg/test-alias-attribute.c | 50 ++ .../jit.dg/test-always_inline-attribute.c | 153 ++++++ gcc/testsuite/jit.dg/test-cold-attribute.c | 54 ++ gcc/testsuite/jit.dg/test-const-attribute.c | 134 +++++ .../jit.dg/test-noinline-attribute.c | 121 +++++ gcc/testsuite/jit.dg/test-nonnull-attribute.c | 94 ++++ gcc/testsuite/jit.dg/test-pure-attribute.c | 134 +++++ .../jit.dg/test-restrict-attribute.c | 77 +++ gcc/testsuite/jit.dg/test-used-attribute.c | 112 ++++ .../jit.dg/test-variable-attribute.c | 46 ++ gcc/testsuite/jit.dg/test-weak-attribute.c | 41 ++ 23 files changed, 2132 insertions(+), 74 deletions(-) create mode 100644 gcc/testsuite/jit.dg/test-alias-attribute.c create mode 100644 gcc/testsuite/jit.dg/test-always_inline-attribute.c create mode 100644 gcc/testsuite/jit.dg/test-cold-attribute.c create mode 100644 gcc/testsuite/jit.dg/test-const-attribute.c create mode 100644 gcc/testsuite/jit.dg/test-noinline-attribute.c create mode 100644 gcc/testsuite/jit.dg/test-nonnull-attribute.c create mode 100644 gcc/testsuite/jit.dg/test-pure-attribute.c create mode 100644 gcc/testsuite/jit.dg/test-restrict-attribute.c create mode 100644 gcc/testsuite/jit.dg/test-used-attribute.c create mode 100644 gcc/testsuite/jit.dg/test-variable-attribute.c create mode 100644 gcc/testsuite/jit.dg/test-weak-attribute.c diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst index 704de1b51ed..cbf5b414d8c 100644 --- a/gcc/jit/docs/topics/compatibility.rst +++ b/gcc/jit/docs/topics/compatibility.rst @@ -378,3 +378,15 @@ 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 functions to set attributes +on functions and variables: + + * :func:`gcc_jit_function_add_attribute` + * :func:`gcc_jit_function_add_string_attribute` + * :func:`gcc_jit_function_add_integer_array_attribute` + * :func:`gcc_jit_lvalue_add_string_attribute` diff --git a/gcc/jit/docs/topics/types.rst b/gcc/jit/docs/topics/types.rst index bb51f037b7e..b1aedc03787 100644 --- a/gcc/jit/docs/topics/types.rst +++ b/gcc/jit/docs/topics/types.rst @@ -553,3 +553,80 @@ Reflection API .. code-block:: c #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_restrict + +.. function:: void\ + gcc_jit_function_add_attribute (gcc_jit_function *func, + enum gcc_jit_fn_attribute attribute) + + Add an attribute ``attribute`` to a function ``func``. + + This is equivalent to the following code: + + .. code-block:: c + + __attribute__((always_inline)) + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_ATTRIBUTES + +.. function:: void\ + gcc_jit_function_add_string_attribute (gcc_jit_function *func, + enum gcc_jit_fn_attribute attribute, + const char *value) + + Add a string attribute ``attribute`` with value ``value`` to a function + ``func``. + + This is equivalent to the following code: + + .. code-block:: c + + __attribute__ ((alias ("xxx"))) + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_ATTRIBUTES + +.. function:: void\ + gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func, + enum gcc_jit_fn_attribute attribute, + const int *value, + size_t length) + + Add an attribute ``attribute`` with ``length`` integer values ``value`` to a + function ``func``. The integer values must be the same as you would write + them in a C code. + + This is equivalent to the following code: + + .. code-block:: c + + __attribute__ ((nonnull (1, 2))) + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_ATTRIBUTES + +.. function:: void\ + gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable, + enum gcc_jit_variable_attribute attribute, + const char *value) + + Add an attribute ``attribute`` with value ``value`` to a variable ``variable``. + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_ATTRIBUTES diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc index 211f1be98fa..dbeeacd17a8 100644 --- a/gcc/jit/dummy-frontend.cc +++ b/gcc/jit/dummy-frontend.cc @@ -29,30 +29,42 @@ along with GCC; see the file COPYING3. If not see #include "options.h" #include "stringpool.h" #include "attribs.h" +#include "cgraph.h" +#include "target.h" #include /* Attribute handling. */ -static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *); -static tree handle_leaf_attribute (tree *, tree, tree, int, bool *); +static tree handle_alias_attribute (tree *, tree, tree, int, bool *); +static tree handle_always_inline_attribute (tree *, tree, tree, int, + bool *); +static tree handle_cold_attribute (tree *, tree, tree, int, bool *); static tree handle_const_attribute (tree *, tree, tree, int, bool *); +static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *); +static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *); +static tree handle_format_attribute (tree *, tree, tree, int, bool *); +static tree handle_leaf_attribute (tree *, tree, tree, int, bool *); static tree handle_malloc_attribute (tree *, tree, tree, int, bool *); -static tree handle_pure_attribute (tree *, tree, tree, int, bool *); -static tree handle_novops_attribute (tree *, tree, tree, int, bool *); +static tree handle_noinline_attribute (tree *, tree, tree, int, bool *); static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *); +static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *); static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *); -static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *); -static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *); -static tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *); -static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *); +static tree handle_novops_attribute (tree *, tree, tree, int, bool *); static tree handle_patchable_function_entry_attribute (tree *, tree, tree, int, bool *); -static tree ignore_attribute (tree *, tree, tree, int, bool *); +static tree handle_pure_attribute (tree *, tree, tree, int, bool *); +static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *); +static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *); +static tree handle_target_attribute (tree *, tree, tree, int, bool *); +static tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *); +static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *); +static tree handle_used_attribute (tree *, tree, tree, int, bool *); +static tree handle_visibility_attribute (tree *, tree, tree, int, + bool *); +static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; -static tree handle_format_attribute (tree *, tree, tree, int, bool *); -static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *); -static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *); +static tree ignore_attribute (tree *, tree, tree, int, bool *); /* Helper to define attribute exclusions. */ #define ATTR_EXCL(name, function, type, variable) \ @@ -61,7 +73,6 @@ static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *); /* Define attributes that are mutually exclusive with one another. */ static const struct attribute_spec::exclusions attr_noreturn_exclusions[] = { - ATTR_EXCL ("noreturn", true, true, true), ATTR_EXCL ("alloc_align", true, true, true), ATTR_EXCL ("alloc_size", true, true, true), ATTR_EXCL ("const", true, true, true), @@ -78,57 +89,117 @@ static const struct attribute_spec::exclusions attr_returns_twice_exclusions[] = ATTR_EXCL (NULL, false, false, false), }; +/* Exclusions that apply to attribute alloc_align, alloc_size, and malloc. */ +static const struct attribute_spec::exclusions attr_alloc_exclusions[] = +{ + ATTR_EXCL ("const", true, true, true), + ATTR_EXCL ("noreturn", true, true, true), + ATTR_EXCL ("pure", true, true, true), + ATTR_EXCL (NULL, false, false, false), +}; + static const struct attribute_spec::exclusions attr_const_pure_exclusions[] = { ATTR_EXCL ("const", true, true, true), + ATTR_EXCL ("alloc_align", true, true, true), + ATTR_EXCL ("alloc_size", true, true, true), + ATTR_EXCL ("malloc", true, true, true), ATTR_EXCL ("noreturn", true, true, true), ATTR_EXCL ("pure", true, true, true), ATTR_EXCL (NULL, false, false, false) }; +static const struct attribute_spec::exclusions attr_always_inline_exclusions[] = +{ + ATTR_EXCL ("noinline", true, true, true), + ATTR_EXCL ("target_clones", true, true, true), + ATTR_EXCL (NULL, false, false, false), +}; + +extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[] = +{ + ATTR_EXCL ("cold", true, true, true), + ATTR_EXCL ("hot", true, true, true), + ATTR_EXCL (NULL, false, false, false) +}; + +static const struct attribute_spec::exclusions attr_noinline_exclusions[] = +{ + ATTR_EXCL ("always_inline", true, true, true), + ATTR_EXCL ("gnu_inline", true, true, true), + ATTR_EXCL (NULL, false, false, false), +}; + +static const struct attribute_spec::exclusions attr_target_exclusions[] = +{ + ATTR_EXCL ("target_clones", TARGET_HAS_FMV_TARGET_ATTRIBUTE, + TARGET_HAS_FMV_TARGET_ATTRIBUTE, TARGET_HAS_FMV_TARGET_ATTRIBUTE), + ATTR_EXCL (NULL, false, false, false), +}; + /* Table of machine-independent attributes supported in libgccjit. */ static const attribute_spec jit_gnu_attributes[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, affects_type_identity, handler, exclude } */ - { "noreturn", 0, 0, true, false, false, false, - handle_noreturn_attribute, - attr_noreturn_exclusions }, - { "leaf", 0, 0, true, false, false, false, - handle_leaf_attribute, NULL }, + { "alias", 1, 1, true, false, false, false, + handle_alias_attribute, NULL }, + { "always_inline", 0, 0, true, false, false, false, + handle_always_inline_attribute, + attr_always_inline_exclusions }, + { "cold", 0, 0, true, false, false, false, + handle_cold_attribute, + attr_cold_hot_exclusions }, /* The same comments as for noreturn attributes apply to const ones. */ - { "const", 0, 0, true, false, false, false, + { "const", 0, 0, true, false, false, false, handle_const_attribute, attr_const_pure_exclusions }, - { "malloc", 0, 0, true, false, false, false, - handle_malloc_attribute, NULL }, - { "pure", 0, 0, true, false, false, false, - handle_pure_attribute, - attr_const_pure_exclusions }, - { "no vops", 0, 0, true, false, false, false, + { "fn spec", 1, 1, false, true, true, false, + handle_fnspec_attribute, NULL }, + + { "leaf", 0, 0, true, false, false, false, + handle_leaf_attribute, NULL }, + { "malloc", 0, 0, true, false, false, false, + handle_malloc_attribute, attr_alloc_exclusions }, + { "noreturn", 0, 0, true, false, false, false, + handle_noreturn_attribute, + attr_noreturn_exclusions }, + { "no vops", 0, 0, true, false, false, false, handle_novops_attribute, NULL }, - { "nonnull", 0, -1, false, true, true, false, + { "noinline", 0, 0, true, false, false, false, + handle_noinline_attribute, + attr_noinline_exclusions }, + { "nonnull", 0, -1, false, true, true, false, handle_nonnull_attribute, NULL }, - { "nothrow", 0, 0, true, false, false, false, + { "nothrow", 0, 0, true, false, false, false, handle_nothrow_attribute, NULL }, { "patchable_function_entry", 1, 2, true, false, false, false, handle_patchable_function_entry_attribute, NULL }, - { "returns_twice", 0, 0, true, false, false, false, + { "pure", 0, 0, true, false, false, false, + handle_pure_attribute, + attr_const_pure_exclusions }, + { "returns_twice", 0, 0, true, false, false, false, handle_returns_twice_attribute, attr_returns_twice_exclusions }, - { "sentinel", 0, 1, false, true, true, false, + { "sentinel", 0, 1, false, true, true, false, handle_sentinel_attribute, NULL }, - { "type generic", 0, 0, false, true, true, false, + { "target", 1, -1, true, false, false, false, + handle_target_attribute, attr_target_exclusions }, + { "type generic", 0, 0, false, true, true, false, handle_type_generic_attribute, NULL }, - { "fn spec", 1, 1, false, true, true, false, - handle_fnspec_attribute, NULL }, { "transaction_pure", 0, 0, false, true, true, false, handle_transaction_pure_attribute, NULL }, + { "used", 0, 0, true, false, false, false, + handle_used_attribute, NULL }, + { "visibility", 1, 1, false, false, false, false, + handle_visibility_attribute, NULL }, + { "weak", 0, 0, true, false, false, false, + handle_weak_attribute, NULL }, /* For internal use only. The leading '*' both prevents its usage in source code and signals that it may be overridden by machine tables. */ { "*tm regparm", 0, 0, false, true, true, false, - ignore_attribute, NULL } + ignore_attribute, NULL }, }; static const scoped_attribute_specs jit_gnu_attribute_table = @@ -143,7 +214,7 @@ static const attribute_spec jit_format_attributes[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, affects_type_identity, handler, exclude } */ - { "format", 3, 3, false, true, true, false, + { "format", 3, 3, false, true, true, false, handle_format_attribute, NULL }, { "format_arg", 1, 1, false, true, true, false, handle_format_arg_attribute, NULL } @@ -212,14 +283,9 @@ handle_leaf_attribute (tree *node, tree name, struct attribute_spec.handler. */ static tree -handle_const_attribute (tree *node, tree ARG_UNUSED (name), - tree ARG_UNUSED (args), int ARG_UNUSED (flags), - bool * ARG_UNUSED (no_add_attrs)) +handle_const_attribute (tree *node, tree name, tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) { - if (TREE_CODE (*node) != FUNCTION_DECL - || !fndecl_built_in_p (*node)) - inform (UNKNOWN_LOCATION, "%s:%s: %E: %E", __FILE__, __func__, *node, name); - tree type = TREE_TYPE (*node); /* See FIXME comment on noreturn in c_common_attribute_table. */ @@ -228,11 +294,16 @@ handle_const_attribute (tree *node, tree ARG_UNUSED (name), else if (TREE_CODE (type) == POINTER_TYPE && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE) TREE_TYPE (*node) - = build_pointer_type - (build_type_variant (TREE_TYPE (type), 1, - TREE_THIS_VOLATILE (TREE_TYPE (type)))); + = (build_qualified_type + (build_pointer_type + (build_type_variant (TREE_TYPE (type), 1, + TREE_THIS_VOLATILE (TREE_TYPE (type)))), + TYPE_QUALS (type))); else - gcc_unreachable (); + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } return NULL_TREE; } @@ -494,6 +565,357 @@ handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name), return NULL_TREE; } +/* Handle an "visibility" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_visibility_attribute (tree *node, tree name, tree args, + int ARG_UNUSED (flags), + bool *ARG_UNUSED (no_add_attrs)) +{ + tree decl = *node; + tree id = TREE_VALUE (args); + enum symbol_visibility vis; + + if (TYPE_P (*node)) + { + if (TREE_CODE (*node) == ENUMERAL_TYPE) + /* OK. */; + else if (!RECORD_OR_UNION_TYPE_P (*node)) + { + warning (OPT_Wattributes, "%qE attribute ignored on non-class types", + name); + return NULL_TREE; + } + else if (TYPE_FIELDS (*node)) + { + error ("%qE attribute ignored because %qT is already defined", + name, *node); + return NULL_TREE; + } + } + else if (decl_function_context (decl) != 0 || !TREE_PUBLIC (decl)) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + return NULL_TREE; + } + + if (TREE_CODE (id) != STRING_CST) + { + error ("visibility argument not a string"); + return NULL_TREE; + } + + /* If this is a type, set the visibility on the type decl. */ + if (TYPE_P (decl)) + { + decl = TYPE_NAME (decl); + if (!decl) + return NULL_TREE; + if (TREE_CODE (decl) == IDENTIFIER_NODE) + { + warning (OPT_Wattributes, "%qE attribute ignored on types", + name); + return NULL_TREE; + } + } + + if (strcmp (TREE_STRING_POINTER (id), "default") == 0) + vis = VISIBILITY_DEFAULT; + else if (strcmp (TREE_STRING_POINTER (id), "internal") == 0) + vis = VISIBILITY_INTERNAL; + else if (strcmp (TREE_STRING_POINTER (id), "hidden") == 0) + vis = VISIBILITY_HIDDEN; + else if (strcmp (TREE_STRING_POINTER (id), "protected") == 0) + vis = VISIBILITY_PROTECTED; + else + { + error ("attribute %qE argument must be one of %qs, %qs, %qs, or %qs", + name, "default", "hidden", "protected", "internal"); + vis = VISIBILITY_DEFAULT; + } + + if (DECL_VISIBILITY_SPECIFIED (decl) + && vis != DECL_VISIBILITY (decl)) + { + tree attributes = (TYPE_P (*node) + ? TYPE_ATTRIBUTES (*node) + : DECL_ATTRIBUTES (decl)); + if (lookup_attribute ("visibility", attributes)) + error ("%qD redeclared with different visibility", decl); + else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES + && lookup_attribute ("dllimport", attributes)) + error ("%qD was declared %qs which implies default visibility", + decl, "dllimport"); + else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES + && lookup_attribute ("dllexport", attributes)) + error ("%qD was declared %qs which implies default visibility", + decl, "dllexport"); + } + + DECL_VISIBILITY (decl) = vis; + DECL_VISIBILITY_SPECIFIED (decl) = 1; + + /* Go ahead and attach the attribute to the node as well. This is needed + so we can determine whether we have VISIBILITY_DEFAULT because the + visibility was not specified, or because it was explicitly overridden + from the containing scope. */ + + return NULL_TREE; +} + +/* Handle a "always_inline" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_always_inline_attribute (tree *node, tree name, + tree ARG_UNUSED (args), + int ARG_UNUSED (flags), + bool *no_add_attrs) +{ + if (TREE_CODE (*node) == FUNCTION_DECL) + { + /* Set the attribute and mark it for disregarding inline + limits. */ + DECL_DISREGARD_INLINE_LIMITS (*node) = 1; + } + else + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle a "cold" and attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + if (TREE_CODE (*node) == FUNCTION_DECL + || TREE_CODE (*node) == LABEL_DECL) + { + /* Attribute cold processing is done later with lookup_attribute. */ + } + else + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle a "noinline" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_noinline_attribute (tree *node, tree name, + tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + if (TREE_CODE (*node) == FUNCTION_DECL) + DECL_UNINLINABLE (*node) = 1; + else + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle a "weak" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_weak_attribute (tree *node, tree name, + tree ARG_UNUSED (args), + int ARG_UNUSED (flags), + bool * ARG_UNUSED (no_add_attrs)) +{ + if (TREE_CODE (*node) == FUNCTION_DECL + && DECL_DECLARED_INLINE_P (*node)) + { + warning (OPT_Wattributes, "inline function %q+D declared weak", *node); + *no_add_attrs = true; + } + else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (*node))) + { + error ("indirect function %q+D cannot be declared weak", *node); + *no_add_attrs = true; + return NULL_TREE; + } + else if (VAR_OR_FUNCTION_DECL_P (*node)) + declare_weak (*node); + else + warning (OPT_Wattributes, "%qE attribute ignored", name); + + return NULL_TREE; +} + +/* Handle a "target" attribute. */ + +static tree +handle_target_attribute (tree *node, tree name, tree args, int flags, + bool *no_add_attrs) +{ + /* Ensure we have a function declaration. */ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + else if (! targetm.target_option.valid_attribute_p (*node, name, args, + flags)) + *no_add_attrs = true; + + /* Check that there's no empty string in values of the attribute. */ + for (tree t = args; t != NULL_TREE; t = TREE_CHAIN (t)) + { + tree value = TREE_VALUE (t); + if (TREE_CODE (value) == STRING_CST + && TREE_STRING_LENGTH (value) == 1 + && TREE_STRING_POINTER (value)[0] == '\0') + { + warning (OPT_Wattributes, "empty string in attribute %"); + *no_add_attrs = true; + } + } + + return NULL_TREE; +} + +/* Handle a "used" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_used_attribute (tree *pnode, tree name, tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + tree node = *pnode; + + if (TREE_CODE (node) == FUNCTION_DECL + || (VAR_P (node) && TREE_STATIC (node)) + || (TREE_CODE (node) == TYPE_DECL)) + { + TREE_USED (node) = 1; + DECL_PRESERVE_P (node) = 1; + if (VAR_P (node)) + DECL_READ_P (node) = 1; + } + else + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle an "alias" or "ifunc" attribute; arguments as in + struct attribute_spec.handler, except that IS_ALIAS tells us + whether this is an alias as opposed to ifunc attribute. */ + +static tree +handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args, + bool *no_add_attrs) +{ + tree decl = *node; + + if (TREE_CODE (decl) != FUNCTION_DECL + && (!is_alias || !VAR_P (decl))) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + else if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl)) + || (TREE_CODE (decl) != FUNCTION_DECL + && TREE_PUBLIC (decl) && !DECL_EXTERNAL (decl)) + /* A static variable declaration is always a tentative definition, + but the alias is a non-tentative definition which overrides. */ + || (TREE_CODE (decl) != FUNCTION_DECL + && ! TREE_PUBLIC (decl) && DECL_INITIAL (decl))) + { + error ("%q+D defined both normally and as %qE attribute", decl, name); + *no_add_attrs = true; + return NULL_TREE; + } + else if (!is_alias + && (lookup_attribute ("weak", DECL_ATTRIBUTES (decl)) + || lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))) + { + error ("weak %q+D cannot be defined %qE", decl, name); + *no_add_attrs = true; + return NULL_TREE; + } + + /* Note that the very first time we process a nested declaration, + decl_function_context will not be set. Indeed, *would* never + be set except for the DECL_INITIAL/DECL_EXTERNAL frobbery that + we do below. After such frobbery, pushdecl would set the context. + In any case, this is never what we want. */ + else if (decl_function_context (decl) == 0 && current_function_decl == NULL) + { + tree id; + + id = TREE_VALUE (args); + if (TREE_CODE (id) != STRING_CST) + { + error ("attribute %qE argument not a string", name); + *no_add_attrs = true; + return NULL_TREE; + } + id = get_identifier (TREE_STRING_POINTER (id)); + /* This counts as a use of the object pointed to. */ + TREE_USED (id) = 1; + + if (TREE_CODE (decl) == FUNCTION_DECL) + DECL_INITIAL (decl) = error_mark_node; + else + TREE_STATIC (decl) = 1; + + if (!is_alias) + { + /* ifuncs are also aliases, so set that attribute too. */ + DECL_ATTRIBUTES (decl) + = tree_cons (get_identifier ("alias"), args, + DECL_ATTRIBUTES (decl)); + DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("ifunc"), + NULL, DECL_ATTRIBUTES (decl)); + } + } + else + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + if (decl_in_symtab_p (*node)) + { + struct symtab_node *n = symtab_node::get (decl); + if (n && n->refuse_visibility_changes) + error ("%+qD declared %qs after being used", + decl, is_alias ? "alias" : "ifunc"); + } + + + return NULL_TREE; +} + +/* Handle an "alias" or "ifunc" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_alias_attribute (tree *node, tree name, tree args, + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs); +} + /* (end of attribute-handling). */ /* Language-dependent contents of a type. */ diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc index f87351eaf82..84df6c100e6 100644 --- a/gcc/jit/jit-playback.cc +++ b/gcc/jit/jit-playback.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MUTEX +#include "libgccjit.h" #include "system.h" #include "coretypes.h" #include "target.h" @@ -499,6 +500,54 @@ new_param (location *loc, return new param (this, inner); } +const char* fn_attribute_to_string (gcc_jit_fn_attribute attr) +{ + switch (attr) + { + case GCC_JIT_FN_ATTRIBUTE_ALIAS: + return "alias"; + case GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE: + return "always_inline"; + case GCC_JIT_FN_ATTRIBUTE_INLINE: + return NULL; + case GCC_JIT_FN_ATTRIBUTE_NOINLINE: + return "noinline"; + case GCC_JIT_FN_ATTRIBUTE_TARGET: + return "target"; + case GCC_JIT_FN_ATTRIBUTE_USED: + return "used"; + case GCC_JIT_FN_ATTRIBUTE_VISIBILITY: + return "visibility"; + case GCC_JIT_FN_ATTRIBUTE_COLD: + return "cold"; + case GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE: + return "returns_twice"; + case GCC_JIT_FN_ATTRIBUTE_PURE: + return "pure"; + case GCC_JIT_FN_ATTRIBUTE_CONST: + return "const"; + case GCC_JIT_FN_ATTRIBUTE_WEAK: + return "weak"; + case GCC_JIT_FN_ATTRIBUTE_NONNULL: + return "nonnull"; + case GCC_JIT_FN_ATTRIBUTE_MAX: + return NULL; + } + return NULL; +} + +const char* variable_attribute_to_string (gcc_jit_variable_attribute attr) +{ + switch (attr) + { + case GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY: + return "visibility"; + case GCC_JIT_VARIABLE_ATTRIBUTE_MAX: + return NULL; + } + return NULL; +} + /* Construct a playback::function instance. */ playback::function * @@ -509,7 +558,13 @@ new_function (location *loc, const char *name, const auto_vec *params, int is_variadic, - enum built_in_function builtin_id) + enum built_in_function builtin_id, + const std::vector &attributes, + const std::vector> &string_attributes, + const std::vector>> + &int_array_attributes) { int i; param *param; @@ -543,6 +598,8 @@ new_function (location *loc, DECL_RESULT (fndecl) = resdecl; DECL_CONTEXT (resdecl) = fndecl; + tree fn_attributes = NULL_TREE; + if (builtin_id) { gcc_assert (loc == NULL); @@ -588,12 +645,62 @@ new_function (location *loc, DECL_DECLARED_INLINE_P (fndecl) = 1; /* Add attribute "always_inline": */ - DECL_ATTRIBUTES (fndecl) = - tree_cons (get_identifier ("always_inline"), - NULL, - DECL_ATTRIBUTES (fndecl)); + fn_attributes = tree_cons (get_identifier ("always_inline"), + NULL, + fn_attributes); } + /* All attributes need to be declared in `dummy-frontend.cc` and more + specifically in `jit_attribute_table`. */ + for (auto attr: attributes) + { + if (attr == GCC_JIT_FN_ATTRIBUTE_INLINE) + DECL_DECLARED_INLINE_P (fndecl) = 1; + + const char* attribute = fn_attribute_to_string (attr); + if (attribute) + { + tree ident = get_identifier (attribute); + fn_attributes = tree_cons (ident, NULL_TREE, fn_attributes); + } + } + + for (auto attr: string_attributes) + { + gcc_jit_fn_attribute& name = std::get<0>(attr); + std::string& value = std::get<1>(attr); + tree attribute_value = build_tree_list (NULL_TREE, + ::build_string (value.length () + 1, value.c_str ())); + const char* attribute = fn_attribute_to_string (name); + tree ident = attribute ? get_identifier (attribute) : NULL; + + if (ident) + fn_attributes = tree_cons (ident, attribute_value, fn_attributes); + } + + for (auto attr: int_array_attributes) + { + gcc_jit_fn_attribute& name = std::get<0>(attr); + std::vector& values = std::get<1>(attr); + + const char* attribute = fn_attribute_to_string (name); + tree ident = attribute ? get_identifier (attribute) : NULL; + + if (!ident) + continue; + + tree tree_list = NULL_TREE; + tree *p_tree_list = &tree_list; + for (auto value : values) + { + tree int_value = build_int_cst (integer_type_node, value); + *p_tree_list = build_tree_list (NULL, int_value); + p_tree_list = &TREE_CHAIN (*p_tree_list); + } + fn_attributes = tree_cons (ident, tree_list, fn_attributes); + } + + decl_attributes (&fndecl, fn_attributes, 0); function *func = new function (this, fndecl, kind); m_functions.safe_push (func); return func; @@ -607,7 +714,9 @@ global_new_decl (location *loc, enum gcc_jit_global_kind kind, type *type, const char *name, - enum global_var_flags flags) + enum global_var_flags flags, + const std::vector> &attributes) { gcc_assert (type); gcc_assert (name); @@ -652,9 +761,32 @@ global_new_decl (location *loc, if (loc) set_tree_location (inner, loc); + set_variable_string_attribute (attributes, inner); + return inner; } +void +playback:: +set_variable_string_attribute ( + const std::vector> &string_attributes, + tree decl) +{ + tree var_attributes = NULL_TREE; + for (auto attr: string_attributes) + { + gcc_jit_variable_attribute& name = std::get<0>(attr); + std::string& value = std::get<1>(attr); + tree attribute_value = build_tree_list (NULL_TREE, + ::build_string (value.length () + 1, value.c_str ())); + tree ident = get_identifier (variable_attribute_to_string (name)); + if (ident) + var_attributes = tree_cons (ident, attribute_value, var_attributes); + } + decl_attributes (&decl, var_attributes, 0); +} + /* In use by new_global and new_global_initialized. */ playback::lvalue * @@ -674,10 +806,12 @@ new_global (location *loc, enum gcc_jit_global_kind kind, type *type, const char *name, - enum global_var_flags flags) + enum global_var_flags flags, + const std::vector> &attributes) { tree inner = - global_new_decl (loc, kind, type, name, flags); + global_new_decl (loc, kind, type, name, flags, attributes); return global_finalize_lvalue (inner); } @@ -818,13 +952,15 @@ playback::context:: new_global_initialized (location *loc, enum gcc_jit_global_kind kind, type *type, - size_t element_size, + size_t element_size, size_t initializer_num_elem, const void *initializer, const char *name, - enum global_var_flags flags) + enum global_var_flags flags, + const std::vector> &attributes) { - tree inner = global_new_decl (loc, kind, type, name, flags); + tree inner = global_new_decl (loc, kind, type, name, flags, attributes); vec *constructor_elements = NULL; @@ -1812,7 +1948,9 @@ playback::lvalue * playback::function:: new_local (location *loc, type *type, - const char *name) + const char *name, + const std::vector> &attributes) { gcc_assert (type); gcc_assert (name); @@ -1825,6 +1963,8 @@ new_local (location *loc, DECL_CHAIN (inner) = BIND_EXPR_VARS (m_inner_bind_expr); BIND_EXPR_VARS (m_inner_bind_expr) = inner; + set_variable_string_attribute (attributes, inner); + if (loc) set_tree_location (inner, loc); return new lvalue (m_ctxt, inner); @@ -1947,6 +2087,9 @@ postprocess () current_function_decl = NULL; } + else + /* Add to cgraph to output aliases: */ + rest_of_decl_compilation (m_inner_fndecl, true, 0); } /* Don't leak vec's internal buffer (in non-GC heap) when we are @@ -3365,7 +3508,7 @@ void playback::context:: init_types () { - /* See lto_init() in lto-lang.cc or void visit (TypeBasic *t) in D's types.cc + /* See lto_init () in lto-lang.cc or void visit (TypeBasic *t) in D's types.cc for reference. If TYPE_NAME is not set, debug info will not contain types */ #define NAME_TYPE(t,n) \ if (t) \ diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h index 9654507a34d..05bafcd21c4 100644 --- a/gcc/jit/jit-playback.h +++ b/gcc/jit/jit-playback.h @@ -21,7 +21,9 @@ along with GCC; see the file COPYING3. If not see #ifndef JIT_PLAYBACK_H #define JIT_PLAYBACK_H +#include #include // for std::pair +#include #include "timevar.h" #include "varasm.h" @@ -35,12 +37,21 @@ namespace gcc { namespace jit { +const char* fn_attribute_to_string (gcc_jit_fn_attribute attr); +const char* variable_attribute_to_string (gcc_jit_variable_attribute attr); + /********************************************************************** Playback. **********************************************************************/ namespace playback { +void +set_variable_string_attribute ( + const std::vector> &attributes, + tree decl); + /* playback::context is an abstract base class. The two concrete subclasses are: @@ -104,14 +115,22 @@ public: const char *name, const auto_vec *params, int is_variadic, - enum built_in_function builtin_id); + enum built_in_function builtin_id, + const std::vector &attributes, + const std::vector> &string_attributes, + const std::vector>> + &int_array_attributes); lvalue * new_global (location *loc, enum gcc_jit_global_kind kind, type *type, const char *name, - enum global_var_flags flags); + enum global_var_flags flags, + const std::vector> &attributes); lvalue * new_global_initialized (location *loc, @@ -121,7 +140,11 @@ public: size_t initializer_num_elem, const void *initializer, const char *name, - enum global_var_flags flags); + enum global_var_flags flags, + const std::vector> + &attributes); rvalue * new_ctor (location *log, @@ -306,7 +329,9 @@ private: enum gcc_jit_global_kind kind, type *type, const char *name, - enum global_var_flags flags); + enum global_var_flags flags, + const std::vector> &attributes); lvalue * global_finalize_lvalue (tree inner); @@ -500,7 +525,9 @@ public: lvalue * new_local (location *loc, type *type, - const char *name); + const char *name, + const std::vector> &attributes); block* new_block (const char *name); diff --git a/gcc/jit/jit-recording.cc b/gcc/jit/jit-recording.cc index 686c0587079..6ffadbea127 100644 --- a/gcc/jit/jit-recording.cc +++ b/gcc/jit/jit-recording.cc @@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see #include "jit-builtins.h" #include "jit-recording.h" #include "jit-playback.h" +#include namespace gcc { namespace jit { @@ -2068,7 +2069,7 @@ recording::memento::get_debug_string () void recording::memento::write_to_dump (dump &d) { - d.write(" %s\n", get_debug_string ()); + d.write (" %s\n", get_debug_string ()); } /* The implementation of class gcc::jit::recording::string. */ @@ -4026,6 +4027,13 @@ void recording::lvalue::set_alignment (unsigned bytes) m_alignment = bytes; } +void recording::lvalue::add_string_attribute ( + gcc_jit_variable_attribute attribute, + const char* value) +{ + m_string_attributes.push_back (std::make_pair (attribute, std::string (value))); +} + /* The implementation of class gcc::jit::recording::param. */ /* Implementation of pure virtual hook recording::memento::replay_into @@ -4102,7 +4110,10 @@ recording::function::function (context *ctxt, m_builtin_id (builtin_id), m_locals (), m_blocks (), - m_fn_ptr_type (NULL) + m_fn_ptr_type (NULL), + m_attributes (), + m_string_attributes (), + m_int_array_attributes () { for (int i = 0; i< num_params; i++) { @@ -4161,7 +4172,10 @@ recording::function::replay_into (replayer *r) m_name->c_str (), ¶ms, m_is_variadic, - m_builtin_id)); + m_builtin_id, + m_attributes, + m_string_attributes, + m_int_array_attributes)); } /* Create a recording::local instance and add it to @@ -4210,6 +4224,40 @@ recording::function::new_block (const char *name) void recording::function::write_to_dump (dump &d) { + for (auto attr: m_attributes) + { + const char* attribute = fn_attribute_to_string (attr); + if (attribute) + d.write ("__attribute(%s)__\n", attribute); + } + for (auto attr: m_string_attributes) + { + gcc_jit_fn_attribute& name = std::get<0>(attr); + std::string& value = std::get<1>(attr); + const char* attribute = fn_attribute_to_string (name); + + if (attribute) + d.write ("__attribute(%s(\"%s\"))__\n", attribute, value.c_str()); + } + for (auto attr: m_int_array_attributes) + { + gcc_jit_fn_attribute& name = std::get<0>(attr); + std::vector& values = std::get<1>(attr); + const char* attribute = fn_attribute_to_string (name); + if (attribute) + { + d.write ("__attribute(%s(", attribute); + for (size_t i = 0; i < values.size(); ++i) + { + if (i > 0) + d.write (", %d", values[i]); + else + d.write ("%d", values[i]); + } + d.write ("))__\n"); + } + } + switch (m_kind) { default: gcc_unreachable (); @@ -4404,6 +4452,31 @@ recording::function::get_address (recording::location *loc) return result; } +void +recording::function::add_attribute (gcc_jit_fn_attribute attribute) +{ + m_attributes.push_back (attribute); +} + +void +recording::function::add_string_attribute (gcc_jit_fn_attribute attribute, + const char* value) +{ + m_string_attributes.push_back ( + std::make_pair (attribute, std::string (value))); +} + +void +recording::function::add_integer_array_attribute ( + gcc_jit_fn_attribute attribute, + const int* value, + size_t length) +{ + m_int_array_attributes.push_back (std::make_pair ( + attribute, + std::vector (value, value + length))); +} + /* Implementation of recording::memento::make_debug_string for functions. */ @@ -4425,6 +4498,39 @@ static const char * const names_of_function_kinds[] = { /* Implementation of recording::memento::write_reproducer for functions. */ +static const char * const fn_attribute_reproducer_strings[] = +{ + "GCC_JIT_FN_ATTRIBUTE_ALIAS", + "GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE", + "GCC_JIT_FN_ATTRIBUTE_INLINE", + "GCC_JIT_FN_ATTRIBUTE_NOINLINE", + "GCC_JIT_FN_ATTRIBUTE_TARGET", + "GCC_JIT_FN_ATTRIBUTE_USED", + "GCC_JIT_FN_ATTRIBUTE_VISIBILITY", + "GCC_JIT_FN_ATTRIBUTE_COLD", + "GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE", + "GCC_JIT_FN_ATTRIBUTE_PURE", + "GCC_JIT_FN_ATTRIBUTE_CONST", + "GCC_JIT_FN_ATTRIBUTE_WEAK", + "GCC_JIT_FN_ATTRIBUTE_NONNULL", +}; + +std::string +get_vector_int_debug (std::vector &values) +{ + std::stringstream s; + + s << "{"; + for(auto it = values.begin(); it != values.end(); ++it) + { + if (it != values.begin() ) + s << ", "; + s << *it; + } + s << "}"; + return s.str(); +} + void recording::function::write_reproducer (reproducer &r) { @@ -4467,6 +4573,25 @@ recording::function::write_reproducer (reproducer &r) m_params.length (), params_id, m_is_variadic); + for (auto attribute : m_attributes) + r.write(" gcc_jit_function_add_attribute (%s, %s);\n", + id, + fn_attribute_reproducer_strings[attribute]); + for (auto attribute : m_string_attributes) + r.write(" gcc_jit_function_add_string_attribute (%s, %s, \"%s\");\n", + id, + fn_attribute_reproducer_strings[std::get<0>(attribute)], + std::get<1>(attribute).c_str()); + for (auto attribute : m_int_array_attributes) { + r.write(" gcc_jit_function_add_integer_array_attribute (%s,\n" + " %s,\n" + " (int[])%s,\n" + " %lu);\n", + id, + fn_attribute_reproducer_strings[std::get<0>(attribute)], + get_vector_int_debug (std::get<1>(attribute)).c_str(), + std::get<1>(attribute).size ()); + } } @@ -4879,12 +5004,14 @@ recording::global::replay_into (replayer *r) / m_type->dereference ()->get_size (), m_initializer, playback_string (m_name), - m_flags) + m_flags, + m_string_attributes) : r->new_global (playback_location (r, m_loc), m_kind, m_type->playback_type (), playback_string (m_name), - m_flags); + m_flags, + m_string_attributes); if (m_tls_model != GCC_JIT_TLS_MODEL_NONE) global->set_tls_model (recording::tls_models[m_tls_model]); @@ -4943,6 +5070,15 @@ recording::global::write_to_dump (dump &d) break; } + for (auto attr: m_string_attributes) + { + gcc_jit_variable_attribute& name = std::get<0>(attr); + std::string& value = std::get<1>(attr); + const char* attribute = variable_attribute_to_string (name); + + if (attribute) + d.write ("__attribute(%s(\"%s\"))__\n", attribute, value.c_str()); + } d.write ("%s %s", m_type->get_debug_string (), get_debug_string ()); @@ -5013,6 +5149,10 @@ static const char * const tls_model_enum_strings[] = { "GCC_JIT_TLS_MODEL_LOCAL_EXEC", }; +static const char * const gcc_jit_variable_attribute_enum_strings[] = { + "GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY", +}; + void recording::global::write_reproducer (reproducer &r) { @@ -5042,6 +5182,13 @@ recording::global::write_reproducer (reproducer &r) id, m_link_section->c_str ()); + for (auto attribute : m_string_attributes) + r.write(" gcc_jit_lvalue_add_string_attribute (%s, %s, \"%s\");\n", + id, + gcc_jit_variable_attribute_enum_strings[std::get<0>(attribute)], + std::get<1>(attribute).c_str()); + + if (m_initializer) switch (m_type->dereference ()->get_size ()) { @@ -6622,7 +6769,8 @@ recording::local::replay_into (replayer *r) playback::lvalue *obj = m_func->playback_function () ->new_local (playback_location (r, m_loc), m_type->playback_type (), - playback_string (m_name)); + playback_string (m_name), + m_string_attributes); if (m_reg_name != NULL) obj->set_register_name (m_reg_name->c_str ()); @@ -6644,9 +6792,9 @@ recording::local::write_to_dump (dump &d) { if (d.update_locations ()) m_loc = d.make_location (); - d.write(" %s %s;\n", - m_type->get_debug_string (), - get_debug_string ()); + d.write (" %s %s;\n", + m_type->get_debug_string (), + get_debug_string ()); } void diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h index b951c715ca5..cd2e0adbe30 100644 --- a/gcc/jit/jit-recording.h +++ b/gcc/jit/jit-recording.h @@ -23,6 +23,10 @@ along with GCC; see the file COPYING3. If not see #include "jit-common.h" #include "jit-logging.h" +#include "libgccjit.h" + +#include +#include class timer; @@ -1216,7 +1220,8 @@ public: m_link_section (NULL), m_reg_name (NULL), m_tls_model (GCC_JIT_TLS_MODEL_NONE), - m_alignment (0) + m_alignment (0), + m_string_attributes () {} playback::lvalue * @@ -1236,8 +1241,12 @@ public: as_rvalue () { return this; } const char *access_as_rvalue (reproducer &r) override; + + void add_string_attribute (gcc_jit_variable_attribute attribute, const char* value); + virtual const char *access_as_lvalue (reproducer &r); virtual bool is_global () const { return false; } + virtual bool is_local () const { return false; } void set_tls_model (enum gcc_jit_tls_model model); void set_link_section (const char *name); void set_register_name (const char *reg_name); @@ -1249,6 +1258,8 @@ protected: string *m_reg_name; enum gcc_jit_tls_model m_tls_model; unsigned m_alignment; + std::vector> m_string_attributes; }; class param : public lvalue @@ -1342,6 +1353,10 @@ public: rvalue *get_address (location *loc); + void add_attribute (gcc_jit_fn_attribute attribute); + void add_string_attribute (gcc_jit_fn_attribute attribute, const char* value); + void add_integer_array_attribute (gcc_jit_fn_attribute attribute, const int* value, size_t length); + private: string * make_debug_string () final override; void write_reproducer (reproducer &r) final override; @@ -1357,6 +1372,9 @@ private: auto_vec m_locals; auto_vec m_blocks; type *m_fn_ptr_type; + std::vector m_attributes; + std::vector> m_string_attributes; + std::vector>> m_int_array_attributes; }; class block : public memento @@ -2086,6 +2104,8 @@ public: void visit_children (rvalue_visitor *) final override {} + bool is_local () const final override { return true; } + void write_to_dump (dump &d) final override; private: diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc index 8ecfe4aaa42..9616f3802b8 100644 --- a/gcc/jit/libgccjit.cc +++ b/gcc/jit/libgccjit.cc @@ -3965,6 +3965,73 @@ gcc_jit_type_get_aligned (gcc_jit_type *type, return (gcc_jit_type *)type->get_aligned (alignment_in_bytes); } +void +gcc_jit_function_add_attribute (gcc_jit_function *func, + gcc_jit_fn_attribute attribute) +{ + RETURN_IF_FAIL (func, NULL, NULL, "NULL func"); + RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_FN_ATTRIBUTE_MAX), + NULL, + NULL, + "attribute should be a `gcc_jit_fn_attribute` enum value"); + + func->add_attribute (attribute); +} + +void +gcc_jit_function_add_string_attribute (gcc_jit_function *func, + gcc_jit_fn_attribute attribute, + const char* value) +{ + RETURN_IF_FAIL (func, NULL, NULL, "NULL func"); + RETURN_IF_FAIL (value, NULL, NULL, "NULL value"); + RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_FN_ATTRIBUTE_MAX), + NULL, + NULL, + "attribute should be a `gcc_jit_fn_attribute` enum value"); + + func->add_string_attribute (attribute, value); +} + +/* This function adds an attribute with multiple integer values. For example + `nonnull(1, 2)`. The numbers in `values` are supposed to map how they + should be written in C code. So for `nonnull(1, 2)`, you should pass `1` + and `2` in `values` (and set `length` to `2`). */ +void +gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func, + gcc_jit_fn_attribute attribute, + const int* values, + size_t length) +{ + RETURN_IF_FAIL (func, NULL, NULL, "NULL func"); + RETURN_IF_FAIL (values, NULL, NULL, "NULL values"); + RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_FN_ATTRIBUTE_MAX), + NULL, + NULL, + "attribute should be a `gcc_jit_fn_attribute` enum value"); + + func->add_integer_array_attribute (attribute, values, length); +} + +void +gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable, + gcc_jit_variable_attribute attribute, + const char* value) +{ + RETURN_IF_FAIL (variable, NULL, NULL, "NULL variable"); + RETURN_IF_FAIL (value, NULL, NULL, "NULL value"); + RETURN_IF_FAIL (variable->is_global () || variable->is_local (), + NULL, + NULL, + "variable should be a variable"); + RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_VARIABLE_ATTRIBUTE_MAX), + NULL, + NULL, + "attribute should be a `gcc_jit_variable_attribute` enum value"); + + variable->add_string_attribute (attribute, value); +} + /* Public entrypoint. See description in libgccjit.h. After error-checking, the real work is done by the diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h index cbcfabba3e8..235cab053e0 100644 --- a/gcc/jit/libgccjit.h +++ b/gcc/jit/libgccjit.h @@ -1999,6 +1999,61 @@ gcc_jit_vector_type_get_element_type (gcc_jit_vector_type *vector_type); extern gcc_jit_type * gcc_jit_type_unqualified (gcc_jit_type *type); +#define LIBGCCJIT_HAVE_ATTRIBUTES + +/* Function attributes. */ +enum gcc_jit_fn_attribute +{ + GCC_JIT_FN_ATTRIBUTE_ALIAS, + GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE, + GCC_JIT_FN_ATTRIBUTE_INLINE, + GCC_JIT_FN_ATTRIBUTE_NOINLINE, + GCC_JIT_FN_ATTRIBUTE_TARGET, + GCC_JIT_FN_ATTRIBUTE_USED, + GCC_JIT_FN_ATTRIBUTE_VISIBILITY, + GCC_JIT_FN_ATTRIBUTE_COLD, + GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE, + GCC_JIT_FN_ATTRIBUTE_PURE, + GCC_JIT_FN_ATTRIBUTE_CONST, + GCC_JIT_FN_ATTRIBUTE_WEAK, + GCC_JIT_FN_ATTRIBUTE_NONNULL, + + /* Maximum value of this enum, should always be last. */ + GCC_JIT_FN_ATTRIBUTE_MAX, +}; + +/* Add an attribute to a function. */ +extern void +gcc_jit_function_add_attribute (gcc_jit_function *func, + enum gcc_jit_fn_attribute attribute); + +extern void +gcc_jit_function_add_string_attribute (gcc_jit_function *func, + enum gcc_jit_fn_attribute attribute, + const char* value); + +extern void +gcc_jit_function_add_integer_array_attribute ( + gcc_jit_function *func, + enum gcc_jit_fn_attribute attribute, + const int* value, + size_t length); + +/* Variable attributes. */ +enum gcc_jit_variable_attribute +{ + GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY, + + /* Maximum value of this enum, should always be last. */ + GCC_JIT_VARIABLE_ATTRIBUTE_MAX, +}; + +/* Add a string attribute to a variable. */ +extern void +gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable, + enum gcc_jit_variable_attribute attribute, + const char* value); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map index b62f5de72d0..dfb8a9d51fb 100644 --- a/gcc/jit/libgccjit.map +++ b/gcc/jit/libgccjit.map @@ -276,3 +276,11 @@ LIBGCCJIT_ABI_25 { global: gcc_jit_type_get_restrict; } LIBGCCJIT_ABI_24; + +LIBGCCJIT_ABI_26 { + global: + gcc_jit_function_add_attribute; + gcc_jit_function_add_string_attribute; + gcc_jit_lvalue_add_string_attribute; + gcc_jit_function_add_integer_array_attribute; +} LIBGCCJIT_ABI_25; diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h index e762563f9bd..84001203352 100644 --- a/gcc/testsuite/jit.dg/all-non-failing-tests.h +++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h @@ -32,6 +32,9 @@ /* test-add-driver-options.c: We don't use this one, since the extra options affect the whole context. */ +/* test-alias-attribute.c: This can't be in the testcases array as it + doesn't have a verify_code implementation. */ + /* test-alignment.c */ #define create_code create_code_alignment #define verify_code verify_code_alignment @@ -39,6 +42,9 @@ #undef create_code #undef verify_code +/* test-always_inline-attribute.c: This can't be in the testcases array as it needs + the `-O0` flag. */ + /* test-arith-overflow.c */ #define create_code create_code_arith_overflow #define verify_code verify_code_arith_overflow @@ -119,6 +125,9 @@ #undef create_code #undef verify_code +/* test-cold-attribute.c: This can't be in the testcases array as it needs + the `-O2` flag. */ + /* test-constants.c */ #define create_code create_code_constants #define verify_code verify_code_constants @@ -126,6 +135,9 @@ #undef create_code #undef verify_code +/* test-const-attribute.c: This can't be in the testcases array as it needs + the `-O3` flag. */ + /* test-debug-strings.c */ #define create_code create_code_debug_strings #define verify_code verify_code_debug_strings @@ -268,6 +280,12 @@ #undef create_code #undef verify_code +/* test-noinline-attribute.c: This can't be in the testcases array as it needs + the `-O2` flag. */ + +/* test-nonnull-attribute.c: This can't be in the testcases array as it needs + the `-O2` flag. */ + /* test-pr103562.c: We don't add this one, since it touches the optimization level of the context as a whole. */ @@ -299,6 +317,9 @@ #undef create_code #undef verify_code +/* test-pure-attribute.c: This can't be in the testcases array as it needs + the `-O3` flag. */ + /* test-reading-struct.c */ #define create_code create_code_reading_struct #define verify_code verify_code_reading_struct @@ -313,7 +334,7 @@ #undef create_code #undef verify_code -/* test-restrict.c: This can't be in the testcases array as it needs +/* test-restrict-attribute.c: This can't be in the testcases array as it needs the `-O3` flag. */ /* test-register-variable.c: This can't be in the testcases array as it @@ -350,6 +371,9 @@ #undef create_code #undef verify_code +/* test-used-attribute.c: This can't be in the testcases array as it needs + the `-O2` flag. */ + /* test-using-global.c */ #define create_code create_code_using_global #define verify_code verify_code_using_global @@ -361,6 +385,9 @@ of gcc_jit_context_set_bool_allow_unreachable_blocks affects the whole context. */ +/* test-variable-attribute.c: This can't be in the testcases array as it + doesn't have a verify_code implementation. */ + /* test-vector-types.cc: We don't use this, since it's C++. */ /* test-version.c */ @@ -377,6 +404,9 @@ #undef create_code #undef verify_code +/* test-weak-attribute.c: This can't be in the testcases array as it + doesn't have a verify_code implementation. */ + /* Now expose the individual testcases as instances of this struct. */ struct testcase diff --git a/gcc/testsuite/jit.dg/jit.exp b/gcc/testsuite/jit.dg/jit.exp index 8bf7e51c24f..56972064d30 100644 --- a/gcc/testsuite/jit.dg/jit.exp +++ b/gcc/testsuite/jit.dg/jit.exp @@ -899,8 +899,41 @@ proc jit-verify-assembler-output { args } { pass "${asm_filename} output pattern test, ${dg-output-text}" verbose "Passed test for output pattern ${dg-output-text}" 3 } +} + +# Assuming that a .s file has been written out named +# OUTPUT_FILENAME, check that the argument doesn't match +# the output file. +proc jit-verify-assembler-output-not { args } { + verbose "jit-verify-assembler: $args" + + set dg-output-text [lindex $args 0] + verbose "dg-output-text: ${dg-output-text}" + + upvar 2 name name + verbose "name: $name" + + upvar 2 prog prog + verbose "prog: $prog" + set asm_filename [jit-get-output-filename $prog] + verbose " asm_filename: ${asm_filename}" + # Read the assembly file. + set f [open $asm_filename r] + set content [read $f] + close $f + + # Verify that the assembly matches the regex. + if { [regexp ${dg-output-text} $content] } { + fail "${asm_filename} output pattern test, is ${content}, should not match ${dg-output-text}" + verbose "Failed test for output pattern ${dg-output-text}" 3 + } else { + pass "${asm_filename} output pattern test, ${dg-output-text}" + verbose "Passed test for output pattern ${dg-output-text}" 3 + } } + + # Assuming that a .o file has been written out named # OUTPUT_FILENAME, invoke the driver to try to turn it into # an executable, and try to run the result. diff --git a/gcc/testsuite/jit.dg/test-alias-attribute.c b/gcc/testsuite/jit.dg/test-alias-attribute.c new file mode 100644 index 00000000000..eb29003dfc9 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-alias-attribute.c @@ -0,0 +1,50 @@ +/* { dg-do compile { target x86_64-*-* } } */ + +#include +#include + +#include "libgccjit.h" + +#define TEST_COMPILING_TO_FILE +#define OUTPUT_KIND GCC_JIT_OUTPUT_KIND_ASSEMBLER +#define OUTPUT_FILENAME "output-of-test-alias-attribute.c.s" +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: + +void xxx () {} +void f () __attribute__ ((alias ("xxx"))); + */ + gcc_jit_type *void_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID); + + /* Creating the `xxx` function. */ + gcc_jit_function *xxx_func = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + void_type, + "xxx", + 0, NULL, + 0); + + /* Creating the `f` function. */ + gcc_jit_function *f_func = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_IMPORTED, + void_type, + "f", + 0, NULL, + 0); + gcc_jit_function_add_string_attribute(f_func, GCC_JIT_FN_ATTRIBUTE_ALIAS, "xxx"); + + /* void xxx () {} */ + gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL); + gcc_jit_block_end_with_void_return (block, NULL); +} + +/* { dg-final { jit-verify-output-file-was-created "" } } */ +/* Check that the attribute was applied correctly */ +/* { dg-final { jit-verify-assembler-output ".set\\s+f,xxx" } } */ diff --git a/gcc/testsuite/jit.dg/test-always_inline-attribute.c b/gcc/testsuite/jit.dg/test-always_inline-attribute.c new file mode 100644 index 00000000000..5c3f386663f --- /dev/null +++ b/gcc/testsuite/jit.dg/test-always_inline-attribute.c @@ -0,0 +1,153 @@ +/* { dg-do compile { target x86_64-*-* } } */ + +#include +#include + +#include "libgccjit.h" + +#define TEST_ESCHEWS_SET_OPTIONS +static void set_options (gcc_jit_context *ctxt, const char *argv0) +{ + // Set "-O0". + gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 0); +} + +#define TEST_COMPILING_TO_FILE +#define OUTPUT_KIND GCC_JIT_OUTPUT_KIND_ASSEMBLER +#define OUTPUT_FILENAME "output-of-test-always_inline-attribute.c.s" +#include "harness.h" + +gcc_jit_function* +create_function (gcc_jit_context *ctxt, + const char *func_name, + gcc_jit_type *int_type, + gcc_jit_type *pint_type) +{ + /* The `a` function argument */ + gcc_jit_param *a = gcc_jit_context_new_param (ctxt, NULL, pint_type, "a"); + gcc_jit_function *func = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_INTERNAL, + int_type, + func_name, + 1, &a, + 0); + + gcc_jit_block *if_cond = + gcc_jit_function_new_block (func, "if_cond"); + gcc_jit_block *if_body = + gcc_jit_function_new_block (func, "if_body"); + gcc_jit_block *after_if = + gcc_jit_function_new_block (func, "after_if"); + + /* if (!a) */ + gcc_jit_block_end_with_conditional ( + if_cond, NULL, + gcc_jit_context_new_comparison ( + ctxt, NULL, + GCC_JIT_COMPARISON_EQ, + gcc_jit_param_as_rvalue (a), + gcc_jit_context_null (ctxt, pint_type)), + if_body, + after_if); + /* return -1; */ + gcc_jit_block_end_with_return ( + if_body, NULL, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, -1)); + + /* return *a; */ + gcc_jit_block_end_with_return ( + after_if, NULL, + gcc_jit_lvalue_as_rvalue ( + gcc_jit_rvalue_dereference ( + gcc_jit_param_as_rvalue (a), NULL))); + + return func; +} + + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: +__attribute__ ((always_inline)) +static inline int removed (int *a) { + if (!a) { + return -1; + } + return *a; +} +static int not_removed (int *a) { + if (!a) { + return -1; + } + return *a; +} +int foo () { + int x = 0; + x += removed(NULL); + x += not_removed(NULL); + return x; +} + */ + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + gcc_jit_type *pint_type = gcc_jit_type_get_pointer (int_type); + + /* Creating the `removed` function. */ + gcc_jit_function *removed_func = + create_function (ctxt, "removed", int_type, pint_type); + /* This one is to declare the function as "inline" */ + gcc_jit_function_add_attribute(removed_func, GCC_JIT_FN_ATTRIBUTE_INLINE); + /* __attribute__ ((always_inline)) */ + gcc_jit_function_add_attribute(removed_func, GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE); + + /* Creating the `not_removed` function. */ + gcc_jit_function *not_removed_func = + create_function (ctxt, "not_removed", int_type, pint_type); + + /* Creating the `foo` function. */ + gcc_jit_function *foo_func = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + int_type, + "foo", + 0, NULL, + 0); + + gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL); + + /* Build locals: */ + gcc_jit_lvalue *x = + gcc_jit_function_new_local (foo_func, NULL, int_type, "x"); + + /* int x = 0; */ + gcc_jit_block_add_assignment ( + foo_block, NULL, + x, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)); + + /* x += removed(NULL); */ + gcc_jit_rvalue *null = gcc_jit_context_null (ctxt, pint_type); + gcc_jit_block_add_assignment_op ( + foo_block, NULL, + x, + GCC_JIT_BINARY_OP_PLUS, + gcc_jit_context_new_call (ctxt, NULL, removed_func, 1, &null)); + + /* x += not_removed(NULL); */ + gcc_jit_block_add_assignment_op ( + foo_block, NULL, + x, + GCC_JIT_BINARY_OP_PLUS, + gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 1, &null)); + + /* return x; */ + gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x)); +} + +/* { dg-final { jit-verify-output-file-was-created "" } } */ +/* Check that the "removed" function was inlined, but not the others */ +/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed,\\s+@function" } } */ +/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed,\\s+@function" } } */ +/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */ diff --git a/gcc/testsuite/jit.dg/test-cold-attribute.c b/gcc/testsuite/jit.dg/test-cold-attribute.c new file mode 100644 index 00000000000..8dc7ec5a34b --- /dev/null +++ b/gcc/testsuite/jit.dg/test-cold-attribute.c @@ -0,0 +1,54 @@ +/* { dg-do compile { target x86_64-*-* } } */ + +#include +#include + +#include "libgccjit.h" + +/* We don't want set_options() in harness.h to set -O2 to see that the cold + attribute affects the optimizations. */ +#define TEST_ESCHEWS_SET_OPTIONS +static void set_options (gcc_jit_context *ctxt, const char *argv0) +{ + // Set "-O2". + gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2); +} + +#define TEST_COMPILING_TO_FILE +#define OUTPUT_KIND GCC_JIT_OUTPUT_KIND_ASSEMBLER +#define OUTPUT_FILENAME "output-of-test-cold-attribute.c.s" +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: +int +__attribute__ ((cold)) +t() +{ + return -1; +} + + */ + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + + gcc_jit_function *func_t = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + int_type, + "t", + 0, NULL, + 0); + gcc_jit_function_add_attribute(func_t, GCC_JIT_FN_ATTRIBUTE_COLD); + gcc_jit_block *block = gcc_jit_function_new_block (func_t, NULL); + gcc_jit_rvalue *ret = gcc_jit_context_new_rvalue_from_int (ctxt, + int_type, + -1); + + gcc_jit_block_end_with_return (block, NULL, ret); +} + +/* { dg-final { jit-verify-output-file-was-created "" } } */ +/* { dg-final { jit-verify-assembler-output "orl" } } */ diff --git a/gcc/testsuite/jit.dg/test-const-attribute.c b/gcc/testsuite/jit.dg/test-const-attribute.c new file mode 100644 index 00000000000..c06742d163f --- /dev/null +++ b/gcc/testsuite/jit.dg/test-const-attribute.c @@ -0,0 +1,134 @@ +/* { dg-do compile { target x86_64-*-* } } */ + +#include +#include + +#include "libgccjit.h" + +/* We don't want set_options() in harness.h to set -O3 to see that the const + attribute affects the optimizations. */ +#define TEST_ESCHEWS_SET_OPTIONS +static void set_options (gcc_jit_context *ctxt, const char *argv0) +{ + // Set "-O3". + gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3); +} + +#define TEST_COMPILING_TO_FILE +#define OUTPUT_KIND GCC_JIT_OUTPUT_KIND_ASSEMBLER +#define OUTPUT_FILENAME "output-of-test-const-attribute.c.s" +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: +__attribute__ ((const)) +int foo (int x); +int xxx(void) +{ + int x = 45; + int sum = 0; + + while (x >>= 1) + sum += foo (x) * 2; + return sum; +} + */ + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + + /* Creating the `foo` function. */ + gcc_jit_param *n = + gcc_jit_context_new_param (ctxt, NULL, int_type, "x"); + gcc_jit_param *params[1] = {n}; + gcc_jit_function *foo_func = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_IMPORTED, + int_type, + "foo", + 1, params, + 0); + gcc_jit_function_add_attribute(foo_func, GCC_JIT_FN_ATTRIBUTE_CONST); + + /* Creating the `xxx` function. */ + gcc_jit_function *xxx_func = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + int_type, + "xxx", + 0, NULL, + 0); + + gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL); + + /* Build locals: */ + gcc_jit_lvalue *x = + gcc_jit_function_new_local (xxx_func, NULL, int_type, "x"); + gcc_jit_lvalue *sum = + gcc_jit_function_new_local (xxx_func, NULL, int_type, "sum"); + + /* int x = 45 */ + gcc_jit_block_add_assignment ( + block, NULL, + x, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 45)); + /* int sum = 0 */ + gcc_jit_block_add_assignment ( + block, NULL, + sum, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)); + + /* while (x >>= 1) { sum += foo (x) * 2; } */ + gcc_jit_block *loop_cond = + gcc_jit_function_new_block (xxx_func, "loop_cond"); + gcc_jit_block *loop_body = + gcc_jit_function_new_block (xxx_func, "loop_body"); + gcc_jit_block *after_loop = + gcc_jit_function_new_block (xxx_func, "after_loop"); + + gcc_jit_block_end_with_jump (block, NULL, loop_cond); + + + /* if (x >>= 1) */ + /* Since gccjit doesn't (yet?) have support for `>>=` operator, we will decompose it into: + `if (x = x >> 1)` */ + gcc_jit_block_add_assignment_op ( + loop_cond, NULL, + x, + GCC_JIT_BINARY_OP_RSHIFT, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 1)); + /* The condition itself */ + gcc_jit_block_end_with_conditional ( + loop_cond, NULL, + gcc_jit_context_new_comparison ( + ctxt, NULL, + GCC_JIT_COMPARISON_NE, + gcc_jit_lvalue_as_rvalue (x), + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)), + after_loop, + loop_body); + + /* sum += foo (x) * 2; */ + gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue(x); + gcc_jit_block_add_assignment_op ( + loop_body, NULL, + x, + GCC_JIT_BINARY_OP_PLUS, + gcc_jit_context_new_binary_op ( + ctxt, NULL, + GCC_JIT_BINARY_OP_MULT, int_type, + gcc_jit_context_new_call (ctxt, NULL, foo_func, 1, &arg), + gcc_jit_context_new_rvalue_from_int ( + ctxt, + int_type, + 2))); + gcc_jit_block_end_with_jump (loop_body, NULL, loop_cond); + + /* return sum; */ + gcc_jit_block_end_with_return (after_loop, NULL, gcc_jit_lvalue_as_rvalue(sum)); +} + +/* { dg-final { jit-verify-output-file-was-created "" } } */ +/* Check that the loop was optimized away */ +/* { dg-final { jit-verify-assembler-output-not "jne" } } */ diff --git a/gcc/testsuite/jit.dg/test-noinline-attribute.c b/gcc/testsuite/jit.dg/test-noinline-attribute.c new file mode 100644 index 00000000000..a455b4493fd --- /dev/null +++ b/gcc/testsuite/jit.dg/test-noinline-attribute.c @@ -0,0 +1,121 @@ +/* { dg-do compile { target x86_64-*-* } } */ + +#include +#include + +#include "libgccjit.h" + +/* We don't want set_options() in harness.h to set -O2 to see that the `noinline` + attribute affects the optimizations. */ +#define TEST_ESCHEWS_SET_OPTIONS +static void set_options (gcc_jit_context *ctxt, const char *argv0) +{ + // Set "-O2". + gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2); +} + +#define TEST_COMPILING_TO_FILE +#define OUTPUT_KIND GCC_JIT_OUTPUT_KIND_ASSEMBLER +#define OUTPUT_FILENAME "output-of-test-noinline-attribute.c.s" +#include "harness.h" + +gcc_jit_function* +create_function (gcc_jit_context *ctxt, + const char *func_name, + gcc_jit_type *int_type, + int returned_value) +{ + gcc_jit_function *func + = gcc_jit_context_new_function(ctxt, NULL, + GCC_JIT_FUNCTION_INTERNAL, + int_type, + func_name, + 0, NULL, + 0); + gcc_jit_block *block = gcc_jit_function_new_block (func, NULL); + + gcc_jit_block_add_extended_asm (block, NULL, ""); + gcc_jit_block_end_with_return (block, NULL, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, returned_value)); + + return func; +} + + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: +__attribute__ ((noinline)) +static int not_removed() { + asm(""); + return 1; +} +static int removed() { + asm(""); + return 2; +} +int foo () { + int x = 0; + x += removed(); + x += not_removed(); + return x; +} + */ + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + + /* Creating the `not_removed` function. */ + gcc_jit_function *not_removed_func = + create_function (ctxt, "not_removed", int_type, 1); + /* __attribute__ ((no_inline)) */ + gcc_jit_function_add_attribute(not_removed_func, GCC_JIT_FN_ATTRIBUTE_NOINLINE); + + /* Creating the `removed` function. */ + gcc_jit_function *removed_func = + create_function (ctxt, "removed", int_type, 2); + + /* Creating the `foo` function. */ + gcc_jit_function *foo_func = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + int_type, + "foo", + 0, NULL, + 0); + + gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL); + + /* Build locals: */ + gcc_jit_lvalue *x = + gcc_jit_function_new_local (foo_func, NULL, int_type, "x"); + + /* int x = 0; */ + gcc_jit_block_add_assignment ( + foo_block, NULL, + x, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)); + + /* x += removed(); */ + gcc_jit_block_add_assignment_op ( + foo_block, NULL, + x, + GCC_JIT_BINARY_OP_PLUS, + gcc_jit_context_new_call (ctxt, NULL, removed_func, 0, NULL)); + + /* x += not_removed(); */ + gcc_jit_block_add_assignment_op ( + foo_block, NULL, + x, + GCC_JIT_BINARY_OP_PLUS, + gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 0, NULL)); + + /* return x; */ + gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x)); +} + +/* { dg-final { jit-verify-output-file-was-created "" } } */ +/* Check that the "removed" function was inlined, but not the others */ +/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed.isra.0,\\s+@function" } } */ +/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed.isra.0,\\s+@function" } } */ +/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */ diff --git a/gcc/testsuite/jit.dg/test-nonnull-attribute.c b/gcc/testsuite/jit.dg/test-nonnull-attribute.c new file mode 100644 index 00000000000..3306f890657 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-nonnull-attribute.c @@ -0,0 +1,94 @@ +/* { dg-do compile { target x86_64-*-* } } */ + +#include +#include + +#include "libgccjit.h" + +/* We don't want set_options() in harness.h to set -O2 to see that the nonnull + attribute affects the optimizations. */ +#define TEST_ESCHEWS_SET_OPTIONS +static void set_options (gcc_jit_context *ctxt, const char *argv0) +{ + // Set "-O2". + gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2); +} + +#define TEST_COMPILING_TO_FILE +#define OUTPUT_KIND GCC_JIT_OUTPUT_KIND_ASSEMBLER +#define OUTPUT_FILENAME "output-of-test-nonnull.c.s" +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: + +__attribute__((nonnull(1))) +int t(int *a) { + if (!a) { + return -1; + } + return *a; +} + */ + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + gcc_jit_type *pint_type = gcc_jit_type_get_pointer(int_type); + + gcc_jit_param *a = + gcc_jit_context_new_param (ctxt, NULL, pint_type, "a"); + + gcc_jit_function *func_t = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + int_type, + "t", + 1, &a, + 0); + /* Adding `nonnull(1)` attribute. */ + int indexes[1] = {1}; + gcc_jit_function_add_integer_array_attribute ( + func_t, + GCC_JIT_FN_ATTRIBUTE_NONNULL, + indexes, + 1 + ); + + /* if (!a) { + return -1; + } */ + gcc_jit_block *if_cond = + gcc_jit_function_new_block (func_t, "if_cond"); + gcc_jit_block *if_body = + gcc_jit_function_new_block (func_t, "if_body"); + gcc_jit_block *after_if = + gcc_jit_function_new_block (func_t, "after_if"); + + /* if (!a) */ + gcc_jit_block_end_with_conditional ( + if_cond, NULL, + gcc_jit_context_new_comparison ( + ctxt, NULL, + GCC_JIT_COMPARISON_EQ, + gcc_jit_param_as_rvalue (a), + gcc_jit_context_null (ctxt, pint_type)), + if_body, + after_if); + /* return -1; */ + gcc_jit_block_end_with_return ( + if_body, NULL, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, -1)); + + /* return *a; */ + gcc_jit_block_end_with_return ( + after_if, NULL, + gcc_jit_lvalue_as_rvalue ( + gcc_jit_rvalue_dereference ( + gcc_jit_param_as_rvalue (a), NULL))); +} + +/* { dg-final { jit-verify-output-file-was-created "" } } */ +/* Check that the "if block" was optimized away */ +/* { dg-final { jit-verify-assembler-output-not "testq" } } */ +/* { dg-final { jit-verify-assembler-output-not "-1" } } */ diff --git a/gcc/testsuite/jit.dg/test-pure-attribute.c b/gcc/testsuite/jit.dg/test-pure-attribute.c new file mode 100644 index 00000000000..0c9ba1366e0 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-pure-attribute.c @@ -0,0 +1,134 @@ +/* { dg-do compile { target x86_64-*-* } } */ + +#include +#include + +#include "libgccjit.h" + +/* We don't want set_options() in harness.h to set -O3 to see that the pure + attribute affects the optimizations. */ +#define TEST_ESCHEWS_SET_OPTIONS +static void set_options (gcc_jit_context *ctxt, const char *argv0) +{ + // Set "-O3". + gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3); +} + +#define TEST_COMPILING_TO_FILE +#define OUTPUT_KIND GCC_JIT_OUTPUT_KIND_ASSEMBLER +#define OUTPUT_FILENAME "output-of-test-pure-attribute.c.s" +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: +__attribute__ ((pure)) +int foo (int x); +int xxx(void) +{ + int x = 45; + int sum = 0; + + while (x >>= 1) + sum += foo (x) * 2; + return sum; +} + */ + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + + /* Creating the `foo` function. */ + gcc_jit_param *n = + gcc_jit_context_new_param (ctxt, NULL, int_type, "x"); + gcc_jit_param *params[1] = {n}; + gcc_jit_function *foo_func = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_IMPORTED, + int_type, + "foo", + 1, params, + 0); + gcc_jit_function_add_attribute(foo_func, GCC_JIT_FN_ATTRIBUTE_PURE); + + /* Creating the `xxx` function. */ + gcc_jit_function *xxx_func = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + int_type, + "xxx", + 0, NULL, + 0); + + gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL); + + /* Build locals: */ + gcc_jit_lvalue *x = + gcc_jit_function_new_local (xxx_func, NULL, int_type, "x"); + gcc_jit_lvalue *sum = + gcc_jit_function_new_local (xxx_func, NULL, int_type, "sum"); + + /* int x = 45 */ + gcc_jit_block_add_assignment ( + block, NULL, + x, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 45)); + /* int sum = 0 */ + gcc_jit_block_add_assignment ( + block, NULL, + sum, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)); + + /* while (x >>= 1) { sum += foo (x) * 2; } */ + gcc_jit_block *loop_cond = + gcc_jit_function_new_block (xxx_func, "loop_cond"); + gcc_jit_block *loop_body = + gcc_jit_function_new_block (xxx_func, "loop_body"); + gcc_jit_block *after_loop = + gcc_jit_function_new_block (xxx_func, "after_loop"); + + gcc_jit_block_end_with_jump (block, NULL, loop_cond); + + + /* if (x >>= 1) */ + /* Since gccjit doesn't (yet?) have support for `>>=` operator, we will decompose it into: + `if (x = x >> 1)` */ + gcc_jit_block_add_assignment_op ( + loop_cond, NULL, + x, + GCC_JIT_BINARY_OP_RSHIFT, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 1)); + /* The condition itself */ + gcc_jit_block_end_with_conditional ( + loop_cond, NULL, + gcc_jit_context_new_comparison ( + ctxt, NULL, + GCC_JIT_COMPARISON_NE, + gcc_jit_lvalue_as_rvalue (x), + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)), + after_loop, + loop_body); + + /* sum += foo (x) * 2; */ + gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue(x); + gcc_jit_block_add_assignment_op ( + loop_body, NULL, + x, + GCC_JIT_BINARY_OP_PLUS, + gcc_jit_context_new_binary_op ( + ctxt, NULL, + GCC_JIT_BINARY_OP_MULT, int_type, + gcc_jit_context_new_call (ctxt, NULL, foo_func, 1, &arg), + gcc_jit_context_new_rvalue_from_int ( + ctxt, + int_type, + 2))); + gcc_jit_block_end_with_jump (loop_body, NULL, loop_cond); + + /* return sum; */ + gcc_jit_block_end_with_return (after_loop, NULL, gcc_jit_lvalue_as_rvalue(sum)); +} + +/* { dg-final { jit-verify-output-file-was-created "" } } */ +/* Check that the loop was optimized away */ +/* { dg-final { jit-verify-assembler-output-not "jne" } } */ diff --git a/gcc/testsuite/jit.dg/test-restrict-attribute.c b/gcc/testsuite/jit.dg/test-restrict-attribute.c new file mode 100644 index 00000000000..7d7444b624f --- /dev/null +++ b/gcc/testsuite/jit.dg/test-restrict-attribute.c @@ -0,0 +1,77 @@ +/* { dg-do compile { target x86_64-*-* } } */ + +#include +#include + +#include "libgccjit.h" + +/* We don't want set_options() in harness.h to set -O3 to see that the restrict + attribute affects the optimizations. */ +#define TEST_ESCHEWS_SET_OPTIONS +static void set_options (gcc_jit_context *ctxt, const char *argv0) +{ + // Set "-O3". + gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3); +} + +#define TEST_COMPILING_TO_FILE +#define OUTPUT_KIND GCC_JIT_OUTPUT_KIND_ASSEMBLER +#define OUTPUT_FILENAME "output-of-test-restrict.c.s" +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: +void t(int *__restrict__ a, int *__restrict__ b, char *__restrict__ c) { + *a += *c; + *b += *c; +} + */ + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + gcc_jit_type *pint_type = gcc_jit_type_get_pointer(int_type); + gcc_jit_type *pint_restrict_type = gcc_jit_type_get_restrict(pint_type); + + gcc_jit_type *void_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID); + + gcc_jit_param *a = + gcc_jit_context_new_param (ctxt, NULL, pint_restrict_type, "a"); + gcc_jit_param *b = + gcc_jit_context_new_param (ctxt, NULL, pint_restrict_type, "b"); + gcc_jit_param *c = + gcc_jit_context_new_param (ctxt, NULL, pint_restrict_type, "c"); + gcc_jit_param *params[3] = {a, b, c}; + + gcc_jit_function *func_t = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + void_type, + "t", + 3, params, + 0); + + gcc_jit_block *block = gcc_jit_function_new_block (func_t, NULL); + + /* *a += *c; */ + gcc_jit_block_add_assignment_op ( + block, NULL, + gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (a), NULL), + GCC_JIT_BINARY_OP_PLUS, + gcc_jit_lvalue_as_rvalue ( + gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (c), NULL))); + /* *b += *c; */ + gcc_jit_block_add_assignment_op ( + block, NULL, + gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (b), NULL), + GCC_JIT_BINARY_OP_PLUS, + gcc_jit_lvalue_as_rvalue ( + gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (c), NULL))); + + gcc_jit_block_end_with_void_return (block, NULL); +} + +/* { dg-final { jit-verify-output-file-was-created "" } } */ +/* { dg-final { jit-verify-assembler-output "addl\\s+%eax,\\s+(%rdi) +\\s+addl\\s+%eax,\\s+(%rsi)" } } */ diff --git a/gcc/testsuite/jit.dg/test-used-attribute.c b/gcc/testsuite/jit.dg/test-used-attribute.c new file mode 100644 index 00000000000..cb20952c687 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-used-attribute.c @@ -0,0 +1,112 @@ +/* { dg-do compile { target x86_64-*-* } } */ + +#include +#include + +#include "libgccjit.h" + +#define TEST_ESCHEWS_SET_OPTIONS +static void set_options (gcc_jit_context *ctxt, const char *argv0) +{ + // Set "-O2". + gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2); +} + +#define TEST_COMPILING_TO_FILE +#define OUTPUT_KIND GCC_JIT_OUTPUT_KIND_ASSEMBLER +#define OUTPUT_FILENAME "output-of-test-used-attribute.c.s" +#include "harness.h" + +gcc_jit_function* +create_function (gcc_jit_context *ctxt, + const char *func_name, + gcc_jit_type *int_type, + int returned_value) +{ + gcc_jit_function *func = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_INTERNAL, + int_type, + func_name, + 0, NULL, + 0); + + gcc_jit_block *foo_block = gcc_jit_function_new_block (func, NULL); + gcc_jit_block_end_with_return (foo_block, NULL, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, returned_value)); + + return func; +} + + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: +__attribute__((used)) +static int not_removed() { return 1; } +static int removed() { return 2; } +int foo() { + int x = 0; + x += not_removed(); + x += removed(); + return x; +} + */ + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + + /* Creating the `not_removed` function. */ + gcc_jit_function *not_removed_func = + create_function (ctxt, "not_removed", int_type, 1); + /* __attribute__ ((used)) */ + gcc_jit_function_add_attribute(not_removed_func, GCC_JIT_FN_ATTRIBUTE_USED); + + /* Creating the `removed` function. */ + gcc_jit_function *removed_func = + create_function (ctxt, "removed", int_type, 2); + + /* Creating the `foo` function. */ + gcc_jit_function *foo_func = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + int_type, + "foo", + 0, NULL, + 0); + + gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL); + + /* Build locals: */ + gcc_jit_lvalue *x = + gcc_jit_function_new_local (foo_func, NULL, int_type, "x"); + + /* int x = 0; */ + gcc_jit_block_add_assignment ( + foo_block, NULL, + x, + gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)); + + /* x += removed(); */ + gcc_jit_block_add_assignment_op ( + foo_block, NULL, + x, + GCC_JIT_BINARY_OP_PLUS, + gcc_jit_context_new_call (ctxt, NULL, removed_func, 0, NULL)); + + /* x += not_removed(); */ + gcc_jit_block_add_assignment_op ( + foo_block, NULL, + x, + GCC_JIT_BINARY_OP_PLUS, + gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 0, NULL)); + + /* return x; */ + gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x)); +} + +/* { dg-final { jit-verify-output-file-was-created "" } } */ +/* Check that the "removed" function was inlined, but not the others */ +/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed,\\s+@function" } } */ +/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed,\\s+@function" } } */ +/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */ diff --git a/gcc/testsuite/jit.dg/test-variable-attribute.c b/gcc/testsuite/jit.dg/test-variable-attribute.c new file mode 100644 index 00000000000..ea854ff4a9f --- /dev/null +++ b/gcc/testsuite/jit.dg/test-variable-attribute.c @@ -0,0 +1,46 @@ +/* { dg-do compile { target x86_64-*-* } } */ + +#include +#include + +#include "libgccjit.h" + +#define TEST_COMPILING_TO_FILE +#define OUTPUT_KIND GCC_JIT_OUTPUT_KIND_ASSEMBLER +#define OUTPUT_FILENAME "output-of-test-variable-attribute.c.s" +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: + +int PRIVATE __attribute__ ((visibility ("hidden"))) = 42; +int PUBLIC = 12; + */ + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + + /* Creating the `PRIVATE` variable. */ + gcc_jit_lvalue *private = gcc_jit_context_new_global (ctxt, + NULL, GCC_JIT_GLOBAL_EXPORTED, int_type, "PRIVATE"); + gcc_jit_lvalue_add_string_attribute (private, + GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY, "hidden"); + gcc_jit_rvalue *rval = gcc_jit_context_new_rvalue_from_int ( + ctxt, int_type, 42); + gcc_jit_global_set_initializer_rvalue (private, rval); + + /* Creating the `PUBLIC` variable. */ + gcc_jit_lvalue *public = gcc_jit_context_new_global (ctxt, + NULL, GCC_JIT_GLOBAL_EXPORTED, int_type, "PUBLIC"); + gcc_jit_rvalue *rval2 = gcc_jit_context_new_rvalue_from_int ( + ctxt, int_type, 12); + gcc_jit_global_set_initializer_rvalue (public, rval2); +} + +/* { dg-final { jit-verify-output-file-was-created "" } } */ +/* Check that the attribute was applied correctly */ +/* { dg-final { jit-verify-assembler-output ".hidden\\s+PRIVATE" } } */ +/* { dg-final { jit-verify-assembler-output ".globl\\s+PRIVATE" } } */ +/* { dg-final { jit-verify-assembler-output-not ".hidden\\s+PUBLIC" } } */ +/* { dg-final { jit-verify-assembler-output ".globl\\s+PUBLIC" } } */ diff --git a/gcc/testsuite/jit.dg/test-weak-attribute.c b/gcc/testsuite/jit.dg/test-weak-attribute.c new file mode 100644 index 00000000000..546ade1c3c4 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-weak-attribute.c @@ -0,0 +1,41 @@ +/* { dg-do compile { target x86_64-*-* } } */ + +#include +#include + +#include "libgccjit.h" + +#define TEST_COMPILING_TO_FILE +#define OUTPUT_KIND GCC_JIT_OUTPUT_KIND_ASSEMBLER +#define OUTPUT_FILENAME "output-of-test-weak-attribute.c.s" +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: + +__attribute__ ((weak)) +void f () {} + */ + gcc_jit_type *void_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID); + + /* Creating the `f` function. */ + gcc_jit_function *f_func = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + void_type, + "f", + 0, NULL, + 0); + gcc_jit_function_add_attribute(f_func, GCC_JIT_FN_ATTRIBUTE_WEAK); + + /* void f () {} */ + gcc_jit_block *block = gcc_jit_function_new_block (f_func, NULL); + gcc_jit_block_end_with_void_return (block, NULL); +} + +/* { dg-final { jit-verify-output-file-was-created "" } } */ +/* Check that the attribute was applied correctly */ +/* { dg-final { jit-verify-assembler-output ".weak\\s+f" } } */ -- 2.34.1