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