LLVM 24.0.0git
DXILOpLowering.cpp
Go to the documentation of this file.
1//===- DXILOpLowering.cpp - Lowering to DXIL operations -------------------===//
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 "DXILOpLowering.h"
10#include "DXILConstants.h"
11#include "DXILOpBuilder.h"
12#include "DXILRootSignature.h"
13#include "DXILShaderFlags.h"
14#include "DirectX.h"
18#include "llvm/CodeGen/Passes.h"
19#include "llvm/IR/Constant.h"
21#include "llvm/IR/IRBuilder.h"
22#include "llvm/IR/Instruction.h"
24#include "llvm/IR/Intrinsics.h"
25#include "llvm/IR/IntrinsicsDirectX.h"
26#include "llvm/IR/Module.h"
27#include "llvm/IR/PassManager.h"
28#include "llvm/IR/Use.h"
29#include "llvm/IR/ValueHandle.h"
31#include "llvm/Pass.h"
34
35#define DEBUG_TYPE "dxil-op-lower"
36
37using namespace llvm;
38using namespace llvm::dxil;
39
40/// Write mask covering all four components of a UAV element. Typed UAV stores
41/// (textures and typed buffers) must always use this mask - the DXIL validator
42/// rejects anything narrower. Only raw and / structured buffer stores may use a
43/// partial mask.
44static constexpr uint8_t TypedUAVStoreWriteMask = 0xF;
45
46namespace {
47class OpLowerer {
48 Module &M;
49 DXILOpBuilder OpBuilder;
50 DXILResourceMap &DRM;
52 const ModuleMetadataInfo &MMDI;
53 SmallVector<CallInst *> CleanupCasts;
54 Function *CleanupNURI = nullptr;
55
56public:
57 OpLowerer(Module &M, DXILResourceMap &DRM, DXILResourceTypeMap &DRTM,
58 const ModuleMetadataInfo &MMDI)
59 : M(M), OpBuilder(M), DRM(DRM), DRTM(DRTM), MMDI(MMDI) {}
60
61 /// Replace every call to \c F using \c ReplaceCall, and then erase \c F. If
62 /// there is an error replacing a call, we emit a diagnostic and return true.
63 [[nodiscard]] bool
64 replaceFunction(Function &F,
65 llvm::function_ref<Error(CallInst *CI)> ReplaceCall) {
66 for (User *U : make_early_inc_range(F.users())) {
68 if (!CI)
69 continue;
70
71 if (Error E = ReplaceCall(CI)) {
72 std::string Message(toString(std::move(E)));
73 M.getContext().diagnose(DiagnosticInfoUnsupported(
74 *CI->getFunction(), Message, CI->getDebugLoc()));
75
76 return true;
77 }
78 }
79 if (F.user_empty())
80 F.eraseFromParent();
81 return false;
82 }
83
84 struct IntrinArgSelect {
85 enum class Type {
86#define DXIL_OP_INTRINSIC_ARG_SELECT_TYPE(name) name,
87#include "DXILOperation.inc"
88 };
89 Type Type;
90 int Value;
91 };
92
93 /// Replaces uses of a struct with uses of an equivalent named struct.
94 ///
95 /// DXIL operations that return structs give them well known names, so we need
96 /// to update uses when we switch from an LLVM intrinsic to an op.
97 Error replaceNamedStructUses(CallInst *Intrin, CallInst *DXILOp) {
98 auto *IntrinTy = cast<StructType>(Intrin->getType());
99 auto *DXILOpTy = cast<StructType>(DXILOp->getType());
100 if (!IntrinTy->isLayoutIdentical(DXILOpTy))
102 "Type mismatch between intrinsic and DXIL op",
104
105 for (Use &U : make_early_inc_range(Intrin->uses()))
106 if (auto *EVI = dyn_cast<ExtractValueInst>(U.getUser()))
107 EVI->setOperand(0, DXILOp);
108 else if (auto *IVI = dyn_cast<InsertValueInst>(U.getUser()))
109 IVI->setOperand(0, DXILOp);
110 else
111 return make_error<StringError>("DXIL ops that return structs may only "
112 "be used by insert- and extractvalue",
114 return Error::success();
115 }
116
117 bool isFast(FastMathFlags Flags) {
118 // HLSL Fast Math doesn't enable AllowContract flag; This can be
119 // removed when we enable it in the future.
120 return Flags.allowReassoc() && Flags.noNaNs() && Flags.noInfs() &&
121 Flags.noSignedZeros() && Flags.allowReciprocal() &&
122 Flags.approxFunc();
123 }
124
125 void setDxPrecise(CallInst *CI) {
126 const StringRef Key = "dx.precise";
127 Module *M = CI->getModule();
128
129 LLVMContext &Ctx = M->getContext();
130 MDNode *One =
131 llvm::MDNode::get(Ctx, ConstantAsMetadata::get(ConstantInt::get(
132 llvm::Type::getInt32Ty(Ctx), 1)));
133
134 CI->setMetadata(Key, One);
135 }
136
137 [[nodiscard]] bool
138 replaceFunctionWithOp(Function &F, dxil::OpCode DXILOp,
139 ArrayRef<IntrinArgSelect> ArgSelects) {
140 return replaceFunction(F, [&](CallInst *CI) -> Error {
141 OpBuilder.getIRB().SetInsertPoint(CI);
143 if (ArgSelects.size()) {
144 for (const IntrinArgSelect &A : ArgSelects) {
145 switch (A.Type) {
146 case IntrinArgSelect::Type::Index:
147 Args.push_back(CI->getArgOperand(A.Value));
148 break;
149 case IntrinArgSelect::Type::I8:
150 Args.push_back(OpBuilder.getIRB().getInt8((uint8_t)A.Value));
151 break;
152 case IntrinArgSelect::Type::I32:
153 Args.push_back(OpBuilder.getIRB().getInt32(A.Value));
154 break;
155 }
156 }
157 } else {
158 Args.append(CI->arg_begin(), CI->arg_end());
159 }
160
161 Expected<CallInst *> OpCall =
162 OpBuilder.tryCreateOp(DXILOp, Args, CI->getName(), F.getReturnType());
163 if (Error E = OpCall.takeError())
164 return E;
165
166 if (isa<FPMathOperator>(CI) &&
168 setDxPrecise(*OpCall);
169
170 if (isa<StructType>(CI->getType())) {
171 if (Error E = replaceNamedStructUses(CI, *OpCall))
172 return E;
173 } else
174 CI->replaceAllUsesWith(*OpCall);
175
176 CI->eraseFromParent();
177 return Error::success();
178 });
179 }
180
181 /// Create a cast between a `target("dx")` type and `dx.types.Handle`, which
182 /// is intended to be removed by the end of lowering. This is used to allow
183 /// lowering of ops which need to change their return or argument types in a
184 /// piecemeal way - we can add the casts in to avoid updating all of the uses
185 /// or defs, and by the end all of the casts will be redundant.
186 Value *createTmpHandleCast(Value *V, Type *Ty) {
187 CallInst *Cast = OpBuilder.getIRB().CreateIntrinsicWithoutFolding(
188 Intrinsic::dx_resource_casthandle, {Ty, V->getType()}, {V});
189 CleanupCasts.push_back(Cast);
190 return Cast;
191 }
192
193 void cleanupHandleCasts() {
196
197 for (CallInst *Cast : CleanupCasts) {
198 // These casts were only put in to ease the move from `target("dx")` types
199 // to `dx.types.Handle in a piecemeal way. At this point, all of the
200 // non-cast uses should now be `dx.types.Handle`, and remaining casts
201 // should all form pairs to and from the now unused `target("dx")` type.
202 CastFns.push_back(Cast->getCalledFunction());
203
204 // If the cast is not to `dx.types.Handle`, it should be the first part of
205 // the pair. Keep track so we can remove it once it has no more uses.
206 if (Cast->getType() != OpBuilder.getHandleType()) {
207 ToRemove.push_back(Cast);
208 continue;
209 }
210 // Otherwise, we're the second handle in a pair. Forward the arguments and
211 // remove the (second) cast.
212 CallInst *Def = cast<CallInst>(Cast->getOperand(0));
213 assert(Def->getIntrinsicID() == Intrinsic::dx_resource_casthandle &&
214 "Unbalanced pair of temporary handle casts");
215 Cast->replaceAllUsesWith(Def->getOperand(0));
216 Cast->eraseFromParent();
217 }
218 for (CallInst *Cast : ToRemove) {
219 assert(Cast->user_empty() && "Temporary handle cast still has users");
220 Cast->eraseFromParent();
221 }
222
223 // Deduplicate the cast functions so that we only erase each one once.
224 llvm::sort(CastFns);
225 CastFns.erase(llvm::unique(CastFns), CastFns.end());
226 for (Function *F : CastFns)
227 F->eraseFromParent();
228
229 CleanupCasts.clear();
230 }
231
232 void cleanupNonUniformResourceIndexCalls() {
233 // Replace all NonUniformResourceIndex calls with their argument.
234 if (!CleanupNURI)
235 return;
236 for (User *U : make_early_inc_range(CleanupNURI->users())) {
237 CallInst *CI = dyn_cast<CallInst>(U);
238 if (!CI)
239 continue;
241 CI->eraseFromParent();
242 }
243 CleanupNURI->eraseFromParent();
244 CleanupNURI = nullptr;
245 }
246
247 // Remove the resource global associated with the handleFromBinding call
248 // instruction and their uses as they aren't needed anymore.
249 // TODO: We should verify that all the globals get removed.
250 // It's expected we'll need a custom pass in the future that will eliminate
251 // the need for this here.
252 void removeResourceGlobals(CallInst *CI) {
253 for (User *User : make_early_inc_range(CI->users())) {
254 if (StoreInst *Store = dyn_cast<StoreInst>(User)) {
255 Value *V = Store->getOperand(1);
256 Store->eraseFromParent();
257 if (GlobalVariable *GV = dyn_cast<GlobalVariable>(V))
258 if (GV->use_empty()) {
259 GV->removeDeadConstantUsers();
260 GV->eraseFromParent();
261 }
262 }
263 }
264 }
265
266 void replaceHandleFromBindingCall(CallInst *CI, Value *Replacement) {
268 Intrinsic::dx_resource_handlefrombinding);
269
270 removeResourceGlobals(CI);
271
272 auto *NameGlobal = dyn_cast<llvm::GlobalVariable>(CI->getArgOperand(4));
273
274 CI->replaceAllUsesWith(Replacement);
275 CI->eraseFromParent();
276
277 if (NameGlobal && NameGlobal->use_empty())
278 NameGlobal->eraseFromParent();
279 }
280
281 bool hasNonUniformIndex(Value *IndexOp) {
282 if (isa<llvm::Constant>(IndexOp))
283 return false;
284
285 SmallVector<Value *, 16> Worklist;
286 SmallPtrSet<Value *, 16> Visited;
287 Worklist.push_back(IndexOp);
288
289 while (!Worklist.empty()) {
290 Value *V = Worklist.pop_back_val();
291
292 if (isa<llvm::Constant>(V))
293 continue;
294
295 if (!Visited.insert(V).second)
296 continue;
297
298 if (auto *CI = dyn_cast<CallInst>(V))
299 if (CI->getIntrinsicID() == Intrinsic::dx_resource_nonuniformindex)
300 return true;
301
302 // If it's a PHI node, check ALL incoming values —
303 // taint from ANY predecessor counts
304 if (auto *Phi = dyn_cast<PHINode>(V)) {
305 for (Value *Incoming : Phi->incoming_values())
306 Worklist.push_back(Incoming);
307 continue;
308 }
309
310 if (auto *Inst = dyn_cast<Instruction>(V))
311 if (Inst->getNumOperands() > 0 && !Inst->isTerminator())
312 for (Value *Op : Inst->operands())
313 Worklist.push_back(Op);
314 }
315 return false;
316 }
317
318 Error validateRawBufferElementIndex(Value *Resource, Value *ElementIndex) {
319 bool IsStructured =
320 cast<RawBufferExtType>(Resource->getType())->isStructured();
321 bool IsPoison = isa<PoisonValue>(ElementIndex);
322
323 if (IsStructured && IsPoison)
325 "Element index of structured buffer may not be poison",
327
328 if (!IsStructured && !IsPoison)
330 "Element index of raw buffer must be poison",
332
333 return Error::success();
334 }
335
336 [[nodiscard]] bool lowerToCreateHandle(Function &F) {
337 IRBuilder<> &IRB = OpBuilder.getIRB();
338 Type *Int8Ty = IRB.getInt8Ty();
339 Type *Int32Ty = IRB.getInt32Ty();
340 Type *Int1Ty = IRB.getInt1Ty();
341
342 return replaceFunction(F, [&](CallInst *CI) -> Error {
343 IRB.SetInsertPoint(CI);
344
345 auto *It = DRM.find(CI);
346 assert(It != DRM.end() && "Resource not in map?");
347 dxil::ResourceInfo &RI = *It;
348
349 const auto &Binding = RI.getBinding();
350 dxil::ResourceClass RC = DRTM[RI.getHandleTy()].getResourceClass();
351
352 Value *IndexOp = CI->getArgOperand(3);
353 if (Binding.LowerBound != 0)
354 IndexOp = IRB.CreateAdd(IndexOp,
355 ConstantInt::get(Int32Ty, Binding.LowerBound));
356
357 bool HasNonUniformIndex =
358 (Binding.Size == 1) ? false : hasNonUniformIndex(IndexOp);
359 std::array<Value *, 4> Args{
360 ConstantInt::get(Int8Ty, llvm::to_underlying(RC)),
361 ConstantInt::get(Int32Ty, Binding.BindingID), IndexOp,
362 ConstantInt::get(Int1Ty, HasNonUniformIndex)};
363 Expected<CallInst *> OpCall =
364 OpBuilder.tryCreateOp(OpCode::CreateHandle, Args, CI->getName());
365 if (Error E = OpCall.takeError())
366 return E;
367
368 Value *Cast = createTmpHandleCast(*OpCall, CI->getType());
369 replaceHandleFromBindingCall(CI, Cast);
370 return Error::success();
371 });
372 }
373
374 [[nodiscard]] bool lowerToBindAndAnnotateHandle(Function &F) {
375 IRBuilder<> &IRB = OpBuilder.getIRB();
376 Type *Int32Ty = IRB.getInt32Ty();
377 Type *Int1Ty = IRB.getInt1Ty();
378
379 return replaceFunction(F, [&](CallInst *CI) -> Error {
380 IRB.SetInsertPoint(CI);
381
382 auto *It = DRM.find(CI);
383 assert(It != DRM.end() && "Resource not in map?");
384 dxil::ResourceInfo &RI = *It;
385
386 const auto &Binding = RI.getBinding();
387 dxil::ResourceTypeInfo &RTI = DRTM[RI.getHandleTy()];
389
390 Value *IndexOp = CI->getArgOperand(3);
391 if (Binding.LowerBound != 0)
392 IndexOp = IRB.CreateAdd(IndexOp,
393 ConstantInt::get(Int32Ty, Binding.LowerBound));
394
395 std::pair<uint32_t, uint32_t> Props =
396 RI.getAnnotateProps(*F.getParent(), RTI);
397
398 // For `CreateHandleFromBinding` we need the upper bound rather than the
399 // size, so we need to be careful about the difference for "unbounded".
400 uint32_t UpperBound = Binding.Size == 0
401 ? std::numeric_limits<uint32_t>::max()
403 Constant *ResBind = OpBuilder.getResBind(Binding.LowerBound, UpperBound,
404 Binding.Space, RC);
405 bool NonUniformIndex =
406 (Binding.Size == 1) ? false : hasNonUniformIndex(IndexOp);
407 Constant *NonUniformOp = ConstantInt::get(Int1Ty, NonUniformIndex);
408 std::array<Value *, 3> BindArgs{ResBind, IndexOp, NonUniformOp};
409 Expected<CallInst *> OpBind = OpBuilder.tryCreateOp(
410 OpCode::CreateHandleFromBinding, BindArgs, CI->getName());
411 if (Error E = OpBind.takeError())
412 return E;
413
414 std::array<Value *, 2> AnnotateArgs{
415 *OpBind, OpBuilder.getResProps(Props.first, Props.second)};
416 Expected<CallInst *> OpAnnotate = OpBuilder.tryCreateOp(
417 OpCode::AnnotateHandle, AnnotateArgs,
418 CI->hasName() ? CI->getName() + "_annot" : Twine());
419 if (Error E = OpAnnotate.takeError())
420 return E;
421
422 Value *Cast = createTmpHandleCast(*OpAnnotate, CI->getType());
423 replaceHandleFromBindingCall(CI, Cast);
424 return Error::success();
425 });
426 }
427
428 /// Lower `dx.resource.handlefrombinding` intrinsics depending on the shader
429 /// model and taking into account binding information from
430 /// DXILResourceAnalysis.
431 bool lowerHandleFromBinding(Function &F) {
432 if (MMDI.DXILVersion < VersionTuple(1, 6))
433 return lowerToCreateHandle(F);
434 return lowerToBindAndAnnotateHandle(F);
435 }
436
437 bool lowerHandleFromHeap(Function &F) {
438 IRBuilder<> &IRB = OpBuilder.getIRB();
439
440 return replaceFunction(F, [&](CallInst *CI) -> Error {
441 IRB.SetInsertPoint(CI);
442
443 auto *It = DRM.find(CI);
444 assert(It != DRM.end() && "Resource not in map?");
445 dxil::ResourceInfo &RI = *It;
446 dxil::ResourceTypeInfo &RTI = DRTM[RI.getHandleTy()];
447
448 Value *IndexOp = CI->getArgOperand(0);
449 Value *IsSamplerHeap =
451
452 std::pair<uint32_t, uint32_t> Props =
453 RI.getAnnotateProps(*F.getParent(), RTI);
454
455 bool NonUniformIndex = hasNonUniformIndex(IndexOp);
456 Value *NonUniformOp =
457 ConstantInt::getBool(IRB.getContext(), NonUniformIndex);
458
459 std::array<Value *, 3> Args{IndexOp, IsSamplerHeap, NonUniformOp};
460 Expected<CallInst *> OpCreateHandle = OpBuilder.tryCreateOp(
461 OpCode::CreateHandleFromHeap, Args, CI->getName());
462 if (Error E = OpCreateHandle.takeError())
463 return E;
464
465 std::array<Value *, 2> AnnotateArgs{
466 *OpCreateHandle, OpBuilder.getResProps(Props.first, Props.second)};
467 Expected<CallInst *> OpAnnotate = OpBuilder.tryCreateOp(
468 OpCode::AnnotateHandle, AnnotateArgs,
469 CI->hasName() ? CI->getName() + "_annot" : Twine());
470 if (Error E = OpAnnotate.takeError())
471 return E;
472
473 Value *Cast = createTmpHandleCast(*OpAnnotate, CI->getType());
474 CI->replaceAllUsesWith(Cast);
475 CI->eraseFromParent();
476 return Error::success();
477 });
478 }
479
480 /// Replace uses of \c Intrin with the values in the `dx.ResRet` of \c Op.
481 /// Since we expect to be post-scalarization, make an effort to avoid vectors.
482 Error replaceResRetUses(CallInst *Intrin, CallInst *Op, bool HasCheckBit) {
483 IRBuilder<> &IRB = OpBuilder.getIRB();
484
485 Instruction *OldResult = Intrin;
486 Type *OldTy = Intrin->getType();
487
488 if (HasCheckBit) {
489 auto *ST = cast<StructType>(OldTy);
490
491 Value *CheckOp = nullptr;
492 Type *Int32Ty = IRB.getInt32Ty();
493 for (Use &U : make_early_inc_range(OldResult->uses())) {
494 if (auto *EVI = dyn_cast<ExtractValueInst>(U.getUser())) {
495 ArrayRef<unsigned> Indices = EVI->getIndices();
496 assert(Indices.size() == 1);
497 // We're only interested in uses of the check bit for now.
498 if (Indices[0] != 1)
499 continue;
500 if (!CheckOp) {
501 Value *NewEVI = IRB.CreateExtractValue(Op, 4);
502 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
503 OpCode::CheckAccessFullyMapped, {NewEVI},
504 OldResult->hasName() ? OldResult->getName() + "_check"
505 : Twine(),
506 Int32Ty);
507 if (Error E = OpCall.takeError())
508 return E;
509 CheckOp = *OpCall;
510 }
511 EVI->replaceAllUsesWith(CheckOp);
512 EVI->eraseFromParent();
513 }
514 }
515
516 if (OldResult->use_empty()) {
517 // Only the check bit was used, so we're done here.
518 OldResult->eraseFromParent();
519 return Error::success();
520 }
521
522 assert(OldResult->hasOneUse() &&
523 isa<ExtractValueInst>(*OldResult->user_begin()) &&
524 "Expected only use to be extract of first element");
525 OldResult = cast<Instruction>(*OldResult->user_begin());
526 OldTy = ST->getElementType(0);
527 }
528
529 // For scalars, we just extract the first element.
530 if (!isa<FixedVectorType>(OldTy)) {
531 Value *EVI = IRB.CreateExtractValue(Op, 0);
532 OldResult->replaceAllUsesWith(EVI);
533 OldResult->eraseFromParent();
534 if (OldResult != Intrin) {
535 assert(Intrin->use_empty() && "Intrinsic still has uses?");
536 Intrin->eraseFromParent();
537 }
538 return Error::success();
539 }
540
541 std::array<Value *, 4> Extracts = {};
542 SmallVector<ExtractElementInst *> DynamicAccesses;
543
544 // The users of the operation should all be scalarized, so we attempt to
545 // replace the extractelements with extractvalues directly.
546 for (Use &U : make_early_inc_range(OldResult->uses())) {
547 if (auto *EEI = dyn_cast<ExtractElementInst>(U.getUser())) {
548 if (auto *IndexOp = dyn_cast<ConstantInt>(EEI->getIndexOperand())) {
549 size_t IndexVal = IndexOp->getZExtValue();
550 assert(IndexVal < 4 && "Index into buffer load out of range");
551 if (!Extracts[IndexVal])
552 Extracts[IndexVal] = IRB.CreateExtractValue(Op, IndexVal);
553 EEI->replaceAllUsesWith(Extracts[IndexVal]);
554 EEI->eraseFromParent();
555 } else {
556 DynamicAccesses.push_back(EEI);
557 }
558 }
559 }
560
561 const auto *VecTy = cast<FixedVectorType>(OldTy);
562 const unsigned N = VecTy->getNumElements();
563
564 // If there's a dynamic access we need to round trip through stack memory so
565 // that we don't leave vectors around.
566 if (!DynamicAccesses.empty()) {
567 Type *Int32Ty = IRB.getInt32Ty();
568 Constant *Zero = ConstantInt::get(Int32Ty, 0);
569
570 Type *ElTy = VecTy->getElementType();
571 Type *ArrayTy = ArrayType::get(ElTy, N);
572 Value *Alloca = IRB.CreateAlloca(ArrayTy);
573
574 for (int I = 0, E = N; I != E; ++I) {
575 if (!Extracts[I])
576 Extracts[I] = IRB.CreateExtractValue(Op, I);
578 ArrayTy, Alloca, {Zero, ConstantInt::get(Int32Ty, I)}, "",
579 IRB.GetInsertPoint());
580 IRB.CreateStore(Extracts[I], GEP);
581 }
582
583 for (ExtractElementInst *EEI : DynamicAccesses) {
585 ArrayTy, Alloca, {Zero, EEI->getIndexOperand()}, "",
586 IRB.GetInsertPoint());
587 Value *Load = IRB.CreateLoad(ElTy, GEP);
589 EEI->eraseFromParent();
590 }
591 }
592
593 // If we still have uses, then we're not fully scalarized and need to
594 // recreate the vector. This should only happen for things like exported
595 // functions from libraries.
596 if (!OldResult->use_empty()) {
597 for (int I = 0, E = N; I != E; ++I)
598 if (!Extracts[I])
599 Extracts[I] = IRB.CreateExtractValue(Op, I);
600
601 Value *Vec = PoisonValue::get(OldTy);
602 for (int I = 0, E = N; I != E; ++I)
603 Vec = IRB.CreateInsertElement(Vec, Extracts[I], I);
604 OldResult->replaceAllUsesWith(Vec);
605 }
606
607 OldResult->eraseFromParent();
608 if (OldResult != Intrin) {
609 assert(Intrin->use_empty() && "Intrinsic still has uses?");
610 Intrin->eraseFromParent();
611 }
612
613 return Error::success();
614 }
615
616 [[nodiscard]] bool lowerTypedBufferLoad(Function &F, bool HasCheckBit) {
617 IRBuilder<> &IRB = OpBuilder.getIRB();
618 Type *Int32Ty = IRB.getInt32Ty();
619
620 return replaceFunction(F, [&](CallInst *CI) -> Error {
621 IRB.SetInsertPoint(CI);
622
623 Value *Handle =
624 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
625 Value *Index0 = CI->getArgOperand(1);
626 Value *Index1 = UndefValue::get(Int32Ty);
627
628 Type *OldTy = CI->getType();
629 if (HasCheckBit)
630 OldTy = cast<StructType>(OldTy)->getElementType(0);
631 Type *NewRetTy = OpBuilder.getResRetType(OldTy->getScalarType());
632
633 std::array<Value *, 3> Args{Handle, Index0, Index1};
634 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
635 OpCode::BufferLoad, Args, CI->getName(), NewRetTy);
636 if (Error E = OpCall.takeError())
637 return E;
638 if (Error E = replaceResRetUses(CI, *OpCall, HasCheckBit))
639 return E;
640
641 return Error::success();
642 });
643 }
644
645 /// Recover the scalar components of `Vec` from the `insertelement` chain that
646 /// built it. Since we run after the scalarizer, such a chain is usually just
647 /// a temporary gathered to pass the vector to a call.
648 static void collectInsertedElements(Value *Vec,
649 MutableArrayRef<Value *> Elements) {
650 unsigned NumElts = cast<FixedVectorType>(Vec->getType())->getNumElements();
651 assert(NumElts <= Elements.size() && "Not enough room for the components");
652
654 for (auto *IEI = dyn_cast<InsertElementInst>(Vec); IEI;
655 IEI = dyn_cast<InsertElementInst>(IEI->getOperand(0))) {
656 if (!isa<ConstantInt>(IEI->getOperand(2)))
657 break; // This break should never happen below SM6.9.
658 Chain.push_back(IEI);
659 }
660
661 // Replay element insertion from the innermost first, so that a repeated
662 // index ends up holding the live value.
663 while (!Chain.empty()) {
664 InsertElementInst *IEI = Chain.pop_back_val();
665 uint64_t IndexVal = cast<ConstantInt>(IEI->getOperand(2))->getZExtValue();
666 if (IndexVal < NumElts)
667 Elements[IndexVal] = IEI->getOperand(1);
668 }
669 }
670
671 // Copies `Src` into `Args` starting at `ArgIdx`. If `Src` is a vector, its
672 // elements are placed in consecutive slots; otherwise `Src` is stored
673 // directly. At most `MaxElements` elements are expected.
674 static void extractElementsIntoArgs(IRBuilder<> &IRB,
676 unsigned ArgIdx, Value *Src,
677 unsigned MaxElements) {
678 auto *VecTy = dyn_cast<FixedVectorType>(Src->getType());
679 if (!VecTy) {
680 Args[ArgIdx] = Src;
681 return;
682 }
683
684 unsigned Count = VecTy->getNumElements();
685 assert(Count <= MaxElements && "Too many elements for the arg list");
686
687 SmallVector<Value *, 4> Elements(Count, nullptr);
688 collectInsertedElements(Src, Elements);
689
690 for (unsigned I = 0; I < Count; ++I)
691 Args[ArgIdx + I] = Elements[I]
692 ? Elements[I]
693 : IRB.CreateExtractElement(
694 Src, ConstantInt::get(IRB.getInt32Ty(), I));
695 }
696
697 /// Copy offsets into the argument list at the given index, unless
698 /// the offsets are known to be zero (i.e., a null constant).
699 static void extractNonZeroOffsets(IRBuilder<> &IRB,
701 unsigned ArgIdx, Value *Offsets,
702 unsigned MaxElements) {
703 auto *COff = dyn_cast<Constant>(Offsets);
704 bool OffsetsAreZero = COff && COff->isNullValue();
705 if (!OffsetsAreZero)
706 extractElementsIntoArgs(IRB, Args, ArgIdx, Offsets, MaxElements);
707 }
708
709 [[nodiscard]] bool lowerTextureLoad(Function &F) {
710 IRBuilder<> &IRB = OpBuilder.getIRB();
711 Type *Int32Ty = IRB.getInt32Ty();
712
713 return replaceFunction(F, [&](CallInst *CI) -> Error {
714 IRB.SetInsertPoint(CI);
715
716 SmallVector<WeakTrackingVH, 4> VectorArgs = collectVectorArgs(CI);
717 Value *Handle =
718 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
719 Value *Coords = CI->getArgOperand(1);
720 Value *MipLevel = CI->getArgOperand(2);
721 Value *Offsets = CI->getArgOperand(3);
722
723 // A UAV descriptor binds a single mip slice, so there is no mip to select
724 // in the case of a UAV. Multisampled UAVs are the exception: the slot
725 // carries a sample index and stays live.
726 auto *HandleTy = cast<TargetExtType>(CI->getArgOperand(0)->getType());
727 dxil::ResourceTypeInfo &RTI = DRTM[HandleTy];
729 if (RTI.isUAV() && Kind != dxil::ResourceKind::Texture2DMS &&
730 Kind != dxil::ResourceKind::Texture2DMSArray)
731 MipLevel = UndefValue::get(Int32Ty);
732
733 Type *OldTy = CI->getType();
734 Type *NewRetTy = OpBuilder.getResRetType(OldTy->getScalarType());
735
736 Value *Undef = UndefValue::get(Int32Ty);
737 std::array<Value *, 8> Args{Handle, MipLevel, Undef, Undef,
739
740 // Copy coordinates and offsets into Args.
741 extractElementsIntoArgs(IRB, Args, 2, Coords, 3);
742 extractNonZeroOffsets(IRB, Args, 5, Offsets, 3);
743
744 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
745 OpCode::TextureLoad, Args, CI->getName(), NewRetTy);
746 if (Error E = OpCall.takeError())
747 return E;
748 if (Error E = replaceResRetUses(CI, *OpCall, /*HasCheckBit=*/false))
749 return E;
750
751 eraseDeadInsertElementChains(VectorArgs);
752
753 return Error::success();
754 });
755 }
756
757 /// Common helper for lowering sample operations (SampleBias, SampleGrad,
758 /// etc.) that share the same pattern: extract handle/sampler, unpack
759 /// coordinates and offsets, build the DXIL arg list, and replace uses.
760 [[nodiscard]] bool lowerSampleOp(
761 Function &F, OpCode Op, unsigned CoordsIdx, unsigned OffsetsIdx,
762 llvm::function_ref<void(IRBuilder<> &, CallInst *,
763 SmallVectorImpl<Value *> &)> EmitExtraArgs) {
764 IRBuilder<> &IRB = OpBuilder.getIRB();
765 return replaceFunction(F, [&](CallInst *CI) -> Error {
766 IRB.SetInsertPoint(CI);
767
768 SmallVector<WeakTrackingVH, 4> VectorArgs = collectVectorArgs(CI);
769 Value *Handle =
770 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
771 Value *Sampler =
772 createTmpHandleCast(CI->getArgOperand(1), OpBuilder.getHandleType());
773 Value *Coords = CI->getArgOperand(CoordsIdx);
774 Value *Offsets = CI->getArgOperand(OffsetsIdx);
775
776 Type *OldTy = CI->getType();
777 Type *NewRetTy = OpBuilder.getResRetType(OldTy->getScalarType());
778
779 Value *UndefF = UndefValue::get(IRB.getFloatTy());
780 Value *UndefI = UndefValue::get(IRB.getInt32Ty());
781 // Common prefix: Handle, Sampler, Coord0..3, Offset0..2
782 SmallVector<Value *, 17> Args{Handle, Sampler, UndefF, UndefF, UndefF,
783 UndefF, UndefI, UndefI, UndefI};
784
785 // Copy coordinates and offsets into Args.
786 extractElementsIntoArgs(IRB, Args, 2, Coords, 4);
787 extractNonZeroOffsets(IRB, Args, 6, Offsets, 3);
788
789 // Emit op-specific trailing arguments (e.g. Bias+Clamp, DDX+DDY+Clamp).
790 EmitExtraArgs(IRB, CI, Args);
791
792 Expected<CallInst *> OpCall =
793 OpBuilder.tryCreateOp(Op, Args, CI->getName(), NewRetTy);
794 if (Error E = OpCall.takeError())
795 return E;
796 if (Error E = replaceResRetUses(CI, *OpCall, /*HasCheckBit=*/false))
797 return E;
798
799 eraseDeadInsertElementChains(VectorArgs);
800
801 return Error::success();
802 });
803 }
804
805 [[nodiscard]] bool lowerSample(Function &F, bool HasClamp) {
806 return lowerSampleOp(F, OpCode::Sample, /*CoordsIdx=*/2, /*OffsetsIdx=*/3,
807 [HasClamp](IRBuilder<> &IRB, CallInst *CI,
808 SmallVectorImpl<Value *> &Args) {
809 // Clamp
810 Args.push_back(
811 HasClamp ? CI->getArgOperand(4)
812 : UndefValue::get(IRB.getFloatTy()));
813 });
814 }
815
816 [[nodiscard]] bool lowerSampleBias(Function &F, bool HasClamp) {
817 return lowerSampleOp(
818 F, OpCode::SampleBias, /*CoordsIdx=*/2, /*OffsetsIdx=*/4,
819 [HasClamp](IRBuilder<> &IRB, CallInst *CI,
820 SmallVectorImpl<Value *> &Args) {
821 // Bias is operand 3.
822 Args.push_back(CI->getArgOperand(3));
823 // Clamp
824 Args.push_back(HasClamp ? CI->getArgOperand(5)
825 : UndefValue::get(IRB.getFloatTy()));
826 });
827 }
828
829 [[nodiscard]] bool lowerSampleLevel(Function &F) {
830 return lowerSampleOp(
831 F, OpCode::SampleLevel, /*CoordsIdx=*/2, /*OffsetsIdx=*/4,
832 [](IRBuilder<> &, CallInst *CI, SmallVectorImpl<Value *> &Args) {
833 // LOD is operand 3.
834 Args.push_back(CI->getArgOperand(3));
835 });
836 }
837
838 [[nodiscard]] bool lowerSampleGrad(Function &F, bool HasClamp) {
839 return lowerSampleOp(
840 F, OpCode::SampleGrad, /*CoordsIdx=*/2, /*OffsetsIdx=*/5,
841 [HasClamp](IRBuilder<> &IRB, CallInst *CI,
842 SmallVectorImpl<Value *> &Args) {
843 Value *DDX = CI->getArgOperand(3);
844 Value *DDY = CI->getArgOperand(4);
845 Value *UndefF = UndefValue::get(IRB.getFloatTy());
846 // DDX0..2
847 size_t DDXStart = Args.size();
848 Args.append(3, UndefF);
849 extractElementsIntoArgs(IRB, Args, DDXStart, DDX, 3);
850 // DDY0..2
851 size_t DDYStart = Args.size();
852 Args.append(3, UndefF);
853 extractElementsIntoArgs(IRB, Args, DDYStart, DDY, 3);
854 // Clamp
855 Args.push_back(HasClamp ? CI->getArgOperand(6) : UndefF);
856 });
857 }
858
859 [[nodiscard]] bool lowerRawBufferLoad(Function &F) {
860 const DataLayout &DL = F.getDataLayout();
861 IRBuilder<> &IRB = OpBuilder.getIRB();
862 Type *Int8Ty = IRB.getInt8Ty();
863 Type *Int32Ty = IRB.getInt32Ty();
864
865 return replaceFunction(F, [&](CallInst *CI) -> Error {
866 IRB.SetInsertPoint(CI);
867
868 Type *OldTy = cast<StructType>(CI->getType())->getElementType(0);
869 Type *ScalarTy = OldTy->getScalarType();
870 Type *NewRetTy = OpBuilder.getResRetType(ScalarTy);
871
872 Value *Handle =
873 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
874 Value *Index0 = CI->getArgOperand(1);
875 Value *Index1 = CI->getArgOperand(2);
876 uint64_t NumElements =
877 DL.getTypeSizeInBits(OldTy) / DL.getTypeSizeInBits(ScalarTy);
878 Value *Mask = ConstantInt::get(Int8Ty, ~(~0U << NumElements));
879 Value *Align =
880 ConstantInt::get(Int32Ty, DL.getPrefTypeAlign(ScalarTy).value());
881
882 if (Error E = validateRawBufferElementIndex(CI->getOperand(0), Index1))
883 return E;
884 if (isa<PoisonValue>(Index1))
885 Index1 = UndefValue::get(Index1->getType());
886
887 Expected<CallInst *> OpCall =
888 MMDI.DXILVersion >= VersionTuple(1, 2)
889 ? OpBuilder.tryCreateOp(OpCode::RawBufferLoad,
890 {Handle, Index0, Index1, Mask, Align},
891 CI->getName(), NewRetTy)
892 : OpBuilder.tryCreateOp(OpCode::BufferLoad,
893 {Handle, Index0, Index1}, CI->getName(),
894 NewRetTy);
895 if (Error E = OpCall.takeError())
896 return E;
897 if (Error E = replaceResRetUses(CI, *OpCall, /*HasCheckBit=*/true))
898 return E;
899
900 return Error::success();
901 });
902 }
903
904 [[nodiscard]] bool lowerCBufferLoad(Function &F) {
905 IRBuilder<> &IRB = OpBuilder.getIRB();
906
907 return replaceFunction(F, [&](CallInst *CI) -> Error {
908 IRB.SetInsertPoint(CI);
909
910 Type *OldTy = cast<StructType>(CI->getType())->getElementType(0);
911 Type *ScalarTy = OldTy->getScalarType();
912 Type *NewRetTy = OpBuilder.getCBufRetType(ScalarTy);
913
914 Value *Handle =
915 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
916 Value *Index = CI->getArgOperand(1);
917
918 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
919 OpCode::CBufferLoadLegacy, {Handle, Index}, CI->getName(), NewRetTy);
920 if (Error E = OpCall.takeError())
921 return E;
922 if (Error E = replaceNamedStructUses(CI, *OpCall))
923 return E;
924
925 CI->eraseFromParent();
926 return Error::success();
927 });
928 }
929
930 [[nodiscard]] bool lowerUpdateCounter(Function &F) {
931 IRBuilder<> &IRB = OpBuilder.getIRB();
932 Type *Int32Ty = IRB.getInt32Ty();
933
934 return replaceFunction(F, [&](CallInst *CI) -> Error {
935 IRB.SetInsertPoint(CI);
936 Value *Handle =
937 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
938 Value *Op1 = CI->getArgOperand(1);
939
940 std::array<Value *, 2> Args{Handle, Op1};
941
942 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
943 OpCode::UpdateCounter, Args, CI->getName(), Int32Ty);
944
945 if (Error E = OpCall.takeError())
946 return E;
947
948 CI->replaceAllUsesWith(*OpCall);
949 CI->eraseFromParent();
950 return Error::success();
951 });
952 }
953
954 [[nodiscard]] bool lowerGetDimensionsX(Function &F) {
955 IRBuilder<> &IRB = OpBuilder.getIRB();
956 Type *Int32Ty = IRB.getInt32Ty();
957
958 return replaceFunction(F, [&](CallInst *CI) -> Error {
959 IRB.SetInsertPoint(CI);
960 Value *Handle =
961 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
962 Value *Undef = UndefValue::get(Int32Ty);
963
964 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
965 OpCode::GetDimensions, {Handle, Undef}, CI->getName(), Int32Ty);
966 if (Error E = OpCall.takeError())
967 return E;
968 Value *Dim = IRB.CreateExtractValue(*OpCall, 0);
969
970 CI->replaceAllUsesWith(Dim);
971 CI->eraseFromParent();
972 return Error::success();
973 });
974 }
975
976 [[nodiscard]] bool lowerGetPointer(Function &F) {
977 // These should have already been handled in DXILResourceAccess, so we can
978 // just clean up the dead prototype.
979 assert(F.user_empty() && "getpointer operations should have been removed");
980 F.eraseFromParent();
981 return false;
982 }
983
984 /// Splits the value operand of a resource store into its (at most four)
985 /// scalar components. Slots beyond the length of `Data` are filled with
986 /// `undef` when `FillWithUndef` is set (raw and structured buffers), or with
987 /// the first component otherwise (typed UAVs, which must write all four
988 /// components - repeating the first one matches DXC).
989 static std::array<Value *, 4> splitStoreData(IRBuilder<> &IRB, Value *Data,
990 uint64_t NumElements,
991 bool FillWithUndef) {
992 std::array<Value *, 4> DataElements{nullptr, nullptr, nullptr, nullptr};
993 extractElementsIntoArgs(IRB, DataElements, 0, Data, 4);
994
995 // For any elements beyond the length of the vector, we should fill it up
996 // with undef - however, for typed UAVs we repeat the first element to
997 // match DXC.
998 for (uint64_t I = NumElements, E = 4; I < E; ++I)
999 if (DataElements[I] == nullptr)
1000 DataElements[I] =
1001 FillWithUndef ? UndefValue::get(Data->getType()->getScalarType())
1002 : DataElements[0];
1003
1004 return DataElements;
1005 }
1006
1007 /// Erase the chain of `insertelement`s that only existed to build up a vector
1008 /// operand of an intrinsic we've just replaced.
1009 static void eraseDeadInsertElementChain(Value *Data) {
1010 auto *IEI = dyn_cast<InsertElementInst>(Data);
1011 while (IEI && IEI->use_empty()) {
1012 InsertElementInst *Tmp = IEI;
1014 Tmp->eraseFromParent();
1015 }
1016 }
1017
1018 [[nodiscard]] bool lowerBufferStore(Function &F, bool IsRaw) {
1019 const DataLayout &DL = F.getDataLayout();
1020 IRBuilder<> &IRB = OpBuilder.getIRB();
1021 Type *Int8Ty = IRB.getInt8Ty();
1022 Type *Int32Ty = IRB.getInt32Ty();
1023
1024 return replaceFunction(F, [&](CallInst *CI) -> Error {
1025 IRB.SetInsertPoint(CI);
1026
1027 Value *Handle =
1028 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
1029 Value *Index0 = CI->getArgOperand(1);
1030 Value *Index1 = IsRaw ? CI->getArgOperand(2) : UndefValue::get(Int32Ty);
1031
1032 if (IsRaw) {
1033 if (Error E = validateRawBufferElementIndex(CI->getOperand(0), Index1))
1034 return E;
1035 if (isa<PoisonValue>(Index1))
1036 Index1 = UndefValue::get(Index1->getType());
1037 }
1038
1039 Value *Data = CI->getArgOperand(IsRaw ? 3 : 2);
1040 Type *DataTy = Data->getType();
1041 Type *ScalarTy = DataTy->getScalarType();
1042
1043 uint64_t NumElements =
1044 DL.getTypeSizeInBits(DataTy) / DL.getTypeSizeInBits(ScalarTy);
1045 Value *Mask = ConstantInt::get(Int8Ty, IsRaw ? ~(~0U << NumElements)
1047
1048 // TODO: check that we only have vector or scalar...
1049 if (NumElements > 4)
1051 "Buffer store data must have at most 4 elements",
1053
1054 std::array<Value *, 4> DataElements =
1055 splitStoreData(IRB, Data, NumElements, /*FillWithUndef=*/IsRaw);
1056
1057 dxil::OpCode Op = OpCode::BufferStore;
1059 Handle, Index0, Index1, DataElements[0],
1060 DataElements[1], DataElements[2], DataElements[3], Mask};
1061 if (IsRaw && MMDI.DXILVersion >= VersionTuple(1, 2)) {
1062 Op = OpCode::RawBufferStore;
1063 // RawBufferStore requires the alignment
1064 Args.push_back(
1065 ConstantInt::get(Int32Ty, DL.getPrefTypeAlign(ScalarTy).value()));
1066 }
1067 Expected<CallInst *> OpCall =
1068 OpBuilder.tryCreateOp(Op, Args, CI->getName());
1069 if (Error E = OpCall.takeError())
1070 return E;
1071
1072 CI->eraseFromParent();
1073 eraseDeadInsertElementChain(Data);
1074
1075 return Error::success();
1076 });
1077 }
1078
1079 /// Snapshot the vector-typed arguments of `CI` so their `insertelement`
1080 /// chains can be cleaned up once `CI` has been replaced. The handles are weak
1081 /// because two arguments can share an `insertelement` chain.
1082 static SmallVector<WeakTrackingVH, 4> collectVectorArgs(CallInst *CI) {
1084 for (Value *Arg : CI->args())
1085 if (isa<FixedVectorType>(Arg->getType()))
1086 Vectors.emplace_back(Arg);
1087 return Vectors;
1088 }
1089
1090 static void eraseDeadInsertElementChains(ArrayRef<WeakTrackingVH> Vectors) {
1091 for (const WeakTrackingVH &VH : Vectors)
1092 if (Value *V = VH)
1093 eraseDeadInsertElementChain(V);
1094 }
1095
1096 [[nodiscard]] bool lowerTextureStore(Function &F) {
1097 const DataLayout &DL = F.getDataLayout();
1098 IRBuilder<> &IRB = OpBuilder.getIRB();
1099 Type *Int8Ty = IRB.getInt8Ty();
1100 Type *Int32Ty = IRB.getInt32Ty();
1101
1102 return replaceFunction(F, [&](CallInst *CI) -> Error {
1103 IRB.SetInsertPoint(CI);
1104
1105 SmallVector<WeakTrackingVH, 4> VectorArgs = collectVectorArgs(CI);
1106 Value *Handle =
1107 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
1108 Value *Coords = CI->getArgOperand(1);
1109 Value *Data = CI->getArgOperand(2);
1110
1111 Type *DataTy = Data->getType();
1112 Type *ScalarTy = DataTy->getScalarType();
1113 uint64_t NumElements =
1114 DL.getTypeSizeInBits(DataTy) / DL.getTypeSizeInBits(ScalarTy);
1115 if (NumElements > 4)
1117 "Texture store data must have at most 4 elements",
1119
1120 Value *Mask = ConstantInt::get(Int8Ty, TypedUAVStoreWriteMask);
1121 std::array<Value *, 4> DataElements =
1122 splitStoreData(IRB, Data, NumElements, /*FillWithUndef=*/false);
1123
1124 Value *Undef = UndefValue::get(Int32Ty);
1125 std::array<Value *, 9> Args{
1126 Handle, Undef, Undef,
1127 Undef, DataElements[0], DataElements[1],
1128 DataElements[2], DataElements[3], Mask};
1129
1130 // Copy the coordinates into Args.
1131 extractElementsIntoArgs(IRB, Args, 1, Coords, 3);
1132
1133 Expected<CallInst *> OpCall =
1134 OpBuilder.tryCreateOp(OpCode::TextureStore, Args, CI->getName());
1135 if (Error E = OpCall.takeError())
1136 return E;
1137
1138 CI->eraseFromParent();
1139 eraseDeadInsertElementChains(VectorArgs);
1140
1141 return Error::success();
1142 });
1143 }
1144
1145 [[nodiscard]] bool lowerResourceAtomicBinOp(Function &F) {
1146 IRBuilder<> &IRB = OpBuilder.getIRB();
1147
1148 return replaceFunction(F, [&](CallInst *CI) -> Error {
1149 IRB.SetInsertPoint(CI);
1150
1151 // Cast the target-extension typed handle to `%dx.types.Handle`, tracked
1152 // via CleanupCasts so the pair is reconciled by `cleanupHandleCasts`.
1153 Value *Handle =
1154 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
1155 Value *BinOp = CI->getArgOperand(1);
1156 Value *Coord0 = CI->getArgOperand(2);
1157 Value *Coord1 = CI->getArgOperand(3);
1158 Value *Coord2 = CI->getArgOperand(4);
1159 Value *NewValue = CI->getArgOperand(5);
1160
1161 std::array<Value *, 6> Args{Handle, BinOp, Coord0,
1162 Coord1, Coord2, NewValue};
1163 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
1164 dxil::OpCode::AtomicBinOp, Args, CI->getName(), CI->getType());
1165 if (Error E = OpCall.takeError()) {
1166 // Preserve the DXIL op error text but attach it as a
1167 // DiagnosticInfoUnsupported so we don't crash with a dangling call.
1168 std::string Message(toString(std::move(E)));
1169 CI->getContext().diagnose(DiagnosticInfoUnsupported(
1170 *CI->getFunction(), Message, CI->getDebugLoc()));
1172 CI->eraseFromParent();
1173 return Error::success();
1174 }
1175
1176 CI->replaceAllUsesWith(*OpCall);
1177 CI->eraseFromParent();
1178 return Error::success();
1179 });
1180 }
1181
1182 [[nodiscard]] bool lowerResourceAtomicCompareExchange(Function &F) {
1183 IRBuilder<> &IRB = OpBuilder.getIRB();
1184
1185 return replaceFunction(F, [&](CallInst *CI) -> Error {
1186 IRB.SetInsertPoint(CI);
1187
1188 // Cast the target-extension typed handle to `%dx.types.Handle`, tracked
1189 // via CleanupCasts so the pair is reconciled by `cleanupHandleCasts`.
1190 Value *Handle =
1191 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
1192 Value *Coord0 = CI->getArgOperand(1);
1193 Value *Coord1 = CI->getArgOperand(2);
1194 Value *Coord2 = CI->getArgOperand(3);
1195 Value *CompareValue = CI->getArgOperand(4);
1196 Value *NewValue = CI->getArgOperand(5);
1197
1198 std::array<Value *, 6> Args{Handle, Coord0, Coord1,
1199 Coord2, CompareValue, NewValue};
1200 Expected<CallInst *> OpCall =
1201 OpBuilder.tryCreateOp(dxil::OpCode::AtomicCompareExchange, Args,
1202 CI->getName(), CI->getType());
1203 if (Error E = OpCall.takeError()) {
1204 // Preserve the DXIL op error text but attach it as a
1205 // DiagnosticInfoUnsupported so we don't crash with a dangling call.
1206 std::string Message(toString(std::move(E)));
1207 CI->getContext().diagnose(DiagnosticInfoUnsupported(
1208 *CI->getFunction(), Message, CI->getDebugLoc()));
1210 CI->eraseFromParent();
1211 return Error::success();
1212 }
1213
1214 CI->replaceAllUsesWith(*OpCall);
1215 CI->eraseFromParent();
1216 return Error::success();
1217 });
1218 }
1219
1220 [[nodiscard]] bool lowerCtpopToCountBits(Function &F) {
1221 IRBuilder<> &IRB = OpBuilder.getIRB();
1222 Type *Int32Ty = IRB.getInt32Ty();
1223
1224 return replaceFunction(F, [&](CallInst *CI) -> Error {
1225 IRB.SetInsertPoint(CI);
1227 Args.append(CI->arg_begin(), CI->arg_end());
1228
1229 Type *RetTy = Int32Ty;
1230 Type *FRT = F.getReturnType();
1231 if (const auto *VT = dyn_cast<VectorType>(FRT))
1232 RetTy = VectorType::get(RetTy, VT);
1233
1234 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
1235 dxil::OpCode::CountBits, Args, CI->getName(), RetTy);
1236 if (Error E = OpCall.takeError())
1237 return E;
1238
1239 // If the result type is 32 bits we can do a direct replacement.
1240 if (FRT->isIntOrIntVectorTy(32)) {
1241 CI->replaceAllUsesWith(*OpCall);
1242 CI->eraseFromParent();
1243 return Error::success();
1244 }
1245
1246 unsigned CastOp;
1247 unsigned CastOp2;
1248 if (FRT->isIntOrIntVectorTy(16)) {
1249 CastOp = Instruction::ZExt;
1250 CastOp2 = Instruction::SExt;
1251 } else { // must be 64 bits
1252 assert(FRT->isIntOrIntVectorTy(64) &&
1253 "Currently only lowering 16, 32, or 64 bit ctpop to CountBits \
1254 is supported.");
1255 CastOp = Instruction::Trunc;
1256 CastOp2 = Instruction::Trunc;
1257 }
1258
1259 // It is correct to replace the ctpop with the dxil op and
1260 // remove all casts to i32
1261 bool NeedsCast = false;
1262 for (User *User : make_early_inc_range(CI->users())) {
1264 if (I && (I->getOpcode() == CastOp || I->getOpcode() == CastOp2) &&
1265 I->getType() == RetTy) {
1266 I->replaceAllUsesWith(*OpCall);
1267 I->eraseFromParent();
1268 } else
1269 NeedsCast = true;
1270 }
1271
1272 // It is correct to replace a ctpop with the dxil op and
1273 // a cast from i32 to the return type of the ctpop
1274 // the cast is emitted here if there is a non-cast to i32
1275 // instr which uses the ctpop
1276 if (NeedsCast) {
1277 Value *Cast =
1278 IRB.CreateZExtOrTrunc(*OpCall, F.getReturnType(), "ctpop.cast");
1279 CI->replaceAllUsesWith(Cast);
1280 }
1281
1282 CI->eraseFromParent();
1283 return Error::success();
1284 });
1285 }
1286
1287 [[nodiscard]] bool lowerLifetimeIntrinsic(Function &F) {
1288 IRBuilder<> &IRB = OpBuilder.getIRB();
1289 return replaceFunction(F, [&](CallInst *CI) -> Error {
1290 IRB.SetInsertPoint(CI);
1291 Value *Ptr = CI->getArgOperand(0);
1292 assert(Ptr->getType()->isPointerTy() &&
1293 "Expected operand of lifetime intrinsic to be a pointer");
1294
1295 auto ZeroOrUndef = [&](Type *Ty) {
1296 return MMDI.ValidatorVersion < VersionTuple(1, 6)
1298 : UndefValue::get(Ty);
1299 };
1300
1301 Value *Val = nullptr;
1302 if (auto *GV = dyn_cast<GlobalVariable>(Ptr)) {
1303 if (GV->hasInitializer() || GV->isExternallyInitialized())
1304 return Error::success();
1305 Val = ZeroOrUndef(GV->getValueType());
1306 } else if (auto *AI = dyn_cast<AllocaInst>(Ptr))
1307 Val = ZeroOrUndef(AI->getAllocatedType());
1308
1309 assert(Val && "Expected operand of lifetime intrinsic to be a global "
1310 "variable or alloca instruction");
1311 IRB.CreateStore(Val, Ptr, false);
1312
1313 CI->eraseFromParent();
1314 return Error::success();
1315 });
1316 }
1317
1318 [[nodiscard]] bool lowerIsFPClass(Function &F) {
1319 IRBuilder<> &IRB = OpBuilder.getIRB();
1320 Type *RetTy = IRB.getInt1Ty();
1321
1322 return replaceFunction(F, [&](CallInst *CI) -> Error {
1323 IRB.SetInsertPoint(CI);
1325 Value *Fl = CI->getArgOperand(0);
1326 Args.push_back(Fl);
1327
1329 Value *T = CI->getArgOperand(1);
1330 auto *TCI = dyn_cast<ConstantInt>(T);
1331 switch (TCI->getZExtValue()) {
1332 case FPClassTest::fcInf:
1333 OpCode = dxil::OpCode::IsInf;
1334 break;
1335 case FPClassTest::fcNan:
1336 OpCode = dxil::OpCode::IsNaN;
1337 break;
1338 case FPClassTest::fcNormal:
1339 OpCode = dxil::OpCode::IsNormal;
1340 break;
1341 case FPClassTest::fcFinite:
1342 OpCode = dxil::OpCode::IsFinite;
1343 break;
1344 default:
1345 SmallString<128> Msg =
1346 formatv("Unsupported FPClassTest {0} for DXIL Op Lowering",
1347 TCI->getZExtValue());
1349 }
1350
1351 Expected<CallInst *> OpCall =
1352 OpBuilder.tryCreateOp(OpCode, Args, CI->getName(), RetTy);
1353 if (Error E = OpCall.takeError())
1354 return E;
1355
1356 CI->replaceAllUsesWith(*OpCall);
1357 CI->eraseFromParent();
1358 return Error::success();
1359 });
1360 }
1361
1362 bool lowerIntrinsics() {
1363 bool Updated = false;
1364 bool HasErrors = false;
1365
1366 for (Function &F : make_early_inc_range(M.functions())) {
1367 if (!F.isDeclaration())
1368 continue;
1369 Intrinsic::ID ID = F.getIntrinsicID();
1370 switch (ID) {
1371 // NOTE: Skip dx_resource_casthandle here. They are
1372 // resolved after this loop in cleanupHandleCasts.
1373 case Intrinsic::dx_resource_casthandle:
1374 // NOTE: llvm.dbg.value is supported as is in DXIL.
1375 case Intrinsic::dbg_value:
1377 if (F.use_empty())
1378 F.eraseFromParent();
1379 continue;
1380 default:
1381 if (F.use_empty())
1382 F.eraseFromParent();
1383 else {
1384 SmallString<128> Msg = formatv(
1385 "Unsupported intrinsic {0} for DXIL lowering", F.getName());
1386 M.getContext().emitError(Msg);
1387 HasErrors |= true;
1388 }
1389 break;
1390
1391#define DXIL_OP_INTRINSIC(OpCode, Intrin, ...) \
1392 case Intrin: \
1393 HasErrors |= replaceFunctionWithOp( \
1394 F, OpCode, ArrayRef<IntrinArgSelect>{__VA_ARGS__}); \
1395 break;
1396#include "DXILOperation.inc"
1397 case Intrinsic::dx_resource_handlefrombinding:
1398 HasErrors |= lowerHandleFromBinding(F);
1399 break;
1400 case Intrinsic::dx_resource_handlefromheap:
1401 HasErrors |= lowerHandleFromHeap(F);
1402 break;
1403 case Intrinsic::dx_resource_getbasepointer:
1404 case Intrinsic::dx_resource_getpointer:
1405 HasErrors |= lowerGetPointer(F);
1406 break;
1407 case Intrinsic::dx_resource_nonuniformindex:
1408 assert(!CleanupNURI &&
1409 "overloaded llvm.dx.resource.nonuniformindex intrinsics?");
1410 CleanupNURI = &F;
1411 break;
1412 case Intrinsic::dx_resource_load_typedbuffer:
1413 HasErrors |= lowerTypedBufferLoad(F, /*HasCheckBit=*/true);
1414 break;
1415 case Intrinsic::dx_resource_load_level:
1416 HasErrors |= lowerTextureLoad(F);
1417 break;
1418 case Intrinsic::dx_resource_sample:
1419 HasErrors |= lowerSample(F, /*HasClamp=*/false);
1420 break;
1421 case Intrinsic::dx_resource_sample_clamp:
1422 HasErrors |= lowerSample(F, /*HasClamp=*/true);
1423 break;
1424 case Intrinsic::dx_resource_samplebias:
1425 HasErrors |= lowerSampleBias(F, /*HasClamp=*/false);
1426 break;
1427 case Intrinsic::dx_resource_samplebias_clamp:
1428 HasErrors |= lowerSampleBias(F, /*HasClamp=*/true);
1429 break;
1430 case Intrinsic::dx_resource_samplelevel:
1431 HasErrors |= lowerSampleLevel(F);
1432 break;
1433 case Intrinsic::dx_resource_samplegrad:
1434 HasErrors |= lowerSampleGrad(F, /*HasClamp=*/false);
1435 break;
1436 case Intrinsic::dx_resource_samplegrad_clamp:
1437 HasErrors |= lowerSampleGrad(F, /*HasClamp=*/true);
1438 break;
1439 case Intrinsic::dx_resource_store_typedbuffer:
1440 HasErrors |= lowerBufferStore(F, /*IsRaw=*/false);
1441 break;
1442 case Intrinsic::dx_resource_store_texture:
1443 HasErrors |= lowerTextureStore(F);
1444 break;
1445 case Intrinsic::dx_resource_load_rawbuffer:
1446 HasErrors |= lowerRawBufferLoad(F);
1447 break;
1448 case Intrinsic::dx_resource_store_rawbuffer:
1449 HasErrors |= lowerBufferStore(F, /*IsRaw=*/true);
1450 break;
1451 case Intrinsic::dx_resource_load_cbufferrow_2:
1452 case Intrinsic::dx_resource_load_cbufferrow_4:
1453 case Intrinsic::dx_resource_load_cbufferrow_8:
1454 HasErrors |= lowerCBufferLoad(F);
1455 break;
1456 case Intrinsic::dx_resource_updatecounter:
1457 HasErrors |= lowerUpdateCounter(F);
1458 break;
1459 case Intrinsic::dx_resource_atomic_binop:
1460 HasErrors |= lowerResourceAtomicBinOp(F);
1461 break;
1462 case Intrinsic::dx_resource_atomic_compare_exchange:
1463 HasErrors |= lowerResourceAtomicCompareExchange(F);
1464 break;
1465 case Intrinsic::dx_resource_getdimensions_x:
1466 HasErrors |= lowerGetDimensionsX(F);
1467 break;
1468 case Intrinsic::ctpop:
1469 HasErrors |= lowerCtpopToCountBits(F);
1470 break;
1471 case Intrinsic::lifetime_start:
1472 case Intrinsic::lifetime_end:
1473 if (F.use_empty())
1474 F.eraseFromParent();
1475 else {
1476 if (MMDI.DXILVersion < VersionTuple(1, 6))
1477 HasErrors |= lowerLifetimeIntrinsic(F);
1478 else
1479 continue;
1480 }
1481 break;
1482 case Intrinsic::is_fpclass:
1483 HasErrors |= lowerIsFPClass(F);
1484 break;
1485 }
1486 Updated = true;
1487 }
1488 if (Updated && !HasErrors) {
1489 cleanupHandleCasts();
1490 cleanupNonUniformResourceIndexCalls();
1491 }
1492
1493 return Updated;
1494 }
1495};
1496} // namespace
1497
1499 DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(M);
1500 DXILResourceTypeMap &DRTM = MAM.getResult<DXILResourceTypeAnalysis>(M);
1501 const ModuleMetadataInfo MMDI = MAM.getResult<DXILMetadataAnalysis>(M);
1502
1503 const bool MadeChanges = OpLowerer(M, DRM, DRTM, MMDI).lowerIntrinsics();
1504 if (!MadeChanges)
1505 return PreservedAnalyses::all();
1511 return PA;
1512}
1513
1514namespace {
1515class DXILOpLoweringLegacy : public ModulePass {
1516public:
1517 bool runOnModule(Module &M) override {
1518 DXILResourceMap &DRM =
1519 getAnalysis<DXILResourceWrapperPass>().getResourceMap();
1520 DXILResourceTypeMap &DRTM =
1521 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
1522 const ModuleMetadataInfo MMDI =
1523 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
1524
1525 return OpLowerer(M, DRM, DRTM, MMDI).lowerIntrinsics();
1526 }
1527 StringRef getPassName() const override { return "DXIL Op Lowering"; }
1528 DXILOpLoweringLegacy() : ModulePass(ID) {}
1529
1530 static char ID; // Pass identification.
1531 void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
1532 AU.addRequired<DXILResourceTypeWrapperPass>();
1533 AU.addRequired<DXILResourceWrapperPass>();
1534 AU.addRequired<DXILMetadataAnalysisWrapperPass>();
1535 AU.addPreserved<DXILResourceWrapperPass>();
1536 AU.addPreserved<DXILMetadataAnalysisWrapperPass>();
1537 AU.addPreserved<ShaderFlagsAnalysisWrapper>();
1538 AU.addPreserved<RootSignatureAnalysisWrapper>();
1539 }
1540};
1541char DXILOpLoweringLegacy::ID = 0;
1542} // end anonymous namespace
1543
1544INITIALIZE_PASS_BEGIN(DXILOpLoweringLegacy, DEBUG_TYPE, "DXIL Op Lowering",
1545 false, false)
1548INITIALIZE_PASS_END(DXILOpLoweringLegacy, DEBUG_TYPE, "DXIL Op Lowering", false,
1549 false)
1550
1552 return new DXILOpLoweringLegacy();
1553}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
ReachingDefInfo InstSet & ToRemove
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static constexpr uint8_t TypedUAVStoreWriteMask
Write mask covering all four components of a UAV element.
DXIL Resource Implicit Binding
#define DEBUG_TYPE
Hexagon Common GEP
Module.h This file contains the declarations for the Module class.
This header defines various interfaces for pass management in LLVM.
This defines the Use class.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
#define T
ModuleAnalysisManager MAM
if(PassOpts->AAPipeline)
#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
const char * Msg
static unsigned getFastMathFlags(const MachineInstr &I, const SPIRVSubtarget &ST)
This file defines the SmallVector class.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
size_t size() const
Get the array size.
Definition ArrayRef.h:141
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
User::op_iterator arg_begin()
Return the iterator pointing to the beginning of the argument list.
Value * getArgOperand(unsigned i) const
User::op_iterator arg_end()
Return the iterator pointing to the end of the argument list.
LLVM_ABI Intrinsic::ID getIntrinsicID() const
Returns the intrinsic ID of the intrinsic called or Intrinsic::not_intrinsic if the called function i...
iterator_range< User::op_iterator > args()
Iteration adapter for range-for loops.
This class represents a function call, abstracting a target machine's calling convention.
static ConstantAsMetadata * get(Constant *C)
Definition Metadata.h:548
static LLVM_ABI ConstantInt * getBool(LLVMContext &Context, bool V)
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM)
Diagnostic information for unsupported feature in backend.
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
Intrinsic::ID getIntrinsicID() const LLVM_READONLY
getIntrinsicID - This method returns the ID number of the specified function, or Intrinsic::not_intri...
Definition Function.h:247
static GetElementPtrInst * CreateInBounds(Type *PointeeType, Value *Ptr, ArrayRef< Value * > IdxList, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
Create an "inbounds" getelementptr.
Value * CreateInsertElement(Type *VecTy, Value *NewElt, Value *Idx, const Twine &Name="")
Definition IRBuilder.h:2661
AllocaInst * CreateAlloca(Type *Ty, unsigned AddrSpace, Value *ArraySize=nullptr, const Twine &Name="")
Definition IRBuilder.h:1871
IntegerType * getInt1Ty()
Fetch the type representing a single bit.
Definition IRBuilder.h:498
Value * CreateZExtOrTrunc(Value *V, Type *DestTy, const Twine &Name="")
Create a ZExt or Trunc from the integer value V to DestTy.
Definition IRBuilder.h:2131
Value * CreateExtractValue(Value *Agg, ArrayRef< unsigned > Idxs, const Twine &Name="")
Definition IRBuilder.h:2708
BasicBlock::iterator GetInsertPoint() const
Definition IRBuilder.h:176
IntegerType * getInt32Ty()
Fetch the type representing a 32-bit integer.
Definition IRBuilder.h:513
LoadInst * CreateLoad(Type *Ty, Value *Ptr, const char *Name)
Provided to resolve 'CreateLoad(Ty, Ptr, "...")' correctly, instead of converting the string to 'bool...
Definition IRBuilder.h:1898
LLVMContext & getContext() const
Definition IRBuilder.h:177
StoreInst * CreateStore(Value *Val, Value *Ptr, bool isVolatile=false)
Definition IRBuilder.h:1917
Value * CreateAdd(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Definition IRBuilder.h:1409
Type * getFloatTy()
Fetch the type representing a 32-bit floating point value.
Definition IRBuilder.h:541
void SetInsertPoint(BasicBlock *TheBB)
This specifies that created instructions should be appended to the end of the specified block.
Definition IRBuilder.h:181
IntegerType * getInt8Ty()
Fetch the type representing an 8-bit integer.
Definition IRBuilder.h:503
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
LLVM_ABI const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
LLVM_ABI const Function * getFunction() const
Return the function this instruction belongs to.
iterator_range< user_iterator > users()
LLVM_ABI void setMetadata(unsigned KindID, MDNode *Node)
Set the metadata of the specified kind to the specified node.
user_iterator user_begin()
LLVM_ABI void diagnose(const DiagnosticInfo &DI)
Report a message to the currently installed diagnostic handler.
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1579
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
LLVMContext & getContext() const
Get the global data context.
Definition Module.h:332
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
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
reference emplace_back(ArgTypes &&... Args)
iterator erase(const_iterator CI)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
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 isIntOrIntVectorTy() const
Return true if this is an integer type or a vector of integer types.
Definition Type.h:258
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
static LLVM_ABI UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
Value * getOperand(unsigned i) const
Definition User.h:207
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
bool hasOneUse() const
Return true if there is exactly one use of this value.
Definition Value.h:441
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
LLVMContext & getContext() const
All values hold a context through their type.
Definition Value.h:260
bool use_empty() const
Definition Value.h:348
iterator_range< use_iterator > uses()
Definition Value.h:382
bool hasName() const
Definition Value.h:263
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:319
bool user_empty() const
Definition Value.h:391
TargetExtType * getHandleTy() const
LLVM_ABI std::pair< uint32_t, uint32_t > getAnnotateProps(Module &M, dxil::ResourceTypeInfo &RTI) const
const ResourceBinding & getBinding() const
dxil::ResourceClass getResourceClass() const
LLVM_ABI bool isUAV() const
LLVM_ABI bool isSampler() const
dxil::ResourceKind getResourceKind() const
An efficient, type-erasing, non-owning reference to a callable.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
Offsets
Offsets in bytes from the start of the input buffer.
ResourceKind
The kind of resource for an SRV or UAV resource.
Definition DXILABI.h:44
NodeAddr< DefNode * > Def
Definition RDFGraph.h:384
NodeAddr< PhiNode * > Phi
Definition RDFGraph.h:390
NodeAddr< UseNode * > Use
Definition RDFGraph.h:385
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
This is an optimization pass for GlobalISel generic memory operations.
@ Undef
Value of the register doesn't matter.
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).
@ Store
The extracted value is stored (ExtractElement only).
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
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
auto unique(Range &&R, Predicate P)
Definition STLExtras.h:2150
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
decltype(auto) get(const PointerIntPair< PointerTy, IntBits, IntType, PtrTraits, Info > &Pair)
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1652
IRBuilder(LLVMContext &, FolderTy, InserterTy) -> IRBuilder< FolderTy, InserterTy >
constexpr std::underlying_type_t< Enum > to_underlying(Enum E)
Returns underlying integer value of an enum.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
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
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
MutableArrayRef(T &OneElt) -> MutableArrayRef< T >
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
Definition InstrProf.h:145
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >
std::string toString(const APInt &I, unsigned Radix, bool Signed, bool formatAsCLiteral=false, bool UpperCase=true, bool InsertSeparators=false)
ModulePass * createDXILOpLoweringLegacyPass()
Pass to lowering LLVM intrinsic call to DXIL op function call.
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
#define N