mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 20:23:11 +01:00
[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:
parent
173cbf6ad2
commit
abf79b4a39
@ -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
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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&);
|
||||||
|
@ -517,6 +517,7 @@ FunctionPass *createLoopDataPrefetchPass();
|
|||||||
|
|
||||||
///===---------------------------------------------------------------------===//
|
///===---------------------------------------------------------------------===//
|
||||||
ModulePass *createNameAnonGlobalPass();
|
ModulePass *createNameAnonGlobalPass();
|
||||||
|
ModulePass *createRelLookupTableConverterPass();
|
||||||
ModulePass *createCanonicalizeAliasesPass();
|
ModulePass *createCanonicalizeAliasesPass();
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
70
include/llvm/Transforms/Utils/RelLookupTableConverter.h
Normal file
70
include/llvm/Transforms/Utils/RelLookupTableConverter.h
Normal 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
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,8 +63,8 @@ MODULE_PASS("khwasan", HWAddressSanitizerPass(true, true))
|
|||||||
MODULE_PASS("inferattrs", InferFunctionAttrsPass())
|
MODULE_PASS("inferattrs", InferFunctionAttrsPass())
|
||||||
MODULE_PASS("inliner-wrapper", ModuleInlinerWrapperPass())
|
MODULE_PASS("inliner-wrapper", ModuleInlinerWrapperPass())
|
||||||
MODULE_PASS("inliner-wrapper-no-mandatory-first", ModuleInlinerWrapperPass(
|
MODULE_PASS("inliner-wrapper-no-mandatory-first", ModuleInlinerWrapperPass(
|
||||||
getInlineParams(),
|
getInlineParams(),
|
||||||
DebugLogging,
|
DebugLogging,
|
||||||
false))
|
false))
|
||||||
MODULE_PASS("insert-gcov-profiling", GCOVProfilerPass())
|
MODULE_PASS("insert-gcov-profiling", GCOVProfilerPass())
|
||||||
MODULE_PASS("instrorderfile", InstrOrderFilePass())
|
MODULE_PASS("instrorderfile", InstrOrderFilePass())
|
||||||
@ -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())
|
||||||
@ -281,7 +282,7 @@ FUNCTION_PASS("print<demanded-bits>", DemandedBitsPrinterPass(dbgs()))
|
|||||||
FUNCTION_PASS("print<domfrontier>", DominanceFrontierPrinterPass(dbgs()))
|
FUNCTION_PASS("print<domfrontier>", DominanceFrontierPrinterPass(dbgs()))
|
||||||
FUNCTION_PASS("print<func-properties>", FunctionPropertiesPrinterPass(dbgs()))
|
FUNCTION_PASS("print<func-properties>", FunctionPropertiesPrinterPass(dbgs()))
|
||||||
FUNCTION_PASS("print<inline-cost>", InlineCostAnnotationPrinterPass(dbgs()))
|
FUNCTION_PASS("print<inline-cost>", InlineCostAnnotationPrinterPass(dbgs()))
|
||||||
FUNCTION_PASS("print<inliner-size-estimator>",
|
FUNCTION_PASS("print<inliner-size-estimator>",
|
||||||
InlineSizeEstimatorAnalysisPrinterPass(dbgs()))
|
InlineSizeEstimatorAnalysisPrinterPass(dbgs()))
|
||||||
FUNCTION_PASS("print<loops>", LoopPrinterPass(dbgs()))
|
FUNCTION_PASS("print<loops>", LoopPrinterPass(dbgs()))
|
||||||
FUNCTION_PASS("print<memoryssa>", MemorySSAPrinterPass(dbgs()))
|
FUNCTION_PASS("print<memoryssa>", MemorySSAPrinterPass(dbgs()))
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
257
lib/Transforms/Utils/RelLookupTableConverter.cpp
Normal file
257
lib/Transforms/Utils/RelLookupTableConverter.cpp
Normal 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
|
@ -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);
|
||||||
|
@ -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:
|
||||||
|
@ -113,9 +113,9 @@
|
|||||||
; CHECK-O-NEXT: Running analysis: CallGraphAnalysis
|
; CHECK-O-NEXT: Running analysis: CallGraphAnalysis
|
||||||
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis
|
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis
|
||||||
; CHECK-O-NEXT: Running analysis: ProfileSummaryAnalysis
|
; CHECK-O-NEXT: Running analysis: ProfileSummaryAnalysis
|
||||||
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
|
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
|
||||||
; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis
|
; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis
|
||||||
; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
|
; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
|
||||||
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}>
|
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}>
|
||||||
; CHECK-O-NEXT: Running pass: DevirtSCCRepeatedPass
|
; CHECK-O-NEXT: Running pass: DevirtSCCRepeatedPass
|
||||||
; CHECK-O-NEXT: Starting CGSCC pass manager run.
|
; CHECK-O-NEXT: Starting CGSCC pass manager run.
|
||||||
@ -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
|
||||||
|
@ -98,9 +98,9 @@
|
|||||||
; CHECK-O-NEXT: Running analysis: CallGraphAnalysis
|
; CHECK-O-NEXT: Running analysis: CallGraphAnalysis
|
||||||
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis
|
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis
|
||||||
; CHECK-PRELINK-O-NEXT: Running analysis: ProfileSummaryAnalysis
|
; CHECK-PRELINK-O-NEXT: Running analysis: ProfileSummaryAnalysis
|
||||||
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
|
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
|
||||||
; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis
|
; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis
|
||||||
; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
|
; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
|
||||||
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy
|
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy
|
||||||
; CHECK-O-NEXT: Running pass: DevirtSCCRepeatedPass
|
; CHECK-O-NEXT: Running pass: DevirtSCCRepeatedPass
|
||||||
; CHECK-O-NEXT: Starting CGSCC pass manager run.
|
; CHECK-O-NEXT: Starting CGSCC pass manager run.
|
||||||
@ -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
|
||||||
|
@ -68,10 +68,10 @@
|
|||||||
; CHECK-O-NEXT: Running pass: ModuleInlinerWrapperPass
|
; CHECK-O-NEXT: Running pass: ModuleInlinerWrapperPass
|
||||||
; CHECK-O-NEXT: Running analysis: InlineAdvisorAnalysis
|
; CHECK-O-NEXT: Running analysis: InlineAdvisorAnalysis
|
||||||
; CHECK-O-NEXT: Starting {{.*}}Module pass manager run.
|
; CHECK-O-NEXT: Starting {{.*}}Module pass manager run.
|
||||||
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}GlobalsAA
|
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}GlobalsAA
|
||||||
; CHECK-O-NEXT: Running analysis: GlobalsAA
|
; CHECK-O-NEXT: Running analysis: GlobalsAA
|
||||||
; CHECK-O-NEXT: Running analysis: CallGraphAnalysis
|
; CHECK-O-NEXT: Running analysis: CallGraphAnalysis
|
||||||
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis
|
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis
|
||||||
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
|
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
|
||||||
; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis
|
; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis
|
||||||
; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
|
; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
|
||||||
@ -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
|
||||||
|
|
||||||
|
@ -78,9 +78,9 @@
|
|||||||
; CHECK-O-NEXT: Running pass: ModuleInlinerWrapperPass
|
; CHECK-O-NEXT: Running pass: ModuleInlinerWrapperPass
|
||||||
; CHECK-O-NEXT: Running analysis: InlineAdvisorAnalysis
|
; CHECK-O-NEXT: Running analysis: InlineAdvisorAnalysis
|
||||||
; CHECK-O-NEXT: Starting {{.*}}Module pass manager run.
|
; CHECK-O-NEXT: Starting {{.*}}Module pass manager run.
|
||||||
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}GlobalsAA
|
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}GlobalsAA
|
||||||
; CHECK-O-NEXT: Running analysis: GlobalsAA
|
; CHECK-O-NEXT: Running analysis: GlobalsAA
|
||||||
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis
|
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis
|
||||||
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
|
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
|
||||||
; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis
|
; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis
|
||||||
; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
|
; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy
|
||||||
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
}
|
@ -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"}
|
Loading…
Reference in New Issue
Block a user