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