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.
This commit is contained in:
parent
8288cd635f
commit
91418c4208
9 changed files with 204 additions and 12 deletions
|
@ -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_noplt_attribute (tree *, tree, tree, int, bool *) ;
|
||||||
static tree d_handle_alloc_size_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_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_restrict_attribute (tree *, tree, tree, int, bool *);
|
||||||
static tree d_handle_used_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 *);
|
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),
|
d_handle_cold_attribute, attr_cold_hot_exclusions),
|
||||||
ATTR_SPEC ("no_sanitize", 1, -1, true, false, false, false,
|
ATTR_SPEC ("no_sanitize", 1, -1, true, false, false, false,
|
||||||
d_handle_no_sanitize_attribute, NULL),
|
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,
|
ATTR_SPEC ("restrict", 0, 0, true, false, false, false,
|
||||||
d_handle_restrict_attribute, NULL),
|
d_handle_restrict_attribute, NULL),
|
||||||
ATTR_SPEC ("used", 0, 0, true, false, false, false,
|
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
|
else
|
||||||
{
|
{
|
||||||
DECL_ATTRIBUTES (*node) = tree_cons (get_identifier ("no_sanitize"),
|
DECL_ATTRIBUTES (*node) = tree_cons (get_identifier ("no_sanitize"),
|
||||||
build_int_cst (d_uint_type, flags),
|
build_int_cst (d_uint_type, flags),
|
||||||
DECL_ATTRIBUTES (*node));
|
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;
|
return NULL_TREE;
|
||||||
|
|
|
@ -697,11 +697,12 @@ build_address (tree exp)
|
||||||
return compound_expr (init, exp);
|
return compound_expr (init, exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mark EXP saying that we need to be able to take the
|
/* Mark EXP saying that we need to be able to take the address of it; it should
|
||||||
address of it; it should not be allocated in a register. */
|
not be allocated in a register. When COMPLAIN is true, issue an error if we
|
||||||
|
are marking a register variable. */
|
||||||
|
|
||||||
tree
|
tree
|
||||||
d_mark_addressable (tree exp)
|
d_mark_addressable (tree exp, bool complain)
|
||||||
{
|
{
|
||||||
switch (TREE_CODE (exp))
|
switch (TREE_CODE (exp))
|
||||||
{
|
{
|
||||||
|
@ -713,12 +714,22 @@ d_mark_addressable (tree exp)
|
||||||
d_mark_addressable (TREE_OPERAND (exp, 0));
|
d_mark_addressable (TREE_OPERAND (exp, 0));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PARM_DECL:
|
|
||||||
case VAR_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 RESULT_DECL:
|
||||||
case CONST_DECL:
|
case CONST_DECL:
|
||||||
case FUNCTION_DECL:
|
case FUNCTION_DECL:
|
||||||
TREE_ADDRESSABLE (exp) = 1;
|
if (!VAR_P (exp) || !DECL_HARD_REGISTER (exp))
|
||||||
|
TREE_ADDRESSABLE (exp) = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONSTRUCTOR:
|
case CONSTRUCTOR:
|
||||||
|
@ -2704,7 +2715,16 @@ build_frame_type (tree ffi, FuncDeclaration *fd)
|
||||||
if ((v->edtor && (v->storage_class & STCparameter))
|
if ((v->edtor && (v->storage_class & STCparameter))
|
||||||
|| v->needsScopeDtor ())
|
|| v->needsScopeDtor ())
|
||||||
error_at (make_location_t (v->loc),
|
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 build_target_expr (tree, tree);
|
||||||
extern tree force_target_expr (tree);
|
extern tree force_target_expr (tree);
|
||||||
extern tree build_address (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_used (tree);
|
||||||
extern tree d_mark_read (tree);
|
extern tree d_mark_read (tree);
|
||||||
extern tree build_memcmp_call (tree, tree, tree);
|
extern tree build_memcmp_call (tree, tree, tree);
|
||||||
|
|
|
@ -670,10 +670,14 @@ public:
|
||||||
rest_of_decl_compilation (decl, 1, 0);
|
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);
|
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
|
/* Duplicated VarDeclarations map to the same symbol. Check if this
|
||||||
is the one declaration which will be emitted. */
|
is the one declaration which will be emitted. */
|
||||||
tree ident = DECL_ASSEMBLER_NAME (decl);
|
tree ident = DECL_ASSEMBLER_NAME (decl);
|
||||||
|
@ -1343,7 +1347,11 @@ get_symbol_decl (Declaration *decl)
|
||||||
if (decl->storage_class & STCvolatile)
|
if (decl->storage_class & STCvolatile)
|
||||||
TREE_THIS_VOLATILE (decl->csym) = 1;
|
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)
|
if (decl->storage_class & STCdeprecated)
|
||||||
TREE_DEPRECATED (decl->csym) = 1;
|
TREE_DEPRECATED (decl->csym) = 1;
|
||||||
|
|
||||||
|
@ -1376,6 +1384,18 @@ get_symbol_decl (Declaration *decl)
|
||||||
/* Apply any user attributes that may affect semantic meaning. */
|
/* Apply any user attributes that may affect semantic meaning. */
|
||||||
apply_user_attributes (decl, decl->csym);
|
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. */
|
/* %% Probably should be a little more intelligent about setting this. */
|
||||||
TREE_USED (decl->csym) = 1;
|
TREE_USED (decl->csym) = 1;
|
||||||
d_keep (decl->csym);
|
d_keep (decl->csym);
|
||||||
|
|
|
@ -1250,7 +1250,7 @@ public:
|
||||||
tree array_type =
|
tree array_type =
|
||||||
build_array_type_nelts (TREE_TYPE (TREE_TYPE (array)),
|
build_array_type_nelts (TREE_TYPE (TREE_TYPE (array)),
|
||||||
TYPE_VECTOR_SUBPARTS (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);
|
array = build1 (VIEW_CONVERT_EXPR, array_type, array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
55
gcc/testsuite/gdc.dg/attr_register1.d
Normal file
55
gcc/testsuite/gdc.dg/attr_register1.d
Normal file
|
@ -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;
|
||||||
|
}
|
11
gcc/testsuite/gdc.dg/attr_register2.d
Normal file
11
gcc/testsuite/gdc.dg/attr_register2.d
Normal file
|
@ -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" }
|
22
gcc/testsuite/gdc.dg/attr_register3.d
Normal file
22
gcc/testsuite/gdc.dg/attr_register3.d
Normal file
|
@ -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");
|
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
|
* The `@restrict` attribute specifies that a function parameter is to be
|
||||||
* restrict-qualified in the C99 sense of the term. The parameter needs to
|
* restrict-qualified in the C99 sense of the term. The parameter needs to
|
||||||
|
|
Loading…
Add table
Reference in a new issue