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

Reply via email to