Validator: FPRoundingMode decoration (#1482)
This commit checks the following when Shader capability exists:
"The FPRoundingMode decoration can be applied only to a width-only
conversion instruction that is used as the Object operand of an
OpStore storing through a pointer to a 16-bit floating-point object
in the StorageBuffer, Uniform, PushConstant, Input, or Output
Storage Classes.".
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index cfb38ef..c4c390a 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -864,7 +864,82 @@
}
}
}
+ return SPV_SUCCESS;
+}
+spv_result_t CheckDecorationsOfConversions(ValidationState_t& vstate) {
+ // Validates FPRoundingMode decoration for Shader Capabilities
+ if (!vstate.HasCapability(SpvCapabilityShader)) return SPV_SUCCESS;
+
+ for (const auto& kv : vstate.id_decorations()) {
+ const uint32_t id = kv.first;
+ const auto& decorations = kv.second;
+ if (decorations.empty()) {
+ continue;
+ }
+
+ const Instruction* inst = vstate.FindDef(id);
+ assert(inst);
+
+ // Validates FPRoundingMode decoration
+ for (const auto& decoration : decorations) {
+ if (decoration.dec_type() != SpvDecorationFPRoundingMode) {
+ continue;
+ }
+
+ // Validates width-only conversion instruction for floating-point object
+ // i.e., OpFConvert
+ if (inst->opcode() != SpvOpFConvert) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, inst)
+ << "FPRoundingMode decoration can be applied only to a "
+ "width-only conversion instruction for floating-point "
+ "object.";
+ }
+
+ // Validates Object operand of an OpStore
+ for (const auto& use : inst->uses()) {
+ const auto store = use.first;
+ if (store->opcode() != SpvOpStore) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, inst)
+ << "FPRoundingMode decoration can be applied only to the "
+ "Object operand of an OpStore.";
+ }
+
+ if (use.second != 2) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, inst)
+ << "FPRoundingMode decoration can be applied only to the "
+ "Object operand of an OpStore.";
+ }
+
+ const auto ptr_inst = vstate.FindDef(store->GetOperandAs<uint32_t>(0));
+ const auto ptr_type =
+ vstate.FindDef(ptr_inst->GetOperandAs<uint32_t>(0));
+
+ const auto half_float_id = ptr_type->GetOperandAs<uint32_t>(2);
+ if (!vstate.IsFloatScalarType(half_float_id) ||
+ vstate.GetBitWidth(half_float_id) != 16) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, inst)
+ << "FPRoundingMode decoration can be applied only to the "
+ "Object operand of an OpStore storing through a pointer to "
+ "a 16-bit floating-point object.";
+ }
+
+ // Validates storage class of the pointer to the OpStore
+ const auto storage = ptr_type->GetOperandAs<uint32_t>(1);
+ if (storage != SpvStorageClassStorageBuffer &&
+ storage != SpvStorageClassUniform &&
+ storage != SpvStorageClassPushConstant &&
+ storage != SpvStorageClassInput &&
+ storage != SpvStorageClassOutput) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, inst)
+ << "FPRoundingMode decoration can be applied only to the "
+ "Object operand of an OpStore in the StorageBuffer, "
+ "Uniform, PushConstant, Input, or Output Storage "
+ "Classes.";
+ }
+ }
+ }
+ }
return SPV_SUCCESS;
}
@@ -879,6 +954,7 @@
if (auto error = CheckDescriptorSetArrayOfArrays(vstate)) return error;
if (auto error = CheckVulkanMemoryModelDeprecatedDecorations(vstate))
return error;
+ if (auto error = CheckDecorationsOfConversions(vstate)) return error;
return SPV_SUCCESS;
}
diff --git a/test/val/val_data_test.cpp b/test/val/val_data_test.cpp
index 88e0578..e6bb673 100644
--- a/test/val/val_data_test.cpp
+++ b/test/val/val_data_test.cpp
@@ -614,11 +614,24 @@
OpCapability Linkage
OpCapability )") +
cap + R"(
+ OpExtension "SPV_KHR_storage_buffer_storage_class"
+ OpExtension "SPV_KHR_variable_pointers"
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel Logical GLSL450
- OpDecorate %2 FPRoundingMode )" + mode + R"(
- %1 = OpTypeFloat 32
- %2 = OpConstant %1 1.25
+ OpDecorate %_ FPRoundingMode )" + mode + R"(
+ %half = OpTypeFloat 16
+ %float = OpTypeFloat 32
+ %float_1_25 = OpConstant %float 1.25
+ %half_ptr = OpTypePointer StorageBuffer %half
+ %half_ptr_var = OpVariable %half_ptr StorageBuffer
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %main = OpFunction %void None %func
+ %main_entry = OpLabel
+ %_ = OpFConvert %half %float_1_25
+ OpStore %half_ptr_var %_
+ OpReturn
+ OpFunctionEnd
)";
CompileSuccessfully(str.c_str());
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -631,11 +644,24 @@
for (const auto env : {SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1}) {
std::string str = std::string(R"(
OpCapability Shader
+ OpExtension "SPV_KHR_storage_buffer_storage_class"
+ OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
- OpDecorate %2 FPRoundingMode )") +
+ OpDecorate %_ FPRoundingMode )") +
mode + R"(
- %1 = OpTypeFloat 32
- %2 = OpConstant %1 1.25
+ %half = OpTypeFloat 16
+ %float = OpTypeFloat 32
+ %float_1_25 = OpConstant %float 1.25
+ %half_ptr = OpTypePointer StorageBuffer %half
+ %half_ptr_var = OpVariable %half_ptr StorageBuffer
+ %void = OpTypeVoid
+ %func = OpTypeFunction %void
+ %main = OpFunction %void None %func
+ %main_entry = OpLabel
+ %_ = OpFConvert %half %float_1_25
+ OpStore %half_ptr_var %_
+ OpReturn
+ OpFunctionEnd
)";
CompileSuccessfully(str.c_str());
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env));
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index 6f3f401..21803f9 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -3145,6 +3145,271 @@
"banned when using "
"the Vulkan memory model."));
}
+
+TEST_F(ValidateDecorations, FPRoundingModeGood) {
+ std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability StorageBuffer16BitAccess
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_KHR_variable_pointers"
+OpExtension "SPV_KHR_16bit_storage"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %_ FPRoundingMode RTE
+%half = OpTypeFloat 16
+%float = OpTypeFloat 32
+%float_1_25 = OpConstant %float 1.25
+%half_ptr = OpTypePointer StorageBuffer %half
+%half_ptr_var = OpVariable %half_ptr StorageBuffer
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+%_ = OpFConvert %half %float_1_25
+OpStore %half_ptr_var %_
+OpReturn
+OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+}
+
+TEST_F(ValidateDecorations, FPRoundingModeNotOpFConvert) {
+ std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability StorageBuffer16BitAccess
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_KHR_variable_pointers"
+OpExtension "SPV_KHR_16bit_storage"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %_ FPRoundingMode RTE
+%short = OpTypeInt 16 1
+%int = OpTypeInt 32 1
+%int_17 = OpConstant %int 17
+%short_ptr = OpTypePointer StorageBuffer %short
+%short_ptr_var = OpVariable %short_ptr StorageBuffer
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+%_ = OpSConvert %short %int_17
+OpStore %short_ptr_var %_
+OpReturn
+OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("FPRoundingMode decoration can be applied only to a "
+ "width-only conversion instruction for floating-point "
+ "object."));
+}
+
+TEST_F(ValidateDecorations, FPRoundingModeNoOpStoreGood) {
+ std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability StorageBuffer16BitAccess
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_KHR_variable_pointers"
+OpExtension "SPV_KHR_16bit_storage"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %_ FPRoundingMode RTE
+%half = OpTypeFloat 16
+%float = OpTypeFloat 32
+%float_1_25 = OpConstant %float 1.25
+%half_ptr = OpTypePointer StorageBuffer %half
+%half_ptr_var = OpVariable %half_ptr StorageBuffer
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+%_ = OpFConvert %half %float_1_25
+OpReturn
+OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+}
+
+TEST_F(ValidateDecorations, FPRoundingModeFConvert64to16Good) {
+ std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability StorageBuffer16BitAccess
+OpCapability Float64
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_KHR_variable_pointers"
+OpExtension "SPV_KHR_16bit_storage"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %_ FPRoundingMode RTE
+%half = OpTypeFloat 16
+%double = OpTypeFloat 64
+%double_1_25 = OpConstant %double 1.25
+%half_ptr = OpTypePointer StorageBuffer %half
+%half_ptr_var = OpVariable %half_ptr StorageBuffer
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+%_ = OpFConvert %half %double_1_25
+OpStore %half_ptr_var %_
+OpReturn
+OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+}
+
+TEST_F(ValidateDecorations, FPRoundingModeNotStoreInFloat16) {
+ std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability StorageBuffer16BitAccess
+OpCapability Float64
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_KHR_variable_pointers"
+OpExtension "SPV_KHR_16bit_storage"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %_ FPRoundingMode RTE
+%float = OpTypeFloat 32
+%double = OpTypeFloat 64
+%double_1_25 = OpConstant %double 1.25
+%float_ptr = OpTypePointer StorageBuffer %float
+%float_ptr_var = OpVariable %float_ptr StorageBuffer
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+%_ = OpFConvert %float %double_1_25
+OpStore %float_ptr_var %_
+OpReturn
+OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("FPRoundingMode decoration can be applied only to the "
+ "Object operand of an OpStore storing through a "
+ "pointer to a 16-bit floating-point object."));
+}
+
+TEST_F(ValidateDecorations, FPRoundingModeBadStorageClass) {
+ std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability StorageBuffer16BitAccess
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_KHR_variable_pointers"
+OpExtension "SPV_KHR_16bit_storage"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %_ FPRoundingMode RTE
+%half = OpTypeFloat 16
+%float = OpTypeFloat 32
+%float_1_25 = OpConstant %float 1.25
+%half_ptr = OpTypePointer Private %half
+%half_ptr_var = OpVariable %half_ptr Private
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+%_ = OpFConvert %half %float_1_25
+OpStore %half_ptr_var %_
+OpReturn
+OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("FPRoundingMode decoration can be applied only to the "
+ "Object operand of an OpStore in the StorageBuffer, Uniform, "
+ "PushConstant, Input, or Output Storage Classes."));
+}
+
+TEST_F(ValidateDecorations, FPRoundingModeMultipleOpStoreGood) {
+ std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability StorageBuffer16BitAccess
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_KHR_variable_pointers"
+OpExtension "SPV_KHR_16bit_storage"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %_ FPRoundingMode RTE
+%half = OpTypeFloat 16
+%float = OpTypeFloat 32
+%float_1_25 = OpConstant %float 1.25
+%half_ptr = OpTypePointer StorageBuffer %half
+%half_ptr_var_0 = OpVariable %half_ptr StorageBuffer
+%half_ptr_var_1 = OpVariable %half_ptr StorageBuffer
+%half_ptr_var_2 = OpVariable %half_ptr StorageBuffer
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+%_ = OpFConvert %half %float_1_25
+OpStore %half_ptr_var_0 %_
+OpStore %half_ptr_var_1 %_
+OpStore %half_ptr_var_2 %_
+OpReturn
+OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
+}
+
+TEST_F(ValidateDecorations, FPRoundingModeMultipleUsesBad) {
+ std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability StorageBuffer16BitAccess
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_KHR_variable_pointers"
+OpExtension "SPV_KHR_16bit_storage"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %_ FPRoundingMode RTE
+%half = OpTypeFloat 16
+%float = OpTypeFloat 32
+%float_1_25 = OpConstant %float 1.25
+%half_ptr = OpTypePointer StorageBuffer %half
+%half_ptr_var_0 = OpVariable %half_ptr StorageBuffer
+%half_ptr_var_1 = OpVariable %half_ptr StorageBuffer
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+%_ = OpFConvert %half %float_1_25
+OpStore %half_ptr_var_0 %_
+%result = OpFAdd %half %_ %_
+OpStore %half_ptr_var_1 %_
+OpReturn
+OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("FPRoundingMode decoration can be applied only to the "
+ "Object operand of an OpStore."));
+}
+
} // namespace
} // namespace val
} // namespace spvtools