[llvm] Add a pass to convert jump tables to switches. (PR #77709)
Alexander Shaposhnikov via llvm-commits
llvm-commits at lists.llvm.org
Wed Jan 10 16:59:23 PST 2024
https://github.com/alexander-shaposhnikov updated https://github.com/llvm/llvm-project/pull/77709
>From cfb675819230b9087620c187e104c5489c9c0c1c Mon Sep 17 00:00:00 2001
From: Alexander Shaposhnikov <ashaposhnikov at google.com>
Date: Thu, 11 Jan 2024 00:58:53 +0000
Subject: [PATCH] Add a pass to convert jump tables to switches
---
.../Transforms/Scalar/JumpTableToSwitch.h | 24 ++
llvm/lib/Passes/PassBuilder.cpp | 1 +
llvm/lib/Passes/PassBuilderPipelines.cpp | 4 +
llvm/lib/Passes/PassRegistry.def | 1 +
llvm/lib/Transforms/Scalar/CMakeLists.txt | 1 +
.../Transforms/Scalar/JumpTableToSwitch.cpp | 205 ++++++++++++++++++
llvm/test/Other/new-pm-defaults.ll | 1 +
.../Other/new-pm-thinlto-postlink-defaults.ll | 1 +
.../new-pm-thinlto-postlink-pgo-defaults.ll | 1 +
...-pm-thinlto-postlink-samplepgo-defaults.ll | 1 +
.../Other/new-pm-thinlto-prelink-defaults.ll | 1 +
.../new-pm-thinlto-prelink-pgo-defaults.ll | 1 +
...w-pm-thinlto-prelink-samplepgo-defaults.ll | 1 +
.../Transforms/JumpTableToSwitch/basic.ll | 94 ++++++++
.../test/Transforms/JumpTableToSwitch/skip.ll | 70 ++++++
.../Transforms/JumpTableToSwitch/struct.ll | 42 ++++
16 files changed, 449 insertions(+)
create mode 100644 llvm/include/llvm/Transforms/Scalar/JumpTableToSwitch.h
create mode 100644 llvm/lib/Transforms/Scalar/JumpTableToSwitch.cpp
create mode 100644 llvm/test/Transforms/JumpTableToSwitch/basic.ll
create mode 100644 llvm/test/Transforms/JumpTableToSwitch/skip.ll
create mode 100644 llvm/test/Transforms/JumpTableToSwitch/struct.ll
diff --git a/llvm/include/llvm/Transforms/Scalar/JumpTableToSwitch.h b/llvm/include/llvm/Transforms/Scalar/JumpTableToSwitch.h
new file mode 100644
index 00000000000000..61786227d7a335
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Scalar/JumpTableToSwitch.h
@@ -0,0 +1,24 @@
+//===- JumpTableToSwitch.h - ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_SCALAR_JUMP_TABLE_TO_SWITCH_H
+#define LLVM_TRANSFORMS_SCALAR_JUMP_TABLE_TO_SWITCH_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class Function;
+
+struct JumpTableToSwitchPass : PassInfoMixin<JumpTableToSwitchPass> {
+ /// Run the pass over the function.
+ PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_SCALAR_JUMP_TABLE_TO_SWITCH_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 649451edc0e2c6..196d6d171a3ba5 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -192,6 +192,7 @@
#include "llvm/Transforms/Scalar/InferAddressSpaces.h"
#include "llvm/Transforms/Scalar/InferAlignment.h"
#include "llvm/Transforms/Scalar/InstSimplifyPass.h"
+#include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
#include "llvm/Transforms/Scalar/JumpThreading.h"
#include "llvm/Transforms/Scalar/LICM.h"
#include "llvm/Transforms/Scalar/LoopAccessAnalysisPrinter.h"
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 5c6c391049a7b2..72d82d6c453d21 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -91,6 +91,7 @@
#include "llvm/Transforms/Scalar/IndVarSimplify.h"
#include "llvm/Transforms/Scalar/InferAlignment.h"
#include "llvm/Transforms/Scalar/InstSimplifyPass.h"
+#include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
#include "llvm/Transforms/Scalar/JumpThreading.h"
#include "llvm/Transforms/Scalar/LICM.h"
#include "llvm/Transforms/Scalar/LoopDeletion.h"
@@ -557,6 +558,7 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
// Optimize based on known information about branches, and cleanup afterward.
FPM.addPass(JumpThreadingPass());
+ FPM.addPass(JumpTableToSwitchPass());
FPM.addPass(CorrelatedValuePropagationPass());
FPM.addPass(
@@ -695,6 +697,7 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
FPM.addPass(DFAJumpThreadingPass());
FPM.addPass(JumpThreadingPass());
+
FPM.addPass(CorrelatedValuePropagationPass());
// Finally, do an expensive DCE pass to catch all the dead code exposed by
@@ -1926,6 +1929,7 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
invokePeepholeEPCallbacks(MainFPM, Level);
MainFPM.addPass(JumpThreadingPass());
+
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(MainFPM),
PTO.EagerlyInvalidateAnalyses));
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index eaa7c3fc89241b..7417d639a9ecd7 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -330,6 +330,7 @@ FUNCTION_PASS("interleaved-load-combine", InterleavedLoadCombinePass(TM))
FUNCTION_PASS("invalidate<all>", InvalidateAllAnalysesPass())
FUNCTION_PASS("irce", IRCEPass())
FUNCTION_PASS("jump-threading", JumpThreadingPass())
+FUNCTION_PASS("jump-table-to-switch", JumpTableToSwitchPass());
FUNCTION_PASS("kcfi", KCFIPass())
FUNCTION_PASS("lcssa", LCSSAPass())
FUNCTION_PASS("libcalls-shrinkwrap", LibCallsShrinkWrapPass())
diff --git a/llvm/lib/Transforms/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt
index 2dd27037a17de7..7b49c8010c874a 100644
--- a/llvm/lib/Transforms/Scalar/CMakeLists.txt
+++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt
@@ -25,6 +25,7 @@ add_llvm_component_library(LLVMScalarOpts
InferAlignment.cpp
InstSimplifyPass.cpp
JumpThreading.cpp
+ JumpTableToSwitch.cpp
LICM.cpp
LoopAccessAnalysisPrinter.cpp
LoopBoundSplit.cpp
diff --git a/llvm/lib/Transforms/Scalar/JumpTableToSwitch.cpp b/llvm/lib/Transforms/Scalar/JumpTableToSwitch.cpp
new file mode 100644
index 00000000000000..63b0869e933682
--- /dev/null
+++ b/llvm/lib/Transforms/Scalar/JumpTableToSwitch.cpp
@@ -0,0 +1,205 @@
+//===- JumpTableToSwitch.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/Analysis/DomTreeUpdater.h"
+#include "llvm/Analysis/PostDominators.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/Analysis/ValueTracking.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/PatternMatch.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/Local.h"
+
+using namespace llvm;
+using namespace PatternMatch;
+
+static cl::opt<unsigned>
+ JumpTableSizeThreshold("jump-table-to-switch-size-threshold", cl::Hidden,
+ cl::desc("Only split jump tables with size less or "
+ "equal than JumpTableSizeThreshold."),
+ cl::init(10));
+
+#define DEBUG_TYPE "jump-table-to-switch"
+
+static Constant *getElementWithGivenTypeAtOffset(Constant *C, const Type *Ty,
+ uint64_t Offset, Module &M) {
+ if (Offset == 0 && C->getType() == Ty)
+ return C;
+ if (auto *CS = dyn_cast<ConstantStruct>(C)) {
+ const DataLayout &DL = M.getDataLayout();
+ const StructLayout *SL = DL.getStructLayout(CS->getType());
+ if (Offset >= SL->getSizeInBytes())
+ return nullptr;
+ const unsigned Op = SL->getElementContainingOffset(Offset);
+ const uint64_t AdjustedOffset = Offset - SL->getElementOffset(Op);
+ Constant *Element = cast<Constant>(CS->getOperand(Op));
+ return getElementWithGivenTypeAtOffset(Element, Ty, AdjustedOffset, M);
+ }
+ // TODO: add support for arrays.
+ return nullptr;
+}
+
+namespace {
+struct JumpTableTy {
+ Value *Index;
+ SmallVector<Function *, 5> Funcs;
+};
+} // anonymous namespace
+
+static std::optional<JumpTableTy> parseJumpTable(GetElementPtrInst *GEP) {
+ if (!GEP || !GEP->isInBounds())
+ return std::nullopt;
+ ArrayType *ArrayTy = dyn_cast<ArrayType>(GEP->getSourceElementType());
+ if (!ArrayTy || ArrayTy->getArrayNumElements() > JumpTableSizeThreshold)
+ return std::nullopt;
+ Constant *Ptr = dyn_cast<Constant>(GEP->getPointerOperand());
+ if (!Ptr)
+ return std::nullopt;
+
+ Function &F = *GEP->getParent()->getParent();
+ const DataLayout &DL = F.getParent()->getDataLayout();
+ const unsigned BitWidth =
+ DL.getIndexSizeInBits(GEP->getPointerAddressSpace());
+ MapVector<Value *, APInt> VariableOffsets;
+ APInt ConstantOffset(BitWidth, 0);
+ if (!GEP->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset))
+ return std::nullopt;
+ if (VariableOffsets.empty() || VariableOffsets.size() > 1)
+ return std::nullopt;
+
+ Module &M = *F.getParent();
+ unsigned Offset = ConstantOffset.getZExtValue();
+ // TODO: support more general patterns
+ // (see also TODO in getElementWithGivenTypeAtOffset).
+ if (Offset != 0)
+ return std::nullopt;
+ if (!Ptr->getNumOperands())
+ return std::nullopt;
+ Constant *ConstArray = getElementWithGivenTypeAtOffset(
+ cast<Constant>(Ptr->getOperand(0)), ArrayTy, Offset, M);
+ if (!ConstArray)
+ return std::nullopt;
+
+ JumpTableTy JumpTable;
+ JumpTable.Index = VariableOffsets.front().first;
+
+ const uint64_t N = ArrayTy->getArrayNumElements();
+ JumpTable.Funcs.assign(N, nullptr);
+ for (uint64_t Index = 0; Index < N; ++Index) {
+ auto *Func =
+ dyn_cast_or_null<Function>(ConstArray->getAggregateElement(Index));
+ if (!Func || Func->isDeclaration())
+ return std::nullopt;
+ JumpTable.Funcs[Index] = Func;
+ }
+ return JumpTable;
+}
+
+static BasicBlock *split(CallBase *CB, const JumpTableTy &JT,
+ DomTreeUpdater *DTU) {
+ const bool IsVoid = CB->getType() == Type::getVoidTy(CB->getContext());
+
+ SmallVector<DominatorTree::UpdateType, 8> DTUpdates;
+ BasicBlock *BB = CB->getParent();
+ BasicBlock *Tail =
+ SplitBlock(BB, CB, DTU, nullptr, nullptr, BB->getName() + Twine(".tail"));
+ DTUpdates.push_back({DominatorTree::Delete, BB, Tail});
+ BB->getTerminator()->eraseFromParent();
+
+ Function &F = *BB->getParent();
+ BasicBlock *BBUnreachable = BasicBlock::Create(
+ F.getContext(), "default.switch.case.unreachable", &F, Tail);
+ IRBuilder<> BuilderUnreachable(BBUnreachable);
+ BuilderUnreachable.CreateUnreachable();
+
+ IRBuilder<> Builder(BB);
+ SwitchInst *Switch = Builder.CreateSwitch(JT.Index, BBUnreachable);
+ DTUpdates.push_back({DominatorTree::Insert, BB, BBUnreachable});
+
+ IRBuilder<> BuilderTail(CB);
+ PHINode *PHI =
+ IsVoid ? nullptr : BuilderTail.CreatePHI(CB->getType(), JT.Funcs.size());
+
+ for (auto [Index, Func] : llvm::enumerate(JT.Funcs)) {
+ BasicBlock *B = BasicBlock::Create(Func->getContext(),
+ "call." + Twine(Index), &F, Tail);
+ DTUpdates.push_back({DominatorTree::Insert, BB, B});
+ DTUpdates.push_back({DominatorTree::Insert, B, Tail});
+
+ CallBase *Call = cast<CallBase>(CB->clone());
+ Call->setCalledFunction(Func);
+ Call->insertInto(B, B->end());
+ Switch->addCase(
+ cast<ConstantInt>(ConstantInt::get(JT.Index->getType(), Index)), B);
+ BranchInst::Create(Tail, B);
+ if (PHI)
+ PHI->addIncoming(Call, B);
+ }
+ if (DTU)
+ DTU->applyUpdates(DTUpdates);
+ if (PHI)
+ CB->replaceAllUsesWith(PHI);
+ CB->eraseFromParent();
+ return Tail;
+}
+
+PreservedAnalyses JumpTableToSwitchPass::run(Function &F,
+ FunctionAnalysisManager &AM) {
+ DominatorTree *DT = AM.getCachedResult<DominatorTreeAnalysis>(F);
+ PostDominatorTree *PDT = AM.getCachedResult<PostDominatorTreeAnalysis>(F);
+ std::unique_ptr<DomTreeUpdater> DTU;
+ bool Changed = false;
+ for (BasicBlock &BB : make_early_inc_range(F)) {
+ BasicBlock *CurrentBB = &BB;
+ while (CurrentBB) {
+ BasicBlock *SplittedOutTail = nullptr;
+ for (Instruction &I : make_early_inc_range(*CurrentBB)) {
+ CallBase *CB = dyn_cast<CallBase>(&I);
+ if (!CB || isa<IntrinsicInst>(CB) || CB->getCalledFunction() ||
+ isa<InvokeInst>(CB) || CB->isMustTailCall())
+ continue;
+
+ Value *V;
+ if (!match(CB->getCalledOperand(), m_Load(m_Value(V))))
+ continue;
+ auto *GEP = dyn_cast<GetElementPtrInst>(V);
+ if (!GEP)
+ continue;
+
+ std::optional<JumpTableTy> JumpTable = parseJumpTable(GEP);
+ if (!JumpTable)
+ continue;
+ if ((DT || PDT) && !DTU)
+ DTU = std::make_unique<DomTreeUpdater>(
+ DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy);
+ SplittedOutTail = split(CB, *JumpTable, DTU.get());
+ Changed = true;
+ break;
+ }
+ CurrentBB = SplittedOutTail ? SplittedOutTail : nullptr;
+ }
+ }
+
+ if (!Changed)
+ return PreservedAnalyses::all();
+
+ PreservedAnalyses PA;
+ if (DT)
+ PA.preserve<DominatorTreeAnalysis>();
+ if (PDT)
+ PA.preserve<PostDominatorTreeAnalysis>();
+ return PA;
+}
diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll
index ecdb5a5e010d92..fb4254363c3eaa 100644
--- a/llvm/test/Other/new-pm-defaults.ll
+++ b/llvm/test/Other/new-pm-defaults.ll
@@ -149,6 +149,7 @@
; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass
; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
+; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
index 064362eabbf839..4f54becf5871f5 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
@@ -88,6 +88,7 @@
; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass
; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
+; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
index 19a44867e434ac..8b869b1871a815 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
@@ -76,6 +76,7 @@
; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass
; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
+; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
index ac80a31d8fd4bc..da5d8466711017 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
@@ -84,6 +84,7 @@
; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass
; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
+; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
index 6486639e07b49c..809b8abb206040 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
@@ -119,6 +119,7 @@
; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass
; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
+; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
index 09f9f0f48baddb..438c4ba364be3a 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
@@ -116,6 +116,7 @@
; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass
; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
+; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
index 47bdbfd2d357d4..1b7ce7bbf9680f 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
@@ -88,6 +88,7 @@
; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass
; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
+; CHECK-O23SZ-NEXT: Running pass: JumpTableToSwitchPass
; CHECK-O23SZ-NEXT: Running pass: CorrelatedValuePropagationPass
; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
diff --git a/llvm/test/Transforms/JumpTableToSwitch/basic.ll b/llvm/test/Transforms/JumpTableToSwitch/basic.ll
new file mode 100644
index 00000000000000..3feb760909d08a
--- /dev/null
+++ b/llvm/test/Transforms/JumpTableToSwitch/basic.ll
@@ -0,0 +1,94 @@
+; RUN: opt < %s -passes=jump-table-to-switch -verify-dom-info -S | FileCheck %s
+; RUN: opt < %s -passes=jump-table-to-switch -jump-table-to-switch-size-threshold=0 -verify-dom-info -S | FileCheck %s --check-prefix=THRESHOLD-0
+
+ at func_array = global [2 x ptr] [ptr @func0, ptr @func1]
+
+define i32 @func0() {
+ ret i32 1
+}
+
+define i32 @func1() {
+ ret i32 2
+}
+
+define i32 @function_with_jump_table(i32 %index) {
+ %gep = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 %index
+ %func_ptr = load ptr, ptr %gep
+ %result = call i32 %func_ptr()
+ ret i32 %result
+}
+
+; CHECK-LABEL: define i32 @function_with_jump_table
+; CHECK: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 %index
+; CHECK: switch i32 %index, label %[[DEFAULT_LABEL:.*]] [
+; CHECK-NEXT: i32 0, label %[[CALL_0_LABEL:.*]]
+; CHECK-NEXT: i32 1, label %[[CALL_1_LABEL:.*]]
+; CHECK-NEXT: ]
+
+; CHECK: [[DEFAULT_LABEL]]
+; CHECK-NEXT: unreachable
+
+; CHECK: [[CALL_0_LABEL]]:
+; CHECK-NEXT: %1 = call i32 @func0()
+; CHECK-NEXT: br label %[[TAIL_LABEL:.*]]
+
+; CHECK: [[CALL_1_LABEL]]:
+; CHECK-NEXT: %2 = call i32 @func1()
+; CHECK-NEXT: br label %[[TAIL_LABEL]]
+
+; CHECK: [[TAIL_LABEL]]:
+; CHECK-NEXT: %3 = phi i32 [ %1, %[[CALL_0_LABEL]] ], [ %2, %[[CALL_1_LABEL]] ]
+; CHECK-NEXT: ret i32 %3
+
+; THRESHOLD-0-LABEL: define i32 @function_with_jump_table(i32 %index) {
+; THRESHOLD-0: %gep = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 %index
+; THRESHOLD-0-NEXT: %func_ptr = load ptr, ptr %gep
+; THRESHOLD-0-NEXT: %result = call i32 %func_ptr()
+; THRESHOLD-0-NEXT: ret i32 %result
+; THRESHOLD-0-NEXT: }
+
+define void @void_func0() {
+ ret void
+}
+
+define void @void_func1() {
+ ret void
+}
+
+ at void_func_array = global [2 x ptr] [ptr @void_func0, ptr @void_func1]
+
+define void @void_function_with_jump_table(i32 %index) {
+ %gep = getelementptr inbounds [2 x ptr], ptr @void_func_array, i32 0, i32 %index
+ %func_ptr = load ptr, ptr %gep
+ call void %func_ptr()
+ ret void
+}
+
+; CHECK-LABEL: define void @void_function_with_jump_table
+; CHECK: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @void_func_array, i32 0, i32 %index
+; CHECK: switch i32 %index, label %[[VOID_DEFAULT_LABEL:.*]] [
+; CHECK-NEXT: i32 0, label %[[VOID_CALL_0_LABEL:.*]]
+; CHECK-NEXT: i32 1, label %[[VOID_CALL_1_LABEL:.*]]
+; CHECK-NEXT: ]
+
+; CHECK: [[VOID_DEFAULT_LABEL]]
+; CHECK-NEXT: unreachable
+
+; CHECK: [[VOID_CALL_0_LABEL]]:
+; CHECK-NEXT: call void @void_func0()
+; CHECK-NEXT: br label %[[VOID_TAIL_LABEL:.*]]
+
+; CHECK: [[VOID_CALL_1_LABEL]]:
+; CHECK-NEXT: call void @void_func1()
+; CHECK-NEXT: br label %[[VOID_TAIL_LABEL]]
+
+; CHECK: [[VOID_TAIL_LABEL]]:
+; CHECK-NEXT: ret void
+
+; THRESHOLD-0-LABEL: define void @void_function_with_jump_table(i32 %index) {
+; THRESHOLD-0: %gep = getelementptr inbounds [2 x ptr], ptr @void_func_array, i32 0, i32 %index
+; THRESHOLD-0-NEXT: %func_ptr = load ptr, ptr %gep
+; THRESHOLD-0-NEXT: call void %func_ptr()
+; THRESHOLD-0-NEXT: ret void
+; THRESHOLD-0-NEXT: }
+
diff --git a/llvm/test/Transforms/JumpTableToSwitch/skip.ll b/llvm/test/Transforms/JumpTableToSwitch/skip.ll
new file mode 100644
index 00000000000000..f39bd6e7d37781
--- /dev/null
+++ b/llvm/test/Transforms/JumpTableToSwitch/skip.ll
@@ -0,0 +1,70 @@
+; RUN: opt < %s -passes=jump-table-to-switch -verify-dom-info -S | FileCheck %s
+
+ at func_array0 = global [2 x ptr] [ptr @func0, ptr @declared_only_func1]
+
+define i32 @func0() {
+ ret i32 1
+}
+
+declare i32 @declared_only_func1()
+
+define i32 @function_with_jump_table0(i32 %index) {
+ %gep = getelementptr inbounds [2 x ptr], ptr @func_array0, i32 0, i32 %index
+ %func_ptr = load ptr, ptr %gep, align 8
+ %result = call i32 %func_ptr()
+ ret i32 %result
+}
+
+; CHECK-LABEL: define i32 @function_with_jump_table0(i32 %index) {
+; CHECK: %gep = getelementptr inbounds [2 x ptr], ptr @func_array0, i32 0, i32 %index
+; CHECK-NEXT: %func_ptr = load ptr, ptr %gep, align 8
+; CHECK-NEXT: %result = call i32 %func_ptr()
+; CHECK-NEXT: ret i32 %result
+; CHECK-NEXT: }
+
+declare i32 @__gxx_personality_v0(...)
+
+define i32 @function_with_jump_table1(i32 %index) personality ptr @__gxx_personality_v0 {
+ %gep = getelementptr inbounds [2 x ptr], ptr @func_array0, i32 0, i32 %index
+ %func_ptr = load ptr, ptr %gep, align 8
+ %result = invoke i32 %func_ptr() to label %normal unwind label %exceptional
+normal:
+ ret i32 %result
+exceptional:
+ %landing_pad = landingpad { ptr, i32 } catch ptr null
+ resume { ptr, i32 } %landing_pad
+}
+
+; CHECK-LABEL: define i32 @function_with_jump_table1(i32 %index) personality ptr @__gxx_personality_v0 {
+; CHECK: %gep = getelementptr inbounds [2 x ptr], ptr @func_array0, i32 0, i32 %index
+; CHECK-NEXT: %func_ptr = load ptr, ptr %gep, align 8
+; CHECK-NEXT: %result = invoke i32 %func_ptr()
+; CHECK-NEXT: to label %normal unwind label %exceptional
+; CHECK: normal:
+; CHECK-NEXT: ret i32 %result
+; CHECK: exceptional:
+; CHECK-NEXT: %landing_pad = landingpad { ptr, i32 }
+; CHECK-NEXT: catch ptr null
+; CHECK: resume { ptr, i32 } %landing_pad
+; CHECK-NEXT: }
+
+ at func_array1 = global [1 x ptr] [ptr @func2]
+
+define i32 @func2(i32 %arg) {
+ ret i32 %arg
+}
+
+define i32 @function_with_jump_table2(i32 %index) {
+ %gep = getelementptr inbounds [1 x ptr], ptr @func_array1, i32 0, i32 %index
+ %func_ptr = load ptr, ptr %gep, align 8
+ %result = musttail call i32 %func_ptr(i32 %index)
+ ret i32 %result
+}
+
+; CHECK-LABEL: define i32 @function_with_jump_table2(i32 %index) {
+; CHECK: %gep = getelementptr inbounds [1 x ptr], ptr @func_array1, i32 0, i32 %index
+; CHECK-NEXT: %func_ptr = load ptr, ptr %gep, align 8
+; CHECK-NEXT: %result = musttail call i32 %func_ptr(i32 %index)
+; CHECK-NEXT: ret i32 %result
+; CHECK-NEXT: }
+
diff --git a/llvm/test/Transforms/JumpTableToSwitch/struct.ll b/llvm/test/Transforms/JumpTableToSwitch/struct.ll
new file mode 100644
index 00000000000000..0a5389f3014e86
--- /dev/null
+++ b/llvm/test/Transforms/JumpTableToSwitch/struct.ll
@@ -0,0 +1,42 @@
+; RUN: opt < %s -passes=jump-table-to-switch -verify-dom-info -S | FileCheck %s
+
+%"struct_ty" = type { [2 x ptr] }
+
+ at func_array = global %"struct_ty" { [2 x ptr] [ptr @func0, ptr @func1] }
+
+define i32 @func0() {
+ ret i32 1
+}
+
+define i32 @func1() {
+ ret i32 2
+}
+
+define i32 @function_with_jump_table(i32 %index) {
+ %gep = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 %index
+ %func_ptr = load ptr, ptr %gep
+ %result = call i32 %func_ptr()
+ ret i32 %result
+}
+
+; CHECK-LABEL: define i32 @function_with_jump_table
+; CHECK: [[GEP:%.*]] = getelementptr inbounds [2 x ptr], ptr @func_array, i32 0, i32 %index
+; CHECK: switch i32 %index, label %[[DEFAULT_LABEL:.*]] [
+; CHECK-NEXT: i32 0, label %[[CALL_0_LABEL:.*]]
+; CHECK-NEXT: i32 1, label %[[CALL_1_LABEL:.*]]
+; CHECK-NEXT: ]
+
+; CHECK: [[DEFAULT_LABEL]]
+; CHECK-NEXT: unreachable
+
+; CHECK: [[CALL_0_LABEL]]:
+; CHECK-NEXT: %1 = call i32 @func0()
+; CHECK-NEXT: br label %[[TAIL_LABEL:.*]]
+
+; CHECK: [[CALL_1_LABEL]]:
+; CHECK-NEXT: %2 = call i32 @func1()
+; CHECK-NEXT: br label %[[TAIL_LABEL]]
+
+; CHECK: [[TAIL_LABEL]]:
+; CHECK-NEXT: %3 = phi i32 [ %1, %[[CALL_0_LABEL]] ], [ %2, %[[CALL_1_LABEL]] ]
+; CHECK-NEXT: ret i32 %3
More information about the llvm-commits
mailing list