/* d-attribs.c -- D attributes handling. Copyright (C) 2015-2023 Free Software Foundation, Inc. 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 . */ /* Implementation of attribute handlers for user defined attributes and internal built-in functions. */ #include "config.h" #include "system.h" #include "coretypes.h" #include "dmd/attrib.h" #include "dmd/declaration.h" #include "dmd/expression.h" #include "dmd/module.h" #include "dmd/mtype.h" #include "dmd/template.h" #include "tree.h" #include "diagnostic.h" #include "tm.h" #include "cgraph.h" #include "toplev.h" #include "target.h" #include "common/common-target.h" #include "stringpool.h" #include "attribs.h" #include "varasm.h" #include "fold-const.h" #include "opts.h" #include "d-tree.h" /* Internal attribute handlers for built-in functions. */ static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *); static tree handle_leaf_attribute (tree *, tree, tree, int, bool *); static tree handle_const_attribute (tree *, tree, tree, int, bool *); static tree handle_malloc_attribute (tree *, tree, tree, int, bool *); static tree handle_pure_attribute (tree *, tree, tree, int, bool *); static tree handle_novops_attribute (tree *, tree, tree, int, bool *); static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *); static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *); static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *); static tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *); static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *); static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *); static tree handle_omp_declare_simd_attribute (tree *, tree, tree, int, bool *); /* D attribute handlers for user defined attributes. */ static tree d_handle_noinline_attribute (tree *, tree, tree, int, bool *); static tree d_handle_always_inline_attribute (tree *, tree, tree, int, bool *); static tree d_handle_flatten_attribute (tree *, tree, tree, int, bool *); static tree d_handle_target_attribute (tree *, tree, tree, int, bool *); static tree d_handle_target_clones_attribute (tree *, tree, tree, int, bool *); static tree d_handle_optimize_attribute (tree *, tree, tree, int, bool *); static tree d_handle_noclone_attribute (tree *, tree, tree, int, bool *); static tree d_handle_noicf_attribute (tree *, tree, tree, int, bool *); static tree d_handle_noipa_attribute (tree *, tree, tree, int, bool *); static tree d_handle_section_attribute (tree *, tree, tree, int, bool *); static tree d_handle_symver_attribute (tree *, tree, tree, int, bool *); static tree d_handle_weak_attribute (tree *, tree, tree, int, bool *) ; static tree d_handle_noplt_attribute (tree *, tree, tree, int, bool *) ; static tree d_handle_alloc_size_attribute (tree *, tree, tree, int, bool *); static tree d_handle_cold_attribute (tree *, tree, tree, int, bool *); static tree d_handle_register_attribute (tree *, tree, tree, int, bool *); static tree d_handle_restrict_attribute (tree *, tree, tree, int, bool *); static tree d_handle_used_attribute (tree *, tree, tree, int, bool *); static tree d_handle_visibility_attribute (tree *, tree, tree, int, bool *); static tree d_handle_no_sanitize_attribute (tree *, tree, tree, int, bool *); static tree d_handle_simd_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_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 (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), }; static const struct attribute_spec::exclusions attr_const_pure_exclusions[] = { ATTR_EXCL ("alloc_size", true, true, true), 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_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 (NULL, false, false, false), }; static const struct attribute_spec::exclusions attr_target_exclusions[] = { ATTR_EXCL ("target_clones", true, true, true), ATTR_EXCL (NULL, false, false, false), }; static const struct attribute_spec::exclusions attr_target_clones_exclusions[] = { ATTR_EXCL ("always_inline", true, true, true), ATTR_EXCL ("target", true, true, true), ATTR_EXCL (NULL, false, false, false), }; 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), }; 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) }; /* Helper to define an attribute. */ #define ATTR_SPEC(name, min_len, max_len, decl_req, type_req, fn_type_req, \ affects_type_identity, handler, exclude) \ { name, min_len, max_len, decl_req, type_req, fn_type_req, \ affects_type_identity, handler, exclude } /* Table of machine-independent attributes. For internal use (marking of built-ins) only. */ const attribute_spec d_langhook_common_attribute_table[] = { ATTR_SPEC ("noreturn", 0, 0, true, false, false, false, handle_noreturn_attribute, attr_noreturn_exclusions), ATTR_SPEC ("leaf", 0, 0, true, false, false, false, handle_leaf_attribute, NULL), ATTR_SPEC ("const", 0, 0, true, false, false, false, handle_const_attribute, attr_const_pure_exclusions), ATTR_SPEC ("malloc", 0, 0, true, false, false, false, handle_malloc_attribute, NULL), ATTR_SPEC ("returns_twice", 0, 0, true, false, false, false, handle_returns_twice_attribute, attr_returns_twice_exclusions), ATTR_SPEC ("pure", 0, 0, true, false, false, false, handle_pure_attribute, attr_const_pure_exclusions), ATTR_SPEC ("nonnull", 0, -1, false, true, true, false, handle_nonnull_attribute, NULL), ATTR_SPEC ("nothrow", 0, 0, true, false, false, false, handle_nothrow_attribute, NULL), ATTR_SPEC ("transaction_pure", 0, 0, false, true, true, false, handle_transaction_pure_attribute, NULL), ATTR_SPEC ("no vops", 0, 0, true, false, false, false, handle_novops_attribute, NULL), ATTR_SPEC ("type generic", 0, 0, false, true, true, false, handle_type_generic_attribute, NULL), ATTR_SPEC ("fn spec", 1, 1, false, true, true, false, handle_fnspec_attribute, NULL), ATTR_SPEC ("omp declare simd", 0, -1, true, false, false, false, handle_omp_declare_simd_attribute, NULL), ATTR_SPEC (NULL, 0, 0, false, false, false, false, NULL, NULL), }; /* Table of D language attributes exposed by `gcc.attribute' UDAs. */ const attribute_spec d_langhook_attribute_table[] = { ATTR_SPEC ("noinline", 0, 0, true, false, false, false, d_handle_noinline_attribute, attr_noinline_exclusions), ATTR_SPEC ("always_inline", 0, 0, true, false, false, false, d_handle_always_inline_attribute, attr_inline_exclusions), ATTR_SPEC ("flatten", 0, 0, true, false, false, false, d_handle_flatten_attribute, NULL), ATTR_SPEC ("target", 1, -1, true, false, false, false, d_handle_target_attribute, attr_target_exclusions), ATTR_SPEC ("target_clones", 1, -1, true, false, false, false, d_handle_target_clones_attribute, attr_target_clones_exclusions), ATTR_SPEC ("optimize", 1, -1, true, false, false, false, d_handle_optimize_attribute, NULL), ATTR_SPEC ("noclone", 0, 0, true, false, false, false, d_handle_noclone_attribute, NULL), ATTR_SPEC ("no_icf", 0, 0, true, false, false, false, d_handle_noicf_attribute, NULL), ATTR_SPEC ("noipa", 0, 0, true, false, false, false, d_handle_noipa_attribute, NULL), ATTR_SPEC ("section", 1, 1, true, false, false, false, d_handle_section_attribute, NULL), ATTR_SPEC ("symver", 1, -1, true, false, false, false, d_handle_symver_attribute, NULL), ATTR_SPEC ("weak", 0, 0, true, false, false, false, d_handle_weak_attribute, NULL), ATTR_SPEC ("noplt", 0, 0, true, false, false, false, d_handle_noplt_attribute, NULL), ATTR_SPEC ("alloc_size", 1, 3, false, true, true, false, d_handle_alloc_size_attribute, attr_alloc_exclusions), ATTR_SPEC ("cold", 0, 0, true, false, false, false, d_handle_cold_attribute, attr_cold_hot_exclusions), ATTR_SPEC ("no_sanitize", 1, -1, true, false, false, false, d_handle_no_sanitize_attribute, NULL), ATTR_SPEC ("register", 1, 1, true, false, false, false, d_handle_register_attribute, NULL), ATTR_SPEC ("restrict", 0, 0, true, false, false, false, d_handle_restrict_attribute, NULL), ATTR_SPEC ("simd", 0, 1, true, false, false, false, d_handle_simd_attribute, NULL), ATTR_SPEC ("used", 0, 0, true, false, false, false, d_handle_used_attribute, NULL), ATTR_SPEC ("visibility", 1, 1, false, false, false, false, d_handle_visibility_attribute, NULL), ATTR_SPEC (NULL, 0, 0, false, false, false, false, NULL, NULL), }; /* Insert the type attribute ATTRNAME with value VALUE into TYPE. Returns a new variant of the original type declaration. */ tree insert_type_attribute (tree type, const char *attrname, tree value) { tree ident = get_identifier (attrname); if (value) value = tree_cons (NULL_TREE, value, NULL_TREE); decl_attributes (&type, build_tree_list (ident, value), ATTR_FLAG_TYPE_IN_PLACE); return type; } /* Insert the decl attribute ATTRNAME with value VALUE into DECL. */ tree insert_decl_attribute (tree decl, const char *attrname, tree value) { tree ident = get_identifier (attrname); if (value) value = tree_cons (NULL_TREE, value, NULL_TREE); decl_attributes (&decl, build_tree_list (ident, value), 0); return decl; } /* Returns TRUE if NAME is an attribute recognized as being handled by the `gcc.attribute' module. */ static bool uda_attribute_p (const char *name) { tree ident = get_identifier (name); /* Search both our language, and target attribute tables. Common and format attributes are kept internal. */ for (const attribute_spec *p = d_langhook_attribute_table; p->name; p++) { if (get_identifier (p->name) == ident) return true; } if (targetm.attribute_table) { for (const attribute_spec *p = targetm.attribute_table; p->name; p++) { if (get_identifier (p->name) == ident) return true; } } return false; } /* [attribute/uda] User Defined Attributes (UDA) are compile time expressions that can be attached to a declaration. These attributes can then be queried, extracted, and manipulated at compile-time. There is no run-time component to them. Expand and merge all UDAs found in the EATTRS list that are of type `gcc.attribute.Attribute'. This symbol is internally recognized by the compiler and maps them to their equivalent GCC attribute. */ static tree build_attributes (Expressions *eattrs) { if (!eattrs) return NULL_TREE; expandTuples (eattrs); tree attribs = NULL_TREE; for (size_t i = 0; i < eattrs->length; i++) { Expression *attr = (*eattrs)[i]; Dsymbol *sym = attr->type->toDsymbol (0); if (!sym) { /* If attribute is a template symbol, perhaps arguments were not supplied, so warn about attribute having no effect. */ if (TemplateExp *te = attr->isTemplateExp ()) { if (!te->td || !te->td->onemember) continue; sym = te->td->onemember; } else continue; } /* Attribute symbol must come from the `gcc.attribute' module. */ Dsymbol *mod = sym->getModule (); if (!(strcmp (mod->toChars (), "attributes") == 0 && mod->parent != NULL && strcmp (mod->parent->toChars (), "gcc") == 0 && !mod->parent->parent)) continue; /* Get the result of the attribute if it hasn't already been folded. */ if (attr->op == EXP::call) attr = attr->ctfeInterpret (); if (attr->op != EXP::structLiteral) { warning_at (make_location_t (attr->loc), OPT_Wattributes, "%qE attribute has no effect", get_identifier (sym->toChars ())); continue; } /* Should now have a struct `Attribute("attrib", "value", ...)' initializer list. */ Expressions *elems = attr->isStructLiteralExp ()->elements; Expression *e0 = (*elems)[0]; if (e0->op != EXP::string_) { warning_at (make_location_t (attr->loc), OPT_Wattributes, "unknown attribute %qs", e0->toChars()); continue; } StringExp *se = e0->toStringExp (); gcc_assert (se->sz == 1); /* Empty string attribute, just ignore it. */ if (se->len == 0) continue; /* Check if the attribute is recognized and handled. Done here to report the diagnostic at the right location. */ const char *name = (const char *)(se->len ? se->string : ""); if (!uda_attribute_p (name)) { warning_at (make_location_t (attr->loc), OPT_Wattributes, "unknown attribute %qs", name); continue; } /* Chain all attribute arguments together. */ tree args = NULL_TREE; for (size_t j = 1; j < elems->length; j++) { Expression *e = (*elems)[j]; /* Stop after the first `void' argument. */ if (e == NULL) break; StringExp *s = e->isStringExp (); tree t; if (s != NULL && s->sz == 1) { const char *string = (const char *)(s->len ? s->string : ""); t = build_string (s->len, string); } else t = build_expr (e); args = chainon (args, build_tree_list (0, t)); } tree list = build_tree_list (get_identifier (name), args); attribs = chainon (attribs, list); } return attribs; } /* If any GCC attributes are found in the declaration SYM, apply them to the type or decl NODE. */ void apply_user_attributes (Dsymbol *sym, tree node) { UserAttributeDeclaration *uda = sym->userAttribDecl (); if (uda == NULL) return; location_t saved_location = input_location; input_location = make_location_t (sym->loc); int attr_flags = 0; if (TYPE_P (node) && !COMPLETE_TYPE_P (node)) attr_flags |= ATTR_FLAG_TYPE_IN_PLACE; Expressions *attrs = uda->getAttributes (); decl_attributes (&node, build_attributes (attrs), attr_flags); input_location = saved_location; } /* Built-in attribute handlers. These functions take the arguments: (tree *node, tree name, tree args, int flags, bool *no_add_attrs) */ /* Handle a "noreturn" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_noreturn_attribute (tree *node, tree, tree, int, bool *) { tree type = TREE_TYPE (*node); if (TREE_CODE (*node) == FUNCTION_DECL) TREE_THIS_VOLATILE (*node) = 1; else if (TREE_CODE (type) == POINTER_TYPE && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE) TREE_TYPE (*node) = build_pointer_type (build_type_variant (TREE_TYPE (type), TYPE_READONLY (TREE_TYPE (type)), 1)); else gcc_unreachable (); return NULL_TREE; } /* Handle a "leaf" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_leaf_attribute (tree *node, tree name, tree, int, 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", 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, tree, int, bool *) { tree type = TREE_TYPE (*node); 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_pointer_type (build_type_variant (TREE_TYPE (type), 1, TREE_THIS_VOLATILE (TREE_TYPE (type)))); else gcc_unreachable (); return NULL_TREE; } /* Handle a "malloc" attribute; arguments as in struct attribute_spec.handler. */ tree handle_malloc_attribute (tree *node, tree, tree, int, bool *) { gcc_assert (TREE_CODE (*node) == FUNCTION_DECL && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (*node)))); DECL_IS_MALLOC (*node) = 1; return NULL_TREE; } /* Handle a "pure" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_pure_attribute (tree *node, tree, tree, int, bool *) { gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); DECL_PURE_P (*node) = 1; return NULL_TREE; } /* Handle a "no vops" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_novops_attribute (tree *node, tree, tree, int, bool *) { 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. */ static bool get_nonnull_operand (tree arg_num_expr, unsigned HOST_WIDE_INT *valp) { /* Verify the arg number is a constant. */ if (!tree_fits_uhwi_p (arg_num_expr)) return false; *valp = TREE_INT_CST_LOW (arg_num_expr); return true; } /* Handle the "nonnull" attribute. */ static tree handle_nonnull_attribute (tree *node, tree, tree args, int, bool *) { tree type = *node; /* If no arguments are specified, all pointer arguments should be non-null. Verify a full prototype is given so that the arguments will have the correct types when we actually check them later. Avoid diagnosing type-generic built-ins since those have no prototype. */ if (!args) { gcc_assert (prototype_p (type) || !TYPE_ATTRIBUTES (type) || lookup_attribute ("type generic", TYPE_ATTRIBUTES (type))); return NULL_TREE; } /* Argument list specified. Verify that each argument number references a pointer argument. */ for (; args; args = TREE_CHAIN (args)) { tree argument; unsigned HOST_WIDE_INT arg_num = 0, ck_num; if (!get_nonnull_operand (TREE_VALUE (args), &arg_num)) gcc_unreachable (); argument = TYPE_ARG_TYPES (type); if (argument) { for (ck_num = 1; ; ck_num++) { if (!argument || ck_num == arg_num) break; argument = TREE_CHAIN (argument); } gcc_assert (argument && TREE_CODE (TREE_VALUE (argument)) == POINTER_TYPE); } } return NULL_TREE; } /* Handle a "nothrow" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_nothrow_attribute (tree *node, tree, tree, int, bool *) { gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); TREE_NOTHROW (*node) = 1; return NULL_TREE; } /* Handle a "type generic" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_type_generic_attribute (tree *node, tree, tree, int, bool *) { /* 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; arguments as in struct attribute_spec.handler. */ static tree handle_transaction_pure_attribute (tree *node, tree, tree, int, bool *) { /* Ensure we have a function type. */ gcc_assert (TREE_CODE (*node) == FUNCTION_TYPE); return NULL_TREE; } /* Handle a "returns_twice" attribute; arguments as in struct attribute_spec.handler. */ static tree handle_returns_twice_attribute (tree *node, tree, tree, int, bool *) { gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); DECL_IS_RETURNS_TWICE (*node) = 1; return NULL_TREE; } /* Handle a "fn spec" attribute; arguments as in struct attribute_spec.handler. */ tree handle_fnspec_attribute (tree *, tree, tree args, int, bool *) { gcc_assert (args && TREE_CODE (TREE_VALUE (args)) == STRING_CST && !TREE_CHAIN (args)); return NULL_TREE; } /* Handle an "omp declare simd" attribute; arguments as in struct attribute_spec.handler. */ tree handle_omp_declare_simd_attribute (tree *node, tree, tree, int, bool *) { gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); return NULL_TREE; } /* Language specific attribute handlers. These functions take the arguments: (tree *node, tree name, tree args, int flags, bool *no_add_attrs) */ /* Handle a "noinline" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_noinline_attribute (tree *node, tree name, tree, int, 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 "always_inline" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_always_inline_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { if (TREE_CODE (*node) == FUNCTION_DECL) { DECL_DECLARED_INLINE_P (*node) = 1; DECL_DISREGARD_INLINE_LIMITS (*node) = 1; } else { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "flatten" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_flatten_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "target" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_target_attribute (tree *node, tree name, tree args, int flags, bool *no_add_attrs) { /* Ensure we have a function type. */ 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) != 0 && TREE_STRING_POINTER (value)[0] != '\0')) continue; warning (OPT_Wattributes, "empty string in attribute %"); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "target_clones" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_target_clones_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { /* Ensure we have a function type. */ if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } else { /* Do not inline functions with multiple clone targets. */ DECL_UNINLINABLE (*node) = 1; } return NULL_TREE; } /* Arguments being collected for optimization. */ static GTY(()) vec *optimize_args; /* Inner function to convert a TREE_LIST to argv string to parse the optimize options in ARGS. */ static bool parse_optimize_options (tree args) { bool ret = true; /* Build up argv vector. Just in case the string is stored away, use garbage collected strings. */ vec_safe_truncate (optimize_args, 0); vec_safe_push (optimize_args, (const char *) NULL); for (tree ap = args; ap != NULL_TREE; ap = TREE_CHAIN (ap)) { tree value = TREE_VALUE (ap); if (TREE_CODE (value) == INTEGER_CST) { char buffer[20]; sprintf (buffer, "-O%ld", (long) TREE_INT_CST_LOW (value)); vec_safe_push (optimize_args, ggc_strdup (buffer)); } else if (TREE_CODE (value) == STRING_CST) { size_t len = TREE_STRING_LENGTH (value); const char *p = TREE_STRING_POINTER (value); /* If the user supplied -Oxxx or -fxxx, only allow -Oxxx or -fxxx options. */ if (*p == '-' && p[1] != 'O' && p[1] != 'f') { ret = false; warning (OPT_Wattributes, "bad option %qs to attribute %", p); continue; } /* Can't use GC memory here. */ char *q = XOBNEWVEC (&opts_obstack, char, len + 3); char *r = q; if (*p != '-') { *r++ = '-'; /* Assume that Ox is -Ox, a numeric value is -Ox, a s by itself is -Os, and any other switch begins with a -f. */ if ((*p >= '0' && *p <= '9') || (p[0] == 's' && p[1] == '\0')) *r++ = 'O'; else if (*p != 'O') *r++ = 'f'; } memcpy (r, p, len); r[len] = '\0'; vec_safe_push (optimize_args, (const char *) q); } } unsigned opt_argc = optimize_args->length (); const char **opt_argv = (const char **) alloca (sizeof (char *) * (opt_argc + 1)); for (unsigned i = 1; i < opt_argc; i++) opt_argv[i] = (*optimize_args)[i]; /* Now parse the options. */ struct cl_decoded_option *decoded_options; unsigned int decoded_options_count; decode_cmdline_options_to_array_default_mask (opt_argc, opt_argv, &decoded_options, &decoded_options_count); /* Drop non-Optimization options. */ unsigned j = 1; for (unsigned i = 1; i < decoded_options_count; ++i) { unsigned opt_index = decoded_options[i].opt_index; if (opt_index >= cl_options_count || ! (cl_options[opt_index].flags & CL_OPTIMIZATION)) { ret = false; warning (OPT_Wattributes, "bad option %qs to attribute %", decoded_options[i].orig_option_with_args_text); continue; } if (i != j) decoded_options[j] = decoded_options[i]; j++; } decoded_options_count = j; /* And apply them. */ decode_options (&global_options, &global_options_set, decoded_options, decoded_options_count, input_location, global_dc, NULL); targetm.override_options_after_change(); optimize_args->truncate (0); return ret; } /* Handle a "optimize" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_optimize_attribute (tree *node, tree name, tree args, int, bool *no_add_attrs) { /* Ensure we have a function type. */ if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } else { struct cl_optimization cur_opts; tree old_opts = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (*node); /* Save current options. */ cl_optimization_save (&cur_opts, &global_options, &global_options_set); tree prev_target_node = build_target_option_node (&global_options, &global_options_set); /* If we previously had some optimization options, use them as the default. */ gcc_options *saved_global_options = NULL; if (flag_checking) { saved_global_options = XNEW (gcc_options); *saved_global_options = global_options; } if (old_opts) cl_optimization_restore (&global_options, &global_options_set, TREE_OPTIMIZATION (old_opts)); /* Parse options, and update the vector. */ parse_optimize_options (args); DECL_FUNCTION_SPECIFIC_OPTIMIZATION (*node) = build_optimization_node (&global_options, &global_options_set); tree target_node = build_target_option_node (&global_options, &global_options_set); if (prev_target_node != target_node) DECL_FUNCTION_SPECIFIC_TARGET (*node) = target_node; /* Restore current options. */ cl_optimization_restore (&global_options, &global_options_set, &cur_opts); cl_target_option_restore (&global_options, &global_options_set, TREE_TARGET_OPTION (prev_target_node)); if (saved_global_options != NULL) { cl_optimization_compare (saved_global_options, &global_options); free (saved_global_options); } } return NULL_TREE; } /* Handle a "noclone" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_noclone_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "no_icf" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_noicf_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "noipa" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_noipa_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "section" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_section_attribute (tree *node, tree name, tree args, int flags, bool *no_add_attrs) { if (!targetm_common.have_named_sections) { error ("section attributes are not supported for this target"); *no_add_attrs = true; return NULL_TREE; } if (!VAR_OR_FUNCTION_DECL_P (*node)) { error ("section attribute not allowed for %q+D", *node); *no_add_attrs = true; return NULL_TREE; } if (TREE_CODE (TREE_VALUE (args)) != STRING_CST) { error ("%qE attribute argument not a string constant", name); *no_add_attrs = true; return NULL_TREE; } if (VAR_P (*node) && current_function_decl != NULL_TREE && !TREE_STATIC (*node)) { error ("section attribute cannot be specified for local variables"); *no_add_attrs = true; return NULL_TREE; } /* The decl may have already been given a section attribute from a previous declaration. Ensure they match. */ if (DECL_SECTION_NAME (*node) != NULL && strcmp (DECL_SECTION_NAME (*node), TREE_STRING_POINTER (TREE_VALUE (args))) != 0) { error ("section of %q+D conflicts with previous declaration", *node); *no_add_attrs = true; return NULL_TREE; } if (VAR_P (*node) && !targetm.have_tls && targetm.emutls.tmpl_section && DECL_THREAD_LOCAL_P (*node)) { error ("section of %q+D cannot be overridden", *node); *no_add_attrs = true; return NULL_TREE; } tree 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) return NULL_TREE; set_decl_section_name (*node, TREE_STRING_POINTER (TREE_VALUE (args))); return res; } /* Handle a "symver" and attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_symver_attribute (tree *node, tree name, tree args, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL && TREE_CODE (*node) != VAR_DECL) { warning (OPT_Wattributes, "% attribute only applies to functions and variables"); *no_add_attrs = true; return NULL_TREE; } if (!decl_in_symtab_p (*node)) { warning (OPT_Wattributes, "% attribute is only applicable to symbols"); *no_add_attrs = true; return NULL_TREE; } for (; args; args = TREE_CHAIN (args)) { tree symver = TREE_VALUE (args); if (TREE_CODE (symver) != STRING_CST) { error ("%qE attribute argument not a string constant", name); *no_add_attrs = true; return NULL_TREE; } const char *symver_str = TREE_STRING_POINTER (symver); int ats = 0; for (int n = 0; (int)n < TREE_STRING_LENGTH (symver); n++) if (symver_str[n] == '@') ats++; if (ats != 1 && ats != 2) { error ("symver attribute argument must have format %"); error ("% attribute argument %qs must contain one or two " "%<@%>", symver_str); *no_add_attrs = true; return NULL_TREE; } } return NULL_TREE; } /* Handle a "weak" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_weak_attribute (tree *node, tree name, tree, int, bool *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 (VAR_OR_FUNCTION_DECL_P (*node)) { struct symtab_node *n = symtab_node::get (*node); if (n && n->refuse_visibility_changes) error ("%q+D declared weak after being used", *node); declare_weak (*node); } else warning (OPT_Wattributes, "%qE attribute ignored", name); return NULL_TREE; } /* Handle a "noplt" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_noplt_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } return NULL_TREE; } /* Verify that argument value POS at position ARGNO to attribute ATNAME applied to function FNTYPE refers to a function parameter at position POS and is a valid integer type. When ZERO_BASED is true, POS is adjusted to be 1-based. If successful, POS is returned. Otherwise, issue appropriate warnings and return null. A non-zero 1-based ARGNO should be passed in by callers only for attributes with more than one argument. */ static tree positional_argument (const_tree fntype, const_tree atname, tree pos, int argno, bool zero_based) { tree postype = TREE_TYPE (pos); if (pos == error_mark_node || !postype) { /* Only mention the positional argument number when it's non-zero. */ if (argno < 1) warning (OPT_Wattributes, "%qE attribute argument is invalid", atname); else warning (OPT_Wattributes, "%qE attribute argument %i is invalid", atname, argno); return NULL_TREE; } if (!INTEGRAL_TYPE_P (postype)) { /* Handle this case specially to avoid mentioning the value of pointer constants in diagnostics. Only mention the positional argument number when it's non-zero. */ if (argno < 1) warning (OPT_Wattributes, "%qE attribute argument has type %qT", atname, postype); else warning (OPT_Wattributes, "%qE attribute argument %i has type %qT", atname, argno, postype); return NULL_TREE; } if (TREE_CODE (pos) != INTEGER_CST) { /* Only mention the argument number when it's non-zero. */ if (argno < 1) warning (OPT_Wattributes, "%qE attribute argument value %qE is not an integer " "constant", atname, pos); else warning (OPT_Wattributes, "%qE attribute argument %i value %qE is not an integer " "constant", atname, argno, pos); return NULL_TREE; } /* Validate the value of the position argument. If 0-based, then it should not be negative. If 1-based, it should be greater than zero. */ if ((zero_based && tree_int_cst_sgn (pos) < 0) || (!zero_based && tree_int_cst_sgn (pos) < 1)) { if (argno < 1) warning (OPT_Wattributes, "%qE attribute argument value %qE does not refer to " "a function parameter", atname, pos); else warning (OPT_Wattributes, "%qE attribute argument %i value %qE does not refer to " "a function parameter", atname, argno, pos); return NULL_TREE; } /* Adjust the value of pos to be 1-based. */ tree adjusted_pos = (zero_based) ? int_const_binop (PLUS_EXPR, pos, integer_one_node) : pos; if (!prototype_p (fntype)) return adjusted_pos; /* Verify that the argument position does not exceed the number of formal arguments to the function. */ unsigned nargs = type_num_arguments (fntype); if (!nargs || !tree_fits_uhwi_p (adjusted_pos) || !IN_RANGE (tree_to_uhwi (adjusted_pos), 1, nargs)) { if (argno < 1) warning (OPT_Wattributes, "%qE attribute argument value %qE exceeds the number " "of function parameters %u", atname, pos, nargs); else warning (OPT_Wattributes, "%qE attribute argument %i value %qE exceeds the number " "of function parameters %u", atname, argno, pos, nargs); return NULL_TREE; } /* Verify that the type of the referenced formal argument matches the expected type. */ unsigned HOST_WIDE_INT ipos = tree_to_uhwi (adjusted_pos); /* Zero was handled above. */ gcc_assert (ipos != 0); if (tree argtype = type_argument_type (fntype, ipos)) { /* Accept types that match INTEGRAL_TYPE_P except for bool. */ if (!INTEGRAL_TYPE_P (argtype) || TREE_CODE (argtype) == BOOLEAN_TYPE) { if (argno < 1) warning (OPT_Wattributes, "%qE attribute argument value %qE refers to " "parameter type %qT", atname, pos, argtype); else warning (OPT_Wattributes, "%qE attribute argument %i value %qE refers to " "parameter type %qT", atname, argno, pos, argtype); return NULL_TREE; } return adjusted_pos; } /* Argument position exceeding number of parameters was handled above. */ gcc_unreachable (); } /* Handle a "alloc_size" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_alloc_size_attribute (tree *node, tree name, tree args, int, bool *no_add_attrs) { tree fntype = *node; tree rettype = TREE_TYPE (fntype); if (!POINTER_TYPE_P (rettype)) { warning (OPT_Wattributes, "%qE attribute ignored on a function returning %qT", name, rettype); *no_add_attrs = true; return NULL_TREE; } /* The first argument SIZE_ARG is never null. */ tree size_arg = TREE_VALUE (args); tree next = TREE_CHAIN (args); /* NUM_ARG is null when the attribute includes just one argument, or is explictly set to null if it has been left uninitialized by the caller. */ tree num_arg = NULL_TREE; if (next != NULL_TREE) { if (TREE_VALUE (next) != TYPE_MIN_VALUE (d_int_type)) num_arg = TREE_VALUE (next); next = TREE_CHAIN (next); } /* If ZERO_ARG is set and true, arguments positions are treated as 0-based. Otherwise the default is 1-based. */ bool zero_based = false; if (next != NULL_TREE) zero_based = integer_truep (TREE_VALUE (next)); /* Update the argument values with the real argument position. */ if (tree val = positional_argument (fntype, name, size_arg, num_arg ? 1 : 0, zero_based)) TREE_VALUE (args) = val; else *no_add_attrs = true; if (num_arg != NULL_TREE) { args = TREE_CHAIN (args); if (tree val = positional_argument (fntype, name, num_arg, 2, zero_based)) TREE_VALUE (args) = val; else *no_add_attrs = true; } /* Terminate the original TREE_CHAIN in `args' to remove any remaining D-specific `alloc_size` arguments. */ TREE_CHAIN (args) = NULL_TREE; return NULL_TREE; } /* Handle a "cold" and attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_cold_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "no_sanitize" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_no_sanitize_attribute (tree *node, tree name, tree args, int, bool *no_add_attrs) { *no_add_attrs = true; if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute ignored", name); return NULL_TREE; } unsigned int flags = 0; for (; args; args = TREE_CHAIN (args)) { tree id = TREE_VALUE (args); if (TREE_CODE (id) != STRING_CST) { error ("%qE attribute argument not a string constant", name); return NULL_TREE; } char *string = ASTRDUP (TREE_STRING_POINTER (id)); flags |= parse_no_sanitize_attribute (string); } /* Store the flags argument back into no_sanitize attribute as an integer, merge existing flags if no_sanitize was previously handled. */ if (tree attr = lookup_attribute ("no_sanitize", DECL_ATTRIBUTES (*node))) { unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr)); flags |= old_value; if (flags != old_value) TREE_VALUE (attr) = build_int_cst (d_uint_type, flags); } else { DECL_ATTRIBUTES (*node) = tree_cons (get_identifier ("no_sanitize"), build_int_cst (d_uint_type, flags), DECL_ATTRIBUTES (*node)); } return NULL_TREE; } /* Handle a "register" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_register_attribute (tree *node, tree name, tree args, int, bool *no_add_attrs) { if (!VAR_P (*node)) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST) { error ("%qE attribute argument not a string constant", name); *no_add_attrs = true; } else if (TREE_STRING_LENGTH (TREE_VALUE (args)) == 0 || TREE_STRING_POINTER (TREE_VALUE (args))[0] == '\0') { error ("register name not specified for %q+D", *node); *no_add_attrs = true; } else { DECL_REGISTER (*node) = 1; set_user_assembler_name (*node, TREE_STRING_POINTER (TREE_VALUE (args))); DECL_HARD_REGISTER (*node) = 1; } return NULL_TREE; } /* Handle a "restrict" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_restrict_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { if (TREE_CODE (*node) == PARM_DECL && POINTER_TYPE_P (TREE_TYPE (*node))) { TREE_TYPE (*node) = build_qualified_type (TREE_TYPE (*node), TYPE_QUAL_RESTRICT); } else { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } return NULL_TREE; } /* Handle a "simd" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_simd_attribute (tree *node, tree name, tree args, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; return NULL_TREE; } tree omp_attr = get_identifier ("omp declare simd"); tree omp_flags = NULL_TREE; if (args) { tree id = TREE_VALUE (args); if (TREE_CODE (id) != STRING_CST) { error ("%qE attribute argument not a string constant", name); *no_add_attrs = true; return NULL_TREE; } if (strcmp (TREE_STRING_POINTER (id), "notinbranch") == 0) omp_flags = build_omp_clause (DECL_SOURCE_LOCATION (*node), OMP_CLAUSE_NOTINBRANCH); else if (strcmp (TREE_STRING_POINTER (id), "inbranch") == 0) omp_flags = build_omp_clause (DECL_SOURCE_LOCATION (*node), OMP_CLAUSE_INBRANCH); else { error ("only % and % flags are " "allowed for % attribute"); *no_add_attrs = true; return NULL_TREE; } } DECL_ATTRIBUTES (*node) = tree_cons (omp_attr, build_tree_list (NULL_TREE, omp_flags), DECL_ATTRIBUTES (*node)); return NULL_TREE; } /* Handle a "used" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_used_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { 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 "visibility" attribute; arguments as in struct attribute_spec.handler. */ static tree d_handle_visibility_attribute (tree *node, tree name, tree args, int, bool *) { /* If this is a type, set the visibility on the type decl. */ tree decl = *node; if (TYPE_P (decl)) { decl = TYPE_NAME (decl); if (decl == NULL_TREE || TREE_CODE (decl) != TYPE_DECL) { warning (OPT_Wattributes, "%qE attribute ignored on types", name); return NULL_TREE; } } if (decl_function_context (decl) != 0 || !TREE_PUBLIC (decl)) { warning (OPT_Wattributes, "%qE attribute ignored", name); return NULL_TREE; } tree id = TREE_VALUE (args); if (TREE_CODE (id) != STRING_CST) { error ("%qE attribute argument not a string constant", name); return NULL_TREE; } enum symbol_visibility vis; 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, "export"); else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES && lookup_attribute ("dllexport", attributes)) error ("%qD was declared %qs which implies default visibility", decl, "export"); } 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; }