From: Kyrylo Tkachov <[email protected]>
Late vsetvl PRE can insert a configuration on an edge from a shared
jump-table dispatch. Such an edge cannot be split for one dispatch
without also changing the other users of the table.
Keep the local configuration in blocks with incoming shared-table edges,
using the existing invalid_opt_bb_p check.
A checking RISC-V cross compiler with an audit-only shared-table hook
reproduced the rtl_split_edge ICE. The guard passed the RVV runtime test
at VLEN 128 and 256. The RISC-V hook is not enabled by this patch, so the
test does not exercise table sharing in the product configuration.
This patch was written by an AI agent as I don't feel familiar enough
with risc-v internals and I can't vouch for it. If the risc-v
maintainers want it I'm happy to commit it, of course.
Assisted-by: OpenAI Codex 5.6 Sol
gcc/ChangeLog:
PR middle-end/126236
* config/riscv/riscv-vsetvl.cc (invalid_opt_bb_p): Keep local
configurations in blocks with incoming shared-table edges.
gcc/testsuite/ChangeLog:
PR middle-end/126236
* gcc.target/riscv/rvv/vsetvl/pr126236.c: New test.
Cc: Jeff Law <[email protected]>
Cc: Robin Dapp <[email protected]>
Signed-off-by: Kyrylo Tkachov <[email protected]>
---
gcc/config/riscv/riscv-vsetvl.cc | 13 +-
.../gcc.target/riscv/rvv/vsetvl/pr126236.c | 114 ++++++++++++++++++
2 files changed, 124 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/vsetvl/pr126236.c
diff --git a/gcc/config/riscv/riscv-vsetvl.cc b/gcc/config/riscv/riscv-vsetvl.cc
index 4db4349a6e1..4dc73249081 100644
--- a/gcc/config/riscv/riscv-vsetvl.cc
+++ b/gcc/config/riscv/riscv-vsetvl.cc
@@ -781,10 +781,17 @@ invalid_opt_bb_p (basic_block cfg_bb)
edge e;
edge_iterator ei;
- /* We don't do LCM optimizations on complex edges. */
+ /* Keep local configurations when an incoming edge cannot be split for
+ a vsetvl insertion. */
FOR_EACH_EDGE (e, ei, cfg_bb->preds)
- if (e->flags & EDGE_COMPLEX)
- return true;
+ {
+ rtx_jump_table_data *table;
+ if ((e->flags & EDGE_COMPLEX)
+ || (e->src != ENTRY_BLOCK_PTR_FOR_FN (cfun)
+ && tablejump_p (BB_END (e->src), nullptr, &table)
+ && JUMP_TABLE_SHARED_P (PATTERN (table))))
+ return true;
+ }
/* We only do LCM optimizations on blocks that are post dominated by
EXIT block, that is, we don't do LCM optimizations on infinite loop. */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/vsetvl/pr126236.c
b/gcc/testsuite/gcc.target/riscv/rvv/vsetvl/pr126236.c
new file mode 100644
index 00000000000..1657f3f691f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/vsetvl/pr126236.c
@@ -0,0 +1,114 @@
+/* PR middle-end/126236 */
+/* { dg-do run { target { riscv_v } } } */
+/* { dg-additional-options "-std=c99 -O2 -fduplicate-switch-dispatch
-mno-explicit-relocs" } */
+
+/* Execute each ordered pair of handlers with different vector configurations.
+ The reference uses the same unsigned operations without vectorization. */
+
+struct state
+{
+ unsigned char byte[128];
+ unsigned short half[64];
+ unsigned int word[32];
+ unsigned long long wide[16];
+};
+
+static struct state actual __attribute__((aligned (64)));
+static struct state expected __attribute__((aligned (64)));
+
+#define DISPATCH_BODY \
+ for (;;) \
+ switch (*pc++) \
+ { \
+ case 0: return; \
+ case 1: \
+ for (unsigned i = 0; i < 128; ++i) \
+ s->byte[i] += 3; \
+ break; \
+ case 2: \
+ for (unsigned i = 0; i < 64; ++i) \
+ s->half[i] ^= 0x1357; \
+ break; \
+ case 3: \
+ for (unsigned i = 0; i < 32; ++i) \
+ s->word[i] += 0x1234567u; \
+ break; \
+ case 4: \
+ for (unsigned i = 0; i < 16; ++i) \
+ s->wide[i] ^= 0x13579bdf02468aceull; \
+ break; \
+ case 5: \
+ for (unsigned i = 0; i < 57; ++i) \
+ s->byte[i] ^= 0x5a; \
+ break; \
+ case 6: \
+ for (unsigned i = 0; i < 31; ++i) \
+ s->half[i] += 71; \
+ break; \
+ case 7: \
+ for (unsigned i = 0; i < 13; ++i) \
+ s->word[i] ^= 0x2468ace0u; \
+ break; \
+ case 8: \
+ for (unsigned i = 0; i < 5; ++i) \
+ s->wide[i] += 0x102030405060708ull; \
+ break; \
+ default: return; \
+ }
+
+__attribute__((noinline, noclone))
+static void
+vector_dispatch (struct state *s, const unsigned char *pc)
+{
+ DISPATCH_BODY
+}
+
+__attribute__((noinline, noclone, optimize ("no-tree-vectorize")))
+static void
+scalar_dispatch (struct state *s, const unsigned char *pc)
+{
+ DISPATCH_BODY
+}
+
+int
+main (void)
+{
+ unsigned char code[129];
+ unsigned pos = 0;
+ for (unsigned first = 1; first <= 8; ++first)
+ for (unsigned second = 1; second <= 8; ++second)
+ {
+ code[pos++] = first;
+ code[pos++] = second;
+ }
+ code[pos] = 0;
+
+ for (unsigned i = 0; i < 128; ++i)
+ actual.byte[i] = expected.byte[i] = i * 7 + 1;
+ for (unsigned i = 0; i < 64; ++i)
+ actual.half[i] = expected.half[i] = i * 257 + 3;
+ for (unsigned i = 0; i < 32; ++i)
+ actual.word[i] = expected.word[i] = i * 0x1234567u + 5;
+ for (unsigned i = 0; i < 16; ++i)
+ actual.wide[i] = expected.wide[i] = i * 0x123456789abcdefull + 7;
+
+ for (unsigned round = 0; round < 12; ++round)
+ {
+ vector_dispatch (&actual, code);
+ scalar_dispatch (&expected, code);
+ }
+
+ for (unsigned i = 0; i < 128; ++i)
+ if (actual.byte[i] != expected.byte[i])
+ return 1;
+ for (unsigned i = 0; i < 64; ++i)
+ if (actual.half[i] != expected.half[i])
+ return 2;
+ for (unsigned i = 0; i < 32; ++i)
+ if (actual.word[i] != expected.word[i])
+ return 3;
+ for (unsigned i = 0; i < 16; ++i)
+ if (actual.wide[i] != expected.wide[i])
+ return 4;
+ return 0;
+}
--
2.50.1 (Apple Git-155)