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 %7 = OpTypeFunction %_struct_6
60 %12 = OpFunction %_struct_6 None %7
61 %13 = OpLabel
62 OpUnreachable
63 OpFunctionEnd
64 %9 = OpFunction %_struct_6 None %7
65 %10 = OpLabel
66 %11 = OpFunctionCall %_struct_6 %12
67 OpUnreachable
68 OpFunctionEnd
69 %1 = OpFunction %void Pure|Const %4
70 %8 = OpLabel
71 %2 = OpFunctionCall %_struct_6 %9
72 OpKill
73 OpFunctionEnd
74 )";
75 
76 const char* kDirectlyRecursiveBody = R"(
77 OpEntryPoint Fragment %1 "main"
78 OpExecutionMode %1 OriginUpperLeft
79 %void = OpTypeVoid
80 %4 = OpTypeFunction %void
81 %float = OpTypeFloat 32
82 %_struct_6 = OpTypeStruct %float %float
83 %7 = OpTypeFunction %_struct_6
84 %9 = OpFunction %_struct_6 None %7
85 %10 = OpLabel
86 %11 = OpFunctionCall %_struct_6 %9
87 OpKill
88 OpFunctionEnd
89 %1 = OpFunction %void Pure|Const %4
90 %8 = OpLabel
91 %2 = OpFunctionCall %_struct_6 %9
92 OpUnreachable
93 OpFunctionEnd
94 )";
95 
96 const char* kIndirectlyRecursiveBody = R"(
97 OpEntryPoint Fragment %1 "main"
98 OpExecutionMode %1 OriginUpperLeft
99 %void = OpTypeVoid
100 %4 = OpTypeFunction %void
101 %float = OpTypeFloat 32
102 %_struct_6 = OpTypeStruct %float %float
103 %7 = OpTypeFunction %_struct_6
104 %9 = OpFunction %_struct_6 None %7
105 %10 = OpLabel
106 %11 = OpFunctionCall %_struct_6 %12
107 OpUnreachable
108 OpFunctionEnd
109 %12 = OpFunction %_struct_6 None %7
110 %13 = OpLabel
111 %14 = OpFunctionCall %_struct_6 %9
112 OpUnreachable
113 OpFunctionEnd
114 %1 = OpFunction %void Pure|Const %4
115 %8 = OpLabel
116 %2 = OpFunctionCall %_struct_6 %9
117 OpKill
118 OpFunctionEnd
119 )";
120 
121 // Tests that the instruction count in ValidationState is correct.
TEST_F(ValidationStateTest,CheckNumInstructions)122 TEST_F(ValidationStateTest, CheckNumInstructions) {
123   std::string spirv = std::string(kHeader) + "%int = OpTypeInt 32 0";
124   CompileSuccessfully(spirv);
125   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
126   EXPECT_EQ(size_t(4), vstate_->ordered_instructions().size());
127 }
128 
129 // Tests that the number of global variables in ValidationState is correct.
TEST_F(ValidationStateTest,CheckNumGlobalVars)130 TEST_F(ValidationStateTest, CheckNumGlobalVars) {
131   std::string spirv = std::string(kHeader) + R"(
132      %int = OpTypeInt 32 0
133 %_ptr_int = OpTypePointer Input %int
134    %var_1 = OpVariable %_ptr_int Input
135    %var_2 = OpVariable %_ptr_int Input
136   )";
137   CompileSuccessfully(spirv);
138   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
139   EXPECT_EQ(unsigned(2), vstate_->num_global_vars());
140 }
141 
142 // Tests that the number of local variables in ValidationState is correct.
TEST_F(ValidationStateTest,CheckNumLocalVars)143 TEST_F(ValidationStateTest, CheckNumLocalVars) {
144   std::string spirv = std::string(kHeader) + R"(
145  %int      = OpTypeInt 32 0
146  %_ptr_int = OpTypePointer Function %int
147  %voidt    = OpTypeVoid
148  %funct    = OpTypeFunction %voidt
149  %main     = OpFunction %voidt None %funct
150  %entry    = OpLabel
151  %var_1    = OpVariable %_ptr_int Function
152  %var_2    = OpVariable %_ptr_int Function
153  %var_3    = OpVariable %_ptr_int Function
154  OpReturn
155  OpFunctionEnd
156   )";
157   CompileSuccessfully(spirv);
158   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
159   EXPECT_EQ(unsigned(3), vstate_->num_local_vars());
160 }
161 
162 // Tests that the "id bound" in ValidationState is correct.
TEST_F(ValidationStateTest,CheckIdBound)163 TEST_F(ValidationStateTest, CheckIdBound) {
164   std::string spirv = std::string(kHeader) + R"(
165  %int      = OpTypeInt 32 0
166  %voidt    = OpTypeVoid
167   )";
168   CompileSuccessfully(spirv);
169   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
170   EXPECT_EQ(unsigned(3), vstate_->getIdBound());
171 }
172 
173 // Tests that the entry_points in ValidationState is correct.
TEST_F(ValidationStateTest,CheckEntryPoints)174 TEST_F(ValidationStateTest, CheckEntryPoints) {
175   std::string spirv = std::string(kHeader) +
176                       " OpEntryPoint Vertex %func \"shader\"" +
177                       std::string(kVoidFVoid);
178   CompileSuccessfully(spirv);
179   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
180   EXPECT_EQ(size_t(1), vstate_->entry_points().size());
181   EXPECT_EQ(SpvOpFunction,
182             vstate_->FindDef(vstate_->entry_points()[0])->opcode());
183 }
184 
TEST_F(ValidationStateTest,CheckStructMemberLimitOption)185 TEST_F(ValidationStateTest, CheckStructMemberLimitOption) {
186   spvValidatorOptionsSetUniversalLimit(
187       options_, spv_validator_limit_max_struct_members, 32000u);
188   EXPECT_EQ(32000u, options_->universal_limits_.max_struct_members);
189 }
190 
TEST_F(ValidationStateTest,CheckNumGlobalVarsLimitOption)191 TEST_F(ValidationStateTest, CheckNumGlobalVarsLimitOption) {
192   spvValidatorOptionsSetUniversalLimit(
193       options_, spv_validator_limit_max_global_variables, 100u);
194   EXPECT_EQ(100u, options_->universal_limits_.max_global_variables);
195 }
196 
TEST_F(ValidationStateTest,CheckNumLocalVarsLimitOption)197 TEST_F(ValidationStateTest, CheckNumLocalVarsLimitOption) {
198   spvValidatorOptionsSetUniversalLimit(
199       options_, spv_validator_limit_max_local_variables, 100u);
200   EXPECT_EQ(100u, options_->universal_limits_.max_local_variables);
201 }
202 
TEST_F(ValidationStateTest,CheckStructDepthLimitOption)203 TEST_F(ValidationStateTest, CheckStructDepthLimitOption) {
204   spvValidatorOptionsSetUniversalLimit(
205       options_, spv_validator_limit_max_struct_depth, 100u);
206   EXPECT_EQ(100u, options_->universal_limits_.max_struct_depth);
207 }
208 
TEST_F(ValidationStateTest,CheckSwitchBranchesLimitOption)209 TEST_F(ValidationStateTest, CheckSwitchBranchesLimitOption) {
210   spvValidatorOptionsSetUniversalLimit(
211       options_, spv_validator_limit_max_switch_branches, 100u);
212   EXPECT_EQ(100u, options_->universal_limits_.max_switch_branches);
213 }
214 
TEST_F(ValidationStateTest,CheckFunctionArgsLimitOption)215 TEST_F(ValidationStateTest, CheckFunctionArgsLimitOption) {
216   spvValidatorOptionsSetUniversalLimit(
217       options_, spv_validator_limit_max_function_args, 100u);
218   EXPECT_EQ(100u, options_->universal_limits_.max_function_args);
219 }
220 
TEST_F(ValidationStateTest,CheckCFGDepthLimitOption)221 TEST_F(ValidationStateTest, CheckCFGDepthLimitOption) {
222   spvValidatorOptionsSetUniversalLimit(
223       options_, spv_validator_limit_max_control_flow_nesting_depth, 100u);
224   EXPECT_EQ(100u, options_->universal_limits_.max_control_flow_nesting_depth);
225 }
226 
TEST_F(ValidationStateTest,CheckAccessChainIndexesLimitOption)227 TEST_F(ValidationStateTest, CheckAccessChainIndexesLimitOption) {
228   spvValidatorOptionsSetUniversalLimit(
229       options_, spv_validator_limit_max_access_chain_indexes, 100u);
230   EXPECT_EQ(100u, options_->universal_limits_.max_access_chain_indexes);
231 }
232 
TEST_F(ValidationStateTest,CheckNonRecursiveBodyGood)233 TEST_F(ValidationStateTest, CheckNonRecursiveBodyGood) {
234   std::string spirv = std::string(kHeader) + kNonRecursiveBody;
235   CompileSuccessfully(spirv);
236   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
237 }
238 
TEST_F(ValidationStateTest,CheckVulkanNonRecursiveBodyGood)239 TEST_F(ValidationStateTest, CheckVulkanNonRecursiveBodyGood) {
240   std::string spirv = std::string(kVulkanMemoryHeader) + kNonRecursiveBody;
241   CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
242   EXPECT_EQ(SPV_SUCCESS,
243             ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
244 }
245 
TEST_F(ValidationStateTest,CheckWebGPUNonRecursiveBodyGood)246 TEST_F(ValidationStateTest, CheckWebGPUNonRecursiveBodyGood) {
247   std::string spirv = std::string(kVulkanMemoryHeader) + kNonRecursiveBody;
248   CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
249   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
250 }
251 
TEST_F(ValidationStateTest,CheckDirectlyRecursiveBodyGood)252 TEST_F(ValidationStateTest, CheckDirectlyRecursiveBodyGood) {
253   std::string spirv = std::string(kHeader) + kDirectlyRecursiveBody;
254   CompileSuccessfully(spirv);
255   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
256 }
257 
TEST_F(ValidationStateTest,CheckVulkanDirectlyRecursiveBodyBad)258 TEST_F(ValidationStateTest, CheckVulkanDirectlyRecursiveBodyBad) {
259   std::string spirv = std::string(kVulkanMemoryHeader) + kDirectlyRecursiveBody;
260   CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
261   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
262             ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
263   EXPECT_THAT(getDiagnosticString(),
264               HasSubstr("Entry points may not have a call graph with cycles.\n "
265                         " %1 = OpFunction %void Pure|Const %3\n"));
266 }
267 
TEST_F(ValidationStateTest,CheckWebGPUDirectlyRecursiveBodyBad)268 TEST_F(ValidationStateTest, CheckWebGPUDirectlyRecursiveBodyBad) {
269   std::string spirv = std::string(kVulkanMemoryHeader) + kDirectlyRecursiveBody;
270   CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
271   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
272             ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
273   EXPECT_THAT(getDiagnosticString(),
274               HasSubstr("Entry points may not have a call graph with cycles.\n "
275                         " %1 = OpFunction %void Pure|Const %3\n"));
276 }
277 
TEST_F(ValidationStateTest,CheckIndirectlyRecursiveBodyGood)278 TEST_F(ValidationStateTest, CheckIndirectlyRecursiveBodyGood) {
279   std::string spirv = std::string(kHeader) + kIndirectlyRecursiveBody;
280   CompileSuccessfully(spirv);
281   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
282 }
283 
TEST_F(ValidationStateTest,CheckVulkanIndirectlyRecursiveBodyBad)284 TEST_F(ValidationStateTest, CheckVulkanIndirectlyRecursiveBodyBad) {
285   std::string spirv =
286       std::string(kVulkanMemoryHeader) + kIndirectlyRecursiveBody;
287   CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
288   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
289             ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
290   EXPECT_THAT(getDiagnosticString(),
291               HasSubstr("Entry points may not have a call graph with cycles.\n "
292                         " %1 = OpFunction %void Pure|Const %3\n"));
293 }
294 
295 // Indirectly recursive functions are caught by the function definition layout
296 // rules, because they cause a situation where there are 2 functions that have
297 // to be before each other, and layout is checked earlier.
TEST_F(ValidationStateTest,CheckWebGPUIndirectlyRecursiveBodyBad)298 TEST_F(ValidationStateTest, CheckWebGPUIndirectlyRecursiveBodyBad) {
299   std::string spirv =
300       std::string(kVulkanMemoryHeader) + kIndirectlyRecursiveBody;
301   CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
302   EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT,
303             ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
304   EXPECT_THAT(getDiagnosticString(),
305               HasSubstr("For WebGPU, functions need to be defined before being "
306                         "called.\n  %9 = OpFunctionCall %_struct_5 %10\n"));
307 }
308 
309 }  // namespace
310 }  // namespace val
311 }  // namespace spvtools
312