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