X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gcc%2Fattribs.c;h=a6f6b70e39e199a9b08e943bb9598bf4d5b15da7;hb=8441545d4f2afb9e9342e0dac378eafd03f00462;hp=affb21d8578ae679b15eef82b18defaacb0f8d76;hpb=3b1661a9b93fe8000faa6ab4b721a96ffb48d525;p=gcc.git diff --git a/gcc/attribs.c b/gcc/attribs.c index affb21d8578..a6f6b70e39e 100644 --- a/gcc/attribs.c +++ b/gcc/attribs.c @@ -1,5 +1,5 @@ /* Functions dealing with attribute handling, used by most front ends. - Copyright (C) 1992-2015 Free Software Foundation, Inc. + Copyright (C) 1992-2020 Free Software Foundation, Inc. This file is part of GCC. @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ +#define INCLUDE_STRING #include "config.h" #include "system.h" #include "coretypes.h" @@ -25,9 +26,16 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "diagnostic-core.h" #include "attribs.h" +#include "fold-const.h" #include "stor-layout.h" #include "langhooks.h" #include "plugin.h" +#include "selftest.h" +#include "hash-set.h" +#include "diagnostic.h" +#include "pretty-print.h" +#include "tree-pretty-print.h" +#include "intl.h" /* Table of the tables of attributes (common, language, format, machine) searched. */ @@ -94,7 +102,7 @@ static bool attributes_initialized = false; static const struct attribute_spec empty_attribute_table[] = { - { NULL, 0, 0, false, false, false, NULL, false } + { NULL, 0, 0, false, false, false, false, NULL, NULL } }; /* Return base name of the attribute. Ie '__attr__' is turned into 'attr'. @@ -116,9 +124,9 @@ extract_attribute_substring (struct substring *str) namespace. The function returns the namespace into which the attributes have been registered. */ -scoped_attributes* -register_scoped_attributes (const struct attribute_spec * attributes, - const char* ns) +scoped_attributes * +register_scoped_attributes (const struct attribute_spec *attributes, + const char *ns) { scoped_attributes *result = NULL; @@ -130,7 +138,7 @@ register_scoped_attributes (const struct attribute_spec * attributes, /* We don't have any namespace NS yet. Create one. */ scoped_attributes sa; - if (!attributes_table.is_empty ()) + if (attributes_table.is_empty ()) attributes_table.create (64); memset (&sa, 0, sizeof (sa)); @@ -335,7 +343,7 @@ lookup_attribute_spec (const_tree name) Please read the comments of cxx11_attribute_p to understand the format of attributes. */ -static tree +tree get_attribute_namespace (const_tree attr) { if (cxx11_attribute_p (attr)) @@ -343,6 +351,113 @@ get_attribute_namespace (const_tree attr) return get_identifier ("gnu"); } +/* Check LAST_DECL and NODE of the same symbol for attributes that are + recorded in SPEC to be mutually exclusive with ATTRNAME, diagnose + them, and return true if any have been found. NODE can be a DECL + or a TYPE. */ + +static bool +diag_attr_exclusions (tree last_decl, tree node, tree attrname, + const attribute_spec *spec) +{ + const attribute_spec::exclusions *excl = spec->exclude; + + tree_code code = TREE_CODE (node); + + if ((code == FUNCTION_DECL && !excl->function + && (!excl->type || !spec->affects_type_identity)) + || (code == VAR_DECL && !excl->variable + && (!excl->type || !spec->affects_type_identity)) + || (((code == TYPE_DECL || RECORD_OR_UNION_TYPE_P (node)) && !excl->type))) + return false; + + /* True if an attribute that's mutually exclusive with ATTRNAME + has been found. */ + bool found = false; + + if (last_decl && last_decl != node && TREE_TYPE (last_decl) != node) + { + /* Check both the last DECL and its type for conflicts with + the attribute being added to the current decl or type. */ + found |= diag_attr_exclusions (last_decl, last_decl, attrname, spec); + tree decl_type = TREE_TYPE (last_decl); + found |= diag_attr_exclusions (last_decl, decl_type, attrname, spec); + } + + /* NODE is either the current DECL to which the attribute is being + applied or its TYPE. For the former, consider the attributes on + both the DECL and its type. */ + tree attrs[2]; + + if (DECL_P (node)) + { + attrs[0] = DECL_ATTRIBUTES (node); + attrs[1] = TYPE_ATTRIBUTES (TREE_TYPE (node)); + } + else + { + attrs[0] = TYPE_ATTRIBUTES (node); + attrs[1] = NULL_TREE; + } + + /* Iterate over the mutually exclusive attribute names and verify + that the symbol doesn't contain it. */ + for (unsigned i = 0; i != sizeof attrs / sizeof *attrs; ++i) + { + if (!attrs[i]) + continue; + + for ( ; excl->name; ++excl) + { + /* Avoid checking the attribute against itself. */ + if (is_attribute_p (excl->name, attrname)) + continue; + + if (!lookup_attribute (excl->name, attrs[i])) + continue; + + /* An exclusion may apply either to a function declaration, + type declaration, or a field/variable declaration, or + any subset of the three. */ + if (TREE_CODE (node) == FUNCTION_DECL + && !excl->function) + continue; + + if (TREE_CODE (node) == TYPE_DECL + && !excl->type) + continue; + + if ((TREE_CODE (node) == FIELD_DECL + || TREE_CODE (node) == VAR_DECL) + && !excl->variable) + continue; + + found = true; + + /* Print a note? */ + bool note = last_decl != NULL_TREE; + auto_diagnostic_group d; + if (TREE_CODE (node) == FUNCTION_DECL + && fndecl_built_in_p (node)) + note &= warning (OPT_Wattributes, + "ignoring attribute %qE in declaration of " + "a built-in function %qD because it conflicts " + "with attribute %qs", + attrname, node, excl->name); + else + note &= warning (OPT_Wattributes, + "ignoring attribute %qE because " + "it conflicts with attribute %qs", + attrname, excl->name); + + if (note) + inform (DECL_SOURCE_LOCATION (last_decl), + "previous declaration here"); + } + } + + return found; +} /* Process the attributes listed in ATTRIBUTES and install them in *NODE, which is either a DECL (including a TYPE_DECL) or a TYPE. If a DECL, @@ -354,9 +469,9 @@ get_attribute_namespace (const_tree attr) a decl attribute to the declaration rather than to its type). */ tree -decl_attributes (tree *node, tree attributes, int flags) +decl_attributes (tree *node, tree attributes, int flags, + tree last_decl /* = NULL_TREE */) { - tree a; tree returned_attrs = NULL_TREE; if (TREE_TYPE (*node) == error_mark_node || attributes == error_mark_node) @@ -404,35 +519,54 @@ decl_attributes (tree *node, tree attributes, int flags) those targets that support it. */ if (TREE_CODE (*node) == FUNCTION_DECL && attributes - && lookup_attribute_spec (get_identifier ("naked")) - && lookup_attribute ("naked", attributes) != NULL) + && lookup_attribute ("naked", attributes) != NULL + && lookup_attribute_spec (get_identifier ("naked"))) + { + if (lookup_attribute ("noinline", attributes) == NULL) + attributes = tree_cons (get_identifier ("noinline"), NULL, attributes); + + if (lookup_attribute ("noclone", attributes) == NULL) + attributes = tree_cons (get_identifier ("noclone"), NULL, attributes); + } + + /* A "noipa" function attribute implies "noinline", "noclone" and "no_icf" + for those targets that support it. */ + if (TREE_CODE (*node) == FUNCTION_DECL + && attributes + && lookup_attribute ("noipa", attributes) != NULL + && lookup_attribute_spec (get_identifier ("noipa"))) { if (lookup_attribute ("noinline", attributes) == NULL) attributes = tree_cons (get_identifier ("noinline"), NULL, attributes); if (lookup_attribute ("noclone", attributes) == NULL) attributes = tree_cons (get_identifier ("noclone"), NULL, attributes); + + if (lookup_attribute ("no_icf", attributes) == NULL) + attributes = tree_cons (get_identifier ("no_icf"), NULL, attributes); } targetm.insert_attributes (*node, &attributes); - for (a = attributes; a; a = TREE_CHAIN (a)) + /* Note that attributes on the same declaration are not necessarily + in the same order as in the source. */ + for (tree attr = attributes; attr; attr = TREE_CHAIN (attr)) { - tree ns = get_attribute_namespace (a); - tree name = get_attribute_name (a); - tree args = TREE_VALUE (a); + tree ns = get_attribute_namespace (attr); + tree name = get_attribute_name (attr); + tree args = TREE_VALUE (attr); tree *anode = node; - const struct attribute_spec *spec = - lookup_scoped_attribute_spec (ns, name); - bool no_add_attrs = 0; + const struct attribute_spec *spec + = lookup_scoped_attribute_spec (ns, name); int fn_ptr_quals = 0; tree fn_ptr_tmp = NULL_TREE; + const bool cxx11_attr_p = cxx11_attribute_p (attr); if (spec == NULL) { if (!(flags & (int) ATTR_FLAG_BUILT_IN)) { - if (ns == NULL_TREE || !cxx11_attribute_p (a)) + if (ns == NULL_TREE || !cxx11_attr_p) warning (OPT_Wattributes, "%qE attribute directive ignored", name); else @@ -442,30 +576,26 @@ decl_attributes (tree *node, tree attributes, int flags) } continue; } - else if (list_length (args) < spec->min_length - || (spec->max_length >= 0 - && list_length (args) > spec->max_length)) + else { - error ("wrong number of arguments specified for %qE attribute", - name); - continue; + int nargs = list_length (args); + if (nargs < spec->min_length + || (spec->max_length >= 0 + && nargs > spec->max_length)) + { + error ("wrong number of arguments specified for %qE attribute", + name); + if (spec->max_length < 0) + inform (input_location, "expected %i or more, found %i", + spec->min_length, nargs); + else + inform (input_location, "expected between %i and %i, found %i", + spec->min_length, spec->max_length, nargs); + continue; + } } gcc_assert (is_attribute_p (spec->name, name)); - if (TYPE_P (*node) - && cxx11_attribute_p (a) - && !(flags & ATTR_FLAG_TYPE_IN_PLACE)) - { - /* This is a c++11 attribute that appertains to a - type-specifier, outside of the definition of, a class - type. Ignore it. */ - if (warning (OPT_Wattributes, "attribute ignored")) - inform (input_location, - "an attribute that appertains to a type-specifier " - "is ignored"); - continue; - } - if (spec->decl_required && !DECL_P (*anode)) { if (flags & ((int) ATTR_FLAG_DECL_NEXT @@ -473,7 +603,8 @@ decl_attributes (tree *node, tree attributes, int flags) | (int) ATTR_FLAG_ARRAY_NEXT)) { /* Pass on this attribute to be tried again. */ - returned_attrs = tree_cons (name, args, returned_attrs); + tree attr = tree_cons (name, args, NULL_TREE); + returned_attrs = chainon (returned_attrs, attr); continue; } else @@ -518,7 +649,8 @@ decl_attributes (tree *node, tree attributes, int flags) else if (flags & (int) ATTR_FLAG_FUNCTION_NEXT) { /* Pass on this attribute to be tried again. */ - returned_attrs = tree_cons (name, args, returned_attrs); + tree attr = tree_cons (name, args, NULL_TREE); + returned_attrs = chainon (returned_attrs, attr); continue; } @@ -540,20 +672,70 @@ decl_attributes (tree *node, tree attributes, int flags) continue; } - if (spec->handler != NULL) + bool no_add_attrs = false; + + /* Check for exclusions with other attributes on the current + declation as well as the last declaration of the same + symbol already processed (if one exists). Detect and + reject incompatible attributes. */ + bool built_in = flags & ATTR_FLAG_BUILT_IN; + if (spec->exclude + && (flag_checking || !built_in) + && !error_operand_p (last_decl)) { - int cxx11_flag = - cxx11_attribute_p (a) ? ATTR_FLAG_CXX11 : 0; + /* Always check attributes on user-defined functions. + Check them on built-ins only when -fchecking is set. + Ignore __builtin_unreachable -- it's both const and + noreturn. */ + + if (!built_in + || !DECL_P (*anode) + || DECL_BUILT_IN_CLASS (*anode) != BUILT_IN_NORMAL + || (DECL_FUNCTION_CODE (*anode) != BUILT_IN_UNREACHABLE + && (DECL_FUNCTION_CODE (*anode) + != BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE))) + { + bool no_add = diag_attr_exclusions (last_decl, *anode, name, spec); + if (!no_add && anode != node) + no_add = diag_attr_exclusions (last_decl, *node, name, spec); + no_add_attrs |= no_add; + } + } + + if (no_add_attrs) + continue; - returned_attrs = chainon ((*spec->handler) (anode, name, args, - flags|cxx11_flag, - &no_add_attrs), - returned_attrs); + if (spec->handler != NULL) + { + int cxx11_flag = (cxx11_attr_p ? ATTR_FLAG_CXX11 : 0); + + /* Pass in an array of the current declaration followed + by the last pushed/merged declaration if one exists. + For calls that modify the type attributes of a DECL + and for which *ANODE is *NODE's type, also pass in + the DECL as the third element to use in diagnostics. + If the handler changes CUR_AND_LAST_DECL[0] replace + *ANODE with its value. */ + tree cur_and_last_decl[3] = { *anode, last_decl }; + if (anode != node && DECL_P (*node)) + cur_and_last_decl[2] = *node; + + tree ret = (spec->handler) (cur_and_last_decl, name, args, + flags|cxx11_flag, &no_add_attrs); + + *anode = cur_and_last_decl[0]; + if (ret == error_mark_node) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + no_add_attrs = true; + } + else + returned_attrs = chainon (ret, returned_attrs); } /* Layout the decl in case anything changed. */ if (spec->type_required && DECL_P (*node) - && (TREE_CODE (*node) == VAR_DECL + && (VAR_P (*node) || TREE_CODE (*node) == PARM_DECL || TREE_CODE (*node) == RESULT_DECL)) relayout_decl (*node); @@ -579,17 +761,23 @@ decl_attributes (tree *node, tree attributes, int flags) if (a == NULL_TREE) { /* This attribute isn't already in the list. */ + tree r; + /* Preserve the C++11 form. */ + if (cxx11_attr_p) + r = tree_cons (build_tree_list (ns, name), args, old_attrs); + else + r = tree_cons (name, args, old_attrs); + if (DECL_P (*anode)) - DECL_ATTRIBUTES (*anode) = tree_cons (name, args, old_attrs); + DECL_ATTRIBUTES (*anode) = r; else if (flags & (int) ATTR_FLAG_TYPE_IN_PLACE) { - TYPE_ATTRIBUTES (*anode) = tree_cons (name, args, old_attrs); + TYPE_ATTRIBUTES (*anode) = r; /* If this is the main variant, also push the attributes out to the other variants. */ if (*anode == TYPE_MAIN_VARIANT (*anode)) { - tree variant; - for (variant = *anode; variant; + for (tree variant = *anode; variant; variant = TYPE_NEXT_VARIANT (variant)) { if (TYPE_ATTRIBUTES (variant) == old_attrs) @@ -603,9 +791,7 @@ decl_attributes (tree *node, tree attributes, int flags) } } else - *anode = build_type_attribute_variant (*anode, - tree_cons (name, args, - old_attrs)); + *anode = build_type_attribute_variant (*anode, r); } } @@ -690,3 +876,1559 @@ make_attribute (const char *name, const char *arg_name, tree chain) attr = tree_cons (attr_name, attr_args, chain); return attr; } + + +/* Common functions used for target clone support. */ + +/* Comparator function to be used in qsort routine to sort attribute + specification strings to "target". */ + +static int +attr_strcmp (const void *v1, const void *v2) +{ + const char *c1 = *(char *const*)v1; + const char *c2 = *(char *const*)v2; + return strcmp (c1, c2); +} + +/* ARGLIST is the argument to target attribute. This function tokenizes + the comma separated arguments, sorts them and returns a string which + is a unique identifier for the comma separated arguments. It also + replaces non-identifier characters "=,-" with "_". */ + +char * +sorted_attr_string (tree arglist) +{ + tree arg; + size_t str_len_sum = 0; + char **args = NULL; + char *attr_str, *ret_str; + char *attr = NULL; + unsigned int argnum = 1; + unsigned int i; + + for (arg = arglist; arg; arg = TREE_CHAIN (arg)) + { + const char *str = TREE_STRING_POINTER (TREE_VALUE (arg)); + size_t len = strlen (str); + str_len_sum += len + 1; + if (arg != arglist) + argnum++; + for (i = 0; i < strlen (str); i++) + if (str[i] == ',') + argnum++; + } + + attr_str = XNEWVEC (char, str_len_sum); + str_len_sum = 0; + for (arg = arglist; arg; arg = TREE_CHAIN (arg)) + { + const char *str = TREE_STRING_POINTER (TREE_VALUE (arg)); + size_t len = strlen (str); + memcpy (attr_str + str_len_sum, str, len); + attr_str[str_len_sum + len] = TREE_CHAIN (arg) ? ',' : '\0'; + str_len_sum += len + 1; + } + + /* Replace "=,-" with "_". */ + for (i = 0; i < strlen (attr_str); i++) + if (attr_str[i] == '=' || attr_str[i]== '-') + attr_str[i] = '_'; + + if (argnum == 1) + return attr_str; + + args = XNEWVEC (char *, argnum); + + i = 0; + attr = strtok (attr_str, ","); + while (attr != NULL) + { + args[i] = attr; + i++; + attr = strtok (NULL, ","); + } + + qsort (args, argnum, sizeof (char *), attr_strcmp); + + ret_str = XNEWVEC (char, str_len_sum); + str_len_sum = 0; + for (i = 0; i < argnum; i++) + { + size_t len = strlen (args[i]); + memcpy (ret_str + str_len_sum, args[i], len); + ret_str[str_len_sum + len] = i < argnum - 1 ? '_' : '\0'; + str_len_sum += len + 1; + } + + XDELETEVEC (args); + XDELETEVEC (attr_str); + return ret_str; +} + + +/* This function returns true if FN1 and FN2 are versions of the same function, + that is, the target strings of the function decls are different. This assumes + that FN1 and FN2 have the same signature. */ + +bool +common_function_versions (tree fn1, tree fn2) +{ + tree attr1, attr2; + char *target1, *target2; + bool result; + + if (TREE_CODE (fn1) != FUNCTION_DECL + || TREE_CODE (fn2) != FUNCTION_DECL) + return false; + + attr1 = lookup_attribute ("target", DECL_ATTRIBUTES (fn1)); + attr2 = lookup_attribute ("target", DECL_ATTRIBUTES (fn2)); + + /* At least one function decl should have the target attribute specified. */ + if (attr1 == NULL_TREE && attr2 == NULL_TREE) + return false; + + /* Diagnose missing target attribute if one of the decls is already + multi-versioned. */ + if (attr1 == NULL_TREE || attr2 == NULL_TREE) + { + if (DECL_FUNCTION_VERSIONED (fn1) || DECL_FUNCTION_VERSIONED (fn2)) + { + if (attr2 != NULL_TREE) + { + std::swap (fn1, fn2); + attr1 = attr2; + } + error_at (DECL_SOURCE_LOCATION (fn2), + "missing % attribute for multi-versioned %qD", + fn2); + inform (DECL_SOURCE_LOCATION (fn1), + "previous declaration of %qD", fn1); + /* Prevent diagnosing of the same error multiple times. */ + DECL_ATTRIBUTES (fn2) + = tree_cons (get_identifier ("target"), + copy_node (TREE_VALUE (attr1)), + DECL_ATTRIBUTES (fn2)); + } + return false; + } + + target1 = sorted_attr_string (TREE_VALUE (attr1)); + target2 = sorted_attr_string (TREE_VALUE (attr2)); + + /* The sorted target strings must be different for fn1 and fn2 + to be versions. */ + if (strcmp (target1, target2) == 0) + result = false; + else + result = true; + + XDELETEVEC (target1); + XDELETEVEC (target2); + + return result; +} + +/* Return a new name by appending SUFFIX to the DECL name. If make_unique + is true, append the full path name of the source file. */ + +char * +make_unique_name (tree decl, const char *suffix, bool make_unique) +{ + char *global_var_name; + int name_len; + const char *name; + const char *unique_name = NULL; + + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + + /* Get a unique name that can be used globally without any chances + of collision at link time. */ + if (make_unique) + unique_name = IDENTIFIER_POINTER (get_file_function_name ("\0")); + + name_len = strlen (name) + strlen (suffix) + 2; + + if (make_unique) + name_len += strlen (unique_name) + 1; + global_var_name = XNEWVEC (char, name_len); + + /* Use '.' to concatenate names as it is demangler friendly. */ + if (make_unique) + snprintf (global_var_name, name_len, "%s.%s.%s", name, unique_name, + suffix); + else + snprintf (global_var_name, name_len, "%s.%s", name, suffix); + + return global_var_name; +} + +/* Make a dispatcher declaration for the multi-versioned function DECL. + Calls to DECL function will be replaced with calls to the dispatcher + by the front-end. Return the decl created. */ + +tree +make_dispatcher_decl (const tree decl) +{ + tree func_decl; + char *func_name; + tree fn_type, func_type; + + func_name = xstrdup (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); + + fn_type = TREE_TYPE (decl); + func_type = build_function_type (TREE_TYPE (fn_type), + TYPE_ARG_TYPES (fn_type)); + + func_decl = build_fn_decl (func_name, func_type); + XDELETEVEC (func_name); + TREE_USED (func_decl) = 1; + DECL_CONTEXT (func_decl) = NULL_TREE; + DECL_INITIAL (func_decl) = error_mark_node; + DECL_ARTIFICIAL (func_decl) = 1; + /* Mark this func as external, the resolver will flip it again if + it gets generated. */ + DECL_EXTERNAL (func_decl) = 1; + /* This will be of type IFUNCs have to be externally visible. */ + TREE_PUBLIC (func_decl) = 1; + + return func_decl; +} + +/* Returns true if decl is multi-versioned and DECL is the default function, + that is it is not tagged with target specific optimization. */ + +bool +is_function_default_version (const tree decl) +{ + if (TREE_CODE (decl) != FUNCTION_DECL + || !DECL_FUNCTION_VERSIONED (decl)) + return false; + tree attr = lookup_attribute ("target", DECL_ATTRIBUTES (decl)); + gcc_assert (attr); + attr = TREE_VALUE (TREE_VALUE (attr)); + return (TREE_CODE (attr) == STRING_CST + && strcmp (TREE_STRING_POINTER (attr), "default") == 0); +} + +/* Return a declaration like DDECL except that its DECL_ATTRIBUTES + is ATTRIBUTE. */ + +tree +build_decl_attribute_variant (tree ddecl, tree attribute) +{ + DECL_ATTRIBUTES (ddecl) = attribute; + return ddecl; +} + +/* Return a type like TTYPE except that its TYPE_ATTRIBUTE + is ATTRIBUTE and its qualifiers are QUALS. + + Record such modified types already made so we don't make duplicates. */ + +tree +build_type_attribute_qual_variant (tree otype, tree attribute, int quals) +{ + tree ttype = otype; + if (! attribute_list_equal (TYPE_ATTRIBUTES (ttype), attribute)) + { + tree ntype; + + /* Building a distinct copy of a tagged type is inappropriate; it + causes breakage in code that expects there to be a one-to-one + relationship between a struct and its fields. + build_duplicate_type is another solution (as used in + handle_transparent_union_attribute), but that doesn't play well + with the stronger C++ type identity model. */ + if (TREE_CODE (ttype) == RECORD_TYPE + || TREE_CODE (ttype) == UNION_TYPE + || TREE_CODE (ttype) == QUAL_UNION_TYPE + || TREE_CODE (ttype) == ENUMERAL_TYPE) + { + warning (OPT_Wattributes, + "ignoring attributes applied to %qT after definition", + TYPE_MAIN_VARIANT (ttype)); + return build_qualified_type (ttype, quals); + } + + ttype = build_qualified_type (ttype, TYPE_UNQUALIFIED); + if (lang_hooks.types.copy_lang_qualifiers + && otype != TYPE_MAIN_VARIANT (otype)) + ttype = (lang_hooks.types.copy_lang_qualifiers + (ttype, TYPE_MAIN_VARIANT (otype))); + + tree dtype = ntype = build_distinct_type_copy (ttype); + + TYPE_ATTRIBUTES (ntype) = attribute; + + hashval_t hash = type_hash_canon_hash (ntype); + ntype = type_hash_canon (hash, ntype); + + if (ntype != dtype) + /* This variant was already in the hash table, don't mess with + TYPE_CANONICAL. */; + else if (TYPE_STRUCTURAL_EQUALITY_P (ttype) + || !comp_type_attributes (ntype, ttype)) + /* If the target-dependent attributes make NTYPE different from + its canonical type, we will need to use structural equality + checks for this type. + + We shouldn't get here for stripping attributes from a type; + the no-attribute type might not need structural comparison. But + we can if was discarded from type_hash_table. */ + SET_TYPE_STRUCTURAL_EQUALITY (ntype); + else if (TYPE_CANONICAL (ntype) == ntype) + TYPE_CANONICAL (ntype) = TYPE_CANONICAL (ttype); + + ttype = build_qualified_type (ntype, quals); + if (lang_hooks.types.copy_lang_qualifiers + && otype != TYPE_MAIN_VARIANT (otype)) + ttype = lang_hooks.types.copy_lang_qualifiers (ttype, otype); + } + else if (TYPE_QUALS (ttype) != quals) + ttype = build_qualified_type (ttype, quals); + + return ttype; +} + +/* Compare two identifier nodes representing attributes. + Return true if they are the same, false otherwise. */ + +static bool +cmp_attrib_identifiers (const_tree attr1, const_tree attr2) +{ + /* Make sure we're dealing with IDENTIFIER_NODEs. */ + gcc_checking_assert (TREE_CODE (attr1) == IDENTIFIER_NODE + && TREE_CODE (attr2) == IDENTIFIER_NODE); + + /* Identifiers can be compared directly for equality. */ + if (attr1 == attr2) + return true; + + return cmp_attribs (IDENTIFIER_POINTER (attr1), IDENTIFIER_LENGTH (attr1), + IDENTIFIER_POINTER (attr2), IDENTIFIER_LENGTH (attr2)); +} + +/* Compare two constructor-element-type constants. Return 1 if the lists + are known to be equal; otherwise return 0. */ + +static bool +simple_cst_list_equal (const_tree l1, const_tree l2) +{ + while (l1 != NULL_TREE && l2 != NULL_TREE) + { + if (simple_cst_equal (TREE_VALUE (l1), TREE_VALUE (l2)) != 1) + return false; + + l1 = TREE_CHAIN (l1); + l2 = TREE_CHAIN (l2); + } + + return l1 == l2; +} + +/* Check if "omp declare simd" attribute arguments, CLAUSES1 and CLAUSES2, are + the same. */ + +static bool +omp_declare_simd_clauses_equal (tree clauses1, tree clauses2) +{ + tree cl1, cl2; + for (cl1 = clauses1, cl2 = clauses2; + cl1 && cl2; + cl1 = OMP_CLAUSE_CHAIN (cl1), cl2 = OMP_CLAUSE_CHAIN (cl2)) + { + if (OMP_CLAUSE_CODE (cl1) != OMP_CLAUSE_CODE (cl2)) + return false; + if (OMP_CLAUSE_CODE (cl1) != OMP_CLAUSE_SIMDLEN) + { + if (simple_cst_equal (OMP_CLAUSE_DECL (cl1), + OMP_CLAUSE_DECL (cl2)) != 1) + return false; + } + switch (OMP_CLAUSE_CODE (cl1)) + { + case OMP_CLAUSE_ALIGNED: + if (simple_cst_equal (OMP_CLAUSE_ALIGNED_ALIGNMENT (cl1), + OMP_CLAUSE_ALIGNED_ALIGNMENT (cl2)) != 1) + return false; + break; + case OMP_CLAUSE_LINEAR: + if (simple_cst_equal (OMP_CLAUSE_LINEAR_STEP (cl1), + OMP_CLAUSE_LINEAR_STEP (cl2)) != 1) + return false; + break; + case OMP_CLAUSE_SIMDLEN: + if (simple_cst_equal (OMP_CLAUSE_SIMDLEN_EXPR (cl1), + OMP_CLAUSE_SIMDLEN_EXPR (cl2)) != 1) + return false; + default: + break; + } + } + return true; +} + + +/* Compare two attributes for their value identity. Return true if the + attribute values are known to be equal; otherwise return false. */ + +bool +attribute_value_equal (const_tree attr1, const_tree attr2) +{ + if (TREE_VALUE (attr1) == TREE_VALUE (attr2)) + return true; + + if (TREE_VALUE (attr1) != NULL_TREE + && TREE_CODE (TREE_VALUE (attr1)) == TREE_LIST + && TREE_VALUE (attr2) != NULL_TREE + && TREE_CODE (TREE_VALUE (attr2)) == TREE_LIST) + { + /* Handle attribute format. */ + if (is_attribute_p ("format", get_attribute_name (attr1))) + { + attr1 = TREE_VALUE (attr1); + attr2 = TREE_VALUE (attr2); + /* Compare the archetypes (printf/scanf/strftime/...). */ + if (!cmp_attrib_identifiers (TREE_VALUE (attr1), TREE_VALUE (attr2))) + return false; + /* Archetypes are the same. Compare the rest. */ + return (simple_cst_list_equal (TREE_CHAIN (attr1), + TREE_CHAIN (attr2)) == 1); + } + return (simple_cst_list_equal (TREE_VALUE (attr1), + TREE_VALUE (attr2)) == 1); + } + + if (TREE_VALUE (attr1) + && TREE_CODE (TREE_VALUE (attr1)) == OMP_CLAUSE + && TREE_VALUE (attr2) + && TREE_CODE (TREE_VALUE (attr2)) == OMP_CLAUSE) + return omp_declare_simd_clauses_equal (TREE_VALUE (attr1), + TREE_VALUE (attr2)); + + return (simple_cst_equal (TREE_VALUE (attr1), TREE_VALUE (attr2)) == 1); +} + +/* Return 0 if the attributes for two types are incompatible, 1 if they + are compatible, and 2 if they are nearly compatible (which causes a + warning to be generated). */ +int +comp_type_attributes (const_tree type1, const_tree type2) +{ + const_tree a1 = TYPE_ATTRIBUTES (type1); + const_tree a2 = TYPE_ATTRIBUTES (type2); + const_tree a; + + if (a1 == a2) + return 1; + for (a = a1; a != NULL_TREE; a = TREE_CHAIN (a)) + { + const struct attribute_spec *as; + const_tree attr; + + as = lookup_attribute_spec (get_attribute_name (a)); + if (!as || as->affects_type_identity == false) + continue; + + attr = lookup_attribute (as->name, CONST_CAST_TREE (a2)); + if (!attr || !attribute_value_equal (a, attr)) + break; + } + if (!a) + { + for (a = a2; a != NULL_TREE; a = TREE_CHAIN (a)) + { + const struct attribute_spec *as; + + as = lookup_attribute_spec (get_attribute_name (a)); + if (!as || as->affects_type_identity == false) + continue; + + if (!lookup_attribute (as->name, CONST_CAST_TREE (a1))) + break; + /* We don't need to compare trees again, as we did this + already in first loop. */ + } + /* All types - affecting identity - are equal, so + there is no need to call target hook for comparison. */ + if (!a) + return 1; + } + if (lookup_attribute ("transaction_safe", CONST_CAST_TREE (a))) + return 0; + if ((lookup_attribute ("nocf_check", TYPE_ATTRIBUTES (type1)) != NULL) + ^ (lookup_attribute ("nocf_check", TYPE_ATTRIBUTES (type2)) != NULL)) + return 0; + /* As some type combinations - like default calling-convention - might + be compatible, we have to call the target hook to get the final result. */ + return targetm.comp_type_attributes (type1, type2); +} + +/* Return a type like TTYPE except that its TYPE_ATTRIBUTE + is ATTRIBUTE. + + Record such modified types already made so we don't make duplicates. */ + +tree +build_type_attribute_variant (tree ttype, tree attribute) +{ + return build_type_attribute_qual_variant (ttype, attribute, + TYPE_QUALS (ttype)); +} + +/* A variant of lookup_attribute() that can be used with an identifier + as the first argument, and where the identifier can be either + 'text' or '__text__'. + + Given an attribute ATTR_IDENTIFIER, and a list of attributes LIST, + return a pointer to the attribute's list element if the attribute + is part of the list, or NULL_TREE if not found. If the attribute + appears more than once, this only returns the first occurrence; the + TREE_CHAIN of the return value should be passed back in if further + occurrences are wanted. ATTR_IDENTIFIER must be an identifier but + can be in the form 'text' or '__text__'. */ +static tree +lookup_ident_attribute (tree attr_identifier, tree list) +{ + gcc_checking_assert (TREE_CODE (attr_identifier) == IDENTIFIER_NODE); + + while (list) + { + gcc_checking_assert (TREE_CODE (get_attribute_name (list)) + == IDENTIFIER_NODE); + + if (cmp_attrib_identifiers (attr_identifier, + get_attribute_name (list))) + /* Found it. */ + break; + list = TREE_CHAIN (list); + } + + return list; +} + +/* Remove any instances of attribute ATTR_NAME in LIST and return the + modified list. */ + +tree +remove_attribute (const char *attr_name, tree list) +{ + tree *p; + gcc_checking_assert (attr_name[0] != '_'); + + for (p = &list; *p;) + { + tree l = *p; + + tree attr = get_attribute_name (l); + if (is_attribute_p (attr_name, attr)) + *p = TREE_CHAIN (l); + else + p = &TREE_CHAIN (l); + } + + return list; +} + +/* Return an attribute list that is the union of a1 and a2. */ + +tree +merge_attributes (tree a1, tree a2) +{ + tree attributes; + + /* Either one unset? Take the set one. */ + + if ((attributes = a1) == 0) + attributes = a2; + + /* One that completely contains the other? Take it. */ + + else if (a2 != 0 && ! attribute_list_contained (a1, a2)) + { + if (attribute_list_contained (a2, a1)) + attributes = a2; + else + { + /* Pick the longest list, and hang on the other list. */ + + if (list_length (a1) < list_length (a2)) + attributes = a2, a2 = a1; + + for (; a2 != 0; a2 = TREE_CHAIN (a2)) + { + tree a; + for (a = lookup_ident_attribute (get_attribute_name (a2), + attributes); + a != NULL_TREE && !attribute_value_equal (a, a2); + a = lookup_ident_attribute (get_attribute_name (a2), + TREE_CHAIN (a))) + ; + if (a == NULL_TREE) + { + a1 = copy_node (a2); + TREE_CHAIN (a1) = attributes; + attributes = a1; + } + } + } + } + return attributes; +} + +/* Given types T1 and T2, merge their attributes and return + the result. */ + +tree +merge_type_attributes (tree t1, tree t2) +{ + return merge_attributes (TYPE_ATTRIBUTES (t1), + TYPE_ATTRIBUTES (t2)); +} + +/* Given decls OLDDECL and NEWDECL, merge their attributes and return + the result. */ + +tree +merge_decl_attributes (tree olddecl, tree newdecl) +{ + return merge_attributes (DECL_ATTRIBUTES (olddecl), + DECL_ATTRIBUTES (newdecl)); +} + +/* Duplicate all attributes with name NAME in ATTR list to *ATTRS if + they are missing there. */ + +void +duplicate_one_attribute (tree *attrs, tree attr, const char *name) +{ + attr = lookup_attribute (name, attr); + if (!attr) + return; + tree a = lookup_attribute (name, *attrs); + while (attr) + { + tree a2; + for (a2 = a; a2; a2 = lookup_attribute (name, TREE_CHAIN (a2))) + if (attribute_value_equal (attr, a2)) + break; + if (!a2) + { + a2 = copy_node (attr); + TREE_CHAIN (a2) = *attrs; + *attrs = a2; + } + attr = lookup_attribute (name, TREE_CHAIN (attr)); + } +} + +/* Duplicate all attributes from user DECL to the corresponding + builtin that should be propagated. */ + +void +copy_attributes_to_builtin (tree decl) +{ + tree b = builtin_decl_explicit (DECL_FUNCTION_CODE (decl)); + if (b) + duplicate_one_attribute (&DECL_ATTRIBUTES (b), + DECL_ATTRIBUTES (decl), "omp declare simd"); +} + +#if TARGET_DLLIMPORT_DECL_ATTRIBUTES + +/* Specialization of merge_decl_attributes for various Windows targets. + + This handles the following situation: + + __declspec (dllimport) int foo; + int foo; + + The second instance of `foo' nullifies the dllimport. */ + +tree +merge_dllimport_decl_attributes (tree old, tree new_tree) +{ + tree a; + int delete_dllimport_p = 1; + + /* What we need to do here is remove from `old' dllimport if it doesn't + appear in `new'. dllimport behaves like extern: if a declaration is + marked dllimport and a definition appears later, then the object + is not dllimport'd. We also remove a `new' dllimport if the old list + contains dllexport: dllexport always overrides dllimport, regardless + of the order of declaration. */ + if (!VAR_OR_FUNCTION_DECL_P (new_tree)) + delete_dllimport_p = 0; + else if (DECL_DLLIMPORT_P (new_tree) + && lookup_attribute ("dllexport", DECL_ATTRIBUTES (old))) + { + DECL_DLLIMPORT_P (new_tree) = 0; + warning (OPT_Wattributes, "%q+D already declared with dllexport " + "attribute: dllimport ignored", new_tree); + } + else if (DECL_DLLIMPORT_P (old) && !DECL_DLLIMPORT_P (new_tree)) + { + /* Warn about overriding a symbol that has already been used, e.g.: + extern int __attribute__ ((dllimport)) foo; + int* bar () {return &foo;} + int foo; + */ + if (TREE_USED (old)) + { + warning (0, "%q+D redeclared without dllimport attribute " + "after being referenced with dll linkage", new_tree); + /* If we have used a variable's address with dllimport linkage, + keep the old DECL_DLLIMPORT_P flag: the ADDR_EXPR using the + decl may already have had TREE_CONSTANT computed. + We still remove the attribute so that assembler code refers + to '&foo rather than '_imp__foo'. */ + if (VAR_P (old) && TREE_ADDRESSABLE (old)) + DECL_DLLIMPORT_P (new_tree) = 1; + } + + /* Let an inline definition silently override the external reference, + but otherwise warn about attribute inconsistency. */ + else if (VAR_P (new_tree) || !DECL_DECLARED_INLINE_P (new_tree)) + warning (OPT_Wattributes, "%q+D redeclared without dllimport " + "attribute: previous dllimport ignored", new_tree); + } + else + delete_dllimport_p = 0; + + a = merge_attributes (DECL_ATTRIBUTES (old), DECL_ATTRIBUTES (new_tree)); + + if (delete_dllimport_p) + a = remove_attribute ("dllimport", a); + + return a; +} + +/* Handle a "dllimport" or "dllexport" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_dll_attribute (tree * pnode, tree name, tree args, int flags, + bool *no_add_attrs) +{ + tree node = *pnode; + bool is_dllimport; + + /* These attributes may apply to structure and union types being created, + but otherwise should pass to the declaration involved. */ + if (!DECL_P (node)) + { + if (flags & ((int) ATTR_FLAG_DECL_NEXT | (int) ATTR_FLAG_FUNCTION_NEXT + | (int) ATTR_FLAG_ARRAY_NEXT)) + { + *no_add_attrs = true; + return tree_cons (name, args, NULL_TREE); + } + if (TREE_CODE (node) == RECORD_TYPE + || TREE_CODE (node) == UNION_TYPE) + { + node = TYPE_NAME (node); + if (!node) + return NULL_TREE; + } + else + { + warning (OPT_Wattributes, "%qE attribute ignored", + name); + *no_add_attrs = true; + return NULL_TREE; + } + } + + if (!VAR_OR_FUNCTION_DECL_P (node) && TREE_CODE (node) != TYPE_DECL) + { + *no_add_attrs = true; + warning (OPT_Wattributes, "%qE attribute ignored", + name); + return NULL_TREE; + } + + if (TREE_CODE (node) == TYPE_DECL + && TREE_CODE (TREE_TYPE (node)) != RECORD_TYPE + && TREE_CODE (TREE_TYPE (node)) != UNION_TYPE) + { + *no_add_attrs = true; + warning (OPT_Wattributes, "%qE attribute ignored", + name); + return NULL_TREE; + } + + is_dllimport = is_attribute_p ("dllimport", name); + + /* Report error on dllimport ambiguities seen now before they cause + any damage. */ + if (is_dllimport) + { + /* Honor any target-specific overrides. */ + if (!targetm.valid_dllimport_attribute_p (node)) + *no_add_attrs = true; + + else if (TREE_CODE (node) == FUNCTION_DECL + && DECL_DECLARED_INLINE_P (node)) + { + warning (OPT_Wattributes, "inline function %q+D declared as " + "dllimport: attribute ignored", node); + *no_add_attrs = true; + } + /* Like MS, treat definition of dllimported variables and + non-inlined functions on declaration as syntax errors. */ + else if (TREE_CODE (node) == FUNCTION_DECL && DECL_INITIAL (node)) + { + error ("function %q+D definition is marked dllimport", node); + *no_add_attrs = true; + } + + else if (VAR_P (node)) + { + if (DECL_INITIAL (node)) + { + error ("variable %q+D definition is marked dllimport", + node); + *no_add_attrs = true; + } + + /* `extern' needn't be specified with dllimport. + Specify `extern' now and hope for the best. Sigh. */ + DECL_EXTERNAL (node) = 1; + /* Also, implicitly give dllimport'd variables declared within + a function global scope, unless declared static. */ + if (current_function_decl != NULL_TREE && !TREE_STATIC (node)) + TREE_PUBLIC (node) = 1; + /* Clear TREE_STATIC because DECL_EXTERNAL is set, unless + it is a C++ static data member. */ + if (DECL_CONTEXT (node) == NULL_TREE + || !RECORD_OR_UNION_TYPE_P (DECL_CONTEXT (node))) + TREE_STATIC (node) = 0; + } + + if (*no_add_attrs == false) + DECL_DLLIMPORT_P (node) = 1; + } + else if (TREE_CODE (node) == FUNCTION_DECL + && DECL_DECLARED_INLINE_P (node) + && flag_keep_inline_dllexport) + /* An exported function, even if inline, must be emitted. */ + DECL_EXTERNAL (node) = 0; + + /* Report error if symbol is not accessible at global scope. */ + if (!TREE_PUBLIC (node) && VAR_OR_FUNCTION_DECL_P (node)) + { + error ("external linkage required for symbol %q+D because of " + "%qE attribute", node, name); + *no_add_attrs = true; + } + + /* A dllexport'd entity must have default visibility so that other + program units (shared libraries or the main executable) can see + it. A dllimport'd entity must have default visibility so that + the linker knows that undefined references within this program + unit can be resolved by the dynamic linker. */ + if (!*no_add_attrs) + { + if (DECL_VISIBILITY_SPECIFIED (node) + && DECL_VISIBILITY (node) != VISIBILITY_DEFAULT) + error ("%qE implies default visibility, but %qD has already " + "been declared with a different visibility", + name, node); + DECL_VISIBILITY (node) = VISIBILITY_DEFAULT; + DECL_VISIBILITY_SPECIFIED (node) = 1; + } + + return NULL_TREE; +} + +#endif /* TARGET_DLLIMPORT_DECL_ATTRIBUTES */ + +/* Given two lists of attributes, return true if list l2 is + equivalent to l1. */ + +int +attribute_list_equal (const_tree l1, const_tree l2) +{ + if (l1 == l2) + return 1; + + return attribute_list_contained (l1, l2) + && attribute_list_contained (l2, l1); +} + +/* Given two lists of attributes, return true if list L2 is + completely contained within L1. */ +/* ??? This would be faster if attribute names were stored in a canonicalized + form. Otherwise, if L1 uses `foo' and L2 uses `__foo__', the long method + must be used to show these elements are equivalent (which they are). */ +/* ??? It's not clear that attributes with arguments will always be handled + correctly. */ + +int +attribute_list_contained (const_tree l1, const_tree l2) +{ + const_tree t1, t2; + + /* First check the obvious, maybe the lists are identical. */ + if (l1 == l2) + return 1; + + /* Maybe the lists are similar. */ + for (t1 = l1, t2 = l2; + t1 != 0 && t2 != 0 + && get_attribute_name (t1) == get_attribute_name (t2) + && TREE_VALUE (t1) == TREE_VALUE (t2); + t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2)) + ; + + /* Maybe the lists are equal. */ + if (t1 == 0 && t2 == 0) + return 1; + + for (; t2 != 0; t2 = TREE_CHAIN (t2)) + { + const_tree attr; + /* This CONST_CAST is okay because lookup_attribute does not + modify its argument and the return value is assigned to a + const_tree. */ + for (attr = lookup_ident_attribute (get_attribute_name (t2), + CONST_CAST_TREE (l1)); + attr != NULL_TREE && !attribute_value_equal (t2, attr); + attr = lookup_ident_attribute (get_attribute_name (t2), + TREE_CHAIN (attr))) + ; + + if (attr == NULL_TREE) + return 0; + } + + return 1; +} + +/* The backbone of lookup_attribute(). ATTR_LEN is the string length + of ATTR_NAME, and LIST is not NULL_TREE. + + The function is called from lookup_attribute in order to optimize + for size. */ + +tree +private_lookup_attribute (const char *attr_name, size_t attr_len, tree list) +{ + while (list) + { + tree attr = get_attribute_name (list); + size_t ident_len = IDENTIFIER_LENGTH (attr); + if (cmp_attribs (attr_name, attr_len, IDENTIFIER_POINTER (attr), + ident_len)) + break; + list = TREE_CHAIN (list); + } + + return list; +} + +/* Return true if the function decl or type NODE has been declared + with attribute ANAME among attributes ATTRS. */ + +static bool +has_attribute (tree node, tree attrs, const char *aname) +{ + if (!strcmp (aname, "const")) + { + if (DECL_P (node) && TREE_READONLY (node)) + return true; + } + else if (!strcmp (aname, "malloc")) + { + if (DECL_P (node) && DECL_IS_MALLOC (node)) + return true; + } + else if (!strcmp (aname, "noreturn")) + { + if (DECL_P (node) && TREE_THIS_VOLATILE (node)) + return true; + } + else if (!strcmp (aname, "nothrow")) + { + if (TREE_NOTHROW (node)) + return true; + } + else if (!strcmp (aname, "pure")) + { + if (DECL_P (node) && DECL_PURE_P (node)) + return true; + } + + return lookup_attribute (aname, attrs); +} + +/* Return the number of mismatched function or type attributes between + the "template" function declaration TMPL and DECL. The word "template" + doesn't necessarily refer to a C++ template but rather a declaration + whose attributes should be matched by those on DECL. For a non-zero + return value set *ATTRSTR to a string representation of the list of + mismatched attributes with quoted names. + ATTRLIST is a list of additional attributes that SPEC should be + taken to ultimately be declared with. */ + +unsigned +decls_mismatched_attributes (tree tmpl, tree decl, tree attrlist, + const char* const blacklist[], + pretty_printer *attrstr) +{ + if (TREE_CODE (tmpl) != FUNCTION_DECL) + return 0; + + /* Avoid warning if either declaration or its type is deprecated. */ + if (TREE_DEPRECATED (tmpl) + || TREE_DEPRECATED (decl)) + return 0; + + const tree tmpls[] = { tmpl, TREE_TYPE (tmpl) }; + const tree decls[] = { decl, TREE_TYPE (decl) }; + + if (TREE_DEPRECATED (tmpls[1]) + || TREE_DEPRECATED (decls[1]) + || TREE_DEPRECATED (TREE_TYPE (tmpls[1])) + || TREE_DEPRECATED (TREE_TYPE (decls[1]))) + return 0; + + tree tmpl_attrs[] = { DECL_ATTRIBUTES (tmpl), TYPE_ATTRIBUTES (tmpls[1]) }; + tree decl_attrs[] = { DECL_ATTRIBUTES (decl), TYPE_ATTRIBUTES (decls[1]) }; + + if (!decl_attrs[0]) + decl_attrs[0] = attrlist; + else if (!decl_attrs[1]) + decl_attrs[1] = attrlist; + + /* Avoid warning if the template has no attributes. */ + if (!tmpl_attrs[0] && !tmpl_attrs[1]) + return 0; + + /* Avoid warning if either declaration contains an attribute on + the white list below. */ + const char* const whitelist[] = { + "error", "warning" + }; + + for (unsigned i = 0; i != 2; ++i) + for (unsigned j = 0; j != sizeof whitelist / sizeof *whitelist; ++j) + if (lookup_attribute (whitelist[j], tmpl_attrs[i]) + || lookup_attribute (whitelist[j], decl_attrs[i])) + return 0; + + /* Put together a list of the black-listed attributes that the template + is declared with and the declaration is not, in case it's not apparent + from the most recent declaration of the template. */ + unsigned nattrs = 0; + + for (unsigned i = 0; blacklist[i]; ++i) + { + /* Attribute leaf only applies to extern functions. Avoid mentioning + it when it's missing from a static declaration. */ + if (!TREE_PUBLIC (decl) + && !strcmp ("leaf", blacklist[i])) + continue; + + for (unsigned j = 0; j != 2; ++j) + { + if (!has_attribute (tmpls[j], tmpl_attrs[j], blacklist[i])) + continue; + + bool found = false; + unsigned kmax = 1 + !!decl_attrs[1]; + for (unsigned k = 0; k != kmax; ++k) + { + if (has_attribute (decls[k], decl_attrs[k], blacklist[i])) + { + found = true; + break; + } + } + + if (!found) + { + if (nattrs) + pp_string (attrstr, ", "); + pp_begin_quote (attrstr, pp_show_color (global_dc->printer)); + pp_string (attrstr, blacklist[i]); + pp_end_quote (attrstr, pp_show_color (global_dc->printer)); + ++nattrs; + } + + break; + } + } + + return nattrs; +} + +/* Issue a warning for the declaration ALIAS for TARGET where ALIAS + specifies either attributes that are incompatible with those of + TARGET, or attributes that are missing and that declaring ALIAS + with would benefit. */ + +void +maybe_diag_alias_attributes (tree alias, tree target) +{ + /* Do not expect attributes to match between aliases and ifunc + resolvers. There is no obvious correspondence between them. */ + if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (alias))) + return; + + const char* const blacklist[] = { + "alloc_align", "alloc_size", "cold", "const", "hot", "leaf", "malloc", + "nonnull", "noreturn", "nothrow", "pure", "returns_nonnull", + "returns_twice", NULL + }; + + pretty_printer attrnames; + if (warn_attribute_alias > 1) + { + /* With -Wattribute-alias=2 detect alias declarations that are more + restrictive than their targets first. Those indicate potential + codegen bugs. */ + if (unsigned n = decls_mismatched_attributes (alias, target, NULL_TREE, + blacklist, &attrnames)) + { + auto_diagnostic_group d; + if (warning_n (DECL_SOURCE_LOCATION (alias), + OPT_Wattribute_alias_, n, + "%qD specifies more restrictive attribute than " + "its target %qD: %s", + "%qD specifies more restrictive attributes than " + "its target %qD: %s", + alias, target, pp_formatted_text (&attrnames))) + inform (DECL_SOURCE_LOCATION (target), + "%qD target declared here", alias); + return; + } + } + + /* Detect alias declarations that are less restrictive than their + targets. Those suggest potential optimization opportunities + (solved by adding the missing attribute(s) to the alias). */ + if (unsigned n = decls_mismatched_attributes (target, alias, NULL_TREE, + blacklist, &attrnames)) + { + auto_diagnostic_group d; + if (warning_n (DECL_SOURCE_LOCATION (alias), + OPT_Wmissing_attributes, n, + "%qD specifies less restrictive attribute than " + "its target %qD: %s", + "%qD specifies less restrictive attributes than " + "its target %qD: %s", + alias, target, pp_formatted_text (&attrnames))) + inform (DECL_SOURCE_LOCATION (target), + "%qD target declared here", alias); + } +} + +/* Initialize a mapping RWM for a call to a function declared with + attribute access in ATTRS. Each attribute positional operand + inserts one entry into the mapping with the operand number as + the key. */ + +void +init_attr_rdwr_indices (rdwr_map *rwm, tree attrs) +{ + if (!attrs) + return; + + for (tree access = attrs; + (access = lookup_attribute ("access", access)); + access = TREE_CHAIN (access)) + { + /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE + is the attribute argument's value. */ + tree mode = TREE_VALUE (access); + if (!mode) + return; + + /* The (optional) list of VLA bounds. */ + tree vblist = TREE_CHAIN (mode); + if (vblist) + vblist = TREE_VALUE (vblist); + + mode = TREE_VALUE (mode); + if (TREE_CODE (mode) != STRING_CST) + continue; + gcc_assert (TREE_CODE (mode) == STRING_CST); + + for (const char *m = TREE_STRING_POINTER (mode); *m; ) + { + attr_access acc = { }; + + /* Skip the internal-only plus sign. */ + if (*m == '+') + ++m; + + acc.str = m; + acc.mode = acc.from_mode_char (*m); + acc.sizarg = UINT_MAX; + + const char *end; + acc.ptrarg = strtoul (++m, const_cast(&end), 10); + m = end; + + if (*m == '[') + { + /* Forms containing the square bracket are internal-only + (not specified by an attribute declaration), and used + for various forms of array and VLA parameters. */ + acc.internal_p = true; + + /* Search to the closing bracket and look at the preceding + code: it determines the form of the most significant + bound of the array. Others prior to it encode the form + of interior VLA bounds. They're not of interest here. */ + end = strchr (m, ']'); + const char *p = end; + gcc_assert (p); + + while (ISDIGIT (p[-1])) + --p; + + if (ISDIGIT (*p)) + { + /* A digit denotes a constant bound (as in T[3]). */ + acc.static_p = p[-1] == 's'; + acc.minsize = strtoull (p, NULL, 10); + } + else if (' ' == p[-1]) + { + /* A space denotes an ordinary array of unspecified bound + (as in T[]). */ + acc.minsize = 0; + } + else if ('*' == p[-1] || '$' == p[-1]) + { + /* An asterisk denotes a VLA. When the closing bracket + is followed by a comma and a dollar sign its bound is + on the list. Otherwise it's a VLA with an unspecified + bound. */ + acc.static_p = p[-2] == 's'; + acc.minsize = HOST_WIDE_INT_M1U; + } + + m = end + 1; + } + + if (*m == ',') + { + ++m; + do + { + if (*m == '$') + { + ++m; + if (!acc.size) + { + /* Extract the list of VLA bounds for the current + parameter, store it in ACC.SIZE, and advance + to the list of bounds for the next VLA parameter. + */ + acc.size = TREE_VALUE (vblist); + vblist = TREE_CHAIN (vblist); + } + } + + if (ISDIGIT (*m)) + { + /* Extract the positional argument. It's absent + for VLAs whose bound doesn't name a function + parameter. */ + unsigned pos = strtoul (m, const_cast(&end), 10); + if (acc.sizarg == UINT_MAX) + acc.sizarg = pos; + m = end; + } + } + while (*m == '$'); + } + + acc.end = m; + + bool existing; + auto &ref = rwm->get_or_insert (acc.ptrarg, &existing); + if (existing) + { + /* Merge the new spec with the existing. */ + if (acc.minsize == HOST_WIDE_INT_M1U) + ref.minsize = HOST_WIDE_INT_M1U; + + if (acc.sizarg != UINT_MAX) + ref.sizarg = acc.sizarg; + + if (acc.mode) + ref.mode = acc.mode; + } + else + ref = acc; + + /* Unconditionally add an entry for the required pointer + operand of the attribute, and one for the optional size + operand when it's specified. */ + if (acc.sizarg != UINT_MAX) + rwm->put (acc.sizarg, acc); + } + } +} + +/* Return the access specification for a function parameter PARM + or null if the current function has no such specification. */ + +attr_access * +get_parm_access (rdwr_map &rdwr_idx, tree parm, + tree fndecl /* = current_function_decl */) +{ + tree fntype = TREE_TYPE (fndecl); + init_attr_rdwr_indices (&rdwr_idx, TYPE_ATTRIBUTES (fntype)); + + if (rdwr_idx.is_empty ()) + return NULL; + + unsigned argpos = 0; + tree fnargs = DECL_ARGUMENTS (fndecl); + for (tree arg = fnargs; arg; arg = TREE_CHAIN (arg), ++argpos) + if (arg == parm) + return rdwr_idx.get (argpos); + + return NULL; +} + +/* Return the internal representation as STRING_CST. Internal positional + arguments are zero-based. */ + +tree +attr_access::to_internal_string () const +{ + return build_string (end - str, str); +} + +/* Return the human-readable representation of the external attribute + specification (as it might appear in the source code) as STRING_CST. + External positional arguments are one-based. */ + +tree +attr_access::to_external_string () const +{ + char buf[80]; + gcc_assert (mode != access_deferred); + int len = snprintf (buf, sizeof buf, "access (%s, %u", + mode_names[mode], ptrarg + 1); + if (sizarg != UINT_MAX) + len += snprintf (buf + len, sizeof buf - len, ", %u", sizarg + 1); + strcpy (buf + len, ")"); + return build_string (len + 2, buf); +} + +/* Return the number of specified VLA bounds and set *nunspec to + the number of unspecified ones (those designated by [*]). */ + +unsigned +attr_access::vla_bounds (unsigned *nunspec) const +{ + *nunspec = 0; + for (const char* p = strrchr (str, ']'); p && *p != '['; --p) + if (*p == '*') + ++*nunspec; + return list_length (size); +} + + +/* Defined in attr_access. */ +constexpr char attr_access::mode_chars[]; +constexpr char attr_access::mode_names[][11]; + +/* Format an array, including a VLA, pointed to by TYPE and used as + a function parameter as a human-readable string. ACC describes + an access to the parameter and is used to determine the outermost + form of the array including its bound which is otherwise obviated + by its decay to pointer. Return the formatted string. */ + +std::string +attr_access::array_as_string (tree type) const +{ + std::string typstr; + + if (type == error_mark_node) + return std::string (); + + if (this->str) + { + /* For array parameters (but not pointers) create a temporary array + type that corresponds to the form of the parameter including its + qualifiers even though they apply to the pointer, not the array + type. */ + const bool vla_p = minsize == HOST_WIDE_INT_M1U; + tree eltype = TREE_TYPE (type); + tree index_type = NULL_TREE; + + if (minsize == HOST_WIDE_INT_M1U) + { + /* Determine if this is a VLA (an array whose most significant + bound is nonconstant and whose access string has "$]" in it) + extract the bound expression from SIZE. */ + const char *p = end; + for ( ; p != str && *p-- != ']'; ); + if (*p == '$') + index_type = build_index_type (TREE_VALUE (size)); + } + else if (minsize) + index_type = build_index_type (size_int (minsize - 1)); + + tree arat = NULL_TREE; + if (static_p || vla_p) + { + tree flag = static_p ? integer_one_node : NULL_TREE; + /* Hack: there's no language-independent way to encode + the "static" specifier or the "*" notation in an array type. + Add a "fake" attribute to have the pretty-printer add "static" + or "*". The "[static N]" notation is only valid in the most + significant bound but [*] can be used for any bound. Because + [*] is represented the same as [0] this hack only works for + the most significant bound like static and the others are + rendered as [0]. */ + arat = build_tree_list (get_identifier ("array"), flag); + } + + const int quals = TYPE_QUALS (type); + type = build_array_type (eltype, index_type); + type = build_type_attribute_qual_variant (type, arat, quals); + } + + /* Format the type using the current pretty printer. The generic tree + printer does a terrible job. */ + pretty_printer *pp = global_dc->printer->clone (); + pp_printf (pp, "%qT", type); + typstr = pp_formatted_text (pp); + delete pp; + + return typstr; +} + +#if CHECKING_P + +namespace selftest +{ + +/* Helper types to verify the consistency attribute exclusions. */ + +typedef std::pair excl_pair; + +struct excl_hash_traits: typed_noop_remove +{ + typedef excl_pair value_type; + typedef value_type compare_type; + + static hashval_t hash (const value_type &x) + { + hashval_t h1 = htab_hash_string (x.first); + hashval_t h2 = htab_hash_string (x.second); + return h1 ^ h2; + } + + static bool equal (const value_type &x, const value_type &y) + { + return !strcmp (x.first, y.first) && !strcmp (x.second, y.second); + } + + static void mark_deleted (value_type &x) + { + x = value_type (NULL, NULL); + } + + static const bool empty_zero_p = false; + + static void mark_empty (value_type &x) + { + x = value_type ("", ""); + } + + static bool is_deleted (const value_type &x) + { + return !x.first && !x.second; + } + + static bool is_empty (const value_type &x) + { + return !*x.first && !*x.second; + } +}; + + +/* Self-test to verify that each attribute exclusion is symmetric, + meaning that if attribute A is encoded as incompatible with + attribute B then the opposite relationship is also encoded. + This test also detects most cases of misspelled attribute names + in exclusions. */ + +static void +test_attribute_exclusions () +{ + /* Iterate over the array of attribute tables first (with TI0 as + the index) and over the array of attribute_spec in each table + (with SI0 as the index). */ + const size_t ntables = ARRAY_SIZE (attribute_tables); + + /* Set of pairs of mutually exclusive attributes. */ + typedef hash_set exclusion_set; + exclusion_set excl_set; + + for (size_t ti0 = 0; ti0 != ntables; ++ti0) + for (size_t s0 = 0; attribute_tables[ti0][s0].name; ++s0) + { + const attribute_spec::exclusions *excl + = attribute_tables[ti0][s0].exclude; + + /* Skip each attribute that doesn't define exclusions. */ + if (!excl) + continue; + + const char *attr_name = attribute_tables[ti0][s0].name; + + /* Iterate over the set of exclusions for every attribute + (with EI0 as the index) adding the exclusions defined + for each to the set. */ + for (size_t ei0 = 0; excl[ei0].name; ++ei0) + { + const char *excl_name = excl[ei0].name; + + if (!strcmp (attr_name, excl_name)) + continue; + + excl_set.add (excl_pair (attr_name, excl_name)); + } + } + + /* Traverse the set of mutually exclusive pairs of attributes + and verify that they are symmetric. */ + for (exclusion_set::iterator it = excl_set.begin (); + it != excl_set.end (); + ++it) + { + if (!excl_set.contains (excl_pair ((*it).second, (*it).first))) + { + /* An exclusion for an attribute has been found that + doesn't have a corresponding exclusion in the opposite + direction. */ + char desc[120]; + sprintf (desc, "'%s' attribute exclusion '%s' must be symmetric", + (*it).first, (*it).second); + fail (SELFTEST_LOCATION, desc); + } + } +} + +void +attribute_c_tests () +{ + test_attribute_exclusions (); +} + +} /* namespace selftest */ + +#endif /* CHECKING_P */