find_cond_trap emits the sequence from gen_cond_trap before cond_earliest.
On SPARC that sequence is a comparison followed by a trap on the condition
codes, but the branch it replaces need not read them: a cbcond tests
general registers, so a condition code set earlier can be live across
TEST_BB and read by a later branch, which then tests the wrong value.

The fix for PR126501 added noce_clobbers_live_cc_p for the same problem in
the noce paths.  Use it here too.

On sparc64 with -mcbcond this miscompiles a switch over a sparse enum whose
default arm is unreachable, which is why LLVM 23 does not build there.

Bootstrapped on sparc64-unknown-linux-gnu.  The C and C++ testsuites,
run against an otherwise identical unpatched tree, change only in the
new test, which fails without the patch and passes with it.

gcc/ChangeLog:

        PR rtl-optimization/127578
        * doc/sourcebuild.texi (cbcond_hw): Document.
        * ifcvt.cc (find_cond_trap): Reject a sequence that clobbers a
        condition-code register live out of TEST_BB.

gcc/testsuite/ChangeLog:

        PR rtl-optimization/127578
        * lib/target-supports.exp (check_effective_target_cbcond_hw): New.
        * gcc.target/sparc/cbcond-trap-1.c: New test.
---
Changes since v2: document cbcond_hw in doc/sourcebuild.texi.
Changes since v1: reuse noce_clobbers_live_cc_p, the check that
end_ifcvt_sequence already makes, instead of adding a new liveness check.

 gcc/doc/sourcebuild.texi                      |  4 +++
 gcc/ifcvt.cc                                  |  3 ++
 .../gcc.target/sparc/cbcond-trap-1.c          | 34 +++++++++++++++++++
 gcc/testsuite/lib/target-supports.exp         |  9 +++++
 4 files changed, 50 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/sparc/cbcond-trap-1.c

diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi
index b02cb28..3aa9fcb 100644
--- a/gcc/doc/sourcebuild.texi
+++ b/gcc/doc/sourcebuild.texi
@@ -2758,6 +2758,10 @@ Target supports the execution of @code{avxvnniint16} 
instructions.
 Target runtime permits dereferencing a null pointer without trapping, for
 example because address zero is mapped and readable.
 
+@item cbcond_hw
+Test system can execute SPARC compare-and-branch (@code{cbcond})
+instructions.
+
 @item cell_hw
 Test system can execute AltiVec and Cell PPU instructions.
 
diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc
index 618c297..ab9d356 100644
--- a/gcc/ifcvt.cc
+++ b/gcc/ifcvt.cc
@@ -6114,6 +6114,9 @@ find_cond_trap (basic_block test_bb, edge then_edge, edge 
else_edge)
        : recog_memoized (x) < 0)
       return false;
 
+  if (noce_clobbers_live_cc_p (test_bb, seq))
+    return false;
+
   /* Emit the new insns before cond_earliest.  */
   emit_insn_before_setloc (seq, cond_earliest, INSN_LOCATION (trap));
 
diff --git a/gcc/testsuite/gcc.target/sparc/cbcond-trap-1.c 
b/gcc/testsuite/gcc.target/sparc/cbcond-trap-1.c
new file mode 100644
index 0000000..8fc3ddc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/sparc/cbcond-trap-1.c
@@ -0,0 +1,34 @@
+/* { dg-do run { target cbcond_hw } } */
+/* { dg-options "-O2 -mcpu=niagara4" } */
+
+/* find_cond_trap used to emit the comparison of the conditional trap on top
+   of a %icc value that was still live, because a cbcond jump does not read
+   the CC.  */
+
+struct S { unsigned short bt; unsigned char kind; unsigned bw; };
+
+__attribute__((noinline)) void f (struct S *s)
+{
+  switch (s->bt)
+    {
+    case 1:   s->bw = 8;  s->kind = 1; break;
+    case 2:   s->bw = 16; s->kind = 1; break;
+    case 4:   s->bw = 32; s->kind = 1; break;
+    case 8:   s->bw = 64; s->kind = 1; break;
+    case 16:  s->bw = 16; s->kind = 2; break;
+    case 32:  s->bw = 16; s->kind = 3; break;
+    case 64:  s->bw = 32; s->kind = 3; break;
+    case 128: s->bw = 64; s->kind = 3; break;
+    case 256: s->bw = 8;  s->kind = 4; break;
+    case 512: s->bw = 8;  s->kind = 5; break;
+    default:  __builtin_trap ();
+    }
+}
+
+int
+main (void)
+{
+  struct S s = { 1, 0, 0 };
+  f (&s);
+  return s.bw == 8 ? 0 : 1;
+}
diff --git a/gcc/testsuite/lib/target-supports.exp 
b/gcc/testsuite/lib/target-supports.exp
index 5eb44cb..ae04b6a 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -8564,6 +8564,15 @@ proc check_effective_target_ultrasparc_vis3_hw { } {
     } "-mcpu=niagara3"]
 }
 
+# Return 1 if the test environment supports executing SPARC compare-and-branch
+# instructions.  We check this by attempting: "cwbe %g0, %g0, .+4"
+
+proc check_effective_target_cbcond_hw { } {
+    return [check_runtime cbcond_hw {
+       int main() { __asm__(".word 0x12c00020"); return 0; }
+    } "-mcpu=niagara4"]
+}
+
 # Return 1 if this is a SPARC-V9 target.
 
 proc check_effective_target_sparc_v9 { } {
-- 
2.43.0

Reply via email to