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