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