Commit ec74725c by Jim Wilson Committed by Jim Wilson

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
parent 1b58c736
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 { ...@@ -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 { ...@@ -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 = { ...@@ -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[] = ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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 @@ ...@@ -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. ...@@ -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
......
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)) ...@@ -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" } */
}
/* Verify the return instruction is mret. */
/* { dg-do compile } */
/* { dg-options "-O" } */
void __attribute__ ((interrupt ("machine")))
foo (void)
{
}
/* { dg-final { scan-assembler "mret" } } */
/* Verify the return instruction is mret. */
/* { dg-do compile } */
/* { dg-options "-O" } */
void __attribute__ ((interrupt ("supervisor")))
foo (void)
{
}
/* { dg-final { scan-assembler "sret" } } */
/* Verify the return instruction is mret. */
/* { dg-do compile } */
/* { dg-options "-O" } */
void __attribute__ ((interrupt ("user")))
foo (void)
{
}
/* { dg-final { scan-assembler "uret" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment