Add builtin validation for SPV_NV_shader_sm_builtins (#2656)

Also add a Builtin test generator variant that takes
capabilities and extensions.

Tests
 - verify that the SMCountNV, SMIDNV, WarpsPerSMNV, and WarpIDNV Builtins are
   accepted as Inputs in Vertex, Fragment, TessControl, TessEval, Geometry,
   and Compute.
 - verify that the SMCountNV, SMIDNV, WarpsPerSMNV, and WarpIDNV Builtins are
   accepted as Inputs in MeshNV and TaskNV shaders.
 - verify that the SMCountNV, SMIDNV, WarpsPerSMNV, and WarpIDNV Builtins are
   accepted as Inputs in the 6 ray tracing stages
 - verify that the SMCountNV, SMIDNV, WarpsPerSMNV, and WarpIDNV Builtins are
   NOT accepted as Outputs.
 - verify that the SMCountNV, SMIDNV, WarpsPerSMNV, and WarpIDNV Builtins are
   NOT accepted as non-scalar integers (f32, uvec3)
 - verify that the SMCountNV, SMIDNV, WarpsPerSMNV, and WarpIDNV Builtins are
   NOT accepted as non-32-bit integers (u64)
This commit is contained in:
Daniel Koch 2019-06-06 14:53:48 -04:00 committed by alan-baker
parent 43fb2403a6
commit 0755d6ce82
3 changed files with 272 additions and 19 deletions

View File

@ -63,7 +63,7 @@ build_script:
- ninja install
test_script:
- ctest -C %CONFIGURATION% --output-on-failure --timeout 300
- ctest -C %CONFIGURATION% --output-on-failure --timeout 310
after_test:
# Zip build artifacts for uploading and deploying

View File

@ -208,6 +208,8 @@ class BuiltInsValidator {
// Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId.
spv_result_t ValidateComputeShaderI32Vec3InputAtDefinition(
const Decoration& decoration, const Instruction& inst);
spv_result_t ValidateSMBuiltinsAtDefinition(const Decoration& decoration,
const Instruction& inst);
// The following section contains functions which are called when id defined
// by |referenced_inst| is
@ -332,6 +334,11 @@ class BuiltInsValidator {
const Instruction& referenced_inst,
const Instruction& referenced_from_inst);
spv_result_t ValidateSMBuiltinsAtReference(
const Decoration& decoration, const Instruction& built_in_inst,
const Instruction& referenced_inst,
const Instruction& referenced_from_inst);
// Validates that |built_in_inst| is not (even indirectly) referenced from
// within a function which can be called with |execution_model|.
//
@ -2654,6 +2661,61 @@ spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference(
return SPV_SUCCESS;
}
spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtDefinition(
const Decoration& decoration, const Instruction& inst) {
if (spvIsVulkanEnv(_.context()->target_env)) {
if (spv_result_t error = ValidateI32(
decoration, inst,
[this, &inst,
&decoration](const std::string& message) -> spv_result_t {
return _.diag(SPV_ERROR_INVALID_DATA, &inst)
<< "According to the "
<< spvLogStringForEnv(_.context()->target_env)
<< " spec BuiltIn "
<< _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
decoration.params()[0])
<< " variable needs to be a 32-bit int scalar. "
<< message;
})) {
return error;
}
}
// Seed at reference checks with this built-in.
return ValidateSMBuiltinsAtReference(decoration, inst, inst, inst);
}
spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtReference(
const Decoration& decoration, const Instruction& built_in_inst,
const Instruction& referenced_inst,
const Instruction& referenced_from_inst) {
if (spvIsVulkanEnv(_.context()->target_env)) {
const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
if (storage_class != SpvStorageClassMax &&
storage_class != SpvStorageClassInput) {
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
<< spvLogStringForEnv(_.context()->target_env)
<< " spec allows BuiltIn "
<< _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
decoration.params()[0])
<< " to be only used for "
"variables with Input storage class. "
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
referenced_from_inst)
<< " " << GetStorageClassDesc(referenced_from_inst);
}
}
if (function_id_ == 0) {
// Propagate this rule to all dependant ids in the global scope.
id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
&BuiltInsValidator::ValidateSMBuiltinsAtReference, this, decoration,
built_in_inst, referenced_from_inst, std::placeholders::_1));
}
return SPV_SUCCESS;
}
spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
const Decoration& decoration, const Instruction& inst) {
const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
@ -2748,6 +2810,12 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
case SpvBuiltInLocalInvocationIndex: {
return ValidateLocalInvocationIndexAtDefinition(decoration, inst);
}
case SpvBuiltInWarpsPerSMNV:
case SpvBuiltInSMCountNV:
case SpvBuiltInWarpIDNV:
case SpvBuiltInSMIDNV: {
return ValidateSMBuiltinsAtDefinition(decoration, inst);
}
case SpvBuiltInWorkDim:
case SpvBuiltInGlobalSize:
case SpvBuiltInEnqueuedWorkgroupSize:
@ -2810,11 +2878,7 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
case SpvBuiltInWorldToObjectNV:
case SpvBuiltInHitTNV:
case SpvBuiltInHitKindNV:
case SpvBuiltInIncomingRayFlagsNV:
case SpvBuiltInWarpsPerSMNV:
case SpvBuiltInSMCountNV:
case SpvBuiltInWarpIDNV:
case SpvBuiltInSMIDNV: {
case SpvBuiltInIncomingRayFlagsNV: {
// No validation rules (for the moment).
break;
}

View File

@ -62,7 +62,10 @@ using ValidateVulkanCombineBuiltInArrayedVariable = spvtest::ValidateBase<
std::tuple<const char*, const char*, const char*, const char*, TestResult>>;
using ValidateWebGPUCombineBuiltInArrayedVariable = spvtest::ValidateBase<
std::tuple<const char*, const char*, const char*, const char*, TestResult>>;
using ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult =
spvtest::ValidateBase<
std::tuple<const char*, const char*, const char*, const char*,
const char*, const char*, TestResult>>;
bool InitializerRequired(spv_target_env env, const char* const storage_class) {
return spvIsWebGPUEnv(env) && (strncmp(storage_class, "Output", 6) == 0 ||
@ -74,11 +77,20 @@ CodeGenerator GetInMainCodeGenerator(spv_target_env env,
const char* const built_in,
const char* const execution_model,
const char* const storage_class,
const char* const capabilities,
const char* const extensions,
const char* const data_type) {
CodeGenerator generator =
spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator()
: CodeGenerator::GetDefaultShaderCodeGenerator();
if (capabilities) {
generator.capabilities_ += capabilities;
}
if (extensions) {
generator.extensions_ += extensions;
}
generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn ";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
@ -144,8 +156,9 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InMain) {
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetInMainCodeGenerator(
SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, data_type);
CodeGenerator generator =
GetInMainCodeGenerator(SPV_ENV_VULKAN_1_0, built_in, execution_model,
storage_class, NULL, NULL, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
@ -165,8 +178,9 @@ TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, InMain) {
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetInMainCodeGenerator(
SPV_ENV_WEBGPU_0, built_in, execution_model, storage_class, data_type);
CodeGenerator generator =
GetInMainCodeGenerator(SPV_ENV_WEBGPU_0, built_in, execution_model,
storage_class, NULL, NULL, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(test_result.validation_result,
@ -179,15 +193,50 @@ TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, InMain) {
}
}
TEST_P(
ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
InMain) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const char* const capabilities = std::get<4>(GetParam());
const char* const extensions = std::get<5>(GetParam());
const TestResult& test_result = std::get<6>(GetParam());
CodeGenerator generator = GetInMainCodeGenerator(
SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class,
capabilities, extensions, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
ValidateInstructions(SPV_ENV_VULKAN_1_0));
if (test_result.error_str) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str));
}
if (test_result.error_str2) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2));
}
}
CodeGenerator GetInFunctionCodeGenerator(spv_target_env env,
const char* const built_in,
const char* const execution_model,
const char* const storage_class,
const char* const capabilities,
const char* const extensions,
const char* const data_type) {
CodeGenerator generator =
spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator()
: CodeGenerator::GetDefaultShaderCodeGenerator();
if (capabilities) {
generator.capabilities_ += capabilities;
}
if (extensions) {
generator.extensions_ += extensions;
}
generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn ";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
@ -267,8 +316,9 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InFunction) {
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetInFunctionCodeGenerator(
SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, data_type);
CodeGenerator generator =
GetInFunctionCodeGenerator(SPV_ENV_VULKAN_1_0, built_in, execution_model,
storage_class, NULL, NULL, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
@ -288,8 +338,9 @@ TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, InFunction) {
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetInFunctionCodeGenerator(
SPV_ENV_WEBGPU_0, built_in, execution_model, storage_class, data_type);
CodeGenerator generator =
GetInFunctionCodeGenerator(SPV_ENV_WEBGPU_0, built_in, execution_model,
storage_class, NULL, NULL, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(test_result.validation_result,
@ -302,15 +353,50 @@ TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, InFunction) {
}
}
TEST_P(
ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
InFunction) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const char* const capabilities = std::get<4>(GetParam());
const char* const extensions = std::get<5>(GetParam());
const TestResult& test_result = std::get<6>(GetParam());
CodeGenerator generator = GetInFunctionCodeGenerator(
SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class,
capabilities, extensions, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
ValidateInstructions(SPV_ENV_VULKAN_1_0));
if (test_result.error_str) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str));
}
if (test_result.error_str2) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2));
}
}
CodeGenerator GetVariableCodeGenerator(spv_target_env env,
const char* const built_in,
const char* const execution_model,
const char* const storage_class,
const char* const capabilities,
const char* const extensions,
const char* const data_type) {
CodeGenerator generator =
spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator()
: CodeGenerator::GetDefaultShaderCodeGenerator();
if (capabilities) {
generator.capabilities_ += capabilities;
}
if (extensions) {
generator.extensions_ += extensions;
}
generator.before_types_ = "OpDecorate %built_in_var BuiltIn ";
generator.before_types_ += built_in;
generator.before_types_ += "\n";
@ -373,8 +459,9 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Variable) {
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetVariableCodeGenerator(
SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, data_type);
CodeGenerator generator =
GetVariableCodeGenerator(SPV_ENV_VULKAN_1_0, built_in, execution_model,
storage_class, NULL, NULL, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
@ -394,8 +481,9 @@ TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, Variable) {
const char* const data_type = std::get<3>(GetParam());
const TestResult& test_result = std::get<4>(GetParam());
CodeGenerator generator = GetVariableCodeGenerator(
SPV_ENV_WEBGPU_0, built_in, execution_model, storage_class, data_type);
CodeGenerator generator =
GetVariableCodeGenerator(SPV_ENV_WEBGPU_0, built_in, execution_model,
storage_class, NULL, NULL, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
ASSERT_EQ(test_result.validation_result,
@ -408,6 +496,32 @@ TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, Variable) {
}
}
TEST_P(
ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
Variable) {
const char* const built_in = std::get<0>(GetParam());
const char* const execution_model = std::get<1>(GetParam());
const char* const storage_class = std::get<2>(GetParam());
const char* const data_type = std::get<3>(GetParam());
const char* const capabilities = std::get<4>(GetParam());
const char* const extensions = std::get<5>(GetParam());
const TestResult& test_result = std::get<6>(GetParam());
CodeGenerator generator = GetVariableCodeGenerator(
SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class,
capabilities, extensions, data_type);
CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0);
ASSERT_EQ(test_result.validation_result,
ValidateInstructions(SPV_ENV_VULKAN_1_0));
if (test_result.error_str) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str));
}
if (test_result.error_str2) {
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2));
}
}
INSTANTIATE_TEST_SUITE_P(
ClipAndCullDistanceOutputSuccess,
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
@ -2028,6 +2142,81 @@ INSTANTIATE_TEST_SUITE_P(
"needs to be a 32-bit float array",
"components are not float scalar"))));
INSTANTIATE_TEST_SUITE_P(
SMBuiltinsInputSuccess,
ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
Combine(Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"),
Values("Vertex", "Fragment", "TessellationControl",
"TessellationEvaluation", "Geometry", "GLCompute"),
Values("Input"), Values("%u32"),
Values("OpCapability ShaderSMBuiltinsNV\n"),
Values("OpExtension \"SPV_NV_shader_sm_builtins\"\n"),
Values(TestResult())));
INSTANTIATE_TEST_SUITE_P(
SMBuiltinsInputMeshSuccess,
ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
Combine(
Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"),
Values("MeshNV", "TaskNV"), Values("Input"), Values("%u32"),
Values("OpCapability ShaderSMBuiltinsNV\nOpCapability MeshShadingNV\n"),
Values("OpExtension \"SPV_NV_shader_sm_builtins\"\nOpExtension "
"\"SPV_NV_mesh_shader\"\n"),
Values(TestResult())));
INSTANTIATE_TEST_SUITE_P(
SMBuiltinsInputRaySuccess,
ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
Combine(
Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"),
Values("RayGenerationNV", "IntersectionNV", "AnyHitNV", "ClosestHitNV",
"MissNV", "CallableNV"),
Values("Input"), Values("%u32"),
Values("OpCapability ShaderSMBuiltinsNV\nOpCapability RayTracingNV\n"),
Values("OpExtension \"SPV_NV_shader_sm_builtins\"\nOpExtension "
"\"SPV_NV_ray_tracing\"\n"),
Values(TestResult())));
INSTANTIATE_TEST_SUITE_P(
SMBuiltinsNotInput,
ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
Combine(Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"),
Values("Vertex", "Fragment", "TessellationControl",
"TessellationEvaluation", "Geometry", "GLCompute"),
Values("Output"), Values("%u32"),
Values("OpCapability ShaderSMBuiltinsNV\n"),
Values("OpExtension \"SPV_NV_shader_sm_builtins\"\n"),
Values(TestResult(
SPV_ERROR_INVALID_DATA,
"to be only used for variables with Input storage class",
"uses storage class Output"))));
INSTANTIATE_TEST_SUITE_P(
SMBuiltinsNotIntScalar,
ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
Combine(Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"),
Values("Vertex", "Fragment", "TessellationControl",
"TessellationEvaluation", "Geometry", "GLCompute"),
Values("Input"), Values("%f32", "%u32vec3"),
Values("OpCapability ShaderSMBuiltinsNV\n"),
Values("OpExtension \"SPV_NV_shader_sm_builtins\"\n"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"needs to be a 32-bit int scalar",
"is not an int scalar"))));
INSTANTIATE_TEST_SUITE_P(
SMBuiltinsNotInt32,
ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult,
Combine(Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"),
Values("Vertex", "Fragment", "TessellationControl",
"TessellationEvaluation", "Geometry", "GLCompute"),
Values("Input"), Values("%u64"),
Values("OpCapability ShaderSMBuiltinsNV\n"),
Values("OpExtension \"SPV_NV_shader_sm_builtins\"\n"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"needs to be a 32-bit int scalar",
"has bit width 64"))));
CodeGenerator GetWorkgroupSizeSuccessGenerator(spv_target_env env) {
CodeGenerator generator =
env == SPV_ENV_WEBGPU_0 ? CodeGenerator::GetWebGPUShaderCodeGenerator()