[Passes] Add relative lookup table converter pass

Lookup tables generate non PIC-friendly code, which requires dynamic relocation as described in:
https://bugs.llvm.org/show_bug.cgi?id=45244

This patch adds a new pass that converts lookup tables to relative lookup tables to make them PIC-friendly.

Differential Revision: https://reviews.llvm.org/D94355
This commit is contained in:
Gulfem Savrun Yeniceri 2020-12-29 21:32:13 +00:00
parent 482283042f
commit 5178ffc7cf
26 changed files with 743 additions and 16 deletions

View File

@ -973,6 +973,11 @@ corresponding to the reverse post order traversal of current function (starting
at 2), which effectively gives values in deep loops higher rank than values not at 2), which effectively gives values in deep loops higher rank than values not
in loops. in loops.
``-rel-lookup-table-converter``: Relative lookup table converter
----------------------------------------------------------------
This pass converts lookup tables to PIC-friendly relative lookup tables.
``-reg2mem``: Demote all values to stack slots ``-reg2mem``: Demote all values to stack slots
---------------------------------------------- ----------------------------------------------

View File

@ -720,6 +720,9 @@ public:
/// containing this constant value for the target. /// containing this constant value for the target.
bool shouldBuildLookupTablesForConstant(Constant *C) const; bool shouldBuildLookupTablesForConstant(Constant *C) const;
/// Return true if lookup tables should be turned into relative lookup tables.
bool shouldBuildRelLookupTables() const;
/// Return true if the input function which is cold at all call sites, /// Return true if the input function which is cold at all call sites,
/// should use coldcc calling convention. /// should use coldcc calling convention.
bool useColdCCForColdCall(Function &F) const; bool useColdCCForColdCall(Function &F) const;
@ -1483,6 +1486,7 @@ public:
virtual unsigned getRegUsageForType(Type *Ty) = 0; virtual unsigned getRegUsageForType(Type *Ty) = 0;
virtual bool shouldBuildLookupTables() = 0; virtual bool shouldBuildLookupTables() = 0;
virtual bool shouldBuildLookupTablesForConstant(Constant *C) = 0; virtual bool shouldBuildLookupTablesForConstant(Constant *C) = 0;
virtual bool shouldBuildRelLookupTables() = 0;
virtual bool useColdCCForColdCall(Function &F) = 0; virtual bool useColdCCForColdCall(Function &F) = 0;
virtual unsigned getScalarizationOverhead(VectorType *Ty, virtual unsigned getScalarizationOverhead(VectorType *Ty,
const APInt &DemandedElts, const APInt &DemandedElts,
@ -1869,6 +1873,9 @@ public:
bool shouldBuildLookupTablesForConstant(Constant *C) override { bool shouldBuildLookupTablesForConstant(Constant *C) override {
return Impl.shouldBuildLookupTablesForConstant(C); return Impl.shouldBuildLookupTablesForConstant(C);
} }
bool shouldBuildRelLookupTables() override {
return Impl.shouldBuildRelLookupTables();
}
bool useColdCCForColdCall(Function &F) override { bool useColdCCForColdCall(Function &F) override {
return Impl.useColdCCForColdCall(F); return Impl.useColdCCForColdCall(F);
} }

View File

@ -292,8 +292,11 @@ public:
unsigned getRegUsageForType(Type *Ty) const { return 1; } unsigned getRegUsageForType(Type *Ty) const { return 1; }
bool shouldBuildLookupTables() const { return true; } bool shouldBuildLookupTables() const { return true; }
bool shouldBuildLookupTablesForConstant(Constant *C) const { return true; } bool shouldBuildLookupTablesForConstant(Constant *C) const { return true; }
bool shouldBuildRelLookupTables() const { return false; }
bool useColdCCForColdCall(Function &F) const { return false; } bool useColdCCForColdCall(Function &F) const { return false; }
unsigned getScalarizationOverhead(VectorType *Ty, const APInt &DemandedElts, unsigned getScalarizationOverhead(VectorType *Ty, const APInt &DemandedElts,

View File

@ -45,6 +45,7 @@
#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MachineValueType.h" #include "llvm/Support/MachineValueType.h"
#include "llvm/Support/MathExtras.h" #include "llvm/Support/MathExtras.h"
#include "llvm/Target/TargetMachine.h"
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
@ -379,6 +380,25 @@ public:
TLI->isOperationLegalOrCustom(ISD::BRIND, MVT::Other); TLI->isOperationLegalOrCustom(ISD::BRIND, MVT::Other);
} }
bool shouldBuildRelLookupTables() {
const TargetMachine &TM = getTLI()->getTargetMachine();
// If non-PIC mode, do not generate a relative lookup table.
if (!TM.isPositionIndependent())
return false;
if (!TM.getTargetTriple().isArch64Bit())
return false;
/// Relative lookup table entries consist of 32-bit offsets.
/// Do not generate relative lookup tables for large code models
/// in 64-bit achitectures where 32-bit offsets might not be enough.
if (TM.getCodeModel() == CodeModel::Medium ||
TM.getCodeModel() == CodeModel::Large)
return false;
return true;
}
bool haveFastSqrt(Type *Ty) { bool haveFastSqrt(Type *Ty) {
const TargetLoweringBase *TLI = getTLI(); const TargetLoweringBase *TLI = getTLI();
EVT VT = TLI->getValueType(DL, Ty); EVT VT = TLI->getValueType(DL, Ty);

View File

@ -318,6 +318,7 @@ void initializeModuloScheduleTestPass(PassRegistry&);
void initializeMustExecutePrinterPass(PassRegistry&); void initializeMustExecutePrinterPass(PassRegistry&);
void initializeMustBeExecutedContextPrinterPass(PassRegistry&); void initializeMustBeExecutedContextPrinterPass(PassRegistry&);
void initializeNameAnonGlobalLegacyPassPass(PassRegistry&); void initializeNameAnonGlobalLegacyPassPass(PassRegistry&);
void initializeRelLookupTableConverterLegacyPassPass(PassRegistry &);
void initializeNaryReassociateLegacyPassPass(PassRegistry&); void initializeNaryReassociateLegacyPassPass(PassRegistry&);
void initializeNewGVNLegacyPassPass(PassRegistry&); void initializeNewGVNLegacyPassPass(PassRegistry&);
void initializeObjCARCAAWrapperPassPass(PassRegistry&); void initializeObjCARCAAWrapperPassPass(PassRegistry&);

View File

@ -517,6 +517,7 @@ FunctionPass *createLoopDataPrefetchPass();
///===---------------------------------------------------------------------===// ///===---------------------------------------------------------------------===//
ModulePass *createNameAnonGlobalPass(); ModulePass *createNameAnonGlobalPass();
ModulePass *createRelLookupTableConverterPass();
ModulePass *createCanonicalizeAliasesPass(); ModulePass *createCanonicalizeAliasesPass();
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -0,0 +1,70 @@
//===-- RelLookupTableConverterPass.h - Rel Table Conv ----------*- 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
//
//===----------------------------------------------------------------------===//
//
/// \file
/// This file implements relative lookup table converter that converts
/// lookup tables to relative lookup tables to make them PIC-friendly.
///
/// Switch lookup table example:
/// @switch.table.foo = private unnamed_addr constant [3 x i8*]
/// [
/// i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0),
/// i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i64 0, i64 0),
/// i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0)
/// ], align 8
///
/// switch.lookup:
/// %1 = sext i32 %cond to i64
/// %switch.gep = getelementptr inbounds [3 x i8*],
/// [3 x i8*]* @switch.table.foo, i64 0, i64 %1
/// %switch.load = load i8*, i8** %switch.gep, align 8
/// ret i8* %switch.load
///
/// Switch lookup table will become a relative lookup table that
/// consists of relative offsets.
///
/// @reltable.foo = private unnamed_addr constant [3 x i32]
/// [
/// i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str to i64),
/// i64 ptrtoint ([3 x i32]* @reltable.foo to i64)) to i32),
/// i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.1 to i64),
/// i64 ptrtoint ([3 x i32]* @reltable.foo to i64)) to i32),
/// i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.2 to i64),
/// i64 ptrtoint ([3 x i32]* @reltable.foo to i64)) to i32)
/// ], align 4
///
/// IR after converting to a relative lookup table:
/// switch.lookup:
/// %1 = sext i32 %cond to i64
/// %reltable.shift = shl i64 %1, 2
/// %reltable.intrinsic = call i8* @llvm.load.relative.i64(
/// i8* bitcast ([3 x i32]* @reltable.foo to i8*),
/// i64 %reltable.shift)
/// ret i8* %reltable.intrinsic
//===----------------------------------------------------------------------===//
#ifndef LLVM_TRANSFORMS_UTILS_RELLOOKUPTABLECONVERTER_H
#define LLVM_TRANSFORMS_UTILS_RELLOOKUPTABLECONVERTER_H
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
namespace llvm {
// Pass that converts lookup tables to relative lookup tables.
class RelLookupTableConverterPass
: public PassInfoMixin<RelLookupTableConverterPass> {
public:
RelLookupTableConverterPass() = default;
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
};
} // end namespace llvm
#endif // LLVM_TRANSFORMS_UTILS_RELLOOKUPTABLECONVERTER_H

View File

@ -456,11 +456,16 @@ unsigned TargetTransformInfo::getRegUsageForType(Type *Ty) const {
bool TargetTransformInfo::shouldBuildLookupTables() const { bool TargetTransformInfo::shouldBuildLookupTables() const {
return TTIImpl->shouldBuildLookupTables(); return TTIImpl->shouldBuildLookupTables();
} }
bool TargetTransformInfo::shouldBuildLookupTablesForConstant( bool TargetTransformInfo::shouldBuildLookupTablesForConstant(
Constant *C) const { Constant *C) const {
return TTIImpl->shouldBuildLookupTablesForConstant(C); return TTIImpl->shouldBuildLookupTablesForConstant(C);
} }
bool TargetTransformInfo::shouldBuildRelLookupTables() const {
return TTIImpl->shouldBuildRelLookupTables();
}
bool TargetTransformInfo::useColdCCForColdCall(Function &F) const { bool TargetTransformInfo::useColdCCForColdCall(Function &F) const {
return TTIImpl->useColdCCForColdCall(F); return TTIImpl->useColdCCForColdCall(F);
} }

View File

@ -227,6 +227,7 @@
#include "llvm/Transforms/Utils/Mem2Reg.h" #include "llvm/Transforms/Utils/Mem2Reg.h"
#include "llvm/Transforms/Utils/MetaRenamer.h" #include "llvm/Transforms/Utils/MetaRenamer.h"
#include "llvm/Transforms/Utils/NameAnonGlobals.h" #include "llvm/Transforms/Utils/NameAnonGlobals.h"
#include "llvm/Transforms/Utils/RelLookupTableConverter.h"
#include "llvm/Transforms/Utils/StripGCRelocates.h" #include "llvm/Transforms/Utils/StripGCRelocates.h"
#include "llvm/Transforms/Utils/StripNonLineTableDebugInfo.h" #include "llvm/Transforms/Utils/StripNonLineTableDebugInfo.h"
#include "llvm/Transforms/Utils/SymbolRewriter.h" #include "llvm/Transforms/Utils/SymbolRewriter.h"
@ -1408,6 +1409,8 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
MPM.addPass(GlobalDCEPass()); MPM.addPass(GlobalDCEPass());
MPM.addPass(ConstantMergePass()); MPM.addPass(ConstantMergePass());
MPM.addPass(RelLookupTableConverterPass());
return MPM; return MPM;
} }

View File

@ -93,6 +93,7 @@ MODULE_PASS("print-lcg-dot", LazyCallGraphDOTPrinterPass(dbgs()))
MODULE_PASS("print-must-be-executed-contexts", MustBeExecutedContextPrinterPass(dbgs())) MODULE_PASS("print-must-be-executed-contexts", MustBeExecutedContextPrinterPass(dbgs()))
MODULE_PASS("print-stack-safety", StackSafetyGlobalPrinterPass(dbgs())) MODULE_PASS("print-stack-safety", StackSafetyGlobalPrinterPass(dbgs()))
MODULE_PASS("print<module-debuginfo>", ModuleDebugInfoPrinterPass(dbgs())) MODULE_PASS("print<module-debuginfo>", ModuleDebugInfoPrinterPass(dbgs()))
MODULE_PASS("rel-lookup-table-converter", RelLookupTableConverterPass())
MODULE_PASS("rewrite-statepoints-for-gc", RewriteStatepointsForGC()) MODULE_PASS("rewrite-statepoints-for-gc", RewriteStatepointsForGC())
MODULE_PASS("rewrite-symbols", RewriteSymbolPass()) MODULE_PASS("rewrite-symbols", RewriteSymbolPass())
MODULE_PASS("rpo-function-attrs", ReversePostOrderFunctionAttrsPass()) MODULE_PASS("rpo-function-attrs", ReversePostOrderFunctionAttrsPass())

View File

@ -909,6 +909,8 @@ void PassManagerBuilder::populateModulePassManager(
// resulted in single-entry-single-exit or empty blocks. Clean up the CFG. // resulted in single-entry-single-exit or empty blocks. Clean up the CFG.
MPM.add(createCFGSimplificationPass()); MPM.add(createCFGSimplificationPass());
MPM.add(createRelLookupTableConverterPass());
addExtensionsToPM(EP_OptimizerLast, MPM); addExtensionsToPM(EP_OptimizerLast, MPM);
if (PrepareForLTO) { if (PrepareForLTO) {

View File

@ -54,6 +54,7 @@ add_llvm_component_library(LLVMTransformUtils
NameAnonGlobals.cpp NameAnonGlobals.cpp
PredicateInfo.cpp PredicateInfo.cpp
PromoteMemoryToRegister.cpp PromoteMemoryToRegister.cpp
RelLookupTableConverter.cpp
ScalarEvolutionExpander.cpp ScalarEvolutionExpander.cpp
StripGCRelocates.cpp StripGCRelocates.cpp
SSAUpdater.cpp SSAUpdater.cpp

View File

@ -0,0 +1,257 @@
//===- RelLookupTableConverterPass - Rel Table Conv -----------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file implements relative lookup table converter that converts
// lookup tables to relative lookup tables to make them PIC-friendly.
//
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Utils/RelLookupTableConverter.h"
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
using namespace llvm;
static bool shouldConvertToRelLookupTable(Module &M, GlobalVariable &GV) {
if (!GV.hasInitializer())
return false;
// If lookup table has more than one user,
// do not generate a relative lookup table.
// This is to simplify the analysis that needs to be done for this pass.
// TODO: Add support for lookup tables with multiple uses.
// For ex, this can happen when a function that uses a lookup table gets
// inlined into multiple call sites.
if (!GV.hasOneUse())
return false;
GetElementPtrInst *GEP =
dyn_cast<GetElementPtrInst>(GV.use_begin()->getUser());
if (!GEP || !GEP->hasOneUse())
return false;
if (!isa<LoadInst>(GEP->use_begin()->getUser()))
return false;
// If the original lookup table does not have local linkage and is
// not dso_local, do not generate a relative lookup table.
// This optimization creates a relative lookup table that consists of
// offsets between the start of the lookup table and its elements.
// To be able to generate these offsets, relative lookup table and
// its elements should have internal linkage and be dso_local, which means
// that they should resolve to symbols within the same linkage unit.
if (!GV.hasLocalLinkage() ||
!GV.isDSOLocal() ||
!GV.isImplicitDSOLocal())
return false;
ConstantArray *Array = dyn_cast<ConstantArray>(GV.getInitializer());
// If values are not pointers, do not generate a relative lookup table.
if (!Array || !Array->getType()->getElementType()->isPointerTy())
return false;
const DataLayout &DL = M.getDataLayout();
for (const Use &Op : Array->operands()) {
Constant *ConstOp = cast<Constant>(&Op);
GlobalValue *GVOp;
APInt Offset;
// If an operand is not a constant offset from a lookup table,
// do not generate a relative lookup table.
if (!IsConstantOffsetFromGlobal(ConstOp, GVOp, Offset, DL))
return false;
if (!GVOp->hasLocalLinkage() ||
!GVOp->isDSOLocal() ||
!GVOp->isImplicitDSOLocal())
return false;
}
return true;
}
static GlobalVariable *createRelLookupTable(Function &Func,
GlobalVariable &LookupTable) {
Module &M = *Func.getParent();
ConstantArray *LookupTableArr =
cast<ConstantArray>(LookupTable.getInitializer());
unsigned NumElts = LookupTableArr->getType()->getNumElements();
ArrayType *IntArrayTy =
ArrayType::get(Type::getInt32Ty(M.getContext()), NumElts);
GlobalVariable *RelLookupTable = new GlobalVariable(
M, IntArrayTy, LookupTable.isConstant(), LookupTable.getLinkage(),
nullptr, "reltable." + Func.getName(), &LookupTable,
LookupTable.getThreadLocalMode(), LookupTable.getAddressSpace(),
LookupTable.isExternallyInitialized());
uint64_t Idx = 0;
SmallVector<Constant *, 64> RelLookupTableContents(NumElts);
for (Use &Operand : LookupTableArr->operands()) {
Constant *Element = cast<Constant>(Operand);
Type *IntPtrTy = M.getDataLayout().getIntPtrType(M.getContext());
Constant *Base = llvm::ConstantExpr::getPtrToInt(RelLookupTable, IntPtrTy);
Constant *Target = llvm::ConstantExpr::getPtrToInt(Element, IntPtrTy);
Constant *Sub = llvm::ConstantExpr::getSub(Target, Base);
Constant *RelOffset =
llvm::ConstantExpr::getTrunc(Sub, Type::getInt32Ty(M.getContext()));
RelLookupTableContents[Idx++] = RelOffset;
}
Constant *Initializer =
ConstantArray::get(IntArrayTy, RelLookupTableContents);
RelLookupTable->setInitializer(Initializer);
RelLookupTable->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
RelLookupTable->setAlignment(llvm::Align(4));
return RelLookupTable;
}
static void convertToRelLookupTable(GlobalVariable &LookupTable) {
GetElementPtrInst *GEP =
cast<GetElementPtrInst>(LookupTable.use_begin()->getUser());
LoadInst *Load = cast<LoadInst>(GEP->use_begin()->getUser());
Module &M = *LookupTable.getParent();
BasicBlock *BB = GEP->getParent();
IRBuilder<> Builder(BB);
Function &Func = *BB->getParent();
// Generate an array that consists of relative offsets.
GlobalVariable *RelLookupTable = createRelLookupTable(Func, LookupTable);
// Place new instruction sequence after GEP.
Builder.SetInsertPoint(GEP);
Value *Index = GEP->getOperand(2);
IntegerType *IntTy = cast<IntegerType>(Index->getType());
Value *Offset =
Builder.CreateShl(Index, ConstantInt::get(IntTy, 2), "reltable.shift");
Function *LoadRelIntrinsic = llvm::Intrinsic::getDeclaration(
&M, Intrinsic::load_relative, {Index->getType()});
Value *Base = Builder.CreateBitCast(RelLookupTable, Builder.getInt8PtrTy());
// Create a call to load.relative intrinsic that computes the target address
// by adding base address (lookup table address) and relative offset.
Value *Result = Builder.CreateCall(LoadRelIntrinsic, {Base, Offset},
"reltable.intrinsic");
// Create a bitcast instruction if necessary.
if (Load->getType() != Builder.getInt8PtrTy())
Result = Builder.CreateBitCast(Result, Load->getType(), "reltable.bitcast");
// Replace load instruction with the new generated instruction sequence.
BasicBlock::iterator InsertPoint(Load);
ReplaceInstWithValue(Load->getParent()->getInstList(), InsertPoint, Result);
// Remove GEP instruction.
GEP->eraseFromParent();
}
// Convert lookup tables to relative lookup tables in the module.
static bool convertToRelativeLookupTables(
Module &M, function_ref<TargetTransformInfo &(Function &)> GetTTI) {
Module::iterator FI = M.begin();
if (FI == M.end())
return false;
// Check if we have a target that supports relative lookup tables.
if (!GetTTI(*FI).shouldBuildRelLookupTables())
return false;
bool Changed = false;
for (auto GVI = M.global_begin(), E = M.global_end(); GVI != E;) {
GlobalVariable &GV = *GVI++;
if (!shouldConvertToRelLookupTable(M, GV))
continue;
convertToRelLookupTable(GV);
// Remove the original lookup table.
GV.eraseFromParent();
Changed = true;
}
return Changed;
}
PreservedAnalyses RelLookupTableConverterPass::run(Module &M,
ModuleAnalysisManager &AM) {
FunctionAnalysisManager &FAM =
AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
auto GetTTI = [&](Function &F) -> TargetTransformInfo & {
return FAM.getResult<TargetIRAnalysis>(F);
};
if (!convertToRelativeLookupTables(M, GetTTI))
return PreservedAnalyses::all();
PreservedAnalyses PA;
PA.preserveSet<CFGAnalyses>();
return PA;
}
namespace {
/// Pass that converts lookup tables to relative lookup tables.
class RelLookupTableConverterLegacyPass : public ModulePass {
public:
/// Pass identification, replacement for typeid
static char ID;
/// Specify pass name for debug output
StringRef getPassName() const override {
return "Relative Lookup Table Converter";
}
RelLookupTableConverterLegacyPass() : ModulePass(ID) {
initializeRelLookupTableConverterLegacyPassPass(
*PassRegistry::getPassRegistry());
}
bool runOnModule(Module &M) override {
auto GetTTI = [this](Function &F) -> TargetTransformInfo & {
return this->getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
};
return convertToRelativeLookupTables(M, GetTTI);
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<TargetTransformInfoWrapperPass>();
}
};
} // anonymous namespace
char RelLookupTableConverterLegacyPass::ID = 0;
INITIALIZE_PASS_BEGIN(RelLookupTableConverterLegacyPass,
"rel-lookup-table-converter",
"Convert to relative lookup tables", false, false)
INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
INITIALIZE_PASS_END(RelLookupTableConverterLegacyPass,
"rel-lookup-table-converter",
"Convert to relative lookup tables", false, false)
namespace llvm {
ModulePass *createRelLookupTableConverterPass() {
return new RelLookupTableConverterLegacyPass();
}
} // end namespace llvm

View File

@ -37,6 +37,7 @@ void llvm::initializeTransformUtils(PassRegistry &Registry) {
initializeLowerSwitchLegacyPassPass(Registry); initializeLowerSwitchLegacyPassPass(Registry);
initializeNameAnonGlobalLegacyPassPass(Registry); initializeNameAnonGlobalLegacyPassPass(Registry);
initializePromoteLegacyPassPass(Registry); initializePromoteLegacyPassPass(Registry);
initializeRelLookupTableConverterLegacyPassPass(Registry);
initializeStripNonLineTableDebugLegacyPassPass(Registry); initializeStripNonLineTableDebugLegacyPassPass(Registry);
initializeUnifyFunctionExitNodesLegacyPassPass(Registry); initializeUnifyFunctionExitNodesLegacyPassPass(Registry);
initializeMetaRenamerPass(Registry); initializeMetaRenamerPass(Registry);

View File

@ -306,6 +306,8 @@
; GCN-O1-NEXT: Remove redundant instructions ; GCN-O1-NEXT: Remove redundant instructions
; GCN-O1-NEXT: Hoist/decompose integer division and remainder ; GCN-O1-NEXT: Hoist/decompose integer division and remainder
; GCN-O1-NEXT: Simplify the CFG ; GCN-O1-NEXT: Simplify the CFG
; GCN-O1-NEXT: Relative Lookup Table Converter
; GCN-O1-NEXT: FunctionPass Manager
; GCN-O1-NEXT: Annotation Remarks ; GCN-O1-NEXT: Annotation Remarks
; GCN-O1-NEXT: Pass Arguments: ; GCN-O1-NEXT: Pass Arguments:
@ -660,6 +662,8 @@
; GCN-O2-NEXT: Remove redundant instructions ; GCN-O2-NEXT: Remove redundant instructions
; GCN-O2-NEXT: Hoist/decompose integer division and remainder ; GCN-O2-NEXT: Hoist/decompose integer division and remainder
; GCN-O2-NEXT: Simplify the CFG ; GCN-O2-NEXT: Simplify the CFG
; GCN-O2-NEXT: Relative Lookup Table Converter
; GCN-O2-NEXT: FunctionPass Manager
; GCN-O2-NEXT: Annotation Remarks ; GCN-O2-NEXT: Annotation Remarks
; GCN-O2-NEXT: Pass Arguments: ; GCN-O2-NEXT: Pass Arguments:
@ -1019,6 +1023,8 @@
; GCN-O3-NEXT: Remove redundant instructions ; GCN-O3-NEXT: Remove redundant instructions
; GCN-O3-NEXT: Hoist/decompose integer division and remainder ; GCN-O3-NEXT: Hoist/decompose integer division and remainder
; GCN-O3-NEXT: Simplify the CFG ; GCN-O3-NEXT: Simplify the CFG
; GCN-O3-NEXT: Relative Lookup Table Converter
; GCN-O3-NEXT: FunctionPass Manager
; GCN-O3-NEXT: Annotation Remarks ; GCN-O3-NEXT: Annotation Remarks
; GCN-O3-NEXT: Pass Arguments: ; GCN-O3-NEXT: Pass Arguments:

View File

@ -256,6 +256,8 @@
; CHECK-O-NEXT: Running pass: CGProfilePass ; CHECK-O-NEXT: Running pass: CGProfilePass
; CHECK-O-NEXT: Running pass: GlobalDCEPass ; CHECK-O-NEXT: Running pass: GlobalDCEPass
; CHECK-O-NEXT: Running pass: ConstantMergePass ; CHECK-O-NEXT: Running pass: ConstantMergePass
; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass
; CHECK-O-NEXT: Running analysis: TargetIRAnalysis
; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
; CHECK-LTO-NEXT: Running pass: CanonicalizeAliasesPass ; CHECK-LTO-NEXT: Running pass: CanonicalizeAliasesPass
; CHECK-LTO-NEXT: Running pass: NameAnonGlobalPass ; CHECK-LTO-NEXT: Running pass: NameAnonGlobalPass

View File

@ -243,6 +243,8 @@
; CHECK-POSTLINK-O-NEXT: Running pass: CGProfilePass ; CHECK-POSTLINK-O-NEXT: Running pass: CGProfilePass
; CHECK-POSTLINK-O-NEXT: Running pass: GlobalDCEPass ; CHECK-POSTLINK-O-NEXT: Running pass: GlobalDCEPass
; CHECK-POSTLINK-O-NEXT: Running pass: ConstantMergePass ; CHECK-POSTLINK-O-NEXT: Running pass: ConstantMergePass
; CHECK-POSTLINK-O-NEXT: Running pass: RelLookupTableConverterPass
; CHECK-POSTLINK-O-NEXT: Running analysis: TargetIRAnalysis
; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
; CHECK-PRELINK-O-NEXT: Running pass: CanonicalizeAliasesPass ; CHECK-PRELINK-O-NEXT: Running pass: CanonicalizeAliasesPass
; CHECK-PRELINK-O-NEXT: Running pass: NameAnonGlobalPass ; CHECK-PRELINK-O-NEXT: Running pass: NameAnonGlobalPass

View File

@ -212,6 +212,8 @@
; CHECK-O-NEXT: Running pass: CGProfilePass ; CHECK-O-NEXT: Running pass: CGProfilePass
; CHECK-O-NEXT: Running pass: GlobalDCEPass ; CHECK-O-NEXT: Running pass: GlobalDCEPass
; CHECK-O-NEXT: Running pass: ConstantMergePass ; CHECK-O-NEXT: Running pass: ConstantMergePass
; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass
; CHECK-O-NEXT: Running analysis: TargetIRAnalysis
; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
; CHECK-O-NEXT: Running pass: PrintModulePass ; CHECK-O-NEXT: Running pass: PrintModulePass

View File

@ -224,6 +224,8 @@
; CHECK-O-NEXT: Running pass: CGProfilePass ; CHECK-O-NEXT: Running pass: CGProfilePass
; CHECK-O-NEXT: Running pass: GlobalDCEPass ; CHECK-O-NEXT: Running pass: GlobalDCEPass
; CHECK-O-NEXT: Running pass: ConstantMergePass ; CHECK-O-NEXT: Running pass: ConstantMergePass
; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass
; CHECK-O-NEXT: Running analysis: TargetIRAnalysis
; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
; CHECK-O-NEXT: Running pass: PrintModulePass ; CHECK-O-NEXT: Running pass: PrintModulePass

View File

@ -307,6 +307,8 @@
; CHECK-NEXT: Remove redundant instructions ; CHECK-NEXT: Remove redundant instructions
; CHECK-NEXT: Hoist/decompose integer division and remainder ; CHECK-NEXT: Hoist/decompose integer division and remainder
; CHECK-NEXT: Simplify the CFG ; CHECK-NEXT: Simplify the CFG
; CHECK-NEXT: Relative Lookup Table Converter
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Annotation Remarks ; CHECK-NEXT: Annotation Remarks
; CHECK-NEXT: Module Verifier ; CHECK-NEXT: Module Verifier
; CHECK-NEXT: Bitcode Writer ; CHECK-NEXT: Bitcode Writer

View File

@ -319,6 +319,8 @@
; CHECK-NEXT: Remove redundant instructions ; CHECK-NEXT: Remove redundant instructions
; CHECK-NEXT: Hoist/decompose integer division and remainder ; CHECK-NEXT: Hoist/decompose integer division and remainder
; CHECK-NEXT: Simplify the CFG ; CHECK-NEXT: Simplify the CFG
; CHECK-NEXT: Relative Lookup Table Converter
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Annotation Remarks ; CHECK-NEXT: Annotation Remarks
; CHECK-NEXT: Module Verifier ; CHECK-NEXT: Module Verifier
; CHECK-NEXT: Bitcode Writer ; CHECK-NEXT: Bitcode Writer

View File

@ -312,6 +312,8 @@
; CHECK-NEXT: Remove redundant instructions ; CHECK-NEXT: Remove redundant instructions
; CHECK-NEXT: Hoist/decompose integer division and remainder ; CHECK-NEXT: Hoist/decompose integer division and remainder
; CHECK-NEXT: Simplify the CFG ; CHECK-NEXT: Simplify the CFG
; CHECK-NEXT: Relative Lookup Table Converter
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Annotation Remarks ; CHECK-NEXT: Annotation Remarks
; CHECK-NEXT: Module Verifier ; CHECK-NEXT: Module Verifier
; CHECK-NEXT: Bitcode Writer ; CHECK-NEXT: Bitcode Writer

View File

@ -293,6 +293,8 @@
; CHECK-NEXT: Remove redundant instructions ; CHECK-NEXT: Remove redundant instructions
; CHECK-NEXT: Hoist/decompose integer division and remainder ; CHECK-NEXT: Hoist/decompose integer division and remainder
; CHECK-NEXT: Simplify the CFG ; CHECK-NEXT: Simplify the CFG
; CHECK-NEXT: Relative Lookup Table Converter
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Annotation Remarks ; CHECK-NEXT: Annotation Remarks
; CHECK-NEXT: Module Verifier ; CHECK-NEXT: Module Verifier
; CHECK-NEXT: Bitcode Writer ; CHECK-NEXT: Bitcode Writer

View File

@ -106,6 +106,8 @@
; CHECK-O2: Loop Pass Manager ; CHECK-O2: Loop Pass Manager
; CHECK-O2-NEXT: Loop Sink ; CHECK-O2-NEXT: Loop Sink
; CHECK-O2: Simplify the CFG ; CHECK-O2: Simplify the CFG
; CHECK-O2: Relative Lookup Table Converter
; CHECK-O2: FunctionPass Manager
; CHECK-O2-NOT: Manager ; CHECK-O2-NOT: Manager
; ;
; FIXME: There really shouldn't be another pass manager, especially one that ; FIXME: There really shouldn't be another pass manager, especially one that

View File

@ -0,0 +1,57 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -rel-lookup-table-converter -mtriple=x86_64-linux -S | FileCheck %s
; RUN: opt < %s -rel-lookup-table-converter -mtriple=i386-unknown-unknown -relocation-model=pic -S | FileCheck %s
; RUN: opt < %s -rel-lookup-table-converter -mtriple=x86_64-linux -relocation-model=pic -code-model=medium -S | FileCheck %s
; RUN: opt < %s -rel-lookup-table-converter -mtriple=x86_64-linux -relocation-model=pic -code-model=large -S | FileCheck %s
; RUN: opt < %s -passes=rel-lookup-table-converter -mtriple=x86_64-linux -S | FileCheck %s
; RUN: opt < %s -passes=rel-lookup-table-converter -mtriple=i386-unknown-unknown -relocation-model=pic -S | FileCheck %s
; RUN: opt < %s -passes=rel-lookup-table-converter -mtriple=x86_64-linux -relocation-model=pic -code-model=medium -S | FileCheck %s
; RUN: opt < %s -passes=rel-lookup-table-converter -mtriple=x86_64-linux -relocation-model=pic -code-model=large -S | FileCheck %s
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
@.str = private unnamed_addr constant [5 x i8] c"zero\00", align 1
@.str.1 = private unnamed_addr constant [4 x i8] c"one\00", align 1
@.str.2 = private unnamed_addr constant [4 x i8] c"two\00", align 1
@.str.3 = private unnamed_addr constant [8 x i8] c"default\00", align 1
@switch.table.string_table = private unnamed_addr constant [3 x i8*]
[
i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0),
i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i64 0, i64 0),
i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0)
], align 8
; Switch lookup table
; CHECK: @switch.table.string_table = private unnamed_addr constant [3 x i8*]
; CHECK-SAME: [
; CHECK-SAME: i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0),
; CHECK-SAME: i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i64 0, i64 0),
; CHECK-SAME: i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0)
; CHECK-SAME: ], align 8
; ; Relative switch lookup table for strings
define i8* @string_table(i32 %cond) {
; CHECK-LABEL: @string_table(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[COND:%.*]], 3
; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]]
; CHECK: switch.lookup:
; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [3 x i8*], [3 x i8*]* @switch.table.string_table, i32 0, i32 [[COND]]
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i8*, i8** [[SWITCH_GEP]], align 8
; CHECK-NEXT: ret i8* [[SWITCH_LOAD]]
; CHECK: return:
; CHECK-NEXT: ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0)
entry:
%0 = icmp ult i32 %cond, 3
br i1 %0, label %switch.lookup, label %return
switch.lookup: ; preds = %entry
%switch.gep = getelementptr inbounds [3 x i8*], [3 x i8*]* @switch.table.string_table, i32 0, i32 %cond
%switch.load = load i8*, i8** %switch.gep, align 8
ret i8* %switch.load
return: ; preds = %entry
ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0)
}

View File

@ -0,0 +1,269 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; REQUIRES: x86-registered-target
; RUN: opt < %s -rel-lookup-table-converter -relocation-model=pic -S | FileCheck %s
; RUN: opt < %s -passes=rel-lookup-table-converter -relocation-model=pic -S | FileCheck %s
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@.str = private unnamed_addr constant [5 x i8] c"zero\00", align 1
@.str.1 = private unnamed_addr constant [4 x i8] c"one\00", align 1
@.str.2 = private unnamed_addr constant [4 x i8] c"two\00", align 1
@.str.3 = private unnamed_addr constant [8 x i8] c"default\00", align 1
@.str.4 = private unnamed_addr constant [6 x i8] c"three\00", align 1
@.str.5 = private unnamed_addr constant [5 x i8] c"str1\00", align 1
@.str.6 = private unnamed_addr constant [5 x i8] c"str2\00", align 1
@.str.7 = private unnamed_addr constant [12 x i8] c"singlevalue\00", align 1
@a1 = external global i32, align 4
@b1 = external global i32, align 4
@c1 = external global i32, align 4
@d1 = external global i32, align 4
@a2 = internal global i32 0, align 4
@b2 = internal global i32 0, align 4
@c2 = internal global i32 0, align 4
@d2 = internal global i32 0, align 4
@switch.table.external_linkage = private unnamed_addr constant [3 x i32*] [i32* @a1, i32* @b1, i32* @c1], align 8
@switch.table.internal_linkage = private unnamed_addr constant [3 x i32*] [i32* @a2, i32* @b2, i32* @c2], align 8
@switch.table.string_table = private unnamed_addr constant [3 x i8*]
[
i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0),
i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i64 0, i64 0),
i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0)
], align 8
@switch.table.string_table_holes = private unnamed_addr constant [4 x i8*]
[
i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0),
i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0),
i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0),
i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.4, i64 0, i64 0)
], align 8
@switch.table.single_value = private unnamed_addr constant [3 x i8*]
[
i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0),
i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i64 0, i64 0),
i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0)
], align 8
@user_defined_lookup_table.table = internal unnamed_addr constant [3 x i8*]
[
i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i32 0, i32 0),
i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0),
i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i32 0, i32 0)
], align 16
; Lookup table check for integer pointers that have external linkage
; CHECK: @switch.table.external_linkage = private unnamed_addr constant [3 x i32*] [i32* @a1, i32* @b1, i32* @c1], align
; Lookup table check for integer pointers that have internal linkage
; CHECK: @reltable.internal_linkage = private unnamed_addr constant [3 x i32]
; CHECK-SAME: [
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint (i32* @a2 to i64), i64 ptrtoint ([3 x i32]* @reltable.internal_linkage to i64)) to i32),
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint (i32* @b2 to i64), i64 ptrtoint ([3 x i32]* @reltable.internal_linkage to i64)) to i32),
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint (i32* @c2 to i64), i64 ptrtoint ([3 x i32]* @reltable.internal_linkage to i64)) to i32)
; CHECK-SAME: ], align 4
; Relative switch lookup table for strings
; CHECK: @reltable.string_table = private unnamed_addr constant [3 x i32]
; CHECK-SAME: [
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str to i64), i64 ptrtoint ([3 x i32]* @reltable.string_table to i64)) to i32),
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.1 to i64), i64 ptrtoint ([3 x i32]* @reltable.string_table to i64)) to i32),
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.2 to i64), i64 ptrtoint ([3 x i32]* @reltable.string_table to i64)) to i32)
; CHECK-SAME: ], align 4
; Relative switch lookup table for strings with holes, where holes are filled with relative offset to default values
; CHECK: @reltable.string_table_holes = private unnamed_addr constant [4 x i32]
; CHECK-SAME: [
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str to i64), i64 ptrtoint ([4 x i32]* @reltable.string_table_holes to i64)) to i32),
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([8 x i8]* @.str.3 to i64), i64 ptrtoint ([4 x i32]* @reltable.string_table_holes to i64)) to i32),
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.2 to i64), i64 ptrtoint ([4 x i32]* @reltable.string_table_holes to i64)) to i32),
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([6 x i8]* @.str.4 to i64), i64 ptrtoint ([4 x i32]* @reltable.string_table_holes to i64)) to i32)
; CHECK-SAME: ], align 4
; Single value check
; CHECK: @reltable.single_value = private unnamed_addr constant [3 x i32]
; CHECK-SAME: [
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str to i64), i64 ptrtoint ([3 x i32]* @reltable.single_value to i64)) to i32),
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.1 to i64), i64 ptrtoint ([3 x i32]* @reltable.single_value to i64)) to i32),
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.2 to i64), i64 ptrtoint ([3 x i32]* @reltable.single_value to i64)) to i32)
; CHECK-SAME: ], align 4
;
; Lookup table check for integer pointers that have external linkage
define i32* @external_linkage(i32 %cond) {
; CHECK-LABEL: @external_linkage(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[COND:%.*]], 3
; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]]
; CHECK: switch.lookup:
; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [3 x i32*], [3 x i32*]* @switch.table.external_linkage, i32 0, i32 [[COND:%.*]]
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32*, i32** [[SWITCH_GEP]], align 8
; CHECK-NEXT: ret i32* [[SWITCH_LOAD]]
; CHECK: return:
; CHECK-NEXT: ret i32* @d1
;
entry:
%0 = icmp ult i32 %cond, 3
br i1 %0, label %switch.lookup, label %return
switch.lookup: ; preds = %entry
%switch.gep = getelementptr inbounds [3 x i32*], [3 x i32*]* @switch.table.external_linkage, i32 0, i32 %cond
%switch.load = load i32*, i32** %switch.gep, align 8
ret i32* %switch.load
return: ; preds = %entry
ret i32* @d1
}
; Relative switch lookup table for integer pointers that have internal linkage
define i32* @internal_linkage(i32 %cond) {
; CHECK-LABEL: @internal_linkage(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[COND:%.*]], 3
; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]]
; CHECK: switch.lookup:
; CHECK-NEXT: [[RELTABLE_SHIFT:%.*]] = shl i32 %cond, 2
; CHECK-NEXT: [[RELTABLE_INTRINSIC:%.*]] = call i8* @llvm.load.relative.i32(i8* bitcast ([3 x i32]* @reltable.internal_linkage to i8*), i32 [[RELTABLE_SHIFT]])
; CHECK-NEXT: [[BIT_CAST:%.*]] = bitcast i8* [[RELTABLE_INTRINSIC]] to i32*
; CHECK-NEXT: ret i32* [[BIT_CAST]]
; CHECK: return:
; CHECK-NEXT: ret i32* @d2
;
entry:
%0 = icmp ult i32 %cond, 3
br i1 %0, label %switch.lookup, label %return
switch.lookup: ; preds = %entry
%switch.gep = getelementptr inbounds [3 x i32*], [3 x i32*]* @switch.table.internal_linkage, i32 0, i32 %cond
%switch.load = load i32*, i32** %switch.gep, align 8
ret i32* %switch.load
return: ; preds = %entry
ret i32* @d2
}
; ; Relative switch lookup table for strings
define i8* @string_table(i32 %cond) {
; CHECK-LABEL: @string_table(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[COND:%.*]], 3
; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]]
; CHECK: switch.lookup:
; CHECK-NEXT: [[RELTABLE_SHIFT:%.*]] = shl i32 %cond, 2
; CHECK-NEXT: [[RELTABLE_INTRINSIC:%.*]] = call i8* @llvm.load.relative.i32(i8* bitcast ([3 x i32]* @reltable.string_table to i8*), i32 [[RELTABLE_SHIFT]])
; CHECK-NEXT: ret i8* [[RELTABLE_INTRINSIC]]
; CHECK: return:
; CHECK-NEXT: ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0)
;
entry:
%0 = icmp ult i32 %cond, 3
br i1 %0, label %switch.lookup, label %return
switch.lookup: ; preds = %entry
%switch.gep = getelementptr inbounds [3 x i8*], [3 x i8*]* @switch.table.string_table, i32 0, i32 %cond
%switch.load = load i8*, i8** %switch.gep, align 8
ret i8* %switch.load
return: ; preds = %entry
ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0)
}
; Relative switch lookup table for strings with holes, where holes are filled with relative offset to default values
define i8* @string_table_holes(i32 %cond) {
; CHECK-LABEL: @string_table_holes(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[COND:%.*]], 4
; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]]
; CHECK: switch.lookup:
; CHECK-NEXT: [[RELTABLE_SHIFT:%.*]] = shl i32 [[COND]], 2
; CHECK-NEXT: [[RELTABLE_INTRINSIC:%.*]] = call i8* @llvm.load.relative.i32(i8* bitcast ([4 x i32]* @reltable.string_table_holes to i8*), i32 [[RELTABLE_SHIFT]])
; CHECK-NEXT: ret i8* [[RELTABLE_INTRINSIC]]
; CHECK: return:
; CHECK-NEXT: ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0)
;
entry:
%0 = icmp ult i32 %cond, 4
br i1 %0, label %switch.lookup, label %return
switch.lookup: ; preds = %entry
%switch.gep = getelementptr inbounds [4 x i8*], [4 x i8*]* @switch.table.string_table_holes, i32 0, i32 %cond
%switch.load = load i8*, i8** %switch.gep, align 8
ret i8* %switch.load
return: ; preds = %entry
ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0)
}
; Single value check
; If there is a lookup table, where each element contains the same value,
; a relative lookup should not be generated
define void @single_value(i32 %cond) {
; CHECK-LABEL: @single_value(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[COND:%.*]], 3
; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]]
; CHECK: switch.lookup:
; CHECK-NEXT: [[RELTABLE_SHIFT:%.*]] = shl i32 [[COND]], 2
; CHECK-NEXT: [[RELTABLE_INTRINSIC:%.*]] = call i8* @llvm.load.relative.i32(i8* bitcast ([3 x i32]* @reltable.single_value to i8*), i32 [[RELTABLE_SHIFT]])
; CHECK: sw.epilog:
; CHECK-NEXT: [[STR1:%.*]] = phi i8* [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str.5, i64 0, i64 0), %entry ], [ getelementptr inbounds ([12 x i8], [12 x i8]* @.str.7, i64 0, i64 0), %switch.lookup ]
; CHECK-NEXT: [[STR2:%.*]] = phi i8* [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str.6, i64 0, i64 0), %entry ], [ [[RELTABLE_INTRINSIC]], [[SWITCH_LOOKUP]] ]
; CHECK-NEXT: ret void
entry:
%0 = icmp ult i32 %cond, 3
br i1 %0, label %switch.lookup, label %sw.epilog
switch.lookup: ; preds = %entry
%switch.gep = getelementptr inbounds [3 x i8*], [3 x i8*]* @switch.table.single_value, i32 0, i32 %cond
%switch.load = load i8*, i8** %switch.gep, align 8
br label %sw.epilog
sw.epilog: ; preds = %switch.lookup, %entry
%str1.0 = phi i8* [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str.5, i64 0, i64 0), %entry ], [ getelementptr inbounds ([12 x i8], [12 x i8]* @.str.7, i64 0, i64 0), %switch.lookup ]
%str2.0 = phi i8* [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str.6, i64 0, i64 0), %entry ], [ %switch.load, %switch.lookup ]
ret void
}
; Relative lookup table generated for a user-defined lookup table
define i8* @user_defined_lookup_table(i32 %cond) {
; CHECK-LABEL: @user_defined_lookup_table(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[COND:%.*]], 3
; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]]
; CHECK: cond.false:
; CHECK-NEXT: [[IDX_PROM:%.*]] = sext i32 [[COND]] to i64
; CHECK-NEXT: [[RELTABLE_SHIFT:%.*]] = shl i64 [[IDX_PROM]], 2
; CHECK-NEXT: [[RELTABLE_INTRINSIC:%.*]] = call i8* @llvm.load.relative.i64(i8* bitcast ([3 x i32]* @reltable.user_defined_lookup_table to i8*), i64 [[RELTABLE_SHIFT]])
; CHECK-NEXT: br label %cond.end
; CHECK: cond.end:
; CHECK-NEXT: [[COND1:%.*]] = phi i8* [ [[RELTABLE_INTRINSIC]], %cond.false ], [ getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0), %entry ]
; CHECK-NEXT: ret i8* [[COND1]]
;
entry:
%cmp = icmp sgt i32 %cond, 3
br i1 %cmp, label %cond.end, label %cond.false
cond.false: ; preds = %entry
%idxprom = sext i32 %cond to i64
%arrayidx = getelementptr inbounds [3 x i8*], [3 x i8*]* @user_defined_lookup_table.table, i64 0, i64 %idxprom
%0 = load i8*, i8** %arrayidx, align 8, !tbaa !4
br label %cond.end
cond.end: ; preds = %entry, %cond.false
%cond1 = phi i8* [ %0, %cond.false ], [ getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0), %entry ]
ret i8* %cond1
}
!llvm.module.flags = !{!0, !1}
!0 = !{i32 7, !"PIC Level", i32 2}
!1 = !{i32 1, !"Code Model", i32 1}
!4 = !{!"any pointer", !5, i64 0}
!5 = !{!"omnipotent char", !6, i64 0}
!6 = !{!"Simple C/C++ TBAA"}