From d0e0c1300f9f08608873df5571e14a61308dd0c0 Mon Sep 17 00:00:00 2001 From: Kito Cheng Date: Wed, 10 Jun 2020 19:40:59 -0700 Subject: [PATCH] RISC-V: Describe correct USEs for gpr_save pattern [PR95252] - Verified on rv32emc/rv32gc/rv64gc bare-metal target and rv32gc/rv64gc linux target with qemu. gcc/ChangeLog: * config/riscv/predicates.md (gpr_save_operation): New. * config/riscv/riscv-protos.h (riscv_gen_gpr_save_insn): New. (riscv_gpr_save_operation_p): Ditto. * config/riscv/riscv-sr.c (riscv_remove_unneeded_save_restore_calls): Ignore USEs for gpr_save patter. * config/riscv/riscv.c (gpr_save_reg_order): New. (riscv_expand_prologue): Use riscv_gen_gpr_save_insn to gen gpr_save. (riscv_gen_gpr_save_insn): New. (riscv_gpr_save_operation_p): Ditto. * config/riscv/riscv.md (S3_REGNUM): New. (S4_REGNUM): Ditto. (S5_REGNUM): Ditto. (S6_REGNUM): Ditto. (S7_REGNUM): Ditto. (S8_REGNUM): Ditto. (S9_REGNUM): Ditto. (S10_REGNUM): Ditto. (S11_REGNUM): Ditto. (gpr_save): Model USEs correctly. gcc/testsuite/ChangeLog: * gcc.target/riscv/pr95252.c: New. --- gcc/config/riscv/predicates.md | 6 ++ gcc/config/riscv/riscv-protos.h | 2 + gcc/config/riscv/riscv-sr.c | 4 ++ gcc/config/riscv/riscv.c | 83 +++++++++++++++++++++++- gcc/config/riscv/riscv.md | 19 +++++- gcc/testsuite/gcc.target/riscv/pr95252.c | 47 ++++++++++++++ 6 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/gcc.target/riscv/pr95252.c diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md index f722881b3cd..f764fe7ba01 100644 --- a/gcc/config/riscv/predicates.md +++ b/gcc/config/riscv/predicates.md @@ -206,3 +206,9 @@ (define_predicate "fp_branch_comparison" (match_code "unordered,ordered,unlt,unge,unle,ungt,uneq,ltgt,ne,eq,lt,le,gt,ge")) + +(define_special_predicate "gpr_save_operation" + (match_code "parallel") +{ + return riscv_gpr_save_operation_p (op); +}) diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h index 2f3ca99710b..9cda6a88879 100644 --- a/gcc/config/riscv/riscv-protos.h +++ b/gcc/config/riscv/riscv-protos.h @@ -73,6 +73,8 @@ 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); extern bool riscv_store_data_bypass_p (rtx_insn *, rtx_insn *); +extern rtx riscv_gen_gpr_save_insn (struct riscv_frame_info *); +extern bool riscv_gpr_save_operation_p (rtx); /* Routines implemented in riscv-c.c. */ void riscv_cpu_cpp_builtins (cpp_reader *); diff --git a/gcc/config/riscv/riscv-sr.c b/gcc/config/riscv/riscv-sr.c index 744d0c48c33..b8fe9d08fb0 100644 --- a/gcc/config/riscv/riscv-sr.c +++ b/gcc/config/riscv/riscv-sr.c @@ -306,6 +306,10 @@ riscv_remove_unneeded_save_restore_calls (void) if (CALL_P (insn)) ++call_count; + /* Ignore any USEs in the gpr_save pattern. They don't prevent us + from optimizing away the save call. */ + else if (insn == prologue_matched) + ; else { df_ref use; diff --git a/gcc/config/riscv/riscv.c b/gcc/config/riscv/riscv.c index 1ad9799fce4..c11ed060116 100644 --- a/gcc/config/riscv/riscv.c +++ b/gcc/config/riscv/riscv.c @@ -334,6 +334,14 @@ static const struct attribute_spec riscv_attribute_table[] = { NULL, 0, 0, false, false, false, false, NULL, NULL } }; +/* Order for the CLOBBERs/USEs of gpr_save. */ +static const unsigned gpr_save_reg_order[] = { + INVALID_REGNUM, T0_REGNUM, T1_REGNUM, RETURN_ADDR_REGNUM, + S0_REGNUM, S1_REGNUM, S2_REGNUM, S3_REGNUM, S4_REGNUM, + S5_REGNUM, S6_REGNUM, S7_REGNUM, S8_REGNUM, S9_REGNUM, + S10_REGNUM, S11_REGNUM +}; + /* A table describing all the processors GCC knows about. */ static const struct riscv_cpu_info riscv_cpu_info_table[] = { { "rocket", generic, &rocket_tune_info }, @@ -4069,9 +4077,9 @@ riscv_expand_prologue (void) rtx dwarf = NULL_RTX; dwarf = riscv_adjust_libcall_cfi_prologue (); - frame->mask = 0; /* Temporarily fib that we need not save GPRs. */ size -= frame->save_libcall_adjustment; - insn = emit_insn (gen_gpr_save (GEN_INT (mask))); + insn = emit_insn (riscv_gen_gpr_save_insn (frame)); + frame->mask = 0; /* Temporarily fib that we need not save GPRs. */ RTX_FRAME_RELATED_P (insn) = 1; REG_NOTES (insn) = dwarf; @@ -5177,6 +5185,77 @@ riscv_new_address_profitable_p (rtx memref, rtx_insn *insn, rtx new_addr) return new_cost <= old_cost; } +/* Helper function for generating gpr_save pattern. */ + +rtx +riscv_gen_gpr_save_insn (struct riscv_frame_info *frame) +{ + unsigned count = riscv_save_libcall_count (frame->mask); + /* 1 for unspec 2 for clobber t0/t1 and 1 for ra. */ + unsigned veclen = 1 + 2 + 1 + count; + rtvec vec = rtvec_alloc (veclen); + + gcc_assert (veclen <= ARRAY_SIZE (gpr_save_reg_order)); + + RTVEC_ELT (vec, 0) = + gen_rtx_UNSPEC_VOLATILE (VOIDmode, + gen_rtvec (1, GEN_INT (frame->mask)), UNSPECV_GPR_SAVE); + + for (int i = 1; i < veclen; ++i) + { + unsigned regno = gpr_save_reg_order[i]; + rtx reg = gen_rtx_REG (Pmode, regno); + rtx elt; + + /* t0 and t1 are CLOBBERs, others are USEs. */ + if (i < 3) + elt = gen_rtx_CLOBBER (Pmode, reg); + else + elt = gen_rtx_USE (Pmode, reg); + + RTVEC_ELT (vec, i) = elt; + } + + /* Largest number of caller-save register must set in mask if we are + not using __riscv_save_0. */ + gcc_assert ((count == 0) || + BITSET_P (frame->mask, gpr_save_reg_order[veclen - 1])); + + return gen_rtx_PARALLEL (VOIDmode, vec); +} + +/* Return true if it's valid gpr_save pattern. */ + +bool +riscv_gpr_save_operation_p (rtx op) +{ + HOST_WIDE_INT len = XVECLEN (op, 0); + gcc_assert (len <= ARRAY_SIZE (gpr_save_reg_order)); + for (int i = 0; i < len; i++) + { + rtx elt = XVECEXP (op, 0, i); + if (i == 0) + { + /* First element in parallel is unspec. */ + if (GET_CODE (elt) != UNSPEC_VOLATILE + || GET_CODE (XVECEXP (elt, 0, 0)) != CONST_INT + || XINT (elt, 1) != UNSPECV_GPR_SAVE) + return false; + } + else + { + /* Two CLOBBER and USEs, must check the order. */ + unsigned expect_code = i < 3 ? CLOBBER : USE; + if (GET_CODE (elt) != expect_code + || !REG_P (XEXP (elt, 1)) + || (REGNO (XEXP (elt, 1)) != gpr_save_reg_order[i])) + return false; + } + break; + } + return true; +} + /* Initialize the GCC target structure. */ #undef TARGET_ASM_ALIGNED_HI_OP #define TARGET_ASM_ALIGNED_HI_OP "\t.half\t" diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md index f4bdb7d8cfe..d9028c50b9a 100644 --- a/gcc/config/riscv/riscv.md +++ b/gcc/config/riscv/riscv.md @@ -75,6 +75,15 @@ (S0_REGNUM 8) (S1_REGNUM 9) (S2_REGNUM 18) + (S3_REGNUM 19) + (S4_REGNUM 20) + (S5_REGNUM 21) + (S6_REGNUM 22) + (S7_REGNUM 23) + (S8_REGNUM 24) + (S9_REGNUM 25) + (S10_REGNUM 26) + (S11_REGNUM 27) (NORMAL_RETURN 0) (SIBCALL_RETURN 1) @@ -2427,10 +2436,14 @@ "" "ebreak") +;; Must use the registers that we save to prevent the rename reg optimization +;; pass from using them before the gpr_save pattern when shrink wrapping +;; occurs. See bug 95252 for instance. + (define_insn "gpr_save" - [(unspec_volatile [(match_operand 0 "const_int_operand")] UNSPECV_GPR_SAVE) - (clobber (reg:SI T0_REGNUM)) - (clobber (reg:SI T1_REGNUM))] + [(match_parallel 1 "gpr_save_operation" + [(unspec_volatile [(match_operand 0 "const_int_operand")] + UNSPECV_GPR_SAVE)])] "" { return riscv_output_gpr_save (INTVAL (operands[0])); }) diff --git a/gcc/testsuite/gcc.target/riscv/pr95252.c b/gcc/testsuite/gcc.target/riscv/pr95252.c new file mode 100644 index 00000000000..0366c089f83 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr95252.c @@ -0,0 +1,47 @@ +/* PR target/95252 */ +/* { dg-options "-O3 -funroll-loops -msave-restore" } */ +/* { dg-do run } */ + +int a[6], b = 1, d, e; +long long c; +static int f = 1; + +void +fn1 (int p1) +{ + b = (b >> 1) & (1 ^ a[(1 ^ p1) & 5]); +} + +void +fn2 () +{ + b = (b >> 1) & (1 ^ a[(b ^ 1) & 1]); + fn1 (c >> 1 & 5); + fn1 (c >> 2 & 5); + fn1 (c >> 4 & 5); + fn1 (c >> 8 & 5); +} + +int +main () +{ + int i, j; + for (; d;) + { + for (; e;) + fn2 (); + f = 0; + } + for (i = 0; i < 8; i++) + { + if (f) + i = 9; + for (j = 0; j < 7; j++) + fn2 (); + } + + if (b != 0) + __builtin_abort (); + + return 0; +} -- 2.30.2