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()); 3881ad6265SDimitry Andric if (!GEP || !GEP->hasOneUse() || 3981ad6265SDimitry Andric GV.getValueType() != GEP->getSourceElementType()) 40fe6060f1SDimitry Andric return false; 41fe6060f1SDimitry Andric 42fe6060f1SDimitry Andric LoadInst *Load = dyn_cast<LoadInst>(GEP->use_begin()->getUser()); 4381ad6265SDimitry Andric if (!Load || !Load->hasOneUse() || 4481ad6265SDimitry 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()); 6061cfbce3SDimitry Andric if (!Array) 61fe6060f1SDimitry Andric return false; 62fe6060f1SDimitry Andric 6361cfbce3SDimitry Andric // If values are not 64-bit pointers, do not generate a relative lookup table. 64fe6060f1SDimitry Andric const DataLayout &DL = M.getDataLayout(); 6561cfbce3SDimitry Andric Type *ElemType = Array->getType()->getElementType(); 6661cfbce3SDimitry Andric if (!ElemType->isPointerTy() || DL.getPointerTypeSizeInBits(ElemType) != 64) 6761cfbce3SDimitry Andric return false; 6861cfbce3SDimitry Andric 69fe6060f1SDimitry Andric for (const Use &Op : Array->operands()) { 70fe6060f1SDimitry Andric Constant *ConstOp = cast<Constant>(&Op); 71fe6060f1SDimitry Andric GlobalValue *GVOp; 72fe6060f1SDimitry Andric APInt Offset; 73fe6060f1SDimitry Andric 74fe6060f1SDimitry Andric // If an operand is not a constant offset from a lookup table, 75fe6060f1SDimitry Andric // do not generate a relative lookup table. 76fe6060f1SDimitry Andric if (!IsConstantOffsetFromGlobal(ConstOp, GVOp, Offset, DL)) 77fe6060f1SDimitry Andric return false; 78fe6060f1SDimitry Andric 79fe6060f1SDimitry Andric // If operand is mutable, do not generate a relative lookup table. 80fe6060f1SDimitry Andric auto *GlovalVarOp = dyn_cast<GlobalVariable>(GVOp); 81fe6060f1SDimitry Andric if (!GlovalVarOp || !GlovalVarOp->isConstant()) 82fe6060f1SDimitry Andric return false; 83fe6060f1SDimitry Andric 84fe6060f1SDimitry Andric if (!GlovalVarOp->hasLocalLinkage() || 85fe6060f1SDimitry Andric !GlovalVarOp->isDSOLocal() || 86fe6060f1SDimitry Andric !GlovalVarOp->isImplicitDSOLocal()) 87fe6060f1SDimitry Andric return false; 88fe6060f1SDimitry Andric } 89fe6060f1SDimitry Andric 90fe6060f1SDimitry Andric return true; 91fe6060f1SDimitry Andric } 92fe6060f1SDimitry Andric 93fe6060f1SDimitry Andric static GlobalVariable *createRelLookupTable(Function &Func, 94fe6060f1SDimitry Andric GlobalVariable &LookupTable) { 95fe6060f1SDimitry Andric Module &M = *Func.getParent(); 96fe6060f1SDimitry Andric ConstantArray *LookupTableArr = 97fe6060f1SDimitry Andric cast<ConstantArray>(LookupTable.getInitializer()); 98fe6060f1SDimitry Andric unsigned NumElts = LookupTableArr->getType()->getNumElements(); 99fe6060f1SDimitry Andric ArrayType *IntArrayTy = 100fe6060f1SDimitry Andric ArrayType::get(Type::getInt32Ty(M.getContext()), NumElts); 101fe6060f1SDimitry Andric 102fe6060f1SDimitry Andric GlobalVariable *RelLookupTable = new GlobalVariable( 103fe6060f1SDimitry Andric M, IntArrayTy, LookupTable.isConstant(), LookupTable.getLinkage(), 104fe6060f1SDimitry Andric nullptr, "reltable." + Func.getName(), &LookupTable, 105fe6060f1SDimitry Andric LookupTable.getThreadLocalMode(), LookupTable.getAddressSpace(), 106fe6060f1SDimitry Andric LookupTable.isExternallyInitialized()); 107fe6060f1SDimitry Andric 108fe6060f1SDimitry Andric uint64_t Idx = 0; 109fe6060f1SDimitry Andric SmallVector<Constant *, 64> RelLookupTableContents(NumElts); 110fe6060f1SDimitry Andric 111fe6060f1SDimitry Andric for (Use &Operand : LookupTableArr->operands()) { 112fe6060f1SDimitry Andric Constant *Element = cast<Constant>(Operand); 113fe6060f1SDimitry Andric Type *IntPtrTy = M.getDataLayout().getIntPtrType(M.getContext()); 114fe6060f1SDimitry Andric Constant *Base = llvm::ConstantExpr::getPtrToInt(RelLookupTable, IntPtrTy); 115fe6060f1SDimitry Andric Constant *Target = llvm::ConstantExpr::getPtrToInt(Element, IntPtrTy); 116fe6060f1SDimitry Andric Constant *Sub = llvm::ConstantExpr::getSub(Target, Base); 117fe6060f1SDimitry Andric Constant *RelOffset = 118fe6060f1SDimitry Andric llvm::ConstantExpr::getTrunc(Sub, Type::getInt32Ty(M.getContext())); 119fe6060f1SDimitry Andric RelLookupTableContents[Idx++] = RelOffset; 120fe6060f1SDimitry Andric } 121fe6060f1SDimitry Andric 122fe6060f1SDimitry Andric Constant *Initializer = 123fe6060f1SDimitry Andric ConstantArray::get(IntArrayTy, RelLookupTableContents); 124fe6060f1SDimitry Andric RelLookupTable->setInitializer(Initializer); 125fe6060f1SDimitry Andric RelLookupTable->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); 126fe6060f1SDimitry Andric RelLookupTable->setAlignment(llvm::Align(4)); 127fe6060f1SDimitry Andric return RelLookupTable; 128fe6060f1SDimitry Andric } 129fe6060f1SDimitry Andric 130fe6060f1SDimitry Andric static void convertToRelLookupTable(GlobalVariable &LookupTable) { 131fe6060f1SDimitry Andric GetElementPtrInst *GEP = 132fe6060f1SDimitry Andric cast<GetElementPtrInst>(LookupTable.use_begin()->getUser()); 133fe6060f1SDimitry Andric LoadInst *Load = cast<LoadInst>(GEP->use_begin()->getUser()); 134fe6060f1SDimitry Andric 135fe6060f1SDimitry Andric Module &M = *LookupTable.getParent(); 136fe6060f1SDimitry Andric BasicBlock *BB = GEP->getParent(); 137fe6060f1SDimitry Andric IRBuilder<> Builder(BB); 138fe6060f1SDimitry Andric Function &Func = *BB->getParent(); 139fe6060f1SDimitry Andric 140fe6060f1SDimitry Andric // Generate an array that consists of relative offsets. 141fe6060f1SDimitry Andric GlobalVariable *RelLookupTable = createRelLookupTable(Func, LookupTable); 142fe6060f1SDimitry Andric 143fe6060f1SDimitry Andric // Place new instruction sequence before GEP. 144fe6060f1SDimitry Andric Builder.SetInsertPoint(GEP); 145fe6060f1SDimitry Andric Value *Index = GEP->getOperand(2); 146fe6060f1SDimitry Andric IntegerType *IntTy = cast<IntegerType>(Index->getType()); 147fe6060f1SDimitry Andric Value *Offset = 148fe6060f1SDimitry Andric Builder.CreateShl(Index, ConstantInt::get(IntTy, 2), "reltable.shift"); 149fe6060f1SDimitry Andric 15081ad6265SDimitry Andric // Insert the call to load.relative intrinsic before LOAD. 1515a925e46SDimitry Andric // GEP might not be immediately followed by a LOAD, like it can be hoisted 1525a925e46SDimitry Andric // outside the loop or another instruction might be inserted them in between. 1535a925e46SDimitry Andric Builder.SetInsertPoint(Load); 154fe6060f1SDimitry Andric Function *LoadRelIntrinsic = llvm::Intrinsic::getDeclaration( 155fe6060f1SDimitry Andric &M, Intrinsic::load_relative, {Index->getType()}); 156fe6060f1SDimitry Andric 157fe6060f1SDimitry Andric // Create a call to load.relative intrinsic that computes the target address 158fe6060f1SDimitry Andric // by adding base address (lookup table address) and relative offset. 159*5f757f3fSDimitry Andric Value *Result = Builder.CreateCall(LoadRelIntrinsic, {RelLookupTable, Offset}, 160fe6060f1SDimitry Andric "reltable.intrinsic"); 161fe6060f1SDimitry Andric 162fe6060f1SDimitry Andric // Replace load instruction with the new generated instruction sequence. 163fe6060f1SDimitry Andric Load->replaceAllUsesWith(Result); 164fe6060f1SDimitry Andric // Remove Load and GEP instructions. 165fe6060f1SDimitry Andric Load->eraseFromParent(); 166fe6060f1SDimitry Andric GEP->eraseFromParent(); 167fe6060f1SDimitry Andric } 168fe6060f1SDimitry Andric 169fe6060f1SDimitry Andric // Convert lookup tables to relative lookup tables in the module. 170fe6060f1SDimitry Andric static bool convertToRelativeLookupTables( 171fe6060f1SDimitry Andric Module &M, function_ref<TargetTransformInfo &(Function &)> GetTTI) { 17281ad6265SDimitry Andric for (Function &F : M) { 17381ad6265SDimitry Andric if (F.isDeclaration()) 17481ad6265SDimitry Andric continue; 175fe6060f1SDimitry Andric 176fe6060f1SDimitry Andric // Check if we have a target that supports relative lookup tables. 17781ad6265SDimitry Andric if (!GetTTI(F).shouldBuildRelLookupTables()) 178fe6060f1SDimitry Andric return false; 179fe6060f1SDimitry Andric 18081ad6265SDimitry Andric // We assume that the result is independent of the checked function. 18181ad6265SDimitry Andric break; 18281ad6265SDimitry Andric } 18381ad6265SDimitry Andric 184fe6060f1SDimitry Andric bool Changed = false; 185fe6060f1SDimitry Andric 186349cc55cSDimitry Andric for (GlobalVariable &GV : llvm::make_early_inc_range(M.globals())) { 187fe6060f1SDimitry Andric if (!shouldConvertToRelLookupTable(M, GV)) 188fe6060f1SDimitry Andric continue; 189fe6060f1SDimitry Andric 190fe6060f1SDimitry Andric convertToRelLookupTable(GV); 191fe6060f1SDimitry Andric 192fe6060f1SDimitry Andric // Remove the original lookup table. 193fe6060f1SDimitry Andric GV.eraseFromParent(); 194fe6060f1SDimitry Andric 195fe6060f1SDimitry Andric Changed = true; 196fe6060f1SDimitry Andric } 197fe6060f1SDimitry Andric 198fe6060f1SDimitry Andric return Changed; 199fe6060f1SDimitry Andric } 200fe6060f1SDimitry Andric 201fe6060f1SDimitry Andric PreservedAnalyses RelLookupTableConverterPass::run(Module &M, 202fe6060f1SDimitry Andric ModuleAnalysisManager &AM) { 203fe6060f1SDimitry Andric FunctionAnalysisManager &FAM = 204fe6060f1SDimitry Andric AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); 205fe6060f1SDimitry Andric 206fe6060f1SDimitry Andric auto GetTTI = [&](Function &F) -> TargetTransformInfo & { 207fe6060f1SDimitry Andric return FAM.getResult<TargetIRAnalysis>(F); 208fe6060f1SDimitry Andric }; 209fe6060f1SDimitry Andric 210fe6060f1SDimitry Andric if (!convertToRelativeLookupTables(M, GetTTI)) 211fe6060f1SDimitry Andric return PreservedAnalyses::all(); 212fe6060f1SDimitry Andric 213fe6060f1SDimitry Andric PreservedAnalyses PA; 214fe6060f1SDimitry Andric PA.preserveSet<CFGAnalyses>(); 215fe6060f1SDimitry Andric return PA; 216fe6060f1SDimitry Andric } 217