LLVM 23.0.0git
DXILResourceAccess.cpp
Go to the documentation of this file.
1//===- DXILResourceAccess.cpp - Resource access via load/store ------------===//
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
10#include "DirectX.h"
11#include "llvm/ADT/SetVector.h"
14#include "llvm/IR/BasicBlock.h"
16#include "llvm/IR/Dominators.h"
17#include "llvm/IR/IRBuilder.h"
18#include "llvm/IR/Instruction.h"
21#include "llvm/IR/Intrinsics.h"
22#include "llvm/IR/IntrinsicsDirectX.h"
23#include "llvm/IR/LLVMContext.h"
24#include "llvm/IR/User.h"
28
29#define DEBUG_TYPE "dxil-resource-access"
30
31using namespace llvm;
32
35 LLVMContext &Context = I->getContext();
36 std::string InstStr;
37 raw_string_ostream InstOS(InstStr);
38 I->print(InstOS);
39 Context.diagnose(
40 DiagnosticInfoGeneric("At resource access:" + Twine(InstStr), DS_Note));
41
42 for (auto *Handle : Handles) {
43 std::string HandleStr;
44 raw_string_ostream HandleOS(HandleStr);
45 Handle->print(HandleOS);
46 Context.diagnose(DiagnosticInfoGeneric(
47 "Uses resource handle:" + Twine(HandleStr), DS_Note));
48 }
49 Context.diagnose(DiagnosticInfoGeneric(
50 "Resource access is not guaranteed to map to a unique global resource"));
51}
52
54 Value *Ptr, uint64_t AccessSize) {
55 Value *Offset = nullptr;
56
57 while (Ptr) {
58 if (auto *II = dyn_cast<IntrinsicInst>(Ptr)) {
59 assert(II->getIntrinsicID() == Intrinsic::dx_resource_getpointer &&
60 "Resource access through unexpected intrinsic");
61 return Offset ? Offset : ConstantInt::get(Builder.getInt32Ty(), 0);
62 }
63
65 assert(GEP && "Resource access through unexpected instruction");
66
67 unsigned NumIndices = GEP->getNumIndices();
68 uint64_t IndexScale = DL.getTypeAllocSize(GEP->getSourceElementType());
69 APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
70 Value *GEPOffset;
71 if (GEP->accumulateConstantOffset(DL, ConstantOffset)) {
72 // We have a constant offset (in bytes).
73 GEPOffset =
74 ConstantInt::get(DL.getIndexType(GEP->getType()), ConstantOffset);
75 IndexScale = 1;
76 } else if (NumIndices == 1) {
77 // If we have a single index we're indexing into a top level array. This
78 // generally only happens with cbuffers.
79 GEPOffset = *GEP->idx_begin();
80 } else if (NumIndices == 2) {
81 // If we have two indices, this should be an access through a pointer.
82 auto *IndexIt = GEP->idx_begin();
83 assert(cast<ConstantInt>(IndexIt)->getZExtValue() == 0 &&
84 "GEP is not indexing through pointer");
85 GEPOffset = *(++IndexIt);
86 } else
87 llvm_unreachable("Unhandled GEP structure for resource access");
88
89 uint64_t ElemSize = AccessSize;
90 if (!(IndexScale % ElemSize)) {
91 // If our scale is an exact multiple of the access size, adjust the
92 // scaling to avoid an unnecessary division.
93 IndexScale /= ElemSize;
94 ElemSize = 1;
95 }
96 if (IndexScale != 1)
97 GEPOffset = Builder.CreateMul(
98 GEPOffset, ConstantInt::get(Builder.getInt32Ty(), IndexScale));
99 if (ElemSize != 1)
100 GEPOffset = Builder.CreateUDiv(
101 GEPOffset, ConstantInt::get(Builder.getInt32Ty(), ElemSize));
102
103 Offset = Offset ? Builder.CreateAdd(Offset, GEPOffset) : GEPOffset;
104 Ptr = GEP->getPointerOperand();
105 }
106
107 llvm_unreachable("GEP of null pointer?");
108}
109
112 const DataLayout &DL = SI->getDataLayout();
113 IRBuilder<> Builder(SI);
114 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
115 Type *ScalarType = ContainedType->getScalarType();
116 Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
117
118 Value *V = SI->getValueOperand();
119 if (V->getType() == ContainedType) {
120 // V is already the right type.
121 assert(SI->getPointerOperand() == II &&
122 "Store of whole element has mismatched address to store to");
123 } else if (V->getType() == ScalarType) {
124 // We're storing a scalar, so we need to load the current value and only
125 // replace the relevant part.
126 auto *Load = Builder.CreateIntrinsic(
127 LoadType, Intrinsic::dx_resource_load_typedbuffer,
128 {II->getOperand(0), II->getOperand(1)});
129 auto *Struct = Builder.CreateExtractValue(Load, {0});
130
131 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
132 Value *Offset =
133 traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
134 V = Builder.CreateInsertElement(Struct, V, Offset);
135 } else {
136 llvm_unreachable("Store to typed resource has invalid type");
137 }
138
139 auto *Inst = Builder.CreateIntrinsic(
140 Builder.getVoidTy(), Intrinsic::dx_resource_store_typedbuffer,
141 {II->getOperand(0), II->getOperand(1), V});
142 SI->replaceAllUsesWith(Inst);
143}
144
147 const DataLayout &DL = SI->getDataLayout();
148 IRBuilder<> Builder(SI);
149
150 Value *V = SI->getValueOperand();
151 assert(!V->getType()->isAggregateType() &&
152 "Resource store should be scalar or vector type");
153
154 Value *Index = II->getOperand(1);
155 // The offset for the rawbuffer load and store ops is always in bytes.
156 uint64_t AccessSize = 1;
157 Value *Offset =
158 traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
159
160 // For raw buffer (ie, HLSL's ByteAddressBuffer), we need to fold the access
161 // entirely into the index.
162 if (!RTI.isStruct()) {
163 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
164 if (!ConstantOffset || !ConstantOffset->isZero())
165 Index = Builder.CreateAdd(Index, Offset);
166 Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
167 }
168
169 auto *Inst = Builder.CreateIntrinsic(Builder.getVoidTy(),
170 Intrinsic::dx_resource_store_rawbuffer,
171 {II->getOperand(0), Index, Offset, V});
172 SI->replaceAllUsesWith(Inst);
173}
174
206
209 const DataLayout &DL = LI->getDataLayout();
210 IRBuilder<> Builder(LI);
211 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
212 Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
213
214 Value *V =
215 Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_typedbuffer,
216 {II->getOperand(0), II->getOperand(1)});
217 V = Builder.CreateExtractValue(V, {0});
218
219 Type *ScalarType = ContainedType->getScalarType();
220 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
221 Value *Offset =
222 traverseGEPOffsets(DL, Builder, LI->getPointerOperand(), AccessSize);
223 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
224 if (!ConstantOffset || !ConstantOffset->isZero())
225 V = Builder.CreateExtractElement(V, Offset);
226
227 // If we loaded a <1 x ...> instead of a scalar (presumably to feed a
228 // shufflevector), then make sure we're maintaining the resulting type.
229 if (auto *VT = dyn_cast<FixedVectorType>(LI->getType()))
230 if (VT->getNumElements() == 1 && !isa<FixedVectorType>(V->getType()))
231 V = Builder.CreateInsertElement(PoisonValue::get(VT), V,
232 Builder.getInt32(0));
233
234 LI->replaceAllUsesWith(V);
235}
236
239 const DataLayout &DL = LI->getDataLayout();
240 IRBuilder<> Builder(LI);
241
242 Type *LoadType = StructType::get(LI->getType(), Builder.getInt1Ty());
243 assert(!LI->getType()->isAggregateType() &&
244 "Resource load should be scalar or vector type");
245
246 Value *Index = II->getOperand(1);
247 // The offset for the rawbuffer load and store ops is always in bytes.
248 uint64_t AccessSize = 1;
249 Value *Offset =
250 traverseGEPOffsets(DL, Builder, LI->getPointerOperand(), AccessSize);
251
252 // For raw buffer (ie, HLSL's ByteAddressBuffer), we need to fold the access
253 // entirely into the index.
254 if (!RTI.isStruct()) {
255 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
256 if (!ConstantOffset || !ConstantOffset->isZero())
257 Index = Builder.CreateAdd(Index, Offset);
258 Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
259 }
260
261 Value *V =
262 Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_rawbuffer,
263 {II->getOperand(0), Index, Offset});
264 V = Builder.CreateExtractValue(V, {0});
265
266 LI->replaceAllUsesWith(V);
267}
268
269namespace {
270/// Helper for building a `load.cbufferrow` intrinsic given a simple type.
271struct CBufferRowIntrin {
272 Intrinsic::ID IID;
273 Type *RetTy;
274 unsigned int EltSize;
275 unsigned int NumElts;
276
277 CBufferRowIntrin(const DataLayout &DL, Type *Ty) {
278 assert(Ty == Ty->getScalarType() && "Expected scalar type");
279
280 switch (DL.getTypeSizeInBits(Ty)) {
281 case 16:
282 IID = Intrinsic::dx_resource_load_cbufferrow_8;
283 RetTy = StructType::get(Ty, Ty, Ty, Ty, Ty, Ty, Ty, Ty);
284 EltSize = 2;
285 NumElts = 8;
286 break;
287 case 32:
288 IID = Intrinsic::dx_resource_load_cbufferrow_4;
289 RetTy = StructType::get(Ty, Ty, Ty, Ty);
290 EltSize = 4;
291 NumElts = 4;
292 break;
293 case 64:
294 IID = Intrinsic::dx_resource_load_cbufferrow_2;
295 RetTy = StructType::get(Ty, Ty);
296 EltSize = 8;
297 NumElts = 2;
298 break;
299 default:
300 llvm_unreachable("Only 16, 32, and 64 bit types supported");
301 }
302 }
303};
304} // namespace
305
308 const DataLayout &DL = LI->getDataLayout();
309
310 Type *Ty = LI->getType();
311 assert(!isa<StructType>(Ty) && "Structs not handled yet");
312 CBufferRowIntrin Intrin(DL, Ty->getScalarType());
313
314 StringRef Name = LI->getName();
315 Value *Handle = II->getOperand(0);
316
317 IRBuilder<> Builder(LI);
318
319 ConstantInt *GlobalOffset = dyn_cast<ConstantInt>(II->getOperand(1));
320 assert(GlobalOffset && "CBuffer getpointer index must be constant");
321
322 uint64_t GlobalOffsetVal = GlobalOffset->getZExtValue();
323 Value *CurrentRow = ConstantInt::get(
324 Builder.getInt32Ty(), GlobalOffsetVal / hlsl::CBufferRowSizeInBytes);
325 unsigned int CurrentIndex =
326 (GlobalOffsetVal % hlsl::CBufferRowSizeInBytes) / Intrin.EltSize;
327
328 // Every object in a cbuffer either fits in a row or is aligned to a row. This
329 // means that only the very last pointer access can point into a row.
330 auto *LastGEP = dyn_cast<GEPOperator>(LI->getPointerOperand());
331 if (!LastGEP) {
332 // If we don't have a GEP at all we're just accessing the resource through
333 // the result of getpointer directly.
334 assert(LI->getPointerOperand() == II &&
335 "Unexpected indirect access to resource without GEP");
336 } else {
337 Value *GEPOffset = traverseGEPOffsets(
338 DL, Builder, LastGEP->getPointerOperand(), hlsl::CBufferRowSizeInBytes);
339 CurrentRow = Builder.CreateAdd(GEPOffset, CurrentRow);
340
341 APInt ConstantOffset(DL.getIndexTypeSizeInBits(LastGEP->getType()), 0);
342 if (LastGEP->accumulateConstantOffset(DL, ConstantOffset)) {
343 APInt Remainder(DL.getIndexTypeSizeInBits(LastGEP->getType()),
345 APInt::udivrem(ConstantOffset, Remainder, ConstantOffset, Remainder);
346 CurrentRow = Builder.CreateAdd(
347 CurrentRow, ConstantInt::get(Builder.getInt32Ty(), ConstantOffset));
348 CurrentIndex += Remainder.udiv(Intrin.EltSize).getZExtValue();
349 } else {
350 assert(LastGEP->getNumIndices() == 1 &&
351 "Last GEP of cbuffer access is not array or struct access");
352 // We assume a non-constant access will be row-aligned. This is safe
353 // because arrays and structs are always row aligned, and accesses to
354 // vector elements will show up as a load of the vector followed by an
355 // extractelement.
356 CurrentRow = cast<ConstantInt>(CurrentRow)->isZero()
357 ? *LastGEP->idx_begin()
358 : Builder.CreateAdd(CurrentRow, *LastGEP->idx_begin());
359 CurrentIndex = 0;
360 }
361 }
362
363 auto *CBufLoad = Builder.CreateIntrinsic(
364 Intrin.RetTy, Intrin.IID, {Handle, CurrentRow}, nullptr, Name + ".load");
365 auto *Elt =
366 Builder.CreateExtractValue(CBufLoad, {CurrentIndex++}, Name + ".extract");
367
368 // At this point we've loaded the first scalar of our result, but our original
369 // type may have been a vector.
370 unsigned int Remaining =
371 ((DL.getTypeSizeInBits(Ty) / 8) / Intrin.EltSize) - 1;
372 if (Remaining == 0) {
373 // We only have a single element, so we're done.
374 Value *Result = Elt;
375
376 // However, if we loaded a <1 x T>, then we need to adjust the type.
377 if (auto *VT = dyn_cast<FixedVectorType>(Ty)) {
378 assert(VT->getNumElements() == 1 && "Can't have multiple elements here");
379 Result = Builder.CreateInsertElement(PoisonValue::get(VT), Result,
380 Builder.getInt32(0), Name);
381 }
382 LI->replaceAllUsesWith(Result);
383 return;
384 }
385
386 // Walk each element and extract it, wrapping to new rows as needed.
387 SmallVector<Value *> Extracts{Elt};
388 while (Remaining--) {
389 CurrentIndex %= Intrin.NumElts;
390
391 if (CurrentIndex == 0) {
392 CurrentRow = Builder.CreateAdd(CurrentRow,
393 ConstantInt::get(Builder.getInt32Ty(), 1));
394 CBufLoad = Builder.CreateIntrinsic(Intrin.RetTy, Intrin.IID,
395 {Handle, CurrentRow}, nullptr,
396 Name + ".load");
397 }
398
399 Extracts.push_back(Builder.CreateExtractValue(CBufLoad, {CurrentIndex++},
400 Name + ".extract"));
401 }
402
403 // Finally, we build up the original loaded value.
404 Value *Result = PoisonValue::get(Ty);
405 for (int I = 0, E = Extracts.size(); I < E; ++I)
406 Result = Builder.CreateInsertElement(
407 Result, Extracts[I], Builder.getInt32(I), Name + formatv(".upto{}", I));
408 LI->replaceAllUsesWith(Result);
409}
410
443
445 if (auto *LI = dyn_cast<LoadInst>(AI))
446 return dyn_cast<Instruction>(LI->getPointerOperand());
447 if (auto *SI = dyn_cast<StoreInst>(AI))
448 return dyn_cast<Instruction>(SI->getPointerOperand());
449
450 return nullptr;
451}
452
453static const std::array<Intrinsic::ID, 2> HandleIntrins = {
454 Intrinsic::dx_resource_handlefrombinding,
455 Intrinsic::dx_resource_handlefromimplicitbinding,
456};
457
459 SmallVector<Value *> Worklist = {Ptr};
461
462 while (!Worklist.empty()) {
463 Value *X = Worklist.pop_back_val();
464
465 if (!X->getType()->isPointerTy() && !X->getType()->isTargetExtTy())
466 return {}; // Early exit on store/load into non-resource
467
468 if (auto *Phi = dyn_cast<PHINode>(X))
469 for (Use &V : Phi->incoming_values())
470 Worklist.push_back(V.get());
471 else if (auto *Select = dyn_cast<SelectInst>(X))
472 for (Value *V : {Select->getTrueValue(), Select->getFalseValue()})
473 Worklist.push_back(V);
474 else if (auto *II = dyn_cast<IntrinsicInst>(X)) {
475 Intrinsic::ID IID = II->getIntrinsicID();
476
477 if (IID == Intrinsic::dx_resource_getpointer)
478 Worklist.push_back(II->getArgOperand(/*Handle=*/0));
479
481 Handles.push_back(II);
482 }
483 }
484
485 return Handles;
486}
487
489 DXILResourceTypeMap &DRTM) {
491 "Only expects a Handle as determined from collectUsedHandles.");
492
493 auto *HandleTy = cast<TargetExtType>(Handle->getType());
494 dxil::ResourceClass Class = DRTM[HandleTy].getResourceClass();
495 uint32_t Space = cast<ConstantInt>(Handle->getArgOperand(0))->getZExtValue();
496 uint32_t LowerBound =
497 cast<ConstantInt>(Handle->getArgOperand(1))->getZExtValue();
498 uint32_t Size = cast<ConstantInt>(Handle->getArgOperand(2))->getZExtValue();
499 uint32_t UpperBound = Size == UINT32_MAX ? UINT32_MAX : LowerBound + Size - 1;
500
501 return hlsl::Binding(Class, Space, LowerBound, UpperBound, nullptr);
502}
503
504namespace {
505/// Helper for propagating the current handle and ptr indices.
506struct AccessIndices {
507 Value *GetPtrIdx;
508 Value *HandleIdx;
509
510 bool hasGetPtrIdx() { return GetPtrIdx != nullptr; }
511 bool hasHandleIdx() { return HandleIdx != nullptr; }
512};
513} // namespace
514
515// getAccessIndices traverses up the control flow that a ptr came from and
516// propagates back the indicies used to access the resource (AccessIndices):
517//
518// - GetPtrIdx is the index of dx.resource.getpointer
519// - HandleIdx is the index of dx.resource.handlefrom.*
520static AccessIndices
522 if (auto *II = dyn_cast<IntrinsicInst>(I)) {
523 if (llvm::is_contained(HandleIntrins, II->getIntrinsicID())) {
524 DeadInsts.insert(II);
525 return {nullptr, II->getArgOperand(/*Index=*/3)};
526 }
527
528 if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
529 auto *V = dyn_cast<Instruction>(II->getArgOperand(/*Handle=*/0));
530 auto AccessIdx = getAccessIndices(V, DeadInsts);
531 assert(!AccessIdx.hasGetPtrIdx() &&
532 "Encountered multiple dx.resource.getpointers in ptr chain?");
533 AccessIdx.GetPtrIdx = II->getArgOperand(1);
534
535 DeadInsts.insert(II);
536 return AccessIdx;
537 }
538 }
539
540 if (auto *Phi = dyn_cast<PHINode>(I)) {
541 unsigned NumEdges = Phi->getNumIncomingValues();
542 assert(NumEdges != 0 && "Malformed Phi Node");
543
544 IRBuilder<> Builder(Phi);
545 PHINode *GetPtrPhi = PHINode::Create(Builder.getInt32Ty(), NumEdges);
546 PHINode *HandlePhi = PHINode::Create(Builder.getInt32Ty(), NumEdges);
547
548 bool HasGetPtr = true;
549 for (unsigned Idx = 0; Idx < NumEdges; Idx++) {
550 auto *BB = Phi->getIncomingBlock(Idx);
551 auto *V = dyn_cast<Instruction>(Phi->getIncomingValue(Idx));
552 auto AccessIdx = getAccessIndices(V, DeadInsts);
553 HasGetPtr &= AccessIdx.hasGetPtrIdx();
554 if (HasGetPtr)
555 GetPtrPhi->addIncoming(AccessIdx.GetPtrIdx, BB);
556 HandlePhi->addIncoming(AccessIdx.HandleIdx, BB);
557 }
558
559 if (HasGetPtr)
560 Builder.Insert(GetPtrPhi);
561 else
562 GetPtrPhi = nullptr;
563
564 Builder.Insert(HandlePhi);
565
566 DeadInsts.insert(Phi);
567 return {GetPtrPhi, HandlePhi};
568 }
569
570 if (auto *Select = dyn_cast<SelectInst>(I)) {
571 auto *TrueV = dyn_cast<Instruction>(Select->getTrueValue());
572 auto TrueAccessIdx = getAccessIndices(TrueV, DeadInsts);
573
574 auto *FalseV = dyn_cast<Instruction>(Select->getFalseValue());
575 auto FalseAccessIdx = getAccessIndices(FalseV, DeadInsts);
576
577 IRBuilder<> Builder(Select);
578 Value *GetPtrSelect = nullptr;
579
580 if (TrueAccessIdx.hasGetPtrIdx() && FalseAccessIdx.hasGetPtrIdx())
581 GetPtrSelect =
582 Builder.CreateSelect(Select->getCondition(), TrueAccessIdx.GetPtrIdx,
583 FalseAccessIdx.GetPtrIdx);
584
585 auto *HandleSelect =
586 Builder.CreateSelect(Select->getCondition(), TrueAccessIdx.HandleIdx,
587 FalseAccessIdx.HandleIdx);
588 DeadInsts.insert(Select);
589 return {GetPtrSelect, HandleSelect};
590 }
591
592 llvm_unreachable("collectUsedHandles should assure this does not occur");
593}
594
595static void
598 auto AccessIdx = getAccessIndices(Ptr, DeadInsts);
599 assert(AccessIdx.hasGetPtrIdx() && AccessIdx.hasHandleIdx() &&
600 "Couldn't retrieve indices. This is guaranteed by getAccessIndices");
601
602 IRBuilder<> Builder(Ptr);
603 IntrinsicInst *Handle = cast<IntrinsicInst>(OldHandle->clone());
604 Handle->setArgOperand(/*Index=*/3, AccessIdx.HandleIdx);
605 Builder.Insert(Handle);
606
607 auto *GetPtr =
608 Builder.CreateIntrinsic(Ptr->getType(), Intrinsic::dx_resource_getpointer,
609 {Handle, AccessIdx.GetPtrIdx});
610
611 Ptr->replaceAllUsesWith(GetPtr);
612 DeadInsts.insert(Ptr);
613}
614
615// Try to legalize dx.resource.handlefrom.*.binding and dx.resource.getpointer
616// calls with their respective index values and propagate the index values to
617// be used at resource access.
618//
619// If it can't be transformed to be legal then:
620//
621// Reports an error if a resource access is not guaranteed into a unique global
622// resource.
623//
624// Returns true if any changes are made.
627 for (BasicBlock &BB : make_early_inc_range(F)) {
628 for (Instruction &I : BB) {
629 if (auto *PtrOp = getStoreLoadPointerOperand(&I)) {
631 unsigned NumHandles = Handles.size();
632 if (NumHandles <= 1)
633 continue; // Legal, no-replacement required
634
635 bool SameGlobalBinding = true;
636 hlsl::Binding B = getHandleIntrinsicBinding(Handles[0], DRTM);
637 for (unsigned Idx = 1; Idx < NumHandles; Idx++)
638 SameGlobalBinding &=
639 (B == getHandleIntrinsicBinding(Handles[Idx], DRTM));
640
641 if (!SameGlobalBinding) {
643 continue;
644 }
645
646 replaceHandleWithIndices(PtrOp, Handles[0], DeadInsts);
647 }
648 }
649 }
650
651 bool MadeChanges = false;
652
653 for (auto *I : llvm::reverse(DeadInsts))
654 if (I->hasNUses(0)) { // Handle can still be used outside of replaced path
655 I->eraseFromParent();
656 MadeChanges = true;
657 }
658
659 return MadeChanges;
660}
661
663 SmallVector<User *> Worklist;
664 for (User *U : II->users())
665 Worklist.push_back(U);
666
668 while (!Worklist.empty()) {
669 User *U = Worklist.back();
670 Worklist.pop_back();
671
672 if (auto *GEP = dyn_cast<GetElementPtrInst>(U)) {
673 for (User *U : GEP->users())
674 Worklist.push_back(U);
675 DeadInsts.push_back(GEP);
676
677 } else if (auto *SI = dyn_cast<StoreInst>(U)) {
678 assert(SI->getValueOperand() != II && "Pointer escaped!");
680 DeadInsts.push_back(SI);
681
682 } else if (auto *LI = dyn_cast<LoadInst>(U)) {
683 createLoadIntrinsic(II, LI, RTI);
684 DeadInsts.push_back(LI);
685 } else
686 llvm_unreachable("Unhandled instruction - pointer escaped?");
687 }
688
689 // Traverse the now-dead instructions in RPO and remove them.
690 for (Instruction *Dead : llvm::reverse(DeadInsts))
691 Dead->eraseFromParent();
692 II->eraseFromParent();
693}
694
698 for (Instruction &I : BB)
699 if (auto *II = dyn_cast<IntrinsicInst>(&I))
700 if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
701 auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());
702 Resources.emplace_back(II, DRTM[HandleTy]);
703 }
704
705 for (auto &[II, RI] : Resources)
706 replaceAccess(II, RI);
707
708 return !Resources.empty();
709}
710
713 auto &MAMProxy = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
714 DXILResourceTypeMap *DRTM =
715 MAMProxy.getCachedResult<DXILResourceTypeAnalysis>(*F.getParent());
716 assert(DRTM && "DXILResourceTypeAnalysis must be available");
717
718 bool MadeHandleChanges = legalizeResourceHandles(F, *DRTM);
719 bool MadeResourceChanges = transformResourcePointers(F, *DRTM);
720 if (!(MadeHandleChanges || MadeResourceChanges))
721 return PreservedAnalyses::all();
722
726 return PA;
727}
728
729namespace {
730class DXILResourceAccessLegacy : public FunctionPass {
731public:
732 bool runOnFunction(Function &F) override {
733 DXILResourceTypeMap &DRTM =
734 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
735 bool MadeHandleChanges = legalizeResourceHandles(F, DRTM);
736 bool MadeResourceChanges = transformResourcePointers(F, DRTM);
737 return MadeHandleChanges || MadeResourceChanges;
738 }
739 StringRef getPassName() const override { return "DXIL Resource Access"; }
740 DXILResourceAccessLegacy() : FunctionPass(ID) {}
741
742 static char ID; // Pass identification.
743 void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
744 AU.addRequired<DXILResourceTypeWrapperPass>();
745 AU.addPreserved<DominatorTreeWrapperPass>();
746 }
747};
748char DXILResourceAccessLegacy::ID = 0;
749} // end anonymous namespace
750
751INITIALIZE_PASS_BEGIN(DXILResourceAccessLegacy, DEBUG_TYPE,
752 "DXIL Resource Access", false, false)
754INITIALIZE_PASS_END(DXILResourceAccessLegacy, DEBUG_TYPE,
755 "DXIL Resource Access", false, false)
756
758 return new DXILResourceAccessLegacy();
759}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
AMDGPU Register Bank Select
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 void diagnoseNonUniqueResourceAccess(Instruction *I, ArrayRef< IntrinsicInst * > Handles)
static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, dxil::ResourceTypeInfo &RTI)
static void createRawStore(IntrinsicInst *II, StoreInst *SI, dxil::ResourceTypeInfo &RTI)
static bool legalizeResourceHandles(Function &F, DXILResourceTypeMap &DRTM)
static AccessIndices getAccessIndices(Instruction *I, SmallSetVector< Instruction *, 16 > &DeadInsts)
static void createTypedBufferLoad(IntrinsicInst *II, LoadInst *LI, dxil::ResourceTypeInfo &RTI)
static void createTypedBufferStore(IntrinsicInst *II, StoreInst *SI, dxil::ResourceTypeInfo &RTI)
static SmallVector< IntrinsicInst * > collectUsedHandles(Value *Ptr)
static const std::array< Intrinsic::ID, 2 > HandleIntrins
static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM)
static void replaceHandleWithIndices(Instruction *Ptr, IntrinsicInst *OldHandle, SmallSetVector< Instruction *, 16 > &DeadInsts)
static void createRawLoad(IntrinsicInst *II, LoadInst *LI, dxil::ResourceTypeInfo &RTI)
static Value * traverseGEPOffsets(const DataLayout &DL, IRBuilder<> &Builder, Value *Ptr, uint64_t AccessSize)
static hlsl::Binding getHandleIntrinsicBinding(IntrinsicInst *Handle, DXILResourceTypeMap &DRTM)
static void createStoreIntrinsic(IntrinsicInst *II, StoreInst *SI, dxil::ResourceTypeInfo &RTI)
static void createCBufferLoad(IntrinsicInst *II, LoadInst *LI, dxil::ResourceTypeInfo &RTI)
static Instruction * getStoreLoadPointerOperand(Instruction *AI)
static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI)
static bool runOnFunction(Function &F, bool PostInlining)
#define DEBUG_TYPE
Hexagon Common GEP
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
uint64_t IntrinsicInst * II
FunctionAnalysisManager FAM
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
This file implements a set that has insertion order iteration characteristics.
static TableGen::Emitter::OptClass< SkeletonEmitter > X("gen-skeleton-class", "Generate example skeleton class")
Class for arbitrary precision integers.
Definition APInt.h:78
LLVM_ABI APInt udiv(const APInt &RHS) const
Unsigned division operation.
Definition APInt.cpp:1584
static LLVM_ABI void udivrem(const APInt &LHS, const APInt &RHS, APInt &Quotient, APInt &Remainder)
Dual division/remainder interface.
Definition APInt.cpp:1769
uint64_t getZExtValue() const
Get zero extended value.
Definition APInt.h:1555
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
LLVM Basic Block Representation.
Definition BasicBlock.h:62
Value * getArgOperand(unsigned i) const
void setArgOperand(unsigned i, Value *v)
This is the shared class of boolean and integer constants.
Definition Constants.h:87
uint64_t getZExtValue() const
Return the constant as a 64-bit unsigned integer value after it has been zero extended as appropriate...
Definition Constants.h:168
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM)
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
Analysis pass which computes a DominatorTree.
Definition Dominators.h:283
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2788
LLVM_ABI Instruction * clone() const
Create a copy of 'this' instruction that is identical in all ways except the following:
LLVM_ABI const DataLayout & getDataLayout() const
Get the data layout of the module this instruction belongs to.
A wrapper class for inspecting calls to intrinsic functions.
Intrinsic::ID getIntrinsicID() const
Return the intrinsic ID of this intrinsic.
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
An instruction for reading from memory.
Value * getPointerOperand()
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
static PHINode * Create(Type *Ty, unsigned NumReservedValues, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
Constructors - NumReservedValues is a hint for the number of incoming edges that this phi node will h...
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 all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserve()
Mark an analysis as preserved.
Definition Analysis.h:132
bool insert(const value_type &X)
Insert a new element into the SetVector.
Definition SetVector.h:151
A SetVector that performs no allocations if smaller than a certain size.
Definition SetVector.h:339
reference emplace_back(ArgTypes &&... Args)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
static LLVM_ABI StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition Type.cpp:413
Type * getTypeParameter(unsigned i) const
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:45
Type * getScalarType() const
If this is a vector type, return the element type, otherwise return 'this'.
Definition Type.h:352
bool isAggregateType() const
Return true if the type is an aggregate type.
Definition Type.h:304
static LLVM_ABI IntegerType * getInt1Ty(LLVMContext &C)
Definition Type.cpp:293
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:256
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:322
TargetExtType * getHandleTy() const
LLVM_ABI bool isStruct() const
dxil::ResourceKind getResourceKind() const
A raw_ostream that writes to an std::string.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
const unsigned CBufferRowSizeInBytes
This is an optimization pass for GlobalISel generic memory operations.
Definition Types.h:26
@ Offset
Definition DWP.cpp:532
FunctionAddr VTableAddr Value
Definition InstrProf.h:137
@ Dead
Unused definition.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
OuterAnalysisManagerProxy< ModuleAnalysisManager, Function > ModuleAnalysisManagerFunctionProxy
Provide the ModuleAnalysisManager to Function proxy.
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:634
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
auto reverse(ContainerTy &&C)
Definition STLExtras.h:408
FunctionPass * createDXILResourceAccessLegacyPass()
Pass to update resource accesses to use load/store directly.
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition STLExtras.h:1947
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
LLVM_ABI void reportFatalUsageError(Error Err)
Report a fatal error that does not indicate a bug in LLVM.
Definition Error.cpp:177