LLVM 24.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"
15#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"
29#include <optional>
30
31#define DEBUG_TYPE "dxil-resource-access"
32
33using namespace llvm;
34
37 LLVMContext &Context = I->getContext();
38 std::string InstStr;
39 raw_string_ostream InstOS(InstStr);
40 I->print(InstOS);
41 Context.diagnose(
42 DiagnosticInfoGeneric("At resource access:" + Twine(InstStr), DS_Note));
43
44 for (auto *Handle : Handles) {
45 std::string HandleStr;
46 raw_string_ostream HandleOS(HandleStr);
47 Handle->print(HandleOS);
48 Context.diagnose(DiagnosticInfoGeneric(
49 "Uses resource handle:" + Twine(HandleStr), DS_Note));
50 }
51 Context.diagnose(DiagnosticInfoGeneric(
52 "Resource access is not guaranteed to map to a unique global resource"));
53}
54
56 Value *Ptr, uint64_t AccessSize) {
57 Value *Offset = nullptr;
58
59 while (Ptr) {
60 if (auto *II = dyn_cast<IntrinsicInst>(Ptr)) {
61 assert((II->getIntrinsicID() == Intrinsic::dx_resource_getpointer ||
62 II->getIntrinsicID() == Intrinsic::dx_resource_getbasepointer) &&
63 "Resource access through unexpected intrinsic");
64 return Offset ? Offset : ConstantInt::get(Builder.getInt32Ty(), 0);
65 }
66
68 assert(GEP && "Resource access through unexpected instruction");
69
70 unsigned NumIndices = GEP->getNumIndices();
71 uint64_t IndexScale = DL.getTypeAllocSize(GEP->getSourceElementType());
72 APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
73 Value *GEPOffset;
74 if (GEP->accumulateConstantOffset(DL, ConstantOffset)) {
75 // We have a constant offset (in bytes).
76 GEPOffset =
77 ConstantInt::get(DL.getIndexType(GEP->getType()), ConstantOffset);
78 IndexScale = 1;
79 } else if (NumIndices == 1) {
80 // If we have a single index we're indexing into a top level array. This
81 // generally only happens with cbuffers.
82 GEPOffset = *GEP->idx_begin();
83 } else if (NumIndices == 2) {
84 // If we have two indices, this should be an access through a pointer.
85 auto *IndexIt = GEP->idx_begin();
86 assert(cast<ConstantInt>(IndexIt)->getZExtValue() == 0 &&
87 "GEP is not indexing through pointer");
88 GEPOffset = *(++IndexIt);
89 } else
90 llvm_unreachable("Unhandled GEP structure for resource access");
91
92 uint64_t ElemSize = AccessSize;
93 if (!(IndexScale % ElemSize)) {
94 // If our scale is an exact multiple of the access size, adjust the
95 // scaling to avoid an unnecessary division.
96 IndexScale /= ElemSize;
97 ElemSize = 1;
98 }
99 if (IndexScale != 1)
100 GEPOffset = Builder.CreateMul(
101 GEPOffset, ConstantInt::get(Builder.getInt32Ty(), IndexScale));
102 if (ElemSize != 1)
103 GEPOffset = Builder.CreateUDiv(
104 GEPOffset, ConstantInt::get(Builder.getInt32Ty(), ElemSize));
105
106 Offset = Offset ? Builder.CreateAdd(Offset, GEPOffset) : GEPOffset;
107 Ptr = GEP->getPointerOperand();
108 }
109
110 llvm_unreachable("GEP of null pointer?");
111}
112
115 const DataLayout &DL = SI->getDataLayout();
116 IRBuilder<> Builder(SI);
117 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
118 Type *ScalarType = ContainedType->getScalarType();
119 Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
120
121 Value *V = SI->getValueOperand();
122 if (V->getType() == ContainedType) {
123 // V is already the right type.
124 assert(SI->getPointerOperand() == II &&
125 "Store of whole element has mismatched address to store to");
126 } else if (V->getType() == ScalarType) {
127 // We're storing a scalar, so we need to load the current value and only
128 // replace the relevant part.
129 auto *Load = Builder.CreateIntrinsic(
130 LoadType, Intrinsic::dx_resource_load_typedbuffer,
131 {II->getOperand(0), II->getOperand(1)});
132 auto *Struct = Builder.CreateExtractValue(Load, {0});
133
134 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
135 Value *Offset =
136 traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
137 V = Builder.CreateInsertElement(Struct, V, Offset);
138 } else {
139 llvm_unreachable("Store to typed resource has invalid type");
140 }
141
142 auto *Inst = Builder.CreateIntrinsic(
143 Builder.getVoidTy(), Intrinsic::dx_resource_store_typedbuffer,
144 {II->getOperand(0), II->getOperand(1), V});
145 SI->replaceAllUsesWith(Inst);
146}
147
148/// Build a zero-initialized offset operand matching the shape of the given
149/// coordinate operand. Accesses through `operator[]` never have offsets.
150static Value *getNullOffsetsFor(IRBuilder<> &Builder, Value *Coords) {
151 Type *CoordTy = Coords->getType();
152 Type *OffsetTy;
153 if (auto *VecTy = dyn_cast<FixedVectorType>(CoordTy))
154 OffsetTy =
155 FixedVectorType::get(Builder.getInt32Ty(), VecTy->getNumElements());
156 else
157 OffsetTy = Builder.getInt32Ty();
158 return Constant::getNullValue(OffsetTy);
159}
160
163 const DataLayout &DL = SI->getDataLayout();
164 IRBuilder<> Builder(SI);
165 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
166 Type *ScalarType = ContainedType->getScalarType();
167
168 Value *Handle = II->getOperand(0);
169 Value *Coords = II->getOperand(1);
170
171 Value *V = SI->getValueOperand();
172 if (V->getType() == ContainedType) {
173 // V is already the right type.
174 assert(SI->getPointerOperand() == II &&
175 "Store of whole element has mismatched address to store to");
176 } else if (V->getType() == ScalarType) {
177 // We're storing a scalar, so we need to load the current value and only
178 // replace the relevant part. For operator[] the mip level and the offsets
179 // are always zero; DXILOpLowering drops the mip level for UAVs.
180 Value *MipLevel = Builder.getInt32(0);
181 Value *Offsets = getNullOffsetsFor(Builder, Coords);
182 auto *Load = Builder.CreateIntrinsic(ContainedType,
183 Intrinsic::dx_resource_load_level,
184 {Handle, Coords, MipLevel, Offsets});
185
186 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
187 Value *Offset =
188 traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
189 V = Builder.CreateInsertElement(Load, V, Offset);
190 } else {
191 llvm_unreachable("Store to texture resource has invalid type");
192 }
193
194 auto *Inst = Builder.CreateIntrinsic(Builder.getVoidTy(),
195 Intrinsic::dx_resource_store_texture,
196 {Handle, Coords, V});
197 SI->replaceAllUsesWith(Inst);
198}
199
200static void emitRawStore(IRBuilder<> &Builder, Value *Buffer, Value *Index,
202 // For raw buffer (ie, HLSL's ByteAddressBuffer), we need to fold the access
203 // entirely into the index.
204 if (!RTI.isStruct()) {
205 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
206 if (!ConstantOffset || !ConstantOffset->isZero())
207 Index = Builder.CreateAdd(Index, Offset);
208 Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
209 }
210
211 Builder.CreateIntrinsic(Builder.getVoidTy(),
212 Intrinsic::dx_resource_store_rawbuffer,
213 {Buffer, Index, Offset, V});
214}
215
218 const DataLayout &DL = SI->getDataLayout();
219 IRBuilder<> Builder(SI);
220
221 Value *V = SI->getValueOperand();
222 assert(!V->getType()->isAggregateType() &&
223 "Resource store should be scalar or vector type");
224
225 Value *Index = II->getOperand(1);
226 // The offset for the rawbuffer load and store ops is always in bytes.
227 uint64_t AccessSize = 1;
228 Value *Offset =
229 traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
230
231 auto *VT = dyn_cast<FixedVectorType>(V->getType());
232 if (VT && VT->getNumElements() > 4) {
233 // Split into stores of at most 4 elements.
234 Type *EltTy = VT->getElementType();
235 Value *Stride = ConstantInt::get(Builder.getInt32Ty(),
236 4 * (DL.getTypeSizeInBits(EltTy) / 8));
237
238 SmallVector<int, 4> Indices;
239 for (unsigned int I = 0, N = VT->getNumElements(); I < N; I += 4) {
240 if (I > 0)
241 Offset = Builder.CreateAdd(Offset, Stride);
242
243 for (unsigned int J = I, E = std::min(N, J + 4); J < E; ++J)
244 Indices.push_back(J);
245 Value *Part = Builder.CreateShuffleVector(V, Indices);
246 emitRawStore(Builder, II->getOperand(0), Index, Offset, Part, RTI);
247
248 Indices.clear();
249 }
250 } else
251 emitRawStore(Builder, II->getOperand(0), Index, Offset, V, RTI);
252}
253
287
288static std::optional<dxil::AtomicBinOpCode>
328
331 std::optional<dxil::AtomicBinOpCode> BinOpCode =
333 if (!BinOpCode) {
334 reportFatalUsageError("DXIL resource atomicrmw operation not implemented");
335 return;
336 }
337
338 const DataLayout &DL = AI->getDataLayout();
339 IRBuilder<> Builder(AI);
340 Value *Index = II->getOperand(1);
341
342 // The offset for the rawbuffer load/store/atomic ops is always in bytes.
343 uint64_t AccessSize = 1;
344 Value *Offset =
345 traverseGEPOffsets(DL, Builder, AI->getPointerOperand(), AccessSize);
346
347 // For non-struct buffers (RawBuffer or TypedBuffer), fold the byte offset
348 // into the index and mark the coord1 arg as poison — only StructuredBuffer
349 // atomics use both a struct index and a byte offset.
350 if (!RTI.isStruct()) {
351 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
352 if (!ConstantOffset || !ConstantOffset->isZero())
353 Index = Builder.CreateAdd(Index, Offset);
354 Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
355 }
356
357 Value *BinOp = Builder.getInt32(static_cast<uint32_t>(*BinOpCode));
358
359 // Emit the target-independent intrinsic; DXILOpLowering lowers it to the
360 // DXIL `AtomicBinOp` op and handles the target-ext-typed handle cast via
361 // its `createTmpHandleCast` bookkeeping.
362 Value *Result = Builder.CreateIntrinsic(
363 AI->getType(), Intrinsic::dx_resource_atomic_binop,
364 {II->getOperand(0), BinOp, Index, Offset, AI->getValOperand()});
365
366 AI->replaceAllUsesWith(Result);
367}
368
403
406 const DataLayout &DL = LI->getDataLayout();
407 IRBuilder<> Builder(LI);
408 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
409 Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
410
411 Value *V =
412 Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_typedbuffer,
413 {II->getOperand(0), II->getOperand(1)});
414 V = Builder.CreateExtractValue(V, {0});
415
416 Type *ScalarType = ContainedType->getScalarType();
417 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
418 Value *Offset =
419 traverseGEPOffsets(DL, Builder, LI->getPointerOperand(), AccessSize);
420 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
421 if (!ConstantOffset || !ConstantOffset->isZero())
422 V = Builder.CreateExtractElement(V, Offset);
423
424 // If we loaded a <1 x ...> instead of a scalar (presumably to feed a
425 // shufflevector), then make sure we're maintaining the resulting type.
426 if (auto *VT = dyn_cast<FixedVectorType>(LI->getType()))
427 if (VT->getNumElements() == 1 && !isa<FixedVectorType>(V->getType()))
428 V = Builder.CreateInsertElement(PoisonValue::get(VT), V,
429 Builder.getInt32(0));
430
431 LI->replaceAllUsesWith(V);
432}
433
436 const DataLayout &DL = LI->getDataLayout();
437 IRBuilder<> Builder(LI);
438 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
439
440 Value *Handle = II->getOperand(0);
441 Value *Coords = II->getOperand(1);
442
443 // For operator[], mip level is 0.
444 Value *MipLevel = Builder.getInt32(0);
445
446 // For operator[], offsets are zero.
447 Value *Offsets = getNullOffsetsFor(Builder, Coords);
448
449 Value *V =
450 Builder.CreateIntrinsic(ContainedType, Intrinsic::dx_resource_load_level,
451 {Handle, Coords, MipLevel, Offsets});
452
453 Type *ScalarType = ContainedType->getScalarType();
454 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
455 Value *Offset =
456 traverseGEPOffsets(DL, Builder, LI->getPointerOperand(), AccessSize);
457 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
458 if (!ConstantOffset || !ConstantOffset->isZero())
459 V = Builder.CreateExtractElement(V, Offset);
460
461 // If we loaded a <1 x ...> instead of a scalar (presumably to feed a
462 // shufflevector), then make sure we're maintaining the resulting type.
463 if (auto *VT = dyn_cast<FixedVectorType>(LI->getType()))
464 if (VT->getNumElements() == 1 && !isa<FixedVectorType>(V->getType()))
465 V = Builder.CreateInsertElement(PoisonValue::get(VT), V,
466 Builder.getInt32(0));
467
468 LI->replaceAllUsesWith(V);
469}
470
471static Value *emitRawLoad(IRBuilder<> &Builder, Type *Ty, Value *Buffer,
472 Value *Index, Value *Offset,
474 // For raw buffer (ie, HLSL's ByteAddressBuffer), we need to fold the access
475 // entirely into the index.
476 if (!RTI.isStruct()) {
477 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
478 if (!ConstantOffset || !ConstantOffset->isZero())
479 Index = Builder.CreateAdd(Index, Offset);
480 Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
481 }
482
483 // The load intrinsic includes the bit for CheckAccessFullyMapped, so we need
484 // to add that to the return type.
485 Type *TypeWithCheck = StructType::get(Ty, Builder.getInt1Ty());
486 Value *V = Builder.CreateIntrinsic(TypeWithCheck,
487 Intrinsic::dx_resource_load_rawbuffer,
488 {Buffer, Index, Offset});
489 return Builder.CreateExtractValue(V, {0});
490}
491
494 const DataLayout &DL = LI->getDataLayout();
495 IRBuilder<> Builder(LI);
496
497 Value *Index = II->getOperand(1);
498 // The offset for the rawbuffer load and store ops is always in bytes.
499 uint64_t AccessSize = 1;
500 Value *Offset =
501 traverseGEPOffsets(DL, Builder, LI->getPointerOperand(), AccessSize);
502
503 // TODO: We could make this handle aggregates by walking the structure and
504 // handling each field individually, but we don't ever generate code that
505 // would hit that so it seems superfluous.
506 assert(!LI->getType()->isAggregateType() &&
507 "Resource load should be scalar or vector type");
508
509 Value *V;
510 if (auto *VT = dyn_cast<FixedVectorType>(LI->getType())) {
511 // Split into loads of at most 4 elements.
512 Type *EltTy = VT->getElementType();
513 Value *Stride = ConstantInt::get(Builder.getInt32Ty(),
514 4 * (DL.getTypeSizeInBits(EltTy) / 8));
515
517 for (unsigned int I = 0, N = VT->getNumElements(); I < N; I += 4) {
518 Type *Ty = FixedVectorType::get(EltTy, N - I < 4 ? N - I : 4);
519 if (I > 0)
520 Offset = Builder.CreateAdd(Offset, Stride);
521 Parts.push_back(
522 emitRawLoad(Builder, Ty, II->getOperand(0), Index, Offset, RTI));
523 }
524
525 V = Parts.size() > 1 ? concatenateVectors(Builder, Parts) : Parts[0];
526 } else
527 V = emitRawLoad(Builder, LI->getType(), II->getOperand(0), Index, Offset,
528 RTI);
529
530 LI->replaceAllUsesWith(V);
531}
532
533namespace {
534/// Helper for building a `load.cbufferrow` intrinsic given a simple type.
535struct CBufferRowIntrin {
536 Intrinsic::ID IID;
537 Type *RetTy;
538 unsigned int EltSize;
539 unsigned int NumElts;
540
541 CBufferRowIntrin(const DataLayout &DL, Type *Ty) {
542 assert(Ty == Ty->getScalarType() && "Expected scalar type");
543
544 switch (DL.getTypeSizeInBits(Ty)) {
545 case 16:
546 IID = Intrinsic::dx_resource_load_cbufferrow_8;
547 RetTy = StructType::get(Ty, Ty, Ty, Ty, Ty, Ty, Ty, Ty);
548 EltSize = 2;
549 NumElts = 8;
550 break;
551 case 32:
552 IID = Intrinsic::dx_resource_load_cbufferrow_4;
553 RetTy = StructType::get(Ty, Ty, Ty, Ty);
554 EltSize = 4;
555 NumElts = 4;
556 break;
557 case 64:
558 IID = Intrinsic::dx_resource_load_cbufferrow_2;
559 RetTy = StructType::get(Ty, Ty);
560 EltSize = 8;
561 NumElts = 2;
562 break;
563 default:
564 llvm_unreachable("Only 16, 32, and 64 bit types supported");
565 }
566 }
567};
568} // namespace
569
572 const DataLayout &DL = LI->getDataLayout();
573
574 Type *Ty = LI->getType();
575 assert(!isa<StructType>(Ty) && "Structs not handled yet");
576 CBufferRowIntrin Intrin(DL, Ty->getScalarType());
577
578 StringRef Name = LI->getName();
579 Value *Handle = II->getOperand(0);
580
581 IRBuilder<> Builder(LI);
582
583 ConstantInt *GlobalOffset =
584 II->getIntrinsicID() == Intrinsic::dx_resource_getbasepointer
585 ? ConstantInt::get(Builder.getInt32Ty(), 0)
586 : dyn_cast<ConstantInt>(II->getOperand(1));
587 assert(GlobalOffset && "CBuffer getpointer index must be constant");
588
589 uint64_t GlobalOffsetVal = GlobalOffset->getZExtValue();
590 Value *CurrentRow = ConstantInt::get(
591 Builder.getInt32Ty(), GlobalOffsetVal / hlsl::CBufferRowSizeInBytes);
592 unsigned int CurrentIndex =
593 (GlobalOffsetVal % hlsl::CBufferRowSizeInBytes) / Intrin.EltSize;
594
595 // Every object in a cbuffer either fits in a row or is aligned to a row. This
596 // means that only the very last pointer access can point into a row.
597 auto *LastGEP = dyn_cast<GEPOperator>(LI->getPointerOperand());
598 if (!LastGEP) {
599 // If we don't have a GEP at all we're just accessing the resource through
600 // the result of getpointer directly.
601 assert(LI->getPointerOperand() == II &&
602 "Unexpected indirect access to resource without GEP");
603 } else {
604 Value *GEPOffset = traverseGEPOffsets(
605 DL, Builder, LastGEP->getPointerOperand(), hlsl::CBufferRowSizeInBytes);
606 CurrentRow = Builder.CreateAdd(GEPOffset, CurrentRow);
607
608 APInt ConstantOffset(DL.getIndexTypeSizeInBits(LastGEP->getType()), 0);
609 if (LastGEP->accumulateConstantOffset(DL, ConstantOffset)) {
610 APInt Remainder(DL.getIndexTypeSizeInBits(LastGEP->getType()),
612 APInt::udivrem(ConstantOffset, Remainder, ConstantOffset, Remainder);
613 CurrentRow = Builder.CreateAdd(
614 CurrentRow, ConstantInt::get(Builder.getInt32Ty(), ConstantOffset));
615 CurrentIndex += Remainder.udiv(Intrin.EltSize).getZExtValue();
616 } else {
617 assert(LastGEP->getNumIndices() == 1 &&
618 "Last GEP of cbuffer access is not array or struct access");
619 // We assume a non-constant access will be row-aligned. This is safe
620 // because arrays and structs are always row aligned, and accesses to
621 // vector elements will show up as a load of the vector followed by an
622 // extractelement.
623 CurrentRow = cast<ConstantInt>(CurrentRow)->isZero()
624 ? *LastGEP->idx_begin()
625 : Builder.CreateAdd(CurrentRow, *LastGEP->idx_begin());
626 CurrentIndex = 0;
627 }
628 }
629
630 auto *CBufLoad = Builder.CreateIntrinsic(
631 Intrin.RetTy, Intrin.IID, {Handle, CurrentRow}, nullptr, Name + ".load");
632 auto *Elt =
633 Builder.CreateExtractValue(CBufLoad, {CurrentIndex++}, Name + ".extract");
634
635 // At this point we've loaded the first scalar of our result, but our original
636 // type may have been a vector.
637 unsigned int Remaining =
638 ((DL.getTypeSizeInBits(Ty) / 8) / Intrin.EltSize) - 1;
639 if (Remaining == 0) {
640 // We only have a single element, so we're done.
641 Value *Result = Elt;
642
643 // However, if we loaded a <1 x T>, then we need to adjust the type.
644 if (auto *VT = dyn_cast<FixedVectorType>(Ty)) {
645 assert(VT->getNumElements() == 1 && "Can't have multiple elements here");
646 Result = Builder.CreateInsertElement(PoisonValue::get(VT), Result,
647 Builder.getInt32(0), Name);
648 }
649 LI->replaceAllUsesWith(Result);
650 return;
651 }
652
653 // Walk each element and extract it, wrapping to new rows as needed.
654 SmallVector<Value *> Extracts{Elt};
655 while (Remaining--) {
656 CurrentIndex %= Intrin.NumElts;
657
658 if (CurrentIndex == 0) {
659 CurrentRow = Builder.CreateAdd(CurrentRow,
660 ConstantInt::get(Builder.getInt32Ty(), 1));
661 CBufLoad = Builder.CreateIntrinsic(Intrin.RetTy, Intrin.IID,
662 {Handle, CurrentRow}, nullptr,
663 Name + ".load");
664 }
665
666 Extracts.push_back(Builder.CreateExtractValue(CBufLoad, {CurrentIndex++},
667 Name + ".extract"));
668 }
669
670 // Finally, we build up the original loaded value.
671 Value *Result = PoisonValue::get(Ty);
672 for (int I = 0, E = Extracts.size(); I < E; ++I)
673 Result = Builder.CreateInsertElement(
674 Result, Extracts[I], Builder.getInt32(I), Name + formatv(".upto{}", I));
675 LI->replaceAllUsesWith(Result);
676}
677
711
713 if (auto *LI = dyn_cast<LoadInst>(AI))
714 return dyn_cast<Instruction>(LI->getPointerOperand());
715 if (auto *SI = dyn_cast<StoreInst>(AI))
716 return dyn_cast<Instruction>(SI->getPointerOperand());
717 if (auto *RMWI = dyn_cast<AtomicRMWInst>(AI))
718 return dyn_cast<Instruction>(RMWI->getPointerOperand());
719
720 return nullptr;
721}
722
723static const std::array<Intrinsic::ID, 2> HandleIntrins = {
724 Intrinsic::dx_resource_handlefrombinding,
725 Intrinsic::dx_resource_handlefromimplicitbinding,
726};
727
729 SmallVector<Value *> Worklist = {Ptr};
731
732 while (!Worklist.empty()) {
733 Value *X = Worklist.pop_back_val();
734
735 if (!X->getType()->isPointerTy() && !X->getType()->isTargetExtTy())
736 return {}; // Early exit on store/load into non-resource
737
738 if (auto *Phi = dyn_cast<PHINode>(X))
739 for (Use &V : Phi->incoming_values())
740 Worklist.push_back(V.get());
741 else if (auto *Select = dyn_cast<SelectInst>(X))
742 for (Value *V : {Select->getTrueValue(), Select->getFalseValue()})
743 Worklist.push_back(V);
744 else if (auto *II = dyn_cast<IntrinsicInst>(X)) {
745 Intrinsic::ID IID = II->getIntrinsicID();
746
747 if (IID == Intrinsic::dx_resource_getpointer)
748 Worklist.push_back(II->getArgOperand(/*Handle=*/0));
749
751 Handles.push_back(II);
752 }
753 }
754
755 return Handles;
756}
757
759 DXILResourceTypeMap &DRTM) {
761 "Only expects a Handle as determined from collectUsedHandles.");
762
763 auto *HandleTy = cast<TargetExtType>(Handle->getType());
764 dxil::ResourceClass Class = DRTM[HandleTy].getResourceClass();
765 uint32_t Space = cast<ConstantInt>(Handle->getArgOperand(0))->getZExtValue();
766 uint32_t LowerBound =
767 cast<ConstantInt>(Handle->getArgOperand(1))->getZExtValue();
768 uint32_t Size = cast<ConstantInt>(Handle->getArgOperand(2))->getZExtValue();
769 uint32_t UpperBound = Size == UINT32_MAX ? UINT32_MAX : LowerBound + Size - 1;
770
771 return hlsl::Binding(Class, Space, LowerBound, UpperBound, nullptr);
772}
773
774namespace {
775/// Helper for propagating the current handle and ptr indices.
776struct AccessIndices {
777 Value *GetPtrIdx;
778 Value *HandleIdx;
779
780 bool hasGetPtrIdx() { return GetPtrIdx != nullptr; }
781 bool hasHandleIdx() { return HandleIdx != nullptr; }
782};
783} // namespace
784
785// getAccessIndices traverses up the control flow that a ptr came from and
786// propagates back the indicies used to access the resource (AccessIndices):
787//
788// - GetPtrIdx is the index of dx.resource.getpointer
789// - HandleIdx is the index of dx.resource.handlefrom.*
790static AccessIndices
792 if (auto *II = dyn_cast<IntrinsicInst>(I)) {
793 if (llvm::is_contained(HandleIntrins, II->getIntrinsicID())) {
794 DeadInsts.insert(II);
795 return {nullptr, II->getArgOperand(/*Index=*/3)};
796 }
797
798 if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
799 auto *V = dyn_cast<Instruction>(II->getArgOperand(/*Handle=*/0));
800 auto AccessIdx = getAccessIndices(V, DeadInsts);
801 assert(!AccessIdx.hasGetPtrIdx() &&
802 "Encountered multiple dx.resource.getpointers in ptr chain?");
803 AccessIdx.GetPtrIdx = II->getArgOperand(1);
804
805 DeadInsts.insert(II);
806 return AccessIdx;
807 }
808 }
809
810 if (auto *Phi = dyn_cast<PHINode>(I)) {
811 unsigned NumEdges = Phi->getNumIncomingValues();
812 assert(NumEdges != 0 && "Malformed Phi Node");
813
814 IRBuilder<> Builder(Phi);
815 PHINode *GetPtrPhi = PHINode::Create(Builder.getInt32Ty(), NumEdges);
816 PHINode *HandlePhi = PHINode::Create(Builder.getInt32Ty(), NumEdges);
817
818 bool HasGetPtr = true;
819 for (unsigned Idx = 0; Idx < NumEdges; Idx++) {
820 auto *BB = Phi->getIncomingBlock(Idx);
821 auto *V = dyn_cast<Instruction>(Phi->getIncomingValue(Idx));
822 auto AccessIdx = getAccessIndices(V, DeadInsts);
823 HasGetPtr &= AccessIdx.hasGetPtrIdx();
824 if (HasGetPtr)
825 GetPtrPhi->addIncoming(AccessIdx.GetPtrIdx, BB);
826 HandlePhi->addIncoming(AccessIdx.HandleIdx, BB);
827 }
828
829 if (HasGetPtr)
830 Builder.Insert(GetPtrPhi);
831 else
832 GetPtrPhi = nullptr;
833
834 Builder.Insert(HandlePhi);
835
836 DeadInsts.insert(Phi);
837 return {GetPtrPhi, HandlePhi};
838 }
839
840 if (auto *Select = dyn_cast<SelectInst>(I)) {
841 auto *TrueV = dyn_cast<Instruction>(Select->getTrueValue());
842 auto TrueAccessIdx = getAccessIndices(TrueV, DeadInsts);
843
844 auto *FalseV = dyn_cast<Instruction>(Select->getFalseValue());
845 auto FalseAccessIdx = getAccessIndices(FalseV, DeadInsts);
846
847 IRBuilder<> Builder(Select);
848 Value *GetPtrSelect = nullptr;
849
850 if (TrueAccessIdx.hasGetPtrIdx() && FalseAccessIdx.hasGetPtrIdx())
851 GetPtrSelect =
852 Builder.CreateSelect(Select->getCondition(), TrueAccessIdx.GetPtrIdx,
853 FalseAccessIdx.GetPtrIdx);
854
855 auto *HandleSelect =
856 Builder.CreateSelect(Select->getCondition(), TrueAccessIdx.HandleIdx,
857 FalseAccessIdx.HandleIdx);
858 DeadInsts.insert(Select);
859 return {GetPtrSelect, HandleSelect};
860 }
861
862 llvm_unreachable("collectUsedHandles should assure this does not occur");
863}
864
865static void
868 auto AccessIdx = getAccessIndices(Ptr, DeadInsts);
869 assert(AccessIdx.hasGetPtrIdx() && AccessIdx.hasHandleIdx() &&
870 "Couldn't retrieve indices. This is guaranteed by getAccessIndices");
871
872 IRBuilder<> Builder(Ptr);
873 if (isa<PHINode>(Ptr))
874 Builder.SetInsertPoint(Ptr->getParent()->getFirstNonPHIIt());
875 IntrinsicInst *Handle = cast<IntrinsicInst>(OldHandle->clone());
876 Handle->setArgOperand(/*Index=*/3, AccessIdx.HandleIdx);
877 Builder.Insert(Handle);
878
879 auto *GetPtr =
880 Builder.CreateIntrinsic(Ptr->getType(), Intrinsic::dx_resource_getpointer,
881 {Handle, AccessIdx.GetPtrIdx});
882
883 Ptr->replaceAllUsesWith(GetPtr);
884 DeadInsts.insert(Ptr);
885}
886
887// Try to legalize dx.resource.handlefrom.*.binding and dx.resource.getpointer
888// calls with their respective index values and propagate the index values to
889// be used at resource access.
890//
891// If it can't be transformed to be legal then:
892//
893// Reports an error if a resource access is not guaranteed into a unique global
894// resource.
895//
896// Returns true if any changes are made.
899 for (BasicBlock &BB : make_early_inc_range(F)) {
900 for (Instruction &I : BB) {
901 if (auto *PtrOp = getStoreLoadPointerOperand(&I)) {
903 unsigned NumHandles = Handles.size();
904 if (NumHandles <= 1)
905 continue; // Legal, no-replacement required
906
907 bool SameGlobalBinding = true;
908 hlsl::Binding B = getHandleIntrinsicBinding(Handles[0], DRTM);
909 for (unsigned Idx = 1; Idx < NumHandles; Idx++)
910 SameGlobalBinding &=
911 (B == getHandleIntrinsicBinding(Handles[Idx], DRTM));
912
913 if (!SameGlobalBinding) {
915 continue;
916 }
917
918 replaceHandleWithIndices(PtrOp, Handles[0], DeadInsts);
919 }
920 }
921 }
922
923 bool MadeChanges = false;
924
925 for (auto *I : llvm::reverse(DeadInsts))
926 if (I->hasNUses(0)) { // Handle can still be used outside of replaced path
927 I->eraseFromParent();
928 MadeChanges = true;
929 }
930
931 return MadeChanges;
932}
933
935 SmallVector<User *> Worklist;
936 for (User *U : II->users())
937 Worklist.push_back(U);
938
940 while (!Worklist.empty()) {
941 User *U = Worklist.back();
942 Worklist.pop_back();
943
944 if (auto *GEP = dyn_cast<GetElementPtrInst>(U)) {
945 for (User *U : GEP->users())
946 Worklist.push_back(U);
947 DeadInsts.push_back(GEP);
948
949 } else if (auto *SI = dyn_cast<StoreInst>(U)) {
950 assert(SI->getValueOperand() != II && "Pointer escaped!");
952 DeadInsts.push_back(SI);
953
954 } else if (auto *LI = dyn_cast<LoadInst>(U)) {
955 createLoadIntrinsic(II, LI, RTI);
956 DeadInsts.push_back(LI);
957 } else if (auto *AI = dyn_cast<AtomicRMWInst>(U)) {
959 DeadInsts.push_back(AI);
960 } else
961 llvm_unreachable("Unhandled instruction - pointer escaped?");
962 }
963
964 // Traverse the now-dead instructions in RPO and remove them.
965 for (Instruction *Dead : llvm::reverse(DeadInsts))
966 Dead->eraseFromParent();
967 II->eraseFromParent();
968}
969
973 for (Instruction &I : BB)
974 if (auto *II = dyn_cast<IntrinsicInst>(&I))
975 if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer ||
976 II->getIntrinsicID() == Intrinsic::dx_resource_getbasepointer) {
977 auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());
978 assert(
979 (DRTM[HandleTy].isCBuffer() ||
980 II->getIntrinsicID() != Intrinsic::dx_resource_getbasepointer) &&
981 "dx_resource_getbasepointer should only be used by cbuffers");
982 Resources.emplace_back(II, DRTM[HandleTy]);
983 }
984
985 for (auto &[II, RI] : Resources)
986 replaceAccess(II, RI);
987
988 return !Resources.empty();
989}
990
993 auto &MAMProxy = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
994 DXILResourceTypeMap *DRTM =
995 MAMProxy.getCachedResult<DXILResourceTypeAnalysis>(*F.getParent());
996 assert(DRTM && "DXILResourceTypeAnalysis must be available");
997
998 bool MadeHandleChanges = legalizeResourceHandles(F, *DRTM);
999 bool MadeResourceChanges = transformResourcePointers(F, *DRTM);
1000 if (!(MadeHandleChanges || MadeResourceChanges))
1001 return PreservedAnalyses::all();
1002
1006 return PA;
1007}
1008
1009namespace {
1010class DXILResourceAccessLegacy : public FunctionPass {
1011public:
1012 bool runOnFunction(Function &F) override {
1013 DXILResourceTypeMap &DRTM =
1014 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
1015 bool MadeHandleChanges = legalizeResourceHandles(F, DRTM);
1016 bool MadeResourceChanges = transformResourcePointers(F, DRTM);
1017 return MadeHandleChanges || MadeResourceChanges;
1018 }
1019 StringRef getPassName() const override { return "DXIL Resource Access"; }
1020 DXILResourceAccessLegacy() : FunctionPass(ID) {}
1021
1022 static char ID; // Pass identification.
1023 void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
1024 AU.addRequired<DXILResourceTypeWrapperPass>();
1025 AU.addPreserved<DominatorTreeWrapperPass>();
1026 }
1027};
1028char DXILResourceAccessLegacy::ID = 0;
1029} // end anonymous namespace
1030
1031INITIALIZE_PASS_BEGIN(DXILResourceAccessLegacy, DEBUG_TYPE,
1032 "DXIL Resource Access", false, false)
1034INITIALIZE_PASS_END(DXILResourceAccessLegacy, DEBUG_TYPE,
1035 "DXIL Resource Access", false, false)
1036
1038 return new DXILResourceAccessLegacy();
1039}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
AMDGPU Register Bank Select
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
#define X(NUM, ENUM, NAME)
Definition ELF.h:856
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
DXIL Remove Unused Resources
static void diagnoseNonUniqueResourceAccess(Instruction *I, ArrayRef< IntrinsicInst * > Handles)
static std::optional< dxil::AtomicBinOpCode > getAtomicBinOpCode(AtomicRMWInst::BinOp BinOp)
static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, dxil::ResourceTypeInfo &RTI)
static void createTextureStore(IntrinsicInst *II, StoreInst *SI, dxil::ResourceTypeInfo &RTI)
static Value * emitRawLoad(IRBuilder<> &Builder, Type *Ty, Value *Buffer, Value *Index, Value *Offset, 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 createTextureLoad(IntrinsicInst *II, LoadInst *LI, dxil::ResourceTypeInfo &RTI)
static void replaceHandleWithIndices(Instruction *Ptr, IntrinsicInst *OldHandle, SmallSetVector< Instruction *, 16 > &DeadInsts)
static void emitRawStore(IRBuilder<> &Builder, Value *Buffer, Value *Index, Value *Offset, Value *V, dxil::ResourceTypeInfo &RTI)
static Value * getNullOffsetsFor(IRBuilder<> &Builder, Value *Coords)
Build a zero-initialized offset operand matching the shape of the given coordinate operand.
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 void createRawStores(IntrinsicInst *II, StoreInst *SI, dxil::ResourceTypeInfo &RTI)
static void createRawLoads(IntrinsicInst *II, LoadInst *LI, dxil::ResourceTypeInfo &RTI)
static void createAtomicBinOp(IntrinsicInst *II, AtomicRMWInst *AI, dxil::ResourceTypeInfo &RTI)
static Instruction * getStoreLoadPointerOperand(Instruction *AI)
static void createAtomicBinOpIntrinsic(IntrinsicInst *II, AtomicRMWInst *AI, dxil::ResourceTypeInfo &RTI)
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.
Class for arbitrary precision integers.
Definition APInt.h:78
LLVM_ABI APInt udiv(const APInt &RHS) const
Unsigned division operation.
Definition APInt.cpp:1599
static LLVM_ABI void udivrem(const APInt &LHS, const APInt &RHS, APInt &Quotient, APInt &Remainder)
Dual division/remainder interface.
Definition APInt.cpp:1793
uint64_t getZExtValue() const
Get zero extended value.
Definition APInt.h:1565
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
an instruction that atomically reads a memory location, combines it with another value,...
BinOp
This enumeration lists the possible modifications atomicrmw can make.
@ Add
*p = old + v
@ FAdd
*p = old + v
@ USubCond
Subtract only if no unsigned overflow.
@ FMinimum
*p = minimum(old, v) minimum matches the behavior of llvm.minimum.
@ Min
*p = old <signed v ? old : v
@ Sub
*p = old - v
@ And
*p = old & v
@ Xor
*p = old ^ v
@ USubSat
*p = usub.sat(old, v) usub.sat matches the behavior of llvm.usub.sat.
@ FMaximum
*p = maximum(old, v) maximum matches the behavior of llvm.maximum.
@ FSub
*p = old - v
@ UIncWrap
Increment one up to a maximum value.
@ Max
*p = old >signed v ? old : v
@ UMin
*p = old <unsigned v ? old : v
@ FMin
*p = minnum(old, v) minnum matches the behavior of llvm.minnum.
@ UMax
*p = old >unsigned v ? old : v
@ FMaximumNum
*p = maximumnum(old, v) maximumnum matches the behavior of llvm.maximumnum.
@ FMax
*p = maxnum(old, v) maxnum matches the behavior of llvm.maxnum.
@ UDecWrap
Decrement one until a minimum value or zero.
@ FMinimumNum
*p = minimumnum(old, v) minimumnum matches the behavior of llvm.minimumnum.
@ Nand
*p = ~(old & v)
Value * getPointerOperand()
BinOp getOperation() const
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
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
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:241
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
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2893
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:157
A SetVector that performs no allocations if smaller than a certain size.
Definition SetVector.h:345
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.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
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:477
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:46
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:309
Type * getScalarType() const
If this is a vector type, return the element type, otherwise return 'this'.
Definition Type.h:368
bool isAggregateType() const
Return true if the type is an aggregate type.
Definition Type.h:319
static LLVM_ABI IntegerType * getInt1Ty(LLVMContext &C)
Definition Type.cpp:306
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:255
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:319
TargetExtType * getHandleTy() const
LLVM_ABI bool isStruct() const
dxil::ResourceKind getResourceKind() const
const ParentTy * getParent() const
Definition ilist_node.h:34
A raw_ostream that writes to an std::string.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
const unsigned CBufferRowSizeInBytes
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:578
@ 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
@ Load
The value being inserted comes from a load (InsertElement only).
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:633
LLVM_ABI Value * concatenateVectors(IRBuilderBase &Builder, ArrayRef< Value * > Vecs)
Concatenate a list of vectors.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
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
#define N