[spirv] Validate vk::push_constant usage (#807)
Attaching vk::push_constant and vk::binding to the same global variable is disallowed.
This commit is contained in:
Родитель
3cad152a90
Коммит
dc0babf2ff
|
@ -273,7 +273,8 @@ SPIRVEmitter::SPIRVEmitter(CompilerInstance &ci,
|
||||||
theContext(), theBuilder(&theContext),
|
theContext(), theBuilder(&theContext),
|
||||||
declIdMapper(shaderModel, astContext, theBuilder, spirvOptions),
|
declIdMapper(shaderModel, astContext, theBuilder, spirvOptions),
|
||||||
typeTranslator(astContext, theBuilder, diags), entryFunctionId(0),
|
typeTranslator(astContext, theBuilder, diags), entryFunctionId(0),
|
||||||
curFunction(nullptr), curThis(0), needsLegalization(false) {
|
curFunction(nullptr), curThis(0), seenPushConstantAt(),
|
||||||
|
needsLegalization(false) {
|
||||||
if (shaderModel.GetKind() == hlsl::ShaderModel::Kind::Invalid)
|
if (shaderModel.GetKind() == hlsl::ShaderModel::Kind::Invalid)
|
||||||
emitError("unknown shader module: %0", {}) << shaderModel.GetName();
|
emitError("unknown shader module: %0", {}) << shaderModel.GetName();
|
||||||
}
|
}
|
||||||
|
@ -310,6 +311,8 @@ void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {
|
||||||
<< bufferDecl->isCBuffer() << init->getSourceRange();
|
<< bufferDecl->isCBuffer() << init->getSourceRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateVKAttributes(decl);
|
||||||
|
|
||||||
(void)declIdMapper.createCTBuffer(bufferDecl);
|
(void)declIdMapper.createCTBuffer(bufferDecl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -631,7 +634,42 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) {
|
||||||
curFunction = nullptr;
|
curFunction = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SPIRVEmitter::validateVKAttributes(const Decl *decl) {
|
||||||
|
// The frontend will make sure that
|
||||||
|
// * vk::push_constant applies to global variables of struct type
|
||||||
|
// * vk::binding applies to global variables or cbuffers/tbuffers
|
||||||
|
// * vk::counter_binding applies to global variables of RW/Append/Consume
|
||||||
|
// StructuredBuffer
|
||||||
|
// * vk::location applies to function parameters/returns and struct fields
|
||||||
|
// So the only case we need to check co-existence is vk::push_constant and
|
||||||
|
// vk::binding.
|
||||||
|
|
||||||
|
if (const auto *pcAttr = decl->getAttr<VKPushConstantAttr>()) {
|
||||||
|
if (seenPushConstantAt.isInvalid()) {
|
||||||
|
seenPushConstantAt = pcAttr->getLocation();
|
||||||
|
} else {
|
||||||
|
// TODO: Actually this is slightly incorrect. The Vulkan spec says:
|
||||||
|
// There must be no more than one push constant block statically used
|
||||||
|
// per shader entry point.
|
||||||
|
// But we are checking whether there are more than one push constant
|
||||||
|
// blocks defined. Tracking usage requires more work.
|
||||||
|
emitError("cannot have more than one push constant block",
|
||||||
|
pcAttr->getLocation());
|
||||||
|
emitNote("push constant block previously defined here",
|
||||||
|
seenPushConstantAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decl->hasAttr<VKBindingAttr>()) {
|
||||||
|
emitError("'push_constant' attribute cannot be used together with "
|
||||||
|
"'binding' attribute",
|
||||||
|
pcAttr->getLocation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SPIRVEmitter::doVarDecl(const VarDecl *decl) {
|
void SPIRVEmitter::doVarDecl(const VarDecl *decl) {
|
||||||
|
validateVKAttributes(decl);
|
||||||
|
|
||||||
if (decl->hasAttr<VKPushConstantAttr>()) {
|
if (decl->hasAttr<VKPushConstantAttr>()) {
|
||||||
// This is a VarDecl for PushConstant block.
|
// This is a VarDecl for PushConstant block.
|
||||||
(void)declIdMapper.createPushConstant(decl);
|
(void)declIdMapper.createPushConstant(decl);
|
||||||
|
|
|
@ -238,6 +238,10 @@ private:
|
||||||
collectArrayStructIndices(const Expr *expr,
|
collectArrayStructIndices(const Expr *expr,
|
||||||
llvm::SmallVectorImpl<uint32_t> *indices);
|
llvm::SmallVectorImpl<uint32_t> *indices);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Validates that vk::* attributes are used correctly.
|
||||||
|
void validateVKAttributes(const Decl *decl);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Processes the given expr, casts the result into the given bool (vector)
|
/// Processes the given expr, casts the result into the given bool (vector)
|
||||||
/// type and returns the <result-id> of the casted value.
|
/// type and returns the <result-id> of the casted value.
|
||||||
|
@ -675,6 +679,15 @@ private:
|
||||||
return diags.Report(loc, diagId);
|
return diags.Report(loc, diagId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Wrapper method to create a note message and report it
|
||||||
|
/// in the diagnostic engine associated with this consumer
|
||||||
|
template <unsigned N>
|
||||||
|
DiagnosticBuilder emitNote(const char (&message)[N], SourceLocation loc) {
|
||||||
|
const auto diagId =
|
||||||
|
diags.getCustomDiagID(clang::DiagnosticsEngine::Note, message);
|
||||||
|
return diags.Report(loc, diagId);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CompilerInstance &theCompilerInstance;
|
CompilerInstance &theCompilerInstance;
|
||||||
ASTContext &astContext;
|
ASTContext &astContext;
|
||||||
|
@ -706,6 +719,10 @@ private:
|
||||||
/// The SPIR-V function parameter for the current this object.
|
/// The SPIR-V function parameter for the current this object.
|
||||||
uint32_t curThis;
|
uint32_t curThis;
|
||||||
|
|
||||||
|
/// The source location of a push constant block we have previously seen.
|
||||||
|
/// Invalid means no push constant blocks defined thus far.
|
||||||
|
SourceLocation seenPushConstantAt;
|
||||||
|
|
||||||
/// Whether the translated SPIR-V binary needs legalization.
|
/// Whether the translated SPIR-V binary needs legalization.
|
||||||
///
|
///
|
||||||
/// The following cases will require legalization:
|
/// The following cases will require legalization:
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Run: %dxc -T ps_6_0 -E main
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
float f;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[vk::push_constant, vk::binding(5)]]
|
||||||
|
S pcs;
|
||||||
|
|
||||||
|
float main() : A {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK: :7:3: error: 'push_constant' attribute cannot be used together with 'binding' attribute
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Run: %dxc -T vs_6_0 -E main
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
float4 f;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[vk::push_constant]]
|
||||||
|
S pcs1;
|
||||||
|
|
||||||
|
[[vk::push_constant]] // error
|
||||||
|
S pcs2;
|
||||||
|
|
||||||
|
[[vk::push_constant]] // error
|
||||||
|
S pcs3;
|
||||||
|
|
||||||
|
float main() : A {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK: :10:3: error: cannot have more than one push constant block
|
||||||
|
// CHECK: :7:3: note: push constant block previously defined here
|
||||||
|
|
||||||
|
// CHECK: :13:3: error: cannot have more than one push constant block
|
||||||
|
// CHECK: :7:3: note: push constant block previously defined here
|
|
@ -872,6 +872,9 @@ TEST_F(FileTest, SpirvInterpolationError) {
|
||||||
TEST_F(FileTest, VulkanAttributeErrors) {
|
TEST_F(FileTest, VulkanAttributeErrors) {
|
||||||
runFileTest("vk.attribute.error.hlsl", FileTest::Expect::Failure);
|
runFileTest("vk.attribute.error.hlsl", FileTest::Expect::Failure);
|
||||||
}
|
}
|
||||||
|
TEST_F(FileTest, VulkanAttributeInvalidUsages) {
|
||||||
|
runFileTest("vk.attribute.invalid.hlsl", FileTest::Expect::Failure);
|
||||||
|
}
|
||||||
|
|
||||||
// Vulkan specific
|
// Vulkan specific
|
||||||
TEST_F(FileTest, VulkanLocation) { runFileTest("vk.location.hlsl"); }
|
TEST_F(FileTest, VulkanLocation) { runFileTest("vk.location.hlsl"); }
|
||||||
|
@ -924,6 +927,9 @@ TEST_F(FileTest, VulkanStructuredBufferCounter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FileTest, VulkanPushConstant) { runFileTest("vk.push-constant.hlsl"); }
|
TEST_F(FileTest, VulkanPushConstant) { runFileTest("vk.push-constant.hlsl"); }
|
||||||
|
TEST_F(FileTest, VulkanMultiplePushConstant) {
|
||||||
|
runFileTest("vk.push-constant.multiple.hlsl", FileTest::Expect::Failure);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(FileTest, VulkanLayoutCBufferStd140) {
|
TEST_F(FileTest, VulkanLayoutCBufferStd140) {
|
||||||
runFileTest("vk.layout.cbuffer.std140.hlsl");
|
runFileTest("vk.layout.cbuffer.std140.hlsl");
|
||||||
|
|
Загрузка…
Ссылка в новой задаче