From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1873) id A057D3858D32; Wed, 15 Jun 2022 18:11:50 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A057D3858D32 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Iain Buclaw To: gcc-cvs@gcc.gnu.org Subject: [gcc r13-1113] d: Add `@visibility' and `@hidden' attributes. X-Act-Checkin: gcc X-Git-Author: Iain Buclaw X-Git-Refname: refs/heads/master X-Git-Oldrev: 49d14a841fd9a798fe6d68ae49c6fbb753d21032 X-Git-Newrev: 636b01ab4910da0b96d844301fea1a2b56c5344d Message-Id: <20220615181150.A057D3858D32@sourceware.org> Date: Wed, 15 Jun 2022 18:11:50 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 15 Jun 2022 18:11:50 -0000 https://gcc.gnu.org/g:636b01ab4910da0b96d844301fea1a2b56c5344d commit r13-1113-g636b01ab4910da0b96d844301fea1a2b56c5344d Author: Iain Buclaw Date: Wed Jun 15 19:44:36 2022 +0200 d: Add `@visibility' and `@hidden' attributes. The `@visibility' attribute is functionality the same as `__attribute__((visibility))', and `@hidden' is a convenience alias to `@visibility("hidden")' defined in the `gcc.attributes' module. As the visibility of a symbol is also indirectly controlled by the `export' keyword, the handling of this in the code generation pass has been improved so that conflicts will be appropriately diagnosed. gcc/d/ChangeLog: * d-attribs.cc (d_langhook_attribute_table): Add visibility. (insert_type_attribute): Use decl_attributes instead of merge_attributes. (insert_decl_attribute): Likewise. (apply_user_attributes): Do nothing when no UDAs applied. (d_handle_visibility_attribute): New function. * d-gimplify.cc (d_gimplify_binary_expr): Adjust. * d-tree.h (set_visibility_for_decl): Declare. * decl.cc (get_symbol_decl): Move setting of visibility flags to... (set_visibility_for_decl): ... here. New function. * types.cc (TypeVisitor::visit (TypeStruct *)): Call set_visibility_for_decl(). (TypeVisitor::visit (TypeClass *)): Likewise. gcc/testsuite/ChangeLog: * gdc.dg/attr_visibility1.d: New test. * gdc.dg/attr_visibility2.d: New test. * gdc.dg/attr_visibility3.d: New test. libphobos/ChangeLog: * libdruntime/gcc/attributes.d (visibility): Define. (hidden): Define. Diff: --- gcc/d/d-attribs.cc | 98 ++++++++++++++++++++++++++++----- gcc/d/d-gimplify.cc | 4 +- gcc/d/d-tree.h | 1 + gcc/d/decl.cc | 54 ++++++++++++------ gcc/d/types.cc | 2 + gcc/testsuite/gdc.dg/attr_visibility1.d | 25 +++++++++ gcc/testsuite/gdc.dg/attr_visibility2.d | 26 +++++++++ gcc/testsuite/gdc.dg/attr_visibility3.d | 29 ++++++++++ libphobos/libdruntime/gcc/attributes.d | 34 ++++++++++++ 9 files changed, 241 insertions(+), 32 deletions(-) diff --git a/gcc/d/d-attribs.cc b/gcc/d/d-attribs.cc index b8ce30cf3c9..4c6e7a7a4bd 100644 --- a/gcc/d/d-attribs.cc +++ b/gcc/d/d-attribs.cc @@ -77,6 +77,7 @@ 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 *); +static tree d_handle_visibility_attribute (tree *, tree, tree, int, bool *); /* Helper to define attribute exclusions. */ #define ATTR_EXCL(name, function, type, variable) \ @@ -223,6 +224,8 @@ const attribute_spec d_langhook_attribute_table[] = d_handle_restrict_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), }; @@ -238,10 +241,9 @@ insert_type_attribute (tree type, const char *attrname, tree value) if (value) value = tree_cons (NULL_TREE, value, NULL_TREE); - tree attribs = merge_attributes (TYPE_ATTRIBUTES (type), - tree_cons (ident, value, NULL_TREE)); - - return build_type_attribute_variant (type, attribs); + 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. */ @@ -254,10 +256,9 @@ insert_decl_attribute (tree decl, const char *attrname, tree value) if (value) value = tree_cons (NULL_TREE, value, NULL_TREE); - tree attribs = merge_attributes (DECL_ATTRIBUTES (decl), - tree_cons (ident, value, NULL_TREE)); + decl_attributes (&decl, build_tree_list (ident, value), 0); - return build_decl_attribute_variant (decl, attribs); + return decl; } /* Returns TRUE if NAME is an attribute recognized as being handled by @@ -414,12 +415,7 @@ void apply_user_attributes (Dsymbol *sym, tree node) { if (!sym->userAttribDecl) - { - if (DECL_P (node) && DECL_ATTRIBUTES (node) != NULL) - decl_attributes (&node, DECL_ATTRIBUTES (node), 0); - - return; - } + return; location_t saved_location = input_location; input_location = make_location_t (sym->loc); @@ -1412,3 +1408,79 @@ d_handle_used_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) 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 ("visibility argument not a string"); + 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; +} diff --git a/gcc/d/d-gimplify.cc b/gcc/d/d-gimplify.cc index 36b76da3acc..33fe65c3466 100644 --- a/gcc/d/d-gimplify.cc +++ b/gcc/d/d-gimplify.cc @@ -235,10 +235,10 @@ d_gimplify_binary_expr (tree *expr_p) if (bit_field_ref (op0) || bit_field_ref (op1)) { if (TREE_TYPE (op0) != TREE_TYPE (*expr_p)) - TREE_OPERAND (*expr_p, 0) = convert (TREE_TYPE (*expr_p), op0); + TREE_OPERAND (*expr_p, 0) = convert (TREE_TYPE (*expr_p), op0); if (TREE_TYPE (op1) != TREE_TYPE (*expr_p)) - TREE_OPERAND (*expr_p, 1) = convert (TREE_TYPE (*expr_p), op1); + TREE_OPERAND (*expr_p, 1) = convert (TREE_TYPE (*expr_p), op1); return GS_OK; } diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h index 48a40a6afa4..751746395e6 100644 --- a/gcc/d/d-tree.h +++ b/gcc/d/d-tree.h @@ -654,6 +654,7 @@ extern tree build_artificial_decl (tree, tree, const char * = NULL); extern tree create_field_decl (tree, const char *, int, int); extern void build_type_decl (tree, Dsymbol *); extern void set_linkage_for_decl (tree); +extern void set_visibility_for_decl (tree, Dsymbol *); /* In expr.cc. */ extern tree build_expr (Expression *, bool = false, bool = false); diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc index 518d84c1a49..8676a1b588b 100644 --- a/gcc/d/decl.cc +++ b/gcc/d/decl.cc @@ -1343,27 +1343,10 @@ get_symbol_decl (Declaration *decl) if (decl->storage_class & STCvolatile) TREE_THIS_VOLATILE (decl->csym) = 1; - /* Visibility attributes are used by the debugger. */ - if (decl->visibility.kind == Visibility::private_) - TREE_PRIVATE (decl->csym) = 1; - else if (decl->visibility.kind == Visibility::protected_) - TREE_PROTECTED (decl->csym) = 1; - /* Likewise, so could the deprecated attribute. */ if (decl->storage_class & STCdeprecated) TREE_DEPRECATED (decl->csym) = 1; -#if TARGET_DLLIMPORT_DECL_ATTRIBUTES - /* Have to test for import first. */ - if (decl->isImportedSymbol ()) - { - insert_decl_attribute (decl->csym, "dllimport"); - DECL_DLLIMPORT_P (decl->csym) = 1; - } - else if (decl->isExport ()) - insert_decl_attribute (decl->csym, "dllexport"); -#endif - if (decl->isDataseg () || decl->isCodeseg () || decl->isThreadlocal ()) { /* Set TREE_PUBLIC by default, but allow private template to override. */ @@ -1374,6 +1357,9 @@ get_symbol_decl (Declaration *decl) /* The decl has not been defined -- yet. */ DECL_EXTERNAL (decl->csym) = 1; + /* Visibility attributes are used by the debugger. */ + set_visibility_for_decl (decl->csym, decl); + DECL_INSTANTIATED (decl->csym) = (decl->isInstantiated () != NULL); set_linkage_for_decl (decl->csym); } @@ -2447,3 +2433,37 @@ set_linkage_for_decl (tree decl) if (DECL_ARTIFICIAL (decl)) return d_weak_linkage (decl); } + +/* NODE is a FUNCTION_DECL, VAR_DECL or RECORD_TYPE for the declaration SYM. + Set flags to reflect visibility that NODE will get in the object file. */ + +void +set_visibility_for_decl (tree node, Dsymbol *sym) +{ + Visibility visibility = sym->visible (); + if (visibility.kind == Visibility::private_) + TREE_PRIVATE (node) = 1; + else if (visibility.kind == Visibility::protected_) + TREE_PROTECTED (node) = 1; + + /* If the declaration was declared `export', append either the dllimport + or dllexport attribute. */ + if (TARGET_DLLIMPORT_DECL_ATTRIBUTES) + { + const char *attrname = NULL; + + /* Have to test for import first. */ + if (sym->isImportedSymbol ()) + attrname = "dllimport"; + else if (sym->isExport ()) + attrname = "dllexport"; + + if (attrname != NULL) + { + if (DECL_P (node)) + insert_decl_attribute (node, attrname); + else if (TYPE_P (node)) + insert_type_attribute (node, attrname); + } + } +} diff --git a/gcc/d/types.cc b/gcc/d/types.cc index 0926715b7dc..b706c91560e 100644 --- a/gcc/d/types.cc +++ b/gcc/d/types.cc @@ -1180,6 +1180,7 @@ public: /* Put out all fields. */ layout_aggregate_type (t->sym, t->ctype, t->sym); build_type_decl (t->ctype, t->sym); + set_visibility_for_decl (t->ctype, t->sym); apply_user_attributes (t->sym, t->ctype); finish_aggregate_type (structsize, alignsize, t->ctype); } @@ -1224,6 +1225,7 @@ public: /* Put out all fields, including from each base class. */ layout_aggregate_type (t->sym, basetype, t->sym); build_type_decl (basetype, t->sym); + set_visibility_for_decl (basetype, t->sym); apply_user_attributes (t->sym, basetype); finish_aggregate_type (t->sym->structsize, t->sym->alignsize, basetype); diff --git a/gcc/testsuite/gdc.dg/attr_visibility1.d b/gcc/testsuite/gdc.dg/attr_visibility1.d new file mode 100644 index 00000000000..a7ed4065605 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_visibility1.d @@ -0,0 +1,25 @@ +// { dg-do compile } +// { dg-require-visibility "" } + +import gcc.attributes; + +void nested() +{ + @attribute("visibility", "default") + struct nested_struct { } // { dg-warning ".visibility. attribute ignored" } + + @attribute("visibility", "default") + void nested_func() { } // { dg-warning ".visibility. attribute ignored" } +} + +@attribute("visibility", 123) +int not_a_string(); // { dg-error "visibility argument not a string" } + +@attribute("visibility", "invalid argument") +int invalid_argument(); // { dg-error ".visibility. argument must be one of" } + +@attribute("visibility", "default") +int redeclared_visibility(); + +@attribute("visibility", "internal") +int redeclared_visibility(); // { dg-error "redeclared with different visibility" } diff --git a/gcc/testsuite/gdc.dg/attr_visibility2.d b/gcc/testsuite/gdc.dg/attr_visibility2.d new file mode 100644 index 00000000000..a3398822647 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_visibility2.d @@ -0,0 +1,26 @@ +// { dg-do compile } +// { dg-require-visibility "" } + +module attr_visibility2; + +import gcc.attributes; + +// { dg-final { scan-hidden "_D16attr_visibility25func1FZv" } } + +@hidden void func1() { } + +// { dg-final { scan-hidden "_D16attr_visibility25func2FZv" } } + +@hidden void func2(); + +void func2() { } + +// { dg-final { scan-hidden "_D16attr_visibility25func3FZv" } } + +void func3(); + +@hidden void func3() { } + +// { dg-final { scan-hidden "_D16attr_visibility210globalvar1i" } } + +@hidden __gshared int globalvar1 = 5; diff --git a/gcc/testsuite/gdc.dg/attr_visibility3.d b/gcc/testsuite/gdc.dg/attr_visibility3.d new file mode 100644 index 00000000000..32984287d89 --- /dev/null +++ b/gcc/testsuite/gdc.dg/attr_visibility3.d @@ -0,0 +1,29 @@ +// { dg-do compile } +// { dg-require-visibility "" } +// { dg-require-dll "" } + +import gcc.attributes; + +@visibility("hidden") +export void func1(); // { dg-error ".func1. was declared .export." } + +@visibility("hidden") +export void func2() { } // { dg-error ".func2. was declared .export." } + +@visibility("default") +export void func3(); + +@visibility("default") +export void func4() { }; + +@visibility("hidden") +export struct type1 { } // { dg-error ".type1. was declared .export." } + +@visibility("default") +export struct type2 { } + +@visibility("hidden") +export class type3 { } // { dg-error ".type3. was declared .export." } + +@visibility("default") +export class type4 { } diff --git a/libphobos/libdruntime/gcc/attributes.d b/libphobos/libdruntime/gcc/attributes.d index 09f684cd44e..ca066cef822 100644 --- a/libphobos/libdruntime/gcc/attributes.d +++ b/libphobos/libdruntime/gcc/attributes.d @@ -424,6 +424,30 @@ auto target_clones(A...)(A arguments) */ enum used = attribute("used"); +/** + * The `@visibility` attribute affects the linkage of the declaration to which + * it is attached. It can be applied to variables, types, and functions. + * + * There are four supported visibility_type values: `default`, `hidden`, + * `protected`, or `internal` visibility. + * + * Example: + * --- + * import gcc.attributes; + * + * @visibility("protected") void func() { } + * --- + */ +auto visibility(string visibilityName) +{ + return attribute("visibility", visibilityName); +} + +auto visibility(A...)(A arguments) +{ + assert(false, "visibility attribute argument not a string constant"); +} + /** * 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 @@ -542,6 +566,16 @@ enum dynamicCompileEmit = false; */ enum fastmath = optimize("Ofast"); +/** + * Sets the visibility of a function or global variable to "hidden". + * Such symbols aren't directly accessible from outside the DSO + * (executable or DLL/.so/.dylib) and are resolved inside the DSO + * during linking. If unreferenced within the DSO, the linker can + * strip a hidden symbol. + * An `export` visibility overrides this attribute. + */ +enum hidden = visibility("hidden"); + /** * Adds GCC's "naked" attribute to a function, disabling function prologue / * epilogue emission.