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