diff --git a/llvm/include/llvm/BinaryFormat/DXContainer.h b/llvm/include/llvm/BinaryFormat/DXContainer.h --- a/llvm/include/llvm/BinaryFormat/DXContainer.h +++ b/llvm/include/llvm/BinaryFormat/DXContainer.h @@ -127,6 +127,19 @@ static_assert(sizeof(ProgramHeader) == 24, "ProgramHeader Size incorrect!"); +struct ProgramSignature { + uint32_t ParamCount; + uint32_t ParamOffset; + + void swapBytes() { + sys::swapByteOrder(ParamCount); + sys::swapByteOrder(ParamOffset); + } +}; + +static_assert(sizeof(ProgramSignature) == 8, + "ProgramSignature Size incorrect!"); + #define CONTAINER_PART(Part) Part, enum class PartType { Unknown = 0, diff --git a/llvm/include/llvm/BinaryFormat/DXContainerConstants.def b/llvm/include/llvm/BinaryFormat/DXContainerConstants.def --- a/llvm/include/llvm/BinaryFormat/DXContainerConstants.def +++ b/llvm/include/llvm/BinaryFormat/DXContainerConstants.def @@ -3,6 +3,8 @@ CONTAINER_PART(DXIL) CONTAINER_PART(SFI0) CONTAINER_PART(HASH) +CONTAINER_PART(ISG1) +CONTAINER_PART(OSG1) #undef CONTAINER_PART #endif diff --git a/llvm/include/llvm/Object/DXContainer.h b/llvm/include/llvm/Object/DXContainer.h --- a/llvm/include/llvm/Object/DXContainer.h +++ b/llvm/include/llvm/Object/DXContainer.h @@ -36,12 +36,15 @@ std::optional DXIL; std::optional ShaderFlags; std::optional Hash; + std::optional InputSig; + std::optional OutputSig; Error parseHeader(); Error parsePartOffsets(); Error parseDXILHeader(StringRef Part); Error parseShaderFlags(StringRef Part); Error parseHash(StringRef Part); + Error parseProgramSignature(StringRef Part, dxbc::ProgramSignature &Sig); friend class PartIterator; public: @@ -123,6 +126,13 @@ std::optional getShaderFlags() const { return ShaderFlags; } std::optional getShaderHash() const { return Hash; } + + std::optional getInputSignature() const { + return InputSig; + } + std::optional getOutputSignature() const { + return OutputSig; + } }; } // namespace object diff --git a/llvm/include/llvm/ObjectYAML/DXContainerYAML.h b/llvm/include/llvm/ObjectYAML/DXContainerYAML.h --- a/llvm/include/llvm/ObjectYAML/DXContainerYAML.h +++ b/llvm/include/llvm/ObjectYAML/DXContainerYAML.h @@ -71,6 +71,16 @@ std::vector Digest; }; +struct ProgramSignature { + ProgramSignature() = default; + ProgramSignature(const dxbc::ProgramSignature &Data); + // FIXME: support case when ParamCount is not 0. + // see issue https://github.com/llvm/llvm-project/issues/57743 and + // https://github.com/llvm/llvm-project/issues/57744. + uint32_t ParamCount; + uint32_t ParamOffset; +}; + struct Part { Part() = default; Part(std::string N, uint32_t S) : Name(N), Size(S) {} @@ -79,6 +89,8 @@ std::optional Program; std::optional Flags; std::optional Hash; + std::optional InputSig; + std::optional OutputSig; }; struct Object { @@ -116,6 +128,10 @@ static void mapping(IO &IO, DXContainerYAML::ShaderHash &Hash); }; +template <> struct MappingTraits { + static void mapping(IO &IO, DXContainerYAML::ProgramSignature &Sig); +}; + template <> struct MappingTraits { static void mapping(IO &IO, DXContainerYAML::Part &Version); }; diff --git a/llvm/lib/Object/DXContainer.cpp b/llvm/lib/Object/DXContainer.cpp --- a/llvm/lib/Object/DXContainer.cpp +++ b/llvm/lib/Object/DXContainer.cpp @@ -91,6 +91,14 @@ return Error::success(); } +Error DXContainer::parseProgramSignature(StringRef Part, + dxbc::ProgramSignature &Sig) { + if (Error Err = readStruct(Part, Part.begin(), Sig)) + return Err; + + return Error::success(); +} + Error DXContainer::parsePartOffsets() { uint32_t LastOffset = sizeof(dxbc::Header) + (Header.PartCount * sizeof(uint32_t)); @@ -140,6 +148,18 @@ if (Error Err = parseHash(PartData)) return Err; break; + case dxbc::PartType::ISG1: { + dxbc::ProgramSignature Sig; + if (Error Err = parseProgramSignature(PartData, Sig)) + return Err; + InputSig = Sig; + } break; + case dxbc::PartType::OSG1: { + dxbc::ProgramSignature Sig; + if (Error Err = parseProgramSignature(PartData, Sig)) + return Err; + OutputSig = Sig; + } break; case dxbc::PartType::Unknown: break; } diff --git a/llvm/lib/ObjectYAML/DXContainerEmitter.cpp b/llvm/lib/ObjectYAML/DXContainerEmitter.cpp --- a/llvm/lib/ObjectYAML/DXContainerEmitter.cpp +++ b/llvm/lib/ObjectYAML/DXContainerEmitter.cpp @@ -193,6 +193,17 @@ OS.write(reinterpret_cast(&Hash), sizeof(dxbc::ShaderHash)); break; } + case dxbc::PartType::ISG1: + case dxbc::PartType::OSG1: { + bool IsInput = PT == dxbc::PartType::ISG1; + dxbc::ProgramSignature Sig = { + IsInput ? P.InputSig->ParamCount : P.OutputSig->ParamCount, + IsInput ? P.InputSig->ParamOffset : P.OutputSig->ParamOffset}; + if (sys::IsBigEndianHost) + Sig.swapBytes(); + + OS.write(reinterpret_cast(&Sig), sizeof(dxbc::ProgramSignature)); + } break; case dxbc::PartType::Unknown: break; // Skip any handling for unrecognized parts. } diff --git a/llvm/lib/ObjectYAML/DXContainerYAML.cpp b/llvm/lib/ObjectYAML/DXContainerYAML.cpp --- a/llvm/lib/ObjectYAML/DXContainerYAML.cpp +++ b/llvm/lib/ObjectYAML/DXContainerYAML.cpp @@ -43,6 +43,10 @@ memcpy(Digest.data(), &Data.Digest[0], 16); } +DXContainerYAML::ProgramSignature::ProgramSignature( + const dxbc::ProgramSignature &Data) + : ParamCount(Data.ParamCount), ParamOffset(Data.ParamOffset) {} + namespace yaml { void MappingTraits::mapping( @@ -78,6 +82,12 @@ #include "llvm/BinaryFormat/DXContainerConstants.def" } +void MappingTraits::mapping( + IO &IO, DXContainerYAML::ProgramSignature &Sig) { + IO.mapRequired("ParamCount", Sig.ParamCount); + IO.mapRequired("ParamOffset", Sig.ParamOffset); +} + void MappingTraits::mapping( IO &IO, DXContainerYAML::ShaderHash &Hash) { IO.mapRequired("IncludesSource", Hash.IncludesSource); @@ -91,6 +101,8 @@ IO.mapOptional("Program", P.Program); IO.mapOptional("Flags", P.Flags); IO.mapOptional("Hash", P.Hash); + IO.mapOptional("InputSig", P.InputSig); + IO.mapOptional("OutputSig", P.OutputSig); } void MappingTraits::mapping( diff --git a/llvm/lib/Target/DirectX/DXContainerGlobals.cpp b/llvm/lib/Target/DirectX/DXContainerGlobals.cpp --- a/llvm/lib/Target/DirectX/DXContainerGlobals.cpp +++ b/llvm/lib/Target/DirectX/DXContainerGlobals.cpp @@ -21,14 +21,21 @@ #include "llvm/Support/MD5.h" #include "llvm/Transforms/Utils/ModuleUtils.h" +#define DEBUG_TYPE "dxil-globals" + using namespace llvm; using namespace llvm::dxil; namespace { class DXContainerGlobals : public llvm::ModulePass { + GlobalVariable *buildContainerGlobal(Module &M, Constant *Content, + StringRef Name, StringRef SectionName); GlobalVariable *getShaderFlags(Module &M); GlobalVariable *computeShaderHash(Module &M); + GlobalVariable *buildInputSingature(Module &M, Constant *Content); + GlobalVariable *buildOutputSingature(Module &M, Constant *Content); + void addSingature(Module &M, SmallVector &Globals); public: static char ID; // Pass identification, replacement for typeid @@ -54,7 +61,7 @@ llvm::SmallVector Globals; Globals.push_back(getShaderFlags(M)); Globals.push_back(computeShaderHash(M)); - + addSingature(M, Globals); appendToCompilerUsed(M, Globals); return true; } @@ -100,11 +107,45 @@ return GV; } +GlobalVariable *DXContainerGlobals::buildContainerGlobal( + Module &M, Constant *Content, StringRef Name, StringRef SectionName) { + auto *GV = new llvm::GlobalVariable( + M, Content->getType(), true, GlobalValue::PrivateLinkage, Content, Name); + GV->setSection(SectionName); + GV->setAlignment(Align(4)); + return GV; +} + +GlobalVariable *DXContainerGlobals::buildInputSingature(Module &M, + Constant *Content) { + return buildContainerGlobal(M, Content, "dx.isg1", "ISG1"); +} +GlobalVariable *DXContainerGlobals::buildOutputSingature(Module &M, + Constant *Content) { + return buildContainerGlobal(M, Content, "dx.osg1", "OSG1"); +} +void DXContainerGlobals::addSingature(Module &M, + SmallVector &Globals) { + dxbc::ProgramSignature Sig; + Sig.ParamCount = 0; + Sig.ParamOffset = sizeof(dxbc::ProgramSignature); + // FIXME: support case when ParamCount is not 0. + // see issue https://github.com/llvm/llvm-project/issues/57743 and + // https://github.com/llvm/llvm-project/issues/57744. + Type *Int32Ty = Type::getInt32Ty(M.getContext()); + Constant *EmptySig = ConstantStruct::get( + StructType::get(Int32Ty, Int32Ty), + {ConstantInt::get(M.getContext(), APInt(32, Sig.ParamCount)), + ConstantInt::get(M.getContext(), APInt(32, Sig.ParamOffset))}); + Globals.emplace_back(buildInputSingature(M, EmptySig)); + Globals.emplace_back(buildOutputSingature(M, EmptySig)); +} + char DXContainerGlobals::ID = 0; -INITIALIZE_PASS_BEGIN(DXContainerGlobals, "dxil-globals", +INITIALIZE_PASS_BEGIN(DXContainerGlobals, DEBUG_TYPE, "DXContainer Global Emitter", false, true) INITIALIZE_PASS_DEPENDENCY(ShaderFlagsAnalysisWrapper) -INITIALIZE_PASS_END(DXContainerGlobals, "dxil-globals", +INITIALIZE_PASS_END(DXContainerGlobals, DEBUG_TYPE, "DXContainer Global Emitter", false, true) ModulePass *llvm::createDXContainerGlobalsPass() { diff --git a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp --- a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp +++ b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp @@ -46,6 +46,7 @@ initializeDXILTranslateMetadataPass(*PR); initializeDXILResourceWrapperPass(*PR); initializeShaderFlagsAnalysisWrapperPass(*PR); + initializeDXContainerGlobalsPass(*PR); } class DXILTargetObjectFile : public TargetLoweringObjectFile { diff --git a/llvm/test/CodeGen/DirectX/ContainerData/EmptyProgramSignature.ll b/llvm/test/CodeGen/DirectX/ContainerData/EmptyProgramSignature.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/DirectX/ContainerData/EmptyProgramSignature.ll @@ -0,0 +1,37 @@ +; RUN:opt -S -dxil-globals 2>&1 %s | FileCheck %s + +; Make sure empty program signature generated for compute shader. +; CHECK:@dx.isg1 = private constant { i32, i32 } { i32 0, i32 8 }, section "ISG1", align 4 +; CHECK:@dx.osg1 = private constant { i32, i32 } { i32 0, i32 8 }, section "OSG1", align 4 + +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.0-compute" + +@dx.dxil = private constant [1 x i8] c"0", section "DXIL", align 4 +@llvm.compiler.used = appending global [1 x ptr] [ptr @dx.dxil], section "llvm.metadata" + +; Function Attrs: nounwind memory(readwrite, inaccessiblemem: read) +define void @CSMain() local_unnamed_addr #0 { + ret void +} + + +attributes #0 = { nounwind memory(readwrite, inaccessiblemem: read) } + +!dx.valver = !{!0} +!llvm.ident = !{!1} +!dx.shaderModel = !{!2} +!dx.version = !{!3} + +!dx.entryPoints = !{!4} +!llvm.module.flags = !{!7} + +!0 = !{i32 1, i32 7} +!1 = !{!"clang version 16.0.0 (https://github.com/llvm/llvm-project.git 9641897a6b7b727fd5139d7bf92cb6b027037193)"} +!2 = !{!"cs", i32 6, i32 0} +!3 = !{i32 1, i32 0} + +!4 = !{ptr @CSMain, !"CSMain", null, null, !5} +!5 = !{i32 4, !6} +!6 = !{i32 1024, i32 1, i32 1} +!7 = !{i32 1, !"wchar_size", i32 4} diff --git a/llvm/test/tools/obj2yaml/DXContainer/EmptyProgramSignature.yaml b/llvm/test/tools/obj2yaml/DXContainer/EmptyProgramSignature.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/obj2yaml/DXContainer/EmptyProgramSignature.yaml @@ -0,0 +1,35 @@ +# RUN: yaml2obj %s | obj2yaml | FileCheck %s + +# This test verifies that empty program signature works. +--- !dxcontainer +Header: + Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ] + Version: + Major: 1 + Minor: 0 + PartCount: 2 +Parts: + - Name: ISG1 + Size: 8 + InputSig: + ParamCount: 0 + ParamOffset: 8 + - Name: OSG1 + Size: 8 + OutputSig: + ParamCount: 0 + ParamOffset: 8 +... + +# CHECK: Parts: +# CHECK-NEXT: - Name: ISG1 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: InputSig: +# CHECK-NEXT: ParamCount: 0 +# CHECK-NEXT: ParamOffset: 8 +# CHECK-NEXT: - Name: OSG1 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: OutputSig: +# CHECK-NEXT: ParamCount: 0 +# CHECK-NEXT: ParamOffset: 8 diff --git a/llvm/tools/obj2yaml/dxcontainer2yaml.cpp b/llvm/tools/obj2yaml/dxcontainer2yaml.cpp --- a/llvm/tools/obj2yaml/dxcontainer2yaml.cpp +++ b/llvm/tools/obj2yaml/dxcontainer2yaml.cpp @@ -73,6 +73,17 @@ NewPart.Hash = DXContainerYAML::ShaderHash(*Hash); break; } + case dxbc::PartType::ISG1: { + std::optional Sig = Container.getInputSignature(); + if (Sig) + NewPart.InputSig = DXContainerYAML::ProgramSignature(*Sig); + } break; + case dxbc::PartType::OSG1: { + std::optional Sig = + Container.getOutputSignature(); + if (Sig) + NewPart.OutputSig = DXContainerYAML::ProgramSignature(*Sig); + } break; case dxbc::PartType::Unknown: break; }