[PATCH v1 1/1] Merge common attribute handling code between libgccjit and c-family.
Guillaume Gomez via Sourceware Forge <forge-bot+imperio-Wfwr+2zPm/[email protected]> Mon, 03 Aug 2026 14:37:03 +0000
| Newsgroups | gmane.comp.gcc.jit,gmane.comp.gcc.patches |
|---|---|
| Message-ID | <bmm.hl02ewfvoa.gcc.gcc.imperio.207.1.1@forge-stage.sourceware.org> |
From: Guillaume Gomez <[email protected]> gcc/ChangeLog: * Makefile.in: Compile new attr-handlers.cc file * attr-handlers.cc: New file. * attr-handlers.h: New file. gcc/c-family/ChangeLog: * c-attribs.cc: Move code into `attr-handlers.cc` gcc/jit/ChangeLog: * Make-lang.in: Compile new attr-handlers.cc file * dummy-frontend.cc: Remove code duplicated with `attr-handlers.cc` --- gcc/Makefile.in | 2 +- gcc/attr-handlers.cc | 896 ++++++++++++++++++++++++++++++++++++++ gcc/attr-handlers.h | 61 +++ gcc/c-family/c-attribs.cc | 844 +---------------------------------- gcc/jit/Make-lang.in | 1 + gcc/jit/dummy-frontend.cc | 646 +-------------------------- 6 files changed, 988 insertions(+), 1462 deletions(-) create mode 100644 gcc/attr-handlers.cc create mode 100644 gcc/attr-handlers.h diff --git a/gcc/Makefile.in b/gcc/Makefile.in index ee2f9022eab7f..260622b8dd0cf 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1329,7 +1329,7 @@ C_COMMON_OBJS = c-family/c-common.o c-family/c-cppbuiltin.o c-family/c-dump.o \ c-family/c-semantics.o c-family/c-ada-spec.o \ c-family/c-ubsan.o c-family/known-headers.o \ c-family/c-attribs.o c-family/c-warn.o c-family/c-spellcheck.o \ - c-family/c-type-mismatch.o + c-family/c-type-mismatch.o attr-handlers.o # Analyzer object files ANALYZER_OBJS = \ diff --git a/gcc/attr-handlers.cc b/gcc/attr-handlers.cc new file mode 100644 index 0000000000000..adbbe7df44303 --- /dev/null +++ b/gcc/attr-handlers.cc @@ -0,0 +1,896 @@ +/* Language-independent attribute handlers shared across front ends. + Copyright (C) 2026 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "target.h" +#include "function.h" +#include "tree.h" +#include "stringpool.h" +#include "attribs.h" +#include "attr-handlers.h" +#include "diagnostic-core.h" +#include "options.h" +#include "cgraph.h" +#include "varasm.h" +#include "common/common-target.h" +#include "tree-pretty-print.h" + +/* Helper to define attribute exclusions. */ +#define ATTR_EXCL(name, function, type, variable) \ + { name, function, type, variable } + +/* Define attributes that are mutually exclusive with one another. */ +const struct attribute_spec::exclusions attr_noreturn_exclusions[] = +{ + ATTR_EXCL ("alloc_align", true, true, true), + ATTR_EXCL ("alloc_size", true, true, true), + ATTR_EXCL ("const", true, true, true), + ATTR_EXCL ("malloc", true, true, true), + ATTR_EXCL ("pure", true, true, true), + ATTR_EXCL ("returns_twice", true, true, true), + ATTR_EXCL ("warn_unused_result", true, true, true), + ATTR_EXCL (NULL, false, false, false), +}; + +const struct attribute_spec::exclusions attr_returns_twice_exclusions[] = +{ + ATTR_EXCL ("noreturn", true, true, true), + ATTR_EXCL (NULL, false, false, false), +}; + +/* Exclusions that apply to attribute alloc_align, alloc_size, and malloc. */ +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), +}; + +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) +}; + +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), +}; + +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) +}; + +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), +}; + +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), +}; + +/* Exclusions that apply to attributes that put declarations in specific + sections. */ +const struct attribute_spec::exclusions attr_section_exclusions[] = +{ + ATTR_EXCL ("noinit", true, true, true), + ATTR_EXCL ("persistent", true, true, true), + ATTR_EXCL ("section", true, true, true), + ATTR_EXCL (NULL, false, false, false), +}; + +/* Return the first of DECL or TYPE attributes installed in NODE if it's + a DECL, or TYPE attributes if it's a TYPE, or null otherwise. */ + +static tree +decl_or_type_attrs (tree node) +{ + if (DECL_P (node)) + { + if (tree attrs = DECL_ATTRIBUTES (node)) + return attrs; + + tree type = TREE_TYPE (node); + if (type == error_mark_node) + return NULL_TREE; + return TYPE_ATTRIBUTES (type); + } + + if (TYPE_P (node)) + return TYPE_ATTRIBUTES (node); + + return NULL_TREE; +} + +/* Given a pair of NODEs for arbitrary DECLs or TYPEs, validate one or + two integral or string attribute arguments NEWARGS to be applied to + NODE[0] for the absence of conflicts with the same attribute arguments + already applied to NODE[1]. Issue a warning for conflicts and return + false. Otherwise, when no conflicts are found, return true. */ + +bool +validate_attr_args (tree node[2], tree name, tree newargs[2]) +{ + /* First validate the arguments against those already applied to + the same declaration (or type). */ + tree self[2] = { node[0], node[0] }; + if (node[0] != node[1] && !validate_attr_args (self, name, newargs)) + return false; + + if (!node[1]) + return true; + + /* Extract the same attribute from the previous declaration or type. */ + tree prevattr = decl_or_type_attrs (node[1]); + const char* const namestr = IDENTIFIER_POINTER (name); + prevattr = lookup_attribute (namestr, prevattr); + if (!prevattr) + return true; + + /* Extract one or both attribute arguments. */ + tree prevargs[2]; + prevargs[0] = TREE_VALUE (TREE_VALUE (prevattr)); + prevargs[1] = TREE_CHAIN (TREE_VALUE (prevattr)); + if (prevargs[1]) + prevargs[1] = TREE_VALUE (prevargs[1]); + + /* Both arguments must be equal or, for the second pair, neither must + be provided to succeed. */ + bool arg1eq, arg2eq; + if (TREE_CODE (newargs[0]) == INTEGER_CST) + { + arg1eq = tree_int_cst_equal (newargs[0], prevargs[0]); + if (newargs[1] && prevargs[1]) + arg2eq = tree_int_cst_equal (newargs[1], prevargs[1]); + else + arg2eq = newargs[1] == prevargs[1]; + } + else if (TREE_CODE (newargs[0]) == STRING_CST) + { + const char *s0 = TREE_STRING_POINTER (newargs[0]); + const char *s1 = TREE_STRING_POINTER (prevargs[0]); + arg1eq = strcmp (s0, s1) == 0; + if (newargs[1] && prevargs[1]) + { + s0 = TREE_STRING_POINTER (newargs[1]); + s1 = TREE_STRING_POINTER (prevargs[1]); + arg2eq = strcmp (s0, s1) == 0; + } + else + arg2eq = newargs[1] == prevargs[1]; + } + else + gcc_unreachable (); + + if (arg1eq && arg2eq) + return true; + + /* If the two locations are different print a note pointing to + the previous one. */ + const location_t curloc = input_location; + const location_t prevloc = DECL_P ( + node[1]) ? DECL_SOURCE_LOCATION (node[1]) : curloc; + + /* Format the attribute specification for convenience. */ + char newspec[80], prevspec[80]; + if (newargs[1]) + snprintf (newspec, sizeof newspec, "%s (%s, %s)", namestr, + print_generic_expr_to_str (newargs[0]), + print_generic_expr_to_str (newargs[1])); + else + snprintf (newspec, sizeof newspec, "%s (%s)", namestr, + print_generic_expr_to_str (newargs[0])); + + if (prevargs[1]) + snprintf (prevspec, sizeof prevspec, "%s (%s, %s)", namestr, + print_generic_expr_to_str (prevargs[0]), + print_generic_expr_to_str (prevargs[1])); + else + snprintf (prevspec, sizeof prevspec, "%s (%s)", namestr, + print_generic_expr_to_str (prevargs[0])); + + if (warning_at (curloc, OPT_Wattributes, + "ignoring attribute %qs because it conflicts " + "with previous %qs", + newspec, prevspec) + && curloc != prevloc) + inform (prevloc, "previous declaration here"); + + return false; +} + +/* Convenience wrapper for validate_attr_args to validate a single + attribute argument. Used by handlers for attributes that take + just a single argument. */ + +bool +validate_attr_arg (tree node[2], tree name, tree newarg) +{ + tree argarray[2] = { newarg, NULL_TREE }; + return validate_attr_args (node, name, argarray); +} + +/* 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. */ + +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; +} + +/* Ignore the given attribute. Used when this attribute may be usefully + overridden by the target, but is not used generically. */ + +tree +ignore_attribute (tree * ARG_UNUSED (node), tree ARG_UNUSED (name), + tree ARG_UNUSED (args), int ARG_UNUSED (flags), + bool *no_add_attrs) +{ + *no_add_attrs = true; + return NULL_TREE; +} + +/* Handle a "leaf" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_leaf_attribute (tree *node, tree name, + tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + if (!TREE_PUBLIC (*node)) + { + warning (OPT_Wattributes, "%qE attribute has no effect on unit local " + "functions", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle a "const" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_const_attribute (tree *node, tree name, tree ARG_UNUSED (args), + int flags, bool *no_add_attrs) +{ + tree type = TREE_TYPE (*node); + + /* See FIXME comment on noreturn in c_common_attribute_table. */ + if (TREE_CODE (*node) == FUNCTION_DECL) + TREE_READONLY (*node) = 1; + else if (TREE_CODE (type) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE) + TREE_TYPE (*node) + = (build_qualified_type + (build_pointer_type + (build_type_variant (TREE_TYPE (type), 1, + TREE_THIS_VOLATILE (TREE_TYPE (type)))), + TYPE_QUALS (type))); + else + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + /* void __builtin_unreachable (void) is const. Accept other such + built-ins but warn on user-defined functions that return void. */ + if (!(flags & ATTR_FLAG_BUILT_IN) + && TREE_CODE (*node) == FUNCTION_DECL + && VOID_TYPE_P (TREE_TYPE (type))) + warning (OPT_Wattributes, "%qE attribute on function " + "returning %<void%>", name); + + return NULL_TREE; +} + +/* Handle a "pure" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_pure_attribute (tree *node, tree name, tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + if (TREE_CODE (*node) == FUNCTION_DECL) + { + tree type = TREE_TYPE (*node); + if (VOID_TYPE_P (TREE_TYPE (type))) + warning (OPT_Wattributes, "%qE attribute on function " + "returning %<void%>", name); + + DECL_PURE_P (*node) = 1; + /* ??? TODO: Support types. */ + } + else + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle a "no vops" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_novops_attribute (tree *node, tree ARG_UNUSED (name), + tree ARG_UNUSED (args), int ARG_UNUSED (flags), + bool *ARG_UNUSED (no_add_attrs)) +{ + gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); + DECL_IS_NOVOPS (*node) = 1; + return NULL_TREE; +} + +/* Handle a "nothrow" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_nothrow_attribute (tree *node, tree name, tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + if (TREE_CODE (*node) == FUNCTION_DECL) + TREE_NOTHROW (*node) = 1; + /* ??? TODO: Support types. */ + else + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle a "type_generic" attribute. */ + +tree +handle_type_generic_attribute (tree *node, tree ARG_UNUSED (name), + tree ARG_UNUSED (args), int ARG_UNUSED (flags), + bool * ARG_UNUSED (no_add_attrs)) +{ + /* Ensure we have a function type. */ + gcc_assert (TREE_CODE (*node) == FUNCTION_TYPE); + + /* Ensure we have a variadic function. */ + gcc_assert (!prototype_p (*node) || stdarg_p (*node)); + + return NULL_TREE; +} + +/* Handle a "transaction_pure" attribute. */ + +tree +handle_transaction_pure_attribute (tree *node, tree ARG_UNUSED (name), + tree ARG_UNUSED (args), + int ARG_UNUSED (flags), + bool * ARG_UNUSED (no_add_attrs)) +{ + /* Ensure we have a function type. */ + gcc_assert (TREE_CODE (*node) == FUNCTION_TYPE); + + return NULL_TREE; +} + +/* Handle a "returns_twice" attribute. */ + +tree +handle_returns_twice_attribute (tree *node, tree name, tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + if (TREE_CODE (*node) == FUNCTION_DECL) + DECL_IS_RETURNS_TWICE (*node) = 1; + else + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle a "fn spec" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name), + tree args, int ARG_UNUSED (flags), + bool *no_add_attrs ATTRIBUTE_UNUSED) +{ + gcc_assert (args + && TREE_CODE (TREE_VALUE (args)) == STRING_CST + && !TREE_CHAIN (args)); + return NULL_TREE; +} + +/* Handle an "visibility" attribute; arguments as in + struct attribute_spec.handler. */ + +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. */ + +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 "noinline" attribute; arguments as in + struct attribute_spec.handler. */ + +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. */ + +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. */ + +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 %<target%>"); + *no_add_attrs = true; + } + } + + return NULL_TREE; +} + +/* Handle a "used" attribute; arguments as in + struct attribute_spec.handler. */ + +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. */ + +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); +} + +/* Handle a "section" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_section_attribute (tree *node, tree name, tree args, + int flags, bool *no_add_attrs) +{ + tree decl = *node; + tree res = NULL_TREE; + tree argval = TREE_VALUE (args); + const char* new_section_name; + + if (!targetm_common.have_named_sections) + { + error_at (DECL_SOURCE_LOCATION (*node), + "section attributes are not supported for this target"); + goto fail; + } + + if (!VAR_OR_FUNCTION_DECL_P (decl)) + { + error ("section attribute not allowed for %q+D", *node); + goto fail; + } + + if (TREE_CODE (argval) != STRING_CST) + { + error ("section attribute argument not a string constant"); + goto fail; + } + + if (VAR_P (decl) + && current_function_decl != NULL_TREE + && !TREE_STATIC (decl)) + { + error_at (DECL_SOURCE_LOCATION (decl), + "section attribute cannot be specified for local variables"); + goto fail; + } + + new_section_name = TREE_STRING_POINTER (argval); + + /* The decl may have already been given a section attribute + from a previous declaration. Ensure they match. */ + if (const char* const old_section_name = DECL_SECTION_NAME (decl)) + if (strcmp (old_section_name, new_section_name) != 0) + { + error ("section of %q+D conflicts with previous declaration", + *node); + goto fail; + } + + if (VAR_P (decl) + && !targetm.have_tls && targetm.emutls.tmpl_section + && DECL_THREAD_LOCAL_P (decl)) + { + error ("section of %q+D cannot be overridden", *node); + goto fail; + } + + if (!validate_attr_arg (node, name, argval)) + goto fail; + + res = targetm.handle_generic_attribute (node, name, args, flags, + no_add_attrs); + + /* If the back end confirms the attribute can be added then continue onto + final processing. */ + if (!(*no_add_attrs)) + { + set_decl_section_name (decl, new_section_name); + return res; + } + +fail: + *no_add_attrs = true; + return res; +} + +/* Handle a "retain" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_retain_attribute (tree *pnode, tree name, tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + tree node = *pnode; + + if (SUPPORTS_SHF_GNU_RETAIN + && (TREE_CODE (node) == FUNCTION_DECL + || (VAR_P (node) && TREE_STATIC (node)))) + ; + else + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Part of the `malloc` attribute handling which is shared by libgccjit and + the C-family front-end. */ + +tree +handle_malloc_common (tree *node, tree name, tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute ignored; valid only " + "for functions", name); + *no_add_attrs = true; + return NULL_TREE; + } + + tree rettype = TREE_TYPE (TREE_TYPE (*node)); + if (!POINTER_TYPE_P (rettype)) + { + warning (OPT_Wattributes, "%qE attribute ignored on functions " + "returning %qT; valid only for pointer return types", + name, rettype); + *no_add_attrs = true; + return NULL_TREE; + } + + DECL_IS_MALLOC (*node) = 1; + return NULL_TREE; +} diff --git a/gcc/attr-handlers.h b/gcc/attr-handlers.h new file mode 100644 index 0000000000000..0f6ba12af4058 --- /dev/null +++ b/gcc/attr-handlers.h @@ -0,0 +1,61 @@ +/* Language-independent attribute handlers shared across front ends. + Copyright (C) 2026 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_ATTR_HANDLERS_H +#define GCC_ATTR_HANDLERS_H + +/* Attribute exclusion tables used by `c-family` and `libgccjit`. */ +extern const struct attribute_spec::exclusions attr_noreturn_exclusions[]; +extern const struct attribute_spec::exclusions attr_returns_twice_exclusions[]; +extern const struct attribute_spec::exclusions attr_alloc_exclusions[]; +extern const struct attribute_spec::exclusions attr_const_pure_exclusions[]; +extern const struct attribute_spec::exclusions attr_always_inline_exclusions[]; +extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[]; +extern const struct attribute_spec::exclusions attr_noinline_exclusions[]; +extern const struct attribute_spec::exclusions attr_target_exclusions[]; +extern const struct attribute_spec::exclusions attr_section_exclusions[]; + +/* Shared helpers used by the handlers. */ +extern bool validate_attr_args (tree node[2], tree name, tree newargs[2]); +extern bool validate_attr_arg (tree node[2], tree name, tree newarg); + +/* Attribute handlers. */ +extern tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *); +extern tree handle_malloc_common (tree *, tree, tree, int, bool *); +extern tree handle_alias_attribute (tree *, tree, tree, int, bool *); +extern tree handle_always_inline_attribute (tree *, tree, tree, int, bool *); +extern tree handle_const_attribute (tree *, tree, tree, int, bool *); +extern tree handle_fnspec_attribute (tree *, tree, tree, int, bool *); +extern tree handle_leaf_attribute (tree *, tree, tree, int, bool *); +extern tree handle_noinline_attribute (tree *, tree, tree, int, bool *); +extern tree handle_nothrow_attribute (tree *, tree, tree, int, bool *); +extern tree handle_novops_attribute (tree *, tree, tree, int, bool *); +extern tree handle_pure_attribute (tree *, tree, tree, int, bool *); +extern tree handle_retain_attribute (tree *, tree, tree, int, bool *); +extern tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *); +extern tree handle_section_attribute (tree *, tree, tree, int, bool *); +extern tree handle_target_attribute (tree *, tree, tree, int, bool *); +extern tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *); +extern tree handle_type_generic_attribute (tree *, tree, tree, int, bool *); +extern tree handle_used_attribute (tree *, tree, tree, int, bool *); +extern tree handle_visibility_attribute (tree *, tree, tree, int, bool *); +extern tree handle_weak_attribute (tree *, tree, tree, int, bool *); +extern tree ignore_attribute (tree *, tree, tree, int, bool *); + +#endif // GCC_ATTR_HANDLERS_H diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index d668ab96630af..1999c8aa0b6a6 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see #include "stor-layout.h" #include "calls.h" #include "attribs.h" +#include "attr-handlers.h" #include "varasm.h" #include "trans-mem.h" #include "c-objc.h" @@ -73,26 +74,20 @@ static tree handle_stack_protect_attribute (tree *, tree, tree, int, bool *); static tree handle_no_stack_protector_function_attribute (tree *, tree, tree, int, bool *); static tree handle_strub_attribute (tree *, tree, tree, int, bool *); -static tree handle_noinline_attribute (tree *, tree, tree, int, bool *); static tree handle_noclone_attribute (tree *, tree, tree, int, bool *); static tree handle_nocf_check_attribute (tree *, tree, tree, int, bool *); static tree handle_symver_attribute (tree *, tree, tree, int, bool *); static tree handle_noicf_attribute (tree *, tree, tree, int, bool *); static tree handle_noipa_attribute (tree *, tree, tree, int, bool *); -static tree handle_leaf_attribute (tree *, tree, tree, int, bool *); -static tree handle_always_inline_attribute (tree *, tree, tree, int, - bool *); static tree handle_gnu_inline_attribute (tree *, tree, tree, int, bool *); static tree handle_artificial_attribute (tree *, tree, tree, int, bool *); static tree handle_flatten_attribute (tree *, tree, tree, int, bool *); static tree handle_error_attribute (tree *, tree, tree, int, bool *); -static tree handle_used_attribute (tree *, tree, tree, int, bool *); static tree handle_uninitialized_attribute (tree *, tree, tree, int, bool *); static tree handle_externally_visible_attribute (tree *, tree, tree, int, bool *); static tree handle_no_reorder_attribute (tree *, tree, tree, int, bool *); -static tree handle_const_attribute (tree *, tree, tree, int, bool *); static tree handle_transparent_union_attribute (tree *, tree, tree, int, bool *); static tree handle_scalar_storage_order_attribute (tree *, tree, tree, @@ -100,7 +95,6 @@ static tree handle_scalar_storage_order_attribute (tree *, tree, tree, static tree handle_constructor_attribute (tree *, tree, tree, int, bool *); static tree handle_destructor_attribute (tree *, tree, tree, int, bool *); static tree handle_mode_attribute (tree *, tree, tree, int, bool *); -static tree handle_section_attribute (tree *, tree, tree, int, bool *); static tree handle_special_var_sec_attribute (tree *, tree, tree, int, bool *); static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree, int, bool *); @@ -108,14 +102,9 @@ static tree handle_strict_flex_array_attribute (tree *, tree, tree, int, bool *); static tree handle_counted_by_attribute (tree *, tree, tree, int, bool *); -static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ; static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ; -static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *); static tree handle_ifunc_attribute (tree *, tree, tree, int, bool *); -static tree handle_alias_attribute (tree *, tree, tree, int, bool *); static tree handle_weakref_attribute (tree *, tree, tree, int, bool *) ; -static tree handle_visibility_attribute (tree *, tree, tree, int, - bool *); static tree handle_tls_model_attribute (tree *, tree, tree, int, bool *); static tree handle_no_instrument_function_attribute (tree *, tree, @@ -125,13 +114,10 @@ static tree handle_no_profile_instrument_function_attribute (tree *, tree, static tree handle_malloc_attribute (tree *, tree, tree, int, bool *); static tree handle_dealloc_attribute (tree *, tree, tree, int, bool *); static tree handle_tainted_args_attribute (tree *, tree, tree, int, bool *); -static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *); static tree handle_no_limit_stack_attribute (tree *, tree, tree, int, bool *); -static tree handle_pure_attribute (tree *, tree, tree, int, bool *); static tree handle_tm_attribute (tree *, tree, tree, int, bool *); static tree handle_tm_wrap_attribute (tree *, tree, tree, int, bool *); -static tree handle_novops_attribute (tree *, tree, tree, int, bool *); static tree handle_unavailable_attribute (tree *, tree, tree, int, bool *); static tree handle_vector_size_attribute (tree *, tree, tree, int, @@ -142,7 +128,6 @@ static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *); static tree handle_nonnull_if_nonzero_attribute (tree *, tree, tree, int, bool *); static tree handle_nonstring_attribute (tree *, tree, tree, int, bool *); -static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *); static tree handle_expected_throw_attribute (tree *, tree, tree, int, bool *); static tree handle_cleanup_attribute (tree *, tree, tree, int, bool *); static tree handle_warn_unused_result_attribute (tree *, tree, tree, int, @@ -150,21 +135,17 @@ static tree handle_warn_unused_result_attribute (tree *, tree, tree, int, static tree handle_access_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_alloc_size_attribute (tree *, tree, tree, int, bool *); static tree handle_alloc_align_attribute (tree *, tree, tree, int, bool *); static tree handle_assume_aligned_attribute (tree *, tree, tree, int, bool *); static tree handle_assume_attribute (tree *, tree, tree, int, bool *); -static tree handle_target_attribute (tree *, tree, tree, int, bool *); static tree handle_target_version_attribute (tree *, tree, tree, int, bool *); static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *); static tree handle_optimize_attribute (tree *, tree, tree, int, bool *); -static tree ignore_attribute (tree *, tree, tree, int, bool *); static tree handle_no_split_stack_attribute (tree *, tree, tree, int, bool *); static tree handle_zero_call_used_regs_attribute (tree *, tree, tree, int, bool *); static tree handle_argspec_attribute (tree *, tree, tree, int, bool *); -static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *); static tree handle_warn_unused_attribute (tree *, tree, tree, int, bool *); static tree handle_returns_nonnull_attribute (tree *, tree, tree, int, bool *); static tree handle_omp_declare_simd_attribute (tree *, tree, tree, int, @@ -185,7 +166,6 @@ static tree handle_objc_nullability_attribute (tree *, tree, tree, int, bool *); static tree handle_signed_bool_precision_attribute (tree *, tree, tree, int, bool *); static tree handle_hardbool_attribute (tree *, tree, tree, int, bool *); -static tree handle_retain_attribute (tree *, tree, tree, int, bool *); static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *); static tree handle_flag_enum_attribute (tree *, tree, tree, int, bool *); static tree handle_null_terminated_string_arg_attribute (tree *, tree, tree, int, bool *); @@ -207,13 +187,6 @@ extern const struct attribute_spec::exclusions attr_aligned_exclusions[] = 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_common_exclusions[] = { ATTR_EXCL ("common", true, true, true), @@ -227,27 +200,6 @@ static const struct attribute_spec::exclusions attr_inline_exclusions[] = 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), -}; - -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), -}; - static const struct attribute_spec::exclusions attr_target_clones_exclusions[] = { ATTR_EXCL ("always_inline", true, true, true), @@ -279,18 +231,6 @@ static const struct attribute_spec::exclusions attr_simd_exclusions[] = ATTR_EXCL (NULL, false, false, false), }; -extern const struct attribute_spec::exclusions attr_noreturn_exclusions[] = -{ - ATTR_EXCL ("alloc_align", true, true, true), - ATTR_EXCL ("alloc_size", true, true, true), - ATTR_EXCL ("const", true, true, true), - ATTR_EXCL ("malloc", true, true, true), - ATTR_EXCL ("pure", true, true, true), - ATTR_EXCL ("returns_twice", true, true, true), - ATTR_EXCL ("warn_unused_result", true, true, true), - ATTR_EXCL (NULL, false, false, false), -}; - static const struct attribute_spec::exclusions attr_warn_unused_result_exclusions[] = { @@ -299,42 +239,6 @@ attr_warn_unused_result_exclusions[] = ATTR_EXCL (NULL, false, false, false), }; -static const struct attribute_spec::exclusions attr_returns_twice_exclusions[] = -{ - ATTR_EXCL ("noreturn", true, true, true), - 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) -}; - -/* Exclusions that apply to attributes that put declarations in specific - sections. */ -static const struct attribute_spec::exclusions attr_section_exclusions[] = -{ - ATTR_EXCL ("noinit", true, true, true), - ATTR_EXCL ("persistent", true, true, true), - ATTR_EXCL ("section", true, true, true), - ATTR_EXCL (NULL, false, false, false), -}; - static const struct attribute_spec::exclusions attr_stack_protect_exclusions[] = { ATTR_EXCL ("stack_protect", true, false, false), @@ -968,137 +872,6 @@ positional_argument (const_tree fn, const_tree atname, tree &pos, return build_int_cst (TREE_TYPE (pos), ipos); } -/* Return the first of DECL or TYPE attributes installed in NODE if it's - a DECL, or TYPE attributes if it's a TYPE, or null otherwise. */ - -static tree -decl_or_type_attrs (tree node) -{ - if (DECL_P (node)) - { - if (tree attrs = DECL_ATTRIBUTES (node)) - return attrs; - - tree type = TREE_TYPE (node); - if (type == error_mark_node) - return NULL_TREE; - return TYPE_ATTRIBUTES (type); - } - - if (TYPE_P (node)) - return TYPE_ATTRIBUTES (node); - - return NULL_TREE; -} - -/* Given a pair of NODEs for arbitrary DECLs or TYPEs, validate one or - two integral or string attribute arguments NEWARGS to be applied to - NODE[0] for the absence of conflicts with the same attribute arguments - already applied to NODE[1]. Issue a warning for conflicts and return - false. Otherwise, when no conflicts are found, return true. */ - -static bool -validate_attr_args (tree node[2], tree name, tree newargs[2]) -{ - /* First validate the arguments against those already applied to - the same declaration (or type). */ - tree self[2] = { node[0], node[0] }; - if (node[0] != node[1] && !validate_attr_args (self, name, newargs)) - return false; - - if (!node[1]) - return true; - - /* Extract the same attribute from the previous declaration or type. */ - tree prevattr = decl_or_type_attrs (node[1]); - const char* const namestr = IDENTIFIER_POINTER (name); - prevattr = lookup_attribute (namestr, prevattr); - if (!prevattr) - return true; - - /* Extract one or both attribute arguments. */ - tree prevargs[2]; - prevargs[0] = TREE_VALUE (TREE_VALUE (prevattr)); - prevargs[1] = TREE_CHAIN (TREE_VALUE (prevattr)); - if (prevargs[1]) - prevargs[1] = TREE_VALUE (prevargs[1]); - - /* Both arguments must be equal or, for the second pair, neither must - be provided to succeed. */ - bool arg1eq, arg2eq; - if (TREE_CODE (newargs[0]) == INTEGER_CST) - { - arg1eq = tree_int_cst_equal (newargs[0], prevargs[0]); - if (newargs[1] && prevargs[1]) - arg2eq = tree_int_cst_equal (newargs[1], prevargs[1]); - else - arg2eq = newargs[1] == prevargs[1]; - } - else if (TREE_CODE (newargs[0]) == STRING_CST) - { - const char *s0 = TREE_STRING_POINTER (newargs[0]); - const char *s1 = TREE_STRING_POINTER (prevargs[0]); - arg1eq = strcmp (s0, s1) == 0; - if (newargs[1] && prevargs[1]) - { - s0 = TREE_STRING_POINTER (newargs[1]); - s1 = TREE_STRING_POINTER (prevargs[1]); - arg2eq = strcmp (s0, s1) == 0; - } - else - arg2eq = newargs[1] == prevargs[1]; - } - else - gcc_unreachable (); - - if (arg1eq && arg2eq) - return true; - - /* If the two locations are different print a note pointing to - the previous one. */ - const location_t curloc = input_location; - const location_t prevloc = - DECL_P (node[1]) ? DECL_SOURCE_LOCATION (node[1]) : curloc; - - /* Format the attribute specification for convenience. */ - char newspec[80], prevspec[80]; - if (newargs[1]) - snprintf (newspec, sizeof newspec, "%s (%s, %s)", namestr, - print_generic_expr_to_str (newargs[0]), - print_generic_expr_to_str (newargs[1])); - else - snprintf (newspec, sizeof newspec, "%s (%s)", namestr, - print_generic_expr_to_str (newargs[0])); - - if (prevargs[1]) - snprintf (prevspec, sizeof prevspec, "%s (%s, %s)", namestr, - print_generic_expr_to_str (prevargs[0]), - print_generic_expr_to_str (prevargs[1])); - else - snprintf (prevspec, sizeof prevspec, "%s (%s)", namestr, - print_generic_expr_to_str (prevargs[0])); - - if (warning_at (curloc, OPT_Wattributes, - "ignoring attribute %qs because it conflicts " - "with previous %qs", - newspec, prevspec) - && curloc != prevloc) - inform (prevloc, "previous declaration here"); - - return false; -} - -/* Convenience wrapper for validate_attr_args to validate a single - attribute argument. Used by handlers for attributes that take - just a single argument. */ - -static bool -validate_attr_arg (tree node[2], tree name, tree newarg) -{ - tree argarray[2] = { newarg, NULL_TREE }; - return validate_attr_args (node, name, argarray); -} - /* Attribute handlers common to C front ends. */ /* Handle a "signed_bool_precision" attribute; arguments as in @@ -1724,25 +1497,6 @@ handle_strub_attribute (tree *node, tree name, 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 "noclone" attribute; arguments as in struct attribute_spec.handler. */ @@ -1803,31 +1557,6 @@ handle_noicf_attribute (tree *node, tree name, 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 "gnu_inline" attribute; arguments as in struct attribute_spec.handler. */ @@ -1851,29 +1580,6 @@ handle_gnu_inline_attribute (tree *node, tree name, return NULL_TREE; } -/* Handle a "leaf" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_leaf_attribute (tree *node, tree name, - tree ARG_UNUSED (args), - int ARG_UNUSED (flags), bool *no_add_attrs) -{ - if (TREE_CODE (*node) != FUNCTION_DECL) - { - warning (OPT_Wattributes, "%qE attribute ignored", name); - *no_add_attrs = true; - } - if (!TREE_PUBLIC (*node)) - { - warning (OPT_Wattributes, "%qE attribute has no effect on unit local " - "functions", name); - *no_add_attrs = true; - } - - return NULL_TREE; -} - /* Handle an "artificial" attribute; arguments as in struct attribute_spec.handler. */ @@ -1939,33 +1645,6 @@ handle_error_attribute (tree *node, tree name, tree args, 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 a "unused" attribute; arguments as in struct attribute_spec.handler. */ @@ -2004,28 +1683,6 @@ handle_unused_attribute (tree *node, tree name, tree ARG_UNUSED (args), return NULL_TREE; } -/* Handle a "retain" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_retain_attribute (tree *pnode, tree name, tree ARG_UNUSED (args), - int ARG_UNUSED (flags), bool *no_add_attrs) -{ - tree node = *pnode; - - if (SUPPORTS_SHF_GNU_RETAIN - && (TREE_CODE (node) == FUNCTION_DECL - || (VAR_P (node) && TREE_STATIC (node)))) - ; - else - { - warning (OPT_Wattributes, "%qE attribute ignored", name); - *no_add_attrs = true; - } - - return NULL_TREE; -} - /* Handle an "uninitialized" attribute; arguments as in struct attribute_spec.handler. */ @@ -2104,43 +1761,6 @@ handle_no_reorder_attribute (tree *pnode, return NULL_TREE; } -/* Handle a "const" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_const_attribute (tree *node, tree name, tree ARG_UNUSED (args), - int flags, bool *no_add_attrs) -{ - tree type = TREE_TYPE (*node); - - /* See FIXME comment on noreturn in c_common_attribute_table. */ - if (TREE_CODE (*node) == FUNCTION_DECL) - TREE_READONLY (*node) = 1; - else if (TREE_CODE (type) == POINTER_TYPE - && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE) - TREE_TYPE (*node) - = (build_qualified_type - (build_pointer_type - (build_type_variant (TREE_TYPE (type), 1, - TREE_THIS_VOLATILE (TREE_TYPE (type)))), - TYPE_QUALS (type))); - else - { - warning (OPT_Wattributes, "%qE attribute ignored", name); - *no_add_attrs = true; - } - - /* void __builtin_unreachable(void) is const. Accept other such - built-ins but warn on user-defined functions that return void. */ - if (!(flags & ATTR_FLAG_BUILT_IN) - && TREE_CODE (*node) == FUNCTION_DECL - && VOID_TYPE_P (TREE_TYPE (type))) - warning (OPT_Wattributes, "%qE attribute on function " - "returning %<void%>", name); - - return NULL_TREE; -} - /* Handle a "scalar_storage_order" attribute; arguments as in struct attribute_spec.handler. */ @@ -2603,85 +2223,6 @@ handle_mode_attribute (tree *node, tree name, tree args, return NULL_TREE; } -/* Handle a "section" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_section_attribute (tree *node, tree name, tree args, - int flags, bool *no_add_attrs) -{ - tree decl = *node; - tree res = NULL_TREE; - tree argval = TREE_VALUE (args); - const char* new_section_name; - - if (!targetm_common.have_named_sections) - { - error_at (DECL_SOURCE_LOCATION (*node), - "section attributes are not supported for this target"); - goto fail; - } - - if (!VAR_OR_FUNCTION_DECL_P (decl)) - { - error ("section attribute not allowed for %q+D", *node); - goto fail; - } - - if (TREE_CODE (argval) != STRING_CST) - { - error ("section attribute argument not a string constant"); - goto fail; - } - - if (VAR_P (decl) - && current_function_decl != NULL_TREE - && !TREE_STATIC (decl)) - { - error_at (DECL_SOURCE_LOCATION (decl), - "section attribute cannot be specified for local variables"); - goto fail; - } - - new_section_name = TREE_STRING_POINTER (argval); - - /* The decl may have already been given a section attribute - from a previous declaration. Ensure they match. */ - if (const char* const old_section_name = DECL_SECTION_NAME (decl)) - if (strcmp (old_section_name, new_section_name) != 0) - { - error ("section of %q+D conflicts with previous declaration", - *node); - goto fail; - } - - if (VAR_P (decl) - && !targetm.have_tls && targetm.emutls.tmpl_section - && DECL_THREAD_LOCAL_P (decl)) - { - error ("section of %q+D cannot be overridden", *node); - goto fail; - } - - if (!validate_attr_arg (node, name, argval)) - goto fail; - - res = targetm.handle_generic_attribute (node, name, args, flags, - no_add_attrs); - - /* If the back end confirms the attribute can be added then continue onto - final processing. */ - if (!(*no_add_attrs)) - { - set_decl_section_name (decl, new_section_name); - return res; - } - -fail: - *no_add_attrs = true; - return res; -} - /* Common codes shared by handle_warn_if_not_aligned_attribute and handle_aligned_attribute. */ @@ -3027,35 +2568,6 @@ handle_counted_by_attribute (tree *node, tree name, 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 "noinit" or "persistent" attribute; arguments as in struct attribute_spec.handler. This generic handler is used for "special variable sections" that allow the @@ -3236,97 +2748,6 @@ handle_symver_attribute (tree *node, tree ARG_UNUSED (name), tree args, 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. */ @@ -3337,16 +2758,6 @@ handle_ifunc_attribute (tree *node, tree name, tree args, return handle_alias_ifunc_attribute (false, node, name, args, no_add_attrs); } -/* 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); -} - /* Handle the "copy" attribute NAME by copying the set of attributes from the symbol referenced by ARGS to the declaration of *NODE. */ @@ -3597,105 +3008,6 @@ handle_weakref_attribute (tree *node, tree name, tree args, 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 an "tls_model" attribute; arguments as in struct attribute_spec.handler. */ @@ -3845,6 +3157,12 @@ handle_malloc_attribute (tree *node, tree name, tree args, int flags, /* Recursive call. */ return NULL_TREE; + /* The argument-less form (declaring the function malloc-like) is shared + with libgccjit; the deallocator-argument form below is + specific to the C family. */ + if (!args) + return handle_malloc_common (node, name, args, flags, no_add_attrs); + tree fndecl = *node; if (TREE_CODE (*node) != FUNCTION_DECL) @@ -4209,19 +3527,6 @@ handle_argspec_attribute (tree *, tree, tree args, int, bool *) return NULL_TREE; } -/* Handle the internal-only "fn spec" attribute. */ - -static tree -handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name), - tree args, int ARG_UNUSED (flags), - bool *no_add_attrs ATTRIBUTE_UNUSED) -{ - gcc_assert (args - && TREE_CODE (TREE_VALUE (args)) == STRING_CST - && !TREE_CHAIN (args)); - return NULL_TREE; -} - /* Handle a "warn_unused" attribute; arguments as in struct attribute_spec.handler. */ @@ -4327,24 +3632,6 @@ handle_non_overlapping_attribute (tree *, tree, tree, int, bool *) return NULL_TREE; } -/* Handle a "returns_twice" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_returns_twice_attribute (tree *node, tree name, tree ARG_UNUSED (args), - int ARG_UNUSED (flags), bool *no_add_attrs) -{ - if (TREE_CODE (*node) == FUNCTION_DECL) - DECL_IS_RETURNS_TWICE (*node) = 1; - else - { - warning (OPT_Wattributes, "%qE attribute ignored", name); - *no_add_attrs = true; - } - - return NULL_TREE; -} - /* Handle a "no_limit_stack" attribute; arguments as in struct attribute_spec.handler. */ @@ -4374,32 +3661,6 @@ handle_no_limit_stack_attribute (tree *node, tree name, return NULL_TREE; } -/* Handle a "pure" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_pure_attribute (tree *node, tree name, tree ARG_UNUSED (args), - int ARG_UNUSED (flags), bool *no_add_attrs) -{ - if (TREE_CODE (*node) == FUNCTION_DECL) - { - tree type = TREE_TYPE (*node); - if (VOID_TYPE_P (TREE_TYPE (type))) - warning (OPT_Wattributes, "%qE attribute on function " - "returning %<void%>", name); - - DECL_PURE_P (*node) = 1; - /* ??? TODO: Support types. */ - } - else - { - warning (OPT_Wattributes, "%qE attribute ignored", name); - *no_add_attrs = true; - } - - return NULL_TREE; -} - /* Handle an "unsequenced" attribute; arguments as in struct attribute_spec.handler. */ @@ -4672,31 +3933,6 @@ handle_tm_wrap_attribute (tree *node, tree name, tree args, return NULL_TREE; } -/* Ignore the given attribute. Used when this attribute may be usefully - overridden by the target, but is not used generically. */ - -static tree -ignore_attribute (tree * ARG_UNUSED (node), tree ARG_UNUSED (name), - tree ARG_UNUSED (args), int ARG_UNUSED (flags), - bool *no_add_attrs) -{ - *no_add_attrs = true; - return NULL_TREE; -} - -/* Handle a "no vops" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_novops_attribute (tree *node, tree ARG_UNUSED (name), - tree ARG_UNUSED (args), int ARG_UNUSED (flags), - bool *ARG_UNUSED (no_add_attrs)) -{ - gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); - DECL_IS_NOVOPS (*node) = 1; - return NULL_TREE; -} - /* Handle a "deprecated" attribute; arguments as in struct attribute_spec.handler. */ @@ -6182,24 +5418,6 @@ build_attr_access_from_parms (tree parms, bool skip_voidptr) return tree_cons (name, attrargs, nnlist); } -/* Handle a "nothrow" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_nothrow_attribute (tree *node, tree name, tree ARG_UNUSED (args), - int ARG_UNUSED (flags), bool *no_add_attrs) -{ - if (TREE_CODE (*node) == FUNCTION_DECL) - TREE_NOTHROW (*node) = 1; - /* ??? TODO: Support types. */ - else - { - warning (OPT_Wattributes, "%qE attribute ignored", name); - *no_add_attrs = true; - } - - return NULL_TREE; -} /* Handle a "nothrow" attribute; arguments as in struct attribute_spec.handler. */ @@ -6332,54 +5550,6 @@ handle_sentinel_attribute (tree *node, tree name, tree args, return NULL_TREE; } -/* Handle a "type_generic" attribute. */ - -static tree -handle_type_generic_attribute (tree *node, tree ARG_UNUSED (name), - tree ARG_UNUSED (args), int ARG_UNUSED (flags), - bool * ARG_UNUSED (no_add_attrs)) -{ - /* Ensure we have a function type. */ - gcc_assert (TREE_CODE (*node) == FUNCTION_TYPE); - - /* Ensure we have a variadic function. */ - gcc_assert (!prototype_p (*node) || stdarg_p (*node)); - - 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 %<target%>"); - *no_add_attrs = true; - } - } - - return NULL_TREE; -} - /* Handle a "target_version" attribute. */ static tree diff --git a/gcc/jit/Make-lang.in b/gcc/jit/Make-lang.in index eb1c996606270..3c13448d9bc69 100644 --- a/gcc/jit/Make-lang.in +++ b/gcc/jit/Make-lang.in @@ -131,6 +131,7 @@ jit.serial = $(LIBGCCJIT_FILENAME) .PHONY: jit JIT_OBJS = attribs.o \ + attr-handlers.o \ jit/dummy-frontend.o \ jit/libgccjit.o \ jit/jit-logging.o \ diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc index 4df9eada65bd2..7f259897670ee 100644 --- a/gcc/jit/dummy-frontend.cc +++ b/gcc/jit/dummy-frontend.cc @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see #include "options.h" #include "stringpool.h" #include "attribs.h" +#include "attr-handlers.h" #include "cgraph.h" #include "target.h" #include "diagnostics/text-sink.h" @@ -40,109 +41,22 @@ along with GCC; see the file COPYING3. If not see using namespace gcc::jit; -/* Attribute handling. */ +/* Attribute handling. These are the libgccjit-specific attribute handlers; + the handlers shared with the C family live in gcc/attr-handlers.cc. */ -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_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_novops_attribute (tree *, tree, tree, int, bool *); static tree handle_patchable_function_entry_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 ignore_attribute (tree *, tree, tree, int, bool *); /* Helper to define attribute exclusions. */ #define ATTR_EXCL(name, function, type, variable) \ { name, function, type, variable } -/* Define attributes that are mutually exclusive with one another. */ -static const struct attribute_spec::exclusions attr_noreturn_exclusions[] = -{ - ATTR_EXCL ("alloc_align", true, true, true), - ATTR_EXCL ("alloc_size", true, true, true), - ATTR_EXCL ("const", true, true, true), - ATTR_EXCL ("malloc", true, true, true), - ATTR_EXCL ("pure", true, true, true), - ATTR_EXCL ("returns_twice", true, true, true), - ATTR_EXCL ("warn_unused_result", true, true, true), - ATTR_EXCL (NULL, false, false, false), -}; - -static const struct attribute_spec::exclusions attr_returns_twice_exclusions[] = -{ - ATTR_EXCL ("noreturn", true, true, true), - 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), -}; - /* 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. */ @@ -174,7 +88,7 @@ static const attribute_spec jit_gnu_attributes[] = { "leaf", 0, 0, true, false, false, false, handle_leaf_attribute, NULL }, { "malloc", 0, 0, true, false, false, false, - handle_malloc_attribute, attr_alloc_exclusions }, + handle_malloc_common, attr_alloc_exclusions }, { "noreturn", 0, 0, true, false, false, false, handle_noreturn_attribute, attr_noreturn_exclusions }, @@ -247,6 +161,11 @@ static const scoped_attribute_specs *const jit_attribute_table[] = /* Attribute handlers. */ +/* Attribute handlers specific to libgccjit. If the attribute handler code + is the same as the one in `c-family/c-attribs.cc`, please move the + handler into `attr-handlers.cc`. */ + + /* Handle a "noreturn" attribute; arguments as in struct attribute_spec.handler. */ @@ -271,107 +190,6 @@ handle_noreturn_attribute (tree *node, tree ARG_UNUSED (name), return NULL_TREE; } -/* Handle a "leaf" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_leaf_attribute (tree *node, tree name, - tree ARG_UNUSED (args), - int ARG_UNUSED (flags), bool *no_add_attrs) -{ - if (TREE_CODE (*node) != FUNCTION_DECL) - { - warning (OPT_Wattributes, "%qE attribute ignored", name); - *no_add_attrs = true; - } - if (!TREE_PUBLIC (*node)) - { - warning (OPT_Wattributes, "%qE attribute has no effect on unit local functions", name); - *no_add_attrs = true; - } - - return NULL_TREE; -} - -/* Handle a "const" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_const_attribute (tree *node, tree name, tree ARG_UNUSED (args), - int ARG_UNUSED (flags), bool *no_add_attrs) -{ - tree type = TREE_TYPE (*node); - - /* See FIXME comment on noreturn in c_common_attribute_table. */ - if (TREE_CODE (*node) == FUNCTION_DECL) - TREE_READONLY (*node) = 1; - else if (TREE_CODE (type) == POINTER_TYPE - && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE) - TREE_TYPE (*node) - = (build_qualified_type - (build_pointer_type - (build_type_variant (TREE_TYPE (type), 1, - TREE_THIS_VOLATILE (TREE_TYPE (type)))), - TYPE_QUALS (type))); - else - { - warning (OPT_Wattributes, "%qE attribute ignored", name); - *no_add_attrs = true; - } - - return NULL_TREE; -} - - -/* Handle a "malloc" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_malloc_attribute (tree *node, tree ARG_UNUSED (name), - tree ARG_UNUSED (args), int ARG_UNUSED (flags), - bool * ARG_UNUSED (no_add_attrs)) -{ - if (TREE_CODE (*node) == FUNCTION_DECL - && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (*node)))) - DECL_IS_MALLOC (*node) = 1; - else - gcc_unreachable (); - - return NULL_TREE; -} - - -/* Handle a "pure" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_pure_attribute (tree *node, tree ARG_UNUSED (name), - tree ARG_UNUSED (args), int ARG_UNUSED (flags), - bool * ARG_UNUSED (no_add_attrs)) -{ - if (TREE_CODE (*node) == FUNCTION_DECL) - DECL_PURE_P (*node) = 1; - else - gcc_unreachable (); - - return NULL_TREE; -} - - -/* Handle a "no vops" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_novops_attribute (tree *node, tree ARG_UNUSED (name), - tree ARG_UNUSED (args), int ARG_UNUSED (flags), - bool *ARG_UNUSED (no_add_attrs)) -{ - gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); - DECL_IS_NOVOPS (*node) = 1; - return NULL_TREE; -} - - /* Helper for nonnull attribute handling; fetch the operand number from the attribute argument list. */ @@ -437,24 +255,6 @@ handle_nonnull_attribute (tree *node, tree ARG_UNUSED (name), return NULL_TREE; } - -/* Handle a "nothrow" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_nothrow_attribute (tree *node, tree ARG_UNUSED (name), - tree ARG_UNUSED (args), int ARG_UNUSED (flags), - bool * ARG_UNUSED (no_add_attrs)) -{ - if (TREE_CODE (*node) == FUNCTION_DECL) - TREE_NOTHROW (*node) = 1; - else - gcc_unreachable (); - - return NULL_TREE; -} - - /* Handle a "sentinel" attribute. */ static tree @@ -475,233 +275,6 @@ handle_sentinel_attribute (tree *node, tree ARG_UNUSED (name), tree args, return NULL_TREE; } -/* Handle a "type_generic" attribute. */ - -static tree -handle_type_generic_attribute (tree *node, tree ARG_UNUSED (name), - tree ARG_UNUSED (args), int ARG_UNUSED (flags), - bool * ARG_UNUSED (no_add_attrs)) -{ - /* Ensure we have a function type. */ - gcc_assert (TREE_CODE (*node) == FUNCTION_TYPE); - - /* Ensure we have a variadic function. */ - gcc_assert (!prototype_p (*node) || stdarg_p (*node)); - - return NULL_TREE; -} - -/* Handle a "transaction_pure" attribute. */ - -static tree -handle_transaction_pure_attribute (tree *node, tree ARG_UNUSED (name), - tree ARG_UNUSED (args), - int ARG_UNUSED (flags), - bool * ARG_UNUSED (no_add_attrs)) -{ - /* Ensure we have a function type. */ - gcc_assert (TREE_CODE (*node) == FUNCTION_TYPE); - - return NULL_TREE; -} - -/* Handle a "returns_twice" attribute. */ - -static tree -handle_returns_twice_attribute (tree *node, tree ARG_UNUSED (name), - tree ARG_UNUSED (args), - int ARG_UNUSED (flags), - bool * ARG_UNUSED (no_add_attrs)) -{ - gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); - - DECL_IS_RETURNS_TWICE (*node) = 1; - - return NULL_TREE; -} - -static tree -handle_patchable_function_entry_attribute (tree *, tree, tree, int, bool *) -{ - /* Nothing to be done here. */ - return NULL_TREE; -} - -/* Ignore the given attribute. Used when this attribute may be usefully - overridden by the target, but is not used generically. */ - -static tree -ignore_attribute (tree * ARG_UNUSED (node), tree ARG_UNUSED (name), - tree ARG_UNUSED (args), int ARG_UNUSED (flags), - bool *no_add_attrs) -{ - *no_add_attrs = true; - return NULL_TREE; -} - -/* Handle a "format" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_format_attribute (tree * ARG_UNUSED (node), tree ARG_UNUSED (name), - tree ARG_UNUSED (args), int ARG_UNUSED (flags), - bool *no_add_attrs) -{ - *no_add_attrs = true; - return NULL_TREE; -} - - -/* Handle a "format_arg" attribute; arguments as in - struct attribute_spec.handler. */ - -tree -handle_format_arg_attribute (tree * ARG_UNUSED (node), tree ARG_UNUSED (name), - tree ARG_UNUSED (args), int ARG_UNUSED (flags), - bool *no_add_attrs) -{ - *no_add_attrs = true; - return NULL_TREE; -} - - -/* Handle a "fn spec" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name), - tree args, int ARG_UNUSED (flags), - bool *no_add_attrs ATTRIBUTE_UNUSED) -{ - gcc_assert (args - && TREE_CODE (TREE_VALUE (args)) == STRING_CST - && !TREE_CHAIN (args)); - 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. */ @@ -723,211 +296,36 @@ handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args), 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) +handle_patchable_function_entry_attribute (tree *, tree, tree, int, bool *) { - if (TREE_CODE (*node) == FUNCTION_DECL) - DECL_UNINLINABLE (*node) = 1; - else - { - warning (OPT_Wattributes, "%qE attribute ignored", name); - *no_add_attrs = true; - } - + /* Nothing to be done here. */ return NULL_TREE; } -/* Handle a "weak" attribute; arguments as in +/* Handle a "format" 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, +handle_format_attribute (tree * ARG_UNUSED (node), tree ARG_UNUSED (name), + tree ARG_UNUSED (args), int ARG_UNUSED (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 %<target%>"); - *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; - } - + *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 +/* Handle a "format_arg" 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) +tree +handle_format_arg_attribute (tree * ARG_UNUSED (node), tree ARG_UNUSED (name), + tree ARG_UNUSED (args), int ARG_UNUSED (flags), + bool *no_add_attrs) { - return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs); + *no_add_attrs = true; + return NULL_TREE; } /* (end of attribute-handling). */ -- 2.54.0