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/DenseMap.h"
12#include "llvm/ADT/SetVector.h"
13#include "llvm/ADT/SmallSet.h"
17#include "llvm/IR/BasicBlock.h"
18#include "llvm/IR/Dominators.h"
19#include "llvm/IR/IRBuilder.h"
20#include "llvm/IR/Instruction.h"
23#include "llvm/IR/Intrinsics.h"
24#include "llvm/IR/IntrinsicsDirectX.h"
25#include "llvm/IR/LLVMContext.h"
26#include "llvm/IR/User.h"
27#include "llvm/IR/ValueHandle.h"
34#include <optional>
35
36#define DEBUG_TYPE "dxil-resource-access"
37
38using namespace llvm;
39
40[[noreturn]] static void
43 LLVMContext &Context = I->getContext();
44 std::string InstStr;
45 raw_string_ostream InstOS(InstStr);
46 I->print(InstOS);
47 Context.diagnose(
48 DiagnosticInfoGeneric("At resource access:" + Twine(InstStr), DS_Note));
49
50 for (auto *Handle : Handles) {
51 std::string HandleStr;
52 raw_string_ostream HandleOS(HandleStr);
53 Handle->print(HandleOS);
54 Context.diagnose(DiagnosticInfoGeneric(
55 "Uses resource handle:" + Twine(HandleStr), DS_Note));
56 }
58 "Resource access is not guaranteed to map to a unique global resource",
59 /*gen_crash_diag=*/false);
60}
61
63 Value *Ptr, uint64_t AccessSize) {
64 Value *Offset = nullptr;
65
66 while (Ptr) {
67 if ([[maybe_unused]] auto *II = dyn_cast<IntrinsicInst>(Ptr)) {
68 assert((II->getIntrinsicID() == Intrinsic::dx_resource_getpointer ||
69 II->getIntrinsicID() == Intrinsic::dx_resource_getbasepointer) &&
70 "Resource access through unexpected intrinsic");
71 return Offset ? Offset : ConstantInt::get(Builder.getInt32Ty(), 0);
72 }
73
75 assert(GEP && "Resource access through unexpected instruction");
76
77 unsigned NumIndices = GEP->getNumIndices();
78 uint64_t IndexScale = DL.getTypeAllocSize(GEP->getSourceElementType());
79 APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
80 Value *GEPOffset;
81 if (GEP->accumulateConstantOffset(DL, ConstantOffset)) {
82 // We have a constant offset (in bytes).
83 GEPOffset =
84 ConstantInt::get(DL.getIndexType(GEP->getType()), ConstantOffset);
85 IndexScale = 1;
86 } else if (NumIndices == 1) {
87 // If we have a single index we're indexing into a top level array. This
88 // generally only happens with cbuffers.
89 GEPOffset = *GEP->idx_begin();
90 } else if (NumIndices == 2) {
91 // If we have two indices, this should be an access through a pointer.
92 auto *IndexIt = GEP->idx_begin();
93 assert(cast<ConstantInt>(IndexIt)->getZExtValue() == 0 &&
94 "GEP is not indexing through pointer");
95 GEPOffset = *(++IndexIt);
96 } else
97 llvm_unreachable("Unhandled GEP structure for resource access");
98
99 uint64_t ElemSize = AccessSize;
100 if (!(IndexScale % ElemSize)) {
101 // If our scale is an exact multiple of the access size, adjust the
102 // scaling to avoid an unnecessary division.
103 IndexScale /= ElemSize;
104 ElemSize = 1;
105 }
106 if (IndexScale != 1)
107 GEPOffset = Builder.CreateMul(
108 GEPOffset, ConstantInt::get(Builder.getInt32Ty(), IndexScale));
109 if (ElemSize != 1)
110 GEPOffset = Builder.CreateUDiv(
111 GEPOffset, ConstantInt::get(Builder.getInt32Ty(), ElemSize));
112
113 Offset = Offset ? Builder.CreateAdd(Offset, GEPOffset) : GEPOffset;
114 Ptr = GEP->getPointerOperand();
115 }
116
117 llvm_unreachable("GEP of null pointer?");
118}
119
122 const DataLayout &DL = SI->getDataLayout();
123 IRBuilder<> Builder(SI);
124 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
125 Type *ScalarType = ContainedType->getScalarType();
126 Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
127
128 Value *V = SI->getValueOperand();
129 if (V->getType() == ContainedType) {
130 // V is already the right type.
131 assert(SI->getPointerOperand() == II &&
132 "Store of whole element has mismatched address to store to");
133 } else if (V->getType() == ScalarType) {
134 // We're storing a scalar, so we need to load the current value and only
135 // replace the relevant part.
136 auto *Load = Builder.CreateIntrinsic(
137 LoadType, Intrinsic::dx_resource_load_typedbuffer,
138 {II->getOperand(0), II->getOperand(1)});
139 auto *Struct = Builder.CreateExtractValue(Load, {0});
140
141 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
142 Value *Offset =
143 traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
144 V = Builder.CreateInsertElement(Struct, V, Offset);
145 } else {
146 llvm_unreachable("Store to typed resource has invalid type");
147 }
148
149 auto *Inst = Builder.CreateIntrinsic(
150 Builder.getVoidTy(), Intrinsic::dx_resource_store_typedbuffer,
151 {II->getOperand(0), II->getOperand(1), V});
152 SI->replaceAllUsesWith(Inst);
153}
154
155/// Build a zero-initialized offset operand matching the shape of the given
156/// coordinate operand. Accesses through `operator[]` never have offsets.
157static Value *getNullOffsetsFor(IRBuilder<> &Builder, Value *Coords) {
158 Type *CoordTy = Coords->getType();
159 Type *OffsetTy;
160 if (auto *VecTy = dyn_cast<FixedVectorType>(CoordTy))
161 OffsetTy =
162 FixedVectorType::get(Builder.getInt32Ty(), VecTy->getNumElements());
163 else
164 OffsetTy = Builder.getInt32Ty();
165 return Constant::getNullValue(OffsetTy);
166}
167
170 const DataLayout &DL = SI->getDataLayout();
171 IRBuilder<> Builder(SI);
172 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
173 Type *ScalarType = ContainedType->getScalarType();
174
175 Value *Handle = II->getOperand(0);
176 Value *Coords = II->getOperand(1);
177
178 Value *V = SI->getValueOperand();
179 if (V->getType() == ContainedType) {
180 // V is already the right type.
181 assert(SI->getPointerOperand() == II &&
182 "Store of whole element has mismatched address to store to");
183 } else if (V->getType() == ScalarType) {
184 // We're storing a scalar, so we need to load the current value and only
185 // replace the relevant part. For operator[] the mip level and the offsets
186 // are always zero; DXILOpLowering drops the mip level for UAVs.
187 Value *MipLevel = Builder.getInt32(0);
188 Value *Offsets = getNullOffsetsFor(Builder, Coords);
189 auto *Load = Builder.CreateIntrinsic(ContainedType,
190 Intrinsic::dx_resource_load_level,
191 {Handle, Coords, MipLevel, Offsets});
192
193 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
194 Value *Offset =
195 traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
196 V = Builder.CreateInsertElement(Load, V, Offset);
197 } else {
198 llvm_unreachable("Store to texture resource has invalid type");
199 }
200
201 auto *Inst = Builder.CreateIntrinsic(Builder.getVoidTy(),
202 Intrinsic::dx_resource_store_texture,
203 {Handle, Coords, V});
204 SI->replaceAllUsesWith(Inst);
205}
206
207static void emitRawStore(IRBuilder<> &Builder, Value *Buffer, Value *Index,
209 // For raw buffer (ie, HLSL's ByteAddressBuffer), we need to fold the access
210 // entirely into the index.
211 if (!RTI.isStruct()) {
212 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
213 if (!ConstantOffset || !ConstantOffset->isZero())
214 Index = Builder.CreateAdd(Index, Offset);
215 Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
216 }
217
218 Builder.CreateIntrinsic(Builder.getVoidTy(),
219 Intrinsic::dx_resource_store_rawbuffer,
220 {Buffer, Index, Offset, V});
221}
222
225 const DataLayout &DL = SI->getDataLayout();
226 IRBuilder<> Builder(SI);
227
228 Value *V = SI->getValueOperand();
229 assert(!V->getType()->isAggregateType() &&
230 "Resource store should be scalar or vector type");
231
232 Value *Index = II->getOperand(1);
233 // The offset for the rawbuffer load and store ops is always in bytes.
234 uint64_t AccessSize = 1;
235 Value *Offset =
236 traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
237
238 auto *VT = dyn_cast<FixedVectorType>(V->getType());
239 if (VT && VT->getNumElements() > 4) {
240 // Split into stores of at most 4 elements.
241 Type *EltTy = VT->getElementType();
242 Value *Stride = ConstantInt::get(Builder.getInt32Ty(),
243 4 * (DL.getTypeSizeInBits(EltTy) / 8));
244
245 SmallVector<int, 4> Indices;
246 for (unsigned int I = 0, N = VT->getNumElements(); I < N; I += 4) {
247 if (I > 0)
248 Offset = Builder.CreateAdd(Offset, Stride);
249
250 for (unsigned int J = I, E = std::min(N, J + 4); J < E; ++J)
251 Indices.push_back(J);
252 Value *Part = Builder.CreateShuffleVector(V, Indices);
253 emitRawStore(Builder, II->getOperand(0), Index, Offset, Part, RTI);
254
255 Indices.clear();
256 }
257 } else
258 emitRawStore(Builder, II->getOperand(0), Index, Offset, V, RTI);
259}
260
294
295static std::optional<dxil::AtomicBinOpCode>
335
336// Compute the (coord0, coord1) pair for a buffer resource atomic operation.
337// Non-struct buffers (RawBuffer or TypedBuffer) fold the byte offset into the
338// index and leave coord1 poison. Only StructuredBuffer atomics use both a
339// struct index and a byte offset.
340static std::pair<Value *, Value *>
342 dxil::ResourceTypeInfo &RTI, IRBuilder<> &Builder,
343 const DataLayout &DL) {
344 Value *Index = II->getOperand(1);
345
346 // The offset for the rawbuffer load/store/atomic ops is always in bytes.
347 uint64_t AccessSize = 1;
348 Value *Offset = traverseGEPOffsets(DL, Builder, PointerOperand, AccessSize);
349
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 return {Index, Offset};
358}
359
360// The coordinates of a texture access are a scalar or a vector with one element
361// per texture dimension, including the array slice if there is one. These map
362// directly onto the coordinate operands of the atomic ops.
364 IRBuilder<> &Builder) {
365 Value *Coords = II->getOperand(1);
366 SmallVector<Value *, 3> CoordArgs;
367 if (auto *VecTy = dyn_cast<FixedVectorType>(Coords->getType())) {
368 assert(VecTy->getNumElements() <= 3 && "Too many texture coordinates");
369 for (unsigned I = 0, E = VecTy->getNumElements(); I != E; ++I)
370 CoordArgs.push_back(Builder.CreateExtractElement(Coords, I));
371 } else {
372 CoordArgs.push_back(Coords);
373 }
374 return CoordArgs;
375}
376
377static void emitAtomicBinOp(IRBuilder<> &Builder, AtomicRMWInst *AI,
378 Value *Handle, ArrayRef<Value *> Coords) {
379 assert(!Coords.empty() && Coords.size() <= 3 &&
380 "Atomic operations take between one and three coordinates");
381
382 std::optional<dxil::AtomicBinOpCode> BinOpCode =
384 if (!BinOpCode) {
385 reportFatalUsageError("DXIL resource atomicrmw operation not implemented");
386 return;
387 }
388
389 // DXIL has no floating-point atomic op. A float exchange only moves the bit
390 // pattern, so cast the value to an integer of the same width, exchange, and
391 // cast the result back. This matches what DXC emits.
392 Value *Val = AI->getValOperand();
393 Type *ValTy = Val->getType();
394 Type *OpTy = ValTy;
395 if (ValTy->isFloatingPointTy()) {
396 OpTy = Builder.getIntNTy(ValTy->getPrimitiveSizeInBits());
397 Val = Builder.CreateBitCast(Val, OpTy);
398 }
399
401 Handle, Builder.getInt32(static_cast<uint32_t>(*BinOpCode))};
402 append_range(Args, Coords);
403 Args.append(3 - Coords.size(), PoisonValue::get(Builder.getInt32Ty()));
404 Args.push_back(Val);
405
406 // Emit the target-independent intrinsic; DXILOpLowering lowers it to the
407 // DXIL `AtomicBinOp` op and handles the target-ext-typed handle cast via
408 // its `createTmpHandleCast` bookkeeping.
409 Value *Result =
410 Builder.CreateIntrinsic(OpTy, Intrinsic::dx_resource_atomic_binop, Args);
411
412 if (OpTy != ValTy)
413 Result = Builder.CreateBitCast(Result, ValTy);
414
415 AI->replaceAllUsesWith(Result);
416}
417
420 const DataLayout &DL = AI->getDataLayout();
421 IRBuilder<> Builder(AI);
422 auto [Index, Offset] =
423 getAtomicResourceCoords(II, AI->getPointerOperand(), RTI, Builder, DL);
424
425 emitAtomicBinOp(Builder, AI, II->getOperand(0), {Index, Offset});
426}
427
430 // A texture atomic operates on a whole texel, so a multi-component texel has
431 // no single addressable component. A scalar float texel is allowed, because
432 // emitAtomicBinOp exchanges its bit pattern as an integer.
433 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
434 if (!ContainedType->isIntegerTy() && !ContainedType->isFloatingPointTy()) {
435 reportFatalUsageError("DXIL atomicrmw requires a texture resource with a "
436 "scalar element type");
437 return;
438 }
439
440 IRBuilder<> Builder(AI);
441
442 emitAtomicBinOp(Builder, AI, II->getOperand(0),
443 getTextureAtomicCoords(II, Builder));
444}
445
447 AtomicCmpXchgInst *AI, Value *Handle,
448 ArrayRef<Value *> Coords) {
449 assert(!Coords.empty() && Coords.size() <= 3 &&
450 "Atomic operations take between one and three coordinates");
451
452 Value *Compare = AI->getCompareOperand();
453 Value *NewValue = AI->getNewValOperand();
454
455 SmallVector<Value *, 6> Args{Handle};
456 append_range(Args, Coords);
457 Args.append(3 - Coords.size(), PoisonValue::get(Builder.getInt32Ty()));
458 Args.push_back(Compare);
459 Args.push_back(NewValue);
460
461 Value *Original = Builder.CreateIntrinsic(
462 NewValue->getType(), Intrinsic::dx_resource_atomic_compare_exchange,
463 Args);
464
465 // `cmpxchg` yields a { original, success } pair, but the DXIL op returns
466 // only the original value. DXIL has no way to express the success flag, and
467 // no HLSL builtin reads it, so replace the users of the pair directly
468 // instead of building it again. No pass after this one removes dead code.
470 for (User *U : AI->users()) {
471 auto *EV = dyn_cast<ExtractValueInst>(U);
472 if (!EV || EV->getIndices()[0] != 0)
473 reportFatalUsageError("DXIL cmpxchg provides only the original value");
474 Extracts.push_back(EV);
475 }
476
477 for (ExtractValueInst *EV : Extracts) {
478 EV->replaceAllUsesWith(Original);
479 EV->eraseFromParent();
480 }
481}
482
486 const DataLayout &DL = AI->getDataLayout();
487 IRBuilder<> Builder(AI);
488 auto [Index, Offset] =
489 getAtomicResourceCoords(II, AI->getPointerOperand(), RTI, Builder, DL);
490
491 emitAtomicCompareExchange(Builder, AI, II->getOperand(0), {Index, Offset});
492}
493
494// `cmpxchg` operands are always integers, so unlike atomicrmw there is no
495// float element type to convert here. The element type must be scalar.
499 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
500 if (!ContainedType->isIntegerTy() && !ContainedType->isFloatingPointTy()) {
501 reportFatalUsageError("DXIL cmpxchg requires a texture resource with a "
502 "scalar element type");
503 return;
504 }
505
506 IRBuilder<> Builder(AI);
507
508 emitAtomicCompareExchange(Builder, AI, II->getOperand(0),
509 getTextureAtomicCoords(II, Builder));
510}
511
514 switch (RTI.getResourceKind()) {
518 return createBufferAtomicBinOp(II, AI, RTI);
524 return createTextureAtomicBinOp(II, AI, RTI);
532 "DXIL atomicrmw not implemented for this texture resource kind");
533 return;
538 "DXIL atomicrmw not implemented for this resource type");
539 return;
543 llvm_unreachable("Invalid resource kind for atomicrmw");
544 }
545 llvm_unreachable("Unhandled case in switch");
546}
547
584
587 const DataLayout &DL = LI->getDataLayout();
588 IRBuilder<> Builder(LI);
589 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
590 Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
591
592 Value *V =
593 Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_typedbuffer,
594 {II->getOperand(0), II->getOperand(1)});
595 V = Builder.CreateExtractValue(V, {0});
596
597 Type *ScalarType = ContainedType->getScalarType();
598 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
599 Value *Offset =
600 traverseGEPOffsets(DL, Builder, LI->getPointerOperand(), AccessSize);
601 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
602 if (!ConstantOffset || !ConstantOffset->isZero())
603 V = Builder.CreateExtractElement(V, Offset);
604
605 // If we loaded a <1 x ...> instead of a scalar (presumably to feed a
606 // shufflevector), then make sure we're maintaining the resulting type.
607 if (auto *VT = dyn_cast<FixedVectorType>(LI->getType()))
608 if (VT->getNumElements() == 1 && !isa<FixedVectorType>(V->getType()))
609 V = Builder.CreateInsertElement(PoisonValue::get(VT), V,
610 Builder.getInt32(0));
611
612 LI->replaceAllUsesWith(V);
613}
614
617 const DataLayout &DL = LI->getDataLayout();
618 IRBuilder<> Builder(LI);
619 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
620
621 Value *Handle = II->getOperand(0);
622 Value *Coords = II->getOperand(1);
623
624 // For operator[], mip level is 0.
625 Value *MipLevel = Builder.getInt32(0);
626
627 // For operator[], offsets are zero.
628 Value *Offsets = getNullOffsetsFor(Builder, Coords);
629
630 Value *V =
631 Builder.CreateIntrinsic(ContainedType, Intrinsic::dx_resource_load_level,
632 {Handle, Coords, MipLevel, Offsets});
633
634 Type *ScalarType = ContainedType->getScalarType();
635 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
636 Value *Offset =
637 traverseGEPOffsets(DL, Builder, LI->getPointerOperand(), AccessSize);
638 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
639 if (!ConstantOffset || !ConstantOffset->isZero())
640 V = Builder.CreateExtractElement(V, Offset);
641
642 // If we loaded a <1 x ...> instead of a scalar (presumably to feed a
643 // shufflevector), then make sure we're maintaining the resulting type.
644 if (auto *VT = dyn_cast<FixedVectorType>(LI->getType()))
645 if (VT->getNumElements() == 1 && !isa<FixedVectorType>(V->getType()))
646 V = Builder.CreateInsertElement(PoisonValue::get(VT), V,
647 Builder.getInt32(0));
648
649 LI->replaceAllUsesWith(V);
650}
651
652static Value *emitRawLoad(IRBuilder<> &Builder, Type *Ty, Value *Buffer,
653 Value *Index, Value *Offset,
655 // For raw buffer (ie, HLSL's ByteAddressBuffer), we need to fold the access
656 // entirely into the index.
657 if (!RTI.isStruct()) {
658 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
659 if (!ConstantOffset || !ConstantOffset->isZero())
660 Index = Builder.CreateAdd(Index, Offset);
661 Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
662 }
663
664 // The load intrinsic includes the bit for CheckAccessFullyMapped, so we need
665 // to add that to the return type.
666 Type *TypeWithCheck = StructType::get(Ty, Builder.getInt1Ty());
667 Value *V = Builder.CreateIntrinsic(TypeWithCheck,
668 Intrinsic::dx_resource_load_rawbuffer,
669 {Buffer, Index, Offset});
670 return Builder.CreateExtractValue(V, {0});
671}
672
675 const DataLayout &DL = LI->getDataLayout();
676 IRBuilder<> Builder(LI);
677
678 Value *Index = II->getOperand(1);
679 // The offset for the rawbuffer load and store ops is always in bytes.
680 uint64_t AccessSize = 1;
681 Value *Offset =
682 traverseGEPOffsets(DL, Builder, LI->getPointerOperand(), AccessSize);
683
684 // TODO: We could make this handle aggregates by walking the structure and
685 // handling each field individually, but we don't ever generate code that
686 // would hit that so it seems superfluous.
687 assert(!LI->getType()->isAggregateType() &&
688 "Resource load should be scalar or vector type");
689
690 Value *V;
691 if (auto *VT = dyn_cast<FixedVectorType>(LI->getType())) {
692 // Split into loads of at most 4 elements.
693 Type *EltTy = VT->getElementType();
694 Value *Stride = ConstantInt::get(Builder.getInt32Ty(),
695 4 * (DL.getTypeSizeInBits(EltTy) / 8));
696
698 for (unsigned int I = 0, N = VT->getNumElements(); I < N; I += 4) {
699 Type *Ty = FixedVectorType::get(EltTy, N - I < 4 ? N - I : 4);
700 if (I > 0)
701 Offset = Builder.CreateAdd(Offset, Stride);
702 Parts.push_back(
703 emitRawLoad(Builder, Ty, II->getOperand(0), Index, Offset, RTI));
704 }
705
706 V = Parts.size() > 1 ? concatenateVectors(Builder, Parts) : Parts[0];
707 } else
708 V = emitRawLoad(Builder, LI->getType(), II->getOperand(0), Index, Offset,
709 RTI);
710
711 LI->replaceAllUsesWith(V);
712}
713
714namespace {
715/// Helper for building a `load.cbufferrow` intrinsic given a simple type.
716struct CBufferRowIntrin {
717 Intrinsic::ID IID;
718 Type *RetTy;
719 unsigned int EltSize;
720 unsigned int NumElts;
721
722 CBufferRowIntrin(const DataLayout &DL, Type *Ty) {
723 assert(Ty == Ty->getScalarType() && "Expected scalar type");
724
725 switch (DL.getTypeSizeInBits(Ty)) {
726 case 16:
727 IID = Intrinsic::dx_resource_load_cbufferrow_8;
728 RetTy = StructType::get(Ty, Ty, Ty, Ty, Ty, Ty, Ty, Ty);
729 EltSize = 2;
730 NumElts = 8;
731 break;
732 case 32:
733 IID = Intrinsic::dx_resource_load_cbufferrow_4;
734 RetTy = StructType::get(Ty, Ty, Ty, Ty);
735 EltSize = 4;
736 NumElts = 4;
737 break;
738 case 64:
739 IID = Intrinsic::dx_resource_load_cbufferrow_2;
740 RetTy = StructType::get(Ty, Ty);
741 EltSize = 8;
742 NumElts = 2;
743 break;
744 default:
745 llvm_unreachable("Only 16, 32, and 64 bit types supported");
746 }
747 }
748};
749} // namespace
750
753 const DataLayout &DL = LI->getDataLayout();
754
755 Type *Ty = LI->getType();
756 assert(!isa<StructType>(Ty) && "Structs not handled yet");
757 CBufferRowIntrin Intrin(DL, Ty->getScalarType());
758
759 StringRef Name = LI->getName();
760 Value *Handle = II->getOperand(0);
761
762 IRBuilder<> Builder(LI);
763
764 ConstantInt *GlobalOffset =
765 II->getIntrinsicID() == Intrinsic::dx_resource_getbasepointer
766 ? ConstantInt::get(Builder.getInt32Ty(), 0)
767 : dyn_cast<ConstantInt>(II->getOperand(1));
768 assert(GlobalOffset && "CBuffer getpointer index must be constant");
769
770 uint64_t GlobalOffsetVal = GlobalOffset->getZExtValue();
771 Value *CurrentRow = ConstantInt::get(
772 Builder.getInt32Ty(), GlobalOffsetVal / hlsl::CBufferRowSizeInBytes);
773 unsigned int CurrentIndex =
774 (GlobalOffsetVal % hlsl::CBufferRowSizeInBytes) / Intrin.EltSize;
775
776 // Every object in a cbuffer either fits in a row or is aligned to a row. This
777 // means that only the very last pointer access can point into a row.
778 auto *LastGEP = dyn_cast<GEPOperator>(LI->getPointerOperand());
779 if (!LastGEP) {
780 // If we don't have a GEP at all we're just accessing the resource through
781 // the result of getpointer directly.
782 assert(LI->getPointerOperand() == II &&
783 "Unexpected indirect access to resource without GEP");
784 } else {
785 Value *GEPOffset = traverseGEPOffsets(
786 DL, Builder, LastGEP->getPointerOperand(), hlsl::CBufferRowSizeInBytes);
787 CurrentRow = Builder.CreateAdd(GEPOffset, CurrentRow);
788
789 APInt ConstantOffset(DL.getIndexTypeSizeInBits(LastGEP->getType()), 0);
790 if (LastGEP->accumulateConstantOffset(DL, ConstantOffset)) {
791 APInt Remainder(DL.getIndexTypeSizeInBits(LastGEP->getType()),
793 APInt::udivrem(ConstantOffset, Remainder, ConstantOffset, Remainder);
794 CurrentRow = Builder.CreateAdd(
795 CurrentRow, ConstantInt::get(Builder.getInt32Ty(), ConstantOffset));
796 CurrentIndex += Remainder.udiv(Intrin.EltSize).getZExtValue();
797 } else {
798 assert(LastGEP->getNumIndices() == 1 &&
799 "Last GEP of cbuffer access is not array or struct access");
800 // We assume a non-constant access will be row-aligned. This is safe
801 // because arrays and structs are always row aligned, and accesses to
802 // vector elements will show up as a load of the vector followed by an
803 // extractelement.
804 CurrentRow = cast<ConstantInt>(CurrentRow)->isZero()
805 ? *LastGEP->idx_begin()
806 : Builder.CreateAdd(CurrentRow, *LastGEP->idx_begin());
807 CurrentIndex = 0;
808 }
809 }
810
811 auto *CBufLoad = Builder.CreateIntrinsic(
812 Intrin.RetTy, Intrin.IID, {Handle, CurrentRow}, nullptr, Name + ".load");
813 auto *Elt =
814 Builder.CreateExtractValue(CBufLoad, {CurrentIndex++}, Name + ".extract");
815
816 // At this point we've loaded the first scalar of our result, but our original
817 // type may have been a vector.
818 unsigned int Remaining =
819 ((DL.getTypeSizeInBits(Ty) / 8) / Intrin.EltSize) - 1;
820 if (Remaining == 0) {
821 // We only have a single element, so we're done.
822 Value *Result = Elt;
823
824 // However, if we loaded a <1 x T>, then we need to adjust the type.
825 if (auto *VT = dyn_cast<FixedVectorType>(Ty)) {
826 assert(VT->getNumElements() == 1 && "Can't have multiple elements here");
827 Result = Builder.CreateInsertElement(PoisonValue::get(VT), Result,
828 Builder.getInt32(0), Name);
829 }
830 LI->replaceAllUsesWith(Result);
831 return;
832 }
833
834 // Walk each element and extract it, wrapping to new rows as needed.
835 SmallVector<Value *> Extracts{Elt};
836 while (Remaining--) {
837 CurrentIndex %= Intrin.NumElts;
838
839 if (CurrentIndex == 0) {
840 CurrentRow = Builder.CreateAdd(CurrentRow,
841 ConstantInt::get(Builder.getInt32Ty(), 1));
842 CBufLoad = Builder.CreateIntrinsic(Intrin.RetTy, Intrin.IID,
843 {Handle, CurrentRow}, nullptr,
844 Name + ".load");
845 }
846
847 Extracts.push_back(Builder.CreateExtractValue(CBufLoad, {CurrentIndex++},
848 Name + ".extract"));
849 }
850
851 // Finally, we build up the original loaded value.
852 Value *Result = PoisonValue::get(Ty);
853 for (int I = 0, E = Extracts.size(); I < E; ++I)
854 Result = Builder.CreateInsertElement(
855 Result, Extracts[I], Builder.getInt32(I), Name + formatv(".upto{}", I));
856 LI->replaceAllUsesWith(Result);
857}
858
892
894 if (auto *LI = dyn_cast<LoadInst>(AI))
895 return dyn_cast<Instruction>(LI->getPointerOperand());
896 if (auto *SI = dyn_cast<StoreInst>(AI))
897 return dyn_cast<Instruction>(SI->getPointerOperand());
898 if (auto *RMWI = dyn_cast<AtomicRMWInst>(AI))
899 return dyn_cast<Instruction>(RMWI->getPointerOperand());
900 if (auto *CXI = dyn_cast<AtomicCmpXchgInst>(AI))
901 return dyn_cast<Instruction>(CXI->getPointerOperand());
902 if (auto *II = dyn_cast<IntrinsicInst>(AI))
903 if (II->getIntrinsicID() == Intrinsic::dx_resource_updatecounter)
904 return dyn_cast<Instruction>(II->getArgOperand(0));
905
906 return nullptr;
907}
908
909static const std::array<Intrinsic::ID, 2> HandleIntrins = {
910 Intrinsic::dx_resource_handlefrombinding,
911 Intrinsic::dx_resource_handlefromimplicitbinding,
912};
913
915 SmallVector<Value *> Worklist = {Ptr};
917 SmallSet<Value *, 4> VisitedPhis;
918
919 while (!Worklist.empty()) {
920 Value *X = Worklist.pop_back_val();
921
922 if (!X->getType()->isPointerTy() && !X->getType()->isTargetExtTy())
923 return {}; // Early exit on store/load into non-resource
924
925 if (auto *Phi = dyn_cast<PHINode>(X)) {
926 if (VisitedPhis.contains(X))
927 continue;
928 for (Use &V : Phi->incoming_values())
929 Worklist.push_back(V.get());
930 VisitedPhis.insert(Phi);
931 } else if (auto *Select = dyn_cast<SelectInst>(X))
932 for (Value *V : {Select->getTrueValue(), Select->getFalseValue()})
933 Worklist.push_back(V);
934 else if (auto *II = dyn_cast<IntrinsicInst>(X)) {
935 Intrinsic::ID IID = II->getIntrinsicID();
936
937 if (IID == Intrinsic::dx_resource_getpointer)
938 Worklist.push_back(II->getArgOperand(/*Handle=*/0));
939
941 Handles.push_back(II);
942 }
943 }
944
945 return Handles;
946}
947
949 DXILResourceTypeMap &DRTM) {
951 "Only expects a Handle as determined from collectUsedHandles.");
952
953 auto *HandleTy = cast<TargetExtType>(Handle->getType());
954 dxil::ResourceClass Class = DRTM[HandleTy].getResourceClass();
955 uint32_t Space = cast<ConstantInt>(Handle->getArgOperand(0))->getZExtValue();
956 uint32_t LowerBound =
957 cast<ConstantInt>(Handle->getArgOperand(1))->getZExtValue();
958 uint32_t Size = cast<ConstantInt>(Handle->getArgOperand(2))->getZExtValue();
959 uint32_t UpperBound = Size == UINT32_MAX ? UINT32_MAX : LowerBound + Size - 1;
960
961 return hlsl::Binding(Class, Space, LowerBound, UpperBound, nullptr);
962}
963
964namespace {
965/// Helper for propagating the current handle and ptr indices.
966struct AccessIndices {
967 Value *GetPtrIdx;
968 Value *HandleIdx;
969
970 bool hasGetPtrIdx() { return GetPtrIdx != nullptr; }
971 bool hasHandleIdx() { return HandleIdx != nullptr; }
972};
973} // namespace
974
975// getAccessIndices traverses up the control flow that a ptr came from and
976// propagates back the indicies used to access the resource (AccessIndices):
977//
978// - GetPtrIdx is the index of dx.resource.getpointer
979// - HandleIdx is the index of dx.resource.handlefrom.*
980static AccessIndices
983 if (auto *II = dyn_cast<IntrinsicInst>(I)) {
984 if (llvm::is_contained(HandleIntrins, II->getIntrinsicID())) {
985 DeadInsts.insert(II);
986 return {nullptr, II->getArgOperand(/*Index=*/3)};
987 }
988
989 if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
990 auto *V = dyn_cast<Instruction>(II->getArgOperand(/*Handle=*/0));
991 auto AccessIdx = getAccessIndices(V, DeadInsts, VisitedPhis);
992 assert(!AccessIdx.hasGetPtrIdx() &&
993 "Encountered multiple dx.resource.getpointers in ptr chain?");
994 AccessIdx.GetPtrIdx = II->getArgOperand(1);
995
996 DeadInsts.insert(II);
997 return AccessIdx;
998 }
999 }
1000
1001 if (auto *Phi = dyn_cast<PHINode>(I)) {
1002 // If we're already building indices for this phi, return a ref to the phi
1003 if (auto It = VisitedPhis.find(Phi); It != VisitedPhis.end())
1004 return {nullptr, It->second};
1005
1006 unsigned NumEdges = Phi->getNumIncomingValues();
1007 assert(NumEdges != 0 && "Malformed Phi Node");
1008
1009 IRBuilder<> Builder(Phi);
1010 std::unique_ptr<PHINode> GetPtrPhi(
1011 PHINode::Create(Builder.getInt32Ty(), NumEdges));
1012 std::unique_ptr<PHINode> HandlePhi(
1013 PHINode::Create(Builder.getInt32Ty(), NumEdges));
1014
1015 // Register a ref to this phi for a recursive phi. This is safe to add to
1016 // the map even if we end up deleting newly created phi below since we can't
1017 // possibly have a constant value if we recursed.
1018 if (Phi->getType()->isTargetExtTy())
1019 VisitedPhis[Phi] = HandlePhi.get();
1020
1021 for (unsigned Idx = 0; Idx < NumEdges; Idx++) {
1022 auto *BB = Phi->getIncomingBlock(Idx);
1023 auto *V = dyn_cast<Instruction>(Phi->getIncomingValue(Idx));
1024 auto AccessIdx = getAccessIndices(V, DeadInsts, VisitedPhis);
1025 if (AccessIdx.hasGetPtrIdx())
1026 GetPtrPhi->addIncoming(AccessIdx.GetPtrIdx, BB);
1027 HandlePhi->addIncoming(AccessIdx.HandleIdx, BB);
1028 }
1029
1030 Value *GetPtrIdx;
1031 if (GetPtrPhi->getNumIncomingValues() == 0)
1032 GetPtrIdx = nullptr;
1033 else if (Value *ConstantGetPtr = GetPtrPhi->hasConstantValue())
1034 GetPtrIdx = ConstantGetPtr;
1035 else {
1036 GetPtrIdx = GetPtrPhi.release();
1037 Builder.Insert(GetPtrIdx);
1038 }
1039
1040 Value *HandleIdx;
1041 if (Value *ConstantHandle = HandlePhi->hasConstantValue())
1042 HandleIdx = ConstantHandle;
1043 else {
1044 HandleIdx = HandlePhi.release();
1045 Builder.Insert(HandleIdx);
1046 }
1047
1048 DeadInsts.insert(Phi);
1049 return {GetPtrIdx, HandleIdx};
1050 }
1051
1052 if (auto *Select = dyn_cast<SelectInst>(I)) {
1053 auto *TrueV = dyn_cast<Instruction>(Select->getTrueValue());
1054 auto TrueAccessIdx = getAccessIndices(TrueV, DeadInsts, VisitedPhis);
1055
1056 auto *FalseV = dyn_cast<Instruction>(Select->getFalseValue());
1057 auto FalseAccessIdx = getAccessIndices(FalseV, DeadInsts, VisitedPhis);
1058
1059 IRBuilder<> Builder(Select);
1060 Value *GetPtrSelect = nullptr;
1061
1062 if (TrueAccessIdx.hasGetPtrIdx() && FalseAccessIdx.hasGetPtrIdx())
1063 GetPtrSelect =
1064 Builder.CreateSelect(Select->getCondition(), TrueAccessIdx.GetPtrIdx,
1065 FalseAccessIdx.GetPtrIdx);
1066
1067 auto *HandleSelect =
1068 Builder.CreateSelect(Select->getCondition(), TrueAccessIdx.HandleIdx,
1069 FalseAccessIdx.HandleIdx);
1070 DeadInsts.insert(Select);
1071 return {GetPtrSelect, HandleSelect};
1072 }
1073
1074 llvm_unreachable("collectUsedHandles should assure this does not occur");
1075}
1076
1077static void
1081 auto AccessIdx = getAccessIndices(Ptr, DeadInsts, VisitedPhis);
1082 assert(AccessIdx.hasHandleIdx() &&
1083 "Couldn't retrieve handle index. This is guaranteed by "
1084 "getAccessIndices");
1085
1086 IRBuilder<> Builder(Ptr);
1087 if (isa<PHINode>(Ptr))
1088 Builder.SetInsertPoint(Ptr->getParent()->getFirstNonPHIIt());
1089 IntrinsicInst *Handle = cast<IntrinsicInst>(OldHandle->clone());
1090 Handle->setArgOperand(/*Index=*/3, AccessIdx.HandleIdx);
1091 Builder.Insert(Handle);
1092
1093 if (Ptr->getType()->isPointerTy()) {
1094 assert(AccessIdx.hasGetPtrIdx() &&
1095 "Couldn't retrieve getpointer index. This is guaranteed by "
1096 "getAccessIndices");
1097 auto *GetPtr = Builder.CreateIntrinsic(Ptr->getType(),
1098 Intrinsic::dx_resource_getpointer,
1099 {Handle, AccessIdx.GetPtrIdx});
1100 Ptr->replaceAllUsesWith(GetPtr);
1101 } else {
1102 assert(Ptr->getType()->isTargetExtTy() && !AccessIdx.hasGetPtrIdx() &&
1103 "Unexpected resource access operand type");
1104 Ptr->replaceAllUsesWith(Handle);
1105 }
1106
1107 DeadInsts.insert(Ptr);
1108}
1109
1110// Try to legalize dx.resource.handlefrom.*.binding and dx.resource.getpointer
1111// calls with their respective index values and propagate the index values to
1112// be used at resource access.
1113//
1114// If it can't be transformed to be legal then:
1115//
1116// Reports an error if a resource access is not guaranteed into a unique global
1117// resource.
1118//
1119// Returns true if any changes are made.
1123
1124 for (BasicBlock &BB : make_early_inc_range(F)) {
1125 for (Instruction &I : BB) {
1126 if (auto *HandleOp = getHandleOperand(&I)) {
1128 unsigned NumHandles = Handles.size();
1129 if (NumHandles <= 1)
1130 continue; // Legal, no-replacement required
1131
1132 bool SameGlobalBinding = true;
1133 hlsl::Binding B = getHandleIntrinsicBinding(Handles[0], DRTM);
1134 for (unsigned Idx = 1; Idx < NumHandles; Idx++)
1135 SameGlobalBinding &=
1136 (B == getHandleIntrinsicBinding(Handles[Idx], DRTM));
1137
1138 if (!SameGlobalBinding)
1140
1141 replaceHandleWithIndices(HandleOp, Handles[0], DeadInsts, VisitedPhis);
1142 }
1143 }
1144 }
1145
1146 bool MadeChanges = false;
1147
1148 // Set up the phis to track if they are erased below
1149 SmallVector<WeakTrackingVH> ResourcePhis;
1150 for (const auto &HandleToIndex : VisitedPhis)
1151 ResourcePhis.push_back(HandleToIndex.first);
1152
1153 for (auto *I : llvm::reverse(DeadInsts))
1154 if (I->hasNUses(0)) { // Handle can still be used outside of replaced path
1155 I->eraseFromParent();
1156 MadeChanges = true;
1157 }
1158
1159 // Any remaining phi nodes are now looped with another phi node and have no
1160 // other uses
1161 for (WeakTrackingVH &VH : ResourcePhis)
1162 if (VH) // True if not removed above or already in this loop
1163 MadeChanges |= RecursivelyDeleteDeadPHINode(cast<PHINode>(VH));
1164
1165 return MadeChanges;
1166}
1167
1169 SmallVector<User *> Worklist;
1170 for (User *U : II->users())
1171 Worklist.push_back(U);
1172
1174 while (!Worklist.empty()) {
1175 User *U = Worklist.back();
1176 Worklist.pop_back();
1177
1178 if (auto *GEP = dyn_cast<GetElementPtrInst>(U)) {
1179 for (User *U : GEP->users())
1180 Worklist.push_back(U);
1181 DeadInsts.push_back(GEP);
1182
1183 } else if (auto *SI = dyn_cast<StoreInst>(U)) {
1184 assert(SI->getValueOperand() != II && "Pointer escaped!");
1185 createStoreIntrinsic(II, SI, RTI);
1186 DeadInsts.push_back(SI);
1187
1188 } else if (auto *LI = dyn_cast<LoadInst>(U)) {
1189 createLoadIntrinsic(II, LI, RTI);
1190 DeadInsts.push_back(LI);
1191 } else if (auto *AI = dyn_cast<AtomicRMWInst>(U)) {
1193 DeadInsts.push_back(AI);
1194 } else if (auto *CXI = dyn_cast<AtomicCmpXchgInst>(U)) {
1196 DeadInsts.push_back(CXI);
1197 } else
1198 llvm_unreachable("Unhandled instruction - pointer escaped?");
1199 }
1200
1201 // Traverse the now-dead instructions in RPO and remove them.
1202 for (Instruction *Dead : llvm::reverse(DeadInsts))
1203 Dead->eraseFromParent();
1204 II->eraseFromParent();
1205}
1206
1209 for (BasicBlock &BB : make_early_inc_range(F))
1210 for (Instruction &I : BB)
1211 if (auto *II = dyn_cast<IntrinsicInst>(&I))
1212 if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer ||
1213 II->getIntrinsicID() == Intrinsic::dx_resource_getbasepointer) {
1214 auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());
1215 assert(
1216 (DRTM[HandleTy].isCBuffer() ||
1217 II->getIntrinsicID() != Intrinsic::dx_resource_getbasepointer) &&
1218 "dx_resource_getbasepointer should only be used by cbuffers");
1219 Resources.emplace_back(II, DRTM[HandleTy]);
1220 }
1221
1222 for (auto &[II, RI] : Resources)
1223 replaceAccess(II, RI);
1224
1225 return !Resources.empty();
1226}
1227
1230 auto &MAMProxy = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
1231 DXILResourceTypeMap *DRTM =
1232 MAMProxy.getCachedResult<DXILResourceTypeAnalysis>(*F.getParent());
1233 assert(DRTM && "DXILResourceTypeAnalysis must be available");
1234
1235 bool MadeHandleChanges = legalizeResourceHandles(F, *DRTM);
1236 bool MadeResourceChanges = transformResourcePointers(F, *DRTM);
1237 if (!(MadeHandleChanges || MadeResourceChanges))
1238 return PreservedAnalyses::all();
1239
1243 return PA;
1244}
1245
1246namespace {
1247class DXILResourceAccessLegacy : public FunctionPass {
1248public:
1249 bool runOnFunction(Function &F) override {
1250 DXILResourceTypeMap &DRTM =
1251 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
1252 bool MadeHandleChanges = legalizeResourceHandles(F, DRTM);
1253 bool MadeResourceChanges = transformResourcePointers(F, DRTM);
1254 return MadeHandleChanges || MadeResourceChanges;
1255 }
1256 StringRef getPassName() const override { return "DXIL Resource Access"; }
1257 DXILResourceAccessLegacy() : FunctionPass(ID) {}
1258
1259 static char ID; // Pass identification.
1260 void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
1261 AU.addRequired<DXILResourceTypeWrapperPass>();
1262 AU.addPreserved<DominatorTreeWrapperPass>();
1263 }
1264};
1265char DXILResourceAccessLegacy::ID = 0;
1266} // end anonymous namespace
1267
1268INITIALIZE_PASS_BEGIN(DXILResourceAccessLegacy, DEBUG_TYPE,
1269 "DXIL Resource Access", false, false)
1271INITIALIZE_PASS_END(DXILResourceAccessLegacy, DEBUG_TYPE,
1272 "DXIL Resource Access", false, false)
1273
1275 return new DXILResourceAccessLegacy();
1276}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
AMDGPU Register Bank Select
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
#define X(NUM, ENUM, NAME)
Definition ELF.h:857
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 AccessIndices getAccessIndices(Instruction *I, SmallSetVector< Instruction *, 16 > &DeadInsts, SmallDenseMap< PHINode *, PHINode * > &VisitedPhis)
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 void emitAtomicCompareExchange(IRBuilder<> &Builder, AtomicCmpXchgInst *AI, Value *Handle, ArrayRef< Value * > Coords)
static bool legalizeResourceHandles(Function &F, DXILResourceTypeMap &DRTM)
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 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 void createBufferAtomicCompareExchange(IntrinsicInst *II, AtomicCmpXchgInst *AI, dxil::ResourceTypeInfo &RTI)
static void replaceHandleWithIndices(Instruction *Ptr, IntrinsicInst *OldHandle, SmallSetVector< Instruction *, 16 > &DeadInsts, SmallDenseMap< PHINode *, PHINode * > &VisitedPhis)
static Value * traverseGEPOffsets(const DataLayout &DL, IRBuilder<> &Builder, Value *Ptr, uint64_t AccessSize)
static void createBufferAtomicBinOp(IntrinsicInst *II, AtomicRMWInst *AI, dxil::ResourceTypeInfo &RTI)
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 std::pair< Value *, Value * > getAtomicResourceCoords(IntrinsicInst *II, Value *PointerOperand, dxil::ResourceTypeInfo &RTI, IRBuilder<> &Builder, const DataLayout &DL)
static void createRawStores(IntrinsicInst *II, StoreInst *SI, dxil::ResourceTypeInfo &RTI)
static SmallVector< Value *, 3 > getTextureAtomicCoords(IntrinsicInst *II, IRBuilder<> &Builder)
static void createTextureAtomicBinOp(IntrinsicInst *II, AtomicRMWInst *AI, dxil::ResourceTypeInfo &RTI)
static void emitAtomicBinOp(IRBuilder<> &Builder, AtomicRMWInst *AI, Value *Handle, ArrayRef< Value * > Coords)
static void createRawLoads(IntrinsicInst *II, LoadInst *LI, dxil::ResourceTypeInfo &RTI)
static void createAtomicCompareExchangeIntrinsic(IntrinsicInst *II, AtomicCmpXchgInst *AI, dxil::ResourceTypeInfo &RTI)
static void createAtomicBinOpIntrinsic(IntrinsicInst *II, AtomicRMWInst *AI, dxil::ResourceTypeInfo &RTI)
static Instruction * getHandleOperand(Instruction *AI)
static void createTextureAtomicCompareExchange(IntrinsicInst *II, AtomicCmpXchgInst *AI, dxil::ResourceTypeInfo &RTI)
static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI)
This file defines the DenseMap class.
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.
This file defines the SmallSet class.
Class for arbitrary precision integers.
Definition APInt.h:78
LLVM_ABI APInt udiv(const APInt &RHS) const
Unsigned division operation.
Definition APInt.cpp:1602
static LLVM_ABI void udivrem(const APInt &LHS, const APInt &RHS, APInt &Quotient, APInt &Remainder)
Dual division/remainder interface.
Definition APInt.cpp:1796
uint64_t getZExtValue() const
Get zero extended value.
Definition APInt.h:1560
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
size_t size() const
Get the array size.
Definition ArrayRef.h:141
bool empty() const
Check if the array is empty.
Definition ArrayRef.h:136
An instruction that atomically checks whether a specified value is in a memory location,...
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
iterator find(const_arg_type_t< KeyT > Val)
Definition DenseMap.h:782
iterator end()
Definition DenseMap.h:702
Analysis pass which computes a DominatorTree.
Definition Dominators.h:241
This instruction extracts a struct member or array element value from an aggregate value.
static LLVM_ABI FixedVectorType * get(Type *ElementType, unsigned NumElts)
Definition Type.cpp:843
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2901
LLVM_ABI Instruction * clone() const
Create a copy of 'this' instruction that is identical in all ways except the following:
iterator_range< user_iterator > users()
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()
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
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition SmallSet.h:134
bool contains(const T &V) const
Check if the SmallSet contains the given element.
Definition SmallSet.h:229
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
Definition SmallSet.h:184
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:467
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:299
bool isPointerTy() const
True if this is an instance of PointerType.
Definition Type.h:277
Type * getScalarType() const
If this is a vector type, return the element type, otherwise return 'this'.
Definition Type.h:363
LLVM_ABI TypeSize getPrimitiveSizeInBits() const LLVM_READONLY
Return the basic size of this type if it is a primitive type.
Definition Type.cpp:187
bool isTargetExtTy() const
Return true if this is a target extension type.
Definition Type.h:205
bool isAggregateType() const
Return true if the type is an aggregate type.
Definition Type.h:314
static LLVM_ABI IntegerType * getInt1Ty(LLVMContext &C)
Definition Type.cpp:296
bool isFloatingPointTy() const
Return true if this is one of the floating-point types.
Definition Type.h:186
bool isIntegerTy() const
True if this is an instance of IntegerType.
Definition Type.h:252
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:257
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
Value handle that is nullable, but tries to track the Value.
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:577
@ 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.
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
Definition STLExtras.h:2224
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:649
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:408
FunctionPass * createDXILResourceAccessLegacyPass()
Pass to update resource accesses to use load/store directly.
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
LLVM_ABI bool RecursivelyDeleteDeadPHINode(PHINode *PN, const TargetLibraryInfo *TLI=nullptr, MemorySSAUpdater *MSSAU=nullptr, SmallPtrSetImpl< PHINode * > *KnownNonDeadPHIs=nullptr)
If the specified value is an effectively dead PHI node, due to being a def-use chain of single-use no...
Definition Local.cpp:622
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:1963
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