| // Copyright (c) 2016 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. | |
| // Basic tests for the ValidationState_t datastructure. | |
| #include <string> | |
| #include "gmock/gmock.h" | |
| #include "source/spirv_validator_options.h" | |
| #include "test/unit_spirv.h" | |
| #include "test/val/val_fixtures.h" | |
| namespace spvtools { | |
| namespace val { | |
| namespace { | |
| using ::testing::HasSubstr; | |
| using ValidationStateTest = spvtest::ValidateBase<bool>; | |
| const char kHeader[] = | |
| " OpCapability Shader" | |
| " OpCapability Linkage" | |
| " OpMemoryModel Logical GLSL450 "; | |
| const char kVulkanMemoryHeader[] = | |
| " OpCapability Shader" | |
| " OpCapability VulkanMemoryModelKHR" | |
| " OpExtension \"SPV_KHR_vulkan_memory_model\"" | |
| " OpMemoryModel Logical VulkanKHR "; | |
| const char kVoidFVoid[] = | |
| " %void = OpTypeVoid" | |
| " %void_f = OpTypeFunction %void" | |
| " %func = OpFunction %void None %void_f" | |
| " %label = OpLabel" | |
| " OpReturn" | |
| " OpFunctionEnd "; | |
| // k*RecursiveBody examples originally from test/opt/function_test.cpp | |
| const char* kNonRecursiveBody = R"( | |
| OpEntryPoint Fragment %1 "main" | |
| OpExecutionMode %1 OriginUpperLeft | |
| %void = OpTypeVoid | |
| %4 = OpTypeFunction %void | |
| %float = OpTypeFloat 32 | |
| %_struct_6 = OpTypeStruct %float %float | |
| %null = OpConstantNull %_struct_6 | |
| %7 = OpTypeFunction %_struct_6 | |
| %12 = OpFunction %_struct_6 None %7 | |
| %13 = OpLabel | |
| OpReturnValue %null | |
| OpFunctionEnd | |
| %9 = OpFunction %_struct_6 None %7 | |
| %10 = OpLabel | |
| %11 = OpFunctionCall %_struct_6 %12 | |
| OpReturnValue %null | |
| OpFunctionEnd | |
| %1 = OpFunction %void Pure|Const %4 | |
| %8 = OpLabel | |
| %2 = OpFunctionCall %_struct_6 %9 | |
| OpKill | |
| OpFunctionEnd | |
| )"; | |
| const char* kDirectlyRecursiveBody = R"( | |
| OpEntryPoint Fragment %1 "main" | |
| OpExecutionMode %1 OriginUpperLeft | |
| %void = OpTypeVoid | |
| %4 = OpTypeFunction %void | |
| %float = OpTypeFloat 32 | |
| %_struct_6 = OpTypeStruct %float %float | |
| %7 = OpTypeFunction %_struct_6 | |
| %9 = OpFunction %_struct_6 None %7 | |
| %10 = OpLabel | |
| %11 = OpFunctionCall %_struct_6 %9 | |
| OpKill | |
| OpFunctionEnd | |
| %1 = OpFunction %void Pure|Const %4 | |
| %8 = OpLabel | |
| %2 = OpFunctionCall %_struct_6 %9 | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| const char* kIndirectlyRecursiveBody = R"( | |
| OpEntryPoint Fragment %1 "main" | |
| OpExecutionMode %1 OriginUpperLeft | |
| %void = OpTypeVoid | |
| %4 = OpTypeFunction %void | |
| %float = OpTypeFloat 32 | |
| %_struct_6 = OpTypeStruct %float %float | |
| %null = OpConstantNull %_struct_6 | |
| %7 = OpTypeFunction %_struct_6 | |
| %9 = OpFunction %_struct_6 None %7 | |
| %10 = OpLabel | |
| %11 = OpFunctionCall %_struct_6 %12 | |
| OpReturnValue %null | |
| OpFunctionEnd | |
| %12 = OpFunction %_struct_6 None %7 | |
| %13 = OpLabel | |
| %14 = OpFunctionCall %_struct_6 %9 | |
| OpReturnValue %null | |
| OpFunctionEnd | |
| %1 = OpFunction %void Pure|Const %4 | |
| %8 = OpLabel | |
| %2 = OpFunctionCall %_struct_6 %9 | |
| OpKill | |
| OpFunctionEnd | |
| )"; | |
| // Tests that the instruction count in ValidationState is correct. | |
| TEST_F(ValidationStateTest, CheckNumInstructions) { | |
| std::string spirv = std::string(kHeader) + "%int = OpTypeInt 32 0"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); | |
| EXPECT_EQ(size_t(4), vstate_->ordered_instructions().size()); | |
| } | |
| // Tests that the number of global variables in ValidationState is correct. | |
| TEST_F(ValidationStateTest, CheckNumGlobalVars) { | |
| std::string spirv = std::string(kHeader) + R"( | |
| %int = OpTypeInt 32 0 | |
| %_ptr_int = OpTypePointer Input %int | |
| %var_1 = OpVariable %_ptr_int Input | |
| %var_2 = OpVariable %_ptr_int Input | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); | |
| EXPECT_EQ(unsigned(2), vstate_->num_global_vars()); | |
| } | |
| // Tests that the number of local variables in ValidationState is correct. | |
| TEST_F(ValidationStateTest, CheckNumLocalVars) { | |
| std::string spirv = std::string(kHeader) + R"( | |
| %int = OpTypeInt 32 0 | |
| %_ptr_int = OpTypePointer Function %int | |
| %voidt = OpTypeVoid | |
| %funct = OpTypeFunction %voidt | |
| %main = OpFunction %voidt None %funct | |
| %entry = OpLabel | |
| %var_1 = OpVariable %_ptr_int Function | |
| %var_2 = OpVariable %_ptr_int Function | |
| %var_3 = OpVariable %_ptr_int Function | |
| OpReturn | |
| OpFunctionEnd | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); | |
| EXPECT_EQ(unsigned(3), vstate_->num_local_vars()); | |
| } | |
| // Tests that the "id bound" in ValidationState is correct. | |
| TEST_F(ValidationStateTest, CheckIdBound) { | |
| std::string spirv = std::string(kHeader) + R"( | |
| %int = OpTypeInt 32 0 | |
| %voidt = OpTypeVoid | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); | |
| EXPECT_EQ(unsigned(3), vstate_->getIdBound()); | |
| } | |
| // Tests that the entry_points in ValidationState is correct. | |
| TEST_F(ValidationStateTest, CheckEntryPoints) { | |
| std::string spirv = std::string(kHeader) + | |
| " OpEntryPoint Vertex %func \"shader\"" + | |
| std::string(kVoidFVoid); | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); | |
| EXPECT_EQ(size_t(1), vstate_->entry_points().size()); | |
| EXPECT_EQ(spv::Op::OpFunction, | |
| vstate_->FindDef(vstate_->entry_points()[0])->opcode()); | |
| } | |
| TEST_F(ValidationStateTest, CheckStructMemberLimitOption) { | |
| spvValidatorOptionsSetUniversalLimit( | |
| options_, spv_validator_limit_max_struct_members, 32000u); | |
| EXPECT_EQ(32000u, options_->universal_limits_.max_struct_members); | |
| } | |
| TEST_F(ValidationStateTest, CheckNumGlobalVarsLimitOption) { | |
| spvValidatorOptionsSetUniversalLimit( | |
| options_, spv_validator_limit_max_global_variables, 100u); | |
| EXPECT_EQ(100u, options_->universal_limits_.max_global_variables); | |
| } | |
| TEST_F(ValidationStateTest, CheckNumLocalVarsLimitOption) { | |
| spvValidatorOptionsSetUniversalLimit( | |
| options_, spv_validator_limit_max_local_variables, 100u); | |
| EXPECT_EQ(100u, options_->universal_limits_.max_local_variables); | |
| } | |
| TEST_F(ValidationStateTest, CheckStructDepthLimitOption) { | |
| spvValidatorOptionsSetUniversalLimit( | |
| options_, spv_validator_limit_max_struct_depth, 100u); | |
| EXPECT_EQ(100u, options_->universal_limits_.max_struct_depth); | |
| } | |
| TEST_F(ValidationStateTest, CheckSwitchBranchesLimitOption) { | |
| spvValidatorOptionsSetUniversalLimit( | |
| options_, spv_validator_limit_max_switch_branches, 100u); | |
| EXPECT_EQ(100u, options_->universal_limits_.max_switch_branches); | |
| } | |
| TEST_F(ValidationStateTest, CheckFunctionArgsLimitOption) { | |
| spvValidatorOptionsSetUniversalLimit( | |
| options_, spv_validator_limit_max_function_args, 100u); | |
| EXPECT_EQ(100u, options_->universal_limits_.max_function_args); | |
| } | |
| TEST_F(ValidationStateTest, CheckCFGDepthLimitOption) { | |
| spvValidatorOptionsSetUniversalLimit( | |
| options_, spv_validator_limit_max_control_flow_nesting_depth, 100u); | |
| EXPECT_EQ(100u, options_->universal_limits_.max_control_flow_nesting_depth); | |
| } | |
| TEST_F(ValidationStateTest, CheckAccessChainIndexesLimitOption) { | |
| spvValidatorOptionsSetUniversalLimit( | |
| options_, spv_validator_limit_max_access_chain_indexes, 100u); | |
| EXPECT_EQ(100u, options_->universal_limits_.max_access_chain_indexes); | |
| } | |
| TEST_F(ValidationStateTest, CheckNonRecursiveBodyGood) { | |
| std::string spirv = std::string(kHeader) + kNonRecursiveBody; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); | |
| } | |
| TEST_F(ValidationStateTest, CheckVulkanNonRecursiveBodyGood) { | |
| std::string spirv = std::string(kVulkanMemoryHeader) + kNonRecursiveBody; | |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); | |
| EXPECT_EQ(SPV_SUCCESS, | |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); | |
| } | |
| TEST_F(ValidationStateTest, CheckDirectlyRecursiveBodyGood) { | |
| std::string spirv = std::string(kHeader) + kDirectlyRecursiveBody; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); | |
| } | |
| TEST_F(ValidationStateTest, CheckVulkanDirectlyRecursiveBodyBad) { | |
| std::string spirv = std::string(kVulkanMemoryHeader) + kDirectlyRecursiveBody; | |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); | |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, | |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| AnyVUID("VUID-StandaloneSpirv-None-04634")); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr("Entry points may not have a call graph with cycles.\n " | |
| " %1 = OpFunction %void Pure|Const %3\n")); | |
| } | |
| TEST_F(ValidationStateTest, CheckIndirectlyRecursiveBodyGood) { | |
| std::string spirv = std::string(kHeader) + kIndirectlyRecursiveBody; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); | |
| } | |
| TEST_F(ValidationStateTest, CheckVulkanIndirectlyRecursiveBodyBad) { | |
| std::string spirv = | |
| std::string(kVulkanMemoryHeader) + kIndirectlyRecursiveBody; | |
| CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); | |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, | |
| ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| AnyVUID("VUID-StandaloneSpirv-None-04634")); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr("Entry points may not have a call graph with cycles.\n " | |
| " %1 = OpFunction %void Pure|Const %3\n")); | |
| } | |
| } // namespace | |
| } // namespace val | |
| } // namespace spvtools |