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>
|
||||
|
||||
PR target/63177
|
||||
|
|
|
|||
|
|
@ -122,6 +122,10 @@ struct GTY(()) riscv_frame_info {
|
|||
HOST_WIDE_INT arg_pointer_offset;
|
||||
};
|
||||
|
||||
enum riscv_privilege_levels {
|
||||
USER_MODE, SUPERVISOR_MODE, MACHINE_MODE
|
||||
};
|
||||
|
||||
struct GTY(()) machine_function {
|
||||
/* 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. */
|
||||
|
|
@ -132,6 +136,8 @@ struct GTY(()) machine_function {
|
|||
|
||||
/* True if current function is an interrupt function. */
|
||||
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. */
|
||||
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_type_attribute (tree *, tree, tree, int, bool *);
|
||||
|
||||
/* Defining target-specific uses of __attribute__. */
|
||||
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,
|
||||
riscv_handle_fndecl_attribute, NULL },
|
||||
/* 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. */
|
||||
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
|
||||
|
|
@ -2721,6 +2729,47 @@ riscv_handle_fndecl_attribute (tree *node, tree name,
|
|||
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. */
|
||||
static bool
|
||||
riscv_interrupt_type_p (tree type)
|
||||
|
|
@ -3932,7 +3981,16 @@ riscv_expand_epilogue (int style)
|
|||
|
||||
/* Return from interrupt. */
|
||||
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)
|
||||
emit_jump_insn (gen_simple_return_internal (ra));
|
||||
}
|
||||
|
|
@ -4494,14 +4552,32 @@ riscv_set_current_function (tree decl)
|
|||
|
||||
if (cfun->machine->interrupt_handler_p)
|
||||
{
|
||||
tree args = TYPE_ARG_TYPES (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)
|
||||
error ("%qs function cannot return a value", "interrupt");
|
||||
|
||||
if (args && TREE_CODE (TREE_VALUE (args)) != VOID_TYPE)
|
||||
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. */
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@
|
|||
|
||||
;; Interrupt handler instructions.
|
||||
UNSPECV_MRET
|
||||
UNSPECV_SRET
|
||||
UNSPECV_URET
|
||||
|
||||
;; Blockage and synchronization.
|
||||
UNSPECV_BLOCKAGE
|
||||
|
|
@ -2298,6 +2300,16 @@
|
|||
""
|
||||
"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>"
|
||||
[(set (mem:BLK (scratch))
|
||||
(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
|
||||
handler. The compiler generates function entry and exit sequences suitable
|
||||
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
|
||||
|
||||
@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>
|
||||
|
||||
PR c++/85977
|
||||
|
|
|
|||
|
|
@ -14,3 +14,8 @@ void __attribute__ ((interrupt, naked))
|
|||
sub2 (void)
|
||||
{ /* { 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