aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Wilson <jimw@sifive.com>2018-05-25 22:29:17 +0000
committerJim Wilson <wilson@gcc.gnu.org>2018-05-25 15:29:17 -0700
commitd0ebdd9fce00f5fbfec615d72f1dedf0cd7762a6 (patch)
treefd44a708cc3f3ebbd9eab6e79f8573f972a67663
parentc0e8f02b27afc1a07f30a7597f8ae34094821f9d (diff)
downloadgcc-d0ebdd9fce00f5fbfec615d72f1dedf0cd7762a6.zip
gcc-d0ebdd9fce00f5fbfec615d72f1dedf0cd7762a6.tar.gz
gcc-d0ebdd9fce00f5fbfec615d72f1dedf0cd7762a6.tar.bz2
RISC-V: Add interrupt attribute support.
gcc/ * config/riscv/riscv-protos.h (riscv_epilogue_uses): New. * config/riscv/riscv.c (struct machine_function): Add interrupt_handler_p and attribute_checked_p fields. (riscv_attribute_table): Add interrupt. (riscv_interrupt_type_p): New. (riscv_save_reg_p): Save extra regs for interrupt handler. (riscv_use_save_libcall): Return false for interrupt handler. (riscv_first_stack_step): Add forward declaration. (riscv_compute_frame_info): New local interrupt_save_t1. Set it for interrupt handler with large frame. Use it for saved reg list. (riscv_expand_prologue): Move flag_stack_usage_info support to eliminate duplication. (riscv_expand_epilogue): Generate mret for interrupt handler. (riscv_epilogue_uses): New. (riscv_can_use_return_insn): Return false for interrupt handler. (riscv_function_ok_for_sibcall): Likewise. (riscv_set_current_function): Add interrupt handler support. * config/riscv/riscv.h (EPILOGUE_USES): Call riscv_epilogue_uses. * config/riscv/riscv.md (UNSPECV_MRET): New. (GP_REGNUM): New. (riscv_frflags, riscv_fsflags): Use tab after opcode. (riscv_mret): New. * doc/extend.texi (RISC-V Function Attributes) <interrupt>: New. gcc/testsuite/ * gcc.target/riscv/interrupt-1.c: New. * gcc.target/riscv/interrupt-2.c: New. * gcc.target/riscv/interrupt-3.c: New. * gcc.target/riscv/interrupt-4.c: New. * gcc.target/riscv/interrupt-5.c: New. From-SVN: r260785
-rw-r--r--gcc/ChangeLog26
-rw-r--r--gcc/config/riscv/riscv-protos.h1
-rw-r--r--gcc/config/riscv/riscv.c127
-rw-r--r--gcc/config/riscv/riscv.h2
-rw-r--r--gcc/config/riscv/riscv.md13
-rw-r--r--gcc/doc/extend.texi6
-rw-r--r--gcc/testsuite/ChangeLog8
-rw-r--r--gcc/testsuite/gcc.target/riscv/interrupt-1.c8
-rw-r--r--gcc/testsuite/gcc.target/riscv/interrupt-2.c17
-rw-r--r--gcc/testsuite/gcc.target/riscv/interrupt-3.c9
-rw-r--r--gcc/testsuite/gcc.target/riscv/interrupt-4.c18
-rw-r--r--gcc/testsuite/gcc.target/riscv/interrupt-5.c16
12 files changed, 233 insertions, 18 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index aef4d9a..b683687 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,29 @@
+2018-05-25 Jim Wilson <jimw@sifive.com>
+
+ * config/riscv/riscv-protos.h (riscv_epilogue_uses): New.
+ * config/riscv/riscv.c (struct machine_function): Add
+ interrupt_handler_p and attribute_checked_p fields.
+ (riscv_attribute_table): Add interrupt.
+ (riscv_interrupt_type_p): New.
+ (riscv_save_reg_p): Save extra regs for interrupt handler.
+ (riscv_use_save_libcall): Return false for interrupt handler.
+ (riscv_first_stack_step): Add forward declaration.
+ (riscv_compute_frame_info): New local interrupt_save_t1. Set it
+ for interrupt handler with large frame. Use it for saved reg list.
+ (riscv_expand_prologue): Move flag_stack_usage_info support to
+ eliminate duplication.
+ (riscv_expand_epilogue): Generate mret for interrupt handler.
+ (riscv_epilogue_uses): New.
+ (riscv_can_use_return_insn): Return false for interrupt handler.
+ (riscv_function_ok_for_sibcall): Likewise.
+ (riscv_set_current_function): Add interrupt handler support.
+ * config/riscv/riscv.h (EPILOGUE_USES): Call riscv_epilogue_uses.
+ * config/riscv/riscv.md (UNSPECV_MRET): New.
+ (GP_REGNUM): New.
+ (riscv_frflags, riscv_fsflags): Use tab after opcode.
+ (riscv_mret): New.
+ * doc/extend.texi (RISC-V Function Attributes) <interrupt>: New.
+
2018-05-25 Bill Schmidt <wschmidt@linux.ibm.com>
PR tree-optimization/85712
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 0538ede..a194b19 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -67,6 +67,7 @@ extern rtx riscv_return_addr (int, rtx);
extern HOST_WIDE_INT riscv_initial_elimination_offset (int, int);
extern void riscv_expand_prologue (void);
extern void riscv_expand_epilogue (bool);
+extern bool riscv_epilogue_uses (unsigned int);
extern bool riscv_can_use_return_insn (void);
extern rtx riscv_function_value (const_tree, const_tree, enum machine_mode);
extern bool riscv_expand_block_move (rtx, rtx, rtx);
diff --git a/gcc/config/riscv/riscv.c b/gcc/config/riscv/riscv.c
index 9a9d9e1..7ea2657 100644
--- a/gcc/config/riscv/riscv.c
+++ b/gcc/config/riscv/riscv.c
@@ -130,6 +130,12 @@ struct GTY(()) machine_function {
/* True if current function is a naked function. */
bool naked_p;
+ /* True if current function is an interrupt function. */
+ bool interrupt_handler_p;
+
+ /* True if attributes on current function have been checked. */
+ bool attributes_checked_p;
+
/* The current frame information, calculated by riscv_compute_frame_info. */
struct riscv_frame_info frame;
};
@@ -287,6 +293,8 @@ static const struct attribute_spec riscv_attribute_table[] =
/* The attribute telling no prologue/epilogue. */
{ "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 },
/* The last attribute spec is set to be NULL. */
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
@@ -2713,7 +2721,14 @@ riscv_handle_fndecl_attribute (tree *node, tree name,
return NULL_TREE;
}
-/* Return true if func is a naked function. */
+/* Return true if funcion TYPE is an interrupt function. */
+static bool
+riscv_interrupt_type_p (tree type)
+{
+ return lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type)) != NULL;
+}
+
+/* Return true if FUNC is a naked function. */
static bool
riscv_naked_function_p (tree func)
{
@@ -3219,6 +3234,28 @@ riscv_save_reg_p (unsigned int regno)
if (regno == RETURN_ADDR_REGNUM && crtl->calls_eh_return)
return true;
+ /* If this is an interrupt handler, then must save extra registers. */
+ if (cfun->machine->interrupt_handler_p)
+ {
+ /* zero register is always zero. */
+ if (regno == GP_REG_FIRST)
+ return false;
+
+ /* The function will return the stack pointer to its original value. */
+ if (regno == STACK_POINTER_REGNUM)
+ return false;
+
+ /* By convention, we assume that gp and tp are safe. */
+ if (regno == GP_REGNUM || regno == THREAD_POINTER_REGNUM)
+ return false;
+
+ /* We must save every register used in this function. If this is not a
+ leaf function, then we must save all temporary registers. */
+ if (df_regs_ever_live_p (regno)
+ || (!crtl->is_leaf && call_used_regs[regno]))
+ return true;
+ }
+
return false;
}
@@ -3226,7 +3263,8 @@ riscv_save_reg_p (unsigned int regno)
static bool
riscv_use_save_libcall (const struct riscv_frame_info *frame)
{
- if (!TARGET_SAVE_RESTORE || crtl->calls_eh_return || frame_pointer_needed)
+ if (!TARGET_SAVE_RESTORE || crtl->calls_eh_return || frame_pointer_needed
+ || cfun->machine->interrupt_handler_p)
return false;
return frame->save_libcall_adjustment != 0;
@@ -3285,21 +3323,35 @@ riscv_save_libcall_count (unsigned mask)
They decrease stack_pointer_rtx but leave frame_pointer_rtx and
hard_frame_pointer_rtx unchanged. */
+static HOST_WIDE_INT riscv_first_stack_step (struct riscv_frame_info *frame);
+
static void
riscv_compute_frame_info (void)
{
struct riscv_frame_info *frame;
HOST_WIDE_INT offset;
+ bool interrupt_save_t1 = false;
unsigned int regno, i, num_x_saved = 0, num_f_saved = 0;
frame = &cfun->machine->frame;
+
+ /* In an interrupt function, if we have a large frame, then we need to
+ save/restore t1. We check for this before clearing the frame struct. */
+ if (cfun->machine->interrupt_handler_p)
+ {
+ HOST_WIDE_INT step1 = riscv_first_stack_step (frame);
+ if (! SMALL_OPERAND (frame->total_size - step1))
+ interrupt_save_t1 = true;
+ }
+
memset (frame, 0, sizeof (*frame));
if (!cfun->machine->naked_p)
{
/* Find out which GPRs we need to save. */
for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
- if (riscv_save_reg_p (regno))
+ if (riscv_save_reg_p (regno)
+ || (interrupt_save_t1 && (regno == T1_REGNUM)))
frame->mask |= 1 << (regno - GP_REG_FIRST), num_x_saved++;
/* If this function calls eh_return, we must also save and restore the
@@ -3612,17 +3664,12 @@ riscv_expand_prologue (void)
unsigned mask = frame->mask;
rtx insn;
- if (cfun->machine->naked_p)
- {
- if (flag_stack_usage_info)
- current_function_static_stack_size = 0;
-
- return;
- }
-
if (flag_stack_usage_info)
current_function_static_stack_size = size;
+ if (cfun->machine->naked_p)
+ return;
+
/* When optimizing for size, call a subroutine to save the registers. */
if (riscv_use_save_libcall (frame))
{
@@ -3859,10 +3906,33 @@ riscv_expand_epilogue (bool sibcall_p)
emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
EH_RETURN_STACKADJ_RTX));
- if (!sibcall_p)
+ /* Return from interrupt. */
+ if (cfun->machine->interrupt_handler_p)
+ emit_insn (gen_riscv_mret ());
+ else if (!sibcall_p)
emit_jump_insn (gen_simple_return_internal (ra));
}
+/* Implement EPILOGUE_USES. */
+
+bool
+riscv_epilogue_uses (unsigned int regno)
+{
+ if (regno == RETURN_ADDR_REGNUM)
+ return true;
+
+ if (epilogue_completed && cfun->machine->interrupt_handler_p)
+ {
+ /* An interrupt function restores temp regs, so we must indicate that
+ they are live at function end. */
+ if (df_regs_ever_live_p (regno)
+ || (!crtl->is_leaf && call_used_regs[regno]))
+ return true;
+ }
+
+ return false;
+}
+
/* Return nonzero if this function is known to have a null epilogue.
This allows the optimizer to omit jumps to jumps if no stack
was created. */
@@ -3870,7 +3940,8 @@ riscv_expand_epilogue (bool sibcall_p)
bool
riscv_can_use_return_insn (void)
{
- return reload_completed && cfun->machine->frame.total_size == 0;
+ return (reload_completed && cfun->machine->frame.total_size == 0
+ && ! cfun->machine->interrupt_handler_p);
}
/* Implement TARGET_SECONDARY_MEMORY_NEEDED.
@@ -4366,10 +4437,14 @@ riscv_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
if (TARGET_SAVE_RESTORE)
return false;
- /* Don't use sibcall for naked function. */
+ /* Don't use sibcall for naked functions. */
if (cfun->machine->naked_p)
return false;
+ /* Don't use sibcall for interrupt functions. */
+ if (cfun->machine->interrupt_handler_p)
+ return false;
+
return true;
}
@@ -4381,10 +4456,32 @@ riscv_set_current_function (tree decl)
if (decl == NULL_TREE
|| current_function_decl == NULL_TREE
|| current_function_decl == error_mark_node
- || !cfun->machine)
+ || ! cfun->machine
+ || cfun->machine->attributes_checked_p)
return;
cfun->machine->naked_p = riscv_naked_function_p (decl);
+ cfun->machine->interrupt_handler_p
+ = riscv_interrupt_type_p (TREE_TYPE (decl));
+
+ if (cfun->machine->naked_p && cfun->machine->interrupt_handler_p)
+ error ("function attributes %qs and %qs are mutually exclusive",
+ "interrupt", "naked");
+
+ if (cfun->machine->interrupt_handler_p)
+ {
+ tree args = TYPE_ARG_TYPES (TREE_TYPE (decl));
+ tree ret = TREE_TYPE (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");
+ }
+
+ /* Don't print the above diagnostics more than once. */
+ cfun->machine->attributes_checked_p = 1;
}
/* Implement TARGET_CANNOT_COPY_INSN_P. */
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index a9111a4..5651d17 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -538,7 +538,7 @@ typedef struct {
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
memset (&(CUM), 0, sizeof (CUM))
-#define EPILOGUE_USES(REGNO) ((REGNO) == RETURN_ADDR_REGNUM)
+#define EPILOGUE_USES(REGNO) riscv_epilogue_uses (REGNO)
/* Align based on stack boundary, which might have been set by the user. */
#define RISCV_STACK_ALIGN(LOC) \
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 56fe516..fa68197 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -56,6 +56,9 @@
UNSPECV_FRFLAGS
UNSPECV_FSFLAGS
+ ;; Interrupt handler instructions.
+ UNSPECV_MRET
+
;; Blockage and synchronization.
UNSPECV_BLOCKAGE
UNSPECV_FENCE
@@ -64,6 +67,7 @@
(define_constants
[(RETURN_ADDR_REGNUM 1)
+ (GP_REGNUM 3)
(T0_REGNUM 5)
(T1_REGNUM 6)
(S0_REGNUM 8)
@@ -2267,12 +2271,17 @@
[(set (match_operand:SI 0 "register_operand" "=r")
(unspec_volatile [(const_int 0)] UNSPECV_FRFLAGS))]
"TARGET_HARD_FLOAT"
- "frflags %0")
+ "frflags\t%0")
(define_insn "riscv_fsflags"
[(unspec_volatile [(match_operand:SI 0 "csr_operand" "rK")] UNSPECV_FSFLAGS)]
"TARGET_HARD_FLOAT"
- "fsflags %0")
+ "fsflags\t%0")
+
+(define_insn "riscv_mret"
+ [(unspec_volatile [(const_int 0)] UNSPECV_MRET)]
+ ""
+ "mret")
(define_insn "stack_tie<mode>"
[(set (mem:BLK (scratch))
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 942548f..c6d23b5 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5141,6 +5141,12 @@ prologue/epilogue sequences generated by the compiler. Only basic
(@pxref{Basic Asm}). While using extended @code{asm} or a mixture of
basic @code{asm} and C code may appear to work, they cannot be
depended upon to work reliably and are not supported.
+
+@item interrupt
+@cindex @code{interrupt} function attribute, RISC-V
+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.
@end table
@node RL78 Function Attributes
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index b8133dd..4f38b53 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,11 @@
+2018-05-25 Jim Wilson <jimw@sifive.com>
+
+ * gcc.target/riscv/interrupt-1.c: New.
+ * gcc.target/riscv/interrupt-2.c: New.
+ * gcc.target/riscv/interrupt-3.c: New.
+ * gcc.target/riscv/interrupt-4.c: New.
+ * gcc.target/riscv/interrupt-5.c: New.
+
2018-05-25 Steven G. Kargl <kargl@gcc.gnu.org>
PR fortran/85786
diff --git a/gcc/testsuite/gcc.target/riscv/interrupt-1.c b/gcc/testsuite/gcc.target/riscv/interrupt-1.c
new file mode 100644
index 0000000..666b29a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/interrupt-1.c
@@ -0,0 +1,8 @@
+/* Verify the return instruction is mret. */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+void __attribute__ ((interrupt))
+foo (void)
+{
+}
+/* { dg-final { scan-assembler "mret" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/interrupt-2.c b/gcc/testsuite/gcc.target/riscv/interrupt-2.c
new file mode 100644
index 0000000..9559007
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/interrupt-2.c
@@ -0,0 +1,17 @@
+/* Verify that arg regs used as temporaries get saved. */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+void __attribute__ ((interrupt))
+foo2 (void)
+{
+ extern volatile int INTERRUPT_FLAG;
+ INTERRUPT_FLAG = 0;
+
+ extern volatile int COUNTER;
+#ifdef __riscv_atomic
+ __atomic_fetch_add (&COUNTER, 1, __ATOMIC_RELAXED);
+#else
+ COUNTER++;
+#endif
+}
+/* { dg-final { scan-assembler-times "s\[wd\]\ta\[0-7\],\[0-9\]+\\(sp\\)" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/interrupt-3.c b/gcc/testsuite/gcc.target/riscv/interrupt-3.c
new file mode 100644
index 0000000..bc9e0c1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/interrupt-3.c
@@ -0,0 +1,9 @@
+/* Verify t1 is saved before use. */
+/* { dg-do compile } */
+/* { dg-options "-O0 -fomit-frame-pointer" } */
+void __attribute__ ((interrupt))
+foo (void)
+{
+ char array[4096];
+}
+/* { dg-final { scan-assembler "s\[wd\]\tt1" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/interrupt-4.c b/gcc/testsuite/gcc.target/riscv/interrupt-4.c
new file mode 100644
index 0000000..b6fdd19
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/interrupt-4.c
@@ -0,0 +1,18 @@
+/* Verify t1 is saved before use. */
+/* { dg-do compile } */
+/* { dg-options "-O0 -fomit-frame-pointer" } */
+void __attribute__ ((interrupt))
+foo2 (void)
+{
+ char array[4096];
+ extern volatile int INTERRUPT_FLAG;
+ INTERRUPT_FLAG = 0;
+
+ extern volatile int COUNTER;
+#ifdef __riscv_atomic
+ __atomic_fetch_add (&COUNTER, 1, __ATOMIC_RELAXED);
+#else
+ COUNTER++;
+#endif
+}
+/* { dg-final { scan-assembler "s\[wd\]\tt1" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/interrupt-5.c b/gcc/testsuite/gcc.target/riscv/interrupt-5.c
new file mode 100644
index 0000000..bf65e98
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/interrupt-5.c
@@ -0,0 +1,16 @@
+/* Verify proper errors are generated for invalid code. */
+int __attribute__ ((interrupt))
+sub0 (void)
+{ /* { dg-error "function cannot return a value" } */
+ return 10;
+}
+
+void __attribute__ ((interrupt))
+sub1 (int i)
+{ /* { dg-error "function cannot have arguments" } */
+}
+
+void __attribute__ ((interrupt, naked))
+sub2 (void)
+{ /* { dg-error "are mutually exclusive" } */
+}