| /* d-attribs.c -- D attributes handling. |
| Copyright (C) 2015-2022 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 |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* 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 %<target%>"); |
| *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 <const char *, va_gc> *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 %<optimize%>", 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 %<optimize%>", |
| 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, |
| "%<symver%> attribute only applies to functions and variables"); |
| *no_add_attrs = true; |
| return NULL_TREE; |
| } |
| |
| if (!decl_in_symtab_p (*node)) |
| { |
| warning (OPT_Wattributes, |
| "%<symver%> 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 %<name@nodename%>"); |
| error ("%<symver%> 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 %<inbranch%> and %<notinbranch%> flags are " |
| "allowed for %<simd%> 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; |
| } |