From b85637e5d20e84c263a134f329e09d081714b983 Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Sun, 5 Jul 2015 16:44:24 -0700 Subject: gcc: add experimental support for compressed prologues/epilogues These call library routines to save and restore the callee-saved registers and link register. --- gcc/gcc/common/config/riscv/riscv-common.c | 1 + gcc/gcc/config/riscv/riscv-opc.h | 6 +- gcc/gcc/config/riscv/riscv-protos.h | 2 +- gcc/gcc/config/riscv/riscv.c | 146 ++++++++++++++----- gcc/gcc/config/riscv/riscv.h | 4 + gcc/gcc/config/riscv/riscv.md | 28 +++- gcc/gcc/config/riscv/riscv.opt | 4 + gcc/libgcc/config/riscv/save-restore.S | 220 +++++++++++++++++++++++++++++ gcc/libgcc/config/riscv/t-elf | 1 + 9 files changed, 373 insertions(+), 39 deletions(-) create mode 100644 gcc/libgcc/config/riscv/save-restore.S (limited to 'gcc') 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 -- cgit v1.1