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