1 // Copyright (c) 2015-2016 The Khronos Group 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 // Validation tests for Control Flow Graph
16
17 #include <array>
18 #include <functional>
19 #include <iterator>
20 #include <sstream>
21 #include <string>
22 #include <utility>
23 #include <vector>
24
25 #include "gmock/gmock.h"
26 #include "source/diagnostic.h"
27 #include "source/spirv_target_env.h"
28 #include "source/val/validate.h"
29 #include "test/test_fixture.h"
30 #include "test/unit_spirv.h"
31 #include "test/val/val_fixtures.h"
32
33 namespace spvtools {
34 namespace val {
35 namespace {
36
37 using ::testing::HasSubstr;
38 using ::testing::MatchesRegex;
39
40 using ValidateCFG = spvtest::ValidateBase<SpvCapability>;
41 using spvtest::ScopedContext;
42
nameOps()43 std::string nameOps() { return ""; }
44
45 template <typename... Args>
nameOps(std::pair<std::string,std::string> head,Args...names)46 std::string nameOps(std::pair<std::string, std::string> head, Args... names) {
47 return "OpName %" + head.first + " \"" + head.second + "\"\n" +
48 nameOps(names...);
49 }
50
51 template <typename... Args>
nameOps(std::string head,Args...names)52 std::string nameOps(std::string head, Args... names) {
53 return "OpName %" + head + " \"" + head + "\"\n" + nameOps(names...);
54 }
55
56 /// This class allows the easy creation of complex control flow without writing
57 /// SPIR-V. This class is used in the test cases below.
58 class Block {
59 std::string label_;
60 std::string body_;
61 SpvOp type_;
62 std::vector<Block> successors_;
63
64 public:
65 /// Creates a Block with a given label
66 ///
67 /// @param[in]: label the label id of the block
68 /// @param[in]: type the branch instruciton that ends the block
Block(std::string label,SpvOp type=SpvOpBranch)69 explicit Block(std::string label, SpvOp type = SpvOpBranch)
70 : label_(label), body_(), type_(type), successors_() {}
71
72 /// Sets the instructions which will appear in the body of the block
SetBody(std::string body)73 Block& SetBody(std::string body) {
74 body_ = body;
75 return *this;
76 }
77
AppendBody(std::string body)78 Block& AppendBody(std::string body) {
79 body_ += body;
80 return *this;
81 }
82
83 /// Converts the block into a SPIR-V string
operator std::string()84 operator std::string() {
85 std::stringstream out;
86 out << std::setw(8) << "%" + label_ + " = OpLabel \n";
87 if (!body_.empty()) {
88 out << body_;
89 }
90
91 switch (type_) {
92 case SpvOpBranchConditional:
93 out << "OpBranchConditional %cond ";
94 for (Block& b : successors_) {
95 out << "%" + b.label_ + " ";
96 }
97 break;
98 case SpvOpSwitch: {
99 out << "OpSwitch %one %" + successors_.front().label_;
100 std::stringstream ss;
101 for (size_t i = 1; i < successors_.size(); i++) {
102 ss << " " << i << " %" << successors_[i].label_;
103 }
104 out << ss.str();
105 } break;
106 case SpvOpLoopMerge: {
107 assert(successors_.size() == 2);
108 out << "OpLoopMerge %" + successors_[0].label_ + " %" +
109 successors_[0].label_ + "None";
110 } break;
111
112 case SpvOpReturn:
113 assert(successors_.size() == 0);
114 out << "OpReturn\n";
115 break;
116 case SpvOpUnreachable:
117 assert(successors_.size() == 0);
118 out << "OpUnreachable\n";
119 break;
120 case SpvOpBranch:
121 assert(successors_.size() == 1);
122 out << "OpBranch %" + successors_.front().label_;
123 break;
124 case SpvOpKill:
125 assert(successors_.size() == 0);
126 out << "OpKill\n";
127 break;
128 default:
129 assert(1 == 0 && "Unhandled");
130 }
131 out << "\n";
132
133 return out.str();
134 }
135 friend Block& operator>>(Block& curr, std::vector<Block> successors);
136 friend Block& operator>>(Block& lhs, Block& successor);
137 };
138
139 /// Assigns the successors for the Block on the lhs
operator >>(Block & lhs,std::vector<Block> successors)140 Block& operator>>(Block& lhs, std::vector<Block> successors) {
141 if (lhs.type_ == SpvOpBranchConditional) {
142 assert(successors.size() == 2);
143 } else if (lhs.type_ == SpvOpSwitch) {
144 assert(successors.size() > 1);
145 }
146 lhs.successors_ = successors;
147 return lhs;
148 }
149
150 /// Assigns the successor for the Block on the lhs
operator >>(Block & lhs,Block & successor)151 Block& operator>>(Block& lhs, Block& successor) {
152 assert(lhs.type_ == SpvOpBranch);
153 lhs.successors_.push_back(successor);
154 return lhs;
155 }
156
GetDefaultHeader(SpvCapability cap)157 const std::string& GetDefaultHeader(SpvCapability cap) {
158 static const std::string shader_header =
159 "OpCapability Shader\n"
160 "OpCapability Linkage\n"
161 "OpMemoryModel Logical GLSL450\n";
162
163 static const std::string kernel_header =
164 "OpCapability Kernel\n"
165 "OpCapability Linkage\n"
166 "OpMemoryModel Logical OpenCL\n";
167
168 return (cap == SpvCapabilityShader) ? shader_header : kernel_header;
169 }
170
types_consts()171 const std::string& types_consts() {
172 static const std::string types =
173 "%voidt = OpTypeVoid\n"
174 "%boolt = OpTypeBool\n"
175 "%intt = OpTypeInt 32 0\n"
176 "%one = OpConstant %intt 1\n"
177 "%two = OpConstant %intt 2\n"
178 "%ptrt = OpTypePointer Function %intt\n"
179 "%funct = OpTypeFunction %voidt\n";
180 return types;
181 }
182
183 INSTANTIATE_TEST_SUITE_P(StructuredControlFlow, ValidateCFG,
184 ::testing::Values(SpvCapabilityShader,
185 SpvCapabilityKernel));
186
TEST_P(ValidateCFG,LoopReachableFromEntryButNeverLeadingToReturn)187 TEST_P(ValidateCFG, LoopReachableFromEntryButNeverLeadingToReturn) {
188 // In this case, the loop is reachable from a node without a predecessor,
189 // but never reaches a node with a return.
190 //
191 // This motivates the need for the pseudo-exit node to have a node
192 // from a cycle in its predecessors list. Otherwise the validator's
193 // post-dominance calculation will go into an infinite loop.
194 //
195 // For more motivation, see
196 // https://github.com/KhronosGroup/SPIRV-Tools/issues/279
197 std::string str = R"(
198 OpCapability Shader
199 OpCapability Linkage
200 OpMemoryModel Logical GLSL450
201
202 OpName %entry "entry"
203 OpName %loop "loop"
204 OpName %exit "exit"
205
206 %voidt = OpTypeVoid
207 %funct = OpTypeFunction %voidt
208
209 %main = OpFunction %voidt None %funct
210 %entry = OpLabel
211 OpBranch %loop
212 %loop = OpLabel
213 OpLoopMerge %exit %loop None
214 OpBranch %loop
215 %exit = OpLabel
216 OpReturn
217 OpFunctionEnd
218 )";
219 CompileSuccessfully(str);
220 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << str;
221 }
222
TEST_P(ValidateCFG,LoopUnreachableFromEntryButLeadingToReturn)223 TEST_P(ValidateCFG, LoopUnreachableFromEntryButLeadingToReturn) {
224 // In this case, the loop is not reachable from a node without a
225 // predecessor, but eventually reaches a node with a return.
226 //
227 // This motivates the need for the pseudo-entry node to have a node
228 // from a cycle in its successors list. Otherwise the validator's
229 // dominance calculation will go into an infinite loop.
230 //
231 // For more motivation, see
232 // https://github.com/KhronosGroup/SPIRV-Tools/issues/279
233 // Before that fix, we'd have an infinite loop when calculating
234 // post-dominators.
235 std::string str = R"(
236 OpCapability Shader
237 OpCapability Linkage
238 OpMemoryModel Logical GLSL450
239
240 OpName %entry "entry"
241 OpName %loop "loop"
242 OpName %cont "cont"
243 OpName %exit "exit"
244
245 %voidt = OpTypeVoid
246 %funct = OpTypeFunction %voidt
247 %boolt = OpTypeBool
248 %false = OpConstantFalse %boolt
249
250 %main = OpFunction %voidt None %funct
251 %entry = OpLabel
252 OpReturn
253
254 %loop = OpLabel
255 OpLoopMerge %exit %cont None
256 OpBranch %cont
257
258 %cont = OpLabel
259 OpBranchConditional %false %loop %exit
260
261 %exit = OpLabel
262 OpReturn
263 OpFunctionEnd
264 )";
265 CompileSuccessfully(str);
266 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions())
267 << str << getDiagnosticString();
268 }
269
TEST_P(ValidateCFG,Simple)270 TEST_P(ValidateCFG, Simple) {
271 bool is_shader = GetParam() == SpvCapabilityShader;
272 Block entry("entry");
273 Block loop("loop", SpvOpBranchConditional);
274 Block cont("cont");
275 Block merge("merge", SpvOpReturn);
276
277 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
278 if (is_shader) {
279 loop.SetBody("OpLoopMerge %merge %cont None\n");
280 }
281
282 std::string str = GetDefaultHeader(GetParam()) +
283 nameOps("loop", "entry", "cont", "merge",
284 std::make_pair("func", "Main")) +
285 types_consts() +
286 "%func = OpFunction %voidt None %funct\n";
287
288 str += entry >> loop;
289 str += loop >> std::vector<Block>({cont, merge});
290 str += cont >> loop;
291 str += merge;
292 str += "OpFunctionEnd\n";
293
294 CompileSuccessfully(str);
295 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
296 }
297
TEST_P(ValidateCFG,Variable)298 TEST_P(ValidateCFG, Variable) {
299 Block entry("entry");
300 Block cont("cont");
301 Block exit("exit", SpvOpReturn);
302
303 entry.SetBody("%var = OpVariable %ptrt Function\n");
304
305 std::string str = GetDefaultHeader(GetParam()) +
306 nameOps(std::make_pair("func", "Main")) + types_consts() +
307 " %func = OpFunction %voidt None %funct\n";
308 str += entry >> cont;
309 str += cont >> exit;
310 str += exit;
311 str += "OpFunctionEnd\n";
312
313 CompileSuccessfully(str);
314 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
315 }
316
TEST_P(ValidateCFG,VariableNotInFirstBlockBad)317 TEST_P(ValidateCFG, VariableNotInFirstBlockBad) {
318 Block entry("entry");
319 Block cont("cont");
320 Block exit("exit", SpvOpReturn);
321
322 // This operation should only be performed in the entry block
323 cont.SetBody("%var = OpVariable %ptrt Function\n");
324
325 std::string str = GetDefaultHeader(GetParam()) +
326 nameOps(std::make_pair("func", "Main")) + types_consts() +
327 " %func = OpFunction %voidt None %funct\n";
328
329 str += entry >> cont;
330 str += cont >> exit;
331 str += exit;
332 str += "OpFunctionEnd\n";
333
334 CompileSuccessfully(str);
335 ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
336 EXPECT_THAT(getDiagnosticString(),
337 HasSubstr("All OpVariable instructions in a function must be the "
338 "first instructions in the first block"));
339 }
340
TEST_P(ValidateCFG,BlockSelfLoopIsOk)341 TEST_P(ValidateCFG, BlockSelfLoopIsOk) {
342 bool is_shader = GetParam() == SpvCapabilityShader;
343 Block entry("entry");
344 Block loop("loop", SpvOpBranchConditional);
345 Block merge("merge", SpvOpReturn);
346
347 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
348 if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n");
349
350 std::string str = GetDefaultHeader(GetParam()) +
351 nameOps("loop", "merge", std::make_pair("func", "Main")) +
352 types_consts() +
353 "%func = OpFunction %voidt None %funct\n";
354
355 str += entry >> loop;
356 // loop branches to itself, but does not trigger an error.
357 str += loop >> std::vector<Block>({merge, loop});
358 str += merge;
359 str += "OpFunctionEnd\n";
360
361 CompileSuccessfully(str);
362 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
363 }
364
TEST_P(ValidateCFG,BlockAppearsBeforeDominatorBad)365 TEST_P(ValidateCFG, BlockAppearsBeforeDominatorBad) {
366 bool is_shader = GetParam() == SpvCapabilityShader;
367 Block entry("entry");
368 Block cont("cont");
369 Block branch("branch", SpvOpBranchConditional);
370 Block merge("merge", SpvOpReturn);
371
372 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
373 if (is_shader) branch.SetBody("OpSelectionMerge %merge None\n");
374
375 std::string str = GetDefaultHeader(GetParam()) +
376 nameOps("cont", "branch", std::make_pair("func", "Main")) +
377 types_consts() +
378 "%func = OpFunction %voidt None %funct\n";
379
380 str += entry >> branch;
381 str += cont >> merge; // cont appears before its dominator
382 str += branch >> std::vector<Block>({cont, merge});
383 str += merge;
384 str += "OpFunctionEnd\n";
385
386 CompileSuccessfully(str);
387 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
388 EXPECT_THAT(getDiagnosticString(),
389 MatchesRegex("Block .\\[%cont\\] appears in the binary "
390 "before its dominator .\\[%branch\\]\n"
391 " %branch = OpLabel\n"));
392 }
393
TEST_P(ValidateCFG,MergeBlockTargetedByMultipleHeaderBlocksBad)394 TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksBad) {
395 bool is_shader = GetParam() == SpvCapabilityShader;
396 Block entry("entry");
397 Block loop("loop");
398 Block selection("selection", SpvOpBranchConditional);
399 Block merge("merge", SpvOpReturn);
400
401 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
402 if (is_shader) loop.SetBody(" OpLoopMerge %merge %loop None\n");
403
404 // cannot share the same merge
405 if (is_shader) selection.SetBody("OpSelectionMerge %merge None\n");
406
407 std::string str = GetDefaultHeader(GetParam()) +
408 nameOps("merge", std::make_pair("func", "Main")) +
409 types_consts() +
410 "%func = OpFunction %voidt None %funct\n";
411
412 str += entry >> loop;
413 str += loop >> selection;
414 str += selection >> std::vector<Block>({loop, merge});
415 str += merge;
416 str += "OpFunctionEnd\n";
417
418 CompileSuccessfully(str);
419 if (is_shader) {
420 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
421 EXPECT_THAT(getDiagnosticString(),
422 MatchesRegex("Block .\\[%merge\\] is already a merge block "
423 "for another header\n"
424 " %Main = OpFunction %void None %9\n"));
425 } else {
426 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
427 }
428 }
429
TEST_P(ValidateCFG,MergeBlockTargetedByMultipleHeaderBlocksSelectionBad)430 TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksSelectionBad) {
431 bool is_shader = GetParam() == SpvCapabilityShader;
432 Block entry("entry");
433 Block loop("loop", SpvOpBranchConditional);
434 Block selection("selection", SpvOpBranchConditional);
435 Block merge("merge", SpvOpReturn);
436
437 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
438 if (is_shader) selection.SetBody(" OpSelectionMerge %merge None\n");
439
440 // cannot share the same merge
441 if (is_shader) loop.SetBody(" OpLoopMerge %merge %loop None\n");
442
443 std::string str = GetDefaultHeader(GetParam()) +
444 nameOps("merge", std::make_pair("func", "Main")) +
445 types_consts() +
446 "%func = OpFunction %voidt None %funct\n";
447
448 str += entry >> selection;
449 str += selection >> std::vector<Block>({merge, loop});
450 str += loop >> std::vector<Block>({loop, merge});
451 str += merge;
452 str += "OpFunctionEnd\n";
453
454 CompileSuccessfully(str);
455 if (is_shader) {
456 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
457 EXPECT_THAT(getDiagnosticString(),
458 MatchesRegex("Block .\\[%merge\\] is already a merge block "
459 "for another header\n"
460 " %Main = OpFunction %void None %9\n"));
461 } else {
462 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
463 }
464 }
465
TEST_P(ValidateCFG,BranchTargetFirstBlockBadSinceEntryBlock)466 TEST_P(ValidateCFG, BranchTargetFirstBlockBadSinceEntryBlock) {
467 Block entry("entry");
468 Block bad("bad");
469 Block end("end", SpvOpReturn);
470 std::string str = GetDefaultHeader(GetParam()) +
471 nameOps("entry", "bad", std::make_pair("func", "Main")) +
472 types_consts() +
473 "%func = OpFunction %voidt None %funct\n";
474
475 str += entry >> bad;
476 str += bad >> entry; // Cannot target entry block
477 str += end;
478 str += "OpFunctionEnd\n";
479
480 CompileSuccessfully(str);
481 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
482 EXPECT_THAT(getDiagnosticString(),
483 MatchesRegex("First block .\\[%entry\\] of function "
484 ".\\[%Main\\] is targeted by block .\\[%bad\\]\n"
485 " %Main = OpFunction %void None %10\n"));
486 }
487
TEST_P(ValidateCFG,BranchTargetFirstBlockBadSinceValue)488 TEST_P(ValidateCFG, BranchTargetFirstBlockBadSinceValue) {
489 Block entry("entry");
490 entry.SetBody("%undef = OpUndef %boolt\n");
491 Block bad("bad");
492 Block end("end", SpvOpReturn);
493 Block badvalue("undef"); // This references the OpUndef.
494 std::string str = GetDefaultHeader(GetParam()) +
495 nameOps("entry", "bad", std::make_pair("func", "Main")) +
496 types_consts() +
497 "%func = OpFunction %voidt None %funct\n";
498
499 str += entry >> bad;
500 str +=
501 bad >> badvalue; // Check branch to a function value (it's not a block!)
502 str += end;
503 str += "OpFunctionEnd\n";
504
505 CompileSuccessfully(str);
506 ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
507 EXPECT_THAT(getDiagnosticString(),
508 HasSubstr("'Target Label' operands for OpBranch must "
509 "be the ID of an OpLabel instruction"));
510 }
511
TEST_P(ValidateCFG,BranchConditionalTrueTargetFirstBlockBad)512 TEST_P(ValidateCFG, BranchConditionalTrueTargetFirstBlockBad) {
513 Block entry("entry");
514 Block bad("bad", SpvOpBranchConditional);
515 Block exit("exit", SpvOpReturn);
516
517 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
518 bad.SetBody(" OpLoopMerge %entry %exit None\n");
519
520 std::string str = GetDefaultHeader(GetParam()) +
521 nameOps("entry", "bad", std::make_pair("func", "Main")) +
522 types_consts() +
523 "%func = OpFunction %voidt None %funct\n";
524
525 str += entry >> bad;
526 str += bad >> std::vector<Block>({entry, exit}); // cannot target entry block
527 str += exit;
528 str += "OpFunctionEnd\n";
529
530 CompileSuccessfully(str);
531 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
532 EXPECT_THAT(getDiagnosticString(),
533 MatchesRegex("First block .\\[%entry\\] of function .\\[%Main\\] "
534 "is targeted by block .\\[%bad\\]\n"
535 " %Main = OpFunction %void None %10\n"));
536 }
537
TEST_P(ValidateCFG,BranchConditionalFalseTargetFirstBlockBad)538 TEST_P(ValidateCFG, BranchConditionalFalseTargetFirstBlockBad) {
539 Block entry("entry");
540 Block bad("bad", SpvOpBranchConditional);
541 Block t("t");
542 Block merge("merge");
543 Block end("end", SpvOpReturn);
544
545 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
546 bad.SetBody("OpLoopMerge %merge %cont None\n");
547
548 std::string str = GetDefaultHeader(GetParam()) +
549 nameOps("entry", "bad", std::make_pair("func", "Main")) +
550 types_consts() +
551 "%func = OpFunction %voidt None %funct\n";
552
553 str += entry >> bad;
554 str += bad >> std::vector<Block>({t, entry});
555 str += merge >> end;
556 str += end;
557 str += "OpFunctionEnd\n";
558
559 CompileSuccessfully(str);
560 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
561 EXPECT_THAT(getDiagnosticString(),
562 MatchesRegex("First block .\\[%entry\\] of function .\\[%Main\\] "
563 "is targeted by block .\\[%bad\\]\n"
564 " %Main = OpFunction %void None %10\n"));
565 }
566
TEST_P(ValidateCFG,SwitchTargetFirstBlockBad)567 TEST_P(ValidateCFG, SwitchTargetFirstBlockBad) {
568 Block entry("entry");
569 Block bad("bad", SpvOpSwitch);
570 Block block1("block1");
571 Block block2("block2");
572 Block block3("block3");
573 Block def("def"); // default block
574 Block merge("merge");
575 Block end("end", SpvOpReturn);
576
577 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
578 bad.SetBody("OpSelectionMerge %merge None\n");
579
580 std::string str = GetDefaultHeader(GetParam()) +
581 nameOps("entry", "bad", std::make_pair("func", "Main")) +
582 types_consts() +
583 "%func = OpFunction %voidt None %funct\n";
584
585 str += entry >> bad;
586 str += bad >> std::vector<Block>({def, block1, block2, block3, entry});
587 str += def >> merge;
588 str += block1 >> merge;
589 str += block2 >> merge;
590 str += block3 >> merge;
591 str += merge >> end;
592 str += end;
593 str += "OpFunctionEnd\n";
594
595 CompileSuccessfully(str);
596 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
597 EXPECT_THAT(getDiagnosticString(),
598 MatchesRegex("First block .\\[%entry\\] of function .\\[%Main\\] "
599 "is targeted by block .\\[%bad\\]\n"
600 " %Main = OpFunction %void None %10\n"));
601 }
602
TEST_P(ValidateCFG,BranchToBlockInOtherFunctionBad)603 TEST_P(ValidateCFG, BranchToBlockInOtherFunctionBad) {
604 Block entry("entry");
605 Block middle("middle", SpvOpBranchConditional);
606 Block end("end", SpvOpReturn);
607
608 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
609 middle.SetBody("OpSelectionMerge %end None\n");
610
611 Block entry2("entry2");
612 Block middle2("middle2");
613 Block end2("end2", SpvOpReturn);
614
615 std::string str = GetDefaultHeader(GetParam()) +
616 nameOps("middle2", std::make_pair("func", "Main")) +
617 types_consts() +
618 "%func = OpFunction %voidt None %funct\n";
619
620 str += entry >> middle;
621 str += middle >> std::vector<Block>({end, middle2});
622 str += end;
623 str += "OpFunctionEnd\n";
624
625 str += "%func2 = OpFunction %voidt None %funct\n";
626 str += entry2 >> middle2;
627 str += middle2 >> end2;
628 str += end2;
629 str += "OpFunctionEnd\n";
630
631 CompileSuccessfully(str);
632 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
633 EXPECT_THAT(
634 getDiagnosticString(),
635 MatchesRegex("Block\\(s\\) \\{.\\[%middle2\\]\\} are referenced but not "
636 "defined in function .\\[%Main\\]\n"
637 " %Main = OpFunction %void None %9\n"));
638 }
639
TEST_P(ValidateCFG,HeaderDoesntDominatesMergeBad)640 TEST_P(ValidateCFG, HeaderDoesntDominatesMergeBad) {
641 bool is_shader = GetParam() == SpvCapabilityShader;
642 Block entry("entry");
643 Block head("head", SpvOpBranchConditional);
644 Block f("f");
645 Block merge("merge", SpvOpReturn);
646
647 head.SetBody("%cond = OpSLessThan %boolt %one %two\n");
648
649 if (is_shader) head.AppendBody("OpSelectionMerge %merge None\n");
650
651 std::string str = GetDefaultHeader(GetParam()) +
652 nameOps("head", "merge", std::make_pair("func", "Main")) +
653 types_consts() +
654 "%func = OpFunction %voidt None %funct\n";
655
656 str += entry >> merge;
657 str += head >> std::vector<Block>({merge, f});
658 str += f >> merge;
659 str += merge;
660 str += "OpFunctionEnd\n";
661
662 CompileSuccessfully(str);
663 if (is_shader) {
664 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
665 EXPECT_THAT(
666 getDiagnosticString(),
667 MatchesRegex("The selection construct with the selection header "
668 ".\\[%head\\] does not dominate the merge block "
669 ".\\[%merge\\]\n %merge = OpLabel\n"));
670 } else {
671 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
672 }
673 }
674
TEST_P(ValidateCFG,HeaderDoesntStrictlyDominateMergeBad)675 TEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) {
676 // If a merge block is reachable, then it must be strictly dominated by
677 // its header block.
678 bool is_shader = GetParam() == SpvCapabilityShader;
679 Block head("head", SpvOpBranchConditional);
680 Block exit("exit", SpvOpReturn);
681
682 head.SetBody("%cond = OpSLessThan %boolt %one %two\n");
683
684 if (is_shader) head.AppendBody("OpSelectionMerge %head None\n");
685
686 std::string str = GetDefaultHeader(GetParam()) +
687 nameOps("head", "exit", std::make_pair("func", "Main")) +
688 types_consts() +
689 "%func = OpFunction %voidt None %funct\n";
690
691 str += head >> std::vector<Block>({exit, exit});
692 str += exit;
693 str += "OpFunctionEnd\n";
694
695 CompileSuccessfully(str);
696 if (is_shader) {
697 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
698 EXPECT_THAT(
699 getDiagnosticString(),
700 MatchesRegex("The selection construct with the selection header "
701 ".\\[%head\\] does not strictly dominate the merge block "
702 ".\\[%head\\]\n %head = OpLabel\n"));
703 } else {
704 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << str;
705 }
706 }
707
GetUnreachableMergeNoMergeInst(SpvCapability cap)708 std::string GetUnreachableMergeNoMergeInst(SpvCapability cap) {
709 std::string header = GetDefaultHeader(cap);
710 Block entry("entry");
711 Block branch("branch", SpvOpBranchConditional);
712 Block t("t", SpvOpReturn);
713 Block f("f", SpvOpReturn);
714 Block merge("merge", SpvOpReturn);
715
716 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
717 if (cap == SpvCapabilityShader)
718 branch.AppendBody("OpSelectionMerge %merge None\n");
719
720 std::string str = header;
721 str += nameOps("branch", "merge", std::make_pair("func", "Main"));
722 str += types_consts() + "%func = OpFunction %voidt None %funct\n";
723 str += entry >> branch;
724 str += branch >> std::vector<Block>({t, f});
725 str += t;
726 str += f;
727 str += merge;
728 str += "OpFunctionEnd\n";
729
730 return str;
731 }
732
TEST_P(ValidateCFG,UnreachableMergeNoMergeInst)733 TEST_P(ValidateCFG, UnreachableMergeNoMergeInst) {
734 CompileSuccessfully(GetUnreachableMergeNoMergeInst(GetParam()));
735 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
736 }
737
GetUnreachableMergeTerminatedBy(SpvCapability cap,SpvOp op)738 std::string GetUnreachableMergeTerminatedBy(SpvCapability cap, SpvOp op) {
739 std::string header = GetDefaultHeader(cap);
740
741 Block entry("entry");
742 Block branch("branch", SpvOpBranchConditional);
743 Block t("t", SpvOpReturn);
744 Block f("f", SpvOpReturn);
745 Block merge("merge", op);
746
747 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
748 std::string str = header;
749 if (cap == SpvCapabilityShader)
750 branch.AppendBody("OpSelectionMerge %merge None\n");
751
752 str += nameOps("branch", "merge", std::make_pair("func", "Main"));
753 str += types_consts();
754 str += "%func = OpFunction %voidt None %funct\n";
755 str += entry >> branch;
756 str += branch >> std::vector<Block>({t, f});
757 str += t;
758 str += f;
759 str += merge;
760 str += "OpFunctionEnd\n";
761
762 return str;
763 }
764
TEST_P(ValidateCFG,UnreachableMergeTerminatedByOpUnreachable)765 TEST_P(ValidateCFG, UnreachableMergeTerminatedByOpUnreachable) {
766 CompileSuccessfully(
767 GetUnreachableMergeTerminatedBy(GetParam(), SpvOpUnreachable));
768 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
769 }
770
TEST_F(ValidateCFG,UnreachableMergeTerminatedByOpKill)771 TEST_F(ValidateCFG, UnreachableMergeTerminatedByOpKill) {
772 CompileSuccessfully(
773 GetUnreachableMergeTerminatedBy(SpvCapabilityShader, SpvOpKill));
774 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
775 }
776
TEST_P(ValidateCFG,UnreachableMergeTerminatedByOpReturn)777 TEST_P(ValidateCFG, UnreachableMergeTerminatedByOpReturn) {
778 CompileSuccessfully(GetUnreachableMergeTerminatedBy(GetParam(), SpvOpReturn));
779 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
780 }
781
GetUnreachableContinueTerminatedBy(SpvCapability cap,SpvOp op)782 std::string GetUnreachableContinueTerminatedBy(SpvCapability cap, SpvOp op) {
783 std::string header = GetDefaultHeader(cap);
784
785 Block entry("entry");
786 Block branch("branch", SpvOpBranch);
787 Block merge("merge", SpvOpReturn);
788 Block target("target", op);
789
790 if (op == SpvOpBranch) target >> branch;
791
792 std::string str = header;
793 if (cap == SpvCapabilityShader)
794 branch.AppendBody("OpLoopMerge %merge %target None\n");
795
796 str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
797 str += types_consts();
798 str += "%func = OpFunction %voidt None %funct\n";
799 str += entry >> branch;
800 str += branch >> std::vector<Block>({merge});
801 str += merge;
802 str += target;
803 str += "OpFunctionEnd\n";
804
805 return str;
806 }
807
TEST_P(ValidateCFG,UnreachableContinueTerminatedBySpvOpUnreachable)808 TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpUnreachable) {
809 CompileSuccessfully(
810 GetUnreachableContinueTerminatedBy(GetParam(), SpvOpUnreachable));
811 if (GetParam() == SpvCapabilityShader) {
812 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
813 EXPECT_THAT(getDiagnosticString(),
814 HasSubstr("targeted by 0 back-edge blocks"));
815 } else {
816 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
817 }
818 }
819
TEST_F(ValidateCFG,UnreachableContinueTerminatedBySpvOpKill)820 TEST_F(ValidateCFG, UnreachableContinueTerminatedBySpvOpKill) {
821 CompileSuccessfully(
822 GetUnreachableContinueTerminatedBy(SpvCapabilityShader, SpvOpKill));
823 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
824 EXPECT_THAT(getDiagnosticString(),
825 HasSubstr("targeted by 0 back-edge blocks"));
826 }
827
TEST_P(ValidateCFG,UnreachableContinueTerminatedBySpvOpReturn)828 TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpReturn) {
829 CompileSuccessfully(
830 GetUnreachableContinueTerminatedBy(GetParam(), SpvOpReturn));
831 if (GetParam() == SpvCapabilityShader) {
832 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
833 EXPECT_THAT(getDiagnosticString(),
834 HasSubstr("targeted by 0 back-edge blocks"));
835 } else {
836 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
837 }
838 }
839
TEST_P(ValidateCFG,UnreachableContinueTerminatedBySpvOpBranch)840 TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpBranch) {
841 CompileSuccessfully(
842 GetUnreachableContinueTerminatedBy(GetParam(), SpvOpBranch));
843 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
844 }
845
GetUnreachableMergeUnreachableMergeInst(SpvCapability cap)846 std::string GetUnreachableMergeUnreachableMergeInst(SpvCapability cap) {
847 std::string header = GetDefaultHeader(cap);
848
849 Block body("body", SpvOpReturn);
850 Block entry("entry");
851 Block branch("branch", SpvOpBranchConditional);
852 Block t("t", SpvOpReturn);
853 Block f("f", SpvOpReturn);
854 Block merge("merge", SpvOpUnreachable);
855
856 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
857 std::string str = header;
858 if (cap == SpvCapabilityShader)
859 branch.AppendBody("OpSelectionMerge %merge None\n");
860
861 str += nameOps("branch", "merge", std::make_pair("func", "Main"));
862 str += types_consts();
863 str += "%func = OpFunction %voidt None %funct\n";
864 str += body;
865 str += merge;
866 str += entry >> branch;
867 str += branch >> std::vector<Block>({t, f});
868 str += t;
869 str += f;
870 str += "OpFunctionEnd\n";
871
872 return str;
873 }
874
TEST_P(ValidateCFG,UnreachableMergeUnreachableMergeInst)875 TEST_P(ValidateCFG, UnreachableMergeUnreachableMergeInst) {
876 CompileSuccessfully(GetUnreachableMergeUnreachableMergeInst(GetParam()));
877 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
878 }
879
GetUnreachableContinueUnreachableLoopInst(SpvCapability cap)880 std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap) {
881 std::string header = GetDefaultHeader(cap);
882
883 Block body("body", SpvOpReturn);
884 Block entry("entry");
885 Block branch("branch", SpvOpBranch);
886 Block merge("merge", SpvOpReturn);
887 Block target("target", SpvOpBranch);
888
889 target >> branch;
890
891 std::string str = header;
892 if (cap == SpvCapabilityShader)
893 branch.AppendBody("OpLoopMerge %merge %target None\n");
894
895 str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
896 str += types_consts();
897 str += "%func = OpFunction %voidt None %funct\n";
898 str += body;
899 str += target;
900 str += merge;
901 str += entry >> branch;
902 str += branch >> std::vector<Block>({merge});
903 str += "OpFunctionEnd\n";
904
905 return str;
906 }
907
TEST_P(ValidateCFG,UnreachableContinueUnreachableLoopInst)908 TEST_P(ValidateCFG, UnreachableContinueUnreachableLoopInst) {
909 CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst(GetParam()));
910 if (GetParam() == SpvCapabilityShader) {
911 // Shader causes additional structured CFG checks that cause a failure.
912 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
913 EXPECT_THAT(getDiagnosticString(),
914 HasSubstr("Back-edges (1[%branch] -> 3[%target]) can only be "
915 "formed between a block and a loop header."));
916
917 } else {
918 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
919 }
920 }
921
GetUnreachableMergeWithComplexBody(SpvCapability cap)922 std::string GetUnreachableMergeWithComplexBody(SpvCapability cap) {
923 std::string header = GetDefaultHeader(cap);
924
925 Block entry("entry");
926 Block branch("branch", SpvOpBranchConditional);
927 Block t("t", SpvOpReturn);
928 Block f("f", SpvOpReturn);
929 Block merge("merge", SpvOpUnreachable);
930
931 entry.AppendBody("%placeholder = OpVariable %intptrt Function\n");
932 entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n");
933 merge.AppendBody("OpStore %placeholder %one\n");
934
935 std::string str = header;
936 if (cap == SpvCapabilityShader)
937 branch.AppendBody("OpSelectionMerge %merge None\n");
938
939 str += nameOps("branch", "merge", std::make_pair("func", "Main"));
940 str += types_consts();
941 str += "%intptrt = OpTypePointer Function %intt\n";
942 str += "%func = OpFunction %voidt None %funct\n";
943 str += entry >> branch;
944 str += branch >> std::vector<Block>({t, f});
945 str += t;
946 str += f;
947 str += merge;
948 str += "OpFunctionEnd\n";
949
950 return str;
951 }
952
TEST_P(ValidateCFG,UnreachableMergeWithComplexBody)953 TEST_P(ValidateCFG, UnreachableMergeWithComplexBody) {
954 CompileSuccessfully(GetUnreachableMergeWithComplexBody(GetParam()));
955 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
956 }
957
GetUnreachableContinueWithComplexBody(SpvCapability cap)958 std::string GetUnreachableContinueWithComplexBody(SpvCapability cap) {
959 std::string header = GetDefaultHeader(cap);
960
961 Block entry("entry");
962 Block branch("branch", SpvOpBranch);
963 Block merge("merge", SpvOpReturn);
964 Block target("target", SpvOpBranch);
965
966 target >> branch;
967
968 entry.AppendBody("%placeholder = OpVariable %intptrt Function\n");
969 target.AppendBody("OpStore %placeholder %one\n");
970
971 std::string str = header;
972 if (cap == SpvCapabilityShader)
973 branch.AppendBody("OpLoopMerge %merge %target None\n");
974
975 str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
976 str += types_consts();
977 str += "%intptrt = OpTypePointer Function %intt\n";
978 str += "%func = OpFunction %voidt None %funct\n";
979 str += entry >> branch;
980 str += branch >> std::vector<Block>({merge});
981 str += merge;
982 str += target;
983 str += "OpFunctionEnd\n";
984
985 return str;
986 }
987
TEST_P(ValidateCFG,UnreachableContinueWithComplexBody)988 TEST_P(ValidateCFG, UnreachableContinueWithComplexBody) {
989 CompileSuccessfully(GetUnreachableContinueWithComplexBody(GetParam()));
990 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
991 }
992
GetUnreachableMergeWithBranchUse(SpvCapability cap)993 std::string GetUnreachableMergeWithBranchUse(SpvCapability cap) {
994 std::string header = GetDefaultHeader(cap);
995
996 Block entry("entry");
997 Block branch("branch", SpvOpBranchConditional);
998 Block t("t", SpvOpBranch);
999 Block f("f", SpvOpReturn);
1000 Block merge("merge", SpvOpUnreachable);
1001
1002 entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n");
1003
1004 std::string str = header;
1005 if (cap == SpvCapabilityShader)
1006 branch.AppendBody("OpSelectionMerge %merge None\n");
1007
1008 str += nameOps("branch", "merge", std::make_pair("func", "Main"));
1009 str += types_consts();
1010 str += "%func = OpFunction %voidt None %funct\n";
1011 str += entry >> branch;
1012 str += branch >> std::vector<Block>({t, f});
1013 str += t >> merge;
1014 str += f;
1015 str += merge;
1016 str += "OpFunctionEnd\n";
1017
1018 return str;
1019 }
1020
TEST_P(ValidateCFG,UnreachableMergeWithBranchUse)1021 TEST_P(ValidateCFG, UnreachableMergeWithBranchUse) {
1022 CompileSuccessfully(GetUnreachableMergeWithBranchUse(GetParam()));
1023 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
1024 }
1025
GetUnreachableMergeWithMultipleUses(SpvCapability cap)1026 std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap) {
1027 std::string header = GetDefaultHeader(cap);
1028
1029 Block entry("entry");
1030 Block branch("branch", SpvOpBranchConditional);
1031 Block t("t", SpvOpReturn);
1032 Block f("f", SpvOpReturn);
1033 Block merge("merge", SpvOpUnreachable);
1034 Block duplicate("duplicate", SpvOpBranchConditional);
1035
1036 entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n");
1037
1038 std::string str = header;
1039 if (cap == SpvCapabilityShader) {
1040 branch.AppendBody("OpSelectionMerge %merge None\n");
1041 duplicate.AppendBody("OpSelectionMerge %merge None\n");
1042 }
1043
1044 str += nameOps("branch", "merge", std::make_pair("func", "Main"));
1045 str += types_consts();
1046 str += "%func = OpFunction %voidt None %funct\n";
1047 str += entry >> branch;
1048 str += branch >> std::vector<Block>({t, f});
1049 str += duplicate >> std::vector<Block>({t, f});
1050 str += t;
1051 str += f;
1052 str += merge;
1053 str += "OpFunctionEnd\n";
1054
1055 return str;
1056 }
1057
TEST_P(ValidateCFG,UnreachableMergeWithMultipleUses)1058 TEST_P(ValidateCFG, UnreachableMergeWithMultipleUses) {
1059 CompileSuccessfully(GetUnreachableMergeWithMultipleUses(GetParam()));
1060 if (GetParam() == SpvCapabilityShader) {
1061 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1062 EXPECT_THAT(getDiagnosticString(),
1063 HasSubstr("is already a merge block for another header"));
1064 } else {
1065 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1066 }
1067 }
1068
GetUnreachableContinueWithBranchUse(SpvCapability cap)1069 std::string GetUnreachableContinueWithBranchUse(SpvCapability cap) {
1070 std::string header = GetDefaultHeader(cap);
1071
1072 Block entry("entry");
1073 Block foo("foo", SpvOpBranch);
1074 Block branch("branch", SpvOpBranch);
1075 Block merge("merge", SpvOpReturn);
1076 Block target("target", SpvOpBranch);
1077
1078 foo >> target;
1079 target >> branch;
1080
1081 entry.AppendBody("%placeholder = OpVariable %intptrt Function\n");
1082
1083 std::string str = header;
1084 if (cap == SpvCapabilityShader)
1085 branch.AppendBody("OpLoopMerge %merge %target None\n");
1086
1087 str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
1088 str += types_consts();
1089 str += "%intptrt = OpTypePointer Function %intt\n";
1090 str += "%func = OpFunction %voidt None %funct\n";
1091 str += entry >> branch;
1092 str += branch >> std::vector<Block>({merge});
1093 str += merge;
1094 str += target;
1095 str += foo;
1096 str += "OpFunctionEnd\n";
1097
1098 return str;
1099 }
1100
TEST_P(ValidateCFG,UnreachableContinueWithBranchUse)1101 TEST_P(ValidateCFG, UnreachableContinueWithBranchUse) {
1102 CompileSuccessfully(GetUnreachableContinueWithBranchUse(GetParam()));
1103 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1104 }
1105
GetReachableMergeAndContinue(SpvCapability cap)1106 std::string GetReachableMergeAndContinue(SpvCapability cap) {
1107 std::string header = GetDefaultHeader(cap);
1108
1109 Block entry("entry");
1110 Block branch("branch", SpvOpBranch);
1111 Block merge("merge", SpvOpReturn);
1112 Block target("target", SpvOpBranch);
1113 Block body("body", SpvOpBranchConditional);
1114 Block t("t", SpvOpBranch);
1115 Block f("f", SpvOpBranch);
1116
1117 target >> branch;
1118 body.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1119 t >> merge;
1120 f >> target;
1121
1122 std::string str = header;
1123 if (cap == SpvCapabilityShader) {
1124 branch.AppendBody("OpLoopMerge %merge %target None\n");
1125 body.AppendBody("OpSelectionMerge %f None\n");
1126 }
1127
1128 str += nameOps("branch", "merge", "target", "body", "t", "f",
1129 std::make_pair("func", "Main"));
1130 str += types_consts();
1131 str += "%func = OpFunction %voidt None %funct\n";
1132 str += entry >> branch;
1133 str += branch >> std::vector<Block>({body});
1134 str += body >> std::vector<Block>({t, f});
1135 str += t;
1136 str += f;
1137 str += merge;
1138 str += target;
1139 str += "OpFunctionEnd\n";
1140
1141 return str;
1142 }
1143
TEST_P(ValidateCFG,ReachableMergeAndContinue)1144 TEST_P(ValidateCFG, ReachableMergeAndContinue) {
1145 CompileSuccessfully(GetReachableMergeAndContinue(GetParam()));
1146 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1147 }
1148
GetUnreachableMergeAndContinue(SpvCapability cap)1149 std::string GetUnreachableMergeAndContinue(SpvCapability cap) {
1150 std::string header = GetDefaultHeader(cap);
1151
1152 Block entry("entry");
1153 Block branch("branch", SpvOpBranch);
1154 Block merge("merge", SpvOpReturn);
1155 Block target("target", SpvOpBranch);
1156 Block body("body", SpvOpBranchConditional);
1157 Block t("t", SpvOpReturn);
1158 Block f("f", SpvOpReturn);
1159
1160 target >> branch;
1161 body.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1162
1163 std::string str = header;
1164 if (cap == SpvCapabilityShader) {
1165 branch.AppendBody("OpLoopMerge %merge %target None\n");
1166 body.AppendBody("OpSelectionMerge %target None\n");
1167 }
1168
1169 str += nameOps("branch", "merge", "target", "body", "t", "f",
1170 std::make_pair("func", "Main"));
1171 str += types_consts();
1172 str += "%func = OpFunction %voidt None %funct\n";
1173 str += entry >> branch;
1174 str += branch >> std::vector<Block>({body});
1175 str += body >> std::vector<Block>({t, f});
1176 str += t;
1177 str += f;
1178 str += merge;
1179 str += target;
1180 str += "OpFunctionEnd\n";
1181
1182 return str;
1183 }
1184
TEST_P(ValidateCFG,UnreachableMergeAndContinue)1185 TEST_P(ValidateCFG, UnreachableMergeAndContinue) {
1186 CompileSuccessfully(GetUnreachableMergeAndContinue(GetParam()));
1187 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1188 }
1189
GetUnreachableBlock(SpvCapability cap)1190 std::string GetUnreachableBlock(SpvCapability cap) {
1191 std::string header = GetDefaultHeader(cap);
1192
1193 Block entry("entry");
1194 Block unreachable("unreachable");
1195 Block exit("exit", SpvOpReturn);
1196
1197 std::string str = header;
1198 str += nameOps("unreachable", "exit", std::make_pair("func", "Main"));
1199 str += types_consts();
1200 str += "%func = OpFunction %voidt None %funct\n";
1201 str += entry >> exit;
1202 str += unreachable >> exit;
1203 str += exit;
1204 str += "OpFunctionEnd\n";
1205
1206 return str;
1207 }
1208
TEST_P(ValidateCFG,UnreachableBlock)1209 TEST_P(ValidateCFG, UnreachableBlock) {
1210 CompileSuccessfully(GetUnreachableBlock(GetParam()));
1211 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1212 }
1213
GetUnreachableBranch(SpvCapability cap)1214 std::string GetUnreachableBranch(SpvCapability cap) {
1215 std::string header = GetDefaultHeader(cap);
1216
1217 Block entry("entry");
1218 Block unreachable("unreachable", SpvOpBranchConditional);
1219 Block unreachablechildt("unreachablechildt");
1220 Block unreachablechildf("unreachablechildf");
1221 Block merge("merge");
1222 Block exit("exit", SpvOpReturn);
1223
1224 unreachable.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1225 if (cap == SpvCapabilityShader)
1226 unreachable.AppendBody("OpSelectionMerge %merge None\n");
1227
1228 std::string str = header;
1229 str += nameOps("unreachable", "exit", std::make_pair("func", "Main"));
1230 str += types_consts();
1231 str += "%func = OpFunction %voidt None %funct\n";
1232
1233 str += entry >> exit;
1234 str +=
1235 unreachable >> std::vector<Block>({unreachablechildt, unreachablechildf});
1236 str += unreachablechildt >> merge;
1237 str += unreachablechildf >> merge;
1238 str += merge >> exit;
1239 str += exit;
1240 str += "OpFunctionEnd\n";
1241
1242 return str;
1243 }
1244
TEST_P(ValidateCFG,UnreachableBranch)1245 TEST_P(ValidateCFG, UnreachableBranch) {
1246 CompileSuccessfully(GetUnreachableBranch(GetParam()));
1247 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1248 }
1249
TEST_P(ValidateCFG,EmptyFunction)1250 TEST_P(ValidateCFG, EmptyFunction) {
1251 std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) +
1252 R"(%func = OpFunction %voidt None %funct
1253 %l = OpLabel
1254 OpReturn
1255 OpFunctionEnd)";
1256
1257 CompileSuccessfully(str);
1258 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1259 }
1260
TEST_P(ValidateCFG,SingleBlockLoop)1261 TEST_P(ValidateCFG, SingleBlockLoop) {
1262 bool is_shader = GetParam() == SpvCapabilityShader;
1263 Block entry("entry");
1264 Block loop("loop", SpvOpBranchConditional);
1265 Block exit("exit", SpvOpReturn);
1266
1267 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1268 if (is_shader) loop.AppendBody("OpLoopMerge %exit %loop None\n");
1269
1270 std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) +
1271 "%func = OpFunction %voidt None %funct\n";
1272
1273 str += entry >> loop;
1274 str += loop >> std::vector<Block>({loop, exit});
1275 str += exit;
1276 str += "OpFunctionEnd";
1277
1278 CompileSuccessfully(str);
1279 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1280 }
1281
TEST_P(ValidateCFG,NestedLoops)1282 TEST_P(ValidateCFG, NestedLoops) {
1283 bool is_shader = GetParam() == SpvCapabilityShader;
1284 Block entry("entry");
1285 Block loop1("loop1");
1286 Block loop1_cont_break_block("loop1_cont_break_block",
1287 SpvOpBranchConditional);
1288 Block loop2("loop2", SpvOpBranchConditional);
1289 Block loop2_merge("loop2_merge");
1290 Block loop1_merge("loop1_merge");
1291 Block exit("exit", SpvOpReturn);
1292
1293 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1294 if (is_shader) {
1295 loop1.SetBody("OpLoopMerge %loop1_merge %loop2 None\n");
1296 loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n");
1297 }
1298
1299 std::string str = GetDefaultHeader(GetParam()) +
1300 nameOps("loop2", "loop2_merge") + types_consts() +
1301 "%func = OpFunction %voidt None %funct\n";
1302
1303 str += entry >> loop1;
1304 str += loop1 >> loop1_cont_break_block;
1305 str += loop1_cont_break_block >> std::vector<Block>({loop1_merge, loop2});
1306 str += loop2 >> std::vector<Block>({loop2, loop2_merge});
1307 str += loop2_merge >> loop1;
1308 str += loop1_merge >> exit;
1309 str += exit;
1310 str += "OpFunctionEnd";
1311
1312 CompileSuccessfully(str);
1313 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1314 }
1315
TEST_P(ValidateCFG,NestedSelection)1316 TEST_P(ValidateCFG, NestedSelection) {
1317 bool is_shader = GetParam() == SpvCapabilityShader;
1318 Block entry("entry");
1319 const int N = 256;
1320 std::vector<Block> if_blocks;
1321 std::vector<Block> merge_blocks;
1322 Block inner("inner");
1323
1324 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1325
1326 if_blocks.emplace_back("if0", SpvOpBranchConditional);
1327
1328 if (is_shader) if_blocks[0].SetBody("OpSelectionMerge %if_merge0 None\n");
1329 merge_blocks.emplace_back("if_merge0", SpvOpReturn);
1330
1331 for (int i = 1; i < N; i++) {
1332 std::stringstream ss;
1333 ss << i;
1334 if_blocks.emplace_back("if" + ss.str(), SpvOpBranchConditional);
1335 if (is_shader)
1336 if_blocks[i].SetBody("OpSelectionMerge %if_merge" + ss.str() + " None\n");
1337 merge_blocks.emplace_back("if_merge" + ss.str(), SpvOpBranch);
1338 }
1339 std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) +
1340 "%func = OpFunction %voidt None %funct\n";
1341
1342 str += entry >> if_blocks[0];
1343 for (int i = 0; i < N - 1; i++) {
1344 str +=
1345 if_blocks[i] >> std::vector<Block>({if_blocks[i + 1], merge_blocks[i]});
1346 }
1347 str += if_blocks.back() >> std::vector<Block>({inner, merge_blocks.back()});
1348 str += inner >> merge_blocks.back();
1349 for (int i = N - 1; i > 0; i--) {
1350 str += merge_blocks[i] >> merge_blocks[i - 1];
1351 }
1352 str += merge_blocks[0];
1353 str += "OpFunctionEnd";
1354
1355 CompileSuccessfully(str);
1356 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1357 }
1358
TEST_P(ValidateCFG,BackEdgeBlockDoesntPostDominateContinueTargetBad)1359 TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) {
1360 bool is_shader = GetParam() == SpvCapabilityShader;
1361 Block entry("entry");
1362 Block loop1("loop1", SpvOpBranchConditional);
1363 Block loop2("loop2", SpvOpBranchConditional);
1364 Block loop2_merge("loop2_merge");
1365 Block loop1_cont("loop1_cont", SpvOpBranchConditional);
1366 Block be_block("be_block");
1367 Block exit("exit", SpvOpReturn);
1368
1369 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1370 if (is_shader) {
1371 loop1.SetBody("OpLoopMerge %exit %loop1_cont None\n");
1372 loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n");
1373 }
1374
1375 std::string str =
1376 GetDefaultHeader(GetParam()) +
1377 nameOps("loop1", "loop2", "be_block", "loop1_cont", "loop2_merge") +
1378 types_consts() + "%func = OpFunction %voidt None %funct\n";
1379
1380 str += entry >> loop1;
1381 str += loop1 >> std::vector<Block>({loop2, exit});
1382 str += loop2 >> std::vector<Block>({loop2, loop2_merge});
1383 str += loop2_merge >> loop1_cont;
1384 str += loop1_cont >> std::vector<Block>({be_block, exit});
1385 str += be_block >> loop1;
1386 str += exit;
1387 str += "OpFunctionEnd";
1388
1389 CompileSuccessfully(str);
1390 if (GetParam() == SpvCapabilityShader) {
1391 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1392 EXPECT_THAT(getDiagnosticString(),
1393 MatchesRegex("The continue construct with the continue target "
1394 ".\\[%loop1_cont\\] is not post dominated by the "
1395 "back-edge block .\\[%be_block\\]\n"
1396 " %be_block = OpLabel\n"));
1397 } else {
1398 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1399 }
1400 }
1401
TEST_P(ValidateCFG,BranchingToNonLoopHeaderBlockBad)1402 TEST_P(ValidateCFG, BranchingToNonLoopHeaderBlockBad) {
1403 bool is_shader = GetParam() == SpvCapabilityShader;
1404 Block entry("entry");
1405 Block split("split", SpvOpBranchConditional);
1406 Block t("t");
1407 Block f("f");
1408 Block exit("exit", SpvOpReturn);
1409
1410 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1411 if (is_shader) split.SetBody("OpSelectionMerge %exit None\n");
1412
1413 std::string str = GetDefaultHeader(GetParam()) + nameOps("split", "f") +
1414 types_consts() +
1415 "%func = OpFunction %voidt None %funct\n";
1416
1417 str += entry >> split;
1418 str += split >> std::vector<Block>({t, f});
1419 str += t >> exit;
1420 str += f >> split;
1421 str += exit;
1422 str += "OpFunctionEnd";
1423
1424 CompileSuccessfully(str);
1425 if (is_shader) {
1426 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1427 EXPECT_THAT(
1428 getDiagnosticString(),
1429 MatchesRegex("Back-edges \\(.\\[%f\\] -> .\\[%split\\]\\) can only "
1430 "be formed between a block and a loop header.\n"
1431 " %f = OpLabel\n"));
1432 } else {
1433 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1434 }
1435 }
1436
TEST_P(ValidateCFG,BranchingToSameNonLoopHeaderBlockBad)1437 TEST_P(ValidateCFG, BranchingToSameNonLoopHeaderBlockBad) {
1438 bool is_shader = GetParam() == SpvCapabilityShader;
1439 Block entry("entry");
1440 Block split("split", SpvOpBranchConditional);
1441 Block exit("exit", SpvOpReturn);
1442
1443 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1444 if (is_shader) split.SetBody("OpSelectionMerge %exit None\n");
1445
1446 std::string str = GetDefaultHeader(GetParam()) + nameOps("split") +
1447 types_consts() +
1448 "%func = OpFunction %voidt None %funct\n";
1449
1450 str += entry >> split;
1451 str += split >> std::vector<Block>({split, exit});
1452 str += exit;
1453 str += "OpFunctionEnd";
1454
1455 CompileSuccessfully(str);
1456 if (is_shader) {
1457 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1458 EXPECT_THAT(
1459 getDiagnosticString(),
1460 MatchesRegex(
1461 "Back-edges \\(.\\[%split\\] -> .\\[%split\\]\\) can only be "
1462 "formed between a block and a loop header.\n %split = OpLabel\n"));
1463 } else {
1464 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1465 }
1466 }
1467
TEST_P(ValidateCFG,MultipleBackEdgeBlocksToLoopHeaderBad)1468 TEST_P(ValidateCFG, MultipleBackEdgeBlocksToLoopHeaderBad) {
1469 bool is_shader = GetParam() == SpvCapabilityShader;
1470 Block entry("entry");
1471 Block loop("loop", SpvOpBranchConditional);
1472 Block back0("back0");
1473 Block back1("back1");
1474 Block merge("merge", SpvOpReturn);
1475
1476 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1477 if (is_shader) loop.SetBody("OpLoopMerge %merge %back0 None\n");
1478
1479 std::string str = GetDefaultHeader(GetParam()) +
1480 nameOps("loop", "back0", "back1") + types_consts() +
1481 "%func = OpFunction %voidt None %funct\n";
1482
1483 str += entry >> loop;
1484 str += loop >> std::vector<Block>({back0, back1});
1485 str += back0 >> loop;
1486 str += back1 >> loop;
1487 str += merge;
1488 str += "OpFunctionEnd";
1489
1490 CompileSuccessfully(str);
1491 if (is_shader) {
1492 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1493 EXPECT_THAT(
1494 getDiagnosticString(),
1495 MatchesRegex(
1496 "Loop header .\\[%loop\\] is targeted by 2 back-edge blocks but "
1497 "the standard requires exactly one\n %loop = OpLabel\n"))
1498 << str;
1499 } else {
1500 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1501 }
1502 }
1503
TEST_P(ValidateCFG,ContinueTargetMustBePostDominatedByBackEdge)1504 TEST_P(ValidateCFG, ContinueTargetMustBePostDominatedByBackEdge) {
1505 bool is_shader = GetParam() == SpvCapabilityShader;
1506 Block entry("entry");
1507 Block loop("loop", SpvOpBranchConditional);
1508 Block cheader("cheader", SpvOpBranchConditional);
1509 Block be_block("be_block");
1510 Block merge("merge", SpvOpReturn);
1511 Block exit("exit", SpvOpReturn);
1512
1513 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1514 if (is_shader) loop.SetBody("OpLoopMerge %merge %cheader None\n");
1515
1516 std::string str = GetDefaultHeader(GetParam()) +
1517 nameOps("cheader", "be_block") + types_consts() +
1518 "%func = OpFunction %voidt None %funct\n";
1519
1520 str += entry >> loop;
1521 str += loop >> std::vector<Block>({cheader, merge});
1522 str += cheader >> std::vector<Block>({exit, be_block});
1523 str += exit; // Branches out of a continue construct
1524 str += be_block >> loop;
1525 str += merge;
1526 str += "OpFunctionEnd";
1527
1528 CompileSuccessfully(str);
1529 if (is_shader) {
1530 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1531 EXPECT_THAT(getDiagnosticString(),
1532 MatchesRegex("The continue construct with the continue target "
1533 ".\\[%cheader\\] is not post dominated by the "
1534 "back-edge block .\\[%be_block\\]\n"
1535 " %be_block = OpLabel\n"));
1536 } else {
1537 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1538 }
1539 }
1540
TEST_P(ValidateCFG,BranchOutOfConstructToMergeBad)1541 TEST_P(ValidateCFG, BranchOutOfConstructToMergeBad) {
1542 bool is_shader = GetParam() == SpvCapabilityShader;
1543 Block entry("entry");
1544 Block loop("loop", SpvOpBranchConditional);
1545 Block cont("cont", SpvOpBranchConditional);
1546 Block merge("merge", SpvOpReturn);
1547
1548 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1549 if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n");
1550
1551 std::string str = GetDefaultHeader(GetParam()) + nameOps("cont", "loop") +
1552 types_consts() +
1553 "%func = OpFunction %voidt None %funct\n";
1554
1555 str += entry >> loop;
1556 str += loop >> std::vector<Block>({cont, merge});
1557 str += cont >> std::vector<Block>({loop, merge});
1558 str += merge;
1559 str += "OpFunctionEnd";
1560
1561 CompileSuccessfully(str);
1562 if (is_shader) {
1563 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1564 EXPECT_THAT(getDiagnosticString(),
1565 MatchesRegex("The continue construct with the continue target "
1566 ".\\[%loop\\] is not post dominated by the "
1567 "back-edge block .\\[%cont\\]\n"
1568 " %cont = OpLabel\n"))
1569 << str;
1570 } else {
1571 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1572 }
1573 }
1574
TEST_P(ValidateCFG,BranchOutOfConstructBad)1575 TEST_P(ValidateCFG, BranchOutOfConstructBad) {
1576 bool is_shader = GetParam() == SpvCapabilityShader;
1577 Block entry("entry");
1578 Block loop("loop", SpvOpBranchConditional);
1579 Block cont("cont", SpvOpBranchConditional);
1580 Block merge("merge");
1581 Block exit("exit", SpvOpReturn);
1582
1583 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1584 if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n");
1585
1586 std::string str = GetDefaultHeader(GetParam()) + nameOps("cont", "loop") +
1587 types_consts() +
1588 "%func = OpFunction %voidt None %funct\n";
1589
1590 str += entry >> loop;
1591 str += loop >> std::vector<Block>({cont, merge});
1592 str += cont >> std::vector<Block>({loop, exit});
1593 str += merge >> exit;
1594 str += exit;
1595 str += "OpFunctionEnd";
1596
1597 CompileSuccessfully(str);
1598 if (is_shader) {
1599 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1600 EXPECT_THAT(getDiagnosticString(),
1601 MatchesRegex("The continue construct with the continue target "
1602 ".\\[%loop\\] is not post dominated by the "
1603 "back-edge block .\\[%cont\\]\n"
1604 " %cont = OpLabel\n"));
1605 } else {
1606 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1607 }
1608 }
1609
TEST_F(ValidateCFG,OpSwitchToUnreachableBlock)1610 TEST_F(ValidateCFG, OpSwitchToUnreachableBlock) {
1611 Block entry("entry", SpvOpSwitch);
1612 Block case0("case0");
1613 Block case1("case1");
1614 Block case2("case2");
1615 Block def("default", SpvOpUnreachable);
1616 Block phi("phi", SpvOpReturn);
1617
1618 std::string str = R"(
1619 OpCapability Shader
1620 OpMemoryModel Logical GLSL450
1621 OpEntryPoint GLCompute %main "main" %id
1622 OpExecutionMode %main LocalSize 1 1 1
1623 OpSource GLSL 430
1624 OpName %main "main"
1625 OpDecorate %id BuiltIn GlobalInvocationId
1626 %void = OpTypeVoid
1627 %voidf = OpTypeFunction %void
1628 %u32 = OpTypeInt 32 0
1629 %f32 = OpTypeFloat 32
1630 %uvec3 = OpTypeVector %u32 3
1631 %fvec3 = OpTypeVector %f32 3
1632 %uvec3ptr = OpTypePointer Input %uvec3
1633 %id = OpVariable %uvec3ptr Input
1634 %one = OpConstant %u32 1
1635 %three = OpConstant %u32 3
1636 %main = OpFunction %void None %voidf
1637 )";
1638
1639 entry.SetBody(
1640 "%idval = OpLoad %uvec3 %id\n"
1641 "%x = OpCompositeExtract %u32 %idval 0\n"
1642 "%selector = OpUMod %u32 %x %three\n"
1643 "OpSelectionMerge %phi None\n");
1644 str += entry >> std::vector<Block>({def, case0, case1, case2});
1645 str += case1 >> phi;
1646 str += def;
1647 str += phi;
1648 str += case0 >> phi;
1649 str += case2 >> phi;
1650 str += "OpFunctionEnd";
1651
1652 CompileSuccessfully(str);
1653 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
1654 }
1655
TEST_F(ValidateCFG,LoopWithZeroBackEdgesBad)1656 TEST_F(ValidateCFG, LoopWithZeroBackEdgesBad) {
1657 std::string str = R"(
1658 OpCapability Shader
1659 OpMemoryModel Logical GLSL450
1660 OpEntryPoint Fragment %main "main"
1661 OpExecutionMode %main OriginUpperLeft
1662 OpName %loop "loop"
1663 %voidt = OpTypeVoid
1664 %funct = OpTypeFunction %voidt
1665 %main = OpFunction %voidt None %funct
1666 %loop = OpLabel
1667 OpLoopMerge %exit %loop None
1668 OpBranch %exit
1669 %exit = OpLabel
1670 OpReturn
1671 OpFunctionEnd
1672 )";
1673 CompileSuccessfully(str);
1674 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1675 EXPECT_THAT(
1676 getDiagnosticString(),
1677 MatchesRegex("Loop header .\\[%loop\\] is targeted by "
1678 "0 back-edge blocks but the standard requires exactly "
1679 "one\n %loop = OpLabel\n"));
1680 }
1681
TEST_F(ValidateCFG,LoopWithBackEdgeFromUnreachableContinueConstructGood)1682 TEST_F(ValidateCFG, LoopWithBackEdgeFromUnreachableContinueConstructGood) {
1683 std::string str = R"(
1684 OpCapability Shader
1685 OpMemoryModel Logical GLSL450
1686 OpEntryPoint Fragment %main "main"
1687 OpExecutionMode %main OriginUpperLeft
1688 OpName %loop "loop"
1689 %voidt = OpTypeVoid
1690 %funct = OpTypeFunction %voidt
1691 %floatt = OpTypeFloat 32
1692 %boolt = OpTypeBool
1693 %one = OpConstant %floatt 1
1694 %two = OpConstant %floatt 2
1695 %main = OpFunction %voidt None %funct
1696 %entry = OpLabel
1697 OpBranch %loop
1698 %loop = OpLabel
1699 OpLoopMerge %exit %cont None
1700 OpBranch %16
1701 %16 = OpLabel
1702 %cond = OpFOrdLessThan %boolt %one %two
1703 OpBranchConditional %cond %body %exit
1704 %body = OpLabel
1705 OpReturn
1706 %cont = OpLabel ; Reachable only from OpLoopMerge ContinueTarget parameter
1707 OpBranch %loop ; Should be considered a back-edge
1708 %exit = OpLabel
1709 OpReturn
1710 OpFunctionEnd
1711 )";
1712
1713 CompileSuccessfully(str);
1714 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
1715 }
1716
TEST_P(ValidateCFG,NestedConstructWithUnreachableMergeBlockBranchingToOuterMergeBlock)1717 TEST_P(ValidateCFG,
1718 NestedConstructWithUnreachableMergeBlockBranchingToOuterMergeBlock) {
1719 // Test for https://github.com/KhronosGroup/SPIRV-Tools/issues/297
1720 // The nested construct has an unreachable merge block. In the
1721 // augmented CFG that merge block
1722 // we still determine that the
1723 bool is_shader = GetParam() == SpvCapabilityShader;
1724 Block entry("entry", SpvOpBranchConditional);
1725 Block inner_head("inner_head", SpvOpBranchConditional);
1726 Block inner_true("inner_true", SpvOpReturn);
1727 Block inner_false("inner_false", SpvOpReturn);
1728 Block inner_merge("inner_merge");
1729 Block exit("exit", SpvOpReturn);
1730
1731 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1732 if (is_shader) {
1733 entry.AppendBody("OpSelectionMerge %exit None\n");
1734 inner_head.SetBody("OpSelectionMerge %inner_merge None\n");
1735 }
1736
1737 std::string str = GetDefaultHeader(GetParam()) +
1738 nameOps("entry", "inner_merge", "exit") + types_consts() +
1739 "%func = OpFunction %voidt None %funct\n";
1740
1741 str += entry >> std::vector<Block>({inner_head, exit});
1742 str += inner_head >> std::vector<Block>({inner_true, inner_false});
1743 str += inner_true;
1744 str += inner_false;
1745 str += inner_merge >> exit;
1746 str += exit;
1747 str += "OpFunctionEnd";
1748
1749 CompileSuccessfully(str);
1750 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
1751 }
1752
TEST_P(ValidateCFG,ContinueTargetCanBeMergeBlockForNestedStructure)1753 TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructure) {
1754 // The continue construct cannot be the merge target of a nested selection
1755 // because the loop construct must contain "if_merge" because it contains
1756 // "if_head".
1757 bool is_shader = GetParam() == SpvCapabilityShader;
1758 Block entry("entry");
1759 Block loop("loop");
1760 Block if_head("if_head", SpvOpBranchConditional);
1761 Block if_true("if_true");
1762 Block if_merge("if_merge", SpvOpBranchConditional);
1763 Block merge("merge", SpvOpReturn);
1764
1765 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1766 if (is_shader) {
1767 loop.SetBody("OpLoopMerge %merge %if_merge None\n");
1768 if_head.SetBody("OpSelectionMerge %if_merge None\n");
1769 }
1770
1771 std::string str =
1772 GetDefaultHeader(GetParam()) +
1773 nameOps("entry", "loop", "if_head", "if_true", "if_merge", "merge") +
1774 types_consts() + "%func = OpFunction %voidt None %funct\n";
1775
1776 str += entry >> loop;
1777 str += loop >> if_head;
1778 str += if_head >> std::vector<Block>({if_true, if_merge});
1779 str += if_true >> if_merge;
1780 str += if_merge >> std::vector<Block>({loop, merge});
1781 str += merge;
1782 str += "OpFunctionEnd";
1783
1784 CompileSuccessfully(str);
1785 if (is_shader) {
1786 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1787 EXPECT_THAT(
1788 getDiagnosticString(),
1789 HasSubstr("Header block 3[%if_head] is contained in the loop construct "
1790 "headed "
1791 "by 2[%loop], but its merge block 5[%if_merge] is not"));
1792 } else {
1793 EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
1794 }
1795 }
1796
TEST_P(ValidateCFG,SingleLatchBlockMultipleBranchesToLoopHeader)1797 TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) {
1798 // This test case ensures we allow both branches of a loop latch block
1799 // to go back to the loop header. It still counts as a single back edge.
1800 bool is_shader = GetParam() == SpvCapabilityShader;
1801 Block entry("entry");
1802 Block loop("loop", SpvOpBranchConditional);
1803 Block latch("latch", SpvOpBranchConditional);
1804 Block merge("merge", SpvOpReturn);
1805
1806 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1807 if (is_shader) {
1808 loop.SetBody("OpLoopMerge %merge %latch None\n");
1809 }
1810
1811 std::string str = GetDefaultHeader(GetParam()) +
1812 nameOps("entry", "loop", "latch", "merge") +
1813 types_consts() +
1814 "%func = OpFunction %voidt None %funct\n";
1815
1816 str += entry >> loop;
1817 str += loop >> std::vector<Block>({latch, merge});
1818 str += latch >> std::vector<Block>({loop, loop}); // This is the key
1819 str += merge;
1820 str += "OpFunctionEnd";
1821
1822 CompileSuccessfully(str);
1823 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions())
1824 << str << getDiagnosticString();
1825 }
1826
TEST_P(ValidateCFG,SingleLatchBlockHeaderContinueTargetIsItselfGood)1827 TEST_P(ValidateCFG, SingleLatchBlockHeaderContinueTargetIsItselfGood) {
1828 // This test case ensures we don't count a Continue Target from a loop
1829 // header to itself as a self-loop when computing back edges.
1830 // Also, it detects that there is an edge from %latch to the pseudo-exit
1831 // node, rather than from %loop. In particular, it detects that we
1832 // have used the *reverse* textual order of blocks when computing
1833 // predecessor traversal roots.
1834 bool is_shader = GetParam() == SpvCapabilityShader;
1835 Block entry("entry");
1836 Block loop("loop");
1837 Block latch("latch");
1838 Block merge("merge", SpvOpReturn);
1839
1840 entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
1841 if (is_shader) {
1842 loop.SetBody("OpLoopMerge %merge %loop None\n");
1843 }
1844
1845 std::string str = GetDefaultHeader(GetParam()) +
1846 nameOps("entry", "loop", "latch", "merge") +
1847 types_consts() +
1848 "%func = OpFunction %voidt None %funct\n";
1849
1850 str += entry >> loop;
1851 str += loop >> latch;
1852 str += latch >> loop;
1853 str += merge;
1854 str += "OpFunctionEnd";
1855
1856 CompileSuccessfully(str);
1857 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions())
1858 << str << getDiagnosticString();
1859 }
1860
1861 // Unit test to check the case where a basic block is the entry block of 2
1862 // different constructs. In this case, the basic block is the entry block of a
1863 // continue construct as well as a selection construct. See issue# 517 for more
1864 // details.
TEST_F(ValidateCFG,BasicBlockIsEntryBlockOfTwoConstructsGood)1865 TEST_F(ValidateCFG, BasicBlockIsEntryBlockOfTwoConstructsGood) {
1866 std::string spirv = R"(
1867 OpCapability Shader
1868 OpCapability Linkage
1869 OpMemoryModel Logical GLSL450
1870 %void = OpTypeVoid
1871 %bool = OpTypeBool
1872 %int = OpTypeInt 32 1
1873 %void_func = OpTypeFunction %void
1874 %int_0 = OpConstant %int 0
1875 %testfun = OpFunction %void None %void_func
1876 %label_1 = OpLabel
1877 OpBranch %start
1878 %start = OpLabel
1879 %cond = OpSLessThan %bool %int_0 %int_0
1880 ;
1881 ; Note: In this case, the "target" block is both the entry block of
1882 ; the continue construct of the loop as well as the entry block of
1883 ; the selection construct.
1884 ;
1885 OpLoopMerge %loop_merge %target None
1886 OpBranchConditional %cond %target %loop_merge
1887 %loop_merge = OpLabel
1888 OpReturn
1889 %target = OpLabel
1890 OpSelectionMerge %selection_merge None
1891 OpBranchConditional %cond %do_stuff %do_other_stuff
1892 %do_other_stuff = OpLabel
1893 OpBranch %selection_merge
1894 %selection_merge = OpLabel
1895 OpBranch %start
1896 %do_stuff = OpLabel
1897 OpBranch %selection_merge
1898 OpFunctionEnd
1899 )";
1900 CompileSuccessfully(spirv);
1901 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
1902 }
1903
TEST_F(ValidateCFG,OpReturnInNonVoidFunc)1904 TEST_F(ValidateCFG, OpReturnInNonVoidFunc) {
1905 std::string spirv = R"(
1906 OpCapability Shader
1907 OpCapability Linkage
1908 OpMemoryModel Logical GLSL450
1909 %int = OpTypeInt 32 1
1910 %int_func = OpTypeFunction %int
1911 %testfun = OpFunction %int None %int_func
1912 %label_1 = OpLabel
1913 OpReturn
1914 OpFunctionEnd
1915 )";
1916 CompileSuccessfully(spirv);
1917 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1918 EXPECT_THAT(
1919 getDiagnosticString(),
1920 HasSubstr(
1921 "OpReturn can only be called from a function with void return type.\n"
1922 " OpReturn"));
1923 }
1924
TEST_F(ValidateCFG,StructuredCFGBranchIntoSelectionBody)1925 TEST_F(ValidateCFG, StructuredCFGBranchIntoSelectionBody) {
1926 std::string spirv = R"(
1927 OpCapability Shader
1928 OpMemoryModel Logical GLSL450
1929 OpEntryPoint Fragment %func "func"
1930 OpExecutionMode %func OriginUpperLeft
1931 %void = OpTypeVoid
1932 %bool = OpTypeBool
1933 %true = OpConstantTrue %bool
1934 %functy = OpTypeFunction %void
1935 %func = OpFunction %void None %functy
1936 %entry = OpLabel
1937 OpSelectionMerge %merge None
1938 OpBranchConditional %true %then %merge
1939 %merge = OpLabel
1940 OpBranch %then
1941 %then = OpLabel
1942 OpReturn
1943 OpFunctionEnd
1944 )";
1945
1946 CompileSuccessfully(spirv);
1947 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
1948 EXPECT_THAT(getDiagnosticString(),
1949 HasSubstr("branches to the selection construct, but not to the "
1950 "selection header <ID> 6\n %7 = OpLabel"));
1951 }
1952
TEST_F(ValidateCFG,SwitchDefaultOnly)1953 TEST_F(ValidateCFG, SwitchDefaultOnly) {
1954 std::string text = R"(
1955 OpCapability Shader
1956 OpCapability Linkage
1957 OpMemoryModel Logical GLSL450
1958 %1 = OpTypeVoid
1959 %2 = OpTypeInt 32 0
1960 %3 = OpConstant %2 0
1961 %4 = OpTypeFunction %1
1962 %5 = OpFunction %1 None %4
1963 %6 = OpLabel
1964 OpSelectionMerge %7 None
1965 OpSwitch %3 %7
1966 %7 = OpLabel
1967 OpReturn
1968 OpFunctionEnd
1969 )";
1970
1971 CompileSuccessfully(text);
1972 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1973 }
1974
TEST_F(ValidateCFG,SwitchSingleCase)1975 TEST_F(ValidateCFG, SwitchSingleCase) {
1976 std::string text = R"(
1977 OpCapability Shader
1978 OpCapability Linkage
1979 OpMemoryModel Logical GLSL450
1980 %1 = OpTypeVoid
1981 %2 = OpTypeInt 32 0
1982 %3 = OpConstant %2 0
1983 %4 = OpTypeFunction %1
1984 %5 = OpFunction %1 None %4
1985 %6 = OpLabel
1986 OpSelectionMerge %7 None
1987 OpSwitch %3 %7 0 %8
1988 %8 = OpLabel
1989 OpBranch %7
1990 %7 = OpLabel
1991 OpReturn
1992 OpFunctionEnd
1993 )";
1994
1995 CompileSuccessfully(text);
1996 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
1997 }
1998
TEST_F(ValidateCFG,MultipleFallThroughBlocks)1999 TEST_F(ValidateCFG, MultipleFallThroughBlocks) {
2000 std::string text = R"(
2001 OpCapability Shader
2002 OpCapability Linkage
2003 OpMemoryModel Logical GLSL450
2004 %1 = OpTypeVoid
2005 %2 = OpTypeInt 32 0
2006 %3 = OpConstant %2 0
2007 %4 = OpTypeFunction %1
2008 %5 = OpTypeBool
2009 %6 = OpConstantTrue %5
2010 %7 = OpFunction %1 None %4
2011 %8 = OpLabel
2012 OpSelectionMerge %9 None
2013 OpSwitch %3 %10 0 %11 1 %12
2014 %10 = OpLabel
2015 OpBranchConditional %6 %11 %12
2016 %11 = OpLabel
2017 OpBranch %9
2018 %12 = OpLabel
2019 OpBranch %9
2020 %9 = OpLabel
2021 OpReturn
2022 OpFunctionEnd
2023 )";
2024
2025 CompileSuccessfully(text);
2026 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2027 EXPECT_THAT(
2028 getDiagnosticString(),
2029 HasSubstr(
2030 "Case construct that targets 10[%10] has branches to multiple other "
2031 "case construct targets 12[%12] and 11[%11]\n %10 = OpLabel"));
2032 }
2033
TEST_F(ValidateCFG,MultipleFallThroughToDefault)2034 TEST_F(ValidateCFG, MultipleFallThroughToDefault) {
2035 std::string text = R"(
2036 OpCapability Shader
2037 OpCapability Linkage
2038 OpMemoryModel Logical GLSL450
2039 %1 = OpTypeVoid
2040 %2 = OpTypeInt 32 0
2041 %3 = OpConstant %2 0
2042 %4 = OpTypeFunction %1
2043 %5 = OpTypeBool
2044 %6 = OpConstantTrue %5
2045 %7 = OpFunction %1 None %4
2046 %8 = OpLabel
2047 OpSelectionMerge %9 None
2048 OpSwitch %3 %10 0 %11 1 %12
2049 %10 = OpLabel
2050 OpBranch %9
2051 %11 = OpLabel
2052 OpBranch %10
2053 %12 = OpLabel
2054 OpBranch %10
2055 %9 = OpLabel
2056 OpReturn
2057 OpFunctionEnd
2058 )";
2059
2060 CompileSuccessfully(text);
2061 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2062 EXPECT_THAT(
2063 getDiagnosticString(),
2064 HasSubstr("Multiple case constructs have branches to the case construct "
2065 "that targets 10[%10]\n %10 = OpLabel"));
2066 }
2067
TEST_F(ValidateCFG,MultipleFallThroughToNonDefault)2068 TEST_F(ValidateCFG, MultipleFallThroughToNonDefault) {
2069 std::string text = R"(
2070 OpCapability Shader
2071 OpCapability Linkage
2072 OpMemoryModel Logical GLSL450
2073 %1 = OpTypeVoid
2074 %2 = OpTypeInt 32 0
2075 %3 = OpConstant %2 0
2076 %4 = OpTypeFunction %1
2077 %5 = OpTypeBool
2078 %6 = OpConstantTrue %5
2079 %7 = OpFunction %1 None %4
2080 %8 = OpLabel
2081 OpSelectionMerge %9 None
2082 OpSwitch %3 %10 0 %11 1 %12
2083 %10 = OpLabel
2084 OpBranch %12
2085 %11 = OpLabel
2086 OpBranch %12
2087 %12 = OpLabel
2088 OpBranch %9
2089 %9 = OpLabel
2090 OpReturn
2091 OpFunctionEnd
2092 )";
2093
2094 CompileSuccessfully(text);
2095 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2096 EXPECT_THAT(
2097 getDiagnosticString(),
2098 HasSubstr("Multiple case constructs have branches to the case construct "
2099 "that targets 12[%12]\n %12 = OpLabel"));
2100 }
2101
TEST_F(ValidateCFG,DuplicateTargetWithFallThrough)2102 TEST_F(ValidateCFG, DuplicateTargetWithFallThrough) {
2103 std::string text = R"(
2104 OpCapability Shader
2105 OpCapability Linkage
2106 OpMemoryModel Logical GLSL450
2107 %1 = OpTypeVoid
2108 %2 = OpTypeInt 32 0
2109 %3 = OpConstant %2 0
2110 %4 = OpTypeFunction %1
2111 %5 = OpTypeBool
2112 %6 = OpConstantTrue %5
2113 %7 = OpFunction %1 None %4
2114 %8 = OpLabel
2115 OpSelectionMerge %9 None
2116 OpSwitch %3 %10 0 %10 1 %11
2117 %10 = OpLabel
2118 OpBranch %11
2119 %11 = OpLabel
2120 OpBranch %9
2121 %9 = OpLabel
2122 OpReturn
2123 OpFunctionEnd
2124 )";
2125
2126 CompileSuccessfully(text);
2127 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
2128 }
2129
TEST_F(ValidateCFG,WrongOperandList)2130 TEST_F(ValidateCFG, WrongOperandList) {
2131 std::string text = R"(
2132 OpCapability Shader
2133 OpCapability Linkage
2134 OpMemoryModel Logical GLSL450
2135 %1 = OpTypeVoid
2136 %2 = OpTypeInt 32 0
2137 %3 = OpConstant %2 0
2138 %4 = OpTypeFunction %1
2139 %5 = OpTypeBool
2140 %6 = OpConstantTrue %5
2141 %7 = OpFunction %1 None %4
2142 %8 = OpLabel
2143 OpSelectionMerge %9 None
2144 OpSwitch %3 %10 0 %11 1 %12
2145 %10 = OpLabel
2146 OpBranch %9
2147 %12 = OpLabel
2148 OpBranch %11
2149 %11 = OpLabel
2150 OpBranch %9
2151 %9 = OpLabel
2152 OpReturn
2153 OpFunctionEnd
2154 )";
2155
2156 CompileSuccessfully(text);
2157 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2158 EXPECT_THAT(
2159 getDiagnosticString(),
2160 HasSubstr("Case construct that targets 12[%12] has branches to the case "
2161 "construct that targets 11[%11], but does not immediately "
2162 "precede it in the OpSwitch's target list\n"
2163 " OpSwitch %uint_0 %10 0 %11 1 %12"));
2164 }
2165
TEST_F(ValidateCFG,WrongOperandListThroughDefault)2166 TEST_F(ValidateCFG, WrongOperandListThroughDefault) {
2167 std::string text = R"(
2168 OpCapability Shader
2169 OpCapability Linkage
2170 OpMemoryModel Logical GLSL450
2171 %1 = OpTypeVoid
2172 %2 = OpTypeInt 32 0
2173 %3 = OpConstant %2 0
2174 %4 = OpTypeFunction %1
2175 %5 = OpTypeBool
2176 %6 = OpConstantTrue %5
2177 %7 = OpFunction %1 None %4
2178 %8 = OpLabel
2179 OpSelectionMerge %9 None
2180 OpSwitch %3 %10 0 %11 1 %12
2181 %10 = OpLabel
2182 OpBranch %11
2183 %12 = OpLabel
2184 OpBranch %10
2185 %11 = OpLabel
2186 OpBranch %9
2187 %9 = OpLabel
2188 OpReturn
2189 OpFunctionEnd
2190 )";
2191
2192 CompileSuccessfully(text);
2193 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2194 EXPECT_THAT(
2195 getDiagnosticString(),
2196 HasSubstr("Case construct that targets 12[%12] has branches to the case "
2197 "construct that targets 11[%11], but does not immediately "
2198 "precede it in the OpSwitch's target list\n"
2199 " OpSwitch %uint_0 %10 0 %11 1 %12"));
2200 }
2201
TEST_F(ValidateCFG,WrongOperandListNotLast)2202 TEST_F(ValidateCFG, WrongOperandListNotLast) {
2203 std::string text = R"(
2204 OpCapability Shader
2205 OpCapability Linkage
2206 OpMemoryModel Logical GLSL450
2207 %1 = OpTypeVoid
2208 %2 = OpTypeInt 32 0
2209 %3 = OpConstant %2 0
2210 %4 = OpTypeFunction %1
2211 %5 = OpTypeBool
2212 %6 = OpConstantTrue %5
2213 %7 = OpFunction %1 None %4
2214 %8 = OpLabel
2215 OpSelectionMerge %9 None
2216 OpSwitch %3 %10 0 %11 1 %12 2 %13
2217 %10 = OpLabel
2218 OpBranch %9
2219 %12 = OpLabel
2220 OpBranch %11
2221 %11 = OpLabel
2222 OpBranch %9
2223 %13 = OpLabel
2224 OpBranch %9
2225 %9 = OpLabel
2226 OpReturn
2227 OpFunctionEnd
2228 )";
2229
2230 CompileSuccessfully(text);
2231 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2232 EXPECT_THAT(
2233 getDiagnosticString(),
2234 HasSubstr("Case construct that targets 12[%12] has branches to the case "
2235 "construct that targets 11[%11], but does not immediately "
2236 "precede it in the OpSwitch's target list\n"
2237 " OpSwitch %uint_0 %10 0 %11 1 %12 2 %13"));
2238 }
2239
TEST_F(ValidateCFG,GoodUnreachableSwitch)2240 TEST_F(ValidateCFG, GoodUnreachableSwitch) {
2241 const std::string text = R"(
2242 OpCapability Shader
2243 OpMemoryModel Logical GLSL450
2244 OpEntryPoint Fragment %2 "main"
2245 OpExecutionMode %2 OriginUpperLeft
2246 %3 = OpTypeVoid
2247 %4 = OpTypeFunction %3
2248 %5 = OpTypeBool
2249 %6 = OpConstantTrue %5
2250 %7 = OpTypeInt 32 1
2251 %9 = OpConstant %7 0
2252 %2 = OpFunction %3 None %4
2253 %10 = OpLabel
2254 OpSelectionMerge %11 None
2255 OpBranchConditional %6 %12 %13
2256 %12 = OpLabel
2257 OpReturn
2258 %13 = OpLabel
2259 OpReturn
2260 %11 = OpLabel
2261 OpSelectionMerge %14 None
2262 OpSwitch %9 %14 0 %15
2263 %15 = OpLabel
2264 OpBranch %14
2265 %14 = OpLabel
2266 OpReturn
2267 OpFunctionEnd
2268 )";
2269
2270 CompileSuccessfully(text);
2271 EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
2272 }
2273
TEST_F(ValidateCFG,InvalidCaseExit)2274 TEST_F(ValidateCFG, InvalidCaseExit) {
2275 const std::string text = R"(
2276 OpCapability Shader
2277 OpMemoryModel Logical GLSL450
2278 OpEntryPoint Fragment %1 "func"
2279 OpExecutionMode %1 OriginUpperLeft
2280 %2 = OpTypeVoid
2281 %3 = OpTypeInt 32 0
2282 %4 = OpTypeFunction %2
2283 %5 = OpConstant %3 0
2284 %1 = OpFunction %2 None %4
2285 %6 = OpLabel
2286 OpSelectionMerge %7 None
2287 OpSwitch %5 %7 0 %8 1 %9
2288 %8 = OpLabel
2289 OpBranch %10
2290 %9 = OpLabel
2291 OpBranch %10
2292 %10 = OpLabel
2293 OpReturn
2294 %7 = OpLabel
2295 OpReturn
2296 OpFunctionEnd
2297 )";
2298
2299 CompileSuccessfully(text);
2300 ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2301 EXPECT_THAT(getDiagnosticString(),
2302 HasSubstr("Case construct that targets 8[%8] has invalid branch "
2303 "to block 10[%10] (not another case construct, "
2304 "corresponding merge, outer loop merge or outer loop "
2305 "continue)"));
2306 }
2307
TEST_F(ValidateCFG,GoodCaseExitsToOuterConstructs)2308 TEST_F(ValidateCFG, GoodCaseExitsToOuterConstructs) {
2309 const std::string text = R"(
2310 OpCapability Shader
2311 OpMemoryModel Logical GLSL450
2312 OpEntryPoint Fragment %func "func"
2313 OpExecutionMode %func OriginUpperLeft
2314 %void = OpTypeVoid
2315 %bool = OpTypeBool
2316 %true = OpConstantTrue %bool
2317 %int = OpTypeInt 32 0
2318 %int0 = OpConstant %int 0
2319 %func_ty = OpTypeFunction %void
2320 %func = OpFunction %void None %func_ty
2321 %1 = OpLabel
2322 OpBranch %2
2323 %2 = OpLabel
2324 OpLoopMerge %7 %6 None
2325 OpBranch %3
2326 %3 = OpLabel
2327 OpSelectionMerge %5 None
2328 OpSwitch %int0 %5 0 %4
2329 %4 = OpLabel
2330 OpBranchConditional %true %6 %7
2331 %5 = OpLabel
2332 OpBranchConditional %true %6 %7
2333 %6 = OpLabel
2334 OpBranch %2
2335 %7 = OpLabel
2336 OpReturn
2337 OpFunctionEnd
2338 )";
2339
2340 CompileSuccessfully(text);
2341 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
2342 }
2343
TEST_F(ValidateCFG,SwitchCaseOrderingBad1)2344 TEST_F(ValidateCFG, SwitchCaseOrderingBad1) {
2345 const std::string text = R"(
2346 OpCapability Shader
2347 OpCapability Linkage
2348 OpMemoryModel Logical GLSL450
2349 OpName %default "default"
2350 OpName %other "other"
2351 %void = OpTypeVoid
2352 %int = OpTypeInt 32 0
2353 %undef = OpUndef %int
2354 %void_fn = OpTypeFunction %void
2355 %func = OpFunction %void None %void_fn
2356 %entry = OpLabel
2357 OpSelectionMerge %merge None
2358 OpSwitch %undef %default 0 %other 1 %default
2359 %default = OpLabel
2360 OpBranch %other
2361 %other = OpLabel
2362 OpBranch %merge
2363 %merge = OpLabel
2364 OpReturn
2365 OpFunctionEnd
2366 )";
2367
2368 CompileSuccessfully(text);
2369 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2370 EXPECT_THAT(
2371 getDiagnosticString(),
2372 HasSubstr("Case construct that targets 1[%default] has branches to the "
2373 "case construct that targets 2[%other], but does not "
2374 "immediately precede it in the OpSwitch's target list"));
2375 }
2376
TEST_F(ValidateCFG,SwitchCaseOrderingBad2)2377 TEST_F(ValidateCFG, SwitchCaseOrderingBad2) {
2378 const std::string text = R"(
2379 OpCapability Shader
2380 OpCapability Linkage
2381 OpMemoryModel Logical GLSL450
2382 OpName %default "default"
2383 OpName %other "other"
2384 %void = OpTypeVoid
2385 %int = OpTypeInt 32 0
2386 %undef = OpUndef %int
2387 %void_fn = OpTypeFunction %void
2388 %func = OpFunction %void None %void_fn
2389 %entry = OpLabel
2390 OpSelectionMerge %merge None
2391 OpSwitch %undef %default 0 %default 1 %other
2392 %other = OpLabel
2393 OpBranch %default
2394 %default = OpLabel
2395 OpBranch %merge
2396 %merge = OpLabel
2397 OpReturn
2398 OpFunctionEnd
2399 )";
2400
2401 CompileSuccessfully(text);
2402 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2403 EXPECT_THAT(
2404 getDiagnosticString(),
2405 HasSubstr("Case construct that targets 2[%other] has branches to the "
2406 "case construct that targets 1[%default], but does not "
2407 "immediately precede it in the OpSwitch's target list"));
2408 }
2409
TEST_F(ValidateCFG,SwitchMultipleDefaultWithFallThroughGood)2410 TEST_F(ValidateCFG, SwitchMultipleDefaultWithFallThroughGood) {
2411 const std::string text = R"(
2412 OpCapability Shader
2413 OpCapability Linkage
2414 OpMemoryModel Logical GLSL450
2415 OpName %first "first"
2416 OpName %second "second"
2417 OpName %third "third"
2418 %void = OpTypeVoid
2419 %int = OpTypeInt 32 0
2420 %undef = OpUndef %int
2421 %void_fn = OpTypeFunction %void
2422 %func = OpFunction %void None %void_fn
2423 %entry = OpLabel
2424 OpSelectionMerge %merge None
2425 OpSwitch %undef %second 0 %first 1 %second 2 %third
2426 %first = OpLabel
2427 OpBranch %second
2428 %second = OpLabel
2429 OpBranch %third
2430 %third = OpLabel
2431 OpBranch %merge
2432 %merge = OpLabel
2433 OpReturn
2434 OpFunctionEnd
2435 )";
2436
2437 CompileSuccessfully(text);
2438 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2439 }
2440
TEST_F(ValidateCFG,SwitchMultipleDefaultWithFallThroughBad)2441 TEST_F(ValidateCFG, SwitchMultipleDefaultWithFallThroughBad) {
2442 const std::string text = R"(
2443 OpCapability Shader
2444 OpCapability Linkage
2445 OpMemoryModel Logical GLSL450
2446 OpName %first "first"
2447 OpName %second "second"
2448 OpName %third "third"
2449 %void = OpTypeVoid
2450 %int = OpTypeInt 32 0
2451 %undef = OpUndef %int
2452 %void_fn = OpTypeFunction %void
2453 %func = OpFunction %void None %void_fn
2454 %entry = OpLabel
2455 OpSelectionMerge %merge None
2456 OpSwitch %undef %second 0 %second 1 %first 2 %third
2457 %first = OpLabel
2458 OpBranch %second
2459 %second = OpLabel
2460 OpBranch %third
2461 %third = OpLabel
2462 OpBranch %merge
2463 %merge = OpLabel
2464 OpReturn
2465 OpFunctionEnd
2466 )";
2467
2468 CompileSuccessfully(text);
2469 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2470 }
2471
TEST_F(ValidateCFG,GoodUnreachableSelection)2472 TEST_F(ValidateCFG, GoodUnreachableSelection) {
2473 const std::string text = R"(
2474 OpCapability Shader
2475 %1 = OpExtInstImport "GLSL.std.450"
2476 OpMemoryModel Logical GLSL450
2477 OpEntryPoint Fragment %main "main"
2478 OpExecutionMode %main OriginUpperLeft
2479 %void = OpTypeVoid
2480 %8 = OpTypeFunction %void
2481 %bool = OpTypeBool
2482 %false = OpConstantFalse %bool
2483 %main = OpFunction %void None %8
2484 %15 = OpLabel
2485 OpBranch %16
2486 %16 = OpLabel
2487 OpLoopMerge %17 %18 None
2488 OpBranch %19
2489 %19 = OpLabel
2490 OpBranchConditional %false %21 %17
2491 %21 = OpLabel
2492 OpSelectionMerge %22 None
2493 OpBranchConditional %false %23 %22
2494 %23 = OpLabel
2495 OpBranch %24
2496 %24 = OpLabel
2497 OpLoopMerge %25 %26 None
2498 OpBranch %27
2499 %27 = OpLabel
2500 OpReturn
2501 %26 = OpLabel
2502 OpBranchConditional %false %24 %25
2503 %25 = OpLabel
2504 OpSelectionMerge %28 None
2505 OpBranchConditional %false %18 %28
2506 %28 = OpLabel
2507 OpBranch %22
2508 %22 = OpLabel
2509 OpBranch %18
2510 %18 = OpLabel
2511 OpBranch %16
2512 %17 = OpLabel
2513 OpReturn
2514 OpFunctionEnd
2515 )";
2516
2517 CompileSuccessfully(text);
2518 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
2519 }
2520
TEST_F(ValidateCFG,ShaderWithPhiPtr)2521 TEST_F(ValidateCFG, ShaderWithPhiPtr) {
2522 const std::string text = R"(
2523 OpCapability Shader
2524 OpMemoryModel Logical GLSL450
2525 OpEntryPoint GLCompute %1 "main"
2526 OpExecutionMode %1 LocalSize 1 1 1
2527 OpSource HLSL 600
2528 %bool = OpTypeBool
2529 %_ptr_Function_bool = OpTypePointer Function %bool
2530 %void = OpTypeVoid
2531 %5 = OpTypeFunction %void
2532 %1 = OpFunction %void None %5
2533 %6 = OpLabel
2534 %7 = OpVariable %_ptr_Function_bool Function
2535 %8 = OpVariable %_ptr_Function_bool Function
2536 %9 = OpUndef %bool
2537 OpSelectionMerge %10 None
2538 OpBranchConditional %9 %11 %10
2539 %11 = OpLabel
2540 OpBranch %10
2541 %10 = OpLabel
2542 %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11
2543 OpReturn
2544 OpFunctionEnd
2545 )";
2546
2547 CompileSuccessfully(text);
2548 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
2549 EXPECT_THAT(getDiagnosticString(),
2550 HasSubstr("Using pointers with OpPhi requires capability "
2551 "VariablePointers or VariablePointersStorageBuffer"));
2552 }
2553
TEST_F(ValidateCFG,VarPtrShaderWithPhiPtr)2554 TEST_F(ValidateCFG, VarPtrShaderWithPhiPtr) {
2555 const std::string text = R"(
2556 OpCapability Shader
2557 OpCapability VariablePointers
2558 OpExtension "SPV_KHR_variable_pointers"
2559 OpMemoryModel Logical GLSL450
2560 OpEntryPoint GLCompute %1 "main"
2561 OpExecutionMode %1 LocalSize 1 1 1
2562 OpSource HLSL 600
2563 %bool = OpTypeBool
2564 %_ptr_Function_bool = OpTypePointer Function %bool
2565 %void = OpTypeVoid
2566 %5 = OpTypeFunction %void
2567 %1 = OpFunction %void None %5
2568 %6 = OpLabel
2569 %7 = OpVariable %_ptr_Function_bool Function
2570 %8 = OpVariable %_ptr_Function_bool Function
2571 %9 = OpUndef %bool
2572 OpSelectionMerge %10 None
2573 OpBranchConditional %9 %11 %10
2574 %11 = OpLabel
2575 OpBranch %10
2576 %10 = OpLabel
2577 %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11
2578 OpReturn
2579 OpFunctionEnd
2580 )";
2581
2582 CompileSuccessfully(text);
2583 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2584 }
2585
TEST_F(ValidateCFG,VarPtrStgBufShaderWithPhiStgBufPtr)2586 TEST_F(ValidateCFG, VarPtrStgBufShaderWithPhiStgBufPtr) {
2587 const std::string text = R"(
2588 OpCapability Shader
2589 OpCapability VariablePointersStorageBuffer
2590 OpExtension "SPV_KHR_variable_pointers"
2591 OpMemoryModel Logical GLSL450
2592 OpEntryPoint GLCompute %1 "main"
2593 OpExecutionMode %1 LocalSize 1 1 1
2594 OpSource HLSL 600
2595 %bool = OpTypeBool
2596 %float = OpTypeFloat 32
2597 %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
2598 %7 = OpVariable %_ptr_StorageBuffer_float StorageBuffer
2599 %8 = OpVariable %_ptr_StorageBuffer_float StorageBuffer
2600 %void = OpTypeVoid
2601 %5 = OpTypeFunction %void
2602 %1 = OpFunction %void None %5
2603 %6 = OpLabel
2604 %9 = OpUndef %bool
2605 OpSelectionMerge %10 None
2606 OpBranchConditional %9 %11 %10
2607 %11 = OpLabel
2608 OpBranch %10
2609 %10 = OpLabel
2610 %12 = OpPhi %_ptr_StorageBuffer_float %7 %6 %8 %11
2611 OpReturn
2612 OpFunctionEnd
2613 )";
2614
2615 CompileSuccessfully(text);
2616 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2617 }
2618
TEST_F(ValidateCFG,KernelWithPhiPtr)2619 TEST_F(ValidateCFG, KernelWithPhiPtr) {
2620 const std::string text = R"(
2621 OpCapability Kernel
2622 OpCapability Addresses
2623 OpMemoryModel Physical32 OpenCL
2624 OpEntryPoint Kernel %1 "main"
2625 OpExecutionMode %1 LocalSize 1 1 1
2626 OpSource HLSL 600
2627 %bool = OpTypeBool
2628 %_ptr_Function_bool = OpTypePointer Function %bool
2629 %void = OpTypeVoid
2630 %5 = OpTypeFunction %void
2631 %1 = OpFunction %void None %5
2632 %6 = OpLabel
2633 %7 = OpVariable %_ptr_Function_bool Function
2634 %8 = OpVariable %_ptr_Function_bool Function
2635 %9 = OpUndef %bool
2636 OpSelectionMerge %10 None
2637 OpBranchConditional %9 %11 %10
2638 %11 = OpLabel
2639 OpBranch %10
2640 %10 = OpLabel
2641 %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11
2642 OpReturn
2643 OpFunctionEnd
2644 )";
2645
2646 CompileSuccessfully(text);
2647 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2648 }
2649
TEST_F(ValidateCFG,SwitchTargetMustBeLabel)2650 TEST_F(ValidateCFG, SwitchTargetMustBeLabel) {
2651 const std::string text = R"(
2652 OpCapability Shader
2653 OpMemoryModel Logical GLSL450
2654 OpEntryPoint GLCompute %1 "foo"
2655 %uint = OpTypeInt 32 0
2656 %uint_0 = OpConstant %uint 0
2657 %void = OpTypeVoid
2658 %5 = OpTypeFunction %void
2659 %1 = OpFunction %void None %5
2660 %6 = OpLabel
2661 %7 = OpCopyObject %uint %uint_0
2662 OpSelectionMerge %8 None
2663 OpSwitch %uint_0 %8 0 %7
2664 %8 = OpLabel
2665 OpReturn
2666 OpFunctionEnd
2667 )";
2668
2669 CompileSuccessfully(text);
2670 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
2671 EXPECT_THAT(getDiagnosticString(),
2672 HasSubstr("'Target Label' operands for OpSwitch must "
2673 "be IDs of an OpLabel instruction"));
2674 }
2675
TEST_F(ValidateCFG,BranchTargetMustBeLabel)2676 TEST_F(ValidateCFG, BranchTargetMustBeLabel) {
2677 const std::string text = R"(
2678 OpCapability Shader
2679 OpMemoryModel Logical GLSL450
2680 OpEntryPoint GLCompute %1 "foo"
2681 %uint = OpTypeInt 32 0
2682 %uint_0 = OpConstant %uint 0
2683 %void = OpTypeVoid
2684 %5 = OpTypeFunction %void
2685 %1 = OpFunction %void None %5
2686 %2 = OpLabel
2687 %7 = OpCopyObject %uint %uint_0
2688 OpBranch %7
2689 %8 = OpLabel
2690 OpReturn
2691 OpFunctionEnd
2692 )";
2693
2694 CompileSuccessfully(text);
2695 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
2696 EXPECT_THAT(getDiagnosticString(),
2697 HasSubstr("'Target Label' operands for OpBranch must "
2698 "be the ID of an OpLabel instruction"));
2699 }
2700
TEST_F(ValidateCFG,ReachableOpUnreachableOneBlock)2701 TEST_F(ValidateCFG, ReachableOpUnreachableOneBlock) {
2702 const std::string text = R"(
2703 OpCapability Shader
2704 OpCapability Linkage
2705 OpMemoryModel Logical GLSL450
2706 %void = OpTypeVoid
2707 %void_fn = OpTypeFunction %void
2708 %func = OpFunction %void None %void_fn
2709 %entry = OpLabel
2710 OpUnreachable
2711 OpFunctionEnd
2712 )";
2713
2714 CompileSuccessfully(text);
2715 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2716 }
2717
TEST_F(ValidateCFG,ReachableOpUnreachableOpBranch)2718 TEST_F(ValidateCFG, ReachableOpUnreachableOpBranch) {
2719 const std::string text = R"(
2720 OpCapability Shader
2721 OpCapability Linkage
2722 OpMemoryModel Logical GLSL450
2723 %void = OpTypeVoid
2724 %void_fn = OpTypeFunction %void
2725 %func = OpFunction %void None %void_fn
2726 %entry = OpLabel
2727 OpBranch %block
2728 %block = OpLabel
2729 OpUnreachable
2730 OpFunctionEnd
2731 )";
2732
2733 CompileSuccessfully(text);
2734 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2735 }
2736
TEST_F(ValidateCFG,ReachableOpUnreachableOpBranchConditional)2737 TEST_F(ValidateCFG, ReachableOpUnreachableOpBranchConditional) {
2738 const std::string text = R"(
2739 OpCapability Shader
2740 OpCapability Linkage
2741 OpMemoryModel Logical GLSL450
2742 %void = OpTypeVoid
2743 %void_fn = OpTypeFunction %void
2744 %bool = OpTypeBool
2745 %undef = OpUndef %bool
2746 %func = OpFunction %void None %void_fn
2747 %entry = OpLabel
2748 OpSelectionMerge %block None
2749 OpBranchConditional %undef %block %unreachable
2750 %block = OpLabel
2751 OpReturn
2752 %unreachable = OpLabel
2753 OpUnreachable
2754 OpFunctionEnd
2755 )";
2756
2757 CompileSuccessfully(text);
2758 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2759 }
2760
TEST_F(ValidateCFG,ReachableOpUnreachableOpSwitch)2761 TEST_F(ValidateCFG, ReachableOpUnreachableOpSwitch) {
2762 const std::string text = R"(
2763 OpCapability Shader
2764 OpCapability Linkage
2765 OpMemoryModel Logical GLSL450
2766 %void = OpTypeVoid
2767 %void_fn = OpTypeFunction %void
2768 %int = OpTypeInt 32 0
2769 %undef = OpUndef %int
2770 %func = OpFunction %void None %void_fn
2771 %entry = OpLabel
2772 OpSelectionMerge %block1 None
2773 OpSwitch %undef %block1 0 %unreachable 1 %block2
2774 %block1 = OpLabel
2775 OpReturn
2776 %unreachable = OpLabel
2777 OpUnreachable
2778 %block2 = OpLabel
2779 OpReturn
2780 OpFunctionEnd
2781 )";
2782
2783 CompileSuccessfully(text);
2784 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2785 }
2786
TEST_F(ValidateCFG,ReachableOpUnreachableLoop)2787 TEST_F(ValidateCFG, ReachableOpUnreachableLoop) {
2788 const std::string text = R"(
2789 OpCapability Shader
2790 OpCapability Linkage
2791 OpMemoryModel Logical GLSL450
2792 %void = OpTypeVoid
2793 %void_fn = OpTypeFunction %void
2794 %bool = OpTypeBool
2795 %undef = OpUndef %bool
2796 %func = OpFunction %void None %void_fn
2797 %entry = OpLabel
2798 OpBranch %loop
2799 %loop = OpLabel
2800 OpLoopMerge %unreachable %loop None
2801 OpBranchConditional %undef %loop %unreachable
2802 %unreachable = OpLabel
2803 OpUnreachable
2804 OpFunctionEnd
2805 )";
2806
2807 CompileSuccessfully(text);
2808 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2809 }
2810
TEST_F(ValidateCFG,UnreachableLoopBadBackedge)2811 TEST_F(ValidateCFG, UnreachableLoopBadBackedge) {
2812 const std::string text = R"(
2813 OpCapability Shader
2814 OpMemoryModel Logical GLSL450
2815 OpEntryPoint Fragment %2 "main"
2816 OpExecutionMode %2 OriginUpperLeft
2817 %4 = OpTypeVoid
2818 %5 = OpTypeFunction %4
2819 %8 = OpTypeBool
2820 %13 = OpConstantTrue %8
2821 %2 = OpFunction %4 None %5
2822 %14 = OpLabel
2823 OpSelectionMerge %15 None
2824 OpBranchConditional %13 %15 %15
2825 %16 = OpLabel
2826 OpLoopMerge %17 %18 None
2827 OpBranch %17
2828 %18 = OpLabel
2829 OpBranch %17
2830 %17 = OpLabel
2831 OpBranch %15
2832 %15 = OpLabel
2833 OpReturn
2834 OpFunctionEnd
2835 )";
2836
2837 // The back-edge in this test is bad, but the validator fails to identify it
2838 // because it is in an entirely unreachable section of code. Prior to #2488
2839 // this code failed an assert in Construct::blocks().
2840 CompileSuccessfully(text);
2841 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
2842 }
2843
TEST_F(ValidateCFG,OneContinueTwoBackedges)2844 TEST_F(ValidateCFG, OneContinueTwoBackedges) {
2845 const std::string text = R"(
2846 OpCapability Shader
2847 OpMemoryModel Logical GLSL450
2848 OpEntryPoint GLCompute %1 "main"
2849 OpExecutionMode %1 LocalSize 1 1 1
2850 %void = OpTypeVoid
2851 %bool = OpTypeBool
2852 %true = OpConstantTrue %bool
2853 %5 = OpTypeFunction %void
2854 %1 = OpFunction %void None %5
2855 %6 = OpLabel
2856 OpBranch %7
2857 %7 = OpLabel
2858 OpLoopMerge %8 %9 None
2859 OpBranch %10
2860 %10 = OpLabel
2861 OpLoopMerge %11 %9 None
2862 OpBranchConditional %true %11 %9
2863 %9 = OpLabel
2864 OpBranchConditional %true %10 %7
2865 %11 = OpLabel
2866 OpBranch %8
2867 %8 = OpLabel
2868 OpReturn
2869 OpFunctionEnd
2870 )";
2871
2872 CompileSuccessfully(text);
2873 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
2874 EXPECT_THAT(getDiagnosticString(),
2875 HasSubstr("block <ID> 9 branches to the loop construct, but not "
2876 "to the loop header <ID> 7"));
2877 }
2878
TEST_F(ValidateCFG,LoopMergeMergeBlockNotLabel)2879 TEST_F(ValidateCFG, LoopMergeMergeBlockNotLabel) {
2880 const std::string text = R"(
2881 OpCapability Shader
2882 OpCapability Linkage
2883 OpMemoryModel Logical GLSL450
2884 OpName %undef "undef"
2885 %void = OpTypeVoid
2886 %bool = OpTypeBool
2887 %undef = OpUndef %bool
2888 %void_fn = OpTypeFunction %void
2889 %func = OpFunction %void None %void_fn
2890 %1 = OpLabel
2891 OpLoopMerge %undef %2 None
2892 OpBranchConditional %undef %2 %2
2893 %2 = OpLabel
2894 OpReturn
2895 OpFunctionEnd
2896 )";
2897
2898 CompileSuccessfully(text);
2899 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
2900 EXPECT_THAT(getDiagnosticString(),
2901 HasSubstr("Merge Block 1[%undef] must be an OpLabel"));
2902 }
2903
TEST_F(ValidateCFG,LoopMergeContinueTargetNotLabel)2904 TEST_F(ValidateCFG, LoopMergeContinueTargetNotLabel) {
2905 const std::string text = R"(
2906 OpCapability Shader
2907 OpCapability Linkage
2908 OpMemoryModel Logical GLSL450
2909 OpName %undef "undef"
2910 %void = OpTypeVoid
2911 %bool = OpTypeBool
2912 %undef = OpUndef %bool
2913 %void_fn = OpTypeFunction %void
2914 %func = OpFunction %void None %void_fn
2915 %1 = OpLabel
2916 OpLoopMerge %2 %undef None
2917 OpBranchConditional %undef %2 %2
2918 %2 = OpLabel
2919 OpReturn
2920 OpFunctionEnd
2921 )";
2922
2923 CompileSuccessfully(text);
2924 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
2925 EXPECT_THAT(getDiagnosticString(),
2926 HasSubstr("Continue Target 1[%undef] must be an OpLabel"));
2927 }
2928
TEST_F(ValidateCFG,LoopMergeMergeBlockContinueTargetSameLabel)2929 TEST_F(ValidateCFG, LoopMergeMergeBlockContinueTargetSameLabel) {
2930 const std::string text = R"(
2931 OpCapability Shader
2932 OpCapability Linkage
2933 OpMemoryModel Logical GLSL450
2934 OpName %undef "undef"
2935 %void = OpTypeVoid
2936 %bool = OpTypeBool
2937 %undef = OpUndef %bool
2938 %void_fn = OpTypeFunction %void
2939 %func = OpFunction %void None %void_fn
2940 %1 = OpLabel
2941 OpLoopMerge %2 %2 None
2942 OpBranchConditional %undef %2 %2
2943 %2 = OpLabel
2944 OpReturn
2945 OpFunctionEnd
2946 )";
2947
2948 CompileSuccessfully(text);
2949 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
2950 EXPECT_THAT(
2951 getDiagnosticString(),
2952 HasSubstr("Merge Block and Continue Target must be different ids"));
2953 }
2954
TEST_F(ValidateCFG,LoopMergeUnrollAndDontUnroll)2955 TEST_F(ValidateCFG, LoopMergeUnrollAndDontUnroll) {
2956 const std::string text = R"(
2957 OpCapability Shader
2958 OpCapability Linkage
2959 OpMemoryModel Logical GLSL450
2960 OpName %undef "undef"
2961 %void = OpTypeVoid
2962 %bool = OpTypeBool
2963 %undef = OpUndef %bool
2964 %void_fn = OpTypeFunction %void
2965 %func = OpFunction %void None %void_fn
2966 %5 = OpLabel
2967 OpBranch %1
2968 %1 = OpLabel
2969 OpLoopMerge %2 %3 Unroll|DontUnroll
2970 OpBranchConditional %undef %2 %3
2971 %3 = OpLabel
2972 OpBranch %1
2973 %2 = OpLabel
2974 OpReturn
2975 OpFunctionEnd
2976 )";
2977
2978 CompileSuccessfully(text);
2979 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
2980 EXPECT_THAT(
2981 getDiagnosticString(),
2982 HasSubstr(
2983 "Unroll and DontUnroll loop controls must not both be specified"));
2984 }
2985
TEST_F(ValidateCFG,LoopMergePeelCountAndDontUnroll)2986 TEST_F(ValidateCFG, LoopMergePeelCountAndDontUnroll) {
2987 const std::string text = R"(
2988 OpCapability Shader
2989 OpCapability Linkage
2990 OpMemoryModel Logical GLSL450
2991 OpName %undef "undef"
2992 %void = OpTypeVoid
2993 %bool = OpTypeBool
2994 %undef = OpUndef %bool
2995 %void_fn = OpTypeFunction %void
2996 %func = OpFunction %void None %void_fn
2997 %5 = OpLabel
2998 OpBranch %1
2999 %1 = OpLabel
3000 OpLoopMerge %2 %3 DontUnroll|PeelCount 1
3001 OpBranchConditional %undef %2 %3
3002 %3 = OpLabel
3003 OpBranch %1
3004 %2 = OpLabel
3005 OpReturn
3006 OpFunctionEnd
3007 )";
3008
3009 CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
3010 EXPECT_EQ(SPV_ERROR_INVALID_DATA,
3011 ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
3012 EXPECT_THAT(
3013 getDiagnosticString(),
3014 HasSubstr(
3015 "PeelCount and DontUnroll loop controls must not both be specified"));
3016 }
3017
TEST_F(ValidateCFG,LoopMergePartialCountAndDontUnroll)3018 TEST_F(ValidateCFG, LoopMergePartialCountAndDontUnroll) {
3019 const std::string text = R"(
3020 OpCapability Shader
3021 OpCapability Linkage
3022 OpMemoryModel Logical GLSL450
3023 OpName %undef "undef"
3024 %void = OpTypeVoid
3025 %bool = OpTypeBool
3026 %undef = OpUndef %bool
3027 %void_fn = OpTypeFunction %void
3028 %func = OpFunction %void None %void_fn
3029 %5 = OpLabel
3030 OpBranch %1
3031 %1 = OpLabel
3032 OpLoopMerge %2 %3 DontUnroll|PartialCount 1
3033 OpBranchConditional %undef %2 %3
3034 %3 = OpLabel
3035 OpBranch %1
3036 %2 = OpLabel
3037 OpReturn
3038 OpFunctionEnd
3039 )";
3040
3041 CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
3042 EXPECT_EQ(SPV_ERROR_INVALID_DATA,
3043 ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
3044 EXPECT_THAT(getDiagnosticString(),
3045 HasSubstr("PartialCount and DontUnroll loop controls must not "
3046 "both be specified"));
3047 }
3048
TEST_F(ValidateCFG,LoopMergeIterationMultipleZero)3049 TEST_F(ValidateCFG, LoopMergeIterationMultipleZero) {
3050 const std::string text = R"(
3051 OpCapability Shader
3052 OpCapability Linkage
3053 OpMemoryModel Logical GLSL450
3054 OpName %undef "undef"
3055 %void = OpTypeVoid
3056 %bool = OpTypeBool
3057 %undef = OpUndef %bool
3058 %void_fn = OpTypeFunction %void
3059 %func = OpFunction %void None %void_fn
3060 %5 = OpLabel
3061 OpBranch %1
3062 %1 = OpLabel
3063 OpLoopMerge %2 %3 IterationMultiple 0
3064 OpBranchConditional %undef %2 %3
3065 %3 = OpLabel
3066 OpBranch %1
3067 %2 = OpLabel
3068 OpReturn
3069 OpFunctionEnd
3070 )";
3071
3072 CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
3073 EXPECT_EQ(SPV_ERROR_INVALID_DATA,
3074 ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
3075 EXPECT_THAT(
3076 getDiagnosticString(),
3077 HasSubstr(
3078 "IterationMultiple loop control operand must be greater than zero"));
3079 }
3080
TEST_F(ValidateCFG,LoopMergeIterationMultipleZeroMoreOperands)3081 TEST_F(ValidateCFG, LoopMergeIterationMultipleZeroMoreOperands) {
3082 const std::string text = R"(
3083 OpCapability Shader
3084 OpCapability Linkage
3085 OpMemoryModel Logical GLSL450
3086 OpName %undef "undef"
3087 %void = OpTypeVoid
3088 %bool = OpTypeBool
3089 %undef = OpUndef %bool
3090 %void_fn = OpTypeFunction %void
3091 %func = OpFunction %void None %void_fn
3092 %5 = OpLabel
3093 OpBranch %1
3094 %1 = OpLabel
3095 OpLoopMerge %2 %3 MaxIterations|IterationMultiple 4 0
3096 OpBranchConditional %undef %2 %3
3097 %3 = OpLabel
3098 OpBranch %1
3099 %2 = OpLabel
3100 OpReturn
3101 OpFunctionEnd
3102 )";
3103
3104 CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
3105 EXPECT_EQ(SPV_ERROR_INVALID_DATA,
3106 ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
3107 EXPECT_THAT(
3108 getDiagnosticString(),
3109 HasSubstr(
3110 "IterationMultiple loop control operand must be greater than zero"));
3111 }
3112
TEST_F(ValidateCFG,LoopMergeTargetsHeader)3113 TEST_F(ValidateCFG, LoopMergeTargetsHeader) {
3114 const std::string text = R"(
3115 OpCapability Shader
3116 OpCapability Linkage
3117 OpMemoryModel Logical GLSL450
3118 %void = OpTypeVoid
3119 %bool = OpTypeBool
3120 %undef = OpUndef %bool
3121 %void_fn = OpTypeFunction %void
3122 %fn = OpFunction %void None %void_fn
3123 %entry = OpLabel
3124 OpBranch %loop
3125 %loop = OpLabel
3126 OpLoopMerge %loop %continue None
3127 OpBranch %body
3128 %continue = OpLabel
3129 OpBranch %loop
3130 %body = OpLabel
3131 OpReturn
3132 OpFunctionEnd
3133 )";
3134
3135 CompileSuccessfully(text);
3136 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
3137 EXPECT_THAT(
3138 getDiagnosticString(),
3139 HasSubstr("Merge Block may not be the block containing the OpLoopMerge"));
3140 }
3141
TEST_F(ValidateCFG,InvalidSelectionExit)3142 TEST_F(ValidateCFG, InvalidSelectionExit) {
3143 const std::string text = R"(
3144 OpCapability Shader
3145 OpMemoryModel Logical GLSL450
3146 OpEntryPoint Fragment %1 "main"
3147 OpExecutionMode %1 OriginUpperLeft
3148 %2 = OpTypeVoid
3149 %3 = OpTypeBool
3150 %4 = OpConstantTrue %3
3151 %5 = OpTypeFunction %2
3152 %1 = OpFunction %2 None %5
3153 %6 = OpLabel
3154 OpSelectionMerge %7 None
3155 OpBranchConditional %4 %7 %8
3156 %8 = OpLabel
3157 OpSelectionMerge %9 None
3158 OpBranchConditional %4 %10 %9
3159 %10 = OpLabel
3160 OpBranch %7
3161 %9 = OpLabel
3162 OpBranch %7
3163 %7 = OpLabel
3164 OpReturn
3165 OpFunctionEnd
3166 )";
3167
3168 CompileSuccessfully(text);
3169 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3170 EXPECT_THAT(getDiagnosticString(),
3171 HasSubstr("block <ID> 10[%10] exits the selection headed by <ID> "
3172 "8[%8], but not via a structured exit"));
3173 }
3174
TEST_F(ValidateCFG,InvalidLoopExit)3175 TEST_F(ValidateCFG, InvalidLoopExit) {
3176 const std::string text = R"(
3177 OpCapability Shader
3178 OpMemoryModel Logical GLSL450
3179 OpEntryPoint Fragment %1 "main"
3180 OpExecutionMode %1 OriginUpperLeft
3181 %2 = OpTypeVoid
3182 %3 = OpTypeBool
3183 %4 = OpConstantTrue %3
3184 %5 = OpTypeFunction %2
3185 %1 = OpFunction %2 None %5
3186 %6 = OpLabel
3187 OpSelectionMerge %7 None
3188 OpBranchConditional %4 %7 %8
3189 %8 = OpLabel
3190 OpLoopMerge %9 %10 None
3191 OpBranchConditional %4 %9 %11
3192 %11 = OpLabel
3193 OpBranchConditional %4 %7 %10
3194 %10 = OpLabel
3195 OpBranch %8
3196 %9 = OpLabel
3197 OpBranch %7
3198 %7 = OpLabel
3199 OpReturn
3200 OpFunctionEnd
3201 )";
3202
3203 CompileSuccessfully(text);
3204 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3205 EXPECT_THAT(getDiagnosticString(),
3206 HasSubstr("block <ID> 11[%11] exits the loop headed by <ID> "
3207 "8[%8], but not via a structured exit"));
3208 }
3209
TEST_F(ValidateCFG,InvalidContinueExit)3210 TEST_F(ValidateCFG, InvalidContinueExit) {
3211 const std::string text = R"(
3212 OpCapability Shader
3213 OpMemoryModel Logical GLSL450
3214 OpEntryPoint Fragment %1 "main"
3215 OpExecutionMode %1 OriginUpperLeft
3216 %2 = OpTypeVoid
3217 %3 = OpTypeBool
3218 %4 = OpConstantTrue %3
3219 %5 = OpTypeFunction %2
3220 %1 = OpFunction %2 None %5
3221 %6 = OpLabel
3222 OpSelectionMerge %7 None
3223 OpBranchConditional %4 %7 %8
3224 %8 = OpLabel
3225 OpLoopMerge %9 %10 None
3226 OpBranchConditional %4 %9 %10
3227 %10 = OpLabel
3228 OpBranch %11
3229 %11 = OpLabel
3230 OpBranchConditional %4 %8 %7
3231 %9 = OpLabel
3232 OpBranch %7
3233 %7 = OpLabel
3234 OpReturn
3235 OpFunctionEnd
3236 )";
3237
3238 CompileSuccessfully(text);
3239 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3240 EXPECT_THAT(getDiagnosticString(),
3241 HasSubstr("block <ID> 11[%11] exits the continue headed by <ID> "
3242 "10[%10], but not via a structured exit"));
3243 }
3244
TEST_F(ValidateCFG,InvalidSelectionExitBackedge)3245 TEST_F(ValidateCFG, InvalidSelectionExitBackedge) {
3246 const std::string text = R"(
3247 OpCapability Shader
3248 OpCapability Linkage
3249 OpMemoryModel Logical GLSL450
3250 %1 = OpTypeVoid
3251 %2 = OpTypeBool
3252 %3 = OpUndef %2
3253 %4 = OpTypeFunction %1
3254 %5 = OpFunction %1 None %4
3255 %6 = OpLabel
3256 OpBranch %7
3257 %7 = OpLabel
3258 OpLoopMerge %8 %9 None
3259 OpBranchConditional %3 %8 %9
3260 %9 = OpLabel
3261 OpSelectionMerge %10 None
3262 OpBranchConditional %3 %11 %12
3263 %11 = OpLabel
3264 OpBranch %13
3265 %12 = OpLabel
3266 OpBranch %13
3267 %13 = OpLabel
3268 OpBranch %7
3269 %10 = OpLabel
3270 OpUnreachable
3271 %8 = OpLabel
3272 OpReturn
3273 OpFunctionEnd
3274 )";
3275
3276 CompileSuccessfully(text);
3277 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3278 EXPECT_THAT(getDiagnosticString(),
3279 HasSubstr("block <ID> 13[%13] exits the selection headed by <ID> "
3280 "9[%9], but not via a structured exit"));
3281 }
3282
TEST_F(ValidateCFG,BreakFromSwitch)3283 TEST_F(ValidateCFG, BreakFromSwitch) {
3284 const std::string text = R"(
3285 OpCapability Shader
3286 OpCapability Linkage
3287 OpMemoryModel Logical GLSL450
3288 %1 = OpTypeVoid
3289 %2 = OpTypeBool
3290 %3 = OpTypeInt 32 0
3291 %4 = OpUndef %2
3292 %5 = OpUndef %3
3293 %6 = OpTypeFunction %1
3294 %7 = OpFunction %1 None %6
3295 %8 = OpLabel
3296 OpSelectionMerge %9 None
3297 OpSwitch %5 %9 0 %10
3298 %10 = OpLabel
3299 OpSelectionMerge %11 None
3300 OpBranchConditional %4 %11 %12
3301 %12 = OpLabel
3302 OpBranch %9
3303 %11 = OpLabel
3304 OpBranch %9
3305 %9 = OpLabel
3306 OpReturn
3307 OpFunctionEnd
3308 )";
3309
3310 CompileSuccessfully(text);
3311 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3312 }
3313
TEST_F(ValidateCFG,InvalidBreakFromSwitch)3314 TEST_F(ValidateCFG, InvalidBreakFromSwitch) {
3315 const std::string text = R"(
3316 OpCapability Shader
3317 OpCapability Linkage
3318 OpMemoryModel Logical GLSL450
3319 %1 = OpTypeVoid
3320 %2 = OpTypeBool
3321 %3 = OpTypeInt 32 0
3322 %4 = OpUndef %2
3323 %5 = OpUndef %3
3324 %6 = OpTypeFunction %1
3325 %7 = OpFunction %1 None %6
3326 %8 = OpLabel
3327 OpSelectionMerge %9 None
3328 OpSwitch %5 %9 0 %10
3329 %10 = OpLabel
3330 OpSelectionMerge %11 None
3331 OpSwitch %5 %11 0 %12
3332 %12 = OpLabel
3333 OpBranch %9
3334 %11 = OpLabel
3335 OpBranch %9
3336 %9 = OpLabel
3337 OpReturn
3338 OpFunctionEnd
3339 )";
3340
3341 CompileSuccessfully(text);
3342 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3343 EXPECT_THAT(getDiagnosticString(),
3344 HasSubstr("block <ID> 12[%12] exits the selection headed by <ID> "
3345 "10[%10], but not via a structured exit"));
3346 }
3347
TEST_F(ValidateCFG,BreakToOuterSwitch)3348 TEST_F(ValidateCFG, BreakToOuterSwitch) {
3349 const std::string text = R"(
3350 OpCapability Shader
3351 OpCapability Linkage
3352 OpMemoryModel Logical GLSL450
3353 %1 = OpTypeVoid
3354 %2 = OpTypeBool
3355 %3 = OpTypeInt 32 0
3356 %4 = OpUndef %2
3357 %5 = OpUndef %3
3358 %6 = OpTypeFunction %1
3359 %7 = OpFunction %1 None %6
3360 %8 = OpLabel
3361 OpSelectionMerge %9 None
3362 OpSwitch %5 %9 0 %10
3363 %10 = OpLabel
3364 OpSelectionMerge %11 None
3365 OpSwitch %5 %11 0 %12
3366 %12 = OpLabel
3367 OpSelectionMerge %13 None
3368 OpBranchConditional %4 %13 %14
3369 %14 = OpLabel
3370 OpBranch %9
3371 %13 = OpLabel
3372 OpBranch %11
3373 %11 = OpLabel
3374 OpBranch %9
3375 %9 = OpLabel
3376 OpReturn
3377 OpFunctionEnd
3378 )";
3379
3380 CompileSuccessfully(text);
3381 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3382 EXPECT_THAT(getDiagnosticString(),
3383 HasSubstr("block <ID> 14[%14] exits the selection headed by <ID> "
3384 "10[%10], but not via a structured exit"));
3385 }
3386
TEST_F(ValidateCFG,BreakToOuterLoop)3387 TEST_F(ValidateCFG, BreakToOuterLoop) {
3388 const std::string text = R"(
3389 OpCapability Shader
3390 OpCapability Linkage
3391 OpMemoryModel Logical GLSL450
3392 %1 = OpTypeVoid
3393 %2 = OpTypeBool
3394 %3 = OpUndef %2
3395 %4 = OpTypeFunction %1
3396 %5 = OpFunction %1 None %4
3397 %6 = OpLabel
3398 OpBranch %7
3399 %7 = OpLabel
3400 OpLoopMerge %8 %9 None
3401 OpBranch %10
3402 %10 = OpLabel
3403 OpLoopMerge %11 %12 None
3404 OpBranch %13
3405 %13 = OpLabel
3406 OpSelectionMerge %14 None
3407 OpBranchConditional %3 %14 %15
3408 %15 = OpLabel
3409 OpBranch %8
3410 %14 = OpLabel
3411 OpBranch %12
3412 %12 = OpLabel
3413 OpBranchConditional %3 %10 %11
3414 %11 = OpLabel
3415 OpBranch %9
3416 %9 = OpLabel
3417 OpBranchConditional %3 %7 %8
3418 %8 = OpLabel
3419 OpReturn
3420 OpFunctionEnd
3421 )";
3422
3423 CompileSuccessfully(text);
3424 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3425 EXPECT_THAT(getDiagnosticString(),
3426 HasSubstr("block <ID> 15[%15] exits the loop headed by <ID> "
3427 "10[%10], but not via a structured exit"));
3428 }
3429
TEST_F(ValidateCFG,ContinueFromNestedSelection)3430 TEST_F(ValidateCFG, ContinueFromNestedSelection) {
3431 const std::string text = R"(
3432 OpCapability Shader
3433 OpCapability Linkage
3434 OpMemoryModel Logical GLSL450
3435 %void = OpTypeVoid
3436 %void_fn = OpTypeFunction %void
3437 %bool = OpTypeBool
3438 %undef = OpUndef %bool
3439 %4 = OpFunction %void None %void_fn
3440 %5 = OpLabel
3441 OpBranch %48
3442 %48 = OpLabel
3443 OpLoopMerge %47 %50 None
3444 OpBranch %10
3445 %10 = OpLabel
3446 OpLoopMerge %12 %37 None
3447 OpBranchConditional %undef %11 %12
3448 %11 = OpLabel
3449 OpSelectionMerge %31 None
3450 OpBranchConditional %undef %30 %31
3451 %30 = OpLabel
3452 OpSelectionMerge %38 None
3453 OpBranchConditional %undef %36 %38
3454 %36 = OpLabel
3455 OpBranch %38
3456 %38 = OpLabel
3457 OpBranch %37
3458 %37 = OpLabel
3459 OpBranch %10
3460 %31 = OpLabel
3461 OpBranch %12
3462 %12 = OpLabel
3463 OpSelectionMerge %55 None
3464 OpBranchConditional %undef %47 %55
3465 %55 = OpLabel
3466 OpBranch %47
3467 %50 = OpLabel
3468 OpBranch %48
3469 %47 = OpLabel
3470 OpReturn
3471 OpFunctionEnd
3472 )";
3473
3474 CompileSuccessfully(text);
3475 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3476 }
3477
TEST_F(ValidateCFG,MissingMergeConditionalBranchBad)3478 TEST_F(ValidateCFG, MissingMergeConditionalBranchBad) {
3479 const std::string text = R"(
3480 OpCapability Shader
3481 OpCapability Linkage
3482 OpMemoryModel Logical GLSL450
3483 %void = OpTypeVoid
3484 %void_fn = OpTypeFunction %void
3485 %bool = OpTypeBool
3486 %undef = OpUndef %bool
3487 %func = OpFunction %void None %void_fn
3488 %entry = OpLabel
3489 OpBranchConditional %undef %then %else
3490 %then = OpLabel
3491 OpReturn
3492 %else = OpLabel
3493 OpReturn
3494 OpFunctionEnd
3495 )";
3496
3497 CompileSuccessfully(text);
3498 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3499 EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
3500 }
3501
TEST_F(ValidateCFG,MissingMergeSwitchBad)3502 TEST_F(ValidateCFG, MissingMergeSwitchBad) {
3503 const std::string text = R"(
3504 OpCapability Shader
3505 OpCapability Linkage
3506 OpMemoryModel Logical GLSL450
3507 %void = OpTypeVoid
3508 %void_fn = OpTypeFunction %void
3509 %int = OpTypeInt 32 0
3510 %undef = OpUndef %int
3511 %func = OpFunction %void None %void_fn
3512 %entry = OpLabel
3513 OpSwitch %undef %then 0 %else
3514 %then = OpLabel
3515 OpReturn
3516 %else = OpLabel
3517 OpReturn
3518 OpFunctionEnd
3519 )";
3520
3521 CompileSuccessfully(text);
3522 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3523 EXPECT_THAT(
3524 getDiagnosticString(),
3525 HasSubstr(
3526 "OpSwitch must be preceeded by an OpSelectionMerge instruction"));
3527 }
3528
TEST_F(ValidateCFG,MissingMergeSwitchBad2)3529 TEST_F(ValidateCFG, MissingMergeSwitchBad2) {
3530 const std::string text = R"(
3531 OpCapability Shader
3532 OpCapability Linkage
3533 OpMemoryModel Logical GLSL450
3534 %void = OpTypeVoid
3535 %void_fn = OpTypeFunction %void
3536 %int = OpTypeInt 32 0
3537 %undef = OpUndef %int
3538 %func = OpFunction %void None %void_fn
3539 %entry = OpLabel
3540 OpSwitch %undef %then 0 %then 1 %then 2 %else
3541 %then = OpLabel
3542 OpReturn
3543 %else = OpLabel
3544 OpReturn
3545 OpFunctionEnd
3546 )";
3547
3548 CompileSuccessfully(text);
3549 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3550 EXPECT_THAT(
3551 getDiagnosticString(),
3552 HasSubstr(
3553 "OpSwitch must be preceeded by an OpSelectionMerge instruction"));
3554 }
3555
TEST_F(ValidateCFG,MissingMergeOneBranchToMergeGood)3556 TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) {
3557 const std::string text = R"(
3558 OpCapability Shader
3559 OpCapability Linkage
3560 OpMemoryModel Logical GLSL450
3561 %void = OpTypeVoid
3562 %void_fn = OpTypeFunction %void
3563 %bool = OpTypeBool
3564 %undef = OpUndef %bool
3565 %func = OpFunction %void None %void_fn
3566 %entry = OpLabel
3567 OpSelectionMerge %b3 None
3568 OpBranchConditional %undef %b1 %b2
3569 %b1 = OpLabel
3570 OpBranchConditional %undef %b2 %b3
3571 %b2 = OpLabel
3572 OpBranch %b3
3573 %b3 = OpLabel
3574 OpReturn
3575 OpFunctionEnd
3576 )";
3577
3578 CompileSuccessfully(text);
3579 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3580 }
3581
TEST_F(ValidateCFG,MissingMergeSameTargetConditionalBranchGood)3582 TEST_F(ValidateCFG, MissingMergeSameTargetConditionalBranchGood) {
3583 const std::string text = R"(
3584 OpCapability Shader
3585 OpCapability Linkage
3586 OpMemoryModel Logical GLSL450
3587 %void = OpTypeVoid
3588 %void_fn = OpTypeFunction %void
3589 %bool = OpTypeBool
3590 %undef = OpUndef %bool
3591 %func = OpFunction %void None %void_fn
3592 %entry = OpLabel
3593 OpBranchConditional %undef %then %then
3594 %then = OpLabel
3595 OpReturn
3596 OpFunctionEnd
3597 )";
3598
3599 CompileSuccessfully(text);
3600 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3601 }
3602
TEST_F(ValidateCFG,MissingMergeOneTargetSwitchBad)3603 TEST_F(ValidateCFG, MissingMergeOneTargetSwitchBad) {
3604 const std::string text = R"(
3605 OpCapability Shader
3606 OpCapability Linkage
3607 OpMemoryModel Logical GLSL450
3608 %void = OpTypeVoid
3609 %void_fn = OpTypeFunction %void
3610 %int = OpTypeInt 32 0
3611 %undef = OpUndef %int
3612 %func = OpFunction %void None %void_fn
3613 %entry = OpLabel
3614 OpSwitch %undef %then 0 %then 1 %then
3615 %then = OpLabel
3616 OpReturn
3617 OpFunctionEnd
3618 )";
3619
3620 CompileSuccessfully(text);
3621 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3622 EXPECT_THAT(
3623 getDiagnosticString(),
3624 HasSubstr(
3625 "OpSwitch must be preceeded by an OpSelectionMerge instruction"));
3626 }
3627
TEST_F(ValidateCFG,MissingMergeOneUnseenTargetSwitchBad)3628 TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchBad) {
3629 const std::string text = R"(
3630 OpCapability Shader
3631 OpCapability Linkage
3632 OpMemoryModel Logical GLSL450
3633 %void = OpTypeVoid
3634 %void_fn = OpTypeFunction %void
3635 %int = OpTypeInt 32 0
3636 %undef_int = OpUndef %int
3637 %bool = OpTypeBool
3638 %undef_bool = OpUndef %bool
3639 %func = OpFunction %void None %void_fn
3640 %entry = OpLabel
3641 OpSelectionMerge %merge None
3642 OpBranchConditional %undef_bool %merge %b1
3643 %b1 = OpLabel
3644 OpSwitch %undef_int %b2 0 %b2 1 %merge 2 %b2
3645 %b2 = OpLabel
3646 OpBranch %merge
3647 %merge = OpLabel
3648 OpReturn
3649 OpFunctionEnd
3650 )";
3651
3652 CompileSuccessfully(text);
3653 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3654 EXPECT_THAT(
3655 getDiagnosticString(),
3656 HasSubstr(
3657 "OpSwitch must be preceeded by an OpSelectionMerge instruction"));
3658 }
3659
TEST_F(ValidateCFG,MissingMergeLoopBreakGood)3660 TEST_F(ValidateCFG, MissingMergeLoopBreakGood) {
3661 const std::string text = R"(
3662 OpCapability Shader
3663 OpCapability Linkage
3664 OpMemoryModel Logical GLSL450
3665 %void = OpTypeVoid
3666 %void_fn = OpTypeFunction %void
3667 %bool = OpTypeBool
3668 %undef = OpUndef %bool
3669 %func = OpFunction %void None %void_fn
3670 %entry = OpLabel
3671 OpBranch %loop
3672 %loop = OpLabel
3673 OpLoopMerge %exit %continue None
3674 OpBranch %body
3675 %body = OpLabel
3676 OpBranchConditional %undef %body2 %exit
3677 %body2 = OpLabel
3678 OpBranch %continue
3679 %continue = OpLabel
3680 OpBranch %loop
3681 %exit = OpLabel
3682 OpReturn
3683 OpFunctionEnd
3684 )";
3685
3686 CompileSuccessfully(text);
3687 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3688 }
3689
TEST_F(ValidateCFG,MissingMergeLoopContinueGood)3690 TEST_F(ValidateCFG, MissingMergeLoopContinueGood) {
3691 const std::string text = R"(
3692 OpCapability Shader
3693 OpCapability Linkage
3694 OpMemoryModel Logical GLSL450
3695 %void = OpTypeVoid
3696 %void_fn = OpTypeFunction %void
3697 %bool = OpTypeBool
3698 %undef = OpUndef %bool
3699 %func = OpFunction %void None %void_fn
3700 %entry = OpLabel
3701 OpBranch %loop
3702 %loop = OpLabel
3703 OpLoopMerge %exit %continue None
3704 OpBranch %body
3705 %body = OpLabel
3706 OpBranchConditional %undef %body2 %continue
3707 %body2 = OpLabel
3708 OpBranch %continue
3709 %continue = OpLabel
3710 OpBranch %loop
3711 %exit = OpLabel
3712 OpReturn
3713 OpFunctionEnd
3714 )";
3715
3716 CompileSuccessfully(text);
3717 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3718 }
3719
TEST_F(ValidateCFG,MissingMergeSwitchBreakGood)3720 TEST_F(ValidateCFG, MissingMergeSwitchBreakGood) {
3721 const std::string text = R"(
3722 OpCapability Shader
3723 OpCapability Linkage
3724 OpMemoryModel Logical GLSL450
3725 %void = OpTypeVoid
3726 %void_fn = OpTypeFunction %void
3727 %bool = OpTypeBool
3728 %undef = OpUndef %bool
3729 %int = OpTypeInt 32 0
3730 %int_0 = OpConstant %int 0
3731 %func = OpFunction %void None %void_fn
3732 %entry = OpLabel
3733 OpSelectionMerge %merge None
3734 OpSwitch %int_0 %merge 1 %b1
3735 %b1 = OpLabel
3736 OpBranchConditional %undef %merge %b2
3737 %b2 = OpLabel
3738 OpBranch %merge
3739 %merge = OpLabel
3740 OpReturn
3741 OpFunctionEnd
3742 )";
3743
3744 CompileSuccessfully(text);
3745 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3746 }
3747
TEST_F(ValidateCFG,MissingMergeSwitchFallThroughGood)3748 TEST_F(ValidateCFG, MissingMergeSwitchFallThroughGood) {
3749 const std::string text = R"(
3750 OpCapability Shader
3751 OpCapability Linkage
3752 OpMemoryModel Logical GLSL450
3753 %void = OpTypeVoid
3754 %void_fn = OpTypeFunction %void
3755 %bool = OpTypeBool
3756 %undef = OpUndef %bool
3757 %int = OpTypeInt 32 0
3758 %int_0 = OpConstant %int 0
3759 %func = OpFunction %void None %void_fn
3760 %entry = OpLabel
3761 OpSelectionMerge %merge None
3762 OpSwitch %int_0 %b1 1 %b2
3763 %b1 = OpLabel
3764 OpBranchConditional %undef %b3 %b2
3765 %b2 = OpLabel
3766 OpBranch %merge
3767 %b3 = OpLabel
3768 OpBranch %merge
3769 %merge = OpLabel
3770 OpReturn
3771 OpFunctionEnd
3772 )";
3773
3774 CompileSuccessfully(text);
3775 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3776 }
3777
TEST_F(ValidateCFG,MissingMergeInALoopBad)3778 TEST_F(ValidateCFG, MissingMergeInALoopBad) {
3779 const std::string text = R"(
3780 OpCapability Shader
3781 OpCapability Linkage
3782 OpMemoryModel Logical GLSL450
3783 %void = OpTypeVoid
3784 %void_fn = OpTypeFunction %void
3785 %bool = OpTypeBool
3786 %undef = OpUndef %bool
3787 %func = OpFunction %void None %void_fn
3788 %entry = OpLabel
3789 OpBranch %loop
3790 %loop = OpLabel
3791 OpLoopMerge %exit %continue None
3792 OpBranch %body
3793 %body = OpLabel
3794 OpBranchConditional %undef %b1 %b2
3795 %b1 = OpLabel
3796 OpBranch %exit
3797 %b2 = OpLabel
3798 OpBranch %continue
3799 %continue = OpLabel
3800 OpBranch %loop
3801 %exit = OpLabel
3802 OpReturn
3803 OpFunctionEnd
3804 )";
3805
3806 CompileSuccessfully(text);
3807 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3808 EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
3809 }
3810
TEST_F(ValidateCFG,MissingMergeCrissCrossBad)3811 TEST_F(ValidateCFG, MissingMergeCrissCrossBad) {
3812 const std::string text = R"(
3813 OpCapability Shader
3814 OpCapability Linkage
3815 OpMemoryModel Logical GLSL450
3816 %void = OpTypeVoid
3817 %void_fn = OpTypeFunction %void
3818 %bool = OpTypeBool
3819 %undef = OpUndef %bool
3820 %func = OpFunction %void None %void_fn
3821 %entry = OpLabel
3822 OpSelectionMerge %merge None
3823 OpBranchConditional %undef %b1 %b2
3824 %b1 = OpLabel
3825 OpBranchConditional %undef %b3 %b4
3826 %b2 = OpLabel
3827 OpBranchConditional %undef %b3 %b4
3828 %b3 = OpLabel
3829 OpBranch %merge
3830 %b4 = OpLabel
3831 OpBranch %merge
3832 %merge = OpLabel
3833 OpReturn
3834 OpFunctionEnd
3835 )";
3836
3837 CompileSuccessfully(text);
3838 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3839 EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
3840 }
3841
TEST_F(ValidateCFG,ContinueCannotBeSelectionMergeTarget)3842 TEST_F(ValidateCFG, ContinueCannotBeSelectionMergeTarget) {
3843 const std::string text = R"(
3844 OpCapability Shader
3845 OpCapability Linkage
3846 OpMemoryModel Logical GLSL450
3847 OpName %loop "loop"
3848 OpName %continue "continue"
3849 OpName %body "body"
3850 %void = OpTypeVoid
3851 %void_fn = OpTypeFunction %void
3852 %bool = OpTypeBool
3853 %undef = OpUndef %bool
3854 %func = OpFunction %void None %void_fn
3855 %entry = OpLabel
3856 OpBranch %loop
3857 %loop = OpLabel
3858 OpLoopMerge %exit %continue None
3859 OpBranch %body
3860 %body = OpLabel
3861 OpSelectionMerge %continue None
3862 OpBranchConditional %undef %exit %continue
3863 %continue = OpLabel
3864 OpBranch %loop
3865 %exit = OpLabel
3866 OpReturn
3867 OpFunctionEnd
3868 )";
3869
3870 CompileSuccessfully(text);
3871 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3872 EXPECT_THAT(
3873 getDiagnosticString(),
3874 HasSubstr(
3875 "Header block 3[%body] is contained in the loop construct headed by "
3876 "1[%loop], but its merge block 2[%continue] is not"));
3877 }
3878
TEST_F(ValidateCFG,ContinueCannotBeLoopMergeTarget)3879 TEST_F(ValidateCFG, ContinueCannotBeLoopMergeTarget) {
3880 const std::string text = R"(
3881 OpCapability Shader
3882 OpCapability Linkage
3883 OpMemoryModel Logical GLSL450
3884 OpName %loop "loop"
3885 OpName %continue "continue"
3886 OpName %inner "inner"
3887 %void = OpTypeVoid
3888 %void_fn = OpTypeFunction %void
3889 %bool = OpTypeBool
3890 %undef = OpUndef %bool
3891 %func = OpFunction %void None %void_fn
3892 %entry = OpLabel
3893 OpBranch %loop
3894 %loop = OpLabel
3895 OpLoopMerge %exit %continue None
3896 OpBranchConditional %undef %exit %inner
3897 %inner = OpLabel
3898 OpLoopMerge %continue %inner None
3899 OpBranchConditional %undef %inner %continue
3900 %continue = OpLabel
3901 OpBranch %loop
3902 %exit = OpLabel
3903 OpReturn
3904 OpFunctionEnd
3905 )";
3906
3907 CompileSuccessfully(text);
3908 EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
3909 EXPECT_THAT(
3910 getDiagnosticString(),
3911 HasSubstr(
3912 "Header block 3[%inner] is contained in the loop construct headed by "
3913 "1[%loop], but its merge block 2[%continue] is not"));
3914 }
3915
TEST_F(ValidateCFG,ExitFromConstructWhoseHeaderIsAMerge)3916 TEST_F(ValidateCFG, ExitFromConstructWhoseHeaderIsAMerge) {
3917 const std::string text = R"(
3918 OpCapability Shader
3919 OpCapability Linkage
3920 OpMemoryModel Logical GLSL450
3921 %void = OpTypeVoid
3922 %2 = OpTypeFunction %void
3923 %int = OpTypeInt 32 1
3924 %4 = OpUndef %int
3925 %bool = OpTypeBool
3926 %6 = OpUndef %bool
3927 %7 = OpFunction %void None %2
3928 %8 = OpLabel
3929 OpSelectionMerge %9 None
3930 OpSwitch %4 %10 0 %11
3931 %10 = OpLabel
3932 OpBranch %9
3933 %11 = OpLabel
3934 OpBranch %12
3935 %12 = OpLabel
3936 OpLoopMerge %13 %14 None
3937 OpBranch %15
3938 %15 = OpLabel
3939 OpSelectionMerge %16 None
3940 OpSwitch %4 %17 1 %18 2 %19
3941 %17 = OpLabel
3942 OpBranch %16
3943 %18 = OpLabel
3944 OpBranch %14
3945 %19 = OpLabel
3946 OpBranch %16
3947 %16 = OpLabel
3948 OpBranch %14
3949 %14 = OpLabel
3950 OpBranchConditional %6 %12 %13
3951 %13 = OpLabel
3952 OpSelectionMerge %20 None
3953 OpBranchConditional %6 %21 %20
3954 %21 = OpLabel
3955 OpBranch %9
3956 %20 = OpLabel
3957 OpBranch %10
3958 %9 = OpLabel
3959 OpReturn
3960 OpFunctionEnd
3961 )";
3962
3963 CompileSuccessfully(text);
3964 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
3965 }
3966
TEST_F(ValidateCFG,ExitFromConstructWhoseHeaderIsAMerge2)3967 TEST_F(ValidateCFG, ExitFromConstructWhoseHeaderIsAMerge2) {
3968 const std::string text = R"(
3969 OpCapability Shader
3970 %1 = OpExtInstImport "GLSL.std.450"
3971 OpMemoryModel Logical GLSL450
3972 OpEntryPoint Fragment %2 "main"
3973 OpExecutionMode %2 OriginUpperLeft
3974 %void = OpTypeVoid
3975 %4 = OpTypeFunction %void
3976 %int = OpTypeInt 32 1
3977 %6 = OpUndef %int
3978 %bool = OpTypeBool
3979 %8 = OpUndef %bool
3980 %2 = OpFunction %void None %4
3981 %9 = OpLabel
3982 OpSelectionMerge %10 None
3983 OpSwitch %6 %11 0 %12
3984 %11 = OpLabel
3985 OpBranch %10
3986 %12 = OpLabel
3987 OpBranch %13
3988 %13 = OpLabel
3989 OpLoopMerge %14 %15 None
3990 OpBranch %16
3991 %16 = OpLabel
3992 OpSelectionMerge %17 None
3993 OpSwitch %6 %18 1 %19 2 %20
3994 %18 = OpLabel
3995 OpBranch %17
3996 %19 = OpLabel
3997 OpBranch %15
3998 %20 = OpLabel
3999 OpBranch %17
4000 %17 = OpLabel
4001 OpBranch %15
4002 %15 = OpLabel
4003 OpBranchConditional %8 %13 %14
4004 %14 = OpLabel
4005 OpSelectionMerge %21 None
4006 OpBranchConditional %8 %22 %21
4007 %22 = OpLabel
4008 OpSelectionMerge %23 None
4009 OpBranchConditional %8 %24 %23
4010 %24 = OpLabel
4011 OpBranch %10
4012 %23 = OpLabel
4013 OpBranch %21
4014 %21 = OpLabel
4015 OpBranch %11
4016 %10 = OpLabel
4017 OpReturn
4018 OpFunctionEnd
4019 )";
4020
4021 CompileSuccessfully(text);
4022 EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
4023 }
4024
TEST_F(ValidateCFG,PhiResultInvalidSampler)4025 TEST_F(ValidateCFG, PhiResultInvalidSampler) {
4026 const std::string text = R"(
4027 OpCapability Shader
4028 OpCapability Linkage
4029 OpMemoryModel Logical GLSL450
4030 %void = OpTypeVoid
4031 %bool = OpTypeBool
4032 %f32 = OpTypeFloat 32
4033 %sampler = OpTypeSampler
4034 %ptr_uc_sampler = OpTypePointer UniformConstant %sampler
4035 %sampler_var = OpVariable %ptr_uc_sampler UniformConstant
4036 %undef_bool = OpUndef %bool
4037 %undef_sampler = OpUndef %sampler
4038 %void_fn = OpTypeFunction %void
4039 %fn = OpFunction %void None %void_fn
4040 %entry = OpLabel
4041 %ld_sampler = OpLoad %sampler %sampler_var
4042 OpBranch %loop
4043 %loop = OpLabel
4044 %phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop
4045 OpLoopMerge %exit %loop None
4046 OpBranchConditional %undef_bool %exit %loop
4047 %exit = OpLabel
4048 OpReturn
4049 OpFunctionEnd
4050 )";
4051
4052 CompileSuccessfully(text);
4053 ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
4054 EXPECT_THAT(getDiagnosticString(),
4055 HasSubstr("Result type cannot be OpTypeSampler"));
4056 }
4057
TEST_F(ValidateCFG,PhiResultInvalidImage)4058 TEST_F(ValidateCFG, PhiResultInvalidImage) {
4059 const std::string text = R"(
4060 OpCapability Shader
4061 OpCapability Linkage
4062 OpMemoryModel Logical GLSL450
4063 %void = OpTypeVoid
4064 %bool = OpTypeBool
4065 %f32 = OpTypeFloat 32
4066 %image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
4067 %ptr_uc_image = OpTypePointer UniformConstant %image
4068 %image_var = OpVariable %ptr_uc_image UniformConstant
4069 %undef_bool = OpUndef %bool
4070 %undef_image = OpUndef %image
4071 %void_fn = OpTypeFunction %void
4072 %fn = OpFunction %void None %void_fn
4073 %entry = OpLabel
4074 %ld_image = OpLoad %image %image_var
4075 OpBranch %loop
4076 %loop = OpLabel
4077 %phi = OpPhi %image %undef_image %entry %ld_image %loop
4078 OpLoopMerge %exit %loop None
4079 OpBranchConditional %undef_bool %exit %loop
4080 %exit = OpLabel
4081 OpReturn
4082 OpFunctionEnd
4083 )";
4084
4085 CompileSuccessfully(text);
4086 ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
4087 EXPECT_THAT(getDiagnosticString(),
4088 HasSubstr("Result type cannot be OpTypeImage"));
4089 }
4090
TEST_F(ValidateCFG,PhiResultInvalidSampledImage)4091 TEST_F(ValidateCFG, PhiResultInvalidSampledImage) {
4092 const std::string text = R"(
4093 OpCapability Shader
4094 OpCapability Linkage
4095 OpMemoryModel Logical GLSL450
4096 %void = OpTypeVoid
4097 %bool = OpTypeBool
4098 %f32 = OpTypeFloat 32
4099 %sampler = OpTypeSampler
4100 %ptr_uc_sampler = OpTypePointer UniformConstant %sampler
4101 %sampler_var = OpVariable %ptr_uc_sampler UniformConstant
4102 %image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
4103 %ptr_uc_image = OpTypePointer UniformConstant %image
4104 %image_var = OpVariable %ptr_uc_image UniformConstant
4105 %sampled_image = OpTypeSampledImage %image
4106 %undef_bool = OpUndef %bool
4107 %undef_sampled_image = OpUndef %sampled_image
4108 %void_fn = OpTypeFunction %void
4109 %fn = OpFunction %void None %void_fn
4110 %entry = OpLabel
4111 %ld_image = OpLoad %image %image_var
4112 %ld_sampler = OpLoad %sampler %sampler_var
4113 OpBranch %loop
4114 %loop = OpLabel
4115 %phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop
4116 %sample = OpSampledImage %sampled_image %ld_image %ld_sampler
4117 OpLoopMerge %exit %loop None
4118 OpBranchConditional %undef_bool %exit %loop
4119 %exit = OpLabel
4120 OpReturn
4121 OpFunctionEnd
4122 )";
4123
4124 CompileSuccessfully(text);
4125 ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
4126 EXPECT_THAT(getDiagnosticString(),
4127 HasSubstr("Result type cannot be OpTypeSampledImage"));
4128 }
4129
TEST_F(ValidateCFG,PhiResultValidPreLegalizationSampler)4130 TEST_F(ValidateCFG, PhiResultValidPreLegalizationSampler) {
4131 const std::string text = R"(
4132 OpCapability Shader
4133 OpCapability Linkage
4134 OpMemoryModel Logical GLSL450
4135 %void = OpTypeVoid
4136 %bool = OpTypeBool
4137 %f32 = OpTypeFloat 32
4138 %sampler = OpTypeSampler
4139 %ptr_uc_sampler = OpTypePointer UniformConstant %sampler
4140 %sampler_var = OpVariable %ptr_uc_sampler UniformConstant
4141 %undef_bool = OpUndef %bool
4142 %undef_sampler = OpUndef %sampler
4143 %void_fn = OpTypeFunction %void
4144 %fn = OpFunction %void None %void_fn
4145 %entry = OpLabel
4146 %ld_sampler = OpLoad %sampler %sampler_var
4147 OpBranch %loop
4148 %loop = OpLabel
4149 %phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop
4150 OpLoopMerge %exit %loop None
4151 OpBranchConditional %undef_bool %exit %loop
4152 %exit = OpLabel
4153 OpReturn
4154 OpFunctionEnd
4155 )";
4156
4157 options_->before_hlsl_legalization = true;
4158 CompileSuccessfully(text);
4159 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
4160 }
4161
TEST_F(ValidateCFG,PhiResultValidPreLegalizationImage)4162 TEST_F(ValidateCFG, PhiResultValidPreLegalizationImage) {
4163 const std::string text = R"(
4164 OpCapability Shader
4165 OpCapability Linkage
4166 OpMemoryModel Logical GLSL450
4167 %void = OpTypeVoid
4168 %bool = OpTypeBool
4169 %f32 = OpTypeFloat 32
4170 %image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
4171 %ptr_uc_image = OpTypePointer UniformConstant %image
4172 %image_var = OpVariable %ptr_uc_image UniformConstant
4173 %undef_bool = OpUndef %bool
4174 %undef_image = OpUndef %image
4175 %void_fn = OpTypeFunction %void
4176 %fn = OpFunction %void None %void_fn
4177 %entry = OpLabel
4178 %ld_image = OpLoad %image %image_var
4179 OpBranch %loop
4180 %loop = OpLabel
4181 %phi = OpPhi %image %undef_image %entry %ld_image %loop
4182 OpLoopMerge %exit %loop None
4183 OpBranchConditional %undef_bool %exit %loop
4184 %exit = OpLabel
4185 OpReturn
4186 OpFunctionEnd
4187 )";
4188
4189 options_->before_hlsl_legalization = true;
4190 CompileSuccessfully(text);
4191 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
4192 }
4193
TEST_F(ValidateCFG,PhiResultValidPreLegalizationSampledImage)4194 TEST_F(ValidateCFG, PhiResultValidPreLegalizationSampledImage) {
4195 const std::string text = R"(
4196 OpCapability Shader
4197 OpCapability Linkage
4198 OpMemoryModel Logical GLSL450
4199 %void = OpTypeVoid
4200 %bool = OpTypeBool
4201 %f32 = OpTypeFloat 32
4202 %sampler = OpTypeSampler
4203 %ptr_uc_sampler = OpTypePointer UniformConstant %sampler
4204 %sampler_var = OpVariable %ptr_uc_sampler UniformConstant
4205 %image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
4206 %ptr_uc_image = OpTypePointer UniformConstant %image
4207 %image_var = OpVariable %ptr_uc_image UniformConstant
4208 %sampled_image = OpTypeSampledImage %image
4209 %undef_bool = OpUndef %bool
4210 %undef_sampled_image = OpUndef %sampled_image
4211 %void_fn = OpTypeFunction %void
4212 %fn = OpFunction %void None %void_fn
4213 %entry = OpLabel
4214 %ld_image = OpLoad %image %image_var
4215 %ld_sampler = OpLoad %sampler %sampler_var
4216 OpBranch %loop
4217 %loop = OpLabel
4218 %phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop
4219 %sample = OpSampledImage %sampled_image %ld_image %ld_sampler
4220 OpLoopMerge %exit %loop None
4221 OpBranchConditional %undef_bool %exit %loop
4222 %exit = OpLabel
4223 OpReturn
4224 OpFunctionEnd
4225 )";
4226
4227 options_->before_hlsl_legalization = true;
4228 CompileSuccessfully(text);
4229 ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
4230 }
4231
TEST_F(ValidateCFG,UnreachableIsStaticallyReachable)4232 TEST_F(ValidateCFG, UnreachableIsStaticallyReachable) {
4233 const std::string text = R"(
4234 OpCapability Shader
4235 OpCapability Linkage
4236 OpMemoryModel Logical GLSL450
4237 %1 = OpTypeVoid
4238 %2 = OpTypeFunction %1
4239 %3 = OpFunction %1 None %2
4240 %4 = OpLabel
4241 OpBranch %5
4242 %5 = OpLabel
4243 OpUnreachable
4244 OpFunctionEnd
4245 )";
4246
4247 CompileSuccessfully(text);
4248 EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
4249
4250 auto f = vstate_->function(3);
4251 auto entry = f->GetBlock(4).first;
4252 ASSERT_TRUE(entry->reachable());
4253 auto end = f->GetBlock(5).first;
4254 ASSERT_TRUE(end->reachable());
4255 }
4256
TEST_F(ValidateCFG,BlockOrderDoesNotAffectReachability)4257 TEST_F(ValidateCFG, BlockOrderDoesNotAffectReachability) {
4258 const std::string text = R"(
4259 OpCapability Shader
4260 OpCapability Linkage
4261 OpMemoryModel Logical GLSL450
4262 %1 = OpTypeVoid
4263 %2 = OpTypeFunction %1
4264 %3 = OpTypeBool
4265 %4 = OpUndef %3
4266 %5 = OpFunction %1 None %2
4267 %6 = OpLabel
4268 OpBranch %7
4269 %7 = OpLabel
4270 OpSelectionMerge %8 None
4271 OpBranchConditional %4 %9 %10
4272 %8 = OpLabel
4273 OpReturn
4274 %9 = OpLabel
4275 OpBranch %8
4276 %10 = OpLabel
4277 OpBranch %8
4278 %11 = OpLabel
4279 OpUnreachable
4280 OpFunctionEnd
4281 )";
4282
4283 CompileSuccessfully(text);
4284 EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
4285
4286 auto f = vstate_->function(5);
4287 auto b6 = f->GetBlock(6).first;
4288 auto b7 = f->GetBlock(7).first;
4289 auto b8 = f->GetBlock(8).first;
4290 auto b9 = f->GetBlock(9).first;
4291 auto b10 = f->GetBlock(10).first;
4292 auto b11 = f->GetBlock(11).first;
4293
4294 ASSERT_TRUE(b6->reachable());
4295 ASSERT_TRUE(b7->reachable());
4296 ASSERT_TRUE(b8->reachable());
4297 ASSERT_TRUE(b9->reachable());
4298 ASSERT_TRUE(b10->reachable());
4299 ASSERT_FALSE(b11->reachable());
4300 }
4301
TEST_F(ValidateCFG,PhiInstructionWithDuplicateIncomingEdges)4302 TEST_F(ValidateCFG, PhiInstructionWithDuplicateIncomingEdges) {
4303 const std::string text = R"(
4304 OpCapability Shader
4305 %1 = OpExtInstImport "GLSL.std.450"
4306 OpMemoryModel Logical GLSL450
4307 OpEntryPoint Fragment %4 "main"
4308 OpExecutionMode %4 OriginUpperLeft
4309 OpSource ESSL 320
4310 %2 = OpTypeVoid
4311 %3 = OpTypeFunction %2
4312 %6 = OpTypeBool
4313 %7 = OpConstantTrue %6
4314 %4 = OpFunction %2 None %3
4315 %5 = OpLabel
4316 OpSelectionMerge %10 None
4317 OpBranchConditional %7 %8 %9
4318 %8 = OpLabel
4319 OpBranch %10
4320 %9 = OpLabel
4321 OpBranch %10
4322 %10 = OpLabel
4323 %11 = OpPhi %6 %7 %8 %7 %8
4324 OpReturn
4325 OpFunctionEnd
4326 )";
4327
4328 CompileSuccessfully(text);
4329 EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
4330 EXPECT_THAT(getDiagnosticString(),
4331 HasSubstr("OpPhi references incoming basic block <id> "));
4332 EXPECT_THAT(getDiagnosticString(), HasSubstr("multiple times."));
4333 }
4334
TEST_F(ValidateCFG,PhiOnVoid)4335 TEST_F(ValidateCFG, PhiOnVoid) {
4336 const std::string text = R"(
4337 OpCapability Shader
4338 %1 = OpExtInstImport "GLSL.std.450"
4339 OpMemoryModel Logical GLSL450
4340 OpEntryPoint Fragment %4 "main"
4341 OpExecutionMode %4 OriginUpperLeft
4342 OpSource ESSL 320
4343 OpName %4 "main"
4344 OpName %6 "foo("
4345 %2 = OpTypeVoid
4346 %3 = OpTypeFunction %2
4347 %4 = OpFunction %2 None %3
4348 %5 = OpLabel
4349 %8 = OpFunctionCall %2 %6
4350 OpBranch %20
4351 %20 = OpLabel
4352 %21 = OpPhi %2 %8 %20
4353 OpReturn
4354 OpFunctionEnd
4355 %6 = OpFunction %2 None %3
4356 %7 = OpLabel
4357 OpReturn
4358 OpFunctionEnd
4359 )";
4360
4361 CompileSuccessfully(text);
4362 EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
4363 EXPECT_THAT(getDiagnosticString(),
4364 HasSubstr("OpPhi must not have void result type"));
4365 }
4366
4367 } // namespace
4368 } // namespace val
4369 } // namespace spvtools
4370