[llvm] SimplifyCFG: Enable switch replacements in more cases (PR #156477)

Jessica Del via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 8 03:01:45 PDT 2025


https://github.com/OutOfCache updated https://github.com/llvm/llvm-project/pull/156477

>From 5d6728698a4399be3b71afb6d9a6019ec6e3d5ae Mon Sep 17 00:00:00 2001
From: Jessica Del <Jessica.Del at amd.com>
Date: Tue, 2 Sep 2025 16:24:33 +0200
Subject: [PATCH 1/8] SimplifyCFG: Add tests for switch-replacements

Currently, some switch optimizations are only possible, if lookup table
support is enabled. However, some of these optimizations are no lookup
tables at all, but a series of simple instructions like mul, add
or shift instructions.

These tests will show how a future change will enable these other kinds
of optimization, even if lookup tables are not supported.
---
 .../switch-transformations-no-lut.ll          | 577 ++++++++++++++++++
 1 file changed, 577 insertions(+)
 create mode 100644 llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll

diff --git a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
new file mode 100644
index 0000000000000..ecca479ad0e4b
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
@@ -0,0 +1,577 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes='simplifycfg' < %s | FileCheck %s --check-prefix=OPTNOLUT
+; RUN: opt -mtriple=amdgcn--amdpal -S -passes='simplifycfg<switch-to-lookup>' < %s | FileCheck %s --check-prefix=TTINOLUT
+;
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+define i32 @linear_transform_with_default(i32 %x) {
+; OPTNOLUT-LABEL: define i32 @linear_transform_with_default(
+; OPTNOLUT-SAME: i32 [[X:%.*]]) {
+; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
+; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[END:.*]] [
+; OPTNOLUT-NEXT:      i32 0, label %[[CASE0:.*]]
+; OPTNOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; OPTNOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; OPTNOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; OPTNOLUT-NEXT:    ]
+; OPTNOLUT:       [[CASE0]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE1]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE2]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE3]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[END]]:
+; OPTNOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 1, %[[CASE0]] ], [ 4, %[[CASE1]] ], [ 7, %[[CASE2]] ], [ 10, %[[CASE3]] ], [ 13, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:    ret i32 [[IDX]]
+;
+; TTINOLUT-LABEL: define i32 @linear_transform_with_default(
+; TTINOLUT-SAME: i32 [[X:%.*]]) {
+; TTINOLUT-NEXT:  [[ENTRY:.*]]:
+; TTINOLUT-NEXT:    switch i32 [[X]], label %[[END:.*]] [
+; TTINOLUT-NEXT:      i32 0, label %[[CASE0:.*]]
+; TTINOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; TTINOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; TTINOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; TTINOLUT-NEXT:    ]
+; TTINOLUT:       [[CASE0]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE1]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE2]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE3]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[END]]:
+; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 1, %[[CASE0]] ], [ 4, %[[CASE1]] ], [ 7, %[[CASE2]] ], [ 10, %[[CASE3]] ], [ 13, %[[ENTRY]] ]
+; TTINOLUT-NEXT:    ret i32 [[IDX]]
+;
+entry:
+  switch i32 %x, label %end [
+  i32 0, label %case0
+  i32 1, label %case1
+  i32 2, label %case2
+  i32 3, label %case3
+  ]
+
+case0:
+  br label %end
+case1:
+  br label %end
+case2:
+  br label %end
+case3:
+  br label %end
+
+end:
+  %idx = phi i32 [ 1, %case0 ], [ 4, %case1 ], [ 7, %case2 ], [ 10, %case3 ], [ 13, %entry ]
+  ret i32 %idx
+}
+
+define i32 @linear_transform_with_outlier(i32 %x) {
+; OPTNOLUT-LABEL: define i32 @linear_transform_with_outlier(
+; OPTNOLUT-SAME: i32 [[X:%.*]]) {
+; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
+; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[END:.*]] [
+; OPTNOLUT-NEXT:      i32 0, label %[[CASE0:.*]]
+; OPTNOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; OPTNOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; OPTNOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; OPTNOLUT-NEXT:      i32 4, label %[[CASE4:.*]]
+; OPTNOLUT-NEXT:    ]
+; OPTNOLUT:       [[CASE0]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE1]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE2]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE3]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE4]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[END]]:
+; OPTNOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 0, %[[CASE0]] ], [ 3, %[[CASE1]] ], [ 6, %[[CASE2]] ], [ 9, %[[CASE3]] ], [ 13, %[[CASE4]] ], [ 12, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:    ret i32 [[IDX]]
+;
+; TTINOLUT-LABEL: define i32 @linear_transform_with_outlier(
+; TTINOLUT-SAME: i32 [[X:%.*]]) {
+; TTINOLUT-NEXT:  [[ENTRY:.*]]:
+; TTINOLUT-NEXT:    switch i32 [[X]], label %[[END:.*]] [
+; TTINOLUT-NEXT:      i32 0, label %[[CASE0:.*]]
+; TTINOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; TTINOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; TTINOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; TTINOLUT-NEXT:      i32 4, label %[[CASE4:.*]]
+; TTINOLUT-NEXT:    ]
+; TTINOLUT:       [[CASE0]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE1]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE2]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE3]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE4]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[END]]:
+; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 0, %[[CASE0]] ], [ 3, %[[CASE1]] ], [ 6, %[[CASE2]] ], [ 9, %[[CASE3]] ], [ 13, %[[CASE4]] ], [ 12, %[[ENTRY]] ]
+; TTINOLUT-NEXT:    ret i32 [[IDX]]
+;
+entry:
+  switch i32 %x, label %end [
+  i32 0, label %case0
+  i32 1, label %case1
+  i32 2, label %case2
+  i32 3, label %case3
+  i32 4, label %case4
+  ]
+
+case0:
+  br label %end
+case1:
+  br label %end
+case2:
+  br label %end
+case3:
+  br label %end
+case4:
+  br label %end
+
+end:
+  %idx = phi i32 [ 0, %case0 ], [ 3, %case1 ], [ 6, %case2 ], [ 9, %case3 ], [ 13, %case4 ], [ 12, %entry ]
+  ret i32 %idx
+}
+
+define i32 @linear_transform_no_default(i32 %x) {
+; OPTNOLUT-LABEL: define i32 @linear_transform_no_default(
+; OPTNOLUT-SAME: i32 [[X:%.*]]) {
+; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
+; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
+; OPTNOLUT-NEXT:      i32 0, label %[[END:.*]]
+; OPTNOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; OPTNOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; OPTNOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; OPTNOLUT-NEXT:      i32 4, label %[[CASE4:.*]]
+; OPTNOLUT-NEXT:    ]
+; OPTNOLUT:       [[CASE1]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE2]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE3]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE4]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[DEFAULT]]:
+; OPTNOLUT-NEXT:    unreachable
+; OPTNOLUT:       [[END]]:
+; OPTNOLUT-NEXT:    [[SWITCH_IDX_MULT:%.*]] = phi i32 [ 3, %[[CASE1]] ], [ 6, %[[CASE2]] ], [ 9, %[[CASE3]] ], [ 12, %[[CASE4]] ], [ 0, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:    ret i32 [[SWITCH_IDX_MULT]]
+;
+; TTINOLUT-LABEL: define i32 @linear_transform_no_default(
+; TTINOLUT-SAME: i32 [[X:%.*]]) {
+; TTINOLUT-NEXT:  [[ENTRY:.*]]:
+; TTINOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
+; TTINOLUT-NEXT:      i32 0, label %[[END:.*]]
+; TTINOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; TTINOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; TTINOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; TTINOLUT-NEXT:      i32 4, label %[[CASE4:.*]]
+; TTINOLUT-NEXT:    ]
+; TTINOLUT:       [[CASE1]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE2]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE3]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE4]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[DEFAULT]]:
+; TTINOLUT-NEXT:    unreachable
+; TTINOLUT:       [[END]]:
+; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 3, %[[CASE1]] ], [ 6, %[[CASE2]] ], [ 9, %[[CASE3]] ], [ 12, %[[CASE4]] ], [ 0, %[[ENTRY]] ]
+; TTINOLUT-NEXT:    ret i32 [[IDX]]
+;
+entry:
+  switch i32 %x, label %default [
+  i32 0, label %case0
+  i32 1, label %case1
+  i32 2, label %case2
+  i32 3, label %case3
+  i32 4, label %case4
+  ]
+
+case0:
+  br label %end
+case1:
+  br label %end
+case2:
+  br label %end
+case3:
+  br label %end
+case4:
+  br label %end
+default:
+  unreachable
+
+end:
+  %idx = phi i32 [ 0, %case0 ], [ 3, %case1 ], [ 6, %case2 ], [ 9, %case3 ], [ 12, %case4 ]
+  ret i32 %idx
+}
+
+define i4 @bitmap_no_default(i32 %x) {
+; OPTNOLUT-LABEL: define i4 @bitmap_no_default(
+; OPTNOLUT-SAME: i32 [[X:%.*]]) {
+; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
+; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
+; OPTNOLUT-NEXT:      i32 0, label %[[END:.*]]
+; OPTNOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; OPTNOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; OPTNOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; OPTNOLUT-NEXT:    ]
+; OPTNOLUT:       [[CASE1]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE2]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE3]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[DEFAULT]]:
+; OPTNOLUT-NEXT:    unreachable
+; OPTNOLUT:       [[END]]:
+; OPTNOLUT-NEXT:    [[SWITCH_MASKED:%.*]] = phi i4 [ 2, %[[CASE1]] ], [ 4, %[[CASE2]] ], [ -8, %[[CASE3]] ], [ 0, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:    ret i4 [[SWITCH_MASKED]]
+;
+; TTINOLUT-LABEL: define i4 @bitmap_no_default(
+; TTINOLUT-SAME: i32 [[X:%.*]]) {
+; TTINOLUT-NEXT:  [[ENTRY:.*]]:
+; TTINOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
+; TTINOLUT-NEXT:      i32 0, label %[[END:.*]]
+; TTINOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; TTINOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; TTINOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; TTINOLUT-NEXT:    ]
+; TTINOLUT:       [[CASE1]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE2]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE3]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[DEFAULT]]:
+; TTINOLUT-NEXT:    unreachable
+; TTINOLUT:       [[END]]:
+; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i4 [ 2, %[[CASE1]] ], [ 4, %[[CASE2]] ], [ -8, %[[CASE3]] ], [ 0, %[[ENTRY]] ]
+; TTINOLUT-NEXT:    ret i4 [[IDX]]
+;
+entry:
+  switch i32 %x, label %default [
+  i32 0, label %case0
+  i32 1, label %case1
+  i32 2, label %case2
+  i32 3, label %case3
+  ]
+
+case0:
+  br label %end
+case1:
+  br label %end
+case2:
+  br label %end
+case3:
+  br label %end
+default:
+  unreachable
+
+end:
+  %idx = phi i4 [ 0, %case0 ], [ 2, %case1 ], [ 4, %case2 ], [ 8, %case3 ]
+  ret i4 %idx
+}
+
+define i4 @bitmap_with_default(i32 %x) {
+; OPTNOLUT-LABEL: define i4 @bitmap_with_default(
+; OPTNOLUT-SAME: i32 [[X:%.*]]) {
+; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
+; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
+; OPTNOLUT-NEXT:      i32 0, label %[[END:.*]]
+; OPTNOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; OPTNOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; OPTNOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; OPTNOLUT-NEXT:    ]
+; OPTNOLUT:       [[CASE1]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE2]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE3]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[DEFAULT]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[END]]:
+; OPTNOLUT-NEXT:    [[IDX:%.*]] = phi i4 [ 2, %[[CASE1]] ], [ 4, %[[CASE2]] ], [ -8, %[[CASE3]] ], [ -1, %[[DEFAULT]] ], [ 0, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:    ret i4 [[IDX]]
+;
+; TTINOLUT-LABEL: define i4 @bitmap_with_default(
+; TTINOLUT-SAME: i32 [[X:%.*]]) {
+; TTINOLUT-NEXT:  [[ENTRY:.*]]:
+; TTINOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
+; TTINOLUT-NEXT:      i32 0, label %[[END:.*]]
+; TTINOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; TTINOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; TTINOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; TTINOLUT-NEXT:    ]
+; TTINOLUT:       [[CASE1]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE2]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE3]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[DEFAULT]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[END]]:
+; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i4 [ 2, %[[CASE1]] ], [ 4, %[[CASE2]] ], [ -8, %[[CASE3]] ], [ -1, %[[DEFAULT]] ], [ 0, %[[ENTRY]] ]
+; TTINOLUT-NEXT:    ret i4 [[IDX]]
+;
+entry:
+  switch i32 %x, label %default [
+  i32 0, label %case0
+  i32 1, label %case1
+  i32 2, label %case2
+  i32 3, label %case3
+  ]
+
+case0:
+  br label %end
+case1:
+  br label %end
+case2:
+  br label %end
+case3:
+  br label %end
+default:
+  br label %end
+
+end:
+  %idx = phi i4 [ 0, %case0 ], [ 2, %case1 ], [ 4, %case2 ], [ 8, %case3 ], [15, %default]
+  ret i4 %idx
+}
+
+define i32 @single_value_no_default(i32 %x) {
+; OPTNOLUT-LABEL: define i32 @single_value_no_default(
+; OPTNOLUT-SAME: i32 [[X:%.*]]) {
+; OPTNOLUT-NEXT:  [[ENTRY:.*:]]
+; OPTNOLUT-NEXT:    ret i32 2
+;
+; TTINOLUT-LABEL: define i32 @single_value_no_default(
+; TTINOLUT-SAME: i32 [[X:%.*]]) {
+; TTINOLUT-NEXT:  [[ENTRY:.*:]]
+; TTINOLUT-NEXT:    ret i32 2
+;
+entry:
+  switch i32 %x, label %default [
+  i32 0, label %case0
+  i32 1, label %case1
+  i32 2, label %case2
+  i32 3, label %case3
+  i32 4, label %case4
+  ]
+
+case0:
+  br label %end
+case1:
+  br label %end
+case2:
+  br label %end
+case3:
+  br label %end
+case4:
+  br label %end
+default:
+  unreachable
+
+end:
+  %idx = phi i32 [ 2, %case0 ], [ 2, %case1 ], [ 2, %case2 ], [ 2, %case3 ], [ 2, %case4 ]
+  ret i32 %idx
+}
+
+define i32 @single_value_withdefault(i32 %x) {
+; OPTNOLUT-LABEL: define i32 @single_value_withdefault(
+; OPTNOLUT-SAME: i32 [[X:%.*]]) {
+; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
+; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
+; OPTNOLUT-NEXT:      i32 0, label %[[END:.*]]
+; OPTNOLUT-NEXT:      i32 1, label %[[END]]
+; OPTNOLUT-NEXT:      i32 2, label %[[END]]
+; OPTNOLUT-NEXT:      i32 3, label %[[END]]
+; OPTNOLUT-NEXT:      i32 4, label %[[END]]
+; OPTNOLUT-NEXT:    ]
+; OPTNOLUT:       [[DEFAULT]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[END]]:
+; OPTNOLUT-NEXT:    [[DOT:%.*]] = phi i32 [ 3, %[[DEFAULT]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:    ret i32 [[DOT]]
+;
+; TTINOLUT-LABEL: define i32 @single_value_withdefault(
+; TTINOLUT-SAME: i32 [[X:%.*]]) {
+; TTINOLUT-NEXT:  [[ENTRY:.*]]:
+; TTINOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
+; TTINOLUT-NEXT:      i32 0, label %[[END:.*]]
+; TTINOLUT-NEXT:      i32 1, label %[[END]]
+; TTINOLUT-NEXT:      i32 2, label %[[END]]
+; TTINOLUT-NEXT:      i32 3, label %[[END]]
+; TTINOLUT-NEXT:      i32 4, label %[[END]]
+; TTINOLUT-NEXT:    ]
+; TTINOLUT:       [[DEFAULT]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[END]]:
+; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 3, %[[DEFAULT]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ]
+; TTINOLUT-NEXT:    ret i32 [[IDX]]
+;
+entry:
+  switch i32 %x, label %default [
+  i32 0, label %case0
+  i32 1, label %case1
+  i32 2, label %case2
+  i32 3, label %case3
+  i32 4, label %case4
+  ]
+
+case0:
+  br label %end
+case1:
+  br label %end
+case2:
+  br label %end
+case3:
+  br label %end
+case4:
+  br label %end
+default:
+  br label %end
+
+end:
+  %idx = phi i32 [ 2, %case0 ], [ 2, %case1 ], [ 2, %case2 ], [ 2, %case3 ], [ 2, %case4 ], [ 3, %default ]
+  ret i32 %idx
+}
+
+define i32 @single_value_no_jump_tables(i32 %x) "no-jump-tables"="true" {
+; OPTNOLUT-LABEL: define i32 @single_value_no_jump_tables(
+; OPTNOLUT-SAME: i32 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
+; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
+; OPTNOLUT-NEXT:      i32 0, label %[[END:.*]]
+; OPTNOLUT-NEXT:      i32 1, label %[[END]]
+; OPTNOLUT-NEXT:      i32 2, label %[[END]]
+; OPTNOLUT-NEXT:      i32 3, label %[[END]]
+; OPTNOLUT-NEXT:      i32 4, label %[[END]]
+; OPTNOLUT-NEXT:    ]
+; OPTNOLUT:       [[DEFAULT]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[END]]:
+; OPTNOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 3, %[[DEFAULT]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:    ret i32 [[IDX]]
+;
+; TTINOLUT-LABEL: define i32 @single_value_no_jump_tables(
+; TTINOLUT-SAME: i32 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; TTINOLUT-NEXT:  [[ENTRY:.*]]:
+; TTINOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
+; TTINOLUT-NEXT:      i32 0, label %[[END:.*]]
+; TTINOLUT-NEXT:      i32 1, label %[[END]]
+; TTINOLUT-NEXT:      i32 2, label %[[END]]
+; TTINOLUT-NEXT:      i32 3, label %[[END]]
+; TTINOLUT-NEXT:      i32 4, label %[[END]]
+; TTINOLUT-NEXT:    ]
+; TTINOLUT:       [[DEFAULT]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[END]]:
+; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 3, %[[DEFAULT]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ]
+; TTINOLUT-NEXT:    ret i32 [[IDX]]
+;
+entry:
+  switch i32 %x, label %default [
+  i32 0, label %case0
+  i32 1, label %case1
+  i32 2, label %case2
+  i32 3, label %case3
+  i32 4, label %case4
+  ]
+
+case0:
+  br label %end
+case1:
+  br label %end
+case2:
+  br label %end
+case3:
+  br label %end
+case4:
+  br label %end
+default:
+  br label %end
+
+end:
+  %idx = phi i32 [ 2, %case0 ], [ 2, %case1 ], [ 2, %case2 ], [ 2, %case3 ], [ 2, %case4 ], [ 3, %default ]
+  ret i32 %idx
+}
+
+define i32 @lookup_table(i32 %x) {
+; OPTNOLUT-LABEL: define i32 @lookup_table(
+; OPTNOLUT-SAME: i32 [[X:%.*]]) {
+; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
+; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[END:.*]] [
+; OPTNOLUT-NEXT:      i32 0, label %[[CASE0:.*]]
+; OPTNOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; OPTNOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; OPTNOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; OPTNOLUT-NEXT:    ]
+; OPTNOLUT:       [[CASE0]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE1]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE2]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE3]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[END]]:
+; OPTNOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 13, %[[CASE0]] ], [ 3, %[[CASE1]] ], [ 11, %[[CASE2]] ], [ 8, %[[CASE3]] ], [ 24, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:    ret i32 [[IDX]]
+;
+; TTINOLUT-LABEL: define i32 @lookup_table(
+; TTINOLUT-SAME: i32 [[X:%.*]]) {
+; TTINOLUT-NEXT:  [[ENTRY:.*]]:
+; TTINOLUT-NEXT:    switch i32 [[X]], label %[[END:.*]] [
+; TTINOLUT-NEXT:      i32 0, label %[[CASE0:.*]]
+; TTINOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; TTINOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; TTINOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; TTINOLUT-NEXT:    ]
+; TTINOLUT:       [[CASE0]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE1]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE2]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[CASE3]]:
+; TTINOLUT-NEXT:    br label %[[END]]
+; TTINOLUT:       [[END]]:
+; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 13, %[[CASE0]] ], [ 3, %[[CASE1]] ], [ 11, %[[CASE2]] ], [ 8, %[[CASE3]] ], [ 24, %[[ENTRY]] ]
+; TTINOLUT-NEXT:    ret i32 [[IDX]]
+;
+entry:
+  switch i32 %x, label %end [
+  i32 0, label %case0
+  i32 1, label %case1
+  i32 2, label %case2
+  i32 3, label %case3
+  ]
+
+case0:
+  br label %end
+case1:
+  br label %end
+case2:
+  br label %end
+case3:
+  br label %end
+
+end:
+  %idx = phi i32 [ 13, %case0 ], [ 3, %case1 ], [ 11, %case2 ], [ 8, %case3 ], [ 24, %entry ]
+  ret i32 %idx
+}

>From 50031260feb5bcdf1eca9c8f7822276e5140c793 Mon Sep 17 00:00:00 2001
From: Jessica Del <Jessica.Del at amd.com>
Date: Tue, 2 Sep 2025 14:08:35 +0200
Subject: [PATCH 2/8] SimplifyCFG: Add `isLookupTable` method

Check if the SwitchReplacement is a lookup table or not.
---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 02d6393dd5815..34e4725a6a177 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -6466,6 +6466,9 @@ class SwitchReplacement {
   /// Return the default value of the switch.
   Constant *getDefaultValue();
 
+  /// Return true if the replacement is a lookup table.
+  bool isLookupTable();
+
 private:
   // Depending on the switch, there are different alternatives.
   enum {
@@ -6753,6 +6756,8 @@ static bool isTypeLegalForLookupTable(Type *Ty, const TargetTransformInfo &TTI,
 
 Constant *SwitchReplacement::getDefaultValue() { return DefaultValue; }
 
+bool SwitchReplacement::isLookupTable() { return Kind == LookupTableKind; }
+
 static bool isSwitchDense(uint64_t NumCases, uint64_t CaseRange) {
   // 40% is the default density for building a jump table in optsize/minsize
   // mode. See also TargetLoweringBase::isSuitableForJumpTable(), which this

>From 081d8503d0430f6a3a150b1c0a8d7de27f2b4b63 Mon Sep 17 00:00:00 2001
From: Jessica Del <Jessica.Del at amd.com>
Date: Tue, 2 Sep 2025 14:10:53 +0200
Subject: [PATCH 3/8] SimplifyCFG: Delay check for switch-to-lookup option

The option prevents any switch replacements from happening earlier in
the compilation pipeline, even though it should only apply to lookup
tables.

Delay the check until we are sure that we would create a lookup table.
If not, continue with the replacement.
---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     |  24 ++--
 .../CodeGen/Thumb2/bti-indirect-branches.ll   |   5 +-
 .../PhaseOrdering/X86/merge-functions.ll      |   2 +-
 .../switch-transformations-no-lut.ll          | 105 ++++--------------
 4 files changed, 42 insertions(+), 94 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 34e4725a6a177..fb04ccba55f34 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -6923,7 +6923,8 @@ static void reuseTableCompare(
 /// lookup tables.
 static bool simplifySwitchLookup(SwitchInst *SI, IRBuilder<> &Builder,
                                  DomTreeUpdater *DTU, const DataLayout &DL,
-                                 const TargetTransformInfo &TTI) {
+                                 const TargetTransformInfo &TTI,
+                                 bool ConvertSwitchToLookupTable) {
   assert(SI->getNumCases() > 1 && "Degenerate switch?");
 
   BasicBlock *BB = SI->getParent();
@@ -7091,6 +7092,18 @@ static bool simplifySwitchLookup(SwitchInst *SI, IRBuilder<> &Builder,
     PhiToReplacementMap.insert({PHI, Replacement});
   }
 
+  bool AnyLookupTables = any_of(
+      PhiToReplacementMap, [](auto &KV) { return KV.second.isLookupTable(); });
+
+  // The conversion from switch to lookup tables results in difficult-to-analyze
+  // code and makes pruning branches much harder. This is a problem if the
+  // switch expression itself can still be restricted as a result of inlining or
+  // CVP. Therefore, only apply this transformation during late stages of the
+  // optimisation pipeline.
+  // However, other switch replacements can be applied much earlier.
+  if (AnyLookupTables && !ConvertSwitchToLookupTable)
+    return false;
+
   Builder.SetInsertPoint(SI);
   // TableIndex is the switch condition - TableIndexOffset if we don't
   // use the condition directly
@@ -7732,13 +7745,8 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
   if (Options.ForwardSwitchCondToPhi && forwardSwitchConditionToPHI(SI))
     return requestResimplify();
 
-  // The conversion from switch to lookup tables results in difficult-to-analyze
-  // code and makes pruning branches much harder. This is a problem if the
-  // switch expression itself can still be restricted as a result of inlining or
-  // CVP. Therefore, only apply this transformation during late stages of the
-  // optimisation pipeline.
-  if (Options.ConvertSwitchToLookupTable &&
-      simplifySwitchLookup(SI, Builder, DTU, DL, TTI))
+  if (simplifySwitchLookup(SI, Builder, DTU, DL, TTI,
+                           Options.ConvertSwitchToLookupTable))
     return requestResimplify();
 
   if (simplifySwitchOfPowersOfTwo(SI, Builder, DL, TTI))
diff --git a/llvm/test/CodeGen/Thumb2/bti-indirect-branches.ll b/llvm/test/CodeGen/Thumb2/bti-indirect-branches.ll
index c6ffb92d60d8d..bb1545deebe16 100644
--- a/llvm/test/CodeGen/Thumb2/bti-indirect-branches.ll
+++ b/llvm/test/CodeGen/Thumb2/bti-indirect-branches.ll
@@ -9,6 +9,7 @@ define internal i32 @table_switch(i32 %x) "branch-target-enforcement" {
 ; CHECK-NEXT:    cmp r1, #3
 ; CHECK-NEXT:    bhi .LBB0_6
 ; CHECK-NEXT:  @ %bb.1: @ %entry
+; CHECK-NEXT:    movs r0, #3
 ; CHECK-NEXT:  .LCPI0_0:
 ; CHECK-NEXT:    tbb [pc, r1]
 ; CHECK-NEXT:  @ %bb.2:
@@ -22,7 +23,7 @@ define internal i32 @table_switch(i32 %x) "branch-target-enforcement" {
 ; CHECK-NEXT:    movs r0, #2
 ; CHECK-NEXT:    bx lr
 ; CHECK-NEXT:  .LBB0_4: @ %bb3
-; CHECK-NEXT:    movs r0, #3
+; CHECK-NEXT:    movs r0, #1
 ; CHECK-NEXT:    bx lr
 ; CHECK-NEXT:  .LBB0_5: @ %bb4
 ; CHECK-NEXT:    movs r0, #4
@@ -51,7 +52,7 @@ sw.epilog:
   br label %return
 
 return:
-  %ret = phi i32 [ 0, %sw.epilog ], [ 1, %bb1 ], [ 2, %bb2 ], [ 3, %bb3 ], [ 4, %bb4 ]
+  %ret = phi i32 [ 0, %sw.epilog ], [ 3, %bb1 ], [ 2, %bb2 ], [ 1, %bb3 ], [ 4, %bb4 ]
   ret i32 %ret
 }
 
diff --git a/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll b/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll
index 708cdc9ca45ec..8cdc593bbfe0b 100644
--- a/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll
+++ b/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll
@@ -89,7 +89,7 @@ bb3:                                              ; preds = %bb1, %bb2
 
 define i1 @test2(i32 %c) {
 ; CHECK-LABEL: @test2(
-; CHECK-NEXT:    [[TMP2:%.*]] = tail call noundef i1 @test1(i32 [[TMP0:%.*]]) #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    [[TMP2:%.*]] = tail call i1 @test1(i32 [[TMP0:%.*]]) #[[ATTR0:[0-9]+]]
 ; CHECK-NEXT:    ret i1 [[TMP2]]
 ;
 entry:
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
index ecca479ad0e4b..dd72de9511586 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
@@ -7,23 +7,11 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 define i32 @linear_transform_with_default(i32 %x) {
 ; OPTNOLUT-LABEL: define i32 @linear_transform_with_default(
 ; OPTNOLUT-SAME: i32 [[X:%.*]]) {
-; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
-; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[END:.*]] [
-; OPTNOLUT-NEXT:      i32 0, label %[[CASE0:.*]]
-; OPTNOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
-; OPTNOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
-; OPTNOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
-; OPTNOLUT-NEXT:    ]
-; OPTNOLUT:       [[CASE0]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[CASE1]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[CASE2]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[CASE3]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[END]]:
-; OPTNOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 1, %[[CASE0]] ], [ 4, %[[CASE1]] ], [ 7, %[[CASE2]] ], [ 10, %[[CASE3]] ], [ 13, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:  [[ENTRY:.*:]]
+; OPTNOLUT-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[X]], 4
+; OPTNOLUT-NEXT:    [[SWITCH_IDX_MULT:%.*]] = mul nsw i32 [[X]], 3
+; OPTNOLUT-NEXT:    [[SWITCH_OFFSET:%.*]] = add nsw i32 [[SWITCH_IDX_MULT]], 1
+; OPTNOLUT-NEXT:    [[IDX:%.*]] = select i1 [[TMP0]], i32 [[SWITCH_OFFSET]], i32 13
 ; OPTNOLUT-NEXT:    ret i32 [[IDX]]
 ;
 ; TTINOLUT-LABEL: define i32 @linear_transform_with_default(
@@ -146,26 +134,8 @@ end:
 define i32 @linear_transform_no_default(i32 %x) {
 ; OPTNOLUT-LABEL: define i32 @linear_transform_no_default(
 ; OPTNOLUT-SAME: i32 [[X:%.*]]) {
-; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
-; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
-; OPTNOLUT-NEXT:      i32 0, label %[[END:.*]]
-; OPTNOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
-; OPTNOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
-; OPTNOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
-; OPTNOLUT-NEXT:      i32 4, label %[[CASE4:.*]]
-; OPTNOLUT-NEXT:    ]
-; OPTNOLUT:       [[CASE1]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[CASE2]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[CASE3]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[CASE4]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[DEFAULT]]:
-; OPTNOLUT-NEXT:    unreachable
-; OPTNOLUT:       [[END]]:
-; OPTNOLUT-NEXT:    [[SWITCH_IDX_MULT:%.*]] = phi i32 [ 3, %[[CASE1]] ], [ 6, %[[CASE2]] ], [ 9, %[[CASE3]] ], [ 12, %[[CASE4]] ], [ 0, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:  [[ENTRY:.*:]]
+; OPTNOLUT-NEXT:    [[SWITCH_IDX_MULT:%.*]] = mul nsw i32 [[X]], 3
 ; OPTNOLUT-NEXT:    ret i32 [[SWITCH_IDX_MULT]]
 ;
 ; TTINOLUT-LABEL: define i32 @linear_transform_no_default(
@@ -222,23 +192,11 @@ end:
 define i4 @bitmap_no_default(i32 %x) {
 ; OPTNOLUT-LABEL: define i4 @bitmap_no_default(
 ; OPTNOLUT-SAME: i32 [[X:%.*]]) {
-; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
-; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
-; OPTNOLUT-NEXT:      i32 0, label %[[END:.*]]
-; OPTNOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
-; OPTNOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
-; OPTNOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
-; OPTNOLUT-NEXT:    ]
-; OPTNOLUT:       [[CASE1]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[CASE2]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[CASE3]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[DEFAULT]]:
-; OPTNOLUT-NEXT:    unreachable
-; OPTNOLUT:       [[END]]:
-; OPTNOLUT-NEXT:    [[SWITCH_MASKED:%.*]] = phi i4 [ 2, %[[CASE1]] ], [ 4, %[[CASE2]] ], [ -8, %[[CASE3]] ], [ 0, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:  [[ENTRY:.*:]]
+; OPTNOLUT-NEXT:    [[SWITCH_CAST:%.*]] = trunc i32 [[X]] to i16
+; OPTNOLUT-NEXT:    [[SWITCH_SHIFTAMT:%.*]] = mul nuw nsw i16 [[SWITCH_CAST]], 4
+; OPTNOLUT-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i16 -31712, [[SWITCH_SHIFTAMT]]
+; OPTNOLUT-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i16 [[SWITCH_DOWNSHIFT]] to i4
 ; OPTNOLUT-NEXT:    ret i4 [[SWITCH_MASKED]]
 ;
 ; TTINOLUT-LABEL: define i4 @bitmap_no_default(
@@ -289,23 +247,13 @@ end:
 define i4 @bitmap_with_default(i32 %x) {
 ; OPTNOLUT-LABEL: define i4 @bitmap_with_default(
 ; OPTNOLUT-SAME: i32 [[X:%.*]]) {
-; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
-; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
-; OPTNOLUT-NEXT:      i32 0, label %[[END:.*]]
-; OPTNOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
-; OPTNOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
-; OPTNOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
-; OPTNOLUT-NEXT:    ]
-; OPTNOLUT:       [[CASE1]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[CASE2]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[CASE3]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[DEFAULT]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[END]]:
-; OPTNOLUT-NEXT:    [[IDX:%.*]] = phi i4 [ 2, %[[CASE1]] ], [ 4, %[[CASE2]] ], [ -8, %[[CASE3]] ], [ -1, %[[DEFAULT]] ], [ 0, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:  [[ENTRY:.*:]]
+; OPTNOLUT-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[X]], 4
+; OPTNOLUT-NEXT:    [[SWITCH_CAST:%.*]] = trunc i32 [[X]] to i16
+; OPTNOLUT-NEXT:    [[SWITCH_SHIFTAMT:%.*]] = mul nuw nsw i16 [[SWITCH_CAST]], 4
+; OPTNOLUT-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i16 -31712, [[SWITCH_SHIFTAMT]]
+; OPTNOLUT-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i16 [[SWITCH_DOWNSHIFT]] to i4
+; OPTNOLUT-NEXT:    [[IDX:%.*]] = select i1 [[TMP0]], i4 [[SWITCH_MASKED]], i4 -1
 ; OPTNOLUT-NEXT:    ret i4 [[IDX]]
 ;
 ; TTINOLUT-LABEL: define i4 @bitmap_with_default(
@@ -394,18 +342,9 @@ end:
 define i32 @single_value_withdefault(i32 %x) {
 ; OPTNOLUT-LABEL: define i32 @single_value_withdefault(
 ; OPTNOLUT-SAME: i32 [[X:%.*]]) {
-; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
-; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
-; OPTNOLUT-NEXT:      i32 0, label %[[END:.*]]
-; OPTNOLUT-NEXT:      i32 1, label %[[END]]
-; OPTNOLUT-NEXT:      i32 2, label %[[END]]
-; OPTNOLUT-NEXT:      i32 3, label %[[END]]
-; OPTNOLUT-NEXT:      i32 4, label %[[END]]
-; OPTNOLUT-NEXT:    ]
-; OPTNOLUT:       [[DEFAULT]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[END]]:
-; OPTNOLUT-NEXT:    [[DOT:%.*]] = phi i32 [ 3, %[[DEFAULT]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:  [[ENTRY:.*:]]
+; OPTNOLUT-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[X]], 5
+; OPTNOLUT-NEXT:    [[DOT:%.*]] = select i1 [[TMP0]], i32 2, i32 3
 ; OPTNOLUT-NEXT:    ret i32 [[DOT]]
 ;
 ; TTINOLUT-LABEL: define i32 @single_value_withdefault(

>From 81a8530f332cd139c2c316081b7d19f772862615 Mon Sep 17 00:00:00 2001
From: Jessica Del <Jessica.Del at amd.com>
Date: Tue, 2 Sep 2025 15:20:12 +0200
Subject: [PATCH 4/8] SimplifyCFG: Delay check for LUT support in target

Only check the target support for LUTs if we would create one.
Otherwise, proceed with the optimization.
---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     |  26 +++--
 .../switch-transformations-no-lut.ll          | 101 +++++-------------
 2 files changed, 39 insertions(+), 88 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index fb04ccba55f34..a51b965ffb3f0 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -6929,10 +6929,8 @@ static bool simplifySwitchLookup(SwitchInst *SI, IRBuilder<> &Builder,
 
   BasicBlock *BB = SI->getParent();
   Function *Fn = BB->getParent();
-  // Only build lookup table when we have a target that supports it or the
-  // attribute is not set.
-  if (!TTI.shouldBuildLookupTables() ||
-      (Fn->getFnAttribute("no-jump-tables").getValueAsBool()))
+  // Only build lookup table when the attribute is not set.
+  if (Fn->getFnAttribute("no-jump-tables").getValueAsBool())
     return false;
 
   // FIXME: If the switch is too sparse for a lookup table, perhaps we could
@@ -7095,13 +7093,19 @@ static bool simplifySwitchLookup(SwitchInst *SI, IRBuilder<> &Builder,
   bool AnyLookupTables = any_of(
       PhiToReplacementMap, [](auto &KV) { return KV.second.isLookupTable(); });
 
-  // The conversion from switch to lookup tables results in difficult-to-analyze
-  // code and makes pruning branches much harder. This is a problem if the
-  // switch expression itself can still be restricted as a result of inlining or
-  // CVP. Therefore, only apply this transformation during late stages of the
-  // optimisation pipeline.
-  // However, other switch replacements can be applied much earlier.
-  if (AnyLookupTables && !ConvertSwitchToLookupTable)
+  // A few conditions prevent the generation of lookup tables:
+  //     1. Not setting the ConvertSwitchToLookupTable option
+  //        This option prevents the LUT creation until a later stage in the
+  //        pipeline, because it would otherwise result in some
+  //        difficult-to-analyze code and make pruning branches much harder.
+  //        This is a problem if the switch expression itself can be restricted
+  //        by inlining or CVP.
+  //     2. The target does not support lookup tables.
+  // However, these objections do not apply to other switch replacements, like
+  // the bitmap, so we only stop here if any of these conditions are met and we
+  // want to create a LUT. Otherwise, continue with the switch replacement.
+  if (AnyLookupTables &&
+      (!ConvertSwitchToLookupTable || !TTI.shouldBuildLookupTables()))
     return false;
 
   Builder.SetInsertPoint(SI);
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
index dd72de9511586..2bc068445cc80 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
@@ -17,22 +17,14 @@ define i32 @linear_transform_with_default(i32 %x) {
 ; TTINOLUT-LABEL: define i32 @linear_transform_with_default(
 ; TTINOLUT-SAME: i32 [[X:%.*]]) {
 ; TTINOLUT-NEXT:  [[ENTRY:.*]]:
-; TTINOLUT-NEXT:    switch i32 [[X]], label %[[END:.*]] [
-; TTINOLUT-NEXT:      i32 0, label %[[CASE0:.*]]
-; TTINOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
-; TTINOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
-; TTINOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
-; TTINOLUT-NEXT:    ]
-; TTINOLUT:       [[CASE0]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[CASE1]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[CASE2]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[CASE3]]:
+; TTINOLUT-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[X]], 4
+; TTINOLUT-NEXT:    br i1 [[TMP0]], label %[[SWITCH_LOOKUP:.*]], label %[[END:.*]]
+; TTINOLUT:       [[SWITCH_LOOKUP]]:
+; TTINOLUT-NEXT:    [[SWITCH_IDX_MULT:%.*]] = mul nsw i32 [[X]], 3
+; TTINOLUT-NEXT:    [[SWITCH_OFFSET:%.*]] = add nsw i32 [[SWITCH_IDX_MULT]], 1
 ; TTINOLUT-NEXT:    br label %[[END]]
 ; TTINOLUT:       [[END]]:
-; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 1, %[[CASE0]] ], [ 4, %[[CASE1]] ], [ 7, %[[CASE2]] ], [ 10, %[[CASE3]] ], [ 13, %[[ENTRY]] ]
+; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 13, %[[ENTRY]] ], [ [[SWITCH_OFFSET]], %[[SWITCH_LOOKUP]] ]
 ; TTINOLUT-NEXT:    ret i32 [[IDX]]
 ;
 entry:
@@ -140,26 +132,8 @@ define i32 @linear_transform_no_default(i32 %x) {
 ;
 ; TTINOLUT-LABEL: define i32 @linear_transform_no_default(
 ; TTINOLUT-SAME: i32 [[X:%.*]]) {
-; TTINOLUT-NEXT:  [[ENTRY:.*]]:
-; TTINOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
-; TTINOLUT-NEXT:      i32 0, label %[[END:.*]]
-; TTINOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
-; TTINOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
-; TTINOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
-; TTINOLUT-NEXT:      i32 4, label %[[CASE4:.*]]
-; TTINOLUT-NEXT:    ]
-; TTINOLUT:       [[CASE1]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[CASE2]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[CASE3]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[CASE4]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[DEFAULT]]:
-; TTINOLUT-NEXT:    unreachable
-; TTINOLUT:       [[END]]:
-; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 3, %[[CASE1]] ], [ 6, %[[CASE2]] ], [ 9, %[[CASE3]] ], [ 12, %[[CASE4]] ], [ 0, %[[ENTRY]] ]
+; TTINOLUT-NEXT:  [[ENTRY:.*:]]
+; TTINOLUT-NEXT:    [[IDX:%.*]] = mul nsw i32 [[X]], 3
 ; TTINOLUT-NEXT:    ret i32 [[IDX]]
 ;
 entry:
@@ -201,23 +175,11 @@ define i4 @bitmap_no_default(i32 %x) {
 ;
 ; TTINOLUT-LABEL: define i4 @bitmap_no_default(
 ; TTINOLUT-SAME: i32 [[X:%.*]]) {
-; TTINOLUT-NEXT:  [[ENTRY:.*]]:
-; TTINOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
-; TTINOLUT-NEXT:      i32 0, label %[[END:.*]]
-; TTINOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
-; TTINOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
-; TTINOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
-; TTINOLUT-NEXT:    ]
-; TTINOLUT:       [[CASE1]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[CASE2]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[CASE3]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[DEFAULT]]:
-; TTINOLUT-NEXT:    unreachable
-; TTINOLUT:       [[END]]:
-; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i4 [ 2, %[[CASE1]] ], [ 4, %[[CASE2]] ], [ -8, %[[CASE3]] ], [ 0, %[[ENTRY]] ]
+; TTINOLUT-NEXT:  [[ENTRY:.*:]]
+; TTINOLUT-NEXT:    [[SWITCH_CAST:%.*]] = trunc i32 [[X]] to i16
+; TTINOLUT-NEXT:    [[SWITCH_SHIFTAMT:%.*]] = mul nuw nsw i16 [[SWITCH_CAST]], 4
+; TTINOLUT-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i16 -31712, [[SWITCH_SHIFTAMT]]
+; TTINOLUT-NEXT:    [[IDX:%.*]] = trunc i16 [[SWITCH_DOWNSHIFT]] to i4
 ; TTINOLUT-NEXT:    ret i4 [[IDX]]
 ;
 entry:
@@ -259,22 +221,16 @@ define i4 @bitmap_with_default(i32 %x) {
 ; TTINOLUT-LABEL: define i4 @bitmap_with_default(
 ; TTINOLUT-SAME: i32 [[X:%.*]]) {
 ; TTINOLUT-NEXT:  [[ENTRY:.*]]:
-; TTINOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
-; TTINOLUT-NEXT:      i32 0, label %[[END:.*]]
-; TTINOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
-; TTINOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
-; TTINOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
-; TTINOLUT-NEXT:    ]
-; TTINOLUT:       [[CASE1]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[CASE2]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[CASE3]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[DEFAULT]]:
+; TTINOLUT-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[X]], 4
+; TTINOLUT-NEXT:    br i1 [[TMP0]], label %[[SWITCH_LOOKUP:.*]], label %[[END:.*]]
+; TTINOLUT:       [[SWITCH_LOOKUP]]:
+; TTINOLUT-NEXT:    [[SWITCH_CAST:%.*]] = trunc i32 [[X]] to i16
+; TTINOLUT-NEXT:    [[SWITCH_SHIFTAMT:%.*]] = mul nuw nsw i16 [[SWITCH_CAST]], 4
+; TTINOLUT-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i16 -31712, [[SWITCH_SHIFTAMT]]
+; TTINOLUT-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i16 [[SWITCH_DOWNSHIFT]] to i4
 ; TTINOLUT-NEXT:    br label %[[END]]
 ; TTINOLUT:       [[END]]:
-; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i4 [ 2, %[[CASE1]] ], [ 4, %[[CASE2]] ], [ -8, %[[CASE3]] ], [ -1, %[[DEFAULT]] ], [ 0, %[[ENTRY]] ]
+; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i4 [ [[SWITCH_MASKED]], %[[SWITCH_LOOKUP]] ], [ -1, %[[ENTRY]] ]
 ; TTINOLUT-NEXT:    ret i4 [[IDX]]
 ;
 entry:
@@ -349,18 +305,9 @@ define i32 @single_value_withdefault(i32 %x) {
 ;
 ; TTINOLUT-LABEL: define i32 @single_value_withdefault(
 ; TTINOLUT-SAME: i32 [[X:%.*]]) {
-; TTINOLUT-NEXT:  [[ENTRY:.*]]:
-; TTINOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
-; TTINOLUT-NEXT:      i32 0, label %[[END:.*]]
-; TTINOLUT-NEXT:      i32 1, label %[[END]]
-; TTINOLUT-NEXT:      i32 2, label %[[END]]
-; TTINOLUT-NEXT:      i32 3, label %[[END]]
-; TTINOLUT-NEXT:      i32 4, label %[[END]]
-; TTINOLUT-NEXT:    ]
-; TTINOLUT:       [[DEFAULT]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[END]]:
-; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 3, %[[DEFAULT]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ]
+; TTINOLUT-NEXT:  [[ENTRY:.*:]]
+; TTINOLUT-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[X]], 5
+; TTINOLUT-NEXT:    [[IDX:%.*]] = select i1 [[TMP0]], i32 2, i32 3
 ; TTINOLUT-NEXT:    ret i32 [[IDX]]
 ;
 entry:

>From eb89aab6b8ce1d8763904385e7b9ba39a7a3f028 Mon Sep 17 00:00:00 2001
From: Jessica Del <Jessica.Del at amd.com>
Date: Tue, 2 Sep 2025 15:30:26 +0200
Subject: [PATCH 5/8] SimplifyCFG: Delay check for "no-jump-tables" attribute

Allow the replacement of switches with anything other than a lookup
table, even if the "no-jump-table" attribute is set.
---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     |  7 ++---
 .../switch-transformations-no-lut.ll          | 30 ++++---------------
 2 files changed, 9 insertions(+), 28 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index a51b965ffb3f0..40c5fd1f9367d 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -6929,9 +6929,6 @@ static bool simplifySwitchLookup(SwitchInst *SI, IRBuilder<> &Builder,
 
   BasicBlock *BB = SI->getParent();
   Function *Fn = BB->getParent();
-  // Only build lookup table when the attribute is not set.
-  if (Fn->getFnAttribute("no-jump-tables").getValueAsBool())
-    return false;
 
   // FIXME: If the switch is too sparse for a lookup table, perhaps we could
   // split off a dense part and build a lookup table for that.
@@ -7101,11 +7098,13 @@ static bool simplifySwitchLookup(SwitchInst *SI, IRBuilder<> &Builder,
   //        This is a problem if the switch expression itself can be restricted
   //        by inlining or CVP.
   //     2. The target does not support lookup tables.
+  //     3. The "no-jump-tables" function attribute is set.
   // However, these objections do not apply to other switch replacements, like
   // the bitmap, so we only stop here if any of these conditions are met and we
   // want to create a LUT. Otherwise, continue with the switch replacement.
   if (AnyLookupTables &&
-      (!ConvertSwitchToLookupTable || !TTI.shouldBuildLookupTables()))
+      (!ConvertSwitchToLookupTable || !TTI.shouldBuildLookupTables() ||
+       Fn->getFnAttribute("no-jump-tables").getValueAsBool()))
     return false;
 
   Builder.SetInsertPoint(SI);
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
index 2bc068445cc80..4e17c072776b9 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
@@ -340,34 +340,16 @@ end:
 define i32 @single_value_no_jump_tables(i32 %x) "no-jump-tables"="true" {
 ; OPTNOLUT-LABEL: define i32 @single_value_no_jump_tables(
 ; OPTNOLUT-SAME: i32 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
-; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
-; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
-; OPTNOLUT-NEXT:      i32 0, label %[[END:.*]]
-; OPTNOLUT-NEXT:      i32 1, label %[[END]]
-; OPTNOLUT-NEXT:      i32 2, label %[[END]]
-; OPTNOLUT-NEXT:      i32 3, label %[[END]]
-; OPTNOLUT-NEXT:      i32 4, label %[[END]]
-; OPTNOLUT-NEXT:    ]
-; OPTNOLUT:       [[DEFAULT]]:
-; OPTNOLUT-NEXT:    br label %[[END]]
-; OPTNOLUT:       [[END]]:
-; OPTNOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 3, %[[DEFAULT]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ]
+; OPTNOLUT-NEXT:  [[ENTRY:.*:]]
+; OPTNOLUT-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[X]], 5
+; OPTNOLUT-NEXT:    [[IDX:%.*]] = select i1 [[TMP0]], i32 2, i32 3
 ; OPTNOLUT-NEXT:    ret i32 [[IDX]]
 ;
 ; TTINOLUT-LABEL: define i32 @single_value_no_jump_tables(
 ; TTINOLUT-SAME: i32 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
-; TTINOLUT-NEXT:  [[ENTRY:.*]]:
-; TTINOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
-; TTINOLUT-NEXT:      i32 0, label %[[END:.*]]
-; TTINOLUT-NEXT:      i32 1, label %[[END]]
-; TTINOLUT-NEXT:      i32 2, label %[[END]]
-; TTINOLUT-NEXT:      i32 3, label %[[END]]
-; TTINOLUT-NEXT:      i32 4, label %[[END]]
-; TTINOLUT-NEXT:    ]
-; TTINOLUT:       [[DEFAULT]]:
-; TTINOLUT-NEXT:    br label %[[END]]
-; TTINOLUT:       [[END]]:
-; TTINOLUT-NEXT:    [[IDX:%.*]] = phi i32 [ 3, %[[DEFAULT]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ], [ 2, %[[ENTRY]] ]
+; TTINOLUT-NEXT:  [[ENTRY:.*:]]
+; TTINOLUT-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[X]], 5
+; TTINOLUT-NEXT:    [[IDX:%.*]] = select i1 [[TMP0]], i32 2, i32 3
 ; TTINOLUT-NEXT:    ret i32 [[IDX]]
 ;
 entry:

>From 6f5d99ecfcba0d80443b6bea7ad67cd4e061c2fa Mon Sep 17 00:00:00 2001
From: Jessica Del <Jessica.Del at amd.com>
Date: Tue, 2 Sep 2025 19:59:58 +0200
Subject: [PATCH 6/8] fixup! SimplifyCFG: Add tests for switch-replacements

---
 .../Transforms/SimplifyCFG/switch-transformations-no-lut.ll     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
index 4e17c072776b9..efe6b91c126af 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
@@ -1,6 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; RUN: opt -S -passes='simplifycfg' < %s | FileCheck %s --check-prefix=OPTNOLUT
-; RUN: opt -mtriple=amdgcn--amdpal -S -passes='simplifycfg<switch-to-lookup>' < %s | FileCheck %s --check-prefix=TTINOLUT
+; RUN: %if amdgpu-registered-target %{ opt -mtriple=amdgcn--amdpal -S -passes='simplifycfg<switch-to-lookup>' < %s | FileCheck %s --check-prefix=TTINOLUT %}
 ;
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 

>From 27179dc87b8b574f87697c7a4ac622e374559348 Mon Sep 17 00:00:00 2001
From: Jessica Del <Jessica.Del at amd.com>
Date: Fri, 5 Sep 2025 14:28:15 +0200
Subject: [PATCH 7/8] SimplifyCFG: Delay creation of bitmaps

Early bitmap creation can, similar to lookup tables,
cause missed optimizations because they are more difficult to analyze.

Delay the creation until later in the pipeline, when the option
`switch-to-lookup` is set in in SimplifyCFG.
---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     | 13 ++++++
 .../PhaseOrdering/X86/merge-functions.ll      |  2 +-
 .../switch-transformations-no-lut.ll          | 46 ++++++++++++++-----
 3 files changed, 48 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 40c5fd1f9367d..7485230127723 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -6469,6 +6469,9 @@ class SwitchReplacement {
   /// Return true if the replacement is a lookup table.
   bool isLookupTable();
 
+  /// Return true if the replacement is a bitmap.
+  bool isBitMap();
+
 private:
   // Depending on the switch, there are different alternatives.
   enum {
@@ -6758,6 +6761,8 @@ Constant *SwitchReplacement::getDefaultValue() { return DefaultValue; }
 
 bool SwitchReplacement::isLookupTable() { return Kind == LookupTableKind; }
 
+bool SwitchReplacement::isBitMap() { return Kind == BitMapKind; }
+
 static bool isSwitchDense(uint64_t NumCases, uint64_t CaseRange) {
   // 40% is the default density for building a jump table in optsize/minsize
   // mode. See also TargetLoweringBase::isSuitableForJumpTable(), which this
@@ -7107,6 +7112,14 @@ static bool simplifySwitchLookup(SwitchInst *SI, IRBuilder<> &Builder,
        Fn->getFnAttribute("no-jump-tables").getValueAsBool()))
     return false;
 
+  bool AnyBitMaps = any_of(PhiToReplacementMap,
+                           [](auto &KV) { return KV.second.isBitMap(); });
+
+  // Bitmaps can also cause missed optimizations due to difficult-to-analyze
+  // code. Delay the creation of bitmaps until later in the pipeline.
+  if (AnyBitMaps && !ConvertSwitchToLookupTable)
+    return false;
+
   Builder.SetInsertPoint(SI);
   // TableIndex is the switch condition - TableIndexOffset if we don't
   // use the condition directly
diff --git a/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll b/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll
index 8cdc593bbfe0b..708cdc9ca45ec 100644
--- a/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll
+++ b/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll
@@ -89,7 +89,7 @@ bb3:                                              ; preds = %bb1, %bb2
 
 define i1 @test2(i32 %c) {
 ; CHECK-LABEL: @test2(
-; CHECK-NEXT:    [[TMP2:%.*]] = tail call i1 @test1(i32 [[TMP0:%.*]]) #[[ATTR0:[0-9]+]]
+; CHECK-NEXT:    [[TMP2:%.*]] = tail call noundef i1 @test1(i32 [[TMP0:%.*]]) #[[ATTR0:[0-9]+]]
 ; CHECK-NEXT:    ret i1 [[TMP2]]
 ;
 entry:
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
index efe6b91c126af..01200136b6d40 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-transformations-no-lut.ll
@@ -166,11 +166,23 @@ end:
 define i4 @bitmap_no_default(i32 %x) {
 ; OPTNOLUT-LABEL: define i4 @bitmap_no_default(
 ; OPTNOLUT-SAME: i32 [[X:%.*]]) {
-; OPTNOLUT-NEXT:  [[ENTRY:.*:]]
-; OPTNOLUT-NEXT:    [[SWITCH_CAST:%.*]] = trunc i32 [[X]] to i16
-; OPTNOLUT-NEXT:    [[SWITCH_SHIFTAMT:%.*]] = mul nuw nsw i16 [[SWITCH_CAST]], 4
-; OPTNOLUT-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i16 -31712, [[SWITCH_SHIFTAMT]]
-; OPTNOLUT-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i16 [[SWITCH_DOWNSHIFT]] to i4
+; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
+; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
+; OPTNOLUT-NEXT:      i32 0, label %[[END:.*]]
+; OPTNOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; OPTNOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; OPTNOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; OPTNOLUT-NEXT:    ]
+; OPTNOLUT:       [[CASE1]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE2]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE3]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[DEFAULT]]:
+; OPTNOLUT-NEXT:    unreachable
+; OPTNOLUT:       [[END]]:
+; OPTNOLUT-NEXT:    [[SWITCH_MASKED:%.*]] = phi i4 [ 2, %[[CASE1]] ], [ 4, %[[CASE2]] ], [ -8, %[[CASE3]] ], [ 0, %[[ENTRY]] ]
 ; OPTNOLUT-NEXT:    ret i4 [[SWITCH_MASKED]]
 ;
 ; TTINOLUT-LABEL: define i4 @bitmap_no_default(
@@ -209,13 +221,23 @@ end:
 define i4 @bitmap_with_default(i32 %x) {
 ; OPTNOLUT-LABEL: define i4 @bitmap_with_default(
 ; OPTNOLUT-SAME: i32 [[X:%.*]]) {
-; OPTNOLUT-NEXT:  [[ENTRY:.*:]]
-; OPTNOLUT-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[X]], 4
-; OPTNOLUT-NEXT:    [[SWITCH_CAST:%.*]] = trunc i32 [[X]] to i16
-; OPTNOLUT-NEXT:    [[SWITCH_SHIFTAMT:%.*]] = mul nuw nsw i16 [[SWITCH_CAST]], 4
-; OPTNOLUT-NEXT:    [[SWITCH_DOWNSHIFT:%.*]] = lshr i16 -31712, [[SWITCH_SHIFTAMT]]
-; OPTNOLUT-NEXT:    [[SWITCH_MASKED:%.*]] = trunc i16 [[SWITCH_DOWNSHIFT]] to i4
-; OPTNOLUT-NEXT:    [[IDX:%.*]] = select i1 [[TMP0]], i4 [[SWITCH_MASKED]], i4 -1
+; OPTNOLUT-NEXT:  [[ENTRY:.*]]:
+; OPTNOLUT-NEXT:    switch i32 [[X]], label %[[DEFAULT:.*]] [
+; OPTNOLUT-NEXT:      i32 0, label %[[END:.*]]
+; OPTNOLUT-NEXT:      i32 1, label %[[CASE1:.*]]
+; OPTNOLUT-NEXT:      i32 2, label %[[CASE2:.*]]
+; OPTNOLUT-NEXT:      i32 3, label %[[CASE3:.*]]
+; OPTNOLUT-NEXT:    ]
+; OPTNOLUT:       [[CASE1]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE2]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[CASE3]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[DEFAULT]]:
+; OPTNOLUT-NEXT:    br label %[[END]]
+; OPTNOLUT:       [[END]]:
+; OPTNOLUT-NEXT:    [[IDX:%.*]] = phi i4 [ 2, %[[CASE1]] ], [ 4, %[[CASE2]] ], [ -8, %[[CASE3]] ], [ -1, %[[DEFAULT]] ], [ 0, %[[ENTRY]] ]
 ; OPTNOLUT-NEXT:    ret i4 [[IDX]]
 ;
 ; TTINOLUT-LABEL: define i4 @bitmap_with_default(

>From 913bcb474aaa37757ecb7f65d7f394c7a256ff6b Mon Sep 17 00:00:00 2001
From: Jessica Del <Jessica.Del at amd.com>
Date: Mon, 8 Sep 2025 11:59:24 +0200
Subject: [PATCH 8/8] SimplifyCFG: Collect lookup table statistics at creation

Since the SwitchReplacement is created before we decide to replace the
switch, only increase the table counters when we actually create them.
---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 7485230127723..0fc064f761f84 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -6619,7 +6619,6 @@ SwitchReplacement::SwitchReplacement(
         (void)M.smul_ov(APInt(M.getBitWidth(), TableSize - 1), MayWrap);
       LinearMapValWrapped = NonMonotonic || MayWrap;
       Kind = LinearMapKind;
-      ++NumLinearMaps;
       return;
     }
   }
@@ -6639,7 +6638,6 @@ SwitchReplacement::SwitchReplacement(
     BitMap = ConstantInt::get(M.getContext(), TableInt);
     BitMapElementTy = IT;
     Kind = BitMapKind;
-    ++NumBitMaps;
     return;
   }
 
@@ -6656,6 +6654,7 @@ Value *SwitchReplacement::replaceSwitch(Value *Index, IRBuilder<> &Builder,
   case SingleValueKind:
     return SingleValue;
   case LinearMapKind: {
+    ++NumLinearMaps;
     // Derive the result value from the input value.
     Value *Result = Builder.CreateIntCast(Index, LinearMultiplier->getType(),
                                           false, "switch.idx.cast");
@@ -6671,6 +6670,7 @@ Value *SwitchReplacement::replaceSwitch(Value *Index, IRBuilder<> &Builder,
     return Result;
   }
   case BitMapKind: {
+    ++NumBitMaps;
     // Type of the bitmap (e.g. i59).
     IntegerType *MapTy = BitMap->getIntegerType();
 
@@ -6693,6 +6693,7 @@ Value *SwitchReplacement::replaceSwitch(Value *Index, IRBuilder<> &Builder,
     return Builder.CreateTrunc(DownShifted, BitMapElementTy, "switch.masked");
   }
   case LookupTableKind: {
+    ++NumLookupTables;
     auto *Table =
         new GlobalVariable(*Func->getParent(), Initializer->getType(),
                            /*isConstant=*/true, GlobalVariable::PrivateLinkage,
@@ -7261,7 +7262,6 @@ static bool simplifySwitchLookup(SwitchInst *SI, IRBuilder<> &Builder,
   if (DTU)
     DTU->applyUpdates(Updates);
 
-  ++NumLookupTables;
   if (NeedMask)
     ++NumLookupTablesHoles;
   return true;



More information about the llvm-commits mailing list