LLVM 24.0.0git
SPIRVLegalizePointerCast.cpp
Go to the documentation of this file.
1//===-- SPIRVLegalizePointerCast.cpp ----------------------*- C++ -*-===//
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// The LLVM IR has multiple legal patterns we cannot lower to Logical SPIR-V.
10// This pass modifies such loads to have an IR we can directly lower to valid
11// logical SPIR-V.
12// OpenCL can avoid this because they rely on ptrcast, which is not supported
13// by logical SPIR-V.
14//
15// This pass relies on the assign_ptr_type intrinsic to deduce the type of the
16// pointed values, must replace all occurences of `ptrcast`. This is why
17// unhandled cases are reported as unreachable: we MUST cover all cases.
18//
19// 1. Loading the first element of an array
20//
21// %array = [10 x i32]
22// %value = load i32, ptr %array
23//
24// LLVM can skip the GEP instruction, and only request loading the first 4
25// bytes. In logical SPIR-V, we need an OpAccessChain to access the first
26// element. This pass will add a getelementptr instruction before the load.
27//
28//
29// 2. Implicit downcast from load
30//
31// %1 = getelementptr <4 x i32>, ptr %vec4, i64 0
32// %2 = load <3 x i32>, ptr %1
33//
34// The pointer in the GEP instruction is only used for offset computations,
35// but it doesn't NEED to match the pointed type. OpAccessChain however
36// requires this. Also, LLVM loads define the bitwidth of the load, not the
37// pointer. In this example, we can guess %vec4 is a vec4 thanks to the GEP
38// instruction basetype, but we only want to load the first 3 elements, hence
39// do a partial load. In logical SPIR-V, this is not legal. What we must do
40// is load the full vector (basetype), extract 3 elements, and recombine them
41// to form a 3-element vector.
42//
43//===----------------------------------------------------------------------===//
44
45#include "SPIRV.h"
46#include "SPIRVSubtarget.h"
47#include "SPIRVTargetMachine.h"
48#include "SPIRVUtils.h"
49#include "llvm/IR/IRBuilder.h"
51#include "llvm/IR/Intrinsics.h"
52#include "llvm/IR/IntrinsicsSPIRV.h"
55
56using namespace llvm;
57
58namespace {
59class SPIRVLegalizePointerCastImpl {
60
61 // Builds the `spv_assign_type` assigning |Ty| to |Value| at the current
62 // builder position.
63 void buildAssignType(IRBuilder<> &B, Type *Ty, Value *Arg) {
64 Value *OfType = PoisonValue::get(Ty);
65 CallInst *AssignCI = buildIntrWithMD(Intrinsic::spv_assign_type,
66 {Arg->getType()}, OfType, Arg, {}, B);
67 GR->addAssignPtrTypeInstr(Arg, AssignCI);
68 }
69
70 static FixedVectorType *makeVectorFromTotalBits(Type *ElemTy,
71 TypeSize TotalBits) {
72 unsigned ElemBits = ElemTy->getScalarSizeInBits();
73 assert(ElemBits && TotalBits % ElemBits == 0 &&
74 "TotalBits must be divisible by element bit size");
75 return FixedVectorType::get(ElemTy, TotalBits / ElemBits);
76 }
77
78 Value *resizeVectorBitsWithShuffle(IRBuilder<> &B, Value *V,
79 FixedVectorType *DstTy) {
80 auto *SrcTy = cast<FixedVectorType>(V->getType());
81 assert(SrcTy->getElementType() == DstTy->getElementType() &&
82 "shuffle resize expects identical element types");
83
84 const unsigned NumNeeded = DstTy->getNumElements();
85 const unsigned NumSource = SrcTy->getNumElements();
86
87 SmallVector<int> Mask(NumNeeded);
88 for (unsigned I = 0; I < NumNeeded; ++I)
89 Mask[I] = (I < NumSource) ? static_cast<int>(I) : -1;
90
91 Value *Resized = B.CreateShuffleVector(V, V, Mask);
92 buildAssignType(B, DstTy, Resized);
93 return Resized;
94 }
95
96 // Loads parts of the vector of type |SourceType| from the pointer |Source|
97 // and create a new vector of type |TargetType|. |TargetType| must be a vector
98 // type.
99 // Returns the loaded value.
100 Value *loadVectorFromVector(IRBuilder<> &B, FixedVectorType *SourceType,
101 FixedVectorType *TargetType, Value *Source,
102 Align OriginalAlign) {
103 LoadInst *NewLoad = B.CreateLoad(SourceType, Source);
104 NewLoad->setAlignment(OriginalAlign);
105 buildAssignType(B, SourceType, NewLoad);
106 Value *AssignValue = NewLoad;
107 if (TargetType->getElementType() != SourceType->getElementType()) {
108 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
109 TypeSize TargetTypeSize = DL.getTypeSizeInBits(TargetType);
110 TypeSize SourceTypeSize = DL.getTypeSizeInBits(SourceType);
111
112 Value *BitcastSrcVal = NewLoad;
113 FixedVectorType *BitcastSrcTy =
114 cast<FixedVectorType>(BitcastSrcVal->getType());
115 FixedVectorType *BitcastDstTy = TargetType;
116
117 if (TargetTypeSize != SourceTypeSize) {
118 unsigned TargetElemBits =
119 TargetType->getElementType()->getScalarSizeInBits();
120 if (SourceTypeSize % TargetElemBits == 0) {
121 // No Resize needed. Same total bits as source, but use target element
122 // type.
123 BitcastDstTy = makeVectorFromTotalBits(TargetType->getElementType(),
124 SourceTypeSize);
125 } else {
126 // Resize source to target total bitwidth using source element type.
127 BitcastSrcTy = makeVectorFromTotalBits(SourceType->getElementType(),
128 TargetTypeSize);
129 BitcastSrcVal = resizeVectorBitsWithShuffle(B, NewLoad, BitcastSrcTy);
130 }
131 }
132 AssignValue =
133 B.CreateIntrinsic(Intrinsic::spv_bitcast,
134 {BitcastDstTy, BitcastSrcTy}, {BitcastSrcVal});
135 buildAssignType(B, BitcastDstTy, AssignValue);
136 if (BitcastDstTy == TargetType)
137 return AssignValue;
138 }
139
140 auto *AssignVecTy = cast<FixedVectorType>(AssignValue->getType());
141 const unsigned NumTarget = TargetType->getNumElements();
142 const unsigned NumSource = AssignVecTy->getNumElements();
143
144 // Optimizations may widen a narrow load to cover padding (e.g., loading a
145 // <1 x float> column as <4 x float>). Since extra lanes read trailing
146 // padding, insert only the valid lanes into a poison vector to avoid poison
147 // scalars.
148 if (NumTarget > NumSource) {
149 Value *Result = PoisonValue::get(TargetType);
150 buildAssignType(B, TargetType, Result);
151 for (unsigned I = 0; I < NumSource; ++I) {
152 Value *Scalar = extractScalarFromVector(B, AssignValue, I);
153 Result = makeInsertElement(B, Result, Scalar, I);
154 }
155 return Result;
156 }
157
158 assert(NumTarget < NumSource);
159 SmallVector<int> Mask(/* Size= */ NumTarget);
160 for (unsigned I = 0; I < NumTarget; ++I)
161 Mask[I] = I;
162 Value *Output = B.CreateShuffleVector(AssignValue, AssignValue, Mask);
163 buildAssignType(B, TargetType, Output);
164 return Output;
165 }
166
167 // Returns true if |FromTy| has a memory layout compatible with loading or
168 // storing |ToTy|.
169 bool isCompatibleMemoryLayout(Type *ToTy, Type *FromTy) {
170 if (ToTy == FromTy)
171 return true;
172 auto *SVT = dyn_cast<FixedVectorType>(FromTy);
173 auto *DVT = dyn_cast<FixedVectorType>(ToTy);
174 if (SVT && DVT)
175 return true;
176 auto *SAT = dyn_cast<ArrayType>(FromTy);
177 if (SAT && DVT) {
178 if (SAT->getElementType() == DVT->getElementType())
179 return true;
180 if (auto *MAT = dyn_cast<FixedVectorType>(SAT->getElementType()))
181 if (MAT->getElementType() == DVT->getElementType())
182 return true;
183 }
184 return false;
185 }
186
187 // Traverses the aggregate type to find the first sub-type that matches
188 // the TargetElemType's memory layout, optionally emitting a GEP intrinsic.
189 std::optional<std::pair<Value *, Type *>>
190 getPointerToFirstCompatibleType(IRBuilder<> &B, Value *BasePtr,
191 Type *PointerType, Type *TargetElemType,
192 bool IsInBounds) {
193 Type *CurrentTy = GR->findDeducedElementType(BasePtr);
194 assert(CurrentTy && "Could not deduce aggregate type");
195 SmallVector<Value *, 8> Args{/* isInBounds= */ B.getInt1(IsInBounds),
196 BasePtr};
197 Args.push_back(B.getInt32(0)); // Pointer offset
198
199 while (!isCompatibleMemoryLayout(TargetElemType, CurrentTy)) {
200 if (auto *ST = dyn_cast<StructType>(CurrentTy)) {
201 if (ST->getNumElements() == 0)
202 return std::nullopt;
203 CurrentTy = ST->getTypeAtIndex(0u);
204 } else if (auto *AT = dyn_cast<ArrayType>(CurrentTy)) {
205 CurrentTy = AT->getElementType();
206 } else if (auto *VT = dyn_cast<FixedVectorType>(CurrentTy)) {
207 CurrentTy = VT->getElementType();
208 } else {
209 return std::nullopt;
210 }
211 Args.push_back(B.getInt32(0));
212 }
213
214 Value *GEP = BasePtr;
215 if (Args.size() > 3) {
216 std::array<Type *, 2> Types = {PointerType, BasePtr->getType()};
217 GEP = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
218 GR->buildAssignPtr(B, CurrentTy, GEP);
219 }
220
221 return std::make_pair(GEP, CurrentTy);
222 }
223
224 static IntrinsicInst *getResourceGetPointer(Value *Ptr) {
225 if (auto *II = dyn_cast<IntrinsicInst>(Ptr))
226 if (II->getIntrinsicID() == Intrinsic::spv_resource_getpointer)
227 return II;
228 return nullptr;
229 }
230
231 Value *gepByteOffset(IRBuilder<> &B, Value *BasePtr, unsigned ByteOffset) {
232 if (ByteOffset == 0)
233 return BasePtr;
234
235 if (IntrinsicInst *ResourcePtr = getResourceGetPointer(BasePtr)) {
236 Value *Handle = ResourcePtr->getOperand(0);
237 Value *BaseOffset = ResourcePtr->getOperand(1);
238 Value *NewOffset;
239 if (auto *CI = dyn_cast<ConstantInt>(BaseOffset))
240 NewOffset =
241 ConstantInt::get(CI->getType(), CI->getZExtValue() + ByteOffset);
242 else
243 NewOffset = B.CreateAdd(
244 BaseOffset, ConstantInt::get(BaseOffset->getType(), ByteOffset));
246 ResourcePtr->getOperandBundlesAsDefs(OpBundles);
247 CallInst *ResourcePtrAtOffset = B.CreateCall(
248 ResourcePtr->getFunctionType(), ResourcePtr->getCalledOperand(),
249 {Handle, NewOffset}, OpBundles);
250 ResourcePtrAtOffset->setAttributes(ResourcePtr->getAttributes());
251 ResourcePtrAtOffset->setCallingConv(ResourcePtr->getCallingConv());
252 Type *I8Ty = Type::getInt8Ty(B.getContext());
253 GR->buildAssignPtr(B, I8Ty, ResourcePtrAtOffset);
254 return ResourcePtrAtOffset;
255 }
257 "byte layout pointer must come from spv.resource.getpointer");
258 }
259
260 Value *scalarToStoreInt(IRBuilder<> &B, Value *Scalar) {
261 Type *Ty = Scalar->getType();
262 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
263 Type *IntTy =
264 IntegerType::get(B.getContext(), DL.getTypeStoreSizeInBits(Ty));
265 if (Ty == IntTy)
266 return Scalar;
267 if (Ty->isIntOrIntVectorTy())
268 return B.CreateIntCast(Scalar, IntTy, /*isSigned=*/false);
269 return B.CreateIntrinsic(Intrinsic::spv_bitcast, {IntTy, Ty}, {Scalar});
270 }
271
272 Value *storeIntToScalar(IRBuilder<> &B, Value *IntVal, Type *ScalarTy) {
273 if (IntVal->getType() == ScalarTy)
274 return IntVal;
275 if (ScalarTy->isIntOrIntVectorTy())
276 return B.CreateIntCast(IntVal, ScalarTy, /*isSigned=*/false);
277 return B.CreateIntrinsic(Intrinsic::spv_bitcast,
278 {ScalarTy, IntVal->getType()}, {IntVal});
279 }
280
281 void storeScalarToByteLayout(IRBuilder<> &B, Value *Src, Value *Dst,
282 Align Alignment) {
283 LLVMContext &Ctx = B.getContext();
284 Type *I8Ty = Type::getInt8Ty(Ctx);
285 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
286 Value *IntVal = scalarToStoreInt(B, Src);
287 if (IntVal != Src)
288 buildAssignType(B, IntVal->getType(), IntVal);
289 unsigned NumBytes = DL.getTypeStoreSize(Src->getType());
290
291 auto StoreByte = [&](unsigned I, Value *Shifted) {
292 Value *Byte = B.CreateTrunc(Shifted, I8Ty);
293 buildAssignType(B, I8Ty, Byte);
294 Value *Ptr = gepByteOffset(B, Dst, I);
295 StoreInst *SI = B.CreateStore(Byte, Ptr);
296 SI->setAlignment(commonAlignment(Alignment, I));
297 };
298
299 if (NumBytes > 0)
300 StoreByte(0, IntVal);
301
302 for (unsigned I = 1; I < NumBytes; ++I) {
303 Value *Shifted =
304 B.CreateLShr(IntVal, ConstantInt::get(IntVal->getType(), 8 * I));
305 StoreByte(I, Shifted);
306 }
307 }
308
309 Value *loadScalarFromByteLayout(IRBuilder<> &B, Type *AccessTy, Value *Src,
310 Align Alignment) {
311 LLVMContext &Ctx = B.getContext();
312 Type *I8Ty = Type::getInt8Ty(Ctx);
313 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
314 unsigned NumBytes = DL.getTypeStoreSize(AccessTy);
315 Type *IntTy = IntegerType::get(Ctx, DL.getTypeStoreSizeInBits(AccessTy));
316 Value *IntVal = ConstantInt::get(IntTy, 0);
317
318 for (unsigned I = 0; I < NumBytes; ++I) {
319 Value *Ptr = gepByteOffset(B, Src, I);
320 LoadInst *LI = B.CreateLoad(I8Ty, Ptr);
321 LI->setAlignment(commonAlignment(Alignment, I));
322 buildAssignType(B, I8Ty, LI);
323 Value *Extended = B.CreateZExt(LI, IntTy);
324 buildAssignType(B, IntTy, Extended);
325
326 if (I == 0) {
328 } else {
329 Value *Shifted = B.CreateShl(Extended, ConstantInt::get(IntTy, 8 * I));
330 buildAssignType(B, IntTy, Shifted);
331 IntVal = B.CreateOr(IntVal, Shifted);
332 }
333 buildAssignType(B, IntTy, IntVal);
334 }
335
336 Value *Result = storeIntToScalar(B, IntVal, AccessTy);
337 if (Result != IntVal)
338 buildAssignType(B, AccessTy, Result);
339 return Result;
340 }
341
342 // Classifies a ptrcast reinterpretation: casted pointee matches the access
343 // type but differs from the original storage layout (e.g. i8 byte buffer as
344 // i32). ByteWise means multi-byte access must use per-byte i8 load/store.
345 bool shouldReinterpretByteWise(IRBuilder<> &B, Type *AccessTy,
346 Value *OriginalPtr) {
347 Type *OriginalElemTy = GR->findDeducedElementType(OriginalPtr);
348 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
349 if (OriginalElemTy && OriginalElemTy == Type::getInt8Ty(B.getContext()) &&
350 AccessTy->isSingleValueType() && DL.getTypeStoreSize(AccessTy) > 1)
351 return true;
352
353 return false;
354 }
355
356 bool tryReinterpretLoad(IRBuilder<> &B, Type *AccessTy, Value *OriginalPtr,
357 Value *CastedPtr, LoadInst *IllegalLoad) {
358 Type *CastedElemTy = GR->findDeducedElementType(CastedPtr);
359 if (!CastedElemTy || CastedElemTy != AccessTy)
360 return false;
361
362 Align Alignment = IllegalLoad->getAlign();
363 if (shouldReinterpretByteWise(B, AccessTy, OriginalPtr)) {
364 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
365 Value *Loaded;
366 if (auto *VT = dyn_cast<FixedVectorType>(AccessTy)) {
367 unsigned ElemSize = DL.getTypeStoreSize(VT->getElementType());
368 SmallVector<Value *, 4> LoadedElements;
369 for (unsigned I = 0; I < VT->getNumElements(); ++I) {
370 Value *ElemPtr = gepByteOffset(B, OriginalPtr, I * ElemSize);
371 LoadedElements.push_back(loadScalarFromByteLayout(
372 B, VT->getElementType(), ElemPtr,
373 commonAlignment(Alignment, I * ElemSize)));
374 }
375 Loaded = buildVectorFromLoadedElements(B, VT, LoadedElements);
376 } else {
377 Loaded = loadScalarFromByteLayout(B, AccessTy, OriginalPtr, Alignment);
378 buildAssignType(B, AccessTy, Loaded);
379 }
380 GR->replaceAllUsesWith(IllegalLoad, Loaded, /* DeleteOld= */ true);
381 DeadInstructions.push_back(IllegalLoad);
382 return true;
383 }
384
385 GR->buildAssignPtr(B, AccessTy, OriginalPtr);
386 LoadInst *LI = B.CreateLoad(AccessTy, OriginalPtr);
387 LI->setAlignment(Alignment);
388 buildAssignType(B, AccessTy, LI);
389 GR->replaceAllUsesWith(IllegalLoad, LI, /* DeleteOld= */ true);
390 DeadInstructions.push_back(IllegalLoad);
391 return true;
392 }
393
394 bool tryReinterpretStore(IRBuilder<> &B, Type *AccessTy, Value *OriginalPtr,
395 Value *CastedPtr, Value *StoreSrc, Align Alignment) {
396 Type *CastedElemTy = GR->findDeducedElementType(CastedPtr);
397 if (!CastedElemTy || CastedElemTy != AccessTy)
398 return false;
399
400 if (shouldReinterpretByteWise(B, AccessTy, OriginalPtr)) {
401 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
402 if (auto *VT = dyn_cast<FixedVectorType>(StoreSrc->getType())) {
403 unsigned ElemSize = DL.getTypeStoreSize(VT->getElementType());
404 for (unsigned I = 0; I < VT->getNumElements(); ++I) {
405 Value *Elem = extractScalarFromVector(B, StoreSrc, I);
406 Value *ElemPtr = gepByteOffset(B, OriginalPtr, I * ElemSize);
407 storeScalarToByteLayout(B, Elem, ElemPtr,
408 commonAlignment(Alignment, I * ElemSize));
409 }
410 } else {
411 storeScalarToByteLayout(B, StoreSrc, OriginalPtr, Alignment);
412 }
413 return true;
414 }
415
416 GR->buildAssignPtr(B, AccessTy, OriginalPtr);
417 StoreInst *SI = B.CreateStore(StoreSrc, OriginalPtr);
418 SI->setAlignment(Alignment);
419 return true;
420 }
421
422 // Builds a legalized load from a pointer, drilling down through
423 // memory layouts to find a compatible type. Load flags will be
424 // copied from |IllegalLoad|, which should be the load being legalized.
425 Value *buildLegalizedLoad(IRBuilder<> &B, Type *ElementType, Value *Source,
426 LoadInst *IllegalLoad, Value *CastedPtr) {
427 auto ResultOpt = getPointerToFirstCompatibleType(
428 B, Source, IllegalLoad->getPointerOperandType(), ElementType, false);
429 if (!ResultOpt) {
430 if (tryReinterpretLoad(B, ElementType, Source, CastedPtr, IllegalLoad))
431 return nullptr;
432 llvm_unreachable("Failed to load from aggregate: "
433 "Could not find compatible memory layout.");
434 }
435 auto [GEP, CurrentTy] = *ResultOpt;
436
437 auto *SAT = dyn_cast<ArrayType>(CurrentTy);
438 auto *SVT = dyn_cast<FixedVectorType>(CurrentTy);
439 auto *DVT = dyn_cast<FixedVectorType>(ElementType);
440 auto *MAT =
441 SAT ? dyn_cast<FixedVectorType>(SAT->getElementType()) : nullptr;
442
443 if (ElementType == CurrentTy) {
444 LoadInst *LI = B.CreateLoad(ElementType, GEP);
445 LI->setAlignment(IllegalLoad->getAlign());
446 buildAssignType(B, ElementType, LI);
447 return LI;
448 }
449 if (SVT && DVT)
450 return loadVectorFromVector(B, SVT, DVT, GEP, IllegalLoad->getAlign());
451 if (SAT && DVT && SAT->getElementType() == DVT->getElementType())
452 return loadVectorFromArray(B, DVT, GEP, IllegalLoad->getAlign());
453 if (MAT && DVT && MAT->getElementType() == DVT->getElementType())
454 return loadVectorFromMatrixArray(B, DVT, GEP, MAT,
455 IllegalLoad->getAlign());
456
457 llvm_unreachable("Failed to load from aggregate.");
458 }
459
460 Value *
461 buildVectorFromLoadedElements(IRBuilder<> &B, FixedVectorType *TargetType,
462 SmallVector<Value *, 4> &LoadedElements) {
463 // <1 x T> shares the SPIR-V type with T, so emitting OpCompositeInsert on
464 // a scalar would be invalid. Bridge with spv_bitcast instead unless
465 // SPV_EXT_long_vector is available.
466 bool CanUseAnyVectorRank = TM.getSubtargetImpl()->canUseExtension(
467 SPIRV::Extension::SPV_EXT_long_vector);
468 if (TargetType->getNumElements() == 1 && !CanUseAnyVectorRank) {
469 Value *Scalar = LoadedElements[0];
470 Value *NewVector = B.CreateIntrinsic(
471 Intrinsic::spv_bitcast, {TargetType, Scalar->getType()}, {Scalar});
472 buildAssignType(B, TargetType, NewVector);
473 return NewVector;
474 }
475
476 // Build the vector from the loaded elements.
477 Value *NewVector = PoisonValue::get(TargetType);
478 buildAssignType(B, TargetType, NewVector);
479
480 for (unsigned I = 0, E = TargetType->getNumElements(); I < E; ++I) {
481 Value *Index = B.getInt32(I);
483 TargetType->getElementType(),
484 Index->getType()};
485 SmallVector<Value *> Args = {NewVector, LoadedElements[I], Index};
486 NewVector = B.CreateIntrinsic(Intrinsic::spv_insertelt, {Types}, {Args});
487 buildAssignType(B, TargetType, NewVector);
488 }
489 return NewVector;
490 }
491
492 // Loads elements from a matrix with an array of vector memory layout and
493 // constructs a vector.
494 Value *loadVectorFromMatrixArray(IRBuilder<> &B, FixedVectorType *TargetType,
495 Value *Source, FixedVectorType *ArrElemVecTy,
496 Align OriginalAlign) {
497 Type *TargetElemTy = TargetType->getElementType();
498 unsigned ScalarsPerArrayElement = ArrElemVecTy->getNumElements();
499 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
500 uint64_t ArrElemVecSize = DL.getTypeAllocSize(ArrElemVecTy);
501 // Load each element of the array.
502 SmallVector<Value *, 4> LoadedElements;
503 std::array<Type *, 2> Types = {Source->getType(), Source->getType()};
504 for (unsigned I = 0, E = TargetType->getNumElements(); I < E; ++I) {
505 unsigned ArrayIndex = I / ScalarsPerArrayElement;
506 unsigned ElementIndexInArrayElem = I % ScalarsPerArrayElement;
507 // Create a GEP to access the i-th element of the array.
508 std::array<Value *, 4> Args = {
509 B.getInt1(/*Inbounds=*/false), Source, B.getInt32(0),
510 ConstantInt::get(B.getInt32Ty(), ArrayIndex)};
511 auto *ElementPtr = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
512 GR->buildAssignPtr(B, ArrElemVecTy, ElementPtr);
513 LoadInst *LoadVec = B.CreateLoad(ArrElemVecTy, ElementPtr);
514 LoadVec->setAlignment(
515 commonAlignment(OriginalAlign, ArrayIndex * ArrElemVecSize));
516 buildAssignType(B, ArrElemVecTy, LoadVec);
517 LoadedElements.push_back(makeExtractElement(B, TargetElemTy, LoadVec,
518 ElementIndexInArrayElem));
519 }
520 return buildVectorFromLoadedElements(B, TargetType, LoadedElements);
521 }
522
523 // Loads elements from an array and constructs a vector.
524 Value *loadVectorFromArray(IRBuilder<> &B, FixedVectorType *TargetType,
525 Value *Source, Align OriginalAlign) {
526 // Load each element of the array.
527 SmallVector<Value *, 4> LoadedElements;
528 std::array<Type *, 2> Types = {Source->getType(), Source->getType()};
529 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
530 uint64_t ElemSize = DL.getTypeAllocSize(TargetType->getElementType());
531 for (unsigned I = 0, E = TargetType->getNumElements(); I < E; ++I) {
532 // Create a GEP to access the i-th element of the array.
533 std::array<Value *, 4> Args = {B.getInt1(/*Inbounds=*/false), Source,
534 B.getInt32(0),
535 ConstantInt::get(B.getInt32Ty(), I)};
536 auto *ElementPtr = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
537 GR->buildAssignPtr(B, TargetType->getElementType(), ElementPtr);
538
539 // Load the value from the element pointer.
540 LoadInst *Load = B.CreateLoad(TargetType->getElementType(), ElementPtr);
541 Load->setAlignment(commonAlignment(OriginalAlign, I * ElemSize));
542 buildAssignType(B, TargetType->getElementType(), Load);
543 LoadedElements.push_back(Load);
544 }
545 return buildVectorFromLoadedElements(B, TargetType, LoadedElements);
546 }
547
548 // Stores elements from a vector into a matrix (an array of vectors).
549 void storeMatrixArrayFromVector(IRBuilder<> &B, Value *SrcVector,
550 Value *DstArrayPtr, ArrayType *ArrTy,
551 Align Alignment) {
552 auto *SrcVecTy = cast<FixedVectorType>(SrcVector->getType());
553 auto *ArrElemVecTy = cast<FixedVectorType>(ArrTy->getElementType());
554 Type *ElemTy = ArrElemVecTy->getElementType();
555 unsigned ScalarsPerArrayElement = ArrElemVecTy->getNumElements();
556 unsigned SrcNumElements = SrcVecTy->getNumElements();
557 assert(
558 SrcNumElements % ScalarsPerArrayElement == 0 &&
559 "Source vector size must be a multiple of array element vector size");
560
561 std::array<Type *, 2> Types = {DstArrayPtr->getType(),
562 DstArrayPtr->getType()};
563 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
564 uint64_t ArrElemVecSize = DL.getTypeAllocSize(ArrElemVecTy);
565
566 for (unsigned I = 0; I < SrcNumElements; I += ScalarsPerArrayElement) {
567 unsigned ArrayIndex = I / ScalarsPerArrayElement;
568 // Create a GEP to access the array element.
569 std::array<Value *, 4> Args = {
570 B.getInt1(/*Inbounds=*/false), DstArrayPtr, B.getInt32(0),
571 ConstantInt::get(B.getInt32Ty(), ArrayIndex)};
572 auto *ElementPtr = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
573 GR->buildAssignPtr(B, ArrElemVecTy, ElementPtr);
574
575 // Extract scalar elements from the source vector for this array slot.
576 SmallVector<Value *, 4> Elements;
577 for (unsigned J = 0; J < ScalarsPerArrayElement; ++J)
578 Elements.push_back(makeExtractElement(B, ElemTy, SrcVector, I + J));
579
580 // Build a vector from the extracted elements and store it.
581 Value *Vec = buildVectorFromLoadedElements(B, ArrElemVecTy, Elements);
582 StoreInst *SI = B.CreateStore(Vec, ElementPtr);
583 SI->setAlignment(commonAlignment(Alignment, ArrayIndex * ArrElemVecSize));
584 }
585 }
586
587 // Stores elements from a vector into an array.
588 void storeArrayFromVector(IRBuilder<> &B, Value *SrcVector,
589 Value *DstArrayPtr, ArrayType *ArrTy,
590 Align Alignment) {
591 auto *VecTy = cast<FixedVectorType>(SrcVector->getType());
592 Type *ElemTy = ArrTy->getElementType();
593
594 // Ensure the element types of the array and vector are the same.
595 assert(VecTy->getElementType() == ElemTy &&
596 "Element types of array and vector must be the same.");
597 std::array<Type *, 2> Types = {DstArrayPtr->getType(),
598 DstArrayPtr->getType()};
599 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
600 uint64_t ElemSize = DL.getTypeAllocSize(ElemTy);
601
602 for (unsigned I = 0, E = VecTy->getNumElements(); I < E; ++I) {
603 // Create a GEP to access the i-th element of the array.
604 std::array<Value *, 4> Args = {B.getInt1(/*Inbounds=*/false), DstArrayPtr,
605 B.getInt32(0),
606 ConstantInt::get(B.getInt32Ty(), I)};
607 auto *ElementPtr = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
608 GR->buildAssignPtr(B, ElemTy, ElementPtr);
609
610 // Extract the element from the vector and store it.
611 bool CanUseAnyVectorRank = TM.getSubtargetImpl()->canUseExtension(
612 SPIRV::Extension::SPV_EXT_long_vector);
613 Value *Element = (E == 1 && !CanUseAnyVectorRank)
614 ? SrcVector
615 : makeExtractElement(B, ElemTy, SrcVector, I);
616 StoreInst *SI = B.CreateStore(Element, ElementPtr);
617 SI->setAlignment(commonAlignment(Alignment, I * ElemSize));
618 }
619 }
620
621 // Replaces the load instruction to get rid of the ptrcast used as source
622 // operand.
623 void transformLoad(IRBuilder<> &B, LoadInst *LI, Value *CastedOperand,
624 Value *OriginalOperand) {
625 Type *ToTy = GR->findDeducedElementType(CastedOperand);
626 B.SetInsertPoint(LI);
627
628 Value *Output =
629 buildLegalizedLoad(B, ToTy, OriginalOperand, LI, CastedOperand);
630 if (!Output)
631 return;
632
633 GR->replaceAllUsesWith(LI, Output, /* DeleteOld= */ true);
634 DeadInstructions.push_back(LI);
635 }
636
637 // Creates an spv_insertelt instruction (equivalent to llvm's insertelement).
638 Value *makeInsertElement(IRBuilder<> &B, Value *Vector, Value *Element,
639 unsigned Index) {
640 Type *Int32Ty = Type::getInt32Ty(B.getContext());
641 SmallVector<Type *, 4> Types = {Vector->getType(), Vector->getType(),
642 Element->getType(), Int32Ty};
643 SmallVector<Value *> Args = {Vector, Element, B.getInt32(Index)};
644 Value *NewI = B.CreateIntrinsic(Intrinsic::spv_insertelt, {Types}, {Args});
645 buildAssignType(B, Vector->getType(), NewI);
646 return NewI;
647 }
648
649 // Creates an spv_extractelt instruction (equivalent to llvm's
650 // extractelement).
651 Value *makeExtractElement(IRBuilder<> &B, Type *ElementType, Value *Vector,
652 unsigned Index) {
653 Type *Int32Ty = Type::getInt32Ty(B.getContext());
654 SmallVector<Type *, 3> Types = {ElementType, Vector->getType(), Int32Ty};
655 SmallVector<Value *> Args = {Vector, B.getInt32(Index)};
656 Value *NewI = B.CreateIntrinsic(Intrinsic::spv_extractelt, {Types}, {Args});
657 buildAssignType(B, ElementType, NewI);
658 return NewI;
659 }
660
661 // Extracts scalar element |Index| from |Vector|. A <1 x T> vector shares its
662 // SPIR-V type with the scalar T, so a plain extractelement would be invalid;
663 // bridge it with spv_bitcast instead.
664 Value *extractScalarFromVector(IRBuilder<> &B, Value *Vector,
665 unsigned Index) {
666 auto *VecTy = cast<FixedVectorType>(Vector->getType());
667 Type *ElemTy = VecTy->getElementType();
668 if (VecTy->getNumElements() == 1) {
669 Value *Scalar =
670 B.CreateIntrinsic(Intrinsic::spv_bitcast, {ElemTy, VecTy}, {Vector});
671 buildAssignType(B, ElemTy, Scalar);
672 return Scalar;
673 }
674 return makeExtractElement(B, ElemTy, Vector, Index);
675 }
676
677 // Stores the given Src vector operand into the Dst vector, adjusting the size
678 // if required.
679 Value *storeVectorFromVector(IRBuilder<> &B, Value *Src, Value *Dst,
680 Align Alignment) {
681 FixedVectorType *SrcType = cast<FixedVectorType>(Src->getType());
682 FixedVectorType *DstType =
683 cast<FixedVectorType>(GR->findDeducedElementType(Dst));
684 auto dstNumElements = DstType->getNumElements();
685 auto srcNumElements = SrcType->getNumElements();
686
687 // if the element type differs, it is a bitcast.
688 if (DstType->getElementType() != SrcType->getElementType()) {
689 // Support bitcast between vectors of different sizes only if
690 // the total bitwidth is the same.
691 [[maybe_unused]] auto dstBitWidth =
692 DstType->getElementType()->getScalarSizeInBits() * dstNumElements;
693 [[maybe_unused]] auto srcBitWidth =
694 SrcType->getElementType()->getScalarSizeInBits() * srcNumElements;
695 assert(dstBitWidth == srcBitWidth &&
696 "Unsupported bitcast between vectors of different sizes.");
697
698 Src =
699 B.CreateIntrinsic(Intrinsic::spv_bitcast, {DstType, SrcType}, {Src});
700 buildAssignType(B, DstType, Src);
701 SrcType = DstType;
702
703 StoreInst *SI = B.CreateStore(Src, Dst);
704 SI->setAlignment(Alignment);
705 return SI;
706 }
707
708 assert(DstType->getNumElements() >= SrcType->getNumElements());
709 LoadInst *LI = B.CreateLoad(DstType, Dst);
710 LI->setAlignment(Alignment);
711 Value *OldValues = LI;
712 buildAssignType(B, OldValues->getType(), OldValues);
713
714 for (unsigned I = 0; I < SrcType->getNumElements(); ++I) {
715 Value *Element = extractScalarFromVector(B, Src, I);
716 OldValues = makeInsertElement(B, OldValues, Element, I);
717 }
718
719 StoreInst *SI = B.CreateStore(OldValues, Dst);
720 SI->setAlignment(Alignment);
721 return SI;
722 }
723
724 // Builds a legalized store to a pointer, drilling down through
725 // memory layouts to find a compatible type.
726 void buildLegalizedStore(IRBuilder<> &B, Value *Src, Value *Dst,
727 Align Alignment, Value *CastedPtr,
728 Instruction *IllegalStore) {
729 auto ResultOpt = getPointerToFirstCompatibleType(B, Dst, Dst->getType(),
730 Src->getType(), true);
731 if (!ResultOpt) {
732 if (tryReinterpretStore(B, Src->getType(), Dst, CastedPtr, Src,
733 Alignment))
734 return;
735 llvm_unreachable("Failed to store to aggregate: "
736 "Could not find compatible memory layout.");
737 }
738 auto [GEP, CurrentTy] = *ResultOpt;
739
740 auto *DAT = dyn_cast<ArrayType>(CurrentTy);
741 auto *DVT = dyn_cast<FixedVectorType>(CurrentTy);
742 auto *SVT = dyn_cast<FixedVectorType>(Src->getType());
743 auto *DMAT =
744 DAT ? dyn_cast<FixedVectorType>(DAT->getElementType()) : nullptr;
745
746 if (Src->getType() == CurrentTy) {
747 StoreInst *SI = B.CreateStore(Src, GEP);
748 SI->setAlignment(Alignment);
749 return;
750 }
751 if (DVT && SVT) {
752 storeVectorFromVector(B, Src, GEP, Alignment);
753 return;
754 }
755 if (DAT && SVT && SVT->getElementType() == DAT->getElementType()) {
756 storeArrayFromVector(B, Src, GEP, DAT, Alignment);
757 return;
758 }
759 if (DMAT && SVT && DMAT->getElementType() == SVT->getElementType()) {
760 storeMatrixArrayFromVector(B, Src, GEP, DAT, Alignment);
761 return;
762 }
763
764 llvm_unreachable("Failed to store to aggregate.");
765 }
766
767 // Transforms a store instruction (or SPV intrinsic) using a ptrcast as
768 // operand into a valid logical SPIR-V store with no ptrcast.
769 void transformStore(IRBuilder<> &B, Instruction *IllegalStore, Value *Src,
770 Value *Dst, Value *CastedOperand, Align Alignment) {
771 B.SetInsertPoint(IllegalStore);
772 buildLegalizedStore(B, Src, Dst, Alignment, CastedOperand, IllegalStore);
773 DeadInstructions.push_back(IllegalStore);
774 }
775
776 void legalizePointerCast(IntrinsicInst *II) {
777 Value *CastedOperand = II;
778 Value *OriginalOperand = II->getOperand(0);
779
780 IRBuilder<> B(II->getContext());
781 std::vector<Value *> Users;
782 for (Use &U : II->uses())
783 Users.push_back(U.getUser());
784
785 for (Value *User : Users) {
786 if (LoadInst *LI = dyn_cast<LoadInst>(User)) {
787 transformLoad(B, LI, CastedOperand, OriginalOperand);
788 continue;
789 }
790
791 if (StoreInst *SI = dyn_cast<StoreInst>(User)) {
792 transformStore(B, SI, SI->getValueOperand(), OriginalOperand,
793 CastedOperand, SI->getAlign());
794 continue;
795 }
796
797 if (IntrinsicInst *Intrin = dyn_cast<IntrinsicInst>(User)) {
798 if (Intrin->getIntrinsicID() == Intrinsic::spv_assign_ptr_type) {
799 DeadInstructions.push_back(Intrin);
800 continue;
801 }
802
803 if (Intrin->getIntrinsicID() == Intrinsic::spv_gep) {
804 GR->replaceAllUsesWith(CastedOperand, OriginalOperand,
805 /* DeleteOld= */ false);
806 continue;
807 }
808
809 if (Intrin->getIntrinsicID() == Intrinsic::spv_store) {
811 if (ConstantInt *C = dyn_cast<ConstantInt>(Intrin->getOperand(3)))
812 Alignment = Align(C->getZExtValue());
813 transformStore(B, Intrin, Intrin->getArgOperand(0), OriginalOperand,
814 CastedOperand, Alignment);
815 continue;
816 }
817 }
818
819 llvm_unreachable("Unsupported ptrcast user. Please fix.");
820 }
821
822 DeadInstructions.push_back(II);
823 }
824
825public:
826 SPIRVLegalizePointerCastImpl(const SPIRVTargetMachine &TM) : TM(TM) {}
827
828 bool run(Function &F) {
829 const SPIRVSubtarget &ST = TM.getSubtarget<SPIRVSubtarget>(F);
830 GR = ST.getSPIRVGlobalRegistry();
831 DeadInstructions.clear();
832
833 std::vector<IntrinsicInst *> WorkList;
834 for (auto &BB : F) {
835 for (auto &I : BB) {
836 auto *II = dyn_cast<IntrinsicInst>(&I);
837 if (II && II->getIntrinsicID() == Intrinsic::spv_ptrcast)
838 WorkList.push_back(II);
839 }
840 }
841
842 for (IntrinsicInst *II : WorkList)
843 legalizePointerCast(II);
844
845 for (Instruction *I : DeadInstructions)
846 I->eraseFromParent();
847
848 return DeadInstructions.size() != 0;
849 }
850
851private:
852 const SPIRVTargetMachine &TM;
853 SPIRVGlobalRegistry *GR = nullptr;
854 std::vector<Instruction *> DeadInstructions;
855};
856
857class SPIRVLegalizePointerCastLegacy : public FunctionPass {
858public:
859 static char ID;
860 SPIRVLegalizePointerCastLegacy(const SPIRVTargetMachine &TM)
861 : FunctionPass(ID), TM(TM) {}
862
863 bool runOnFunction(Function &F) override {
864 return SPIRVLegalizePointerCastImpl(TM).run(F);
865 }
866
867private:
868 const SPIRVTargetMachine &TM;
869};
870} // namespace
871
874 return SPIRVLegalizePointerCastImpl(TM).run(F) ? PreservedAnalyses::none()
876}
877
878char SPIRVLegalizePointerCastLegacy::ID = 0;
879INITIALIZE_PASS(SPIRVLegalizePointerCastLegacy, "spirv-legalize-pointer-cast",
880 "SPIRV legalize pointer cast pass", false, false)
881
883 return new SPIRVLegalizePointerCastLegacy(*TM);
884}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static bool runOnFunction(Function &F, bool PostInlining)
Hexagon Common GEP
iv Induction Variable Users
Definition IVUsers.cpp:48
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
uint64_t IntrinsicInst * II
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
void setCallingConv(CallingConv::ID CC)
void setAttributes(AttributeList A)
Set the attributes for this call.
unsigned getNumElements() const
static LLVM_ABI FixedVectorType * get(Type *ElementType, unsigned NumElts)
Definition Type.cpp:843
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition Type.cpp:338
void setAlignment(Align Align)
Type * getPointerOperandType() const
Align getAlign() const
Return the alignment of the access that is being performed.
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM)
void push_back(const T &Elt)
bool isIntOrIntVectorTy() const
Return true if this is an integer type or a vector of integer types.
Definition Type.h:258
bool isSingleValueType() const
Return true if the type is a valid type for a register in codegen.
Definition Type.h:306
LLVM_ABI unsigned getScalarSizeInBits() const LLVM_READONLY
If this is a vector type, return the getPrimitiveSizeInBits value for the element type.
Definition Type.cpp:222
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
Type * getElementType() const
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
ElementType
The element type of an SRV or UAV resource.
Definition DXILABI.h:68
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
@ Load
The value being inserted comes from a load (InsertElement only).
CallInst * buildIntrWithMD(Intrinsic::ID IntrID, ArrayRef< Type * > Types, Value *Arg, Value *Arg2, ArrayRef< Constant * > Imms, IRBuilder<> &B)
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
Align commonAlignment(Align A, uint64_t Offset)
Returns the alignment that satisfies both alignments.
Definition Alignment.h:201
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
FunctionPass * createSPIRVLegalizePointerCastPass(SPIRVTargetMachine *TM)