1// Copyright (c) 2016 Google Inc. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// Basic tests for the ValidationState_t datastructure. 16 17#include <string> 18 19#include "gmock/gmock.h" 20#include "source/spirv_validator_options.h" 21#include "test/unit_spirv.h" 22#include "test/val/val_fixtures.h" 23 24namespace spvtools { 25namespace val { 26namespace { 27 28using ::testing::HasSubstr; 29 30using ValidationStateTest = spvtest::ValidateBase<bool>; 31 32const char kHeader[] = 33 " OpCapability Shader" 34 " OpCapability Linkage" 35 " OpMemoryModel Logical GLSL450 "; 36 37const char kVulkanMemoryHeader[] = 38 " OpCapability Shader" 39 " OpCapability VulkanMemoryModelKHR" 40 " OpExtension \"SPV_KHR_vulkan_memory_model\"" 41 " OpMemoryModel Logical VulkanKHR "; 42 43const char kVoidFVoid[] = 44 " %void = OpTypeVoid" 45 " %void_f = OpTypeFunction %void" 46 " %func = OpFunction %void None %void_f" 47 " %label = OpLabel" 48 " OpReturn" 49 " OpFunctionEnd "; 50 51// k*RecursiveBody examples originally from test/opt/function_test.cpp 52const char* kNonRecursiveBody = R"( 53OpEntryPoint Fragment %1 "main" 54OpExecutionMode %1 OriginUpperLeft 55%void = OpTypeVoid 56%4 = OpTypeFunction %void 57%float = OpTypeFloat 32 58%_struct_6 = OpTypeStruct %float %float 59%null = OpConstantNull %_struct_6 60%7 = OpTypeFunction %_struct_6 61%12 = OpFunction %_struct_6 None %7 62%13 = OpLabel 63OpReturnValue %null 64OpFunctionEnd 65%9 = OpFunction %_struct_6 None %7 66%10 = OpLabel 67%11 = OpFunctionCall %_struct_6 %12 68OpReturnValue %null 69OpFunctionEnd 70%1 = OpFunction %void Pure|Const %4 71%8 = OpLabel 72%2 = OpFunctionCall %_struct_6 %9 73OpKill 74OpFunctionEnd 75)"; 76 77const char* kDirectlyRecursiveBody = R"( 78OpEntryPoint Fragment %1 "main" 79OpExecutionMode %1 OriginUpperLeft 80%void = OpTypeVoid 81%4 = OpTypeFunction %void 82%float = OpTypeFloat 32 83%_struct_6 = OpTypeStruct %float %float 84%7 = OpTypeFunction %_struct_6 85%9 = OpFunction %_struct_6 None %7 86%10 = OpLabel 87%11 = OpFunctionCall %_struct_6 %9 88OpKill 89OpFunctionEnd 90%1 = OpFunction %void Pure|Const %4 91%8 = OpLabel 92%2 = OpFunctionCall %_struct_6 %9 93OpReturn 94OpFunctionEnd 95)"; 96 97const char* kIndirectlyRecursiveBody = R"( 98OpEntryPoint Fragment %1 "main" 99OpExecutionMode %1 OriginUpperLeft 100%void = OpTypeVoid 101%4 = OpTypeFunction %void 102%float = OpTypeFloat 32 103%_struct_6 = OpTypeStruct %float %float 104%null = OpConstantNull %_struct_6 105%7 = OpTypeFunction %_struct_6 106%9 = OpFunction %_struct_6 None %7 107%10 = OpLabel 108%11 = OpFunctionCall %_struct_6 %12 109OpReturnValue %null 110OpFunctionEnd 111%12 = OpFunction %_struct_6 None %7 112%13 = OpLabel 113%14 = OpFunctionCall %_struct_6 %9 114OpReturnValue %null 115OpFunctionEnd 116%1 = OpFunction %void Pure|Const %4 117%8 = OpLabel 118%2 = OpFunctionCall %_struct_6 %9 119OpKill 120OpFunctionEnd 121)"; 122 123// Tests that the instruction count in ValidationState is correct. 124TEST_F(ValidationStateTest, CheckNumInstructions) { 125 std::string spirv = std::string(kHeader) + "%int = OpTypeInt 32 0"; 126 CompileSuccessfully(spirv); 127 EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); 128 EXPECT_EQ(size_t(4), vstate_->ordered_instructions().size()); 129} 130 131// Tests that the number of global variables in ValidationState is correct. 132TEST_F(ValidationStateTest, CheckNumGlobalVars) { 133 std::string spirv = std::string(kHeader) + R"( 134 %int = OpTypeInt 32 0 135%_ptr_int = OpTypePointer Input %int 136 %var_1 = OpVariable %_ptr_int Input 137 %var_2 = OpVariable %_ptr_int Input 138 )"; 139 CompileSuccessfully(spirv); 140 EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); 141 EXPECT_EQ(unsigned(2), vstate_->num_global_vars()); 142} 143 144// Tests that the number of local variables in ValidationState is correct. 145TEST_F(ValidationStateTest, CheckNumLocalVars) { 146 std::string spirv = std::string(kHeader) + R"( 147 %int = OpTypeInt 32 0 148 %_ptr_int = OpTypePointer Function %int 149 %voidt = OpTypeVoid 150 %funct = OpTypeFunction %voidt 151 %main = OpFunction %voidt None %funct 152 %entry = OpLabel 153 %var_1 = OpVariable %_ptr_int Function 154 %var_2 = OpVariable %_ptr_int Function 155 %var_3 = OpVariable %_ptr_int Function 156 OpReturn 157 OpFunctionEnd 158 )"; 159 CompileSuccessfully(spirv); 160 EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); 161 EXPECT_EQ(unsigned(3), vstate_->num_local_vars()); 162} 163 164// Tests that the "id bound" in ValidationState is correct. 165TEST_F(ValidationStateTest, CheckIdBound) { 166 std::string spirv = std::string(kHeader) + R"( 167 %int = OpTypeInt 32 0 168 %voidt = OpTypeVoid 169 )"; 170 CompileSuccessfully(spirv); 171 EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); 172 EXPECT_EQ(unsigned(3), vstate_->getIdBound()); 173} 174 175// Tests that the entry_points in ValidationState is correct. 176TEST_F(ValidationStateTest, CheckEntryPoints) { 177 std::string spirv = std::string(kHeader) + 178 " OpEntryPoint Vertex %func \"shader\"" + 179 std::string(kVoidFVoid); 180 CompileSuccessfully(spirv); 181 EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); 182 EXPECT_EQ(size_t(1), vstate_->entry_points().size()); 183 EXPECT_EQ(spv::Op::OpFunction, 184 vstate_->FindDef(vstate_->entry_points()[0])->opcode()); 185} 186 187TEST_F(ValidationStateTest, CheckStructMemberLimitOption) { 188 spvValidatorOptionsSetUniversalLimit( 189 options_, spv_validator_limit_max_struct_members, 32000u); 190 EXPECT_EQ(32000u, options_->universal_limits_.max_struct_members); 191} 192 193TEST_F(ValidationStateTest, CheckNumGlobalVarsLimitOption) { 194 spvValidatorOptionsSetUniversalLimit( 195 options_, spv_validator_limit_max_global_variables, 100u); 196 EXPECT_EQ(100u, options_->universal_limits_.max_global_variables); 197} 198 199TEST_F(ValidationStateTest, CheckNumLocalVarsLimitOption) { 200 spvValidatorOptionsSetUniversalLimit( 201 options_, spv_validator_limit_max_local_variables, 100u); 202 EXPECT_EQ(100u, options_->universal_limits_.max_local_variables); 203} 204 205TEST_F(ValidationStateTest, CheckStructDepthLimitOption) { 206 spvValidatorOptionsSetUniversalLimit( 207 options_, spv_validator_limit_max_struct_depth, 100u); 208 EXPECT_EQ(100u, options_->universal_limits_.max_struct_depth); 209} 210 211TEST_F(ValidationStateTest, CheckSwitchBranchesLimitOption) { 212 spvValidatorOptionsSetUniversalLimit( 213 options_, spv_validator_limit_max_switch_branches, 100u); 214 EXPECT_EQ(100u, options_->universal_limits_.max_switch_branches); 215} 216 217TEST_F(ValidationStateTest, CheckFunctionArgsLimitOption) { 218 spvValidatorOptionsSetUniversalLimit( 219 options_, spv_validator_limit_max_function_args, 100u); 220 EXPECT_EQ(100u, options_->universal_limits_.max_function_args); 221} 222 223TEST_F(ValidationStateTest, CheckCFGDepthLimitOption) { 224 spvValidatorOptionsSetUniversalLimit( 225 options_, spv_validator_limit_max_control_flow_nesting_depth, 100u); 226 EXPECT_EQ(100u, options_->universal_limits_.max_control_flow_nesting_depth); 227} 228 229TEST_F(ValidationStateTest, CheckAccessChainIndexesLimitOption) { 230 spvValidatorOptionsSetUniversalLimit( 231 options_, spv_validator_limit_max_access_chain_indexes, 100u); 232 EXPECT_EQ(100u, options_->universal_limits_.max_access_chain_indexes); 233} 234 235TEST_F(ValidationStateTest, CheckNonRecursiveBodyGood) { 236 std::string spirv = std::string(kHeader) + kNonRecursiveBody; 237 CompileSuccessfully(spirv); 238 EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); 239} 240 241TEST_F(ValidationStateTest, CheckVulkanNonRecursiveBodyGood) { 242 std::string spirv = std::string(kVulkanMemoryHeader) + kNonRecursiveBody; 243 CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); 244 EXPECT_EQ(SPV_SUCCESS, 245 ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); 246} 247 248TEST_F(ValidationStateTest, CheckDirectlyRecursiveBodyGood) { 249 std::string spirv = std::string(kHeader) + kDirectlyRecursiveBody; 250 CompileSuccessfully(spirv); 251 EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); 252} 253 254TEST_F(ValidationStateTest, CheckVulkanDirectlyRecursiveBodyBad) { 255 std::string spirv = std::string(kVulkanMemoryHeader) + kDirectlyRecursiveBody; 256 CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); 257 EXPECT_EQ(SPV_ERROR_INVALID_BINARY, 258 ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); 259 EXPECT_THAT(getDiagnosticString(), 260 AnyVUID("VUID-StandaloneSpirv-None-04634")); 261 EXPECT_THAT(getDiagnosticString(), 262 HasSubstr("Entry points may not have a call graph with cycles.\n " 263 " %1 = OpFunction %void Pure|Const %3\n")); 264} 265 266TEST_F(ValidationStateTest, CheckIndirectlyRecursiveBodyGood) { 267 std::string spirv = std::string(kHeader) + kIndirectlyRecursiveBody; 268 CompileSuccessfully(spirv); 269 EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); 270} 271 272TEST_F(ValidationStateTest, CheckVulkanIndirectlyRecursiveBodyBad) { 273 std::string spirv = 274 std::string(kVulkanMemoryHeader) + kIndirectlyRecursiveBody; 275 CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); 276 EXPECT_EQ(SPV_ERROR_INVALID_BINARY, 277 ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); 278 EXPECT_THAT(getDiagnosticString(), 279 AnyVUID("VUID-StandaloneSpirv-None-04634")); 280 EXPECT_THAT(getDiagnosticString(), 281 HasSubstr("Entry points may not have a call graph with cycles.\n " 282 " %1 = OpFunction %void Pure|Const %3\n")); 283} 284 285} // namespace 286} // namespace val 287} // namespace spvtools 288