find_cond_trap emits the sequence returned by gen_cond_trap before
cond_earliest without checking what it sets. On targets where a
conditional trap is a comparison feeding a trap insn the sequence sets the
condition code register, which need not be dead there: with SPARC -mcbcond
the jump does not read the CC, so a CC value set in an earlier block is
live across TEST_BB and used by a later branch. Overwriting it silently
changes what that branch tests.
On sparc64 this miscompiles a switch over a sparse enum whose default arm
is unreachable. It is why LLVM 23 does not build on sparc64: clang-tblgen
takes the unreachable arm for a valid enumerator in
clang::RISCV::RVVType::applyBasicType(), and with that worked around the
resulting libLLVM is miscompiled in the same way and clang then traps in
llvm::TruncInstCombine::getReducedOperand().
Bootstrapped on sparc64-unknown-linux-gnu. check-gcc against an otherwise
identical unpatched tree is unchanged. LLVM 23.1.1, which does not
otherwise build on this target, builds with the patch applied. The new
test fails without the patch and passes with it.
gcc/ChangeLog:
PR rtl-optimization/127578
* ifcvt.cc (find_cond_trap): Do not convert when the generated
sequence sets a register that is live before cond_earliest.
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.
---
gcc/ifcvt.cc | 19 +++++++++++
.../gcc.target/sparc/cbcond-trap-1.c | 34 +++++++++++++++++++
gcc/testsuite/lib/target-supports.exp | 9 +++++
3 files changed, 62 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/sparc/cbcond-trap-1.c
diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc
index 618c297..3e69c84 100644
--- a/gcc/ifcvt.cc
+++ b/gcc/ifcvt.cc
@@ -6114,6 +6114,25 @@ find_cond_trap (basic_block test_bb, edge then_edge,
edge else_edge)
: recog_memoized (x) < 0)
return false;
+ /* SEQ may set the condition code register (a comparison feeding the trap
+ insn). With SPARC -mcbcond the jump does not read the CC, so it can be
+ live across TEST_BB and used by a later branch. */
+ HARD_REG_SET seq_sets;
+ CLEAR_HARD_REG_SET (seq_sets);
+ for (rtx_insn *x = seq; x; x = NEXT_INSN (x))
+ if (INSN_P (x))
+ note_stores (x, record_hard_reg_sets, &seq_sets);
+
+ if (!hard_reg_set_empty_p (seq_sets))
+ {
+ auto_bitmap live (®_obstack);
+ HARD_REG_SET live_regs;
+ simulate_backwards_to_point (test_bb, live, PREV_INSN (cond_earliest));
+ REG_SET_TO_HARD_REG_SET (live_regs, live);
+ if (hard_reg_set_intersect_p (seq_sets, live_regs))
+ 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.55.0