diff mbox series

[committed] d: Add `@register' attribute to compiler and library (PR105413)

Message ID 20220624185826.3907247-1-ibuclaw@gdcproject.org
State New
Headers show
Series [committed] d: Add `@register' attribute to compiler and library (PR105413) | expand

Commit Message

Iain Buclaw June 24, 2022, 6:58 p.m. UTC
Hi,

This patch adds a new `@register' attribute to the D compiler and
library.  Addressing a feature request in PR105413.

The `@register` attribute specifies that a local or `__gshared` variable
is to be given a register storage-class in the C sense of the term, and
will be placed into a register named `registerName`.

The variable needs to boiled down to a data type that fits the target
register.  It also cannot have either thread-local or `extern` storage.
It is an error to take the address of a register variable.

Bootstrapped and regression tested on x86_64-linux-gnu/-m32/-mx32, and
committed to mainline.

Regards,
Iain.

---
gcc/d/ChangeLog:

	* d-attribs.cc (d_handle_register_attribute): New function.
	(d_langhook_attribute_table): Add register attribute.
	* d-codegen.cc (d_mark_addressable): Error if taken address of
	register variable.
	(build_frame_type): Error if register variable has non-local
	references.
	* d-tree.h (d_mark_addressable): Add complain parameter.
	* decl.cc (get_symbol_decl): Mark register varibles DECL_REGISTER.
	Error when register variable declared thread-local or extern.
	* expr.cc (ExprVisitor::visit (IndexExp *)): Don't complain about
	marking register vectors as addressable in an ARRAY_REF.

libphobos/ChangeLog:

	* libdruntime/gcc/attributes.d (register): Define.

gcc/testsuite/ChangeLog:

	* gdc.dg/attr_register1.d: New test.
	* gdc.dg/attr_register2.d: New test.
	* gdc.dg/attr_register3.d: New test.
---
 gcc/d/d-attribs.cc                     | 40 ++++++++++++++++++-
 gcc/d/d-codegen.cc                     | 32 ++++++++++++---
 gcc/d/d-tree.h                         |  2 +-
 gcc/d/decl.cc                          | 24 ++++++++++-
 gcc/d/expr.cc                          |  2 +-
 gcc/testsuite/gdc.dg/attr_register1.d  | 55 ++++++++++++++++++++++++++
 gcc/testsuite/gdc.dg/attr_register2.d  | 11 ++++++
 gcc/testsuite/gdc.dg/attr_register3.d  | 22 +++++++++++
 libphobos/libdruntime/gcc/attributes.d | 28 +++++++++++++
 9 files changed, 204 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/gdc.dg/attr_register1.d
 create mode 100644 gcc/testsuite/gdc.dg/attr_register2.d
 create mode 100644 gcc/testsuite/gdc.dg/attr_register3.d
diff mbox series

Patch

diff --git a/gcc/d/d-attribs.cc b/gcc/d/d-attribs.cc
index 4b54426be39..23bbe3946fb 100644
--- a/gcc/d/d-attribs.cc
+++ b/gcc/d/d-attribs.cc
@@ -75,6 +75,7 @@  static tree d_handle_weak_attribute (tree *, tree, tree, int, bool *) ;
 static tree d_handle_noplt_attribute (tree *, tree, tree, int, bool *) ;
 static tree d_handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
 static tree d_handle_cold_attribute (tree *, tree, tree, int, bool *);
+static tree d_handle_register_attribute (tree *, tree, tree, int, bool *);
 static tree d_handle_restrict_attribute (tree *, tree, tree, int, bool *);
 static tree d_handle_used_attribute (tree *, tree, tree, int, bool *);
 static tree d_handle_visibility_attribute (tree *, tree, tree, int, bool *);
@@ -223,6 +224,8 @@  const attribute_spec d_langhook_attribute_table[] =
 	     d_handle_cold_attribute, attr_cold_hot_exclusions),
   ATTR_SPEC ("no_sanitize", 1, -1, true, false, false, false,
 	     d_handle_no_sanitize_attribute, NULL),
+  ATTR_SPEC ("register", 1, 1, true, false, false, false,
+	     d_handle_register_attribute, NULL),
   ATTR_SPEC ("restrict", 0, 0, true, false, false, false,
 	     d_handle_restrict_attribute, NULL),
   ATTR_SPEC ("used", 0, 0, true, false, false, false,
@@ -1409,8 +1412,41 @@  d_handle_no_sanitize_attribute (tree *node, tree name, tree args, int,
   else
     {
       DECL_ATTRIBUTES (*node) = tree_cons (get_identifier ("no_sanitize"),
-		      			   build_int_cst (d_uint_type, flags),
-		      			   DECL_ATTRIBUTES (*node));
+					   build_int_cst (d_uint_type, flags),
+					   DECL_ATTRIBUTES (*node));
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "register" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+d_handle_register_attribute (tree *node, tree name, tree args, int,
+			     bool *no_add_attrs)
+{
+  if (!VAR_P (*node))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+    {
+      error ("%qE attribute argument not a string constant", name);
+      *no_add_attrs = true;
+    }
+  else if (TREE_STRING_LENGTH (TREE_VALUE (args)) == 0
+	   || TREE_STRING_POINTER (TREE_VALUE (args))[0] == '\0')
+    {
+      error ("register name not specified for %q+D", *node);
+      *no_add_attrs = true;
+    }
+  else
+    {
+      DECL_REGISTER (*node) = 1;
+      set_user_assembler_name (*node, TREE_STRING_POINTER (TREE_VALUE (args)));
+      DECL_HARD_REGISTER (*node) = 1;
     }
 
   return NULL_TREE;
diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc
index 3a201149d74..8a8bf12e7fc 100644
--- a/gcc/d/d-codegen.cc
+++ b/gcc/d/d-codegen.cc
@@ -697,11 +697,12 @@  build_address (tree exp)
   return compound_expr (init, exp);
 }
 
-/* Mark EXP saying that we need to be able to take the
-   address of it; it should not be allocated in a register.  */
+/* Mark EXP saying that we need to be able to take the address of it; it should
+   not be allocated in a register.  When COMPLAIN is true, issue an error if we
+   are marking a register variable.  */
 
 tree
-d_mark_addressable (tree exp)
+d_mark_addressable (tree exp, bool complain)
 {
   switch (TREE_CODE (exp))
     {
@@ -713,12 +714,22 @@  d_mark_addressable (tree exp)
       d_mark_addressable (TREE_OPERAND (exp, 0));
       break;
 
-    case PARM_DECL:
     case VAR_DECL:
+      if (complain && DECL_REGISTER (exp))
+	{
+	  if (DECL_HARD_REGISTER (exp) || DECL_EXTERNAL (exp))
+	    error ("address of explicit register variable %qD requested", exp);
+	  else
+	    error ("address of register variable %qD requested", exp);
+	}
+
+      /* Fall through.  */
+    case PARM_DECL:
     case RESULT_DECL:
     case CONST_DECL:
     case FUNCTION_DECL:
-      TREE_ADDRESSABLE (exp) = 1;
+      if (!VAR_P (exp) || !DECL_HARD_REGISTER (exp))
+	TREE_ADDRESSABLE (exp) = 1;
       break;
 
     case CONSTRUCTOR:
@@ -2704,7 +2715,16 @@  build_frame_type (tree ffi, FuncDeclaration *fd)
 	  if ((v->edtor && (v->storage_class & STCparameter))
 	      || v->needsScopeDtor ())
 	    error_at (make_location_t (v->loc),
-		      "has scoped destruction, cannot build closure");
+		      "variable %qs has scoped destruction, "
+		      "cannot build closure", v->toChars ());
+	}
+
+      if (DECL_REGISTER (vsym))
+	{
+	  /* Because the value will be in memory, not a register.  */
+	  error_at (make_location_t (v->loc),
+		    "explicit register variable %qs cannot be used in nested "
+		    "function", v->toChars ());
 	}
     }
 
diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h
index a6c38119458..c3e95e4d2d2 100644
--- a/gcc/d/d-tree.h
+++ b/gcc/d/d-tree.h
@@ -549,7 +549,7 @@  extern tree stabilize_expr (tree *);
 extern tree build_target_expr (tree, tree);
 extern tree force_target_expr (tree);
 extern tree build_address (tree);
-extern tree d_mark_addressable (tree);
+extern tree d_mark_addressable (tree, bool = true);
 extern tree d_mark_used (tree);
 extern tree d_mark_read (tree);
 extern tree build_memcmp_call (tree, tree, tree);
diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc
index b82e2d55c13..5032ae02d6b 100644
--- a/gcc/d/decl.cc
+++ b/gcc/d/decl.cc
@@ -670,10 +670,14 @@  public:
 	    rest_of_decl_compilation (decl, 1, 0);
 	  }
       }
-    else if (d->isDataseg () && !(d->storage_class & STCextern))
+    else if (d->isDataseg ())
       {
 	tree decl = get_symbol_decl (d);
 
+	/* Only need to build the VAR_DECL for extern declarations.  */
+	if (d->storage_class & STCextern)
+	  return;
+
 	/* Duplicated VarDeclarations map to the same symbol.  Check if this
 	   is the one declaration which will be emitted.  */
 	tree ident = DECL_ASSEMBLER_NAME (decl);
@@ -1343,7 +1347,11 @@  get_symbol_decl (Declaration *decl)
   if (decl->storage_class & STCvolatile)
     TREE_THIS_VOLATILE (decl->csym) = 1;
 
-  /* Likewise, so could the deprecated attribute.  */
+  /* Symbol was marked register.  */
+  if (decl->storage_class & STCregister)
+    DECL_REGISTER (decl->csym) = 1;
+
+  /* Symbol was declared with deprecated attribute.  */
   if (decl->storage_class & STCdeprecated)
     TREE_DEPRECATED (decl->csym) = 1;
 
@@ -1376,6 +1384,18 @@  get_symbol_decl (Declaration *decl)
   /* Apply any user attributes that may affect semantic meaning.  */
   apply_user_attributes (decl, decl->csym);
 
+  /* Handle any conflicts between D language attributes and compiler-recognized
+   * user attributes.  */
+  if (VAR_P (decl->csym) && DECL_HARD_REGISTER (decl->csym))
+    {
+      if (decl->storage_class & STCextern)
+	error_at (make_location_t (decl->loc), "explicit register variable "
+		  "%qs declared %<extern%>", decl->toChars ());
+      else if (decl->isThreadlocal ())
+	error_at (make_location_t (decl->loc), "explicit register variable "
+		  "%qs declared thread local", decl->toChars ());
+    }
+
   /* %% Probably should be a little more intelligent about setting this.  */
   TREE_USED (decl->csym) = 1;
   d_keep (decl->csym);
diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc
index bf750924594..34b3ddd3f10 100644
--- a/gcc/d/expr.cc
+++ b/gcc/d/expr.cc
@@ -1250,7 +1250,7 @@  public:
 	    tree array_type =
 	      build_array_type_nelts (TREE_TYPE (TREE_TYPE (array)),
 				      TYPE_VECTOR_SUBPARTS (TREE_TYPE (array)));
-	    d_mark_addressable (array);
+	    d_mark_addressable (array, false);
 	    array = build1 (VIEW_CONVERT_EXPR, array_type, array);
 	  }
 
diff --git a/gcc/testsuite/gdc.dg/attr_register1.d b/gcc/testsuite/gdc.dg/attr_register1.d
new file mode 100644
index 00000000000..01fc245ab40
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/attr_register1.d
@@ -0,0 +1,55 @@ 
+// { dg-do compile }
+
+import gcc.attributes;
+
+@attribute("register", null) int var1; // { dg-error "attribute argument not a string constant" }
+
+@attribute("register", "") int var2; // { dg-error "register name not specified for .var2." }
+
+@attribute("register", "invalid") __gshared int var3; // { dg-error "invalid register name for .var3." }
+
+void f1(ref int r) { }
+
+void test1()
+{
+    @register("ref") int var6;
+    f1(var6); // { dg-error "address of explicit register variable .var6. requested" }
+}
+
+void f2(out int r) { }
+
+void test2()
+{
+    @register("out") int var7;
+    f2(var7); // { dg-error "address of explicit register variable .var7. requested" }
+}
+
+void f3(lazy int r) { }
+
+void test3()
+{
+    @register("lazy") int var8; // { dg-error "explicit register variable .var8. cannot be used in nested function" }
+    f3(var8);
+}
+
+void test4()
+{
+    @register("addr") int var9;
+    auto ptr3 = &var9; // { dg-error "address of explicit register variable .var9. requested" }
+}
+
+ref int test5()
+{
+    @register("refreturn") __gshared int var10; // { dg-error "invalid register name" }
+    return var10; // { dg-error "address of explicit register variable .var10. requested" }
+}
+
+auto test6()
+{
+    @register("closure") int var11; // { dg-error "explicit register variable .var11. cannot be used in nested function" }
+    int nested()
+    {
+        return var11;
+    }
+    return &nested;
+}
diff --git a/gcc/testsuite/gdc.dg/attr_register2.d b/gcc/testsuite/gdc.dg/attr_register2.d
new file mode 100644
index 00000000000..9061a643f31
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/attr_register2.d
@@ -0,0 +1,11 @@ 
+// { dg-do compile  { target x86_64-*-* } }
+
+import gcc.attributes;
+
+@register("ebx") static int var1 = void; // { dg-error "explicit register variable .var1. declared thread local" }
+
+@register("ebx") extern int var2; // { dg-error "explicit register variable .var2. declared .extern." }
+
+@register("r12") __gshared int var3 = 0x2a; // { dg-error "global register variable has initial value" }
+
+@register("r12") __gshared int[256] var4 = void; // { dg-error "data type of .var4. isn.t suitable for a register" }
diff --git a/gcc/testsuite/gdc.dg/attr_register3.d b/gcc/testsuite/gdc.dg/attr_register3.d
new file mode 100644
index 00000000000..706a39985d4
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/attr_register3.d
@@ -0,0 +1,22 @@ 
+// { dg-do compile }
+// { dg-options "-Wall -O2 -fdump-tree-optimized" }
+
+import gcc.attributes;
+
+pragma(inline, true)
+void syscall()(int val)
+{
+    @register("4") int reg = val;
+    asm { "/* Some Code %0 */" :: "r" (reg); }
+}
+
+void do_syscalls()
+{
+    for (int s = 0; s < 2; s++)
+    {
+        syscall (0);
+        syscall (1);
+    }
+}
+
+// { dg-final { scan-tree-dump-times "reg = " 4 "optimized" } }
diff --git a/libphobos/libdruntime/gcc/attributes.d b/libphobos/libdruntime/gcc/attributes.d
index 710e8ab8a59..40a18bfa947 100644
--- a/libphobos/libdruntime/gcc/attributes.d
+++ b/libphobos/libdruntime/gcc/attributes.d
@@ -301,6 +301,34 @@  auto optimize(A...)(A arguments)
     assert(false, "optimize attribute argument not a string or integer constant");
 }
 
+/**
+ * The `@register` attribute specifies that a local or `__gshared` variable
+ * is to be given a register storage-class in the C99 sense of the term, and
+ * will be placed into a register named `registerName`.
+ *
+ * The variable needs to boiled down to a data type that fits the target
+ * register.  It also cannot have either thread-local or `extern` storage.
+ * It is an error to take the address of a register variable.
+ *
+ * Example:
+ * ---
+ * import gcc.attributes;
+ *
+ * @register("ebx") __gshared int ebx = void;
+ *
+ * void func() { @register("r10") long r10 = 0x2a; }
+ * ---
+ */
+auto register(string registerName)
+{
+    return attribute("register", registerName);
+}
+
+auto register(A...)(A arguments)
+{
+    assert(false, "register attribute argument not a string 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