Skip to content

Commit d684cc4

Browse files
Add a pass to convert jump tables to switches
1 parent 2a869ce commit d684cc4

16 files changed

+497
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//===- JumpTableToSwitch.h - ------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_TRANSFORMS_SCALAR_JUMP_TABLE_TO_SWITCH_H
10+
#define LLVM_TRANSFORMS_SCALAR_JUMP_TABLE_TO_SWITCH_H
11+
12+
#include "llvm/IR/PassManager.h"
13+
14+
namespace llvm {
15+
16+
class Function;
17+
18+
struct JumpTableToSwitchPass : PassInfoMixin<JumpTableToSwitchPass> {
19+
/// Run the pass over the function.
20+
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
21+
};
22+
} // end namespace llvm
23+
24+
#endif // LLVM_TRANSFORMS_SCALAR_JUMP_TABLE_TO_SWITCH_H

llvm/lib/Passes/PassBuilder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@
198198
#include "llvm/Transforms/Scalar/InferAddressSpaces.h"
199199
#include "llvm/Transforms/Scalar/InferAlignment.h"
200200
#include "llvm/Transforms/Scalar/InstSimplifyPass.h"
201+
#include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
201202
#include "llvm/Transforms/Scalar/JumpThreading.h"
202203
#include "llvm/Transforms/Scalar/LICM.h"
203204
#include "llvm/Transforms/Scalar/LoopAccessAnalysisPrinter.h"

llvm/lib/Passes/PassBuilderPipelines.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
#include "llvm/Transforms/Scalar/IndVarSimplify.h"
9292
#include "llvm/Transforms/Scalar/InferAlignment.h"
9393
#include "llvm/Transforms/Scalar/InstSimplifyPass.h"
94+
#include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
9495
#include "llvm/Transforms/Scalar/JumpThreading.h"
9596
#include "llvm/Transforms/Scalar/LICM.h"
9697
#include "llvm/Transforms/Scalar/LoopDeletion.h"
@@ -558,6 +559,7 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
558559
// Optimize based on known information about branches, and cleanup afterward.
559560
FPM.addPass(JumpThreadingPass());
560561
FPM.addPass(CorrelatedValuePropagationPass());
562+
FPM.addPass(JumpTableToSwitchPass());
561563

562564
FPM.addPass(
563565
SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true)));

llvm/lib/Passes/PassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ FUNCTION_PASS("interleaved-load-combine", InterleavedLoadCombinePass(TM))
342342
FUNCTION_PASS("invalidate<all>", InvalidateAllAnalysesPass())
343343
FUNCTION_PASS("irce", IRCEPass())
344344
FUNCTION_PASS("jump-threading", JumpThreadingPass())
345+
FUNCTION_PASS("jump-table-to-switch", JumpTableToSwitchPass());
345346
FUNCTION_PASS("kcfi", KCFIPass())
346347
FUNCTION_PASS("lcssa", LCSSAPass())
347348
FUNCTION_PASS("libcalls-shrinkwrap", LibCallsShrinkWrapPass())

llvm/lib/Transforms/Scalar/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ add_llvm_component_library(LLVMScalarOpts
2525
InferAlignment.cpp
2626
InstSimplifyPass.cpp
2727
JumpThreading.cpp
28+
JumpTableToSwitch.cpp
2829
LICM.cpp
2930
LoopAccessAnalysisPrinter.cpp
3031
LoopBoundSplit.cpp
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
//===- JumpTableToSwitch.cpp ----------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
10+
#include "llvm/ADT/DenseMap.h"
11+
#include "llvm/ADT/SmallSet.h"
12+
#include "llvm/Analysis/ConstantFolding.h"
13+
#include "llvm/Analysis/DomTreeUpdater.h"
14+
#include "llvm/Analysis/PostDominators.h"
15+
#include "llvm/Analysis/TargetLibraryInfo.h"
16+
#include "llvm/Analysis/TargetTransformInfo.h"
17+
#include "llvm/Analysis/ValueTracking.h"
18+
#include "llvm/IR/IRBuilder.h"
19+
#include "llvm/IR/IntrinsicInst.h"
20+
#include "llvm/IR/PatternMatch.h"
21+
#include "llvm/Support/CommandLine.h"
22+
#include "llvm/Support/Debug.h"
23+
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
24+
#include "llvm/Transforms/Utils/Cloning.h"
25+
#include "llvm/Transforms/Utils/Local.h"
26+
27+
using namespace llvm;
28+
using namespace PatternMatch;
29+
30+
static cl::opt<unsigned>
31+
JumpTableSizeThreshold("jump-table-to-switch-size-threshold", cl::Hidden,
32+
cl::desc("Only split jump tables with size less or "
33+
"equal than JumpTableSizeThreshold."),
34+
cl::init(10));
35+
36+
#define DEBUG_TYPE "jump-table-to-switch"
37+
38+
namespace {
39+
struct JumpTableTy {
40+
Value *Index;
41+
SmallVector<Function *, 5> Funcs;
42+
};
43+
} // anonymous namespace
44+
45+
static std::optional<JumpTableTy> parseJumpTable(GetElementPtrInst *GEP) {
46+
if (!GEP || !GEP->isInBounds())
47+
return std::nullopt;
48+
ArrayType *ArrayTy = dyn_cast<ArrayType>(GEP->getSourceElementType());
49+
if (!ArrayTy || ArrayTy->getArrayNumElements() > JumpTableSizeThreshold)
50+
return std::nullopt;
51+
const uint64_t N = ArrayTy->getArrayNumElements();
52+
Constant *Ptr = dyn_cast<Constant>(GEP->getPointerOperand());
53+
if (!Ptr || !Ptr->getNumOperands())
54+
return std::nullopt;
55+
56+
GlobalVariable *GV = dyn_cast<GlobalVariable>(Ptr);
57+
if (!GV || !GV->isConstant())
58+
return std::nullopt;
59+
60+
Function &F = *GEP->getParent()->getParent();
61+
62+
const DataLayout &DL = F.getParent()->getDataLayout();
63+
const unsigned BitWidth =
64+
DL.getIndexSizeInBits(GEP->getPointerAddressSpace());
65+
MapVector<Value *, APInt> VariableOffsets;
66+
APInt ConstantOffset(BitWidth, 0);
67+
if (!GEP->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset))
68+
return std::nullopt;
69+
if (VariableOffsets.empty() || VariableOffsets.size() > 1)
70+
return std::nullopt;
71+
unsigned Offset = ConstantOffset.getZExtValue();
72+
// TODO: consider supporting more general patterns
73+
if (Offset != 0)
74+
return std::nullopt;
75+
76+
JumpTableTy JumpTable;
77+
JumpTable.Index = VariableOffsets.front().first;
78+
JumpTable.Funcs.assign(N, nullptr);
79+
PointerType *PtrTy =
80+
PointerType::get(F.getContext(), DL.getProgramAddressSpace());
81+
const unsigned PtrSizeBits = DL.getPointerTypeSizeInBits(PtrTy);
82+
const unsigned PtrSizeBytes = DL.getPointerTypeSize(PtrTy);
83+
for (uint64_t Index = 0; Index < N; ++Index) {
84+
APInt Offset(PtrSizeBits, Index * PtrSizeBytes);
85+
Constant *C = ConstantFoldLoadFromConst(
86+
cast<Constant>(GV->getOperand(0)),
87+
PointerType::get(F.getContext(), DL.getProgramAddressSpace()), Offset,
88+
DL);
89+
auto *Func = dyn_cast_or_null<Function>(C);
90+
if (!Func || Func->isDeclaration())
91+
return std::nullopt;
92+
JumpTable.Funcs[Index] = Func;
93+
}
94+
return JumpTable;
95+
}
96+
97+
static BasicBlock *split(CallBase *CB, const JumpTableTy &JT,
98+
DomTreeUpdater *DTU) {
99+
const bool IsVoid = CB->getType() == Type::getVoidTy(CB->getContext());
100+
101+
SmallVector<DominatorTree::UpdateType, 8> DTUpdates;
102+
BasicBlock *BB = CB->getParent();
103+
BasicBlock *Tail =
104+
SplitBlock(BB, CB, DTU, nullptr, nullptr, BB->getName() + Twine(".tail"));
105+
DTUpdates.push_back({DominatorTree::Delete, BB, Tail});
106+
BB->getTerminator()->eraseFromParent();
107+
108+
Function &F = *BB->getParent();
109+
BasicBlock *BBUnreachable = BasicBlock::Create(
110+
F.getContext(), "default.switch.case.unreachable", &F, Tail);
111+
IRBuilder<> BuilderUnreachable(BBUnreachable);
112+
BuilderUnreachable.CreateUnreachable();
113+
114+
IRBuilder<> Builder(BB);
115+
SwitchInst *Switch = Builder.CreateSwitch(JT.Index, BBUnreachable);
116+
DTUpdates.push_back({DominatorTree::Insert, BB, BBUnreachable});
117+
118+
IRBuilder<> BuilderTail(CB);
119+
PHINode *PHI =
120+
IsVoid ? nullptr : BuilderTail.CreatePHI(CB->getType(), JT.Funcs.size());
121+
122+
for (auto [Index, Func] : llvm::enumerate(JT.Funcs)) {
123+
BasicBlock *B = BasicBlock::Create(Func->getContext(),
124+
"call." + Twine(Index), &F, Tail);
125+
DTUpdates.push_back({DominatorTree::Insert, BB, B});
126+
DTUpdates.push_back({DominatorTree::Insert, B, Tail});
127+
128+
CallBase *Call = cast<CallBase>(CB->clone());
129+
Call->setCalledFunction(Func);
130+
Call->insertInto(B, B->end());
131+
Switch->addCase(
132+
cast<ConstantInt>(ConstantInt::get(JT.Index->getType(), Index)), B);
133+
BranchInst::Create(Tail, B);
134+
if (PHI)
135+
PHI->addIncoming(Call, B);
136+
}
137+
if (DTU)
138+
DTU->applyUpdates(DTUpdates);
139+
if (PHI)
140+
CB->replaceAllUsesWith(PHI);
141+
CB->eraseFromParent();
142+
return Tail;
143+
}
144+
145+
PreservedAnalyses JumpTableToSwitchPass::run(Function &F,
146+
FunctionAnalysisManager &AM) {
147+
DominatorTree *DT = AM.getCachedResult<DominatorTreeAnalysis>(F);
148+
PostDominatorTree *PDT = AM.getCachedResult<PostDominatorTreeAnalysis>(F);
149+
std::unique_ptr<DomTreeUpdater> DTU;
150+
bool Changed = false;
151+
for (BasicBlock &BB : make_early_inc_range(F)) {
152+
BasicBlock *CurrentBB = &BB;
153+
while (CurrentBB) {
154+
BasicBlock *SplittedOutTail = nullptr;
155+
for (Instruction &I : make_early_inc_range(*CurrentBB)) {
156+
CallBase *CB = dyn_cast<CallBase>(&I);
157+
if (!CB || isa<IntrinsicInst>(CB) || CB->getCalledFunction() ||
158+
isa<InvokeInst>(CB) || CB->isMustTailCall())
159+
continue;
160+
161+
Value *V;
162+
if (!match(CB->getCalledOperand(), m_Load(m_Value(V))))
163+
continue;
164+
// Skip volatile loads.
165+
if (cast<LoadInst>(CB->getCalledOperand())->isVolatile())
166+
continue;
167+
auto *GEP = dyn_cast<GetElementPtrInst>(V);
168+
if (!GEP)
169+
continue;
170+
171+
std::optional<JumpTableTy> JumpTable = parseJumpTable(GEP);
172+
if (!JumpTable)
173+
continue;
174+
if ((DT || PDT) && !DTU)
175+
DTU = std::make_unique<DomTreeUpdater>(
176+
DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);
177+
SplittedOutTail = split(CB, *JumpTable, DTU.get());
178+
Changed = true;
179+
break;
180+
}
181+
CurrentBB = SplittedOutTail ? SplittedOutTail : nullptr;
182+
}
183+
}
184+
185+
if (!Changed)
186+
return PreservedAnalyses::all();
187+
188+
PreservedAnalyses PA;
189+
if (DT)
190+
PA.preserve<DominatorTreeAnalysis>();
191+
if (PDT)
192+
PA.preserve<PostDominatorTreeAnalysis>();
193+
return PA;
194+
}

llvm/test/Other/new-pm-defaults.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@
151151
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
152152
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
153153
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
154+
; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
154155
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
155156
; CHECK-O-NEXT: Running pass: InstCombinePass
156157
; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass

llvm/test/Other/new-pm-thinlto-postlink-defaults.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
9191
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
9292
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
93+
; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
9394
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
9495
; CHECK-O-NEXT: Running pass: InstCombinePass
9596
; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass

llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
7979
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
8080
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
81+
; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
8182
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
8283
; CHECK-O-NEXT: Running pass: InstCombinePass
8384
; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass

llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
8787
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
8888
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
89+
; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
8990
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
9091
; CHECK-O-NEXT: Running pass: InstCombinePass
9192
; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass

llvm/test/Other/new-pm-thinlto-prelink-defaults.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
122122
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
123123
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
124+
; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
124125
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
125126
; CHECK-O-NEXT: Running pass: InstCombinePass
126127
; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass

llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
119119
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
120120
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
121+
; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
121122
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
122123
; CHECK-O-NEXT: Running pass: InstCombinePass
123124
; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on foo

llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
9191
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
9292
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
93+
; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
9394
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
9495
; CHECK-O-NEXT: Running pass: InstCombinePass
9596
; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass

0 commit comments

Comments
 (0)