1*fe6060f1SDimitry Andric //===- RelLookupTableConverterPass - Rel Table Conv -----------------------===// 2*fe6060f1SDimitry Andric // 3*fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*fe6060f1SDimitry Andric // 7*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 8*fe6060f1SDimitry Andric // 9*fe6060f1SDimitry Andric // This file implements relative lookup table converter that converts 10*fe6060f1SDimitry Andric // lookup tables to relative lookup tables to make them PIC-friendly. 11*fe6060f1SDimitry Andric // 12*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 13*fe6060f1SDimitry Andric 14*fe6060f1SDimitry Andric #include "llvm/Transforms/Utils/RelLookupTableConverter.h" 15*fe6060f1SDimitry Andric #include "llvm/Analysis/ConstantFolding.h" 16*fe6060f1SDimitry Andric #include "llvm/Analysis/TargetTransformInfo.h" 17*fe6060f1SDimitry Andric #include "llvm/IR/BasicBlock.h" 18*fe6060f1SDimitry Andric #include "llvm/IR/IRBuilder.h" 19*fe6060f1SDimitry Andric #include "llvm/IR/Instructions.h" 20*fe6060f1SDimitry Andric #include "llvm/IR/Module.h" 21*fe6060f1SDimitry Andric #include "llvm/InitializePasses.h" 22*fe6060f1SDimitry Andric #include "llvm/Pass.h" 23*fe6060f1SDimitry Andric #include "llvm/Transforms/Utils/BasicBlockUtils.h" 24*fe6060f1SDimitry Andric 25*fe6060f1SDimitry Andric using namespace llvm; 26*fe6060f1SDimitry Andric 27*fe6060f1SDimitry Andric static bool shouldConvertToRelLookupTable(Module &M, GlobalVariable &GV) { 28*fe6060f1SDimitry Andric // If lookup table has more than one user, 29*fe6060f1SDimitry Andric // do not generate a relative lookup table. 30*fe6060f1SDimitry Andric // This is to simplify the analysis that needs to be done for this pass. 31*fe6060f1SDimitry Andric // TODO: Add support for lookup tables with multiple uses. 32*fe6060f1SDimitry Andric // For ex, this can happen when a function that uses a lookup table gets 33*fe6060f1SDimitry Andric // inlined into multiple call sites. 34*fe6060f1SDimitry Andric if (!GV.hasInitializer() || 35*fe6060f1SDimitry Andric !GV.isConstant() || 36*fe6060f1SDimitry Andric !GV.hasOneUse()) 37*fe6060f1SDimitry Andric return false; 38*fe6060f1SDimitry Andric 39*fe6060f1SDimitry Andric GetElementPtrInst *GEP = 40*fe6060f1SDimitry Andric dyn_cast<GetElementPtrInst>(GV.use_begin()->getUser()); 41*fe6060f1SDimitry Andric if (!GEP || !GEP->hasOneUse()) 42*fe6060f1SDimitry Andric return false; 43*fe6060f1SDimitry Andric 44*fe6060f1SDimitry Andric LoadInst *Load = dyn_cast<LoadInst>(GEP->use_begin()->getUser()); 45*fe6060f1SDimitry Andric if (!Load || !Load->hasOneUse()) 46*fe6060f1SDimitry Andric return false; 47*fe6060f1SDimitry Andric 48*fe6060f1SDimitry Andric // If the original lookup table does not have local linkage and is 49*fe6060f1SDimitry Andric // not dso_local, do not generate a relative lookup table. 50*fe6060f1SDimitry Andric // This optimization creates a relative lookup table that consists of 51*fe6060f1SDimitry Andric // offsets between the start of the lookup table and its elements. 52*fe6060f1SDimitry Andric // To be able to generate these offsets, relative lookup table and 53*fe6060f1SDimitry Andric // its elements should have internal linkage and be dso_local, which means 54*fe6060f1SDimitry Andric // that they should resolve to symbols within the same linkage unit. 55*fe6060f1SDimitry Andric if (!GV.hasLocalLinkage() || 56*fe6060f1SDimitry Andric !GV.isDSOLocal() || 57*fe6060f1SDimitry Andric !GV.isImplicitDSOLocal()) 58*fe6060f1SDimitry Andric return false; 59*fe6060f1SDimitry Andric 60*fe6060f1SDimitry Andric ConstantArray *Array = dyn_cast<ConstantArray>(GV.getInitializer()); 61*fe6060f1SDimitry Andric // If values are not pointers, do not generate a relative lookup table. 62*fe6060f1SDimitry Andric if (!Array || !Array->getType()->getElementType()->isPointerTy()) 63*fe6060f1SDimitry Andric return false; 64*fe6060f1SDimitry Andric 65*fe6060f1SDimitry Andric const DataLayout &DL = M.getDataLayout(); 66*fe6060f1SDimitry Andric for (const Use &Op : Array->operands()) { 67*fe6060f1SDimitry Andric Constant *ConstOp = cast<Constant>(&Op); 68*fe6060f1SDimitry Andric GlobalValue *GVOp; 69*fe6060f1SDimitry Andric APInt Offset; 70*fe6060f1SDimitry Andric 71*fe6060f1SDimitry Andric // If an operand is not a constant offset from a lookup table, 72*fe6060f1SDimitry Andric // do not generate a relative lookup table. 73*fe6060f1SDimitry Andric if (!IsConstantOffsetFromGlobal(ConstOp, GVOp, Offset, DL)) 74*fe6060f1SDimitry Andric return false; 75*fe6060f1SDimitry Andric 76*fe6060f1SDimitry Andric // If operand is mutable, do not generate a relative lookup table. 77*fe6060f1SDimitry Andric auto *GlovalVarOp = dyn_cast<GlobalVariable>(GVOp); 78*fe6060f1SDimitry Andric if (!GlovalVarOp || !GlovalVarOp->isConstant()) 79*fe6060f1SDimitry Andric return false; 80*fe6060f1SDimitry Andric 81*fe6060f1SDimitry Andric if (!GlovalVarOp->hasLocalLinkage() || 82*fe6060f1SDimitry Andric !GlovalVarOp->isDSOLocal() || 83*fe6060f1SDimitry Andric !GlovalVarOp->isImplicitDSOLocal()) 84*fe6060f1SDimitry Andric return false; 85*fe6060f1SDimitry Andric } 86*fe6060f1SDimitry Andric 87*fe6060f1SDimitry Andric return true; 88*fe6060f1SDimitry Andric } 89*fe6060f1SDimitry Andric 90*fe6060f1SDimitry Andric static GlobalVariable *createRelLookupTable(Function &Func, 91*fe6060f1SDimitry Andric GlobalVariable &LookupTable) { 92*fe6060f1SDimitry Andric Module &M = *Func.getParent(); 93*fe6060f1SDimitry Andric ConstantArray *LookupTableArr = 94*fe6060f1SDimitry Andric cast<ConstantArray>(LookupTable.getInitializer()); 95*fe6060f1SDimitry Andric unsigned NumElts = LookupTableArr->getType()->getNumElements(); 96*fe6060f1SDimitry Andric ArrayType *IntArrayTy = 97*fe6060f1SDimitry Andric ArrayType::get(Type::getInt32Ty(M.getContext()), NumElts); 98*fe6060f1SDimitry Andric 99*fe6060f1SDimitry Andric GlobalVariable *RelLookupTable = new GlobalVariable( 100*fe6060f1SDimitry Andric M, IntArrayTy, LookupTable.isConstant(), LookupTable.getLinkage(), 101*fe6060f1SDimitry Andric nullptr, "reltable." + Func.getName(), &LookupTable, 102*fe6060f1SDimitry Andric LookupTable.getThreadLocalMode(), LookupTable.getAddressSpace(), 103*fe6060f1SDimitry Andric LookupTable.isExternallyInitialized()); 104*fe6060f1SDimitry Andric 105*fe6060f1SDimitry Andric uint64_t Idx = 0; 106*fe6060f1SDimitry Andric SmallVector<Constant *, 64> RelLookupTableContents(NumElts); 107*fe6060f1SDimitry Andric 108*fe6060f1SDimitry Andric for (Use &Operand : LookupTableArr->operands()) { 109*fe6060f1SDimitry Andric Constant *Element = cast<Constant>(Operand); 110*fe6060f1SDimitry Andric Type *IntPtrTy = M.getDataLayout().getIntPtrType(M.getContext()); 111*fe6060f1SDimitry Andric Constant *Base = llvm::ConstantExpr::getPtrToInt(RelLookupTable, IntPtrTy); 112*fe6060f1SDimitry Andric Constant *Target = llvm::ConstantExpr::getPtrToInt(Element, IntPtrTy); 113*fe6060f1SDimitry Andric Constant *Sub = llvm::ConstantExpr::getSub(Target, Base); 114*fe6060f1SDimitry Andric Constant *RelOffset = 115*fe6060f1SDimitry Andric llvm::ConstantExpr::getTrunc(Sub, Type::getInt32Ty(M.getContext())); 116*fe6060f1SDimitry Andric RelLookupTableContents[Idx++] = RelOffset; 117*fe6060f1SDimitry Andric } 118*fe6060f1SDimitry Andric 119*fe6060f1SDimitry Andric Constant *Initializer = 120*fe6060f1SDimitry Andric ConstantArray::get(IntArrayTy, RelLookupTableContents); 121*fe6060f1SDimitry Andric RelLookupTable->setInitializer(Initializer); 122*fe6060f1SDimitry Andric RelLookupTable->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); 123*fe6060f1SDimitry Andric RelLookupTable->setAlignment(llvm::Align(4)); 124*fe6060f1SDimitry Andric return RelLookupTable; 125*fe6060f1SDimitry Andric } 126*fe6060f1SDimitry Andric 127*fe6060f1SDimitry Andric static void convertToRelLookupTable(GlobalVariable &LookupTable) { 128*fe6060f1SDimitry Andric GetElementPtrInst *GEP = 129*fe6060f1SDimitry Andric cast<GetElementPtrInst>(LookupTable.use_begin()->getUser()); 130*fe6060f1SDimitry Andric LoadInst *Load = cast<LoadInst>(GEP->use_begin()->getUser()); 131*fe6060f1SDimitry Andric 132*fe6060f1SDimitry Andric Module &M = *LookupTable.getParent(); 133*fe6060f1SDimitry Andric BasicBlock *BB = GEP->getParent(); 134*fe6060f1SDimitry Andric IRBuilder<> Builder(BB); 135*fe6060f1SDimitry Andric Function &Func = *BB->getParent(); 136*fe6060f1SDimitry Andric 137*fe6060f1SDimitry Andric // Generate an array that consists of relative offsets. 138*fe6060f1SDimitry Andric GlobalVariable *RelLookupTable = createRelLookupTable(Func, LookupTable); 139*fe6060f1SDimitry Andric 140*fe6060f1SDimitry Andric // Place new instruction sequence before GEP. 141*fe6060f1SDimitry Andric Builder.SetInsertPoint(GEP); 142*fe6060f1SDimitry Andric Value *Index = GEP->getOperand(2); 143*fe6060f1SDimitry Andric IntegerType *IntTy = cast<IntegerType>(Index->getType()); 144*fe6060f1SDimitry Andric Value *Offset = 145*fe6060f1SDimitry Andric Builder.CreateShl(Index, ConstantInt::get(IntTy, 2), "reltable.shift"); 146*fe6060f1SDimitry Andric 147*fe6060f1SDimitry Andric Function *LoadRelIntrinsic = llvm::Intrinsic::getDeclaration( 148*fe6060f1SDimitry Andric &M, Intrinsic::load_relative, {Index->getType()}); 149*fe6060f1SDimitry Andric Value *Base = Builder.CreateBitCast(RelLookupTable, Builder.getInt8PtrTy()); 150*fe6060f1SDimitry Andric 151*fe6060f1SDimitry Andric // Create a call to load.relative intrinsic that computes the target address 152*fe6060f1SDimitry Andric // by adding base address (lookup table address) and relative offset. 153*fe6060f1SDimitry Andric Value *Result = Builder.CreateCall(LoadRelIntrinsic, {Base, Offset}, 154*fe6060f1SDimitry Andric "reltable.intrinsic"); 155*fe6060f1SDimitry Andric 156*fe6060f1SDimitry Andric // Create a bitcast instruction if necessary. 157*fe6060f1SDimitry Andric if (Load->getType() != Builder.getInt8PtrTy()) 158*fe6060f1SDimitry Andric Result = Builder.CreateBitCast(Result, Load->getType(), "reltable.bitcast"); 159*fe6060f1SDimitry Andric 160*fe6060f1SDimitry Andric // Replace load instruction with the new generated instruction sequence. 161*fe6060f1SDimitry Andric Load->replaceAllUsesWith(Result); 162*fe6060f1SDimitry Andric // Remove Load and GEP instructions. 163*fe6060f1SDimitry Andric Load->eraseFromParent(); 164*fe6060f1SDimitry Andric GEP->eraseFromParent(); 165*fe6060f1SDimitry Andric } 166*fe6060f1SDimitry Andric 167*fe6060f1SDimitry Andric // Convert lookup tables to relative lookup tables in the module. 168*fe6060f1SDimitry Andric static bool convertToRelativeLookupTables( 169*fe6060f1SDimitry Andric Module &M, function_ref<TargetTransformInfo &(Function &)> GetTTI) { 170*fe6060f1SDimitry Andric Module::iterator FI = M.begin(); 171*fe6060f1SDimitry Andric if (FI == M.end()) 172*fe6060f1SDimitry Andric return false; 173*fe6060f1SDimitry Andric 174*fe6060f1SDimitry Andric // Check if we have a target that supports relative lookup tables. 175*fe6060f1SDimitry Andric if (!GetTTI(*FI).shouldBuildRelLookupTables()) 176*fe6060f1SDimitry Andric return false; 177*fe6060f1SDimitry Andric 178*fe6060f1SDimitry Andric bool Changed = false; 179*fe6060f1SDimitry Andric 180*fe6060f1SDimitry Andric for (auto GVI = M.global_begin(), E = M.global_end(); GVI != E;) { 181*fe6060f1SDimitry Andric GlobalVariable &GV = *GVI++; 182*fe6060f1SDimitry Andric 183*fe6060f1SDimitry Andric if (!shouldConvertToRelLookupTable(M, GV)) 184*fe6060f1SDimitry Andric continue; 185*fe6060f1SDimitry Andric 186*fe6060f1SDimitry Andric convertToRelLookupTable(GV); 187*fe6060f1SDimitry Andric 188*fe6060f1SDimitry Andric // Remove the original lookup table. 189*fe6060f1SDimitry Andric GV.eraseFromParent(); 190*fe6060f1SDimitry Andric 191*fe6060f1SDimitry Andric Changed = true; 192*fe6060f1SDimitry Andric } 193*fe6060f1SDimitry Andric 194*fe6060f1SDimitry Andric return Changed; 195*fe6060f1SDimitry Andric } 196*fe6060f1SDimitry Andric 197*fe6060f1SDimitry Andric PreservedAnalyses RelLookupTableConverterPass::run(Module &M, 198*fe6060f1SDimitry Andric ModuleAnalysisManager &AM) { 199*fe6060f1SDimitry Andric FunctionAnalysisManager &FAM = 200*fe6060f1SDimitry Andric AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); 201*fe6060f1SDimitry Andric 202*fe6060f1SDimitry Andric auto GetTTI = [&](Function &F) -> TargetTransformInfo & { 203*fe6060f1SDimitry Andric return FAM.getResult<TargetIRAnalysis>(F); 204*fe6060f1SDimitry Andric }; 205*fe6060f1SDimitry Andric 206*fe6060f1SDimitry Andric if (!convertToRelativeLookupTables(M, GetTTI)) 207*fe6060f1SDimitry Andric return PreservedAnalyses::all(); 208*fe6060f1SDimitry Andric 209*fe6060f1SDimitry Andric PreservedAnalyses PA; 210*fe6060f1SDimitry Andric PA.preserveSet<CFGAnalyses>(); 211*fe6060f1SDimitry Andric return PA; 212*fe6060f1SDimitry Andric } 213