2121
2222using namespace llvm ;
2323
24- static bool shouldConvertToRelLookupTable (Module &M, GlobalVariable &GV) {
24+ struct LookupTableInfo {
25+ Value *Index;
26+ SmallVector<Constant *> Ptrs;
27+ };
28+
29+ static bool shouldConvertToRelLookupTable (LookupTableInfo &Info, Module &M,
30+ GlobalVariable &GV) {
2531 // If lookup table has more than one user,
2632 // do not generate a relative lookup table.
2733 // This is to simplify the analysis that needs to be done for this pass.
2834 // TODO: Add support for lookup tables with multiple uses.
2935 // For ex, this can happen when a function that uses a lookup table gets
3036 // inlined into multiple call sites.
31- if (!GV.hasInitializer () ||
32- !GV.isConstant () ||
33- !GV.hasOneUse ())
34- return false ;
35-
36- GetElementPtrInst *GEP =
37- dyn_cast<GetElementPtrInst>(GV.use_begin ()->getUser ());
38- if (!GEP || !GEP->hasOneUse () ||
39- GV.getValueType () != GEP->getSourceElementType ())
40- return false ;
41-
42- LoadInst *Load = dyn_cast<LoadInst>(GEP->use_begin ()->getUser ());
43- if (!Load || !Load->hasOneUse () ||
44- Load->getType () != GEP->getResultElementType ())
45- return false ;
46-
37+ //
4738 // If the original lookup table does not have local linkage and is
4839 // not dso_local, do not generate a relative lookup table.
4940 // This optimization creates a relative lookup table that consists of
5041 // offsets between the start of the lookup table and its elements.
5142 // To be able to generate these offsets, relative lookup table and
5243 // its elements should have internal linkage and be dso_local, which means
5344 // that they should resolve to symbols within the same linkage unit.
54- if (!GV.hasLocalLinkage () ||
55- !GV.isDSOLocal () ||
56- !GV.isImplicitDSOLocal ())
45+ if (!GV.hasInitializer () || !GV.isConstant () || !GV.hasOneUse () ||
46+ !GV.hasLocalLinkage () || !GV.isDSOLocal () || !GV.isImplicitDSOLocal ())
5747 return false ;
5848
59- ConstantArray *Array = dyn_cast<ConstantArray>(GV.getInitializer ());
60- if (!Array)
49+ auto *GEP = dyn_cast<GetElementPtrInst>(GV.use_begin ()->getUser ());
50+ if (!GEP || !GEP->hasOneUse ())
51+ return false ;
52+
53+ auto *Load = dyn_cast<LoadInst>(GEP->use_begin ()->getUser ());
54+ if (!Load || !Load->hasOneUse ())
6155 return false ;
6256
6357 // If values are not 64-bit pointers, do not generate a relative lookup table.
6458 const DataLayout &DL = M.getDataLayout ();
65- Type *ElemType = Array ->getType ()-> getElementType ();
59+ Type *ElemType = Load ->getType ();
6660 if (!ElemType->isPointerTy () || DL.getPointerTypeSizeInBits (ElemType) != 64 )
6761 return false ;
6862
63+ // Make sure this is a gep of the form GV + scale*var.
64+ unsigned IndexWidth =
65+ DL.getIndexTypeSizeInBits (Load->getPointerOperand ()->getType ());
66+ SmallMapVector<Value *, APInt, 4 > VarOffsets;
67+ APInt ConstOffset (IndexWidth, 0 );
68+ if (!GEP->collectOffset (DL, IndexWidth, VarOffsets, ConstOffset) ||
69+ !ConstOffset.isZero () || VarOffsets.size () != 1 )
70+ return false ;
71+
72+ // This can't be a pointer lookup table if the stride is smaller than a
73+ // pointer.
74+ Info.Index = VarOffsets.front ().first ;
75+ const APInt &Stride = VarOffsets.front ().second ;
76+ if (Stride.ult (DL.getTypeStoreSize (ElemType)))
77+ return false ;
78+
6979 SmallVector<GlobalVariable *, 4 > GVOps;
7080 Triple TT = M.getTargetTriple ();
7181 // FIXME: This should be removed in the future.
@@ -80,14 +90,20 @@ static bool shouldConvertToRelLookupTable(Module &M, GlobalVariable &GV) {
8090 // https://github.com/rust-lang/rust/issues/141306.
8191 || (TT.isX86 () && TT.isOSDarwin ());
8292
83- for (const Use &Op : Array->operands ()) {
84- Constant *ConstOp = cast<Constant>(&Op);
93+ APInt Offset (IndexWidth, 0 );
94+ uint64_t GVSize = DL.getTypeAllocSize (GV.getValueType ());
95+ for (; Offset.ult (GVSize); Offset += Stride) {
96+ Constant *C =
97+ ConstantFoldLoadFromConst (GV.getInitializer (), ElemType, Offset, DL);
98+ if (!C)
99+ return false ;
100+
85101 GlobalValue *GVOp;
86- APInt Offset ;
102+ APInt GVOffset ;
87103
88104 // If an operand is not a constant offset from a lookup table,
89105 // do not generate a relative lookup table.
90- if (!IsConstantOffsetFromGlobal (ConstOp , GVOp, Offset , DL))
106+ if (!IsConstantOffsetFromGlobal (C , GVOp, GVOffset , DL))
91107 return false ;
92108
93109 // If operand is mutable, do not generate a relative lookup table.
@@ -102,6 +118,8 @@ static bool shouldConvertToRelLookupTable(Module &M, GlobalVariable &GV) {
102118
103119 if (ShouldDropUnnamedAddr)
104120 GVOps.push_back (GlovalVarOp);
121+
122+ Info.Ptrs .push_back (C);
105123 }
106124
107125 if (ShouldDropUnnamedAddr)
@@ -111,14 +129,12 @@ static bool shouldConvertToRelLookupTable(Module &M, GlobalVariable &GV) {
111129 return true ;
112130}
113131
114- static GlobalVariable *createRelLookupTable (Function &Func,
132+ static GlobalVariable *createRelLookupTable (LookupTableInfo &Info,
133+ Function &Func,
115134 GlobalVariable &LookupTable) {
116135 Module &M = *Func.getParent ();
117- ConstantArray *LookupTableArr =
118- cast<ConstantArray>(LookupTable.getInitializer ());
119- unsigned NumElts = LookupTableArr->getType ()->getNumElements ();
120136 ArrayType *IntArrayTy =
121- ArrayType::get (Type::getInt32Ty (M.getContext ()), NumElts );
137+ ArrayType::get (Type::getInt32Ty (M.getContext ()), Info. Ptrs . size () );
122138
123139 GlobalVariable *RelLookupTable = new GlobalVariable (
124140 M, IntArrayTy, LookupTable.isConstant (), LookupTable.getLinkage (),
@@ -127,10 +143,9 @@ static GlobalVariable *createRelLookupTable(Function &Func,
127143 LookupTable.isExternallyInitialized ());
128144
129145 uint64_t Idx = 0 ;
130- SmallVector<Constant *, 64 > RelLookupTableContents (NumElts );
146+ SmallVector<Constant *, 64 > RelLookupTableContents (Info. Ptrs . size () );
131147
132- for (Use &Operand : LookupTableArr->operands ()) {
133- Constant *Element = cast<Constant>(Operand);
148+ for (Constant *Element : Info.Ptrs ) {
134149 Type *IntPtrTy = M.getDataLayout ().getIntPtrType (M.getContext ());
135150 Constant *Base = llvm::ConstantExpr::getPtrToInt (RelLookupTable, IntPtrTy);
136151 Constant *Target = llvm::ConstantExpr::getPtrToInt (Element, IntPtrTy);
@@ -148,7 +163,8 @@ static GlobalVariable *createRelLookupTable(Function &Func,
148163 return RelLookupTable;
149164}
150165
151- static void convertToRelLookupTable (GlobalVariable &LookupTable) {
166+ static void convertToRelLookupTable (LookupTableInfo &Info,
167+ GlobalVariable &LookupTable) {
152168 GetElementPtrInst *GEP =
153169 cast<GetElementPtrInst>(LookupTable.use_begin ()->getUser ());
154170 LoadInst *Load = cast<LoadInst>(GEP->use_begin ()->getUser ());
@@ -159,21 +175,21 @@ static void convertToRelLookupTable(GlobalVariable &LookupTable) {
159175 Function &Func = *BB->getParent ();
160176
161177 // Generate an array that consists of relative offsets.
162- GlobalVariable *RelLookupTable = createRelLookupTable (Func, LookupTable);
178+ GlobalVariable *RelLookupTable =
179+ createRelLookupTable (Info, Func, LookupTable);
163180
164181 // Place new instruction sequence before GEP.
165182 Builder.SetInsertPoint (GEP);
166- Value *Index = GEP->getOperand (2 );
167- IntegerType *IntTy = cast<IntegerType>(Index->getType ());
168- Value *Offset =
169- Builder.CreateShl (Index, ConstantInt::get (IntTy, 2 ), " reltable.shift" );
183+ IntegerType *IntTy = cast<IntegerType>(Info.Index ->getType ());
184+ Value *Offset = Builder.CreateShl (Info.Index , ConstantInt::get (IntTy, 2 ),
185+ " reltable.shift" );
170186
171187 // Insert the call to load.relative intrinsic before LOAD.
172188 // GEP might not be immediately followed by a LOAD, like it can be hoisted
173189 // outside the loop or another instruction might be inserted them in between.
174190 Builder.SetInsertPoint (Load);
175191 Function *LoadRelIntrinsic = llvm::Intrinsic::getOrInsertDeclaration (
176- &M, Intrinsic::load_relative, {Index->getType ()});
192+ &M, Intrinsic::load_relative, {Info. Index ->getType ()});
177193
178194 // Create a call to load.relative intrinsic that computes the target address
179195 // by adding base address (lookup table address) and relative offset.
@@ -205,10 +221,11 @@ static bool convertToRelativeLookupTables(
205221 bool Changed = false ;
206222
207223 for (GlobalVariable &GV : llvm::make_early_inc_range (M.globals ())) {
208- if (!shouldConvertToRelLookupTable (M, GV))
224+ LookupTableInfo Info;
225+ if (!shouldConvertToRelLookupTable (Info, M, GV))
209226 continue ;
210227
211- convertToRelLookupTable (GV);
228+ convertToRelLookupTable (Info, GV);
212229
213230 // Remove the original lookup table.
214231 GV.eraseFromParent ();
0 commit comments