24#include "llvm/IR/IntrinsicsDirectX.h"
32#define DEBUG_TYPE "dxil-op-lower"
49 : M(M), OpBuilder(M), DRM(DRM), DRTM(DRTM), MMDI(MMDI) {}
61 if (
Error E = ReplaceCall(CI)) {
62 std::string Message(
toString(std::move(
E)));
74 struct IntrinArgSelect {
76#define DXIL_OP_INTRINSIC_ARG_SELECT_TYPE(name) name,
77#include "DXILOperation.inc"
87 Error replaceNamedStructUses(CallInst *Intrin, CallInst *DXILOp) {
90 if (!IntrinTy->isLayoutIdentical(DXILOpTy))
92 "Type mismatch between intrinsic and DXIL op",
97 EVI->setOperand(0, DXILOp);
99 IVI->setOperand(0, DXILOp);
102 "be used by insert- and extractvalue",
110 return replaceFunction(
F, [&](CallInst *CI) ->
Error {
111 OpBuilder.getIRB().SetInsertPoint(CI);
113 if (ArgSelects.
size()) {
114 for (
const IntrinArgSelect &
A : ArgSelects) {
116 case IntrinArgSelect::Type::Index:
119 case IntrinArgSelect::Type::I8:
120 Args.push_back(OpBuilder.getIRB().getInt8((uint8_t)
A.Value));
122 case IntrinArgSelect::Type::I32:
123 Args.push_back(OpBuilder.getIRB().getInt32(
A.Value));
131 Expected<CallInst *> OpCall =
132 OpBuilder.tryCreateOp(DXILOp, Args, CI->
getName(),
F.getReturnType());
137 if (
Error E = replaceNamedStructUses(CI, *OpCall))
153 CallInst *Cast = OpBuilder.getIRB().CreateIntrinsic(
154 Intrinsic::dx_resource_casthandle, {Ty,
V->getType()}, {
V});
155 CleanupCasts.push_back(Cast);
159 void cleanupHandleCasts() {
163 for (CallInst *Cast : CleanupCasts) {
172 if (Cast->
getType() != OpBuilder.getHandleType()) {
179 assert(
Def->getIntrinsicID() == Intrinsic::dx_resource_casthandle &&
180 "Unbalanced pair of temporary handle casts");
192 for (Function *
F : CastFns)
193 F->eraseFromParent();
195 CleanupCasts.clear();
203 void removeResourceGlobals(CallInst *CI) {
207 Store->eraseFromParent();
209 if (GV->use_empty()) {
210 GV->removeDeadConstantUsers();
211 GV->eraseFromParent();
217 void replaceHandleFromBindingCall(CallInst *CI,
Value *Replacement) {
219 Intrinsic::dx_resource_handlefrombinding);
221 removeResourceGlobals(CI);
228 if (NameGlobal && NameGlobal->use_empty())
229 NameGlobal->removeFromParent();
232 [[nodiscard]]
bool lowerToCreateHandle(Function &
F) {
238 return replaceFunction(
F, [&](CallInst *CI) ->
Error {
241 auto *It = DRM.find(CI);
242 assert(It != DRM.end() &&
"Resource not in map?");
243 dxil::ResourceInfo &RI = *It;
256 std::array<Value *, 4>
Args{
259 ConstantInt::get(Int1Ty,
false)};
260 Expected<CallInst *> OpCall =
261 OpBuilder.tryCreateOp(OpCode::CreateHandle, Args, CI->
getName());
265 Value *Cast = createTmpHandleCast(*OpCall, CI->
getType());
266 replaceHandleFromBindingCall(CI, Cast);
271 [[nodiscard]]
bool lowerToBindAndAnnotateHandle(Function &
F) {
276 return replaceFunction(
F, [&](CallInst *CI) ->
Error {
279 auto *It = DRM.find(CI);
280 assert(It != DRM.end() &&
"Resource not in map?");
281 dxil::ResourceInfo &RI = *It;
284 dxil::ResourceTypeInfo &RTI = DRTM[RI.
getHandleTy()];
292 std::pair<uint32_t, uint32_t> Props =
297 uint32_t Unbounded = std::numeric_limits<uint32_t>::max();
298 uint32_t UpperBound =
Binding.Size == Unbounded
301 Constant *ResBind = OpBuilder.getResBind(
Binding.LowerBound, UpperBound,
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());
313 std::array<Value *, 2> AnnotateArgs{
314 *OpBind, OpBuilder.getResProps(Props.first, Props.second)};
315 Expected<CallInst *> OpAnnotate = OpBuilder.tryCreateOp(
316 OpCode::AnnotateHandle, AnnotateArgs,
321 Value *Cast = createTmpHandleCast(*OpAnnotate, CI->
getType());
322 replaceHandleFromBindingCall(CI, Cast);
330 bool lowerHandleFromBinding(Function &
F) {
331 if (MMDI.DXILVersion < VersionTuple(1, 6))
332 return lowerToCreateHandle(
F);
333 return lowerToBindAndAnnotateHandle(
F);
338 Error replaceResRetUses(CallInst *Intrin, CallInst *
Op,
bool HasCheckBit) {
347 Value *CheckOp =
nullptr;
351 ArrayRef<unsigned> Indices = EVI->getIndices();
358 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
359 OpCode::CheckAccessFullyMapped, {NewEVI},
367 EVI->replaceAllUsesWith(CheckOp);
368 EVI->eraseFromParent();
380 "Expected only use to be extract of first element");
382 OldTy =
ST->getElementType(0);
390 if (OldResult != Intrin) {
397 std::array<Value *, 4> Extracts = {};
405 size_t IndexVal = IndexOp->getZExtValue();
406 assert(IndexVal < 4 &&
"Index into buffer load out of range");
407 if (!Extracts[IndexVal])
410 EEI->eraseFromParent();
418 const unsigned N = VecTy->getNumElements();
422 if (!DynamicAccesses.
empty()) {
426 Type *ElTy = VecTy->getElementType();
427 Type *ArrayTy = ArrayType::get(ElTy,
N);
430 for (
int I = 0,
E =
N;
I !=
E; ++
I) {
438 for (ExtractElementInst *EEI : DynamicAccesses) {
440 {
Zero, EEI->getIndexOperand()});
443 EEI->eraseFromParent();
451 for (
int I = 0,
E =
N;
I !=
E; ++
I)
456 for (
int I = 0,
E =
N;
I !=
E; ++
I)
462 if (OldResult != Intrin) {
470 [[nodiscard]]
bool lowerTypedBufferLoad(Function &
F,
bool HasCheckBit) {
474 return replaceFunction(
F, [&](CallInst *CI) ->
Error {
478 createTmpHandleCast(CI->
getArgOperand(0), OpBuilder.getHandleType());
487 std::array<Value *, 3>
Args{Handle, Index0, Index1};
488 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
489 OpCode::BufferLoad, Args, CI->
getName(), NewRetTy);
492 if (
Error E = replaceResRetUses(CI, *OpCall, HasCheckBit))
499 [[nodiscard]]
bool lowerRawBufferLoad(Function &
F) {
500 const DataLayout &
DL =
F.getDataLayout();
505 return replaceFunction(
F, [&](CallInst *CI) ->
Error {
510 Type *NewRetTy = OpBuilder.getResRetType(ScalarTy);
513 createTmpHandleCast(CI->
getArgOperand(0), OpBuilder.getHandleType());
516 uint64_t NumElements =
517 DL.getTypeSizeInBits(OldTy) /
DL.getTypeSizeInBits(ScalarTy);
518 Value *
Mask = ConstantInt::get(Int8Ty, ~(~0U << NumElements));
520 ConstantInt::get(
Int32Ty,
DL.getPrefTypeAlign(ScalarTy).value());
522 Expected<CallInst *> OpCall =
523 MMDI.DXILVersion >= VersionTuple(1, 2)
524 ? OpBuilder.tryCreateOp(OpCode::RawBufferLoad,
527 : OpBuilder.tryCreateOp(OpCode::BufferLoad,
528 {Handle, Index0, Index1}, CI->
getName(),
532 if (
Error E = replaceResRetUses(CI, *OpCall,
true))
539 [[nodiscard]]
bool lowerCBufferLoad(Function &
F) {
542 return replaceFunction(
F, [&](CallInst *CI) ->
Error {
547 Type *NewRetTy = OpBuilder.getCBufRetType(ScalarTy);
550 createTmpHandleCast(CI->
getArgOperand(0), OpBuilder.getHandleType());
553 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
554 OpCode::CBufferLoadLegacy, {Handle,
Index}, CI->
getName(), NewRetTy);
557 if (
Error E = replaceNamedStructUses(CI, *OpCall))
565 [[nodiscard]]
bool lowerUpdateCounter(Function &
F) {
569 return replaceFunction(
F, [&](CallInst *CI) ->
Error {
572 createTmpHandleCast(CI->
getArgOperand(0), OpBuilder.getHandleType());
575 std::array<Value *, 2>
Args{Handle, Op1};
577 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
589 [[nodiscard]]
bool lowerGetPointer(Function &
F) {
592 assert(
F.user_empty() &&
"getpointer operations should have been removed");
597 [[nodiscard]]
bool lowerBufferStore(Function &
F,
bool IsRaw) {
598 const DataLayout &
DL =
F.getDataLayout();
603 return replaceFunction(
F, [&](CallInst *CI) ->
Error {
607 createTmpHandleCast(CI->
getArgOperand(0), OpBuilder.getHandleType());
615 uint64_t NumElements =
616 DL.getTypeSizeInBits(DataTy) /
DL.getTypeSizeInBits(ScalarTy);
618 ConstantInt::get(Int8Ty, IsRaw ? ~(~0U << NumElements) : 15U);
623 "Buffer store data must have at most 4 elements",
626 std::array<Value *, 4> DataElements{
nullptr,
nullptr,
nullptr,
nullptr};
627 if (DataTy == ScalarTy)
628 DataElements[0] =
Data;
638 size_t IndexVal = IndexOp->getZExtValue();
639 assert(IndexVal < 4 &&
"Too many elements for buffer store");
640 DataElements[IndexVal] = IEI->getOperand(1);
648 for (
int I = 0,
E = NumElements;
I <
E; ++
I)
649 if (DataElements[
I] ==
nullptr)
656 for (
int I = NumElements,
E = 4;
I <
E; ++
I)
657 if (DataElements[
I] ==
nullptr)
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;
668 ConstantInt::get(
Int32Ty,
DL.getPrefTypeAlign(ScalarTy).value()));
670 Expected<CallInst *> OpCall =
671 OpBuilder.tryCreateOp(
Op, Args, CI->
getName());
678 while (IEI && IEI->use_empty()) {
679 InsertElementInst *Tmp = IEI;
688 [[nodiscard]]
bool lowerCtpopToCountBits(Function &
F) {
692 return replaceFunction(
F, [&](CallInst *CI) ->
Error {
698 Type *FRT =
F.getReturnType();
700 RetTy = VectorType::get(RetTy, VT);
702 Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
703 dxil::OpCode::CountBits, Args, CI->
getName(), RetTy);
717 CastOp = Instruction::ZExt;
718 CastOp2 = Instruction::SExt;
721 "Currently only lowering 16, 32, or 64 bit ctpop to CountBits \
723 CastOp = Instruction::Trunc;
724 CastOp2 = Instruction::Trunc;
729 bool NeedsCast =
false;
732 if (
I && (
I->getOpcode() == CastOp ||
I->getOpcode() == CastOp2) &&
733 I->getType() == RetTy) {
734 I->replaceAllUsesWith(*OpCall);
735 I->eraseFromParent();
755 [[nodiscard]]
bool lowerLifetimeIntrinsic(Function &
F) {
757 return replaceFunction(
F, [&](CallInst *CI) ->
Error {
761 "Expected operand of lifetime intrinsic to be a pointer");
763 auto ZeroOrUndef = [&](
Type *Ty) {
764 return MMDI.ValidatorVersion < VersionTuple(1, 6)
766 : UndefValue::
get(Ty);
769 Value *Val =
nullptr;
771 if (GV->hasInitializer() || GV->isExternallyInitialized())
773 Val = ZeroOrUndef(GV->getValueType());
775 Val = ZeroOrUndef(AI->getAllocatedType());
777 assert(Val &&
"Expected operand of lifetime intrinsic to be a global "
778 "variable or alloca instruction");
786 [[nodiscard]]
bool lowerIsFPClass(Function &
F) {
790 return replaceFunction(
F, [&](CallInst *CI) ->
Error {
799 switch (TCI->getZExtValue()) {
800 case FPClassTest::fcInf:
801 OpCode = dxil::OpCode::IsInf;
803 case FPClassTest::fcNan:
804 OpCode = dxil::OpCode::IsNaN;
806 case FPClassTest::fcNormal:
807 OpCode = dxil::OpCode::IsNormal;
809 case FPClassTest::fcFinite:
810 OpCode = dxil::OpCode::IsFinite;
813 SmallString<128> Msg =
814 formatv(
"Unsupported FPClassTest {0} for DXIL Op Lowering",
815 TCI->getZExtValue());
819 Expected<CallInst *> OpCall =
830 bool lowerIntrinsics() {
831 bool Updated =
false;
832 bool HasErrors =
false;
835 if (!
F.isDeclaration())
841 case Intrinsic::dx_resource_casthandle:
843 case Intrinsic::dbg_value:
852 SmallString<128> Msg =
formatv(
853 "Unsupported intrinsic {0} for DXIL lowering",
F.getName());
854 M.getContext().emitError(Msg);
859#define DXIL_OP_INTRINSIC(OpCode, Intrin, ...) \
861 HasErrors |= replaceFunctionWithOp( \
862 F, OpCode, ArrayRef<IntrinArgSelect>{__VA_ARGS__}); \
864#include "DXILOperation.inc"
865 case Intrinsic::dx_resource_handlefrombinding:
866 HasErrors |= lowerHandleFromBinding(
F);
868 case Intrinsic::dx_resource_getpointer:
869 HasErrors |= lowerGetPointer(
F);
871 case Intrinsic::dx_resource_load_typedbuffer:
872 HasErrors |= lowerTypedBufferLoad(
F,
true);
874 case Intrinsic::dx_resource_store_typedbuffer:
875 HasErrors |= lowerBufferStore(
F,
false);
877 case Intrinsic::dx_resource_load_rawbuffer:
878 HasErrors |= lowerRawBufferLoad(
F);
880 case Intrinsic::dx_resource_store_rawbuffer:
881 HasErrors |= lowerBufferStore(
F,
true);
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);
888 case Intrinsic::dx_resource_updatecounter:
889 HasErrors |= lowerUpdateCounter(
F);
891 case Intrinsic::ctpop:
892 HasErrors |= lowerCtpopToCountBits(
F);
894 case Intrinsic::lifetime_start:
895 case Intrinsic::lifetime_end:
899 if (MMDI.DXILVersion < VersionTuple(1, 6))
900 HasErrors |= lowerLifetimeIntrinsic(
F);
905 case Intrinsic::is_fpclass:
906 HasErrors |= lowerIsFPClass(
F);
911 if (Updated && !HasErrors)
912 cleanupHandleCasts();
924 const bool MadeChanges = OpLowerer(M, DRM, DRTM, MMDI).lowerIntrinsics();
936class DXILOpLoweringLegacy :
public ModulePass {
938 bool runOnModule(
Module &M)
override {
940 getAnalysis<DXILResourceWrapperPass>().getResourceMap();
942 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
944 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
946 return OpLowerer(M, DRM, DRTM, MMDI).lowerIntrinsics();
948 StringRef getPassName()
const override {
return "DXIL Op Lowering"; }
949 DXILOpLoweringLegacy() : ModulePass(
ID) {}
952 void getAnalysisUsage(llvm::AnalysisUsage &AU)
const override {
962char DXILOpLoweringLegacy::ID = 0;
973 return new DXILOpLoweringLegacy();
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
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")
DXIL Resource Implicit Binding
Module.h This file contains the declarations for the Module class.
This header defines various interfaces for pass management in LLVM.
ModuleAnalysisManager MAM
#define INITIALIZE_PASS_DEPENDENCY(depName)
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
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
size - Get the array size.
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.
This class represents a function call, abstracting a target machine's calling convention.
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.
static ErrorSuccess success()
Create a success value.
Error takeError()
Take ownership of the stored error.
Intrinsic::ID getIntrinsicID() const LLVM_READONLY
getIntrinsicID - This method returns the ID number of the specified function, or Intrinsic::not_intri...
Value * CreateInsertElement(Type *VecTy, Value *NewElt, Value *Idx, const Twine &Name="")
AllocaInst * CreateAlloca(Type *Ty, unsigned AddrSpace, Value *ArraySize=nullptr, const Twine &Name="")
IntegerType * getInt1Ty()
Fetch the type representing a single bit.
Value * CreateExtractElement(Value *Vec, Value *Idx, const Twine &Name="")
Value * CreateZExtOrTrunc(Value *V, Type *DestTy, const Twine &Name="")
Create a ZExt or Trunc from the integer value V to DestTy.
Value * CreateExtractValue(Value *Agg, ArrayRef< unsigned > Idxs, const Twine &Name="")
IntegerType * getInt32Ty()
Fetch the type representing a 32-bit integer.
Value * CreateInBoundsGEP(Type *Ty, Value *Ptr, ArrayRef< Value * > IdxList, const Twine &Name="")
LoadInst * CreateLoad(Type *Ty, Value *Ptr, const char *Name)
Provided to resolve 'CreateLoad(Ty, Ptr, "...")' correctly, instead of converting the string to 'bool...
StoreInst * CreateStore(Value *Val, Value *Ptr, bool isVolatile=false)
Value * CreateAdd(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
void SetInsertPoint(BasicBlock *TheBB)
This specifies that created instructions should be appended to the end of the specified block.
IntegerType * getInt8Ty()
Fetch the type representing an 8-bit integer.
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
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.
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
A Module instance is used to store all the information related to an LLVM module.
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.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
PreservedAnalyses & preserve()
Mark an analysis as preserved.
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.
StringRef - Represent a constant reference to a string, i.e.
The instances of the Type class are immutable: once they are created, they are never changed.
bool isIntOrIntVectorTy() const
Return true if this is an integer type or a vector of integer types.
Type * getScalarType() const
If this is a vector type, return the element type, otherwise return 'this'.
static LLVM_ABI UndefValue * get(Type *T)
Static factory methods - Return an 'undef' object of the specified type.
Value * getOperand(unsigned i) const
Type * getType() const
All values are typed, get the type of this value.
user_iterator user_begin()
bool hasOneUse() const
Return true if there is exactly one use of this value.
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
iterator_range< user_iterator > users()
iterator_range< use_iterator > uses()
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
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
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.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
NodeAddr< DefNode * > Def
NodeAddr< UseNode * > Use
friend class Instruction
Iterator for Instructions in a `BasicBlock.
This is an optimization pass for GlobalISel generic memory operations.
FunctionAddr VTableAddr Value
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
FunctionAddr VTableAddr uintptr_t uintptr_t Int32Ty
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
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...
auto unique(Range &&R, Predicate P)
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)
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...
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
FunctionAddr VTableAddr uintptr_t uintptr_t Data
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
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.
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.