mirror of git://gcc.gnu.org/git/gcc.git
RISC-V: Add interrupt attribute modes.
gcc/ * config/riscv/riscv.c (enum riscv_privilege_levels): New. (struct machine_function): New field interrupt_mode. (riscv_handle_type_attribute): New function. Add forward declaration. (riscv_attribute_table) <interrupt>: Use riscv_handle_type_attribute. (riscv_expand_epilogue): Check interrupt_mode field. (riscv_set_current_function): Check interrupt attribute args and set interrupt_mode field. * config/riscv/riscv.md (UNSPECV_SRET, UNSPECV_URET): New. (riscv_sret, riscv_uret): New. * doc/extend.texi (RISC-V Function Attributes) <interrupt>: Document new arguments to interrupt attribute. gcc/testsuite/ * gcc.target/riscv/interrupt-5.c (sub3): Add new test. * gcc.target/riscv/interrupt-mmode.c: New. * gcc.target/riscv/interrupt-smode.c: New. * gcc.target/riscv/interrupt-umode.c: New. From-SVN: r261244
This commit is contained in:
parent
1b58c736db
commit
ec74725ce3
|
|
@ -1,3 +1,17 @@
|
||||||
|
2018-06-06 Jim Wilson <jimw@sifive.com>
|
||||||
|
|
||||||
|
* config/riscv/riscv.c (enum riscv_privilege_levels): New.
|
||||||
|
(struct machine_function): New field interrupt_mode.
|
||||||
|
(riscv_handle_type_attribute): New function. Add forward declaration.
|
||||||
|
(riscv_attribute_table) <interrupt>: Use riscv_handle_type_attribute.
|
||||||
|
(riscv_expand_epilogue): Check interrupt_mode field.
|
||||||
|
(riscv_set_current_function): Check interrupt attribute args and
|
||||||
|
set interrupt_mode field.
|
||||||
|
* config/riscv/riscv.md (UNSPECV_SRET, UNSPECV_URET): New.
|
||||||
|
(riscv_sret, riscv_uret): New.
|
||||||
|
* doc/extend.texi (RISC-V Function Attributes) <interrupt>: Document
|
||||||
|
new arguments to interrupt attribute.
|
||||||
|
|
||||||
2018-06-06 Peter Bergner <bergner@vnet.ibm.com>
|
2018-06-06 Peter Bergner <bergner@vnet.ibm.com>
|
||||||
|
|
||||||
PR target/63177
|
PR target/63177
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,10 @@ struct GTY(()) riscv_frame_info {
|
||||||
HOST_WIDE_INT arg_pointer_offset;
|
HOST_WIDE_INT arg_pointer_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum riscv_privilege_levels {
|
||||||
|
USER_MODE, SUPERVISOR_MODE, MACHINE_MODE
|
||||||
|
};
|
||||||
|
|
||||||
struct GTY(()) machine_function {
|
struct GTY(()) machine_function {
|
||||||
/* The number of extra stack bytes taken up by register varargs.
|
/* The number of extra stack bytes taken up by register varargs.
|
||||||
This area is allocated by the callee at the very top of the frame. */
|
This area is allocated by the callee at the very top of the frame. */
|
||||||
|
|
@ -132,6 +136,8 @@ struct GTY(()) machine_function {
|
||||||
|
|
||||||
/* True if current function is an interrupt function. */
|
/* True if current function is an interrupt function. */
|
||||||
bool interrupt_handler_p;
|
bool interrupt_handler_p;
|
||||||
|
/* For an interrupt handler, indicates the privilege level. */
|
||||||
|
enum riscv_privilege_levels interrupt_mode;
|
||||||
|
|
||||||
/* True if attributes on current function have been checked. */
|
/* True if attributes on current function have been checked. */
|
||||||
bool attributes_checked_p;
|
bool attributes_checked_p;
|
||||||
|
|
@ -282,6 +288,7 @@ static const struct riscv_tune_info optimize_size_tune_info = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static tree riscv_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
|
static tree riscv_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
|
||||||
|
static tree riscv_handle_type_attribute (tree *, tree, tree, int, bool *);
|
||||||
|
|
||||||
/* Defining target-specific uses of __attribute__. */
|
/* Defining target-specific uses of __attribute__. */
|
||||||
static const struct attribute_spec riscv_attribute_table[] =
|
static const struct attribute_spec riscv_attribute_table[] =
|
||||||
|
|
@ -294,7 +301,8 @@ static const struct attribute_spec riscv_attribute_table[] =
|
||||||
{ "naked", 0, 0, true, false, false, false,
|
{ "naked", 0, 0, true, false, false, false,
|
||||||
riscv_handle_fndecl_attribute, NULL },
|
riscv_handle_fndecl_attribute, NULL },
|
||||||
/* This attribute generates prologue/epilogue for interrupt handlers. */
|
/* This attribute generates prologue/epilogue for interrupt handlers. */
|
||||||
{ "interrupt", 0, 0, false, true, true, false, NULL, NULL },
|
{ "interrupt", 0, 1, false, true, true, false,
|
||||||
|
riscv_handle_type_attribute, NULL },
|
||||||
|
|
||||||
/* The last attribute spec is set to be NULL. */
|
/* The last attribute spec is set to be NULL. */
|
||||||
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
|
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
|
||||||
|
|
@ -2721,6 +2729,47 @@ riscv_handle_fndecl_attribute (tree *node, tree name,
|
||||||
return NULL_TREE;
|
return NULL_TREE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Verify type based attributes. NODE is the what the attribute is being
|
||||||
|
applied to. NAME is the attribute name. ARGS are the attribute args.
|
||||||
|
FLAGS gives info about the context. NO_ADD_ATTRS should be set to true if
|
||||||
|
the attribute should be ignored. */
|
||||||
|
|
||||||
|
static tree
|
||||||
|
riscv_handle_type_attribute (tree *node ATTRIBUTE_UNUSED, tree name, tree args,
|
||||||
|
int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
|
||||||
|
{
|
||||||
|
/* Check for an argument. */
|
||||||
|
if (is_attribute_p ("interrupt", name))
|
||||||
|
{
|
||||||
|
if (args)
|
||||||
|
{
|
||||||
|
tree cst = TREE_VALUE (args);
|
||||||
|
const char *string;
|
||||||
|
|
||||||
|
if (TREE_CODE (cst) != STRING_CST)
|
||||||
|
{
|
||||||
|
warning (OPT_Wattributes,
|
||||||
|
"%qE attribute requires a string argument",
|
||||||
|
name);
|
||||||
|
*no_add_attrs = true;
|
||||||
|
return NULL_TREE;
|
||||||
|
}
|
||||||
|
|
||||||
|
string = TREE_STRING_POINTER (cst);
|
||||||
|
if (strcmp (string, "user") && strcmp (string, "supervisor")
|
||||||
|
&& strcmp (string, "machine"))
|
||||||
|
{
|
||||||
|
warning (OPT_Wattributes,
|
||||||
|
"argument to %qE attribute is not \"user\", \"supervisor\", or \"machine\"",
|
||||||
|
name);
|
||||||
|
*no_add_attrs = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL_TREE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Return true if function TYPE is an interrupt function. */
|
/* Return true if function TYPE is an interrupt function. */
|
||||||
static bool
|
static bool
|
||||||
riscv_interrupt_type_p (tree type)
|
riscv_interrupt_type_p (tree type)
|
||||||
|
|
@ -3932,7 +3981,16 @@ riscv_expand_epilogue (int style)
|
||||||
|
|
||||||
/* Return from interrupt. */
|
/* Return from interrupt. */
|
||||||
if (cfun->machine->interrupt_handler_p)
|
if (cfun->machine->interrupt_handler_p)
|
||||||
emit_insn (gen_riscv_mret ());
|
{
|
||||||
|
enum riscv_privilege_levels mode = cfun->machine->interrupt_mode;
|
||||||
|
|
||||||
|
if (mode == MACHINE_MODE)
|
||||||
|
emit_insn (gen_riscv_mret ());
|
||||||
|
else if (mode == SUPERVISOR_MODE)
|
||||||
|
emit_insn (gen_riscv_sret ());
|
||||||
|
else
|
||||||
|
emit_insn (gen_riscv_uret ());
|
||||||
|
}
|
||||||
else if (style != SIBCALL_RETURN)
|
else if (style != SIBCALL_RETURN)
|
||||||
emit_jump_insn (gen_simple_return_internal (ra));
|
emit_jump_insn (gen_simple_return_internal (ra));
|
||||||
}
|
}
|
||||||
|
|
@ -4494,14 +4552,32 @@ riscv_set_current_function (tree decl)
|
||||||
|
|
||||||
if (cfun->machine->interrupt_handler_p)
|
if (cfun->machine->interrupt_handler_p)
|
||||||
{
|
{
|
||||||
tree args = TYPE_ARG_TYPES (TREE_TYPE (decl));
|
|
||||||
tree ret = TREE_TYPE (TREE_TYPE (decl));
|
tree ret = TREE_TYPE (TREE_TYPE (decl));
|
||||||
|
tree args = TYPE_ARG_TYPES (TREE_TYPE (decl));
|
||||||
|
tree attr_args
|
||||||
|
= TREE_VALUE (lookup_attribute ("interrupt",
|
||||||
|
TYPE_ATTRIBUTES (TREE_TYPE (decl))));
|
||||||
|
|
||||||
if (TREE_CODE (ret) != VOID_TYPE)
|
if (TREE_CODE (ret) != VOID_TYPE)
|
||||||
error ("%qs function cannot return a value", "interrupt");
|
error ("%qs function cannot return a value", "interrupt");
|
||||||
|
|
||||||
if (args && TREE_CODE (TREE_VALUE (args)) != VOID_TYPE)
|
if (args && TREE_CODE (TREE_VALUE (args)) != VOID_TYPE)
|
||||||
error ("%qs function cannot have arguments", "interrupt");
|
error ("%qs function cannot have arguments", "interrupt");
|
||||||
|
|
||||||
|
if (attr_args && TREE_CODE (TREE_VALUE (attr_args)) != VOID_TYPE)
|
||||||
|
{
|
||||||
|
const char *string = TREE_STRING_POINTER (TREE_VALUE (attr_args));
|
||||||
|
|
||||||
|
if (!strcmp (string, "user"))
|
||||||
|
cfun->machine->interrupt_mode = USER_MODE;
|
||||||
|
else if (!strcmp (string, "supervisor"))
|
||||||
|
cfun->machine->interrupt_mode = SUPERVISOR_MODE;
|
||||||
|
else /* Must be "machine". */
|
||||||
|
cfun->machine->interrupt_mode = MACHINE_MODE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* Interrupt attributes are machine mode by default. */
|
||||||
|
cfun->machine->interrupt_mode = MACHINE_MODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't print the above diagnostics more than once. */
|
/* Don't print the above diagnostics more than once. */
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,8 @@
|
||||||
|
|
||||||
;; Interrupt handler instructions.
|
;; Interrupt handler instructions.
|
||||||
UNSPECV_MRET
|
UNSPECV_MRET
|
||||||
|
UNSPECV_SRET
|
||||||
|
UNSPECV_URET
|
||||||
|
|
||||||
;; Blockage and synchronization.
|
;; Blockage and synchronization.
|
||||||
UNSPECV_BLOCKAGE
|
UNSPECV_BLOCKAGE
|
||||||
|
|
@ -2298,6 +2300,16 @@
|
||||||
""
|
""
|
||||||
"mret")
|
"mret")
|
||||||
|
|
||||||
|
(define_insn "riscv_sret"
|
||||||
|
[(unspec_volatile [(const_int 0)] UNSPECV_SRET)]
|
||||||
|
""
|
||||||
|
"sret")
|
||||||
|
|
||||||
|
(define_insn "riscv_uret"
|
||||||
|
[(unspec_volatile [(const_int 0)] UNSPECV_URET)]
|
||||||
|
""
|
||||||
|
"uret")
|
||||||
|
|
||||||
(define_insn "stack_tie<mode>"
|
(define_insn "stack_tie<mode>"
|
||||||
[(set (mem:BLK (scratch))
|
[(set (mem:BLK (scratch))
|
||||||
(unspec:BLK [(match_operand:X 0 "register_operand" "r")
|
(unspec:BLK [(match_operand:X 0 "register_operand" "r")
|
||||||
|
|
|
||||||
|
|
@ -5147,6 +5147,17 @@ depended upon to work reliably and are not supported.
|
||||||
Use this attribute to indicate that the specified function is an interrupt
|
Use this attribute to indicate that the specified function is an interrupt
|
||||||
handler. The compiler generates function entry and exit sequences suitable
|
handler. The compiler generates function entry and exit sequences suitable
|
||||||
for use in an interrupt handler when this attribute is present.
|
for use in an interrupt handler when this attribute is present.
|
||||||
|
|
||||||
|
You can specify the kind of interrupt to be handled by adding an optional
|
||||||
|
parameter to the interrupt attribute like this:
|
||||||
|
|
||||||
|
@smallexample
|
||||||
|
void f (void) __attribute__ ((interrupt ("user")));
|
||||||
|
@end smallexample
|
||||||
|
|
||||||
|
Permissible values for this parameter are @code{user}, @code{supervisor},
|
||||||
|
and @code{machine}. If there is no parameter, then it defaults to
|
||||||
|
@code{machine}.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
@node RL78 Function Attributes
|
@node RL78 Function Attributes
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
2018-06-06 Jim Wilson <jimw@sifive.com>
|
||||||
|
|
||||||
|
* gcc.target/riscv/interrupt-5.c (sub3): Add new test.
|
||||||
|
* gcc.target/riscv/interrupt-mmode.c: New.
|
||||||
|
* gcc.target/riscv/interrupt-smode.c: New.
|
||||||
|
* gcc.target/riscv/interrupt-umode.c: New.
|
||||||
|
|
||||||
2018-06-06 Marek Polacek <polacek@redhat.com>
|
2018-06-06 Marek Polacek <polacek@redhat.com>
|
||||||
|
|
||||||
PR c++/85977
|
PR c++/85977
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,8 @@ void __attribute__ ((interrupt, naked))
|
||||||
sub2 (void)
|
sub2 (void)
|
||||||
{ /* { dg-error "are mutually exclusive" } */
|
{ /* { dg-error "are mutually exclusive" } */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __attribute__ ((interrupt ("hypervisor")))
|
||||||
|
sub3 (void)
|
||||||
|
{ /* { dg-warning "argument to" } */
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
/* Verify the return instruction is mret. */
|
||||||
|
/* { dg-do compile } */
|
||||||
|
/* { dg-options "-O" } */
|
||||||
|
void __attribute__ ((interrupt ("machine")))
|
||||||
|
foo (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
/* { dg-final { scan-assembler "mret" } } */
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
/* Verify the return instruction is mret. */
|
||||||
|
/* { dg-do compile } */
|
||||||
|
/* { dg-options "-O" } */
|
||||||
|
void __attribute__ ((interrupt ("supervisor")))
|
||||||
|
foo (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
/* { dg-final { scan-assembler "sret" } } */
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
/* Verify the return instruction is mret. */
|
||||||
|
/* { dg-do compile } */
|
||||||
|
/* { dg-options "-O" } */
|
||||||
|
void __attribute__ ((interrupt ("user")))
|
||||||
|
foo (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
/* { dg-final { scan-assembler "uret" } } */
|
||||||
Loading…
Reference in New Issue