aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2022-06-23 18:24:07 +0200
committerIain Buclaw <ibuclaw@gdcproject.org>2022-06-24 20:49:58 +0200
commit91418c42089cd1cbe71edcd6b2f5b26559819372 (patch)
tree6419c5301b4fb08e5b0cac64baddc66f7e3acadf /gcc
parent8288cd635fa0bd75a8c5f25c7a90d4a7a4acec81 (diff)
downloadgcc-91418c42089cd1cbe71edcd6b2f5b26559819372.zip
gcc-91418c42089cd1cbe71edcd6b2f5b26559819372.tar.gz
gcc-91418c42089cd1cbe71edcd6b2f5b26559819372.tar.bz2
d: Add `@register' attribute to compiler and library.
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. PR d/105413 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.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/d/d-attribs.cc40
-rw-r--r--gcc/d/d-codegen.cc32
-rw-r--r--gcc/d/d-tree.h2
-rw-r--r--gcc/d/decl.cc24
-rw-r--r--gcc/d/expr.cc2
-rw-r--r--gcc/testsuite/gdc.dg/attr_register1.d55
-rw-r--r--gcc/testsuite/gdc.dg/attr_register2.d11
-rw-r--r--gcc/testsuite/gdc.dg/attr_register3.d22
8 files changed, 176 insertions, 12 deletions
diff --git a/gcc/d/d-attribs.cc b/gcc/d/d-attribs.cc
index 4b54426..23bbe39 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 3a20114..8a8bf12 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 a6c3811..c3e95e4 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 b82e2d5..5032ae0 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 bf75092..34b3ddd 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 0000000..01fc245
--- /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 0000000..9061a64
--- /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 0000000..706a399
--- /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" } }