// Copyright (c) 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Common validation fixtures for unit tests #include "gmock/gmock.h" #include "source/val/decoration.h" #include "unit_spirv.h" #include "val_fixtures.h" namespace { using ::testing::Eq; using ::testing::HasSubstr; using libspirv::Decoration; using std::string; using std::vector; using ValidateDecorations = spvtest::ValidateBase; TEST_F(ValidateDecorations, ValidateOpDecorateRegistration) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpDecorate %1 ArrayStride 4 OpDecorate %1 Uniform %2 = OpTypeFloat 32 %1 = OpTypeRuntimeArray %2 ; Since %1 is used first in Decoration, it gets id 1. )"; const uint32_t id = 1; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); // Must have 2 decorations. EXPECT_THAT(vstate_->id_decorations(id), Eq(vector{Decoration(SpvDecorationArrayStride, {4}), Decoration(SpvDecorationUniform)})); } TEST_F(ValidateDecorations, ValidateOpMemberDecorateRegistration) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpDecorate %_arr_double_uint_6 ArrayStride 4 OpMemberDecorate %_struct_115 2 NonReadable OpMemberDecorate %_struct_115 2 Offset 2 OpDecorate %_struct_115 BufferBlock %float = OpTypeFloat 32 %uint = OpTypeInt 32 0 %uint_6 = OpConstant %uint 6 %_arr_double_uint_6 = OpTypeArray %float %uint_6 %_struct_115 = OpTypeStruct %float %float %_arr_double_uint_6 )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); // The array must have 1 decoration. const uint32_t arr_id = 1; EXPECT_THAT( vstate_->id_decorations(arr_id), Eq(vector{Decoration(SpvDecorationArrayStride, {4})})); // The struct must have 3 decorations. const uint32_t struct_id = 2; EXPECT_THAT(vstate_->id_decorations(struct_id), Eq(vector{Decoration(SpvDecorationNonReadable, {}, 2), Decoration(SpvDecorationOffset, {2}, 2), Decoration(SpvDecorationBufferBlock)})); } TEST_F(ValidateDecorations, ValidateGroupDecorateRegistration) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpDecorate %1 DescriptorSet 0 OpDecorate %1 NonWritable OpDecorate %1 Restrict %1 = OpDecorationGroup OpGroupDecorate %1 %2 %3 OpGroupDecorate %1 %4 %float = OpTypeFloat 32 %_runtimearr_float = OpTypeRuntimeArray %float %_struct_9 = OpTypeStruct %_runtimearr_float %_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9 %2 = OpVariable %_ptr_Uniform__struct_9 Uniform %_struct_10 = OpTypeStruct %_runtimearr_float %_ptr_Uniform__struct_10 = OpTypePointer Uniform %_struct_10 %3 = OpVariable %_ptr_Uniform__struct_10 Uniform %_struct_11 = OpTypeStruct %_runtimearr_float %_ptr_Uniform__struct_11 = OpTypePointer Uniform %_struct_11 %4 = OpVariable %_ptr_Uniform__struct_11 Uniform )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); // Decoration group has 3 decorations. auto expected_decorations = vector{ Decoration(SpvDecorationDescriptorSet, {0}), Decoration(SpvDecorationNonWritable), Decoration(SpvDecorationRestrict)}; // Decoration group is applied to id 1, 2, 3, and 4. Note that id 1 (which is // the decoration group id) also has all the decorations. EXPECT_THAT(vstate_->id_decorations(1), Eq(expected_decorations)); EXPECT_THAT(vstate_->id_decorations(2), Eq(expected_decorations)); EXPECT_THAT(vstate_->id_decorations(3), Eq(expected_decorations)); EXPECT_THAT(vstate_->id_decorations(4), Eq(expected_decorations)); } TEST_F(ValidateDecorations, ValidateGroupMemberDecorateRegistration) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpDecorate %1 Offset 3 %1 = OpDecorationGroup OpGroupMemberDecorate %1 %_struct_1 3 %_struct_2 3 %_struct_3 3 %float = OpTypeFloat 32 %_runtimearr = OpTypeRuntimeArray %float %_struct_1 = OpTypeStruct %float %float %float %_runtimearr %_struct_2 = OpTypeStruct %float %float %float %_runtimearr %_struct_3 = OpTypeStruct %float %float %float %_runtimearr )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); // Decoration group has 1 decoration. auto expected_decorations = vector{Decoration(SpvDecorationOffset, {3}, 3)}; // Decoration group is applied to id 2, 3, and 4. EXPECT_THAT(vstate_->id_decorations(2), Eq(expected_decorations)); EXPECT_THAT(vstate_->id_decorations(3), Eq(expected_decorations)); EXPECT_THAT(vstate_->id_decorations(4), Eq(expected_decorations)); } TEST_F(ValidateDecorations, LinkageImportUsedForInitializedVariableBad) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpDecorate %target LinkageAttributes "link_ptr" Import %float = OpTypeFloat 32 %_ptr_float = OpTypePointer Uniform %float %zero = OpConstantNull %float %target = OpVariable %_ptr_float Uniform %zero )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); EXPECT_THAT(getDiagnosticString(), HasSubstr("A module-scope OpVariable with initialization value " "cannot be marked with the Import Linkage Type.")); } TEST_F(ValidateDecorations, LinkageExportUsedForInitializedVariableGood) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpDecorate %target LinkageAttributes "link_ptr" Export %float = OpTypeFloat 32 %_ptr_float = OpTypePointer Uniform %float %zero = OpConstantNull %float %target = OpVariable %_ptr_float Uniform %zero )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); } TEST_F(ValidateDecorations, StructAllMembersHaveBuiltInDecorationsGood) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpMemberDecorate %_struct_1 0 BuiltIn Position OpMemberDecorate %_struct_1 1 BuiltIn Position OpMemberDecorate %_struct_1 2 BuiltIn Position OpMemberDecorate %_struct_1 3 BuiltIn Position %float = OpTypeFloat 32 %_runtimearr = OpTypeRuntimeArray %float %_struct_1 = OpTypeStruct %float %float %float %_runtimearr )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); } TEST_F(ValidateDecorations, MixedBuiltInDecorationsBad) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpMemberDecorate %_struct_1 0 BuiltIn Position OpMemberDecorate %_struct_1 1 BuiltIn Position %float = OpTypeFloat 32 %_runtimearr = OpTypeRuntimeArray %float %_struct_1 = OpTypeStruct %float %float %float %_runtimearr )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); EXPECT_THAT( getDiagnosticString(), HasSubstr("When BuiltIn decoration is applied to a structure-type " "member, all members of that structure type must also be " "decorated with BuiltIn (No allowed mixing of built-in " "variables and non-built-in variables within a single " "structure). Structure id 1 does not meet this requirement.")); } TEST_F(ValidateDecorations, StructContainsBuiltInStructBad) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpMemberDecorate %_struct_1 0 BuiltIn Position OpMemberDecorate %_struct_1 1 BuiltIn Position OpMemberDecorate %_struct_1 2 BuiltIn Position OpMemberDecorate %_struct_1 3 BuiltIn Position %float = OpTypeFloat 32 %_runtimearr = OpTypeRuntimeArray %float %_struct_1 = OpTypeStruct %float %float %float %_runtimearr %_struct_2 = OpTypeStruct %_struct_1 )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Structure 1 contains members with BuiltIn " "decoration. Therefore this structure may not be " "contained as a member of another structure type. " "Structure 4 contains structure 1.")); } TEST_F(ValidateDecorations, StructContainsNonBuiltInStructGood) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 %float = OpTypeFloat 32 %_struct_1 = OpTypeStruct %float %_struct_2 = OpTypeStruct %_struct_1 )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); } TEST_F(ValidateDecorations, MultipleBuiltInObjectsConsumedByOpEntryPointBad) { string spirv = R"( OpCapability Shader OpCapability Geometry OpMemoryModel Logical GLSL450 OpEntryPoint Geometry %main "main" %in_1 %in_2 OpMemberDecorate %struct_1 0 BuiltIn InvocationId OpMemberDecorate %struct_2 0 BuiltIn Position %int = OpTypeInt 32 1 %void = OpTypeVoid %func = OpTypeFunction %void %float = OpTypeFloat 32 %struct_1 = OpTypeStruct %int %struct_2 = OpTypeStruct %float %ptr_builtin_1 = OpTypePointer Input %struct_1 %ptr_builtin_2 = OpTypePointer Input %struct_2 %in_1 = OpVariable %ptr_builtin_1 Input %in_2 = OpVariable %ptr_builtin_2 Input %main = OpFunction %void None %func %5 = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState()); EXPECT_THAT(getDiagnosticString(), HasSubstr("There must be at most one object per Storage Class " "that can contain a structure type containing members " "decorated with BuiltIn, consumed per entry-point.")); } TEST_F(ValidateDecorations, OneBuiltInObjectPerStorageClassConsumedByOpEntryPointGood) { string spirv = R"( OpCapability Shader OpCapability Geometry OpMemoryModel Logical GLSL450 OpEntryPoint Geometry %main "main" %in_1 %out_1 OpMemberDecorate %struct_1 0 BuiltIn InvocationId OpMemberDecorate %struct_2 0 BuiltIn Position %int = OpTypeInt 32 1 %void = OpTypeVoid %func = OpTypeFunction %void %float = OpTypeFloat 32 %struct_1 = OpTypeStruct %int %struct_2 = OpTypeStruct %float %ptr_builtin_1 = OpTypePointer Input %struct_1 %ptr_builtin_2 = OpTypePointer Output %struct_2 %in_1 = OpVariable %ptr_builtin_1 Input %out_1 = OpVariable %ptr_builtin_2 Output %main = OpFunction %void None %func %5 = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); } TEST_F(ValidateDecorations, NoBuiltInObjectsConsumedByOpEntryPointGood) { string spirv = R"( OpCapability Shader OpCapability Geometry OpMemoryModel Logical GLSL450 OpEntryPoint Geometry %main "main" %in_1 %out_1 %int = OpTypeInt 32 1 %void = OpTypeVoid %func = OpTypeFunction %void %float = OpTypeFloat 32 %struct_1 = OpTypeStruct %int %struct_2 = OpTypeStruct %float %ptr_builtin_1 = OpTypePointer Input %struct_1 %ptr_builtin_2 = OpTypePointer Output %struct_2 %in_1 = OpVariable %ptr_builtin_1 Input %out_1 = OpVariable %ptr_builtin_2 Output %main = OpFunction %void None %func %5 = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); } TEST_F(ValidateDecorations, EntryPointFunctionHasLinkageAttributeBad) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" OpDecorate %main LinkageAttributes "import_main" Import %1 = OpTypeVoid %2 = OpTypeFunction %1 %main = OpFunction %1 None %2 %4 = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv.c_str()); EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("The LinkageAttributes Decoration (Linkage name: import_main) " "cannot be applied to function id 1 because it is targeted by " "an OpEntryPoint instruction.")); } TEST_F(ValidateDecorations, FunctionDeclarationWithoutImportLinkageBad) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 %void = OpTypeVoid %func = OpTypeFunction %void %main = OpFunction %void None %func OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState()); EXPECT_THAT( getDiagnosticString(), HasSubstr("Function declaration (id 3) must have a LinkageAttributes " "decoration with the Import Linkage type.")); } TEST_F(ValidateDecorations, FunctionDeclarationWithImportLinkageGood) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpDecorate %main LinkageAttributes "link_fn" Import %void = OpTypeVoid %func = OpTypeFunction %void %main = OpFunction %void None %func OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); } TEST_F(ValidateDecorations, FunctionDeclarationWithExportLinkageBad) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpDecorate %main LinkageAttributes "link_fn" Export %void = OpTypeVoid %func = OpTypeFunction %void %main = OpFunction %void None %func OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState()); EXPECT_THAT( getDiagnosticString(), HasSubstr("Function declaration (id 1) must have a LinkageAttributes " "decoration with the Import Linkage type.")); } TEST_F(ValidateDecorations, FunctionDefinitionWithImportLinkageBad) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 OpDecorate %main LinkageAttributes "link_fn" Import %void = OpTypeVoid %func = OpTypeFunction %void %main = OpFunction %void None %func %label = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Function definition (id 1) may not be decorated with " "Import Linkage type.")); } TEST_F(ValidateDecorations, FunctionDefinitionWithoutImportLinkageGood) { string spirv = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 %void = OpTypeVoid %func = OpTypeFunction %void %main = OpFunction %void None %func %label = OpLabel OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); } TEST_F(ValidateDecorations, BuiltinVariablesGoodVulkan) { const spv_target_env env = SPV_ENV_VULKAN_1_0; std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %gl_FragCoord %_entryPointOutput OpExecutionMode %main OriginUpperLeft OpSource HLSL 500 OpDecorate %gl_FragCoord BuiltIn FragCoord OpDecorate %_entryPointOutput Location 0 %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %float_0 = OpConstant %float 0 %14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 %_ptr_Input_v4float = OpTypePointer Input %v4float %gl_FragCoord = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput = OpVariable %_ptr_Output_v4float Output %main = OpFunction %void None %3 %5 = OpLabel OpStore %_entryPointOutput %14 OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, env); EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); } TEST_F(ValidateDecorations, BuiltinVariablesWithLocationDecorationVulkan) { const spv_target_env env = SPV_ENV_VULKAN_1_0; std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %gl_FragCoord %_entryPointOutput OpExecutionMode %main OriginUpperLeft OpSource HLSL 500 OpDecorate %gl_FragCoord BuiltIn FragCoord OpDecorate %gl_FragCoord Location 0 OpDecorate %_entryPointOutput Location 0 %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %float_0 = OpConstant %float 0 %14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 %_ptr_Input_v4float = OpTypePointer Input %v4float %gl_FragCoord = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput = OpVariable %_ptr_Output_v4float Output %main = OpFunction %void None %3 %5 = OpLabel OpStore %_entryPointOutput %14 OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, env); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); EXPECT_THAT(getDiagnosticString(), HasSubstr("A BuiltIn variable (id 2) cannot have any Location or " "Component decorations")); } TEST_F(ValidateDecorations, BuiltinVariablesWithComponentDecorationVulkan) { const spv_target_env env = SPV_ENV_VULKAN_1_0; std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %gl_FragCoord %_entryPointOutput OpExecutionMode %main OriginUpperLeft OpSource HLSL 500 OpDecorate %gl_FragCoord BuiltIn FragCoord OpDecorate %gl_FragCoord Component 0 OpDecorate %_entryPointOutput Location 0 %void = OpTypeVoid %3 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %float_0 = OpConstant %float 0 %14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 %_ptr_Input_v4float = OpTypePointer Input %v4float %gl_FragCoord = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %_entryPointOutput = OpVariable %_ptr_Output_v4float Output %main = OpFunction %void None %3 %5 = OpLabel OpStore %_entryPointOutput %14 OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv, env); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); EXPECT_THAT(getDiagnosticString(), HasSubstr("A BuiltIn variable (id 2) cannot have any Location or " "Component decorations")); } } // anonymous namespace