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
46#include "SPIRV.h"
47#include "SPIRVSubtarget.h"
48#include "SPIRVTargetMachine.h"
49#include "SPIRVUtils.h"
50#include "llvm/IR/IRBuilder.h"
52#include "llvm/IR/Intrinsics.h"
53#include "llvm/IR/IntrinsicsSPIRV.h"
56
57using namespace llvm;
58
59namespace {
60class SPIRVLegalizePointerCastImpl {
61
62 // Builds the `spv_assign_type` assigning |Ty| to |Value| at the current
63 // builder position.
64 void buildAssignType(IRBuilder<> &B, Type *Ty, Value *Arg) {
65 Value *OfType = PoisonValue::get(Ty);
66 CallInst *AssignCI = buildIntrWithMD(Intrinsic::spv_assign_type,
67 {Arg->getType()}, OfType, Arg, {}, B);
68 GR->addAssignPtrTypeInstr(Arg, AssignCI);
69 }
70
71 static FixedVectorType *makeVectorFromTotalBits(Type *ElemTy,
72 TypeSize TotalBits) {
73 unsigned ElemBits = ElemTy->getScalarSizeInBits();
74 assert(ElemBits && TotalBits % ElemBits == 0 &&
75 "TotalBits must be divisible by element bit size");
76 return FixedVectorType::get(ElemTy, TotalBits / ElemBits);
77 }
78
79 Value *resizeVectorBitsWithShuffle(IRBuilder<> &B, Value *V,
80 FixedVectorType *DstTy) {
81 auto *SrcTy = cast<FixedVectorType>(V->getType());
82 assert(SrcTy->getElementType() == DstTy->getElementType() &&
83 "shuffle resize expects identical element types");
84
85 const unsigned NumNeeded = DstTy->getNumElements();
86 const unsigned NumSource = SrcTy->getNumElements();
87
88 SmallVector<int> Mask(NumNeeded);
89 for (unsigned I = 0; I < NumNeeded; ++I)
90 Mask[I] = (I < NumSource) ? static_cast<int>(I) : -1;
91
92 Value *Resized = B.CreateShuffleVector(V, V, Mask);
93 buildAssignType(B, DstTy, Resized);
94 return Resized;
95 }
96
97 // Loads parts of the vector of type |SourceType| from the pointer |Source|
98 // and create a new vector of type |TargetType|. |TargetType| must be a vector
99 // type.
100 // Returns the loaded value.
101 Value *loadVectorFromVector(IRBuilder<> &B, FixedVectorType *SourceType,
102 FixedVectorType *TargetType, Value *Source,
103 Align OriginalAlign) {
104 LoadInst *NewLoad = B.CreateLoad(SourceType, Source);
105 NewLoad->setAlignment(OriginalAlign);
106 buildAssignType(B, SourceType, NewLoad);
107 Value *AssignValue = NewLoad;
108 if (TargetType->getElementType() != SourceType->getElementType()) {
109 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
110 TypeSize TargetTypeSize = DL.getTypeSizeInBits(TargetType);
111 TypeSize SourceTypeSize = DL.getTypeSizeInBits(SourceType);
112
113 Value *BitcastSrcVal = NewLoad;
114 FixedVectorType *BitcastSrcTy =
115 cast<FixedVectorType>(BitcastSrcVal->getType());
116 FixedVectorType *BitcastDstTy = TargetType;
117
118 if (TargetTypeSize != SourceTypeSize) {
119 unsigned TargetElemBits =
120 TargetType->getElementType()->getScalarSizeInBits();
121 if (SourceTypeSize % TargetElemBits == 0) {
122 // No Resize needed. Same total bits as source, but use target element
123 // type.
124 BitcastDstTy = makeVectorFromTotalBits(TargetType->getElementType(),
125 SourceTypeSize);
126 } else {
127 // Resize source to target total bitwidth using source element type.
128 BitcastSrcTy = makeVectorFromTotalBits(SourceType->getElementType(),
129 TargetTypeSize);
130 BitcastSrcVal = resizeVectorBitsWithShuffle(B, NewLoad, BitcastSrcTy);
131 }
132 }
133 AssignValue =
134 B.CreateIntrinsic(Intrinsic::spv_bitcast,
135 {BitcastDstTy, BitcastSrcTy}, {BitcastSrcVal});
136 buildAssignType(B, BitcastDstTy, AssignValue);
137 if (BitcastDstTy == TargetType)
138 return AssignValue;
139 }
140
141 auto *AssignVecTy = cast<FixedVectorType>(AssignValue->getType());
142 const unsigned NumTarget = TargetType->getNumElements();
143 const unsigned NumSource = AssignVecTy->getNumElements();
144
145 // Optimizations may widen a narrow load to cover padding (e.g., loading a
146 // <1 x float> column as <4 x float>). Since extra lanes read trailing
147 // padding, insert only the valid lanes into a poison vector to avoid poison
148 // scalars.
149 if (NumTarget > NumSource) {
150 Value *Result = PoisonValue::get(TargetType);
151 buildAssignType(B, TargetType, Result);
152 for (unsigned I = 0; I < NumSource; ++I) {
153 Value *Scalar = extractScalarFromVector(B, AssignValue, I);
154 Result = makeInsertElement(B, Result, Scalar, I);
155 }
156 return Result;
157 }
158
159 assert(NumTarget < NumSource);
160 SmallVector<int> Mask(/* Size= */ NumTarget);
161 for (unsigned I = 0; I < NumTarget; ++I)
162 Mask[I] = I;
163 Value *Output = B.CreateShuffleVector(AssignValue, AssignValue, Mask);
164 buildAssignType(B, TargetType, Output);
165 return Output;
166 }
167
168 // Returns true if |FromTy| has a memory layout compatible with loading or
169 // storing |ToTy|.
170 bool isCompatibleMemoryLayout(Type *ToTy, Type *FromTy) {
171 if (ToTy == FromTy)
172 return true;
173 auto *SVT = dyn_cast<FixedVectorType>(FromTy);
174 auto *DVT = dyn_cast<FixedVectorType>(ToTy);
175 if (SVT && DVT)
176 return true;
177 auto *SAT = dyn_cast<ArrayType>(FromTy);
178 if (SAT && DVT) {
179 if (SAT->getElementType() == DVT->getElementType())
180 return true;
181 if (auto *MAT = dyn_cast<FixedVectorType>(SAT->getElementType()))
182 if (MAT->getElementType() == DVT->getElementType())
183 return true;
184 }
185 return false;
186 }
187
188 // Traverses the aggregate type to find the first sub-type that matches
189 // the TargetElemType's memory layout, optionally emitting a GEP intrinsic.
190 std::optional<std::pair<Value *, Type *>>
191 getPointerToFirstCompatibleType(IRBuilder<> &B, Value *BasePtr,
192 Type *PointerType, Type *TargetElemType,
193 bool IsInBounds) {
194 Type *CurrentTy = GR->findDeducedElementType(BasePtr);
195 assert(CurrentTy && "Could not deduce aggregate type");
196 SmallVector<Value *, 8> Args{/* isInBounds= */ B.getInt1(IsInBounds),
197 BasePtr};
198 Args.push_back(B.getInt32(0)); // Pointer offset
199
200 while (!isCompatibleMemoryLayout(TargetElemType, CurrentTy)) {
201 if (auto *ST = dyn_cast<StructType>(CurrentTy)) {
202 if (ST->getNumElements() == 0)
203 return std::nullopt;
204 CurrentTy = ST->getTypeAtIndex(0u);
205 } else if (auto *AT = dyn_cast<ArrayType>(CurrentTy)) {
206 CurrentTy = AT->getElementType();
207 } else if (auto *VT = dyn_cast<FixedVectorType>(CurrentTy)) {
208 CurrentTy = VT->getElementType();
209 } else {
210 return std::nullopt;
211 }
212 Args.push_back(B.getInt32(0));
213 }
214
215 Value *GEP = BasePtr;
216 if (Args.size() > 3) {
217 std::array<Type *, 2> Types = {PointerType, BasePtr->getType()};
218 GEP = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
219 GR->buildAssignPtr(B, CurrentTy, GEP);
220 }
221
222 return std::make_pair(GEP, CurrentTy);
223 }
224
225 // Builds a legalized load from a pointer, drilling down through
226 // memory layouts to find a compatible type. Load flags will be
227 // copied from |BadLoad|, which should be the load being legalized.
228 Value *buildLegalizedLoad(IRBuilder<> &B, Type *ElementType, Value *Source,
229 LoadInst *BadLoad) {
230 auto ResultOpt = getPointerToFirstCompatibleType(
231 B, Source, BadLoad->getPointerOperandType(), ElementType, false);
232 assert(ResultOpt && "Failed to load from aggregate: "
233 "Could not find compatible memory layout.");
234 auto [GEP, CurrentTy] = *ResultOpt;
235
236 auto *SAT = dyn_cast<ArrayType>(CurrentTy);
237 auto *SVT = dyn_cast<FixedVectorType>(CurrentTy);
238 auto *DVT = dyn_cast<FixedVectorType>(ElementType);
239 auto *MAT =
240 SAT ? dyn_cast<FixedVectorType>(SAT->getElementType()) : nullptr;
241
242 if (ElementType == CurrentTy) {
243 LoadInst *LI = B.CreateLoad(ElementType, GEP);
244 LI->setAlignment(BadLoad->getAlign());
245 buildAssignType(B, ElementType, LI);
246 return LI;
247 }
248 if (SVT && DVT)
249 return loadVectorFromVector(B, SVT, DVT, GEP, BadLoad->getAlign());
250 if (SAT && DVT && SAT->getElementType() == DVT->getElementType())
251 return loadVectorFromArray(B, DVT, GEP, BadLoad->getAlign());
252 if (MAT && DVT && MAT->getElementType() == DVT->getElementType())
253 return loadVectorFromMatrixArray(B, DVT, GEP, MAT, BadLoad->getAlign());
254
255 llvm_unreachable("Failed to load from aggregate.");
256 }
257 Value *
258 buildVectorFromLoadedElements(IRBuilder<> &B, FixedVectorType *TargetType,
259 SmallVector<Value *, 4> &LoadedElements) {
260 // <1 x T> shares the SPIR-V type with T, so emitting OpCompositeInsert on
261 // a scalar would be invalid. Bridge with spv_bitcast instead.
262 if (TargetType->getNumElements() == 1) {
263 Value *Scalar = LoadedElements[0];
264 Value *NewVector = B.CreateIntrinsic(
265 Intrinsic::spv_bitcast, {TargetType, Scalar->getType()}, {Scalar});
266 buildAssignType(B, TargetType, NewVector);
267 return NewVector;
268 }
269
270 // Build the vector from the loaded elements.
271 Value *NewVector = PoisonValue::get(TargetType);
272 buildAssignType(B, TargetType, NewVector);
273
274 for (unsigned I = 0, E = TargetType->getNumElements(); I < E; ++I) {
275 Value *Index = B.getInt32(I);
276 SmallVector<Type *, 4> Types = {TargetType, TargetType,
277 TargetType->getElementType(),
278 Index->getType()};
279 SmallVector<Value *> Args = {NewVector, LoadedElements[I], Index};
280 NewVector = B.CreateIntrinsic(Intrinsic::spv_insertelt, {Types}, {Args});
281 buildAssignType(B, TargetType, NewVector);
282 }
283 return NewVector;
284 }
285
286 // Loads elements from a matrix with an array of vector memory layout and
287 // constructs a vector.
288 Value *loadVectorFromMatrixArray(IRBuilder<> &B, FixedVectorType *TargetType,
289 Value *Source, FixedVectorType *ArrElemVecTy,
290 Align OriginalAlign) {
291 Type *TargetElemTy = TargetType->getElementType();
292 unsigned ScalarsPerArrayElement = ArrElemVecTy->getNumElements();
293 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
294 uint64_t ArrElemVecSize = DL.getTypeAllocSize(ArrElemVecTy);
295 // Load each element of the array.
296 SmallVector<Value *, 4> LoadedElements;
297 std::array<Type *, 2> Types = {Source->getType(), Source->getType()};
298 for (unsigned I = 0, E = TargetType->getNumElements(); I < E; ++I) {
299 unsigned ArrayIndex = I / ScalarsPerArrayElement;
300 unsigned ElementIndexInArrayElem = I % ScalarsPerArrayElement;
301 // Create a GEP to access the i-th element of the array.
302 std::array<Value *, 4> Args = {
303 B.getInt1(/*Inbounds=*/false), Source, B.getInt32(0),
304 ConstantInt::get(B.getInt32Ty(), ArrayIndex)};
305 auto *ElementPtr = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
306 GR->buildAssignPtr(B, ArrElemVecTy, ElementPtr);
307 LoadInst *LoadVec = B.CreateLoad(ArrElemVecTy, ElementPtr);
308 LoadVec->setAlignment(
309 commonAlignment(OriginalAlign, ArrayIndex * ArrElemVecSize));
310 buildAssignType(B, ArrElemVecTy, LoadVec);
311 LoadedElements.push_back(makeExtractElement(B, TargetElemTy, LoadVec,
312 ElementIndexInArrayElem));
313 }
314 return buildVectorFromLoadedElements(B, TargetType, LoadedElements);
315 }
316 // Loads elements from an array and constructs a vector.
317 Value *loadVectorFromArray(IRBuilder<> &B, FixedVectorType *TargetType,
318 Value *Source, Align OriginalAlign) {
319 // Load each element of the array.
320 SmallVector<Value *, 4> LoadedElements;
321 std::array<Type *, 2> Types = {Source->getType(), Source->getType()};
322 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
323 uint64_t ElemSize = DL.getTypeAllocSize(TargetType->getElementType());
324 for (unsigned I = 0, E = TargetType->getNumElements(); I < E; ++I) {
325 // Create a GEP to access the i-th element of the array.
326 std::array<Value *, 4> Args = {B.getInt1(/*Inbounds=*/false), Source,
327 B.getInt32(0),
328 ConstantInt::get(B.getInt32Ty(), I)};
329 auto *ElementPtr = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
330 GR->buildAssignPtr(B, TargetType->getElementType(), ElementPtr);
331
332 // Load the value from the element pointer.
333 LoadInst *Load = B.CreateLoad(TargetType->getElementType(), ElementPtr);
334 Load->setAlignment(commonAlignment(OriginalAlign, I * ElemSize));
335 buildAssignType(B, TargetType->getElementType(), Load);
336 LoadedElements.push_back(Load);
337 }
338 return buildVectorFromLoadedElements(B, TargetType, LoadedElements);
339 }
340
341 // Stores elements from a vector into a matrix (an array of vectors).
342 void storeMatrixArrayFromVector(IRBuilder<> &B, Value *SrcVector,
343 Value *DstArrayPtr, ArrayType *ArrTy,
344 Align Alignment) {
345 auto *SrcVecTy = cast<FixedVectorType>(SrcVector->getType());
346 auto *ArrElemVecTy = cast<FixedVectorType>(ArrTy->getElementType());
347 Type *ElemTy = ArrElemVecTy->getElementType();
348 unsigned ScalarsPerArrayElement = ArrElemVecTy->getNumElements();
349 unsigned SrcNumElements = SrcVecTy->getNumElements();
350 assert(
351 SrcNumElements % ScalarsPerArrayElement == 0 &&
352 "Source vector size must be a multiple of array element vector size");
353
354 std::array<Type *, 2> Types = {DstArrayPtr->getType(),
355 DstArrayPtr->getType()};
356 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
357 uint64_t ArrElemVecSize = DL.getTypeAllocSize(ArrElemVecTy);
358
359 for (unsigned I = 0; I < SrcNumElements; I += ScalarsPerArrayElement) {
360 unsigned ArrayIndex = I / ScalarsPerArrayElement;
361 // Create a GEP to access the array element.
362 std::array<Value *, 4> Args = {
363 B.getInt1(/*Inbounds=*/false), DstArrayPtr, B.getInt32(0),
364 ConstantInt::get(B.getInt32Ty(), ArrayIndex)};
365 auto *ElementPtr = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
366 GR->buildAssignPtr(B, ArrElemVecTy, ElementPtr);
367
368 // Extract scalar elements from the source vector for this array slot.
369 SmallVector<Value *, 4> Elements;
370 for (unsigned J = 0; J < ScalarsPerArrayElement; ++J)
371 Elements.push_back(makeExtractElement(B, ElemTy, SrcVector, I + J));
372
373 // Build a vector from the extracted elements and store it.
374 Value *Vec = buildVectorFromLoadedElements(B, ArrElemVecTy, Elements);
375 StoreInst *SI = B.CreateStore(Vec, ElementPtr);
376 SI->setAlignment(commonAlignment(Alignment, ArrayIndex * ArrElemVecSize));
377 }
378 }
379
380 // Stores elements from a vector into an array.
381 void storeArrayFromVector(IRBuilder<> &B, Value *SrcVector,
382 Value *DstArrayPtr, ArrayType *ArrTy,
383 Align Alignment) {
384 auto *VecTy = cast<FixedVectorType>(SrcVector->getType());
385 Type *ElemTy = ArrTy->getElementType();
386
387 // Ensure the element types of the array and vector are the same.
388 assert(VecTy->getElementType() == ElemTy &&
389 "Element types of array and vector must be the same.");
390 std::array<Type *, 2> Types = {DstArrayPtr->getType(),
391 DstArrayPtr->getType()};
392 const DataLayout &DL = B.GetInsertBlock()->getModule()->getDataLayout();
393 uint64_t ElemSize = DL.getTypeAllocSize(ElemTy);
394
395 for (unsigned I = 0, E = VecTy->getNumElements(); I < E; ++I) {
396 // Create a GEP to access the i-th element of the array.
397 std::array<Value *, 4> Args = {B.getInt1(/*Inbounds=*/false), DstArrayPtr,
398 B.getInt32(0),
399 ConstantInt::get(B.getInt32Ty(), I)};
400 auto *ElementPtr = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
401 GR->buildAssignPtr(B, ElemTy, ElementPtr);
402
403 // Extract the element from the vector and store it.
404 Value *Element =
405 E == 1 ? SrcVector : makeExtractElement(B, ElemTy, SrcVector, I);
406 StoreInst *SI = B.CreateStore(Element, ElementPtr);
407 SI->setAlignment(commonAlignment(Alignment, I * ElemSize));
408 }
409 }
410
411 // Replaces the load instruction to get rid of the ptrcast used as source
412 // operand.
413 void transformLoad(IRBuilder<> &B, LoadInst *LI, Value *CastedOperand,
414 Value *OriginalOperand) {
415 Type *ToTy = GR->findDeducedElementType(CastedOperand);
416 B.SetInsertPoint(LI);
417
418 Value *Output = buildLegalizedLoad(B, ToTy, OriginalOperand, LI);
419
420 GR->replaceAllUsesWith(LI, Output, /* DeleteOld= */ true);
421 DeadInstructions.push_back(LI);
422 }
423
424 // Creates an spv_insertelt instruction (equivalent to llvm's insertelement).
425 Value *makeInsertElement(IRBuilder<> &B, Value *Vector, Value *Element,
426 unsigned Index) {
427 Type *Int32Ty = Type::getInt32Ty(B.getContext());
428 SmallVector<Type *, 4> Types = {Vector->getType(), Vector->getType(),
429 Element->getType(), Int32Ty};
430 SmallVector<Value *> Args = {Vector, Element, B.getInt32(Index)};
431 Value *NewI = B.CreateIntrinsic(Intrinsic::spv_insertelt, {Types}, {Args});
432 buildAssignType(B, Vector->getType(), NewI);
433 return NewI;
434 }
435
436 // Creates an spv_extractelt instruction (equivalent to llvm's
437 // extractelement).
438 Value *makeExtractElement(IRBuilder<> &B, Type *ElementType, Value *Vector,
439 unsigned Index) {
440 Type *Int32Ty = Type::getInt32Ty(B.getContext());
441 SmallVector<Type *, 3> Types = {ElementType, Vector->getType(), Int32Ty};
442 SmallVector<Value *> Args = {Vector, B.getInt32(Index)};
443 Value *NewI = B.CreateIntrinsic(Intrinsic::spv_extractelt, {Types}, {Args});
444 buildAssignType(B, ElementType, NewI);
445 return NewI;
446 }
447
448 // Extracts scalar element |Index| from |Vector|. A <1 x T> vector shares its
449 // SPIR-V type with the scalar T, so a plain extractelement would be invalid;
450 // bridge it with spv_bitcast instead.
451 Value *extractScalarFromVector(IRBuilder<> &B, Value *Vector,
452 unsigned Index) {
453 auto *VecTy = cast<FixedVectorType>(Vector->getType());
454 Type *ElemTy = VecTy->getElementType();
455 if (VecTy->getNumElements() == 1) {
456 Value *Scalar =
457 B.CreateIntrinsic(Intrinsic::spv_bitcast, {ElemTy, VecTy}, {Vector});
458 buildAssignType(B, ElemTy, Scalar);
459 return Scalar;
460 }
461 return makeExtractElement(B, ElemTy, Vector, Index);
462 }
463
464 // Stores the given Src vector operand into the Dst vector, adjusting the size
465 // if required.
466 Value *storeVectorFromVector(IRBuilder<> &B, Value *Src, Value *Dst,
467 Align Alignment) {
468 FixedVectorType *SrcType = cast<FixedVectorType>(Src->getType());
469 FixedVectorType *DstType =
470 cast<FixedVectorType>(GR->findDeducedElementType(Dst));
471 auto dstNumElements = DstType->getNumElements();
472 auto srcNumElements = SrcType->getNumElements();
473
474 // if the element type differs, it is a bitcast.
475 if (DstType->getElementType() != SrcType->getElementType()) {
476 // Support bitcast between vectors of different sizes only if
477 // the total bitwidth is the same.
478 [[maybe_unused]] auto dstBitWidth =
479 DstType->getElementType()->getScalarSizeInBits() * dstNumElements;
480 [[maybe_unused]] auto srcBitWidth =
481 SrcType->getElementType()->getScalarSizeInBits() * srcNumElements;
482 assert(dstBitWidth == srcBitWidth &&
483 "Unsupported bitcast between vectors of different sizes.");
484
485 Src =
486 B.CreateIntrinsic(Intrinsic::spv_bitcast, {DstType, SrcType}, {Src});
487 buildAssignType(B, DstType, Src);
488 SrcType = DstType;
489
490 StoreInst *SI = B.CreateStore(Src, Dst);
491 SI->setAlignment(Alignment);
492 return SI;
493 }
494
495 assert(DstType->getNumElements() >= SrcType->getNumElements());
496 LoadInst *LI = B.CreateLoad(DstType, Dst);
497 LI->setAlignment(Alignment);
498 Value *OldValues = LI;
499 buildAssignType(B, OldValues->getType(), OldValues);
500 Value *NewValues = Src;
501
502 for (unsigned I = 0; I < SrcType->getNumElements(); ++I) {
503 Value *Element =
504 makeExtractElement(B, SrcType->getElementType(), NewValues, I);
505 OldValues = makeInsertElement(B, OldValues, Element, I);
506 }
507
508 StoreInst *SI = B.CreateStore(OldValues, Dst);
509 SI->setAlignment(Alignment);
510 return SI;
511 }
512
513 // Builds a legalized store to a pointer, drilling down through
514 // memory layouts to find a compatible type.
515 void buildLegalizedStore(IRBuilder<> &B, Value *Src, Value *Dst,
516 Align Alignment) {
517 auto ResultOpt = getPointerToFirstCompatibleType(B, Dst, Dst->getType(),
518 Src->getType(), true);
519 assert(ResultOpt && "Failed to store to aggregate: "
520 "Could not find compatible memory layout.");
521 auto [GEP, CurrentTy] = *ResultOpt;
522
523 auto *DAT = dyn_cast<ArrayType>(CurrentTy);
524 auto *DVT = dyn_cast<FixedVectorType>(CurrentTy);
525 auto *SVT = dyn_cast<FixedVectorType>(Src->getType());
526 auto *DMAT =
527 DAT ? dyn_cast<FixedVectorType>(DAT->getElementType()) : nullptr;
528
529 if (Src->getType() == CurrentTy) {
530 StoreInst *SI = B.CreateStore(Src, GEP);
531 SI->setAlignment(Alignment);
532 return;
533 }
534 if (DVT && SVT) {
535 storeVectorFromVector(B, Src, GEP, Alignment);
536 return;
537 }
538 if (DAT && SVT && SVT->getElementType() == DAT->getElementType()) {
539 storeArrayFromVector(B, Src, GEP, DAT, Alignment);
540 return;
541 }
542 if (DMAT && SVT && DMAT->getElementType() == SVT->getElementType()) {
543 storeMatrixArrayFromVector(B, Src, GEP, DAT, Alignment);
544 return;
545 }
546
547 llvm_unreachable("Failed to store to aggregate.");
548 }
549
550 // Transforms a store instruction (or SPV intrinsic) using a ptrcast as
551 // operand into a valid logical SPIR-V store with no ptrcast.
552 void transformStore(IRBuilder<> &B, Instruction *BadStore, Value *Src,
553 Value *Dst, Align Alignment) {
554 B.SetInsertPoint(BadStore);
555 buildLegalizedStore(B, Src, Dst, Alignment);
556 DeadInstructions.push_back(BadStore);
557 }
558
559 void legalizePointerCast(IntrinsicInst *II) {
560 Value *CastedOperand = II;
561 Value *OriginalOperand = II->getOperand(0);
562
563 IRBuilder<> B(II->getContext());
564 std::vector<Value *> Users;
565 for (Use &U : II->uses())
566 Users.push_back(U.getUser());
567
568 for (Value *User : Users) {
569 if (LoadInst *LI = dyn_cast<LoadInst>(User)) {
570 transformLoad(B, LI, CastedOperand, OriginalOperand);
571 continue;
572 }
573
574 if (StoreInst *SI = dyn_cast<StoreInst>(User)) {
575 transformStore(B, SI, SI->getValueOperand(), OriginalOperand,
576 SI->getAlign());
577 continue;
578 }
579
580 if (IntrinsicInst *Intrin = dyn_cast<IntrinsicInst>(User)) {
581 if (Intrin->getIntrinsicID() == Intrinsic::spv_assign_ptr_type) {
582 DeadInstructions.push_back(Intrin);
583 continue;
584 }
585
586 if (Intrin->getIntrinsicID() == Intrinsic::spv_gep) {
587 GR->replaceAllUsesWith(CastedOperand, OriginalOperand,
588 /* DeleteOld= */ false);
589 continue;
590 }
591
592 if (Intrin->getIntrinsicID() == Intrinsic::spv_store) {
593 Align Alignment;
594 if (ConstantInt *C = dyn_cast<ConstantInt>(Intrin->getOperand(3)))
595 Alignment = Align(C->getZExtValue());
596 transformStore(B, Intrin, Intrin->getArgOperand(0), OriginalOperand,
597 Alignment);
598 continue;
599 }
600 }
601
602 llvm_unreachable("Unsupported ptrcast user. Please fix.");
603 }
604
605 DeadInstructions.push_back(II);
606 }
607
608public:
609 SPIRVLegalizePointerCastImpl(const SPIRVTargetMachine &TM) : TM(TM) {}
610
611 bool run(Function &F) {
612 const SPIRVSubtarget &ST = TM.getSubtarget<SPIRVSubtarget>(F);
613 GR = ST.getSPIRVGlobalRegistry();
614 DeadInstructions.clear();
615
616 std::vector<IntrinsicInst *> WorkList;
617 for (auto &BB : F) {
618 for (auto &I : BB) {
619 auto *II = dyn_cast<IntrinsicInst>(&I);
620 if (II && II->getIntrinsicID() == Intrinsic::spv_ptrcast)
621 WorkList.push_back(II);
622 }
623 }
624
625 for (IntrinsicInst *II : WorkList)
626 legalizePointerCast(II);
627
628 for (Instruction *I : DeadInstructions)
629 I->eraseFromParent();
630
631 return DeadInstructions.size() != 0;
632 }
633
634private:
635 const SPIRVTargetMachine &TM;
636 SPIRVGlobalRegistry *GR = nullptr;
637 std::vector<Instruction *> DeadInstructions;
638};
639
640class SPIRVLegalizePointerCastLegacy : public FunctionPass {
641public:
642 static char ID;
643 SPIRVLegalizePointerCastLegacy(const SPIRVTargetMachine &TM)
644 : FunctionPass(ID), TM(TM) {}
645
646 bool runOnFunction(Function &F) override {
647 return SPIRVLegalizePointerCastImpl(TM).run(F);
648 }
649
650private:
651 const SPIRVTargetMachine &TM;
652};
653} // namespace
654
657 return SPIRVLegalizePointerCastImpl(TM).run(F) ? PreservedAnalyses::none()
659}
660
661char SPIRVLegalizePointerCastLegacy::ID = 0;
662INITIALIZE_PASS(SPIRVLegalizePointerCastLegacy, "spirv-legalize-pointer-cast",
663 "SPIRV legalize pointer cast pass", false, false)
664
666 return new SPIRVLegalizePointerCastLegacy(*TM);
667}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
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
unsigned getNumElements() const
static LLVM_ABI FixedVectorType * get(Type *ElementType, unsigned NumElts)
Definition Type.cpp:867
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
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)
LLVM_ABI unsigned getScalarSizeInBits() const LLVM_READONLY
If this is a vector type, return the getPrimitiveSizeInBits value for the element type.
Definition Type.cpp:232
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
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.
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
DXILDebugInfoMap run(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)