LLVM 22.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"
20#include "llvm/IR/IRBuilder.h"
21#include "llvm/IR/Instruction.h"
23#include "llvm/IR/Intrinsics.h"
24#include "llvm/IR/IntrinsicsDirectX.h"
25#include "llvm/IR/Module.h"
26#include "llvm/IR/PassManager.h"
28#include "llvm/Pass.h"
31
32#define DEBUG_TYPE "dxil-op-lower"
33
34using namespace llvm;
35using namespace llvm::dxil;
36
37namespace {
38class OpLowerer {
39 Module &M;
40 DXILOpBuilder OpBuilder;
41 DXILResourceMap &DRM;
43 const ModuleMetadataInfo &MMDI;
44 SmallVector<CallInst *> CleanupCasts;
45
46public:
47 OpLowerer(Module &M, DXILResourceMap &DRM, DXILResourceTypeMap &DRTM,
48 const ModuleMetadataInfo &MMDI)
49 : M(M), OpBuilder(M), DRM(DRM), DRTM(DRTM), MMDI(MMDI) {}
50
51 /// Replace every call to \c F using \c ReplaceCall, and then erase \c F. If
52 /// there is an error replacing a call, we emit a diagnostic and return true.
53 [[nodiscard]] bool
54 replaceFunction(Function &F,
55 llvm::function_ref<Error(CallInst *CI)> ReplaceCall) {
56 for (User *U : make_early_inc_range(F.users())) {
57 CallInst *CI = dyn_cast<CallInst>(U);
58 if (!CI)
59 continue;
60
61 if (Error E = ReplaceCall(CI)) {
62 std::string Message(toString(std::move(E)));
63 M.getContext().diagnose(DiagnosticInfoUnsupported(
64 *CI->getFunction(), Message, CI->getDebugLoc()));
65
66 return true;
67 }
68 }
69 if (F.user_empty())
70 F.eraseFromParent();
71 return false;
72 }
73
74 struct IntrinArgSelect {
75 enum class Type {
76#define DXIL_OP_INTRINSIC_ARG_SELECT_TYPE(name) name,
77#include "DXILOperation.inc"
78 };
79 Type Type;
80 int Value;
81 };
82
83 /// Replaces uses of a struct with uses of an equivalent named struct.
84 ///
85 /// DXIL operations that return structs give them well known names, so we need
86 /// to update uses when we switch from an LLVM intrinsic to an op.
87 Error replaceNamedStructUses(CallInst *Intrin, CallInst *DXILOp) {
88 auto *IntrinTy = cast<StructType>(Intrin->getType());
89 auto *DXILOpTy = cast<StructType>(DXILOp->getType());
90 if (!IntrinTy->isLayoutIdentical(DXILOpTy))
91 return make_error<StringError>(
92 "Type mismatch between intrinsic and DXIL op",
94
95 for (Use &U : make_early_inc_range(Intrin->uses()))
96 if (auto *EVI = dyn_cast<ExtractValueInst>(U.getUser()))
97 EVI->setOperand(0, DXILOp);
98 else if (auto *IVI = dyn_cast<InsertValueInst>(U.getUser()))
99 IVI->setOperand(0, DXILOp);
100 else
101 return make_error<StringError>("DXIL ops that return structs may only "
102 "be used by insert- and extractvalue",
104 return Error::success();
105 }
106
107 [[nodiscard]] bool
108 replaceFunctionWithOp(Function &F, dxil::OpCode DXILOp,
109 ArrayRef<IntrinArgSelect> ArgSelects) {
110 return replaceFunction(F, [&](CallInst *CI) -> Error {
111 OpBuilder.getIRB().SetInsertPoint(CI);
113 if (ArgSelects.size()) {
114 for (const IntrinArgSelect &A : ArgSelects) {
115 switch (A.Type) {
116 case IntrinArgSelect::Type::Index:
117 Args.push_back(CI->getArgOperand(A.Value));
118 break;
119 case IntrinArgSelect::Type::I8:
120 Args.push_back(OpBuilder.getIRB().getInt8((uint8_t)A.Value));
121 break;
122 case IntrinArgSelect::Type::I32:
123 Args.push_back(OpBuilder.getIRB().getInt32(A.Value));
124 break;
125 }
126 }
127 } else {
128 Args.append(CI->arg_begin(), CI->arg_end());
129 }
130
131 Expected<CallInst *> OpCall =
132 OpBuilder.tryCreateOp(DXILOp, Args, CI->getName(), F.getReturnType());
133 if (Error E = OpCall.takeError())
134 return E;
135
136 if (isa<StructType>(CI->getType())) {
137 if (Error E = replaceNamedStructUses(CI, *OpCall))
138 return E;
139 } else
140 CI->replaceAllUsesWith(*OpCall);
141
142 CI->eraseFromParent();
143 return Error::success();
144 });
145 }
146
147 /// Create a cast between a `target("dx")` type and `dx.types.Handle`, which
148 /// is intended to be removed by the end of lowering. This is used to allow
149 /// lowering of ops which need to change their return or argument types in a
150 /// piecemeal way - we can add the casts in to avoid updating all of the uses
151 /// or defs, and by the end all of the casts will be redundant.
152 Value *createTmpHandleCast(Value *V, Type *Ty) {
153 CallInst *Cast = OpBuilder.getIRB().CreateIntrinsic(
154 Intrinsic::dx_resource_casthandle, {Ty, V->getType()}, {V});
155 CleanupCasts.push_back(Cast);
156 return Cast;
157 }
158
159 void cleanupHandleCasts() {
162
163 for (CallInst *Cast : CleanupCasts) {
164 // These casts were only put in to ease the move from `target("dx")` types
165 // to `dx.types.Handle in a piecemeal way. At this point, all of the
166 // non-cast uses should now be `dx.types.Handle`, and remaining casts
167 // should all form pairs to and from the now unused `target("dx")` type.
168 CastFns.push_back(Cast->getCalledFunction());
169
170 // If the cast is not to `dx.types.Handle`, it should be the first part of
171 // the pair. Keep track so we can remove it once it has no more uses.
172 if (Cast->getType() != OpBuilder.getHandleType()) {
173 ToRemove.push_back(Cast);
174 continue;
175 }
176 // Otherwise, we're the second handle in a pair. Forward the arguments and
177 // remove the (second) cast.
178 CallInst *Def = cast<CallInst>(Cast->getOperand(0));
179 assert(Def->getIntrinsicID() == Intrinsic::dx_resource_casthandle &&
180 "Unbalanced pair of temporary handle casts");
181 Cast->replaceAllUsesWith(Def->getOperand(0));
182 Cast->eraseFromParent();
183 }
184 for (CallInst *Cast : ToRemove) {
185 assert(Cast->user_empty() && "Temporary handle cast still has users");
186 Cast->eraseFromParent();
187 }
188
189 // Deduplicate the cast functions so that we only erase each one once.
190 llvm::sort(CastFns);
191 CastFns.erase(llvm::unique(CastFns), CastFns.end());
192 for (Function *F : CastFns)
193 F->eraseFromParent();
194
195 CleanupCasts.clear();
196 }
197
198 // Remove the resource global associated with the handleFromBinding call
199 // instruction and their uses as they aren't needed anymore.
200 // TODO: We should verify that all the globals get removed.
201 // It's expected we'll need a custom pass in the future that will eliminate
202 // the need for this here.
203 void removeResourceGlobals(CallInst *CI) {
204 for (User *User : make_early_inc_range(CI->users())) {
205 if (StoreInst *Store = dyn_cast<StoreInst>(User)) {
206 Value *V = Store->getOperand(1);
207 Store->eraseFromParent();
208 if (GlobalVariable *GV = dyn_cast<GlobalVariable>(V))
209 if (GV->use_empty()) {
210 GV->removeDeadConstantUsers();
211 GV->eraseFromParent();
212 }
213 }
214 }
215 }
216
217 void replaceHandleFromBindingCall(CallInst *CI, Value *Replacement) {
219 Intrinsic::dx_resource_handlefrombinding);
220
221 removeResourceGlobals(CI);
222
223 auto *NameGlobal = dyn_cast<llvm::GlobalVariable>(CI->getArgOperand(4));
224
225 CI->replaceAllUsesWith(Replacement);
226 CI->eraseFromParent();
227
228 if (NameGlobal && NameGlobal->use_empty())
229 NameGlobal->removeFromParent();
230 }
231
232 [[nodiscard]] bool lowerToCreateHandle(Function &F) {
233 IRBuilder<> &IRB = OpBuilder.getIRB();
234 Type *Int8Ty = IRB.getInt8Ty();
235 Type *Int32Ty = IRB.getInt32Ty();
236 Type *Int1Ty = IRB.getInt1Ty();
237
238 return replaceFunction(F, [&](CallInst *CI) -> Error {
239 IRB.SetInsertPoint(CI);
240
241 auto *It = DRM.find(CI);
242 assert(It != DRM.end() && "Resource not in map?");
243 dxil::ResourceInfo &RI = *It;
244
245 const auto &Binding = RI.getBinding();
246 dxil::ResourceClass RC = DRTM[RI.getHandleTy()].getResourceClass();
247
248 Value *IndexOp = CI->getArgOperand(3);
249 if (Binding.LowerBound != 0)
250 IndexOp = IRB.CreateAdd(IndexOp,
251 ConstantInt::get(Int32Ty, Binding.LowerBound));
252
253 // FIXME: The last argument is a NonUniform flag which needs to be set
254 // based on resource analysis.
255 // https://github.com/llvm/llvm-project/issues/155701
256 std::array<Value *, 4> Args{
257 ConstantInt::get(Int8Ty, llvm::to_underlying(RC)),
258 ConstantInt::get(Int32Ty, Binding.RecordID), IndexOp,
259 ConstantInt::get(Int1Ty, false)};
260 Expected<CallInst *> OpCall =
261 OpBuilder.tryCreateOp(OpCode::CreateHandle, Args, CI->getName());
262 if (Error E = OpCall.takeError())
263 return E;
264
265 Value *Cast = createTmpHandleCast(*OpCall, CI->getType());
266 replaceHandleFromBindingCall(CI, Cast);
267 return Error::success();
268 });
269 }
270
271 [[nodiscard]] bool lowerToBindAndAnnotateHandle(Function &F) {
272 IRBuilder<> &IRB = OpBuilder.getIRB();
273 Type *Int32Ty = IRB.getInt32Ty();
274 Type *Int1Ty = IRB.getInt1Ty();
275
276 return replaceFunction(F, [&](CallInst *CI) -> Error {
277 IRB.SetInsertPoint(CI);
278
279 auto *It = DRM.find(CI);
280 assert(It != DRM.end() && "Resource not in map?");
281 dxil::ResourceInfo &RI = *It;
282
283 const auto &Binding = RI.getBinding();
284 dxil::ResourceTypeInfo &RTI = DRTM[RI.getHandleTy()];
286
287 Value *IndexOp = CI->getArgOperand(3);
288 if (Binding.LowerBound != 0)
289 IndexOp = IRB.CreateAdd(IndexOp,
290 ConstantInt::get(Int32Ty, Binding.LowerBound));
291
292 std::pair<uint32_t, uint32_t> Props =
293 RI.getAnnotateProps(*F.getParent(), RTI);
294
295 // For `CreateHandleFromBinding` we need the upper bound rather than the
296 // size, so we need to be careful about the difference for "unbounded".
297 uint32_t Unbounded = std::numeric_limits<uint32_t>::max();
298 uint32_t UpperBound = Binding.Size == Unbounded
299 ? Unbounded
300 : Binding.LowerBound + Binding.Size - 1;
301 Constant *ResBind = OpBuilder.getResBind(Binding.LowerBound, UpperBound,
302 Binding.Space, RC);
303 // FIXME: The last argument is a NonUniform flag which needs to be set
304 // based on resource analysis.
305 // https://github.com/llvm/llvm-project/issues/155701
306 Constant *NonUniform = ConstantInt::get(Int1Ty, false);
307 std::array<Value *, 3> BindArgs{ResBind, IndexOp, NonUniform};
308 Expected<CallInst *> OpBind = OpBuilder.tryCreateOp(
309 OpCode::CreateHandleFromBinding, BindArgs, CI->getName());
310 if (Error E = OpBind.takeError())
311 return E;
312
313 std::array<Value *, 2> AnnotateArgs{
314 *OpBind, OpBuilder.getResProps(Props.first, Props.second)};
315 Expected<CallInst *> OpAnnotate = OpBuilder.tryCreateOp(
316 OpCode::AnnotateHandle, AnnotateArgs,
317 CI->hasName() ? CI->getName() + "_annot" : Twine());
318 if (Error E = OpAnnotate.takeError())
319 return E;
320
321 Value *Cast = createTmpHandleCast(*OpAnnotate, CI->getType());
322 replaceHandleFromBindingCall(CI, Cast);
323 return Error::success();
324 });
325 }
326
327 /// Lower `dx.resource.handlefrombinding` intrinsics depending on the shader
328 /// model and taking into account binding information from
329 /// DXILResourceAnalysis.
330 bool lowerHandleFromBinding(Function &F) {
331 if (MMDI.DXILVersion < VersionTuple(1, 6))
332 return lowerToCreateHandle(F);
333 return lowerToBindAndAnnotateHandle(F);
334 }
335
336 /// Replace uses of \c Intrin with the values in the `dx.ResRet` of \c Op.
337 /// Since we expect to be post-scalarization, make an effort to avoid vectors.
338 Error replaceResRetUses(CallInst *Intrin, CallInst *Op, bool HasCheckBit) {
339 IRBuilder<> &IRB = OpBuilder.getIRB();
340
341 Instruction *OldResult = Intrin;
342 Type *OldTy = Intrin->getType();
343
344 if (HasCheckBit) {
345 auto *ST = cast<StructType>(OldTy);
346
347 Value *CheckOp = nullptr;
348 Type *Int32Ty = IRB.getInt32Ty();
349 for (Use &U : make_early_inc_range(OldResult->uses())) {
350 if (auto *EVI = dyn_cast<ExtractValueInst>(U.getUser())) {
351 ArrayRef<unsigned> Indices = EVI->getIndices();
352 assert(Indices.size() == 1);
353 // We're only interested in uses of the check bit for now.
354 if (Indices[0] != 1)
355 continue;
356 if (!CheckOp) {
357 Value *NewEVI = IRB.CreateExtractValue(Op, 4);
358 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
359 OpCode::CheckAccessFullyMapped, {NewEVI},
360 OldResult->hasName() ? OldResult->getName() + "_check"
361 : Twine(),
362 Int32Ty);
363 if (Error E = OpCall.takeError())
364 return E;
365 CheckOp = *OpCall;
366 }
367 EVI->replaceAllUsesWith(CheckOp);
368 EVI->eraseFromParent();
369 }
370 }
371
372 if (OldResult->use_empty()) {
373 // Only the check bit was used, so we're done here.
374 OldResult->eraseFromParent();
375 return Error::success();
376 }
377
378 assert(OldResult->hasOneUse() &&
379 isa<ExtractValueInst>(*OldResult->user_begin()) &&
380 "Expected only use to be extract of first element");
381 OldResult = cast<Instruction>(*OldResult->user_begin());
382 OldTy = ST->getElementType(0);
383 }
384
385 // For scalars, we just extract the first element.
386 if (!isa<FixedVectorType>(OldTy)) {
387 Value *EVI = IRB.CreateExtractValue(Op, 0);
388 OldResult->replaceAllUsesWith(EVI);
389 OldResult->eraseFromParent();
390 if (OldResult != Intrin) {
391 assert(Intrin->use_empty() && "Intrinsic still has uses?");
392 Intrin->eraseFromParent();
393 }
394 return Error::success();
395 }
396
397 std::array<Value *, 4> Extracts = {};
398 SmallVector<ExtractElementInst *> DynamicAccesses;
399
400 // The users of the operation should all be scalarized, so we attempt to
401 // replace the extractelements with extractvalues directly.
402 for (Use &U : make_early_inc_range(OldResult->uses())) {
403 if (auto *EEI = dyn_cast<ExtractElementInst>(U.getUser())) {
404 if (auto *IndexOp = dyn_cast<ConstantInt>(EEI->getIndexOperand())) {
405 size_t IndexVal = IndexOp->getZExtValue();
406 assert(IndexVal < 4 && "Index into buffer load out of range");
407 if (!Extracts[IndexVal])
408 Extracts[IndexVal] = IRB.CreateExtractValue(Op, IndexVal);
409 EEI->replaceAllUsesWith(Extracts[IndexVal]);
410 EEI->eraseFromParent();
411 } else {
412 DynamicAccesses.push_back(EEI);
413 }
414 }
415 }
416
417 const auto *VecTy = cast<FixedVectorType>(OldTy);
418 const unsigned N = VecTy->getNumElements();
419
420 // If there's a dynamic access we need to round trip through stack memory so
421 // that we don't leave vectors around.
422 if (!DynamicAccesses.empty()) {
423 Type *Int32Ty = IRB.getInt32Ty();
424 Constant *Zero = ConstantInt::get(Int32Ty, 0);
425
426 Type *ElTy = VecTy->getElementType();
427 Type *ArrayTy = ArrayType::get(ElTy, N);
428 Value *Alloca = IRB.CreateAlloca(ArrayTy);
429
430 for (int I = 0, E = N; I != E; ++I) {
431 if (!Extracts[I])
432 Extracts[I] = IRB.CreateExtractValue(Op, I);
434 ArrayTy, Alloca, {Zero, ConstantInt::get(Int32Ty, I)});
435 IRB.CreateStore(Extracts[I], GEP);
436 }
437
438 for (ExtractElementInst *EEI : DynamicAccesses) {
439 Value *GEP = IRB.CreateInBoundsGEP(ArrayTy, Alloca,
440 {Zero, EEI->getIndexOperand()});
441 Value *Load = IRB.CreateLoad(ElTy, GEP);
442 EEI->replaceAllUsesWith(Load);
443 EEI->eraseFromParent();
444 }
445 }
446
447 // If we still have uses, then we're not fully scalarized and need to
448 // recreate the vector. This should only happen for things like exported
449 // functions from libraries.
450 if (!OldResult->use_empty()) {
451 for (int I = 0, E = N; I != E; ++I)
452 if (!Extracts[I])
453 Extracts[I] = IRB.CreateExtractValue(Op, I);
454
455 Value *Vec = PoisonValue::get(OldTy);
456 for (int I = 0, E = N; I != E; ++I)
457 Vec = IRB.CreateInsertElement(Vec, Extracts[I], I);
458 OldResult->replaceAllUsesWith(Vec);
459 }
460
461 OldResult->eraseFromParent();
462 if (OldResult != Intrin) {
463 assert(Intrin->use_empty() && "Intrinsic still has uses?");
464 Intrin->eraseFromParent();
465 }
466
467 return Error::success();
468 }
469
470 [[nodiscard]] bool lowerTypedBufferLoad(Function &F, bool HasCheckBit) {
471 IRBuilder<> &IRB = OpBuilder.getIRB();
472 Type *Int32Ty = IRB.getInt32Ty();
473
474 return replaceFunction(F, [&](CallInst *CI) -> Error {
475 IRB.SetInsertPoint(CI);
476
477 Value *Handle =
478 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
479 Value *Index0 = CI->getArgOperand(1);
480 Value *Index1 = UndefValue::get(Int32Ty);
481
482 Type *OldTy = CI->getType();
483 if (HasCheckBit)
484 OldTy = cast<StructType>(OldTy)->getElementType(0);
485 Type *NewRetTy = OpBuilder.getResRetType(OldTy->getScalarType());
486
487 std::array<Value *, 3> Args{Handle, Index0, Index1};
488 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
489 OpCode::BufferLoad, Args, CI->getName(), NewRetTy);
490 if (Error E = OpCall.takeError())
491 return E;
492 if (Error E = replaceResRetUses(CI, *OpCall, HasCheckBit))
493 return E;
494
495 return Error::success();
496 });
497 }
498
499 [[nodiscard]] bool lowerRawBufferLoad(Function &F) {
500 const DataLayout &DL = F.getDataLayout();
501 IRBuilder<> &IRB = OpBuilder.getIRB();
502 Type *Int8Ty = IRB.getInt8Ty();
503 Type *Int32Ty = IRB.getInt32Ty();
504
505 return replaceFunction(F, [&](CallInst *CI) -> Error {
506 IRB.SetInsertPoint(CI);
507
508 Type *OldTy = cast<StructType>(CI->getType())->getElementType(0);
509 Type *ScalarTy = OldTy->getScalarType();
510 Type *NewRetTy = OpBuilder.getResRetType(ScalarTy);
511
512 Value *Handle =
513 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
514 Value *Index0 = CI->getArgOperand(1);
515 Value *Index1 = CI->getArgOperand(2);
516 uint64_t NumElements =
517 DL.getTypeSizeInBits(OldTy) / DL.getTypeSizeInBits(ScalarTy);
518 Value *Mask = ConstantInt::get(Int8Ty, ~(~0U << NumElements));
519 Value *Align =
520 ConstantInt::get(Int32Ty, DL.getPrefTypeAlign(ScalarTy).value());
521
522 Expected<CallInst *> OpCall =
523 MMDI.DXILVersion >= VersionTuple(1, 2)
524 ? OpBuilder.tryCreateOp(OpCode::RawBufferLoad,
525 {Handle, Index0, Index1, Mask, Align},
526 CI->getName(), NewRetTy)
527 : OpBuilder.tryCreateOp(OpCode::BufferLoad,
528 {Handle, Index0, Index1}, CI->getName(),
529 NewRetTy);
530 if (Error E = OpCall.takeError())
531 return E;
532 if (Error E = replaceResRetUses(CI, *OpCall, /*HasCheckBit=*/true))
533 return E;
534
535 return Error::success();
536 });
537 }
538
539 [[nodiscard]] bool lowerCBufferLoad(Function &F) {
540 IRBuilder<> &IRB = OpBuilder.getIRB();
541
542 return replaceFunction(F, [&](CallInst *CI) -> Error {
543 IRB.SetInsertPoint(CI);
544
545 Type *OldTy = cast<StructType>(CI->getType())->getElementType(0);
546 Type *ScalarTy = OldTy->getScalarType();
547 Type *NewRetTy = OpBuilder.getCBufRetType(ScalarTy);
548
549 Value *Handle =
550 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
551 Value *Index = CI->getArgOperand(1);
552
553 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
554 OpCode::CBufferLoadLegacy, {Handle, Index}, CI->getName(), NewRetTy);
555 if (Error E = OpCall.takeError())
556 return E;
557 if (Error E = replaceNamedStructUses(CI, *OpCall))
558 return E;
559
560 CI->eraseFromParent();
561 return Error::success();
562 });
563 }
564
565 [[nodiscard]] bool lowerUpdateCounter(Function &F) {
566 IRBuilder<> &IRB = OpBuilder.getIRB();
567 Type *Int32Ty = IRB.getInt32Ty();
568
569 return replaceFunction(F, [&](CallInst *CI) -> Error {
570 IRB.SetInsertPoint(CI);
571 Value *Handle =
572 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
573 Value *Op1 = CI->getArgOperand(1);
574
575 std::array<Value *, 2> Args{Handle, Op1};
576
577 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
578 OpCode::UpdateCounter, Args, CI->getName(), Int32Ty);
579
580 if (Error E = OpCall.takeError())
581 return E;
582
583 CI->replaceAllUsesWith(*OpCall);
584 CI->eraseFromParent();
585 return Error::success();
586 });
587 }
588
589 [[nodiscard]] bool lowerGetPointer(Function &F) {
590 // These should have already been handled in DXILResourceAccess, so we can
591 // just clean up the dead prototype.
592 assert(F.user_empty() && "getpointer operations should have been removed");
593 F.eraseFromParent();
594 return false;
595 }
596
597 [[nodiscard]] bool lowerBufferStore(Function &F, bool IsRaw) {
598 const DataLayout &DL = F.getDataLayout();
599 IRBuilder<> &IRB = OpBuilder.getIRB();
600 Type *Int8Ty = IRB.getInt8Ty();
601 Type *Int32Ty = IRB.getInt32Ty();
602
603 return replaceFunction(F, [&](CallInst *CI) -> Error {
604 IRB.SetInsertPoint(CI);
605
606 Value *Handle =
607 createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
608 Value *Index0 = CI->getArgOperand(1);
609 Value *Index1 = IsRaw ? CI->getArgOperand(2) : UndefValue::get(Int32Ty);
610
611 Value *Data = CI->getArgOperand(IsRaw ? 3 : 2);
612 Type *DataTy = Data->getType();
613 Type *ScalarTy = DataTy->getScalarType();
614
615 uint64_t NumElements =
616 DL.getTypeSizeInBits(DataTy) / DL.getTypeSizeInBits(ScalarTy);
617 Value *Mask =
618 ConstantInt::get(Int8Ty, IsRaw ? ~(~0U << NumElements) : 15U);
619
620 // TODO: check that we only have vector or scalar...
621 if (NumElements > 4)
622 return make_error<StringError>(
623 "Buffer store data must have at most 4 elements",
625
626 std::array<Value *, 4> DataElements{nullptr, nullptr, nullptr, nullptr};
627 if (DataTy == ScalarTy)
628 DataElements[0] = Data;
629 else {
630 // Since we're post-scalarizer, if we see a vector here it's likely
631 // constructed solely for the argument of the store. Just use the scalar
632 // values from before they're inserted into the temporary.
633 auto *IEI = dyn_cast<InsertElementInst>(Data);
634 while (IEI) {
635 auto *IndexOp = dyn_cast<ConstantInt>(IEI->getOperand(2));
636 if (!IndexOp)
637 break;
638 size_t IndexVal = IndexOp->getZExtValue();
639 assert(IndexVal < 4 && "Too many elements for buffer store");
640 DataElements[IndexVal] = IEI->getOperand(1);
641 IEI = dyn_cast<InsertElementInst>(IEI->getOperand(0));
642 }
643 }
644
645 // If for some reason we weren't able to forward the arguments from the
646 // scalarizer artifact, then we may need to actually extract elements from
647 // the vector.
648 for (int I = 0, E = NumElements; I < E; ++I)
649 if (DataElements[I] == nullptr)
650 DataElements[I] =
651 IRB.CreateExtractElement(Data, ConstantInt::get(Int32Ty, I));
652
653 // For any elements beyond the length of the vector, we should fill it up
654 // with undef - however, for typed buffers we repeat the first element to
655 // match DXC.
656 for (int I = NumElements, E = 4; I < E; ++I)
657 if (DataElements[I] == nullptr)
658 DataElements[I] = IsRaw ? UndefValue::get(ScalarTy) : DataElements[0];
659
660 dxil::OpCode Op = OpCode::BufferStore;
662 Handle, Index0, Index1, DataElements[0],
663 DataElements[1], DataElements[2], DataElements[3], Mask};
664 if (IsRaw && MMDI.DXILVersion >= VersionTuple(1, 2)) {
665 Op = OpCode::RawBufferStore;
666 // RawBufferStore requires the alignment
667 Args.push_back(
668 ConstantInt::get(Int32Ty, DL.getPrefTypeAlign(ScalarTy).value()));
669 }
670 Expected<CallInst *> OpCall =
671 OpBuilder.tryCreateOp(Op, Args, CI->getName());
672 if (Error E = OpCall.takeError())
673 return E;
674
675 CI->eraseFromParent();
676 // Clean up any leftover `insertelement`s
677 auto *IEI = dyn_cast<InsertElementInst>(Data);
678 while (IEI && IEI->use_empty()) {
679 InsertElementInst *Tmp = IEI;
680 IEI = dyn_cast<InsertElementInst>(IEI->getOperand(0));
681 Tmp->eraseFromParent();
682 }
683
684 return Error::success();
685 });
686 }
687
688 [[nodiscard]] bool lowerCtpopToCountBits(Function &F) {
689 IRBuilder<> &IRB = OpBuilder.getIRB();
690 Type *Int32Ty = IRB.getInt32Ty();
691
692 return replaceFunction(F, [&](CallInst *CI) -> Error {
693 IRB.SetInsertPoint(CI);
695 Args.append(CI->arg_begin(), CI->arg_end());
696
697 Type *RetTy = Int32Ty;
698 Type *FRT = F.getReturnType();
699 if (const auto *VT = dyn_cast<VectorType>(FRT))
700 RetTy = VectorType::get(RetTy, VT);
701
702 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
703 dxil::OpCode::CountBits, Args, CI->getName(), RetTy);
704 if (Error E = OpCall.takeError())
705 return E;
706
707 // If the result type is 32 bits we can do a direct replacement.
708 if (FRT->isIntOrIntVectorTy(32)) {
709 CI->replaceAllUsesWith(*OpCall);
710 CI->eraseFromParent();
711 return Error::success();
712 }
713
714 unsigned CastOp;
715 unsigned CastOp2;
716 if (FRT->isIntOrIntVectorTy(16)) {
717 CastOp = Instruction::ZExt;
718 CastOp2 = Instruction::SExt;
719 } else { // must be 64 bits
720 assert(FRT->isIntOrIntVectorTy(64) &&
721 "Currently only lowering 16, 32, or 64 bit ctpop to CountBits \
722 is supported.");
723 CastOp = Instruction::Trunc;
724 CastOp2 = Instruction::Trunc;
725 }
726
727 // It is correct to replace the ctpop with the dxil op and
728 // remove all casts to i32
729 bool NeedsCast = false;
731 Instruction *I = dyn_cast<Instruction>(User);
732 if (I && (I->getOpcode() == CastOp || I->getOpcode() == CastOp2) &&
733 I->getType() == RetTy) {
734 I->replaceAllUsesWith(*OpCall);
735 I->eraseFromParent();
736 } else
737 NeedsCast = true;
738 }
739
740 // It is correct to replace a ctpop with the dxil op and
741 // a cast from i32 to the return type of the ctpop
742 // the cast is emitted here if there is a non-cast to i32
743 // instr which uses the ctpop
744 if (NeedsCast) {
745 Value *Cast =
746 IRB.CreateZExtOrTrunc(*OpCall, F.getReturnType(), "ctpop.cast");
747 CI->replaceAllUsesWith(Cast);
748 }
749
750 CI->eraseFromParent();
751 return Error::success();
752 });
753 }
754
755 [[nodiscard]] bool lowerLifetimeIntrinsic(Function &F) {
756 IRBuilder<> &IRB = OpBuilder.getIRB();
757 return replaceFunction(F, [&](CallInst *CI) -> Error {
758 IRB.SetInsertPoint(CI);
759 Value *Ptr = CI->getArgOperand(0);
760 assert(Ptr->getType()->isPointerTy() &&
761 "Expected operand of lifetime intrinsic to be a pointer");
762
763 auto ZeroOrUndef = [&](Type *Ty) {
764 return MMDI.ValidatorVersion < VersionTuple(1, 6)
766 : UndefValue::get(Ty);
767 };
768
769 Value *Val = nullptr;
770 if (auto *GV = dyn_cast<GlobalVariable>(Ptr)) {
771 if (GV->hasInitializer() || GV->isExternallyInitialized())
772 return Error::success();
773 Val = ZeroOrUndef(GV->getValueType());
774 } else if (auto *AI = dyn_cast<AllocaInst>(Ptr))
775 Val = ZeroOrUndef(AI->getAllocatedType());
776
777 assert(Val && "Expected operand of lifetime intrinsic to be a global "
778 "variable or alloca instruction");
779 IRB.CreateStore(Val, Ptr, false);
780
781 CI->eraseFromParent();
782 return Error::success();
783 });
784 }
785
786 [[nodiscard]] bool lowerIsFPClass(Function &F) {
787 IRBuilder<> &IRB = OpBuilder.getIRB();
788 Type *RetTy = IRB.getInt1Ty();
789
790 return replaceFunction(F, [&](CallInst *CI) -> Error {
791 IRB.SetInsertPoint(CI);
793 Value *Fl = CI->getArgOperand(0);
794 Args.push_back(Fl);
795
797 Value *T = CI->getArgOperand(1);
798 auto *TCI = dyn_cast<ConstantInt>(T);
799 switch (TCI->getZExtValue()) {
800 case FPClassTest::fcInf:
801 OpCode = dxil::OpCode::IsInf;
802 break;
803 case FPClassTest::fcNan:
804 OpCode = dxil::OpCode::IsNaN;
805 break;
806 case FPClassTest::fcNormal:
807 OpCode = dxil::OpCode::IsNormal;
808 break;
809 case FPClassTest::fcFinite:
810 OpCode = dxil::OpCode::IsFinite;
811 break;
812 default:
813 SmallString<128> Msg =
814 formatv("Unsupported FPClassTest {0} for DXIL Op Lowering",
815 TCI->getZExtValue());
816 return make_error<StringError>(Msg, inconvertibleErrorCode());
817 }
818
819 Expected<CallInst *> OpCall =
820 OpBuilder.tryCreateOp(OpCode, Args, CI->getName(), RetTy);
821 if (Error E = OpCall.takeError())
822 return E;
823
824 CI->replaceAllUsesWith(*OpCall);
825 CI->eraseFromParent();
826 return Error::success();
827 });
828 }
829
830 bool lowerIntrinsics() {
831 bool Updated = false;
832 bool HasErrors = false;
833
834 for (Function &F : make_early_inc_range(M.functions())) {
835 if (!F.isDeclaration())
836 continue;
837 Intrinsic::ID ID = F.getIntrinsicID();
838 switch (ID) {
839 // NOTE: Skip dx_resource_casthandle here. They are
840 // resolved after this loop in cleanupHandleCasts.
841 case Intrinsic::dx_resource_casthandle:
842 // NOTE: llvm.dbg.value is supported as is in DXIL.
843 case Intrinsic::dbg_value:
845 if (F.use_empty())
846 F.eraseFromParent();
847 continue;
848 default:
849 if (F.use_empty())
850 F.eraseFromParent();
851 else {
853 "Unsupported intrinsic {0} for DXIL lowering", F.getName());
854 M.getContext().emitError(Msg);
855 HasErrors |= true;
856 }
857 break;
858
859#define DXIL_OP_INTRINSIC(OpCode, Intrin, ...) \
860 case Intrin: \
861 HasErrors |= replaceFunctionWithOp( \
862 F, OpCode, ArrayRef<IntrinArgSelect>{__VA_ARGS__}); \
863 break;
864#include "DXILOperation.inc"
865 case Intrinsic::dx_resource_handlefrombinding:
866 HasErrors |= lowerHandleFromBinding(F);
867 break;
868 case Intrinsic::dx_resource_getpointer:
869 HasErrors |= lowerGetPointer(F);
870 break;
871 case Intrinsic::dx_resource_load_typedbuffer:
872 HasErrors |= lowerTypedBufferLoad(F, /*HasCheckBit=*/true);
873 break;
874 case Intrinsic::dx_resource_store_typedbuffer:
875 HasErrors |= lowerBufferStore(F, /*IsRaw=*/false);
876 break;
877 case Intrinsic::dx_resource_load_rawbuffer:
878 HasErrors |= lowerRawBufferLoad(F);
879 break;
880 case Intrinsic::dx_resource_store_rawbuffer:
881 HasErrors |= lowerBufferStore(F, /*IsRaw=*/true);
882 break;
883 case Intrinsic::dx_resource_load_cbufferrow_2:
884 case Intrinsic::dx_resource_load_cbufferrow_4:
885 case Intrinsic::dx_resource_load_cbufferrow_8:
886 HasErrors |= lowerCBufferLoad(F);
887 break;
888 case Intrinsic::dx_resource_updatecounter:
889 HasErrors |= lowerUpdateCounter(F);
890 break;
891 case Intrinsic::ctpop:
892 HasErrors |= lowerCtpopToCountBits(F);
893 break;
894 case Intrinsic::lifetime_start:
895 case Intrinsic::lifetime_end:
896 if (F.use_empty())
897 F.eraseFromParent();
898 else {
899 if (MMDI.DXILVersion < VersionTuple(1, 6))
900 HasErrors |= lowerLifetimeIntrinsic(F);
901 else
902 continue;
903 }
904 break;
905 case Intrinsic::is_fpclass:
906 HasErrors |= lowerIsFPClass(F);
907 break;
908 }
909 Updated = true;
910 }
911 if (Updated && !HasErrors)
912 cleanupHandleCasts();
913
914 return Updated;
915 }
916};
917} // namespace
918
923
924 const bool MadeChanges = OpLowerer(M, DRM, DRTM, MMDI).lowerIntrinsics();
925 if (!MadeChanges)
926 return PreservedAnalyses::all();
932 return PA;
933}
934
935namespace {
936class DXILOpLoweringLegacy : public ModulePass {
937public:
938 bool runOnModule(Module &M) override {
939 DXILResourceMap &DRM =
940 getAnalysis<DXILResourceWrapperPass>().getResourceMap();
941 DXILResourceTypeMap &DRTM =
942 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
943 const ModuleMetadataInfo MMDI =
944 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
945
946 return OpLowerer(M, DRM, DRTM, MMDI).lowerIntrinsics();
947 }
948 StringRef getPassName() const override { return "DXIL Op Lowering"; }
949 DXILOpLoweringLegacy() : ModulePass(ID) {}
950
951 static char ID; // Pass identification.
952 void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
960 }
961};
962char DXILOpLoweringLegacy::ID = 0;
963} // end anonymous namespace
964
965INITIALIZE_PASS_BEGIN(DXILOpLoweringLegacy, DEBUG_TYPE, "DXIL Op Lowering",
966 false, false)
969INITIALIZE_PASS_END(DXILOpLoweringLegacy, DEBUG_TYPE, "DXIL Op Lowering", false,
970 false)
971
973 return new DXILOpLoweringLegacy();
974}
for(const MachineOperand &MO :llvm::drop_begin(OldMI.operands(), Desc.getNumOperands()))
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
ReachingDefAnalysis InstSet & ToRemove
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
DXIL Resource Implicit Binding
return RetTy
#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.
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
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
This file defines the SmallVector class.
A container for analyses that lazily runs them and caches their results.
Definition: PassManager.h:255
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
Definition: PassManager.h:412
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
size_t size() const
size - Get the array size.
Definition: ArrayRef.h:147
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
Definition: InstrTypes.h:1348
User::op_iterator arg_begin()
Return the iterator pointing to the beginning of the argument list.
Definition: InstrTypes.h:1267
Value * getArgOperand(unsigned i) const
Definition: InstrTypes.h:1292
User::op_iterator arg_end()
Return the iterator pointing to the end of the argument list.
Definition: InstrTypes.h:1273
This class represents a function call, abstracting a target machine's calling convention.
This is an important base class in LLVM.
Definition: Constant.h:43
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
Definition: Constants.cpp:373
This class represents an Operation in the Expression.
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM)
iterator find(const CallInst *Key)
Definition: DXILResource.h:505
A parsed version of the target data layout string in and methods for querying it.
Definition: DataLayout.h:63
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
Tagged union holding either a T or a Error.
Definition: Error.h:485
Error takeError()
Take ownership of the stored error.
Definition: Error.h:612
This instruction extracts a single (scalar) element from a VectorType value.
Intrinsic::ID getIntrinsicID() const LLVM_READONLY
getIntrinsicID - This method returns the ID number of the specified function, or Intrinsic::not_intri...
Definition: Function.h:244
Value * CreateInsertElement(Type *VecTy, Value *NewElt, Value *Idx, const Twine &Name="")
Definition: IRBuilder.h:2571
AllocaInst * CreateAlloca(Type *Ty, unsigned AddrSpace, Value *ArraySize=nullptr, const Twine &Name="")
Definition: IRBuilder.h:1830
IntegerType * getInt1Ty()
Fetch the type representing a single bit.
Definition: IRBuilder.h:547
Value * CreateExtractElement(Value *Vec, Value *Idx, const Twine &Name="")
Definition: IRBuilder.h:2559
Value * CreateZExtOrTrunc(Value *V, Type *DestTy, const Twine &Name="")
Create a ZExt or Trunc from the integer value V to DestTy.
Definition: IRBuilder.h:2100
Value * CreateExtractValue(Value *Agg, ArrayRef< unsigned > Idxs, const Twine &Name="")
Definition: IRBuilder.h:2618
IntegerType * getInt32Ty()
Fetch the type representing a 32-bit integer.
Definition: IRBuilder.h:562
Value * CreateInBoundsGEP(Type *Ty, Value *Ptr, ArrayRef< Value * > IdxList, const Twine &Name="")
Definition: IRBuilder.h:1931
LLVM_ABI CallInst * CreateIntrinsic(Intrinsic::ID ID, ArrayRef< Type * > Types, ArrayRef< Value * > Args, FMFSource FMFSource={}, const Twine &Name="")
Create a call to intrinsic ID with Args, mangled using Types.
Definition: IRBuilder.cpp:834
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:1847
StoreInst * CreateStore(Value *Val, Value *Ptr, bool isVolatile=false)
Definition: IRBuilder.h:1860
Value * CreateAdd(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Definition: IRBuilder.h:1403
void SetInsertPoint(BasicBlock *TheBB)
This specifies that created instructions should be appended to the end of the specified block.
Definition: IRBuilder.h:207
IntegerType * getInt8Ty()
Fetch the type representing an 8-bit integer.
Definition: IRBuilder.h:552
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition: IRBuilder.h:2780
This instruction inserts a single (scalar) element into a VectorType value.
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
Definition: Instruction.h:513
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.
Definition: Instruction.cpp:82
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
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
Definition: Constants.cpp:1885
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
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition: SmallString.h:26
bool empty() const
Definition: SmallVector.h:82
iterator erase(const_iterator CI)
Definition: SmallVector.h:738
void push_back(const T &Elt)
Definition: SmallVector.h:414
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1197
An instruction for storing to memory.
Definition: Instructions.h:296
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:55
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:82
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
Type * getScalarType() const
If this is a vector type, return the element type, otherwise return 'this'.
Definition: Type.h:352
'undef' values are things that do not have specified contents.
Definition: Constants.h:1420
static LLVM_ABI UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
Definition: Constants.cpp:1866
A Use represents the edge between a Value definition and its users.
Definition: Use.h:35
Value * getOperand(unsigned i) const
Definition: User.h:232
LLVM Value Representation.
Definition: Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition: Value.h:256
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:546
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:262
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition: Value.cpp:322
bool user_empty() const
Definition: Value.h:389
Represents a version number in the form major[.minor[.subminor[.build]]].
Definition: VersionTuple.h:30
StructType * getResRetType(Type *ElementTy)
Get a dx.types.ResRet type with the given element type.
Expected< CallInst * > tryCreateOp(dxil::OpCode Op, ArrayRef< Value * > Args, const Twine &Name="", Type *RetTy=nullptr)
Try to create a call instruction for the given DXIL op.
Constant * getResBind(uint32_t LowerBound, uint32_t UpperBound, uint32_t SpaceID, dxil::ResourceClass RC)
Get a constant dx.types.ResBind value.
Constant * getResProps(uint32_t Word0, uint32_t Word1)
Get a constant dx.types.ResourceProperties value.
StructType * getHandleType()
Get the dx.types.Handle type.
StructType * getCBufRetType(Type *ElementTy)
Get a dx.types.CBufRet type with the given element type.
TargetExtType * getHandleTy() const
Definition: DXILResource.h:394
LLVM_ABI std::pair< uint32_t, uint32_t > getAnnotateProps(Module &M, dxil::ResourceTypeInfo &RTI) const
const ResourceBinding & getBinding() const
Definition: DXILResource.h:393
dxil::ResourceClass getResourceClass() const
Definition: DXILResource.h:324
Wrapper pass for the legacy pass manager.
Wrapper pass for the legacy pass manager.
An efficient, type-erasing, non-owning reference to a callable.
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.
Definition: BitmaskEnum.h:126
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition: CallingConv.h:24
ResourceClass
Definition: DXILABI.h:26
NodeAddr< DefNode * > Def
Definition: RDFGraph.h:384
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
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:98
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:663
auto unique(Range &&R, Predicate P)
Definition: STLExtras.h:2095
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:1669
constexpr std::underlying_type_t< Enum > to_underlying(Enum E)
Returns underlying integer value of an enum.
ModulePass * createDXILOpLoweringLegacyPass()
Pass to lowering LLVM intrinsic call to DXIL op function call.
const char * toString(DWARFSectionKind Kind)
#define N
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39