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