@@ -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;
@@ -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 ());
}
}
@@ -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);
@@ -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);
@@ -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);
}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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" }
new file mode 100644
@@ -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" } }
@@ -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