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