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