So ext-dce isn't terribly vector aware, it mostly tries to stay out of
the way and stay conservatively correct when vector modes are encountered.
When that code was added in 2023/2024 it goofed a very important case
that has now reared its ugly head.
Consider a use of V2HI. What bits are live as a result of such a use?
Right now we record 0xffff as the bits potentially live. But that's
badly wrong. We have 2 HI objects, so there's actually 32 bits of live
data, so 0xffffffff.
With the bits live being incorrect we can incorrectly remove an
extension because we think the bits are never read. That's precisely
what happens in this PR.
Not much motivation was given for using GET_MODE_INNER rather than
GET_MODE other than it works better for vector/complex. I should have
caught this when it was introduced.
For a destination, GET_MODE_INNER is safe. The worst case is we'll fail
to mark bits as dead which in turn will inhibit optimization. For a
source operand it's a completely different story. By failing to mark
some bits as live we can (as this pr showed) erroneously remove an
extension.
While reviewing all the uses of GET_MODE_INNER, I've become convinced
the vector handling in carry_backpropagate is broken. If it's going to
support vector, it needs to do what it's doing now with the
GET_MODE_INNER stuff which gives us liveness of an element, then
broadcast the liveness state across all the elements of the vector. It
seems like it should be possible to support this case, but having not
seen anything even close to it in practice, I'm punting it for now and
returning a conservatively correct state when presented with vector
modes in carry_backpropagate.
This has been bootstrapped and regression tested on riscv64, x86_64,
aarch64, alpha, hppa and others. It's also been tested without
regressions on the various *-elf targets. Pushing to the trunk.
Jeff
PR rtl-optimization/126426
gcc/
* ext-dce.cc (carry_backpropagate): Return a conservatively
correct mask when presented with vector modes.
(ext_dce_process_uses): Don't use GET_MODE_INNER, we need to
know the full extent of the bits for vector and complex modes.
gcc/testsuite
* gcc.target/riscv/pr126426.c: New test.
diff --git a/gcc/ext-dce.cc b/gcc/ext-dce.cc
index 9e4b0a429b0..02e7348bf15 100644
--- a/gcc/ext-dce.cc
+++ b/gcc/ext-dce.cc
@@ -920,7 +920,23 @@ carry_backpropagate (unsigned HOST_WIDE_INT mask, enum
rtx_code code, rtx x)
if (mask == 0)
return 0;
- enum machine_mode mode = GET_MODE_INNER (GET_MODE (x));
+ /* Consider a vector operation, the bits live are the element bits live
+ broadcasted across the vector. So there can be holes (consider a
+ logical shift).
+
+ Vector modes aren't likely to represent cases we can optimize with
+ any regularity. It seems sensible to just punt that case in a
+ conservatively correct way.
+
+ The conservatively corect choice here would be to return the mode
+ mask for the outer mode. We're already doing that for modes larger
+ than HOST_BITS_PER_WIDE_INT, so it should be safe for larger vectors
+ as well as something like V2HI. */
+ if (VECTOR_MODE_P (GET_MODE (x)) || COMPLEX_MODE_P (GET_MODE (x)))
+ return GET_MODE_MASK (GET_MODE (x));
+
+
+ enum machine_mode mode = GET_MODE (x);
unsigned HOST_WIDE_INT mmask = GET_MODE_MASK (mode);
/* While we don't try to optimize operations on types larger
@@ -994,6 +1010,11 @@ carry_backpropagate (unsigned HOST_WIDE_INT mask, enum
rtx_code code, rtx x)
|| !GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))).is_constant ())
return -1;
+ /* See note about vector modes near the start of this function. */
+ if (VECTOR_MODE_P (GET_MODE (XEXP (x, 0)))
+ || COMPLEX_MODE_P (GET_MODE (XEXP (x, 0))))
+ return GET_MODE_MASK (GET_MODE (XEXP (x, 0)));
+
/* We want the mode of the inner object. We need to ensure its
sign bit is on in MASK. */
mode = GET_MODE_INNER (GET_MODE (XEXP (x, 0)));
@@ -1142,7 +1163,7 @@ ext_dce_process_uses (rtx_insn *insn, rtx obj,
{
rtx inner = XEXP (src, 0);
unsigned HOST_WIDE_INT src_mask
- = GET_MODE_MASK (GET_MODE_INNER (GET_MODE (inner)));
+ = GET_MODE_MASK (GET_MODE (inner));
/* DST_MASK could be zero if we had something in the SET
that we couldn't handle. */
@@ -1335,8 +1356,7 @@ ext_dce_process_uses (rtx_insn *insn, rtx obj,
propagate destination liveness through, then just
set the mask to the mode's mask. */
if (!safe_for_live_propagation (code))
- tmp_mask
- = GET_MODE_MASK (GET_MODE_INNER (GET_MODE (y)));
+ tmp_mask = GET_MODE_MASK (GET_MODE (y));
if (tmp_mask & 0xff)
bitmap_set_bit (livenow, rn);
diff --git a/gcc/testsuite/gcc.target/riscv/pr126426.c
b/gcc/testsuite/gcc.target/riscv/pr126426.c
new file mode 100644
index 00000000000..e7819d61d62
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr126426.c
@@ -0,0 +1,43 @@
+/* { dg-do run } */
+/* { dg-additional-options "--std=gnu99 -march=rv64gcb" { target rv64 } } */
+/* { dg-additional-options "--std=gnu99 -march=rv32gcb" { target rv32 } } */
+
+typedef union {
+ short mv[2];
+ unsigned mv32;
+} MotionVector;
+enum { kProjectionMvClamp, kMaxFrameDistance };
+short kProjectionMvDivisionLookup[] = {0, 6};
+int SpecGetMvProjectionKernel(int mv, int numerator, int denominator) {
+ int value = mv * numerator * kProjectionMvDivisionLookup[denominator];
+ if (value >= 0)
+ value += 3;
+ value >>= 4;
+ if (value > 4)
+ value = 1;
+ return value;
+}
+void SpecGetMvProjectionNoClamp(MotionVector mv, int numerator, int
denominator,
+ MotionVector *projection_mv) {
+ for (int i = 0; i < 2; ++i)
+ projection_mv->mv[i] =
+ SpecGetMvProjectionKernel(mv.mv[i], numerator, denominator);
+}
+int main() {
+ short mvs[5][2] = {{0}};
+ for (int j = 0; j < 5; ++j) {
+ short *mv_value = mvs[j];
+ for (int numerator = -kMaxFrameDistance; numerator; ++numerator)
+ for (int denominator = 0; denominator <= kMaxFrameDistance;
+ ++denominator) {
+ MotionVector mv, spec_projection_mv;
+ mv.mv[0] = mv.mv[1] = mv_value[1];
+ SpecGetMvProjectionNoClamp(mv, numerator, denominator,
+ &spec_projection_mv);
+ if (spec_projection_mv.mv32) {
+ __builtin_abort ();
+ }
+ }
+ }
+ __builtin_exit (0);
+}