From: Kyrylo Tkachov <[email protected]>

Different switch tables can reach the same case block. The CET pass
inserts an ENDBR for each table, although the destination needs only one.
This also affects dispatches that share a table.

Reuse an existing ENDBR after the destination label. The x86 table-sharing
hook remains disabled.

The regression test has two ordinary tables with eight common destinations.
It emits 17 ENDBR instructions before the fix and 9 after it, in both
32-bit and 64-bit code. Both outputs assemble. A 64-bit runtime check
passes on x86_64.

This patch was written by an AI agent as I'm not familiar enough with
the x86 internals and can't vouch for it. But if the x86 maintainers
want it I'm happy to push it.

Assisted-by: OpenAI Codex 5.6 Sol

gcc/ChangeLog:

        * config/i386/i386-features.cc
        (rest_of_insert_endbr_and_patchable_area): Reuse an existing
        ENDBR at a switch destination.

gcc/testsuite/ChangeLog:

        * gcc.target/i386/cet-switch-4.c: New test.

Cc: Uros Bizjak <[email protected]>
Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 gcc/config/i386/i386-features.cc             |  4 ++
 gcc/testsuite/gcc.target/i386/cet-switch-4.c | 56 ++++++++++++++++++++
 2 files changed, 60 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/i386/cet-switch-4.c

diff --git a/gcc/config/i386/i386-features.cc b/gcc/config/i386/i386-features.cc
index 1d26aa0c336..7c992eadf3c 100644
--- a/gcc/config/i386/i386-features.cc
+++ b/gcc/config/i386/i386-features.cc
@@ -3274,6 +3274,10 @@ rest_of_insert_endbr_and_patchable_area (bool need_endbr,
                  dest_blk = e->dest;
                  insn = BB_HEAD (dest_blk);
                  gcc_assert (LABEL_P (insn));
+                 rtx_insn *next = next_nonnote_nondebug_insn (insn);
+                 if (next && INSN_P (next)
+                     && recog_memoized (next) == CODE_FOR_nop_endbr)
+                   continue;
                  endbr = gen_nop_endbr ();
                  emit_insn_after (endbr, insn);
                }
diff --git a/gcc/testsuite/gcc.target/i386/cet-switch-4.c 
b/gcc/testsuite/gcc.target/i386/cet-switch-4.c
new file mode 100644
index 00000000000..53cb960f0d5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/cet-switch-4.c
@@ -0,0 +1,56 @@
+/* PR middle-end/126236 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fcf-protection=branch -mcet-switch -fno-pie -fno-pic" } 
*/
+/* { dg-final { scan-assembler-times "endbr32" 9 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "endbr64" 9 { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-times "\[ \t]+jmp\[ \t]+\[*]" 2 } } */
+
+/* Both tables reach the same eight destinations.  */
+
+void f0 (void);
+void f1 (void);
+void f2 (void);
+void f3 (void);
+void f4 (void);
+void f5 (void);
+void f6 (void);
+void f7 (void);
+
+void
+foo (unsigned x, int which)
+{
+  if (which)
+    switch (x)
+      {
+      case 0: goto a;
+      case 1: goto b;
+      case 2: goto c;
+      case 3: goto d;
+      case 4: goto e;
+      case 5: goto f;
+      case 6: goto g;
+      case 7: goto h;
+      default: return;
+      }
+  else
+    switch (x)
+      {
+      case 0: goto h;
+      case 1: goto g;
+      case 2: goto f;
+      case 3: goto e;
+      case 4: goto d;
+      case 5: goto c;
+      case 6: goto b;
+      case 7: goto a;
+      default: return;
+      }
+a: f0 (); return;
+b: f1 (); return;
+c: f2 (); return;
+d: f3 (); return;
+e: f4 (); return;
+f: f5 (); return;
+g: f6 (); return;
+h: f7 (); return;
+}
-- 
2.50.1 (Apple Git-155)

Reply via email to