• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
24 namespace spvtools {
25 namespace val {
26 namespace {
27 
28 using ::testing::HasSubstr;
29 
30 using ValidationStateTest = spvtest::ValidateBase<bool>;
31 
32 const char kHeader[] =
33     " OpCapability Shader"
34     " OpCapability Linkage"
35     " OpMemoryModel Logical GLSL450 ";
36 
37 const char kVulkanMemoryHeader[] =
38     " OpCapability Shader"
39     " OpCapability VulkanMemoryModelKHR"
40     " OpExtension \"SPV_KHR_vulkan_memory_model\""
41     " OpMemoryModel Logical VulkanKHR ";
42 
43 const 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
52 const char* kNonRecursiveBody = R"(
53 OpEntryPoint Fragment %1 "main"
54 OpExecutionMode %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
63 OpReturnValue %null
64 OpFunctionEnd
65 %9 = OpFunction %_struct_6 None %7
66 %10 = OpLabel
67 %11 = OpFunctionCall %_struct_6 %12
68 OpReturnValue %null
69 OpFunctionEnd
70 %1 = OpFunction %void Pure|Const %4
71 %8 = OpLabel
72 %2 = OpFunctionCall %_struct_6 %9
73 OpKill
74 OpFunctionEnd
75 )";
76 
77 const char* kDirectlyRecursiveBody = R"(
78 OpEntryPoint Fragment %1 "main"
79 OpExecutionMode %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
88 OpKill
89 OpFunctionEnd
90 %1 = OpFunction %void Pure|Const %4
91 %8 = OpLabel
92 %2 = OpFunctionCall %_struct_6 %9
93 OpReturn
94 OpFunctionEnd
95 )";
96 
97 const char* kIndirectlyRecursiveBody = R"(
98 OpEntryPoint Fragment %1 "main"
99 OpExecutionMode %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
109 OpReturnValue %null
110 OpFunctionEnd
111 %12 = OpFunction %_struct_6 None %7
112 %13 = OpLabel
113 %14 = OpFunctionCall %_struct_6 %9
114 OpReturnValue %null
115 OpFunctionEnd
116 %1 = OpFunction %void Pure|Const %4
117 %8 = OpLabel
118 %2 = OpFunctionCall %_struct_6 %9
119 OpKill
120 OpFunctionEnd
121 )";
122 
123 // Tests that the instruction count in ValidationState is correct.
TEST_F(ValidationStateTest,CheckNumInstructions)124 TEST_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.
TEST_F(ValidationStateTest,CheckNumGlobalVars)132 TEST_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.
TEST_F(ValidationStateTest,CheckNumLocalVars)145 TEST_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.
TEST_F(ValidationStateTest,CheckIdBound)165 TEST_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.
TEST_F(ValidationStateTest,CheckEntryPoints)176 TEST_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(SpvOpFunction,
184             vstate_->FindDef(vstate_->entry_points()[0])->opcode());
185 }
186 
TEST_F(ValidationStateTest,CheckStructMemberLimitOption)187 TEST_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 
TEST_F(ValidationStateTest,CheckNumGlobalVarsLimitOption)193 TEST_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 
TEST_F(ValidationStateTest,CheckNumLocalVarsLimitOption)199 TEST_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 
TEST_F(ValidationStateTest,CheckStructDepthLimitOption)205 TEST_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 
TEST_F(ValidationStateTest,CheckSwitchBranchesLimitOption)211 TEST_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 
TEST_F(ValidationStateTest,CheckFunctionArgsLimitOption)217 TEST_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 
TEST_F(ValidationStateTest,CheckCFGDepthLimitOption)223 TEST_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 
TEST_F(ValidationStateTest,CheckAccessChainIndexesLimitOption)229 TEST_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 
TEST_F(ValidationStateTest,CheckNonRecursiveBodyGood)235 TEST_F(ValidationStateTest, CheckNonRecursiveBodyGood) {
236   std::string spirv = std::string(kHeader) + kNonRecursiveBody;
237   CompileSuccessfully(spirv);
238   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
239 }
240 
TEST_F(ValidationStateTest,CheckVulkanNonRecursiveBodyGood)241 TEST_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 
TEST_F(ValidationStateTest,CheckWebGPUNonRecursiveBodyGood)248 TEST_F(ValidationStateTest, CheckWebGPUNonRecursiveBodyGood) {
249   std::string spirv = std::string(kVulkanMemoryHeader) + kNonRecursiveBody;
250   CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
251   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
252 }
253 
TEST_F(ValidationStateTest,CheckDirectlyRecursiveBodyGood)254 TEST_F(ValidationStateTest, CheckDirectlyRecursiveBodyGood) {
255   std::string spirv = std::string(kHeader) + kDirectlyRecursiveBody;
256   CompileSuccessfully(spirv);
257   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
258 }
259 
TEST_F(ValidationStateTest,CheckVulkanDirectlyRecursiveBodyBad)260 TEST_F(ValidationStateTest, CheckVulkanDirectlyRecursiveBodyBad) {
261   std::string spirv = std::string(kVulkanMemoryHeader) + kDirectlyRecursiveBody;
262   CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
263   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
264             ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
265   EXPECT_THAT(getDiagnosticString(),
266               HasSubstr("Entry points may not have a call graph with cycles.\n "
267                         " %1 = OpFunction %void Pure|Const %3\n"));
268 }
269 
TEST_F(ValidationStateTest,CheckWebGPUDirectlyRecursiveBodyBad)270 TEST_F(ValidationStateTest, CheckWebGPUDirectlyRecursiveBodyBad) {
271   std::string spirv = std::string(kVulkanMemoryHeader) + kDirectlyRecursiveBody;
272   CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
273   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
274             ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
275   EXPECT_THAT(getDiagnosticString(),
276               HasSubstr("Entry points may not have a call graph with cycles.\n "
277                         " %1 = OpFunction %void Pure|Const %3\n"));
278 }
279 
TEST_F(ValidationStateTest,CheckIndirectlyRecursiveBodyGood)280 TEST_F(ValidationStateTest, CheckIndirectlyRecursiveBodyGood) {
281   std::string spirv = std::string(kHeader) + kIndirectlyRecursiveBody;
282   CompileSuccessfully(spirv);
283   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
284 }
285 
TEST_F(ValidationStateTest,CheckVulkanIndirectlyRecursiveBodyBad)286 TEST_F(ValidationStateTest, CheckVulkanIndirectlyRecursiveBodyBad) {
287   std::string spirv =
288       std::string(kVulkanMemoryHeader) + kIndirectlyRecursiveBody;
289   CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
290   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
291             ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
292   EXPECT_THAT(getDiagnosticString(),
293               HasSubstr("Entry points may not have a call graph with cycles.\n "
294                         " %1 = OpFunction %void Pure|Const %3\n"));
295 }
296 
297 // Indirectly recursive functions are caught by the function definition layout
298 // rules, because they cause a situation where there are 2 functions that have
299 // to be before each other, and layout is checked earlier.
TEST_F(ValidationStateTest,CheckWebGPUIndirectlyRecursiveBodyBad)300 TEST_F(ValidationStateTest, CheckWebGPUIndirectlyRecursiveBodyBad) {
301   std::string spirv =
302       std::string(kVulkanMemoryHeader) + kIndirectlyRecursiveBody;
303   CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
304   EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT,
305             ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
306   EXPECT_THAT(getDiagnosticString(),
307               HasSubstr("For WebGPU, functions need to be defined before being "
308                         "called.\n  %10 = OpFunctionCall %_struct_5 %11\n"));
309 }
310 
TEST_F(ValidationStateTest,CheckWebGPUDuplicateEntryNamesDifferentFunctionsBad)311 TEST_F(ValidationStateTest,
312        CheckWebGPUDuplicateEntryNamesDifferentFunctionsBad) {
313   std::string spirv = std::string(kVulkanMemoryHeader) + R"(
314 OpEntryPoint Fragment %func_1 "main"
315 OpEntryPoint Vertex %func_2 "main"
316 OpExecutionMode %func_1 OriginUpperLeft
317 %void    = OpTypeVoid
318 %void_f  = OpTypeFunction %void
319 %func_1  = OpFunction %void None %void_f
320 %label_1 = OpLabel
321            OpReturn
322            OpFunctionEnd
323 %func_2  = OpFunction %void None %void_f
324 %label_2 = OpLabel
325            OpReturn
326            OpFunctionEnd
327 )";
328 
329   CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
330   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
331             ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
332   EXPECT_THAT(
333       getDiagnosticString(),
334       HasSubstr("Entry point name \"main\" is not unique, which is not allow "
335                 "in WebGPU env.\n  %1 = OpFunction %void None %4\n"));
336 }
337 
TEST_F(ValidationStateTest,CheckWebGPUDuplicateEntryNamesSameFunctionBad)338 TEST_F(ValidationStateTest, CheckWebGPUDuplicateEntryNamesSameFunctionBad) {
339   std::string spirv = std::string(kVulkanMemoryHeader) + R"(
340 OpEntryPoint GLCompute %func_1 "main"
341 OpEntryPoint Vertex %func_1 "main"
342 %void    = OpTypeVoid
343 %void_f  = OpTypeFunction %void
344 %func_1  = OpFunction %void None %void_f
345 %label_1 = OpLabel
346            OpReturn
347            OpFunctionEnd
348 )";
349 
350   CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
351   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
352             ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
353   EXPECT_THAT(
354       getDiagnosticString(),
355       HasSubstr("Entry point name \"main\" is not unique, which is not allow "
356                 "in WebGPU env.\n  %1 = OpFunction %void None %3\n"));
357 }
358 
359 }  // namespace
360 }  // namespace val
361 }  // namespace spvtools
362