1 // Copyright (c) 2018 LunarG 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 #include <sstream>
16 #include <string>
17
18 #include "gmock/gmock.h"
19 #include "test/unit_spirv.h"
20 #include "test/val/val_fixtures.h"
21
22 namespace spvtools {
23 namespace val {
24 namespace {
25
26 using ::testing::HasSubstr;
27 using ::testing::Not;
28
29 using ValidateAdjacency = spvtest::ValidateBase<bool>;
30
TEST_F(ValidateAdjacency,OpPhiBeginsModuleFail)31 TEST_F(ValidateAdjacency, OpPhiBeginsModuleFail) {
32 const std::string module = R"(
33 %result = OpPhi %bool %true %true_label %false %false_label
34 OpCapability Shader
35 OpMemoryModel Logical GLSL450
36 OpEntryPoint Fragment %main "main"
37 OpExecutionMode %main OriginUpperLeft
38 %void = OpTypeVoid
39 %bool = OpTypeBool
40 %true = OpConstantTrue %bool
41 %false = OpConstantFalse %bool
42 %func = OpTypeFunction %void
43 %main = OpFunction %void None %func
44 %main_entry = OpLabel
45 OpBranch %true_label
46 %true_label = OpLabel
47 OpBranch %false_label
48 %false_label = OpLabel
49 OpBranch %end_label
50 OpReturn
51 OpFunctionEnd
52 )";
53
54 CompileSuccessfully(module);
55 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
56 EXPECT_THAT(getDiagnosticString(),
57 HasSubstr("ID 1[%bool] has not been defined"));
58 }
59
TEST_F(ValidateAdjacency,OpLoopMergeEndsModuleFail)60 TEST_F(ValidateAdjacency, OpLoopMergeEndsModuleFail) {
61 const std::string module = R"(
62 OpCapability Shader
63 OpMemoryModel Logical GLSL450
64 OpEntryPoint Fragment %main "main"
65 OpExecutionMode %main OriginUpperLeft
66 %void = OpTypeVoid
67 %func = OpTypeFunction %void
68 %main = OpFunction %void None %func
69 %main_entry = OpLabel
70 OpBranch %loop
71 %loop = OpLabel
72 OpLoopMerge %end %loop None
73 )";
74
75 CompileSuccessfully(module);
76 EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
77 EXPECT_THAT(getDiagnosticString(),
78 HasSubstr("Missing OpFunctionEnd at end of module"));
79 }
80
TEST_F(ValidateAdjacency,OpSelectionMergeEndsModuleFail)81 TEST_F(ValidateAdjacency, OpSelectionMergeEndsModuleFail) {
82 const std::string module = R"(
83 OpCapability Shader
84 OpMemoryModel Logical GLSL450
85 OpEntryPoint Fragment %main "main"
86 OpExecutionMode %main OriginUpperLeft
87 %void = OpTypeVoid
88 %func = OpTypeFunction %void
89 %main = OpFunction %void None %func
90 %main_entry = OpLabel
91 OpBranch %merge
92 %merge = OpLabel
93 OpSelectionMerge %merge None
94 )";
95
96 CompileSuccessfully(module);
97 EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
98 EXPECT_THAT(getDiagnosticString(),
99 HasSubstr("Missing OpFunctionEnd at end of module"));
100 }
101
GenerateShaderCode(const std::string & body,const std::string & capabilities_and_extensions="OpCapability Shader",const std::string & execution_model="Fragment")102 std::string GenerateShaderCode(
103 const std::string& body,
104 const std::string& capabilities_and_extensions = "OpCapability Shader",
105 const std::string& execution_model = "Fragment") {
106 std::ostringstream ss;
107 ss << capabilities_and_extensions << "\n";
108 ss << "OpMemoryModel Logical GLSL450\n";
109 ss << "OpEntryPoint " << execution_model << " %main \"main\"\n";
110 if (execution_model == "Fragment") {
111 ss << "OpExecutionMode %main OriginUpperLeft\n";
112 }
113
114 ss << R"(
115 %string = OpString ""
116 %void = OpTypeVoid
117 %bool = OpTypeBool
118 %int = OpTypeInt 32 0
119 %true = OpConstantTrue %bool
120 %false = OpConstantFalse %bool
121 %zero = OpConstant %int 0
122 %int_1 = OpConstant %int 1
123 %func = OpTypeFunction %void
124 %func_int = OpTypePointer Function %int
125 %main = OpFunction %void None %func
126 %main_entry = OpLabel
127 )";
128
129 ss << body;
130
131 ss << R"(
132 OpReturn
133 OpFunctionEnd)";
134
135 return ss.str();
136 }
137
TEST_F(ValidateAdjacency,OpPhiPreceededByOpLabelSuccess)138 TEST_F(ValidateAdjacency, OpPhiPreceededByOpLabelSuccess) {
139 const std::string body = R"(
140 OpSelectionMerge %end_label None
141 OpBranchConditional %true %true_label %false_label
142 %true_label = OpLabel
143 OpBranch %end_label
144 %false_label = OpLabel
145 OpBranch %end_label
146 %end_label = OpLabel
147 %line = OpLine %string 0 0
148 %result = OpPhi %bool %true %true_label %false %false_label
149 )";
150
151 CompileSuccessfully(GenerateShaderCode(body));
152 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
153 }
154
TEST_F(ValidateAdjacency,OpPhiPreceededByOpPhiSuccess)155 TEST_F(ValidateAdjacency, OpPhiPreceededByOpPhiSuccess) {
156 const std::string body = R"(
157 OpSelectionMerge %end_label None
158 OpBranchConditional %true %true_label %false_label
159 %true_label = OpLabel
160 OpBranch %end_label
161 %false_label = OpLabel
162 OpBranch %end_label
163 %end_label = OpLabel
164 %1 = OpPhi %bool %true %true_label %false %false_label
165 %2 = OpPhi %bool %true %true_label %false %false_label
166 )";
167
168 CompileSuccessfully(GenerateShaderCode(body));
169 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
170 }
171
TEST_F(ValidateAdjacency,OpPhiPreceededByOpLineSuccess)172 TEST_F(ValidateAdjacency, OpPhiPreceededByOpLineSuccess) {
173 const std::string body = R"(
174 OpSelectionMerge %end_label None
175 OpBranchConditional %true %true_label %false_label
176 %true_label = OpLabel
177 OpBranch %end_label
178 %false_label = OpLabel
179 OpBranch %end_label
180 %end_label = OpLabel
181 %line = OpLine %string 0 0
182 %result = OpPhi %bool %true %true_label %false %false_label
183 )";
184
185 CompileSuccessfully(GenerateShaderCode(body));
186 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
187 }
188
TEST_F(ValidateAdjacency,OpPhiPreceededByBadOpFail)189 TEST_F(ValidateAdjacency, OpPhiPreceededByBadOpFail) {
190 const std::string body = R"(
191 OpSelectionMerge %end_label None
192 OpBranchConditional %true %true_label %false_label
193 %true_label = OpLabel
194 OpBranch %end_label
195 %false_label = OpLabel
196 OpBranch %end_label
197 %end_label = OpLabel
198 OpNop
199 %result = OpPhi %bool %true %true_label %false %false_label
200 )";
201
202 CompileSuccessfully(GenerateShaderCode(body));
203 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
204 EXPECT_THAT(getDiagnosticString(),
205 HasSubstr("OpPhi must appear within a non-entry block before all "
206 "non-OpPhi instructions"));
207 }
208
TEST_F(ValidateAdjacency,OpPhiPreceededByOpLineAndBadOpFail)209 TEST_F(ValidateAdjacency, OpPhiPreceededByOpLineAndBadOpFail) {
210 const std::string body = R"(
211 OpSelectionMerge %end_label None
212 OpBranchConditional %true %true_label %false_label
213 %true_label = OpLabel
214 OpBranch %end_label
215 %false_label = OpLabel
216 OpBranch %end_label
217 %end_label = OpLabel
218 OpNop
219 OpLine %string 1 1
220 %result = OpPhi %bool %true %true_label %false %false_label
221 )";
222
223 CompileSuccessfully(GenerateShaderCode(body));
224 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
225 EXPECT_THAT(getDiagnosticString(),
226 HasSubstr("OpPhi must appear within a non-entry block before all "
227 "non-OpPhi instructions"));
228 }
229
TEST_F(ValidateAdjacency,OpPhiFollowedByOpLineGood)230 TEST_F(ValidateAdjacency, OpPhiFollowedByOpLineGood) {
231 const std::string body = R"(
232 OpSelectionMerge %end_label None
233 OpBranchConditional %true %true_label %false_label
234 %true_label = OpLabel
235 OpBranch %end_label
236 %false_label = OpLabel
237 OpBranch %end_label
238 %end_label = OpLabel
239 %result = OpPhi %bool %true %true_label %false %false_label
240 OpLine %string 1 1
241 OpNop
242 OpNop
243 OpLine %string 2 1
244 OpNop
245 OpLine %string 3 1
246 )";
247
248 CompileSuccessfully(GenerateShaderCode(body));
249 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
250 }
251
TEST_F(ValidateAdjacency,OpPhiMultipleOpLineAndOpPhiFail)252 TEST_F(ValidateAdjacency, OpPhiMultipleOpLineAndOpPhiFail) {
253 const std::string body = R"(
254 OpSelectionMerge %end_label None
255 OpBranchConditional %true %true_label %false_label
256 %true_label = OpLabel
257 OpBranch %end_label
258 %false_label = OpLabel
259 OpBranch %end_label
260 %end_label = OpLabel
261 OpLine %string 1 1
262 %value = OpPhi %int %zero %true_label %int_1 %false_label
263 OpNop
264 OpLine %string 2 1
265 OpNop
266 OpLine %string 3 1
267 %result = OpPhi %bool %true %true_label %false %false_label
268 )";
269
270 CompileSuccessfully(GenerateShaderCode(body));
271 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
272 EXPECT_THAT(getDiagnosticString(),
273 HasSubstr("OpPhi must appear within a non-entry block before all "
274 "non-OpPhi instructions"));
275 }
276
TEST_F(ValidateAdjacency,OpPhiMultipleOpLineAndOpPhiGood)277 TEST_F(ValidateAdjacency, OpPhiMultipleOpLineAndOpPhiGood) {
278 const std::string body = R"(
279 OpSelectionMerge %end_label None
280 OpBranchConditional %true %true_label %false_label
281 %true_label = OpLabel
282 OpBranch %end_label
283 %false_label = OpLabel
284 OpBranch %end_label
285 %end_label = OpLabel
286 OpLine %string 1 1
287 %value = OpPhi %int %zero %true_label %int_1 %false_label
288 OpLine %string 2 1
289 %result = OpPhi %bool %true %true_label %false %false_label
290 OpLine %string 3 1
291 OpNop
292 OpNop
293 OpLine %string 4 1
294 OpNop
295 )";
296
297 CompileSuccessfully(GenerateShaderCode(body));
298 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
299 }
300
TEST_F(ValidateAdjacency,OpPhiInEntryBlockBad)301 TEST_F(ValidateAdjacency, OpPhiInEntryBlockBad) {
302 const std::string body = R"(
303 OpLine %string 1 1
304 %value = OpPhi %int
305 OpLine %string 2 1
306 OpNop
307 OpLine %string 3 1
308 OpNop
309 )";
310
311 CompileSuccessfully(GenerateShaderCode(body));
312 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
313 EXPECT_THAT(getDiagnosticString(),
314 HasSubstr("OpPhi must appear within a non-entry block before all "
315 "non-OpPhi instructions"));
316 }
317
TEST_F(ValidateAdjacency,OpVariableInFunctionGood)318 TEST_F(ValidateAdjacency, OpVariableInFunctionGood) {
319 const std::string body = R"(
320 OpLine %string 1 1
321 %var = OpVariable %func_int Function
322 OpLine %string 2 1
323 OpNop
324 OpLine %string 3 1
325 OpNop
326 )";
327
328 CompileSuccessfully(GenerateShaderCode(body));
329 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
330 }
331
TEST_F(ValidateAdjacency,OpVariableInFunctionMultipleGood)332 TEST_F(ValidateAdjacency, OpVariableInFunctionMultipleGood) {
333 const std::string body = R"(
334 OpLine %string 1 1
335 %1 = OpVariable %func_int Function
336 OpLine %string 2 1
337 %2 = OpVariable %func_int Function
338 %3 = OpVariable %func_int Function
339 OpNop
340 OpLine %string 3 1
341 OpNop
342 )";
343
344 CompileSuccessfully(GenerateShaderCode(body));
345 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
346 }
347
TEST_F(ValidateAdjacency,OpVariableInFunctionBad)348 TEST_F(ValidateAdjacency, OpVariableInFunctionBad) {
349 const std::string body = R"(
350 %1 = OpUndef %int
351 %2 = OpVariable %func_int Function
352 )";
353
354 CompileSuccessfully(GenerateShaderCode(body));
355 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
356 EXPECT_THAT(getDiagnosticString(),
357 HasSubstr("All OpVariable instructions in a function must be the "
358 "first instructions"));
359 }
360
TEST_F(ValidateAdjacency,OpVariableInFunctionMultipleBad)361 TEST_F(ValidateAdjacency, OpVariableInFunctionMultipleBad) {
362 const std::string body = R"(
363 OpNop
364 %1 = OpVariable %func_int Function
365 OpLine %string 1 1
366 %2 = OpVariable %func_int Function
367 OpNop
368 OpNop
369 OpLine %string 2 1
370 %3 = OpVariable %func_int Function
371 )";
372
373 CompileSuccessfully(GenerateShaderCode(body));
374 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
375 EXPECT_THAT(getDiagnosticString(),
376 HasSubstr("All OpVariable instructions in a function must be the "
377 "first instructions"));
378 }
379
TEST_F(ValidateAdjacency,OpLoopMergePreceedsOpBranchSuccess)380 TEST_F(ValidateAdjacency, OpLoopMergePreceedsOpBranchSuccess) {
381 const std::string body = R"(
382 OpBranch %loop
383 %loop = OpLabel
384 OpLoopMerge %end %loop None
385 OpBranch %loop
386 %end = OpLabel
387 )";
388
389 CompileSuccessfully(GenerateShaderCode(body));
390 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
391 }
392
TEST_F(ValidateAdjacency,OpLoopMergePreceedsOpBranchConditionalSuccess)393 TEST_F(ValidateAdjacency, OpLoopMergePreceedsOpBranchConditionalSuccess) {
394 const std::string body = R"(
395 OpBranch %loop
396 %loop = OpLabel
397 OpLoopMerge %end %loop None
398 OpBranchConditional %true %loop %end
399 %end = OpLabel
400 )";
401
402 CompileSuccessfully(GenerateShaderCode(body));
403 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
404 }
405
TEST_F(ValidateAdjacency,OpLoopMergePreceedsBadOpFail)406 TEST_F(ValidateAdjacency, OpLoopMergePreceedsBadOpFail) {
407 const std::string body = R"(
408 OpBranch %loop
409 %loop = OpLabel
410 OpLoopMerge %end %loop None
411 OpNop
412 OpBranchConditional %true %loop %end
413 %end = OpLabel
414 )";
415
416 CompileSuccessfully(GenerateShaderCode(body));
417 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
418 EXPECT_THAT(getDiagnosticString(),
419 HasSubstr("OpLoopMerge must immediately precede either an "
420 "OpBranch or OpBranchConditional instruction."));
421 }
422
TEST_F(ValidateAdjacency,OpSelectionMergePreceedsOpBranchConditionalSuccess)423 TEST_F(ValidateAdjacency, OpSelectionMergePreceedsOpBranchConditionalSuccess) {
424 const std::string body = R"(
425 OpSelectionMerge %end_label None
426 OpBranchConditional %true %true_label %false_label
427 %true_label = OpLabel
428 OpBranch %end_label
429 %false_label = OpLabel
430 OpBranch %end_label
431 %end_label = OpLabel
432 )";
433
434 CompileSuccessfully(GenerateShaderCode(body));
435 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
436 }
437
TEST_F(ValidateAdjacency,OpSelectionMergePreceedsOpSwitchSuccess)438 TEST_F(ValidateAdjacency, OpSelectionMergePreceedsOpSwitchSuccess) {
439 const std::string body = R"(
440 OpSelectionMerge %merge None
441 OpSwitch %zero %merge 0 %label
442 %label = OpLabel
443 OpBranch %merge
444 %merge = OpLabel
445 )";
446
447 CompileSuccessfully(GenerateShaderCode(body));
448 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
449 }
450
TEST_F(ValidateAdjacency,OpSelectionMergePreceedsBadOpFail)451 TEST_F(ValidateAdjacency, OpSelectionMergePreceedsBadOpFail) {
452 const std::string body = R"(
453 OpSelectionMerge %merge None
454 OpNop
455 OpSwitch %zero %merge 0 %label
456 %label = OpLabel
457 OpBranch %merge
458 %merge = OpLabel
459 )";
460
461 CompileSuccessfully(GenerateShaderCode(body));
462 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
463 EXPECT_THAT(getDiagnosticString(),
464 HasSubstr("OpSelectionMerge must immediately precede either an "
465 "OpBranchConditional or OpSwitch instruction"));
466 }
467
468 } // namespace
469 } // namespace val
470 } // namespace spvtools
471