From b861507912d037801546c50613ed91d36279a469 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Mon, 12 Dec 2022 12:51:33 -0500 Subject: [PATCH] [DIrectX backend] emit metadata for entry. New named metadata "dx.entryPoints" is added to save all entries. Each entry is in format of !{ptr to function, name, signature, resource table, extra} For compute shader, the extra will save num of threads in format of {i32 x, i32 y, i32 z} For library profile, an empty entry will be added to save the resource table for the library. Signature and resource table metadata is not generated yet. Differential Revision: https://reviews.llvm.org/D131807 --- llvm/lib/Target/DirectX/DXILMetadata.cpp | 229 ++++++++++++++++++++- llvm/lib/Target/DirectX/DXILMetadata.h | 3 + llvm/lib/Target/DirectX/DXILResource.cpp | 9 +- llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp | 9 + .../CodeGen/DirectX/Metadata/shaderModel-as.ll | 7 + .../CodeGen/DirectX/Metadata/shaderModel-cs.ll | 7 + .../CodeGen/DirectX/Metadata/shaderModel-gs.ll | 7 + .../CodeGen/DirectX/Metadata/shaderModel-hs.ll | 7 + .../CodeGen/DirectX/Metadata/shaderModel-ms.ll | 7 + .../CodeGen/DirectX/Metadata/shaderModel-ps.ll | 7 + .../CodeGen/DirectX/Metadata/shaderModel-vs.ll | 7 + llvm/test/CodeGen/DirectX/UAVMetadata.ll | 2 +- llvm/test/CodeGen/DirectX/empty_cs_entry.ll | 17 ++ llvm/test/CodeGen/DirectX/lib_entry.ll | 22 ++ 14 files changed, 336 insertions(+), 4 deletions(-) create mode 100644 llvm/test/CodeGen/DirectX/empty_cs_entry.ll create mode 100644 llvm/test/CodeGen/DirectX/lib_entry.ll diff --git a/llvm/lib/Target/DirectX/DXILMetadata.cpp b/llvm/lib/Target/DirectX/DXILMetadata.cpp index f52c451..60dda8c 100644 --- a/llvm/lib/Target/DirectX/DXILMetadata.cpp +++ b/llvm/lib/Target/DirectX/DXILMetadata.cpp @@ -12,6 +12,7 @@ #include "DXILMetadata.h" #include "llvm/ADT/Triple.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" @@ -27,7 +28,6 @@ void ValidatorVersionMD::update(VersionTuple ValidatorVer) { auto &Ctx = Entry->getParent()->getContext(); IRBuilder<> B(Ctx); Metadata *MDVals[2]; - MDVals[0] = ConstantAsMetadata::get(B.getInt32(ValidatorVer.getMajor())); MDVals[1] = ConstantAsMetadata::get(B.getInt32(ValidatorVer.getMinor().value_or(0))); @@ -80,3 +80,230 @@ void dxil::createShaderModelMD(Module &M) { Vals[2] = ConstantAsMetadata::get(B.getInt32(Ver.getMinor().value_or(0))); Entry->addOperand(MDNode::get(Ctx, Vals)); } + +static uint32_t getShaderStage(Triple::EnvironmentType Env) { + return (uint32_t)Env - (uint32_t)llvm::Triple::Pixel; +} + +namespace { + +struct EntryProps { + Triple::EnvironmentType ShaderKind; + // FIXME: support more shader profiles. + // See https://github.com/llvm/llvm-project/issues/57927. + struct { + unsigned NumThreads[3]; + } CS; + + EntryProps(Function &F, Triple::EnvironmentType ModuleShaderKind) + : ShaderKind(ModuleShaderKind) { + + if (ShaderKind == Triple::EnvironmentType::Library) { + Attribute EntryAttr = F.getFnAttribute("hlsl.shader"); + StringRef EntryProfile = EntryAttr.getValueAsString(); + Triple T("", "", "", EntryProfile); + ShaderKind = T.getEnvironment(); + } + + if (ShaderKind == Triple::EnvironmentType::Compute) { + auto NumThreadsStr = + F.getFnAttribute("hlsl.numthreads").getValueAsString(); + SmallVector NumThreads; + NumThreadsStr.split(NumThreads, ','); + assert(NumThreads.size() == 3 && "invalid numthreads"); + auto Zip = + llvm::zip(NumThreads, MutableArrayRef(CS.NumThreads)); + for (auto It : Zip) { + StringRef Str = std::get<0>(It); + APInt V; + [[maybe_unused]] bool Result = Str.getAsInteger(10, V); + assert(!Result && "Failed to parse numthreads"); + + unsigned &Num = std::get<1>(It); + Num = V.getLimitedValue(); + } + } + } + + MDTuple *emitDXILEntryProps(uint64_t RawShaderFlag, LLVMContext &Ctx, + bool IsLib) { + std::vector MDVals; + + if (RawShaderFlag != 0) + appendShaderFlags(MDVals, RawShaderFlag, Ctx); + + // Add shader kind for lib entrys. + if (IsLib && ShaderKind != Triple::EnvironmentType::Library) + appendShaderKind(MDVals, Ctx); + + if (ShaderKind == Triple::EnvironmentType::Compute) + appendNumThreads(MDVals, Ctx); + // FIXME: support more props. + // See https://github.com/llvm/llvm-project/issues/57948. + return MDNode::get(Ctx, MDVals); + } + + static MDTuple *emitEntryPropsForEmptyEntry(uint64_t RawShaderFlag, + LLVMContext &Ctx) { + if (RawShaderFlag == 0) + return nullptr; + + std::vector MDVals; + + appendShaderFlags(MDVals, RawShaderFlag, Ctx); + // FIXME: support more props. + // See https://github.com/llvm/llvm-project/issues/57948. + return MDNode::get(Ctx, MDVals); + } + +private: + enum EntryPropsTag { + ShaderFlagsTag = 0, + GSStateTag, + DSStateTag, + HSStateTag, + NumThreadsTag, + AutoBindingSpaceTag, + RayPayloadSizeTag, + RayAttribSizeTag, + ShaderKindTag, + MSStateTag, + ASStateTag, + WaveSizeTag, + EntryRootSigTag, + }; + + void appendNumThreads(std::vector &MDVals, LLVMContext &Ctx) { + MDVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), NumThreadsTag))); + + std::vector NumThreadVals; + for (auto Num : ArrayRef(CS.NumThreads)) + NumThreadVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), Num))); + MDVals.emplace_back(MDNode::get(Ctx, NumThreadVals)); + } + + static void appendShaderFlags(std::vector &MDVals, + uint64_t RawShaderFlag, LLVMContext &Ctx) { + MDVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), ShaderFlagsTag))); + MDVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt64Ty(Ctx), RawShaderFlag))); + } + + void appendShaderKind(std::vector &MDVals, LLVMContext &Ctx) { + MDVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), ShaderKindTag))); + MDVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), getShaderStage(ShaderKind)))); + } +}; + +class EntryMD { + Function &F; + LLVMContext &Ctx; + EntryProps Props; + +public: + EntryMD(Function &F, Triple::EnvironmentType ModuleShaderKind) + : F(F), Ctx(F.getContext()), Props(F, ModuleShaderKind) {} + + MDTuple *emitEntryTuple(MDTuple *Resources, uint64_t RawShaderFlag) { + // FIXME: add signature for profile other than CS. + // See https://github.com/llvm/llvm-project/issues/57928. + MDTuple *Signatures = nullptr; + return emitDxilEntryPointTuple( + &F, F.getName().str(), Signatures, Resources, + Props.emitDXILEntryProps(RawShaderFlag, Ctx, /*IsLib*/ false), Ctx); + } + + MDTuple *emitEntryTupleForLib(uint64_t RawShaderFlag) { + // FIXME: add signature for profile other than CS. + // See https://github.com/llvm/llvm-project/issues/57928. + MDTuple *Signatures = nullptr; + return emitDxilEntryPointTuple( + &F, F.getName().str(), Signatures, + /*entry in lib doesn't need resources metadata*/ nullptr, + Props.emitDXILEntryProps(RawShaderFlag, Ctx, /*IsLib*/ true), Ctx); + } + + // Library will have empty entry metadata which only store the resource table + // metadata. + static MDTuple *emitEmptyEntryForLib(MDTuple *Resources, + uint64_t RawShaderFlag, + LLVMContext &Ctx) { + return emitDxilEntryPointTuple( + nullptr, "", nullptr, Resources, + EntryProps::emitEntryPropsForEmptyEntry(RawShaderFlag, Ctx), Ctx); + } + +private: + static MDTuple *emitDxilEntryPointTuple(Function *Fn, const std::string &Name, + MDTuple *Signatures, + MDTuple *Resources, + MDTuple *Properties, + LLVMContext &Ctx) { + Metadata *MDVals[5]; + MDVals[0] = Fn ? ValueAsMetadata::get(Fn) : nullptr; + MDVals[1] = MDString::get(Ctx, Name.c_str()); + MDVals[2] = Signatures; + MDVals[3] = Resources; + MDVals[4] = Properties; + return MDNode::get(Ctx, MDVals); + } +}; +} // namespace + +void dxil::createEntryMD(Module &M, const uint64_t ShaderFlags) { + SmallVector EntryList; + for (auto &F : M.functions()) { + if (!F.hasFnAttribute("hlsl.shader")) + continue; + EntryList.emplace_back(&F); + } + + auto &Ctx = M.getContext(); + // FIXME: generate metadata for resource. + // See https://github.com/llvm/llvm-project/issues/57926. + MDTuple *MDResources = nullptr; + if (auto *NamedResources = M.getNamedMetadata("dx.resources")) + MDResources = dyn_cast(NamedResources->getOperand(0)); + + std::vector Entries; + Triple T = Triple(M.getTargetTriple()); + switch (T.getEnvironment()) { + case Triple::EnvironmentType::Library: { + // Add empty entry to put resource metadata. + MDTuple *EmptyEntry = + EntryMD::emitEmptyEntryForLib(MDResources, ShaderFlags, Ctx); + Entries.emplace_back(EmptyEntry); + + for (Function *Entry : EntryList) { + EntryMD MD(*Entry, T.getEnvironment()); + Entries.emplace_back(MD.emitEntryTupleForLib(0)); + } + } break; + case Triple::EnvironmentType::Compute: + case Triple::EnvironmentType::Amplification: + case Triple::EnvironmentType::Mesh: + case Triple::EnvironmentType::Vertex: + case Triple::EnvironmentType::Hull: + case Triple::EnvironmentType::Domain: + case Triple::EnvironmentType::Geometry: + case Triple::EnvironmentType::Pixel: { + assert(EntryList.size() == 1 && + "non-lib profiles should only have one entry"); + EntryMD MD(*EntryList.front(), T.getEnvironment()); + Entries.emplace_back(MD.emitEntryTuple(MDResources, ShaderFlags)); + } break; + default: + assert(0 && "invalid profile"); + break; + } + + NamedMDNode *EntryPointsNamedMD = + M.getOrInsertNamedMetadata("dx.entryPoints"); + for (auto *Entry : Entries) + EntryPointsNamedMD->addOperand(Entry); +} diff --git a/llvm/lib/Target/DirectX/DXILMetadata.h b/llvm/lib/Target/DirectX/DXILMetadata.h index 138269f..2f5d7d9 100644 --- a/llvm/lib/Target/DirectX/DXILMetadata.h +++ b/llvm/lib/Target/DirectX/DXILMetadata.h @@ -13,6 +13,8 @@ #ifndef LLVM_TARGET_DIRECTX_DXILMETADATA_H #define LLVM_TARGET_DIRECTX_DXILMETADATA_H +#include + namespace llvm { class Module; class NamedMDNode; @@ -31,6 +33,7 @@ public: }; void createShaderModelMD(Module &M); +void createEntryMD(Module &M, const uint64_t ShaderFlags); } // namespace dxil } // namespace llvm diff --git a/llvm/lib/Target/DirectX/DXILResource.cpp b/llvm/lib/Target/DirectX/DXILResource.cpp index 53a40a1..518f77f 100644 --- a/llvm/lib/Target/DirectX/DXILResource.cpp +++ b/llvm/lib/Target/DirectX/DXILResource.cpp @@ -324,8 +324,13 @@ void Resources::write(Module &M) const { if (!UAVMDs.empty()) ResourceMDs[1] = MDNode::get(M.getContext(), UAVMDs); - NamedMDNode *DXResMD = M.getOrInsertNamedMetadata("dx.resources"); - DXResMD->addOperand(MDNode::get(M.getContext(), ResourceMDs)); + bool HasResource = ResourceMDs[0] != nullptr || ResourceMDs[1] != nullptr || + ResourceMDs[2] != nullptr || ResourceMDs[3] != nullptr; + + if (HasResource) { + NamedMDNode *DXResMD = M.getOrInsertNamedMetadata("dx.resources"); + DXResMD->addOperand(MDNode::get(M.getContext(), ResourceMDs)); + } NamedMDNode *Entry = M.getNamedMetadata("hlsl.uavs"); if (Entry) diff --git a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp index 37074ff..4995712 100644 --- a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp +++ b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp @@ -11,6 +11,7 @@ #include "DXILMetadata.h" #include "DXILResource.h" #include "DXILResourceAnalysis.h" +#include "DXILShaderFlags.h" #include "DirectX.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" @@ -20,6 +21,7 @@ #include "llvm/Pass.h" using namespace llvm; +using namespace llvm::dxil; namespace { class DXILTranslateMetadata : public ModulePass { @@ -32,6 +34,7 @@ public: void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); AU.addRequired(); + AU.addRequired(); } bool runOnModule(Module &M) override; @@ -49,6 +52,11 @@ bool DXILTranslateMetadata::runOnModule(Module &M) { const dxil::Resources &Res = getAnalysis().getDXILResource(); Res.write(M); + + const uint64_t Flags = + (uint64_t)(getAnalysis().getShaderFlags()); + dxil::createEntryMD(M, Flags); + return false; } @@ -61,5 +69,6 @@ ModulePass *llvm::createDXILTranslateMetadataPass() { INITIALIZE_PASS_BEGIN(DXILTranslateMetadata, "dxil-metadata-emit", "DXIL Metadata Emit", false, false) INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapper) +INITIALIZE_PASS_DEPENDENCY(ShaderFlagsAnalysisWrapper) INITIALIZE_PASS_END(DXILTranslateMetadata, "dxil-metadata-emit", "DXIL Metadata Emit", false, false) diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-as.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-as.ll index e3ccc46..fe3361c 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-as.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-as.ll @@ -3,3 +3,10 @@ target triple = "dxil-pc-shadermodel6-amplification" ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"as", i32 6, i32 0} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.shader"="amplification" } diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs.ll index 97c9c1b..be4b46f2 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs.ll @@ -3,3 +3,10 @@ target triple = "dxil-pc-shadermodel6.6-compute" ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"cs", i32 6, i32 6} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.numthreads"="1,2,1" "hlsl.shader"="compute" } diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-gs.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-gs.ll index 6775968..a0a1b7c 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-gs.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-gs.ll @@ -3,3 +3,10 @@ target triple = "dxil-pc-shadermodel6.6-geometry" ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"gs", i32 6, i32 6} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.shader"="geometry" } diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-hs.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-hs.ll index 1a1bd46..6b1fa46 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-hs.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-hs.ll @@ -3,3 +3,10 @@ target triple = "dxil-pc-shadermodel6.6-hull" ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"hs", i32 6, i32 6} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.shader"="hull" } diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ms.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ms.ll index 4a0dbb3..766e8e2 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ms.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ms.ll @@ -3,3 +3,10 @@ target triple = "dxil-pc-shadermodel6.6-mesh" ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"ms", i32 6, i32 6} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.shader"="mesh" } diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ps.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ps.ll index 2e89b63..46e8f3b 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ps.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ps.ll @@ -3,3 +3,10 @@ target triple = "dxil-pc-shadermodel5.0-pixel" ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"ps", i32 5, i32 0} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.shader"="pixel" } diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-vs.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-vs.ll index 9b7668b..7a0cfdf 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-vs.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-vs.ll @@ -3,3 +3,10 @@ target triple = "dxil-pc-shadermodel-vertex" ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"vs", i32 0, i32 0} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.shader"="vertex" } diff --git a/llvm/test/CodeGen/DirectX/UAVMetadata.ll b/llvm/test/CodeGen/DirectX/UAVMetadata.ll index 3520c0b..e86d53c 100644 --- a/llvm/test/CodeGen/DirectX/UAVMetadata.ll +++ b/llvm/test/CodeGen/DirectX/UAVMetadata.ll @@ -3,7 +3,7 @@ ; RUN: llc %s --filetype=asm -o - < %s 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" -target triple = "dxil-pc-shadermodel6.0-compute" +target triple = "dxil-pc-shadermodel6.0-library" %"class.hlsl::RWBuffer" = type { ptr } diff --git a/llvm/test/CodeGen/DirectX/empty_cs_entry.ll b/llvm/test/CodeGen/DirectX/empty_cs_entry.ll new file mode 100644 index 0000000..5fe8461 --- /dev/null +++ b/llvm/test/CodeGen/DirectX/empty_cs_entry.ll @@ -0,0 +1,17 @@ +; RUN: opt -S -dxil-metadata-emit < %s | FileCheck %s +target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" +target triple = "dxil-unknown-shadermodel6.7-compute" + +;CHECK:!dx.entryPoints = !{![[entry:[0-9]+]]} + +;CHECK:![[entry]] = !{ptr @entry, !"entry", null, null, ![[extra:[0-9]+]]} +;CHECK:![[extra]] = !{i32 4, ![[numthreads:[0-9]+]]} +;CHECK:![[numthreads]] = !{i32 1, i32 2, i32 1} + +; Function Attrs: noinline nounwind +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.numthreads"="1,2,1" "hlsl.shader"="compute" } diff --git a/llvm/test/CodeGen/DirectX/lib_entry.ll b/llvm/test/CodeGen/DirectX/lib_entry.ll new file mode 100644 index 0000000..9208d6d --- /dev/null +++ b/llvm/test/CodeGen/DirectX/lib_entry.ll @@ -0,0 +1,22 @@ +; RUN: opt -S -dxil-metadata-emit < %s | FileCheck %s +target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" +target triple = "dxil-unknown-shadermodel6.7-library" + +;CHECK:!dx.entryPoints = !{![[empty_entry:[0-9]+]], ![[entry:[0-9]+]]} + +; Make sure generate empty entry for lib profile. +;CHECK:![[empty_entry]] = !{null, !"", null, null, ![[shader_flags:[0-9]+]]} +; Make sure double is marked for shader flags. +;CHECK:![[shader_flags]] = !{i32 0, i64 1} +;CHECK:![[entry]] = !{ptr @entry, !"entry", null, null, ![[extra:[0-9]+]]} +;CHECK:![[extra]] = !{i32 8, i32 5, i32 4, ![[numthreads:[0-9]+]]} +;CHECK:![[numthreads]] = !{i32 1, i32 2, i32 1} + +; Function Attrs: noinline nounwind +define void @entry() #0 { +entry: + %0 = fpext float 2.000000e+00 to double + ret void +} + +attributes #0 = { noinline nounwind "hlsl.numthreads"="1,2,1" "hlsl.shader"="compute" } -- 2.7.4