[llvm] [DirectX][SimplifyCFG] Don't narrow to i8 arrays for switch tables (PR #203103)

Farzon Lotfi via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 10 13:54:13 PDT 2026


https://github.com/farzonl created https://github.com/llvm/llvm-project/pull/203103

fixes #202481

This change adds a TTI to disable the narrowing for targets that don't support it like DirectX.

The `isTypeLegal` TTI was considered, instead of adding a new one, but that was causing significant test failures that needed to be triaged through.

>From 53566b51d20fd7a8f47af4c43641897d3188ad14 Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzon at farzon.org>
Date: Mon, 8 Jun 2026 22:11:48 -0400
Subject: [PATCH] [DirectX][SimplifyCFG] Don't narrow to i8 arrays for switch
 tables fixes #202481

This change adds a TTI to disable the narrowing for targets that don't
support it like DirectX.

The `isTypeLegal` TTI was considered, instead of adding a new one, but
that was causing significant test failures that needed to be triaged
through.
---
 .../llvm/Analysis/TargetTransformInfo.h       |  4 ++
 .../llvm/Analysis/TargetTransformInfoImpl.h   |  2 +
 llvm/lib/Analysis/TargetTransformInfo.cpp     |  4 ++
 .../DirectX/DirectXTargetTransformInfo.cpp    |  6 +++
 .../DirectX/DirectXTargetTransformInfo.h      |  1 +
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     | 15 ++++---
 .../switch-to-lookup-table-no-i8-narrowing.ll | 42 +++++++++++++++++++
 7 files changed, 69 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/CodeGen/DirectX/switch-to-lookup-table-no-i8-narrowing.ll

diff --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h b/llvm/include/llvm/Analysis/TargetTransformInfo.h
index 7d58473b81265..7ef643a288a3e 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfo.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h
@@ -1025,6 +1025,10 @@ class TargetTransformInfo {
   /// containing this constant value for the target.
   LLVM_ABI bool shouldBuildLookupTablesForConstant(Constant *C) const;
 
+  /// Return true if \p Ty may be used as the element type of a switch lookup
+  /// table on this target.
+  LLVM_ABI bool isLegalLookupTableElementType(Type *Ty) const;
+
   /// Return true if lookup tables should be turned into relative lookup tables.
   LLVM_ABI bool shouldBuildRelLookupTables() const;
 
diff --git a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
index 89fd4a1e7628e..b94df409dcb11 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
@@ -475,6 +475,8 @@ class LLVM_ABI TargetTransformInfoImplBase {
     return true;
   }
 
+  virtual bool isLegalLookupTableElementType(Type *Ty) const { return true; }
+
   virtual bool shouldBuildRelLookupTables() const { return false; }
 
   virtual bool useColdCCForColdCall(Function &F) const { return false; }
diff --git a/llvm/lib/Analysis/TargetTransformInfo.cpp b/llvm/lib/Analysis/TargetTransformInfo.cpp
index 856950ccae595..d8eaff3ca1c73 100644
--- a/llvm/lib/Analysis/TargetTransformInfo.cpp
+++ b/llvm/lib/Analysis/TargetTransformInfo.cpp
@@ -617,6 +617,10 @@ bool TargetTransformInfo::shouldBuildLookupTablesForConstant(
   return TTIImpl->shouldBuildLookupTablesForConstant(C);
 }
 
+bool TargetTransformInfo::isLegalLookupTableElementType(Type *Ty) const {
+  return TTIImpl->isLegalLookupTableElementType(Ty);
+}
+
 bool TargetTransformInfo::shouldBuildRelLookupTables() const {
   return TTIImpl->shouldBuildRelLookupTables();
 }
diff --git a/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp b/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
index 9952038aaf7bd..7453bd84dd1fc 100644
--- a/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
+++ b/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
@@ -44,3 +44,9 @@ bool DirectXTTIImpl::isTargetIntrinsicWithOverloadTypeAtArg(Intrinsic::ID ID,
     return OpdIdx == -1;
   }
 }
+
+bool DirectXTTIImpl::isLegalLookupTableElementType(Type *Ty) const {
+  // DXIL does not support i8, so switch lookup tables must not be narrowed to
+  // an i8 element type.
+  return !Ty->isIntegerTy(8);
+}
diff --git a/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.h b/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.h
index fdbbc18003419..b860324b11295 100644
--- a/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.h
+++ b/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.h
@@ -38,6 +38,7 @@ class DirectXTTIImpl final : public BasicTTIImplBase<DirectXTTIImpl> {
                                           unsigned ScalarOpdIdx) const override;
   bool isTargetIntrinsicWithOverloadTypeAtArg(Intrinsic::ID ID,
                                               int OpdIdx) const override;
+  bool isLegalLookupTableElementType(Type *Ty) const override;
 
   InstructionCost getPartialReductionCost(
       unsigned Opcode, Type *InputTypeA, Type *InputTypeB, Type *AccumType,
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 4b21b20beb895..b04b0cdd39d5b 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -6780,7 +6780,8 @@ class SwitchReplacement {
   SwitchReplacement(
       Module &M, uint64_t TableSize, ConstantInt *Offset,
       const SmallVectorImpl<std::pair<ConstantInt *, Constant *>> &Values,
-      Constant *DefaultValue, const DataLayout &DL, const StringRef &FuncName);
+      Constant *DefaultValue, const DataLayout &DL,
+      const TargetTransformInfo &TTI, const StringRef &FuncName);
 
   /// Build instructions with Builder to retrieve values using Index
   /// and replace the switch.
@@ -6850,7 +6851,8 @@ class SwitchReplacement {
 SwitchReplacement::SwitchReplacement(
     Module &M, uint64_t TableSize, ConstantInt *Offset,
     const SmallVectorImpl<std::pair<ConstantInt *, Constant *>> &Values,
-    Constant *DefaultValue, const DataLayout &DL, const StringRef &FuncName)
+    Constant *DefaultValue, const DataLayout &DL,
+    const TargetTransformInfo &TTI, const StringRef &FuncName)
     : DefaultValue(DefaultValue) {
   assert(Values.size() && "Can't build lookup table without values!");
   assert(TableSize >= Values.size() && "Can't fit values in table!");
@@ -6980,8 +6982,11 @@ SwitchReplacement::SwitchReplacement(
         std::max(8u, unsigned(PowerOf2Ceil(Range.getActiveBits())));
     if (NeededBitWidth < IT->getBitWidth()) {
       IntegerType *DstTy = IntegerType::get(IT->getContext(), NeededBitWidth);
-      for (Constant *&Value : TableContents)
-        Value = ConstantFoldCastInstruction(Instruction::Trunc, Value, DstTy);
+      // Only narrow the table element type if the narrower type is legal on
+      // this target.
+      if (TTI.isLegalLookupTableElementType(DstTy))
+        for (Constant *&Value : TableContents)
+          Value = ConstantFoldCastInstruction(Instruction::Trunc, Value, DstTy);
     }
   }
 
@@ -7439,7 +7444,7 @@ static bool simplifySwitchLookup(SwitchInst *SI, IRBuilder<> &Builder,
         AllHolesArePoison ? PoisonValue::get(ResultType) : DefaultResults[PHI];
     StringRef FuncName = Fn->getName();
     SwitchReplacement Replacement(*Fn->getParent(), TableSize, TableIndexOffset,
-                                  ResultList, DefaultVal, DL, FuncName);
+                                  ResultList, DefaultVal, DL, TTI, FuncName);
     PhiToReplacementMap.insert({PHI, Replacement});
   }
 
diff --git a/llvm/test/CodeGen/DirectX/switch-to-lookup-table-no-i8-narrowing.ll b/llvm/test/CodeGen/DirectX/switch-to-lookup-table-no-i8-narrowing.ll
new file mode 100644
index 0000000000000..86259cdcb0799
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/switch-to-lookup-table-no-i8-narrowing.ll
@@ -0,0 +1,42 @@
+; RUN: opt -S -passes=simplifycfg -switch-to-lookup=true -keep-loops=false -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
+
+; The switch result values all fit in i8, so on a typical target SimplifyCFG
+; narrows the lookup table element type from i32 down to i8. DXIL/DXC does not
+; support i8, and the DirectX target transform reports i8 as an illegal type, so
+; SimplifyCFG must keep the original i32 element type instead of downcasting the
+; table to i8.
+
+; CHECK: @switch.table.test = private unnamed_addr constant [4 x i32] [i32 3, i32 1, i32 2, i32 5]
+; CHECK-NOT: @switch.table.test = private unnamed_addr constant [4 x i8]
+
+define i32 @test(i32 %i) {
+; CHECK-LABEL: define i32 @test(
+; CHECK:         getelementptr inbounds [4 x i32], ptr @switch.table.test
+; CHECK:         load i32, ptr
+entry:
+  switch i32 %i, label %def [
+    i32 0, label %bb0
+    i32 1, label %bb1
+    i32 2, label %bb2
+    i32 3, label %bb3
+  ]
+
+bb0:
+  br label %ret
+
+bb1:
+  br label %ret
+
+bb2:
+  br label %ret
+
+bb3:
+  br label %ret
+
+def:
+  br label %ret
+
+ret:
+  %r = phi i32 [ 3, %bb0 ], [ 1, %bb1 ], [ 2, %bb2 ], [ 5, %bb3 ], [ 0, %def ]
+  ret i32 %r
+}



More information about the llvm-commits mailing list