diff --git a/gcc/d/d-attribs.cc b/gcc/d/d-attribs.cc index d371114a3c3..070866020a2 100644 --- a/gcc/d/d-attribs.cc +++ b/gcc/d/d-attribs.cc @@ -24,7 +24,9 @@ along with GCC; see the file COPYING3. If not see #include "dmd/attrib.h" #include "dmd/declaration.h" +#include "dmd/module.h" #include "dmd/mtype.h" +#include "dmd/template.h" #include "tree.h" #include "diagnostic.h" @@ -36,6 +38,8 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "attribs.h" #include "varasm.h" +#include "fold-const.h" +#include "opts.h" #include "d-tree.h" @@ -53,17 +57,25 @@ 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_always_inline_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_forceinline_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_alias_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_restrict_attribute (tree *, tree, tree, int, bool *); +static tree d_handle_used_attribute (tree *, tree, tree, int, bool *); /* Helper to define attribute exclusions. */ #define ATTR_EXCL(name, function, type, variable) \ @@ -72,6 +84,7 @@ static tree d_handle_weak_attribute (tree *, tree, tree, int, bool *) ; /* Define attributes that are mutually exclusive with one another. */ static const struct attribute_spec::exclusions attr_noreturn_exclusions[] = { + ATTR_EXCL ("alloc_size", true, true, true), ATTR_EXCL ("const", true, true, true), ATTR_EXCL ("malloc", true, true, true), ATTR_EXCL ("pure", true, true, true), @@ -87,6 +100,7 @@ static const struct attribute_spec::exclusions attr_returns_twice_exclusions[] = 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), @@ -96,15 +110,44 @@ static const struct attribute_spec::exclusions attr_const_pure_exclusions[] = 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 ("forceinline", true, true, true), + 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) \ @@ -139,8 +182,6 @@ const attribute_spec d_langhook_common_attribute_table[] = handle_type_generic_attribute, NULL), ATTR_SPEC ("fn spec", 1, 1, false, true, true, false, handle_fnspec_attribute, NULL), - ATTR_SPEC ("always_inline", 0, 0, true, false, false, false, - handle_always_inline_attribute, NULL), ATTR_SPEC (NULL, 0, 0, false, false, false, false, NULL, NULL), }; @@ -149,20 +190,38 @@ 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 ("forceinline", 0, 0, true, false, false, false, - d_handle_forceinline_attribute, attr_inline_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, NULL), + 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 ("alias", 1, 1, true, false, false, false, - d_handle_alias_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 ("restrict", 0, 0, true, false, false, false, + d_handle_restrict_attribute, NULL), + ATTR_SPEC ("used", 0, 0, true, false, false, false, + d_handle_used_attribute, NULL), ATTR_SPEC (NULL, 0, 0, false, false, false, false, NULL, NULL), }; @@ -254,11 +313,23 @@ build_attributes (Expressions *eattrs) Dsymbol *sym = attr->type->toDsymbol (0); if (!sym) - continue; + { + /* 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 = (Dsymbol *) sym->getModule (); - if (!(strcmp (mod->toChars (), "attribute") == 0 + Dsymbol *mod = sym->getModule (); + if (!(strcmp (mod->toChars (), "attributes") == 0 && mod->parent != NULL && strcmp (mod->parent->toChars (), "gcc") == 0 && !mod->parent->parent)) @@ -268,16 +339,24 @@ build_attributes (Expressions *eattrs) if (attr->op == TOKcall) attr = attr->ctfeInterpret (); + if (attr->op != TOKstructliteral) + { + 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. */ - gcc_assert (attr->op == TOKstructliteral); Expressions *elems = attr->isStructLiteralExp ()->elements; Expression *e0 = (*elems)[0]; if (e0->op != TOKstring) { - error ("expected string attribute, not %qs", e0->toChars ()); - return error_mark_node; + warning_at (make_location_t (attr->loc), OPT_Wattributes, + "unknown attribute %qs", e0->toChars()); + continue; } StringExp *se = e0->toStringExp (); @@ -292,9 +371,9 @@ build_attributes (Expressions *eattrs) const char *name = (const char *)(se->len ? se->string : ""); if (!uda_attribute_p (name)) { - warning_at (make_location_t (e0->loc), OPT_Wattributes, + warning_at (make_location_t (attr->loc), OPT_Wattributes, "unknown attribute %qs", name); - return error_mark_node; + continue; } /* Chain all attribute arguments together. */ @@ -303,6 +382,10 @@ build_attributes (Expressions *eattrs) 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) @@ -523,7 +606,8 @@ handle_nothrow_attribute (tree *node, tree, tree, int, bool *) return NULL_TREE; } -/* Handle a "type_generic" attribute. */ +/* Handle a "type generic" attribute; arguments as in + struct attribute_spec.handler. */ static tree handle_type_generic_attribute (tree *node, tree, tree, int, bool *) @@ -537,7 +621,8 @@ handle_type_generic_attribute (tree *node, tree, tree, int, bool *) return NULL_TREE; } -/* Handle a "transaction_pure" attribute. */ +/* Handle a "transaction_pure" attribute; arguments as in + struct attribute_spec.handler. */ static tree handle_transaction_pure_attribute (tree *node, tree, tree, int, bool *) @@ -548,7 +633,8 @@ handle_transaction_pure_attribute (tree *node, tree, tree, int, bool *) return NULL_TREE; } -/* Handle a "returns_twice" attribute. */ +/* Handle a "returns_twice" attribute; arguments as in + struct attribute_spec.handler. */ static tree handle_returns_twice_attribute (tree *node, tree, tree, int, bool *) @@ -572,30 +658,18 @@ handle_fnspec_attribute (tree *, tree, tree args, int, bool *) return NULL_TREE; } -/* Handle a "always_inline" attribute; arguments as in - struct attribute_spec.handler. */ - -static tree -handle_always_inline_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. */ +/* 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) { - Type *t = TYPE_LANG_FRONTEND (TREE_TYPE (*node)); - - if (t->ty == Tfunction) + if (TREE_CODE (*node) == FUNCTION_DECL) DECL_UNINLINABLE (*node) = 1; else { @@ -606,25 +680,16 @@ d_handle_noinline_attribute (tree *node, tree name, tree, int, return NULL_TREE; } -/* Handle a "forceinline" attribute. */ +/* Handle a "always_inline" attribute; arguments as in + struct attribute_spec.handler. */ static tree -d_handle_forceinline_attribute (tree *node, tree name, tree, int, - bool *no_add_attrs) +d_handle_always_inline_attribute (tree *node, tree name, tree, int, + bool *no_add_attrs) { - Type *t = TYPE_LANG_FRONTEND (TREE_TYPE (*node)); - - if (t->ty == Tfunction) + if (TREE_CODE (*node) == FUNCTION_DECL) { - tree attributes = DECL_ATTRIBUTES (*node); - - /* Push attribute always_inline. */ - if (!lookup_attribute ("always_inline", attributes)) - DECL_ATTRIBUTES (*node) = tree_cons (get_identifier ("always_inline"), - NULL_TREE, attributes); - DECL_DECLARED_INLINE_P (*node) = 1; - DECL_NO_INLINE_WARNING_P (*node) = 1; DECL_DISREGARD_INLINE_LIMITS (*node) = 1; } else @@ -636,15 +701,14 @@ d_handle_forceinline_attribute (tree *node, tree name, tree, int, return NULL_TREE; } -/* Handle a "flatten" attribute. */ +/* 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) { - Type *t = TYPE_LANG_FRONTEND (TREE_TYPE (*node)); - - if (t->ty != Tfunction) + if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; @@ -653,16 +717,15 @@ d_handle_flatten_attribute (tree *node, tree name, tree, int, return NULL_TREE; } -/* Handle a "target" attribute. */ +/* 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) { - Type *t = TYPE_LANG_FRONTEND (TREE_TYPE (*node)); - /* Ensure we have a function type. */ - if (t->ty != Tfunction) + if (TREE_CODE (*node) != FUNCTION_DECL) { warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; @@ -670,27 +733,241 @@ d_handle_target_attribute (tree *node, tree name, tree args, int flags, 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 "noclone" attribute. */ +/* 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) + { + if (! (cl_options[decoded_options[i].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); + + /* 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); + + /* Restore current options. */ + cl_optimization_restore (&global_options, &global_options_set, + &cur_opts); + 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) { - Type *t = TYPE_LANG_FRONTEND (TREE_TYPE (*node)); - - if (t->ty == Tfunction) + if (TREE_CODE (*node) != FUNCTION_DECL) { - tree attributes = DECL_ATTRIBUTES (*node); - - /* Push attribute noclone. */ - if (!lookup_attribute ("noclone", attributes)) - DECL_ATTRIBUTES (*node) = tree_cons (get_identifier ("noclone"), - NULL_TREE, attributes); + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; } - else + + 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; @@ -703,119 +980,121 @@ d_handle_noclone_attribute (tree *node, tree name, tree, int, struct attribute_spec.handler. */ static tree -d_handle_section_attribute (tree *node, tree, tree args, int, +d_handle_section_attribute (tree *node, tree name, tree args, int flags, bool *no_add_attrs) { - tree decl = *node; - - if (targetm_common.have_named_sections) + if (!targetm_common.have_named_sections) { - if (VAR_OR_FUNCTION_DECL_P (decl) - && TREE_CODE (TREE_VALUE (args)) == STRING_CST) - { - if (VAR_P (decl) - && current_function_decl != NULL_TREE - && !TREE_STATIC (decl)) - { - error_at (DECL_SOURCE_LOCATION (decl), - "section attribute cannot be specified for " - "local variables"); - *no_add_attrs = true; - } - - /* The decl may have already been given a section attribute - from a previous declaration. Ensure they match. */ - else if (DECL_SECTION_NAME (decl) != NULL - && strcmp (DECL_SECTION_NAME (decl), - TREE_STRING_POINTER (TREE_VALUE (args))) != 0) - { - error ("section of %q+D conflicts with previous declaration", - *node); - *no_add_attrs = true; - } - else if (VAR_P (decl) - && !targetm.have_tls && targetm.emutls.tmpl_section - && DECL_THREAD_LOCAL_P (decl)) - { - error ("section of %q+D cannot be overridden", *node); - *no_add_attrs = true; - } - else - set_decl_section_name (decl, - TREE_STRING_POINTER (TREE_VALUE (args))); - } - else - { - error ("section attribute not allowed for %q+D", *node); - *no_add_attrs = true; - } - } - else - { - error_at (DECL_SOURCE_LOCATION (*node), - "section attributes are not supported for this target"); + error ("section attributes are not supported for this target"); *no_add_attrs = true; + return NULL_TREE; } - 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 ("section attribute argument not a string constant"); + *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 an "alias" attribute; arguments as in +/* Handle a "symver" and attribute; arguments as in struct attribute_spec.handler. */ static tree -d_handle_alias_attribute (tree *node, tree name, tree args, int, - bool *no_add_attrs) +d_handle_symver_attribute (tree *node, tree, tree args, int, bool *no_add_attrs) { - tree decl = *node; + 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 (TREE_CODE (decl) != FUNCTION_DECL - && TREE_CODE (decl) != VAR_DECL) + if (!decl_in_symtab_p (*node)) { - warning (OPT_Wattributes, "%qE attribute ignored", name); + warning (OPT_Wattributes, + "% attribute is only applicable to symbols"); *no_add_attrs = true; return NULL_TREE; } - else if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl)) - || (TREE_CODE (decl) != FUNCTION_DECL - && TREE_PUBLIC (decl) && !DECL_EXTERNAL (decl)) - /* A static variable declaration is always a tentative definition, - but the alias is a non-tentative definition which overrides. */ - || (TREE_CODE (decl) != FUNCTION_DECL - && !TREE_PUBLIC (decl) && DECL_INITIAL (decl))) - { - error ("%q+D defined both normally and as %qE attribute", decl, name); - *no_add_attrs = true; - return NULL_TREE; - } - else if (decl_function_context (decl)) - { - error ("%q+D alias functions must be global", name); - *no_add_attrs = true; - return NULL_TREE; - } - else - { - tree id; - id = TREE_VALUE (args); - if (TREE_CODE (id) != STRING_CST) + for (; args; args = TREE_CHAIN (args)) + { + tree symver = TREE_VALUE (args); + if (TREE_CODE (symver) != STRING_CST) { - error ("attribute %qE argument not a string", name); + error ("% attribute argument not a string constant"); *no_add_attrs = true; return NULL_TREE; } - id = get_identifier (TREE_STRING_POINTER (id)); - /* This counts as a use of the object pointed to. */ - TREE_USED (id) = 1; - if (TREE_CODE (decl) == FUNCTION_DECL) - DECL_INITIAL (decl) = error_mark_node; - else - TREE_STATIC (decl) = 1; + const char *symver_str = TREE_STRING_POINTER (symver); - return NULL_TREE; + 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 @@ -843,3 +1122,279 @@ d_handle_weak_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) 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 "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 "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; +} diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc index a59f00d4f2a..b07068ed6ca 100644 --- a/gcc/d/decl.cc +++ b/gcc/d/decl.cc @@ -108,7 +108,7 @@ gcc_attribute_p (Dsymbol *decl) if (md && md->packages && md->packages->length == 1) { if (!strcmp ((*md->packages)[0]->toChars (), "gcc") - && !strcmp (md->id->toChars (), "attribute")) + && !strcmp (md->id->toChars (), "attributes")) return true; } @@ -1124,6 +1124,10 @@ get_symbol_decl (Declaration *decl) tree olddecl = decl->csym; decl->csym = get_symbol_decl (other); + /* Update the symbol location to the current definition. */ + if (DECL_EXTERNAL (decl->csym) && !DECL_INITIAL (decl->csym)) + DECL_SOURCE_LOCATION (decl->csym) = DECL_SOURCE_LOCATION (olddecl); + /* The current declaration is a prototype or marked extern, merge applied user attributes and return. */ if (DECL_EXTERNAL (olddecl) && !DECL_INITIAL (olddecl)) diff --git a/gcc/d/types.cc b/gcc/d/types.cc index 924d8298211..ec617407b3d 100644 --- a/gcc/d/types.cc +++ b/gcc/d/types.cc @@ -354,6 +354,7 @@ layout_aggregate_members (Dsymbols *members, tree context, bool inherited_p) const char *ident = var->ident ? var->ident->toChars () : NULL; tree field = create_field_decl (declaration_type (var), ident, inherited_p, inherited_p); + apply_user_attributes (var, field); insert_aggregate_field (context, field, var->offset); /* Because the front-end shares field decls across classes, don't @@ -403,6 +404,7 @@ layout_aggregate_members (Dsymbols *members, tree context, bool inherited_p) /* And make the corresponding data member. */ tree field = create_field_decl (type, NULL, 0, 0); + apply_user_attributes (ad, field); insert_aggregate_field (context, field, ad->anonoffset); continue; } diff --git a/gcc/testsuite/gdc.dg/attr_allocsize1.d b/gcc/testsuite/gdc.dg/attr_allocsize1.d new file mode 100644 index 00000000000..9efba197289 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_allocsize1.d @@ -0,0 +1,44 @@ +// { dg-do compile } + +import gcc.attributes; + +@alloc_size(1) +int ignoredfunc(int size); // { dg-warning ".alloc_size. attribute ignored on a function returning .int." } + +@alloc_size(0) int var; // { dg-warning ".alloc_size. attribute only applies to function types" } + +@attribute("alloc_size", "1") +void* invalid1(int size); // { dg-warning ".alloc_size. attribute argument is invalid" } + +@attribute("alloc_size", 1, "2") +void* invalid2(int count, int size); // { dg-warning ".alloc_size. attribute argument 2 is invalid" } + +@attribute("alloc_size", 0.1) +void* wrongtype1(int size); // { dg-warning ".alloc_size. attribute argument has type .double." } + +@attribute("alloc_size", 1, 0.2) +void* wrongtype2(int count, int size); // { dg-warning ".alloc_size. attribute argument 2 has type .double." } + +@alloc_size(0) +void* malloc0(int size); // { dg-warning ".alloc_size. attribute argument value .0. does not refer to a function parameter" } + +@alloc_size(1, 0) +void* malloc0(int count, int size); // { dg-warning ".alloc_size. attribute argument 2 value .0. does not refer to a function parameter" } + +@alloc_size(1, 0, true) +void* malloc0pos(int count, int size); + +@alloc_size(1, -1, true) +void* mallocminus1(int count, int size); // { dg-warning ".alloc_size. attribute argument 2 value .-1. does not refer to a function parameter" } + +@alloc_size(99) +void* malloc99(int size); // { dg-warning ".alloc_size. attribute argument value .99. exceeds the number of function parameters 1" } + +@alloc_size(1, 99) +void* malloc99(int count, int size); // { dg-warning ".alloc_size. attribute argument 2 value .99. exceeds the number of function parameters 2" } + +@alloc_size(1) +void* mallocdouble(double size); // { dg-warning ".alloc_size. attribute argument value .1. refers to parameter type .double." } + +@alloc_size(2, 1) +void* mallocdouble(int count, double size); // { dg-warning ".alloc_size. attribute argument 1 value .2. refers to parameter type .double." } diff --git a/gcc/testsuite/gdc.dg/attr_allocsize2.d b/gcc/testsuite/gdc.dg/attr_allocsize2.d new file mode 100644 index 00000000000..a10dbbee498 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_allocsize2.d @@ -0,0 +1,13 @@ +// { dg-do compile } + +import gcc.attributes; + +void* my_calloc(size_t num, size_t size) @allocSize(1, 0) +{ + return null; +} + +void* my_malloc(int a, int b, size_t size, int c) @allocSize(2) +{ + return null; +} diff --git a/gcc/testsuite/gdc.dg/attr_alwaysinline1.d b/gcc/testsuite/gdc.dg/attr_alwaysinline1.d new file mode 100644 index 00000000000..8fbe25cdfc1 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_alwaysinline1.d @@ -0,0 +1,17 @@ +// { dg-do compile } +// { dg-options "-O0" } + +import gcc.attributes; + +int func() +{ + int nested_function() @always_inline + { + return 13; + } + return nested_function(); +} + +@always_inline int var = 0; // { dg-warning ".always_inline. attribute ignored" } + +// { dg-final { scan-assembler-not "nested_function" } } diff --git a/gcc/testsuite/gdc.dg/attr_cold1.d b/gcc/testsuite/gdc.dg/attr_cold1.d new file mode 100644 index 00000000000..bf44d7b28d5 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_cold1.d @@ -0,0 +1,13 @@ +// { dg-do compile } +// { dg-options "-O2 -fdump-tree-optimized" } + +import gcc.attributes; + +int func() @cold +{ + return 0; +} + +@cold int var = 0; // { dg-warning ".cold. attribute ignored" } + +// { dg-final { scan-tree-dump "func\[^\r\n\]*(unlikely executed)" "optimized" } } diff --git a/gcc/testsuite/gdc.dg/attr_exclusions1.d b/gcc/testsuite/gdc.dg/attr_exclusions1.d new file mode 100644 index 00000000000..90d4e5d77e6 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_exclusions1.d @@ -0,0 +1,19 @@ +// { dg-do compile } + +import gcc.attributes; + +// always_inline + +@noinline +@always_inline +void i0(); // { dg-warning "ignoring attribute .always_inline. because it conflicts with attribute .noinline." } + +@target_clones("") +@always_inline +void i1(); // { dg-warning "ignoring attribute .always_inline. because it conflicts with attribute .target_clones." } + +// noinline + +@always_inline +@noinline +void n0(); // { dg-warning "ignoring attribute .noinline. because it conflicts with attribute .always_inline." } diff --git a/gcc/testsuite/gdc.dg/attr_exclusions2.d b/gcc/testsuite/gdc.dg/attr_exclusions2.d new file mode 100644 index 00000000000..791e9afeb48 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_exclusions2.d @@ -0,0 +1,19 @@ +// { dg-do compile { target x86_64-*-* } } + +import gcc.attributes; + +// target + +@target_clones("default") +@target("default") +void tc0(); // { dg-warning "ignoring attribute .target. because it conflicts with attribute .target_clones." } + +// target_clones + +@target("default") +@target_clones("default") +void t0(); // { dg-warning "ignoring attribute .target_clones. because it conflicts with attribute .target." } + +@always_inline +@target_clones("default") +void tc1(); // { dg-warning "ignoring attribute .target_clones. because it conflicts with attribute .always_inline." } diff --git a/gcc/testsuite/gdc.dg/attr_flatten1.d b/gcc/testsuite/gdc.dg/attr_flatten1.d new file mode 100644 index 00000000000..47bcc3883fb --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_flatten1.d @@ -0,0 +1,21 @@ +// { dg-do compile } +// { dg-options "-O1" } + +import gcc.attributes; + +int func() @flatten +{ + __gshared int count = 0; + int nested_function() + { + return count++; + } + static foreach (_; 0 .. 1000) + nested_function(); + + return nested_function(); +} + +@flatten int var = 0; // { dg-warning ".flatten. attribute ignored" } + +// { dg-final { scan-assembler-not "nested_function" } } diff --git a/gcc/testsuite/gdc.dg/attr_module.d b/gcc/testsuite/gdc.dg/attr_module.d new file mode 100644 index 00000000000..e7a93414fd0 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_module.d @@ -0,0 +1,40 @@ +// { dg-do compile } +// { dg-additional-sources "imports/attributes.d" } + +import gcc.attributes; + +@value_ignored +int f0() +{ + return 0; +} + +@type_symbol // { dg-warning ".type_symbol. attribute has no effect" } +int f1() +{ + return 1; +} + +@template_symbol // { dg-warning ".template_symbol. attribute has no effect" } +int f2() +{ + return 2; +} + +@struct_wrong_field(123) // { dg-warning "unknown attribute .123." } +int f3() +{ + return 3; +} + +@struct_void_init() +int f4() +{ + return 4; +} + +@unknown_attribute() // { dg-warning "unknown attribute .made up name." } +int f5() +{ + return 5; +} diff --git a/gcc/testsuite/gdc.dg/attr_noclone1.d b/gcc/testsuite/gdc.dg/attr_noclone1.d new file mode 100644 index 00000000000..0289135318a --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_noclone1.d @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +import gcc.attributes; + +@target_clones("avx", "default") +@noclone +void func() // { dg-error "clones for .target_clones. attribute cannot be created" } +{ // { dg-message "function .func. can never be copied because it has .noclone. attribute" "" { target *-*-* } .-1 } +} + +@noclone int var = 0; // { dg-warning ".noclone. attribute ignored" } diff --git a/gcc/testsuite/gdc.dg/attr_noicf1.d b/gcc/testsuite/gdc.dg/attr_noicf1.d new file mode 100644 index 00000000000..71a9aa10283 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_noicf1.d @@ -0,0 +1,30 @@ +// { dg-do compile } +// { dg-options "-O2 -fno-inline" } + +import gcc.attributes; + +extern int t(); + +int func() +{ + __gshared int var = 42; + int nested_1() @no_icf + { + return var++; + } + int nested_2() + { + return var++; + } + int nested_3() + { + return var++; + } + return nested_1() + nested_2() + nested_3(); +} + +@no_icf int var = 0; // { dg-warning ".no_icf. attribute ignored" } + +// { dg-final { scan-assembler "nested_1" } } +// { dg-final { scan-assembler "nested_2" } } +// { dg-final { scan-assembler-not "nested_3" } } diff --git a/gcc/testsuite/gdc.dg/attr_noinline1.d b/gcc/testsuite/gdc.dg/attr_noinline1.d new file mode 100644 index 00000000000..51fffe52e78 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_noinline1.d @@ -0,0 +1,19 @@ +// { dg-do compile } +// { dg-options "-O2 -finline-functions -fno-ipa-icf" } + +import gcc.attributes; + +extern int t(); + +void func() +{ + void nested_function() @noinline + { + t(); + } + nested_function(); +} + +@noinline int var = 0; // { dg-warning ".noinline. attribute ignored" } + +// { dg-final { scan-assembler "nested_function" } } diff --git a/gcc/testsuite/gdc.dg/attr_noipa1.d b/gcc/testsuite/gdc.dg/attr_noipa1.d new file mode 100644 index 00000000000..a1f58dc42c1 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_noipa1.d @@ -0,0 +1,17 @@ +// { dg-do compile } +// { dg-options "-O2 -finline-functions" } + +import gcc.attributes; + +int func(int x) +{ + int nested_function(int y, int z) @noipa + { + return y + z; + } + return nested_function(x, 0); +} + +@noipa int var = 0; // { dg-warning ".noipa. attribute ignored" } + +// { dg-final { scan-assembler "nested_function" } } diff --git a/gcc/testsuite/gdc.dg/attr_noplt1.d b/gcc/testsuite/gdc.dg/attr_noplt1.d new file mode 100644 index 00000000000..73a2fb53704 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_noplt1.d @@ -0,0 +1,13 @@ +// { dg-do compile { target x86_64-*-linux* } } +// { dg-options "-O2 -fno-pic" } + +import gcc.attributes; + +@noplt int func(); + +@noplt int var = 0; // { dg-warning ".noplt. attribute ignored" } + +int main() +{ + return func(); +} diff --git a/gcc/testsuite/gdc.dg/attr_optimize1.d b/gcc/testsuite/gdc.dg/attr_optimize1.d new file mode 100644 index 00000000000..043202a8f5f --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_optimize1.d @@ -0,0 +1,48 @@ +// { dg-do compile } + +import gcc.attributes; + +int func() +{ + int return_zero() @optimize(0) + { + return 0; + } + + int return_one() @optimize("0") + { + return 1; + } + + int return_two() @optimize("s") + { + return 2; + } + + int return_three() @optimize("O3") + { + return 3; + } + + int return_four() @optimize("fast-math") + { + return 4; + } + + return return_one + return_two + return_three + return_four; +} + +@optimize(3) +int var = 0; // { dg-warning ".optimize. attribute ignored" } + +@optimize("-f_") +int bad_option() // { dg-warning "bad option .-f_. to attribute .optimize." } +{ + return 0; +} + +@optimize("-z") +int bad_option2() // { dg-warning "bad option .-z. to attribute .optimize." } +{ + return 0; +} diff --git a/gcc/testsuite/gdc.dg/attr_optimize2.d b/gcc/testsuite/gdc.dg/attr_optimize2.d new file mode 100644 index 00000000000..13bc1d7cb6e --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_optimize2.d @@ -0,0 +1,9 @@ +// { dg-do compile } + +import gcc.attributes; + +@optimize(-1) +int non_negative() // { dg-error "argument to .-O. should be a non-negative integer" } +{ + return 0; +} diff --git a/gcc/testsuite/gdc.dg/attr_optimize3.d b/gcc/testsuite/gdc.dg/attr_optimize3.d new file mode 100644 index 00000000000..2a9ad123036 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_optimize3.d @@ -0,0 +1,19 @@ +// { dg-do compile } +// { dg-options "-O0 -fdump-tree-optimized-raw" } + +import gcc.attributes; + +double notfast(double x) +{ + return x * x * x * x * x * x * x * x; +} + +// { dg-final { scan-tree-dump-times "mult_expr, _" 7 "optimized" } } + +@fastmath +static double fast(double x) +{ + return x * x * x * x * x * x * x * x; +} + +// { dg-final { scan-tree-dump-times "mult_expr, powmult_" 3 "optimized" } } diff --git a/gcc/testsuite/gdc.dg/attr_optimize4.d b/gcc/testsuite/gdc.dg/attr_optimize4.d new file mode 100644 index 00000000000..54d793ed94f --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_optimize4.d @@ -0,0 +1,45 @@ +// { dg-do compile } +// { dg-options "-O3 -fdump-tree-optimized-raw" } + +import gcc.attributes; + +int glob1; +int easily_inlinable(int i) { glob1 = i; return 2; } + +@optStrategy("none") +int call_easily_inlinable(int i) +{ + return easily_inlinable(i); +} + +// { dg-final { scan-tree-dump "gimple_call gcc/$(am__dirstamp) gcc/attribute.lo: gcc/$(am__dirstamp) +gcc/attributes.lo: gcc/$(am__dirstamp) gcc/backtrace.lo: gcc/$(am__dirstamp) gcc/builtins.lo: gcc/$(am__dirstamp) gcc/deh.lo: gcc/$(am__dirstamp) diff --git a/libphobos/libdruntime/gcc/attribute.d b/libphobos/libdruntime/gcc/attribute.d index fc90d2a6a5a..d1ff5c71271 100644 --- a/libphobos/libdruntime/gcc/attribute.d +++ b/libphobos/libdruntime/gcc/attribute.d @@ -20,14 +20,7 @@ // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see // . +deprecated("Import gcc.attributes instead") module gcc.attribute; -private struct Attribute(A...) -{ - A args; -} - -auto attribute(A...)(A args) if (A.length > 0 && is(A[0] == string)) -{ - return Attribute!A(args); -} +public import gcc.attributes; diff --git a/libphobos/libdruntime/gcc/attributes.d b/libphobos/libdruntime/gcc/attributes.d new file mode 100644 index 00000000000..58a4023edca --- /dev/null +++ b/libphobos/libdruntime/gcc/attributes.d @@ -0,0 +1,605 @@ +// GNU D Compiler attribute support declarations. +// Copyright (C) 2021 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. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +module gcc.attributes; + +// Private helper templates. +private struct Attribute(A...) +{ + A arguments; +} + +private enum bool isStringValue(alias T) = is(typeof(T) == string); + +private enum bool isStringOrIntValue(alias T) + = is(typeof(T) == string) || is(typeof(T) == int); + +private template allSatisfy(alias F, T...) +{ + static if (T.length == 0) + enum allSatisfy = true; + else static if (T.length == 1) + enum allSatisfy = F!(T[0]); + else + { + enum allSatisfy = allSatisfy!(F, T[ 0 .. $/2]) + && allSatisfy!(F, T[$/2 .. $ ]); + } +} + +/** + * Generic entrypoint for applying GCC attributes to a function or type. + * There is no type checking done, as well as no deprecation path for + * attributes removed from the compiler. So the recommendation is to use any + , of the other UDAs available unless it is a target-specific attribute. + * + * Function attributes introduced by the @attribute UDA are used in the + * declaration of a function, followed by an attribute name string and + * any arguments separated by commas enclosed in parentheses. + * + * Example: + * --- + * import gcc.attributes; + * + * @attribute("regparm", 1) int func(int size); + * --- + */ +@system +auto attribute(A...)(A arguments) + if (A.length > 0 && is(A[0] == string)) +{ + return Attribute!A(arguments); +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Supported common attributes exposed by GDC. +// +/////////////////////////////////////////////////////////////////////////////// + +/** + * The `@alloc_size` attribute may be applied to a function that returns a + * pointer and takes at least one argument of an integer or enumerated type. + * It indicates that the returned pointer points to memory whose size is given + * by the function argument at `sizeArgIdx`, or by the product of the arguments + * at `sizeArgIdx` and `numArgIdx`. Meaningful sizes are positive values less + * than `ptrdiff_t.max`. Unless `zeroBasedNumbering` is true, argument + * numbering starts at one for ordinary functions, and at two for non-static + * member functions. + * + * If `numArgIdx` is less than `0`, it is taken to mean there is no argument + * specifying the element count. + * + * Example: + * --- + * import gcc.attributes; + * + * @alloc_size(1) extern(C) void* malloc(size_t size); + * @alloc_size(3,2) extern(C) void* reallocarray(void *ptr, size_t nmemb, + * size_t size); + * @alloc_size(1,2) void* my_calloc(size_t element_size, size_t count, + * bool irrelevant); + * --- + */ +auto alloc_size(int sizeArgIdx) +{ + return attribute("alloc_size", sizeArgIdx); +} + +/// ditto +auto alloc_size(int sizeArgIdx, int numArgIdx) +{ + return attribute("alloc_size", sizeArgIdx, numArgIdx); +} + +/// ditto +auto alloc_size(int sizeArgIdx, int numArgIdx, bool zeroBasedNumbering) +{ + return attribute("alloc_size", sizeArgIdx, numArgIdx, zeroBasedNumbering); +} + +auto alloc_size(A...)(A arguments) +{ + assert(false, "alloc_size attribute argument value is not an integer constant"); +} + +/** + * The `@always_inline` attribute inlines the function independent of any + * restrictions that otherwise apply to inlining. Failure to inline such a + * function is diagnosed as an error. + * + * Example: + * --- + * import gcc.attributes; + * + * @always_inline int func(); + * --- + */ +enum always_inline = attribute("always_inline"); + +/** + * The `@cold` attribute on functions is used to inform the compiler that the + * function is unlikely to be executed. The function is optimized for size + * rather than speed and on many targets it is placed into a special subsection + * of the text section so all cold functions appear close together, improving + * code locality of non-cold parts of program. The paths leading to calls of + * cold functions within code are considered to be cold too. + * + * Example: + * --- + * import gcc.attributes; + * + * @cold int func(); + * --- + */ +enum cold = attribute("cold"); + +/** + * The `@flatten` attribute is used to inform the compiler that every call + * inside this function should be inlined, if possible. Functions declared with + * attribute `@noinline` and similar are not inlined. + * + * Example: + * --- + * import gcc.attributes; + * + * @flatten int func(); + * --- + */ +enum flatten = attribute("flatten"); + +/** + * The `@no_icf` attribute prevents a functions from being merged with another + * semantically equivalent function. + * + * Example: + * --- + * import gcc.attributes; + * + * @no_icf int func(); + * --- + */ +enum no_icf = attribute("no_icf"); + +/** + * The `@noclone` attribute prevents a function from being considered for + * cloning - a mechanism that produces specialized copies of functions and + * which is (currently) performed by interprocedural constant propagation. + * + * Example: + * --- + * import gcc.attributes; + * + * @noclone int func(); + * --- + */ +enum noclone = attribute("noclone"); + +/** + * The `@noinline` attribute prevents a function from being considered for + * inlining. If the function does not have side effects, there are + * optimizations other than inlining that cause function calls to be optimized + * away, although the function call is live. To keep such calls from being + * optimized away, put `asm { ""; }` in the called function, to serve as a + * special side effect. + * + * Example: + * --- + * import gcc.attributes; + * + * @noinline int func(); + * --- + */ +enum noinline = attribute("noinline"); + +/** + * The `@noipa` attribute disables interprocedural optimizations between the + * function with this attribute and its callers, as if the body of the function + * is not available when optimizing callers and the callers are unavailable when + * optimizing the body. This attribute implies `@noinline`, `@noclone`, and + * `@no_icf` attributes. However, this attribute is not equivalent to a + * combination of other attributes, because its purpose is to suppress existing + * and future optimizations employing interprocedural analysis, including those + * that do not have an attribute suitable for disabling them individually. + * + * This attribute is supported mainly for the purpose of testing the compiler. + * + * Example: + * --- + * import gcc.attributes; + * + * @noipa int func(); + * --- + */ +enum noipa = attribute("noipa"); + +/** + * The `@optimize` attribute is used to specify that a function is to be + * compiled with different optimization options than specified on the command + * line. Valid `arguments` are constant non-negative integers and strings. + * Multiple arguments can be provided, separated by commas to specify multiple + * options. Each numeric argument specifies an optimization level. Each string + * argument that begins with the letter O refers to an optimization option such + * as `-O0` or `-Os`. Other options are taken as suffixes to the `-f` prefix + * jointly forming the name of an optimization option. + * + * Not every optimization option that starts with the `-f` prefix specified by + * the attribute necessarily has an effect on the function. The `@optimize` + * attribute should be used for debugging purposes only. It is not suitable in + * production code. + * + * Example: + * --- + * import gcc.attributes; + * + * @optimize(2) double fn0(double x); + * @optimize("2") double fn1(double x); + * @optimize("s") double fn2(double x); + * @optimize("Ofast") double fn3(double x); + * @optimize("-O2") double fn4(double x); + * @optimize("tree-vectorize") double fn5(double x); + * @optimize("-ftree-vectorize") double fn6(double x); + * @optimize("no-finite-math-only", 3) double fn7(double x); + * --- + */ +auto optimize(A...)(A arguments) + if (allSatisfy!(isStringOrIntValue, arguments)) +{ + return attribute("optimize", arguments); +} + +auto optimize(A...)(A arguments) + if (!allSatisfy!(isStringOrIntValue, arguments)) +{ + assert(false, "optimize attribute argument not a string or integer constant"); +} + +/** + * The `@restrict` attribute specifies that a function parameter is to be + * restrict-qualified in the C99 sense of the term. The parameter needs to + * boil down to either a pointer or reference type, such as a D pointer, + * class reference, or a `ref` parameter. + * + * Example: + * --- + * import gcc.attributes; + * + * void func(@restrict ref const float[16] array); + * --- + */ +enum restrict = attribute("restrict"); + +/** + * The `@section` attribute specifies that a function lives in a particular + * section. For when you need certain particular functions to appear in + * special sections. + * + * Some file formats do not support arbitrary sections so the section attribute + * is not available on all platforms. If you need to map the entire contents + * of a module to a particular section, consider using the facilities of the + * linker instead. + * + * Example: + * --- + * import gcc.attributes; + * + * @section("bar") extern void func(); + * --- + */ +auto section(string sectionName) +{ + return attribute("section", sectionName); +} + +auto section(A...)(A arguments) +{ + assert(false, "section attribute argument not a string constant"); +} + +/** + * The `@symver` attribute creates a symbol version on ELF targets. The syntax + * of the string parameter is `name@nodename`. The `name` part of the parameter + * is the actual name of the symbol by which it will be externally referenced. + * The `nodename` portion should be the name of a node specified in the version + * script supplied to the linker when building a shared library. Versioned + * symbol must be defined and must be exported with default visibility. + * + * Finally if the parameter is `name@@nodename` then in addition to creating a + * symbol version (as if `name@nodename` was used) the version will be also used + * to resolve `name` by the linker. + * + * Example: + * --- + * import gcc.attributes; + * + * @symver("foo@VERS_1") int foo_v1(); + * --- + */ +auto symver(A...)(A arguments) + if (allSatisfy!(isStringValue, arguments)) +{ + return attribute("symver", arguments); +} + +auto symver(A...)(A arguments) + if (!allSatisfy!(isStringValue, arguments)) +{ + assert(false, "symver attribute argument not a string constant"); +} + +/** + * The `@target` attribute is used to specify that a function is to be + * compiled with different target options than specified on the command line. + * One or more strings can be provided as arguments, separated by commas to + * specify multiple options. Each string consists of one or more + * comma-separated suffixes to the `-m` prefix jointly forming the name of a + * machine-dependent option. + * + * The target attribute can be used for instance to have a function compiled + * with a different ISA (instruction set architecture) than the default. + * + * The options supported are specific to each target. + * + * Example: + * --- + * import gcc.attributes; + * + * @target("arch=core2") void core2_func(); + * @target("sse3") void sse3_func(); + * --- + */ +auto target(A...)(A arguments) + if (allSatisfy!(isStringValue, arguments)) +{ + return attribute("target", arguments); +} + +auto target(A...)(A arguments) + if (!allSatisfy!(isStringValue, arguments)) +{ + assert(false, "target attribute argument not a string constant"); +} + +/** + * The `@target_clones` attribute is used to specify that a function be cloned + * into multiple versions compiled with different target `options` than + * specified on the command line. The supported options and restrictions are + * the same as for `@target` attribute. + * + * It also creates a resolver function that dynamically selects a clone suitable + * for current architecture. The resolver is created only if there is a usage + * of a function with `@target_clones` attribute. + * + * Example: + * --- + * import gcc.attributes; + * + * @target_clones("sse4.1,avx,default") double func(double x); + * --- + */ +auto target_clones(A...)(A arguments) + if (allSatisfy!(isStringValue, arguments)) +{ + return attribute("target_clones", arguments); +} + +auto target_clones(A...)(A arguments) + if (!allSatisfy!(isStringValue, arguments)) +{ + assert(false, "target attribute argument not a string constant"); +} + +/** + * The `@used` attribute, annotated to a function, means that code must be + * emitted for the function even if it appears that the function is not + * referenced. This is useful, for example, when the function is referenced + * only in inline assembly. + * + * Example: + * --- + * import gcc.attributes; + * + * @used __gshared int var = 0x1000; + * --- + */ +enum used = attribute("used"); + +/** + * The `@weak` attribute causes a declaration of an external symbol to be + * emitted as a weak symbol rather than a global. This is primarily useful in + * defining library functions that can be overridden in user code, though it can + * also be used with non-function declarations. The overriding symbol must have + * the same type as the weak symbol. In addition, if it designates a variable + * it must also have the same size and alignment as the weak symbol. + * + * Weak symbols are supported for ELF targets, and also for a.out targets when + * using the GNU assembler and linker. + * + * Example: + * --- + * import gcc.attributes; + * + * @weak int func() { return 1; } + * --- + */ +enum weak = attribute("weak"); + +/** + * The `@noplt` attribute is the counterpart to option `-fno-plt`. Calls to + * functions marked with this attribute in position-independent code do not use + * the PLT in position-independent code. + * + * In position-dependant code, a few targets also convert call to functions + * that are marked to not use the PLT to use the GOT instead. + * + * Example: + * --- + * import gcc.attributes; + * + * @noplt int func(); + * + * --- + */ +enum noplt = attribute("noplt"); + +/////////////////////////////////////////////////////////////////////////////// +// +// Attributes defined for compatibility with LDC. +// +/////////////////////////////////////////////////////////////////////////////// + +/** + * Specifies that the function returns `null` or a pointer to at least a + * certain number of allocated bytes. `sizeArgIdx` and `numArgIdx` specify + * the 0-based index of the function arguments that should be used to calculate + * the number of bytes returned. + * + * Example: + * --- + * import gcc.attributes; + * + * @allocSize(0) extern(C) void* malloc(size_t size); + * @allocSize(2,1) extern(C) void* reallocarray(void *ptr, size_t nmemb, + * size_t size); + * @allocSize(0,1) void* my_calloc(size_t element_size, size_t count, + * bool irrelevant); + * --- + */ +auto allocSize(int sizeArgIdx, int numArgIdx = int.min) +{ + return alloc_size(sizeArgIdx, numArgIdx, true); +} + +auto allocSize(A...)(A arguments) +{ + assert(false, "allocSize attribute argument value is not an integer constant"); +} + +/** + * When applied to a global symbol, the compiler, assembler, and linker are + * required to treat the symbol as if there is a reference to the symbol that + * it cannot see (which is why they have to be named). For example, it + * prevents the deletion by the linker of an unreferenced symbol. + * + * Example: + * --- + * import gcc.attributes; + * + * @assumeUsed __gshared int var = 0x1000; + * --- + */ +alias assumeUsed = used; + +/// This attribute has no effect. +enum dynamicCompile = false; + +/// ditto +enum dynamicCompileConst = false; + +/// ditto +enum dynamicCompileEmit = false; + +/** + * Explicitly sets "fast-math" for a function, enabling aggressive math + * optimizations. These optimizations may dramatically change the outcome of + * floating point calculations (e.g. because of reassociation). + * + * Example: + * --- + * import gcc.attributes; + * + * @fastmath + * double dot(double[] a, double[] b) { + * double s = 0; + * foreach(size_t i; 0..a.length) + * { + * // will result in vectorized fused-multiply-add instructions + * s += a * b; + * } + * return s; + * } + * --- + */ +enum fastmath = optimize("Ofast"); + +/** + * Adds GCC's "naked" attribute to a function, disabling function prologue / + * epilogue emission. + * Intended to be used in combination with basic `asm` statement. While using + * extended `asm` or a mixture of basic `asm` and D code may appear to work, + * they cannot be depended upon to work reliably and are not supported. + * + * Example: + * --- + * import gcc.attributes; + * + * @naked void abort() { + * asm { "ud2"; } + * } + * --- + */ +enum naked = attribute("naked"); + +/** + * Sets the optimization strategy for a function. + * Valid strategies are "none", "optsize", "minsize". The strategies are + * mutually exclusive. + * + * Example: + * --- + * import gcc.attributes; + * + * @optStrategy("none") + * int func() { + * return call(); + * } + * --- + */ +auto optStrategy(string strategy) +{ + if (strategy == "none") + return optimize("O0"); + else if (strategy == "optsize" || strategy == "minsize") + return optimize("Os"); + else + { + assert(false, "unrecognized parameter `" ~ strategy + ~ "` for `gcc.attribute.optStrategy`"); + } +} + +auto optStrategy(A...)(A arguments) +{ + assert(false, "optStrategy attribute argument value is not a string constant"); +} + +/** + * When applied to a function, specifies that the function should be optimzed + * by Graphite, GCC's polyhedral optimizer. Useful for optimizing loops for + * data locality, vectorization and parallelism. + * + * Experimental! + * + * Only effective when GDC was built with ISL included. + */ +enum polly = optimize("loop-parallelize-all", "loop-nest-optimize"); diff --git a/libphobos/libdruntime/gcc/deh.d b/libphobos/libdruntime/gcc/deh.d index c747b5132d1..2e679320c38 100644 --- a/libphobos/libdruntime/gcc/deh.d +++ b/libphobos/libdruntime/gcc/deh.d @@ -28,7 +28,7 @@ import gcc.unwind; import gcc.unwind.pe; import gcc.builtins; import gcc.config; -import gcc.attribute; +import gcc.attributes; extern(C) {