Before this patch, when generating epilogue with zcmp enabled, the compiler tries to check if return value is 0. If so, the cm.popret insn in epilogue sequence and the return value a0=0 insn before the epilogue sequence will be replaced with a cm.popretz insn. However, if shrink wrap is active, the epilogue may not be inserted at the end of function, causing return value a0=0 insn missing.
This patch solves the issue by trying to generate cm.popretz insn after shrink wrap completes insertion of epilogue. TC: main function in gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c before patch: main: ... bltu a5,a4,.L6 .L7: cm.push {ra}, -16 call abort .L6: lui a5,%hi(.LC0) lhu a5,%lo(.LC0)(a5) sh a5,%lo(w)(a2) lhu a5,%lo(w)(a2) xori a5,a5,64 and a5,a5,a3 bltu a5,a4,.L7 ret after patch: main: ... bltu a5,a4,.L6 .L7: cm.push {ra}, -16 call abort .L6: lui a5,%hi(.LC0) lhu a5,%lo(.LC0)(a5) sh a5,%lo(w)(a2) lhu a5,%lo(w)(a2) xori a5,a5,64 and a5,a5,a3 bltu a5,a4,.L7 li a0,0 # missing before patch! ret Passed riscv regression tests on rv64gc. gcc/ChangeLog: * config/riscv/riscv.cc (riscv_zcmp_can_use_popretz): Modify to recognize popret with return value pattern. (riscv_gen_multi_pop_insn): Remove popretz generation. (gen_popretz_from_popret_insn): Generate popretz pattern based on popret insn. (riscv_popret_insn_p): Return true if INSN is a popret insn. (riscv_try_to_gen_popretz): Try to generate popretz insn if possible. (riscv_post_epilogue_proc): Implement TARGET_POST_EPILOGUE_PROC. (TARGET_POST_EPILOGUE_PROC): Define RISC-V hook. gcc/testsuite/ChangeLog: * gcc.target/riscv/rv32i_zcmp.c: New case. Signed-off-by: Fei Gao <gao...@eswincomputing.com> --- gcc/config/riscv/riscv.cc | 191 ++++++++++++++------ gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c | 56 ++++++ 2 files changed, 194 insertions(+), 53 deletions(-) diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index 10af38a5a81..975dd9d15d0 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -8151,52 +8151,6 @@ riscv_adjust_libcall_cfi_epilogue () return dwarf; } -/* return true if popretz pattern can be matched. - set (reg 10 a0) (const_int 0) - use (reg 10 a0) - NOTE_INSN_EPILOGUE_BEG */ -static rtx_insn * -riscv_zcmp_can_use_popretz (void) -{ - rtx_insn *insn = NULL, *use = NULL, *clear = NULL; - - /* sequence stack for NOTE_INSN_EPILOGUE_BEG*/ - struct sequence_stack *outer_seq = get_current_sequence ()->next; - if (!outer_seq) - return NULL; - insn = outer_seq->first; - if (!insn || !NOTE_P (insn) || NOTE_KIND (insn) != NOTE_INSN_EPILOGUE_BEG) - return NULL; - - /* sequence stack for the insn before NOTE_INSN_EPILOGUE_BEG*/ - outer_seq = outer_seq->next; - if (outer_seq) - insn = outer_seq->last; - - /* skip notes */ - while (insn && NOTE_P (insn)) - { - insn = PREV_INSN (insn); - } - use = insn; - - /* match use (reg 10 a0) */ - if (use == NULL || !INSN_P (use) || GET_CODE (PATTERN (use)) != USE - || !REG_P (XEXP (PATTERN (use), 0)) - || REGNO (XEXP (PATTERN (use), 0)) != A0_REGNUM) - return NULL; - - /* match set (reg 10 a0) (const_int 0 [0]) */ - clear = PREV_INSN (use); - if (clear != NULL && INSN_P (clear) && GET_CODE (PATTERN (clear)) == SET - && REG_P (SET_DEST (PATTERN (clear))) - && REGNO (SET_DEST (PATTERN (clear))) == A0_REGNUM - && SET_SRC (PATTERN (clear)) == const0_rtx) - return clear; - - return NULL; -} - static void riscv_gen_multi_pop_insn (bool use_multi_pop_normal, unsigned mask, unsigned multipop_size) @@ -8207,13 +8161,6 @@ riscv_gen_multi_pop_insn (bool use_multi_pop_normal, unsigned mask, if (!use_multi_pop_normal) insn = emit_insn ( riscv_gen_multi_push_pop_insn (POP_IDX, multipop_size, regs_count)); - else if (rtx_insn *clear_a0_insn = riscv_zcmp_can_use_popretz ()) - { - delete_insn (NEXT_INSN (clear_a0_insn)); - delete_insn (clear_a0_insn); - insn = emit_jump_insn ( - riscv_gen_multi_push_pop_insn (POPRETZ_IDX, multipop_size, regs_count)); - } else insn = emit_jump_insn ( riscv_gen_multi_push_pop_insn (POPRET_IDX, multipop_size, regs_count)); @@ -8223,6 +8170,141 @@ riscv_gen_multi_pop_insn (bool use_multi_pop_normal, unsigned mask, REG_NOTES (insn) = dwarf; } +/* Generate popretz pattern based on POPRET insn. */ + +static rtx +gen_popretz_from_popret_insn (rtx_insn *popret) +{ + rtx pat = PATTERN (popret); + unsigned regs_count = XVECLEN (pat, 0) - 3; + rtx set_sp = XVECEXP (pat, 0, 0); + HOST_WIDE_INT multipop_size = INTVAL (XEXP (SET_SRC (set_sp), 1)); + return riscv_gen_multi_push_pop_insn (POPRETZ_IDX, multipop_size, regs_count); +} + +/* Return true if INSN is a popret insn. */ + +static bool +riscv_popret_insn_p (rtx_insn *insn) +{ + if (!INSN_P (insn)) + return false; + + recog_memoized (insn); + int insn_code = INSN_CODE (insn); + + return insn_code == CODE_FOR_gpr_multi_popret_up_to_ra_si + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s0_si + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s1_si + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s2_si + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s3_si + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s4_si + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s5_si + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s6_si + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s7_si + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s8_si + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s9_si + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s11_si + || insn_code == CODE_FOR_gpr_multi_popret_up_to_ra_di + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s0_di + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s1_di + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s2_di + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s3_di + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s4_di + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s5_di + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s6_di + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s7_di + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s8_di + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s9_di + || insn_code == CODE_FOR_gpr_multi_popret_up_to_s11_di; +} + +/* Return true if the following pattern recognized. + (insn ... (set (reg/i:SI 10 a0) + (const_int 0 [0])) ...) + (insn ... (use (reg/i:SI 10 a0)) ...) + skip notes + (note ... NOTE_INSN_EPILOGUE_BEG) + ... + (jump_insn/f ... {gpr_multi_popret_up_to_*} */ + +static bool +riscv_zcmp_can_use_popretz (rtx_insn *epilogue, rtx_insn **clear, + rtx_insn **use, rtx_insn **popret) +{ + rtx_insn *insn = epilogue; + + if (!TARGET_ZCMP) + return false; + + /* skip notes */ + while (insn && NOTE_P (insn)) + insn = PREV_INSN (insn); + + /* match use (reg 10 a0) */ + if (insn == NULL || !INSN_P (insn) || GET_CODE (PATTERN (insn)) != USE + || !REG_P (XEXP (PATTERN (insn), 0)) + || REGNO (XEXP (PATTERN (insn), 0)) != A0_REGNUM) + return false; + *use = insn; + if (dump_file) + fprintf (dump_file, "Found use a0 insn in insn %d.\n", INSN_UID (insn)); + + insn = PREV_INSN (insn); + /* match set (reg 10 a0) (const_int 0 [0]) */ + if (insn == NULL || !INSN_P (insn) || GET_CODE (PATTERN (insn)) != SET + || !REG_P (SET_DEST (PATTERN (insn))) + || REGNO (SET_DEST (PATTERN (insn))) != A0_REGNUM + || SET_SRC (PATTERN (insn)) != const0_rtx) + return false; + *clear = insn; + if (dump_file) + fprintf (dump_file, "Found clear a0 in insn %d.\n", INSN_UID (insn)); + + for (insn = epilogue; insn; insn = NEXT_INSN (insn)) + { + if (riscv_popret_insn_p (insn)) + { + *popret = insn; + if (dump_file) + fprintf (dump_file, "Found popret in insn %d.\n", INSN_UID (insn)); + return true; + } + } + + return false; +} + +/* Try to generate popretz insn if possible, + EPILOGUE points to the 1st insn of epilogue. */ + +void static riscv_try_to_gen_popretz (rtx_insn *epilogue) +{ + rtx_insn *clear_a0_insn = NULL, *use_a0 = NULL, *popret = NULL; + if (!riscv_zcmp_can_use_popretz (epilogue, &clear_a0_insn, &use_a0, &popret)) + return; + + rtx pat_popretz = gen_popretz_from_popret_insn (popret); + rtx_insn *popretz + = emit_jump_insn_after_setloc (pat_popretz, PREV_INSN (popret), + INSN_LOCATION (popret)); + REG_NOTES (popretz) = REG_NOTES (popret); + RTX_FRAME_RELATED_P (popretz) = 1; + delete_insn (clear_a0_insn); + delete_insn (use_a0); + delete_insn (popret); + + if (dump_file) + fprintf (dump_file, "popretz successfully generated.\n"); +} + +/* Implement TARGET_POST_EPILOGUE_PROC. */ + +void static riscv_post_epilogue_proc (rtx_insn *epilogue) +{ + riscv_try_to_gen_popretz (epilogue); +} + /* Expand an "epilogue", "sibcall_epilogue", or "eh_return_internal" pattern; style says which. */ @@ -11723,6 +11805,9 @@ riscv_expand_usadd (rtx dest, rtx x, rtx y) #define TARGET_SHRINK_WRAP_SET_HANDLED_COMPONENTS \ riscv_set_handled_components +#undef TARGET_POST_EPILOGUE_PROC +#define TARGET_POST_EPILOGUE_PROC riscv_post_epilogue_proc + /* The generic ELF target does not always have TLS support. */ #ifdef HAVE_AS_TLS #undef TARGET_HAVE_TLS diff --git a/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c b/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c index 1e1a8be8705..64da00bc094 100644 --- a/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c +++ b/gcc/testsuite/gcc.target/riscv/rv32i_zcmp.c @@ -267,3 +267,59 @@ test_popretz () f1 (); return 0; } + +int +f_1 (void) +{ + return __builtin_issignaling (__builtin_nansf16b ("")); +} + +int +f_2 (void) +{ + return __builtin_issignaling (((__bf16) __builtin_nanf (""))); +} + +int +f_3 (void) +{ + return __builtin_issignaling (0.0bf16); +} + +int +f_4 (__bf16 x) +{ + return __builtin_issignaling (x); +} + +__bf16 w; + +/* +**main: +** ... +** li a0,0 +** ... +*/ +int +main () +{ + if (!f_1 () || f_2 () || f_3 ()) + __builtin_abort (); + + asm volatile("" : : : "memory"); + + if (f_4 (w) || !f_4 (__builtin_nansf16b ("0x123")) || f_4 (42.0bf16) + || f_4 (((__bf16) __builtin_nanf ("0x234"))) + || f_4 (((__bf16) __builtin_inff ())) + || f_4 (-((__bf16) __builtin_inff ())) || f_4 (-42.0bf16) + || f_4 (-0.0bf16) || f_4 (0.0bf16)) + __builtin_abort (); + + w = __builtin_nansf16b (""); + asm volatile("" : : : "memory"); + + if (!f_4 (w)) + __builtin_abort (); + + return 0; +} -- 2.17.1