123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- //===- 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"
- using namespace llvm;
- static bool shouldConvertToRelLookupTable(Module &M, GlobalVariable &GV) {
- // 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.hasInitializer() ||
- !GV.isConstant() ||
- !GV.hasOneUse())
- return false;
- GetElementPtrInst *GEP =
- dyn_cast<GetElementPtrInst>(GV.use_begin()->getUser());
- if (!GEP || !GEP->hasOneUse() ||
- GV.getValueType() != GEP->getSourceElementType())
- return false;
- LoadInst *Load = dyn_cast<LoadInst>(GEP->use_begin()->getUser());
- if (!Load || !Load->hasOneUse() ||
- Load->getType() != GEP->getResultElementType())
- 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 (!Array)
- return false;
- // If values are not 64-bit pointers, do not generate a relative lookup table.
- const DataLayout &DL = M.getDataLayout();
- Type *ElemType = Array->getType()->getElementType();
- if (!ElemType->isPointerTy() || DL.getPointerTypeSizeInBits(ElemType) != 64)
- return false;
- 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 operand is mutable, do not generate a relative lookup table.
- auto *GlovalVarOp = dyn_cast<GlobalVariable>(GVOp);
- if (!GlovalVarOp || !GlovalVarOp->isConstant())
- return false;
- if (!GlovalVarOp->hasLocalLinkage() ||
- !GlovalVarOp->isDSOLocal() ||
- !GlovalVarOp->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 before 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");
- // Insert the call to load.relative intrinsic before LOAD.
- // GEP might not be immediately followed by a LOAD, like it can be hoisted
- // outside the loop or another instruction might be inserted them in between.
- Builder.SetInsertPoint(Load);
- 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.
- Load->replaceAllUsesWith(Result);
- // Remove Load and GEP instructions.
- Load->eraseFromParent();
- GEP->eraseFromParent();
- }
- // Convert lookup tables to relative lookup tables in the module.
- static bool convertToRelativeLookupTables(
- Module &M, function_ref<TargetTransformInfo &(Function &)> GetTTI) {
- for (Function &F : M) {
- if (F.isDeclaration())
- continue;
- // Check if we have a target that supports relative lookup tables.
- if (!GetTTI(F).shouldBuildRelLookupTables())
- return false;
- // We assume that the result is independent of the checked function.
- break;
- }
- bool Changed = false;
- for (GlobalVariable &GV : llvm::make_early_inc_range(M.globals())) {
- 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;
- }
|