@@ -194,6 +194,42 @@ class SPIRVEmitIntrinsics
194
194
195
195
void useRoundingMode (ConstrainedFPIntrinsic *FPI, IRBuilder<> &B);
196
196
197
+ // Tries to walk the type accessed by the given GEP instruction.
198
+ // For each nested type access, one of the 2 callbacks is called:
199
+ // - OnLiteralIndexing when the index is a known constant value.
200
+ // Parameters:
201
+ // PointedType: the pointed type resulting of this indexing.
202
+ // If the parent type is an array, this is the index in the array.
203
+ // If the parent type is a struct, this is the field index.
204
+ // Index: index of the element in the parent type.
205
+ // - OnDynamnicIndexing when the index is a non-constant value.
206
+ // This callback is only called when indexing into an array.
207
+ // Parameters:
208
+ // ElementType: the type of the elements stored in the parent array.
209
+ // Offset: the Value* containing the byte offset into the array.
210
+ // Return true if an error occured during the walk, false otherwise.
211
+ bool walkLogicalAccessChain (
212
+ GetElementPtrInst &GEP,
213
+ const std::function<void (Type *PointedType, uint64_t Index)>
214
+ &OnLiteralIndexing,
215
+ const std::function<void(Type *ElementType, Value *Offset)>
216
+ &OnDynamicIndexing);
217
+
218
+ // Returns the type accessed using the given GEP instruction by relying
219
+ // on the GEP type.
220
+ // FIXME: GEP types are not supposed to be used to retrieve the pointed
221
+ // type. This must be fixed.
222
+ Type *getGEPType (GetElementPtrInst *GEP);
223
+
224
+ // Returns the type accessed using the given GEP instruction by walking
225
+ // the source type using the GEP indices.
226
+ // FIXME: without help from the frontend, this method cannot reliably retrieve
227
+ // the stored type, nor can robustly determine the depth of the type
228
+ // we are accessing.
229
+ Type *getGEPTypeLogical (GetElementPtrInst *GEP);
230
+
231
+ Instruction *buildLogicalAccessChainFromGEP (GetElementPtrInst &GEP);
232
+
197
233
public:
198
234
static char ID;
199
235
SPIRVEmitIntrinsics (SPIRVTargetMachine *TM = nullptr )
@@ -246,6 +282,17 @@ bool expectIgnoredInIRTranslation(const Instruction *I) {
246
282
}
247
283
}
248
284
285
+ // Returns the source pointer from `I` ignoring intermediate ptrcast.
286
+ Value *getPointerRoot (Value *I) {
287
+ if (auto *II = dyn_cast<IntrinsicInst>(I)) {
288
+ if (II->getIntrinsicID () == Intrinsic::spv_ptrcast) {
289
+ Value *V = II->getArgOperand (0 );
290
+ return getPointerRoot (V);
291
+ }
292
+ }
293
+ return I;
294
+ }
295
+
249
296
} // namespace
250
297
251
298
char SPIRVEmitIntrinsics::ID = 0 ;
@@ -555,7 +602,111 @@ void SPIRVEmitIntrinsics::maybeAssignPtrType(Type *&Ty, Value *Op, Type *RefTy,
555
602
Ty = RefTy;
556
603
}
557
604
558
- Type *getGEPType (GetElementPtrInst *Ref) {
605
+ bool SPIRVEmitIntrinsics::walkLogicalAccessChain (
606
+ GetElementPtrInst &GEP,
607
+ const std::function<void (Type *, uint64_t )> &OnLiteralIndexing,
608
+ const std::function<void(Type *, Value *)> &OnDynamicIndexing) {
609
+ // We only rewrite i8* GEP. Other should be left as-is.
610
+ // Valid i8* GEP must always have a single index.
611
+ assert (GEP.getSourceElementType () ==
612
+ IntegerType::getInt8Ty (CurrF->getContext ()));
613
+ assert (GEP.getNumIndices () == 1 );
614
+
615
+ auto &DL = CurrF->getDataLayout ();
616
+ Value *Src = getPointerRoot (GEP.getPointerOperand ());
617
+ Type *CurType = deduceElementType (Src, true );
618
+
619
+ Value *Operand = *GEP.idx_begin ();
620
+ ConstantInt *CI = dyn_cast<ConstantInt>(Operand);
621
+ if (!CI) {
622
+ ArrayType *AT = dyn_cast<ArrayType>(CurType);
623
+ // Operand is not constant. Either we have an array and accept it, or we
624
+ // give up.
625
+ if (AT)
626
+ OnDynamicIndexing (AT->getElementType (), Operand);
627
+ return AT == nullptr ;
628
+ }
629
+
630
+ assert (CI);
631
+ uint64_t Offset = CI->getZExtValue ();
632
+
633
+ do {
634
+ if (ArrayType *AT = dyn_cast<ArrayType>(CurType)) {
635
+ uint32_t EltTypeSize = DL.getTypeSizeInBits (AT->getElementType ()) / 8 ;
636
+ assert (Offset < AT->getNumElements () * EltTypeSize);
637
+ uint64_t Index = Offset / EltTypeSize;
638
+ Offset = Offset - (Index * EltTypeSize);
639
+ CurType = AT->getElementType ();
640
+ OnLiteralIndexing (CurType, Index);
641
+ } else if (StructType *ST = dyn_cast<StructType>(CurType)) {
642
+ uint32_t StructSize = DL.getTypeSizeInBits (ST) / 8 ;
643
+ assert (Offset < StructSize);
644
+ const auto &STL = DL.getStructLayout (ST);
645
+ unsigned Element = STL->getElementContainingOffset (Offset);
646
+ Offset -= STL->getElementOffset (Element);
647
+ CurType = ST->getElementType (Element);
648
+ OnLiteralIndexing (CurType, Element);
649
+ } else {
650
+ // Vector type indexing should not use GEP.
651
+ // So if we have an index left, something is wrong. Giving up.
652
+ return true ;
653
+ }
654
+ } while (Offset > 0 );
655
+
656
+ return false ;
657
+ }
658
+
659
+ Instruction *
660
+ SPIRVEmitIntrinsics::buildLogicalAccessChainFromGEP (GetElementPtrInst &GEP) {
661
+ auto &DL = CurrF->getDataLayout ();
662
+ IRBuilder<> B (GEP.getParent ());
663
+ B.SetInsertPoint (&GEP);
664
+
665
+ std::vector<Value *> Indices;
666
+ Indices.push_back (ConstantInt::get (
667
+ IntegerType::getInt32Ty (CurrF->getContext ()), 0 , /* Signed= */ false ));
668
+ walkLogicalAccessChain (
669
+ GEP,
670
+ [&Indices, &B](Type *EltType, uint64_t Index) {
671
+ Indices.push_back (
672
+ ConstantInt::get (B.getInt64Ty (), Index, /* Signed= */ false ));
673
+ },
674
+ [&Indices, &B, &DL](Type *EltType, Value *Offset) {
675
+ uint32_t EltTypeSize = DL.getTypeSizeInBits (EltType) / 8 ;
676
+ Value *Index = B.CreateUDiv (
677
+ Offset, ConstantInt::get (Offset->getType (), EltTypeSize,
678
+ /* Signed= */ false ));
679
+ Indices.push_back (Index);
680
+ });
681
+
682
+ SmallVector<Type *, 2 > Types = {GEP.getType (), GEP.getOperand (0 )->getType ()};
683
+ SmallVector<Value *, 4 > Args;
684
+ Args.push_back (B.getInt1 (GEP.isInBounds ()));
685
+ Args.push_back (GEP.getOperand (0 ));
686
+ llvm::append_range (Args, Indices);
687
+ auto *NewI = B.CreateIntrinsic (Intrinsic::spv_gep, {Types}, {Args});
688
+ replaceAllUsesWithAndErase (B, &GEP, NewI);
689
+ return NewI;
690
+ }
691
+
692
+ Type *SPIRVEmitIntrinsics::getGEPTypeLogical (GetElementPtrInst *GEP) {
693
+
694
+ Type *CurType = GEP->getResultElementType ();
695
+
696
+ bool Interrupted = walkLogicalAccessChain (
697
+ *GEP, [&CurType](Type *EltType, uint64_t Index) { CurType = EltType; },
698
+ [&CurType](Type *EltType, Value *Index) { CurType = EltType; });
699
+
700
+ return Interrupted ? GEP->getResultElementType () : CurType;
701
+ }
702
+
703
+ Type *SPIRVEmitIntrinsics::getGEPType (GetElementPtrInst *Ref) {
704
+ if (Ref->getSourceElementType () ==
705
+ IntegerType::getInt8Ty (CurrF->getContext ()) &&
706
+ TM->getSubtargetImpl ()->isLogicalSPIRV ()) {
707
+ return getGEPTypeLogical (Ref);
708
+ }
709
+
559
710
Type *Ty = nullptr ;
560
711
// TODO: not sure if GetElementPtrInst::getTypeAtIndex() does anything
561
712
// useful here
@@ -1395,6 +1546,13 @@ Instruction *SPIRVEmitIntrinsics::visitSwitchInst(SwitchInst &I) {
1395
1546
}
1396
1547
1397
1548
Instruction *SPIRVEmitIntrinsics::visitGetElementPtrInst (GetElementPtrInst &I) {
1549
+ if (I.getSourceElementType () == IntegerType::getInt8Ty (CurrF->getContext ()) &&
1550
+ TM->getSubtargetImpl ()->isLogicalSPIRV ()) {
1551
+ Instruction *Result = buildLogicalAccessChainFromGEP (I);
1552
+ if (Result)
1553
+ return Result;
1554
+ }
1555
+
1398
1556
IRBuilder<> B (I.getParent ());
1399
1557
B.SetInsertPoint (&I);
1400
1558
SmallVector<Type *, 2 > Types = {I.getType (), I.getOperand (0 )->getType ()};
@@ -1588,7 +1746,24 @@ void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I,
1588
1746
}
1589
1747
if (GetElementPtrInst *GEPI = dyn_cast<GetElementPtrInst>(I)) {
1590
1748
Value *Pointer = GEPI->getPointerOperand ();
1591
- Type *OpTy = GEPI->getSourceElementType ();
1749
+ Type *OpTy = nullptr ;
1750
+
1751
+ // Knowing the accessed type is mandatory for logical SPIR-V. Sadly,
1752
+ // the GEP source element type should not be used for this purpose, and
1753
+ // the alternative type-scavenging method is not working.
1754
+ // Physical SPIR-V can work around this, but not logical, hence still
1755
+ // try to rely on the broken type scavenging for logical.
1756
+ bool IsRewrittenGEP =
1757
+ GEPI->getSourceElementType () == IntegerType::getInt8Ty (I->getContext ());
1758
+ if (IsRewrittenGEP && TM->getSubtargetImpl ()->isLogicalSPIRV ()) {
1759
+ Value *Src = getPointerRoot (Pointer);
1760
+ OpTy = GR->findDeducedElementType (Src);
1761
+ }
1762
+
1763
+ // In all cases, fall back to the GEP type if type scavenging failed.
1764
+ if (!OpTy)
1765
+ OpTy = GEPI->getSourceElementType ();
1766
+
1592
1767
replacePointerOperandWithPtrCast (I, Pointer, OpTy, 0 , B);
1593
1768
if (isNestedPointer (OpTy))
1594
1769
insertTodoType (Pointer);
0 commit comments