aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorAndrew Waterman <waterman@cs.berkeley.edu>2015-07-05 16:44:24 -0700
committerAndrew Waterman <waterman@cs.berkeley.edu>2015-07-05 16:45:43 -0700
commitb85637e5d20e84c263a134f329e09d081714b983 (patch)
tree296c2e62742aaf239d46628f0facc42b2abcf284 /gcc
parent4583979d24a957368afc0672d053155803f4eca9 (diff)
downloadriscv-gnu-toolchain-b85637e5d20e84c263a134f329e09d081714b983.zip
riscv-gnu-toolchain-b85637e5d20e84c263a134f329e09d081714b983.tar.gz
riscv-gnu-toolchain-b85637e5d20e84c263a134f329e09d081714b983.tar.bz2
gcc: add experimental support for compressed prologues/epilogues
These call library routines to save and restore the callee-saved registers and link register.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/gcc/common/config/riscv/riscv-common.c1
-rw-r--r--gcc/gcc/config/riscv/riscv-opc.h6
-rw-r--r--gcc/gcc/config/riscv/riscv-protos.h2
-rw-r--r--gcc/gcc/config/riscv/riscv.c146
-rw-r--r--gcc/gcc/config/riscv/riscv.h4
-rw-r--r--gcc/gcc/config/riscv/riscv.md28
-rw-r--r--gcc/gcc/config/riscv/riscv.opt4
-rw-r--r--gcc/libgcc/config/riscv/save-restore.S220
-rw-r--r--gcc/libgcc/config/riscv/t-elf1
9 files changed, 373 insertions, 39 deletions
diff --git a/gcc/gcc/common/config/riscv/riscv-common.c b/gcc/gcc/common/config/riscv/riscv-common.c
index 2186b2b..921957a 100644
--- a/gcc/gcc/common/config/riscv/riscv-common.c
+++ b/gcc/gcc/common/config/riscv/riscv-common.c
@@ -115,6 +115,7 @@ static const struct default_options riscv_option_optimization_table[] =
{
{ OPT_LEVELS_1_PLUS, OPT_fsection_anchors, NULL, 1 },
{ OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
+ { OPT_LEVELS_SIZE, OPT_msave_restore, NULL, 1 },
{ OPT_LEVELS_NONE, 0, NULL, 0 }
};
diff --git a/gcc/gcc/config/riscv/riscv-opc.h b/gcc/gcc/config/riscv/riscv-opc.h
index a479406..21cabff 100644
--- a/gcc/gcc/config/riscv/riscv-opc.h
+++ b/gcc/gcc/config/riscv/riscv-opc.h
@@ -779,7 +779,6 @@
#define CSR_SSTATUS 0x100
#define CSR_STVEC 0x101
#define CSR_SIE 0x104
-#define CSR_STIMECMP 0x121
#define CSR_SSCRATCH 0x140
#define CSR_SEPC 0x141
#define CSR_SIP 0x144
@@ -818,6 +817,7 @@
#define CSR_INSTRETHW 0x982
#define CSR_STIMEH 0xd81
#define CSR_STIMEHW 0xa81
+#define CSR_MTIMECMPH 0x361
#define CSR_MTIMEH 0x741
#define CAUSE_MISALIGNED_FETCH 0x0
#define CAUSE_FAULT_FETCH 0x1
@@ -1237,7 +1237,6 @@ DECLARE_CSR(uarch15, CSR_UARCH15)
DECLARE_CSR(sstatus, CSR_SSTATUS)
DECLARE_CSR(stvec, CSR_STVEC)
DECLARE_CSR(sie, CSR_SIE)
-DECLARE_CSR(stimecmp, CSR_STIMECMP)
DECLARE_CSR(sscratch, CSR_SSCRATCH)
DECLARE_CSR(sepc, CSR_SEPC)
DECLARE_CSR(sip, CSR_SIP)
@@ -1276,6 +1275,7 @@ DECLARE_CSR(timehw, CSR_TIMEHW)
DECLARE_CSR(instrethw, CSR_INSTRETHW)
DECLARE_CSR(stimeh, CSR_STIMEH)
DECLARE_CSR(stimehw, CSR_STIMEHW)
+DECLARE_CSR(mtimecmph, CSR_MTIMECMPH)
DECLARE_CSR(mtimeh, CSR_MTIMEH)
#endif
#ifdef DECLARE_CAUSE
@@ -1305,7 +1305,6 @@ DECLARE_CAUSE("uarch15", CAUSE_UARCH15)
DECLARE_CAUSE("sstatus", CAUSE_SSTATUS)
DECLARE_CAUSE("stvec", CAUSE_STVEC)
DECLARE_CAUSE("sie", CAUSE_SIE)
-DECLARE_CAUSE("stimecmp", CAUSE_STIMECMP)
DECLARE_CAUSE("sscratch", CAUSE_SSCRATCH)
DECLARE_CAUSE("sepc", CAUSE_SEPC)
DECLARE_CAUSE("sip", CAUSE_SIP)
@@ -1344,5 +1343,6 @@ DECLARE_CAUSE("timehw", CAUSE_TIMEHW)
DECLARE_CAUSE("instrethw", CAUSE_INSTRETHW)
DECLARE_CAUSE("stimeh", CAUSE_STIMEH)
DECLARE_CAUSE("stimehw", CAUSE_STIMEHW)
+DECLARE_CAUSE("mtimecmph", CAUSE_MTIMECMPH)
DECLARE_CAUSE("mtimeh", CAUSE_MTIMEH)
#endif
diff --git a/gcc/gcc/config/riscv/riscv-protos.h b/gcc/gcc/config/riscv/riscv-protos.h
index 84a74cd..8cc6a56 100644
--- a/gcc/gcc/config/riscv/riscv-protos.h
+++ b/gcc/gcc/config/riscv/riscv-protos.h
@@ -56,7 +56,7 @@ extern rtx riscv_subword (rtx, bool);
extern bool riscv_split_64bit_move_p (rtx, rtx);
extern void riscv_split_doubleword_move (rtx, rtx);
extern const char *riscv_output_move (rtx, rtx);
-extern const char *riscv_riscv_output_vector_move (enum machine_mode, rtx, rtx);
+extern const char *riscv_output_gpr_save (unsigned);
#ifdef RTX_CODE
extern void riscv_expand_scc (rtx *);
extern void riscv_expand_conditional_branch (rtx *);
diff --git a/gcc/gcc/config/riscv/riscv.c b/gcc/gcc/config/riscv/riscv.c
index 150791d..52bedff 100644
--- a/gcc/gcc/config/riscv/riscv.c
+++ b/gcc/gcc/config/riscv/riscv.c
@@ -194,6 +194,9 @@ struct GTY(()) riscv_frame_info {
/* Likewise FPR X. */
unsigned int fmask;
+ /* How much the GPR save/restore routines adjust sp (or 0 if unused). */
+ unsigned save_libcall_adjustment;
+
/* Offsets of fixed-point and floating-point save areas from frame bottom */
HOST_WIDE_INT gp_sp_offset;
HOST_WIDE_INT fp_sp_offset;
@@ -640,7 +643,7 @@ riscv_legitimate_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
/* Implement TARGET_CANNOT_FORCE_CONST_MEM. */
static bool
-riscv_cannot_force_const_mem (enum machine_mode mode, rtx x)
+riscv_cannot_force_const_mem (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
{
enum riscv_symbol_type type;
rtx base, offset;
@@ -3150,7 +3153,9 @@ riscv_compute_frame_info (void)
if (frame->mask)
{
unsigned num_saved = __builtin_popcount(frame->mask);
- offset += RISCV_STACK_ALIGN (num_saved * UNITS_PER_WORD);
+ frame->save_libcall_adjustment =
+ RISCV_STACK_ALIGN (num_saved * UNITS_PER_WORD);
+ offset += frame->save_libcall_adjustment;
frame->gp_sp_offset = offset - UNITS_PER_WORD;
}
/* The hard frame pointer points above the callee-saved GPRs. */
@@ -3162,6 +3167,10 @@ riscv_compute_frame_info (void)
offset += crtl->args.pretend_args_size;
frame->total_size = offset;
/* Next points the incoming stack pointer and any incoming arguments. */
+
+ /* Only use save/restore routines when the GPRs are atop the frame. */
+ if (frame->hard_frame_pointer_offset != frame->total_size)
+ frame->save_libcall_adjustment = 0;
}
/* Make sure that we're not trying to eliminate to the wrong hard frame
@@ -3322,22 +3331,75 @@ riscv_save_reg (rtx reg, rtx mem)
riscv_emit_save_slot_move (mem, reg, RISCV_PROLOGUE_TEMP (GET_MODE (reg)));
}
+/* Determine which GPR save/restore routine to call. */
+
+static unsigned
+riscv_save_restore_count (unsigned mask)
+{
+ for (unsigned n = GP_REG_LAST; n > GP_REG_FIRST; n--)
+ if (BITSET_P (mask, n))
+ return CALLEE_SAVED_REG_NUMBER (n) + 1;
+ abort ();
+}
+
+/* Return the code to invoke the GPR save routine. */
+
+const char *
+riscv_output_gpr_save (unsigned mask)
+{
+ static char buf[GP_REG_NUM * 32];
+ size_t len = 0;
+ unsigned n = riscv_save_restore_count (mask), i;
+ unsigned frame_size = RISCV_STACK_ALIGN ((n + 1) * UNITS_PER_WORD);
+
+ len += sprintf (buf + len, "call\tt0,__riscv_save_%u", n);
+
+#ifdef DWARF2_UNWIND_INFO
+ /* Describe the effect of the call to __riscv_save_X. */
+ if (dwarf2out_do_cfi_asm ())
+ {
+ len += sprintf (buf + len, "\n\t.cfi_def_cfa_offset %u", frame_size);
+
+ for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
+ if (BITSET_P (cfun->machine->frame.mask, i))
+ len += sprintf (buf + len, "\n\t.cfi_offset %u,%d", i,
+ (CALLEE_SAVED_REG_NUMBER (i) + 2) * -UNITS_PER_WORD);
+ }
+#endif
+
+ return buf;
+}
+
+static bool
+riscv_use_save_libcall (const struct riscv_frame_info *frame)
+{
+ if (!TARGET_SAVE_RESTORE || crtl->calls_eh_return || frame_pointer_needed)
+ return false;
+
+ return frame->save_libcall_adjustment != 0;
+}
/* Expand the "prologue" pattern. */
void
riscv_expand_prologue (void)
{
- const struct riscv_frame_info *frame;
- HOST_WIDE_INT size;
+ struct riscv_frame_info *frame = &cfun->machine->frame;
+ HOST_WIDE_INT size = frame->total_size;
+ unsigned mask = frame->mask;
rtx insn;
- frame = &cfun->machine->frame;
- size = frame->total_size;
-
if (flag_stack_usage_info)
current_function_static_stack_size = size;
+ /* When optimizing for size, call a subroutine to save the registers. */
+ if (riscv_use_save_libcall (frame))
+ {
+ frame->mask = 0; /* Temporarily fib that we need not save GPRs. */
+ size -= frame->save_libcall_adjustment;
+ emit_insn (gen_gpr_save (GEN_INT (mask)));
+ }
+
/* Save the registers. Allocate up to RISCV_MAX_FIRST_STACK_STEP
bytes beforehand; this is enough to cover the register save area
without going out of range. */
@@ -3354,6 +3416,8 @@ riscv_expand_prologue (void)
riscv_for_each_saved_gpr_and_fpr (size, riscv_save_reg);
}
+ frame->mask = mask; /* Undo the above fib. */
+
/* Set up the frame pointer, if we're using one. */
if (frame_pointer_needed)
{
@@ -3366,22 +3430,24 @@ riscv_expand_prologue (void)
if (size > 0)
{
if (SMALL_OPERAND (-size))
- RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx,
- stack_pointer_rtx,
- GEN_INT (-size)))) = 1;
+ emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (-size)));
else
{
- riscv_emit_move (RISCV_PROLOGUE_TEMP (Pmode), GEN_INT (size));
- emit_insn (gen_sub3_insn (stack_pointer_rtx,
+ riscv_emit_move (RISCV_PROLOGUE_TEMP (Pmode), GEN_INT (-size));
+ emit_insn (gen_add3_insn (stack_pointer_rtx,
stack_pointer_rtx,
RISCV_PROLOGUE_TEMP (Pmode)));
-
- /* Describe the combined effect of the previous instructions. */
- riscv_set_frame_expr
- (gen_rtx_SET (VOIDmode, stack_pointer_rtx,
- plus_constant (Pmode, stack_pointer_rtx, -size)));
}
}
+
+ if (frame->total_size > 0)
+ {
+ /* Describe the effect of the instructions that adjusted sp. */
+ insn = plus_constant (Pmode, stack_pointer_rtx, -frame->total_size);
+ insn = gen_rtx_SET (VOIDmode, stack_pointer_rtx, insn);
+ riscv_set_frame_expr (insn);
+ }
}
/* Emit instructions to restore register REG from slot MEM. */
@@ -3398,8 +3464,17 @@ riscv_restore_reg (rtx reg, rtx mem)
void
riscv_expand_epilogue (bool sibcall_p)
{
- const struct riscv_frame_info *frame;
- HOST_WIDE_INT step1, step2;
+ /* Split the frame into two. STEP1 is the amount of stack we should
+ deallocate before restoring the registers. STEP2 is the amount we
+ should deallocate afterwards.
+
+ Start off by assuming that no registers need to be restored. */
+ struct riscv_frame_info *frame = &cfun->machine->frame;
+ unsigned mask = frame->mask;
+ HOST_WIDE_INT step1 = frame->total_size;
+ HOST_WIDE_INT step2 = 0;
+ bool use_restore_libcall = !sibcall_p && riscv_use_save_libcall (frame);
+ rtx ra = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM);
if (!sibcall_p && riscv_can_use_return_insn ())
{
@@ -3407,16 +3482,7 @@ riscv_expand_epilogue (bool sibcall_p)
return;
}
- /* Split the frame into two. STEP1 is the amount of stack we should
- deallocate before restoring the registers. STEP2 is the amount we
- should deallocate afterwards.
-
- Start off by assuming that no registers need to be restored. */
- frame = &cfun->machine->frame;
- step1 = frame->total_size;
- step2 = 0;
-
- /* Move past any dynamic stack allocations. */
+ /* Move past any dynamic stack allocations. */
if (cfun->calls_alloca)
{
rtx adjust = GEN_INT (-frame->hard_frame_pointer_offset);
@@ -3451,25 +3517,39 @@ riscv_expand_epilogue (bool sibcall_p)
emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, adjust));
}
+ if (use_restore_libcall)
+ frame->mask = 0; /* Temporarily fib that we need not save GPRs. */
+
/* Restore the registers. */
riscv_for_each_saved_gpr_and_fpr (frame->total_size - step2,
riscv_restore_reg);
+ if (use_restore_libcall)
+ {
+ frame->mask = mask; /* Undo the above fib. */
+ gcc_assert (step2 >= frame->save_libcall_adjustment);
+ step2 -= frame->save_libcall_adjustment;
+ }
+
/* Deallocate the final bit of the frame. */
if (step2 > 0)
emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (step2)));
+ if (use_restore_libcall)
+ {
+ emit_insn (gen_gpr_restore (GEN_INT (riscv_save_restore_count (mask))));
+ emit_jump_insn (gen_gpr_restore_return (ra));
+ return;
+ }
+
/* Add in the __builtin_eh_return stack adjustment. */
if (crtl->calls_eh_return)
emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
EH_RETURN_STACKADJ_RTX));
if (!sibcall_p)
- {
- rtx ra = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM);
- emit_jump_insn (gen_simple_return_internal (ra));
- }
+ emit_jump_insn (gen_simple_return_internal (ra));
}
/* Return nonzero if this function is known to have a null epilogue.
diff --git a/gcc/gcc/config/riscv/riscv.h b/gcc/gcc/config/riscv/riscv.h
index 12ca8cf..bbfec02 100644
--- a/gcc/gcc/config/riscv/riscv.h
+++ b/gcc/gcc/config/riscv/riscv.h
@@ -727,6 +727,10 @@ enum reg_class
#define FP_ARG_FIRST (FP_REG_FIRST + 10)
#define FP_ARG_LAST (FP_ARG_FIRST + MAX_ARGS_IN_REGISTERS - 1)
+#define CALLEE_SAVED_REG_NUMBER(REGNO) \
+ ((REGNO) >= 8 && (REGNO) <= 9 ? (REGNO) - 8 : \
+ (REGNO) >= 18 && (REGNO) <= 27 ? (REGNO) - 16 : -1)
+
#define LIBCALL_VALUE(MODE) \
riscv_function_value (NULL_TREE, NULL_TREE, MODE)
diff --git a/gcc/gcc/config/riscv/riscv.md b/gcc/gcc/config/riscv/riscv.md
index be67ceb..8ce17ab 100644
--- a/gcc/gcc/config/riscv/riscv.md
+++ b/gcc/gcc/config/riscv/riscv.md
@@ -36,6 +36,10 @@
UNSPEC_TLS_IE
UNSPEC_TLS_GD
+ ;; Register save and restore.
+ UNSPEC_GPR_SAVE
+ UNSPEC_GPR_RESTORE
+
;; Blockage and synchronisation.
UNSPEC_BLOCKAGE
UNSPEC_FENCE
@@ -44,6 +48,8 @@
(define_constants
[(RETURN_ADDR_REGNUM 1)
+ (T0_REGNUM 5)
+ (T1_REGNUM 6)
])
(include "predicates.md")
@@ -2318,8 +2324,7 @@
(match_operand 2 "" "")))
(set (match_operand 3 "register_operand" "")
(call (mem:SI (match_dup 1))
- (match_dup 2)))
- (clobber (match_scratch:SI 4 "=j,j"))]
+ (match_dup 2)))]
"SIBLING_CALL_P (insn)"
{ return REG_P (operands[1]) ? "jr\t%1"
: absolute_symbolic_operand (operands[1], VOIDmode) ? "tail\t%1"
@@ -2420,5 +2425,24 @@
""
"sbreak")
+(define_insn "gpr_save"
+ [(unspec_volatile [(match_operand 0 "const_int_operand")] UNSPEC_GPR_SAVE)
+ (clobber (reg:SI T0_REGNUM))
+ (clobber (reg:SI T1_REGNUM))]
+ ""
+ { return riscv_output_gpr_save (INTVAL (operands[0])); })
+
+(define_insn "gpr_restore"
+ [(unspec_volatile [(match_operand 0 "const_int_operand")] UNSPEC_GPR_RESTORE)]
+ ""
+ "tail\t__riscv_restore_%0")
+
+(define_insn "gpr_restore_return"
+ [(return)
+ (use (match_operand 0 "pmode_register_operand" ""))
+ (const_int 0)]
+ ""
+ "")
+
(include "sync.md")
(include "peephole.md")
diff --git a/gcc/gcc/config/riscv/riscv.opt b/gcc/gcc/config/riscv/riscv.opt
index e548935..d31105d 100644
--- a/gcc/gcc/config/riscv/riscv.opt
+++ b/gcc/gcc/config/riscv/riscv.opt
@@ -74,6 +74,10 @@ mrvc
Target Report Mask(RVC)
Use compressed instruction encoding
+msave-restore
+Target Report Mask(SAVE_RESTORE)
+Use smaller but slower prologue and epilogue code
+
mlra
Target Report Var(riscv_lra_flag) Init(0) Save
Use LRA instead of reload
diff --git a/gcc/libgcc/config/riscv/save-restore.S b/gcc/libgcc/config/riscv/save-restore.S
new file mode 100644
index 0000000..bbf0e33
--- /dev/null
+++ b/gcc/libgcc/config/riscv/save-restore.S
@@ -0,0 +1,220 @@
+ .text
+
+ .globl __riscv_save_12
+ .globl __riscv_save_11
+ .globl __riscv_save_10
+ .globl __riscv_save_9
+ .globl __riscv_save_8
+ .globl __riscv_save_7
+ .globl __riscv_save_6
+ .globl __riscv_save_5
+ .globl __riscv_save_4
+ .globl __riscv_save_3
+ .globl __riscv_save_2
+ .globl __riscv_save_1
+ .globl __riscv_save_0
+
+ .globl __riscv_restore_12
+ .globl __riscv_restore_11
+ .globl __riscv_restore_10
+ .globl __riscv_restore_9
+ .globl __riscv_restore_8
+ .globl __riscv_restore_7
+ .globl __riscv_restore_6
+ .globl __riscv_restore_5
+ .globl __riscv_restore_4
+ .globl __riscv_restore_3
+ .globl __riscv_restore_2
+ .globl __riscv_restore_1
+ .globl __riscv_restore_0
+
+#ifdef __riscv64
+
+__riscv_save_12:
+ addi sp, sp, -112
+ li t1, 0
+ sd s11, 8(sp)
+ j .Ls10
+
+__riscv_save_11:
+__riscv_save_10:
+ addi sp, sp, -112
+ li t1, -16
+.Ls10:
+ sd s10, 16(sp)
+ sd s9, 24(sp)
+ j .Ls8
+
+__riscv_save_9:
+__riscv_save_8:
+ addi sp, sp, -112
+ li t1, -32
+.Ls8:
+ sd s8, 32(sp)
+ sd s7, 40(sp)
+ j .Ls6
+
+__riscv_save_7:
+__riscv_save_6:
+ addi sp, sp, -112
+ li t1, -48
+.Ls6:
+ sd s6, 48(sp)
+ sd s5, 56(sp)
+ j .Ls4
+
+__riscv_save_5:
+__riscv_save_4:
+ addi sp, sp, -112
+ li t1, -64
+.Ls4:
+ sd s4, 64(sp)
+ sd s3, 72(sp)
+ j .Ls2
+
+__riscv_save_3:
+__riscv_save_2:
+ addi sp, sp, -112
+ li t1, -80
+.Ls2:
+ sd s2, 80(sp)
+ sd s1, 88(sp)
+ sd s0, 96(sp)
+ sd ra, 104(sp)
+ sub sp, sp, t1
+ jr t0
+
+__riscv_save_1:
+__riscv_save_0:
+ addi sp, sp, -16
+ sd s0, 0(sp)
+ sd ra, 8(sp)
+ jr t0
+
+__riscv_restore_12:
+ ld s11, 8(sp)
+ addi sp, sp, 16
+
+__riscv_restore_11:
+__riscv_restore_10:
+ ld s10, 0(sp)
+ ld s9, 8(sp)
+ addi sp, sp, 16
+
+__riscv_restore_9:
+__riscv_restore_8:
+ ld s8, 0(sp)
+ ld s7, 8(sp)
+ addi sp, sp, 16
+
+__riscv_restore_7:
+__riscv_restore_6:
+ ld s6, 0(sp)
+ ld s5, 8(sp)
+ addi sp, sp, 16
+
+__riscv_restore_5:
+__riscv_restore_4:
+ ld s4, 0(sp)
+ ld s3, 8(sp)
+ addi sp, sp, 16
+
+__riscv_restore_3:
+__riscv_restore_2:
+ ld s2, 0(sp)
+ ld s1, 8(sp)
+ addi sp, sp, 16
+
+__riscv_restore_1:
+__riscv_restore_0:
+ ld s0, 0(sp)
+ ld ra, 8(sp)
+ addi sp, sp, 16
+ ret
+
+#else
+
+__riscv_save_12:
+ addi sp, sp, -64
+ li t1, 0
+ sw s11, 12(sp)
+ j .Ls10
+
+__riscv_save_11:
+__riscv_save_10:
+__riscv_save_9:
+__riscv_save_8:
+ addi sp, sp, -64
+ li t1, -16
+.Ls10:
+ sw s10, 16(sp)
+ sw s9, 20(sp)
+ sw s8, 24(sp)
+ sw s7, 28(sp)
+ j .Ls6
+
+__riscv_save_7:
+__riscv_save_6:
+__riscv_save_5:
+__riscv_save_4:
+ addi sp, sp, -64
+ li t1, -32
+.Ls6:
+ sw s6, 32(sp)
+ sw s5, 36(sp)
+ sw s4, 40(sp)
+ sw s3, 44(sp)
+ sw s2, 48(sp)
+ sw s1, 52(sp)
+ sw s0, 56(sp)
+ sw ra, 60(sp)
+ sub sp, sp, t1
+ jr t0
+
+__riscv_save_3:
+__riscv_save_2:
+__riscv_save_1:
+__riscv_save_0:
+ addi sp, sp, -16
+ sw s2, 0(sp)
+ sw s1, 4(sp)
+ sw s0, 8(sp)
+ sw ra, 12(sp)
+ jr t0
+
+__riscv_restore_12:
+ lw s11, 12(sp)
+ addi sp, sp, 16
+
+__riscv_restore_11:
+__riscv_restore_10:
+__riscv_restore_9:
+__riscv_restore_8:
+ lw s10, 0(sp)
+ lw s9, 4(sp)
+ lw s8, 8(sp)
+ lw s7, 12(sp)
+ addi sp, sp, 16
+
+__riscv_restore_7:
+__riscv_restore_6:
+__riscv_restore_5:
+__riscv_restore_4:
+ lw s6, 0(sp)
+ lw s5, 4(sp)
+ lw s4, 8(sp)
+ lw s3, 12(sp)
+ addi sp, sp, 16
+
+__riscv_restore_3:
+__riscv_restore_2:
+__riscv_restore_1:
+__riscv_restore_0:
+ lw s2, 0(sp)
+ lw s1, 4(sp)
+ lw s0, 8(sp)
+ lw ra, 12(sp)
+ addi sp, sp, 16
+ ret
+
+#endif
diff --git a/gcc/libgcc/config/riscv/t-elf b/gcc/libgcc/config/riscv/t-elf
index e4e877e..7b5800f 100644
--- a/gcc/libgcc/config/riscv/t-elf
+++ b/gcc/libgcc/config/riscv/t-elf
@@ -1,3 +1,4 @@
LIB2ADD += $(srcdir)/config/riscv/riscv-fp.c \
+ $(srcdir)/config/riscv/save-restore.S \
$(srcdir)/config/riscv/mul.S \
$(srcdir)/config/riscv/div.S