diff options
author | Justin Bogner <mail@justinbogner.com> | 2024-08-15 00:24:55 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-15 00:24:55 +0300 |
commit | 28d577ecefa1557f5dea5566bf33b885c563d14b (patch) | |
tree | 6a2535d58bebcb32d70c0fc205006a8d031f03a0 | |
parent | 1ca9fe6db3345556c5c6853b3aba8ff209e572df (diff) | |
download | llvm-28d577ecefa1557f5dea5566bf33b885c563d14b.zip llvm-28d577ecefa1557f5dea5566bf33b885c563d14b.tar.gz llvm-28d577ecefa1557f5dea5566bf33b885c563d14b.tar.bz2 |
[DXIL][Analysis] Implement enough of DXILResourceAnalysis for buffers
This implements the DXILResourceAnalysis pass for `dx.TypedBuffer` and
`dx.RawBuffer` types. This should be sufficient to lower
`dx.handle.fromBinding` for this set of types, but it leaves a number
of TODOs around for other resource types.
This also includes a straightforward `print` method in `ResourceInfo`
to make the analysis testable. This is deliberately different than the
printer in `lib/Target/DirectX/DXILResource.cpp`, which attempts to
print bindings in a format compatible with the comments `dxc` prints.
We will eventually want to make that functionality driven by this
analysis pass, but it isn't sufficient for testing so we need both.
Pull Request: https://github.com/llvm/llvm-project/pull/100699
-rw-r--r-- | llvm/include/llvm/Analysis/DXILResource.h | 13 | ||||
-rw-r--r-- | llvm/include/llvm/IR/IntrinsicsDirectX.td | 10 | ||||
-rw-r--r-- | llvm/lib/Analysis/DXILResource.cpp | 380 | ||||
-rw-r--r-- | llvm/test/Analysis/DXILResource/buffer-frombinding.ll | 126 |
4 files changed, 521 insertions, 8 deletions
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h index 7db088d..3ba0ae5 100644 --- a/llvm/include/llvm/Analysis/DXILResource.h +++ b/llvm/include/llvm/Analysis/DXILResource.h @@ -18,19 +18,20 @@ namespace llvm { class CallInst; class MDTuple; +class TargetExtType; namespace dxil { class ResourceInfo { struct ResourceBinding { - uint32_t UniqueID; + uint32_t RecordID; uint32_t Space; uint32_t LowerBound; uint32_t Size; bool operator==(const ResourceBinding &RHS) const { - return std::tie(UniqueID, Space, LowerBound, Size) == - std::tie(RHS.UniqueID, RHS.Space, RHS.LowerBound, RHS.Size); + return std::tie(RecordID, Space, LowerBound, Size) == + std::tie(RHS.RecordID, RHS.Space, RHS.LowerBound, RHS.Size); } bool operator!=(const ResourceBinding &RHS) const { return !(*this == RHS); @@ -128,9 +129,9 @@ public: bool isFeedback() const; bool isMultiSample() const; - void bind(uint32_t UniqueID, uint32_t Space, uint32_t LowerBound, + void bind(uint32_t RecordID, uint32_t Space, uint32_t LowerBound, uint32_t Size) { - Binding.UniqueID = UniqueID; + Binding.RecordID = RecordID; Binding.Space = Space; Binding.LowerBound = LowerBound; Binding.Size = Size; @@ -215,6 +216,8 @@ public: ResourceBinding getBinding() const { return Binding; } std::pair<uint32_t, uint32_t> getAnnotateProps() const; + + void print(raw_ostream &OS) const; }; } // namespace dxil diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td index 904801e..c9102aa 100644 --- a/llvm/include/llvm/IR/IntrinsicsDirectX.td +++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td @@ -20,6 +20,16 @@ def int_dx_flattened_thread_id_in_group : Intrinsic<[llvm_i32_ty], [], [IntrNoMe def int_dx_create_handle : ClangBuiltin<"__builtin_hlsl_create_handle">, Intrinsic<[ llvm_ptr_ty ], [llvm_i8_ty], [IntrWillReturn]>; +// Create resource handle given binding information. Returns a `target("dx.")` +// type appropriate for the kind of resource given a register space ID, lower +// bound and range size of the binding, as well as an index and an indicator +// whether that index may be non-uniform. +def int_dx_handle_fromBinding + : DefaultAttrsIntrinsic< + [llvm_any_ty], + [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty], + [IntrNoMem]>; + def int_dx_all : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>; def int_dx_any : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>; def int_dx_clamp : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>; diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp index 9941a55..d7d10ec 100644 --- a/llvm/lib/Analysis/DXILResource.cpp +++ b/llvm/lib/Analysis/DXILResource.cpp @@ -8,9 +8,14 @@ #include "llvm/Analysis/DXILResource.h" #include "llvm/ADT/APInt.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/IntrinsicsDirectX.h" #include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" #include "llvm/InitializePasses.h" #define DEBUG_TYPE "dxil-resource" @@ -18,6 +23,131 @@ using namespace llvm; using namespace dxil; +static constexpr StringRef getResourceClassName(ResourceClass RC) { + switch (RC) { + case ResourceClass::SRV: + return "SRV"; + case ResourceClass::UAV: + return "UAV"; + case ResourceClass::CBuffer: + return "CBuffer"; + case ResourceClass::Sampler: + return "Sampler"; + } + llvm_unreachable("Unhandled ResourceClass"); +} + +static constexpr StringRef getResourceKindName(ResourceKind RK) { + switch (RK) { + case ResourceKind::Texture1D: + return "Texture1D"; + case ResourceKind::Texture2D: + return "Texture2D"; + case ResourceKind::Texture2DMS: + return "Texture2DMS"; + case ResourceKind::Texture3D: + return "Texture3D"; + case ResourceKind::TextureCube: + return "TextureCube"; + case ResourceKind::Texture1DArray: + return "Texture1DArray"; + case ResourceKind::Texture2DArray: + return "Texture2DArray"; + case ResourceKind::Texture2DMSArray: + return "Texture2DMSArray"; + case ResourceKind::TextureCubeArray: + return "TextureCubeArray"; + case ResourceKind::TypedBuffer: + return "TypedBuffer"; + case ResourceKind::RawBuffer: + return "RawBuffer"; + case ResourceKind::StructuredBuffer: + return "StructuredBuffer"; + case ResourceKind::CBuffer: + return "CBuffer"; + case ResourceKind::Sampler: + return "Sampler"; + case ResourceKind::TBuffer: + return "TBuffer"; + case ResourceKind::RTAccelerationStructure: + return "RTAccelerationStructure"; + case ResourceKind::FeedbackTexture2D: + return "FeedbackTexture2D"; + case ResourceKind::FeedbackTexture2DArray: + return "FeedbackTexture2DArray"; + case ResourceKind::NumEntries: + case ResourceKind::Invalid: + return "<invalid>"; + } + llvm_unreachable("Unhandled ResourceKind"); +} + +static constexpr StringRef getElementTypeName(ElementType ET) { + switch (ET) { + case ElementType::I1: + return "i1"; + case ElementType::I16: + return "i16"; + case ElementType::U16: + return "u16"; + case ElementType::I32: + return "i32"; + case ElementType::U32: + return "u32"; + case ElementType::I64: + return "i64"; + case ElementType::U64: + return "u64"; + case ElementType::F16: + return "f16"; + case ElementType::F32: + return "f32"; + case ElementType::F64: + return "f64"; + case ElementType::SNormF16: + return "snorm_f16"; + case ElementType::UNormF16: + return "unorm_f16"; + case ElementType::SNormF32: + return "snorm_f32"; + case ElementType::UNormF32: + return "unorm_f32"; + case ElementType::SNormF64: + return "snorm_f64"; + case ElementType::UNormF64: + return "unorm_f64"; + case ElementType::PackedS8x32: + return "p32i8"; + case ElementType::PackedU8x32: + return "p32u8"; + case ElementType::Invalid: + return "<invalid>"; + } + llvm_unreachable("Unhandled ElementType"); +} + +static constexpr StringRef getSamplerTypeName(SamplerType ST) { + switch (ST) { + case SamplerType::Default: + return "Default"; + case SamplerType::Comparison: + return "Comparison"; + case SamplerType::Mono: + return "Mono"; + } + llvm_unreachable("Unhandled SamplerType"); +} + +static constexpr StringRef getSamplerFeedbackTypeName(SamplerFeedbackType SFT) { + switch (SFT) { + case SamplerFeedbackType::MinMip: + return "MinMip"; + case SamplerFeedbackType::MipRegionUsed: + return "MipRegionUsed"; + } + llvm_unreachable("Unhandled SamplerFeedbackType"); +} + bool ResourceInfo::isUAV() const { return RC == ResourceClass::UAV; } bool ResourceInfo::isCBuffer() const { return RC == ResourceClass::CBuffer; } @@ -240,7 +370,7 @@ MDTuple *ResourceInfo::getAsMetadata(LLVMContext &Ctx) const { Constant::getIntegerValue(I1Ty, APInt(1, V))); }; - MDVals.push_back(getIntMD(Binding.UniqueID)); + MDVals.push_back(getIntMD(Binding.RecordID)); MDVals.push_back(ValueAsMetadata::get(Symbol)); MDVals.push_back(MDString::get(Ctx, Name)); MDVals.push_back(getIntMD(Binding.Space)); @@ -330,6 +460,248 @@ std::pair<uint32_t, uint32_t> ResourceInfo::getAnnotateProps() const { return {Word0, Word1}; } +void ResourceInfo::print(raw_ostream &OS) const { + OS << " Symbol: "; + Symbol->printAsOperand(OS); + OS << "\n"; + + OS << " Name: \"" << Name << "\"\n" + << " Binding:\n" + << " Record ID: " << Binding.RecordID << "\n" + << " Space: " << Binding.Space << "\n" + << " Lower Bound: " << Binding.LowerBound << "\n" + << " Size: " << Binding.Size << "\n" + << " Class: " << getResourceClassName(RC) << "\n" + << " Kind: " << getResourceKindName(Kind) << "\n"; + + if (isCBuffer()) { + OS << " CBuffer size: " << CBufferSize << "\n"; + } else if (isSampler()) { + OS << " Sampler Type: " << getSamplerTypeName(SamplerTy) << "\n"; + } else { + if (isUAV()) { + OS << " Globally Coherent: " << UAVFlags.GloballyCoherent << "\n" + << " HasCounter: " << UAVFlags.HasCounter << "\n" + << " IsROV: " << UAVFlags.IsROV << "\n"; + } + if (isMultiSample()) + OS << " Sample Count: " << MultiSample.Count << "\n"; + + if (isStruct()) { + OS << " Buffer Stride: " << Struct.Stride << "\n"; + OS << " Alignment: " << Struct.AlignLog2 << "\n"; + } else if (isTyped()) { + OS << " Element Type: " << getElementTypeName(Typed.ElementTy) << "\n" + << " Element Count: " << Typed.ElementCount << "\n"; + } else if (isFeedback()) + OS << " Feedback Type: " << getSamplerFeedbackTypeName(Feedback.Type) + << "\n"; + } +} + +//===----------------------------------------------------------------------===// +// ResourceMapper + +static dxil::ElementType toDXILElementType(Type *Ty, bool IsSigned) { + // TODO: Handle unorm, snorm, and packed. + Ty = Ty->getScalarType(); + + if (Ty->isIntegerTy()) { + switch (Ty->getIntegerBitWidth()) { + case 16: + return IsSigned ? ElementType::I16 : ElementType::U16; + case 32: + return IsSigned ? ElementType::I32 : ElementType::U32; + case 64: + return IsSigned ? ElementType::I64 : ElementType::U64; + case 1: + default: + return ElementType::Invalid; + } + } else if (Ty->isFloatTy()) { + return ElementType::F32; + } else if (Ty->isDoubleTy()) { + return ElementType::F64; + } else if (Ty->isHalfTy()) { + return ElementType::F16; + } + + return ElementType::Invalid; +} + +namespace { + +class ResourceMapper { + Module &M; + LLVMContext &Context; + DXILResourceMap &Resources; + + // In DXC, Record ID is unique per resource type. Match that. + uint32_t NextUAV = 0; + uint32_t NextSRV = 0; + uint32_t NextCBuf = 0; + uint32_t NextSmp = 0; + +public: + ResourceMapper(Module &M, + MapVector<CallInst *, dxil::ResourceInfo> &Resources) + : M(M), Context(M.getContext()), Resources(Resources) {} + + void diagnoseHandle(CallInst *CI, const Twine &Msg, + DiagnosticSeverity Severity = DS_Error) { + std::string S; + raw_string_ostream SS(S); + CI->printAsOperand(SS); + DiagnosticInfoUnsupported Diag(*CI->getFunction(), Msg + ": " + SS.str(), + CI->getDebugLoc(), Severity); + Context.diagnose(Diag); + } + + ResourceInfo *mapBufferType(CallInst *CI, TargetExtType *HandleTy, + bool IsTyped) { + if (HandleTy->getNumTypeParameters() != 1 || + HandleTy->getNumIntParameters() != (IsTyped ? 3 : 2)) { + diagnoseHandle(CI, Twine("Invalid buffer target type")); + return nullptr; + } + + Type *ElTy = HandleTy->getTypeParameter(0); + unsigned IsWriteable = HandleTy->getIntParameter(0); + unsigned IsROV = HandleTy->getIntParameter(1); + bool IsSigned = IsTyped && HandleTy->getIntParameter(2); + + ResourceClass RC = IsWriteable ? ResourceClass::UAV : ResourceClass::SRV; + ResourceKind Kind; + if (IsTyped) + Kind = ResourceKind::TypedBuffer; + else if (ElTy->isIntegerTy(8)) + Kind = ResourceKind::RawBuffer; + else + Kind = ResourceKind::StructuredBuffer; + + // TODO: We need to lower to a typed pointer, can we smuggle the type + // through? + Value *Symbol = UndefValue::get(PointerType::getUnqual(Context)); + // TODO: We don't actually keep track of the name right now... + StringRef Name = ""; + + auto [It, Success] = Resources.try_emplace(CI, RC, Kind, Symbol, Name); + assert(Success && "Mapping the same CallInst again?"); + (void)Success; + // We grab a pointer into the map's storage, which isn't generally safe. + // Since we're just using this to fill in the info the map won't mutate and + // the pointer stays valid for as long as we need it to. + ResourceInfo *RI = &(It->second); + + if (RI->isUAV()) + // TODO: We need analysis for GloballyCoherent and HasCounter + RI->setUAV(false, false, IsROV); + + if (RI->isTyped()) { + dxil::ElementType ET = toDXILElementType(ElTy, IsSigned); + uint32_t Count = 1; + if (auto *VTy = dyn_cast<FixedVectorType>(ElTy)) + Count = VTy->getNumElements(); + RI->setTyped(ET, Count); + } else if (RI->isStruct()) { + const DataLayout &DL = M.getDataLayout(); + + // This mimics what DXC does. Notably, we only ever set the alignment if + // the type is actually a struct type. + uint32_t Stride = DL.getTypeAllocSize(ElTy); + MaybeAlign Alignment; + if (auto *STy = dyn_cast<StructType>(ElTy)) + Alignment = DL.getStructLayout(STy)->getAlignment(); + RI->setStruct(Stride, Alignment); + } + + return RI; + } + + ResourceInfo *mapHandleIntrin(CallInst *CI) { + FunctionType *FTy = CI->getFunctionType(); + Type *RetTy = FTy->getReturnType(); + auto *HandleTy = dyn_cast<TargetExtType>(RetTy); + if (!HandleTy) { + diagnoseHandle(CI, "dx.handle.fromBinding requires target type"); + return nullptr; + } + + StringRef TypeName = HandleTy->getName(); + if (TypeName == "dx.TypedBuffer") { + return mapBufferType(CI, HandleTy, /*IsTyped=*/true); + } else if (TypeName == "dx.RawBuffer") { + return mapBufferType(CI, HandleTy, /*IsTyped=*/false); + } else if (TypeName == "dx.CBuffer") { + // TODO: implement + diagnoseHandle(CI, "dx.CBuffer handles are not implemented yet"); + return nullptr; + } else if (TypeName == "dx.Sampler") { + // TODO: implement + diagnoseHandle(CI, "dx.Sampler handles are not implemented yet"); + return nullptr; + } else if (TypeName == "dx.Texture") { + // TODO: implement + diagnoseHandle(CI, "dx.Texture handles are not implemented yet"); + return nullptr; + } + + diagnoseHandle(CI, "Invalid target(dx) type"); + return nullptr; + } + + ResourceInfo *mapHandleFromBinding(CallInst *CI) { + assert(CI->getIntrinsicID() == Intrinsic::dx_handle_fromBinding && + "Must be dx.handle.fromBinding intrinsic"); + + ResourceInfo *RI = mapHandleIntrin(CI); + if (!RI) + return nullptr; + + uint32_t NextID; + if (RI->isCBuffer()) + NextID = NextCBuf++; + else if (RI->isSampler()) + NextID = NextSmp++; + else if (RI->isUAV()) + NextID = NextUAV++; + else + NextID = NextSRV++; + + uint32_t Space = cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue(); + uint32_t LowerBound = + cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue(); + uint32_t Size = cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue(); + + RI->bind(NextID, Space, LowerBound, Size); + + return RI; + } + + void mapResources() { + for (Function &F : M.functions()) { + if (!F.isDeclaration()) + continue; + LLVM_DEBUG(dbgs() << "Function: " << F.getName() << "\n"); + Intrinsic::ID ID = F.getIntrinsicID(); + switch (ID) { + default: + // TODO: handle `dx.op` functions. + continue; + case Intrinsic::dx_handle_fromBinding: + for (User *U : F.users()) { + LLVM_DEBUG(dbgs() << " Visiting: " << *U << "\n"); + if (CallInst *CI = dyn_cast<CallInst>(U)) + mapHandleFromBinding(CI); + } + break; + } + } + } +}; + +} // namespace + //===----------------------------------------------------------------------===// // DXILResourceAnalysis and DXILResourcePrinterPass @@ -339,6 +711,7 @@ AnalysisKey DXILResourceAnalysis::Key; DXILResourceMap DXILResourceAnalysis::run(Module &M, ModuleAnalysisManager &AM) { DXILResourceMap Data; + ResourceMapper(M, Data).mapResources(); return Data; } @@ -351,7 +724,7 @@ PreservedAnalyses DXILResourcePrinterPass::run(Module &M, OS << "Binding for "; Handle->print(OS); OS << "\n"; - // TODO: Info.print(OS); + Info.print(OS); OS << "\n"; } @@ -373,6 +746,7 @@ void DXILResourceWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { bool DXILResourceWrapperPass::runOnModule(Module &M) { ResourceMap.reset(new DXILResourceMap()); + ResourceMapper(M, *ResourceMap).mapResources(); return false; } @@ -387,7 +761,7 @@ void DXILResourceWrapperPass::print(raw_ostream &OS, const Module *) const { OS << "Binding for "; Handle->print(OS); OS << "\n"; - // TODO: Info.print(OS); + Info.print(OS); OS << "\n"; } } diff --git a/llvm/test/Analysis/DXILResource/buffer-frombinding.ll b/llvm/test/Analysis/DXILResource/buffer-frombinding.ll new file mode 100644 index 0000000..4349adb --- /dev/null +++ b/llvm/test/Analysis/DXILResource/buffer-frombinding.ll @@ -0,0 +1,126 @@ +; RUN: opt -S -disable-output -passes="print<dxil-resource>" < %s 2>&1 | FileCheck %s + +@G = external constant <4 x float>, align 4 + +define void @test_typedbuffer() { + ; RWBuffer<float4> Buf : register(u5, space3) + %typed0 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) + @llvm.dx.handle.fromBinding.tdx.TypedBuffer_f32_1_0( + i32 3, i32 5, i32 1, i32 0, i1 false) + ; CHECK: Binding for %typed0 + ; CHECK: Symbol: ptr undef + ; CHECK: Name: "" + ; CHECK: Binding: + ; CHECK: Record ID: 0 + ; CHECK: Space: 3 + ; CHECK: Lower Bound: 5 + ; CHECK: Size: 1 + ; CHECK: Class: UAV + ; CHECK: Kind: TypedBuffer + ; CHECK: Globally Coherent: 0 + ; CHECK: HasCounter: 0 + ; CHECK: IsROV: 0 + ; CHECK: Element Type: f32 + ; CHECK: Element Count: 4 + + ; RWBuffer<int> Buf : register(u7, space2) + %typed1 = call target("dx.TypedBuffer", i32, 1, 0, 1) + @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_1_0t( + i32 2, i32 7, i32 1, i32 0, i1 false) + ; CHECK: Binding for %typed1 + ; CHECK: Symbol: ptr undef + ; CHECK: Name: "" + ; CHECK: Binding: + ; CHECK: Record ID: 1 + ; CHECK: Space: 2 + ; CHECK: Lower Bound: 7 + ; CHECK: Size: 1 + ; CHECK: Class: UAV + ; CHECK: Kind: TypedBuffer + ; CHECK: Globally Coherent: 0 + ; CHECK: HasCounter: 0 + ; CHECK: IsROV: 0 + ; CHECK: Element Type: i32 + ; CHECK: Element Count: 1 + + ; Buffer<uint4> Buf[24] : register(t3, space5) + %typed2 = call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0) + @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_0_0t( + i32 2, i32 7, i32 24, i32 0, i1 false) + ; CHECK: Binding for %typed2 + ; CHECK: Symbol: ptr undef + ; CHECK: Name: "" + ; CHECK: Binding: + ; CHECK: Record ID: 0 + ; CHECK: Space: 2 + ; CHECK: Lower Bound: 7 + ; CHECK: Size: 24 + ; CHECK: Class: SRV + ; CHECK: Kind: TypedBuffer + ; CHECK: Element Type: u32 + ; CHECK: Element Count: 4 + + ret void +} + +define void @test_structbuffer() { + ; struct S { float4 a; uint4 b; }; + ; StructuredBuffer<S> Buf : register(t2, space4) + %struct0 = call target("dx.RawBuffer", {<4 x float>, <4 x i32>}, 0, 0) + @llvm.dx.handle.fromBinding.tdx.RawBuffer_sl_v4f32v4i32s_0_0t( + i32 4, i32 2, i32 1, i32 0, i1 false) + ; CHECK: Binding for %struct0 + ; CHECK: Symbol: ptr undef + ; CHECK: Name: "" + ; CHECK: Binding: + ; CHECK: Record ID: 1 + ; CHECK: Space: 4 + ; CHECK: Lower Bound: 2 + ; CHECK: Size: 1 + ; CHECK: Class: SRV + ; CHECK: Kind: StructuredBuffer + ; CHECK: Buffer Stride: 32 + ; CHECK: Alignment: 4 + + ret void +} + +define void @test_bytebuffer() { + ; ByteAddressBuffer Buf : register(t8, space1) + %byteaddr0 = call target("dx.RawBuffer", i8, 0, 0) + @llvm.dx.handle.fromBinding.tdx.RawBuffer_i8_0_0t( + i32 1, i32 8, i32 1, i32 0, i1 false) + ; CHECK: Binding for %byteaddr0 + ; CHECK: Symbol: ptr undef + ; CHECK: Name: "" + ; CHECK: Binding: + ; CHECK: Record ID: 2 + ; CHECK: Space: 1 + ; CHECK: Lower Bound: 8 + ; CHECK: Size: 1 + ; CHECK: Class: SRV + ; CHECK: Kind: RawBuffer + + ret void +} + +; Note: We need declarations for each handle.fromBinding in the same +; order as they appear in source to ensure that we can put our CHECK +; lines along side the thing they're checking. +declare target("dx.TypedBuffer", <4 x float>, 1, 0, 0) + @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_1_0_0t( + i32, i32, i32, i32, i1) #0 +declare target("dx.TypedBuffer", i32, 1, 0, 1) + @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_1_0_1t( + i32, i32, i32, i32, i1) #0 +declare target("dx.TypedBuffer", <4 x i32>, 0, 0, 0) + @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4i32_0_0_0t( + i32, i32, i32, i32, i1) #0 +declare target("dx.RawBuffer", { <4 x float>, <4 x i32> }, 0, 0) + @llvm.dx.handle.fromBinding.tdx.RawBuffer_sl_v4f32v4i32s_0_0t( + i32, i32, i32, i32, i1) #0 +declare target("dx.RawBuffer", i8, 0, 0) + @llvm.dx.handle.fromBinding.tdx.RawBuffer_i8_0_0t( + i32, i32, i32, i32, i1) #0 + +attributes #0 = { nocallback nofree nosync nounwind willreturn memory(none) } |