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
9#include "DirectX.h"
10#include "llvm/ADT/DenseMap.h"
11#include "llvm/ADT/MapVector.h"
12#include "llvm/ADT/SetVector.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"
27#include "llvm/IR/User.h"
28#include "llvm/IR/ValueHandle.h"
37#include <optional>
38
39#define DEBUG_TYPE "dxil-resource-access"
40
41using namespace llvm;
42
43[[noreturn]] static void
46 LLVMContext &Context = I->getContext();
47 std::string InstStr;
48 raw_string_ostream InstOS(InstStr);
49 I->print(InstOS);
50 Context.diagnose(
51 DiagnosticInfoGeneric("At resource access:" + Twine(InstStr), DS_Note));
52
53 for (auto *Handle : Handles) {
54 std::string HandleStr;
55 raw_string_ostream HandleOS(HandleStr);
56 Handle->print(HandleOS);
57 Context.diagnose(DiagnosticInfoGeneric(
58 "Uses resource handle:" + Twine(HandleStr), DS_Note));
59 }
61 "Resource access is not guaranteed to map to a unique global resource",
62 /*gen_crash_diag=*/false);
63}
64
66 Value *Ptr, uint64_t AccessSize) {
67 Value *Offset = nullptr;
68
69 while (Ptr) {
70 if ([[maybe_unused]] auto *II = dyn_cast<IntrinsicInst>(Ptr)) {
71 assert((II->getIntrinsicID() == Intrinsic::dx_resource_getpointer ||
72 II->getIntrinsicID() == Intrinsic::dx_resource_getbasepointer) &&
73 "Resource access through unexpected intrinsic");
74 return Offset ? Offset : ConstantInt::get(Builder.getInt32Ty(), 0);
75 }
76
78 assert(GEP && "Resource access through unexpected instruction");
79
80 unsigned NumIndices = GEP->getNumIndices();
81 uint64_t IndexScale = DL.getTypeAllocSize(GEP->getSourceElementType());
82 APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
83 Value *GEPOffset;
84 if (GEP->accumulateConstantOffset(DL, ConstantOffset)) {
85 // We have a constant offset (in bytes).
86 GEPOffset =
87 ConstantInt::get(DL.getIndexType(GEP->getType()), ConstantOffset);
88 IndexScale = 1;
89 } else if (NumIndices == 1) {
90 // If we have a single index we're indexing into a top level array. This
91 // generally only happens with cbuffers.
92 GEPOffset = *GEP->idx_begin();
93 } else if (NumIndices == 2) {
94 // If we have two indices, this should be an access through a pointer.
95 auto *IndexIt = GEP->idx_begin();
96 assert(cast<ConstantInt>(IndexIt)->getZExtValue() == 0 &&
97 "GEP is not indexing through pointer");
98 GEPOffset = *(++IndexIt);
99 } else
100 llvm_unreachable("Unhandled GEP structure for resource access");
101
102 uint64_t ElemSize = AccessSize;
103 if (!(IndexScale % ElemSize)) {
104 // If our scale is an exact multiple of the access size, adjust the
105 // scaling to avoid an unnecessary division.
106 IndexScale /= ElemSize;
107 ElemSize = 1;
108 }
109 if (IndexScale != 1)
110 GEPOffset = Builder.CreateMul(
111 GEPOffset, ConstantInt::get(Builder.getInt32Ty(), IndexScale));
112 if (ElemSize != 1)
113 GEPOffset = Builder.CreateUDiv(
114 GEPOffset, ConstantInt::get(Builder.getInt32Ty(), ElemSize));
115
116 Offset = Offset ? Builder.CreateAdd(Offset, GEPOffset) : GEPOffset;
117 Ptr = GEP->getPointerOperand();
118 }
119
120 llvm_unreachable("GEP of null pointer?");
121}
122
125 const DataLayout &DL = SI->getDataLayout();
126 IRBuilder<> Builder(SI);
127 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
128 Type *ScalarType = ContainedType->getScalarType();
129 Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
130
131 Value *V = SI->getValueOperand();
132 if (V->getType() == ContainedType) {
133 // V is already the right type.
134 assert(SI->getPointerOperand() == II &&
135 "Store of whole element has mismatched address to store to");
136 } else if (V->getType() == ScalarType) {
137 // We're storing a scalar, so we need to load the current value and only
138 // replace the relevant part.
139 auto *Load = Builder.CreateIntrinsic(
140 LoadType, Intrinsic::dx_resource_load_typedbuffer,
141 {II->getOperand(0), II->getOperand(1)});
142 auto *Struct = Builder.CreateExtractValue(Load, {0});
143
144 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
145 Value *Offset =
146 traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
147 V = Builder.CreateInsertElement(Struct, V, Offset);
148 } else {
149 llvm_unreachable("Store to typed resource has invalid type");
150 }
151
152 auto *Inst = Builder.CreateIntrinsic(
153 Builder.getVoidTy(), Intrinsic::dx_resource_store_typedbuffer,
154 {II->getOperand(0), II->getOperand(1), V});
155 SI->replaceAllUsesWith(Inst);
156}
157
158/// Build a zero-initialized offset operand matching the shape of the given
159/// coordinate operand. Accesses through `operator[]` never have offsets.
160static Value *getNullOffsetsFor(IRBuilder<> &Builder, Value *Coords) {
161 Type *CoordTy = Coords->getType();
162 Type *OffsetTy;
163 if (auto *VecTy = dyn_cast<FixedVectorType>(CoordTy))
164 OffsetTy =
165 FixedVectorType::get(Builder.getInt32Ty(), VecTy->getNumElements());
166 else
167 OffsetTy = Builder.getInt32Ty();
168 return Constant::getNullValue(OffsetTy);
169}
170
173 const DataLayout &DL = SI->getDataLayout();
174 IRBuilder<> Builder(SI);
175 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
176 Type *ScalarType = ContainedType->getScalarType();
177
178 Value *Handle = II->getOperand(0);
179 Value *Coords = II->getOperand(1);
180
181 Value *V = SI->getValueOperand();
182 if (V->getType() == ContainedType) {
183 // V is already the right type.
184 assert(SI->getPointerOperand() == II &&
185 "Store of whole element has mismatched address to store to");
186 } else if (V->getType() == ScalarType) {
187 // We're storing a scalar, so we need to load the current value and only
188 // replace the relevant part. For operator[] the mip level and the offsets
189 // are always zero; DXILOpLowering drops the mip level for UAVs.
190 Value *MipLevel = Builder.getInt32(0);
191 Value *Offsets = getNullOffsetsFor(Builder, Coords);
192 auto *Load = Builder.CreateIntrinsic(ContainedType,
193 Intrinsic::dx_resource_load_level,
194 {Handle, Coords, MipLevel, Offsets});
195
196 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
197 Value *Offset =
198 traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
199 V = Builder.CreateInsertElement(Load, V, Offset);
200 } else {
201 llvm_unreachable("Store to texture resource has invalid type");
202 }
203
204 auto *Inst = Builder.CreateIntrinsic(Builder.getVoidTy(),
205 Intrinsic::dx_resource_store_texture,
206 {Handle, Coords, V});
207 SI->replaceAllUsesWith(Inst);
208}
209
210static void emitRawStore(IRBuilder<> &Builder, Value *Buffer, Value *Index,
212 // For raw buffer (ie, HLSL's ByteAddressBuffer), we need to fold the access
213 // entirely into the index.
214 if (!RTI.isStruct()) {
215 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
216 if (!ConstantOffset || !ConstantOffset->isZero())
217 Index = Builder.CreateAdd(Index, Offset);
218 Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
219 }
220
221 Builder.CreateIntrinsic(Builder.getVoidTy(),
222 Intrinsic::dx_resource_store_rawbuffer,
223 {Buffer, Index, Offset, V});
224}
225
228 const DataLayout &DL = SI->getDataLayout();
229 IRBuilder<> Builder(SI);
230
231 Value *V = SI->getValueOperand();
232 assert(!V->getType()->isAggregateType() &&
233 "Resource store should be scalar or vector type");
234
235 Value *Index = II->getOperand(1);
236 // The offset for the rawbuffer load and store ops is always in bytes.
237 uint64_t AccessSize = 1;
238 Value *Offset =
239 traverseGEPOffsets(DL, Builder, SI->getPointerOperand(), AccessSize);
240
241 auto *VT = dyn_cast<FixedVectorType>(V->getType());
242 if (VT && VT->getNumElements() > 4) {
243 // Split into stores of at most 4 elements.
244 Type *EltTy = VT->getElementType();
245 Value *Stride = ConstantInt::get(Builder.getInt32Ty(),
246 4 * (DL.getTypeSizeInBits(EltTy) / 8));
247
248 SmallVector<int, 4> Indices;
249 for (unsigned int I = 0, N = VT->getNumElements(); I < N; I += 4) {
250 if (I > 0)
251 Offset = Builder.CreateAdd(Offset, Stride);
252
253 for (unsigned int J = I, E = std::min(N, J + 4); J < E; ++J)
254 Indices.push_back(J);
255 Value *Part = Builder.CreateShuffleVector(V, Indices);
256 emitRawStore(Builder, II->getOperand(0), Index, Offset, Part, RTI);
257
258 Indices.clear();
259 }
260 } else
261 emitRawStore(Builder, II->getOperand(0), Index, Offset, V, RTI);
262}
263
297
298static std::optional<dxil::AtomicBinOpCode>
338
339// Compute the (coord0, coord1) pair for a buffer resource atomic operation.
340// Non-struct buffers (RawBuffer or TypedBuffer) fold the byte offset into the
341// index and leave coord1 poison. Only StructuredBuffer atomics use both a
342// struct index and a byte offset.
343static std::pair<Value *, Value *>
345 dxil::ResourceTypeInfo &RTI, IRBuilder<> &Builder,
346 const DataLayout &DL) {
347 Value *Index = II->getOperand(1);
348
349 // The offset for the rawbuffer load/store/atomic ops is always in bytes.
350 uint64_t AccessSize = 1;
351 Value *Offset = traverseGEPOffsets(DL, Builder, PointerOperand, AccessSize);
352
353 if (!RTI.isStruct()) {
354 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
355 if (!ConstantOffset || !ConstantOffset->isZero())
356 Index = Builder.CreateAdd(Index, Offset);
357 Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
358 }
359
360 return {Index, Offset};
361}
362
363// The coordinates of a texture access are a scalar or a vector with one element
364// per texture dimension, including the array slice if there is one. These map
365// directly onto the coordinate operands of the atomic ops.
367 IRBuilder<> &Builder) {
368 Value *Coords = II->getOperand(1);
369 SmallVector<Value *, 3> CoordArgs;
370 if (auto *VecTy = dyn_cast<FixedVectorType>(Coords->getType())) {
371 assert(VecTy->getNumElements() <= 3 && "Too many texture coordinates");
372 for (unsigned I = 0, E = VecTy->getNumElements(); I != E; ++I)
373 CoordArgs.push_back(Builder.CreateExtractElement(Coords, I));
374 } else {
375 CoordArgs.push_back(Coords);
376 }
377 return CoordArgs;
378}
379
380static void emitAtomicBinOp(IRBuilder<> &Builder, AtomicRMWInst *AI,
381 Value *Handle, ArrayRef<Value *> Coords) {
382 assert(!Coords.empty() && Coords.size() <= 3 &&
383 "Atomic operations take between one and three coordinates");
384
385 std::optional<dxil::AtomicBinOpCode> BinOpCode =
387 if (!BinOpCode) {
388 reportFatalUsageError("DXIL resource atomicrmw operation not implemented");
389 return;
390 }
391
392 // DXIL has no floating-point atomic op. A float exchange only moves the bit
393 // pattern, so cast the value to an integer of the same width, exchange, and
394 // cast the result back. This matches what DXC emits.
395 Value *Val = AI->getValOperand();
396 Type *ValTy = Val->getType();
397 Type *OpTy = ValTy;
398 if (ValTy->isFloatingPointTy()) {
399 OpTy = Builder.getIntNTy(ValTy->getPrimitiveSizeInBits());
400 Val = Builder.CreateBitCast(Val, OpTy);
401 }
402
404 Handle, Builder.getInt32(static_cast<uint32_t>(*BinOpCode))};
405 append_range(Args, Coords);
406 Args.append(3 - Coords.size(), PoisonValue::get(Builder.getInt32Ty()));
407 Args.push_back(Val);
408
409 // Emit the target-independent intrinsic; DXILOpLowering lowers it to the
410 // DXIL `AtomicBinOp` op and handles the target-ext-typed handle cast via
411 // its `createTmpHandleCast` bookkeeping.
412 Value *Result =
413 Builder.CreateIntrinsic(OpTy, Intrinsic::dx_resource_atomic_binop, Args);
414
415 if (OpTy != ValTy)
416 Result = Builder.CreateBitCast(Result, ValTy);
417
418 AI->replaceAllUsesWith(Result);
419}
420
423 const DataLayout &DL = AI->getDataLayout();
424 IRBuilder<> Builder(AI);
425 auto [Index, Offset] =
426 getAtomicResourceCoords(II, AI->getPointerOperand(), RTI, Builder, DL);
427
428 emitAtomicBinOp(Builder, AI, II->getOperand(0), {Index, Offset});
429}
430
433 // A texture atomic operates on a whole texel, so a multi-component texel has
434 // no single addressable component. A scalar float texel is allowed, because
435 // emitAtomicBinOp exchanges its bit pattern as an integer.
436 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
437 if (!ContainedType->isIntegerTy() && !ContainedType->isFloatingPointTy()) {
438 reportFatalUsageError("DXIL atomicrmw requires a texture resource with a "
439 "scalar element type");
440 return;
441 }
442
443 IRBuilder<> Builder(AI);
444
445 emitAtomicBinOp(Builder, AI, II->getOperand(0),
446 getTextureAtomicCoords(II, Builder));
447}
448
450 AtomicCmpXchgInst *AI, Value *Handle,
451 ArrayRef<Value *> Coords) {
452 assert(!Coords.empty() && Coords.size() <= 3 &&
453 "Atomic operations take between one and three coordinates");
454
455 Value *Compare = AI->getCompareOperand();
456 Value *NewValue = AI->getNewValOperand();
457
458 SmallVector<Value *, 6> Args{Handle};
459 append_range(Args, Coords);
460 Args.append(3 - Coords.size(), PoisonValue::get(Builder.getInt32Ty()));
461 Args.push_back(Compare);
462 Args.push_back(NewValue);
463
464 Value *Original = Builder.CreateIntrinsic(
465 NewValue->getType(), Intrinsic::dx_resource_atomic_compare_exchange,
466 Args);
467
468 // `cmpxchg` yields a { original, success } pair, but the DXIL op returns
469 // only the original value. DXIL has no way to express the success flag, and
470 // no HLSL builtin reads it, so replace the users of the pair directly
471 // instead of building it again. No pass after this one removes dead code.
473 for (User *U : AI->users()) {
474 auto *EV = dyn_cast<ExtractValueInst>(U);
475 if (!EV || EV->getIndices()[0] != 0)
476 reportFatalUsageError("DXIL cmpxchg provides only the original value");
477 Extracts.push_back(EV);
478 }
479
480 for (ExtractValueInst *EV : Extracts) {
481 EV->replaceAllUsesWith(Original);
482 EV->eraseFromParent();
483 }
484}
485
489 const DataLayout &DL = AI->getDataLayout();
490 IRBuilder<> Builder(AI);
491 auto [Index, Offset] =
492 getAtomicResourceCoords(II, AI->getPointerOperand(), RTI, Builder, DL);
493
494 emitAtomicCompareExchange(Builder, AI, II->getOperand(0), {Index, Offset});
495}
496
497// `cmpxchg` operands are always integers, so unlike atomicrmw there is no
498// float element type to convert here. The element type must be scalar.
502 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
503 if (!ContainedType->isIntegerTy() && !ContainedType->isFloatingPointTy()) {
504 reportFatalUsageError("DXIL cmpxchg requires a texture resource with a "
505 "scalar element type");
506 return;
507 }
508
509 IRBuilder<> Builder(AI);
510
511 emitAtomicCompareExchange(Builder, AI, II->getOperand(0),
512 getTextureAtomicCoords(II, Builder));
513}
514
517 switch (RTI.getResourceKind()) {
521 return createBufferAtomicBinOp(II, AI, RTI);
527 return createTextureAtomicBinOp(II, AI, RTI);
535 "DXIL atomicrmw not implemented for this texture resource kind");
536 return;
541 "DXIL atomicrmw not implemented for this resource type");
542 return;
546 llvm_unreachable("Invalid resource kind for atomicrmw");
547 }
548 llvm_unreachable("Unhandled case in switch");
549}
550
587
590 const DataLayout &DL = LI->getDataLayout();
591 IRBuilder<> Builder(LI);
592 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
593 Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
594
595 Value *V =
596 Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_typedbuffer,
597 {II->getOperand(0), II->getOperand(1)});
598 V = Builder.CreateExtractValue(V, {0});
599
600 Type *ScalarType = ContainedType->getScalarType();
601 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
602 Value *Offset =
603 traverseGEPOffsets(DL, Builder, LI->getPointerOperand(), AccessSize);
604 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
605 if (!ConstantOffset || !ConstantOffset->isZero())
606 V = Builder.CreateExtractElement(V, Offset);
607
608 // If we loaded a <1 x ...> instead of a scalar (presumably to feed a
609 // shufflevector), then make sure we're maintaining the resulting type.
610 if (auto *VT = dyn_cast<FixedVectorType>(LI->getType()))
611 if (VT->getNumElements() == 1 && !isa<FixedVectorType>(V->getType()))
612 V = Builder.CreateInsertElement(PoisonValue::get(VT), V,
613 Builder.getInt32(0));
614
616}
617
620 const DataLayout &DL = LI->getDataLayout();
621 IRBuilder<> Builder(LI);
622 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
623
624 Value *Handle = II->getOperand(0);
625 Value *Coords = II->getOperand(1);
626
627 // For operator[], mip level is 0.
628 Value *MipLevel = Builder.getInt32(0);
629
630 // For operator[], offsets are zero.
631 Value *Offsets = getNullOffsetsFor(Builder, Coords);
632
633 Value *V =
634 Builder.CreateIntrinsic(ContainedType, Intrinsic::dx_resource_load_level,
635 {Handle, Coords, MipLevel, Offsets});
636
637 Type *ScalarType = ContainedType->getScalarType();
638 uint64_t AccessSize = DL.getTypeSizeInBits(ScalarType) / 8;
639 Value *Offset =
640 traverseGEPOffsets(DL, Builder, LI->getPointerOperand(), AccessSize);
641 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
642 if (!ConstantOffset || !ConstantOffset->isZero())
643 V = Builder.CreateExtractElement(V, Offset);
644
645 // If we loaded a <1 x ...> instead of a scalar (presumably to feed a
646 // shufflevector), then make sure we're maintaining the resulting type.
647 if (auto *VT = dyn_cast<FixedVectorType>(LI->getType()))
648 if (VT->getNumElements() == 1 && !isa<FixedVectorType>(V->getType()))
649 V = Builder.CreateInsertElement(PoisonValue::get(VT), V,
650 Builder.getInt32(0));
651
653}
654
655static Value *emitRawLoad(IRBuilder<> &Builder, Type *Ty, Value *Buffer,
656 Value *Index, Value *Offset,
658 // For raw buffer (ie, HLSL's ByteAddressBuffer), we need to fold the access
659 // entirely into the index.
660 if (!RTI.isStruct()) {
661 auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
662 if (!ConstantOffset || !ConstantOffset->isZero())
663 Index = Builder.CreateAdd(Index, Offset);
664 Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
665 }
666
667 // The load intrinsic includes the bit for CheckAccessFullyMapped, so we need
668 // to add that to the return type.
669 Type *TypeWithCheck = StructType::get(Ty, Builder.getInt1Ty());
670 Value *V = Builder.CreateIntrinsic(TypeWithCheck,
671 Intrinsic::dx_resource_load_rawbuffer,
672 {Buffer, Index, Offset});
673 return Builder.CreateExtractValue(V, {0});
674}
675
678 const DataLayout &DL = LI->getDataLayout();
679 IRBuilder<> Builder(LI);
680
681 Value *Index = II->getOperand(1);
682 // The offset for the rawbuffer load and store ops is always in bytes.
683 uint64_t AccessSize = 1;
684 Value *Offset =
685 traverseGEPOffsets(DL, Builder, LI->getPointerOperand(), AccessSize);
686
687 // TODO: We could make this handle aggregates by walking the structure and
688 // handling each field individually, but we don't ever generate code that
689 // would hit that so it seems superfluous.
690 assert(!LI->getType()->isAggregateType() &&
691 "Resource load should be scalar or vector type");
692
693 Value *V;
694 if (auto *VT = dyn_cast<FixedVectorType>(LI->getType())) {
695 // Split into loads of at most 4 elements.
696 Type *EltTy = VT->getElementType();
697 Value *Stride = ConstantInt::get(Builder.getInt32Ty(),
698 4 * (DL.getTypeSizeInBits(EltTy) / 8));
699
701 for (unsigned int I = 0, N = VT->getNumElements(); I < N; I += 4) {
702 Type *Ty = FixedVectorType::get(EltTy, N - I < 4 ? N - I : 4);
703 if (I > 0)
704 Offset = Builder.CreateAdd(Offset, Stride);
705 Parts.push_back(
706 emitRawLoad(Builder, Ty, II->getOperand(0), Index, Offset, RTI));
707 }
708
709 V = Parts.size() > 1 ? concatenateVectors(Builder, Parts) : Parts[0];
710 } else
711 V = emitRawLoad(Builder, LI->getType(), II->getOperand(0), Index, Offset,
712 RTI);
713
715}
716
717namespace {
718/// Helper for building a `load.cbufferrow` intrinsic given a simple type.
719struct CBufferRowIntrin {
720 Intrinsic::ID IID;
721 Type *RetTy;
722 unsigned int EltSize;
723 unsigned int NumElts;
724
725 CBufferRowIntrin(const DataLayout &DL, Type *Ty) {
726 assert(Ty == Ty->getScalarType() && "Expected scalar type");
727
728 switch (DL.getTypeSizeInBits(Ty)) {
729 case 16:
730 IID = Intrinsic::dx_resource_load_cbufferrow_8;
731 RetTy = StructType::get(Ty, Ty, Ty, Ty, Ty, Ty, Ty, Ty);
732 EltSize = 2;
733 NumElts = 8;
734 break;
735 case 32:
736 IID = Intrinsic::dx_resource_load_cbufferrow_4;
737 RetTy = StructType::get(Ty, Ty, Ty, Ty);
738 EltSize = 4;
739 NumElts = 4;
740 break;
741 case 64:
742 IID = Intrinsic::dx_resource_load_cbufferrow_2;
743 RetTy = StructType::get(Ty, Ty);
744 EltSize = 8;
745 NumElts = 2;
746 break;
747 default:
748 llvm_unreachable("Only 16, 32, and 64 bit types supported");
749 }
750 }
751};
752} // namespace
753
755 const CBufferRowIntrin &Intrin,
756 Value *Handle, Value *FirstRow,
757 Value *Window, unsigned RowNumber,
758 StringRef Name) {
759 Value *RowIndex =
760 RowNumber == 0 ? FirstRow
761 : Builder.CreateAdd(FirstRow, Builder.getInt32(RowNumber));
762 Value *RowLoad = Builder.CreateIntrinsic(
763 Intrin.RetTy, Intrin.IID, {Handle, RowIndex}, nullptr, Name + ".load");
764 for (unsigned J = 0; J != Intrin.NumElts; ++J) {
765 Value *Element = Builder.CreateExtractValue(RowLoad, {J});
766 Window = Builder.CreateInsertElement(
767 Window, Element, Builder.getInt32(RowNumber * Intrin.NumElts + J));
768 }
769 return Window;
770}
771
773 const CBufferRowIntrin &Intrin,
774 Value *BaseRow, Value *ByteOffset,
775 const KnownBits &WithinRow) {
776 IRBuilder<> Builder(LI);
777 Type *Ty = LI->getType();
778 auto *VT = dyn_cast<FixedVectorType>(Ty);
779 unsigned NumElements = VT ? VT->getNumElements() : 1;
780 StringRef Name = LI->getName();
781 Value *Handle = II->getOperand(0);
782 Value *RowSize = Builder.getInt32(hlsl::CBufferRowSizeInBytes);
783 Value *FirstRow =
784 Builder.CreateAdd(BaseRow, Builder.CreateUDiv(ByteOffset, RowSize));
785 Value *Component = Builder.CreateUDiv(Builder.CreateURem(ByteOffset, RowSize),
786 Builder.getInt32(Intrin.EltSize));
787 unsigned MinComponent =
788 WithinRow.getMinValue().getZExtValue() / Intrin.EltSize;
789 unsigned MaxComponent =
790 WithinRow.getMaxValue().getZExtValue() / Intrin.EltSize;
791 unsigned MinRows = 1 + (MinComponent + NumElements - 1) / Intrin.NumElts;
792 unsigned MaxRows = 1 + (MaxComponent + NumElements - 1) / Intrin.NumElts;
793 auto *WindowTy =
794 FixedVectorType::get(Ty->getScalarType(), MaxRows * Intrin.NumElts);
795 Value *Window = PoisonValue::get(WindowTy);
796 for (unsigned I = 0; I != MinRows; ++I)
797 Window = loadCBufferRowIntoWindow(Builder, Intrin, Handle, FirstRow, Window,
798 I, Name);
799
800 // The unknown starting component can require at most one extra row.
801 // Load it only when needed, rather than accessing beyond the load.
802 if (MaxRows != MinRows) {
803 BasicBlock *Head = LI->getParent();
804 Value *LastComponent =
805 Builder.CreateAdd(Component, Builder.getInt32(NumElements - 1));
806 Value *CrossesRow = Builder.CreateICmpUGE(
807 LastComponent, Builder.getInt32(MinRows * Intrin.NumElts));
808 Instruction *ThenTerm = SplitBlockAndInsertIfThen(CrossesRow, LI, false);
810 DEBUG_TYPE);
811 Builder.SetInsertPoint(ThenTerm);
812 Value *NextWindow = loadCBufferRowIntoWindow(
813 Builder, Intrin, Handle, FirstRow, Window, MinRows, Name);
814 Builder.SetInsertPoint(LI);
815 auto *Rows = Builder.CreatePHI(WindowTy, 2, Name + ".rows");
816 Rows->addIncoming(Window, Head);
817 Rows->addIncoming(NextWindow, ThenTerm->getParent());
818 Window = Rows;
819 }
820
821 Value *Result = PoisonValue::get(Ty);
822 for (unsigned I = 0; I != NumElements; ++I) {
823 Value *Index =
824 I == 0 ? Component : Builder.CreateAdd(Component, Builder.getInt32(I));
825 Value *Element =
826 Builder.CreateExtractElement(Window, Index, Name + ".extract");
827 Result =
828 VT ? Builder.CreateInsertElement(Result, Element, Builder.getInt32(I))
829 : Element;
830 }
831 return Result;
832}
833
835 const DataLayout &DL,
836 const Instruction *CxtI) {
837 KnownBits Known = computeKnownBits(Offset, DL, nullptr, CxtI);
838 auto *Phi = dyn_cast<PHINode>(Offset);
839 if (!Phi || Phi->getNumIncomingValues() == 0)
840 return Known;
841
842 // PHI queries limit recursion into each input, which can hide row alignment.
843 KnownBits IncomingKnown(Known.getBitWidth());
844 IncomingKnown.setAllConflict();
845 for (unsigned I = 0; I != Phi->getNumIncomingValues(); ++I)
846 IncomingKnown = IncomingKnown.intersectWith(
847 computeKnownBits(Phi->getIncomingValue(I), DL, nullptr,
848 Phi->getIncomingBlock(I)->getTerminator()));
849 return Known.unionWith(IncomingKnown);
850}
851
854 const DataLayout &DL = LI->getDataLayout();
855
856 Type *Ty = LI->getType();
857 assert(!isa<StructType>(Ty) && "Structs not handled yet");
858 CBufferRowIntrin Intrin(DL, Ty->getScalarType());
859
860 StringRef Name = LI->getName();
861 Value *Handle = II->getOperand(0);
862
863 IRBuilder<> Builder(LI);
864
865 ConstantInt *GlobalOffset =
866 II->getIntrinsicID() == Intrinsic::dx_resource_getbasepointer
867 ? ConstantInt::get(Builder.getInt32Ty(), 0)
868 : dyn_cast<ConstantInt>(II->getOperand(1));
869 assert(GlobalOffset && "CBuffer getpointer index must be constant");
870
871 uint64_t GlobalOffsetVal = GlobalOffset->getZExtValue();
872 Value *CurrentRow = ConstantInt::get(
873 Builder.getInt32Ty(), GlobalOffsetVal / hlsl::CBufferRowSizeInBytes);
874 unsigned int CurrentIndex =
875 (GlobalOffsetVal % hlsl::CBufferRowSizeInBytes) / Intrin.EltSize;
876
877 // Every object in a cbuffer either fits in a row or is aligned to a row. This
878 // means that only the very last pointer access can point into a row.
879 auto *LastGEP = dyn_cast<GEPOperator>(LI->getPointerOperand());
880 if (!LastGEP) {
881 // If we don't have a GEP at all we're just accessing the resource through
882 // the result of getpointer directly.
883 assert(LI->getPointerOperand() == II &&
884 "Unexpected indirect access to resource without GEP");
885 } else {
886 Value *GEPOffset = traverseGEPOffsets(
887 DL, Builder, LastGEP->getPointerOperand(), hlsl::CBufferRowSizeInBytes);
888 CurrentRow = Builder.CreateAdd(GEPOffset, CurrentRow);
889
890 APInt ConstantOffset(DL.getIndexTypeSizeInBits(LastGEP->getType()), 0);
891 if (LastGEP->accumulateConstantOffset(DL, ConstantOffset)) {
892 APInt Remainder(DL.getIndexTypeSizeInBits(LastGEP->getType()),
894 APInt::udivrem(ConstantOffset, Remainder, ConstantOffset, Remainder);
895 CurrentRow = Builder.CreateAdd(
896 CurrentRow, ConstantInt::get(Builder.getInt32Ty(), ConstantOffset));
897 CurrentIndex += Remainder.udiv(Intrin.EltSize).getZExtValue();
898 } else {
899 assert(LastGEP->getNumIndices() == 1 &&
900 "Last GEP of cbuffer access is not array or struct access");
901 // Aggregate strides are row-aligned, but merged byte offsets can select
902 // different components within a row.
903 Value *RowIndex = Builder.CreateSExtOrTrunc(*LastGEP->idx_begin(),
904 Builder.getInt32Ty());
905 uint64_t IndexScale =
906 DL.getTypeAllocSize(LastGEP->getSourceElementType());
907 if (IndexScale < hlsl::CBufferRowSizeInBytes) {
908 assert(hlsl::CBufferRowSizeInBytes % IndexScale == 0 &&
909 "CBuffer GEP index is not row aligned");
910 Value *ByteOffset =
911 IndexScale == 1
912 ? RowIndex
913 : Builder.CreateMul(RowIndex, Builder.getInt32(IndexScale));
914 uint64_t BaseWithinRow = GlobalOffsetVal % hlsl::CBufferRowSizeInBytes;
915 if (BaseWithinRow != 0)
916 ByteOffset =
917 Builder.CreateAdd(ByteOffset, Builder.getInt32(BaseWithinRow));
918 Value *RowSize = Builder.getInt32(hlsl::CBufferRowSizeInBytes);
919 KnownBits WithinRow =
920 computeCBufferOffsetKnownBits(ByteOffset, DL, LI) &
922 if (!WithinRow.isConstant()) {
924 II, LI, Intrin, CurrentRow, ByteOffset, WithinRow));
925 return;
926 }
927 RowIndex = Builder.CreateUDiv(ByteOffset, RowSize, "",
928 WithinRow.getConstant().isZero());
929 CurrentIndex = WithinRow.getConstant().getZExtValue() / Intrin.EltSize;
930 } else if (IndexScale > hlsl::CBufferRowSizeInBytes) {
931 assert(IndexScale % hlsl::CBufferRowSizeInBytes == 0 &&
932 "CBuffer GEP index is not row aligned");
933 RowIndex = Builder.CreateMul(
934 RowIndex,
935 ConstantInt::get(Builder.getInt32Ty(),
936 IndexScale / hlsl::CBufferRowSizeInBytes));
937 }
938 if (auto *BaseRow = dyn_cast<ConstantInt>(CurrentRow);
939 BaseRow && BaseRow->isZero())
940 CurrentRow = RowIndex;
941 else
942 CurrentRow = Builder.CreateAdd(CurrentRow, RowIndex);
943 }
944 }
945
946 auto *CBufLoad = Builder.CreateIntrinsic(
947 Intrin.RetTy, Intrin.IID, {Handle, CurrentRow}, nullptr, Name + ".load");
948 auto *Elt =
949 Builder.CreateExtractValue(CBufLoad, {CurrentIndex++}, Name + ".extract");
950
951 // At this point we've loaded the first scalar of our result, but our original
952 // type may have been a vector.
953 unsigned int Remaining =
954 ((DL.getTypeSizeInBits(Ty) / 8) / Intrin.EltSize) - 1;
955 if (Remaining == 0) {
956 // We only have a single element, so we're done.
957 Value *Result = Elt;
958
959 // However, if we loaded a <1 x T>, then we need to adjust the type.
960 if (auto *VT = dyn_cast<FixedVectorType>(Ty)) {
961 assert(VT->getNumElements() == 1 && "Can't have multiple elements here");
962 Result = Builder.CreateInsertElement(PoisonValue::get(VT), Result,
963 Builder.getInt32(0), Name);
964 }
965 LI->replaceAllUsesWith(Result);
966 return;
967 }
968
969 // Walk each element and extract it, wrapping to new rows as needed.
970 SmallVector<Value *> Extracts{Elt};
971 while (Remaining--) {
972 CurrentIndex %= Intrin.NumElts;
973
974 if (CurrentIndex == 0) {
975 CurrentRow = Builder.CreateAdd(CurrentRow,
976 ConstantInt::get(Builder.getInt32Ty(), 1));
977 CBufLoad = Builder.CreateIntrinsic(Intrin.RetTy, Intrin.IID,
978 {Handle, CurrentRow}, nullptr,
979 Name + ".load");
980 }
981
982 Extracts.push_back(Builder.CreateExtractValue(CBufLoad, {CurrentIndex++},
983 Name + ".extract"));
984 }
985
986 // Finally, we build up the original loaded value.
987 Value *Result = PoisonValue::get(Ty);
988 for (int I = 0, E = Extracts.size(); I < E; ++I)
989 Result = Builder.CreateInsertElement(
990 Result, Extracts[I], Builder.getInt32(I), Name + formatv(".upto{}", I));
991 LI->replaceAllUsesWith(Result);
992}
993
1027
1029 if (auto *LI = dyn_cast<LoadInst>(AI))
1030 return dyn_cast<Instruction>(LI->getPointerOperand());
1031 if (auto *SI = dyn_cast<StoreInst>(AI))
1032 return dyn_cast<Instruction>(SI->getPointerOperand());
1033 if (auto *RMWI = dyn_cast<AtomicRMWInst>(AI))
1034 return dyn_cast<Instruction>(RMWI->getPointerOperand());
1035 if (auto *CXI = dyn_cast<AtomicCmpXchgInst>(AI))
1036 return dyn_cast<Instruction>(CXI->getPointerOperand());
1037 if (auto *II = dyn_cast<IntrinsicInst>(AI))
1038 if (II->getIntrinsicID() == Intrinsic::dx_resource_updatecounter)
1039 return dyn_cast<Instruction>(II->getArgOperand(0));
1040
1041 return nullptr;
1042}
1043
1045 while (auto *GEP = dyn_cast<GetElementPtrInst>(I)) {
1046 auto *PointerI = dyn_cast<Instruction>(GEP->getPointerOperand());
1047 if (!PointerI)
1048 break;
1049 I = PointerI;
1050 }
1051 return I;
1052}
1053
1054static const std::array<Intrinsic::ID, 2> HandleIntrins = {
1055 Intrinsic::dx_resource_handlefrombinding,
1056 Intrinsic::dx_resource_handlefromimplicitbinding,
1057};
1058
1060 SmallVector<Value *> Worklist = {Ptr};
1062 SmallPtrSet<Value *, 4> VisitedPhis;
1063
1064 while (!Worklist.empty()) {
1065 Value *X = Worklist.pop_back_val();
1066
1067 if (!X->getType()->isPointerTy() && !X->getType()->isTargetExtTy())
1068 return {}; // Early exit on store/load into non-resource
1069
1070 if (auto *Phi = dyn_cast<PHINode>(X)) {
1071 if (VisitedPhis.contains(X))
1072 continue;
1073 for (Use &V : Phi->incoming_values())
1074 Worklist.push_back(V.get());
1075 VisitedPhis.insert(Phi);
1076 } else if (auto *Select = dyn_cast<SelectInst>(X)) {
1077 Worklist.push_back(Select->getTrueValue());
1078 Worklist.push_back(Select->getFalseValue());
1079 } else if (auto *GEP = dyn_cast<GetElementPtrInst>(X)) {
1080 Worklist.push_back(GEP->getPointerOperand());
1081 } else if (auto *II = dyn_cast<IntrinsicInst>(X)) {
1082 Intrinsic::ID IID = II->getIntrinsicID();
1083
1084 if (IID == Intrinsic::dx_resource_getpointer ||
1085 IID == Intrinsic::dx_resource_getbasepointer)
1086 Worklist.push_back(II->getArgOperand(/*Handle=*/0));
1087
1089 Handles.push_back(II);
1090 }
1091 }
1092
1093 return Handles;
1094}
1095
1097 DXILResourceTypeMap &DRTM) {
1099 "Only expects a Handle as determined from collectUsedHandles.");
1100
1101 auto *HandleTy = cast<TargetExtType>(Handle->getType());
1102 dxil::ResourceClass Class = DRTM[HandleTy].getResourceClass();
1103 uint32_t Space = cast<ConstantInt>(Handle->getArgOperand(0))->getZExtValue();
1104 uint32_t LowerBound =
1105 cast<ConstantInt>(Handle->getArgOperand(1))->getZExtValue();
1106 uint32_t Size = cast<ConstantInt>(Handle->getArgOperand(2))->getZExtValue();
1107 uint32_t UpperBound = Size == UINT32_MAX ? UINT32_MAX : LowerBound + Size - 1;
1108
1109 return hlsl::Binding(Class, Space, LowerBound, UpperBound, nullptr);
1110}
1111
1112namespace {
1113/// Helper for propagating the current handle and ptr indices.
1114struct AccessIndices {
1115 Value *GetPtrIdx;
1116 Value *HandleIdx;
1117 Value *OffsetIdx;
1118
1119 bool hasGetPtrIdx() { return GetPtrIdx != nullptr; }
1120 bool hasHandleIdx() { return HandleIdx != nullptr; }
1121 bool hasOffsetIdx() { return OffsetIdx != nullptr; }
1122};
1123
1124/// AccessIndices cached per phi. The values may be placeholder phis that are
1125/// later folded away (via replaceAllUsesWith) and freed, so track them with
1126/// value handles rather than raw pointers.
1127struct TrackedAccessIndices {
1128 WeakTrackingVH GetPtrIdx;
1129 WeakTrackingVH HandleIdx;
1130 WeakTrackingVH OffsetIdx;
1131
1132 TrackedAccessIndices() = default;
1133 TrackedAccessIndices(const AccessIndices &AI)
1134 : GetPtrIdx(AI.GetPtrIdx), HandleIdx(AI.HandleIdx),
1135 OffsetIdx(AI.OffsetIdx) {}
1136 operator AccessIndices() const { return {GetPtrIdx, HandleIdx, OffsetIdx}; }
1137};
1138
1139using PhiAccessIndicesMap = SmallDenseMap<PHINode *, TrackedAccessIndices>;
1140} // namespace
1141
1142// Compute the total byte offset described by a GEP as a single i32 value,
1143// handling both constant and variable indices.
1145 IRBuilder<> &Builder) {
1146 const DataLayout &DL = GEP->getDataLayout();
1147 unsigned BitWidth = DL.getIndexTypeSizeInBits(GEP->getType());
1148 SmallMapVector<Value *, APInt, 4> VariableOffsets;
1149 APInt ConstantOffset(BitWidth, 0);
1150 bool Success =
1151 GEP->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset);
1152 assert(Success && "Unhandled GEP structure for resource access");
1153 (void)Success;
1154
1155 Type *I32 = Builder.getInt32Ty();
1156 Value *Offset = ConstantInt::get(I32, ConstantOffset.getSExtValue());
1157 for (auto &[V, Scale] : VariableOffsets) {
1158 Value *Index = Builder.CreateSExtOrTrunc(V, I32);
1159 if (!Scale.isOne())
1160 Index =
1161 Builder.CreateMul(Index, ConstantInt::get(I32, Scale.getSExtValue()));
1162 Offset = Builder.CreateAdd(Offset, Index);
1163 }
1164 return Offset;
1165}
1166
1167// getAccessIndices traverses up the control flow that a ptr came from and
1168// propagates back the indicies used to access the resource (AccessIndices):
1169//
1170// - GetPtrIdx is the index of dx.resource.getpointer
1171// - HandleIdx is the index of dx.resource.handlefrom.*
1172// - OffsetIdx is the accumulated byte offset of any GEPs in the ptr chain
1173static AccessIndices
1175 PhiAccessIndicesMap &VisitedPhis) {
1176 if (auto *II = dyn_cast<IntrinsicInst>(I)) {
1177 if (llvm::is_contained(HandleIntrins, II->getIntrinsicID())) {
1178 DeadInsts.insert(II);
1179 return {nullptr, II->getArgOperand(/*Index=*/3), nullptr};
1180 }
1181
1182 Intrinsic::ID IID = II->getIntrinsicID();
1183 if (IID == Intrinsic::dx_resource_getpointer ||
1184 IID == Intrinsic::dx_resource_getbasepointer) {
1185 auto *V = dyn_cast<Instruction>(II->getArgOperand(/*Handle=*/0));
1186 auto AccessIdx = getAccessIndices(V, DeadInsts, VisitedPhis);
1187 assert(!AccessIdx.hasGetPtrIdx() &&
1188 "Encountered multiple dx.resource.getpointers in ptr chain?");
1189 IRBuilder<> Builder(II);
1190 AccessIdx.GetPtrIdx = ConstantInt::get(Builder.getInt32Ty(), 0);
1191 if (IID == Intrinsic::dx_resource_getpointer)
1192 AccessIdx.GetPtrIdx = II->getArgOperand(1);
1193
1194 DeadInsts.insert(II);
1195 return AccessIdx;
1196 }
1197 }
1198
1199 if (auto *GEP = dyn_cast<GetElementPtrInst>(I)) {
1200 auto *V = dyn_cast<Instruction>(GEP->getPointerOperand());
1201 auto AccessIdx = getAccessIndices(V, DeadInsts, VisitedPhis);
1202
1203 IRBuilder<> Builder(GEP);
1204 Value *GEPOffset = accumulateGEPOffset(GEP, Builder);
1205 AccessIdx.OffsetIdx =
1206 AccessIdx.hasOffsetIdx()
1207 ? Builder.CreateAdd(AccessIdx.OffsetIdx, GEPOffset)
1208 : GEPOffset;
1209
1210 DeadInsts.insert(GEP);
1211 return AccessIdx;
1212 }
1213
1214 if (auto *Phi = dyn_cast<PHINode>(I)) {
1215 // If we're already building indices for this phi, return a ref to the phi
1216 if (auto It = VisitedPhis.find(Phi); It != VisitedPhis.end())
1217 return It->second;
1218
1219 unsigned NumEdges = Phi->getNumIncomingValues();
1220 assert(NumEdges != 0 && "Malformed Phi Node");
1221
1222 IRBuilder<> Builder(Phi);
1223 std::unique_ptr<PHINode> GetPtrPhi(
1224 PHINode::Create(Builder.getInt32Ty(), NumEdges));
1225 std::unique_ptr<PHINode> HandlePhi(
1226 PHINode::Create(Builder.getInt32Ty(), NumEdges));
1227 std::unique_ptr<PHINode> OffsetPhi(
1228 PHINode::Create(Builder.getInt32Ty(), NumEdges));
1229 bool HasOffset = false;
1230
1231 // Register a ref to this phi for a recursive phi. If a placeholder phi
1232 // below folds to a single value, replaceAllUsesWith updates any cached
1233 // entries that captured it.
1234 if (Phi->getType()->isTargetExtTy())
1235 VisitedPhis[Phi] = AccessIndices{/*GetPtrIdx=*/nullptr, HandlePhi.get(),
1236 /*OffsetIdx=*/nullptr};
1237 else if (Phi->getType()->isPointerTy())
1238 VisitedPhis[Phi] =
1239 AccessIndices{GetPtrPhi.get(), HandlePhi.get(), OffsetPhi.get()};
1240
1241 for (unsigned Idx = 0; Idx < NumEdges; Idx++) {
1242 auto *BB = Phi->getIncomingBlock(Idx);
1243 auto *V = dyn_cast<Instruction>(Phi->getIncomingValue(Idx));
1244 auto AccessIdx = getAccessIndices(V, DeadInsts, VisitedPhis);
1245 if (AccessIdx.hasGetPtrIdx())
1246 GetPtrPhi->addIncoming(AccessIdx.GetPtrIdx, BB);
1247 HandlePhi->addIncoming(AccessIdx.HandleIdx, BB);
1248 if (AccessIdx.hasOffsetIdx()) {
1249 OffsetPhi->addIncoming(AccessIdx.OffsetIdx, BB);
1250 HasOffset = true;
1251 } else {
1252 OffsetPhi->addIncoming(ConstantInt::get(Builder.getInt32Ty(), 0), BB);
1253 }
1254 }
1255
1256 Value *GetPtrIdx;
1257 if (GetPtrPhi->getNumIncomingValues() == 0) {
1258 GetPtrIdx = nullptr;
1259 } else if (Value *ConstantGetPtr = GetPtrPhi->hasConstantValue()) {
1260 GetPtrIdx = ConstantGetPtr;
1261 GetPtrPhi->replaceAllUsesWith(GetPtrIdx);
1262 } else {
1263 GetPtrIdx = GetPtrPhi.release();
1264 Builder.Insert(GetPtrIdx);
1265 }
1266
1267 Value *HandleIdx;
1268 if (Value *ConstantHandle = HandlePhi->hasConstantValue()) {
1269 HandleIdx = ConstantHandle;
1270 HandlePhi->replaceAllUsesWith(HandleIdx);
1271 } else {
1272 HandleIdx = HandlePhi.release();
1273 Builder.Insert(HandleIdx);
1274 }
1275
1276 Value *OffsetIdx = nullptr;
1277 if (HasOffset) {
1278 if (Value *ConstantIdx = OffsetPhi->hasConstantValue()) {
1279 OffsetIdx = ConstantIdx;
1280 OffsetPhi->replaceAllUsesWith(OffsetIdx);
1281 } else {
1282 OffsetIdx = OffsetPhi.release();
1283 Builder.Insert(OffsetIdx);
1284 }
1285 }
1286
1287 // Replace the placeholder entry with the final indices.
1288 AccessIndices Result = {GetPtrIdx, HandleIdx, OffsetIdx};
1289 VisitedPhis[Phi] = Result;
1290 DeadInsts.insert(Phi);
1291 return Result;
1292 }
1293
1294 if (auto *Select = dyn_cast<SelectInst>(I)) {
1295 auto *TrueV = dyn_cast<Instruction>(Select->getTrueValue());
1296 auto TrueAccessIdx = getAccessIndices(TrueV, DeadInsts, VisitedPhis);
1297
1298 auto *FalseV = dyn_cast<Instruction>(Select->getFalseValue());
1299 auto FalseAccessIdx = getAccessIndices(FalseV, DeadInsts, VisitedPhis);
1300
1301 IRBuilder<> Builder(Select);
1302 Value *GetPtrSelect = nullptr;
1303 Value *OffsetSelect = nullptr;
1304
1305 if (TrueAccessIdx.hasGetPtrIdx() && FalseAccessIdx.hasGetPtrIdx())
1306 GetPtrSelect =
1307 Builder.CreateSelect(Select->getCondition(), TrueAccessIdx.GetPtrIdx,
1308 FalseAccessIdx.GetPtrIdx, "", Select);
1309
1310 auto *HandleSelect =
1311 Builder.CreateSelect(Select->getCondition(), TrueAccessIdx.HandleIdx,
1312 FalseAccessIdx.HandleIdx, "", Select);
1313
1314 if (TrueAccessIdx.hasOffsetIdx() || FalseAccessIdx.hasOffsetIdx()) {
1315 Value *Zero = ConstantInt::get(Builder.getInt32Ty(), 0);
1316 Value *TrueOffset =
1317 TrueAccessIdx.hasOffsetIdx() ? TrueAccessIdx.OffsetIdx : Zero;
1318 Value *FalseOffset =
1319 FalseAccessIdx.hasOffsetIdx() ? FalseAccessIdx.OffsetIdx : Zero;
1320 OffsetSelect = Builder.CreateSelect(Select->getCondition(), TrueOffset,
1321 FalseOffset, "", Select);
1322 }
1323
1324 DeadInsts.insert(Select);
1325 return {GetPtrSelect, HandleSelect, OffsetSelect};
1326 }
1327
1328 llvm_unreachable("collectUsedHandles should assure this does not occur");
1329}
1330
1331static void
1334 PhiAccessIndicesMap &VisitedPhis) {
1335 auto AccessIdx = getAccessIndices(Ptr, DeadInsts, VisitedPhis);
1336 assert(AccessIdx.hasHandleIdx() &&
1337 "Couldn't retrieve handle index. This is guaranteed by "
1338 "getAccessIndices");
1339
1340 IRBuilder<> Builder(Ptr);
1341 if (isa<PHINode>(Ptr))
1342 Builder.SetInsertPoint(Ptr->getParent()->getFirstNonPHIIt());
1343 IntrinsicInst *Handle = cast<IntrinsicInst>(OldHandle->clone());
1344 Handle->setArgOperand(/*Index=*/3, AccessIdx.HandleIdx);
1345 Builder.Insert(Handle);
1346
1347 if (Ptr->getType()->isPointerTy()) {
1348 assert(AccessIdx.hasGetPtrIdx() &&
1349 "Couldn't retrieve getpointer index. This is guaranteed by "
1350 "getAccessIndices");
1351 auto *GetPtr = Builder.CreateIntrinsic(Ptr->getType(),
1352 Intrinsic::dx_resource_getpointer,
1353 {Handle, AccessIdx.GetPtrIdx});
1354
1355 // Reapply any byte offset that came from GEPs in the original ptr chain so
1356 // that later access lowering still sees it.
1357 Value *Result = GetPtr;
1358 if (AccessIdx.hasOffsetIdx())
1359 Result = Builder.CreatePtrAdd(GetPtr, AccessIdx.OffsetIdx);
1360 Ptr->replaceAllUsesWith(Result);
1361 } else {
1362 assert(Ptr->getType()->isTargetExtTy() && !AccessIdx.hasGetPtrIdx() &&
1363 "Unexpected resource access operand type");
1364 Ptr->replaceAllUsesWith(Handle);
1365 }
1366
1367 DeadInsts.insert(Ptr);
1368}
1369
1370// Try to legalize dx.resource.handlefrom.*.binding and dx.resource.getpointer
1371// calls with their respective index values and propagate the index values to
1372// be used at resource access.
1373//
1374// If it can't be transformed to be legal then:
1375//
1376// Reports an error if a resource access is not guaranteed into a unique global
1377// resource.
1378//
1379// Returns true if any changes are made.
1382 PhiAccessIndicesMap VisitedPhis;
1383
1384 for (BasicBlock &BB : make_early_inc_range(F)) {
1385 for (Instruction &I : BB) {
1386 if (auto *HandleOp = getHandleOperand(&I)) {
1388 unsigned NumHandles = Handles.size();
1389 if (NumHandles <= 1)
1390 continue; // Legal, no-replacement required
1391
1392 bool SameGlobalBinding = true;
1393 hlsl::Binding B = getHandleIntrinsicBinding(Handles[0], DRTM);
1394 for (unsigned Idx = 1; Idx < NumHandles; Idx++)
1395 SameGlobalBinding &=
1396 (B == getHandleIntrinsicBinding(Handles[Idx], DRTM));
1397
1398 if (!SameGlobalBinding)
1400
1401 replaceHandleWithIndices(getHandleRoot(HandleOp), Handles[0], DeadInsts,
1402 VisitedPhis);
1403 }
1404 }
1405 }
1406
1407 bool MadeChanges = false;
1408
1409 // Set up the phis to track if they are erased below
1410 SmallVector<WeakTrackingVH> ResourcePhis;
1411 for (const auto &HandleToIndex : VisitedPhis)
1412 ResourcePhis.push_back(HandleToIndex.first);
1413
1414 for (auto *I : llvm::reverse(DeadInsts))
1415 if (I->hasNUses(0)) { // Handle can still be used outside of replaced path
1416 I->eraseFromParent();
1417 MadeChanges = true;
1418 }
1419
1420 // Any remaining phi nodes are now looped with another phi node and have no
1421 // other uses
1422 for (WeakTrackingVH &VH : ResourcePhis)
1423 if (VH) // True if not removed above or already in this loop
1424 MadeChanges |= RecursivelyDeleteDeadPHINode(cast<PHINode>(VH));
1425
1426 return MadeChanges;
1427}
1428
1430 SmallVector<User *> Worklist;
1431 for (User *U : II->users())
1432 Worklist.push_back(U);
1433
1435 while (!Worklist.empty()) {
1436 User *U = Worklist.back();
1437 Worklist.pop_back();
1438
1439 if (auto *GEP = dyn_cast<GetElementPtrInst>(U)) {
1440 for (User *U : GEP->users())
1441 Worklist.push_back(U);
1442 DeadInsts.push_back(GEP);
1443
1444 } else if (auto *SI = dyn_cast<StoreInst>(U)) {
1445 assert(SI->getValueOperand() != II && "Pointer escaped!");
1446 createStoreIntrinsic(II, SI, RTI);
1447 DeadInsts.push_back(SI);
1448
1449 } else if (auto *LI = dyn_cast<LoadInst>(U)) {
1450 createLoadIntrinsic(II, LI, RTI);
1451 DeadInsts.push_back(LI);
1452 } else if (auto *AI = dyn_cast<AtomicRMWInst>(U)) {
1454 DeadInsts.push_back(AI);
1455 } else if (auto *CXI = dyn_cast<AtomicCmpXchgInst>(U)) {
1457 DeadInsts.push_back(CXI);
1458 } else
1459 llvm_unreachable("Unhandled instruction - pointer escaped?");
1460 }
1461
1462 // Traverse the now-dead instructions in RPO and remove them.
1463 for (Instruction *Dead : llvm::reverse(DeadInsts))
1464 Dead->eraseFromParent();
1465 II->eraseFromParent();
1466}
1467
1470 for (BasicBlock &BB : make_early_inc_range(F))
1471 for (Instruction &I : BB)
1472 if (auto *II = dyn_cast<IntrinsicInst>(&I))
1473 if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer ||
1474 II->getIntrinsicID() == Intrinsic::dx_resource_getbasepointer) {
1475 auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());
1476 assert(
1477 (DRTM[HandleTy].isCBuffer() ||
1478 II->getIntrinsicID() != Intrinsic::dx_resource_getbasepointer) &&
1479 "dx_resource_getbasepointer should only be used by cbuffers");
1480 Resources.emplace_back(II, DRTM[HandleTy]);
1481 }
1482
1483 for (auto &[II, RI] : Resources)
1484 replaceAccess(II, RI);
1485
1486 return !Resources.empty();
1487}
1488
1491 auto &MAMProxy = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
1492 DXILResourceTypeMap *DRTM =
1493 MAMProxy.getCachedResult<DXILResourceTypeAnalysis>(*F.getParent());
1494 assert(DRTM && "DXILResourceTypeAnalysis must be available");
1495
1496 bool MadeHandleChanges = legalizeResourceHandles(F, *DRTM);
1497 bool MadeResourceChanges = transformResourcePointers(F, *DRTM);
1498 if (!(MadeHandleChanges || MadeResourceChanges))
1499 return PreservedAnalyses::all();
1500
1503 return PA;
1504}
1505
1506namespace {
1507class DXILResourceAccessLegacy : public FunctionPass {
1508public:
1509 bool runOnFunction(Function &F) override {
1510 DXILResourceTypeMap &DRTM =
1511 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
1512 bool MadeHandleChanges = legalizeResourceHandles(F, DRTM);
1513 bool MadeResourceChanges = transformResourcePointers(F, DRTM);
1514 return MadeHandleChanges || MadeResourceChanges;
1515 }
1516 StringRef getPassName() const override { return "DXIL Resource Access"; }
1517 DXILResourceAccessLegacy() : FunctionPass(ID) {}
1518
1519 static char ID; // Pass identification.
1520 void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
1521 AU.addRequired<DXILResourceTypeWrapperPass>();
1522 }
1523};
1524char DXILResourceAccessLegacy::ID = 0;
1525} // end anonymous namespace
1526
1527INITIALIZE_PASS_BEGIN(DXILResourceAccessLegacy, DEBUG_TYPE,
1528 "DXIL Resource Access", false, false)
1530INITIALIZE_PASS_END(DXILResourceAccessLegacy, DEBUG_TYPE,
1531 "DXIL Resource Access", false, false)
1532
1534 return new DXILResourceAccessLegacy();
1535}
#define Success
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t uint64_t
AMDGPU Register Bank Select
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
#define X(NUM, ENUM, NAME)
Definition ELF.h:665
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 replaceHandleWithIndices(Instruction *Ptr, IntrinsicInst *OldHandle, SmallSetVector< Instruction *, 16 > &DeadInsts, PhiAccessIndicesMap &VisitedPhis)
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 Value * createDynamicCBufferLoad(IntrinsicInst *II, LoadInst *LI, const CBufferRowIntrin &Intrin, Value *BaseRow, Value *ByteOffset, const KnownBits &WithinRow)
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 Instruction * getHandleRoot(Instruction *I)
static void createBufferAtomicCompareExchange(IntrinsicInst *II, AtomicCmpXchgInst *AI, dxil::ResourceTypeInfo &RTI)
static Value * traverseGEPOffsets(const DataLayout &DL, IRBuilder<> &Builder, Value *Ptr, uint64_t AccessSize)
static Value * loadCBufferRowIntoWindow(IRBuilder<> &Builder, const CBufferRowIntrin &Intrin, Value *Handle, Value *FirstRow, Value *Window, unsigned RowNumber, StringRef Name)
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 KnownBits computeCBufferOffsetKnownBits(Value *Offset, const DataLayout &DL, const Instruction *CxtI)
static Value * accumulateGEPOffset(GetElementPtrInst *GEP, IRBuilder<> &Builder)
static void emitAtomicBinOp(IRBuilder<> &Builder, AtomicRMWInst *AI, Value *Handle, ArrayRef< Value * > Coords)
static AccessIndices getAccessIndices(Instruction *I, SmallSetVector< Instruction *, 16 > &DeadInsts, PhiAccessIndicesMap &VisitedPhis)
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
This file implements a map that provides insertion order iteration.
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 contains the declarations for profiling metadata utility functions.
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: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
bool isZero() const
Determine if this value is zero, i.e. all bits are clear.
Definition APInt.h:376
int64_t getSExtValue() const
Get sign extended value.
Definition APInt.h:1582
AnalysisUsage & addRequired()
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
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction; assumes that the block is well-formed.
Definition BasicBlock.h:237
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
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:312
an instruction for type-safe pointer arithmetic to access elements of arrays and structs
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2947
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:76
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
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
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: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:549
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:315
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
@ Known
Known to have no common set bits.
@ Dead
Unused definition.
LLVM_ABI void setExplicitlyUnknownBranchWeightsIfProfiled(Instruction &I, StringRef PassName, const Function *F=nullptr)
Like setExplicitlyUnknownBranchWeights(...), but only sets unknown branch weights in the new instruct...
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 void computeKnownBits(const Value *V, KnownBits &Known, const DataLayout &DL, AssumptionCache *AC=nullptr, const Instruction *CtxI=nullptr, const DominatorTree *DT=nullptr, bool UseInstrInfo=true, unsigned Depth=0)
Determine which bits of V are known to be either zero or one and return them in the KnownZero/KnownOn...
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()
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
constexpr unsigned BitWidth
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
LLVM_ABI Instruction * SplitBlockAndInsertIfThen(Value *Cond, BasicBlock::iterator SplitBefore, bool Unreachable, MDNode *BranchWeights=nullptr, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr, BasicBlock *ThenBlock=nullptr)
Split the containing block at the specified instruction - everything before SplitBefore stays in the ...
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
static KnownBits makeConstant(const APInt &C)
Create known bits from a known constant.
Definition KnownBits.h:315
void setAllConflict()
Make all bits known to be both zero and one.
Definition KnownBits.h:97
bool isConstant() const
Returns true if we know the value of all bits.
Definition KnownBits.h:54
KnownBits intersectWith(const KnownBits &RHS) const
Returns KnownBits information that is known to be true for both this and RHS.
Definition KnownBits.h:325
APInt getMaxValue() const
Return the maximal unsigned value possible given these KnownBits.
Definition KnownBits.h:146
APInt getMinValue() const
Return the minimal unsigned value possible given these KnownBits.
Definition KnownBits.h:130
const APInt & getConstant() const
Returns the value when all bits have a known value.
Definition KnownBits.h:58
A MapVector that performs no allocations if smaller than a certain size.
Definition MapVector.h:342