public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-1113] d: Add `@visibility' and `@hidden' attributes.
@ 2022-06-15 18:11 Iain Buclaw
  0 siblings, 0 replies; only message in thread
From: Iain Buclaw @ 2022-06-15 18:11 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:636b01ab4910da0b96d844301fea1a2b56c5344d

commit r13-1113-g636b01ab4910da0b96d844301fea1a2b56c5344d
Author: Iain Buclaw <ibuclaw@gdcproject.org>
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.


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-06-15 18:11 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-15 18:11 [gcc r13-1113] d: Add `@visibility' and `@hidden' attributes Iain Buclaw

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).