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)

Reply via email to