xref: /freebsd-src/contrib/llvm-project/llvm/lib/Transforms/Utils/RelLookupTableConverter.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
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