diff options
author | Jiuyang liu | 2019-06-25 08:38:31 +0000 |
---|---|---|
committer | Jiuyang liu | 2019-06-25 08:38:31 +0000 |
commit | ea4c1a3e9a89cd74a1e844f1db9d7b2ee78e9213 (patch) | |
tree | 34fc748ac62993dcdbf4c9daee922995b49b7d39 /0001-SiFive-CLIC-patches-for-preemptible-and-stack-swappi.patch | |
download | aur-riscv-sifive-elf-gcc-stage1.tar.gz |
init
Diffstat (limited to '0001-SiFive-CLIC-patches-for-preemptible-and-stack-swappi.patch')
-rw-r--r-- | 0001-SiFive-CLIC-patches-for-preemptible-and-stack-swappi.patch | 644 |
1 files changed, 644 insertions, 0 deletions
diff --git a/0001-SiFive-CLIC-patches-for-preemptible-and-stack-swappi.patch b/0001-SiFive-CLIC-patches-for-preemptible-and-stack-swappi.patch new file mode 100644 index 000000000000..9be1181eba25 --- /dev/null +++ b/0001-SiFive-CLIC-patches-for-preemptible-and-stack-swappi.patch @@ -0,0 +1,644 @@ +From d79fde036c461b5fc410e1ef4fb3bac8a66088ca Mon Sep 17 00:00:00 2001 +From: Jim Wilson <jimw@sifive.com> +Date: Thu, 21 Feb 2019 19:50:46 -0800 +Subject: [PATCH 1/3] SiFive CLIC patches, for preemptible and stack-swapping + interrupt support. + +--- + gcc/config/riscv/constraints.md | 5 + + gcc/config/riscv/predicates.md | 4 + + gcc/config/riscv/riscv.c | 193 ++++++++++++++++-- + gcc/config/riscv/riscv.md | 57 ++++++ + gcc/doc/extend.texi | 25 +++ + gcc/doc/md.texi | 3 + + gcc/testsuite/gcc.target/riscv/interrupt-5.c | 15 ++ + .../gcc.target/riscv/preemptible-1.c | 11 + + .../gcc.target/riscv/preemptible-2.c | 11 + + .../gcc.target/riscv/preemptible-3.c | 9 + + gcc/testsuite/gcc.target/riscv/stack-swap-1.c | 18 ++ + 11 files changed, 330 insertions(+), 21 deletions(-) + create mode 100644 gcc/testsuite/gcc.target/riscv/preemptible-1.c + create mode 100644 gcc/testsuite/gcc.target/riscv/preemptible-2.c + create mode 100644 gcc/testsuite/gcc.target/riscv/preemptible-3.c + create mode 100644 gcc/testsuite/gcc.target/riscv/stack-swap-1.c + +diff --git a/gcc/config/riscv/constraints.md b/gcc/config/riscv/constraints.md +index b4de83f8324..a736013d9f6 100644 +--- a/gcc/config/riscv/constraints.md ++++ b/gcc/config/riscv/constraints.md +@@ -49,6 +49,11 @@ + (and (match_code "const_int") + (match_test "IN_RANGE (ival, 0, 31)"))) + ++(define_constraint "C" ++ "A 12-bit unsigned immediate for CSR address." ++ (and (match_code "const_int") ++ (match_test "IN_RANGE (ival, 0, IMM_REACH-1)"))) ++ + ;; Floating-point constant +0.0, used for FCVT-based moves when FMV is + ;; not available in RV32. + (define_constraint "G" +diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md +index 83fc4bd663d..83e698793b3 100644 +--- a/gcc/config/riscv/predicates.md ++++ b/gcc/config/riscv/predicates.md +@@ -35,6 +35,10 @@ + (ior (match_operand 0 "const_csr_operand") + (match_operand 0 "register_operand"))) + ++(define_predicate "csr_address" ++ (and (match_code "const_int") ++ (match_test "IN_RANGE (INTVAL (op), 0, IMM_REACH-1)"))) ++ + (define_predicate "sle_operand" + (and (match_code "const_int") + (match_test "SMALL_OPERAND (INTVAL (op) + 1)"))) +diff --git a/gcc/config/riscv/riscv.c b/gcc/config/riscv/riscv.c +index e7440f39095..d90715cca23 100644 +--- a/gcc/config/riscv/riscv.c ++++ b/gcc/config/riscv/riscv.c +@@ -139,6 +139,12 @@ struct GTY(()) machine_function { + bool interrupt_handler_p; + /* For an interrupt handler, indicates the privilege level. */ + enum riscv_privilege_levels interrupt_mode; ++ /* True if current function is an SiFive CLIC preemptible interrupt ++ function. */ ++ bool sifive_clic_preemptible_p; ++ /* True if current function is an SiFive CLIC stack swap interrupt ++ function. */ ++ bool sifive_clic_stack_swap_p; + + /* True if attributes on current function have been checked. */ + bool attributes_checked_p; +@@ -326,7 +332,7 @@ 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, 1, false, true, true, false, ++ { "interrupt", 0, 2, false, true, true, false, + riscv_handle_type_attribute, NULL }, + + /* The last attribute spec is set to be NULL. */ +@@ -2813,7 +2819,7 @@ riscv_handle_type_attribute (tree *node ATTRIBUTE_UNUSED, tree name, tree args, + /* Check for an argument. */ + if (is_attribute_p ("interrupt", name)) + { +- if (args) ++ while (args != NULL) + { + tree cst = TREE_VALUE (args); + const char *string; +@@ -2829,14 +2835,18 @@ riscv_handle_type_attribute (tree *node ATTRIBUTE_UNUSED, tree name, tree args, + + string = TREE_STRING_POINTER (cst); + if (strcmp (string, "user") && strcmp (string, "supervisor") +- && strcmp (string, "machine")) ++ && strcmp (string, "machine") ++ && strcmp (string, "SiFive-CLIC-preemptible") ++ && strcmp (string, "SiFive-CLIC-stack-swap")) + { + warning (OPT_Wattributes, +- "argument to %qE attribute is not \"user\", \"supervisor\", or \"machine\"", +- name); ++ "unrecognized argument to %qE attribute", name); + *no_add_attrs = true; + } ++ args = TREE_CHAIN (args); + } ++ ++ return NULL_TREE; + } + + return NULL_TREE; +@@ -3147,6 +3157,7 @@ riscv_memmodel_needs_release_fence (enum memmodel model) + 'A' Print the atomic operation suffix for memory model OP. + 'F' Print a FENCE if the memory model requires a release. + 'z' Print x0 if OP is zero, otherwise print OP normally. ++ 'x' Print CONST_INT OP as a CSR register name or as a hex number. + 'i' Print i if the operand is not a register. */ + + static void +@@ -3206,6 +3217,35 @@ riscv_print_operand (FILE *file, rtx op, int letter) + default: + if (letter == 'z' && op == CONST0_RTX (GET_MODE (op))) + fputs (reg_names[GP_REG_FIRST], file); ++ else if (letter == 'x' && GET_CODE (op) == CONST_INT) ++ { ++ unsigned HOST_WIDE_INT reg_num = UINTVAL (op); ++ const char *reg_name = NULL; ++ ++ switch (reg_num) ++ { ++ case MSTATUS_REGNUM: ++ reg_name = "mstatus"; ++ break; ++ case MEPC_REGNUM: ++ reg_name = "mepc"; ++ break; ++ case MCAUSE_REGNUM: ++ reg_name = "mcause"; ++ break; ++ case MSCRATCHI_REGNUM: ++ reg_name = "mscratchi"; ++ break; ++ case MSCRATCHO_REGNUM: ++ reg_name = "mscratcho"; ++ break; ++ } ++ ++ if (reg_name) ++ asm_fprintf (file, "%s", reg_name); ++ else ++ asm_fprintf (file, "0x%wx", reg_num); ++ } + else if (letter && letter != 'z') + output_operand_lossage ("invalid use of '%%%c'", letter); + else +@@ -3505,8 +3545,14 @@ riscv_compute_frame_info (void) + unsigned x_save_size = RISCV_STACK_ALIGN (num_x_saved * UNITS_PER_WORD); + unsigned num_save_restore = 1 + riscv_save_libcall_count (frame->mask); + ++ /* In an SiFive CLIC preemptible interrupt function, we need extra space ++ for the initial saves of S0 and S1. */ ++ if (cfun->machine->sifive_clic_preemptible_p) ++ x_save_size = RISCV_STACK_ALIGN ((num_x_saved + 2) * UNITS_PER_WORD); ++ + /* Only use save/restore routines if they don't alter the stack size. */ +- if (RISCV_STACK_ALIGN (num_save_restore * UNITS_PER_WORD) == x_save_size) ++ else if (RISCV_STACK_ALIGN (num_save_restore * UNITS_PER_WORD) ++ == x_save_size) + { + /* Libcall saves/restores 3 registers at once, so we need to + allocate 12 bytes for callee-saved register. */ +@@ -3811,6 +3857,7 @@ riscv_expand_prologue (void) + { + struct riscv_frame_info *frame = &cfun->machine->frame; + HOST_WIDE_INT size = frame->total_size; ++ HOST_WIDE_INT interrupt_size = 0; + unsigned mask = frame->mask; + rtx insn; + +@@ -3834,6 +3881,12 @@ riscv_expand_prologue (void) + REG_NOTES (insn) = dwarf; + } + ++ /* Swap in the stack pointer from the mscratch register. */ ++ if (cfun->machine->sifive_clic_stack_swap_p) ++ emit_insn (gen_riscv_csr_read_write (stack_pointer_rtx, ++ GEN_INT (MSCRATCHI_REGNUM), ++ stack_pointer_rtx)); ++ + /* Save the registers. */ + if ((frame->mask | frame->fmask) != 0) + { +@@ -3844,7 +3897,34 @@ riscv_expand_prologue (void) + GEN_INT (-step1)); + RTX_FRAME_RELATED_P (emit_insn (insn)) = 1; + size -= step1; +- riscv_for_each_saved_reg (size, riscv_save_reg, false, false); ++ ++ if (cfun->machine->sifive_clic_preemptible_p) ++ { ++ /* Save S0 and S1. */ ++ riscv_save_restore_reg (word_mode, S0_REGNUM, ++ step1 - (1 * UNITS_PER_WORD), ++ riscv_save_reg); ++ riscv_save_restore_reg (word_mode, S1_REGNUM, ++ step1 - (2 * UNITS_PER_WORD), ++ riscv_save_reg); ++ /* Account for the stack size used by interrupt register saving. */ ++ interrupt_size = 2 * UNITS_PER_WORD; ++ ++ /* Load cause into s0. */ ++ emit_insn (gen_riscv_csr_read (gen_rtx_REG (word_mode, S0_REGNUM), ++ GEN_INT (MCAUSE_REGNUM))); ++ /* Load epc into s1. */ ++ emit_insn (gen_riscv_csr_read (gen_rtx_REG (word_mode, S1_REGNUM), ++ GEN_INT (MEPC_REGNUM))); ++ /* Re-enable interrupts. */ ++ emit_insn (gen_riscv_csr_read_set_bits (gen_rtx_REG (word_mode, ++ GP_REG_FIRST), ++ GEN_INT (MSTATUS_REGNUM), ++ GEN_INT (MSTATUS_MIE_BIT))); ++ } ++ ++ riscv_for_each_saved_reg (size + interrupt_size, riscv_save_reg, ++ false, false); + } + + frame->mask = mask; /* Undo the above fib. */ +@@ -3852,6 +3932,10 @@ riscv_expand_prologue (void) + /* Set up the frame pointer, if we're using one. */ + if (frame_pointer_needed) + { ++ if (cfun->machine->sifive_clic_preemptible_p) ++ error ("SiFive CLIC preemptible %qs function cannot use a frame pointer", ++ "interrupt"); ++ + insn = gen_add3_insn (hard_frame_pointer_rtx, stack_pointer_rtx, + GEN_INT (frame->hard_frame_pointer_offset - size)); + RTX_FRAME_RELATED_P (emit_insn (insn)) = 1; +@@ -3921,6 +4005,7 @@ riscv_expand_epilogue (int style) + unsigned mask = frame->mask; + HOST_WIDE_INT step1 = frame->total_size; + HOST_WIDE_INT step2 = 0; ++ HOST_WIDE_INT interrupt_size = 0; + bool use_restore_libcall = ((style == NORMAL_RETURN) + && riscv_use_save_libcall (frame)); + rtx ra = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM); +@@ -4021,10 +4106,13 @@ riscv_expand_epilogue (int style) + + if (use_restore_libcall) + frame->mask = 0; /* Temporarily fib that we need not save GPRs. */ ++ else if (cfun->machine->sifive_clic_preemptible_p) ++ interrupt_size = 2 * UNITS_PER_WORD; + + /* Restore the registers. */ +- riscv_for_each_saved_reg (frame->total_size - step2, riscv_restore_reg, +- true, style == EXCEPTION_RETURN); ++ riscv_for_each_saved_reg (frame->total_size - step2 + interrupt_size, ++ riscv_restore_reg, true, ++ style == EXCEPTION_RETURN); + + if (use_restore_libcall) + { +@@ -4032,6 +4120,29 @@ riscv_expand_epilogue (int style) + gcc_assert (step2 >= frame->save_libcall_adjustment); + step2 -= frame->save_libcall_adjustment; + } ++ else if (cfun->machine->sifive_clic_preemptible_p ++ && (frame->mask | frame->fmask) != 0) ++ { ++ /* Disable interrupts. */ ++ emit_insn (gen_riscv_csr_read_clear_bits (gen_rtx_REG (word_mode, ++ GP_REG_FIRST), ++ GEN_INT (MSTATUS_REGNUM), ++ GEN_INT (MSTATUS_MIE_BIT))); ++ /* Save s1 back into mepc. */ ++ emit_insn (gen_riscv_csr_write (GEN_INT (MEPC_REGNUM), ++ gen_rtx_REG (word_mode, S1_REGNUM))); ++ /* Save s0 back into mcause. */ ++ emit_insn (gen_riscv_csr_write (GEN_INT (MCAUSE_REGNUM), ++ gen_rtx_REG (word_mode, S0_REGNUM))); ++ ++ /* Restore S0 and S1. */ ++ riscv_save_restore_reg (word_mode, S1_REGNUM, ++ step2 - (2 * UNITS_PER_WORD), ++ riscv_restore_reg); ++ riscv_save_restore_reg (word_mode, S0_REGNUM, ++ step2 - (1 * UNITS_PER_WORD), ++ riscv_restore_reg); ++ } + + if (need_barrier_p) + riscv_emit_stack_tie (); +@@ -4051,6 +4162,12 @@ riscv_expand_epilogue (int style) + REG_NOTES (insn) = dwarf; + } + ++ /* Swap out the stack opinter from the mscratch register. */ ++ if (cfun->machine->sifive_clic_stack_swap_p) ++ emit_insn (gen_riscv_csr_read_write (stack_pointer_rtx, ++ GEN_INT (MSCRATCHO_REGNUM), ++ stack_pointer_rtx)); ++ + if (use_restore_libcall) + { + rtx dwarf = riscv_adjust_libcall_cfi_epilogue (); +@@ -4738,7 +4855,8 @@ riscv_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED, + + /* Get the interrupt type, return UNKNOWN_MODE if it's not + interrupt function. */ +-static enum riscv_privilege_levels ++/* ??? HACK ??? */ ++static void + riscv_get_interrupt_type (tree decl) + { + gcc_assert (decl != NULL_TREE); +@@ -4751,20 +4869,37 @@ riscv_get_interrupt_type (tree decl) + = TREE_VALUE (lookup_attribute ("interrupt", + TYPE_ATTRIBUTES (TREE_TYPE (decl)))); + +- if (attr_args && TREE_CODE (TREE_VALUE (attr_args)) != VOID_TYPE) ++ bool interrupt_mode_set = FALSE; ++ ++ /* Interrupt attributes are machine mode by default. */ ++ cfun->machine->interrupt_mode = MACHINE_MODE; ++ ++ while (attr_args) + { + const char *string = TREE_STRING_POINTER (TREE_VALUE (attr_args)); + +- if (!strcmp (string, "user")) +- return USER_MODE; +- else if (!strcmp (string, "supervisor")) +- return SUPERVISOR_MODE; +- else /* Must be "machine". */ +- return MACHINE_MODE; ++ if (!strcmp (string, "SiFive-CLIC-preemptible")) ++ cfun->machine->sifive_clic_preemptible_p = TRUE; ++ else if (!strcmp (string, "SiFive-CLIC-stack-swap")) ++ cfun->machine->sifive_clic_stack_swap_p = TRUE; ++ else ++ { ++ if (interrupt_mode_set) ++ error ("%qs function cannot have two modes", "interrupt"); ++ interrupt_mode_set = TRUE; ++ ++ if (!strcmp (string, "user")) ++ cfun->machine->interrupt_mode = USER_MODE; ++ else if (!strcmp (string, "supervisor")) ++ cfun->machine->interrupt_mode = SUPERVISOR_MODE; ++ else if (!strcmp (string, "machine")) ++ cfun->machine->interrupt_mode = MACHINE_MODE; ++ else ++ abort (); ++ } ++ ++ attr_args = TREE_CHAIN (attr_args); + } +- else +- /* Interrupt attributes are machine mode by default. */ +- return MACHINE_MODE; + } + + /* Implement `TARGET_SET_CURRENT_FUNCTION'. */ +@@ -4798,7 +4933,17 @@ riscv_set_current_function (tree decl) + if (args && TREE_CODE (TREE_VALUE (args)) != VOID_TYPE) + error ("%qs function cannot have arguments", "interrupt"); + +- cfun->machine->interrupt_mode = riscv_get_interrupt_type (decl); ++ riscv_get_interrupt_type (decl); ++ ++ if (cfun->machine->interrupt_mode != MACHINE_MODE) ++ { ++ if (cfun->machine->sifive_clic_preemptible_p) ++ error ("SiFive CLIC preemptible %qs function must be machine mode", ++ "interrupt"); ++ else if (cfun->machine->sifive_clic_stack_swap_p) ++ error ("SiFive CLIC stack-swap %qs function must be machine mode", ++ "interrupt"); ++ } + + gcc_assert (cfun->machine->interrupt_mode != UNKNOWN_MODE); + } +@@ -4807,6 +4952,8 @@ riscv_set_current_function (tree decl) + cfun->machine->attributes_checked_p = 1; + } + ++#if 0 ++/* ??? HACK ??? */ + /* Implement TARGET_MERGE_DECL_ATTRIBUTES. */ + static tree + riscv_merge_decl_attributes (tree olddecl, tree newdecl) +@@ -4830,6 +4977,7 @@ riscv_merge_decl_attributes (tree olddecl, tree newdecl) + + return combined_attrs; + } ++#endif + + /* Implement TARGET_CANNOT_COPY_INSN_P. */ + +@@ -5027,8 +5175,11 @@ riscv_constant_alignment (const_tree exp, HOST_WIDE_INT align) + #undef TARGET_CONSTANT_ALIGNMENT + #define TARGET_CONSTANT_ALIGNMENT riscv_constant_alignment + ++#if 0 ++/* ??? HACK ??? */ + #undef TARGET_MERGE_DECL_ATTRIBUTES + #define TARGET_MERGE_DECL_ATTRIBUTES riscv_merge_decl_attributes ++#endif + + #undef TARGET_ATTRIBUTE_TABLE + #define TARGET_ATTRIBUTE_TABLE riscv_attribute_table +diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md +index e3799a5bdd8..3b7d4f92c1c 100644 +--- a/gcc/config/riscv/riscv.md ++++ b/gcc/config/riscv/riscv.md +@@ -56,6 +56,17 @@ + UNSPECV_FRFLAGS + UNSPECV_FSFLAGS + ++ ;; CSR read ++ UNSPECV_CSR_READ ++ ;; CSR read and set bits ++ UNSPECV_CSR_READ_SET_BITS ++ ;; CSR read and clear bits ++ UNSPECV_CSR_READ_CLEAR_BITS ++ ;; CSR read and write ++ UNSPECV_CSR_READ_WRITE ++ ;; CSR write ++ UNSPECV_CSR_WRITE ++ + ;; Interrupt handler instructions. + UNSPECV_MRET + UNSPECV_SRET +@@ -79,6 +90,14 @@ + (NORMAL_RETURN 0) + (SIBCALL_RETURN 1) + (EXCEPTION_RETURN 2) ++ ++ (MSTATUS_REGNUM 0x300) ++ (MEPC_REGNUM 0x341) ++ (MCAUSE_REGNUM 0x342) ++ (MSCRATCHI_REGNUM 0x348) ++ (MSCRATCHO_REGNUM 0x349) ++ ++ (MSTATUS_MIE_BIT 8) + ]) + + (include "predicates.md") +@@ -2400,6 +2419,44 @@ + "TARGET_HARD_FLOAT" + "fsflags\t%0") + ++(define_insn "riscv_csr_read" ++ [(set (match_operand 0 "register_operand" "=r") ++ (unspec_volatile [(match_operand 1 "csr_address" "C")] ++ UNSPECV_CSR_READ))] ++ "" ++ "csrr\t%0,%x1") ++ ++(define_insn "riscv_csr_read_set_bits" ++ [(set (match_operand 0 "register_operand" "=r") ++ (unspec_volatile [(match_operand 1 "csr_address" "C") ++ (match_operand 2 "csr_operand" "rK")] ++ UNSPECV_CSR_READ_SET_BITS))] ++ "" ++ "csrrsi\t%0,%x1,%2") ++ ++(define_insn "riscv_csr_read_clear_bits" ++ [(set (match_operand 0 "register_operand" "=r") ++ (unspec_volatile [(match_operand 1 "csr_address" "C") ++ (match_operand 2 "csr_operand" "rK")] ++ UNSPECV_CSR_READ_CLEAR_BITS))] ++ "" ++ "csrrci\t%0,%x1,%2") ++ ++(define_insn "riscv_csr_read_write" ++ [(set (match_operand 0 "register_operand" "=r") ++ (unspec_volatile [(match_operand 1 "csr_address" "C") ++ (match_operand 2 "csr_operand" "rK")] ++ UNSPECV_CSR_READ_WRITE))] ++ "" ++ "csrrw\t%0,%x1,%2") ++ ++(define_insn "riscv_csr_write" ++ [(unspec_volatile [(match_operand 0 "csr_address" "C") ++ (match_operand 1 "register_operand" "r")] ++ UNSPECV_CSR_WRITE)] ++ "" ++ "csrw\t%x0,%1") ++ + (define_insn "riscv_mret" + [(return) + (unspec_volatile [(const_int 0)] UNSPECV_MRET)] +diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi +index 91679e8b9ba..883db8894db 100644 +--- a/gcc/doc/extend.texi ++++ b/gcc/doc/extend.texi +@@ -5592,6 +5592,31 @@ void f (void) __attribute__ ((interrupt ("user"))); + Permissible values for this parameter are @code{user}, @code{supervisor}, + and @code{machine}. If there is no parameter, then it defaults to + @code{machine}. ++ ++You can specify an SiFive CLIC preemptible interrupt handler by adding an ++optional parameter to the interrupt attribute like this: ++ ++@smallexample ++void f (void) __attribute__ ((interrupt ("SiFive-CLIC-preemptible"))); ++@end smallexample ++ ++In this type of interrupt handler, in the prologue, the mepc and ++mcause registers are saved, and interrupts are enabled. In the epilogue, ++interrupts are disabled, and the mepc and mcause registers are restored. ++This type of interrupt handler must be @code{machine} mode, and must ++not use the frame pointer. ++ ++You can specify an SiFive CLIC stack swapping interrupt handler by adding an ++optional parameter to the interrupt attribute like this: ++ ++@smallexample ++void f (void) __attribute__ ((interrupt ("SiFive-CLIC-stack-swap"))); ++@end smallexample ++ ++In this type of interrupt handler, the stack pointer will be swapped with ++the @code{mscratch} register in the prologue before the first use of the stack ++pointer, and in the epilogue after the last use of the stack pointer. This ++type of interrupt handler must be @code{machine} mode. + @end table + + @node RL78 Function Attributes +diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi +index 30612a6aecb..3783bb25314 100644 +--- a/gcc/doc/md.texi ++++ b/gcc/doc/md.texi +@@ -3577,6 +3577,9 @@ Integer zero. + @item K + A 5-bit unsigned immediate for CSR access instructions. + ++@item C ++A 12-bit unsigned immediate for CSR register address. ++ + @item A + An address that is held in a general-purpose register. + +diff --git a/gcc/testsuite/gcc.target/riscv/interrupt-5.c b/gcc/testsuite/gcc.target/riscv/interrupt-5.c +index 324954eb1dd..03d8173e5b3 100644 +--- a/gcc/testsuite/gcc.target/riscv/interrupt-5.c ++++ b/gcc/testsuite/gcc.target/riscv/interrupt-5.c +@@ -19,3 +19,18 @@ void __attribute__ ((interrupt ("hypervisor"))) + sub3 (void) + { /* { dg-warning "argument to" } */ + } ++ ++void __attribute__ ((interrupt ("user", "machine"))) ++sub4 (void) ++{ /* { dg-error "cannot have two modes" } */ ++} ++ ++void __attribute__ ((interrupt ("user", "SiFive-CLIC-preemptible"))) ++sub5 (void) ++{ /* { dg-error "must be machine mode" } */ ++} ++ ++void __attribute__ ((interrupt ("user", "SiFive-CLIC-stack-swap"))) ++sub6 (void) ++{ /* { dg-error "must be machine mode" } */ ++} +diff --git a/gcc/testsuite/gcc.target/riscv/preemptible-1.c b/gcc/testsuite/gcc.target/riscv/preemptible-1.c +new file mode 100644 +index 00000000000..578667f46cd +--- /dev/null ++++ b/gcc/testsuite/gcc.target/riscv/preemptible-1.c +@@ -0,0 +1,11 @@ ++/* Verify that csr instructions emitted. */ ++/* { dg-do compile } */ ++/* { dg-options "-O" } */ ++extern int interrupt_count; ++ ++void __attribute__ ((interrupt ("SiFive-CLIC-preemptible"))) ++sub (void) ++{ ++ interrupt_count++; ++} ++/* { dg-final { scan-assembler-times "csr" 6 } } */ +diff --git a/gcc/testsuite/gcc.target/riscv/preemptible-2.c b/gcc/testsuite/gcc.target/riscv/preemptible-2.c +new file mode 100644 +index 00000000000..27cd31de188 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/riscv/preemptible-2.c +@@ -0,0 +1,11 @@ ++/* Verify that all temp regs saved for call. */ ++/* { dg-do compile } */ ++/* { dg-options "-O" } */ ++extern int vector_10 (void); ++ ++void __attribute__ ((interrupt ("SiFive-CLIC-preemptible"))) ++sub (void) ++{ ++ vector_10 (); ++} ++/* { dg-final { scan-assembler-times "s\[wd\]\t\[at\]\[0-7\]" 15 } } */ +diff --git a/gcc/testsuite/gcc.target/riscv/preemptible-3.c b/gcc/testsuite/gcc.target/riscv/preemptible-3.c +new file mode 100644 +index 00000000000..c01d9ded7d5 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/riscv/preemptible-3.c +@@ -0,0 +1,9 @@ ++/* Verify that csr instructions are not emitted. */ ++/* { dg-do compile } */ ++/* { dg-options "-O" } */ ++ ++void __attribute__ ((interrupt ("SiFive-CLIC-preemptible"))) ++sub (void) ++{ ++} ++/* { dg-final { scan-assembler-not "csr" } } */ +diff --git a/gcc/testsuite/gcc.target/riscv/stack-swap-1.c b/gcc/testsuite/gcc.target/riscv/stack-swap-1.c +new file mode 100644 +index 00000000000..2e26432cd89 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/riscv/stack-swap-1.c +@@ -0,0 +1,18 @@ ++/* Verify that stack swapping instructions are emitted. */ ++/* { dg-do compile } */ ++/* { dg-options "-O" } */ ++void __attribute__ ((interrupt ("SiFive-CLIC-stack-swap"))) ++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 "mscratchi" } } */ ++/* { dg-final { scan-assembler "mscratcho" } } */ +-- +2.21.0 + |