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 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 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,NonSemanticBeforeOpPhiBad)318 TEST_F(ValidateAdjacency, NonSemanticBeforeOpPhiBad) {
319 const std::string body = R"(
320 OpSelectionMerge %end_label None
321 OpBranchConditional %true %true_label %false_label
322 %true_label = OpLabel
323 OpBranch %end_label
324 %false_label = OpLabel
325 OpBranch %end_label
326 %end_label = OpLabel
327 %placeholder = OpExtInst %void %extinst 123 %int_1
328 %result = OpPhi %bool %true %true_label %false %false_label
329 )";
330
331 const std::string extra = R"(OpCapability Shader
332 OpExtension "SPV_KHR_non_semantic_info"
333 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
334 )";
335
336 CompileSuccessfully(GenerateShaderCode(body, extra));
337 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
338 EXPECT_THAT(getDiagnosticString(),
339 HasSubstr("OpPhi must appear within a non-entry block before all "
340 "non-OpPhi instructions"));
341 }
342
TEST_F(ValidateAdjacency,NonSemanticBetweenOpPhiBad)343 TEST_F(ValidateAdjacency, NonSemanticBetweenOpPhiBad) {
344 const std::string body = R"(
345 OpSelectionMerge %end_label None
346 OpBranchConditional %true %true_label %false_label
347 %true_label = OpLabel
348 OpBranch %end_label
349 %false_label = OpLabel
350 OpBranch %end_label
351 %end_label = OpLabel
352 %result1 = OpPhi %bool %true %true_label %false %false_label
353 %placeholder = OpExtInst %void %extinst 123 %int_1
354 %result2 = OpPhi %bool %true %true_label %false %false_label
355 )";
356
357 const std::string extra = R"(OpCapability Shader
358 OpExtension "SPV_KHR_non_semantic_info"
359 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
360 )";
361
362 CompileSuccessfully(GenerateShaderCode(body, extra));
363 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
364 EXPECT_THAT(getDiagnosticString(),
365 HasSubstr("OpPhi must appear within a non-entry block before all "
366 "non-OpPhi instructions"));
367 }
368
TEST_F(ValidateAdjacency,NonSemanticAfterOpPhiGood)369 TEST_F(ValidateAdjacency, NonSemanticAfterOpPhiGood) {
370 const std::string body = R"(
371 OpSelectionMerge %end_label None
372 OpBranchConditional %true %true_label %false_label
373 %true_label = OpLabel
374 OpBranch %end_label
375 %false_label = OpLabel
376 OpBranch %end_label
377 %end_label = OpLabel
378 OpLine %string 0 0
379 %result = OpPhi %bool %true %true_label %false %false_label
380 %placeholder = OpExtInst %void %extinst 123 %int_1
381 )";
382
383 const std::string extra = R"(OpCapability Shader
384 OpExtension "SPV_KHR_non_semantic_info"
385 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
386 )";
387
388 CompileSuccessfully(GenerateShaderCode(body, extra));
389 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
390 }
391
TEST_F(ValidateAdjacency,NonSemanticBeforeOpFunctionParameterBad)392 TEST_F(ValidateAdjacency, NonSemanticBeforeOpFunctionParameterBad) {
393 const std::string body = R"(
394 OpCapability Shader
395 OpExtension "SPV_KHR_non_semantic_info"
396 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
397 OpMemoryModel Logical GLSL450
398 OpEntryPoint Fragment %main "main"
399 OpExecutionMode %main OriginUpperLeft
400
401 %string = OpString ""
402 %void = OpTypeVoid
403 %bool = OpTypeBool
404 %int = OpTypeInt 32 0
405 %true = OpConstantTrue %bool
406 %false = OpConstantFalse %bool
407 %zero = OpConstant %int 0
408 %int_1 = OpConstant %int 1
409 %func = OpTypeFunction %void
410 %func_int = OpTypePointer Function %int
411 %paramfunc_type = OpTypeFunction %void %int %int
412
413 %paramfunc = OpFunction %void None %paramfunc_type
414 %placeholder = OpExtInst %void %extinst 123 %int_1
415 %a = OpFunctionParameter %int
416 %b = OpFunctionParameter %int
417 %paramfunc_entry = OpLabel
418 OpReturn
419 OpFunctionEnd
420
421 %main = OpFunction %void None %func
422 %main_entry = OpLabel
423 OpReturn
424 OpFunctionEnd
425 )";
426
427 CompileSuccessfully(body);
428 EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
429 EXPECT_THAT(getDiagnosticString(),
430 HasSubstr("Non-semantic OpExtInst within function definition "
431 "must appear in a block"));
432 }
433
TEST_F(ValidateAdjacency,NonSemanticBetweenOpFunctionParameterBad)434 TEST_F(ValidateAdjacency, NonSemanticBetweenOpFunctionParameterBad) {
435 const std::string body = R"(
436 OpCapability Shader
437 OpExtension "SPV_KHR_non_semantic_info"
438 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
439 OpMemoryModel Logical GLSL450
440 OpEntryPoint Fragment %main "main"
441 OpExecutionMode %main OriginUpperLeft
442
443 %string = OpString ""
444 %void = OpTypeVoid
445 %bool = OpTypeBool
446 %int = OpTypeInt 32 0
447 %true = OpConstantTrue %bool
448 %false = OpConstantFalse %bool
449 %zero = OpConstant %int 0
450 %int_1 = OpConstant %int 1
451 %func = OpTypeFunction %void
452 %func_int = OpTypePointer Function %int
453 %paramfunc_type = OpTypeFunction %void %int %int
454
455 %paramfunc = OpFunction %void None %paramfunc_type
456 %a = OpFunctionParameter %int
457 %placeholder = OpExtInst %void %extinst 123 %int_1
458 %b = OpFunctionParameter %int
459 %paramfunc_entry = OpLabel
460 OpReturn
461 OpFunctionEnd
462
463 %main = OpFunction %void None %func
464 %main_entry = OpLabel
465 OpReturn
466 OpFunctionEnd
467 )";
468
469 CompileSuccessfully(body);
470 EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
471 EXPECT_THAT(getDiagnosticString(),
472 HasSubstr("Non-semantic OpExtInst within function definition "
473 "must appear in a block"));
474 }
475
TEST_F(ValidateAdjacency,NonSemanticAfterOpFunctionParameterGood)476 TEST_F(ValidateAdjacency, NonSemanticAfterOpFunctionParameterGood) {
477 const std::string body = R"(
478 OpCapability Shader
479 OpExtension "SPV_KHR_non_semantic_info"
480 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
481 OpMemoryModel Logical GLSL450
482 OpEntryPoint Fragment %main "main"
483 OpExecutionMode %main OriginUpperLeft
484
485 %string = OpString ""
486 %void = OpTypeVoid
487 %bool = OpTypeBool
488 %int = OpTypeInt 32 0
489 %true = OpConstantTrue %bool
490 %false = OpConstantFalse %bool
491 %zero = OpConstant %int 0
492 %int_1 = OpConstant %int 1
493 %func = OpTypeFunction %void
494 %func_int = OpTypePointer Function %int
495 %paramfunc_type = OpTypeFunction %void %int %int
496
497 %paramfunc = OpFunction %void None %paramfunc_type
498 %a = OpFunctionParameter %int
499 %b = OpFunctionParameter %int
500 %paramfunc_entry = OpLabel
501 %placeholder = OpExtInst %void %extinst 123 %int_1
502 OpReturn
503 OpFunctionEnd
504
505 %main = OpFunction %void None %func
506 %main_entry = OpLabel
507 OpReturn
508 OpFunctionEnd
509 )";
510
511 CompileSuccessfully(body);
512 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
513 }
514
TEST_F(ValidateAdjacency,NonSemanticBetweenFunctionsGood)515 TEST_F(ValidateAdjacency, NonSemanticBetweenFunctionsGood) {
516 const std::string body = R"(
517 OpCapability Shader
518 OpExtension "SPV_KHR_non_semantic_info"
519 %extinst = OpExtInstImport "NonSemantic.Testing.Set"
520 OpMemoryModel Logical GLSL450
521 OpEntryPoint Fragment %main "main"
522 OpExecutionMode %main OriginUpperLeft
523
524 %string = OpString ""
525 %void = OpTypeVoid
526 %bool = OpTypeBool
527 %int = OpTypeInt 32 0
528 %true = OpConstantTrue %bool
529 %false = OpConstantFalse %bool
530 %zero = OpConstant %int 0
531 %int_1 = OpConstant %int 1
532 %func = OpTypeFunction %void
533 %func_int = OpTypePointer Function %int
534 %paramfunc_type = OpTypeFunction %void %int %int
535
536 %paramfunc = OpFunction %void None %paramfunc_type
537 %a = OpFunctionParameter %int
538 %b = OpFunctionParameter %int
539 %paramfunc_entry = OpLabel
540 OpReturn
541 OpFunctionEnd
542
543 %placeholder = OpExtInst %void %extinst 123 %int_1
544
545 %main = OpFunction %void None %func
546 %main_entry = OpLabel
547 OpReturn
548 OpFunctionEnd
549 )";
550
551 CompileSuccessfully(body);
552 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
553 }
554
TEST_F(ValidateAdjacency,OpVariableInFunctionGood)555 TEST_F(ValidateAdjacency, OpVariableInFunctionGood) {
556 const std::string body = R"(
557 OpLine %string 1 1
558 %var = OpVariable %func_int Function
559 OpLine %string 2 1
560 OpNop
561 OpLine %string 3 1
562 OpNop
563 )";
564
565 CompileSuccessfully(GenerateShaderCode(body));
566 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
567 }
568
TEST_F(ValidateAdjacency,OpVariableInFunctionMultipleGood)569 TEST_F(ValidateAdjacency, OpVariableInFunctionMultipleGood) {
570 const std::string body = R"(
571 OpLine %string 1 1
572 %1 = OpVariable %func_int Function
573 OpLine %string 2 1
574 %2 = OpVariable %func_int Function
575 %3 = OpVariable %func_int Function
576 OpNop
577 OpLine %string 3 1
578 OpNop
579 )";
580
581 CompileSuccessfully(GenerateShaderCode(body));
582 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
583 }
584
TEST_F(ValidateAdjacency,OpVariableInFunctionBad)585 TEST_F(ValidateAdjacency, OpVariableInFunctionBad) {
586 const std::string body = R"(
587 %1 = OpUndef %int
588 %2 = OpVariable %func_int Function
589 )";
590
591 CompileSuccessfully(GenerateShaderCode(body));
592 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
593 EXPECT_THAT(getDiagnosticString(),
594 HasSubstr("All OpVariable instructions in a function must be the "
595 "first instructions"));
596 }
597
TEST_F(ValidateAdjacency,OpVariableInFunctionMultipleBad)598 TEST_F(ValidateAdjacency, OpVariableInFunctionMultipleBad) {
599 const std::string body = R"(
600 OpNop
601 %1 = OpVariable %func_int Function
602 OpLine %string 1 1
603 %2 = OpVariable %func_int Function
604 OpNop
605 OpNop
606 OpLine %string 2 1
607 %3 = OpVariable %func_int Function
608 )";
609
610 CompileSuccessfully(GenerateShaderCode(body));
611 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
612 EXPECT_THAT(getDiagnosticString(),
613 HasSubstr("All OpVariable instructions in a function must be the "
614 "first instructions"));
615 }
616
TEST_F(ValidateAdjacency,OpLoopMergePreceedsOpBranchSuccess)617 TEST_F(ValidateAdjacency, OpLoopMergePreceedsOpBranchSuccess) {
618 const std::string body = R"(
619 OpBranch %loop
620 %loop = OpLabel
621 OpLoopMerge %end %loop None
622 OpBranch %loop
623 %end = OpLabel
624 )";
625
626 CompileSuccessfully(GenerateShaderCode(body));
627 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
628 }
629
TEST_F(ValidateAdjacency,OpLoopMergePreceedsOpBranchConditionalSuccess)630 TEST_F(ValidateAdjacency, OpLoopMergePreceedsOpBranchConditionalSuccess) {
631 const std::string body = R"(
632 OpBranch %loop
633 %loop = OpLabel
634 OpLoopMerge %end %loop None
635 OpBranchConditional %true %loop %end
636 %end = OpLabel
637 )";
638
639 CompileSuccessfully(GenerateShaderCode(body));
640 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
641 }
642
TEST_F(ValidateAdjacency,OpLoopMergePreceedsBadOpFail)643 TEST_F(ValidateAdjacency, OpLoopMergePreceedsBadOpFail) {
644 const std::string body = R"(
645 OpBranch %loop
646 %loop = OpLabel
647 OpLoopMerge %end %loop None
648 OpNop
649 OpBranchConditional %true %loop %end
650 %end = OpLabel
651 )";
652
653 CompileSuccessfully(GenerateShaderCode(body));
654 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
655 EXPECT_THAT(getDiagnosticString(),
656 HasSubstr("OpLoopMerge must immediately precede either an "
657 "OpBranch or OpBranchConditional instruction."));
658 }
659
TEST_F(ValidateAdjacency,OpSelectionMergePreceedsOpBranchConditionalSuccess)660 TEST_F(ValidateAdjacency, OpSelectionMergePreceedsOpBranchConditionalSuccess) {
661 const std::string body = R"(
662 OpSelectionMerge %end_label None
663 OpBranchConditional %true %true_label %false_label
664 %true_label = OpLabel
665 OpBranch %end_label
666 %false_label = OpLabel
667 OpBranch %end_label
668 %end_label = OpLabel
669 )";
670
671 CompileSuccessfully(GenerateShaderCode(body));
672 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
673 }
674
TEST_F(ValidateAdjacency,OpSelectionMergePreceedsOpSwitchSuccess)675 TEST_F(ValidateAdjacency, OpSelectionMergePreceedsOpSwitchSuccess) {
676 const std::string body = R"(
677 OpSelectionMerge %merge None
678 OpSwitch %zero %merge 0 %label
679 %label = OpLabel
680 OpBranch %merge
681 %merge = OpLabel
682 )";
683
684 CompileSuccessfully(GenerateShaderCode(body));
685 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
686 }
687
TEST_F(ValidateAdjacency,OpSelectionMergePreceedsBadOpFail)688 TEST_F(ValidateAdjacency, OpSelectionMergePreceedsBadOpFail) {
689 const std::string body = R"(
690 OpSelectionMerge %merge None
691 OpNop
692 OpSwitch %zero %merge 0 %label
693 %label = OpLabel
694 OpBranch %merge
695 %merge = OpLabel
696 )";
697
698 CompileSuccessfully(GenerateShaderCode(body));
699 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
700 EXPECT_THAT(getDiagnosticString(),
701 HasSubstr("OpSelectionMerge must immediately precede either an "
702 "OpBranchConditional or OpSwitch instruction"));
703 }
704
705 } // namespace
706 } // namespace val
707 } // namespace spvtools
708