[HLSL][RootSignature] Add metadata generation for descriptor tables (#139633)
- prereq: Modify `RootSignatureAttr` to hold a reference to the owned declaration - Define and implement `MetadataBuilder` in `HLSLRootSignature` - Integrate and invoke the builder in `CGHLSLRuntime.cpp` to generate the Root Signature for any associated entry functions - Add tests to demonstrate functionality in `RootSignature.hlsl` Resolves https://github.com/llvm/llvm-project/issues/126584 Note: this is essentially just https://github.com/llvm/llvm-project/pull/125131 rebased onto the new approach of constructing a root signature decl, instead of holding the elements in `AdditionalMembers`.
This commit is contained in:
parent
2e6433b829
commit
a4eb0db062
@ -4739,7 +4739,8 @@ def Error : InheritableAttr {
|
||||
def RootSignature : Attr {
|
||||
/// [RootSignature(Signature)]
|
||||
let Spellings = [Microsoft<"RootSignature">];
|
||||
let Args = [IdentifierArgument<"Signature">];
|
||||
let Args = [IdentifierArgument<"SignatureIdent">,
|
||||
DeclArgument<HLSLRootSignature, "SignatureDecl", 0, /*fake=*/1>];
|
||||
let Subjects = SubjectList<[Function],
|
||||
ErrorDiag, "'function'">;
|
||||
let LangOpts = [HLSL];
|
||||
|
||||
@ -68,6 +68,20 @@ void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) {
|
||||
DXILValMD->addOperand(Val);
|
||||
}
|
||||
|
||||
void addRootSignature(ArrayRef<llvm::hlsl::rootsig::RootElement> Elements,
|
||||
llvm::Function *Fn, llvm::Module &M) {
|
||||
auto &Ctx = M.getContext();
|
||||
|
||||
llvm::hlsl::rootsig::MetadataBuilder Builder(Ctx, Elements);
|
||||
MDNode *RootSignature = Builder.BuildRootSignature();
|
||||
MDNode *FnPairing =
|
||||
MDNode::get(Ctx, {ValueAsMetadata::get(Fn), RootSignature});
|
||||
|
||||
StringRef RootSignatureValKey = "dx.rootsignatures";
|
||||
auto *RootSignatureValMD = M.getOrInsertNamedMetadata(RootSignatureValKey);
|
||||
RootSignatureValMD->addOperand(FnPairing);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
llvm::Type *
|
||||
@ -423,6 +437,13 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
|
||||
// FIXME: Handle codegen for return type semantics.
|
||||
// See: https://github.com/llvm/llvm-project/issues/57875
|
||||
B.CreateRetVoid();
|
||||
|
||||
// Add and identify root signature to function, if applicable
|
||||
for (const Attr *Attr : FD->getAttrs()) {
|
||||
if (const auto *RSAttr = dyn_cast<RootSignatureAttr>(Attr))
|
||||
addRootSignature(RSAttr->getSignatureDecl()->getRootElements(), EntryFn,
|
||||
M);
|
||||
}
|
||||
}
|
||||
|
||||
void CGHLSLRuntime::setHLSLFunctionAttributes(const FunctionDecl *FD,
|
||||
|
||||
@ -959,7 +959,7 @@ void SemaHLSL::handleRootSignatureAttr(Decl *D, const ParsedAttr &AL) {
|
||||
|
||||
IdentifierInfo *Ident = AL.getArgAsIdent(0)->getIdentifierInfo();
|
||||
if (auto *RS = D->getAttr<RootSignatureAttr>()) {
|
||||
if (RS->getSignature() != Ident) {
|
||||
if (RS->getSignatureIdent() != Ident) {
|
||||
Diag(AL.getLoc(), diag::err_disallowed_duplicate_attribute) << RS;
|
||||
return;
|
||||
}
|
||||
@ -970,10 +970,11 @@ void SemaHLSL::handleRootSignatureAttr(Decl *D, const ParsedAttr &AL) {
|
||||
|
||||
LookupResult R(SemaRef, Ident, SourceLocation(), Sema::LookupOrdinaryName);
|
||||
if (SemaRef.LookupQualifiedName(R, D->getDeclContext()))
|
||||
if (isa<HLSLRootSignatureDecl>(R.getFoundDecl())) {
|
||||
if (auto *SignatureDecl =
|
||||
dyn_cast<HLSLRootSignatureDecl>(R.getFoundDecl())) {
|
||||
// Perform validation of constructs here
|
||||
D->addAttr(::new (getASTContext())
|
||||
RootSignatureAttr(getASTContext(), AL, Ident));
|
||||
D->addAttr(::new (getASTContext()) RootSignatureAttr(
|
||||
getASTContext(), AL, Ident, SignatureDecl));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
31
clang/test/CodeGenHLSL/RootSignature.hlsl
Normal file
31
clang/test/CodeGenHLSL/RootSignature.hlsl
Normal file
@ -0,0 +1,31 @@
|
||||
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
// CHECK: !dx.rootsignatures = !{![[#FIRST_ENTRY:]], ![[#SECOND_ENTRY:]]}
|
||||
|
||||
// CHECK: ![[#FIRST_ENTRY]] = !{ptr @FirstEntry, ![[#EMPTY:]]}
|
||||
// CHECK: ![[#EMPTY]] = !{}
|
||||
|
||||
[shader("compute"), RootSignature("")]
|
||||
[numthreads(1,1,1)]
|
||||
void FirstEntry() {}
|
||||
|
||||
// CHECK: ![[#SECOND_ENTRY]] = !{ptr @SecondEntry, ![[#SECOND_RS:]]}
|
||||
// CHECK: ![[#SECOND_RS]] = !{![[#TABLE:]]}
|
||||
// CHECK: ![[#TABLE]] = !{!"DescriptorTable", i32 0, ![[#CBV:]], ![[#SRV:]]}
|
||||
// CHECK: ![[#CBV]] = !{!"CBV", i32 1, i32 0, i32 0, i32 -1, i32 4}
|
||||
// CHECK: ![[#SRV]] = !{!"SRV", i32 4, i32 42, i32 3, i32 32, i32 0}
|
||||
|
||||
#define SampleDescriptorTable \
|
||||
"DescriptorTable( " \
|
||||
" CBV(b0), " \
|
||||
" SRV(t42, space = 3, offset = 32, numDescriptors = 4, flags = 0) " \
|
||||
")"
|
||||
[shader("compute"), RootSignature(SampleDescriptorTable)]
|
||||
[numthreads(1,1,1)]
|
||||
void SecondEntry() {}
|
||||
|
||||
// Sanity test to ensure no root is added for this function as there is only
|
||||
// two entries in !dx.roosignatures
|
||||
[shader("compute")]
|
||||
[numthreads(1,1,1)]
|
||||
void ThirdEntry() {}
|
||||
@ -20,6 +20,10 @@
|
||||
#include <variant>
|
||||
|
||||
namespace llvm {
|
||||
class LLVMContext;
|
||||
class MDNode;
|
||||
class Metadata;
|
||||
|
||||
namespace hlsl {
|
||||
namespace rootsig {
|
||||
|
||||
@ -84,7 +88,9 @@ struct RootConstants {
|
||||
// Models the end of a descriptor table and stores its visibility
|
||||
struct DescriptorTable {
|
||||
ShaderVisibility Visibility = ShaderVisibility::All;
|
||||
uint32_t NumClauses = 0; // The number of clauses in the table
|
||||
// Denotes that the previous NumClauses in the RootElement array
|
||||
// are the clauses in the table.
|
||||
uint32_t NumClauses = 0;
|
||||
|
||||
void dump(raw_ostream &OS) const;
|
||||
};
|
||||
@ -119,12 +125,47 @@ struct DescriptorTableClause {
|
||||
void dump(raw_ostream &OS) const;
|
||||
};
|
||||
|
||||
// Models RootElement : RootConstants | DescriptorTable | DescriptorTableClause
|
||||
/// Models RootElement : RootFlags | RootConstants | DescriptorTable
|
||||
/// | DescriptorTableClause
|
||||
///
|
||||
/// A Root Signature is modeled in-memory by an array of RootElements. These
|
||||
/// aim to map closely to their DSL grammar reprsentation defined in the spec.
|
||||
///
|
||||
/// Each optional parameter has its default value defined in the struct, and,
|
||||
/// each mandatory parameter does not have a default initialization.
|
||||
///
|
||||
/// For the variants RootFlags, RootConstants and DescriptorTableClause: each
|
||||
/// data member maps directly to a parameter in the grammar.
|
||||
///
|
||||
/// The DescriptorTable is modelled by having its Clauses as the previous
|
||||
/// RootElements in the array, and it holds a data member for the Visibility
|
||||
/// parameter.
|
||||
using RootElement = std::variant<RootFlags, RootConstants, DescriptorTable,
|
||||
DescriptorTableClause>;
|
||||
|
||||
void dumpRootElements(raw_ostream &OS, ArrayRef<RootElement> Elements);
|
||||
|
||||
class MetadataBuilder {
|
||||
public:
|
||||
MetadataBuilder(llvm::LLVMContext &Ctx, ArrayRef<RootElement> Elements)
|
||||
: Ctx(Ctx), Elements(Elements) {}
|
||||
|
||||
/// Iterates through the elements and dispatches onto the correct Build method
|
||||
///
|
||||
/// Accumulates the root signature and returns the Metadata node that is just
|
||||
/// a list of all the elements
|
||||
MDNode *BuildRootSignature();
|
||||
|
||||
private:
|
||||
/// Define the various builders for the different metadata types
|
||||
MDNode *BuildDescriptorTable(const DescriptorTable &Table);
|
||||
MDNode *BuildDescriptorTableClause(const DescriptorTableClause &Clause);
|
||||
|
||||
llvm::LLVMContext &Ctx;
|
||||
ArrayRef<RootElement> Elements;
|
||||
SmallVector<Metadata *> GeneratedMetadata;
|
||||
};
|
||||
|
||||
} // namespace rootsig
|
||||
} // namespace hlsl
|
||||
} // namespace llvm
|
||||
|
||||
@ -12,6 +12,9 @@
|
||||
|
||||
#include "llvm/Frontend/HLSL/HLSLRootSignature.h"
|
||||
#include "llvm/ADT/bit.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/Metadata.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace hlsl {
|
||||
@ -160,6 +163,65 @@ void dumpRootElements(raw_ostream &OS, ArrayRef<RootElement> Elements) {
|
||||
OS << "}";
|
||||
}
|
||||
|
||||
MDNode *MetadataBuilder::BuildRootSignature() {
|
||||
for (const RootElement &Element : Elements) {
|
||||
MDNode *ElementMD = nullptr;
|
||||
if (const auto &Clause = std::get_if<DescriptorTableClause>(&Element))
|
||||
ElementMD = BuildDescriptorTableClause(*Clause);
|
||||
if (const auto &Table = std::get_if<DescriptorTable>(&Element))
|
||||
ElementMD = BuildDescriptorTable(*Table);
|
||||
|
||||
// FIXME(#126586): remove once all RootElemnt variants are handled in a
|
||||
// visit or otherwise
|
||||
assert(ElementMD != nullptr &&
|
||||
"Constructed an unhandled root element type.");
|
||||
|
||||
GeneratedMetadata.push_back(ElementMD);
|
||||
}
|
||||
|
||||
return MDNode::get(Ctx, GeneratedMetadata);
|
||||
}
|
||||
|
||||
MDNode *MetadataBuilder::BuildDescriptorTable(const DescriptorTable &Table) {
|
||||
IRBuilder<> Builder(Ctx);
|
||||
SmallVector<Metadata *> TableOperands;
|
||||
// Set the mandatory arguments
|
||||
TableOperands.push_back(MDString::get(Ctx, "DescriptorTable"));
|
||||
TableOperands.push_back(ConstantAsMetadata::get(
|
||||
Builder.getInt32(llvm::to_underlying(Table.Visibility))));
|
||||
|
||||
// Remaining operands are references to the table's clauses. The in-memory
|
||||
// representation of the Root Elements created from parsing will ensure that
|
||||
// the previous N elements are the clauses for this table.
|
||||
assert(Table.NumClauses <= GeneratedMetadata.size() &&
|
||||
"Table expected all owned clauses to be generated already");
|
||||
// So, add a refence to each clause to our operands
|
||||
TableOperands.append(GeneratedMetadata.end() - Table.NumClauses,
|
||||
GeneratedMetadata.end());
|
||||
// Then, remove those clauses from the general list of Root Elements
|
||||
GeneratedMetadata.pop_back_n(Table.NumClauses);
|
||||
|
||||
return MDNode::get(Ctx, TableOperands);
|
||||
}
|
||||
|
||||
MDNode *MetadataBuilder::BuildDescriptorTableClause(
|
||||
const DescriptorTableClause &Clause) {
|
||||
IRBuilder<> Builder(Ctx);
|
||||
std::string Name;
|
||||
llvm::raw_string_ostream OS(Name);
|
||||
OS << Clause.Type;
|
||||
return MDNode::get(
|
||||
Ctx, {
|
||||
MDString::get(Ctx, OS.str()),
|
||||
ConstantAsMetadata::get(Builder.getInt32(Clause.NumDescriptors)),
|
||||
ConstantAsMetadata::get(Builder.getInt32(Clause.Reg.Number)),
|
||||
ConstantAsMetadata::get(Builder.getInt32(Clause.Space)),
|
||||
ConstantAsMetadata::get(Builder.getInt32(Clause.Offset)),
|
||||
ConstantAsMetadata::get(
|
||||
Builder.getInt32(llvm::to_underlying(Clause.Flags))),
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace rootsig
|
||||
} // namespace hlsl
|
||||
} // namespace llvm
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user