spirv-val: Add Vulkan FP Mode VUID (#4088)
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index d381276..01b0eca 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -1260,7 +1260,8 @@
// decorations. Otherwise emits a diagnostic and returns something other than
// SPV_SUCCESS.
spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate,
- const Instruction& inst) {
+ const Instruction& inst,
+ const Decoration& decoration) {
// Validates width-only conversion instruction for floating-point object
// i.e., OpFConvert
if (inst.opcode() != SpvOpFConvert) {
@@ -1270,6 +1271,15 @@
"object.";
}
+ if (spvIsVulkanEnv(vstate.context()->target_env)) {
+ const auto mode = decoration.params()[0];
+ if ((mode != SpvFPRoundingModeRTE) && (mode != SpvFPRoundingModeRTZ)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+ << vstate.VkErrorID(4675)
+ << "In Vulkan, the FPRoundingMode mode must only by RTE or RTZ.";
+ }
+ }
+
// Validates Object operand of an OpStore
for (const auto& use : inst.uses()) {
const auto store = use.first;
@@ -1588,7 +1598,8 @@
break;
case SpvDecorationFPRoundingMode:
if (is_shader)
- PASS_OR_BAIL(CheckFPRoundingModeForShaders(vstate, *inst));
+ PASS_OR_BAIL(
+ CheckFPRoundingModeForShaders(vstate, *inst, decoration));
break;
case SpvDecorationNonWritable:
PASS_OR_BAIL(CheckNonWritableDecoration(vstate, *inst, decoration));
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index ae34185..f56d10f 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -1671,6 +1671,8 @@
return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-04656);
case 4658:
return VUID_WRAP(VUID-StandaloneSpirv-OpImageTexelPointer-04658);
+ case 4675:
+ return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675);
case 4685:
return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685);
case 4711:
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index e646162..cacf999 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -4679,6 +4679,95 @@
"Object operand of an OpStore."));
}
+TEST_F(ValidateDecorations, VulkanFPRoundingModeGood) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability StorageBuffer16BitAccess
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main" %_
+ OpExecutionMode %main LocalSize 1 1 1
+ OpMemberDecorate %ssbo 0 Offset 0
+ OpDecorate %ssbo Block
+ OpDecorate %_ DescriptorSet 0
+ OpDecorate %_ Binding 0
+ OpDecorate %17 FPRoundingMode RTE
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+ %float_1 = OpConstant %float 1
+ %half = OpTypeFloat 16
+ %ssbo = OpTypeStruct %half
+%_ptr_StorageBuffer_ssbo = OpTypePointer StorageBuffer %ssbo
+ %_ = OpVariable %_ptr_StorageBuffer_ssbo StorageBuffer
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%_ptr_StorageBuffer_half = OpTypePointer StorageBuffer %half
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %b = OpVariable %_ptr_Function_float Function
+ OpStore %b %float_1
+ %16 = OpLoad %float %b
+ %17 = OpFConvert %half %16
+ %19 = OpAccessChain %_ptr_StorageBuffer_half %_ %int_0
+ OpStore %19 %17
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+ EXPECT_EQ(SPV_SUCCESS,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateDecorations, VulkanFPRoundingModeBadMode) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability StorageBuffer16BitAccess
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main" %_
+ OpExecutionMode %main LocalSize 1 1 1
+ OpMemberDecorate %ssbo 0 Offset 0
+ OpDecorate %ssbo Block
+ OpDecorate %_ DescriptorSet 0
+ OpDecorate %_ Binding 0
+ OpDecorate %17 FPRoundingMode RTP
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+ %float_1 = OpConstant %float 1
+ %half = OpTypeFloat 16
+ %ssbo = OpTypeStruct %half
+%_ptr_StorageBuffer_ssbo = OpTypePointer StorageBuffer %ssbo
+ %_ = OpVariable %_ptr_StorageBuffer_ssbo StorageBuffer
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%_ptr_StorageBuffer_half = OpTypePointer StorageBuffer %half
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %b = OpVariable %_ptr_Function_float Function
+ OpStore %b %float_1
+ %16 = OpLoad %float %b
+ %17 = OpFConvert %half %16
+ %19 = OpAccessChain %_ptr_StorageBuffer_half %_ %int_0
+ OpStore %19 %17
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ AnyVUID("VUID-StandaloneSpirv-FPRoundingMode-04675"));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("In Vulkan, the FPRoundingMode mode must only by RTE or RTZ."));
+}
+
TEST_F(ValidateDecorations, GroupDecorateTargetsDecorationGroup) {
std::string spirv = R"(
OpCapability Shader