-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[HLSL][DirectX] Add support for rootsig
as a target environment
#156373
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: users/inbelic/pr-156372
Are you sure you want to change the base?
Conversation
// Skim through the file to parse to find the define | ||
while (P->getCurToken().getKind() != tok::eof) | ||
P->ConsumeAnyToken(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason this is required when invoking with clang-dxc
. Without it the OBJ
portion of this test case will fail with an error that EntryRS
is not defined.
If these lines are removed all other test cases still pass. The other test-cases are all invoked directly with clang -cc1
and will invoke the MacroDefined
callback during the first lex in P->Initialize()
.
Perhaps there is some option that clang-dxc
invokes clang -cc1
that disables macro expansion before directly needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From offline: this is caused because clang-dxc
will add the -finclude-default-header
option to the invocation of clang -cc1
(thanks Chris for helping me find this).
With reference to the test case.
When we include the header, it means that P->Initialize()
will start parsing from the header file and encounter the first non-preprocessor token there. So we will not have encountered our EntryRS
define in the source file yet.
When we invoke this without the default header, the first non-preprocessor token will be tok::eof
of our source file and EntryRS
will have been found. To solve this, we can either:
- Remove the
-finclude-default-header
when invokingclang cc1
fromclang-dxc
with the root signature target. - Keep this logic in to skim through the file
Noting that DXC will happily ignore any code in the source file, it seems like option 2. makes more sense. For instance,
dxc -T rootsig1_1 -E EntryRS %s
will compile this fine by ignoring the function definition (or any other code):
void some_func() {}
#define EntryRS "CBV(b0)"
Whereas, if we remove the skimming logic above, clang-dxc -T rootsig1_1 -E EntryRS %s
would not parse enough tokens to encounter the macro and issue the error that EntryRS
is not defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we can remove -finclude-default-header
because that may cause the input file to have parse errors, which could impede finding the define. I agree that consuming the file seems necessary here.
This reverts commit 50d72e6.
- P->Initialize() will only parse until the first non-preprocessor token is found. If a user specifies any code before their entry root signature macro definition, we would not find the definition and incorrectly report that it is not defined This change ensures that we will parse through the entire source file to resolve our macro and match DXC's behaviour
if (getCurrentFileKind().getLanguage() != Language::HLSL) | ||
return WrapperFrontendAction::ExecuteAction(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can we get here? I don't really understand what the added language check here is doing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. TLDR: it is not required and I forgot to remove it from early iterations.
It was originally added because I was hitting this assert when invoking with -emit-obj <file>
and so I thought it was because it was first creating a .ll
file and then creating an object from that as two separate jobs. Turns out it is because you need to specify the file as -emit-obj -o <file>
.
bool HaveLexer = S.getPreprocessor().getCurrentLexer(); | ||
if (HaveLexer) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When can we not have a lexer? Is this the case where we try to do this without any input file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took the same pre-caution as here. So it prevents a faulty pre-compiled header to cause further issues, perhaps that is not a concern for us?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, matching the similar logic seems fine here.
// Skim through the file to parse to find the define | ||
while (P->getCurToken().getKind() != tok::eof) | ||
P->ConsumeAnyToken(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we can remove -finclude-default-header
because that may cause the input file to have parse errors, which could impede finding the define. I agree that consuming the file seems necessary here.
if (M.getTargetTriple().getEnvironment() == | ||
Triple::EnvironmentType::RootSignature) { | ||
assert(RootSignatureNode->getNumOperands() == 1); | ||
MDNode *RSDefNode = RootSignatureNode->getOperand(0); | ||
HandleNode(RSDefNode, true); | ||
return RSDMap; | ||
} | ||
|
||
for (MDNode *RSDefNode : RootSignatureNode->operands()) | ||
HandleNode(RSDefNode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's mostly a matter of style, but I think it might be slightly more readable to keep this as a loop rather than pulling the lambda out like this. You'd need to do the root signature book keeping before the loop:
bool AllowNullFunctions = false;
if (M.getTargetTriple().getEnvironment() ==
Triple::EnvironmentType::RootSignature) {
assert(RootSignatureNode->getNumOperands() == 1);
AllowNullFunctions = true;
}
for (MDNode *RSDefNode : RootSignatureNode->operands())
// ...
What do you think?
@llvm/pr-subscribers-hlsl @llvm/pr-subscribers-backend-directx Author: Finn Plummer (inbelic) ChangesThis pr implements support for a root signature as a target, as specified here. This is implemented in the following steps:
Resolves: #150286. Implementation ConsiderationsIdeally we could invoke this as part of Technically, we could avoid generating any of the extra information that is removed in step 6. However, it seems better to re-use the logic in Patch is 30.74 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/156373.diff 24 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 29364c5903d31..f9a8f2f555fbe 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1185,6 +1185,7 @@ static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environm
.Case("callable", llvm::Triple::Callable)
.Case("mesh", llvm::Triple::Mesh)
.Case("amplification", llvm::Triple::Amplification)
+ .Case("rootsignature", llvm::Triple::RootSignature)
.Case("library", llvm::Triple::Library)
.Default(llvm::Triple::UnknownEnvironment);
}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c934fed2c7462..f1d64cdca5da4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13136,6 +13136,8 @@ def err_hlsl_attribute_needs_intangible_type: Error<"attribute %0 can be used on
def err_hlsl_incorrect_num_initializers: Error<
"too %select{few|many}0 initializers in list for type %1 "
"(expected %2 but found %3)">;
+def err_hlsl_rootsignature_entry: Error<
+ "rootsignature specified as target environment but entry, %0, was not defined">;
def err_hlsl_operator_unsupported : Error<
"the '%select{&|*|->}0' operator is unsupported in HLSL">;
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index f507968d30670..0ca2baada1421 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9430,7 +9430,8 @@ def target_profile : DXCJoinedOrSeparate<"T">, MetaVarName<"<profile>">,
"cs_6_0, cs_6_1, cs_6_2, cs_6_3, cs_6_4, cs_6_5, cs_6_6, cs_6_7,"
"lib_6_3, lib_6_4, lib_6_5, lib_6_6, lib_6_7, lib_6_x,"
"ms_6_5, ms_6_6, ms_6_7,"
- "as_6_5, as_6_6, as_6_7">;
+ "as_6_5, as_6_6, as_6_7,"
+ "rootsig_1_0, rootsig_1_1">;
def emit_pristine_llvm : DXCFlag<"emit-pristine-llvm">,
HelpText<"Emit pristine LLVM IR from the frontend by not running any LLVM passes at all."
"Same as -S + -emit-llvm + -disable-llvm-passes.">;
diff --git a/clang/include/clang/Parse/ParseHLSLRootSignature.h b/clang/include/clang/Parse/ParseHLSLRootSignature.h
index c87e6637c7fce..b06846fd83c09 100644
--- a/clang/include/clang/Parse/ParseHLSLRootSignature.h
+++ b/clang/include/clang/Parse/ParseHLSLRootSignature.h
@@ -240,6 +240,8 @@ IdentifierInfo *ParseHLSLRootSignature(Sema &Actions,
llvm::dxbc::RootSignatureVersion Version,
StringLiteral *Signature);
+void HandleRootSignatureTarget(Sema &S, StringRef EntryRootSig);
+
} // namespace hlsl
} // namespace clang
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 5cbe1b658f5cd..4bad26e7a09a7 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -157,6 +157,8 @@ class SemaHLSL : public SemaBase {
RootSigOverrideIdent = DeclIdent;
}
+ HLSLRootSignatureDecl *lookupRootSignatureOverrideDecl(DeclContext *DC) const;
+
// Returns true if any RootSignatureElement is invalid and a diagnostic was
// produced
bool
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index f32d01ae78658..4370c0082ce7f 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -67,9 +67,9 @@ void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) {
DXILValMD->addOperand(Val);
}
-void addRootSignature(llvm::dxbc::RootSignatureVersion RootSigVer,
- ArrayRef<llvm::hlsl::rootsig::RootElement> Elements,
- llvm::Function *Fn, llvm::Module &M) {
+void addRootSignatureMD(llvm::dxbc::RootSignatureVersion RootSigVer,
+ ArrayRef<llvm::hlsl::rootsig::RootElement> Elements,
+ llvm::Function *Fn, llvm::Module &M) {
auto &Ctx = M.getContext();
llvm::hlsl::rootsig::MetadataBuilder RSBuilder(Ctx, Elements);
@@ -77,8 +77,8 @@ void addRootSignature(llvm::dxbc::RootSignatureVersion RootSigVer,
ConstantAsMetadata *Version = ConstantAsMetadata::get(ConstantInt::get(
llvm::Type::getInt32Ty(Ctx), llvm::to_underlying(RootSigVer)));
- MDNode *MDVals =
- MDNode::get(Ctx, {ValueAsMetadata::get(Fn), RootSignature, Version});
+ ValueAsMetadata *EntryFunc = Fn ? ValueAsMetadata::get(Fn) : nullptr;
+ MDNode *MDVals = MDNode::get(Ctx, {EntryFunc, RootSignature, Version});
StringRef RootSignatureValKey = "dx.rootsignatures";
auto *RootSignatureValMD = M.getOrInsertNamedMetadata(RootSignatureValKey);
@@ -381,6 +381,20 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) {
}
}
+void CGHLSLRuntime::addRootSignature(
+ const HLSLRootSignatureDecl *SignatureDecl) {
+ llvm::Module &M = CGM.getModule();
+ Triple T(M.getTargetTriple());
+
+ // If we are not targeting a root signature enviornment then this decl will
+ // be generated when the function decl it is attached is handled
+ if (T.getEnvironment() != Triple::EnvironmentType::RootSignature)
+ return;
+
+ addRootSignatureMD(SignatureDecl->getVersion(),
+ SignatureDecl->getRootElements(), nullptr, M);
+}
+
llvm::TargetExtType *
CGHLSLRuntime::getHLSLBufferLayoutType(const RecordType *StructType) {
const auto Entry = LayoutTypes.find(StructType);
@@ -584,8 +598,8 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
for (const Attr *Attr : FD->getAttrs()) {
if (const auto *RSAttr = dyn_cast<RootSignatureAttr>(Attr)) {
auto *RSDecl = RSAttr->getSignatureDecl();
- addRootSignature(RSDecl->getVersion(), RSDecl->getRootElements(), EntryFn,
- M);
+ addRootSignatureMD(RSDecl->getVersion(), RSDecl->getRootElements(),
+ EntryFn, M);
}
}
}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index b872f9ef0e9b6..613d0ad6bca5c 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -62,6 +62,7 @@ class VarDecl;
class ParmVarDecl;
class InitListExpr;
class HLSLBufferDecl;
+class HLSLRootSignatureDecl;
class HLSLVkBindingAttr;
class HLSLResourceBindingAttr;
class Type;
@@ -150,6 +151,7 @@ class CGHLSLRuntime {
void generateGlobalCtorDtorCalls();
void addBuffer(const HLSLBufferDecl *D);
+ void addRootSignature(const HLSLRootSignatureDecl *D);
void finishCodeGen();
void setHLSLEntryAttributes(const FunctionDecl *FD, llvm::Function *Fn);
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 323823c964a79..f90a8196ecced 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -7535,7 +7535,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
break;
case Decl::HLSLRootSignature:
- // Will be handled by attached function
+ getHLSLRuntime().addRootSignature(cast<HLSLRootSignatureDecl>(D));
break;
case Decl::HLSLBuffer:
getHLSLRuntime().addBuffer(cast<HLSLBufferDecl>(D));
diff --git a/clang/lib/Driver/ToolChains/HLSL.cpp b/clang/lib/Driver/ToolChains/HLSL.cpp
index 660661945d62a..543ab268540da 100644
--- a/clang/lib/Driver/ToolChains/HLSL.cpp
+++ b/clang/lib/Driver/ToolChains/HLSL.cpp
@@ -62,11 +62,15 @@ bool isLegalShaderModel(Triple &T) {
VersionTuple MinVer(6, 5);
return MinVer <= Version;
} break;
+ case Triple::EnvironmentType::RootSignature:
+ VersionTuple MinVer(1, 0);
+ VersionTuple MaxVer(1, 1);
+ return MinVer <= Version && Version <= MaxVer;
}
return false;
}
-std::optional<std::string> tryParseProfile(StringRef Profile) {
+std::optional<llvm::Triple> tryParseTriple(StringRef Profile) {
// [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor]
SmallVector<StringRef, 3> Parts;
Profile.split(Parts, "_");
@@ -84,6 +88,7 @@ std::optional<std::string> tryParseProfile(StringRef Profile) {
.Case("lib", Triple::EnvironmentType::Library)
.Case("ms", Triple::EnvironmentType::Mesh)
.Case("as", Triple::EnvironmentType::Amplification)
+ .Case("rootsig", Triple::EnvironmentType::RootSignature)
.Default(Triple::EnvironmentType::UnknownEnvironment);
if (Kind == Triple::EnvironmentType::UnknownEnvironment)
return std::nullopt;
@@ -144,8 +149,14 @@ std::optional<std::string> tryParseProfile(StringRef Profile) {
T.setOSName(Triple::getOSTypeName(Triple::OSType::ShaderModel).str() +
VersionTuple(Major, Minor).getAsString());
T.setEnvironment(Kind);
- if (isLegalShaderModel(T))
- return T.getTriple();
+
+ return T;
+}
+
+std::optional<std::string> tryParseProfile(StringRef Profile) {
+ std::optional<llvm::Triple> MaybeT = tryParseTriple(Profile);
+ if (MaybeT && isLegalShaderModel(*MaybeT))
+ return MaybeT->getTriple();
else
return std::nullopt;
}
@@ -255,6 +266,19 @@ bool checkExtensionArgsAreValid(ArrayRef<std::string> SpvExtensionArgs,
}
return AllValid;
}
+
+bool isRootSignatureTarget(StringRef Profile) {
+ if (std::optional<llvm::Triple> T = tryParseTriple(Profile))
+ return T->getEnvironment() == Triple::EnvironmentType::RootSignature;
+ return false;
+}
+
+bool isRootSignatureTarget(DerivedArgList &Args) {
+ if (const Arg *A = Args.getLastArg(options::OPT_target_profile))
+ return isRootSignatureTarget(A->getValue());
+ return false;
+}
+
} // namespace
void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA,
@@ -314,6 +338,12 @@ void tools::hlsl::LLVMObjcopy::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(Frs);
}
+ if (const Arg *A = Args.getLastArg(options::OPT_target_profile))
+ if (isRootSignatureTarget(A->getValue())) {
+ const char *Fos = Args.MakeArgString("--only-section=RTS0");
+ CmdArgs.push_back(Fos);
+ }
+
assert(CmdArgs.size() > 2 && "Unnecessary invocation of objcopy.");
C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
@@ -490,7 +520,8 @@ bool HLSLToolChain::requiresBinaryTranslation(DerivedArgList &Args) const {
bool HLSLToolChain::requiresObjcopy(DerivedArgList &Args) const {
return Args.hasArg(options::OPT_dxc_Fo) &&
- Args.hasArg(options::OPT_dxc_strip_rootsignature);
+ (Args.hasArg(options::OPT_dxc_strip_rootsignature) ||
+ isRootSignatureTarget(Args));
}
bool HLSLToolChain::isLastJob(DerivedArgList &Args,
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index ccda2c4ce4b6d..9e00494bcfbcf 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -1296,6 +1296,9 @@ class InjectRootSignatureCallback : public PPCallbacks {
void HLSLFrontendAction::ExecuteAction() {
// Pre-requisites to invoke
+ if (getCurrentFileKind().getLanguage() != Language::HLSL)
+ return WrapperFrontendAction::ExecuteAction();
+
CompilerInstance &CI = getCompilerInstance();
if (!CI.hasASTContext() || !CI.hasPreprocessor())
return WrapperFrontendAction::ExecuteAction();
@@ -1309,16 +1312,27 @@ void HLSLFrontendAction::ExecuteAction() {
/*CodeCompleteConsumer=*/nullptr);
Sema &S = CI.getSema();
+ auto &TargetInfo = CI.getASTContext().getTargetInfo();
+ bool IsRootSignatureTarget =
+ TargetInfo.getTriple().getEnvironment() == llvm::Triple::RootSignature;
+ StringRef HLSLEntry = TargetInfo.getTargetOpts().HLSLEntry;
+
// Register HLSL specific callbacks
auto LangOpts = CI.getLangOpts();
+ StringRef RootSigName =
+ IsRootSignatureTarget ? HLSLEntry : LangOpts.HLSLRootSigOverride;
+
auto MacroCallback = std::make_unique<InjectRootSignatureCallback>(
- S, LangOpts.HLSLRootSigOverride, LangOpts.HLSLRootSigVer);
+ S, RootSigName, LangOpts.HLSLRootSigVer);
Preprocessor &PP = CI.getPreprocessor();
PP.addPPCallbacks(std::move(MacroCallback));
- // Invoke as normal
- WrapperFrontendAction::ExecuteAction();
+ // If we are targeting a root signature, invoke custom handling
+ if (IsRootSignatureTarget)
+ return hlsl::HandleRootSignatureTarget(S, HLSLEntry);
+ else // otherwise, invoke as normal
+ return WrapperFrontendAction::ExecuteAction();
}
HLSLFrontendAction::HLSLFrontendAction(
diff --git a/clang/lib/Parse/ParseHLSLRootSignature.cpp b/clang/lib/Parse/ParseHLSLRootSignature.cpp
index 1af72f8b1c934..91976489ee660 100644
--- a/clang/lib/Parse/ParseHLSLRootSignature.cpp
+++ b/clang/lib/Parse/ParseHLSLRootSignature.cpp
@@ -7,8 +7,9 @@
//===----------------------------------------------------------------------===//
#include "clang/Parse/ParseHLSLRootSignature.h"
-
+#include "clang/AST/ASTConsumer.h"
#include "clang/Lex/LiteralSupport.h"
+#include "clang/Parse/Parser.h"
#include "clang/Sema/Sema.h"
using namespace llvm::hlsl::rootsig;
@@ -1472,5 +1473,38 @@ IdentifierInfo *ParseHLSLRootSignature(Sema &Actions,
return DeclIdent;
}
+void HandleRootSignatureTarget(Sema &S, StringRef EntryRootSig) {
+ ASTConsumer *Consumer = &S.getASTConsumer();
+
+ // Minimally initalize the parser. This does a couple things:
+ // - initializes Sema scope handling
+ // - invokes HLSLExternalSemaSource
+ // - invokes the preprocessor to lex the macros in the file
+ std::unique_ptr<Parser> P(new Parser(S.getPreprocessor(), S, true));
+ S.getPreprocessor().EnterMainSourceFile();
+
+ bool HaveLexer = S.getPreprocessor().getCurrentLexer();
+ if (HaveLexer) {
+ P->Initialize();
+ S.ActOnStartOfTranslationUnit();
+
+ // Skim through the file to parse to find the define
+ while (P->getCurToken().getKind() != tok::eof)
+ P->ConsumeAnyToken();
+
+ HLSLRootSignatureDecl *SignatureDecl =
+ S.HLSL().lookupRootSignatureOverrideDecl(
+ S.getASTContext().getTranslationUnitDecl());
+
+ if (SignatureDecl)
+ Consumer->HandleTopLevelDecl(DeclGroupRef(SignatureDecl));
+ else
+ S.getDiagnostics().Report(diag::err_hlsl_rootsignature_entry)
+ << EntryRootSig;
+ }
+
+ Consumer->HandleTranslationUnit(S.getASTContext());
+}
+
} // namespace hlsl
} // namespace clang
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 1e5ec952c1ecf..6ef43b833b759 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -729,19 +729,15 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) {
// If we have specified a root signature to override the entry function then
// attach it now
- if (RootSigOverrideIdent) {
- LookupResult R(SemaRef, RootSigOverrideIdent, SourceLocation(),
- Sema::LookupOrdinaryName);
- if (SemaRef.LookupQualifiedName(R, FD->getDeclContext()))
- if (auto *SignatureDecl =
- dyn_cast<HLSLRootSignatureDecl>(R.getFoundDecl())) {
- FD->dropAttr<RootSignatureAttr>();
- // We could look up the SourceRange of the macro here as well
- AttributeCommonInfo AL(RootSigOverrideIdent, AttributeScopeInfo(),
- SourceRange(), ParsedAttr::Form::Microsoft());
- FD->addAttr(::new (getASTContext()) RootSignatureAttr(
- getASTContext(), AL, RootSigOverrideIdent, SignatureDecl));
- }
+ HLSLRootSignatureDecl *SignatureDecl =
+ lookupRootSignatureOverrideDecl(FD->getDeclContext());
+ if (SignatureDecl) {
+ FD->dropAttr<RootSignatureAttr>();
+ // We could look up the SourceRange of the macro here as well
+ AttributeCommonInfo AL(RootSigOverrideIdent, AttributeScopeInfo(),
+ SourceRange(), ParsedAttr::Form::Microsoft());
+ FD->addAttr(::new (getASTContext()) RootSignatureAttr(
+ getASTContext(), AL, RootSigOverrideIdent, SignatureDecl));
}
llvm::Triple::EnvironmentType Env = TargetInfo.getTriple().getEnvironment();
@@ -765,6 +761,8 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) {
case llvm::Triple::UnknownEnvironment:
case llvm::Triple::Library:
break;
+ case llvm::Triple::RootSignature:
+ llvm_unreachable("rootsig environment has no functions");
default:
llvm_unreachable("Unhandled environment in triple");
}
@@ -827,6 +825,8 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
}
}
break;
+ case llvm::Triple::RootSignature:
+ llvm_unreachable("rootsig environment has no function entry point");
default:
llvm_unreachable("Unhandled environment in triple");
}
@@ -1107,6 +1107,18 @@ void SemaHLSL::ActOnFinishRootSignatureDecl(
SemaRef.PushOnScopeChains(SignatureDecl, SemaRef.getCurScope());
}
+HLSLRootSignatureDecl *
+SemaHLSL::lookupRootSignatureOverrideDecl(DeclContext *DC) const {
+ if (RootSigOverrideIdent) {
+ LookupResult R(SemaRef, RootSigOverrideIdent, SourceLocation(),
+ Sema::LookupOrdinaryName);
+ if (SemaRef.LookupQualifiedName(R, DC))
+ return dyn_cast<HLSLRootSignatureDecl>(R.getFoundDecl());
+ }
+
+ return nullptr;
+}
+
namespace {
struct PerVisibilityBindingChecker {
diff --git a/clang/test/AST/HLSL/RootSignature-Target-AST.hlsl b/clang/test/AST/HLSL/RootSignature-Target-AST.hlsl
new file mode 100644
index 0000000000000..91441e32e047d
--- /dev/null
+++ b/clang/test/AST/HLSL/RootSignature-Target-AST.hlsl
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-rootsignature -ast-dump \
+// RUN: -hlsl-entry EntryRootSig -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-V1_1
+
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-rootsignature -ast-dump \
+// RUN: -fdx-rootsignature-version=rootsig_1_0 \
+// RUN: -hlsl-entry EntryRootSig -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-V1_0
+
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-rootsignature -ast-dump \
+// RUN: -D CmdRS='"UAV(u0)"'\
+// RUN: -hlsl-entry CmdRS -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CMD
+
+// CHECK: -HLSLRootSignatureDecl 0x{{.*}} {{.*}} implicit [[ENTRY_RS_DECL:__hlsl_rootsig_decl_\d*]]
+// CHECK-V1_0-SAME: version: 1.0,
+// CHECK-V1_1-SAME: version: 1.1,
+// CHECK-SAME: RootElements{
+// CHECK-SAME: RootCBV(b0,
+// CHECK-SAME: space = 0, visibility = All,
+// CHECK-V1_0-SAME: flags = DataVolatile
+// CHECK-V1_1-SAME: flags = DataStaticWhileSetAtExecute
+// CHECK-SAME: )
+// CHECK-SAME: }
+#define EntryRootSig "CBV(b0)"
+
+// CMD: -HLSLRootSignatureDecl 0x{{.*}} {{.*}} implicit [[CMD_RS_DECL:__hlsl_rootsig_decl_\d*]]
+// CMD-SAME: version: 1.1,
+// CMD-SAME: RootElements{
+// CMD-SAME: RootUAV(u0, space = 0, visibility = All, flags = DataVolatile)
+// CMD-SAME: }
diff --git a/clang/test/CodeGenHLSL/RootSignature-Target.hlsl b/clang/test/CodeGenHLSL/RootSignature-Target.hlsl
new file mode 100644
index 0000000000000..50e6bae6786f0
--- /dev/null
+++ b/clang/test/CodeGenHLSL/RootSignature-Target.hlsl
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-rootsignature \
+// RUN: -hlsl-entry EntryRS -emit-llvm -o - %s | FileCheck %s
+
+// CHECK: !dx.rootsignatures = !{![[#ENTRY:]]}
+// CHECK: ![[#ENTRY]] = !{null, ![[#ENTRY_RS:]], i32 2}
+// CHECK: ![[#ENTRY_RS]] = !{![[#ROOT_CBV:]]}
+// CHECK: ![[#ROOT_CBV]] = !{!"RootCBV", i32 0, i32 0, i32 0, i32 4}
+
+#define EntryRS "CBV(b0)"
diff --git a/clang/test/Driver/dxc_rootsignature_target.hlsl b/clang/test/Driver/dxc_rootsignature_target.hlsl
new file mode 100644
index 0000000000000..08cd1ab00089b
--- /dev/null
+++ b/clang/test/Driver/dxc_rootsignature_target.hlsl
@@ -0,0 +1,8 @@
+// RUN: %clang_dxc -E EntryRS -T rootsig_1_1 /Fo %t.dxo -### %s 2>&1 | FileCheck %s --check-prefix=CMDS
+
+// CMDS: "{{.*}}clang{{.*}}" "-cc1"
+// CMDS-SAME: "-triple" "dxilv1.1-unknown-shadermodel1.1-rootsignature"
+// CMDS-SAME: "-hlsl-entry" "EntryRS"
+// CMDS: "{{.*}}llvm-objcopy{{(.exe)?}}" "{{.*}}.dxo" "--only-section=RTS0"
+
+#define EntryRS "UAV(u0)"
diff --git a/clang/test/SemaHLSL/RootSignature-target-err.hlsl b/clang/test/SemaHLSL/RootSignature-target-err.hlsl
new file mode 100644
index 0000000000000..49aca9ed6b377
--- /dev/null
+++ b/clang/test/SemaHLSL/RootSignature-target-err.hlsl
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-rootsignature -hlsl-entry NotFound...
[truncated]
|
This pr implements support for a root signature as a target, as specified here.
This is implemented in the following steps:
rootsignature
as a shader model environment type and definerootsig
as atarget_profile
. Only valid as versions 1.0 and 1.1HLSLFrontendAction
to invoke a special handling of constructing theASTContext
if we are considering anhlsl
file and with arootsignature
targetParser
andSema
to insert theRootSignatureDecl
CGHLSLRuntime
to emit the constructed root signature decl as part ofdx.rootsignatures
with anull
entry functionDXILRootSignature
to handle emitting a root signature without an entry functionToolChains/HLSL
to invokeonly-section=RTS0
to strip any other generated informationResolves: #150286.
Implementation Considerations
Ideally we could invoke this as part of
clang-dxc
without the need of a source file. However, the initialization of theParser
andLexer
becomes quite complicated to handle this.Technically, we could avoid generating any of the extra information that is removed in step 6. However, it seems better to re-use the logic in
llvm-objcopy
without any need for additional custom logic inDXILRootSignature
.