diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index a6a7ce626..51746006a 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -1393,6 +1393,81 @@ spv_result_t CheckIntegerWrapDecoration(ValidationState_t& vstate, << spvOpcodeString(inst.opcode()); } +// Returns SPV_SUCCESS if validation rules are satisfied for the Component +// decoration. Otherwise emits a diagnostic and returns something other than +// SPV_SUCCESS. +spv_result_t CheckComponentDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + assert(inst.id() && "Parser ensures the target of the decoration has an ID"); + + uint32_t type_id; + if (decoration.struct_member_index() == Decoration::kInvalidMember) { + // The target must be a memory object declaration. + const auto opcode = inst.opcode(); + if (opcode != SpvOpVariable && opcode != SpvOpFunctionParameter) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of Component decoration must be a memory object " + "declaration (a variable or a function parameter)"; + } + + // Only valid for the Input and Output Storage Classes. + const auto storage_class = opcode == SpvOpVariable + ? inst.GetOperandAs(2) + : SpvStorageClassMax; + if (storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput && + storage_class != SpvStorageClassMax) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of Component decoration is invalid: must point to a " + "Storage Class of Input(1) or Output(3). Found Storage " + "Class " + << storage_class; + } + + type_id = inst.type_id(); + if (vstate.IsPointerType(type_id)) { + const auto pointer = vstate.FindDef(type_id); + type_id = pointer->GetOperandAs(2); + } + } else { + if (inst.opcode() != SpvOpTypeStruct) { + return vstate.diag(SPV_ERROR_INVALID_DATA, &inst) + << "Attempted to get underlying data type via member index for " + "non-struct type."; + } + type_id = inst.word(decoration.struct_member_index() + 2); + } + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + if (!vstate.IsIntScalarOrVectorType(type_id) && + !vstate.IsFloatScalarOrVectorType(type_id)) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Component decoration specified for type " + << vstate.getIdName(type_id) << " that is not a scalar or vector"; + } + + // For 16-, and 32-bit types, it is invalid if this sequence of components + // gets larger than 3. + const auto bit_width = vstate.GetBitWidth(type_id); + if (bit_width == 16 || bit_width == 32) { + assert(decoration.params().size() == 1 && + "Grammar ensures Component has one parameter"); + + const auto component = decoration.params()[0]; + const auto last_component = component + vstate.GetDimension(type_id) - 1; + if (last_component > 3) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Sequence of components starting with " << component + << " and ending with " << last_component + << " gets larger than 3"; + } + } + } + + return SPV_SUCCESS; +} + #define PASS_OR_BAIL_AT_LINE(X, LINE) \ { \ spv_result_t e##LINE = (X); \ @@ -1421,6 +1496,9 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) { for (const auto& decoration : decorations) { switch (decoration.dec_type()) { + case SpvDecorationComponent: + PASS_OR_BAIL(CheckComponentDecoration(vstate, *inst, decoration)); + break; case SpvDecorationFPRoundingMode: if (is_shader) PASS_OR_BAIL(CheckFPRoundingModeForShaders(vstate, *inst)); diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp index 4fb2a7cbd..5a6e75127 100644 --- a/test/val/val_capability_test.cpp +++ b/test/val/val_capability_test.cpp @@ -1168,8 +1168,10 @@ std::make_pair(std::string(kOpenCLMemoryModel) + ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" - "OpDecorate %intt Component 0\n" - "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + "OpDecorate %var Component 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), ShaderDependencies()), std::make_pair(std::string(kOpenCLMemoryModel) + "OpEntryPoint Kernel %func \"compute\" \n" diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index c454bed54..5ea1d87ad 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp @@ -6400,6 +6400,314 @@ OpDecorate %1 BufferBlock "requires SPIR-V version 1.3 or earlier")); } +// Component + +TEST_F(ValidateDecorations, ComponentDecorationBadTarget) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %t Component 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%t = OpTypeVector %float 2 +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of Component decoration must be " + "a memory object declaration")); +} + +TEST_F(ValidateDecorations, ComponentDecorationBadStorageClass) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %v Component 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%t = OpTypeVector %float 2 +%ptr_private = OpTypePointer Private %t +%v = OpVariable %ptr_private Private +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of Component decoration is invalid: must " + "point to a Storage Class of Input(1) or Output(3)")); +} + +TEST_F(ValidateDecorations, ComponentDecorationBadTypeVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( +OpCapability Shader +OpCapability Matrix +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %v Component 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%vtype = OpTypeVector %float 4 +%t = OpTypeMatrix %vtype 4 +%ptr_input = OpTypePointer Input %t +%v = OpVariable %ptr_input Input +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component decoration specified for type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a scalar or vector")); +} + +std::string ShaderWithComponentDecoration(const std::string& type, + const std::string& decoration) { + return R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %entryPointOutput +OpExecutionMode %main OriginUpperLeft +OpDecorate %entryPointOutput Location 0 +OpDecorate %entryPointOutput )" + + decoration + R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%vtype = )" + type + R"( +%float_0 = OpConstant %float 0 +%_ptr_Output_vtype = OpTypePointer Output %vtype +%entryPointOutput = OpVariable %_ptr_Output_vtype Output +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; +} + +TEST_F(ValidateDecorations, ComponentDecorationIntGood0Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 0"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationIntGood1Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 1"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationIntGood2Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 2"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationIntGood3Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 3"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationIntBad4Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 4"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 4 " + "and ending with 4 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationVector3GoodVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeVector %float 3", "Component 1"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationVector4GoodVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 0"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationVector4Bad1Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 1"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 1 " + "and ending with 4 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationVector4Bad3Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 3"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 3 " + "and ending with 6 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationBlockGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %9 %12 +OpExecutionMode %4 OriginUpperLeft +OpDecorate %9 Location 0 +OpMemberDecorate %block 0 Location 2 +OpMemberDecorate %block 0 Component 1 +OpDecorate %block Block +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%float = OpTypeFloat 32 +%vec3 = OpTypeVector %float 3 +%8 = OpTypePointer Output %vec3 +%9 = OpVariable %8 Output +%block = OpTypeStruct %vec3 +%11 = OpTypePointer Input %block +%12 = OpVariable %11 Input +%int = OpTypeInt 32 1 +%14 = OpConstant %int 0 +%15 = OpTypePointer Input %vec3 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%16 = OpAccessChain %15 %12 %14 +%17 = OpLoad %vec3 %16 +OpStore %9 %17 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationBlockBadVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %9 %12 +OpExecutionMode %4 OriginUpperLeft +OpDecorate %9 Location 0 +OpMemberDecorate %block 0 Location 2 +OpMemberDecorate %block 0 Component 2 +OpDecorate %block Block +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%float = OpTypeFloat 32 +%vec3 = OpTypeVector %float 3 +%8 = OpTypePointer Output %vec3 +%9 = OpVariable %8 Output +%block = OpTypeStruct %vec3 +%11 = OpTypePointer Input %block +%12 = OpVariable %11 Input +%int = OpTypeInt 32 1 +%14 = OpConstant %int 0 +%15 = OpTypePointer Input %vec3 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%16 = OpAccessChain %15 %12 %14 +%17 = OpLoad %vec3 %16 +OpStore %9 %17 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 2 " + "and ending with 4 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationFunctionParameter) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + + OpDecorate %param_f Component 0 + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %int = OpTypeInt 32 0 + %int_2 = OpConstant %int 2 + %struct_b = OpTypeStruct %float + +%extra_fn = OpTypeFunction %void %float + + %helper = OpFunction %void None %extra_fn + %param_f = OpFunctionParameter %float +%helper_label = OpLabel + OpReturn + OpFunctionEnd + + %main = OpFunction %void None %void_fn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + } // namespace } // namespace val } // namespace spvtools