[gcc r17-3009] RISC-V: Don't let popretz combine across a call [PR target/126454]

Kito Cheng via Gcc-cvs <[email protected]> Thu, 6 Aug 2026 08:52:58 +0000 (GMT)
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:c80ac3cb559f6ea2be6be3d0458c1615ff27c9a0

commit r17-3009-gc80ac3cb559f6ea2be6be3d0458c1615ff27c9a0
Author: Kito Cheng <[email protected]>
Date:   Tue Aug 4 10:43:37 2026 +0800

    RISC-V: Don't let popretz combine across a call [PR target/126454]
    
    The popretz pass scans backwards from cm.popret for the "li a0, 0" that
    feeds the (use a0), but nothing stopped the scan at a call: the argument
    registers of a call live in CALL_INSN_FUNCTION_USAGE, which
    reg_referenced_p does not look at, and the a0 set by a call_value hides
    in a PARALLEL, which the bare SET test did not match.  A "li a0, 0" that
    sets up the first argument of a call was therefore deleted and cm.popret
    became cm.popretz, so the callee got garbage in a0 and the caller
    returned 0 instead of the result of the callee.
    
    Use insn level helpers instead, find_reg_fusage for the uses and
    reg_set_p for the definitions, and ask them about the word_mode a0
    rather than about the a0 of the (use a0), whose mode covers a0 and a1
    for a DImode return value on rv32.
    
    gcc/ChangeLog:
    
            PR target/126454
            * config/riscv/riscv-opt-popretz.cc (pass_combine_popretz::execute):
            Stop the backward scan at any use or definition of a0, including
            those hidden in CALL_INSN_FUNCTION_USAGE or in a PARALLEL.
    
    gcc/testsuite/ChangeLog:
    
            PR target/126454
            * gcc.target/riscv/pr126454.c: New test.

Diff:
---
 gcc/config/riscv/riscv-opt-popretz.cc     | 44 ++++++++++++++++++-------------
 gcc/testsuite/gcc.target/riscv/pr126454.c | 36 +++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 19 deletions(-)

diff --git a/gcc/config/riscv/riscv-opt-popretz.cc b/gcc/config/riscv/riscv-opt-popretz.cc
index a109780417b1..3c501d3df239 100644
--- a/gcc/config/riscv/riscv-opt-popretz.cc
+++ b/gcc/config/riscv/riscv-opt-popretz.cc
@@ -205,7 +205,7 @@ pass_combine_popretz::execute (function *fn)
 
       rtx_insn *def_a0_insn = NULL;
       rtx_insn *use_a0_insn = NULL;
-      rtx a0_reg = NULL;
+      rtx a0_reg = regno_reg_rtx[A0_REGNUM];
       /* Scan backwards from popret to find the pattern:
          1. First, find the (use a0) pseudo-instruction
          2. Continue scanning to find "li a0, 0" (set a0 to const0_rtx)
@@ -223,29 +223,35 @@ pass_combine_popretz::execute (function *fn)
 	      && REG_P (XEXP (def_pat, 0))
 	      && REGNO (XEXP (def_pat, 0)) == A0_REGNUM)
 	    {
-	      a0_reg = XEXP (def_pat, 0);
 	      use_a0_insn = def_insn;
 	      continue;
 	    }
 
-	  if (use_a0_insn && reg_referenced_p (a0_reg, def_pat))
+	  if (use_a0_insn)
 	    {
-	      /* a0 is used by other instruction before its use in popret.  */
-	      use_a0_insn = NULL;
-	      break;
-	    }
-
-	  if (use_a0_insn
-	      && GET_CODE (def_pat) == SET
-	      && REG_P (SET_DEST (def_pat))
-	      && REGNO (SET_DEST (def_pat)) == A0_REGNUM)
-	    {
-	      if (SET_SRC (def_pat) == CONST0_RTX (GET_MODE (SET_SRC (def_pat))))
-	        def_a0_insn = def_insn;
-	      /* Stop the search regardless of the value assigned to a0,
-	         because we only want to match the last (most recent)
-	         definition of a0 before the (use a0).  */
-	      break;
+	      if (reg_referenced_p (a0_reg, def_pat)
+		  || (CALL_P (def_insn)
+		      && find_reg_fusage (def_insn, USE, a0_reg)))
+		{
+		  /* a0 is used by other instruction before its use in
+		     popret.  */
+		  use_a0_insn = NULL;
+		  break;
+		}
+
+	      if (reg_set_p (a0_reg, def_insn))
+		{
+		  rtx set = single_set (def_insn);
+		  if (set
+		      && REG_P (SET_DEST (set))
+		      && REGNO (SET_DEST (set)) == A0_REGNUM
+		      && SET_SRC (set) == CONST0_RTX (GET_MODE (SET_SRC (set))))
+		    def_a0_insn = def_insn;
+		  /* Stop the search regardless of the value assigned to a0,
+		     because we only want to match the last (most recent)
+		     definition of a0 before the (use a0).  */
+		  break;
+		}
 	    }
 	  }
 
diff --git a/gcc/testsuite/gcc.target/riscv/pr126454.c b/gcc/testsuite/gcc.target/riscv/pr126454.c
new file mode 100644
index 000000000000..ac30c1756dff
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr126454.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options " -Os -march=rv32ima_zca_zcmp -mabi=ilp32 -mcmodel=medlow" }*/
+/* { dg-skip-if "" { *-*-* } {"-O0" "-O1" "-O2" "-Og" "-O3" "-Oz" "-flto"} } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+int callee (int a, int *p);
+
+/*
+**test_arg_setup:
+**	...
+**	li	a0,0
+**	call	callee(?:@plt)?
+**	cm.popret	{ra}, 32
+**	...
+*/
+int
+test_arg_setup (int mode)
+{
+  int local = mode;
+  return callee (0, &local);
+}
+
+/*
+**test_ret_zero:
+**	...
+**	call	callee(?:@plt)?
+**	cm.popretz	{ra}, 32
+**	...
+*/
+int
+test_ret_zero (int mode)
+{
+  int local = mode;
+  callee (0, &local);
+  return 0;
+}