1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "gmock/gmock.h"
16 #include "src/reader/spirv/function.h"
17 #include "src/reader/spirv/parser_impl_test_helper.h"
18 #include "src/reader/spirv/spirv_tools_helpers_test.h"
19
20 namespace tint {
21 namespace reader {
22 namespace spirv {
23 namespace {
24
25 using ::testing::Eq;
26 using ::testing::HasSubstr;
27
28 // Make a local name so it's easier to triage errors.
29 using SpvParserCFGTest = SpvParserTest;
30
Dump(const std::vector<uint32_t> & v)31 std::string Dump(const std::vector<uint32_t>& v) {
32 std::ostringstream o;
33 o << "{";
34 for (auto a : v) {
35 o << a << " ";
36 }
37 o << "}";
38 return o.str();
39 }
40
41 using ::testing::ElementsAre;
42 using ::testing::Eq;
43 using ::testing::UnorderedElementsAre;
44
CommonTypes()45 std::string CommonTypes() {
46 return R"(
47 OpCapability Shader
48 OpMemoryModel Logical Simple
49 OpEntryPoint Fragment %100 "main"
50 OpExecutionMode %100 OriginUpperLeft
51
52 OpName %var "var"
53
54 %void = OpTypeVoid
55 %voidfn = OpTypeFunction %void
56
57 %bool = OpTypeBool
58 %cond = OpConstantNull %bool
59 %cond2 = OpConstantTrue %bool
60 %cond3 = OpConstantFalse %bool
61
62 %uint = OpTypeInt 32 0
63 %int = OpTypeInt 32 1
64 %selector = OpConstant %uint 42
65 %signed_selector = OpConstant %int 42
66
67 %uintfn = OpTypeFunction %uint
68
69 %uint_0 = OpConstant %uint 0
70 %uint_1 = OpConstant %uint 1
71 %uint_2 = OpConstant %uint 2
72 %uint_3 = OpConstant %uint 3
73 %uint_4 = OpConstant %uint 4
74 %uint_5 = OpConstant %uint 5
75 %uint_6 = OpConstant %uint 6
76 %uint_7 = OpConstant %uint 7
77 %uint_8 = OpConstant %uint 8
78 %uint_10 = OpConstant %uint 10
79 %uint_20 = OpConstant %uint 20
80 %uint_30 = OpConstant %uint 30
81 %uint_40 = OpConstant %uint 40
82 %uint_50 = OpConstant %uint 50
83 %uint_90 = OpConstant %uint 90
84 %uint_99 = OpConstant %uint 99
85
86 %ptr_Private_uint = OpTypePointer Private %uint
87 %var = OpVariable %ptr_Private_uint Private
88
89 %999 = OpConstant %uint 999
90 )";
91 }
92
93 /// Runs the necessary flow until and including labeling control
94 /// flow constructs.
95 /// @returns the result of labeling control flow constructs.
FlowLabelControlFlowConstructs(FunctionEmitter * fe)96 bool FlowLabelControlFlowConstructs(FunctionEmitter* fe) {
97 fe->RegisterBasicBlocks();
98 EXPECT_TRUE(fe->RegisterMerges()) << fe->parser()->error();
99 fe->ComputeBlockOrderAndPositions();
100 EXPECT_TRUE(fe->VerifyHeaderContinueMergeOrder()) << fe->parser()->error();
101 return fe->LabelControlFlowConstructs();
102 }
103
104 /// Runs the necessary flow until and including finding switch case
105 /// headers.
106 /// @returns the result of finding switch case headers.
FlowFindSwitchCaseHeaders(FunctionEmitter * fe)107 bool FlowFindSwitchCaseHeaders(FunctionEmitter* fe) {
108 EXPECT_TRUE(FlowLabelControlFlowConstructs(fe)) << fe->parser()->error();
109 return fe->FindSwitchCaseHeaders();
110 }
111
112 /// Runs the necessary flow until and including classify CFG edges,
113 /// @returns the result of classify CFG edges.
FlowClassifyCFGEdges(FunctionEmitter * fe)114 bool FlowClassifyCFGEdges(FunctionEmitter* fe) {
115 EXPECT_TRUE(FlowFindSwitchCaseHeaders(fe)) << fe->parser()->error();
116 return fe->ClassifyCFGEdges();
117 }
118
119 /// Runs the necessary flow until and including finding if-selection
120 /// internal headers.
121 /// @returns the result of classify CFG edges.
FlowFindIfSelectionInternalHeaders(FunctionEmitter * fe)122 bool FlowFindIfSelectionInternalHeaders(FunctionEmitter* fe) {
123 EXPECT_TRUE(FlowClassifyCFGEdges(fe)) << fe->parser()->error();
124 return fe->FindIfSelectionInternalHeaders();
125 }
126
TEST_F(SpvParserCFGTest,TerminatorsAreValid_SingleBlock)127 TEST_F(SpvParserCFGTest, TerminatorsAreValid_SingleBlock) {
128 auto p = parser(test::Assemble(CommonTypes() + R"(
129 %100 = OpFunction %void None %voidfn
130
131 %42 = OpLabel
132 OpReturn
133
134 OpFunctionEnd
135 )"));
136 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
137 auto fe = p->function_emitter(100);
138 fe.RegisterBasicBlocks();
139 EXPECT_TRUE(fe.TerminatorsAreValid());
140 }
141
TEST_F(SpvParserCFGTest,TerminatorsAreValid_Sequence)142 TEST_F(SpvParserCFGTest, TerminatorsAreValid_Sequence) {
143 auto p = parser(test::Assemble(CommonTypes() + R"(
144 %100 = OpFunction %void None %voidfn
145
146 %20 = OpLabel
147 OpBranch %30
148
149 %30 = OpLabel
150 OpReturn
151
152 OpFunctionEnd
153 )"));
154 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
155 auto fe = p->function_emitter(100);
156 fe.RegisterBasicBlocks();
157 EXPECT_TRUE(fe.TerminatorsAreValid()) << p->error();
158 }
159
TEST_F(SpvParserCFGTest,TerminatorsAreValid_If)160 TEST_F(SpvParserCFGTest, TerminatorsAreValid_If) {
161 auto p = parser(test::Assemble(CommonTypes() + R"(
162 %100 = OpFunction %void None %voidfn
163
164 %20 = OpLabel
165 OpSelectionMerge %99 None
166 OpBranchConditional %cond %30 %40
167
168 %30 = OpLabel
169 OpBranch %99
170
171 %40 = OpLabel
172 OpBranch %99
173
174 %99 = OpLabel
175 OpReturn
176
177 OpFunctionEnd
178 )"));
179 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
180 auto fe = p->function_emitter(100);
181 fe.RegisterBasicBlocks();
182 EXPECT_TRUE(fe.TerminatorsAreValid()) << p->error();
183 }
184
TEST_F(SpvParserCFGTest,TerminatorsAreValid_Switch)185 TEST_F(SpvParserCFGTest, TerminatorsAreValid_Switch) {
186 auto p = parser(test::Assemble(CommonTypes() + R"(
187 %100 = OpFunction %void None %voidfn
188
189 %10 = OpLabel
190 OpSelectionMerge %99 None
191 OpSwitch %selector %80 20 %20 30 %30
192
193 %20 = OpLabel
194 OpBranch %30 ; fall through
195
196 %30 = OpLabel
197 OpBranch %99
198
199 %80 = OpLabel
200 OpBranch %99
201
202 %99 = OpLabel
203 OpReturn
204
205 OpFunctionEnd
206 )"));
207 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
208 auto fe = p->function_emitter(100);
209 fe.RegisterBasicBlocks();
210 EXPECT_TRUE(fe.TerminatorsAreValid());
211 }
212
TEST_F(SpvParserCFGTest,TerminatorsAreValid_Loop_SingleBlock)213 TEST_F(SpvParserCFGTest, TerminatorsAreValid_Loop_SingleBlock) {
214 auto p = parser(test::Assemble(CommonTypes() + R"(
215 %100 = OpFunction %void None %voidfn
216
217 %10 = OpLabel
218 OpBranch %20
219
220 %20 = OpLabel
221 OpLoopMerge %99 %20 None
222 OpBranchConditional %cond %20 %99
223
224 %99 = OpLabel
225 OpReturn
226
227 OpFunctionEnd
228 )"));
229 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
230 auto fe = p->function_emitter(100);
231 fe.RegisterBasicBlocks();
232 EXPECT_TRUE(fe.TerminatorsAreValid());
233 }
234
TEST_F(SpvParserCFGTest,TerminatorsAreValid_Loop_Simple)235 TEST_F(SpvParserCFGTest, TerminatorsAreValid_Loop_Simple) {
236 auto p = parser(test::Assemble(CommonTypes() + R"(
237 %100 = OpFunction %void None %voidfn
238
239 %10 = OpLabel
240 OpBranch %20
241
242 %20 = OpLabel
243 OpLoopMerge %99 %40 None
244 OpBranchConditional %cond %30 %99
245
246 %30 = OpLabel
247 OpBranch %40
248
249 %40 = OpLabel
250 OpBranch %20 ; back edge
251
252 %99 = OpLabel
253 OpReturn
254
255 OpFunctionEnd
256 )"));
257 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
258 auto fe = p->function_emitter(100);
259 fe.RegisterBasicBlocks();
260 EXPECT_TRUE(fe.TerminatorsAreValid());
261 }
262
TEST_F(SpvParserCFGTest,TerminatorsAreValid_Kill)263 TEST_F(SpvParserCFGTest, TerminatorsAreValid_Kill) {
264 auto p = parser(test::Assemble(CommonTypes() + R"(
265 %100 = OpFunction %void None %voidfn
266
267 %10 = OpLabel
268 OpKill
269
270 OpFunctionEnd
271 )"));
272 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
273 auto fe = p->function_emitter(100);
274 fe.RegisterBasicBlocks();
275 EXPECT_TRUE(fe.TerminatorsAreValid());
276 }
277
TEST_F(SpvParserCFGTest,TerminatorsAreValid_Unreachable)278 TEST_F(SpvParserCFGTest, TerminatorsAreValid_Unreachable) {
279 auto p = parser(test::Assemble(CommonTypes() + R"(
280 %100 = OpFunction %void None %voidfn
281
282 %10 = OpLabel
283 OpUnreachable
284
285 OpFunctionEnd
286 )"));
287 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
288 auto fe = p->function_emitter(100);
289 fe.RegisterBasicBlocks();
290 EXPECT_TRUE(fe.TerminatorsAreValid());
291 }
292
TEST_F(SpvParserCFGTest,TerminatorsAreValid_MissingTerminator)293 TEST_F(SpvParserCFGTest, TerminatorsAreValid_MissingTerminator) {
294 auto p = parser(test::Assemble(CommonTypes() + R"(
295 %100 = OpFunction %void None %voidfn
296
297 %10 = OpLabel
298
299 OpFunctionEnd
300 )"));
301 // The SPIRV-Tools internal representation rejects this case earlier.
302 EXPECT_FALSE(p->BuildAndParseInternalModuleExceptFunctions());
303 }
304
TEST_F(SpvParserCFGTest,TerminatorsAreValid_DisallowLoopToEntryBlock)305 TEST_F(SpvParserCFGTest, TerminatorsAreValid_DisallowLoopToEntryBlock) {
306 auto p = parser(test::Assemble(CommonTypes() + R"(
307 %100 = OpFunction %void None %voidfn
308
309 %10 = OpLabel
310 OpBranch %20
311
312 %20 = OpLabel
313 OpBranch %10 ; not allowed
314
315 OpFunctionEnd
316 )"));
317 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
318 auto fe = p->function_emitter(100);
319 fe.RegisterBasicBlocks();
320 EXPECT_FALSE(fe.TerminatorsAreValid());
321 EXPECT_THAT(p->error(), Eq("Block 20 branches to function entry block 10"));
322 }
323
TEST_F(SpvParserCFGTest,TerminatorsAreValid_DisallowNonBlock)324 TEST_F(SpvParserCFGTest, TerminatorsAreValid_DisallowNonBlock) {
325 auto p = parser(test::Assemble(CommonTypes() + R"(
326 %100 = OpFunction %void None %voidfn
327
328 %10 = OpLabel
329 OpBranch %999 ; definitely wrong
330
331 OpFunctionEnd
332 )"));
333 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
334 auto fe = p->function_emitter(100);
335 fe.RegisterBasicBlocks();
336 EXPECT_FALSE(fe.TerminatorsAreValid());
337 EXPECT_THAT(p->error(),
338 Eq("Block 10 in function 100 branches to 999 which is "
339 "not a block in the function"));
340 }
341
TEST_F(SpvParserCFGTest,TerminatorsAreValid_DisallowBlockInDifferentFunction)342 TEST_F(SpvParserCFGTest, TerminatorsAreValid_DisallowBlockInDifferentFunction) {
343 auto p = parser(test::Assemble(CommonTypes() + R"(
344 %100 = OpFunction %void None %voidfn
345
346 %10 = OpLabel
347 OpBranch %210
348
349 OpFunctionEnd
350
351
352 %200 = OpFunction %void None %voidfn
353
354 %210 = OpLabel
355 OpReturn
356
357 OpFunctionEnd
358 )"));
359 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
360 auto fe = p->function_emitter(100);
361 fe.RegisterBasicBlocks();
362 EXPECT_FALSE(fe.TerminatorsAreValid());
363 EXPECT_THAT(p->error(), Eq("Block 10 in function 100 branches to 210 which "
364 "is not a block in the function"));
365 }
366
TEST_F(SpvParserCFGTest,RegisterMerges_NoMerges)367 TEST_F(SpvParserCFGTest, RegisterMerges_NoMerges) {
368 auto p = parser(test::Assemble(CommonTypes() + R"(
369 %100 = OpFunction %void None %voidfn
370
371 %10 = OpLabel
372 OpReturn
373
374 OpFunctionEnd
375 )"));
376 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
377 auto fe = p->function_emitter(100);
378 fe.RegisterBasicBlocks();
379 EXPECT_TRUE(fe.RegisterMerges());
380
381 const auto* bi = fe.GetBlockInfo(10);
382 ASSERT_NE(bi, nullptr);
383 EXPECT_EQ(bi->merge_for_header, 0u);
384 EXPECT_EQ(bi->continue_for_header, 0u);
385 EXPECT_EQ(bi->header_for_merge, 0u);
386 EXPECT_EQ(bi->header_for_continue, 0u);
387 EXPECT_FALSE(bi->is_continue_entire_loop);
388 }
389
TEST_F(SpvParserCFGTest,RegisterMerges_GoodSelectionMerge_BranchConditional)390 TEST_F(SpvParserCFGTest, RegisterMerges_GoodSelectionMerge_BranchConditional) {
391 auto p = parser(test::Assemble(CommonTypes() + R"(
392 %100 = OpFunction %void None %voidfn
393
394 %10 = OpLabel
395 OpSelectionMerge %99 None
396 OpBranchConditional %cond %20 %99
397
398 %20 = OpLabel
399 OpBranch %99
400
401 %99 = OpLabel
402 OpReturn
403
404 OpFunctionEnd
405 )"));
406 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
407 auto fe = p->function_emitter(100);
408 fe.RegisterBasicBlocks();
409 EXPECT_TRUE(fe.RegisterMerges());
410
411 // Header points to the merge
412 const auto* bi10 = fe.GetBlockInfo(10);
413 ASSERT_NE(bi10, nullptr);
414 EXPECT_EQ(bi10->merge_for_header, 99u);
415 EXPECT_EQ(bi10->continue_for_header, 0u);
416 EXPECT_EQ(bi10->header_for_merge, 0u);
417 EXPECT_EQ(bi10->header_for_continue, 0u);
418 EXPECT_FALSE(bi10->is_continue_entire_loop);
419
420 // Middle block is neither header nor merge
421 const auto* bi20 = fe.GetBlockInfo(20);
422 ASSERT_NE(bi20, nullptr);
423 EXPECT_EQ(bi20->merge_for_header, 0u);
424 EXPECT_EQ(bi20->continue_for_header, 0u);
425 EXPECT_EQ(bi20->header_for_merge, 0u);
426 EXPECT_EQ(bi20->header_for_continue, 0u);
427 EXPECT_FALSE(bi20->is_continue_entire_loop);
428
429 // Merge block points to the header
430 const auto* bi99 = fe.GetBlockInfo(99);
431 ASSERT_NE(bi99, nullptr);
432 EXPECT_EQ(bi99->merge_for_header, 0u);
433 EXPECT_EQ(bi99->continue_for_header, 0u);
434 EXPECT_EQ(bi99->header_for_merge, 10u);
435 EXPECT_EQ(bi99->header_for_continue, 0u);
436 EXPECT_FALSE(bi99->is_continue_entire_loop);
437 }
438
TEST_F(SpvParserCFGTest,RegisterMerges_GoodSelectionMerge_Switch)439 TEST_F(SpvParserCFGTest, RegisterMerges_GoodSelectionMerge_Switch) {
440 auto p = parser(test::Assemble(CommonTypes() + R"(
441 %100 = OpFunction %void None %voidfn
442
443 %10 = OpLabel
444 OpSelectionMerge %99 None
445 OpSwitch %selector %99 20 %20
446
447 %20 = OpLabel
448 OpBranch %99
449
450 %99 = OpLabel
451 OpReturn
452
453 OpFunctionEnd
454 )"));
455 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
456 auto fe = p->function_emitter(100);
457 fe.RegisterBasicBlocks();
458 EXPECT_TRUE(fe.RegisterMerges());
459
460 // Header points to the merge
461 const auto* bi10 = fe.GetBlockInfo(10);
462 ASSERT_NE(bi10, nullptr);
463 EXPECT_EQ(bi10->merge_for_header, 99u);
464 EXPECT_EQ(bi10->continue_for_header, 0u);
465 EXPECT_EQ(bi10->header_for_merge, 0u);
466 EXPECT_EQ(bi10->header_for_continue, 0u);
467 EXPECT_FALSE(bi10->is_continue_entire_loop);
468
469 // Middle block is neither header nor merge
470 const auto* bi20 = fe.GetBlockInfo(20);
471 ASSERT_NE(bi20, nullptr);
472 EXPECT_EQ(bi20->merge_for_header, 0u);
473 EXPECT_EQ(bi20->continue_for_header, 0u);
474 EXPECT_EQ(bi20->header_for_merge, 0u);
475 EXPECT_EQ(bi20->header_for_continue, 0u);
476 EXPECT_FALSE(bi20->is_continue_entire_loop);
477
478 // Merge block points to the header
479 const auto* bi99 = fe.GetBlockInfo(99);
480 ASSERT_NE(bi99, nullptr);
481 EXPECT_EQ(bi99->merge_for_header, 0u);
482 EXPECT_EQ(bi99->continue_for_header, 0u);
483 EXPECT_EQ(bi99->header_for_merge, 10u);
484 EXPECT_EQ(bi99->header_for_continue, 0u);
485 EXPECT_FALSE(bi99->is_continue_entire_loop);
486 }
487
TEST_F(SpvParserCFGTest,RegisterMerges_GoodLoopMerge_SingleBlockLoop)488 TEST_F(SpvParserCFGTest, RegisterMerges_GoodLoopMerge_SingleBlockLoop) {
489 auto p = parser(test::Assemble(CommonTypes() + R"(
490 %100 = OpFunction %void None %voidfn
491
492 %10 = OpLabel
493 OpBranch %20
494
495 %20 = OpLabel
496 OpLoopMerge %99 %20 None
497 OpBranchConditional %cond %20 %99
498
499 %99 = OpLabel
500 OpReturn
501
502 OpFunctionEnd
503 )"));
504 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
505 auto fe = p->function_emitter(100);
506 fe.RegisterBasicBlocks();
507 EXPECT_TRUE(fe.RegisterMerges());
508
509 // Entry block is not special
510 const auto* bi10 = fe.GetBlockInfo(10);
511 ASSERT_NE(bi10, nullptr);
512 EXPECT_EQ(bi10->merge_for_header, 0u);
513 EXPECT_EQ(bi10->continue_for_header, 0u);
514 EXPECT_EQ(bi10->header_for_merge, 0u);
515 EXPECT_EQ(bi10->header_for_continue, 0u);
516 EXPECT_FALSE(bi10->is_continue_entire_loop);
517
518 // Single block loop is its own continue, and marked as single block loop.
519 const auto* bi20 = fe.GetBlockInfo(20);
520 ASSERT_NE(bi20, nullptr);
521 EXPECT_EQ(bi20->merge_for_header, 99u);
522 EXPECT_EQ(bi20->continue_for_header, 20u);
523 EXPECT_EQ(bi20->header_for_merge, 0u);
524 EXPECT_EQ(bi20->header_for_continue, 20u);
525 EXPECT_TRUE(bi20->is_continue_entire_loop);
526
527 // Merge block points to the header
528 const auto* bi99 = fe.GetBlockInfo(99);
529 ASSERT_NE(bi99, nullptr);
530 EXPECT_EQ(bi99->merge_for_header, 0u);
531 EXPECT_EQ(bi99->continue_for_header, 0u);
532 EXPECT_EQ(bi99->header_for_merge, 20u);
533 EXPECT_EQ(bi99->header_for_continue, 0u);
534 EXPECT_FALSE(bi99->is_continue_entire_loop);
535 }
536
TEST_F(SpvParserCFGTest,RegisterMerges_GoodLoopMerge_MultiBlockLoop_ContinueIsHeader)537 TEST_F(SpvParserCFGTest,
538 RegisterMerges_GoodLoopMerge_MultiBlockLoop_ContinueIsHeader) {
539 auto p = parser(test::Assemble(CommonTypes() + R"(
540 %100 = OpFunction %void None %voidfn
541
542 %10 = OpLabel
543 OpBranch %20
544
545 %20 = OpLabel
546 OpLoopMerge %99 %20 None
547 OpBranch %40
548
549 %40 = OpLabel
550 OpBranch %20
551
552 %99 = OpLabel
553 OpReturn
554
555 OpFunctionEnd
556 )"));
557 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
558 auto fe = p->function_emitter(100);
559 fe.RegisterBasicBlocks();
560 EXPECT_TRUE(fe.RegisterMerges());
561
562 // Loop header points to continue (itself) and merge
563 const auto* bi20 = fe.GetBlockInfo(20);
564 ASSERT_NE(bi20, nullptr);
565 EXPECT_EQ(bi20->merge_for_header, 99u);
566 EXPECT_EQ(bi20->continue_for_header, 20u);
567 EXPECT_EQ(bi20->header_for_merge, 0u);
568 EXPECT_EQ(bi20->header_for_continue, 20u);
569 EXPECT_TRUE(bi20->is_continue_entire_loop);
570
571 // Backedge block, but is not a declared header, merge, or continue
572 const auto* bi40 = fe.GetBlockInfo(40);
573 ASSERT_NE(bi40, nullptr);
574 EXPECT_EQ(bi40->merge_for_header, 0u);
575 EXPECT_EQ(bi40->continue_for_header, 0u);
576 EXPECT_EQ(bi40->header_for_merge, 0u);
577 EXPECT_EQ(bi40->header_for_continue, 0u);
578 EXPECT_FALSE(bi40->is_continue_entire_loop);
579
580 // Merge block points to the header
581 const auto* bi99 = fe.GetBlockInfo(99);
582 ASSERT_NE(bi99, nullptr);
583 EXPECT_EQ(bi99->merge_for_header, 0u);
584 EXPECT_EQ(bi99->continue_for_header, 0u);
585 EXPECT_EQ(bi99->header_for_merge, 20u);
586 EXPECT_EQ(bi99->header_for_continue, 0u);
587 EXPECT_FALSE(bi99->is_continue_entire_loop);
588 }
589
TEST_F(SpvParserCFGTest,RegisterMerges_GoodLoopMerge_MultiBlockLoop_ContinueIsNotHeader_Branch)590 TEST_F(SpvParserCFGTest,
591 RegisterMerges_GoodLoopMerge_MultiBlockLoop_ContinueIsNotHeader_Branch) {
592 auto p = parser(test::Assemble(CommonTypes() + R"(
593 %100 = OpFunction %void None %voidfn
594
595 %10 = OpLabel
596 OpBranch %20
597
598 %20 = OpLabel
599 OpLoopMerge %99 %40 None
600 OpBranch %30
601
602 %30 = OpLabel
603 OpBranchConditional %cond %40 %99
604
605 %40 = OpLabel
606 OpBranch %20
607
608 %99 = OpLabel
609 OpReturn
610
611 OpFunctionEnd
612 )"));
613 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
614 auto fe = p->function_emitter(100);
615 fe.RegisterBasicBlocks();
616 EXPECT_TRUE(fe.RegisterMerges());
617
618 // Loop header points to continue and merge
619 const auto* bi20 = fe.GetBlockInfo(20);
620 ASSERT_NE(bi20, nullptr);
621 EXPECT_EQ(bi20->merge_for_header, 99u);
622 EXPECT_EQ(bi20->continue_for_header, 40u);
623 EXPECT_EQ(bi20->header_for_merge, 0u);
624 EXPECT_EQ(bi20->header_for_continue, 0u);
625 EXPECT_FALSE(bi20->is_continue_entire_loop);
626
627 // Continue block points to header
628 const auto* bi40 = fe.GetBlockInfo(40);
629 ASSERT_NE(bi40, nullptr);
630 EXPECT_EQ(bi40->merge_for_header, 0u);
631 EXPECT_EQ(bi40->continue_for_header, 0u);
632 EXPECT_EQ(bi40->header_for_merge, 0u);
633 EXPECT_EQ(bi40->header_for_continue, 20u);
634 EXPECT_FALSE(bi40->is_continue_entire_loop);
635
636 // Merge block points to the header
637 const auto* bi99 = fe.GetBlockInfo(99);
638 ASSERT_NE(bi99, nullptr);
639 EXPECT_EQ(bi99->merge_for_header, 0u);
640 EXPECT_EQ(bi99->continue_for_header, 0u);
641 EXPECT_EQ(bi99->header_for_merge, 20u);
642 EXPECT_EQ(bi99->header_for_continue, 0u);
643 EXPECT_FALSE(bi99->is_continue_entire_loop);
644 }
645
TEST_F(SpvParserCFGTest,RegisterMerges_GoodLoopMerge_MultiBlockLoop_ContinueIsNotHeader_BranchConditional)646 TEST_F(
647 SpvParserCFGTest,
648 RegisterMerges_GoodLoopMerge_MultiBlockLoop_ContinueIsNotHeader_BranchConditional) { // NOLINT
649 auto p = parser(test::Assemble(CommonTypes() + R"(
650 %100 = OpFunction %void None %voidfn
651
652 %10 = OpLabel
653 OpBranch %20
654
655 %20 = OpLabel
656 OpLoopMerge %99 %40 None
657 OpBranchConditional %cond %30 %99
658
659 %30 = OpLabel
660 OpBranch %40
661
662 %40 = OpLabel
663 OpBranch %20
664
665 %99 = OpLabel
666 OpReturn
667
668 OpFunctionEnd
669 )"));
670 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
671 auto fe = p->function_emitter(100);
672 fe.RegisterBasicBlocks();
673 EXPECT_TRUE(fe.RegisterMerges());
674
675 // Loop header points to continue and merge
676 const auto* bi20 = fe.GetBlockInfo(20);
677 ASSERT_NE(bi20, nullptr);
678 EXPECT_EQ(bi20->merge_for_header, 99u);
679 EXPECT_EQ(bi20->continue_for_header, 40u);
680 EXPECT_EQ(bi20->header_for_merge, 0u);
681 EXPECT_EQ(bi20->header_for_continue, 0u);
682 EXPECT_FALSE(bi20->is_continue_entire_loop);
683
684 // Continue block points to header
685 const auto* bi40 = fe.GetBlockInfo(40);
686 ASSERT_NE(bi40, nullptr);
687 EXPECT_EQ(bi40->merge_for_header, 0u);
688 EXPECT_EQ(bi40->continue_for_header, 0u);
689 EXPECT_EQ(bi40->header_for_merge, 0u);
690 EXPECT_EQ(bi40->header_for_continue, 20u);
691 EXPECT_FALSE(bi40->is_continue_entire_loop);
692
693 // Merge block points to the header
694 const auto* bi99 = fe.GetBlockInfo(99);
695 ASSERT_NE(bi99, nullptr);
696 EXPECT_EQ(bi99->merge_for_header, 0u);
697 EXPECT_EQ(bi99->continue_for_header, 0u);
698 EXPECT_EQ(bi99->header_for_merge, 20u);
699 EXPECT_EQ(bi99->header_for_continue, 0u);
700 EXPECT_FALSE(bi99->is_continue_entire_loop);
701 }
702
TEST_F(SpvParserCFGTest,RegisterMerges_SelectionMerge_BadTerminator)703 TEST_F(SpvParserCFGTest, RegisterMerges_SelectionMerge_BadTerminator) {
704 auto p = parser(test::Assemble(CommonTypes() + R"(
705 %100 = OpFunction %void None %voidfn
706
707 %10 = OpLabel
708 OpSelectionMerge %99 None
709 OpBranch %20
710
711 %20 = OpLabel
712 OpBranch %99
713
714 %99 = OpLabel
715 OpReturn
716
717 OpFunctionEnd
718 )"));
719 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
720 auto fe = p->function_emitter(100);
721 fe.RegisterBasicBlocks();
722 EXPECT_FALSE(fe.RegisterMerges());
723 EXPECT_THAT(p->error(), Eq("Selection header 10 does not end in an "
724 "OpBranchConditional or OpSwitch instruction"));
725 }
726
TEST_F(SpvParserCFGTest,RegisterMerges_LoopMerge_BadTerminator)727 TEST_F(SpvParserCFGTest, RegisterMerges_LoopMerge_BadTerminator) {
728 auto p = parser(test::Assemble(CommonTypes() + R"(
729 %100 = OpFunction %void None %voidfn
730
731 %10 = OpLabel
732 OpBranch %20
733
734 %20 = OpLabel
735 OpLoopMerge %99 %40 None
736 OpSwitch %selector %99 30 %30
737
738 %30 = OpLabel
739 OpBranch %99
740
741 %40 = OpLabel
742 OpBranch %20
743
744 %99 = OpLabel
745 OpReturn
746
747 OpFunctionEnd
748 )"));
749 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
750 auto fe = p->function_emitter(100);
751 fe.RegisterBasicBlocks();
752 EXPECT_FALSE(fe.RegisterMerges());
753 EXPECT_THAT(p->error(), Eq("Loop header 20 does not end in an OpBranch or "
754 "OpBranchConditional instruction"));
755 }
756
TEST_F(SpvParserCFGTest,RegisterMerges_BadMergeBlock)757 TEST_F(SpvParserCFGTest, RegisterMerges_BadMergeBlock) {
758 auto p = parser(test::Assemble(CommonTypes() + R"(
759 %100 = OpFunction %void None %voidfn
760
761 %10 = OpLabel
762 OpSelectionMerge %void None
763 OpBranchConditional %cond %30 %99
764
765 %30 = OpLabel
766 OpBranch %99
767
768 %99 = OpLabel
769 OpReturn
770
771 OpFunctionEnd
772 )"));
773 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
774 auto fe = p->function_emitter(100);
775 fe.RegisterBasicBlocks();
776 EXPECT_FALSE(fe.RegisterMerges());
777 EXPECT_THAT(p->error(),
778 Eq("Structured header block 10 declares invalid merge block 2"));
779 }
780
TEST_F(SpvParserCFGTest,RegisterMerges_HeaderIsItsOwnMerge)781 TEST_F(SpvParserCFGTest, RegisterMerges_HeaderIsItsOwnMerge) {
782 auto p = parser(test::Assemble(CommonTypes() + R"(
783 %100 = OpFunction %void None %voidfn
784
785 %10 = OpLabel
786 OpSelectionMerge %10 None
787 OpBranchConditional %cond %30 %99
788
789 %30 = OpLabel
790 OpBranch %99
791
792 %99 = OpLabel
793 OpReturn
794
795 OpFunctionEnd
796 )"));
797 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
798 auto fe = p->function_emitter(100);
799 fe.RegisterBasicBlocks();
800 EXPECT_FALSE(fe.RegisterMerges());
801 EXPECT_THAT(p->error(),
802 Eq("Structured header block 10 cannot be its own merge block"));
803 }
804
TEST_F(SpvParserCFGTest,RegisterMerges_MergeReused)805 TEST_F(SpvParserCFGTest, RegisterMerges_MergeReused) {
806 auto p = parser(test::Assemble(CommonTypes() + R"(
807 %100 = OpFunction %void None %voidfn
808
809 %10 = OpLabel
810 OpSelectionMerge %49 None
811 OpBranchConditional %cond %20 %49
812
813 %20 = OpLabel
814 OpBranch %49
815
816 %49 = OpLabel
817 OpBranch %50
818
819 %50 = OpLabel
820 OpSelectionMerge %49 None ; can't reuse merge block
821 OpBranchConditional %cond %60 %99
822
823 %60 = OpLabel
824 OpBranch %99
825
826 %99 = OpLabel
827 OpReturn
828
829 OpFunctionEnd
830 )"));
831 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
832 auto fe = p->function_emitter(100);
833 fe.RegisterBasicBlocks();
834 EXPECT_FALSE(fe.RegisterMerges());
835 EXPECT_THAT(
836 p->error(),
837 Eq("Block 49 declared as merge block for more than one header: 10, 50"));
838 }
839
TEST_F(SpvParserCFGTest,RegisterMerges_EntryBlockIsLoopHeader)840 TEST_F(SpvParserCFGTest, RegisterMerges_EntryBlockIsLoopHeader) {
841 auto p = parser(test::Assemble(CommonTypes() + R"(
842 %100 = OpFunction %void None %voidfn
843
844 %10 = OpLabel
845 OpLoopMerge %99 %30 None
846 OpBranchConditional %cond %10 %99
847
848 %30 = OpLabel
849 OpBranch %10
850
851 %99 = OpLabel
852 OpReturn
853
854 OpFunctionEnd
855 )"));
856 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
857 auto fe = p->function_emitter(100);
858 fe.RegisterBasicBlocks();
859 EXPECT_FALSE(fe.RegisterMerges());
860 EXPECT_THAT(p->error(),
861 Eq("Function entry block 10 cannot be a loop header"));
862 }
863
TEST_F(SpvParserCFGTest,RegisterMerges_BadContinueTarget)864 TEST_F(SpvParserCFGTest, RegisterMerges_BadContinueTarget) {
865 auto p = parser(test::Assemble(CommonTypes() + R"(
866 %100 = OpFunction %void None %voidfn
867
868 %10 = OpLabel
869 OpBranch %20
870
871 %20 = OpLabel
872 OpLoopMerge %99 %999 None
873 OpBranchConditional %cond %20 %99
874
875 %99 = OpLabel
876 OpReturn
877
878 OpFunctionEnd
879 )"));
880 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
881 auto fe = p->function_emitter(100);
882 fe.RegisterBasicBlocks();
883 EXPECT_FALSE(fe.RegisterMerges());
884 EXPECT_THAT(p->error(),
885 Eq("Structured header 20 declares invalid continue target 999"));
886 }
887
TEST_F(SpvParserCFGTest,RegisterMerges_MergeSameAsContinue)888 TEST_F(SpvParserCFGTest, RegisterMerges_MergeSameAsContinue) {
889 auto p = parser(test::Assemble(CommonTypes() + R"(
890 %100 = OpFunction %void None %voidfn
891
892 %10 = OpLabel
893 OpBranch %20
894
895 %20 = OpLabel
896 OpLoopMerge %50 %50 None
897 OpBranchConditional %cond %20 %99
898
899
900 %50 = OpLabel
901 OpBranch %20
902
903 %99 = OpLabel
904 OpReturn
905
906 OpFunctionEnd
907 )"));
908 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
909 auto fe = p->function_emitter(100);
910 fe.RegisterBasicBlocks();
911 EXPECT_FALSE(fe.RegisterMerges());
912 EXPECT_THAT(p->error(),
913 Eq("Invalid structured header block 20: declares block 50 as "
914 "both its merge block and continue target"));
915 }
916
TEST_F(SpvParserCFGTest,RegisterMerges_ContinueReused)917 TEST_F(SpvParserCFGTest, RegisterMerges_ContinueReused) {
918 auto p = parser(test::Assemble(CommonTypes() + R"(
919 %100 = OpFunction %void None %voidfn
920
921 %10 = OpLabel
922 OpBranch %20
923
924 %20 = OpLabel
925 OpLoopMerge %49 %40 None
926 OpBranchConditional %cond %30 %49
927
928 %30 = OpLabel
929 OpBranch %40
930
931 %40 = OpLabel
932 OpBranch %20
933
934 %49 = OpLabel
935 OpBranch %50
936
937 %50 = OpLabel
938 OpLoopMerge %99 %40 None
939 OpBranchConditional %cond %60 %99
940
941 %60 = OpLabel
942 OpBranch %70
943
944 %70 = OpLabel
945 OpBranch %50
946
947 %99 = OpLabel
948 OpReturn
949
950 OpFunctionEnd
951 )"));
952 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
953 auto fe = p->function_emitter(100);
954 fe.RegisterBasicBlocks();
955 EXPECT_FALSE(fe.RegisterMerges());
956 EXPECT_THAT(p->error(), Eq("Block 40 declared as continue target for more "
957 "than one header: 20, 50"));
958 }
959
TEST_F(SpvParserCFGTest,RegisterMerges_SingleBlockLoop_NotItsOwnContinue)960 TEST_F(SpvParserCFGTest, RegisterMerges_SingleBlockLoop_NotItsOwnContinue) {
961 auto p = parser(test::Assemble(CommonTypes() + R"(
962 %100 = OpFunction %void None %voidfn
963
964 %10 = OpLabel
965 OpBranch %20
966
967 %20 = OpLabel
968 OpLoopMerge %99 %30 None
969 OpBranchConditional %cond %20 %99
970
971 %30 = OpLabel
972 OpBranch %20
973
974 %99 = OpLabel
975 OpReturn
976
977 OpFunctionEnd
978 )"));
979 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
980 auto fe = p->function_emitter(100);
981 fe.RegisterBasicBlocks();
982 EXPECT_FALSE(fe.RegisterMerges());
983 EXPECT_THAT(
984 p->error(),
985 Eq("Block 20 branches to itself but is not its own continue target"));
986 }
987
TEST_F(SpvParserCFGTest,ComputeBlockOrder_OneBlock)988 TEST_F(SpvParserCFGTest, ComputeBlockOrder_OneBlock) {
989 auto p = parser(test::Assemble(CommonTypes() + R"(
990 %100 = OpFunction %void None %voidfn
991
992 %42 = OpLabel
993 OpReturn
994
995 OpFunctionEnd
996 )"));
997 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
998 auto fe = p->function_emitter(100);
999 fe.RegisterBasicBlocks();
1000 fe.ComputeBlockOrderAndPositions();
1001
1002 EXPECT_THAT(fe.block_order(), ElementsAre(42));
1003
1004 const auto* bi = fe.GetBlockInfo(42);
1005 ASSERT_NE(bi, nullptr);
1006 EXPECT_EQ(bi->pos, 0u);
1007 }
1008
TEST_F(SpvParserCFGTest,ComputeBlockOrder_IgnoreStaticalyUnreachable)1009 TEST_F(SpvParserCFGTest, ComputeBlockOrder_IgnoreStaticalyUnreachable) {
1010 auto p = parser(test::Assemble(CommonTypes() + R"(
1011 %100 = OpFunction %void None %voidfn
1012
1013 %10 = OpLabel
1014 OpBranch %20
1015
1016 %15 = OpLabel ; statically dead
1017 OpReturn
1018
1019 %20 = OpLabel
1020 OpReturn
1021
1022 OpFunctionEnd
1023 )"));
1024 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1025 auto fe = p->function_emitter(100);
1026 fe.RegisterBasicBlocks();
1027 fe.ComputeBlockOrderAndPositions();
1028
1029 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20));
1030 }
1031
TEST_F(SpvParserCFGTest,ComputeBlockOrder_KillIsDeadEnd)1032 TEST_F(SpvParserCFGTest, ComputeBlockOrder_KillIsDeadEnd) {
1033 auto p = parser(test::Assemble(CommonTypes() + R"(
1034 %100 = OpFunction %void None %voidfn
1035
1036 %10 = OpLabel
1037 OpBranch %20
1038
1039 %15 = OpLabel ; statically dead
1040 OpReturn
1041
1042 %20 = OpLabel
1043 OpKill ; Kill doesn't lead anywhere
1044
1045 OpFunctionEnd
1046 )"));
1047 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1048 auto fe = p->function_emitter(100);
1049 fe.RegisterBasicBlocks();
1050 fe.ComputeBlockOrderAndPositions();
1051
1052 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20));
1053 }
1054
TEST_F(SpvParserCFGTest,ComputeBlockOrder_UnreachableIsDeadEnd)1055 TEST_F(SpvParserCFGTest, ComputeBlockOrder_UnreachableIsDeadEnd) {
1056 auto p = parser(test::Assemble(CommonTypes() + R"(
1057 %100 = OpFunction %void None %voidfn
1058
1059 %10 = OpLabel
1060 OpBranch %20
1061
1062 %15 = OpLabel ; statically dead
1063 OpReturn
1064
1065 %20 = OpLabel
1066 OpUnreachable ; Unreachable doesn't lead anywhere
1067
1068 OpFunctionEnd
1069 )"));
1070 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1071 auto fe = p->function_emitter(100);
1072 fe.RegisterBasicBlocks();
1073 fe.ComputeBlockOrderAndPositions();
1074
1075 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20));
1076 }
1077
TEST_F(SpvParserCFGTest,ComputeBlockOrder_ReorderSequence)1078 TEST_F(SpvParserCFGTest, ComputeBlockOrder_ReorderSequence) {
1079 auto p = parser(test::Assemble(CommonTypes() + R"(
1080 %100 = OpFunction %void None %voidfn
1081
1082 %10 = OpLabel
1083 OpSelectionMerge %99 None
1084 OpBranchConditional %cond %20 %30
1085
1086 %30 = OpLabel
1087 OpReturn
1088
1089 %20 = OpLabel
1090 OpBranch %30 ; backtrack, but does dominate %30
1091
1092 %99 = OpLabel
1093 OpReturn
1094
1095 OpFunctionEnd
1096 )"));
1097 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1098 auto fe = p->function_emitter(100);
1099 fe.RegisterBasicBlocks();
1100 fe.ComputeBlockOrderAndPositions();
1101
1102 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 99));
1103
1104 const auto* bi10 = fe.GetBlockInfo(10);
1105 ASSERT_NE(bi10, nullptr);
1106 EXPECT_EQ(bi10->pos, 0u);
1107 const auto* bi20 = fe.GetBlockInfo(20);
1108 ASSERT_NE(bi20, nullptr);
1109 EXPECT_EQ(bi20->pos, 1u);
1110 const auto* bi30 = fe.GetBlockInfo(30);
1111 ASSERT_NE(bi30, nullptr);
1112 EXPECT_EQ(bi30->pos, 2u);
1113 const auto* bi99 = fe.GetBlockInfo(99);
1114 ASSERT_NE(bi99, nullptr);
1115 EXPECT_EQ(bi99->pos, 3u);
1116 }
1117
TEST_F(SpvParserCFGTest,ComputeBlockOrder_DupConditionalBranch)1118 TEST_F(SpvParserCFGTest, ComputeBlockOrder_DupConditionalBranch) {
1119 auto p = parser(test::Assemble(CommonTypes() + R"(
1120 %100 = OpFunction %void None %voidfn
1121
1122 %10 = OpLabel
1123 OpSelectionMerge %99 None
1124 OpBranchConditional %cond %20 %20
1125
1126 %20 = OpLabel
1127 OpBranch %99
1128
1129 %99 = OpLabel
1130 OpReturn
1131
1132 OpFunctionEnd
1133 )"));
1134 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1135 auto fe = p->function_emitter(100);
1136 fe.RegisterBasicBlocks();
1137 fe.ComputeBlockOrderAndPositions();
1138
1139 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 99));
1140 }
1141
TEST_F(SpvParserCFGTest,ComputeBlockOrder_RespectConditionalBranchOrder)1142 TEST_F(SpvParserCFGTest, ComputeBlockOrder_RespectConditionalBranchOrder) {
1143 auto p = parser(test::Assemble(CommonTypes() + R"(
1144 %100 = OpFunction %void None %voidfn
1145
1146 %10 = OpLabel
1147 OpSelectionMerge %99 None
1148 OpBranchConditional %cond %20 %30
1149
1150 %30 = OpLabel
1151 OpReturn
1152
1153 %20 = OpLabel
1154 OpBranch %99
1155
1156 %99 = OpLabel ; dominated by %20, so follow %20
1157 OpReturn
1158
1159 OpFunctionEnd
1160 )"));
1161 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1162 auto fe = p->function_emitter(100);
1163 fe.RegisterBasicBlocks();
1164 fe.ComputeBlockOrderAndPositions();
1165
1166 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 99));
1167 }
1168
TEST_F(SpvParserCFGTest,ComputeBlockOrder_TrueOnlyBranch)1169 TEST_F(SpvParserCFGTest, ComputeBlockOrder_TrueOnlyBranch) {
1170 auto p = parser(test::Assemble(CommonTypes() + R"(
1171 %100 = OpFunction %void None %voidfn
1172
1173 %10 = OpLabel
1174 OpSelectionMerge %99 None
1175 OpBranchConditional %cond %20 %99
1176
1177 %99 = OpLabel
1178 OpReturn
1179
1180 %20 = OpLabel
1181 OpBranch %99
1182
1183 OpFunctionEnd
1184 )"));
1185 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1186 auto fe = p->function_emitter(100);
1187 fe.RegisterBasicBlocks();
1188 fe.ComputeBlockOrderAndPositions();
1189
1190 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 99));
1191 }
1192
TEST_F(SpvParserCFGTest,ComputeBlockOrder_FalseOnlyBranch)1193 TEST_F(SpvParserCFGTest, ComputeBlockOrder_FalseOnlyBranch) {
1194 auto p = parser(test::Assemble(CommonTypes() + R"(
1195 %100 = OpFunction %void None %voidfn
1196
1197 %10 = OpLabel
1198 OpSelectionMerge %99 None
1199 OpBranchConditional %cond %99 %20
1200
1201 %99 = OpLabel
1202 OpReturn
1203
1204 %20 = OpLabel
1205 OpBranch %99
1206
1207 OpFunctionEnd
1208 )"));
1209 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1210 auto fe = p->function_emitter(100);
1211 fe.RegisterBasicBlocks();
1212 fe.ComputeBlockOrderAndPositions();
1213
1214 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 99));
1215 }
1216
TEST_F(SpvParserCFGTest,ComputeBlockOrder_SwitchOrderNaturallyReversed)1217 TEST_F(SpvParserCFGTest, ComputeBlockOrder_SwitchOrderNaturallyReversed) {
1218 auto p = parser(test::Assemble(CommonTypes() + R"(
1219 %100 = OpFunction %void None %voidfn
1220
1221 %10 = OpLabel
1222 OpSelectionMerge %99 None
1223 OpSwitch %selector %99 20 %20 30 %30
1224
1225 %99 = OpLabel
1226 OpReturn
1227
1228 %30 = OpLabel
1229 OpReturn
1230
1231 %20 = OpLabel
1232 OpBranch %99
1233
1234 OpFunctionEnd
1235 )"));
1236 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1237 auto fe = p->function_emitter(100);
1238 fe.RegisterBasicBlocks();
1239 fe.ComputeBlockOrderAndPositions();
1240
1241 EXPECT_THAT(fe.block_order(), ElementsAre(10, 30, 20, 99));
1242 }
1243
TEST_F(SpvParserCFGTest,ComputeBlockOrder_SwitchWithDefaultOrderNaturallyReversed)1244 TEST_F(SpvParserCFGTest,
1245 ComputeBlockOrder_SwitchWithDefaultOrderNaturallyReversed) {
1246 auto p = parser(test::Assemble(CommonTypes() + R"(
1247 %100 = OpFunction %void None %voidfn
1248
1249 %10 = OpLabel
1250 OpSelectionMerge %99 None
1251 OpSwitch %selector %80 20 %20 30 %30
1252
1253 %80 = OpLabel ; the default case
1254 OpBranch %99
1255
1256 %99 = OpLabel
1257 OpReturn
1258
1259 %30 = OpLabel
1260 OpReturn
1261
1262 %20 = OpLabel
1263 OpBranch %99
1264
1265 OpFunctionEnd
1266 )"));
1267 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1268 auto fe = p->function_emitter(100);
1269 fe.RegisterBasicBlocks();
1270 fe.ComputeBlockOrderAndPositions();
1271
1272 EXPECT_THAT(fe.block_order(), ElementsAre(10, 30, 20, 80, 99));
1273 }
1274
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Switch_DefaultSameAsACase)1275 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Switch_DefaultSameAsACase) {
1276 auto p = parser(test::Assemble(CommonTypes() + R"(
1277 %100 = OpFunction %void None %voidfn
1278
1279 %10 = OpLabel
1280 OpSelectionMerge %99 None
1281 OpSwitch %selector %30 20 %20 30 %30 40 %40
1282
1283 %99 = OpLabel
1284 OpReturn
1285
1286 %30 = OpLabel
1287 OpBranch %99
1288
1289 %20 = OpLabel
1290 OpBranch %99
1291
1292 %40 = OpLabel
1293 OpBranch %99
1294
1295 OpFunctionEnd
1296 )"));
1297 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1298 auto fe = p->function_emitter(100);
1299 fe.RegisterBasicBlocks();
1300 fe.ComputeBlockOrderAndPositions();
1301
1302 EXPECT_THAT(fe.block_order(), ElementsAre(10, 40, 20, 30, 99));
1303 }
1304
TEST_F(SpvParserCFGTest,ComputeBlockOrder_RespectSwitchCaseFallthrough)1305 TEST_F(SpvParserCFGTest, ComputeBlockOrder_RespectSwitchCaseFallthrough) {
1306 auto assembly = CommonTypes() + R"(
1307 %100 = OpFunction %void None %voidfn
1308
1309 %10 = OpLabel
1310 OpSelectionMerge %99 None
1311 ; SPIR-V validation requires a fallthrough destination to immediately
1312 ; follow the source. So %20 -> %40, %30 -> %50
1313 OpSwitch %selector %99 20 %20 40 %40 30 %30 50 %50
1314
1315 %50 = OpLabel
1316 OpBranch %99
1317
1318 %99 = OpLabel
1319 OpReturn
1320
1321 %40 = OpLabel
1322 OpBranch %99
1323
1324 %30 = OpLabel
1325 OpBranch %50 ; fallthrough
1326
1327 %20 = OpLabel
1328 OpBranch %40 ; fallthrough
1329
1330 OpFunctionEnd
1331 )";
1332 auto p = parser(test::Assemble(assembly));
1333 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1334 auto fe = p->function_emitter(100);
1335 fe.RegisterBasicBlocks();
1336 fe.ComputeBlockOrderAndPositions();
1337
1338 EXPECT_THAT(fe.block_order(), ElementsAre(10, 30, 50, 20, 40, 99))
1339 << assembly;
1340 }
1341
TEST_F(SpvParserCFGTest,ComputeBlockOrder_RespectSwitchCaseFallthrough_FromDefault)1342 TEST_F(SpvParserCFGTest,
1343 ComputeBlockOrder_RespectSwitchCaseFallthrough_FromDefault) {
1344 auto assembly = CommonTypes() + R"(
1345 %100 = OpFunction %void None %voidfn
1346
1347 %10 = OpLabel
1348 OpSelectionMerge %99 None
1349 OpSwitch %selector %80 20 %20 30 %30 40 %40
1350
1351 %80 = OpLabel ; the default case
1352 OpBranch %30 ; fallthrough to another case
1353
1354 %99 = OpLabel
1355 OpReturn
1356
1357 %40 = OpLabel
1358 OpBranch %99
1359
1360 %30 = OpLabel
1361 OpBranch %40
1362
1363 %20 = OpLabel
1364 OpBranch %99
1365
1366 OpFunctionEnd
1367 )";
1368 auto p = parser(test::Assemble(assembly));
1369 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1370 auto fe = p->function_emitter(100);
1371 fe.RegisterBasicBlocks();
1372 fe.ComputeBlockOrderAndPositions();
1373
1374 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 80, 30, 40, 99))
1375 << assembly;
1376 }
1377
TEST_F(SpvParserCFGTest,ComputeBlockOrder_RespectSwitchCaseFallthrough_FromCaseToDefaultToCase)1378 TEST_F(SpvParserCFGTest,
1379 ComputeBlockOrder_RespectSwitchCaseFallthrough_FromCaseToDefaultToCase) {
1380 auto assembly = CommonTypes() + R"(
1381 %100 = OpFunction %void None %voidfn
1382
1383 %10 = OpLabel
1384 OpSelectionMerge %99 None
1385 OpSwitch %selector %80 20 %20 30 %30
1386
1387 %20 = OpLabel
1388 OpBranch %80 ; fallthrough to default
1389
1390 %80 = OpLabel ; the default case
1391 OpBranch %30 ; fallthrough to 30
1392
1393 %30 = OpLabel
1394 OpBranch %99
1395
1396 %99 = OpLabel ; dominated by %30, so follow %30
1397 OpReturn
1398
1399 OpFunctionEnd
1400 )";
1401 auto p = parser(test::Assemble(assembly));
1402 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1403 auto fe = p->function_emitter(100);
1404 fe.RegisterBasicBlocks();
1405 fe.ComputeBlockOrderAndPositions();
1406
1407 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 80, 30, 99)) << assembly;
1408 }
1409
TEST_F(SpvParserCFGTest,ComputeBlockOrder_SwitchCasesFallthrough_OppositeDirections)1410 TEST_F(SpvParserCFGTest,
1411 ComputeBlockOrder_SwitchCasesFallthrough_OppositeDirections) {
1412 auto assembly = CommonTypes() + R"(
1413 %100 = OpFunction %void None %voidfn
1414
1415 %10 = OpLabel
1416 OpSelectionMerge %99 None
1417 OpSwitch %selector %99 20 %20 30 %30 40 %40 50 %50
1418
1419 %99 = OpLabel
1420 OpReturn
1421
1422 %20 = OpLabel
1423 OpBranch %30 ; forward
1424
1425 %40 = OpLabel
1426 OpBranch %99
1427
1428 %30 = OpLabel
1429 OpBranch %99
1430
1431 ; SPIR-V doesn't actually allow a fall-through that goes backward in the
1432 ; module. But the block ordering algorithm tolerates it.
1433 %50 = OpLabel
1434 OpBranch %40 ; backward
1435
1436 OpFunctionEnd
1437 )";
1438 auto p = parser(test::Assemble(assembly));
1439 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1440 auto fe = p->function_emitter(100);
1441 fe.RegisterBasicBlocks();
1442 fe.ComputeBlockOrderAndPositions();
1443
1444 EXPECT_THAT(fe.block_order(), ElementsAre(10, 50, 40, 20, 30, 99))
1445 << assembly;
1446
1447 // We're deliberately testing a case that SPIR-V doesn't allow.
1448 p->DeliberatelyInvalidSpirv();
1449 }
1450
TEST_F(SpvParserCFGTest,ComputeBlockOrder_RespectSwitchCaseFallthrough_Interleaved)1451 TEST_F(SpvParserCFGTest,
1452 ComputeBlockOrder_RespectSwitchCaseFallthrough_Interleaved) {
1453 auto assembly = CommonTypes() + R"(
1454 %100 = OpFunction %void None %voidfn
1455
1456 %10 = OpLabel
1457 OpSelectionMerge %99 None
1458 ; SPIR-V validation requires a fallthrough destination to immediately
1459 ; follow the source. So %20 -> %40
1460 OpSwitch %selector %99 20 %20 40 %40 30 %30 50 %50
1461
1462 %99 = OpLabel
1463 OpReturn
1464
1465 %20 = OpLabel
1466 OpBranch %40
1467
1468 %30 = OpLabel
1469 OpBranch %50
1470
1471 %40 = OpLabel
1472 OpBranch %60
1473
1474 %50 = OpLabel
1475 OpBranch %70
1476
1477 %60 = OpLabel
1478 OpBranch %99
1479
1480 %70 = OpLabel
1481 OpBranch %99
1482
1483 OpFunctionEnd
1484 )";
1485 auto p = parser(test::Assemble(assembly));
1486 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1487 auto fe = p->function_emitter(100);
1488 fe.RegisterBasicBlocks();
1489 fe.ComputeBlockOrderAndPositions();
1490
1491 EXPECT_THAT(fe.block_order(), ElementsAre(10, 30, 50, 70, 20, 40, 60, 99))
1492 << assembly;
1493 }
1494
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Nest_If_Contains_If)1495 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Nest_If_Contains_If) {
1496 auto assembly = CommonTypes() + R"(
1497 %100 = OpFunction %void None %voidfn
1498
1499 %10 = OpLabel
1500 OpSelectionMerge %99 None
1501 OpBranchConditional %cond %20 %50
1502
1503 %99 = OpLabel
1504 OpReturn
1505
1506 %20 = OpLabel
1507 OpSelectionMerge %49 None
1508 OpBranchConditional %cond %30 %40
1509
1510 %49 = OpLabel
1511 OpBranch %99
1512
1513 %30 = OpLabel
1514 OpBranch %49
1515
1516 %40 = OpLabel
1517 OpBranch %49
1518
1519 %50 = OpLabel
1520 OpSelectionMerge %79 None
1521 OpBranchConditional %cond %60 %70
1522
1523 %79 = OpLabel
1524 OpBranch %99
1525
1526 %60 = OpLabel
1527 OpBranch %79
1528
1529 %70 = OpLabel
1530 OpBranch %79
1531
1532 OpFunctionEnd
1533 )";
1534 auto p = parser(test::Assemble(assembly));
1535 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1536 auto fe = p->function_emitter(100);
1537 fe.RegisterBasicBlocks();
1538 fe.ComputeBlockOrderAndPositions();
1539
1540 EXPECT_THAT(fe.block_order(),
1541 ElementsAre(10, 20, 30, 40, 49, 50, 60, 70, 79, 99))
1542 << assembly;
1543 }
1544
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Nest_If_In_SwitchCase)1545 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Nest_If_In_SwitchCase) {
1546 auto assembly = CommonTypes() + R"(
1547 %100 = OpFunction %void None %voidfn
1548
1549 %10 = OpLabel
1550 OpSelectionMerge %99 None
1551 OpSwitch %selector %50 20 %20 50 %50
1552
1553 %99 = OpLabel
1554 OpReturn
1555
1556 %20 = OpLabel
1557 OpSelectionMerge %49 None
1558 OpBranchConditional %cond %30 %40
1559
1560 %49 = OpLabel
1561 OpBranch %99
1562
1563 %30 = OpLabel
1564 OpBranch %49
1565
1566 %40 = OpLabel
1567 OpBranch %49
1568
1569 %50 = OpLabel
1570 OpSelectionMerge %79 None
1571 OpBranchConditional %cond %60 %70
1572
1573 %79 = OpLabel
1574 OpBranch %99
1575
1576 %60 = OpLabel
1577 OpBranch %79
1578
1579 %70 = OpLabel
1580 OpBranch %79
1581
1582 OpFunctionEnd
1583 )";
1584 auto p = parser(test::Assemble(assembly));
1585 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1586 auto fe = p->function_emitter(100);
1587 fe.RegisterBasicBlocks();
1588 fe.ComputeBlockOrderAndPositions();
1589
1590 EXPECT_THAT(fe.block_order(),
1591 ElementsAre(10, 20, 30, 40, 49, 50, 60, 70, 79, 99))
1592 << assembly;
1593 }
1594
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Nest_IfFallthrough_In_SwitchCase)1595 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Nest_IfFallthrough_In_SwitchCase) {
1596 auto assembly = CommonTypes() + R"(
1597 %100 = OpFunction %void None %voidfn
1598
1599 %10 = OpLabel
1600 OpSelectionMerge %99 None
1601 OpSwitch %selector %50 20 %20 50 %50
1602
1603 %99 = OpLabel
1604 OpReturn
1605
1606 %20 = OpLabel
1607 OpSelectionMerge %49 None
1608 OpBranchConditional %cond %30 %40
1609
1610 %49 = OpLabel
1611 OpBranchConditional %cond %99 %50 ; fallthrough
1612
1613 %30 = OpLabel
1614 OpBranch %49
1615
1616 %40 = OpLabel
1617 OpBranch %49
1618
1619 %50 = OpLabel
1620 OpSelectionMerge %79 None
1621 OpBranchConditional %cond %60 %70
1622
1623 %79 = OpLabel
1624 OpBranch %99
1625
1626 %60 = OpLabel
1627 OpBranch %79
1628
1629 %70 = OpLabel
1630 OpBranch %79
1631
1632 OpFunctionEnd
1633 )";
1634 auto p = parser(test::Assemble(assembly));
1635 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1636 auto fe = p->function_emitter(100);
1637 fe.RegisterBasicBlocks();
1638 fe.ComputeBlockOrderAndPositions();
1639
1640 EXPECT_THAT(fe.block_order(),
1641 ElementsAre(10, 20, 30, 40, 49, 50, 60, 70, 79, 99))
1642 << assembly;
1643 }
1644
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Nest_IfBreak_In_SwitchCase)1645 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Nest_IfBreak_In_SwitchCase) {
1646 auto assembly = CommonTypes() + R"(
1647 %100 = OpFunction %void None %voidfn
1648
1649 %10 = OpLabel
1650 OpSelectionMerge %99 None
1651 OpSwitch %selector %50 20 %20 50 %50
1652
1653 %99 = OpLabel
1654 OpReturn
1655
1656 %20 = OpLabel
1657 OpSelectionMerge %49 None
1658 OpBranchConditional %cond %99 %40 ; break-if
1659
1660 %40 = OpLabel
1661 OpBranch %49
1662
1663 %49 = OpLabel
1664 OpBranch %99
1665
1666 %50 = OpLabel
1667 OpSelectionMerge %79 None
1668 OpBranchConditional %cond %60 %99 ; break-unless
1669
1670 %60 = OpLabel
1671 OpBranch %79
1672
1673 %79 = OpLabel ; dominated by 60, so must follow 60
1674 OpBranch %99
1675
1676 OpFunctionEnd
1677 )";
1678 auto p = parser(test::Assemble(assembly));
1679 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1680 auto fe = p->function_emitter(100);
1681 fe.RegisterBasicBlocks();
1682 fe.ComputeBlockOrderAndPositions();
1683
1684 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 40, 49, 50, 60, 79, 99))
1685 << assembly;
1686 }
1687
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_SingleBlock_Simple)1688 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_SingleBlock_Simple) {
1689 auto assembly = CommonTypes() + R"(
1690 %100 = OpFunction %void None %voidfn
1691
1692 ; The entry block can't be the target of a branch
1693 %10 = OpLabel
1694 OpBranch %20
1695
1696 %20 = OpLabel
1697 OpLoopMerge %99 %20 None
1698 OpBranchConditional %cond %20 %99
1699
1700 %99 = OpLabel
1701 OpReturn
1702
1703 OpFunctionEnd
1704 )";
1705 auto p = parser(test::Assemble(assembly));
1706 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1707 auto fe = p->function_emitter(100);
1708 fe.RegisterBasicBlocks();
1709 fe.ComputeBlockOrderAndPositions();
1710
1711 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 99)) << assembly;
1712 }
1713
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_SingleBlock_Infinite)1714 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_SingleBlock_Infinite) {
1715 auto assembly = CommonTypes() + R"(
1716 %100 = OpFunction %void None %voidfn
1717
1718 ; The entry block can't be the target of a branch
1719 %10 = OpLabel
1720 OpBranch %20
1721
1722 %20 = OpLabel
1723 OpLoopMerge %99 %20 None
1724 OpBranch %20
1725
1726 %99 = OpLabel
1727 OpReturn
1728
1729 OpFunctionEnd
1730 )";
1731 auto p = parser(test::Assemble(assembly));
1732 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1733 auto fe = p->function_emitter(100);
1734 fe.RegisterBasicBlocks();
1735 fe.ComputeBlockOrderAndPositions();
1736
1737 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 99)) << assembly;
1738 }
1739
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_SingleBlock_DupInfinite)1740 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_SingleBlock_DupInfinite) {
1741 auto assembly = CommonTypes() + R"(
1742 %100 = OpFunction %void None %voidfn
1743
1744 ; The entry block can't be the target of a branch
1745 %10 = OpLabel
1746 OpBranch %20
1747
1748 %20 = OpLabel
1749 OpLoopMerge %99 %20 None
1750 OpBranchConditional %cond %20 %20
1751
1752 %99 = OpLabel
1753 OpReturn
1754
1755 OpFunctionEnd
1756 )";
1757 auto p = parser(test::Assemble(assembly));
1758 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1759 auto fe = p->function_emitter(100);
1760 fe.RegisterBasicBlocks();
1761 fe.ComputeBlockOrderAndPositions();
1762
1763 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 99)) << assembly;
1764 }
1765
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_HeaderHasBreakIf)1766 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_HeaderHasBreakIf) {
1767 auto assembly = CommonTypes() + R"(
1768 %100 = OpFunction %void None %voidfn
1769
1770 %10 = OpLabel
1771 OpBranch %20
1772
1773 %20 = OpLabel
1774 OpLoopMerge %99 %50 None
1775 OpBranchConditional %cond %30 %99 ; like While
1776
1777 %30 = OpLabel ; trivial body
1778 OpBranch %50
1779
1780 %50 = OpLabel
1781 OpBranch %20
1782
1783 %99 = OpLabel
1784 OpReturn
1785
1786 OpFunctionEnd
1787 )";
1788 auto p = parser(test::Assemble(assembly));
1789 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1790 auto fe = p->function_emitter(100);
1791 fe.RegisterBasicBlocks();
1792 fe.ComputeBlockOrderAndPositions();
1793
1794 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99)) << assembly;
1795 }
1796
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_HeaderHasBreakUnless)1797 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_HeaderHasBreakUnless) {
1798 auto assembly = CommonTypes() + R"(
1799 %100 = OpFunction %void None %voidfn
1800
1801 %10 = OpLabel
1802 OpBranch %20
1803
1804 %20 = OpLabel
1805 OpLoopMerge %99 %50 None
1806 OpBranchConditional %cond %99 %30 ; has break-unless
1807
1808 %30 = OpLabel ; trivial body
1809 OpBranch %50
1810
1811 %50 = OpLabel
1812 OpBranch %20
1813
1814 %99 = OpLabel
1815 OpReturn
1816
1817 OpFunctionEnd
1818 )";
1819 auto p = parser(test::Assemble(assembly));
1820 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1821 auto fe = p->function_emitter(100);
1822 fe.RegisterBasicBlocks();
1823 fe.ComputeBlockOrderAndPositions();
1824
1825 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99)) << assembly;
1826 }
1827
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_BodyHasBreak)1828 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_BodyHasBreak) {
1829 auto assembly = CommonTypes() + R"(
1830 %100 = OpFunction %void None %voidfn
1831
1832 %10 = OpLabel
1833 OpBranch %20
1834
1835 %20 = OpLabel
1836 OpLoopMerge %99 %50 None
1837 OpBranchConditional %cond %30 %99
1838
1839 %30 = OpLabel
1840 OpBranch %99 ; break
1841
1842 %50 = OpLabel
1843 OpBranch %20
1844
1845 %99 = OpLabel
1846 OpReturn
1847
1848 OpFunctionEnd
1849 )";
1850 auto p = parser(test::Assemble(assembly));
1851 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1852 auto fe = p->function_emitter(100);
1853 fe.RegisterBasicBlocks();
1854 fe.ComputeBlockOrderAndPositions();
1855
1856 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99)) << assembly;
1857 }
1858
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_BodyHasBreakIf)1859 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_BodyHasBreakIf) {
1860 auto assembly = CommonTypes() + R"(
1861 %100 = OpFunction %void None %voidfn
1862
1863 %10 = OpLabel
1864 OpBranch %20
1865
1866 %20 = OpLabel
1867 OpLoopMerge %99 %50 None
1868 OpBranchConditional %cond %30 %99
1869
1870 %30 = OpLabel
1871 OpBranchConditional %cond2 %99 %40 ; break-if
1872
1873 %40 = OpLabel
1874 OpBranch %50
1875
1876 %50 = OpLabel
1877 OpBranch %20
1878
1879 %99 = OpLabel
1880 OpReturn
1881
1882 OpFunctionEnd
1883 )";
1884 auto p = parser(test::Assemble(assembly));
1885 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1886 auto fe = p->function_emitter(100);
1887 fe.RegisterBasicBlocks();
1888 fe.ComputeBlockOrderAndPositions();
1889
1890 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 50, 99))
1891 << assembly;
1892 }
1893
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_BodyHasBreakUnless)1894 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_BodyHasBreakUnless) {
1895 auto assembly = CommonTypes() + R"(
1896 %100 = OpFunction %void None %voidfn
1897
1898 %10 = OpLabel
1899 OpBranch %20
1900
1901 %20 = OpLabel
1902 OpLoopMerge %99 %50 None
1903 OpBranchConditional %cond %30 %99
1904
1905 %30 = OpLabel
1906 OpBranchConditional %cond2 %40 %99 ; break-unless
1907
1908 %40 = OpLabel
1909 OpBranch %50
1910
1911 %50 = OpLabel
1912 OpBranch %20
1913
1914 %99 = OpLabel
1915 OpReturn
1916
1917 OpFunctionEnd
1918 )";
1919 auto p = parser(test::Assemble(assembly));
1920 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1921 auto fe = p->function_emitter(100);
1922 fe.RegisterBasicBlocks();
1923 fe.ComputeBlockOrderAndPositions();
1924
1925 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 50, 99))
1926 << assembly;
1927 }
1928
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Body_If)1929 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Body_If) {
1930 auto assembly = CommonTypes() + R"(
1931 %100 = OpFunction %void None %voidfn
1932
1933 %10 = OpLabel
1934 OpBranch %20
1935
1936 %20 = OpLabel
1937 OpLoopMerge %99 %50 None
1938 OpBranchConditional %cond %30 %99
1939
1940 %30 = OpLabel
1941 OpSelectionMerge %49 None
1942 OpBranchConditional %cond2 %40 %45 ; nested if
1943
1944 %40 = OpLabel
1945 OpBranch %49
1946
1947 %45 = OpLabel
1948 OpBranch %49
1949
1950 %49 = OpLabel
1951 OpBranch %50
1952
1953 %50 = OpLabel
1954 OpBranch %20
1955
1956 %99 = OpLabel
1957 OpReturn
1958
1959 OpFunctionEnd
1960 )";
1961 auto p = parser(test::Assemble(assembly));
1962 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
1963 auto fe = p->function_emitter(100);
1964 fe.RegisterBasicBlocks();
1965 fe.ComputeBlockOrderAndPositions();
1966
1967 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 45, 49, 50, 99))
1968 << assembly;
1969 }
1970
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Body_If_Break)1971 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Body_If_Break) {
1972 auto assembly = CommonTypes() + R"(
1973 %100 = OpFunction %void None %voidfn
1974
1975 %10 = OpLabel
1976 OpBranch %20
1977
1978 %20 = OpLabel
1979 OpLoopMerge %99 %50 None
1980 OpBranchConditional %cond %30 %99
1981
1982 %30 = OpLabel
1983 OpSelectionMerge %49 None
1984 OpBranchConditional %cond2 %40 %49 ; nested if
1985
1986 %40 = OpLabel
1987 OpBranch %99 ; break from nested if
1988
1989 %49 = OpLabel
1990 OpBranch %50
1991
1992 %50 = OpLabel
1993 OpBranch %20
1994
1995 %99 = OpLabel
1996 OpReturn
1997
1998 OpFunctionEnd
1999 )";
2000 auto p = parser(test::Assemble(assembly));
2001 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2002 auto fe = p->function_emitter(100);
2003 fe.RegisterBasicBlocks();
2004 fe.ComputeBlockOrderAndPositions();
2005
2006 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 49, 50, 99))
2007 << assembly;
2008 }
2009
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_BodyHasContinueIf)2010 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_BodyHasContinueIf) {
2011 auto assembly = CommonTypes() + R"(
2012 %100 = OpFunction %void None %voidfn
2013
2014 %10 = OpLabel
2015 OpBranch %20
2016
2017 %20 = OpLabel
2018 OpLoopMerge %99 %50 None
2019 OpBranchConditional %cond %30 %99
2020
2021 %30 = OpLabel
2022 OpBranchConditional %cond2 %50 %40 ; continue-if
2023
2024 %40 = OpLabel
2025 OpBranch %50
2026
2027 %50 = OpLabel
2028 OpBranch %20
2029
2030 %99 = OpLabel
2031 OpReturn
2032
2033 OpFunctionEnd
2034 )";
2035 auto p = parser(test::Assemble(assembly));
2036 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2037 auto fe = p->function_emitter(100);
2038 fe.RegisterBasicBlocks();
2039 fe.ComputeBlockOrderAndPositions();
2040
2041 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 50, 99))
2042 << assembly;
2043 }
2044
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_BodyHasContinueUnless)2045 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_BodyHasContinueUnless) {
2046 auto assembly = CommonTypes() + R"(
2047 %100 = OpFunction %void None %voidfn
2048
2049 %10 = OpLabel
2050 OpBranch %20
2051
2052 %20 = OpLabel
2053 OpLoopMerge %99 %50 None
2054 OpBranchConditional %cond %30 %99
2055
2056 %30 = OpLabel
2057 OpBranchConditional %cond2 %40 %50 ; continue-unless
2058
2059 %40 = OpLabel
2060 OpBranch %50
2061
2062 %50 = OpLabel
2063 OpBranch %20
2064
2065 %99 = OpLabel
2066 OpReturn
2067
2068 OpFunctionEnd
2069 )";
2070 auto p = parser(test::Assemble(assembly));
2071 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2072 auto fe = p->function_emitter(100);
2073 fe.RegisterBasicBlocks();
2074 fe.ComputeBlockOrderAndPositions();
2075
2076 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 50, 99))
2077 << assembly;
2078 }
2079
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Body_If_Continue)2080 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Body_If_Continue) {
2081 auto assembly = CommonTypes() + R"(
2082 %100 = OpFunction %void None %voidfn
2083
2084 %10 = OpLabel
2085 OpBranch %20
2086
2087 %20 = OpLabel
2088 OpLoopMerge %99 %50 None
2089 OpBranchConditional %cond %30 %99
2090
2091 %30 = OpLabel
2092 OpSelectionMerge %49 None
2093 OpBranchConditional %cond2 %40 %49 ; nested if
2094
2095 %40 = OpLabel
2096 OpBranch %50 ; continue from nested if
2097
2098 %49 = OpLabel
2099 OpBranch %50
2100
2101 %50 = OpLabel
2102 OpBranch %20
2103
2104 %99 = OpLabel
2105 OpReturn
2106
2107 OpFunctionEnd
2108 )";
2109 auto p = parser(test::Assemble(assembly));
2110 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2111 auto fe = p->function_emitter(100);
2112 fe.RegisterBasicBlocks();
2113 fe.ComputeBlockOrderAndPositions();
2114
2115 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 40, 49, 50, 99))
2116 << assembly;
2117 }
2118
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Body_Switch)2119 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Body_Switch) {
2120 auto assembly = CommonTypes() + R"(
2121 %100 = OpFunction %void None %voidfn
2122
2123 %10 = OpLabel
2124 OpBranch %20
2125
2126 %20 = OpLabel
2127 OpLoopMerge %99 %50 None
2128 OpBranchConditional %cond %30 %99
2129
2130 %30 = OpLabel
2131 OpSelectionMerge %49 None
2132 OpSwitch %selector %49 40 %40 45 %45 ; fully nested switch
2133
2134 %40 = OpLabel
2135 OpBranch %49
2136
2137 %45 = OpLabel
2138 OpBranch %49
2139
2140 %49 = OpLabel
2141 OpBranch %50
2142
2143 %50 = OpLabel
2144 OpBranch %20
2145
2146 %99 = OpLabel
2147 OpReturn
2148
2149 OpFunctionEnd
2150 )";
2151 auto p = parser(test::Assemble(assembly));
2152 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2153 auto fe = p->function_emitter(100);
2154 fe.RegisterBasicBlocks();
2155 fe.ComputeBlockOrderAndPositions();
2156
2157 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 45, 40, 49, 50, 99))
2158 << assembly;
2159 }
2160
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Body_Switch_CaseBreaks)2161 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Body_Switch_CaseBreaks) {
2162 auto assembly = CommonTypes() + R"(
2163 %100 = OpFunction %void None %voidfn
2164
2165 %10 = OpLabel
2166 OpBranch %20
2167
2168 %20 = OpLabel
2169 OpLoopMerge %99 %50 None
2170 OpBranchConditional %cond %30 %99
2171
2172 %30 = OpLabel
2173 OpSelectionMerge %49 None
2174 OpSwitch %selector %49 40 %40 45 %45
2175
2176 %40 = OpLabel
2177 ; This case breaks out of the loop. This is not possible in C
2178 ; because "break" will escape the switch only.
2179 OpBranch %99
2180
2181 %45 = OpLabel
2182 OpBranch %49
2183
2184 %49 = OpLabel
2185 OpBranch %50
2186
2187 %50 = OpLabel
2188 OpBranch %20
2189
2190 %99 = OpLabel
2191 OpReturn
2192
2193 OpFunctionEnd
2194 )";
2195 auto p = parser(test::Assemble(assembly));
2196 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2197 auto fe = p->function_emitter(100);
2198 fe.RegisterBasicBlocks();
2199 fe.ComputeBlockOrderAndPositions();
2200
2201 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 45, 40, 49, 50, 99))
2202 << assembly;
2203
2204 // Fails SPIR-V validation:
2205 // Branch from block 40 to block 99 is an invalid exit from construct starting
2206 // at block 30; branch bypasses merge block 49
2207 p->DeliberatelyInvalidSpirv();
2208 }
2209
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Body_Switch_CaseContinues)2210 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Body_Switch_CaseContinues) {
2211 auto assembly = CommonTypes() + R"(
2212 %100 = OpFunction %void None %voidfn
2213
2214 %10 = OpLabel
2215 OpBranch %20
2216
2217 %20 = OpLabel
2218 OpLoopMerge %99 %50 None
2219 OpBranchConditional %cond %30 %99
2220
2221 %30 = OpLabel
2222 OpSelectionMerge %49 None
2223 OpSwitch %selector %49 40 %40 45 %45
2224
2225 %40 = OpLabel
2226 OpBranch %50 ; continue bypasses switch merge
2227
2228 %45 = OpLabel
2229 OpBranch %49
2230
2231 %49 = OpLabel
2232 OpBranch %50
2233
2234 %50 = OpLabel
2235 OpBranch %20
2236
2237 %99 = OpLabel
2238 OpReturn
2239
2240 OpFunctionEnd
2241 )";
2242 auto p = parser(test::Assemble(assembly));
2243 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2244 auto fe = p->function_emitter(100);
2245 fe.RegisterBasicBlocks();
2246 fe.ComputeBlockOrderAndPositions();
2247
2248 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 45, 40, 49, 50, 99))
2249 << assembly;
2250 }
2251
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_BodyHasSwitchContinueBreak)2252 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_BodyHasSwitchContinueBreak) {
2253 auto assembly = CommonTypes() + R"(
2254 %100 = OpFunction %void None %voidfn
2255
2256 %10 = OpLabel
2257 OpBranch %20
2258
2259 %20 = OpLabel
2260 OpLoopMerge %99 %50 None
2261 OpBranchConditional %cond %30 %99
2262
2263 %30 = OpLabel
2264 ; OpSwitch must be preceded by a selection merge
2265 OpSwitch %selector %99 50 %50 ; default is break, 50 is continue
2266
2267 %40 = OpLabel
2268 OpBranch %50
2269
2270 %50 = OpLabel
2271 OpBranch %20
2272
2273 %99 = OpLabel
2274 OpReturn
2275
2276 OpFunctionEnd
2277 )";
2278 auto p = parser(test::Assemble(assembly));
2279 EXPECT_FALSE(p->Parse());
2280 EXPECT_FALSE(p->success());
2281 EXPECT_THAT(p->error(),
2282 HasSubstr("OpSwitch must be preceeded by an OpSelectionMerge"));
2283 }
2284
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Continue_Sequence)2285 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Continue_Sequence) {
2286 auto assembly = CommonTypes() + R"(
2287 %100 = OpFunction %void None %voidfn
2288
2289 %10 = OpLabel
2290 OpBranch %20
2291
2292 %20 = OpLabel
2293 OpLoopMerge %99 %50 None
2294 OpBranchConditional %cond %30 %99
2295
2296 %30 = OpLabel
2297 OpBranch %50
2298
2299 %50 = OpLabel
2300 OpBranch %60
2301
2302 %60 = OpLabel
2303 OpBranch %20
2304
2305 %99 = OpLabel
2306 OpReturn
2307
2308 OpFunctionEnd
2309 )";
2310 auto p = parser(test::Assemble(assembly));
2311 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2312 auto fe = p->function_emitter(100);
2313 fe.RegisterBasicBlocks();
2314 fe.ComputeBlockOrderAndPositions();
2315
2316 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 60, 99));
2317 }
2318
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Continue_ContainsIf)2319 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Continue_ContainsIf) {
2320 auto assembly = CommonTypes() + R"(
2321 %100 = OpFunction %void None %voidfn
2322
2323 %10 = OpLabel
2324 OpBranch %20
2325
2326 %20 = OpLabel
2327 OpLoopMerge %99 %50 None
2328 OpBranchConditional %cond %30 %99
2329
2330 %30 = OpLabel
2331 OpBranch %50
2332
2333 %50 = OpLabel
2334 OpSelectionMerge %89 None
2335 OpBranchConditional %cond2 %60 %70
2336
2337 %89 = OpLabel
2338 OpBranch %20 ; backedge
2339
2340 %60 = OpLabel
2341 OpBranch %89
2342
2343 %70 = OpLabel
2344 OpBranch %89
2345
2346 %99 = OpLabel
2347 OpReturn
2348
2349 OpFunctionEnd
2350 )";
2351 auto p = parser(test::Assemble(assembly));
2352 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2353 auto fe = p->function_emitter(100);
2354 fe.RegisterBasicBlocks();
2355 fe.ComputeBlockOrderAndPositions();
2356
2357 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 60, 70, 89, 99));
2358 }
2359
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Continue_HasBreakIf)2360 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Continue_HasBreakIf) {
2361 auto assembly = CommonTypes() + R"(
2362 %100 = OpFunction %void None %voidfn
2363
2364 %10 = OpLabel
2365 OpBranch %20
2366
2367 %20 = OpLabel
2368 OpLoopMerge %99 %50 None
2369 OpBranchConditional %cond %30 %99
2370
2371 %30 = OpLabel
2372 OpBranch %50
2373
2374 %50 = OpLabel
2375 OpBranchConditional %cond2 %99 %20
2376
2377 %99 = OpLabel
2378 OpReturn
2379
2380 OpFunctionEnd
2381 )";
2382 auto p = parser(test::Assemble(assembly));
2383 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2384 auto fe = p->function_emitter(100);
2385 fe.RegisterBasicBlocks();
2386 fe.ComputeBlockOrderAndPositions();
2387
2388 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99));
2389 }
2390
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Continue_HasBreakUnless)2391 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Continue_HasBreakUnless) {
2392 auto assembly = CommonTypes() + R"(
2393 %100 = OpFunction %void None %voidfn
2394
2395 %10 = OpLabel
2396 OpBranch %20
2397
2398 %20 = OpLabel
2399 OpLoopMerge %99 %50 None
2400 OpBranchConditional %cond %30 %99
2401
2402 %30 = OpLabel
2403 OpBranch %50
2404
2405 %50 = OpLabel
2406 OpBranchConditional %cond2 %20 %99
2407
2408 %99 = OpLabel
2409 OpReturn
2410
2411 OpFunctionEnd
2412 )";
2413 auto p = parser(test::Assemble(assembly));
2414 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2415 auto fe = p->function_emitter(100);
2416 fe.RegisterBasicBlocks();
2417 fe.ComputeBlockOrderAndPositions();
2418
2419 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99));
2420 }
2421
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Continue_SwitchBreak)2422 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Continue_SwitchBreak) {
2423 auto assembly = CommonTypes() + R"(
2424 %100 = OpFunction %void None %voidfn
2425
2426 %10 = OpLabel
2427 OpBranch %20
2428
2429 %20 = OpLabel
2430 OpLoopMerge %99 %50 None
2431 OpBranchConditional %cond %30 %99
2432
2433 %30 = OpLabel
2434 OpBranch %50
2435
2436 %50 = OpLabel
2437 ; Updated SPIR-V rule:
2438 ; OpSwitch must be preceded by a selection.
2439 OpSwitch %selector %20 99 %99
2440
2441 %99 = OpLabel
2442 OpReturn
2443
2444 OpFunctionEnd
2445 )";
2446 auto p = parser(test::Assemble(assembly));
2447 EXPECT_FALSE(p->Parse());
2448 EXPECT_FALSE(p->success());
2449 EXPECT_THAT(p->error(),
2450 HasSubstr("OpSwitch must be preceeded by an OpSelectionMerge"));
2451 }
2452
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Loop)2453 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Loop) {
2454 auto assembly = CommonTypes() + R"(
2455 %100 = OpFunction %void None %voidfn
2456
2457 %10 = OpLabel
2458 OpBranch %20
2459
2460 %20 = OpLabel
2461 OpLoopMerge %99 %50 None
2462 OpBranchConditional %cond %30 %99
2463
2464 %30 = OpLabel
2465 OpLoopMerge %49 %40 None
2466 OpBranchConditional %cond2 %35 %49
2467
2468 %35 = OpLabel
2469 OpBranch %37
2470
2471 %37 = OpLabel
2472 OpBranch %40
2473
2474 %40 = OpLabel ; inner loop's continue
2475 OpBranch %30 ; backedge
2476
2477 %49 = OpLabel ; inner loop's merge
2478 OpBranch %50
2479
2480 %50 = OpLabel ; outer loop's continue
2481 OpBranch %20 ; outer loop's backege
2482
2483 %99 = OpLabel
2484 OpReturn
2485
2486 OpFunctionEnd
2487 )";
2488 auto p = parser(test::Assemble(assembly));
2489 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2490 auto fe = p->function_emitter(100);
2491 fe.RegisterBasicBlocks();
2492 fe.ComputeBlockOrderAndPositions();
2493
2494 EXPECT_THAT(fe.block_order(),
2495 ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
2496 }
2497
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Loop_InnerBreak)2498 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Loop_InnerBreak) {
2499 auto assembly = CommonTypes() + R"(
2500 %100 = OpFunction %void None %voidfn
2501
2502 %10 = OpLabel
2503 OpBranch %20
2504
2505 %20 = OpLabel
2506 OpLoopMerge %99 %50 None
2507 OpBranchConditional %cond %30 %99
2508
2509 %30 = OpLabel
2510 OpLoopMerge %49 %40 None
2511 OpBranchConditional %cond2 %35 %49
2512
2513 %35 = OpLabel
2514 OpBranchConditional %cond3 %49 %37 ; break to inner merge
2515
2516 %37 = OpLabel
2517 OpBranch %40
2518
2519 %40 = OpLabel ; inner loop's continue
2520 OpBranch %30 ; backedge
2521
2522 %49 = OpLabel ; inner loop's merge
2523 OpBranch %50
2524
2525 %50 = OpLabel ; outer loop's continue
2526 OpBranch %20 ; outer loop's backege
2527
2528 %99 = OpLabel
2529 OpReturn
2530
2531 OpFunctionEnd
2532 )";
2533 auto p = parser(test::Assemble(assembly));
2534 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2535 auto fe = p->function_emitter(100);
2536 fe.RegisterBasicBlocks();
2537 fe.ComputeBlockOrderAndPositions();
2538
2539 EXPECT_THAT(fe.block_order(),
2540 ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
2541 }
2542
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Loop_InnerContinue)2543 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Loop_InnerContinue) {
2544 auto assembly = CommonTypes() + R"(
2545 %100 = OpFunction %void None %voidfn
2546
2547 %10 = OpLabel
2548 OpBranch %20
2549
2550 %20 = OpLabel
2551 OpLoopMerge %99 %50 None
2552 OpBranchConditional %cond %30 %99
2553
2554 %30 = OpLabel
2555 OpLoopMerge %49 %40 None
2556 OpBranchConditional %cond2 %35 %49
2557
2558 %35 = OpLabel
2559 OpBranchConditional %cond3 %37 %49 ; continue to inner continue target
2560
2561 %37 = OpLabel
2562 OpBranch %40
2563
2564 %40 = OpLabel ; inner loop's continue
2565 OpBranch %30 ; backedge
2566
2567 %49 = OpLabel ; inner loop's merge
2568 OpBranch %50
2569
2570 %50 = OpLabel ; outer loop's continue
2571 OpBranch %20 ; outer loop's backege
2572
2573 %99 = OpLabel
2574 OpReturn
2575
2576 OpFunctionEnd
2577 )";
2578 auto p = parser(test::Assemble(assembly));
2579 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2580 auto fe = p->function_emitter(100);
2581 fe.RegisterBasicBlocks();
2582 fe.ComputeBlockOrderAndPositions();
2583
2584 EXPECT_THAT(fe.block_order(),
2585 ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
2586 }
2587
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Loop_InnerContinueBreaks)2588 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Loop_InnerContinueBreaks) {
2589 auto assembly = CommonTypes() + R"(
2590 %100 = OpFunction %void None %voidfn
2591
2592 %10 = OpLabel
2593 OpBranch %20
2594
2595 %20 = OpLabel
2596 OpLoopMerge %99 %50 None
2597 OpBranchConditional %cond %30 %99
2598
2599 %30 = OpLabel
2600 OpLoopMerge %49 %40 None
2601 OpBranchConditional %cond2 %35 %49
2602
2603 %35 = OpLabel
2604 OpBranch %37
2605
2606 %37 = OpLabel
2607 OpBranch %40
2608
2609 %40 = OpLabel ; inner loop's continue
2610 OpBranchConditional %cond3 %30 %49 ; backedge and inner break
2611
2612 %49 = OpLabel ; inner loop's merge
2613 OpBranch %50
2614
2615 %50 = OpLabel ; outer loop's continue
2616 OpBranch %20 ; outer loop's backege
2617
2618 %99 = OpLabel
2619 OpReturn
2620
2621 OpFunctionEnd
2622 )";
2623 auto p = parser(test::Assemble(assembly));
2624 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2625 auto fe = p->function_emitter(100);
2626 fe.RegisterBasicBlocks();
2627 fe.ComputeBlockOrderAndPositions();
2628
2629 EXPECT_THAT(fe.block_order(),
2630 ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
2631 }
2632
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Loop_InnerContinueContinues)2633 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Loop_InnerContinueContinues) {
2634 auto assembly = CommonTypes() + R"(
2635 %100 = OpFunction %void None %voidfn
2636
2637 %10 = OpLabel
2638 OpBranch %20
2639
2640 %20 = OpLabel
2641 OpLoopMerge %99 %50 None
2642 OpBranchConditional %cond %30 %99
2643
2644 %30 = OpLabel
2645 OpLoopMerge %49 %40 None
2646 OpBranchConditional %cond2 %35 %49
2647
2648 %35 = OpLabel
2649 OpBranch %37
2650
2651 %37 = OpLabel
2652 OpBranch %40
2653
2654 %40 = OpLabel ; inner loop's continue
2655 OpBranchConditional %cond3 %30 %50 ; backedge and continue to outer
2656
2657 %49 = OpLabel ; inner loop's merge
2658 OpBranch %50
2659
2660 %50 = OpLabel ; outer loop's continue
2661 OpBranch %20 ; outer loop's backege
2662
2663 %99 = OpLabel
2664 OpReturn
2665
2666 OpFunctionEnd
2667 )";
2668 auto p = parser(test::Assemble(assembly));
2669 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2670 auto fe = p->function_emitter(100);
2671 fe.RegisterBasicBlocks();
2672 fe.ComputeBlockOrderAndPositions();
2673
2674 EXPECT_THAT(fe.block_order(),
2675 ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
2676
2677 p->DeliberatelyInvalidSpirv();
2678 // SPIR-V validation fails:
2679 // block <ID> 40[%40] exits the continue headed by <ID> 40[%40], but not
2680 // via a structured exit"
2681 }
2682
TEST_F(SpvParserCFGTest,ComputeBlockOrder_Loop_Loop_SwitchBackedgeBreakContinue)2683 TEST_F(SpvParserCFGTest,
2684 ComputeBlockOrder_Loop_Loop_SwitchBackedgeBreakContinue) {
2685 auto assembly = CommonTypes() + R"(
2686 %100 = OpFunction %void None %voidfn
2687
2688 %10 = OpLabel
2689 OpBranch %20
2690
2691 %20 = OpLabel
2692 OpLoopMerge %99 %50 None
2693 OpBranchConditional %cond %30 %99
2694
2695 %30 = OpLabel
2696 OpLoopMerge %49 %40 None
2697 OpBranchConditional %cond2 %35 %49
2698
2699 %35 = OpLabel
2700 OpBranch %37
2701
2702 %37 = OpLabel
2703 OpBranch %40
2704
2705 %40 = OpLabel ; inner loop's continue
2706 ; This switch does triple duty:
2707 ; default -> backedge
2708 ; 49 -> loop break
2709 ; 49 -> inner loop break
2710 ; 50 -> outer loop continue
2711 OpSwitch %selector %30 49 %49 50 %50
2712
2713 %49 = OpLabel ; inner loop's merge
2714 OpBranch %50
2715
2716 %50 = OpLabel ; outer loop's continue
2717 OpBranch %20 ; outer loop's backege
2718
2719 %99 = OpLabel
2720 OpReturn
2721
2722 OpFunctionEnd
2723 )";
2724 auto p = parser(test::Assemble(assembly));
2725 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2726 auto fe = p->function_emitter(100);
2727 fe.RegisterBasicBlocks();
2728 fe.ComputeBlockOrderAndPositions();
2729
2730 EXPECT_THAT(fe.block_order(),
2731 ElementsAre(10, 20, 30, 35, 37, 40, 49, 50, 99));
2732
2733 p->DeliberatelyInvalidSpirv();
2734 // SPIR-V validation fails:
2735 // block <ID> 40[%40] exits the continue headed by <ID> 40[%40], but not
2736 // via a structured exit"
2737 }
2738
TEST_F(SpvParserCFGTest,VerifyHeaderContinueMergeOrder_Selection_Good)2739 TEST_F(SpvParserCFGTest, VerifyHeaderContinueMergeOrder_Selection_Good) {
2740 auto assembly = CommonTypes() + R"(
2741 %100 = OpFunction %void None %voidfn
2742
2743 %10 = OpLabel
2744 OpSelectionMerge %99 None
2745 OpBranchConditional %cond %20 %30
2746
2747 %20 = OpLabel
2748 OpBranch %99
2749
2750 %30 = OpLabel
2751 OpBranch %99
2752
2753 %99 = OpLabel
2754 OpReturn
2755
2756 OpFunctionEnd
2757 )";
2758 auto p = parser(test::Assemble(assembly));
2759 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2760 auto fe = p->function_emitter(100);
2761 fe.RegisterBasicBlocks();
2762 fe.ComputeBlockOrderAndPositions();
2763 fe.RegisterMerges();
2764 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
2765 }
2766
TEST_F(SpvParserCFGTest,VerifyHeaderContinueMergeOrder_SingleBlockLoop_Good)2767 TEST_F(SpvParserCFGTest, VerifyHeaderContinueMergeOrder_SingleBlockLoop_Good) {
2768 auto assembly = CommonTypes() + R"(
2769 %100 = OpFunction %void None %voidfn
2770
2771 %10 = OpLabel
2772 OpBranch %20
2773
2774 %20 = OpLabel
2775 OpLoopMerge %99 %20 None
2776 OpBranchConditional %cond %20 %99
2777
2778 %99 = OpLabel
2779 OpReturn
2780
2781 OpFunctionEnd
2782 )";
2783 auto p = parser(test::Assemble(assembly));
2784 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2785 auto fe = p->function_emitter(100);
2786 fe.RegisterBasicBlocks();
2787 fe.ComputeBlockOrderAndPositions();
2788 fe.RegisterMerges();
2789 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder()) << p->error();
2790 }
2791
TEST_F(SpvParserCFGTest,VerifyHeaderContinueMergeOrder_MultiBlockLoop_Good)2792 TEST_F(SpvParserCFGTest, VerifyHeaderContinueMergeOrder_MultiBlockLoop_Good) {
2793 auto assembly = CommonTypes() + R"(
2794 %100 = OpFunction %void None %voidfn
2795
2796 %10 = OpLabel
2797 OpBranch %20
2798
2799 %20 = OpLabel
2800 OpLoopMerge %99 %30 None
2801 OpBranchConditional %cond %30 %99
2802
2803 %30 = OpLabel
2804 OpBranch %20
2805
2806 %99 = OpLabel
2807 OpReturn
2808
2809 OpFunctionEnd
2810 )";
2811 auto p = parser(test::Assemble(assembly));
2812 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2813 auto fe = p->function_emitter(100);
2814 fe.RegisterBasicBlocks();
2815 fe.ComputeBlockOrderAndPositions();
2816 fe.RegisterMerges();
2817 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
2818 }
2819
TEST_F(SpvParserCFGTest,VerifyHeaderContinueMergeOrder_HeaderDoesNotStrictlyDominateMerge)2820 TEST_F(SpvParserCFGTest,
2821 VerifyHeaderContinueMergeOrder_HeaderDoesNotStrictlyDominateMerge) {
2822 auto assembly = CommonTypes() + R"(
2823 %100 = OpFunction %void None %voidfn
2824
2825 %10 = OpLabel
2826 OpBranch %20
2827
2828 %20 = OpLabel
2829 OpBranch %50
2830
2831 %50 = OpLabel
2832 OpSelectionMerge %20 None ; this is backward
2833 OpBranchConditional %cond2 %60 %99
2834
2835 %60 = OpLabel
2836 OpBranch %99
2837
2838 %99 = OpLabel
2839 OpReturn
2840
2841 OpFunctionEnd
2842 )";
2843 auto p = parser(test::Assemble(assembly));
2844 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2845 auto fe = p->function_emitter(100);
2846 fe.RegisterBasicBlocks();
2847 fe.ComputeBlockOrderAndPositions();
2848 fe.RegisterMerges();
2849 EXPECT_FALSE(fe.VerifyHeaderContinueMergeOrder());
2850 EXPECT_THAT(p->error(),
2851 Eq("Header 50 does not strictly dominate its merge block 20"))
2852 << *fe.GetBlockInfo(50) << std::endl
2853 << *fe.GetBlockInfo(20) << std::endl
2854 << Dump(fe.block_order());
2855 }
2856
TEST_F(SpvParserCFGTest,VerifyHeaderContinueMergeOrder_HeaderDoesNotStrictlyDominateContinueTarget)2857 TEST_F(
2858 SpvParserCFGTest,
2859 VerifyHeaderContinueMergeOrder_HeaderDoesNotStrictlyDominateContinueTarget) { // NOLINT
2860 auto assembly = CommonTypes() + R"(
2861 %100 = OpFunction %void None %voidfn
2862
2863 %10 = OpLabel
2864 OpBranch %20
2865
2866 %20 = OpLabel
2867 OpBranch %50
2868
2869 %50 = OpLabel
2870 OpLoopMerge %99 %20 None ; this is backward
2871 OpBranchConditional %cond %60 %99
2872
2873 %60 = OpLabel
2874 OpBranch %50
2875
2876 %99 = OpLabel
2877 OpReturn
2878
2879 OpFunctionEnd
2880 )";
2881 auto p = parser(test::Assemble(assembly));
2882 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2883 auto fe = p->function_emitter(100);
2884 fe.RegisterBasicBlocks();
2885 fe.ComputeBlockOrderAndPositions();
2886 fe.RegisterMerges();
2887 EXPECT_FALSE(fe.VerifyHeaderContinueMergeOrder());
2888 EXPECT_THAT(p->error(),
2889 Eq("Loop header 50 does not dominate its continue target 20"))
2890 << *fe.GetBlockInfo(50) << std::endl
2891 << *fe.GetBlockInfo(20) << std::endl
2892 << Dump(fe.block_order());
2893 }
2894
TEST_F(SpvParserCFGTest,VerifyHeaderContinueMergeOrder_MergeInsideContinueTarget)2895 TEST_F(SpvParserCFGTest,
2896 VerifyHeaderContinueMergeOrder_MergeInsideContinueTarget) {
2897 auto assembly = CommonTypes() + R"(
2898 %100 = OpFunction %void None %voidfn
2899
2900 %10 = OpLabel
2901 OpBranch %50
2902
2903 %50 = OpLabel
2904 OpLoopMerge %60 %70 None
2905 OpBranchConditional %cond %60 %99
2906
2907 %60 = OpLabel
2908 OpBranch %70
2909
2910 %70 = OpLabel
2911 OpBranch %50
2912
2913 %99 = OpLabel
2914 OpReturn
2915
2916 OpFunctionEnd
2917 )";
2918 auto p = parser(test::Assemble(assembly));
2919 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2920 auto fe = p->function_emitter(100);
2921 fe.RegisterBasicBlocks();
2922 fe.ComputeBlockOrderAndPositions();
2923 fe.RegisterMerges();
2924 EXPECT_FALSE(fe.VerifyHeaderContinueMergeOrder());
2925 EXPECT_THAT(p->error(),
2926 Eq("Merge block 60 for loop headed at block 50 appears at or "
2927 "before the loop's continue construct headed by block 70"))
2928 << Dump(fe.block_order());
2929 }
2930
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_OuterConstructIsFunction_SingleBlock)2931 TEST_F(SpvParserCFGTest,
2932 LabelControlFlowConstructs_OuterConstructIsFunction_SingleBlock) {
2933 auto assembly = CommonTypes() + R"(
2934 %100 = OpFunction %void None %voidfn
2935
2936 %10 = OpLabel
2937 OpReturn
2938
2939 OpFunctionEnd
2940 )";
2941 auto p = parser(test::Assemble(assembly));
2942 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2943 auto fe = p->function_emitter(100);
2944 fe.RegisterBasicBlocks();
2945 fe.ComputeBlockOrderAndPositions();
2946 fe.RegisterMerges();
2947 EXPECT_TRUE(fe.LabelControlFlowConstructs());
2948 EXPECT_EQ(fe.constructs().size(), 1u);
2949 auto& c = fe.constructs().front();
2950 EXPECT_THAT(ToString(c), Eq("Construct{ Function [0,1) begin_id:10 end_id:0 "
2951 "depth:0 parent:null }"));
2952 EXPECT_EQ(fe.GetBlockInfo(10)->construct, c.get());
2953 }
2954
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_OuterConstructIsFunction_MultiBlock)2955 TEST_F(SpvParserCFGTest,
2956 LabelControlFlowConstructs_OuterConstructIsFunction_MultiBlock) {
2957 auto assembly = CommonTypes() + R"(
2958 %100 = OpFunction %void None %voidfn
2959
2960 %10 = OpLabel
2961 OpBranch %5
2962
2963 %5 = OpLabel
2964 OpReturn
2965
2966 OpFunctionEnd
2967 )";
2968 auto p = parser(test::Assemble(assembly));
2969 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
2970 auto fe = p->function_emitter(100);
2971 fe.RegisterBasicBlocks();
2972 fe.ComputeBlockOrderAndPositions();
2973 fe.RegisterMerges();
2974 EXPECT_TRUE(fe.LabelControlFlowConstructs());
2975 EXPECT_EQ(fe.constructs().size(), 1u);
2976 auto& c = fe.constructs().front();
2977 EXPECT_THAT(ToString(c), Eq("Construct{ Function [0,2) begin_id:10 end_id:0 "
2978 "depth:0 parent:null }"));
2979 EXPECT_EQ(fe.GetBlockInfo(10)->construct, c.get());
2980 EXPECT_EQ(fe.GetBlockInfo(5)->construct, c.get());
2981 }
2982
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_FunctionIsOnlyIfSelectionAndItsMerge)2983 TEST_F(SpvParserCFGTest,
2984 LabelControlFlowConstructs_FunctionIsOnlyIfSelectionAndItsMerge) {
2985 auto assembly = CommonTypes() + R"(
2986 %100 = OpFunction %void None %voidfn
2987
2988 %10 = OpLabel
2989 OpSelectionMerge %99 None
2990 OpBranchConditional %cond %20 %30
2991
2992 %20 = OpLabel
2993 OpBranch %99
2994
2995 %30 = OpLabel
2996 OpBranch %99
2997
2998 %99 = OpLabel
2999 OpReturn
3000
3001 OpFunctionEnd
3002 )";
3003 auto p = parser(test::Assemble(assembly));
3004 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3005 auto fe = p->function_emitter(100);
3006 fe.RegisterBasicBlocks();
3007 fe.ComputeBlockOrderAndPositions();
3008 fe.RegisterMerges();
3009 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3010 const auto& constructs = fe.constructs();
3011 EXPECT_EQ(constructs.size(), 2u);
3012 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3013 Construct{ Function [0,4) begin_id:10 end_id:0 depth:0 parent:null }
3014 Construct{ IfSelection [0,3) begin_id:10 end_id:99 depth:1 parent:Function@10 }
3015 })")) << constructs;
3016 // The block records the nearest enclosing construct.
3017 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[1].get());
3018 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[1].get());
3019 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[1].get());
3020 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3021 }
3022
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_PaddingBlocksBeforeAndAfterStructuredConstruct)3023 TEST_F(
3024 SpvParserCFGTest,
3025 LabelControlFlowConstructs_PaddingBlocksBeforeAndAfterStructuredConstruct) {
3026 auto assembly = CommonTypes() + R"(
3027 %100 = OpFunction %void None %voidfn
3028
3029 %5 = OpLabel
3030 OpBranch %10
3031
3032 %10 = OpLabel
3033 OpSelectionMerge %99 None
3034 OpBranchConditional %cond %20 %30
3035
3036 %20 = OpLabel
3037 OpBranch %99
3038
3039 %30 = OpLabel
3040 OpBranch %99
3041
3042 %99 = OpLabel
3043 OpBranch %200
3044
3045 %200 = OpLabel
3046 OpReturn
3047
3048 OpFunctionEnd
3049 )";
3050 auto p = parser(test::Assemble(assembly));
3051 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3052 auto fe = p->function_emitter(100);
3053 fe.RegisterBasicBlocks();
3054 fe.ComputeBlockOrderAndPositions();
3055 fe.RegisterMerges();
3056 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3057 const auto& constructs = fe.constructs();
3058 EXPECT_EQ(constructs.size(), 2u);
3059 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3060 Construct{ Function [0,6) begin_id:5 end_id:0 depth:0 parent:null }
3061 Construct{ IfSelection [1,4) begin_id:10 end_id:99 depth:1 parent:Function@5 }
3062 })")) << constructs;
3063 // The block records the nearest enclosing construct.
3064 EXPECT_EQ(fe.GetBlockInfo(5)->construct, constructs[0].get());
3065 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[1].get());
3066 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[1].get());
3067 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[1].get());
3068 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3069 EXPECT_EQ(fe.GetBlockInfo(200)->construct, constructs[0].get());
3070 }
3071
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_SwitchSelection)3072 TEST_F(SpvParserCFGTest, LabelControlFlowConstructs_SwitchSelection) {
3073 auto assembly = CommonTypes() + R"(
3074 %100 = OpFunction %void None %voidfn
3075
3076 %10 = OpLabel
3077 OpSelectionMerge %99 None
3078 OpSwitch %selector %40 20 %20 30 %30
3079
3080 %20 = OpLabel
3081 OpBranch %99
3082
3083 %30 = OpLabel
3084 OpBranch %99
3085
3086 %40 = OpLabel
3087 OpBranch %99
3088
3089 %99 = OpLabel
3090 OpReturn
3091
3092 OpFunctionEnd
3093 )";
3094 auto p = parser(test::Assemble(assembly));
3095 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3096 auto fe = p->function_emitter(100);
3097 fe.RegisterBasicBlocks();
3098 fe.ComputeBlockOrderAndPositions();
3099 fe.RegisterMerges();
3100 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3101 const auto& constructs = fe.constructs();
3102 EXPECT_EQ(constructs.size(), 2u);
3103 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3104 Construct{ Function [0,5) begin_id:10 end_id:0 depth:0 parent:null }
3105 Construct{ SwitchSelection [0,4) begin_id:10 end_id:99 depth:1 parent:Function@10 in-c-l-s:SwitchSelection@10 }
3106 })")) << constructs;
3107 // The block records the nearest enclosing construct.
3108 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[1].get());
3109 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[1].get());
3110 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[1].get());
3111 EXPECT_EQ(fe.GetBlockInfo(40)->construct, constructs[1].get());
3112 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3113 }
3114
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_SingleBlockLoop)3115 TEST_F(SpvParserCFGTest, LabelControlFlowConstructs_SingleBlockLoop) {
3116 auto assembly = CommonTypes() + R"(
3117 %100 = OpFunction %void None %voidfn
3118
3119 %10 = OpLabel
3120 OpBranch %20
3121
3122 %20 = OpLabel
3123 OpLoopMerge %99 %20 None
3124 OpBranchConditional %cond %20 %99
3125
3126 %99 = OpLabel
3127 OpReturn
3128
3129 OpFunctionEnd
3130 )";
3131 auto p = parser(test::Assemble(assembly));
3132 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3133 auto fe = p->function_emitter(100);
3134 fe.RegisterBasicBlocks();
3135 fe.ComputeBlockOrderAndPositions();
3136 fe.RegisterMerges();
3137 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3138 const auto& constructs = fe.constructs();
3139 EXPECT_EQ(constructs.size(), 2u);
3140 // A single-block loop consists *only* of a continue target with one block in
3141 // it.
3142 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3143 Construct{ Function [0,3) begin_id:10 end_id:0 depth:0 parent:null }
3144 Construct{ Continue [1,2) begin_id:20 end_id:99 depth:1 parent:Function@10 in-c:Continue@20 }
3145 })")) << constructs;
3146 // The block records the nearest enclosing construct.
3147 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[0].get());
3148 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[1].get());
3149 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3150 }
3151
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_MultiBlockLoop_HeaderIsNotContinue)3152 TEST_F(SpvParserCFGTest,
3153 LabelControlFlowConstructs_MultiBlockLoop_HeaderIsNotContinue) {
3154 // In this case, we have a continue construct and a non-empty loop construct.
3155 auto assembly = CommonTypes() + R"(
3156 %100 = OpFunction %void None %voidfn
3157
3158 %10 = OpLabel
3159 OpBranch %20
3160
3161 %20 = OpLabel
3162 OpLoopMerge %99 %40 None
3163 OpBranchConditional %cond %30 %99
3164
3165 %30 = OpLabel
3166 OpBranch %40
3167
3168 %40 = OpLabel
3169 OpBranch %50
3170
3171 %50 = OpLabel
3172 OpBranch %20
3173
3174 %99 = OpLabel
3175 OpReturn
3176
3177 OpFunctionEnd
3178 )";
3179 auto p = parser(test::Assemble(assembly));
3180 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3181 auto fe = p->function_emitter(100);
3182 fe.RegisterBasicBlocks();
3183 fe.ComputeBlockOrderAndPositions();
3184 fe.RegisterMerges();
3185 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3186 const auto& constructs = fe.constructs();
3187 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3188 Construct{ Function [0,6) begin_id:10 end_id:0 depth:0 parent:null }
3189 Construct{ Continue [3,5) begin_id:40 end_id:99 depth:1 parent:Function@10 in-c:Continue@40 }
3190 Construct{ Loop [1,3) begin_id:20 end_id:40 depth:1 parent:Function@10 scope:[1,5) in-l:Loop@20 }
3191 })")) << constructs;
3192 // The block records the nearest enclosing construct.
3193 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[0].get());
3194 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[2].get());
3195 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[2].get());
3196 EXPECT_EQ(fe.GetBlockInfo(40)->construct, constructs[1].get());
3197 EXPECT_EQ(fe.GetBlockInfo(50)->construct, constructs[1].get());
3198 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3199 }
3200
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_MultiBlockLoop_HeaderIsContinue)3201 TEST_F(SpvParserCFGTest,
3202 LabelControlFlowConstructs_MultiBlockLoop_HeaderIsContinue) {
3203 // In this case, we have only a continue construct and no loop construct.
3204 auto assembly = CommonTypes() + R"(
3205 %100 = OpFunction %void None %voidfn
3206
3207 %10 = OpLabel
3208 OpBranch %20
3209
3210 %20 = OpLabel
3211 OpLoopMerge %99 %20 None
3212 OpBranch %30
3213
3214 %30 = OpLabel
3215 OpBranch %40
3216
3217 %40 = OpLabel
3218 OpBranch %50
3219
3220 %50 = OpLabel
3221 OpBranchConditional %cond %20 %99
3222
3223 %99 = OpLabel
3224 OpReturn
3225
3226 OpFunctionEnd
3227 )";
3228 auto p = parser(test::Assemble(assembly));
3229 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3230 auto fe = p->function_emitter(100);
3231 fe.RegisterBasicBlocks();
3232 fe.ComputeBlockOrderAndPositions();
3233 fe.RegisterMerges();
3234 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3235 const auto& constructs = fe.constructs();
3236 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3237 Construct{ Function [0,6) begin_id:10 end_id:0 depth:0 parent:null }
3238 Construct{ Continue [1,5) begin_id:20 end_id:99 depth:1 parent:Function@10 in-c:Continue@20 }
3239 })")) << constructs;
3240 // The block records the nearest enclosing construct.
3241 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[0].get());
3242 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[1].get());
3243 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[1].get());
3244 EXPECT_EQ(fe.GetBlockInfo(40)->construct, constructs[1].get());
3245 EXPECT_EQ(fe.GetBlockInfo(50)->construct, constructs[1].get());
3246 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3247 }
3248
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_MergeBlockIsAlsoSingleBlockLoop)3249 TEST_F(SpvParserCFGTest,
3250 LabelControlFlowConstructs_MergeBlockIsAlsoSingleBlockLoop) {
3251 auto assembly = CommonTypes() + R"(
3252 %100 = OpFunction %void None %voidfn
3253
3254 %10 = OpLabel
3255 OpSelectionMerge %50 None
3256 OpBranchConditional %cond %20 %50
3257
3258 %20 = OpLabel
3259 OpBranch %50
3260
3261 ; %50 is the merge block for the selection starting at 10,
3262 ; and its own continue target.
3263 %50 = OpLabel
3264 OpLoopMerge %99 %50 None
3265 OpBranchConditional %cond %50 %99
3266
3267 %99 = OpLabel
3268 OpReturn
3269
3270 OpFunctionEnd
3271 )";
3272 auto p = parser(test::Assemble(assembly));
3273 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3274 auto fe = p->function_emitter(100);
3275 fe.RegisterBasicBlocks();
3276 fe.ComputeBlockOrderAndPositions();
3277 fe.RegisterMerges();
3278 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3279 const auto& constructs = fe.constructs();
3280 EXPECT_EQ(constructs.size(), 3u);
3281 // A single-block loop consists *only* of a continue target with one block in
3282 // it.
3283 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3284 Construct{ Function [0,4) begin_id:10 end_id:0 depth:0 parent:null }
3285 Construct{ IfSelection [0,2) begin_id:10 end_id:50 depth:1 parent:Function@10 }
3286 Construct{ Continue [2,3) begin_id:50 end_id:99 depth:1 parent:Function@10 in-c:Continue@50 }
3287 })")) << constructs;
3288 // The block records the nearest enclosing construct.
3289 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[1].get());
3290 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[1].get());
3291 EXPECT_EQ(fe.GetBlockInfo(50)->construct, constructs[2].get());
3292 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3293 }
3294
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_MergeBlockIsAlsoMultiBlockLoopHeader)3295 TEST_F(SpvParserCFGTest,
3296 LabelControlFlowConstructs_MergeBlockIsAlsoMultiBlockLoopHeader) {
3297 auto assembly = CommonTypes() + R"(
3298 %100 = OpFunction %void None %voidfn
3299
3300 %10 = OpLabel
3301 OpSelectionMerge %50 None
3302 OpBranchConditional %cond %20 %50
3303
3304 %20 = OpLabel
3305 OpBranch %50
3306
3307 ; %50 is the merge block for the selection starting at 10,
3308 ; and a loop block header but not its own continue target.
3309 %50 = OpLabel
3310 OpLoopMerge %99 %60 None
3311 OpBranchConditional %cond %60 %99
3312
3313 %60 = OpLabel
3314 OpBranch %50
3315
3316 %99 = OpLabel
3317 OpReturn
3318
3319 OpFunctionEnd
3320 )";
3321 auto p = parser(test::Assemble(assembly));
3322 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3323 auto fe = p->function_emitter(100);
3324 fe.RegisterBasicBlocks();
3325 fe.ComputeBlockOrderAndPositions();
3326 fe.RegisterMerges();
3327 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3328 const auto& constructs = fe.constructs();
3329 EXPECT_EQ(constructs.size(), 4u);
3330 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3331 Construct{ Function [0,5) begin_id:10 end_id:0 depth:0 parent:null }
3332 Construct{ IfSelection [0,2) begin_id:10 end_id:50 depth:1 parent:Function@10 }
3333 Construct{ Continue [3,4) begin_id:60 end_id:99 depth:1 parent:Function@10 in-c:Continue@60 }
3334 Construct{ Loop [2,3) begin_id:50 end_id:60 depth:1 parent:Function@10 scope:[2,4) in-l:Loop@50 }
3335 })")) << constructs;
3336 // The block records the nearest enclosing construct.
3337 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[1].get());
3338 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[1].get());
3339 EXPECT_EQ(fe.GetBlockInfo(50)->construct, constructs[3].get());
3340 EXPECT_EQ(fe.GetBlockInfo(60)->construct, constructs[2].get());
3341 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3342 }
3343
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_Nest_If_If)3344 TEST_F(SpvParserCFGTest, LabelControlFlowConstructs_Nest_If_If) {
3345 auto assembly = CommonTypes() + R"(
3346 %100 = OpFunction %void None %voidfn
3347
3348 %10 = OpLabel
3349 OpSelectionMerge %99 None
3350 OpBranchConditional %cond %20 %50
3351
3352 %20 = OpLabel
3353 OpSelectionMerge %40 None
3354 OpBranchConditional %cond %30 %40 ;; true only
3355
3356 %30 = OpLabel
3357 OpBranch %40
3358
3359 %40 = OpLabel ; merge for first inner "if"
3360 OpBranch %49
3361
3362 %49 = OpLabel ; an extra padding block
3363 OpBranch %99
3364
3365 %50 = OpLabel
3366 OpSelectionMerge %89 None
3367 OpBranchConditional %cond %89 %60 ;; false only
3368
3369 %60 = OpLabel
3370 OpBranch %89
3371
3372 %89 = OpLabel
3373 OpBranch %99
3374
3375 %99 = OpLabel
3376 OpReturn
3377
3378 OpFunctionEnd
3379 )";
3380 auto p = parser(test::Assemble(assembly));
3381 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3382 auto fe = p->function_emitter(100);
3383 fe.RegisterBasicBlocks();
3384 fe.ComputeBlockOrderAndPositions();
3385 fe.RegisterMerges();
3386 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3387 const auto& constructs = fe.constructs();
3388 EXPECT_EQ(constructs.size(), 4u);
3389 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3390 Construct{ Function [0,9) begin_id:10 end_id:0 depth:0 parent:null }
3391 Construct{ IfSelection [0,8) begin_id:10 end_id:99 depth:1 parent:Function@10 }
3392 Construct{ IfSelection [1,3) begin_id:20 end_id:40 depth:2 parent:IfSelection@10 }
3393 Construct{ IfSelection [5,7) begin_id:50 end_id:89 depth:2 parent:IfSelection@10 }
3394 })")) << constructs;
3395 // The block records the nearest enclosing construct.
3396 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[1].get());
3397 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[2].get());
3398 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[2].get());
3399 EXPECT_EQ(fe.GetBlockInfo(40)->construct, constructs[1].get());
3400 EXPECT_EQ(fe.GetBlockInfo(49)->construct, constructs[1].get());
3401 EXPECT_EQ(fe.GetBlockInfo(50)->construct, constructs[3].get());
3402 EXPECT_EQ(fe.GetBlockInfo(60)->construct, constructs[3].get());
3403 EXPECT_EQ(fe.GetBlockInfo(89)->construct, constructs[1].get());
3404 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3405 }
3406
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_Nest_Switch_If)3407 TEST_F(SpvParserCFGTest, LabelControlFlowConstructs_Nest_Switch_If) {
3408 auto assembly = CommonTypes() + R"(
3409 %100 = OpFunction %void None %voidfn
3410
3411 %10 = OpLabel
3412 OpSelectionMerge %99 None
3413 OpSwitch %selector %99 20 %20 50 %50
3414
3415 %20 = OpLabel ; if-then nested in case 20
3416 OpSelectionMerge %49 None
3417 OpBranchConditional %cond %30 %49
3418
3419 %30 = OpLabel
3420 OpBranch %49
3421
3422 %49 = OpLabel
3423 OpBranch %99
3424
3425 %50 = OpLabel ; unles-then nested in case 50
3426 OpSelectionMerge %89 None
3427 OpBranchConditional %cond %89 %60
3428
3429 %60 = OpLabel
3430 OpBranch %89
3431
3432 %89 = OpLabel
3433 OpBranch %99
3434
3435 %99 = OpLabel
3436 OpReturn
3437
3438 OpFunctionEnd
3439 )";
3440 auto p = parser(test::Assemble(assembly));
3441 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3442 auto fe = p->function_emitter(100);
3443 fe.RegisterBasicBlocks();
3444 fe.ComputeBlockOrderAndPositions();
3445 fe.RegisterMerges();
3446 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3447 const auto& constructs = fe.constructs();
3448 EXPECT_EQ(constructs.size(), 4u);
3449 // The ordering among siblings depends on the computed block order.
3450 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3451 Construct{ Function [0,8) begin_id:10 end_id:0 depth:0 parent:null }
3452 Construct{ SwitchSelection [0,7) begin_id:10 end_id:99 depth:1 parent:Function@10 in-c-l-s:SwitchSelection@10 }
3453 Construct{ IfSelection [1,3) begin_id:50 end_id:89 depth:2 parent:SwitchSelection@10 in-c-l-s:SwitchSelection@10 }
3454 Construct{ IfSelection [4,6) begin_id:20 end_id:49 depth:2 parent:SwitchSelection@10 in-c-l-s:SwitchSelection@10 }
3455 })")) << constructs;
3456 // The block records the nearest enclosing construct.
3457 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[1].get());
3458 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[3].get());
3459 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[3].get());
3460 EXPECT_EQ(fe.GetBlockInfo(49)->construct, constructs[1].get());
3461 EXPECT_EQ(fe.GetBlockInfo(50)->construct, constructs[2].get());
3462 EXPECT_EQ(fe.GetBlockInfo(60)->construct, constructs[2].get());
3463 EXPECT_EQ(fe.GetBlockInfo(89)->construct, constructs[1].get());
3464 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3465 }
3466
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_Nest_If_Switch)3467 TEST_F(SpvParserCFGTest, LabelControlFlowConstructs_Nest_If_Switch) {
3468 auto assembly = CommonTypes() + R"(
3469 %100 = OpFunction %void None %voidfn
3470
3471 %10 = OpLabel
3472 OpSelectionMerge %99 None
3473 OpBranchConditional %cond %20 %99
3474
3475 %20 = OpLabel
3476 OpSelectionMerge %89 None
3477 OpSwitch %selector %89 20 %30
3478
3479 %30 = OpLabel
3480 OpBranch %89
3481
3482 %89 = OpLabel
3483 OpBranch %99
3484
3485 %99 = OpLabel
3486 OpReturn
3487
3488 OpFunctionEnd
3489 )";
3490 auto p = parser(test::Assemble(assembly));
3491 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3492 auto fe = p->function_emitter(100);
3493 fe.RegisterBasicBlocks();
3494 fe.ComputeBlockOrderAndPositions();
3495 fe.RegisterMerges();
3496 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3497 const auto& constructs = fe.constructs();
3498 EXPECT_EQ(constructs.size(), 3u);
3499 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3500 Construct{ Function [0,5) begin_id:10 end_id:0 depth:0 parent:null }
3501 Construct{ IfSelection [0,4) begin_id:10 end_id:99 depth:1 parent:Function@10 }
3502 Construct{ SwitchSelection [1,3) begin_id:20 end_id:89 depth:2 parent:IfSelection@10 in-c-l-s:SwitchSelection@20 }
3503 })")) << constructs;
3504 // The block records the nearest enclosing construct.
3505 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[1].get());
3506 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[2].get());
3507 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[2].get());
3508 EXPECT_EQ(fe.GetBlockInfo(89)->construct, constructs[1].get());
3509 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3510 }
3511
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_Nest_Loop_Loop)3512 TEST_F(SpvParserCFGTest, LabelControlFlowConstructs_Nest_Loop_Loop) {
3513 auto assembly = CommonTypes() + R"(
3514 %100 = OpFunction %void None %voidfn
3515
3516 %10 = OpLabel
3517 OpBranch %20
3518
3519 %20 = OpLabel
3520 OpLoopMerge %89 %50 None
3521 OpBranchConditional %cond %30 %89
3522
3523 %30 = OpLabel ; single block loop
3524 OpLoopMerge %40 %30 None
3525 OpBranchConditional %cond2 %30 %40
3526
3527 %40 = OpLabel ; padding block
3528 OpBranch %50
3529
3530 %50 = OpLabel ; outer continue target
3531 OpBranch %60
3532
3533 %60 = OpLabel
3534 OpBranch %20
3535
3536 %89 = OpLabel ; outer merge
3537 OpBranch %99
3538
3539 %99 = OpLabel
3540 OpReturn
3541
3542 OpFunctionEnd
3543 )";
3544 auto p = parser(test::Assemble(assembly));
3545 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3546 auto fe = p->function_emitter(100);
3547 fe.RegisterBasicBlocks();
3548 fe.ComputeBlockOrderAndPositions();
3549 fe.RegisterMerges();
3550 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3551 const auto& constructs = fe.constructs();
3552 EXPECT_EQ(constructs.size(), 4u);
3553 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3554 Construct{ Function [0,8) begin_id:10 end_id:0 depth:0 parent:null }
3555 Construct{ Continue [4,6) begin_id:50 end_id:89 depth:1 parent:Function@10 in-c:Continue@50 }
3556 Construct{ Loop [1,4) begin_id:20 end_id:50 depth:1 parent:Function@10 scope:[1,6) in-l:Loop@20 }
3557 Construct{ Continue [2,3) begin_id:30 end_id:40 depth:2 parent:Loop@20 in-l:Loop@20 in-c:Continue@30 }
3558 })")) << constructs;
3559 // The block records the nearest enclosing construct.
3560 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[0].get());
3561 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[2].get());
3562 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[3].get());
3563 EXPECT_EQ(fe.GetBlockInfo(40)->construct, constructs[2].get());
3564 EXPECT_EQ(fe.GetBlockInfo(50)->construct, constructs[1].get());
3565 EXPECT_EQ(fe.GetBlockInfo(60)->construct, constructs[1].get());
3566 EXPECT_EQ(fe.GetBlockInfo(89)->construct, constructs[0].get());
3567 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3568 }
3569
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_Nest_Loop_If)3570 TEST_F(SpvParserCFGTest, LabelControlFlowConstructs_Nest_Loop_If) {
3571 auto assembly = CommonTypes() + R"(
3572 %100 = OpFunction %void None %voidfn
3573
3574 %10 = OpLabel
3575 OpBranch %20
3576
3577 %20 = OpLabel
3578 OpLoopMerge %99 %80 None
3579 OpBranchConditional %cond %30 %99
3580
3581 %30 = OpLabel ; If, nested in the loop construct
3582 OpSelectionMerge %49 None
3583 OpBranchConditional %cond2 %40 %49
3584
3585 %40 = OpLabel
3586 OpBranch %49
3587
3588 %49 = OpLabel ; merge for inner if
3589 OpBranch %80
3590
3591 %80 = OpLabel ; continue target
3592 OpBranch %20
3593
3594 %99 = OpLabel
3595 OpReturn
3596
3597 OpFunctionEnd
3598 )";
3599 auto p = parser(test::Assemble(assembly));
3600 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3601 auto fe = p->function_emitter(100);
3602 fe.RegisterBasicBlocks();
3603 fe.ComputeBlockOrderAndPositions();
3604 fe.RegisterMerges();
3605 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3606 const auto& constructs = fe.constructs();
3607 EXPECT_EQ(constructs.size(), 4u);
3608 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3609 Construct{ Function [0,7) begin_id:10 end_id:0 depth:0 parent:null }
3610 Construct{ Continue [5,6) begin_id:80 end_id:99 depth:1 parent:Function@10 in-c:Continue@80 }
3611 Construct{ Loop [1,5) begin_id:20 end_id:80 depth:1 parent:Function@10 scope:[1,6) in-l:Loop@20 }
3612 Construct{ IfSelection [2,4) begin_id:30 end_id:49 depth:2 parent:Loop@20 in-l:Loop@20 }
3613 })")) << constructs;
3614 // The block records the nearest enclosing construct.
3615 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[0].get());
3616 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[2].get());
3617 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[3].get());
3618 EXPECT_EQ(fe.GetBlockInfo(40)->construct, constructs[3].get());
3619 EXPECT_EQ(fe.GetBlockInfo(49)->construct, constructs[2].get());
3620 EXPECT_EQ(fe.GetBlockInfo(80)->construct, constructs[1].get());
3621 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3622 }
3623
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_Nest_LoopContinue_If)3624 TEST_F(SpvParserCFGTest, LabelControlFlowConstructs_Nest_LoopContinue_If) {
3625 auto assembly = CommonTypes() + R"(
3626 %100 = OpFunction %void None %voidfn
3627
3628 %10 = OpLabel
3629 OpBranch %20
3630
3631 %20 = OpLabel
3632 OpLoopMerge %99 %30 None
3633 OpBranchConditional %cond %30 %99
3634
3635 %30 = OpLabel ; If, nested at the top of the continue construct head
3636 OpSelectionMerge %49 None
3637 OpBranchConditional %cond2 %40 %49
3638
3639 %40 = OpLabel
3640 OpBranch %49
3641
3642 %49 = OpLabel ; merge for inner if, backedge
3643 OpBranch %20
3644
3645 %99 = OpLabel
3646 OpReturn
3647
3648 OpFunctionEnd
3649 )";
3650 auto p = parser(test::Assemble(assembly));
3651 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3652 auto fe = p->function_emitter(100);
3653 fe.RegisterBasicBlocks();
3654 fe.ComputeBlockOrderAndPositions();
3655 fe.RegisterMerges();
3656 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3657 const auto& constructs = fe.constructs();
3658 EXPECT_EQ(constructs.size(), 4u);
3659 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3660 Construct{ Function [0,6) begin_id:10 end_id:0 depth:0 parent:null }
3661 Construct{ Continue [2,5) begin_id:30 end_id:99 depth:1 parent:Function@10 in-c:Continue@30 }
3662 Construct{ Loop [1,2) begin_id:20 end_id:30 depth:1 parent:Function@10 scope:[1,5) in-l:Loop@20 }
3663 Construct{ IfSelection [2,4) begin_id:30 end_id:49 depth:2 parent:Continue@30 in-c:Continue@30 }
3664 })")) << constructs;
3665 // The block records the nearest enclosing construct.
3666 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[0].get());
3667 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[2].get());
3668 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[3].get());
3669 EXPECT_EQ(fe.GetBlockInfo(40)->construct, constructs[3].get());
3670 EXPECT_EQ(fe.GetBlockInfo(49)->construct, constructs[1].get());
3671 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3672 }
3673
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_Nest_If_SingleBlockLoop)3674 TEST_F(SpvParserCFGTest, LabelControlFlowConstructs_Nest_If_SingleBlockLoop) {
3675 auto assembly = CommonTypes() + R"(
3676 %100 = OpFunction %void None %voidfn
3677
3678 %10 = OpLabel
3679 OpSelectionMerge %99 None
3680 OpBranchConditional %cond %20 %99
3681
3682 %20 = OpLabel
3683 OpLoopMerge %89 %20 None
3684 OpBranchConditional %cond %20 %89
3685
3686 %89 = OpLabel
3687 OpBranch %99
3688
3689 %99 = OpLabel
3690 OpReturn
3691
3692 OpFunctionEnd
3693 )";
3694 auto p = parser(test::Assemble(assembly));
3695 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3696 auto fe = p->function_emitter(100);
3697 fe.RegisterBasicBlocks();
3698 fe.ComputeBlockOrderAndPositions();
3699 fe.RegisterMerges();
3700 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3701 const auto& constructs = fe.constructs();
3702 EXPECT_EQ(constructs.size(), 3u);
3703 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3704 Construct{ Function [0,4) begin_id:10 end_id:0 depth:0 parent:null }
3705 Construct{ IfSelection [0,3) begin_id:10 end_id:99 depth:1 parent:Function@10 }
3706 Construct{ Continue [1,2) begin_id:20 end_id:89 depth:2 parent:IfSelection@10 in-c:Continue@20 }
3707 })")) << constructs;
3708 // The block records the nearest enclosing construct.
3709 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[1].get());
3710 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[2].get());
3711 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3712 }
3713
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_Nest_If_MultiBlockLoop)3714 TEST_F(SpvParserCFGTest, LabelControlFlowConstructs_Nest_If_MultiBlockLoop) {
3715 auto assembly = CommonTypes() + R"(
3716 %100 = OpFunction %void None %voidfn
3717
3718 %10 = OpLabel
3719 OpSelectionMerge %99 None
3720 OpBranchConditional %cond %20 %99
3721
3722 %20 = OpLabel ; start loop body
3723 OpLoopMerge %89 %40 None
3724 OpBranchConditional %cond %30 %89
3725
3726 %30 = OpLabel ; body block
3727 OpBranch %40
3728
3729 %40 = OpLabel ; continue target
3730 OpBranch %50
3731
3732 %50 = OpLabel ; backedge block
3733 OpBranch %20
3734
3735 %89 = OpLabel ; merge for the loop
3736 OpBranch %99
3737
3738 %99 = OpLabel ; merge for the if
3739 OpReturn
3740
3741 OpFunctionEnd
3742 )";
3743 auto p = parser(test::Assemble(assembly));
3744 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3745 auto fe = p->function_emitter(100);
3746 fe.RegisterBasicBlocks();
3747 fe.ComputeBlockOrderAndPositions();
3748 fe.RegisterMerges();
3749 EXPECT_TRUE(fe.LabelControlFlowConstructs());
3750 const auto& constructs = fe.constructs();
3751 EXPECT_EQ(constructs.size(), 4u);
3752 EXPECT_THAT(ToString(constructs), Eq(R"(ConstructList{
3753 Construct{ Function [0,7) begin_id:10 end_id:0 depth:0 parent:null }
3754 Construct{ IfSelection [0,6) begin_id:10 end_id:99 depth:1 parent:Function@10 }
3755 Construct{ Continue [3,5) begin_id:40 end_id:89 depth:2 parent:IfSelection@10 in-c:Continue@40 }
3756 Construct{ Loop [1,3) begin_id:20 end_id:40 depth:2 parent:IfSelection@10 scope:[1,5) in-l:Loop@20 }
3757 })")) << constructs;
3758 // The block records the nearest enclosing construct.
3759 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[1].get());
3760 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[3].get());
3761 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[3].get());
3762 EXPECT_EQ(fe.GetBlockInfo(40)->construct, constructs[2].get());
3763 EXPECT_EQ(fe.GetBlockInfo(50)->construct, constructs[2].get());
3764 EXPECT_EQ(fe.GetBlockInfo(89)->construct, constructs[1].get());
3765 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3766 }
3767
TEST_F(SpvParserCFGTest,LabelControlFlowConstructs_LoopInterallyDiverge)3768 TEST_F(SpvParserCFGTest, LabelControlFlowConstructs_LoopInterallyDiverge) {
3769 // In this case, insert a synthetic if-selection with the same blocks
3770 // as the loop construct.
3771 // crbug.com/tint/524
3772 auto assembly = CommonTypes() + R"(
3773 %100 = OpFunction %void None %voidfn
3774
3775 %10 = OpLabel
3776 OpBranch %20
3777
3778 %20 = OpLabel
3779 OpLoopMerge %99 %90 None
3780 OpBranchConditional %cond %30 %40 ; divergence to distinct targets in the body
3781
3782 %30 = OpLabel
3783 OpBranch %90
3784
3785 %40 = OpLabel
3786 OpBranch %90
3787
3788 %90 = OpLabel ; continue target
3789 OpBranch %20
3790
3791 %99 = OpLabel ; loop merge
3792 OpReturn
3793
3794 OpFunctionEnd
3795 )";
3796 auto p = parser(test::Assemble(assembly));
3797 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3798 auto fe = p->function_emitter(100);
3799 ASSERT_TRUE(FlowLabelControlFlowConstructs(&fe)) << p->error();
3800 const auto& constructs = fe.constructs();
3801 EXPECT_EQ(constructs.size(), 4u);
3802 ASSERT_THAT(ToString(constructs), Eq(R"(ConstructList{
3803 Construct{ Function [0,6) begin_id:10 end_id:0 depth:0 parent:null }
3804 Construct{ Continue [4,5) begin_id:90 end_id:99 depth:1 parent:Function@10 in-c:Continue@90 }
3805 Construct{ Loop [1,4) begin_id:20 end_id:90 depth:1 parent:Function@10 scope:[1,5) in-l:Loop@20 }
3806 Construct{ IfSelection [1,4) begin_id:20 end_id:90 depth:2 parent:Loop@20 in-l:Loop@20 }
3807 })")) << constructs;
3808 // The block records the nearest enclosing construct.
3809 EXPECT_EQ(fe.GetBlockInfo(10)->construct, constructs[0].get());
3810 EXPECT_EQ(fe.GetBlockInfo(20)->construct, constructs[3].get());
3811 EXPECT_EQ(fe.GetBlockInfo(30)->construct, constructs[3].get());
3812 EXPECT_EQ(fe.GetBlockInfo(40)->construct, constructs[3].get());
3813 EXPECT_EQ(fe.GetBlockInfo(90)->construct, constructs[1].get());
3814 EXPECT_EQ(fe.GetBlockInfo(99)->construct, constructs[0].get());
3815 }
3816
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_DefaultIsLongRangeBackedge)3817 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_DefaultIsLongRangeBackedge) {
3818 auto assembly = CommonTypes() + R"(
3819 %100 = OpFunction %void None %voidfn
3820
3821 %10 = OpLabel
3822 OpBranch %20
3823
3824 %20 = OpLabel
3825 OpSelectionMerge %99 None
3826 OpSwitch %selector %10 30 %30
3827
3828 %30 = OpLabel
3829 OpBranch %99
3830
3831 %99 = OpLabel
3832 OpReturn
3833
3834 OpFunctionEnd
3835 )";
3836 auto p = parser(test::Assemble(assembly));
3837 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3838 auto fe = p->function_emitter(100);
3839 fe.RegisterBasicBlocks();
3840 fe.ComputeBlockOrderAndPositions();
3841 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
3842 fe.RegisterMerges();
3843 fe.LabelControlFlowConstructs();
3844 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
3845 EXPECT_THAT(p->error(), Eq("Switch branch from block 20 to default target "
3846 "block 10 can't be a back-edge"));
3847 }
3848
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_DefaultIsSelfLoop)3849 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_DefaultIsSelfLoop) {
3850 auto assembly = CommonTypes() + R"(
3851 %100 = OpFunction %void None %voidfn
3852
3853 %10 = OpLabel
3854 OpBranch %20
3855
3856 %20 = OpLabel
3857 OpSelectionMerge %99 None
3858 OpSwitch %selector %20 30 %30
3859
3860 %30 = OpLabel
3861 OpBranch %99
3862
3863 %99 = OpLabel
3864 OpReturn
3865
3866 OpFunctionEnd
3867 )";
3868 auto p = parser(test::Assemble(assembly));
3869 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3870 auto fe = p->function_emitter(100);
3871 fe.RegisterBasicBlocks();
3872 fe.ComputeBlockOrderAndPositions();
3873 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
3874 fe.RegisterMerges();
3875 fe.LabelControlFlowConstructs();
3876 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
3877 // Self-loop that isn't its own continue target is already rejected with a
3878 // different message.
3879 EXPECT_THAT(
3880 p->error(),
3881 Eq("Block 20 branches to itself but is not its own continue target"));
3882 }
3883
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_DefaultCantEscapeSwitch)3884 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_DefaultCantEscapeSwitch) {
3885 auto assembly = CommonTypes() + R"(
3886 %100 = OpFunction %void None %voidfn
3887
3888 %10 = OpLabel
3889 OpSelectionMerge %50 None
3890 OpSwitch %selector %99 30 %30 ; default goes past the merge
3891
3892 %30 = OpLabel
3893 OpBranch %50
3894
3895 %50 = OpLabel ; merge
3896 OpBranch %99
3897
3898 %99 = OpLabel
3899 OpReturn
3900
3901 OpFunctionEnd
3902 )";
3903 auto p = parser(test::Assemble(assembly));
3904 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3905 auto fe = p->function_emitter(100);
3906 fe.RegisterBasicBlocks();
3907 fe.ComputeBlockOrderAndPositions();
3908 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
3909 fe.RegisterMerges();
3910 fe.LabelControlFlowConstructs();
3911 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
3912 EXPECT_THAT(p->error(), Eq("Switch branch from block 10 to default block 99 "
3913 "escapes the selection construct"));
3914 }
3915
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_DefaultForTwoSwitches_AsMerge)3916 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_DefaultForTwoSwitches_AsMerge) {
3917 auto assembly = CommonTypes() + R"(
3918 %100 = OpFunction %void None %voidfn
3919
3920 %10 = OpLabel
3921 OpSelectionMerge %99 None
3922 OpSwitch %selector %89 20 %20
3923
3924 %20 = OpLabel
3925 OpBranch %50
3926
3927 %50 = OpLabel
3928 OpSelectionMerge %89 None
3929 OpSwitch %selector %89 60 %60
3930
3931 %60 = OpLabel
3932 OpBranch %89
3933
3934 %89 = OpLabel
3935 OpBranch %99
3936
3937 %99 = OpLabel
3938 OpReturn
3939
3940 OpFunctionEnd
3941 )";
3942 auto p = parser(test::Assemble(assembly));
3943 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3944 auto fe = p->function_emitter(100);
3945 fe.RegisterBasicBlocks();
3946 fe.ComputeBlockOrderAndPositions();
3947 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
3948 fe.RegisterMerges();
3949 fe.LabelControlFlowConstructs();
3950 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
3951 EXPECT_THAT(p->error(),
3952 Eq("Block 89 is the default block for switch-selection header 10 "
3953 "and also the merge block for 50 (violates dominance rule)"));
3954 }
3955
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_DefaultForTwoSwitches_AsCaseClause)3956 TEST_F(SpvParserCFGTest,
3957 FindSwitchCaseHeaders_DefaultForTwoSwitches_AsCaseClause) {
3958 auto assembly = CommonTypes() + R"(
3959 %100 = OpFunction %void None %voidfn
3960
3961 %10 = OpLabel
3962 OpSelectionMerge %99 None
3963 OpSwitch %selector %80 20 %20
3964
3965 %20 = OpLabel
3966 OpBranch %50
3967
3968 %50 = OpLabel
3969 OpSelectionMerge %89 None
3970 OpSwitch %selector %80 60 %60
3971
3972 %60 = OpLabel
3973 OpBranch %89 ; fallthrough
3974
3975 %80 = OpLabel ; default for both switches
3976 OpBranch %89
3977
3978 %89 = OpLabel ; inner selection merge
3979 OpBranch %99
3980
3981 %99 = OpLabel ; outer selection mege
3982 OpReturn
3983
3984 OpFunctionEnd
3985 )";
3986 auto p = parser(test::Assemble(assembly));
3987 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
3988 auto fe = p->function_emitter(100);
3989 fe.RegisterBasicBlocks();
3990 fe.ComputeBlockOrderAndPositions();
3991 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
3992 fe.RegisterMerges();
3993 fe.LabelControlFlowConstructs();
3994 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
3995 EXPECT_THAT(p->error(), Eq("Block 80 is declared as the default target for "
3996 "two OpSwitch instructions, at blocks 10 and 50"));
3997 }
3998
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_CaseIsLongRangeBackedge)3999 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_CaseIsLongRangeBackedge) {
4000 auto assembly = CommonTypes() + R"(
4001 %100 = OpFunction %void None %voidfn
4002
4003 %10 = OpLabel
4004 OpBranch %20
4005
4006 %20 = OpLabel
4007 OpSelectionMerge %99 None
4008 OpSwitch %selector %99 10 %10
4009
4010 %99 = OpLabel
4011 OpReturn
4012
4013 OpFunctionEnd
4014 )";
4015 auto p = parser(test::Assemble(assembly));
4016 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4017 auto fe = p->function_emitter(100);
4018 fe.RegisterBasicBlocks();
4019 fe.ComputeBlockOrderAndPositions();
4020 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4021 fe.RegisterMerges();
4022 fe.LabelControlFlowConstructs();
4023 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
4024 EXPECT_THAT(p->error(), Eq("Switch branch from block 20 to case target "
4025 "block 10 can't be a back-edge"));
4026 }
4027
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_CaseIsSelfLoop)4028 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_CaseIsSelfLoop) {
4029 auto assembly = CommonTypes() + R"(
4030 %100 = OpFunction %void None %voidfn
4031
4032 %10 = OpLabel
4033 OpBranch %20
4034
4035 %20 = OpLabel
4036 OpSelectionMerge %99 None
4037 OpSwitch %selector %99 20 %20
4038
4039 %99 = OpLabel
4040 OpReturn
4041
4042 OpFunctionEnd
4043 )";
4044 auto p = parser(test::Assemble(assembly));
4045 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4046 auto fe = p->function_emitter(100);
4047 fe.RegisterBasicBlocks();
4048 fe.ComputeBlockOrderAndPositions();
4049 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4050 fe.RegisterMerges();
4051 fe.LabelControlFlowConstructs();
4052 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
4053 // The error is caught earlier
4054 EXPECT_THAT(
4055 p->error(),
4056 Eq("Block 20 branches to itself but is not its own continue target"));
4057 }
4058
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_CaseCanBeSwitchMerge)4059 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_CaseCanBeSwitchMerge) {
4060 auto assembly = CommonTypes() + R"(
4061 %100 = OpFunction %void None %voidfn
4062
4063 %10 = OpLabel
4064 OpBranch %20
4065
4066 %20 = OpLabel
4067 OpSelectionMerge %99 None
4068 OpSwitch %selector %99 20 %99
4069
4070 %99 = OpLabel
4071 OpReturn
4072
4073 OpFunctionEnd
4074 )";
4075 auto p = parser(test::Assemble(assembly));
4076 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4077 auto fe = p->function_emitter(100);
4078 fe.RegisterBasicBlocks();
4079 fe.ComputeBlockOrderAndPositions();
4080 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4081 fe.RegisterMerges();
4082 fe.LabelControlFlowConstructs();
4083 EXPECT_TRUE(fe.FindSwitchCaseHeaders());
4084
4085 // TODO(crbug.com/tint/774) Re-enable after codegen bug fixed.
4086 p->DeliberatelyInvalidSpirv();
4087 }
4088
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_CaseCantEscapeSwitch)4089 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_CaseCantEscapeSwitch) {
4090 auto assembly = CommonTypes() + R"(
4091 %100 = OpFunction %void None %voidfn
4092
4093 %10 = OpLabel
4094 OpSelectionMerge %99 None ; force %99 to be very late in block order
4095 OpBranchConditional %cond %20 %99
4096
4097 %20 = OpLabel
4098 OpSelectionMerge %89 None
4099 OpSwitch %selector %89 20 %99
4100
4101 %89 = OpLabel
4102 OpBranch %99
4103
4104 %99 = OpLabel
4105 OpReturn
4106
4107 OpFunctionEnd
4108 )";
4109 auto p = parser(test::Assemble(assembly));
4110 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4111 auto fe = p->function_emitter(100);
4112 fe.RegisterBasicBlocks();
4113 fe.ComputeBlockOrderAndPositions();
4114 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4115 fe.RegisterMerges();
4116 fe.LabelControlFlowConstructs();
4117 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
4118 EXPECT_THAT(p->error(), Eq("Switch branch from block 20 to case target block "
4119 "99 escapes the selection construct"));
4120 }
4121
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_CaseForMoreThanOneSwitch)4122 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_CaseForMoreThanOneSwitch) {
4123 auto assembly = CommonTypes() + R"(
4124 %100 = OpFunction %void None %voidfn
4125
4126 %10 = OpLabel
4127 OpSelectionMerge %99 None
4128 OpSwitch %selector %99 20 %20 50 %50
4129
4130 %20 = OpLabel
4131 OpSelectionMerge %89 None
4132 OpSwitch %selector %89 50 %50
4133
4134 %50 = OpLabel
4135 OpBranch %89
4136
4137 %89 = OpLabel
4138 OpBranch %99
4139
4140 %99 = OpLabel
4141 OpReturn
4142
4143 OpFunctionEnd
4144 )";
4145 auto p = parser(test::Assemble(assembly));
4146 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4147 auto fe = p->function_emitter(100);
4148 fe.RegisterBasicBlocks();
4149 fe.ComputeBlockOrderAndPositions();
4150 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4151 fe.RegisterMerges();
4152 fe.LabelControlFlowConstructs();
4153 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
4154 EXPECT_THAT(p->error(),
4155 Eq("Block 50 is declared as the switch case target for two "
4156 "OpSwitch instructions, at blocks 10 and 20"));
4157 }
4158
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_CaseIsMergeForAnotherConstruct)4159 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_CaseIsMergeForAnotherConstruct) {
4160 auto assembly = CommonTypes() + R"(
4161 %100 = OpFunction %void None %voidfn
4162
4163 %10 = OpLabel
4164 OpSelectionMerge %49 None
4165 OpSwitch %selector %49 20 %20
4166
4167 %20 = OpLabel
4168 OpBranch %49
4169
4170 %49 = OpLabel
4171 OpBranch %50
4172
4173 %50 = OpLabel
4174 OpSelectionMerge %20 None ; points back to the case.
4175 OpBranchConditional %cond %60 %99
4176
4177 %60 = OpLabel
4178 OpBranch %99
4179
4180 %99 = OpLabel
4181 OpReturn
4182
4183 OpFunctionEnd
4184 )";
4185 auto p = parser(test::Assemble(assembly));
4186 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4187 auto fe = p->function_emitter(100);
4188 fe.RegisterBasicBlocks();
4189 fe.ComputeBlockOrderAndPositions();
4190 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4191 fe.RegisterMerges();
4192 fe.LabelControlFlowConstructs();
4193 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
4194 EXPECT_THAT(p->error(), Eq("Switch branch from block 10 to case target block "
4195 "20 escapes the selection construct"));
4196 }
4197
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_NoSwitch)4198 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_NoSwitch) {
4199 auto assembly = CommonTypes() + R"(
4200 %100 = OpFunction %void None %voidfn
4201
4202 %10 = OpLabel
4203 OpReturn
4204
4205 OpFunctionEnd
4206 )";
4207 auto p = parser(test::Assemble(assembly));
4208 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4209 auto fe = p->function_emitter(100);
4210 fe.RegisterBasicBlocks();
4211 fe.ComputeBlockOrderAndPositions();
4212 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4213 fe.RegisterMerges();
4214 fe.LabelControlFlowConstructs();
4215 EXPECT_TRUE(fe.FindSwitchCaseHeaders());
4216
4217 const auto* bi10 = fe.GetBlockInfo(10);
4218 ASSERT_NE(bi10, nullptr);
4219 EXPECT_EQ(bi10->case_head_for, nullptr);
4220 EXPECT_EQ(bi10->default_head_for, nullptr);
4221 EXPECT_FALSE(bi10->default_is_merge);
4222 EXPECT_EQ(bi10->case_values.get(), nullptr);
4223 }
4224
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_DefaultIsMerge)4225 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_DefaultIsMerge) {
4226 auto assembly = CommonTypes() + R"(
4227 %100 = OpFunction %void None %voidfn
4228
4229 %10 = OpLabel
4230 OpSelectionMerge %99 None
4231 OpSwitch %selector %99 20 %20
4232
4233 %20 = OpLabel
4234 OpBranch %99
4235
4236 %99 = OpLabel
4237 OpReturn
4238
4239 OpFunctionEnd
4240 )";
4241 auto p = parser(test::Assemble(assembly));
4242 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4243 auto fe = p->function_emitter(100);
4244 fe.RegisterBasicBlocks();
4245 fe.ComputeBlockOrderAndPositions();
4246 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4247 fe.RegisterMerges();
4248 fe.LabelControlFlowConstructs();
4249 EXPECT_TRUE(fe.FindSwitchCaseHeaders());
4250
4251 const auto* bi99 = fe.GetBlockInfo(99);
4252 ASSERT_NE(bi99, nullptr);
4253 EXPECT_EQ(bi99->case_head_for, nullptr);
4254 ASSERT_NE(bi99->default_head_for, nullptr);
4255 EXPECT_EQ(bi99->default_head_for->begin_id, 10u);
4256 EXPECT_TRUE(bi99->default_is_merge);
4257 EXPECT_EQ(bi99->case_values.get(), nullptr);
4258 }
4259
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_DefaultIsNotMerge)4260 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_DefaultIsNotMerge) {
4261 auto assembly = CommonTypes() + R"(
4262 %100 = OpFunction %void None %voidfn
4263
4264 %10 = OpLabel
4265 OpSelectionMerge %99 None
4266 OpSwitch %selector %30 20 %20
4267
4268 %20 = OpLabel
4269 OpBranch %99
4270
4271 %30 = OpLabel
4272 OpBranch %99
4273
4274 %99 = OpLabel
4275 OpReturn
4276
4277 OpFunctionEnd
4278 )";
4279 auto p = parser(test::Assemble(assembly));
4280 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4281 auto fe = p->function_emitter(100);
4282 fe.RegisterBasicBlocks();
4283 fe.ComputeBlockOrderAndPositions();
4284 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4285 fe.RegisterMerges();
4286 fe.LabelControlFlowConstructs();
4287 EXPECT_TRUE(fe.FindSwitchCaseHeaders());
4288
4289 const auto* bi30 = fe.GetBlockInfo(30);
4290 ASSERT_NE(bi30, nullptr);
4291 EXPECT_EQ(bi30->case_head_for, nullptr);
4292 ASSERT_NE(bi30->default_head_for, nullptr);
4293 EXPECT_EQ(bi30->default_head_for->begin_id, 10u);
4294 EXPECT_FALSE(bi30->default_is_merge);
4295 EXPECT_EQ(bi30->case_values.get(), nullptr);
4296 }
4297
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_CaseIsNotDefault)4298 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_CaseIsNotDefault) {
4299 auto assembly = CommonTypes() + R"(
4300 %100 = OpFunction %void None %voidfn
4301
4302 %10 = OpLabel
4303 OpSelectionMerge %99 None
4304 OpSwitch %selector %30 200 %20
4305
4306 %20 = OpLabel
4307 OpBranch %99
4308
4309 %30 = OpLabel
4310 OpBranch %99
4311
4312 %99 = OpLabel
4313 OpReturn
4314
4315 OpFunctionEnd
4316 )";
4317 auto p = parser(test::Assemble(assembly));
4318 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4319 auto fe = p->function_emitter(100);
4320 fe.RegisterBasicBlocks();
4321 fe.ComputeBlockOrderAndPositions();
4322 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4323 fe.RegisterMerges();
4324 fe.LabelControlFlowConstructs();
4325 EXPECT_TRUE(fe.FindSwitchCaseHeaders());
4326
4327 const auto* bi20 = fe.GetBlockInfo(20);
4328 ASSERT_NE(bi20, nullptr);
4329 ASSERT_NE(bi20->case_head_for, nullptr);
4330 EXPECT_EQ(bi20->case_head_for->begin_id, 10u);
4331 EXPECT_EQ(bi20->default_head_for, nullptr);
4332 EXPECT_FALSE(bi20->default_is_merge);
4333 EXPECT_THAT(*(bi20->case_values.get()), UnorderedElementsAre(200));
4334 }
4335
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_CaseIsDefault)4336 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_CaseIsDefault) {
4337 auto assembly = CommonTypes() + R"(
4338 %100 = OpFunction %void None %voidfn
4339
4340 %10 = OpLabel
4341 OpSelectionMerge %99 None
4342 OpSwitch %selector %20 200 %20
4343
4344 %20 = OpLabel
4345 OpBranch %99
4346
4347 %99 = OpLabel
4348 OpReturn
4349
4350 OpFunctionEnd
4351 )";
4352 auto p = parser(test::Assemble(assembly));
4353 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4354 auto fe = p->function_emitter(100);
4355 fe.RegisterBasicBlocks();
4356 fe.ComputeBlockOrderAndPositions();
4357 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4358 fe.RegisterMerges();
4359 fe.LabelControlFlowConstructs();
4360 EXPECT_TRUE(fe.FindSwitchCaseHeaders());
4361
4362 const auto* bi20 = fe.GetBlockInfo(20);
4363 ASSERT_NE(bi20, nullptr);
4364 ASSERT_NE(bi20->case_head_for, nullptr);
4365 EXPECT_EQ(bi20->case_head_for->begin_id, 10u);
4366 EXPECT_EQ(bi20->default_head_for, bi20->case_head_for);
4367 EXPECT_FALSE(bi20->default_is_merge);
4368 EXPECT_THAT(*(bi20->case_values.get()), UnorderedElementsAre(200));
4369 }
4370
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_ManyCasesWithSameValue_IsError)4371 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_ManyCasesWithSameValue_IsError) {
4372 auto assembly = CommonTypes() + R"(
4373 %100 = OpFunction %void None %voidfn
4374
4375 %10 = OpLabel
4376 OpSelectionMerge %99 None
4377 OpSwitch %selector %99 200 %20 200 %30
4378
4379 %20 = OpLabel
4380 OpBranch %99
4381
4382 %30 = OpLabel
4383 OpBranch %99
4384
4385 %99 = OpLabel
4386 OpReturn
4387
4388 OpFunctionEnd
4389 )";
4390 auto p = parser(test::Assemble(assembly));
4391 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4392 auto fe = p->function_emitter(100);
4393 fe.RegisterBasicBlocks();
4394 fe.ComputeBlockOrderAndPositions();
4395 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4396 fe.RegisterMerges();
4397 fe.LabelControlFlowConstructs();
4398 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
4399
4400 EXPECT_THAT(p->error(),
4401 Eq("Duplicate case value 200 in OpSwitch in block 10"));
4402 }
4403
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_ManyValuesWithSameCase)4404 TEST_F(SpvParserCFGTest, FindSwitchCaseHeaders_ManyValuesWithSameCase) {
4405 auto assembly = CommonTypes() + R"(
4406 %100 = OpFunction %void None %voidfn
4407
4408 %10 = OpLabel
4409 OpSelectionMerge %99 None
4410 OpSwitch %selector %99 200 %20 300 %20
4411
4412 %20 = OpLabel
4413 OpBranch %99
4414
4415 %99 = OpLabel
4416 OpReturn
4417
4418 OpFunctionEnd
4419 )";
4420 auto p = parser(test::Assemble(assembly));
4421 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4422 auto fe = p->function_emitter(100);
4423 fe.RegisterBasicBlocks();
4424 fe.ComputeBlockOrderAndPositions();
4425 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
4426 fe.RegisterMerges();
4427 fe.LabelControlFlowConstructs();
4428 EXPECT_TRUE(fe.FindSwitchCaseHeaders());
4429
4430 const auto* bi20 = fe.GetBlockInfo(20);
4431 ASSERT_NE(bi20, nullptr);
4432 ASSERT_NE(bi20->case_head_for, nullptr);
4433 EXPECT_EQ(bi20->case_head_for->begin_id, 10u);
4434 EXPECT_EQ(bi20->default_head_for, nullptr);
4435 EXPECT_FALSE(bi20->default_is_merge);
4436 EXPECT_THAT(*(bi20->case_values.get()), UnorderedElementsAre(200, 300));
4437 }
4438
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_BranchEscapesIfConstruct)4439 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_BranchEscapesIfConstruct) {
4440 auto assembly = CommonTypes() + R"(
4441 %100 = OpFunction %void None %voidfn
4442
4443 %10 = OpLabel
4444 OpSelectionMerge %99 None
4445 OpBranchConditional %cond %20 %99
4446
4447 %20 = OpLabel
4448 OpSelectionMerge %50 None
4449 OpBranchConditional %cond2 %30 %50
4450
4451 %30 = OpLabel
4452 OpBranch %80 ; bad exit to %80
4453
4454 %50 = OpLabel
4455 OpBranch %80
4456
4457 %80 = OpLabel ; bad target
4458 OpBranch %99
4459
4460 %99 = OpLabel
4461 OpReturn
4462
4463 OpFunctionEnd
4464 )";
4465 auto p = parser(test::Assemble(assembly));
4466 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4467 auto fe = p->function_emitter(100);
4468 EXPECT_FALSE(FlowClassifyCFGEdges(&fe)) << p->error();
4469 // Some further processing
4470 EXPECT_THAT(
4471 p->error(),
4472 Eq("Branch from block 30 to block 80 is an invalid exit from construct "
4473 "starting at block 20; branch bypasses merge block 50"));
4474 }
4475
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_ReturnInContinueConstruct)4476 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_ReturnInContinueConstruct) {
4477 auto assembly = CommonTypes() + R"(
4478 %100 = OpFunction %void None %voidfn
4479
4480 %10 = OpLabel
4481 OpBranch %20
4482
4483 %20 = OpLabel
4484 OpLoopMerge %99 %50 None
4485 OpBranchConditional %cond %30 %99
4486
4487 %30 = OpLabel ; body
4488 OpBranch %50
4489
4490 %50 = OpLabel
4491 OpReturn
4492
4493 %99 = OpLabel
4494 OpReturn
4495
4496 OpFunctionEnd
4497 )";
4498 auto p = parser(test::Assemble(assembly));
4499 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4500 auto fe = p->function_emitter(100);
4501 EXPECT_FALSE(FlowClassifyCFGEdges(&fe)) << p->error();
4502 EXPECT_THAT(p->error(), Eq("Invalid function exit at block 50 from continue "
4503 "construct starting at 50"));
4504 }
4505
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_KillInContinueConstruct)4506 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_KillInContinueConstruct) {
4507 auto assembly = CommonTypes() + R"(
4508 %100 = OpFunction %void None %voidfn
4509
4510 %10 = OpLabel
4511 OpBranch %20
4512
4513 %20 = OpLabel
4514 OpLoopMerge %99 %50 None
4515 OpBranchConditional %cond %30 %99
4516
4517 %30 = OpLabel ; body
4518 OpBranch %50
4519
4520 %50 = OpLabel
4521 OpKill
4522
4523 %99 = OpLabel
4524 OpReturn
4525
4526 OpFunctionEnd
4527 )";
4528 auto p = parser(test::Assemble(assembly));
4529 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4530 auto fe = p->function_emitter(100);
4531 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
4532 EXPECT_THAT(p->error(), Eq("Invalid function exit at block 50 from continue "
4533 "construct starting at 50"));
4534 }
4535
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_UnreachableInContinueConstruct)4536 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_UnreachableInContinueConstruct) {
4537 auto assembly = CommonTypes() + R"(
4538 %100 = OpFunction %void None %voidfn
4539
4540 %10 = OpLabel
4541 OpBranch %20
4542
4543 %20 = OpLabel
4544 OpLoopMerge %99 %50 None
4545 OpBranchConditional %cond %30 %99
4546
4547 %30 = OpLabel ; body
4548 OpBranch %50
4549
4550 %50 = OpLabel
4551 OpUnreachable
4552
4553 %99 = OpLabel
4554 OpReturn
4555
4556 OpFunctionEnd
4557 )";
4558 auto p = parser(test::Assemble(assembly));
4559 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4560 auto fe = p->function_emitter(100);
4561 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
4562 EXPECT_THAT(p->error(), Eq("Invalid function exit at block 50 from continue "
4563 "construct starting at 50"));
4564 }
4565
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_BackEdge_NotInContinueConstruct)4566 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_BackEdge_NotInContinueConstruct) {
4567 auto assembly = CommonTypes() + R"(
4568 %100 = OpFunction %void None %voidfn
4569
4570 %10 = OpLabel
4571 OpBranch %20
4572
4573 %20 = OpLabel
4574 OpLoopMerge %99 %50 None
4575 OpBranchConditional %cond %30 %99
4576
4577 %30 = OpLabel ; body
4578 OpBranch %20 ; bad backedge
4579
4580 %50 = OpLabel ; continue target
4581 OpBranch %99
4582
4583 %99 = OpLabel
4584 OpReturn
4585
4586 OpFunctionEnd
4587 )";
4588 auto p = parser(test::Assemble(assembly));
4589 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4590 auto fe = p->function_emitter(100);
4591 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
4592 EXPECT_THAT(
4593 p->error(),
4594 Eq("Invalid backedge (30->20): 30 is not in a continue construct"));
4595 }
4596
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_BackEdge_NotInLastBlockOfContinueConstruct)4597 TEST_F(SpvParserCFGTest,
4598 ClassifyCFGEdges_BackEdge_NotInLastBlockOfContinueConstruct) {
4599 auto assembly = CommonTypes() + R"(
4600 %100 = OpFunction %void None %voidfn
4601
4602 %10 = OpLabel
4603 OpBranch %20
4604
4605 %20 = OpLabel
4606 OpLoopMerge %99 %50 None
4607 OpBranchConditional %cond %30 %99
4608
4609 %30 = OpLabel ; body
4610 OpBranch %50
4611
4612 %50 = OpLabel ; continue target
4613 OpBranchConditional %cond %20 %60 ; bad branch to %20
4614
4615 %60 = OpLabel ; end of continue construct
4616 OpBranch %20
4617
4618 %99 = OpLabel
4619 OpReturn
4620
4621 OpFunctionEnd
4622 )";
4623 auto p = parser(test::Assemble(assembly));
4624 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4625 auto fe = p->function_emitter(100);
4626 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
4627 EXPECT_THAT(p->error(),
4628 Eq("Invalid exit (50->20) from continue construct: 50 is not the "
4629 "last block in the continue construct starting at 50 "
4630 "(violates post-dominance rule)"));
4631 }
4632
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_BackEdge_ToWrongHeader)4633 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_BackEdge_ToWrongHeader) {
4634 auto assembly = CommonTypes() + R"(
4635 %100 = OpFunction %void None %voidfn
4636
4637 %10 = OpLabel
4638 OpSelectionMerge %99 None
4639 OpBranchConditional %cond %20 %99
4640
4641 %20 = OpLabel
4642 OpLoopMerge %89 %50 None
4643 OpBranchConditional %cond %30 %89
4644
4645 %30 = OpLabel ; loop body
4646 OpBranch %50
4647
4648 %50 = OpLabel ; continue target
4649 OpBranch %10
4650
4651 %89 = OpLabel ; inner merge
4652 OpBranch %99
4653
4654 %99 = OpLabel ; outer merge
4655 OpReturn
4656
4657 OpFunctionEnd
4658 )";
4659 auto p = parser(test::Assemble(assembly));
4660 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4661 auto fe = p->function_emitter(100);
4662 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
4663 EXPECT_THAT(p->error(), Eq("Invalid backedge (50->10): does not branch to "
4664 "the corresponding loop header, expected 20"));
4665 }
4666
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_BackEdge_SingleBlockLoop)4667 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_BackEdge_SingleBlockLoop) {
4668 auto assembly = CommonTypes() + R"(
4669 %100 = OpFunction %void None %voidfn
4670
4671 %10 = OpLabel
4672 OpBranch %20
4673
4674 %20 = OpLabel
4675 OpLoopMerge %99 %20 None
4676 OpBranchConditional %cond %20 %99
4677
4678 %99 = OpLabel ; outer merge
4679 OpReturn
4680
4681 OpFunctionEnd
4682 )";
4683 auto p = parser(test::Assemble(assembly));
4684 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4685 auto fe = p->function_emitter(100);
4686 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
4687
4688 auto* bi20 = fe.GetBlockInfo(20);
4689 ASSERT_NE(bi20, nullptr);
4690 EXPECT_EQ(bi20->succ_edge.count(20), 1u);
4691 EXPECT_EQ(bi20->succ_edge[20], EdgeKind::kBack);
4692 }
4693
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_BackEdge_MultiBlockLoop_SingleBlockContinueConstruct)4694 TEST_F(SpvParserCFGTest,
4695 ClassifyCFGEdges_BackEdge_MultiBlockLoop_SingleBlockContinueConstruct) {
4696 auto assembly = CommonTypes() + R"(
4697 %100 = OpFunction %void None %voidfn
4698
4699 %10 = OpLabel
4700 OpBranch %20
4701
4702 %20 = OpLabel
4703 OpLoopMerge %99 %40 None
4704 OpBranchConditional %cond %30 %99
4705
4706 %30 = OpLabel
4707 OpBranch %40
4708
4709 %40 = OpLabel ; continue target
4710 OpBranch %20 ; good back edge
4711
4712 %99 = OpLabel ; outer merge
4713 OpReturn
4714
4715 OpFunctionEnd
4716 )";
4717 auto p = parser(test::Assemble(assembly));
4718 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4719 auto fe = p->function_emitter(100);
4720 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
4721
4722 auto* bi40 = fe.GetBlockInfo(40);
4723 ASSERT_NE(bi40, nullptr);
4724 EXPECT_EQ(bi40->succ_edge.count(20), 1u);
4725 EXPECT_EQ(bi40->succ_edge[20], EdgeKind::kBack);
4726 }
4727
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_BackEdge_MultiBlockLoop_MultiBlockContinueConstruct_ContinueIsNotHeader)4728 TEST_F(
4729 SpvParserCFGTest,
4730 ClassifyCFGEdges_BackEdge_MultiBlockLoop_MultiBlockContinueConstruct_ContinueIsNotHeader) { // NOLINT
4731 auto assembly = CommonTypes() + R"(
4732 %100 = OpFunction %void None %voidfn
4733
4734 %10 = OpLabel
4735 OpBranch %20
4736
4737 %20 = OpLabel
4738 OpLoopMerge %99 %40 None
4739 OpBranchConditional %cond %30 %99
4740
4741 %30 = OpLabel
4742 OpBranch %40
4743
4744 %40 = OpLabel ; continue target
4745 OpBranch %50
4746
4747 %50 = OpLabel
4748 OpBranch %20 ; good back edge
4749
4750 %99 = OpLabel ; outer merge
4751 OpReturn
4752
4753 OpFunctionEnd
4754 )";
4755 auto p = parser(test::Assemble(assembly));
4756 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4757 auto fe = p->function_emitter(100);
4758 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
4759
4760 auto* bi50 = fe.GetBlockInfo(50);
4761 ASSERT_NE(bi50, nullptr);
4762 EXPECT_EQ(bi50->succ_edge.count(20), 1u);
4763 EXPECT_EQ(bi50->succ_edge[20], EdgeKind::kBack);
4764 }
4765
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_BackEdge_MultiBlockLoop_MultiBlockContinueConstruct_ContinueIsHeader)4766 TEST_F(
4767 SpvParserCFGTest,
4768 ClassifyCFGEdges_BackEdge_MultiBlockLoop_MultiBlockContinueConstruct_ContinueIsHeader) { // NOLINT
4769 auto assembly = CommonTypes() + R"(
4770 %100 = OpFunction %void None %voidfn
4771
4772 %10 = OpLabel
4773 OpBranch %20
4774
4775 %20 = OpLabel
4776 OpLoopMerge %99 %20 None ; continue target
4777 OpBranch %30
4778
4779 %30 = OpLabel
4780 OpBranch %40
4781
4782 %40 = OpLabel
4783 OpBranch %50
4784
4785 %50 = OpLabel
4786 OpBranchConditional %cond %20 %99 ; good back edge
4787
4788 %99 = OpLabel ; outer merge
4789 OpReturn
4790
4791 OpFunctionEnd
4792 )";
4793 auto p = parser(test::Assemble(assembly));
4794 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4795 auto fe = p->function_emitter(100);
4796 EXPECT_TRUE(FlowClassifyCFGEdges(&fe)) << p->error();
4797
4798 auto* bi50 = fe.GetBlockInfo(50);
4799 ASSERT_NE(bi50, nullptr);
4800 EXPECT_EQ(bi50->succ_edge.count(20), 1u);
4801 EXPECT_EQ(bi50->succ_edge[20], EdgeKind::kBack);
4802 }
4803
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_PrematureExitFromContinueConstruct)4804 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_PrematureExitFromContinueConstruct) {
4805 auto assembly = CommonTypes() + R"(
4806 %100 = OpFunction %void None %voidfn
4807
4808 %10 = OpLabel
4809 OpBranch %20
4810
4811 %20 = OpLabel
4812 OpLoopMerge %99 %40 None
4813 OpBranchConditional %cond %30 %99
4814
4815 %30 = OpLabel
4816 OpBranch %40
4817
4818 %40 = OpLabel ; continue construct
4819 OpBranchConditional %cond2 %99 %50 ; invalid early exit
4820
4821 %50 = OpLabel
4822 OpBranch %20 ; back edge
4823
4824 %99 = OpLabel ; outer merge
4825 OpReturn
4826
4827 OpFunctionEnd
4828 )";
4829 auto p = parser(test::Assemble(assembly));
4830 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4831 auto fe = p->function_emitter(100);
4832 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
4833 EXPECT_THAT(p->error(),
4834 Eq("Invalid exit (40->99) from continue construct: 40 is not the "
4835 "last block in the continue construct starting at 40 "
4836 "(violates post-dominance rule)"));
4837 }
4838
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopBreak_FromLoopHeader_SingleBlockLoop_TrueBranch)4839 TEST_F(SpvParserCFGTest,
4840 ClassifyCFGEdges_LoopBreak_FromLoopHeader_SingleBlockLoop_TrueBranch) {
4841 auto assembly = CommonTypes() + R"(
4842 %100 = OpFunction %void None %voidfn
4843
4844 %10 = OpLabel
4845 OpBranch %20
4846
4847 %20 = OpLabel ; single block loop
4848 OpLoopMerge %99 %20 None
4849 OpBranchConditional %cond %99 %20
4850
4851 %99 = OpLabel ; outer merge
4852 OpReturn
4853
4854 OpFunctionEnd
4855 )";
4856 auto p = parser(test::Assemble(assembly));
4857 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4858 auto fe = p->function_emitter(100);
4859 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
4860
4861 auto* bi = fe.GetBlockInfo(20);
4862 ASSERT_NE(bi, nullptr);
4863 EXPECT_EQ(bi->succ_edge.count(99), 1u);
4864 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kLoopBreak);
4865 EXPECT_EQ(bi->succ_edge.count(20), 1u);
4866 EXPECT_EQ(bi->succ_edge[20], EdgeKind::kBack);
4867 }
4868
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopBreak_FromLoopHeader_SingleBlockLoop_FalseBranch)4869 TEST_F(SpvParserCFGTest,
4870 ClassifyCFGEdges_LoopBreak_FromLoopHeader_SingleBlockLoop_FalseBranch) {
4871 auto assembly = CommonTypes() + R"(
4872 %100 = OpFunction %void None %voidfn
4873
4874 %10 = OpLabel
4875 OpBranch %20
4876
4877 %20 = OpLabel ; single block loop
4878 OpLoopMerge %99 %20 None
4879 OpBranchConditional %cond %20 %99
4880
4881 %99 = OpLabel ; outer merge
4882 OpReturn
4883
4884 OpFunctionEnd
4885 )";
4886 auto p = parser(test::Assemble(assembly));
4887 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4888 auto fe = p->function_emitter(100);
4889 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
4890
4891 auto* bi = fe.GetBlockInfo(20);
4892 ASSERT_NE(bi, nullptr);
4893 EXPECT_EQ(bi->succ_edge.count(99), 1u);
4894 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kLoopBreak);
4895 EXPECT_EQ(bi->succ_edge.count(20), 1u);
4896 EXPECT_EQ(bi->succ_edge[20], EdgeKind::kBack);
4897 }
4898
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopBreak_FromLoopHeader_MultiBlockLoop)4899 TEST_F(SpvParserCFGTest,
4900 ClassifyCFGEdges_LoopBreak_FromLoopHeader_MultiBlockLoop) {
4901 auto assembly = CommonTypes() + R"(
4902 %100 = OpFunction %void None %voidfn
4903
4904 %10 = OpLabel
4905 OpBranch %20
4906
4907 %20 = OpLabel
4908 OpLoopMerge %99 %30 None
4909 OpBranchConditional %cond %30 %99
4910
4911 %30 = OpLabel
4912 OpBranch %20
4913
4914 %99 = OpLabel
4915 OpReturn
4916
4917 OpFunctionEnd
4918 )";
4919 auto p = parser(test::Assemble(assembly));
4920 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4921 auto fe = p->function_emitter(100);
4922 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
4923
4924 auto* bi = fe.GetBlockInfo(20);
4925 ASSERT_NE(bi, nullptr);
4926 EXPECT_EQ(bi->succ_edge.count(99), 1u);
4927 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kLoopBreak);
4928 }
4929
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopBreak_FromContinueConstructHeader)4930 TEST_F(SpvParserCFGTest,
4931 ClassifyCFGEdges_LoopBreak_FromContinueConstructHeader) {
4932 auto assembly = CommonTypes() + R"(
4933 %100 = OpFunction %void None %voidfn
4934
4935 %10 = OpLabel
4936 OpBranch %20
4937
4938 %20 = OpLabel
4939 OpLoopMerge %99 %30 None
4940 OpBranchConditional %cond %30 %99
4941
4942 %30 = OpLabel ; Single block continue construct
4943 OpBranchConditional %cond2 %20 %99
4944
4945 %99 = OpLabel
4946 OpReturn
4947
4948 OpFunctionEnd
4949 )";
4950 auto p = parser(test::Assemble(assembly));
4951 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4952 auto fe = p->function_emitter(100);
4953 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
4954
4955 auto* bi = fe.GetBlockInfo(30);
4956 ASSERT_NE(bi, nullptr);
4957 EXPECT_EQ(bi->succ_edge.count(99), 1u);
4958 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kLoopBreak);
4959 }
4960
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_IfBreak_FromIfHeader)4961 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_IfBreak_FromIfHeader) {
4962 auto assembly = CommonTypes() + R"(
4963 %100 = OpFunction %void None %voidfn
4964
4965 %10 = OpLabel
4966 OpSelectionMerge %99 None
4967 OpBranchConditional %cond %20 %99
4968
4969 %20 = OpLabel
4970 OpBranch %99
4971
4972 %99 = OpLabel
4973 OpReturn
4974
4975 OpFunctionEnd
4976 )";
4977 auto p = parser(test::Assemble(assembly));
4978 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
4979 auto fe = p->function_emitter(100);
4980 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
4981
4982 auto* bi = fe.GetBlockInfo(20);
4983 ASSERT_NE(bi, nullptr);
4984 EXPECT_EQ(bi->succ_edge.count(99), 1u);
4985 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kIfBreak);
4986 }
4987
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_IfBreak_FromIfThenElse)4988 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_IfBreak_FromIfThenElse) {
4989 auto assembly = CommonTypes() + R"(
4990 %100 = OpFunction %void None %voidfn
4991
4992 %10 = OpLabel
4993 OpSelectionMerge %99 None
4994 OpBranchConditional %cond %20 %50
4995
4996 %20 = OpLabel
4997 OpBranch %99
4998
4999 %50 = OpLabel
5000 OpBranch %99
5001
5002 %99 = OpLabel
5003 OpReturn
5004
5005 OpFunctionEnd
5006 )";
5007 auto p = parser(test::Assemble(assembly));
5008 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5009 auto fe = p->function_emitter(100);
5010 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5011
5012 // Then clause
5013 auto* bi20 = fe.GetBlockInfo(20);
5014 ASSERT_NE(bi20, nullptr);
5015 EXPECT_EQ(bi20->succ_edge.count(99), 1u);
5016 EXPECT_EQ(bi20->succ_edge[99], EdgeKind::kIfBreak);
5017
5018 // Else clause
5019 auto* bi50 = fe.GetBlockInfo(50);
5020 ASSERT_NE(bi50, nullptr);
5021 EXPECT_EQ(bi50->succ_edge.count(99), 1u);
5022 EXPECT_EQ(bi50->succ_edge[99], EdgeKind::kIfBreak);
5023 }
5024
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_IfBreak_BypassesMerge_IsError)5025 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_IfBreak_BypassesMerge_IsError) {
5026 auto assembly = CommonTypes() + R"(
5027 %100 = OpFunction %void None %voidfn
5028
5029 %10 = OpLabel
5030 OpSelectionMerge %50 None
5031 OpBranchConditional %cond %20 %50
5032
5033 %20 = OpLabel
5034 OpBranch %99
5035
5036 %50 = OpLabel ; merge
5037 OpBranch %99
5038
5039 %99 = OpLabel
5040 OpReturn
5041
5042 OpFunctionEnd
5043 )";
5044 auto p = parser(test::Assemble(assembly));
5045 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5046 auto fe = p->function_emitter(100);
5047 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
5048 EXPECT_THAT(
5049 p->error(),
5050 Eq("Branch from block 20 to block 99 is an invalid exit from "
5051 "construct starting at block 10; branch bypasses merge block 50"));
5052 }
5053
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_IfBreak_EscapeSwitchCase_IsError)5054 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_IfBreak_EscapeSwitchCase_IsError) {
5055 // Code generation assumes that you can't have kCaseFallThrough and kIfBreak
5056 // from the same OpBranchConditional.
5057 // This checks one direction of that, where the IfBreak is shown it can't
5058 // escape a switch case.
5059 auto assembly = CommonTypes() + R"(
5060 %100 = OpFunction %void None %voidfn
5061
5062 %10 = OpLabel
5063 OpSelectionMerge %99 None ; Set up if-break to %99
5064 OpBranchConditional %cond %20 %99
5065
5066 %20 = OpLabel
5067 OpSelectionMerge %80 None ; switch-selection
5068 OpSwitch %selector %80 30 %30 40 %40
5069
5070 %30 = OpLabel ; first case
5071 ; branch to %99 would be an if-break, but it bypasess the switch merge
5072 ; Also has case fall-through
5073 OpBranchConditional %cond2 %99 %40
5074
5075 %40 = OpLabel ; second case
5076 OpBranch %80
5077
5078 %80 = OpLabel ; switch-selection's merge
5079 OpBranch %99
5080
5081 %99 = OpLabel ; if-selection's merge
5082 OpReturn
5083
5084 OpFunctionEnd
5085 )";
5086 auto p = parser(test::Assemble(assembly));
5087 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5088 auto fe = p->function_emitter(100);
5089 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
5090 EXPECT_THAT(
5091 p->error(),
5092 Eq("Branch from block 30 to block 99 is an invalid exit from "
5093 "construct starting at block 20; branch bypasses merge block 80"));
5094 }
5095
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_SwitchBreak_FromSwitchCaseDirect)5096 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_SwitchBreak_FromSwitchCaseDirect) {
5097 auto assembly = CommonTypes() + R"(
5098 %100 = OpFunction %void None %voidfn
5099
5100 %10 = OpLabel
5101 OpSelectionMerge %99 None
5102 OpSwitch %selector %30 20 %99 ; directly to merge
5103
5104 %30 = OpLabel
5105 OpBranch %99
5106
5107 %99 = OpLabel
5108 OpReturn
5109
5110 OpFunctionEnd
5111 )";
5112 auto p = parser(test::Assemble(assembly));
5113 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5114 auto fe = p->function_emitter(100);
5115 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5116
5117 auto* bi = fe.GetBlockInfo(10);
5118 ASSERT_NE(bi, nullptr);
5119 EXPECT_EQ(bi->succ_edge.count(99), 1u);
5120 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kSwitchBreak);
5121 }
5122
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_SwitchBreak_FromSwitchCaseBody)5123 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_SwitchBreak_FromSwitchCaseBody) {
5124 auto assembly = CommonTypes() + R"(
5125 %100 = OpFunction %void None %voidfn
5126
5127 %10 = OpLabel
5128 OpSelectionMerge %99 None
5129 OpSwitch %selector %99 20 %20
5130
5131 %20 = OpLabel
5132 OpBranch %99
5133
5134 %99 = OpLabel
5135 OpReturn
5136
5137 OpFunctionEnd
5138 )";
5139 auto p = parser(test::Assemble(assembly));
5140 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5141 auto fe = p->function_emitter(100);
5142 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5143
5144 auto* bi = fe.GetBlockInfo(20);
5145 ASSERT_NE(bi, nullptr);
5146 EXPECT_EQ(bi->succ_edge.count(99), 1u);
5147 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kSwitchBreak);
5148 }
5149
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_SwitchBreak_FromSwitchDefaultBody)5150 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_SwitchBreak_FromSwitchDefaultBody) {
5151 auto assembly = CommonTypes() + R"(
5152 %100 = OpFunction %void None %voidfn
5153
5154 %10 = OpLabel
5155 OpSelectionMerge %99 None
5156 OpSwitch %selector %30 20 %20
5157
5158 %20 = OpLabel
5159 OpBranch %99
5160
5161 %30 = OpLabel
5162 OpBranch %99
5163
5164 %99 = OpLabel
5165 OpReturn
5166
5167 OpFunctionEnd
5168 )";
5169 auto p = parser(test::Assemble(assembly));
5170 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5171 auto fe = p->function_emitter(100);
5172 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5173
5174 auto* bi = fe.GetBlockInfo(30);
5175 ASSERT_NE(bi, nullptr);
5176 EXPECT_EQ(bi->succ_edge.count(99), 1u);
5177 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kSwitchBreak);
5178 }
5179
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_SwitchBreak_FromSwitchDefaultIsMerge)5180 TEST_F(SpvParserCFGTest,
5181 ClassifyCFGEdges_SwitchBreak_FromSwitchDefaultIsMerge) {
5182 auto assembly = CommonTypes() + R"(
5183 %100 = OpFunction %void None %voidfn
5184
5185 %10 = OpLabel
5186 OpSelectionMerge %99 None
5187 OpSwitch %selector %99 20 %20
5188
5189 %20 = OpLabel
5190 OpBranch %99
5191
5192 %99 = OpLabel
5193 OpReturn
5194
5195 OpFunctionEnd
5196 )";
5197 auto p = parser(test::Assemble(assembly));
5198 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5199 auto fe = p->function_emitter(100);
5200 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5201
5202 auto* bi = fe.GetBlockInfo(10);
5203 ASSERT_NE(bi, nullptr);
5204 EXPECT_EQ(bi->succ_edge.count(99), 1u);
5205 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kSwitchBreak);
5206 }
5207
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_SwitchBreak_FromNestedIf_Unconditional)5208 TEST_F(SpvParserCFGTest,
5209 ClassifyCFGEdges_SwitchBreak_FromNestedIf_Unconditional) {
5210 auto assembly = CommonTypes() + R"(
5211 %100 = OpFunction %void None %voidfn
5212
5213 %10 = OpLabel
5214 OpSelectionMerge %99 None
5215 OpSwitch %selector %99 20 %20
5216
5217 %20 = OpLabel
5218 OpSelectionMerge %80 None
5219 OpBranchConditional %cond %30 %80
5220
5221 %30 = OpLabel
5222 OpBranch %99
5223
5224 %80 = OpLabel ; inner merge
5225 OpBranch %99
5226
5227 %99 = OpLabel ; outer merge
5228 OpReturn
5229
5230 OpFunctionEnd
5231 )";
5232 auto p = parser(test::Assemble(assembly));
5233 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5234 auto fe = p->function_emitter(100);
5235 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5236
5237 auto* bi = fe.GetBlockInfo(30);
5238 ASSERT_NE(bi, nullptr);
5239 EXPECT_EQ(bi->succ_edge.count(99), 1u);
5240 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kSwitchBreak);
5241 }
5242
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_SwitchBreak_FromNestedIf_Conditional)5243 TEST_F(SpvParserCFGTest,
5244 ClassifyCFGEdges_SwitchBreak_FromNestedIf_Conditional) {
5245 auto assembly = CommonTypes() + R"(
5246 %100 = OpFunction %void None %voidfn
5247
5248 %10 = OpLabel
5249 OpSelectionMerge %99 None
5250 OpSwitch %selector %99 20 %20
5251
5252 %20 = OpLabel
5253 OpSelectionMerge %80 None
5254 OpBranchConditional %cond %30 %80
5255
5256 %30 = OpLabel
5257 OpBranchConditional %cond2 %99 %80 ; break-if
5258
5259 %80 = OpLabel ; inner merge
5260 OpBranch %99
5261
5262 %99 = OpLabel ; outer merge
5263 OpReturn
5264
5265 OpFunctionEnd
5266 )";
5267 auto p = parser(test::Assemble(assembly));
5268 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5269 auto fe = p->function_emitter(100);
5270 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5271
5272 auto* bi = fe.GetBlockInfo(30);
5273 ASSERT_NE(bi, nullptr);
5274 EXPECT_EQ(bi->succ_edge.count(99), 1u);
5275 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kSwitchBreak);
5276 }
5277
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_SwitchBreak_BypassesMerge_IsError)5278 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_SwitchBreak_BypassesMerge_IsError) {
5279 auto assembly = CommonTypes() + R"(
5280 %100 = OpFunction %void None %voidfn
5281
5282 %10 = OpLabel
5283 OpSelectionMerge %50 None
5284 OpSwitch %selector %50 20 %20
5285
5286 %20 = OpLabel
5287 OpBranch %99 ; invalid exit
5288
5289 %50 = OpLabel ; switch merge
5290 OpBranch %99
5291
5292 %99 = OpLabel
5293 OpReturn
5294
5295 OpFunctionEnd
5296 )";
5297 auto p = parser(test::Assemble(assembly));
5298 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5299 auto fe = p->function_emitter(100);
5300 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
5301 EXPECT_THAT(
5302 p->error(),
5303 Eq("Branch from block 20 to block 99 is an invalid exit from "
5304 "construct starting at block 10; branch bypasses merge block 50"));
5305 }
5306
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_SwitchBreak_FromNestedLoop_IsError)5307 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_SwitchBreak_FromNestedLoop_IsError) {
5308 // It's an error because the break can only go as far as the loop.
5309 auto assembly = CommonTypes() + R"(
5310 %100 = OpFunction %void None %voidfn
5311
5312 %10 = OpLabel
5313 OpSelectionMerge %99 None
5314 OpSwitch %selector %99 20 %20
5315
5316 %20 = OpLabel
5317 OpLoopMerge %80 %70 None
5318 OpBranchConditional %cond %30 %80
5319
5320 %30 = OpLabel ; in loop construct
5321 OpBranch %99 ; break
5322
5323 %70 = OpLabel
5324 OpBranch %20
5325
5326 %80 = OpLabel
5327 OpBranch %99
5328
5329 %99 = OpLabel ; outer merge
5330 OpReturn
5331
5332 OpFunctionEnd
5333 )";
5334 auto p = parser(test::Assemble(assembly));
5335 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5336 auto fe = p->function_emitter(100);
5337 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
5338 EXPECT_THAT(
5339 p->error(),
5340 Eq("Branch from block 30 to block 99 is an invalid exit from "
5341 "construct starting at block 20; branch bypasses merge block 80"));
5342 }
5343
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_SwitchBreak_FromNestedSwitch_IsError)5344 TEST_F(SpvParserCFGTest,
5345 ClassifyCFGEdges_SwitchBreak_FromNestedSwitch_IsError) {
5346 // It's an error because the break can only go as far as inner switch
5347 auto assembly = CommonTypes() + R"(
5348 %100 = OpFunction %void None %voidfn
5349
5350 %10 = OpLabel
5351 OpSelectionMerge %99 None
5352 OpSwitch %selector %99 20 %20
5353
5354 %20 = OpLabel
5355 OpSelectionMerge %80 None
5356 OpSwitch %selector %80 30 %30
5357
5358 %30 = OpLabel
5359 OpBranch %99 ; break
5360
5361 %80 = OpLabel
5362 OpBranch %99
5363
5364 %99 = OpLabel ; outer merge
5365 OpReturn
5366
5367 OpFunctionEnd
5368 )";
5369 auto p = parser(test::Assemble(assembly));
5370 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5371 auto fe = p->function_emitter(100);
5372 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
5373 EXPECT_THAT(
5374 p->error(),
5375 Eq("Branch from block 30 to block 99 is an invalid exit from "
5376 "construct starting at block 20; branch bypasses merge block 80"));
5377 }
5378
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopBreak_FromLoopBody)5379 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_LoopBreak_FromLoopBody) {
5380 auto assembly = CommonTypes() + R"(
5381 %100 = OpFunction %void None %voidfn
5382
5383 %10 = OpLabel
5384 OpBranch %20
5385
5386 %20 = OpLabel
5387 OpLoopMerge %99 %50 None
5388 OpBranchConditional %cond %30 %99
5389
5390 %30 = OpLabel
5391 OpBranchConditional %cond2 %50 %99 ; break-unless
5392
5393 %50 = OpLabel
5394 OpBranch %20
5395
5396 %99 = OpLabel
5397 OpReturn
5398
5399 OpFunctionEnd
5400 )";
5401 auto p = parser(test::Assemble(assembly));
5402 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5403 auto fe = p->function_emitter(100);
5404 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5405
5406 auto* bi = fe.GetBlockInfo(30);
5407 ASSERT_NE(bi, nullptr);
5408 EXPECT_EQ(bi->succ_edge.count(99), 1u);
5409 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kLoopBreak);
5410 }
5411
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopBreak_FromContinueConstructTail)5412 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_LoopBreak_FromContinueConstructTail) {
5413 auto assembly = CommonTypes() + R"(
5414 %100 = OpFunction %void None %voidfn
5415
5416 %10 = OpLabel
5417 OpBranch %20
5418
5419 %20 = OpLabel
5420 OpLoopMerge %99 %50 None
5421 OpBranchConditional %cond %30 %99
5422
5423 %30 = OpLabel
5424 OpBranch %50
5425
5426 %50 = OpLabel ; continue target
5427 OpBranch %60
5428
5429 %60 = OpLabel ; continue construct tail
5430 OpBranchConditional %cond2 %20 %99
5431
5432 %99 = OpLabel
5433 OpReturn
5434
5435 OpFunctionEnd
5436 )";
5437 auto p = parser(test::Assemble(assembly));
5438 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5439 auto fe = p->function_emitter(100);
5440 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5441
5442 auto* bi = fe.GetBlockInfo(60);
5443 ASSERT_NE(bi, nullptr);
5444 EXPECT_EQ(bi->succ_edge.count(99), 1u);
5445 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kLoopBreak);
5446 }
5447
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopBreak_FromLoopBodyDirect)5448 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_LoopBreak_FromLoopBodyDirect) {
5449 auto assembly = CommonTypes() + R"(
5450 %100 = OpFunction %void None %voidfn
5451
5452 %10 = OpLabel
5453 OpBranch %20
5454
5455 %20 = OpLabel
5456 OpLoopMerge %99 %50 None
5457 OpBranchConditional %cond %30 %99
5458
5459 %30 = OpLabel
5460 OpBranch %99 ; unconditional break
5461
5462 %50 = OpLabel
5463 OpBranch %20
5464
5465 %99 = OpLabel
5466 OpReturn
5467
5468 OpFunctionEnd
5469 )";
5470 auto p = parser(test::Assemble(assembly));
5471 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5472 auto fe = p->function_emitter(100);
5473 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5474
5475 auto* bi = fe.GetBlockInfo(30);
5476 ASSERT_NE(bi, nullptr);
5477 EXPECT_EQ(bi->succ_edge.count(99), 1u);
5478 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kLoopBreak);
5479 }
5480
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopBreak_FromLoopBodyNestedSelection_Unconditional)5481 TEST_F(SpvParserCFGTest,
5482 ClassifyCFGEdges_LoopBreak_FromLoopBodyNestedSelection_Unconditional) {
5483 auto assembly = CommonTypes() + R"(
5484 %100 = OpFunction %void None %voidfn
5485
5486 %10 = OpLabel
5487 OpBranch %20
5488
5489 %20 = OpLabel
5490 OpLoopMerge %99 %80 None
5491 OpBranchConditional %cond %30 %99
5492
5493 %30 = OpLabel
5494 OpSelectionMerge %50 None
5495 OpBranchConditional %cond2 %40 %50
5496
5497 %40 = OpLabel
5498 OpBranch %99 ; deeply nested break
5499
5500 %50 = OpLabel ; inner merge
5501 OpBranch %80
5502
5503 %80 = OpLabel
5504 OpBranch %20 ; backedge
5505
5506 %99 = OpLabel
5507 OpReturn
5508
5509 OpFunctionEnd
5510 )";
5511 auto p = parser(test::Assemble(assembly));
5512 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5513 auto fe = p->function_emitter(100);
5514 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5515
5516 auto* bi = fe.GetBlockInfo(40);
5517 ASSERT_NE(bi, nullptr);
5518 EXPECT_EQ(bi->succ_edge.count(99), 1u);
5519 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kLoopBreak);
5520 }
5521
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopBreak_FromLoopBodyNestedSelection_Conditional)5522 TEST_F(SpvParserCFGTest,
5523 ClassifyCFGEdges_LoopBreak_FromLoopBodyNestedSelection_Conditional) {
5524 auto assembly = CommonTypes() + R"(
5525 %100 = OpFunction %void None %voidfn
5526
5527 %10 = OpLabel
5528 OpBranch %20
5529
5530 %20 = OpLabel
5531 OpLoopMerge %99 %80 None
5532 OpBranchConditional %cond %30 %99
5533
5534 %30 = OpLabel
5535 OpSelectionMerge %50 None
5536 OpBranchConditional %cond2 %40 %50
5537
5538 %40 = OpLabel
5539 OpBranchConditional %cond3 %99 %50 ; break-if
5540
5541 %50 = OpLabel ; inner merge
5542 OpBranch %80
5543
5544 %80 = OpLabel
5545 OpBranch %20 ; backedge
5546
5547 %99 = OpLabel
5548 OpReturn
5549
5550 OpFunctionEnd
5551 )";
5552 auto p = parser(test::Assemble(assembly));
5553 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5554 auto fe = p->function_emitter(100);
5555 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5556
5557 auto* bi = fe.GetBlockInfo(40);
5558 ASSERT_NE(bi, nullptr);
5559 EXPECT_EQ(bi->succ_edge.count(99), 1u);
5560 EXPECT_EQ(bi->succ_edge[99], EdgeKind::kLoopBreak);
5561 }
5562
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopBreak_FromContinueConstructNestedFlow_IsError)5563 TEST_F(SpvParserCFGTest,
5564 ClassifyCFGEdges_LoopBreak_FromContinueConstructNestedFlow_IsError) {
5565 auto assembly = CommonTypes() + R"(
5566 %100 = OpFunction %void None %voidfn
5567
5568 %10 = OpLabel
5569 OpBranch %20
5570
5571 %20 = OpLabel
5572 OpLoopMerge %99 %40 None
5573 OpBranchConditional %cond %30 %99
5574
5575 %30 = OpLabel
5576 OpBranch %40
5577
5578 %40 = OpLabel ; continue construct
5579 OpSelectionMerge %79 None
5580 OpBranchConditional %cond2 %50 %79
5581
5582 %50 = OpLabel
5583 OpBranchConditional %cond3 %99 %79 ; attempt to break to 99 should fail
5584
5585 %79 = OpLabel
5586 OpBranch %80 ; inner merge
5587
5588 %80 = OpLabel
5589 OpBranch %20 ; backedge
5590
5591 %99 = OpLabel
5592 OpReturn
5593
5594 OpFunctionEnd
5595 )";
5596 auto p = parser(test::Assemble(assembly));
5597 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5598 auto fe = p->function_emitter(100);
5599 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
5600 EXPECT_THAT(p->error(),
5601 Eq("Invalid exit (50->99) from continue construct: 50 is not the "
5602 "last block in the continue construct starting at 40 "
5603 "(violates post-dominance rule)"));
5604 }
5605
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopBreak_FromLoopBypassesMerge_IsError)5606 TEST_F(SpvParserCFGTest,
5607 ClassifyCFGEdges_LoopBreak_FromLoopBypassesMerge_IsError) {
5608 auto assembly = CommonTypes() + R"(
5609 %100 = OpFunction %void None %voidfn
5610
5611 %10 = OpLabel
5612 OpBranch %20
5613
5614 %20 = OpLabel
5615 OpLoopMerge %50 %40 None
5616 OpBranchConditional %cond %30 %50
5617
5618 %30 = OpLabel
5619 OpBranch %99 ; bad exit
5620
5621 %40 = OpLabel ; continue construct
5622 OpBranch %20
5623
5624 %50 = OpLabel
5625 OpBranch %99
5626
5627 %99 = OpLabel
5628 OpReturn
5629
5630 OpFunctionEnd
5631 )";
5632 auto p = parser(test::Assemble(assembly));
5633 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5634 auto fe = p->function_emitter(100);
5635 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
5636 EXPECT_THAT(
5637 p->error(),
5638 Eq("Branch from block 30 to block 99 is an invalid exit from "
5639 "construct starting at block 20; branch bypasses merge block 50"));
5640 }
5641
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopBreak_FromContinueBypassesMerge_IsError)5642 TEST_F(SpvParserCFGTest,
5643 ClassifyCFGEdges_LoopBreak_FromContinueBypassesMerge_IsError) {
5644 auto assembly = CommonTypes() + R"(
5645 %100 = OpFunction %void None %voidfn
5646
5647 %10 = OpLabel
5648 OpBranch %20
5649
5650 %20 = OpLabel
5651 OpLoopMerge %50 %40 None
5652 OpBranchConditional %cond %30 %50
5653
5654 %30 = OpLabel
5655 OpBranch %40
5656
5657 %40 = OpLabel ; continue construct
5658 OpBranch %45
5659
5660 %45 = OpLabel
5661 OpBranchConditional %cond2 %20 %99 ; branch to %99 is bad exit
5662
5663 %50 = OpLabel
5664 OpBranch %99
5665
5666 %99 = OpLabel
5667 OpReturn
5668
5669 OpFunctionEnd
5670 )";
5671 auto p = parser(test::Assemble(assembly));
5672 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5673 auto fe = p->function_emitter(100);
5674 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
5675 EXPECT_THAT(
5676 p->error(),
5677 Eq("Branch from block 45 to block 99 is an invalid exit from "
5678 "construct starting at block 40; branch bypasses merge block 50"));
5679 }
5680
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopContinue_LoopBodyToContinue)5681 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_LoopContinue_LoopBodyToContinue) {
5682 auto assembly = CommonTypes() + R"(
5683 %100 = OpFunction %void None %voidfn
5684
5685 %10 = OpLabel
5686 OpBranch %20
5687
5688 %20 = OpLabel
5689 OpLoopMerge %99 %80 None
5690 OpBranchConditional %cond %30 %99
5691
5692 %30 = OpLabel
5693 OpBranch %80 ; a forward edge
5694
5695 %80 = OpLabel
5696 OpBranch %20
5697
5698 %99 = OpLabel
5699 OpReturn
5700
5701 OpFunctionEnd
5702 )";
5703 auto p = parser(test::Assemble(assembly));
5704 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5705 auto fe = p->function_emitter(100);
5706 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5707
5708 auto* bi = fe.GetBlockInfo(30);
5709 ASSERT_NE(bi, nullptr);
5710 EXPECT_EQ(bi->succ_edge.count(80), 1u);
5711 EXPECT_EQ(bi->succ_edge[80], EdgeKind::kLoopContinue);
5712 }
5713
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopContinue_FromNestedIf)5714 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_LoopContinue_FromNestedIf) {
5715 auto assembly = CommonTypes() + R"(
5716 %100 = OpFunction %void None %voidfn
5717
5718 %10 = OpLabel
5719 OpBranch %20
5720
5721 %20 = OpLabel
5722 OpLoopMerge %99 %80 None
5723 OpBranchConditional %cond %30 %99
5724
5725 %30 = OpLabel
5726 OpSelectionMerge %79 None
5727 OpBranchConditional %cond2 %40 %79
5728
5729 %40 = OpLabel
5730 OpBranch %80 ; continue
5731
5732 %79 = OpLabel ; inner merge
5733 OpBranch %80
5734
5735 %80 = OpLabel ; continue target
5736 OpBranch %20
5737
5738 %99 = OpLabel
5739 OpReturn
5740
5741 OpFunctionEnd
5742 )";
5743 auto p = parser(test::Assemble(assembly));
5744 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5745 auto fe = p->function_emitter(100);
5746 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5747
5748 auto* bi = fe.GetBlockInfo(40);
5749 ASSERT_NE(bi, nullptr);
5750 EXPECT_EQ(bi->succ_edge.count(80), 1u);
5751 EXPECT_EQ(bi->succ_edge[80], EdgeKind::kLoopContinue);
5752 }
5753
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopContinue_ConditionalFromNestedIf)5754 TEST_F(SpvParserCFGTest,
5755 ClassifyCFGEdges_LoopContinue_ConditionalFromNestedIf) {
5756 auto assembly = CommonTypes() + R"(
5757 %100 = OpFunction %void None %voidfn
5758
5759 %10 = OpLabel
5760 OpBranch %20
5761
5762 %20 = OpLabel
5763 OpLoopMerge %99 %80 None
5764 OpBranchConditional %cond %30 %99
5765
5766 %30 = OpLabel
5767 OpSelectionMerge %79 None
5768 OpBranchConditional %cond2 %40 %79
5769
5770 %40 = OpLabel
5771 OpBranchConditional %cond2 %80 %79 ; continue-if
5772
5773 %79 = OpLabel ; inner merge
5774 OpBranch %80
5775
5776 %80 = OpLabel ; continue target
5777 OpBranch %20
5778
5779 %99 = OpLabel
5780 OpReturn
5781
5782 OpFunctionEnd
5783 )";
5784 auto p = parser(test::Assemble(assembly));
5785 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5786 auto fe = p->function_emitter(100);
5787 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5788
5789 auto* bi = fe.GetBlockInfo(40);
5790 ASSERT_NE(bi, nullptr);
5791 EXPECT_EQ(bi->succ_edge.count(80), 1u);
5792 EXPECT_EQ(bi->succ_edge[80], EdgeKind::kLoopContinue);
5793 }
5794
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopContinue_FromNestedSwitchCaseBody_Unconditional)5795 TEST_F(SpvParserCFGTest,
5796 ClassifyCFGEdges_LoopContinue_FromNestedSwitchCaseBody_Unconditional) {
5797 auto assembly = CommonTypes() + R"(
5798 %100 = OpFunction %void None %voidfn
5799
5800 %10 = OpLabel
5801 OpBranch %20
5802
5803 %20 = OpLabel
5804 OpLoopMerge %99 %80 None
5805 OpBranchConditional %cond %30 %99
5806
5807 %30 = OpLabel
5808 OpSelectionMerge %79 None
5809 OpSwitch %selector %79 40 %40
5810
5811 %40 = OpLabel
5812 OpBranch %80
5813
5814 %79 = OpLabel ; inner merge
5815 OpBranch %80
5816
5817 %80 = OpLabel ; continue target
5818 OpBranch %20
5819
5820 %99 = OpLabel
5821 OpReturn
5822
5823 OpFunctionEnd
5824 )";
5825 auto p = parser(test::Assemble(assembly));
5826 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5827 auto fe = p->function_emitter(100);
5828 EXPECT_TRUE(FlowClassifyCFGEdges(&fe)) << p->error();
5829
5830 auto* bi = fe.GetBlockInfo(40);
5831 ASSERT_NE(bi, nullptr);
5832 EXPECT_EQ(bi->succ_edge.count(80), 1u);
5833 EXPECT_EQ(bi->succ_edge[80], EdgeKind::kLoopContinue);
5834 }
5835
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopContinue_FromNestedSwitchCaseDirect_IsError)5836 TEST_F(SpvParserCFGTest,
5837 ClassifyCFGEdges_LoopContinue_FromNestedSwitchCaseDirect_IsError) {
5838 auto assembly = CommonTypes() + R"(
5839 %100 = OpFunction %void None %voidfn
5840
5841 %10 = OpLabel
5842 OpBranch %20
5843
5844 %20 = OpLabel
5845 OpLoopMerge %99 %80 None
5846 OpBranchConditional %cond %30 %99
5847
5848 %30 = OpLabel
5849 OpSelectionMerge %79 None
5850 OpSwitch %selector %79 40 %80 ; continue here
5851
5852 %79 = OpLabel ; inner merge
5853 OpBranch %80
5854
5855 %80 = OpLabel ; continue target
5856 OpBranch %20
5857
5858 %99 = OpLabel
5859 OpReturn
5860
5861 OpFunctionEnd
5862 )";
5863 auto p = parser(test::Assemble(assembly));
5864 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5865 auto fe = p->function_emitter(100);
5866 fe.RegisterBasicBlocks();
5867 fe.ComputeBlockOrderAndPositions();
5868 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
5869 EXPECT_TRUE(fe.RegisterMerges());
5870 EXPECT_TRUE(fe.LabelControlFlowConstructs());
5871 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
5872 EXPECT_THAT(p->error(), Eq("Switch branch from block 30 to case target block "
5873 "80 escapes the selection construct"));
5874 }
5875
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopContinue_FromNestedSwitchDefaultDirect_IsError)5876 TEST_F(SpvParserCFGTest,
5877 ClassifyCFGEdges_LoopContinue_FromNestedSwitchDefaultDirect_IsError) {
5878 auto assembly = CommonTypes() + R"(
5879 %100 = OpFunction %void None %voidfn
5880
5881 %10 = OpLabel
5882 OpBranch %20
5883
5884 %20 = OpLabel
5885 OpLoopMerge %99 %80 None
5886 OpBranchConditional %cond %30 %99
5887
5888 %30 = OpLabel
5889 OpSelectionMerge %79 None
5890 OpSwitch %selector %80 40 %79 ; continue here
5891
5892 %79 = OpLabel ; inner merge
5893 OpBranch %80
5894
5895 %80 = OpLabel ; continue target
5896 OpBranch %20
5897
5898 %99 = OpLabel
5899 OpReturn
5900
5901 OpFunctionEnd
5902 )";
5903 auto p = parser(test::Assemble(assembly));
5904 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5905 auto fe = p->function_emitter(100);
5906 fe.RegisterBasicBlocks();
5907 fe.ComputeBlockOrderAndPositions();
5908 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
5909 EXPECT_TRUE(fe.RegisterMerges());
5910 EXPECT_TRUE(fe.LabelControlFlowConstructs());
5911 EXPECT_FALSE(fe.FindSwitchCaseHeaders());
5912 EXPECT_THAT(p->error(), Eq("Switch branch from block 30 to default block 80 "
5913 "escapes the selection construct"));
5914 }
5915
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopContinue_FromNestedSwitchDefaultBody_Conditional)5916 TEST_F(SpvParserCFGTest,
5917 ClassifyCFGEdges_LoopContinue_FromNestedSwitchDefaultBody_Conditional) {
5918 auto assembly = CommonTypes() + R"(
5919 %100 = OpFunction %void None %voidfn
5920
5921 %10 = OpLabel
5922 OpBranch %20
5923
5924 %20 = OpLabel
5925 OpLoopMerge %99 %80 None
5926 OpBranchConditional %cond %30 %99
5927
5928 %30 = OpLabel
5929 OpSelectionMerge %79 None
5930 OpSwitch %selector %40 79 %79
5931
5932 %40 = OpLabel
5933 OpBranchConditional %cond2 %80 %79
5934
5935 %79 = OpLabel ; inner merge
5936 OpBranch %80
5937
5938 %80 = OpLabel ; continue target
5939 OpBranch %20
5940
5941 %99 = OpLabel
5942 OpReturn
5943
5944 OpFunctionEnd
5945 )";
5946 auto p = parser(test::Assemble(assembly));
5947 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5948 auto fe = p->function_emitter(100);
5949 EXPECT_TRUE(FlowClassifyCFGEdges(&fe)) << p->error();
5950
5951 auto* bi = fe.GetBlockInfo(40);
5952 ASSERT_NE(bi, nullptr);
5953 EXPECT_EQ(bi->succ_edge.count(80), 1u);
5954 EXPECT_EQ(bi->succ_edge[80], EdgeKind::kLoopContinue);
5955 }
5956
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopContinue_FromNestedSwitchDefaultBody_Unconditional)5957 TEST_F(
5958 SpvParserCFGTest,
5959 ClassifyCFGEdges_LoopContinue_FromNestedSwitchDefaultBody_Unconditional) {
5960 auto assembly = CommonTypes() + R"(
5961 %100 = OpFunction %void None %voidfn
5962
5963 %10 = OpLabel
5964 OpBranch %20
5965
5966 %20 = OpLabel
5967 OpLoopMerge %99 %80 None
5968 OpBranchConditional %cond %30 %99
5969
5970 %30 = OpLabel
5971 OpSelectionMerge %79 None
5972 OpSwitch %selector %40 79 %79
5973
5974 %40 = OpLabel
5975 OpBranch %80
5976
5977 %79 = OpLabel ; inner merge
5978 OpBranch %80
5979
5980 %80 = OpLabel ; continue target
5981 OpBranch %20
5982
5983 %99 = OpLabel
5984 OpReturn
5985
5986 OpFunctionEnd
5987 )";
5988 auto p = parser(test::Assemble(assembly));
5989 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
5990 auto fe = p->function_emitter(100);
5991 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
5992
5993 auto* bi = fe.GetBlockInfo(40);
5994 ASSERT_NE(bi, nullptr);
5995 EXPECT_EQ(bi->succ_edge.count(80), 1u);
5996 EXPECT_EQ(bi->succ_edge[80], EdgeKind::kLoopContinue);
5997 }
5998
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_LoopContinue_FromNestedLoopHeader_IsError)5999 TEST_F(SpvParserCFGTest,
6000 ClassifyCFGEdges_LoopContinue_FromNestedLoopHeader_IsError) {
6001 // Inner loop header tries to do continue to outer loop continue target.
6002 // This is disallowed by the rule:
6003 // "a continue block is valid only for the innermost loop it is nested
6004 // inside of"
6005 auto assembly = CommonTypes() + R"(
6006 %100 = OpFunction %void None %voidfn
6007
6008 %10 = OpLabel
6009 OpBranch %20
6010
6011 %20 = OpLabel
6012 OpStore %var %uint_1
6013 OpLoopMerge %99 %80 None
6014 OpBranchConditional %cond %30 %99
6015
6016 %30 = OpLabel ; inner loop.
6017 OpStore %var %uint_1
6018 OpLoopMerge %59 %50 None
6019 OpBranchConditional %cond %59 %80 ; break and outer continue
6020
6021 %50 = OpLabel
6022 OpStore %var %uint_2
6023 OpBranch %30 ; inner backedge
6024
6025 %59 = OpLabel ; inner merge
6026 OpStore %var %uint_3
6027 OpBranch %80
6028
6029 %80 = OpLabel ; outer continue
6030 OpStore %var %uint_4
6031 OpBranch %20 ; outer backedge
6032
6033 %99 = OpLabel
6034 OpStore %var %uint_5
6035 OpReturn
6036
6037 OpFunctionEnd
6038 )";
6039 auto p = parser(test::Assemble(assembly));
6040 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6041 auto fe = p->function_emitter(100);
6042 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6043 EXPECT_THAT(
6044 p->error(),
6045 Eq("Branch from block 30 to block 80 is an invalid exit from construct "
6046 "starting at block 30; branch bypasses merge block 59"));
6047 }
6048
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Fallthrough_CaseTailToCase)6049 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Fallthrough_CaseTailToCase) {
6050 auto assembly = CommonTypes() + R"(
6051 %100 = OpFunction %void None %voidfn
6052
6053 %10 = OpLabel
6054 OpSelectionMerge %99 None
6055 OpSwitch %selector %99 20 %20 40 %40
6056
6057 %20 = OpLabel ; case 20
6058 OpBranch %30
6059
6060 %30 = OpLabel
6061 OpBranch %40 ; fallthrough
6062
6063 %40 = OpLabel ; case 40
6064 OpBranch %99
6065
6066 %99 = OpLabel
6067 OpReturn
6068
6069 OpFunctionEnd
6070 )";
6071 auto p = parser(test::Assemble(assembly));
6072 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6073 auto fe = p->function_emitter(100);
6074 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
6075
6076 auto* bi = fe.GetBlockInfo(30);
6077 ASSERT_NE(bi, nullptr);
6078 EXPECT_EQ(bi->succ_edge.count(40), 1u);
6079 EXPECT_EQ(bi->succ_edge[40], EdgeKind::kCaseFallThrough);
6080 }
6081
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Fallthrough_CaseTailToDefaultNotMerge)6082 TEST_F(SpvParserCFGTest,
6083 ClassifyCFGEdges_Fallthrough_CaseTailToDefaultNotMerge) {
6084 auto assembly = CommonTypes() + R"(
6085 %100 = OpFunction %void None %voidfn
6086
6087 %10 = OpLabel
6088 OpSelectionMerge %99 None
6089 OpSwitch %selector %40 20 %20
6090
6091 %20 = OpLabel ; case 20
6092 OpBranch %30
6093
6094 %30 = OpLabel
6095 OpBranch %40 ; fallthrough
6096
6097 %40 = OpLabel ; case 40
6098 OpBranch %99
6099
6100 %99 = OpLabel
6101 OpReturn
6102
6103 OpFunctionEnd
6104 )";
6105 auto p = parser(test::Assemble(assembly));
6106 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6107 auto fe = p->function_emitter(100);
6108 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
6109
6110 auto* bi = fe.GetBlockInfo(30);
6111 ASSERT_NE(bi, nullptr);
6112 EXPECT_EQ(bi->succ_edge.count(40), 1u);
6113 EXPECT_EQ(bi->succ_edge[40], EdgeKind::kCaseFallThrough);
6114 }
6115
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Fallthrough_DefaultToCase)6116 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Fallthrough_DefaultToCase) {
6117 auto assembly = CommonTypes() + R"(
6118 %100 = OpFunction %void None %voidfn
6119
6120 %10 = OpLabel
6121 OpSelectionMerge %99 None
6122 OpSwitch %selector %20 40 %40
6123
6124 %20 = OpLabel ; default
6125 OpBranch %30
6126
6127 %30 = OpLabel
6128 OpBranch %40 ; fallthrough
6129
6130 %40 = OpLabel ; case 40
6131 OpBranch %99
6132
6133 %99 = OpLabel
6134 OpReturn
6135
6136 OpFunctionEnd
6137 )";
6138 auto p = parser(test::Assemble(assembly));
6139 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6140 auto fe = p->function_emitter(100);
6141 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
6142
6143 auto* bi = fe.GetBlockInfo(30);
6144 ASSERT_NE(bi, nullptr);
6145 EXPECT_EQ(bi->succ_edge.count(40), 1u);
6146 EXPECT_EQ(bi->succ_edge[40], EdgeKind::kCaseFallThrough);
6147 }
6148
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Fallthrough_BranchConditionalWith_IfBreak_IsError)6149 TEST_F(SpvParserCFGTest,
6150 ClassifyCFGEdges_Fallthrough_BranchConditionalWith_IfBreak_IsError) {
6151 // Code generation assumes OpBranchConditional can't have kCaseFallThrough
6152 // with kIfBreak.
6153 auto assembly = CommonTypes() + R"(
6154 %100 = OpFunction %void None %voidfn
6155
6156 %10 = OpLabel
6157 OpSelectionMerge %99 None ; Set up if-break to %99
6158 OpBranchConditional %cond %20 %99
6159
6160 %20 = OpLabel
6161 OpSelectionMerge %80 None ; switch-selection
6162 OpSwitch %selector %80 30 %30 40 %40
6163
6164 %30 = OpLabel ; first case
6165 ; branch to %99 would be an if-break, but it bypasess the switch merge
6166 ; Also has case fall-through
6167 OpBranchConditional %cond2 %99 %40
6168
6169 %40 = OpLabel ; second case
6170 OpBranch %80
6171
6172 %80 = OpLabel ; switch-selection's merge
6173 OpBranch %99
6174
6175 %99 = OpLabel ; if-selection's merge
6176 OpReturn
6177
6178 OpFunctionEnd
6179 )";
6180 auto p = parser(test::Assemble(assembly));
6181 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6182 auto fe = p->function_emitter(100);
6183 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6184 EXPECT_THAT(
6185 p->error(),
6186 Eq("Branch from block 30 to block 99 is an invalid exit from "
6187 "construct starting at block 20; branch bypasses merge block 80"));
6188 }
6189
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Forward_IsError)6190 TEST_F(SpvParserCFGTest,
6191 ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Forward_IsError) {
6192 // Code generation assumes OpBranchConditional can't have kCaseFallThrough
6193 // with kForward.
6194 auto assembly = CommonTypes() + R"(
6195 %100 = OpFunction %void None %voidfn
6196
6197 %10 = OpLabel
6198 OpSelectionMerge %99 None ; switch-selection
6199 OpSwitch %selector %99 20 %20 30 %30
6200
6201 ; Try to make branch to 35 a kForward branch
6202 %20 = OpLabel ; first case
6203 OpBranchConditional %cond2 %25 %30
6204
6205 %25 = OpLabel
6206 OpBranch %99
6207
6208 %30 = OpLabel ; second case
6209 OpBranch %99
6210
6211 %99 = OpLabel ; if-selection's merge
6212 OpReturn
6213
6214 OpFunctionEnd
6215 )";
6216 auto p = parser(test::Assemble(assembly));
6217 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6218 auto fe = p->function_emitter(100);
6219 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6220 EXPECT_THAT(p->error(),
6221 Eq("Control flow diverges at block 20 (to 25, 30) but it is not "
6222 "a structured header (it has no merge instruction)"));
6223 }
6224
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Back_LoopOnOutside_IsError)6225 TEST_F(
6226 SpvParserCFGTest,
6227 ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Back_LoopOnOutside_IsError) { // NOLINT
6228 // Code generation assumes OpBranchConditional can't have kCaseFallThrough
6229 // with kBack.
6230 //
6231 // This test has the loop on the outside. The backedge coming from a case
6232 // clause means the switch is inside the continue construct, and the nesting
6233 // of the switch's merge means the backedge is coming from a block that is not
6234 // at the end of the continue construct.
6235 auto assembly = CommonTypes() + R"(
6236 %100 = OpFunction %void None %voidfn
6237
6238 %10 = OpLabel
6239 OpBranch %20
6240
6241 %20 = OpLabel
6242 OpLoopMerge %99 %30 None
6243 OpBranch %30
6244
6245 %30 = OpLabel ; continue target and
6246 OpSelectionMerge %80 None ; switch-selection
6247 OpSwitch %selector %80 40 %40 50 %50
6248
6249 ; try to make a back edge with a fallthrough
6250 %40 = OpLabel ; first case
6251 OpBranchConditional %cond2 %20 %50
6252
6253 %50 = OpLabel ; second case
6254 OpBranch %80
6255
6256 %80 = OpLabel ; switch merge
6257 OpBranch %20 ; also backedge
6258
6259 %99 = OpLabel ; loop merge
6260 OpReturn
6261
6262 OpFunctionEnd
6263 )";
6264 auto p = parser(test::Assemble(assembly));
6265 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6266 auto fe = p->function_emitter(100);
6267 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6268 EXPECT_THAT(p->error(),
6269 Eq("Invalid exit (40->20) from continue construct: 40 is not the "
6270 "last block in the continue construct starting at 30 "
6271 "(violates post-dominance rule)"));
6272 }
6273
TEST_F(SpvParserCFGTest,FindSwitchCaseSelectionHeaders_Fallthrough_BranchConditionalWith_Back_LoopOnInside_FallthroughIsMerge_IsError)6274 TEST_F(
6275 SpvParserCFGTest,
6276 FindSwitchCaseSelectionHeaders_Fallthrough_BranchConditionalWith_Back_LoopOnInside_FallthroughIsMerge_IsError) { // NOLINT
6277 // Code generation assumes OpBranchConditional can't have kCaseFallThrough
6278 // with kBack.
6279 //
6280 // This test has the loop on the inside. The merge block is also the
6281 // fallthrough target.
6282 auto assembly = CommonTypes() + R"(
6283 %100 = OpFunction %void None %voidfn
6284
6285 %10 = OpLabel ; continue target and
6286 OpSelectionMerge %99 None ; switch-selection
6287 OpSwitch %selector %99 20 %20 50 %50
6288
6289 %20 = OpLabel ; first case, and loop header
6290 OpLoopMerge %50 %40 None
6291 OpBranch %40
6292
6293 ; try to make a back edge with a fallthrough
6294 %40 = OpLabel
6295 OpBranchConditional %cond2 %20 %50
6296
6297 %50 = OpLabel ; second case. also the loop merge ; header must dominate its merge block !
6298 OpBranch %99
6299
6300 %99 = OpLabel ; switch merge
6301 OpReturn
6302
6303 OpFunctionEnd
6304 )";
6305 auto p = parser(test::Assemble(assembly));
6306 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6307 auto fe = p->function_emitter(100);
6308 EXPECT_FALSE(FlowFindSwitchCaseHeaders(&fe));
6309 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 40, 50, 99));
6310 EXPECT_THAT(p->error(),
6311 Eq("Block 50 is a case block for switch-selection header 10 and "
6312 "also the merge block for 20 (violates dominance rule)"));
6313 }
6314
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Back_LoopOnInside_FallthroughIsNotMerge_IsError)6315 TEST_F(
6316 SpvParserCFGTest,
6317 ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Back_LoopOnInside_FallthroughIsNotMerge_IsError) { // NOLINT
6318 // Code generation assumes OpBranchConditional can't have kCaseFallThrough
6319 // with kBack.
6320 //
6321 // This test has the loop on the inside. The merge block is not the merge
6322 // target But the block order gets messed up because of the weird
6323 // connectivity.
6324 auto assembly = CommonTypes() + R"(
6325 %100 = OpFunction %void None %voidfn
6326
6327 %10 = OpLabel ; continue target and
6328 OpSelectionMerge %99 None ; switch-selection
6329 OpSwitch %selector %99 20 %20 50 %50
6330
6331 %20 = OpLabel ; first case, and loop header
6332 OpLoopMerge %45 %40 None ; move the merge to an unreachable block
6333 OpBranch %40
6334
6335 ; try to make a back edge with a fallthrough
6336 %40 = OpLabel
6337 OpBranchConditional %cond2 %20 %50
6338
6339 %45 = OpLabel ; merge for the loop
6340 OpUnreachable
6341
6342 %50 = OpLabel ; second case. target of fallthrough
6343 OpBranch %99
6344
6345 %99 = OpLabel ; switch merge
6346 OpReturn
6347
6348 OpFunctionEnd
6349 )";
6350 auto p = parser(test::Assemble(assembly));
6351 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6352 auto fe = p->function_emitter(100);
6353 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6354 EXPECT_THAT(p->error(), Eq("Branch from 10 to 50 bypasses continue target 40 "
6355 "(dominance rule violated)"));
6356 }
6357
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Back_LoopOnInside_NestedMerge_IsError)6358 TEST_F(
6359 SpvParserCFGTest,
6360 ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Back_LoopOnInside_NestedMerge_IsError) { // NOLINT
6361 // Code generation assumes OpBranchConditional can't have kCaseFallThrough
6362 // with kBack.
6363 //
6364 // This test has the loop on the inside. The fallthrough is an invalid exit
6365 // from the loop. However, the block order gets all messed up because going
6366 // from 40 to 50 ends up pulling in 99
6367 auto assembly = CommonTypes() + R"(
6368 %100 = OpFunction %void None %voidfn
6369
6370 %10 = OpLabel ; continue target and
6371 OpSelectionMerge %99 None ; switch-selection
6372 OpSwitch %selector %99 20 %20 50 %50
6373
6374 %20 = OpLabel ; first case, and loop header
6375 OpLoopMerge %49 %40 None
6376 OpBranch %40
6377
6378 ; try to make a back edge with a fallthrough
6379 %40 = OpLabel
6380 OpBranchConditional %cond2 %20 %50
6381
6382 %49 = OpLabel ; loop merge
6383 OpBranch %99
6384
6385 %50 = OpLabel ; second case
6386 OpBranch %99
6387
6388 %99 = OpLabel ; switch merge
6389 OpReturn
6390
6391 OpFunctionEnd
6392 )";
6393 auto p = parser(test::Assemble(assembly));
6394 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6395 auto fe = p->function_emitter(100);
6396 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6397 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 40, 50, 49, 99));
6398 EXPECT_THAT(p->error(), Eq("Branch from 10 to 50 bypasses continue target 40 "
6399 "(dominance rule violated)"));
6400 }
6401
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Fallthrough_CaseNonTailToCase_TrueBranch)6402 TEST_F(SpvParserCFGTest,
6403 ClassifyCFGEdges_Fallthrough_CaseNonTailToCase_TrueBranch) {
6404 // This is an unusual one, and is an error. Structurally it looks like this:
6405 // switch (val) {
6406 // case 0: {
6407 // if (cond) {
6408 // fallthrough;
6409 // }
6410 // something = 1;
6411 // }
6412 // case 1: { }
6413 // }
6414 auto assembly = CommonTypes() + R"(
6415 %100 = OpFunction %void None %voidfn
6416
6417 %10 = OpLabel
6418 OpSelectionMerge %99 None
6419 OpSwitch %selector %99 20 %20 50 %50
6420
6421 %20 = OpLabel
6422 OpSelectionMerge %49 None
6423 OpBranchConditional %cond %30 %49
6424
6425 %30 = OpLabel
6426 OpBranch %50 ; attempt to fallthrough
6427
6428 %49 = OpLabel
6429 OpBranch %99
6430
6431 %50 = OpLabel
6432 OpBranch %99
6433
6434 %99 = OpLabel
6435 OpReturn
6436
6437 OpFunctionEnd
6438 )";
6439 auto p = parser(test::Assemble(assembly));
6440 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6441 auto fe = p->function_emitter(100);
6442 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6443 EXPECT_THAT(
6444 p->error(),
6445 Eq("Branch from 10 to 50 bypasses header 20 (dominance rule violated)"));
6446 }
6447
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Fallthrough_CaseNonTailToCase_FalseBranch)6448 TEST_F(SpvParserCFGTest,
6449 ClassifyCFGEdges_Fallthrough_CaseNonTailToCase_FalseBranch) {
6450 // Like previous test, but taking the false branch.
6451
6452 // This is an unusual one, and is an error. Structurally it looks like this:
6453 // switch (val) {
6454 // case 0: {
6455 // if (cond) {
6456 // fallthrough;
6457 // }
6458 // something = 1;
6459 // }
6460 // case 1: { }
6461 // }
6462 auto assembly = CommonTypes() + R"(
6463 %100 = OpFunction %void None %voidfn
6464
6465 %10 = OpLabel
6466 OpSelectionMerge %99 None
6467 OpSwitch %selector %99 20 %20 50 %50
6468
6469 %20 = OpLabel
6470 OpSelectionMerge %49 None
6471 OpBranchConditional %cond %49 %30 ;; this is the difference
6472
6473 %30 = OpLabel
6474 OpBranch %50 ; attempt to fallthrough
6475
6476 %49 = OpLabel
6477 OpBranch %99
6478
6479 %50 = OpLabel
6480 OpBranch %99
6481
6482 %99 = OpLabel
6483 OpReturn
6484
6485 OpFunctionEnd
6486 )";
6487 auto p = parser(test::Assemble(assembly));
6488 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6489 auto fe = p->function_emitter(100);
6490 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6491 EXPECT_THAT(
6492 p->error(),
6493 Eq("Branch from 10 to 50 bypasses header 20 (dominance rule violated)"));
6494 }
6495
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Forward_IfToThen)6496 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Forward_IfToThen) {
6497 auto assembly = CommonTypes() + R"(
6498 %100 = OpFunction %void None %voidfn
6499
6500 %10 = OpLabel
6501 OpSelectionMerge %99 None
6502 OpBranchConditional %cond %20 %99
6503
6504 %20 = OpLabel
6505 OpBranch %99
6506
6507 %99 = OpLabel
6508 OpReturn
6509
6510 OpFunctionEnd
6511 )";
6512 auto p = parser(test::Assemble(assembly));
6513 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6514 auto fe = p->function_emitter(100);
6515 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
6516
6517 auto* bi = fe.GetBlockInfo(10);
6518 ASSERT_NE(bi, nullptr);
6519 EXPECT_EQ(bi->succ_edge.count(20), 1u);
6520 EXPECT_EQ(bi->succ_edge[20], EdgeKind::kForward);
6521 }
6522
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Forward_IfToElse)6523 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Forward_IfToElse) {
6524 auto assembly = CommonTypes() + R"(
6525 %100 = OpFunction %void None %voidfn
6526
6527 %10 = OpLabel
6528 OpSelectionMerge %99 None
6529 OpBranchConditional %cond %99 %30
6530
6531 %30 = OpLabel
6532 OpBranch %99
6533
6534 %99 = OpLabel
6535 OpReturn
6536
6537 OpFunctionEnd
6538 )";
6539 auto p = parser(test::Assemble(assembly));
6540 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6541 auto fe = p->function_emitter(100);
6542 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
6543
6544 auto* bi = fe.GetBlockInfo(10);
6545 ASSERT_NE(bi, nullptr);
6546 EXPECT_EQ(bi->succ_edge.count(30), 1u);
6547 EXPECT_EQ(bi->succ_edge[30], EdgeKind::kForward);
6548 }
6549
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Forward_SwitchToCase)6550 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Forward_SwitchToCase) {
6551 auto assembly = CommonTypes() + R"(
6552 %100 = OpFunction %void None %voidfn
6553
6554 %10 = OpLabel
6555 OpSelectionMerge %99 None
6556 OpSwitch %selector %99 20 %20
6557
6558 %20 = OpLabel
6559 OpBranch %99
6560
6561 %99 = OpLabel
6562 OpReturn
6563
6564 OpFunctionEnd
6565 )";
6566 auto p = parser(test::Assemble(assembly));
6567 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6568 auto fe = p->function_emitter(100);
6569 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
6570
6571 auto* bi = fe.GetBlockInfo(10);
6572 ASSERT_NE(bi, nullptr);
6573 EXPECT_EQ(bi->succ_edge.count(20), 1u);
6574 EXPECT_EQ(bi->succ_edge[20], EdgeKind::kForward);
6575 }
6576
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Forward_SwitchToDefaultNotMerge)6577 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Forward_SwitchToDefaultNotMerge) {
6578 auto assembly = CommonTypes() + R"(
6579 %100 = OpFunction %void None %voidfn
6580
6581 %10 = OpLabel
6582 OpSelectionMerge %99 None
6583 OpSwitch %selector %30 20 %20
6584
6585 %20 = OpLabel
6586 OpBranch %99
6587
6588 %30 = OpLabel
6589 OpBranch %99
6590
6591 %99 = OpLabel
6592 OpReturn
6593
6594 OpFunctionEnd
6595 )";
6596 auto p = parser(test::Assemble(assembly));
6597 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6598 auto fe = p->function_emitter(100);
6599 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
6600
6601 auto* bi = fe.GetBlockInfo(10);
6602 ASSERT_NE(bi, nullptr);
6603 EXPECT_EQ(bi->succ_edge.count(30), 1u);
6604 EXPECT_EQ(bi->succ_edge[30], EdgeKind::kForward);
6605 }
6606
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Forward_LoopHeadToBody)6607 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Forward_LoopHeadToBody) {
6608 auto assembly = CommonTypes() + R"(
6609 %100 = OpFunction %void None %voidfn
6610
6611 %10 = OpLabel
6612 OpBranch %20
6613
6614 %20 = OpLabel
6615 OpLoopMerge %99 %80 None
6616 OpBranchConditional %cond %30 %99
6617
6618 %30 = OpLabel
6619 OpBranch %80
6620
6621 %80 = OpLabel
6622 OpBranch %20
6623
6624 %99 = OpLabel
6625 OpReturn
6626
6627 OpFunctionEnd
6628 )";
6629 auto p = parser(test::Assemble(assembly));
6630 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6631 auto fe = p->function_emitter(100);
6632 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
6633
6634 auto* bi = fe.GetBlockInfo(20);
6635 ASSERT_NE(bi, nullptr);
6636 EXPECT_EQ(bi->succ_edge.count(30), 1u);
6637 EXPECT_EQ(bi->succ_edge[30], EdgeKind::kForward);
6638 }
6639
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_DomViolation_BeforeIfToSelectionInterior)6640 TEST_F(SpvParserCFGTest,
6641 ClassifyCFGEdges_DomViolation_BeforeIfToSelectionInterior) {
6642 auto assembly = CommonTypes() + R"(
6643 %100 = OpFunction %void None %voidfn
6644
6645 %10 = OpLabel
6646 OpSelectionMerge %99 None
6647 OpBranchConditional %cond %20 %50 ;%50 is a bad branch
6648
6649 %20 = OpLabel
6650 OpSelectionMerge %89 None
6651 OpBranchConditional %cond %50 %89
6652
6653 %50 = OpLabel
6654 OpBranch %89
6655
6656 %89 = OpLabel
6657 OpBranch %99
6658
6659 %99 = OpLabel
6660 OpReturn
6661
6662 OpFunctionEnd
6663 )";
6664 auto p = parser(test::Assemble(assembly));
6665 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6666 auto fe = p->function_emitter(100);
6667 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6668 EXPECT_THAT(
6669 p->error(),
6670 Eq("Branch from 10 to 50 bypasses header 20 (dominance rule violated)"));
6671 }
6672
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_DomViolation_BeforeSwitchToSelectionInterior)6673 TEST_F(SpvParserCFGTest,
6674 ClassifyCFGEdges_DomViolation_BeforeSwitchToSelectionInterior) {
6675 auto assembly = CommonTypes() + R"(
6676 %100 = OpFunction %void None %voidfn
6677
6678 %10 = OpLabel
6679 OpSelectionMerge %99 None
6680 OpBranchConditional %cond %20 %50 ;%50 is a bad branch
6681
6682 %20 = OpLabel
6683 OpSelectionMerge %89 None
6684 OpSwitch %selector %89 50 %50
6685
6686 %50 = OpLabel
6687 OpBranch %89
6688
6689 %89 = OpLabel
6690 OpBranch %99
6691
6692 %99 = OpLabel
6693 OpReturn
6694
6695 OpFunctionEnd
6696 )";
6697 auto p = parser(test::Assemble(assembly));
6698 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6699 auto fe = p->function_emitter(100);
6700 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6701 EXPECT_THAT(
6702 p->error(),
6703 Eq("Branch from 10 to 50 bypasses header 20 (dominance rule violated)"));
6704 }
6705
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_DomViolation_BeforeLoopToLoopBodyInterior)6706 TEST_F(SpvParserCFGTest,
6707 ClassifyCFGEdges_DomViolation_BeforeLoopToLoopBodyInterior) {
6708 auto assembly = CommonTypes() + R"(
6709 %100 = OpFunction %void None %voidfn
6710
6711 %10 = OpLabel
6712 OpSelectionMerge %99 None
6713 OpBranchConditional %cond %20 %50 ;%50 is a bad branch
6714
6715 %20 = OpLabel
6716 OpLoopMerge %89 %80 None
6717 OpBranchConditional %cond %50 %89
6718
6719 %50 = OpLabel
6720 OpBranch %89
6721
6722 %80 = OpLabel
6723 OpBranch %20
6724
6725 %89 = OpLabel
6726 OpBranch %99
6727
6728 %99 = OpLabel
6729 OpReturn
6730
6731 OpFunctionEnd
6732 )";
6733 auto p = parser(test::Assemble(assembly));
6734 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6735 auto fe = p->function_emitter(100);
6736 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6737 EXPECT_THAT(p->error(),
6738 // Weird error, but still we caught it.
6739 // Preferred: Eq("Branch from 10 to 50 bypasses header 20
6740 // (dominance rule violated)"))
6741 Eq("Branch from 10 to 50 bypasses continue target 80 (dominance "
6742 "rule violated)"))
6743 << Dump(fe.block_order());
6744 }
6745
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_DomViolation_BeforeContinueToContinueInterior)6746 TEST_F(SpvParserCFGTest,
6747 ClassifyCFGEdges_DomViolation_BeforeContinueToContinueInterior) {
6748 auto assembly = CommonTypes() + R"(
6749 %100 = OpFunction %void None %voidfn
6750
6751 %10 = OpLabel
6752 OpBranch %20
6753
6754 %20 = OpLabel
6755 OpLoopMerge %99 %50 None
6756 OpBranchConditional %cond %30 %99
6757
6758 %30 = OpLabel
6759 OpBranch %60
6760
6761 %50 = OpLabel ; continue target
6762 OpBranch %60
6763
6764 %60 = OpLabel
6765 OpBranch %20
6766
6767 %89 = OpLabel
6768 OpBranch %99
6769
6770 %99 = OpLabel
6771 OpReturn
6772
6773 OpFunctionEnd
6774 )";
6775 auto p = parser(test::Assemble(assembly));
6776 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6777 auto fe = p->function_emitter(100);
6778 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6779 EXPECT_THAT(
6780 p->error(),
6781 Eq("Branch from block 30 to block 60 is an invalid exit from "
6782 "construct starting at block 20; branch bypasses continue target 50"));
6783 }
6784
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_DomViolation_AfterContinueToContinueInterior)6785 TEST_F(SpvParserCFGTest,
6786 ClassifyCFGEdges_DomViolation_AfterContinueToContinueInterior) {
6787 auto assembly = CommonTypes() + R"(
6788 %100 = OpFunction %void None %voidfn
6789
6790 %10 = OpLabel
6791 OpBranch %20
6792
6793 %20 = OpLabel
6794 OpLoopMerge %80 %50 None
6795 OpBranchConditional %cond %30 %80
6796
6797 %30 = OpLabel
6798 OpBranch %50
6799
6800 %50 = OpLabel
6801 OpBranch %60
6802
6803 %60 = OpLabel
6804 OpBranch %20
6805
6806 %80 = OpLabel
6807 OpBranch %60 ; bad branch
6808 )";
6809 auto p = parser(test::Assemble(assembly));
6810 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6811 auto fe = p->function_emitter(100);
6812 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6813 EXPECT_THAT(
6814 p->error(),
6815 Eq("Branch from block 50 to block 60 is an invalid exit from "
6816 "construct starting at block 50; branch bypasses merge block 80"));
6817 }
6818
TEST_F(SpvParserCFGTest,FindSwitchCaseHeaders_DomViolation_SwitchCase_CantBeMergeForOtherConstruct)6819 TEST_F(
6820 SpvParserCFGTest,
6821 FindSwitchCaseHeaders_DomViolation_SwitchCase_CantBeMergeForOtherConstruct) { // NOLINT
6822 auto assembly = CommonTypes() + R"(
6823 %100 = OpFunction %void None %voidfn
6824
6825 %10 = OpLabel
6826 OpSelectionMerge %99 None
6827 OpSwitch %selector %99 20 %20 50 %50
6828
6829 %20 = OpLabel
6830 OpSelectionMerge %50 None
6831 OpBranchConditional %cond %30 %50
6832
6833 %30 = OpLabel
6834 OpBranch %50
6835
6836 %50 = OpLabel ; case and merge block. Error
6837 OpBranch %99
6838
6839 %99 = OpLabel
6840 OpReturn
6841
6842 OpFunctionEnd
6843 )";
6844 auto p = parser(test::Assemble(assembly));
6845 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6846 auto fe = p->function_emitter(100);
6847 EXPECT_FALSE(FlowFindSwitchCaseHeaders(&fe));
6848 EXPECT_THAT(p->error(),
6849 Eq("Block 50 is a case block for switch-selection header 10 and "
6850 "also the merge block for 20 (violates dominance rule)"));
6851 }
6852
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_DomViolation_SwitchDefault_CantBeMergeForOtherConstruct)6853 TEST_F(
6854 SpvParserCFGTest,
6855 ClassifyCFGEdges_DomViolation_SwitchDefault_CantBeMergeForOtherConstruct) {
6856 auto assembly = CommonTypes() + R"(
6857 %100 = OpFunction %void None %voidfn
6858
6859 %10 = OpLabel
6860 OpSelectionMerge %99 None
6861 OpSwitch %selector %50 20 %20
6862
6863 %20 = OpLabel
6864 OpSelectionMerge %50 None
6865 OpBranchConditional %cond %30 %50
6866
6867 %30 = OpLabel
6868 OpBranch %50
6869
6870 %50 = OpLabel ; default-case and merge block. Error
6871 OpBranch %99
6872
6873 %99 = OpLabel
6874 OpReturn
6875
6876 OpFunctionEnd
6877 )";
6878 auto p = parser(test::Assemble(assembly));
6879 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6880 auto fe = p->function_emitter(100);
6881 EXPECT_FALSE(FlowFindSwitchCaseHeaders(&fe));
6882 EXPECT_THAT(p->error(),
6883 Eq("Block 50 is the default block for switch-selection header 10 "
6884 "and also the merge block for 20 (violates dominance rule)"));
6885 }
6886
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_TooManyBackedges)6887 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_TooManyBackedges) {
6888 auto assembly = CommonTypes() + R"(
6889 %100 = OpFunction %void None %voidfn
6890
6891 %10 = OpLabel
6892 OpBranch %20
6893
6894 %20 = OpLabel
6895 OpLoopMerge %99 %50 None
6896 OpBranchConditional %cond %30 %99
6897
6898 %30 = OpLabel
6899 OpBranchConditional %cond2 %20 %50
6900
6901 %50 = OpLabel
6902 OpBranch %20
6903
6904 %99 = OpLabel
6905 OpReturn
6906
6907 OpFunctionEnd
6908 )";
6909 auto p = parser(test::Assemble(assembly));
6910 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6911 auto fe = p->function_emitter(100);
6912 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6913 EXPECT_THAT(
6914 p->error(),
6915 Eq("Invalid backedge (30->20): 30 is not in a continue construct"));
6916 }
6917
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_NeededMerge_BranchConditional)6918 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_NeededMerge_BranchConditional) {
6919 auto assembly = CommonTypes() + R"(
6920 %100 = OpFunction %void None %voidfn
6921
6922 %20 = OpLabel
6923 OpBranchConditional %cond %30 %40
6924
6925 %30 = OpLabel
6926 OpBranch %99
6927
6928 %40 = OpLabel
6929 OpBranch %99
6930
6931 %99 = OpLabel
6932 OpReturn
6933
6934 OpFunctionEnd
6935 )";
6936 auto p = parser(test::Assemble(assembly));
6937 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6938 auto fe = p->function_emitter(100);
6939 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6940 EXPECT_THAT(p->error(),
6941 Eq("Control flow diverges at block 20 (to 30, 40) but it is not "
6942 "a structured header (it has no merge instruction)"));
6943 }
6944
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_NeededMerge_Switch)6945 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_NeededMerge_Switch) {
6946 auto assembly = CommonTypes() + R"(
6947 %100 = OpFunction %void None %voidfn
6948
6949 %10 = OpLabel
6950 OpSwitch %selector %99 20 %20 30 %30
6951
6952 %20 = OpLabel
6953 OpBranch %99
6954
6955 %30 = OpLabel
6956 OpBranch %99
6957
6958 %99 = OpLabel
6959 OpReturn
6960
6961 OpFunctionEnd
6962 )";
6963 auto p = parser(test::Assemble(assembly));
6964 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
6965 auto fe = p->function_emitter(100);
6966 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
6967 EXPECT_THAT(p->error(),
6968 Eq("Control flow diverges at block 10 (to 99, 20) but it is not "
6969 "a structured header (it has no merge instruction)"));
6970 }
6971
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Pathological_Forward_LoopHeadSplitBody)6972 TEST_F(SpvParserCFGTest,
6973 ClassifyCFGEdges_Pathological_Forward_LoopHeadSplitBody) {
6974 // In this case the branch-conditional in the loop header is really also a
6975 // selection header.
6976 auto assembly = CommonTypes() + R"(
6977 %100 = OpFunction %void None %voidfn
6978
6979 %10 = OpLabel
6980 OpBranch %20
6981
6982 %20 = OpLabel
6983 OpLoopMerge %99 %80 None
6984 OpBranchConditional %cond %30 %50 ; what to make of this?
6985
6986 %30 = OpLabel
6987 OpBranch %99
6988
6989 %50 = OpLabel
6990 OpBranch %99
6991
6992 %80 = OpLabel
6993 OpBranch %20
6994
6995 %99 = OpLabel
6996 OpReturn
6997
6998 OpFunctionEnd
6999 )";
7000 auto p = parser(test::Assemble(assembly));
7001 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7002 auto fe = p->function_emitter(100);
7003 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
7004
7005 auto* bi = fe.GetBlockInfo(20);
7006 ASSERT_NE(bi, nullptr);
7007 EXPECT_EQ(bi->succ_edge.count(30), 1u);
7008 EXPECT_EQ(bi->succ_edge[30], EdgeKind::kForward);
7009 EXPECT_EQ(bi->succ_edge.count(50), 1u);
7010 EXPECT_EQ(bi->succ_edge[50], EdgeKind::kForward);
7011 }
7012
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Pathological_Forward_Premerge)7013 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Pathological_Forward_Premerge) {
7014 // Two arms of an if-selection converge early, before the merge block
7015 auto assembly = CommonTypes() + R"(
7016 %100 = OpFunction %void None %voidfn
7017
7018 %10 = OpLabel
7019 OpSelectionMerge %99 None
7020 OpBranchConditional %cond %20 %30
7021
7022 %20 = OpLabel
7023 OpBranch %50
7024
7025 %30 = OpLabel
7026 OpBranch %50
7027
7028 %50 = OpLabel ; this is an early merge!
7029 OpBranch %60
7030
7031 %60 = OpLabel ; still early merge
7032 OpBranch %99
7033
7034 %99 = OpLabel
7035 OpReturn
7036
7037 OpFunctionEnd
7038 )";
7039 auto p = parser(test::Assemble(assembly));
7040 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7041 auto fe = p->function_emitter(100);
7042 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
7043
7044 auto* bi20 = fe.GetBlockInfo(20);
7045 ASSERT_NE(bi20, nullptr);
7046 EXPECT_EQ(bi20->succ_edge.count(50), 1u);
7047 EXPECT_EQ(bi20->succ_edge[50], EdgeKind::kForward);
7048
7049 auto* bi30 = fe.GetBlockInfo(30);
7050 ASSERT_NE(bi30, nullptr);
7051 EXPECT_EQ(bi30->succ_edge.count(50), 1u);
7052 EXPECT_EQ(bi30->succ_edge[50], EdgeKind::kForward);
7053
7054 auto* bi50 = fe.GetBlockInfo(50);
7055 ASSERT_NE(bi50, nullptr);
7056 EXPECT_EQ(bi50->succ_edge.count(60), 1u);
7057 EXPECT_EQ(bi50->succ_edge[60], EdgeKind::kForward);
7058
7059 auto* bi60 = fe.GetBlockInfo(60);
7060 ASSERT_NE(bi60, nullptr);
7061 EXPECT_EQ(bi60->succ_edge.count(99), 1u);
7062 EXPECT_EQ(bi60->succ_edge[99], EdgeKind::kIfBreak);
7063 }
7064
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_Pathological_Forward_Regardless)7065 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_Pathological_Forward_Regardless) {
7066 // Both arms of an OpBranchConditional go to the same target.
7067 auto assembly = CommonTypes() + R"(
7068 %100 = OpFunction %void None %voidfn
7069
7070 %10 = OpLabel
7071 OpSelectionMerge %99 None
7072 OpBranchConditional %cond %20 %20 ; same target!
7073
7074 %20 = OpLabel
7075 OpBranch %99
7076
7077 %99 = OpLabel
7078 OpReturn
7079
7080 OpFunctionEnd
7081 )";
7082 auto p = parser(test::Assemble(assembly));
7083 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7084 auto fe = p->function_emitter(100);
7085 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
7086
7087 auto* bi10 = fe.GetBlockInfo(10);
7088 ASSERT_NE(bi10, nullptr);
7089 EXPECT_EQ(bi10->succ_edge.count(20), 1u);
7090 EXPECT_EQ(bi10->succ_edge[20], EdgeKind::kForward);
7091
7092 auto* bi20 = fe.GetBlockInfo(20);
7093 ASSERT_NE(bi20, nullptr);
7094 EXPECT_EQ(bi20->succ_edge.count(99), 1u);
7095 EXPECT_EQ(bi20->succ_edge[99], EdgeKind::kIfBreak);
7096 }
7097
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_NoIf)7098 TEST_F(SpvParserCFGTest, FindIfSelectionInternalHeaders_NoIf) {
7099 auto assembly = CommonTypes() + R"(
7100 %100 = OpFunction %void None %voidfn
7101
7102 %10 = OpLabel
7103 OpReturn
7104
7105 OpFunctionEnd
7106 )";
7107 auto p = parser(test::Assemble(assembly));
7108 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7109 auto fe = p->function_emitter(100);
7110 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
7111
7112 auto* bi = fe.GetBlockInfo(10);
7113 ASSERT_NE(bi, nullptr);
7114 EXPECT_EQ(bi->true_head, 0u);
7115 EXPECT_EQ(bi->false_head, 0u);
7116 EXPECT_EQ(bi->premerge_head, 0u);
7117 }
7118
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_ThenElse)7119 TEST_F(SpvParserCFGTest, FindIfSelectionInternalHeaders_ThenElse) {
7120 auto assembly = CommonTypes() + R"(
7121 %100 = OpFunction %void None %voidfn
7122
7123 %10 = OpLabel
7124 OpSelectionMerge %99 None
7125 OpBranchConditional %cond %20 %30
7126
7127 %20 = OpLabel
7128 OpBranch %99
7129
7130 %30 = OpLabel
7131 OpBranch %99
7132
7133 %99 = OpLabel
7134 OpReturn
7135
7136 OpFunctionEnd
7137 )";
7138 auto p = parser(test::Assemble(assembly));
7139 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7140 auto fe = p->function_emitter(100);
7141 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7142
7143 auto* bi10 = fe.GetBlockInfo(10);
7144 ASSERT_NE(bi10, nullptr);
7145 EXPECT_EQ(bi10->true_head, 20u);
7146 EXPECT_EQ(bi10->false_head, 30u);
7147 EXPECT_EQ(bi10->premerge_head, 0u);
7148
7149 auto* bi20 = fe.GetBlockInfo(20);
7150 ASSERT_NE(bi20, nullptr);
7151 EXPECT_EQ(bi20->true_head, 0u);
7152 EXPECT_EQ(bi20->false_head, 0u);
7153 EXPECT_EQ(bi20->premerge_head, 0u);
7154
7155 auto* bi30 = fe.GetBlockInfo(30);
7156 ASSERT_NE(bi30, nullptr);
7157 EXPECT_EQ(bi30->true_head, 0u);
7158 EXPECT_EQ(bi30->false_head, 0u);
7159 EXPECT_EQ(bi30->premerge_head, 0u);
7160
7161 auto* bi99 = fe.GetBlockInfo(99);
7162 ASSERT_NE(bi99, nullptr);
7163 EXPECT_EQ(bi99->true_head, 0u);
7164 EXPECT_EQ(bi99->false_head, 0u);
7165 EXPECT_EQ(bi99->premerge_head, 0u);
7166 }
7167
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_IfOnly)7168 TEST_F(SpvParserCFGTest, FindIfSelectionInternalHeaders_IfOnly) {
7169 auto assembly = CommonTypes() + R"(
7170 %100 = OpFunction %void None %voidfn
7171
7172 %10 = OpLabel
7173 OpSelectionMerge %99 None
7174 OpBranchConditional %cond %30 %99
7175
7176 %30 = OpLabel
7177 OpBranch %99
7178
7179 %99 = OpLabel
7180 OpReturn
7181
7182 OpFunctionEnd
7183 )";
7184 auto p = parser(test::Assemble(assembly));
7185 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7186 auto fe = p->function_emitter(100);
7187 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7188
7189 auto* bi10 = fe.GetBlockInfo(10);
7190 ASSERT_NE(bi10, nullptr);
7191 EXPECT_EQ(bi10->true_head, 30u);
7192 EXPECT_EQ(bi10->false_head, 0u);
7193 EXPECT_EQ(bi10->premerge_head, 0u);
7194
7195 auto* bi30 = fe.GetBlockInfo(30);
7196 ASSERT_NE(bi30, nullptr);
7197 EXPECT_EQ(bi30->true_head, 0u);
7198 EXPECT_EQ(bi30->false_head, 0u);
7199 EXPECT_EQ(bi30->premerge_head, 0u);
7200
7201 auto* bi99 = fe.GetBlockInfo(99);
7202 ASSERT_NE(bi99, nullptr);
7203 EXPECT_EQ(bi99->true_head, 0u);
7204 EXPECT_EQ(bi99->false_head, 0u);
7205 EXPECT_EQ(bi99->premerge_head, 0u);
7206 }
7207
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_ElseOnly)7208 TEST_F(SpvParserCFGTest, FindIfSelectionInternalHeaders_ElseOnly) {
7209 auto assembly = CommonTypes() + R"(
7210 %100 = OpFunction %void None %voidfn
7211
7212 %10 = OpLabel
7213 OpSelectionMerge %99 None
7214 OpBranchConditional %cond %99 %30
7215
7216 %30 = OpLabel
7217 OpBranch %99
7218
7219 %99 = OpLabel
7220 OpReturn
7221
7222 OpFunctionEnd
7223 )";
7224 auto p = parser(test::Assemble(assembly));
7225 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7226 auto fe = p->function_emitter(100);
7227 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7228
7229 auto* bi10 = fe.GetBlockInfo(10);
7230 ASSERT_NE(bi10, nullptr);
7231 EXPECT_EQ(bi10->true_head, 0u);
7232 EXPECT_EQ(bi10->false_head, 30u);
7233 EXPECT_EQ(bi10->premerge_head, 0u);
7234
7235 auto* bi30 = fe.GetBlockInfo(30);
7236 ASSERT_NE(bi30, nullptr);
7237 EXPECT_EQ(bi30->true_head, 0u);
7238 EXPECT_EQ(bi30->false_head, 0u);
7239 EXPECT_EQ(bi30->premerge_head, 0u);
7240
7241 auto* bi99 = fe.GetBlockInfo(99);
7242 ASSERT_NE(bi99, nullptr);
7243 EXPECT_EQ(bi99->true_head, 0u);
7244 EXPECT_EQ(bi99->false_head, 0u);
7245 EXPECT_EQ(bi99->premerge_head, 0u);
7246 }
7247
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_Regardless)7248 TEST_F(SpvParserCFGTest, FindIfSelectionInternalHeaders_Regardless) {
7249 auto assembly = CommonTypes() + R"(
7250 %100 = OpFunction %void None %voidfn
7251
7252 %10 = OpLabel
7253 OpSelectionMerge %99 None
7254 OpBranchConditional %cond %20 %20 ; same target
7255
7256 %20 = OpLabel
7257 OpBranch %80
7258
7259 %80 = OpLabel
7260 OpBranch %99
7261
7262 %99 = OpLabel
7263 OpReturn
7264
7265 OpFunctionEnd
7266 )";
7267 auto p = parser(test::Assemble(assembly));
7268 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7269 auto fe = p->function_emitter(100);
7270 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7271
7272 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 80, 99));
7273
7274 auto* bi10 = fe.GetBlockInfo(10);
7275 ASSERT_NE(bi10, nullptr);
7276 EXPECT_EQ(bi10->true_head, 20u);
7277 EXPECT_EQ(bi10->false_head, 20u);
7278 EXPECT_EQ(bi10->premerge_head, 0u);
7279 }
7280
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_Premerge_Simple)7281 TEST_F(SpvParserCFGTest, FindIfSelectionInternalHeaders_Premerge_Simple) {
7282 auto assembly = CommonTypes() + R"(
7283 %100 = OpFunction %void None %voidfn
7284
7285 %10 = OpLabel
7286 OpSelectionMerge %99 None
7287 OpBranchConditional %cond %20 %30
7288
7289 %20 = OpLabel
7290 OpBranch %80
7291
7292 %30 = OpLabel
7293 OpBranch %80
7294
7295 %80 = OpLabel ; premerge node
7296 OpBranch %99
7297
7298 %99 = OpLabel
7299 OpReturn
7300
7301 OpFunctionEnd
7302 )";
7303 auto p = parser(test::Assemble(assembly));
7304 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7305 auto fe = p->function_emitter(100);
7306 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7307
7308 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 80, 99));
7309
7310 auto* bi10 = fe.GetBlockInfo(10);
7311 ASSERT_NE(bi10, nullptr);
7312 EXPECT_EQ(bi10->true_head, 20u);
7313 EXPECT_EQ(bi10->false_head, 30u);
7314 EXPECT_EQ(bi10->premerge_head, 80u);
7315 }
7316
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_Premerge_ThenDirectToElse)7317 TEST_F(SpvParserCFGTest,
7318 FindIfSelectionInternalHeaders_Premerge_ThenDirectToElse) {
7319 auto assembly = CommonTypes() + R"(
7320 %100 = OpFunction %void None %voidfn
7321
7322 %10 = OpLabel
7323 OpSelectionMerge %99 None
7324 OpBranchConditional %cond %20 %30
7325
7326 %20 = OpLabel
7327 OpBranch %30
7328
7329 %30 = OpLabel
7330 OpBranch %80
7331
7332 %80 = OpLabel ; premerge node
7333 OpBranch %99
7334
7335 %99 = OpLabel
7336 OpReturn
7337
7338 OpFunctionEnd
7339 )";
7340 auto p = parser(test::Assemble(assembly));
7341 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7342 auto fe = p->function_emitter(100);
7343 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7344
7345 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 80, 99));
7346
7347 auto* bi10 = fe.GetBlockInfo(10);
7348 ASSERT_NE(bi10, nullptr);
7349 EXPECT_EQ(bi10->true_head, 20u);
7350 EXPECT_EQ(bi10->false_head, 30u);
7351 EXPECT_EQ(bi10->premerge_head, 30u);
7352 }
7353
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_Premerge_ElseDirectToThen)7354 TEST_F(SpvParserCFGTest,
7355 FindIfSelectionInternalHeaders_Premerge_ElseDirectToThen) {
7356 auto assembly = CommonTypes() + R"(
7357 %100 = OpFunction %void None %voidfn
7358
7359 %10 = OpLabel
7360 OpSelectionMerge %99 None
7361 OpBranchConditional %cond %20 %30
7362
7363 %20 = OpLabel
7364 OpBranch %80 ; branches to premerge
7365
7366 %30 = OpLabel ; else
7367 OpBranch %20 ; branches to then
7368
7369 %80 = OpLabel ; premerge node
7370 OpBranch %99
7371
7372 %99 = OpLabel
7373 OpReturn
7374
7375 OpFunctionEnd
7376 )";
7377 auto p = parser(test::Assemble(assembly));
7378 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7379 auto fe = p->function_emitter(100);
7380 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7381
7382 EXPECT_THAT(fe.block_order(), ElementsAre(10, 30, 20, 80, 99));
7383
7384 auto* bi10 = fe.GetBlockInfo(10);
7385 ASSERT_NE(bi10, nullptr);
7386 EXPECT_EQ(bi10->true_head, 20u);
7387 EXPECT_EQ(bi10->false_head, 30u);
7388 EXPECT_EQ(bi10->premerge_head, 20u);
7389 }
7390
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_Premerge_MultiCandidate_IsError)7391 TEST_F(SpvParserCFGTest,
7392 FindIfSelectionInternalHeaders_Premerge_MultiCandidate_IsError) {
7393 auto assembly = CommonTypes() + R"(
7394 %100 = OpFunction %void None %voidfn
7395
7396 %10 = OpLabel
7397 OpSelectionMerge %99 None
7398 OpBranchConditional %cond %20 %30
7399
7400 %20 = OpLabel
7401 ; Try to force several branches down into "else" territory,
7402 ; but we error out earlier in the flow due to lack of merge
7403 ; instruction.
7404 OpBranchConditional %cond2 %70 %80
7405
7406 %30 = OpLabel
7407 OpBranch %70
7408
7409 %70 = OpLabel ; candidate premerge
7410 OpBranch %80
7411
7412 %80 = OpLabel ; canddiate premerge
7413 OpBranch %99
7414
7415 %99 = OpLabel
7416 OpReturn
7417
7418 OpFunctionEnd
7419 )";
7420 auto p = parser(test::Assemble(assembly));
7421 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7422 auto fe = p->function_emitter(100);
7423 // Error out sooner in the flow
7424 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
7425 EXPECT_THAT(p->error(),
7426 Eq("Control flow diverges at block 20 (to 70, 80) but it is not "
7427 "a structured header (it has no merge instruction)"));
7428 }
7429
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_IfBreak_FromThen_ForwardWithinThen)7430 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_IfBreak_FromThen_ForwardWithinThen) {
7431 // SPIR-V allows this unusual configuration.
7432 auto assembly = CommonTypes() + R"(
7433 %100 = OpFunction %void None %voidfn
7434
7435 %10 = OpLabel
7436 OpSelectionMerge %99 None
7437 OpBranchConditional %cond %20 %99
7438
7439 %20 = OpLabel
7440 OpBranchConditional %cond2 %99 %80 ; break with forward edge
7441
7442 %80 = OpLabel ; still in then clause
7443 OpBranch %99
7444
7445 %99 = OpLabel
7446 OpReturn
7447 OpFunctionEnd
7448 )";
7449 auto p = parser(test::Assemble(assembly));
7450 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7451 auto fe = p->function_emitter(100);
7452 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
7453 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 80, 99));
7454
7455 auto* bi20 = fe.GetBlockInfo(20);
7456 ASSERT_NE(bi20, nullptr);
7457 EXPECT_EQ(bi20->succ_edge.count(80), 1u);
7458 EXPECT_EQ(bi20->succ_edge[80], EdgeKind::kForward);
7459 EXPECT_EQ(bi20->succ_edge.count(99), 1u);
7460 EXPECT_EQ(bi20->succ_edge[99], EdgeKind::kIfBreak);
7461
7462 EXPECT_THAT(p->error(), Eq(""));
7463 }
7464
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_IfBreak_FromElse_ForwardWithinElse)7465 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_IfBreak_FromElse_ForwardWithinElse) {
7466 // SPIR-V allows this unusual configuration.
7467 auto assembly = CommonTypes() + R"(
7468 %100 = OpFunction %void None %voidfn
7469
7470 %10 = OpLabel
7471 OpSelectionMerge %99 None
7472 OpBranchConditional %cond %20 %30
7473
7474 %20 = OpLabel
7475 OpBranch %99
7476
7477 %30 = OpLabel ; else clause
7478 OpBranchConditional %cond2 %99 %80 ; break with forward edge
7479
7480 %80 = OpLabel ; still in then clause
7481 OpBranch %99
7482
7483 %99 = OpLabel
7484 OpReturn
7485 OpFunctionEnd
7486 )";
7487 auto p = parser(test::Assemble(assembly));
7488 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7489 auto fe = p->function_emitter(100);
7490 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
7491 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 80, 99));
7492
7493 auto* bi30 = fe.GetBlockInfo(30);
7494 ASSERT_NE(bi30, nullptr);
7495 EXPECT_EQ(bi30->succ_edge.count(80), 1u);
7496 EXPECT_EQ(bi30->succ_edge[80], EdgeKind::kForward);
7497 EXPECT_EQ(bi30->succ_edge.count(99), 1u);
7498 EXPECT_EQ(bi30->succ_edge[99], EdgeKind::kIfBreak);
7499
7500 EXPECT_THAT(p->error(), Eq(""));
7501 }
7502
TEST_F(SpvParserCFGTest,ClassifyCFGEdges_IfBreak_WithForwardToPremerge)7503 TEST_F(SpvParserCFGTest, ClassifyCFGEdges_IfBreak_WithForwardToPremerge) {
7504 auto assembly = CommonTypes() + R"(
7505 %100 = OpFunction %void None %voidfn
7506
7507 %10 = OpLabel
7508 OpSelectionMerge %99 None
7509 OpBranchConditional %cond %20 %30
7510
7511 %20 = OpLabel ; then
7512 OpBranchConditional %cond2 %99 %80 ; break with forward to premerge
7513
7514 %30 = OpLabel ; else
7515 OpBranch %80
7516
7517 %80 = OpLabel ; premerge node
7518 OpBranch %99
7519
7520 %99 = OpLabel
7521 OpReturn
7522 OpFunctionEnd
7523 )";
7524 auto p = parser(test::Assemble(assembly));
7525 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7526 auto fe = p->function_emitter(100);
7527 EXPECT_TRUE(FlowClassifyCFGEdges(&fe));
7528 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 80, 99));
7529
7530 auto* bi20 = fe.GetBlockInfo(20);
7531 ASSERT_NE(bi20, nullptr);
7532 EXPECT_EQ(bi20->succ_edge.count(80), 1u);
7533 EXPECT_EQ(bi20->succ_edge[80], EdgeKind::kForward);
7534 EXPECT_EQ(bi20->succ_edge.count(99), 1u);
7535 EXPECT_EQ(bi20->succ_edge[99], EdgeKind::kIfBreak);
7536
7537 EXPECT_THAT(p->error(), Eq(""));
7538
7539 // TODO(crbug.com/tint/775): The SPIR-V reader errors out on this case.
7540 // Remove this when it's fixed.
7541 p->DeliberatelyInvalidSpirv();
7542 }
7543
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBeTrueHeader)7544 TEST_F(
7545 SpvParserCFGTest,
7546 FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBeTrueHeader) { // NOLINT - line length
7547 auto assembly = CommonTypes() + R"(
7548 %100 = OpFunction %void None %voidfn
7549
7550 %10 = OpLabel
7551 OpSelectionMerge %99 None
7552 OpBranchConditional %cond %40 %20
7553
7554 %20 = OpLabel
7555 OpSelectionMerge %40 None
7556 OpBranchConditional %cond2 %30 %40
7557
7558 %30 = OpLabel
7559 OpBranch %40
7560
7561 %40 = OpLabel ; inner merge, and true-head for outer if-selection
7562 OpBranch %99
7563
7564 %99 = OpLabel ; outer merge
7565 OpReturn
7566
7567 OpFunctionEnd
7568 )";
7569 auto p = parser(test::Assemble(assembly));
7570 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7571 auto fe = p->function_emitter(100);
7572 EXPECT_FALSE(FlowFindIfSelectionInternalHeaders(&fe));
7573 EXPECT_THAT(
7574 p->error(),
7575 Eq("Block 40 is the true branch for if-selection header 10 and also the "
7576 "merge block for header block 20 (violates dominance rule)"));
7577 }
7578
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBeFalseHeader)7579 TEST_F(
7580 SpvParserCFGTest,
7581 FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBeFalseHeader) { // NOLINT - line length
7582 auto assembly = CommonTypes() + R"(
7583 %100 = OpFunction %void None %voidfn
7584
7585 %10 = OpLabel
7586 OpSelectionMerge %99 None
7587 OpBranchConditional %cond %20 %40
7588
7589 %20 = OpLabel
7590 OpSelectionMerge %40 None
7591 OpBranchConditional %cond %30 %40
7592
7593 %30 = OpLabel
7594 OpBranch %40
7595
7596 %40 = OpLabel ; inner merge, and true-head for outer if-selection
7597 OpBranch %99
7598
7599 %99 = OpLabel ; outer merge
7600 OpReturn
7601
7602 OpFunctionEnd
7603 )";
7604 auto p = parser(test::Assemble(assembly));
7605 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7606 auto fe = p->function_emitter(100);
7607 EXPECT_FALSE(FlowFindIfSelectionInternalHeaders(&fe));
7608 EXPECT_THAT(
7609 p->error(),
7610 Eq("Block 40 is the false branch for if-selection header 10 and also the "
7611 "merge block for header block 20 (violates dominance rule)"));
7612 }
7613
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBePremerge)7614 TEST_F(
7615 SpvParserCFGTest,
7616 FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBePremerge) {
7617 auto assembly = CommonTypes() + R"(
7618 %100 = OpFunction %void None %voidfn
7619
7620 %10 = OpLabel ; outer if-header
7621 OpSelectionMerge %99 None
7622 OpBranchConditional %cond %20 %50
7623
7624 %20 = OpLabel
7625 OpBranch %70
7626
7627 %50 = OpLabel ; inner if-header
7628 OpSelectionMerge %70 None
7629 OpBranchConditional %cond %60 %70
7630
7631 %60 = OpLabel
7632 OpBranch %70
7633
7634 %70 = OpLabel ; inner merge, and premerge for outer if-selection
7635 OpBranch %80
7636
7637 %80 = OpLabel
7638 OpBranch %99
7639
7640 %99 = OpLabel ; outer merge
7641 OpReturn
7642
7643 OpFunctionEnd
7644 )";
7645 auto p = parser(test::Assemble(assembly));
7646 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7647 auto fe = p->function_emitter(100);
7648 EXPECT_FALSE(FlowFindIfSelectionInternalHeaders(&fe));
7649 EXPECT_THAT(p->error(),
7650 Eq("Block 70 is the merge block for 50 but has alternate paths "
7651 "reaching it, starting from blocks 20 and 50 which are the "
7652 "true and false branches for the if-selection header block 10 "
7653 "(violates dominance rule)"));
7654 }
7655
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_TrueBranch_LoopBreak_Ok)7656 TEST_F(SpvParserCFGTest,
7657 FindIfSelectionInternalHeaders_TrueBranch_LoopBreak_Ok) {
7658 // crbug.com/tint/243
7659 auto assembly = CommonTypes() + R"(
7660 %100 = OpFunction %void None %voidfn
7661
7662 %5 = OpLabel
7663 OpBranch %10
7664
7665 %10 = OpLabel
7666 OpLoopMerge %99 %90 None
7667 OpBranch %20
7668
7669 %20 = OpLabel
7670 OpSelectionMerge %40 None
7671 OpBranchConditional %cond %99 %30 ; true branch breaking is ok
7672
7673 %30 = OpLabel
7674 OpBranch %40
7675
7676 %40 = OpLabel ; selection merge
7677 OpBranch %90
7678
7679 %90 = OpLabel ; continue target
7680 OpBranch %10 ; backedge
7681
7682 %99 = OpLabel ; loop merge
7683 OpReturn
7684
7685 OpFunctionEnd
7686 )";
7687 auto p = parser(test::Assemble(assembly));
7688 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7689 auto fe = p->function_emitter(100);
7690 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7691 EXPECT_THAT(p->error(), Eq(""));
7692 }
7693
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_TrueBranch_LoopContinue_Ok)7694 TEST_F(SpvParserCFGTest,
7695 FindIfSelectionInternalHeaders_TrueBranch_LoopContinue_Ok) {
7696 // crbug.com/tint/243
7697 auto assembly = CommonTypes() + R"(
7698 %100 = OpFunction %void None %voidfn
7699
7700 %5 = OpLabel
7701 OpBranch %10
7702
7703 %10 = OpLabel
7704 OpLoopMerge %99 %90 None
7705 OpBranch %20
7706
7707 %20 = OpLabel
7708 OpSelectionMerge %40 None
7709 OpBranchConditional %cond %90 %30 ; true branch continue is ok
7710
7711 %30 = OpLabel
7712 OpBranch %40
7713
7714 %40 = OpLabel ; selection merge
7715 OpBranch %90
7716
7717 %90 = OpLabel ; continue target
7718 OpBranch %10 ; backedge
7719
7720 %99 = OpLabel ; loop merge
7721 OpReturn
7722
7723 OpFunctionEnd
7724 )";
7725 auto p = parser(test::Assemble(assembly));
7726 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7727 auto fe = p->function_emitter(100);
7728 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7729 EXPECT_THAT(p->error(), Eq(""));
7730 }
7731
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_TrueBranch_SwitchBreak_Ok)7732 TEST_F(SpvParserCFGTest,
7733 FindIfSelectionInternalHeaders_TrueBranch_SwitchBreak_Ok) {
7734 // crbug.com/tint/243
7735 auto assembly = CommonTypes() + R"(
7736 %100 = OpFunction %void None %voidfn
7737
7738 %10 = OpLabel
7739 OpSelectionMerge %99 None
7740 OpSwitch %uint_20 %99 20 %20
7741
7742 %20 = OpLabel
7743 OpSelectionMerge %40 None
7744 OpBranchConditional %cond %99 %30 ; true branch switch break is ok
7745
7746 %30 = OpLabel
7747 OpBranch %40
7748
7749 %40 = OpLabel ; if-selection merge
7750 OpBranch %99
7751
7752 %99 = OpLabel ; switch merge
7753 OpReturn
7754
7755 OpFunctionEnd
7756 )";
7757 auto p = parser(test::Assemble(assembly));
7758 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7759 auto fe = p->function_emitter(100);
7760 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7761 EXPECT_THAT(p->error(), Eq(""));
7762 }
7763
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_FalseBranch_LoopBreak_Ok)7764 TEST_F(SpvParserCFGTest,
7765 FindIfSelectionInternalHeaders_FalseBranch_LoopBreak_Ok) {
7766 // crbug.com/tint/243
7767 auto assembly = CommonTypes() + R"(
7768 %100 = OpFunction %void None %voidfn
7769
7770 %5 = OpLabel
7771 OpBranch %10
7772
7773 %10 = OpLabel
7774 OpLoopMerge %99 %90 None
7775 OpBranch %20
7776
7777 %20 = OpLabel
7778 OpSelectionMerge %40 None
7779 OpBranchConditional %cond %30 %99 ; false branch breaking is ok
7780
7781 %30 = OpLabel
7782 OpBranch %40
7783
7784 %40 = OpLabel ; selection merge
7785 OpBranch %90
7786
7787 %90 = OpLabel ; continue target
7788 OpBranch %10 ; backedge
7789
7790 %99 = OpLabel ; loop merge
7791 OpReturn
7792
7793 OpFunctionEnd
7794 )";
7795 auto p = parser(test::Assemble(assembly));
7796 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7797 auto fe = p->function_emitter(100);
7798 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7799 EXPECT_THAT(p->error(), Eq(""));
7800 }
7801
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_FalseBranch_LoopContinue_Ok)7802 TEST_F(SpvParserCFGTest,
7803 FindIfSelectionInternalHeaders_FalseBranch_LoopContinue_Ok) {
7804 // crbug.com/tint/243
7805 auto assembly = CommonTypes() + R"(
7806 %100 = OpFunction %void None %voidfn
7807
7808 %5 = OpLabel
7809 OpBranch %10
7810
7811 %10 = OpLabel
7812 OpLoopMerge %99 %90 None
7813 OpBranch %20
7814
7815 %20 = OpLabel
7816 OpSelectionMerge %40 None
7817 OpBranchConditional %cond %30 %90 ; false branch continue is ok
7818
7819 %30 = OpLabel
7820 OpBranch %40
7821
7822 %40 = OpLabel ; selection merge
7823 OpBranch %90
7824
7825 %90 = OpLabel ; continue target
7826 OpBranch %10 ; backedge
7827
7828 %99 = OpLabel ; loop merge
7829 OpReturn
7830
7831 OpFunctionEnd
7832 )";
7833 auto p = parser(test::Assemble(assembly));
7834 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7835 auto fe = p->function_emitter(100);
7836 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7837 EXPECT_THAT(p->error(), Eq(""));
7838 }
7839
TEST_F(SpvParserCFGTest,FindIfSelectionInternalHeaders_FalseBranch_SwitchBreak_Ok)7840 TEST_F(SpvParserCFGTest,
7841 FindIfSelectionInternalHeaders_FalseBranch_SwitchBreak_Ok) {
7842 // crbug.com/tint/243
7843 auto assembly = CommonTypes() + R"(
7844 %100 = OpFunction %void None %voidfn
7845
7846 %10 = OpLabel
7847 OpSelectionMerge %99 None
7848 OpSwitch %uint_20 %99 20 %20
7849
7850 %20 = OpLabel
7851 OpSelectionMerge %40 None
7852 OpBranchConditional %cond %30 %99 ; false branch switch break is ok
7853
7854 %30 = OpLabel
7855 OpBranch %40
7856
7857 %40 = OpLabel ; if-selection merge
7858 OpBranch %99
7859
7860 %99 = OpLabel ; switch merge
7861 OpReturn
7862
7863 OpFunctionEnd
7864 )";
7865 auto p = parser(test::Assemble(assembly));
7866 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7867 auto fe = p->function_emitter(100);
7868 EXPECT_TRUE(FlowFindIfSelectionInternalHeaders(&fe));
7869 EXPECT_THAT(p->error(), Eq(""));
7870 }
7871
TEST_F(SpvParserCFGTest,EmitBody_IfBreak_FromThen_ForwardWithinThen)7872 TEST_F(SpvParserCFGTest, EmitBody_IfBreak_FromThen_ForwardWithinThen) {
7873 // Exercises the hard case where we a single OpBranchConditional has both
7874 // IfBreak and Forward edges, within the true-branch clause.
7875 auto assembly = CommonTypes() + R"(
7876 %100 = OpFunction %void None %voidfn
7877
7878 %10 = OpLabel
7879 OpStore %var %uint_1
7880 OpSelectionMerge %99 None
7881 OpBranchConditional %cond %20 %50
7882
7883 %20 = OpLabel
7884 OpStore %var %uint_2
7885 OpBranchConditional %cond2 %99 %30 ; kIfBreak with kForward
7886
7887 %30 = OpLabel ; still in then clause
7888 OpStore %var %uint_3
7889 OpBranch %99
7890
7891 %50 = OpLabel ; else clause
7892 OpStore %var %uint_4
7893 OpBranch %99
7894
7895 %99 = OpLabel
7896 OpStore %var %uint_5
7897 OpReturn
7898 OpFunctionEnd
7899 )";
7900 auto p = parser(test::Assemble(assembly));
7901 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7902 auto fe = p->function_emitter(100);
7903 EXPECT_TRUE(fe.EmitBody()) << p->error();
7904 auto ast_body = fe.ast_body();
7905 auto got = test::ToString(p->program(), ast_body);
7906 auto* expect = R"(var_1 = 1u;
7907 var guard10 : bool = true;
7908 if (false) {
7909 var_1 = 2u;
7910 if (true) {
7911 guard10 = false;
7912 }
7913 if (guard10) {
7914 var_1 = 3u;
7915 guard10 = false;
7916 }
7917 } else {
7918 if (guard10) {
7919 var_1 = 4u;
7920 guard10 = false;
7921 }
7922 }
7923 var_1 = 5u;
7924 return;
7925 )";
7926 ASSERT_EQ(expect, got);
7927 }
7928
TEST_F(SpvParserCFGTest,EmitBody_IfBreak_FromElse_ForwardWithinElse)7929 TEST_F(SpvParserCFGTest, EmitBody_IfBreak_FromElse_ForwardWithinElse) {
7930 // Exercises the hard case where we a single OpBranchConditional has both
7931 // IfBreak and Forward edges, within the false-branch clause.
7932 auto assembly = CommonTypes() + R"(
7933 %100 = OpFunction %void None %voidfn
7934
7935 %10 = OpLabel
7936 OpStore %var %uint_1
7937 OpSelectionMerge %99 None
7938 OpBranchConditional %cond %20 %50
7939
7940 %20 = OpLabel
7941 OpStore %var %uint_2
7942 OpBranch %99
7943
7944 %50 = OpLabel ; else clause
7945 OpStore %var %uint_3
7946 OpBranchConditional %cond2 %99 %80 ; kIfBreak with kForward
7947
7948 %80 = OpLabel ; still in then clause
7949 OpStore %var %uint_4
7950 OpBranch %99
7951
7952 %99 = OpLabel
7953 OpStore %var %uint_5
7954 OpReturn
7955 OpFunctionEnd
7956 )";
7957 auto p = parser(test::Assemble(assembly));
7958 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
7959 auto fe = p->function_emitter(100);
7960 EXPECT_TRUE(fe.EmitBody()) << p->error();
7961 auto ast_body = fe.ast_body();
7962 auto got = test::ToString(p->program(), ast_body);
7963 auto* expect = R"(var_1 = 1u;
7964 var guard10 : bool = true;
7965 if (false) {
7966 var_1 = 2u;
7967 guard10 = false;
7968 } else {
7969 if (guard10) {
7970 var_1 = 3u;
7971 if (true) {
7972 guard10 = false;
7973 }
7974 if (guard10) {
7975 var_1 = 4u;
7976 guard10 = false;
7977 }
7978 }
7979 }
7980 var_1 = 5u;
7981 return;
7982 )";
7983 ASSERT_EQ(expect, got);
7984 }
7985
TEST_F(SpvParserCFGTest,EmitBody_IfBreak_FromThenWithForward_FromElseWithForward_AlsoPremerge)7986 TEST_F(SpvParserCFGTest,
7987 EmitBody_IfBreak_FromThenWithForward_FromElseWithForward_AlsoPremerge) {
7988 // This is a combination of the previous two, but also adding a premerge.
7989 // We have IfBreak and Forward edges from the same OpBranchConditional, and
7990 // this occurs in the true-branch clause, the false-branch clause, and within
7991 // the premerge clause. Flow guards have to be sprinkled in lots of places.
7992 auto assembly = CommonTypes() + R"(
7993 %100 = OpFunction %void None %voidfn
7994
7995 %10 = OpLabel
7996 OpStore %var %uint_1
7997 OpSelectionMerge %99 None
7998 OpBranchConditional %cond %20 %50
7999
8000 %20 = OpLabel ; then
8001 OpStore %var %uint_2
8002 OpBranchConditional %cond2 %21 %99 ; kForward and kIfBreak
8003
8004 %21 = OpLabel ; still in then clause
8005 OpStore %var %uint_3
8006 OpBranch %80 ; to premerge
8007
8008 %50 = OpLabel ; else clause
8009 OpStore %var %uint_4
8010 OpBranchConditional %cond2 %99 %51 ; kIfBreak with kForward
8011
8012 %51 = OpLabel ; still in else clause
8013 OpStore %var %uint_5
8014 OpBranch %80 ; to premerge
8015
8016 %80 = OpLabel ; premerge
8017 OpStore %var %uint_6
8018 OpBranchConditional %cond3 %81 %99
8019
8020 %81 = OpLabel ; premerge
8021 OpStore %var %uint_7
8022 OpBranch %99
8023
8024 %99 = OpLabel
8025 OpStore %var %uint_8
8026 OpReturn
8027 OpFunctionEnd
8028 )";
8029 auto p = parser(test::Assemble(assembly));
8030 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8031 auto fe = p->function_emitter(100);
8032 EXPECT_TRUE(fe.EmitBody()) << p->error() << assembly;
8033 auto ast_body = fe.ast_body();
8034 auto got = test::ToString(p->program(), ast_body);
8035 auto* expect = R"(var_1 = 1u;
8036 var guard10 : bool = true;
8037 if (false) {
8038 var_1 = 2u;
8039 if (true) {
8040 } else {
8041 guard10 = false;
8042 }
8043 if (guard10) {
8044 var_1 = 3u;
8045 }
8046 } else {
8047 if (guard10) {
8048 var_1 = 4u;
8049 if (true) {
8050 guard10 = false;
8051 }
8052 if (guard10) {
8053 var_1 = 5u;
8054 }
8055 }
8056 }
8057 if (guard10) {
8058 var_1 = 6u;
8059 if (false) {
8060 } else {
8061 guard10 = false;
8062 }
8063 if (guard10) {
8064 var_1 = 7u;
8065 guard10 = false;
8066 }
8067 }
8068 var_1 = 8u;
8069 return;
8070 )";
8071 ASSERT_EQ(expect, got);
8072 }
8073
TEST_F(SpvParserCFGTest,BlockIsContinueForMoreThanOneHeader)8074 TEST_F(SpvParserCFGTest, BlockIsContinueForMoreThanOneHeader) {
8075 // This is disallowed by the rule:
8076 // "a continue block is valid only for the innermost loop it is nested
8077 // inside of"
8078 auto assembly = CommonTypes() + R"(
8079 %100 = OpFunction %void None %voidfn
8080
8081 %10 = OpLabel
8082 OpBranch %20
8083
8084 %20 = OpLabel ; outer loop
8085 OpLoopMerge %99 %50 None
8086 OpBranchConditional %cond %50 %99
8087
8088 %50 = OpLabel ; continue target, but also single-block loop
8089 OpLoopMerge %80 %50 None
8090 OpBranchConditional %cond2 %50 %80
8091
8092 %80 = OpLabel
8093 OpBranch %20 ; backedge for outer loop
8094
8095 %99 = OpLabel
8096 OpReturn
8097 OpFunctionEnd
8098 )";
8099 auto p = parser(test::Assemble(assembly));
8100 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8101 auto fe = p->function_emitter(100);
8102 fe.RegisterBasicBlocks();
8103 fe.ComputeBlockOrderAndPositions();
8104 EXPECT_TRUE(fe.VerifyHeaderContinueMergeOrder());
8105 EXPECT_FALSE(fe.RegisterMerges());
8106 EXPECT_THAT(p->error(), Eq("Block 50 declared as continue target for more "
8107 "than one header: 20, 50"));
8108 }
8109
TEST_F(SpvParserCFGTest,EmitBody_If_Empty)8110 TEST_F(SpvParserCFGTest, EmitBody_If_Empty) {
8111 auto p = parser(test::Assemble(CommonTypes() + R"(
8112 %100 = OpFunction %void None %voidfn
8113
8114 %10 = OpLabel
8115 OpSelectionMerge %99 None
8116 OpBranchConditional %cond %99 %99
8117
8118 %99 = OpLabel
8119 OpReturn
8120 OpFunctionEnd
8121 )"));
8122 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8123 auto fe = p->function_emitter(100);
8124 EXPECT_TRUE(fe.EmitBody()) << p->error();
8125
8126 auto ast_body = fe.ast_body();
8127 auto got = test::ToString(p->program(), ast_body);
8128 auto* expect = R"(if (false) {
8129 }
8130 return;
8131 )";
8132 ASSERT_EQ(expect, got);
8133 }
8134
TEST_F(SpvParserCFGTest,EmitBody_If_Then_NoElse)8135 TEST_F(SpvParserCFGTest, EmitBody_If_Then_NoElse) {
8136 auto p = parser(test::Assemble(CommonTypes() + R"(
8137 %100 = OpFunction %void None %voidfn
8138
8139 %10 = OpLabel
8140 OpStore %var %uint_0
8141 OpSelectionMerge %99 None
8142 OpBranchConditional %cond %30 %99
8143
8144 %99 = OpLabel
8145 OpStore %var %999
8146 OpReturn
8147
8148 %30 = OpLabel
8149 OpStore %var %uint_1
8150 OpBranch %99
8151
8152 OpFunctionEnd
8153 )"));
8154 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8155 auto fe = p->function_emitter(100);
8156 EXPECT_TRUE(fe.EmitBody()) << p->error();
8157
8158 auto ast_body = fe.ast_body();
8159 auto got = test::ToString(p->program(), ast_body);
8160 auto* expect = R"(var_1 = 0u;
8161 if (false) {
8162 var_1 = 1u;
8163 }
8164 var_1 = 999u;
8165 return;
8166 )";
8167 ASSERT_EQ(expect, got);
8168 }
8169
TEST_F(SpvParserCFGTest,EmitBody_If_NoThen_Else)8170 TEST_F(SpvParserCFGTest, EmitBody_If_NoThen_Else) {
8171 auto p = parser(test::Assemble(CommonTypes() + R"(
8172 %100 = OpFunction %void None %voidfn
8173
8174 %10 = OpLabel
8175 OpStore %var %uint_0
8176 OpSelectionMerge %99 None
8177 OpBranchConditional %cond %99 %30
8178
8179 %99 = OpLabel
8180 OpStore %var %999
8181 OpReturn
8182
8183 %30 = OpLabel
8184 OpStore %var %uint_1
8185 OpBranch %99
8186
8187 OpFunctionEnd
8188 )"));
8189 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8190 auto fe = p->function_emitter(100);
8191 EXPECT_TRUE(fe.EmitBody()) << p->error();
8192
8193 auto ast_body = fe.ast_body();
8194 auto got = test::ToString(p->program(), ast_body);
8195 auto* expect = R"(var_1 = 0u;
8196 if (false) {
8197 } else {
8198 var_1 = 1u;
8199 }
8200 var_1 = 999u;
8201 return;
8202 )";
8203 ASSERT_EQ(expect, got);
8204 }
8205
TEST_F(SpvParserCFGTest,EmitBody_If_Then_Else)8206 TEST_F(SpvParserCFGTest, EmitBody_If_Then_Else) {
8207 auto p = parser(test::Assemble(CommonTypes() + R"(
8208 %100 = OpFunction %void None %voidfn
8209
8210 %10 = OpLabel
8211 OpStore %var %uint_0
8212 OpSelectionMerge %99 None
8213 OpBranchConditional %cond %30 %40
8214
8215 %99 = OpLabel
8216 OpStore %var %999
8217 OpReturn
8218
8219 %30 = OpLabel
8220 OpStore %var %uint_1
8221 OpBranch %99
8222
8223 %40 = OpLabel
8224 OpStore %var %uint_2
8225 OpBranch %99
8226
8227 OpFunctionEnd
8228 )"));
8229 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8230 auto fe = p->function_emitter(100);
8231 EXPECT_TRUE(fe.EmitBody()) << p->error();
8232
8233 auto ast_body = fe.ast_body();
8234 auto got = test::ToString(p->program(), ast_body);
8235 auto* expect = R"(var_1 = 0u;
8236 if (false) {
8237 var_1 = 1u;
8238 } else {
8239 var_1 = 2u;
8240 }
8241 var_1 = 999u;
8242 return;
8243 )";
8244 ASSERT_EQ(expect, got);
8245 }
8246
TEST_F(SpvParserCFGTest,EmitBody_If_Then_Else_Premerge)8247 TEST_F(SpvParserCFGTest, EmitBody_If_Then_Else_Premerge) {
8248 // TODO(dneto): This should get an extra if(true) around
8249 // the premerge code.
8250 // See https://bugs.chromium.org/p/tint/issues/detail?id=82
8251 auto p = parser(test::Assemble(CommonTypes() + R"(
8252 %100 = OpFunction %void None %voidfn
8253
8254 %10 = OpLabel
8255 OpStore %var %uint_0
8256 OpSelectionMerge %99 None
8257 OpBranchConditional %cond %30 %40
8258
8259 %80 = OpLabel ; premerge
8260 OpStore %var %uint_3
8261 OpBranch %99
8262
8263 %99 = OpLabel
8264 OpStore %var %999
8265 OpReturn
8266
8267 %30 = OpLabel
8268 OpStore %var %uint_1
8269 OpBranch %80
8270
8271 %40 = OpLabel
8272 OpStore %var %uint_2
8273 OpBranch %80
8274
8275 OpFunctionEnd
8276 )"));
8277 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8278 auto fe = p->function_emitter(100);
8279 EXPECT_TRUE(fe.EmitBody()) << p->error();
8280
8281 auto ast_body = fe.ast_body();
8282 auto got = test::ToString(p->program(), ast_body);
8283 auto* expect = R"(var_1 = 0u;
8284 if (false) {
8285 var_1 = 1u;
8286 } else {
8287 var_1 = 2u;
8288 }
8289 if (true) {
8290 var_1 = 3u;
8291 }
8292 var_1 = 999u;
8293 return;
8294 )";
8295 ASSERT_EQ(expect, got);
8296 }
8297
TEST_F(SpvParserCFGTest,EmitBody_If_Then_Premerge)8298 TEST_F(SpvParserCFGTest, EmitBody_If_Then_Premerge) {
8299 // The premerge *is* the else.
8300 auto p = parser(test::Assemble(CommonTypes() + R"(
8301 %100 = OpFunction %void None %voidfn
8302
8303 %10 = OpLabel
8304 OpStore %var %uint_0
8305 OpSelectionMerge %99 None
8306 OpBranchConditional %cond %30 %80
8307
8308 %80 = OpLabel ; premerge
8309 OpStore %var %uint_3
8310 OpBranch %99
8311
8312 %99 = OpLabel
8313 OpStore %var %999
8314 OpReturn
8315
8316 %30 = OpLabel
8317 OpStore %var %uint_1
8318 OpBranch %80
8319
8320 OpFunctionEnd
8321 )"));
8322 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8323 auto fe = p->function_emitter(100);
8324 EXPECT_TRUE(fe.EmitBody()) << p->error();
8325
8326 auto ast_body = fe.ast_body();
8327 auto got = test::ToString(p->program(), ast_body);
8328 auto* expect = R"(var_1 = 0u;
8329 if (false) {
8330 var_1 = 1u;
8331 }
8332 if (true) {
8333 var_1 = 3u;
8334 }
8335 var_1 = 999u;
8336 return;
8337 )";
8338 ASSERT_EQ(expect, got);
8339 }
8340
TEST_F(SpvParserCFGTest,EmitBody_If_Else_Premerge)8341 TEST_F(SpvParserCFGTest, EmitBody_If_Else_Premerge) {
8342 // The premerge *is* the then-clause.
8343 auto p = parser(test::Assemble(CommonTypes() + R"(
8344 %100 = OpFunction %void None %voidfn
8345
8346 %10 = OpLabel
8347 OpStore %var %uint_0
8348 OpSelectionMerge %99 None
8349 OpBranchConditional %cond %80 %30
8350
8351 %80 = OpLabel ; premerge
8352 OpStore %var %uint_3
8353 OpBranch %99
8354
8355 %99 = OpLabel
8356 OpStore %var %999
8357 OpReturn
8358
8359 %30 = OpLabel
8360 OpStore %var %uint_1
8361 OpBranch %80
8362
8363 OpFunctionEnd
8364 )"));
8365 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8366 auto fe = p->function_emitter(100);
8367 EXPECT_TRUE(fe.EmitBody()) << p->error();
8368
8369 auto ast_body = fe.ast_body();
8370 auto got = test::ToString(p->program(), ast_body);
8371 auto* expect = R"(var_1 = 0u;
8372 if (false) {
8373 } else {
8374 var_1 = 1u;
8375 }
8376 if (true) {
8377 var_1 = 3u;
8378 }
8379 var_1 = 999u;
8380 return;
8381 )";
8382 ASSERT_EQ(expect, got);
8383 }
8384
TEST_F(SpvParserCFGTest,EmitBody_If_Nest_If)8385 TEST_F(SpvParserCFGTest, EmitBody_If_Nest_If) {
8386 auto p = parser(test::Assemble(CommonTypes() + R"(
8387 %100 = OpFunction %void None %voidfn
8388
8389 %10 = OpLabel
8390 OpStore %var %uint_0
8391 OpSelectionMerge %99 None
8392 OpBranchConditional %cond %30 %40
8393
8394 %30 = OpLabel ;; inner if #1
8395 OpStore %var %uint_1
8396 OpSelectionMerge %39 None
8397 OpBranchConditional %cond2 %33 %39
8398
8399 %33 = OpLabel
8400 OpStore %var %uint_2
8401 OpBranch %39
8402
8403 %39 = OpLabel ;; inner merge
8404 OpStore %var %uint_3
8405 OpBranch %99
8406
8407 %40 = OpLabel ;; inner if #2
8408 OpStore %var %uint_4
8409 OpSelectionMerge %49 None
8410 OpBranchConditional %cond2 %49 %43
8411
8412 %43 = OpLabel
8413 OpStore %var %uint_5
8414 OpBranch %49
8415
8416 %49 = OpLabel ;; 2nd inner merge
8417 OpStore %var %uint_6
8418 OpBranch %99
8419
8420 %99 = OpLabel
8421 OpStore %var %999
8422 OpReturn
8423
8424 OpFunctionEnd
8425 )"));
8426 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8427 auto fe = p->function_emitter(100);
8428 EXPECT_TRUE(fe.EmitBody()) << p->error();
8429
8430 auto ast_body = fe.ast_body();
8431 auto got = test::ToString(p->program(), ast_body);
8432 auto* expect = R"(var_1 = 0u;
8433 if (false) {
8434 var_1 = 1u;
8435 if (true) {
8436 var_1 = 2u;
8437 }
8438 var_1 = 3u;
8439 } else {
8440 var_1 = 4u;
8441 if (true) {
8442 } else {
8443 var_1 = 5u;
8444 }
8445 var_1 = 6u;
8446 }
8447 var_1 = 999u;
8448 return;
8449 )";
8450 ASSERT_EQ(expect, got);
8451 }
8452
TEST_F(SpvParserCFGTest,EmitBody_Loop_SingleBlock_TrueBackedge)8453 TEST_F(SpvParserCFGTest, EmitBody_Loop_SingleBlock_TrueBackedge) {
8454 auto p = parser(test::Assemble(CommonTypes() + R"(
8455 %100 = OpFunction %void None %voidfn
8456
8457 %10 = OpLabel
8458 OpStore %var %uint_0
8459 OpBranch %20
8460
8461 %20 = OpLabel
8462 OpStore %var %uint_1
8463 OpLoopMerge %99 %20 None
8464 OpBranchConditional %cond %20 %99
8465
8466 %99 = OpLabel
8467 OpStore %var %999
8468 OpReturn
8469
8470 OpFunctionEnd
8471 )"));
8472 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8473 auto fe = p->function_emitter(100);
8474 EXPECT_TRUE(fe.EmitBody()) << p->error();
8475
8476 auto ast_body = fe.ast_body();
8477 auto got = test::ToString(p->program(), ast_body);
8478 auto* expect = R"(var_1 = 0u;
8479 loop {
8480 var_1 = 1u;
8481 if (false) {
8482 } else {
8483 break;
8484 }
8485 }
8486 var_1 = 999u;
8487 return;
8488 )";
8489 ASSERT_EQ(expect, got);
8490 }
8491
TEST_F(SpvParserCFGTest,EmitBody_Loop_SingleBlock_FalseBackedge)8492 TEST_F(SpvParserCFGTest, EmitBody_Loop_SingleBlock_FalseBackedge) {
8493 auto p = parser(test::Assemble(CommonTypes() + R"(
8494 %100 = OpFunction %void None %voidfn
8495
8496 %10 = OpLabel
8497 OpStore %var %uint_0
8498 OpBranch %20
8499
8500 %20 = OpLabel
8501 OpStore %var %uint_1
8502 OpLoopMerge %99 %20 None
8503 OpBranchConditional %cond %99 %20
8504
8505 %99 = OpLabel
8506 OpStore %var %999
8507 OpReturn
8508
8509 OpFunctionEnd
8510 )"));
8511 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8512 auto fe = p->function_emitter(100);
8513 EXPECT_TRUE(fe.EmitBody()) << p->error();
8514
8515 auto ast_body = fe.ast_body();
8516 auto got = test::ToString(p->program(), ast_body);
8517 auto* expect = R"(var_1 = 0u;
8518 loop {
8519 var_1 = 1u;
8520 if (false) {
8521 break;
8522 }
8523 }
8524 var_1 = 999u;
8525 return;
8526 )";
8527 ASSERT_EQ(expect, got);
8528 }
8529
TEST_F(SpvParserCFGTest,EmitBody_Loop_SingleBlock_BothBackedge)8530 TEST_F(SpvParserCFGTest, EmitBody_Loop_SingleBlock_BothBackedge) {
8531 auto p = parser(test::Assemble(CommonTypes() + R"(
8532 %100 = OpFunction %void None %voidfn
8533
8534 %10 = OpLabel
8535 OpStore %var %uint_0
8536 OpBranch %20
8537
8538 %20 = OpLabel
8539 OpStore %var %uint_1
8540 OpLoopMerge %99 %20 None
8541 OpBranchConditional %cond %20 %20
8542
8543 %99 = OpLabel
8544 OpStore %var %999
8545 OpReturn
8546
8547 OpFunctionEnd
8548 )"));
8549 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8550 auto fe = p->function_emitter(100);
8551 EXPECT_TRUE(fe.EmitBody()) << p->error();
8552
8553 auto ast_body = fe.ast_body();
8554 auto got = test::ToString(p->program(), ast_body);
8555 auto* expect = R"(var_1 = 0u;
8556 loop {
8557 var_1 = 1u;
8558 }
8559 var_1 = 999u;
8560 return;
8561 )";
8562 ASSERT_EQ(expect, got);
8563 }
8564
TEST_F(SpvParserCFGTest,EmitBody_Loop_SingleBlock_UnconditionalBackege)8565 TEST_F(SpvParserCFGTest, EmitBody_Loop_SingleBlock_UnconditionalBackege) {
8566 auto p = parser(test::Assemble(CommonTypes() + R"(
8567 %100 = OpFunction %void None %voidfn
8568
8569 %10 = OpLabel
8570 OpStore %var %uint_0
8571 OpBranch %20
8572
8573 %20 = OpLabel
8574 OpStore %var %uint_1
8575 OpLoopMerge %99 %20 None
8576 OpBranch %20
8577
8578 %99 = OpLabel
8579 OpStore %var %999
8580 OpReturn
8581
8582 OpFunctionEnd
8583 )"));
8584 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8585 auto fe = p->function_emitter(100);
8586 EXPECT_TRUE(fe.EmitBody()) << p->error();
8587
8588 auto ast_body = fe.ast_body();
8589 auto got = test::ToString(p->program(), ast_body);
8590 auto* expect = R"(var_1 = 0u;
8591 loop {
8592 var_1 = 1u;
8593 }
8594 var_1 = 999u;
8595 return;
8596 )";
8597 ASSERT_EQ(expect, got);
8598 }
8599
TEST_F(SpvParserCFGTest,EmitBody_Loop_Unconditional_Body_SingleBlockContinue)8600 TEST_F(SpvParserCFGTest, EmitBody_Loop_Unconditional_Body_SingleBlockContinue) {
8601 auto p = parser(test::Assemble(CommonTypes() + R"(
8602 %100 = OpFunction %void None %voidfn
8603
8604 %10 = OpLabel
8605 OpStore %var %uint_0
8606 OpBranch %20
8607
8608 %20 = OpLabel
8609 OpStore %var %uint_1
8610 OpLoopMerge %99 %50 None
8611 OpBranch %30
8612
8613 %30 = OpLabel
8614 OpStore %var %uint_2
8615 OpBranch %50
8616
8617 %50 = OpLabel
8618 OpStore %var %uint_3
8619 OpBranch %20
8620
8621 %99 = OpLabel
8622 OpStore %var %999
8623 OpReturn
8624
8625 OpFunctionEnd
8626 )"));
8627 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8628 auto fe = p->function_emitter(100);
8629 EXPECT_TRUE(fe.EmitBody()) << p->error();
8630
8631 auto ast_body = fe.ast_body();
8632 auto got = test::ToString(p->program(), ast_body);
8633 auto* expect = R"(var_1 = 0u;
8634 loop {
8635 var_1 = 1u;
8636 var_1 = 2u;
8637
8638 continuing {
8639 var_1 = 3u;
8640 }
8641 }
8642 var_1 = 999u;
8643 return;
8644 )";
8645 ASSERT_EQ(expect, got);
8646 }
8647
TEST_F(SpvParserCFGTest,EmitBody_Loop_Unconditional_Body_MultiBlockContinue)8648 TEST_F(SpvParserCFGTest, EmitBody_Loop_Unconditional_Body_MultiBlockContinue) {
8649 auto p = parser(test::Assemble(CommonTypes() + R"(
8650 %100 = OpFunction %void None %voidfn
8651
8652 %10 = OpLabel
8653 OpStore %var %uint_0
8654 OpBranch %20
8655
8656 %20 = OpLabel
8657 OpStore %var %uint_1
8658 OpLoopMerge %99 %50 None
8659 OpBranch %30
8660
8661 %30 = OpLabel
8662 OpStore %var %uint_2
8663 OpBranch %50
8664
8665 %50 = OpLabel
8666 OpStore %var %uint_3
8667 OpBranch %60
8668
8669 %60 = OpLabel
8670 OpStore %var %uint_4
8671 OpBranch %20
8672
8673 %99 = OpLabel
8674 OpStore %var %999
8675 OpReturn
8676
8677 OpFunctionEnd
8678 )"));
8679 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8680 auto fe = p->function_emitter(100);
8681 EXPECT_TRUE(fe.EmitBody()) << p->error();
8682
8683 auto ast_body = fe.ast_body();
8684 auto got = test::ToString(p->program(), ast_body);
8685 auto* expect = R"(var_1 = 0u;
8686 loop {
8687 var_1 = 1u;
8688 var_1 = 2u;
8689
8690 continuing {
8691 var_1 = 3u;
8692 var_1 = 4u;
8693 }
8694 }
8695 var_1 = 999u;
8696 return;
8697 )";
8698 ASSERT_EQ(expect, got);
8699 }
8700
TEST_F(SpvParserCFGTest,EmitBody_Loop_Unconditional_Body_ContinueNestIf)8701 TEST_F(SpvParserCFGTest, EmitBody_Loop_Unconditional_Body_ContinueNestIf) {
8702 auto p = parser(test::Assemble(CommonTypes() + R"(
8703 %100 = OpFunction %void None %voidfn
8704
8705 %10 = OpLabel
8706 OpStore %var %uint_0
8707 OpBranch %20
8708
8709 %20 = OpLabel
8710 OpStore %var %uint_1
8711 OpLoopMerge %99 %50 None
8712 OpBranch %30
8713
8714 %30 = OpLabel
8715 OpStore %var %uint_2
8716 OpBranch %50
8717
8718 %50 = OpLabel ; continue target; also if-header
8719 OpStore %var %uint_3
8720 OpSelectionMerge %80 None
8721 OpBranchConditional %cond2 %60 %80
8722
8723 %60 = OpLabel
8724 OpStore %var %uint_4
8725 OpBranch %80
8726
8727 %80 = OpLabel
8728 OpStore %var %uint_5
8729 OpBranch %20
8730
8731 %99 = OpLabel
8732 OpStore %var %999
8733 OpReturn
8734
8735 OpFunctionEnd
8736 )"));
8737 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8738 auto fe = p->function_emitter(100);
8739 EXPECT_TRUE(fe.EmitBody()) << p->error();
8740
8741 auto ast_body = fe.ast_body();
8742 auto got = test::ToString(p->program(), ast_body);
8743 auto* expect = R"(var_1 = 0u;
8744 loop {
8745 var_1 = 1u;
8746 var_1 = 2u;
8747
8748 continuing {
8749 var_1 = 3u;
8750 if (true) {
8751 var_1 = 4u;
8752 }
8753 var_1 = 5u;
8754 }
8755 }
8756 var_1 = 999u;
8757 return;
8758 )";
8759 ASSERT_EQ(expect, got);
8760 }
8761
TEST_F(SpvParserCFGTest,EmitBody_Loop_MultiBlockContinueIsEntireLoop)8762 TEST_F(SpvParserCFGTest, EmitBody_Loop_MultiBlockContinueIsEntireLoop) {
8763 // Test case where both branches exit. e.g both go to merge.
8764 auto p = parser(test::Assemble(CommonTypes() + R"(
8765 %100 = OpFunction %void None %voidfn
8766
8767 %10 = OpLabel
8768 OpStore %var %uint_0
8769 OpBranch %20
8770
8771 %20 = OpLabel ; its own continue target
8772 OpStore %var %uint_1
8773 OpLoopMerge %99 %20 None
8774 OpBranch %80
8775
8776 %80 = OpLabel
8777 OpStore %var %uint_2
8778 OpBranchConditional %cond %99 %20
8779
8780 %99 = OpLabel
8781 OpStore %var %uint_3
8782 OpReturn
8783
8784 OpFunctionEnd
8785 )"));
8786 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8787 auto fe = p->function_emitter(100);
8788 EXPECT_TRUE(fe.EmitBody()) << p->error();
8789 auto ast_body = fe.ast_body();
8790 auto got = test::ToString(p->program(), ast_body);
8791 auto* expect = R"(var_1 = 0u;
8792 loop {
8793 var_1 = 1u;
8794 var_1 = 2u;
8795 if (false) {
8796 break;
8797 }
8798 }
8799 var_1 = 3u;
8800 return;
8801 )";
8802 ASSERT_EQ(expect, got);
8803 }
8804
TEST_F(SpvParserCFGTest,EmitBody_Loop_Never)8805 TEST_F(SpvParserCFGTest, EmitBody_Loop_Never) {
8806 // Test case where both branches exit. e.g both go to merge.
8807 auto p = parser(test::Assemble(CommonTypes() + R"(
8808 %100 = OpFunction %void None %voidfn
8809
8810 %10 = OpLabel
8811 OpBranch %20
8812
8813 %20 = OpLabel
8814 OpStore %var %uint_1
8815 OpLoopMerge %99 %80 None
8816 OpBranchConditional %cond %99 %99
8817
8818 %80 = OpLabel ; continue target
8819 OpStore %var %uint_2
8820 OpBranch %20
8821
8822 %99 = OpLabel
8823 OpStore %var %uint_3
8824 OpReturn
8825
8826 OpFunctionEnd
8827 )"));
8828 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8829 auto fe = p->function_emitter(100);
8830 EXPECT_TRUE(fe.EmitBody()) << p->error();
8831 auto ast_body = fe.ast_body();
8832 auto got = test::ToString(p->program(), ast_body);
8833 auto* expect = R"(loop {
8834 var_1 = 1u;
8835 break;
8836
8837 continuing {
8838 var_1 = 2u;
8839 }
8840 }
8841 var_1 = 3u;
8842 return;
8843 )";
8844 ASSERT_EQ(expect, got);
8845 }
8846
TEST_F(SpvParserCFGTest,EmitBody_Loop_HeaderBreakAndContinue)8847 TEST_F(SpvParserCFGTest, EmitBody_Loop_HeaderBreakAndContinue) {
8848 // Header block branches to merge, and to an outer continue.
8849 // This is disallowed by the rule:
8850 // "a continue block is valid only for the innermost loop it is nested
8851 // inside of"
8852 // See test ClassifyCFGEdges_LoopContinue_FromNestedLoopHeader_IsError
8853 }
8854
TEST_F(SpvParserCFGTest,EmitBody_Loop_TrueToBody_FalseBreaks)8855 TEST_F(SpvParserCFGTest, EmitBody_Loop_TrueToBody_FalseBreaks) {
8856 auto p = parser(test::Assemble(CommonTypes() + R"(
8857 %100 = OpFunction %void None %voidfn
8858
8859 %10 = OpLabel
8860 OpBranch %20
8861
8862 %20 = OpLabel
8863 OpStore %var %uint_1
8864 OpLoopMerge %99 %80 None
8865 OpBranchConditional %cond %30 %99
8866
8867 %30 = OpLabel
8868 OpStore %var %uint_2
8869 OpBranch %80
8870
8871 %80 = OpLabel ; continue target
8872 OpStore %var %uint_3
8873 OpBranch %20
8874
8875 %99 = OpLabel
8876 OpStore %var %uint_4
8877 OpReturn
8878
8879 OpFunctionEnd
8880 )"));
8881 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8882 auto fe = p->function_emitter(100);
8883 EXPECT_TRUE(fe.EmitBody()) << p->error();
8884 auto ast_body = fe.ast_body();
8885 auto got = test::ToString(p->program(), ast_body);
8886 auto* expect = R"(loop {
8887 var_1 = 1u;
8888 if (false) {
8889 } else {
8890 break;
8891 }
8892 var_1 = 2u;
8893
8894 continuing {
8895 var_1 = 3u;
8896 }
8897 }
8898 var_1 = 4u;
8899 return;
8900 )";
8901 ASSERT_EQ(expect, got);
8902 }
8903
TEST_F(SpvParserCFGTest,EmitBody_Loop_FalseToBody_TrueBreaks)8904 TEST_F(SpvParserCFGTest, EmitBody_Loop_FalseToBody_TrueBreaks) {
8905 auto p = parser(test::Assemble(CommonTypes() + R"(
8906 %100 = OpFunction %void None %voidfn
8907
8908 %10 = OpLabel
8909 OpBranch %20
8910
8911 %20 = OpLabel
8912 OpStore %var %uint_1
8913 OpLoopMerge %99 %80 None
8914 OpBranchConditional %cond %30 %99
8915
8916 %30 = OpLabel
8917 OpStore %var %uint_2
8918 OpBranch %80
8919
8920 %80 = OpLabel ; continue target
8921 OpStore %var %uint_3
8922 OpBranch %20
8923
8924 %99 = OpLabel
8925 OpStore %var %uint_4
8926 OpReturn
8927
8928 OpFunctionEnd
8929 )"));
8930 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8931 auto fe = p->function_emitter(100);
8932 EXPECT_TRUE(fe.EmitBody()) << p->error();
8933 auto ast_body = fe.ast_body();
8934 auto got = test::ToString(p->program(), ast_body);
8935 auto* expect = R"(loop {
8936 var_1 = 1u;
8937 if (false) {
8938 } else {
8939 break;
8940 }
8941 var_1 = 2u;
8942
8943 continuing {
8944 var_1 = 3u;
8945 }
8946 }
8947 var_1 = 4u;
8948 return;
8949 )";
8950 ASSERT_EQ(expect, got);
8951 }
8952
TEST_F(SpvParserCFGTest,EmitBody_Loop_NestedIfContinue)8953 TEST_F(SpvParserCFGTest, EmitBody_Loop_NestedIfContinue) {
8954 // By construction, it has to come from nested code.
8955 auto p = parser(test::Assemble(CommonTypes() + R"(
8956 %100 = OpFunction %void None %voidfn
8957
8958 %10 = OpLabel
8959 OpBranch %20
8960
8961 %20 = OpLabel
8962 OpLoopMerge %99 %80 None
8963 OpBranch %30
8964
8965 %30 = OpLabel
8966 OpSelectionMerge %50 None
8967 OpBranchConditional %cond %40 %50
8968
8969 %40 = OpLabel
8970 OpStore %var %uint_1
8971 OpBranch %80 ; continue edge
8972
8973 %50 = OpLabel ; inner selection merge
8974 OpStore %var %uint_2
8975 OpBranch %80
8976
8977 %80 = OpLabel ; continue target
8978 OpStore %var %uint_3
8979 OpBranch %20
8980
8981 %99 = OpLabel
8982 OpReturn
8983
8984 OpFunctionEnd
8985 )"));
8986 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
8987 auto fe = p->function_emitter(100);
8988 EXPECT_TRUE(fe.EmitBody()) << p->error();
8989 auto ast_body = fe.ast_body();
8990 auto got = test::ToString(p->program(), ast_body);
8991 auto* expect = R"(loop {
8992 if (false) {
8993 var_1 = 1u;
8994 continue;
8995 }
8996 var_1 = 2u;
8997
8998 continuing {
8999 var_1 = 3u;
9000 }
9001 }
9002 return;
9003 )";
9004 ASSERT_EQ(expect, got);
9005 }
9006
TEST_F(SpvParserCFGTest,EmitBody_Loop_BodyAlwaysBreaks)9007 TEST_F(SpvParserCFGTest, EmitBody_Loop_BodyAlwaysBreaks) {
9008 auto p = parser(test::Assemble(CommonTypes() + R"(
9009 %100 = OpFunction %void None %voidfn
9010
9011 %10 = OpLabel
9012 OpBranch %20
9013
9014 %20 = OpLabel
9015 OpLoopMerge %99 %80 None
9016 OpBranch %30
9017
9018 %30 = OpLabel
9019 OpStore %var %uint_1
9020 OpBranch %99 ; break is here
9021
9022 %80 = OpLabel
9023 OpStore %var %uint_2
9024 OpBranch %20 ; backedge
9025
9026 %99 = OpLabel
9027 OpReturn
9028
9029 OpFunctionEnd
9030 )"));
9031 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9032 auto fe = p->function_emitter(100);
9033 EXPECT_TRUE(fe.EmitBody()) << p->error();
9034
9035 auto ast_body = fe.ast_body();
9036 auto got = test::ToString(p->program(), ast_body);
9037 auto* expect = R"(loop {
9038 var_1 = 1u;
9039 break;
9040
9041 continuing {
9042 var_1 = 2u;
9043 }
9044 }
9045 return;
9046 )";
9047 ASSERT_EQ(expect, got);
9048 }
9049
TEST_F(SpvParserCFGTest,EmitBody_Loop_BodyConditionallyBreaks_FromTrue)9050 TEST_F(SpvParserCFGTest, EmitBody_Loop_BodyConditionallyBreaks_FromTrue) {
9051 // The else-branch has a continue but it's skipped because it's from a
9052 // block that immediately precedes the continue construct.
9053 auto p = parser(test::Assemble(CommonTypes() + R"(
9054 %100 = OpFunction %void None %voidfn
9055
9056 %10 = OpLabel
9057 OpBranch %20
9058
9059 %20 = OpLabel
9060 OpLoopMerge %99 %80 None
9061 OpBranch %30
9062
9063 %30 = OpLabel
9064 OpStore %var %uint_1
9065 OpBranchConditional %cond %99 %80
9066
9067 %80 = OpLabel
9068 OpStore %var %uint_2
9069 OpBranch %20 ; backedge
9070
9071 %99 = OpLabel
9072 OpReturn
9073
9074 OpFunctionEnd
9075 )"));
9076 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9077 auto fe = p->function_emitter(100);
9078 EXPECT_TRUE(fe.EmitBody()) << p->error();
9079
9080 auto ast_body = fe.ast_body();
9081 auto got = test::ToString(p->program(), ast_body);
9082 auto* expect = R"(loop {
9083 var_1 = 1u;
9084 if (false) {
9085 break;
9086 }
9087
9088 continuing {
9089 var_1 = 2u;
9090 }
9091 }
9092 return;
9093 )";
9094 ASSERT_EQ(expect, got);
9095 }
9096
TEST_F(SpvParserCFGTest,EmitBody_Loop_BodyConditionallyBreaks_FromFalse)9097 TEST_F(SpvParserCFGTest, EmitBody_Loop_BodyConditionallyBreaks_FromFalse) {
9098 // The else-branch has a continue but it's skipped because it's from a
9099 // block that immediately precedes the continue construct.
9100 auto p = parser(test::Assemble(CommonTypes() + R"(
9101 %100 = OpFunction %void None %voidfn
9102
9103 %10 = OpLabel
9104 OpBranch %20
9105
9106 %20 = OpLabel
9107 OpLoopMerge %99 %80 None
9108 OpBranch %30
9109
9110 %30 = OpLabel
9111 OpStore %var %uint_1
9112 OpBranchConditional %cond %80 %99
9113
9114 %80 = OpLabel
9115 OpStore %var %uint_2
9116 OpBranch %20 ; backedge
9117
9118 %99 = OpLabel
9119 OpReturn
9120
9121 OpFunctionEnd
9122 )"));
9123 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9124 auto fe = p->function_emitter(100);
9125 EXPECT_TRUE(fe.EmitBody()) << p->error();
9126
9127 auto ast_body = fe.ast_body();
9128 auto got = test::ToString(p->program(), ast_body);
9129 auto* expect = R"(loop {
9130 var_1 = 1u;
9131 if (false) {
9132 } else {
9133 break;
9134 }
9135
9136 continuing {
9137 var_1 = 2u;
9138 }
9139 }
9140 return;
9141 )";
9142 ASSERT_EQ(expect, got);
9143 }
9144
TEST_F(SpvParserCFGTest,EmitBody_Loop_BodyConditionallyBreaks_FromTrue_Early)9145 TEST_F(SpvParserCFGTest, EmitBody_Loop_BodyConditionallyBreaks_FromTrue_Early) {
9146 auto p = parser(test::Assemble(CommonTypes() + R"(
9147 %100 = OpFunction %void None %voidfn
9148
9149 %10 = OpLabel
9150 OpBranch %20
9151
9152 %20 = OpLabel
9153 OpLoopMerge %99 %80 None
9154 OpBranch %30
9155
9156 %30 = OpLabel
9157 OpStore %var %uint_1
9158 OpBranchConditional %cond %99 %70
9159
9160 %70 = OpLabel
9161 OpStore %var %uint_3
9162 OpBranch %80
9163
9164 %80 = OpLabel
9165 OpStore %var %uint_2
9166 OpBranch %20 ; backedge
9167
9168 %99 = OpLabel
9169 OpReturn
9170
9171 OpFunctionEnd
9172 )"));
9173 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9174 auto fe = p->function_emitter(100);
9175 EXPECT_TRUE(fe.EmitBody()) << p->error();
9176
9177 auto ast_body = fe.ast_body();
9178 auto got = test::ToString(p->program(), ast_body);
9179 auto* expect = R"(loop {
9180 var_1 = 1u;
9181 if (false) {
9182 break;
9183 }
9184 var_1 = 3u;
9185
9186 continuing {
9187 var_1 = 2u;
9188 }
9189 }
9190 return;
9191 )";
9192 ASSERT_EQ(expect, got);
9193 }
9194
TEST_F(SpvParserCFGTest,EmitBody_Loop_BodyConditionallyBreaks_FromFalse_Early)9195 TEST_F(SpvParserCFGTest,
9196 EmitBody_Loop_BodyConditionallyBreaks_FromFalse_Early) {
9197 auto p = parser(test::Assemble(CommonTypes() + R"(
9198 %100 = OpFunction %void None %voidfn
9199
9200 %10 = OpLabel
9201 OpBranch %20
9202
9203 %20 = OpLabel
9204 OpLoopMerge %99 %80 None
9205 OpBranch %30
9206
9207 %30 = OpLabel
9208 OpStore %var %uint_1
9209 OpBranchConditional %cond %70 %99
9210
9211 %70 = OpLabel
9212 OpStore %var %uint_3
9213 OpBranch %80
9214
9215 %80 = OpLabel
9216 OpStore %var %uint_2
9217 OpBranch %20 ; backedge
9218
9219 %99 = OpLabel
9220 OpReturn
9221
9222 OpFunctionEnd
9223 )"));
9224 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9225 auto fe = p->function_emitter(100);
9226 EXPECT_TRUE(fe.EmitBody()) << p->error();
9227
9228 auto ast_body = fe.ast_body();
9229 auto got = test::ToString(p->program(), ast_body);
9230 auto* expect = R"(loop {
9231 var_1 = 1u;
9232 if (false) {
9233 } else {
9234 break;
9235 }
9236 var_1 = 3u;
9237
9238 continuing {
9239 var_1 = 2u;
9240 }
9241 }
9242 return;
9243 )";
9244 ASSERT_EQ(expect, got);
9245 }
9246
TEST_F(SpvParserCFGTest,EmitBody_Switch_DefaultIsMerge_NoCases)9247 TEST_F(SpvParserCFGTest, EmitBody_Switch_DefaultIsMerge_NoCases) {
9248 auto p = parser(test::Assemble(CommonTypes() + R"(
9249 %100 = OpFunction %void None %voidfn
9250
9251 %10 = OpLabel
9252 OpStore %var %uint_1
9253 OpSelectionMerge %99 None
9254 OpSwitch %selector %99
9255
9256 %99 = OpLabel
9257 OpStore %var %uint_7
9258 OpReturn
9259
9260 OpFunctionEnd
9261 )"));
9262 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9263 auto fe = p->function_emitter(100);
9264 EXPECT_TRUE(fe.EmitBody()) << p->error();
9265
9266 auto ast_body = fe.ast_body();
9267 auto got = test::ToString(p->program(), ast_body);
9268 auto* expect = R"(var_1 = 1u;
9269 switch(42u) {
9270 default: {
9271 }
9272 }
9273 var_1 = 7u;
9274 return;
9275 )";
9276 ASSERT_EQ(expect, got);
9277 }
9278
9279 // First do no special control flow: no fallthroughs, breaks, continues.
TEST_F(SpvParserCFGTest,EmitBody_Switch_DefaultIsMerge_OneCase)9280 TEST_F(SpvParserCFGTest, EmitBody_Switch_DefaultIsMerge_OneCase) {
9281 auto p = parser(test::Assemble(CommonTypes() + R"(
9282 %100 = OpFunction %void None %voidfn
9283
9284 %10 = OpLabel
9285 OpStore %var %uint_1
9286 OpSelectionMerge %99 None
9287 OpSwitch %selector %99 20 %20
9288
9289 %20 = OpLabel
9290 OpStore %var %uint_20
9291 OpBranch %99
9292
9293 %99 = OpLabel
9294 OpStore %var %uint_7
9295 OpReturn
9296
9297 OpFunctionEnd
9298 )"));
9299 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9300 auto fe = p->function_emitter(100);
9301 EXPECT_TRUE(fe.EmitBody()) << p->error();
9302
9303 auto ast_body = fe.ast_body();
9304 auto got = test::ToString(p->program(), ast_body);
9305 auto* expect = R"(var_1 = 1u;
9306 switch(42u) {
9307 case 20u: {
9308 var_1 = 20u;
9309 }
9310 default: {
9311 }
9312 }
9313 var_1 = 7u;
9314 return;
9315 )";
9316 ASSERT_EQ(expect, got);
9317 }
9318
TEST_F(SpvParserCFGTest,EmitBody_Switch_DefaultIsMerge_TwoCases)9319 TEST_F(SpvParserCFGTest, EmitBody_Switch_DefaultIsMerge_TwoCases) {
9320 auto p = parser(test::Assemble(CommonTypes() + R"(
9321 %100 = OpFunction %void None %voidfn
9322
9323 %10 = OpLabel
9324 OpStore %var %uint_1
9325 OpSelectionMerge %99 None
9326 OpSwitch %selector %99 20 %20 30 %30
9327
9328 %20 = OpLabel
9329 OpStore %var %uint_20
9330 OpBranch %99
9331
9332 %30 = OpLabel
9333 OpStore %var %uint_30
9334 OpBranch %99
9335
9336 %99 = OpLabel
9337 OpStore %var %uint_7
9338 OpReturn
9339
9340 OpFunctionEnd
9341 )"));
9342 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9343 auto fe = p->function_emitter(100);
9344 EXPECT_TRUE(fe.EmitBody()) << p->error();
9345
9346 auto ast_body = fe.ast_body();
9347 auto got = test::ToString(p->program(), ast_body);
9348 auto* expect = R"(var_1 = 1u;
9349 switch(42u) {
9350 case 30u: {
9351 var_1 = 30u;
9352 }
9353 case 20u: {
9354 var_1 = 20u;
9355 }
9356 default: {
9357 }
9358 }
9359 var_1 = 7u;
9360 return;
9361 )";
9362 ASSERT_EQ(expect, got);
9363 }
9364
TEST_F(SpvParserCFGTest,EmitBody_Switch_DefaultIsMerge_CasesWithDup)9365 TEST_F(SpvParserCFGTest, EmitBody_Switch_DefaultIsMerge_CasesWithDup) {
9366 auto p = parser(test::Assemble(CommonTypes() + R"(
9367 %100 = OpFunction %void None %voidfn
9368
9369 %10 = OpLabel
9370 OpStore %var %uint_1
9371 OpSelectionMerge %99 None
9372 OpSwitch %selector %99 20 %20 30 %30 40 %20
9373
9374 %20 = OpLabel
9375 OpStore %var %uint_20
9376 OpBranch %99
9377
9378 %30 = OpLabel
9379 OpStore %var %uint_30
9380 OpBranch %99
9381
9382 %99 = OpLabel
9383 OpStore %var %uint_7
9384 OpReturn
9385
9386 OpFunctionEnd
9387 )"));
9388 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9389 auto fe = p->function_emitter(100);
9390 EXPECT_TRUE(fe.EmitBody()) << p->error();
9391
9392 auto ast_body = fe.ast_body();
9393 auto got = test::ToString(p->program(), ast_body);
9394 auto* expect = R"(var_1 = 1u;
9395 switch(42u) {
9396 case 30u: {
9397 var_1 = 30u;
9398 }
9399 case 20u, 40u: {
9400 var_1 = 20u;
9401 }
9402 default: {
9403 }
9404 }
9405 var_1 = 7u;
9406 return;
9407 )";
9408 ASSERT_EQ(expect, got);
9409 }
9410
TEST_F(SpvParserCFGTest,EmitBody_Switch_DefaultIsCase_NoDupCases)9411 TEST_F(SpvParserCFGTest, EmitBody_Switch_DefaultIsCase_NoDupCases) {
9412 // The default block is not the merge block. But not the same as a case
9413 // either.
9414 auto p = parser(test::Assemble(CommonTypes() + R"(
9415 %100 = OpFunction %void None %voidfn
9416
9417 %10 = OpLabel
9418 OpStore %var %uint_1
9419 OpSelectionMerge %99 None
9420 OpSwitch %selector %30 20 %20 40 %40
9421
9422 %20 = OpLabel
9423 OpStore %var %uint_20
9424 OpBranch %99
9425
9426 %30 = OpLabel ; the named default block
9427 OpStore %var %uint_30
9428 OpBranch %99
9429
9430 %40 = OpLabel
9431 OpStore %var %uint_40
9432 OpBranch %99
9433
9434 %99 = OpLabel
9435 OpStore %var %uint_7
9436 OpReturn
9437
9438 OpFunctionEnd
9439 )"));
9440 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9441 auto fe = p->function_emitter(100);
9442 EXPECT_TRUE(fe.EmitBody()) << p->error();
9443
9444 auto ast_body = fe.ast_body();
9445 auto got = test::ToString(p->program(), ast_body);
9446 auto* expect = R"(var_1 = 1u;
9447 switch(42u) {
9448 case 40u: {
9449 var_1 = 40u;
9450 }
9451 case 20u: {
9452 var_1 = 20u;
9453 }
9454 default: {
9455 var_1 = 30u;
9456 }
9457 }
9458 var_1 = 7u;
9459 return;
9460 )";
9461 ASSERT_EQ(expect, got);
9462 }
9463
TEST_F(SpvParserCFGTest,EmitBody_Switch_DefaultIsCase_WithDupCase)9464 TEST_F(SpvParserCFGTest, EmitBody_Switch_DefaultIsCase_WithDupCase) {
9465 // The default block is not the merge block and is the same as a case.
9466 // We emit the default case separately, but just before the labeled
9467 // case, and with a fallthrough.
9468 auto p = parser(test::Assemble(CommonTypes() + R"(
9469 %100 = OpFunction %void None %voidfn
9470
9471 %10 = OpLabel
9472 OpStore %var %uint_1
9473 OpSelectionMerge %99 None
9474 OpSwitch %selector %30 20 %20 30 %30 40 %40
9475
9476 %20 = OpLabel
9477 OpStore %var %uint_20
9478 OpBranch %99
9479
9480 %30 = OpLabel ; the named default block, also a case
9481 OpStore %var %uint_30
9482 OpBranch %99
9483
9484 %40 = OpLabel
9485 OpStore %var %uint_40
9486 OpBranch %99
9487
9488 %99 = OpLabel
9489 OpStore %var %uint_7
9490 OpReturn
9491
9492 OpFunctionEnd
9493 )"));
9494 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9495 auto fe = p->function_emitter(100);
9496 EXPECT_TRUE(fe.EmitBody()) << p->error();
9497
9498 auto ast_body = fe.ast_body();
9499 auto got = test::ToString(p->program(), ast_body);
9500 auto* expect = R"(var_1 = 1u;
9501 switch(42u) {
9502 case 40u: {
9503 var_1 = 40u;
9504 }
9505 case 20u: {
9506 var_1 = 20u;
9507 }
9508 default: {
9509 fallthrough;
9510 }
9511 case 30u: {
9512 var_1 = 30u;
9513 }
9514 }
9515 var_1 = 7u;
9516 return;
9517 )";
9518 ASSERT_EQ(expect, got);
9519 }
9520
TEST_F(SpvParserCFGTest,EmitBody_Switch_Case_SintValue)9521 TEST_F(SpvParserCFGTest, EmitBody_Switch_Case_SintValue) {
9522 auto p = parser(test::Assemble(CommonTypes() + R"(
9523 %100 = OpFunction %void None %voidfn
9524
9525 %10 = OpLabel
9526 OpStore %var %uint_1
9527 OpSelectionMerge %99 None
9528 ; SPIR-V assembler doesn't support negative literals in switch
9529 OpSwitch %signed_selector %99 20 %20 2000000000 %30 !4000000000 %40
9530
9531 %20 = OpLabel
9532 OpStore %var %uint_20
9533 OpBranch %99
9534
9535 %30 = OpLabel
9536 OpStore %var %uint_30
9537 OpBranch %99
9538
9539 %40 = OpLabel
9540 OpStore %var %uint_40
9541 OpBranch %99
9542
9543 %99 = OpLabel
9544 OpStore %var %uint_7
9545 OpReturn
9546
9547 OpFunctionEnd
9548 )"));
9549 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9550 auto fe = p->function_emitter(100);
9551 EXPECT_TRUE(fe.EmitBody()) << p->error();
9552
9553 auto ast_body = fe.ast_body();
9554 auto got = test::ToString(p->program(), ast_body);
9555 auto* expect = R"(var_1 = 1u;
9556 switch(42) {
9557 case -294967296: {
9558 var_1 = 40u;
9559 }
9560 case 2000000000: {
9561 var_1 = 30u;
9562 }
9563 case 20: {
9564 var_1 = 20u;
9565 }
9566 default: {
9567 }
9568 }
9569 var_1 = 7u;
9570 return;
9571 )";
9572 ASSERT_EQ(expect, got);
9573 }
9574
TEST_F(SpvParserCFGTest,EmitBody_Switch_Case_UintValue)9575 TEST_F(SpvParserCFGTest, EmitBody_Switch_Case_UintValue) {
9576 auto p = parser(test::Assemble(CommonTypes() + R"(
9577 %100 = OpFunction %void None %voidfn
9578
9579 %10 = OpLabel
9580 OpStore %var %uint_1
9581 OpSelectionMerge %99 None
9582 OpSwitch %selector %99 20 %20 2000000000 %30 50 %40
9583
9584 %20 = OpLabel
9585 OpStore %var %uint_20
9586 OpBranch %99
9587
9588 %30 = OpLabel
9589 OpStore %var %uint_30
9590 OpBranch %99
9591
9592 %40 = OpLabel
9593 OpStore %var %uint_40
9594 OpBranch %99
9595
9596 %99 = OpLabel
9597 OpStore %var %uint_7
9598 OpReturn
9599
9600 OpFunctionEnd
9601 )"));
9602 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9603 auto fe = p->function_emitter(100);
9604 EXPECT_TRUE(fe.EmitBody()) << p->error();
9605
9606 auto ast_body = fe.ast_body();
9607 auto got = test::ToString(p->program(), ast_body);
9608 auto* expect = R"(var_1 = 1u;
9609 switch(42u) {
9610 case 50u: {
9611 var_1 = 40u;
9612 }
9613 case 2000000000u: {
9614 var_1 = 30u;
9615 }
9616 case 20u: {
9617 var_1 = 20u;
9618 }
9619 default: {
9620 }
9621 }
9622 var_1 = 7u;
9623 return;
9624 )";
9625 ASSERT_EQ(expect, got);
9626 }
9627
TEST_F(SpvParserCFGTest,EmitBody_Return_TopLevel)9628 TEST_F(SpvParserCFGTest, EmitBody_Return_TopLevel) {
9629 auto p = parser(test::Assemble(CommonTypes() + R"(
9630 %100 = OpFunction %void None %voidfn
9631
9632 %10 = OpLabel
9633 OpReturn
9634
9635 OpFunctionEnd
9636 )"));
9637 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9638 auto fe = p->function_emitter(100);
9639 EXPECT_TRUE(fe.EmitBody()) << p->error();
9640
9641 auto ast_body = fe.ast_body();
9642 auto got = test::ToString(p->program(), ast_body);
9643 auto* expect = R"(return;
9644 )";
9645 ASSERT_EQ(expect, got);
9646 }
9647
TEST_F(SpvParserCFGTest,EmitBody_Return_InsideIf)9648 TEST_F(SpvParserCFGTest, EmitBody_Return_InsideIf) {
9649 auto p = parser(test::Assemble(CommonTypes() + R"(
9650 %100 = OpFunction %void None %voidfn
9651
9652 %10 = OpLabel
9653 OpSelectionMerge %99 None
9654 OpBranchConditional %cond %20 %99
9655
9656 %20 = OpLabel
9657 OpReturn
9658
9659 %99 = OpLabel
9660 OpReturn
9661
9662 OpFunctionEnd
9663 )"));
9664 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9665 auto fe = p->function_emitter(100);
9666 EXPECT_TRUE(fe.EmitBody()) << p->error();
9667
9668 auto ast_body = fe.ast_body();
9669 auto got = test::ToString(p->program(), ast_body);
9670 auto* expect = R"(if (false) {
9671 return;
9672 }
9673 return;
9674 )";
9675 ASSERT_EQ(expect, got);
9676 }
9677
TEST_F(SpvParserCFGTest,EmitBody_Return_InsideLoop)9678 TEST_F(SpvParserCFGTest, EmitBody_Return_InsideLoop) {
9679 auto p = parser(test::Assemble(CommonTypes() + R"(
9680 %100 = OpFunction %void None %voidfn
9681
9682 %10 = OpLabel
9683 OpBranch %20
9684
9685 %20 = OpLabel
9686 OpLoopMerge %99 %80 None
9687 OpBranchConditional %cond %30 %30
9688
9689 %30 = OpLabel
9690 OpReturn
9691
9692 %80 = OpLabel
9693 OpBranch %20
9694
9695 %99 = OpLabel
9696 OpReturn
9697
9698 OpFunctionEnd
9699 )"));
9700 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9701 auto fe = p->function_emitter(100);
9702 EXPECT_TRUE(fe.EmitBody()) << p->error();
9703
9704 auto ast_body = fe.ast_body();
9705 auto got = test::ToString(p->program(), ast_body);
9706 auto* expect = R"(loop {
9707 return;
9708 }
9709 return;
9710 )";
9711 ASSERT_EQ(expect, got);
9712 }
9713
TEST_F(SpvParserCFGTest,EmitBody_ReturnValue_TopLevel)9714 TEST_F(SpvParserCFGTest, EmitBody_ReturnValue_TopLevel) {
9715 auto p = parser(test::Assemble(CommonTypes() + R"(
9716 %200 = OpFunction %uint None %uintfn
9717
9718 %210 = OpLabel
9719 OpReturnValue %uint_2
9720
9721 OpFunctionEnd
9722
9723 %100 = OpFunction %void None %voidfn
9724
9725 %10 = OpLabel
9726 %11 = OpFunctionCall %uint %200
9727 OpReturn
9728
9729 OpFunctionEnd
9730 )"));
9731 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9732 auto fe = p->function_emitter(200);
9733 EXPECT_TRUE(fe.EmitBody()) << p->error();
9734
9735 auto ast_body = fe.ast_body();
9736 auto got = test::ToString(p->program(), ast_body);
9737 auto* expect = R"(return 2u;
9738 )";
9739 ASSERT_EQ(expect, got);
9740 }
9741
TEST_F(SpvParserCFGTest,EmitBody_ReturnValue_InsideIf)9742 TEST_F(SpvParserCFGTest, EmitBody_ReturnValue_InsideIf) {
9743 auto p = parser(test::Assemble(CommonTypes() + R"(
9744 %200 = OpFunction %uint None %uintfn
9745
9746 %210 = OpLabel
9747 OpSelectionMerge %299 None
9748 OpBranchConditional %cond %220 %299
9749
9750 %220 = OpLabel
9751 OpReturnValue %uint_2
9752
9753 %299 = OpLabel
9754 OpReturnValue %uint_3
9755
9756 OpFunctionEnd
9757
9758
9759 %100 = OpFunction %void None %voidfn
9760
9761 %10 = OpLabel
9762 %11 = OpFunctionCall %uint %200
9763 OpReturn
9764
9765 OpFunctionEnd
9766 )"));
9767 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9768 auto fe = p->function_emitter(200);
9769 EXPECT_TRUE(fe.EmitBody()) << p->error();
9770
9771 auto ast_body = fe.ast_body();
9772 auto got = test::ToString(p->program(), ast_body);
9773 auto* expect = R"(if (false) {
9774 return 2u;
9775 }
9776 return 3u;
9777 )";
9778 ASSERT_EQ(expect, got);
9779 }
9780
TEST_F(SpvParserCFGTest,EmitBody_ReturnValue_Loop)9781 TEST_F(SpvParserCFGTest, EmitBody_ReturnValue_Loop) {
9782 auto p = parser(test::Assemble(CommonTypes() + R"(
9783 %200 = OpFunction %uint None %uintfn
9784
9785 %210 = OpLabel
9786 OpBranch %220
9787
9788 %220 = OpLabel
9789 OpLoopMerge %299 %280 None
9790 OpBranchConditional %cond %230 %230
9791
9792 %230 = OpLabel
9793 OpReturnValue %uint_2
9794
9795 %280 = OpLabel
9796 OpBranch %220
9797
9798 %299 = OpLabel
9799 OpReturnValue %uint_3
9800
9801 OpFunctionEnd
9802
9803
9804 %100 = OpFunction %void None %voidfn
9805
9806 %10 = OpLabel
9807 %11 = OpFunctionCall %uint %200
9808 OpReturn
9809
9810 OpFunctionEnd
9811 )"));
9812 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9813 auto fe = p->function_emitter(200);
9814 EXPECT_TRUE(fe.EmitBody()) << p->error();
9815
9816 auto ast_body = fe.ast_body();
9817 auto got = test::ToString(p->program(), ast_body);
9818 auto* expect = R"(loop {
9819 return 2u;
9820 }
9821 return 3u;
9822 )";
9823 ASSERT_EQ(expect, got);
9824 }
9825
TEST_F(SpvParserCFGTest,EmitBody_Kill_TopLevel)9826 TEST_F(SpvParserCFGTest, EmitBody_Kill_TopLevel) {
9827 auto p = parser(test::Assemble(CommonTypes() + R"(
9828 %100 = OpFunction %void None %voidfn
9829
9830 %10 = OpLabel
9831 OpKill
9832
9833 OpFunctionEnd
9834 )"));
9835 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9836 auto fe = p->function_emitter(100);
9837 EXPECT_TRUE(fe.EmitBody()) << p->error();
9838
9839 auto ast_body = fe.ast_body();
9840 auto got = test::ToString(p->program(), ast_body);
9841 auto* expect = R"(discard;
9842 )";
9843 ASSERT_EQ(expect, got);
9844 }
9845
TEST_F(SpvParserCFGTest,EmitBody_Kill_InsideIf)9846 TEST_F(SpvParserCFGTest, EmitBody_Kill_InsideIf) {
9847 auto p = parser(test::Assemble(CommonTypes() + R"(
9848 %100 = OpFunction %void None %voidfn
9849
9850 %10 = OpLabel
9851 OpSelectionMerge %99 None
9852 OpBranchConditional %cond %20 %99
9853
9854 %20 = OpLabel
9855 OpKill
9856
9857 %99 = OpLabel
9858 OpKill
9859
9860 OpFunctionEnd
9861 )"));
9862 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9863 auto fe = p->function_emitter(100);
9864 EXPECT_TRUE(fe.EmitBody()) << p->error();
9865
9866 auto ast_body = fe.ast_body();
9867 auto got = test::ToString(p->program(), ast_body);
9868 auto* expect = R"(if (false) {
9869 discard;
9870 }
9871 discard;
9872 )";
9873 ASSERT_EQ(expect, got);
9874 }
9875
TEST_F(SpvParserCFGTest,EmitBody_Kill_InsideLoop)9876 TEST_F(SpvParserCFGTest, EmitBody_Kill_InsideLoop) {
9877 auto p = parser(test::Assemble(CommonTypes() + R"(
9878 %100 = OpFunction %void None %voidfn
9879
9880 %10 = OpLabel
9881 OpBranch %20
9882
9883 %20 = OpLabel
9884 OpLoopMerge %99 %80 None
9885 OpBranchConditional %cond %30 %30
9886
9887 %30 = OpLabel
9888 OpKill
9889
9890 %80 = OpLabel
9891 OpBranch %20
9892
9893 %99 = OpLabel
9894 OpKill
9895
9896 OpFunctionEnd
9897 )"));
9898 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9899 auto fe = p->function_emitter(100);
9900 EXPECT_TRUE(fe.EmitBody()) << p->error();
9901
9902 auto ast_body = fe.ast_body();
9903 auto got = test::ToString(p->program(), ast_body);
9904 auto* expect = R"(loop {
9905 discard;
9906 }
9907 discard;
9908 )";
9909 ASSERT_EQ(expect, got);
9910 }
9911
TEST_F(SpvParserCFGTest,EmitBody_Unreachable_TopLevel)9912 TEST_F(SpvParserCFGTest, EmitBody_Unreachable_TopLevel) {
9913 auto p = parser(test::Assemble(CommonTypes() + R"(
9914 %100 = OpFunction %void None %voidfn
9915
9916 %10 = OpLabel
9917 OpUnreachable
9918
9919 OpFunctionEnd
9920 )"));
9921 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9922 auto fe = p->function_emitter(100);
9923 EXPECT_TRUE(fe.EmitBody()) << p->error();
9924
9925 auto ast_body = fe.ast_body();
9926 auto got = test::ToString(p->program(), ast_body);
9927 auto* expect = R"(return;
9928 )";
9929 ASSERT_EQ(expect, got);
9930 }
9931
TEST_F(SpvParserCFGTest,EmitBody_Unreachable_InsideIf)9932 TEST_F(SpvParserCFGTest, EmitBody_Unreachable_InsideIf) {
9933 auto p = parser(test::Assemble(CommonTypes() + R"(
9934 %100 = OpFunction %void None %voidfn
9935
9936 %10 = OpLabel
9937 OpSelectionMerge %99 None
9938 OpBranchConditional %cond %20 %99
9939
9940 %20 = OpLabel
9941 OpUnreachable
9942
9943 %99 = OpLabel
9944 OpReturn
9945
9946 OpFunctionEnd
9947 )"));
9948 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9949 auto fe = p->function_emitter(100);
9950 EXPECT_TRUE(fe.EmitBody()) << p->error();
9951
9952 auto ast_body = fe.ast_body();
9953 auto got = test::ToString(p->program(), ast_body);
9954 auto* expect = R"(if (false) {
9955 return;
9956 }
9957 return;
9958 )";
9959 ASSERT_EQ(expect, got);
9960 }
9961
TEST_F(SpvParserCFGTest,EmitBody_Unreachable_InsideLoop)9962 TEST_F(SpvParserCFGTest, EmitBody_Unreachable_InsideLoop) {
9963 auto p = parser(test::Assemble(CommonTypes() + R"(
9964 %100 = OpFunction %void None %voidfn
9965
9966 %10 = OpLabel
9967 OpBranch %20
9968
9969 %20 = OpLabel
9970 OpLoopMerge %99 %80 None
9971 OpBranchConditional %cond %30 %30
9972
9973 %30 = OpLabel
9974 OpUnreachable
9975
9976 %80 = OpLabel
9977 OpBranch %20
9978
9979 %99 = OpLabel
9980 OpReturn
9981
9982 OpFunctionEnd
9983 )"));
9984 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
9985 auto fe = p->function_emitter(100);
9986 EXPECT_TRUE(fe.EmitBody()) << p->error();
9987
9988 auto ast_body = fe.ast_body();
9989 auto got = test::ToString(p->program(), ast_body);
9990 auto* expect = R"(loop {
9991 return;
9992 }
9993 return;
9994 )";
9995 ASSERT_EQ(expect, got);
9996 }
9997
TEST_F(SpvParserCFGTest,EmitBody_Unreachable_InNonVoidFunction)9998 TEST_F(SpvParserCFGTest, EmitBody_Unreachable_InNonVoidFunction) {
9999 auto p = parser(test::Assemble(CommonTypes() + R"(
10000 %200 = OpFunction %uint None %uintfn
10001
10002 %210 = OpLabel
10003 OpUnreachable
10004
10005 OpFunctionEnd
10006
10007 %100 = OpFunction %void None %voidfn
10008
10009 %10 = OpLabel
10010 %11 = OpFunctionCall %uint %200
10011 OpReturn
10012
10013 OpFunctionEnd
10014 )"));
10015 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10016 auto fe = p->function_emitter(200);
10017 EXPECT_TRUE(fe.EmitBody()) << p->error();
10018
10019 auto ast_body = fe.ast_body();
10020 auto got = test::ToString(p->program(), ast_body);
10021 auto* expect = R"(return 0u;
10022 )";
10023 ASSERT_EQ(expect, got);
10024 }
10025
TEST_F(SpvParserCFGTest,EmitBody_Branch_BackEdge_MultiBlockLoop)10026 TEST_F(SpvParserCFGTest, EmitBody_Branch_BackEdge_MultiBlockLoop) {
10027 auto p = parser(test::Assemble(CommonTypes() + R"(
10028 %100 = OpFunction %void None %voidfn
10029
10030 %10 = OpLabel
10031 OpBranch %20
10032
10033 %20 = OpLabel
10034 OpLoopMerge %99 %80 None
10035 OpBranch %80
10036
10037 %80 = OpLabel
10038 OpStore %var %uint_1
10039 OpBranch %20 ; here is one
10040
10041 %99 = OpLabel
10042 OpReturn
10043
10044 OpFunctionEnd
10045 )"));
10046 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10047 auto fe = p->function_emitter(100);
10048 EXPECT_TRUE(fe.EmitBody()) << p->error();
10049
10050 auto ast_body = fe.ast_body();
10051 auto got = test::ToString(p->program(), ast_body);
10052 auto* expect = R"(loop {
10053
10054 continuing {
10055 var_1 = 1u;
10056 }
10057 }
10058 return;
10059 )";
10060 ASSERT_EQ(expect, got);
10061 }
10062
TEST_F(SpvParserCFGTest,EmitBody_Branch_BackEdge_SingleBlockLoop)10063 TEST_F(SpvParserCFGTest, EmitBody_Branch_BackEdge_SingleBlockLoop) {
10064 auto p = parser(test::Assemble(CommonTypes() + R"(
10065 %100 = OpFunction %void None %voidfn
10066
10067 %10 = OpLabel
10068 OpBranch %20
10069
10070 %20 = OpLabel
10071 OpStore %var %uint_1
10072 OpLoopMerge %99 %20 None
10073 OpBranch %20 ; backedge in single block loop
10074
10075 %99 = OpLabel
10076 OpReturn
10077
10078 OpFunctionEnd
10079 )"));
10080 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10081 auto fe = p->function_emitter(100);
10082 EXPECT_TRUE(fe.EmitBody()) << p->error();
10083
10084 auto ast_body = fe.ast_body();
10085 auto got = test::ToString(p->program(), ast_body);
10086 auto* expect = R"(loop {
10087 var_1 = 1u;
10088 }
10089 return;
10090 )";
10091 ASSERT_EQ(expect, got);
10092 }
10093
TEST_F(SpvParserCFGTest,EmitBody_Branch_SwitchBreak_LastInCase)10094 TEST_F(SpvParserCFGTest, EmitBody_Branch_SwitchBreak_LastInCase) {
10095 // When the break is last in its case, we omit it because it's implicit in
10096 // WGSL.
10097 auto p = parser(test::Assemble(CommonTypes() + R"(
10098 %100 = OpFunction %void None %voidfn
10099
10100 %10 = OpLabel
10101 OpStore %var %uint_1
10102 OpSelectionMerge %99 None
10103 OpSwitch %selector %99 20 %20
10104
10105 %20 = OpLabel
10106 OpStore %var %uint_20
10107 OpBranch %99 ; branch to merge. Last in case
10108
10109 %99 = OpLabel
10110 OpStore %var %uint_7
10111 OpReturn
10112
10113 OpFunctionEnd
10114 )"));
10115 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10116 auto fe = p->function_emitter(100);
10117 EXPECT_TRUE(fe.EmitBody()) << p->error();
10118
10119 auto ast_body = fe.ast_body();
10120 auto got = test::ToString(p->program(), ast_body);
10121 auto* expect = R"(var_1 = 1u;
10122 switch(42u) {
10123 case 20u: {
10124 var_1 = 20u;
10125 }
10126 default: {
10127 }
10128 }
10129 var_1 = 7u;
10130 return;
10131 )";
10132 ASSERT_EQ(expect, got);
10133 }
10134
TEST_F(SpvParserCFGTest,EmitBody_Branch_SwitchBreak_NotLastInCase)10135 TEST_F(SpvParserCFGTest, EmitBody_Branch_SwitchBreak_NotLastInCase) {
10136 // When the break is not last in its case, we must emit a 'break'
10137 auto p = parser(test::Assemble(CommonTypes() + R"(
10138 %100 = OpFunction %void None %voidfn
10139
10140 %10 = OpLabel
10141 OpStore %var %uint_1
10142 OpSelectionMerge %99 None
10143 OpSwitch %selector %99 20 %20
10144
10145 %20 = OpLabel
10146 OpStore %var %uint_20
10147 OpSelectionMerge %50 None
10148 OpBranchConditional %cond %40 %50
10149
10150 %40 = OpLabel
10151 OpStore %var %uint_40
10152 OpBranch %99 ; branch to merge. Not last in case
10153
10154 %50 = OpLabel ; inner merge
10155 OpStore %var %uint_50
10156 OpBranch %99
10157
10158 %99 = OpLabel
10159 OpStore %var %uint_7
10160 OpReturn
10161
10162 OpFunctionEnd
10163 )"));
10164 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10165 auto fe = p->function_emitter(100);
10166 EXPECT_TRUE(fe.EmitBody()) << p->error();
10167
10168 auto ast_body = fe.ast_body();
10169 auto got = test::ToString(p->program(), ast_body);
10170 auto* expect = R"(var_1 = 1u;
10171 switch(42u) {
10172 case 20u: {
10173 var_1 = 20u;
10174 if (false) {
10175 var_1 = 40u;
10176 break;
10177 }
10178 var_1 = 50u;
10179 }
10180 default: {
10181 }
10182 }
10183 var_1 = 7u;
10184 return;
10185 )";
10186 ASSERT_EQ(expect, got);
10187 }
10188
TEST_F(SpvParserCFGTest,EmitBody_Branch_LoopBreak_MultiBlockLoop_FromBody)10189 TEST_F(SpvParserCFGTest, EmitBody_Branch_LoopBreak_MultiBlockLoop_FromBody) {
10190 auto p = parser(test::Assemble(CommonTypes() + R"(
10191 %100 = OpFunction %void None %voidfn
10192
10193 %10 = OpLabel
10194 OpBranch %20
10195
10196 %20 = OpLabel
10197 OpLoopMerge %99 %80 None
10198 OpBranch %30
10199
10200 %30 = OpLabel
10201 OpStore %var %uint_1
10202 OpBranch %99 ; break is here
10203
10204 %80 = OpLabel
10205 OpStore %var %uint_2
10206 OpBranch %20 ; backedge
10207
10208 %99 = OpLabel
10209 OpReturn
10210
10211 OpFunctionEnd
10212 )"));
10213 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10214 auto fe = p->function_emitter(100);
10215 EXPECT_TRUE(fe.EmitBody()) << p->error();
10216
10217 auto ast_body = fe.ast_body();
10218 auto got = test::ToString(p->program(), ast_body);
10219 auto* expect = R"(loop {
10220 var_1 = 1u;
10221 break;
10222
10223 continuing {
10224 var_1 = 2u;
10225 }
10226 }
10227 return;
10228 )";
10229 ASSERT_EQ(expect, got);
10230 }
10231
TEST_F(SpvParserCFGTest,EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructConditional)10232 TEST_F(
10233 SpvParserCFGTest,
10234 EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructConditional) {
10235 // This case is invalid because the backedge block doesn't post-dominate the
10236 // continue target.
10237 auto p = parser(test::Assemble(CommonTypes() + R"(
10238 %100 = OpFunction %void None %voidfn
10239
10240 %10 = OpLabel
10241 OpBranch %20
10242
10243 %20 = OpLabel
10244 OpLoopMerge %99 %30 None
10245 OpBranch %30
10246
10247 %30 = OpLabel ; continue target; also an if-header
10248 OpSelectionMerge %80 None
10249 OpBranchConditional %cond %40 %80
10250
10251 %40 = OpLabel
10252 OpBranch %99 ; break, inside a nested if.
10253
10254 %80 = OpLabel
10255 OpBranch %20 ; backedge
10256
10257 %99 = OpLabel
10258 OpReturn
10259
10260 OpFunctionEnd
10261 )"));
10262 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10263 auto fe = p->function_emitter(100);
10264 EXPECT_FALSE(fe.EmitBody()) << p->error();
10265 EXPECT_THAT(p->error(),
10266 Eq("Invalid exit (40->99) from continue construct: 40 is not the "
10267 "last block in the continue construct starting at 30 "
10268 "(violates post-dominance rule)"));
10269 }
10270
TEST_F(SpvParserCFGTest,EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Unconditional)10271 TEST_F(
10272 SpvParserCFGTest,
10273 EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Unconditional) { // NOLINT - line length
10274 auto p = parser(test::Assemble(CommonTypes() + R"(
10275 %100 = OpFunction %void None %voidfn
10276
10277 %10 = OpLabel
10278 OpBranch %20
10279
10280 %20 = OpLabel
10281 OpLoopMerge %99 %80 None
10282 OpBranch %80
10283
10284 %80 = OpLabel ; continue target
10285 OpStore %var %uint_1
10286 OpBranch %99 ; should be a backedge
10287 ; This is invalid as there must be a backedge to the loop header.
10288 ; The SPIR-V allows this and understands how to emit it, even if it's not
10289 ; permitted by the SPIR-V validator.
10290
10291 %99 = OpLabel
10292 OpReturn
10293
10294 OpFunctionEnd
10295 )"));
10296
10297 p->DeliberatelyInvalidSpirv();
10298
10299 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10300 auto fe = p->function_emitter(100);
10301 EXPECT_TRUE(fe.EmitBody()) << p->error();
10302 auto ast_body = fe.ast_body();
10303 auto got = test::ToString(p->program(), ast_body);
10304 auto* expect = R"(loop {
10305
10306 continuing {
10307 var_1 = 1u;
10308 break;
10309 }
10310 }
10311 return;
10312 )";
10313 ASSERT_EQ(expect, got);
10314 }
10315
TEST_F(SpvParserCFGTest,EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Conditional)10316 TEST_F(
10317 SpvParserCFGTest,
10318 EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Conditional) { // NOLINT - line length
10319 auto p = parser(test::Assemble(CommonTypes() + R"(
10320 %100 = OpFunction %void None %voidfn
10321
10322 %10 = OpLabel
10323 OpBranch %20
10324
10325 %20 = OpLabel
10326 OpLoopMerge %99 %80 None
10327 OpBranch %80
10328
10329 %80 = OpLabel ; continue target
10330 OpStore %var %uint_1
10331 OpBranchConditional %cond %20 %99 ; backedge, and exit
10332
10333 %99 = OpLabel
10334 OpReturn
10335
10336 OpFunctionEnd
10337 )"));
10338 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10339 auto fe = p->function_emitter(100);
10340 EXPECT_TRUE(fe.EmitBody()) << p->error();
10341 auto ast_body = fe.ast_body();
10342 auto got = test::ToString(p->program(), ast_body);
10343 auto* expect = R"(loop {
10344
10345 continuing {
10346 var_1 = 1u;
10347 if (false) {
10348 } else {
10349 break;
10350 }
10351 }
10352 }
10353 return;
10354 )";
10355 ASSERT_EQ(expect, got);
10356 }
10357
TEST_F(SpvParserCFGTest,EmitBody_Branch_LoopContinue_LastInLoopConstruct)10358 TEST_F(SpvParserCFGTest, EmitBody_Branch_LoopContinue_LastInLoopConstruct) {
10359 auto p = parser(test::Assemble(CommonTypes() + R"(
10360 %100 = OpFunction %void None %voidfn
10361
10362 %10 = OpLabel
10363 OpBranch %20
10364
10365 %20 = OpLabel
10366 OpLoopMerge %99 %80 None
10367 OpBranch %30
10368
10369 %30 = OpLabel
10370 OpStore %var %uint_1
10371 OpBranch %80 ; continue edge from last block before continue target
10372
10373 %80 = OpLabel ; continue target
10374 OpStore %var %uint_2
10375 OpBranch %20
10376
10377 %99 = OpLabel
10378 OpReturn
10379
10380 OpFunctionEnd
10381 )"));
10382 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10383 auto fe = p->function_emitter(100);
10384 EXPECT_TRUE(fe.EmitBody()) << p->error();
10385 auto ast_body = fe.ast_body();
10386 auto got = test::ToString(p->program(), ast_body);
10387 auto* expect = R"(loop {
10388 var_1 = 1u;
10389
10390 continuing {
10391 var_1 = 2u;
10392 }
10393 }
10394 return;
10395 )";
10396 ASSERT_EQ(expect, got);
10397 }
10398
TEST_F(SpvParserCFGTest,EmitBody_Branch_LoopContinue_BeforeLast)10399 TEST_F(SpvParserCFGTest, EmitBody_Branch_LoopContinue_BeforeLast) {
10400 // By construction, it has to come from nested code.
10401 auto p = parser(test::Assemble(CommonTypes() + R"(
10402 %100 = OpFunction %void None %voidfn
10403
10404 %10 = OpLabel
10405 OpBranch %20
10406
10407 %20 = OpLabel
10408 OpLoopMerge %99 %80 None
10409 OpBranch %30
10410
10411 %30 = OpLabel
10412 OpSelectionMerge %50 None
10413 OpBranchConditional %cond %40 %50
10414
10415 %40 = OpLabel
10416 OpStore %var %uint_1
10417 OpBranch %80 ; continue edge
10418
10419 %50 = OpLabel ; inner selection merge
10420 OpStore %var %uint_2
10421 OpBranch %80
10422
10423 %80 = OpLabel ; continue target
10424 OpStore %var %uint_3
10425 OpBranch %20
10426
10427 %99 = OpLabel
10428 OpReturn
10429
10430 OpFunctionEnd
10431 )"));
10432 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10433 auto fe = p->function_emitter(100);
10434 EXPECT_TRUE(fe.EmitBody()) << p->error();
10435 auto ast_body = fe.ast_body();
10436 auto got = test::ToString(p->program(), ast_body);
10437 auto* expect = R"(loop {
10438 if (false) {
10439 var_1 = 1u;
10440 continue;
10441 }
10442 var_1 = 2u;
10443
10444 continuing {
10445 var_1 = 3u;
10446 }
10447 }
10448 return;
10449 )";
10450 ASSERT_EQ(expect, got);
10451 }
10452
TEST_F(SpvParserCFGTest,EmitBody_Branch_LoopContinue_FromSwitch)10453 TEST_F(SpvParserCFGTest, EmitBody_Branch_LoopContinue_FromSwitch) {
10454 auto p = parser(test::Assemble(CommonTypes() + R"(
10455 %100 = OpFunction %void None %voidfn
10456
10457 %10 = OpLabel
10458 OpStore %var %uint_1
10459 OpBranch %20
10460
10461 %20 = OpLabel
10462 OpStore %var %uint_2
10463 OpLoopMerge %99 %80 None
10464 OpBranch %30
10465
10466 %30 = OpLabel
10467 OpStore %var %uint_3
10468 OpSelectionMerge %79 None
10469 OpSwitch %selector %79 40 %40
10470
10471 %40 = OpLabel
10472 OpStore %var %uint_4
10473 OpBranch %80 ; continue edge
10474
10475 %79 = OpLabel ; switch merge
10476 OpStore %var %uint_5
10477 OpBranch %80
10478
10479 %80 = OpLabel ; continue target
10480 OpStore %var %uint_6
10481 OpBranch %20
10482
10483 %99 = OpLabel
10484 OpStore %var %uint_7
10485 OpReturn
10486
10487 OpFunctionEnd
10488 )"));
10489 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10490 auto fe = p->function_emitter(100);
10491 EXPECT_TRUE(fe.EmitBody()) << p->error();
10492 auto ast_body = fe.ast_body();
10493 auto got = test::ToString(p->program(), ast_body);
10494 auto* expect = R"(var_1 = 1u;
10495 loop {
10496 var_1 = 2u;
10497 var_1 = 3u;
10498 switch(42u) {
10499 case 40u: {
10500 var_1 = 4u;
10501 continue;
10502 }
10503 default: {
10504 }
10505 }
10506 var_1 = 5u;
10507
10508 continuing {
10509 var_1 = 6u;
10510 }
10511 }
10512 var_1 = 7u;
10513 return;
10514 )";
10515 ASSERT_EQ(expect, got);
10516 }
10517
TEST_F(SpvParserCFGTest,EmitBody_Branch_IfBreak_FromThen)10518 TEST_F(SpvParserCFGTest, EmitBody_Branch_IfBreak_FromThen) {
10519 // When unconditional, the if-break must be last in the then clause.
10520 auto p = parser(test::Assemble(CommonTypes() + R"(
10521 %100 = OpFunction %void None %voidfn
10522
10523 %10 = OpLabel
10524 OpSelectionMerge %99 None
10525 OpBranchConditional %cond %30 %99
10526
10527 %30 = OpLabel
10528 OpStore %var %uint_1
10529 OpBranch %99
10530
10531 %99 = OpLabel
10532 OpStore %var %uint_2
10533 OpReturn
10534
10535 OpFunctionEnd
10536 )"));
10537 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10538 auto fe = p->function_emitter(100);
10539 EXPECT_TRUE(fe.EmitBody()) << p->error();
10540 auto ast_body = fe.ast_body();
10541 auto got = test::ToString(p->program(), ast_body);
10542 auto* expect = R"(if (false) {
10543 var_1 = 1u;
10544 }
10545 var_1 = 2u;
10546 return;
10547 )";
10548 ASSERT_EQ(expect, got);
10549 }
10550
TEST_F(SpvParserCFGTest,EmitBody_Branch_IfBreak_FromElse)10551 TEST_F(SpvParserCFGTest, EmitBody_Branch_IfBreak_FromElse) {
10552 // When unconditional, the if-break must be last in the else clause.
10553 auto p = parser(test::Assemble(CommonTypes() + R"(
10554 %100 = OpFunction %void None %voidfn
10555
10556 %10 = OpLabel
10557 OpSelectionMerge %99 None
10558 OpBranchConditional %cond %99 %30
10559
10560 %30 = OpLabel
10561 OpStore %var %uint_1
10562 OpBranch %99
10563
10564 %99 = OpLabel
10565 OpStore %var %uint_2
10566 OpReturn
10567
10568 OpFunctionEnd
10569 )"));
10570 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10571 auto fe = p->function_emitter(100);
10572 EXPECT_TRUE(fe.EmitBody()) << p->error();
10573 auto ast_body = fe.ast_body();
10574 auto got = test::ToString(p->program(), ast_body);
10575 auto* expect = R"(if (false) {
10576 } else {
10577 var_1 = 1u;
10578 }
10579 var_1 = 2u;
10580 return;
10581 )";
10582 ASSERT_EQ(expect, got);
10583 }
10584
TEST_F(SpvParserCFGTest,EmitBody_Branch_Fallthrough)10585 TEST_F(SpvParserCFGTest, EmitBody_Branch_Fallthrough) {
10586 auto p = parser(test::Assemble(CommonTypes() + R"(
10587 %100 = OpFunction %void None %voidfn
10588
10589 %10 = OpLabel
10590 OpStore %var %uint_1
10591 OpSelectionMerge %99 None
10592 OpSwitch %selector %99 20 %20 30 %30
10593
10594 %20 = OpLabel
10595 OpStore %var %uint_20
10596 OpBranch %30 ; uncondtional fallthrough
10597
10598 %30 = OpLabel
10599 OpStore %var %uint_30
10600 OpBranch %99
10601
10602 %99 = OpLabel
10603 OpStore %var %uint_7
10604 OpReturn
10605
10606 OpFunctionEnd
10607 )"));
10608 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10609 auto fe = p->function_emitter(100);
10610 EXPECT_TRUE(fe.EmitBody()) << p->error();
10611
10612 auto ast_body = fe.ast_body();
10613 auto got = test::ToString(p->program(), ast_body);
10614 auto* expect = R"(var_1 = 1u;
10615 switch(42u) {
10616 case 20u: {
10617 var_1 = 20u;
10618 fallthrough;
10619 }
10620 case 30u: {
10621 var_1 = 30u;
10622 }
10623 default: {
10624 }
10625 }
10626 var_1 = 7u;
10627 return;
10628 )";
10629 ASSERT_EQ(expect, got);
10630 }
10631
TEST_F(SpvParserCFGTest,EmitBody_Branch_Forward)10632 TEST_F(SpvParserCFGTest, EmitBody_Branch_Forward) {
10633 auto p = parser(test::Assemble(CommonTypes() + R"(
10634 %100 = OpFunction %void None %voidfn
10635
10636 %10 = OpLabel
10637 OpStore %var %uint_1
10638 OpBranch %99 ; forward
10639
10640 %99 = OpLabel
10641 OpStore %var %uint_2
10642 OpReturn
10643
10644 OpFunctionEnd
10645 )"));
10646 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10647 auto fe = p->function_emitter(100);
10648 EXPECT_TRUE(fe.EmitBody()) << p->error();
10649 auto ast_body = fe.ast_body();
10650 auto got = test::ToString(p->program(), ast_body);
10651 auto* expect = R"(var_1 = 1u;
10652 var_1 = 2u;
10653 return;
10654 )";
10655 ASSERT_EQ(expect, got);
10656 }
10657
10658 // Test matrix for normal OpBranchConditional:
10659 //
10660 // kBack with:
10661 // kBack : TESTED dup general case
10662 // kSwitchBreak: invalid (invalid escape, or invalid backedge)
10663 // kLoopBreak: TESTED in single- and multi block loop configurations
10664 // kLoopContinue: invalid
10665 // If single block loop, then the continue is backward
10666 // If continue is forward, then it's a continue from a
10667 // continue which is also invalid.
10668 // kIfBreak: invalid: loop and if must have distinct merge blocks
10669 // kCaseFallThrough: invalid: loop header must dominate its merge
10670 // kForward: impossible; would be a loop break
10671 //
10672 // kSwitchBreak with:
10673 // kBack : symmetry
10674 // kSwitchBreak: dup general case
10675 // kLoopBreak: invalid; only one kind of break allowed
10676 // kLoopContinue: TESTED
10677 // kIfBreak: invalid: switch and if must have distinct merge blocks
10678 // kCaseFallThrough: TESTED
10679 // kForward: TESTED
10680 //
10681 // kLoopBreak with:
10682 // kBack : symmetry
10683 // kSwitchBreak: symmetry
10684 // kLoopBreak: dup general case
10685 // kLoopContinue: TESTED
10686 // kIfBreak: invalid: switch and if must have distinct merge blocks
10687 // kCaseFallThrough: not possible, because switch break conflicts with loop
10688 // break kForward: TESTED
10689 //
10690 // kLoopContinue with:
10691 // kBack : symmetry
10692 // kSwitchBreak: symmetry
10693 // kLoopBreak: symmetry
10694 // kLoopContinue: dup general case
10695 // kIfBreak: TESTED
10696 // kCaseFallThrough: TESTED
10697 // kForward: TESTED
10698 //
10699 // kIfBreak with:
10700 // kBack : symmetry
10701 // kSwitchBreak: symmetry
10702 // kLoopBreak: symmetry
10703 // kLoopContinue: symmetry
10704 // kIfBreak: dup general case
10705 // kCaseFallThrough: invalid; violates nesting or unique merges
10706 // kForward: invalid: needs a merge instruction
10707 //
10708 // kCaseFallThrough with:
10709 // kBack : symmetry
10710 // kSwitchBreak: symmetry
10711 // kLoopBreak: symmetry
10712 // kLoopContinue: symmetry
10713 // kIfBreak: symmetry
10714 // kCaseFallThrough: dup general case
10715 // kForward: invalid (tested)
10716 //
10717 // kForward with:
10718 // kBack : symmetry
10719 // kSwitchBreak: symmetry
10720 // kLoopBreak: symmetry
10721 // kLoopContinue: symmetry
10722 // kIfBreak: symmetry
10723 // kCaseFallThrough: symmetry
10724 // kForward: dup general case
10725
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Back_SingleBlock_Back)10726 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Back_SingleBlock_Back) {
10727 auto p = parser(test::Assemble(CommonTypes() + R"(
10728 %100 = OpFunction %void None %voidfn
10729
10730 %10 = OpLabel
10731 OpStore %var %uint_0
10732 OpBranch %20
10733
10734 %20 = OpLabel
10735 OpStore %var %uint_1
10736 OpLoopMerge %99 %20 None
10737 OpBranchConditional %cond %20 %20
10738
10739 %99 = OpLabel ; dead
10740 OpStore %var %uint_5
10741 OpReturn
10742
10743 OpFunctionEnd
10744 )"));
10745 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10746 auto fe = p->function_emitter(100);
10747 EXPECT_TRUE(fe.EmitBody()) << p->error();
10748 auto ast_body = fe.ast_body();
10749 auto got = test::ToString(p->program(), ast_body);
10750 auto* expect = R"(var_1 = 0u;
10751 loop {
10752 var_1 = 1u;
10753 }
10754 var_1 = 5u;
10755 return;
10756 )";
10757 ASSERT_EQ(expect, got);
10758 }
10759
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Back_SingleBlock_LoopBreak_OnTrue)10760 TEST_F(SpvParserCFGTest,
10761 EmitBody_BranchConditional_Back_SingleBlock_LoopBreak_OnTrue) {
10762 auto p = parser(test::Assemble(CommonTypes() + R"(
10763 %100 = OpFunction %void None %voidfn
10764
10765 %10 = OpLabel
10766 OpStore %var %uint_0
10767 OpBranch %20
10768
10769 %20 = OpLabel
10770 OpStore %var %uint_1
10771 OpLoopMerge %99 %20 None
10772 OpBranchConditional %cond %99 %20
10773
10774 %99 = OpLabel
10775 OpStore %var %uint_5
10776 OpReturn
10777
10778 OpFunctionEnd
10779 )"));
10780 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10781 auto fe = p->function_emitter(100);
10782 EXPECT_TRUE(fe.EmitBody()) << p->error();
10783 auto ast_body = fe.ast_body();
10784 auto got = test::ToString(p->program(), ast_body);
10785 auto* expect = R"(var_1 = 0u;
10786 loop {
10787 var_1 = 1u;
10788 if (false) {
10789 break;
10790 }
10791 }
10792 var_1 = 5u;
10793 return;
10794 )";
10795 ASSERT_EQ(expect, got);
10796 }
10797
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Back_SingleBlock_LoopBreak_OnFalse)10798 TEST_F(SpvParserCFGTest,
10799 EmitBody_BranchConditional_Back_SingleBlock_LoopBreak_OnFalse) {
10800 auto p = parser(test::Assemble(CommonTypes() + R"(
10801 %100 = OpFunction %void None %voidfn
10802
10803 %10 = OpLabel
10804 OpStore %var %uint_0
10805 OpBranch %20
10806
10807 %20 = OpLabel
10808 OpStore %var %uint_1
10809 OpLoopMerge %99 %20 None
10810 OpBranchConditional %cond %20 %99
10811
10812 %99 = OpLabel
10813 OpStore %var %uint_5
10814 OpReturn
10815
10816 OpFunctionEnd
10817 )"));
10818 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10819 auto fe = p->function_emitter(100);
10820 EXPECT_TRUE(fe.EmitBody()) << p->error();
10821 auto ast_body = fe.ast_body();
10822 auto got = test::ToString(p->program(), ast_body);
10823 auto* expect = R"(var_1 = 0u;
10824 loop {
10825 var_1 = 1u;
10826 if (false) {
10827 } else {
10828 break;
10829 }
10830 }
10831 var_1 = 5u;
10832 return;
10833 )";
10834 ASSERT_EQ(expect, got);
10835 }
10836
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Back_MultiBlock_LoopBreak_OnTrue)10837 TEST_F(SpvParserCFGTest,
10838 EmitBody_BranchConditional_Back_MultiBlock_LoopBreak_OnTrue) {
10839 auto p = parser(test::Assemble(CommonTypes() + R"(
10840 %100 = OpFunction %void None %voidfn
10841
10842 %10 = OpLabel
10843 OpStore %var %uint_0
10844 OpBranch %20
10845
10846 %20 = OpLabel
10847 OpStore %var %uint_1
10848 OpLoopMerge %99 %80 None
10849 OpBranch %80
10850
10851 %80 = OpLabel
10852 OpBranchConditional %cond %99 %20
10853
10854 %99 = OpLabel
10855 OpStore %var %uint_5
10856 OpReturn
10857
10858 OpFunctionEnd
10859 )"));
10860 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10861 auto fe = p->function_emitter(100);
10862 EXPECT_TRUE(fe.EmitBody()) << p->error();
10863 auto ast_body = fe.ast_body();
10864 auto got = test::ToString(p->program(), ast_body);
10865 auto* expect = R"(var_1 = 0u;
10866 loop {
10867 var_1 = 1u;
10868
10869 continuing {
10870 if (false) {
10871 break;
10872 }
10873 }
10874 }
10875 var_1 = 5u;
10876 return;
10877 )";
10878 ASSERT_EQ(expect, got);
10879 }
10880
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Back_MultiBlock_LoopBreak_OnFalse)10881 TEST_F(SpvParserCFGTest,
10882 EmitBody_BranchConditional_Back_MultiBlock_LoopBreak_OnFalse) {
10883 auto p = parser(test::Assemble(CommonTypes() + R"(
10884 %100 = OpFunction %void None %voidfn
10885
10886 %10 = OpLabel
10887 OpStore %var %uint_0
10888 OpBranch %20
10889
10890 %20 = OpLabel
10891 OpStore %var %uint_1
10892 OpLoopMerge %99 %80 None
10893 OpBranch %80
10894
10895 %80 = OpLabel
10896 OpBranchConditional %cond %20 %99
10897
10898 %99 = OpLabel
10899 OpStore %var %uint_5
10900 OpReturn
10901
10902 OpFunctionEnd
10903 )"));
10904 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10905 auto fe = p->function_emitter(100);
10906 EXPECT_TRUE(fe.EmitBody()) << p->error();
10907 auto ast_body = fe.ast_body();
10908 auto got = test::ToString(p->program(), ast_body);
10909 auto* expect = R"(var_1 = 0u;
10910 loop {
10911 var_1 = 1u;
10912
10913 continuing {
10914 if (false) {
10915 } else {
10916 break;
10917 }
10918 }
10919 }
10920 var_1 = 5u;
10921 return;
10922 )";
10923 ASSERT_EQ(expect, got);
10924 }
10925
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_SwitchBreak_SwitchBreak_LastInCase)10926 TEST_F(SpvParserCFGTest,
10927 EmitBody_BranchConditional_SwitchBreak_SwitchBreak_LastInCase) {
10928 // When the break is last in its case, we omit it because it's implicit in
10929 // WGSL.
10930 auto p = parser(test::Assemble(CommonTypes() + R"(
10931 %100 = OpFunction %void None %voidfn
10932
10933 %10 = OpLabel
10934 OpStore %var %uint_1
10935 OpSelectionMerge %99 None
10936 OpSwitch %selector %99 20 %20
10937
10938 %20 = OpLabel
10939 OpStore %var %uint_20
10940 OpBranchConditional %cond2 %99 %99 ; branch to merge. Last in case
10941
10942 %99 = OpLabel
10943 OpStore %var %uint_7
10944 OpReturn
10945
10946 OpFunctionEnd
10947 )"));
10948 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10949 auto fe = p->function_emitter(100);
10950 EXPECT_TRUE(fe.EmitBody()) << p->error();
10951
10952 auto ast_body = fe.ast_body();
10953 auto got = test::ToString(p->program(), ast_body);
10954 auto* expect = R"(var_1 = 1u;
10955 switch(42u) {
10956 case 20u: {
10957 var_1 = 20u;
10958 }
10959 default: {
10960 }
10961 }
10962 var_1 = 7u;
10963 return;
10964 )";
10965 ASSERT_EQ(expect, got);
10966 }
10967
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_SwitchBreak_SwitchBreak_NotLastInCase)10968 TEST_F(SpvParserCFGTest,
10969 EmitBody_BranchConditional_SwitchBreak_SwitchBreak_NotLastInCase) {
10970 // When the break is not last in its case, we must emit a 'break'
10971 auto p = parser(test::Assemble(CommonTypes() + R"(
10972 %100 = OpFunction %void None %voidfn
10973
10974 %10 = OpLabel
10975 OpStore %var %uint_1
10976 OpSelectionMerge %99 None
10977 OpSwitch %selector %99 20 %20
10978
10979 %20 = OpLabel
10980 OpStore %var %uint_20
10981 OpSelectionMerge %50 None
10982 OpBranchConditional %cond %40 %50
10983
10984 %40 = OpLabel
10985 OpStore %var %uint_40
10986 OpBranchConditional %cond2 %99 %99 ; branch to merge. Not last in case
10987
10988 %50 = OpLabel ; inner merge
10989 OpStore %var %uint_50
10990 OpBranch %99
10991
10992 %99 = OpLabel
10993 OpStore %var %uint_7
10994 OpReturn
10995
10996 OpFunctionEnd
10997 )"));
10998 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
10999 auto fe = p->function_emitter(100);
11000 EXPECT_TRUE(fe.EmitBody()) << p->error();
11001
11002 auto ast_body = fe.ast_body();
11003 auto got = test::ToString(p->program(), ast_body);
11004 auto* expect = R"(var_1 = 1u;
11005 switch(42u) {
11006 case 20u: {
11007 var_1 = 20u;
11008 if (false) {
11009 var_1 = 40u;
11010 break;
11011 }
11012 var_1 = 50u;
11013 }
11014 default: {
11015 }
11016 }
11017 var_1 = 7u;
11018 return;
11019 )";
11020 ASSERT_EQ(expect, got);
11021 }
11022
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_SwitchBreak_Continue_OnTrue)11023 TEST_F(SpvParserCFGTest,
11024 EmitBody_BranchConditional_SwitchBreak_Continue_OnTrue) {
11025 auto p = parser(test::Assemble(CommonTypes() + R"(
11026 %100 = OpFunction %void None %voidfn
11027
11028 %10 = OpLabel
11029 OpStore %var %uint_1
11030 OpBranch %20
11031
11032 %20 = OpLabel
11033 OpStore %var %uint_2
11034 OpLoopMerge %99 %80 None
11035 OpBranch %30
11036
11037 %30 = OpLabel
11038 OpStore %var %uint_3
11039 OpSelectionMerge %79 None
11040 OpSwitch %selector %79 40 %40
11041
11042 %40 = OpLabel
11043 OpStore %var %uint_40
11044 OpBranchConditional %cond %80 %79 ; break; continue on true
11045
11046 %79 = OpLabel
11047 OpStore %var %uint_6
11048 OpBranch %80
11049
11050 %80 = OpLabel ; continue target
11051 OpStore %var %uint_7
11052 OpBranch %20
11053
11054 %99 = OpLabel ; loop merge
11055 OpStore %var %uint_8
11056 OpReturn
11057
11058 OpFunctionEnd
11059 )"));
11060 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11061 auto fe = p->function_emitter(100);
11062 EXPECT_TRUE(fe.EmitBody()) << p->error();
11063
11064 auto ast_body = fe.ast_body();
11065 auto got = test::ToString(p->program(), ast_body);
11066 auto* expect = R"(var_1 = 1u;
11067 loop {
11068 var_1 = 2u;
11069 var_1 = 3u;
11070 switch(42u) {
11071 case 40u: {
11072 var_1 = 40u;
11073 if (false) {
11074 continue;
11075 }
11076 }
11077 default: {
11078 }
11079 }
11080 var_1 = 6u;
11081
11082 continuing {
11083 var_1 = 7u;
11084 }
11085 }
11086 var_1 = 8u;
11087 return;
11088 )";
11089 ASSERT_EQ(expect, got);
11090 }
11091
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_SwitchBreak_Continue_OnFalse)11092 TEST_F(SpvParserCFGTest,
11093 EmitBody_BranchConditional_SwitchBreak_Continue_OnFalse) {
11094 auto p = parser(test::Assemble(CommonTypes() + R"(
11095 %100 = OpFunction %void None %voidfn
11096
11097 %10 = OpLabel
11098 OpStore %var %uint_1
11099 OpBranch %20
11100
11101 %20 = OpLabel
11102 OpStore %var %uint_2
11103 OpLoopMerge %99 %80 None
11104 OpBranch %30
11105
11106 %30 = OpLabel
11107 OpStore %var %uint_3
11108 OpSelectionMerge %79 None
11109 OpSwitch %selector %79 40 %40
11110
11111 %40 = OpLabel
11112 OpStore %var %uint_40
11113 OpBranchConditional %cond %79 %80 ; break; continue on false
11114
11115 %79 = OpLabel
11116 OpStore %var %uint_6
11117 OpBranch %80
11118
11119 %80 = OpLabel ; continue target
11120 OpStore %var %uint_7
11121 OpBranch %20
11122
11123 %99 = OpLabel ; loop merge
11124 OpStore %var %uint_8
11125 OpReturn
11126
11127 OpFunctionEnd
11128 )"));
11129 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11130 auto fe = p->function_emitter(100);
11131 EXPECT_TRUE(fe.EmitBody()) << p->error();
11132
11133 auto ast_body = fe.ast_body();
11134 auto got = test::ToString(p->program(), ast_body);
11135 auto* expect = R"(var_1 = 1u;
11136 loop {
11137 var_1 = 2u;
11138 var_1 = 3u;
11139 switch(42u) {
11140 case 40u: {
11141 var_1 = 40u;
11142 if (false) {
11143 } else {
11144 continue;
11145 }
11146 }
11147 default: {
11148 }
11149 }
11150 var_1 = 6u;
11151
11152 continuing {
11153 var_1 = 7u;
11154 }
11155 }
11156 var_1 = 8u;
11157 return;
11158 )";
11159 ASSERT_EQ(expect, got);
11160 }
11161
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_SwitchBreak_Forward_OnTrue)11162 TEST_F(SpvParserCFGTest,
11163 EmitBody_BranchConditional_SwitchBreak_Forward_OnTrue) {
11164 auto p = parser(test::Assemble(CommonTypes() + R"(
11165 %100 = OpFunction %void None %voidfn
11166
11167 %10 = OpLabel
11168 OpStore %var %uint_1
11169 OpSelectionMerge %99 None
11170 OpSwitch %selector %99 20 %20
11171
11172 %20 = OpLabel
11173 OpStore %var %uint_20
11174 OpBranchConditional %cond %30 %99 ; break; forward on true
11175
11176 %30 = OpLabel
11177 OpStore %var %uint_30
11178 OpBranch %99
11179
11180 %99 = OpLabel ; switch merge
11181 OpStore %var %uint_8
11182 OpReturn
11183
11184 OpFunctionEnd
11185 )"));
11186 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11187 auto fe = p->function_emitter(100);
11188 EXPECT_TRUE(fe.EmitBody()) << p->error();
11189 auto ast_body = fe.ast_body();
11190 auto got = test::ToString(p->program(), ast_body);
11191 auto* expect = R"(var_1 = 1u;
11192 switch(42u) {
11193 case 20u: {
11194 var_1 = 20u;
11195 if (false) {
11196 } else {
11197 break;
11198 }
11199 var_1 = 30u;
11200 }
11201 default: {
11202 }
11203 }
11204 var_1 = 8u;
11205 return;
11206 )";
11207 ASSERT_EQ(expect, got);
11208 }
11209
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_SwitchBreak_Forward_OnFalse)11210 TEST_F(SpvParserCFGTest,
11211 EmitBody_BranchConditional_SwitchBreak_Forward_OnFalse) {
11212 auto p = parser(test::Assemble(CommonTypes() + R"(
11213 %100 = OpFunction %void None %voidfn
11214
11215 %10 = OpLabel
11216 OpStore %var %uint_1
11217 OpSelectionMerge %99 None
11218 OpSwitch %selector %99 20 %20
11219
11220 %20 = OpLabel
11221 OpStore %var %uint_20
11222 OpBranchConditional %cond %99 %30 ; break; forward on false
11223
11224 %30 = OpLabel
11225 OpStore %var %uint_30
11226 OpBranch %99
11227
11228 %99 = OpLabel ; switch merge
11229 OpStore %var %uint_8
11230 OpReturn
11231
11232 OpFunctionEnd
11233 )"));
11234 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11235 auto fe = p->function_emitter(100);
11236 EXPECT_TRUE(fe.EmitBody()) << p->error();
11237 auto ast_body = fe.ast_body();
11238 auto got = test::ToString(p->program(), ast_body);
11239 auto* expect = R"(var_1 = 1u;
11240 switch(42u) {
11241 case 20u: {
11242 var_1 = 20u;
11243 if (false) {
11244 break;
11245 }
11246 var_1 = 30u;
11247 }
11248 default: {
11249 }
11250 }
11251 var_1 = 8u;
11252 return;
11253 )";
11254 ASSERT_EQ(expect, got);
11255 }
11256
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_SwitchBreak_Fallthrough_OnTrue)11257 TEST_F(SpvParserCFGTest,
11258 EmitBody_BranchConditional_SwitchBreak_Fallthrough_OnTrue) {
11259 auto p = parser(test::Assemble(CommonTypes() + R"(
11260 %100 = OpFunction %void None %voidfn
11261
11262 %10 = OpLabel
11263 OpStore %var %uint_1
11264 OpSelectionMerge %99 None
11265 OpSwitch %selector %99 20 %20 30 %30
11266
11267 %20 = OpLabel
11268 OpStore %var %uint_20
11269 OpBranchConditional %cond %30 %99; fallthrough on true
11270
11271 %30 = OpLabel
11272 OpStore %var %uint_30
11273 OpBranch %99
11274
11275 %99 = OpLabel
11276 OpStore %var %uint_7
11277 OpReturn
11278
11279 OpFunctionEnd
11280 )"));
11281 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11282 auto fe = p->function_emitter(100);
11283 EXPECT_TRUE(fe.EmitBody()) << p->error();
11284
11285 auto ast_body = fe.ast_body();
11286 auto got = test::ToString(p->program(), ast_body);
11287 auto* expect = R"(var_1 = 1u;
11288 switch(42u) {
11289 case 20u: {
11290 var_1 = 20u;
11291 if (false) {
11292 } else {
11293 break;
11294 }
11295 fallthrough;
11296 }
11297 case 30u: {
11298 var_1 = 30u;
11299 }
11300 default: {
11301 }
11302 }
11303 var_1 = 7u;
11304 return;
11305 )";
11306 ASSERT_EQ(expect, got);
11307 }
11308
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_SwitchBreak_Fallthrough_OnFalse)11309 TEST_F(SpvParserCFGTest,
11310 EmitBody_BranchConditional_SwitchBreak_Fallthrough_OnFalse) {
11311 auto p = parser(test::Assemble(CommonTypes() + R"(
11312 %100 = OpFunction %void None %voidfn
11313
11314 %10 = OpLabel
11315 OpStore %var %uint_1
11316 OpSelectionMerge %99 None
11317 OpSwitch %selector %99 20 %20 30 %30
11318
11319 %20 = OpLabel
11320 OpStore %var %uint_20
11321 OpBranchConditional %cond %99 %30; fallthrough on false
11322
11323 %30 = OpLabel
11324 OpStore %var %uint_30
11325 OpBranch %99
11326
11327 %99 = OpLabel
11328 OpStore %var %uint_7
11329 OpReturn
11330
11331 OpFunctionEnd
11332 )"));
11333 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11334 auto fe = p->function_emitter(100);
11335 EXPECT_TRUE(fe.EmitBody()) << p->error();
11336
11337 auto ast_body = fe.ast_body();
11338 auto got = test::ToString(p->program(), ast_body);
11339 auto* expect = R"(var_1 = 1u;
11340 switch(42u) {
11341 case 20u: {
11342 var_1 = 20u;
11343 if (false) {
11344 break;
11345 }
11346 fallthrough;
11347 }
11348 case 30u: {
11349 var_1 = 30u;
11350 }
11351 default: {
11352 }
11353 }
11354 var_1 = 7u;
11355 return;
11356 )";
11357 ASSERT_EQ(expect, got);
11358 }
11359
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_LoopBreak_SingleBlock_LoopBreak)11360 TEST_F(SpvParserCFGTest,
11361 EmitBody_BranchConditional_LoopBreak_SingleBlock_LoopBreak) {
11362 auto p = parser(test::Assemble(CommonTypes() + R"(
11363 %100 = OpFunction %void None %voidfn
11364
11365 %10 = OpLabel
11366 OpStore %var %uint_0
11367 OpBranch %20
11368
11369 %20 = OpLabel
11370 OpStore %var %uint_1
11371 OpLoopMerge %99 %80 None
11372 OpBranchConditional %cond %99 %99
11373
11374 %80 = OpLabel ; continue target
11375 OpStore %var %uint_4
11376 OpBranch %20
11377
11378 %99 = OpLabel
11379 OpStore %var %uint_5
11380 OpReturn
11381
11382 OpFunctionEnd
11383 )"));
11384 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11385 auto fe = p->function_emitter(100);
11386 EXPECT_TRUE(fe.EmitBody()) << p->error();
11387 auto ast_body = fe.ast_body();
11388 auto got = test::ToString(p->program(), ast_body);
11389 auto* expect = R"(var_1 = 0u;
11390 loop {
11391 var_1 = 1u;
11392 break;
11393
11394 continuing {
11395 var_1 = 4u;
11396 }
11397 }
11398 var_1 = 5u;
11399 return;
11400 )";
11401 ASSERT_EQ(expect, got);
11402 }
11403
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_LoopBreak_MultiBlock_LoopBreak)11404 TEST_F(SpvParserCFGTest,
11405 EmitBody_BranchConditional_LoopBreak_MultiBlock_LoopBreak) {
11406 auto p = parser(test::Assemble(CommonTypes() + R"(
11407 %100 = OpFunction %void None %voidfn
11408
11409 %10 = OpLabel
11410 OpStore %var %uint_0
11411 OpBranch %20
11412
11413 %20 = OpLabel
11414 OpStore %var %uint_1
11415 OpLoopMerge %99 %80 None
11416 OpBranch %30
11417
11418 %30 = OpLabel
11419 OpStore %var %uint_2
11420 OpBranchConditional %cond %99 %99
11421
11422 %80 = OpLabel ; continue target
11423 OpStore %var %uint_4
11424 OpBranch %20
11425
11426 %99 = OpLabel
11427 OpStore %var %uint_5
11428 OpReturn
11429
11430 OpFunctionEnd
11431 )"));
11432 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11433 auto fe = p->function_emitter(100);
11434 EXPECT_TRUE(fe.EmitBody()) << p->error();
11435 auto ast_body = fe.ast_body();
11436 auto got = test::ToString(p->program(), ast_body);
11437 auto* expect = R"(var_1 = 0u;
11438 loop {
11439 var_1 = 1u;
11440 var_1 = 2u;
11441 break;
11442
11443 continuing {
11444 var_1 = 4u;
11445 }
11446 }
11447 var_1 = 5u;
11448 return;
11449 )";
11450 ASSERT_EQ(expect, got);
11451 }
11452
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_LoopBreak_Continue_OnTrue)11453 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_LoopBreak_Continue_OnTrue) {
11454 auto p = parser(test::Assemble(CommonTypes() + R"(
11455 %100 = OpFunction %void None %voidfn
11456
11457 %10 = OpLabel
11458 OpStore %var %uint_0
11459 OpBranch %20
11460
11461 %20 = OpLabel
11462 OpStore %var %uint_1
11463 OpLoopMerge %99 %80 None
11464 OpBranch %25
11465
11466 ; Need this extra selection to make another block between
11467 ; %30 and the continue target, so we actually induce a Continue
11468 ; statement to exist.
11469 %25 = OpLabel
11470 OpSelectionMerge %40 None
11471 OpBranchConditional %cond2 %30 %40
11472
11473 %30 = OpLabel
11474 OpStore %var %uint_2
11475 ; break; continue on true
11476 OpBranchConditional %cond %80 %99
11477
11478 %40 = OpLabel
11479 OpStore %var %uint_3
11480 OpBranch %80
11481
11482 %80 = OpLabel ; continue target
11483 OpStore %var %uint_4
11484 OpBranch %20
11485
11486 %99 = OpLabel
11487 OpStore %var %uint_5
11488 OpReturn
11489
11490 OpFunctionEnd
11491 )"));
11492 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11493 auto fe = p->function_emitter(100);
11494 EXPECT_TRUE(fe.EmitBody()) << p->error();
11495 auto ast_body = fe.ast_body();
11496 auto got = test::ToString(p->program(), ast_body);
11497 auto* expect = R"(var_1 = 0u;
11498 loop {
11499 var_1 = 1u;
11500 if (true) {
11501 var_1 = 2u;
11502 if (false) {
11503 continue;
11504 } else {
11505 break;
11506 }
11507 }
11508 var_1 = 3u;
11509
11510 continuing {
11511 var_1 = 4u;
11512 }
11513 }
11514 var_1 = 5u;
11515 return;
11516 )";
11517 ASSERT_EQ(expect, got);
11518 }
11519
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_LoopBreak_Continue_OnFalse)11520 TEST_F(SpvParserCFGTest,
11521 EmitBody_BranchConditional_LoopBreak_Continue_OnFalse) {
11522 auto p = parser(test::Assemble(CommonTypes() + R"(
11523 %100 = OpFunction %void None %voidfn
11524
11525 %10 = OpLabel
11526 OpStore %var %uint_0
11527 OpBranch %20
11528
11529 %20 = OpLabel
11530 OpStore %var %uint_1
11531 OpLoopMerge %99 %80 None
11532 OpBranch %25
11533
11534 ; Need this extra selection to make another block between
11535 ; %30 and the continue target, so we actually induce a Continue
11536 ; statement to exist.
11537 %25 = OpLabel
11538 OpSelectionMerge %40 None
11539 OpBranchConditional %cond2 %30 %40
11540
11541 %30 = OpLabel
11542 OpStore %var %uint_2
11543 ; break; continue on false
11544 OpBranchConditional %cond %99 %80
11545
11546 %40 = OpLabel
11547 OpStore %var %uint_3
11548 OpBranch %80
11549
11550 %80 = OpLabel ; continue target
11551 OpStore %var %uint_4
11552 OpBranch %20
11553
11554 %99 = OpLabel
11555 OpStore %var %uint_5
11556 OpReturn
11557
11558 OpFunctionEnd
11559 )"));
11560 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11561 auto fe = p->function_emitter(100);
11562 EXPECT_TRUE(fe.EmitBody()) << p->error();
11563 auto ast_body = fe.ast_body();
11564 auto got = test::ToString(p->program(), ast_body);
11565 auto* expect = R"(var_1 = 0u;
11566 loop {
11567 var_1 = 1u;
11568 if (true) {
11569 var_1 = 2u;
11570 if (false) {
11571 break;
11572 } else {
11573 continue;
11574 }
11575 }
11576 var_1 = 3u;
11577
11578 continuing {
11579 var_1 = 4u;
11580 }
11581 }
11582 var_1 = 5u;
11583 return;
11584 )";
11585 ASSERT_EQ(expect, got);
11586 }
11587
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_LoopBreak_Fallthrough_IsError)11588 TEST_F(SpvParserCFGTest,
11589 EmitBody_BranchConditional_LoopBreak_Fallthrough_IsError) {
11590 // It's an error because switch break conflicts with loop break.
11591 auto p = parser(test::Assemble(CommonTypes() + R"(
11592 %100 = OpFunction %void None %voidfn
11593
11594 %10 = OpLabel
11595 OpStore %var %uint_0
11596 OpBranch %20
11597
11598 %20 = OpLabel
11599 OpStore %var %uint_1
11600 OpLoopMerge %99 %80 None
11601 OpBranch %30
11602
11603 %30 = OpLabel
11604 OpSelectionMerge %79 None
11605 OpSwitch %selector %79 40 %40 50 %50
11606
11607 %40 = OpLabel
11608 OpStore %var %uint_40
11609 ; error: branch to 99 bypasses switch's merge
11610 OpBranchConditional %cond %99 %50 ; loop break; fall through
11611
11612 %50 = OpLabel
11613 OpStore %var %uint_50
11614 OpBranch %79
11615
11616 %79 = OpLabel ; switch merge
11617 OpBranch %80
11618
11619 %80 = OpLabel ; continue target
11620 OpStore %var %uint_4
11621 OpBranch %20
11622
11623 %99 = OpLabel
11624 OpStore %var %uint_5
11625 OpReturn
11626
11627 OpFunctionEnd
11628 )"));
11629 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11630 auto fe = p->function_emitter(100);
11631 EXPECT_FALSE(fe.EmitBody()) << p->error();
11632 EXPECT_THAT(
11633 p->error(),
11634 Eq("Branch from block 40 to block 99 is an invalid exit from construct "
11635 "starting at block 30; branch bypasses merge block 79"));
11636 }
11637
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_LoopBreak_Forward_OnTrue)11638 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_LoopBreak_Forward_OnTrue) {
11639 auto p = parser(test::Assemble(CommonTypes() + R"(
11640 %100 = OpFunction %void None %voidfn
11641
11642 %10 = OpLabel
11643 OpStore %var %uint_0
11644 OpBranch %20
11645
11646 %20 = OpLabel
11647 OpStore %var %uint_1
11648 OpLoopMerge %99 %80 None
11649 OpBranch %30
11650
11651 %30 = OpLabel
11652 OpStore %var %uint_2
11653 ; break; forward on true
11654 OpBranchConditional %cond %40 %99
11655
11656 %40 = OpLabel
11657 OpStore %var %uint_3
11658 OpBranch %80
11659
11660 %80 = OpLabel ; continue target
11661 OpStore %var %uint_4
11662 OpBranch %20
11663
11664 %99 = OpLabel
11665 OpStore %var %uint_5
11666 OpReturn
11667
11668 OpFunctionEnd
11669 )"));
11670 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11671 auto fe = p->function_emitter(100);
11672 EXPECT_TRUE(fe.EmitBody()) << p->error();
11673 auto ast_body = fe.ast_body();
11674 auto got = test::ToString(p->program(), ast_body);
11675 auto* expect = R"(var_1 = 0u;
11676 loop {
11677 var_1 = 1u;
11678 var_1 = 2u;
11679 if (false) {
11680 } else {
11681 break;
11682 }
11683 var_1 = 3u;
11684
11685 continuing {
11686 var_1 = 4u;
11687 }
11688 }
11689 var_1 = 5u;
11690 return;
11691 )";
11692 ASSERT_EQ(expect, got);
11693 }
11694
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_LoopBreak_Forward_OnFalse)11695 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_LoopBreak_Forward_OnFalse) {
11696 auto p = parser(test::Assemble(CommonTypes() + R"(
11697 %100 = OpFunction %void None %voidfn
11698
11699 %10 = OpLabel
11700 OpStore %var %uint_0
11701 OpBranch %20
11702
11703 %20 = OpLabel
11704 OpStore %var %uint_1
11705 OpLoopMerge %99 %80 None
11706 OpBranch %30
11707
11708 %30 = OpLabel
11709 OpStore %var %uint_2
11710 ; break; forward on false
11711 OpBranchConditional %cond %99 %40
11712
11713 %40 = OpLabel
11714 OpStore %var %uint_3
11715 OpBranch %80
11716
11717 %80 = OpLabel ; continue target
11718 OpStore %var %uint_4
11719 OpBranch %20
11720
11721 %99 = OpLabel
11722 OpStore %var %uint_5
11723 OpReturn
11724
11725 OpFunctionEnd
11726 )"));
11727 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11728 auto fe = p->function_emitter(100);
11729 EXPECT_TRUE(fe.EmitBody()) << p->error();
11730 auto ast_body = fe.ast_body();
11731 auto got = test::ToString(p->program(), ast_body);
11732 auto* expect = R"(var_1 = 0u;
11733 loop {
11734 var_1 = 1u;
11735 var_1 = 2u;
11736 if (false) {
11737 break;
11738 }
11739 var_1 = 3u;
11740
11741 continuing {
11742 var_1 = 4u;
11743 }
11744 }
11745 var_1 = 5u;
11746 return;
11747 )";
11748 ASSERT_EQ(expect, got);
11749 }
11750
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Continue_Continue_FromHeader)11751 TEST_F(SpvParserCFGTest,
11752 EmitBody_BranchConditional_Continue_Continue_FromHeader) {
11753 auto p = parser(test::Assemble(CommonTypes() + R"(
11754 %100 = OpFunction %void None %voidfn
11755
11756 %10 = OpLabel
11757 OpStore %var %uint_0
11758 OpBranch %20
11759
11760 %20 = OpLabel
11761 OpStore %var %uint_1
11762 OpLoopMerge %99 %80 None
11763 OpBranchConditional %cond %80 %80 ; to continue
11764
11765 %80 = OpLabel ; continue target
11766 OpStore %var %uint_4
11767 OpBranch %20
11768
11769 %99 = OpLabel
11770 OpStore %var %uint_5
11771 OpReturn
11772
11773 OpFunctionEnd
11774 )"));
11775 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11776 auto fe = p->function_emitter(100);
11777 EXPECT_TRUE(fe.EmitBody()) << p->error();
11778 auto ast_body = fe.ast_body();
11779 auto got = test::ToString(p->program(), ast_body);
11780 auto* expect = R"(var_1 = 0u;
11781 loop {
11782 var_1 = 1u;
11783
11784 continuing {
11785 var_1 = 4u;
11786 }
11787 }
11788 var_1 = 5u;
11789 return;
11790 )";
11791 ASSERT_EQ(expect, got);
11792 }
11793
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Continue_Continue_AfterHeader_Unconditional)11794 TEST_F(SpvParserCFGTest,
11795 EmitBody_BranchConditional_Continue_Continue_AfterHeader_Unconditional) {
11796 auto p = parser(test::Assemble(CommonTypes() + R"(
11797 %100 = OpFunction %void None %voidfn
11798
11799 %10 = OpLabel
11800 OpStore %var %uint_0
11801 OpBranch %20
11802
11803 %20 = OpLabel
11804 OpStore %var %uint_1
11805 OpLoopMerge %99 %80 None
11806 OpBranch %30
11807
11808 %30 = OpLabel
11809 OpStore %var %uint_2
11810 OpBranchConditional %cond %80 %80 ; to continue
11811
11812 %80 = OpLabel ; continue target
11813 OpStore %var %uint_4
11814 OpBranch %20
11815
11816 %99 = OpLabel
11817 OpStore %var %uint_5
11818 OpReturn
11819
11820 OpFunctionEnd
11821 )"));
11822 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11823 auto fe = p->function_emitter(100);
11824 EXPECT_TRUE(fe.EmitBody()) << p->error();
11825 auto ast_body = fe.ast_body();
11826 auto got = test::ToString(p->program(), ast_body);
11827 auto* expect = R"(var_1 = 0u;
11828 loop {
11829 var_1 = 1u;
11830 var_1 = 2u;
11831
11832 continuing {
11833 var_1 = 4u;
11834 }
11835 }
11836 var_1 = 5u;
11837 return;
11838 )";
11839 ASSERT_EQ(expect, got);
11840 }
11841
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Continue_Continue_AfterHeader_Conditional)11842 TEST_F(SpvParserCFGTest,
11843 EmitBody_BranchConditional_Continue_Continue_AfterHeader_Conditional) {
11844 // Create an intervening block so we actually require a "continue" statement
11845 // instead of just an adjacent fallthrough to the continue target.
11846 auto p = parser(test::Assemble(CommonTypes() + R"(
11847 %100 = OpFunction %void None %voidfn
11848
11849 %10 = OpLabel
11850 OpStore %var %uint_0
11851 OpBranch %20
11852
11853 %20 = OpLabel
11854 OpStore %var %uint_1
11855 OpLoopMerge %99 %80 None
11856 OpBranch %30
11857
11858 %30 = OpLabel
11859 OpStore %var %uint_2
11860 OpSelectionMerge %50 None
11861 OpBranchConditional %cond2 %40 %50
11862
11863 %40 = OpLabel
11864 OpStore %var %uint_3
11865 OpBranchConditional %cond3 %80 %80 ; to continue
11866
11867 %50 = OpLabel ; merge for selection
11868 OpStore %var %uint_4
11869 OpBranch %80
11870
11871 %80 = OpLabel ; continue target
11872 OpStore %var %uint_5
11873 OpBranch %20
11874
11875 %99 = OpLabel
11876 OpStore %var %uint_6
11877 OpReturn
11878
11879 OpFunctionEnd
11880 )"));
11881 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11882 auto fe = p->function_emitter(100);
11883 EXPECT_TRUE(fe.EmitBody()) << p->error();
11884 auto ast_body = fe.ast_body();
11885 auto got = test::ToString(p->program(), ast_body);
11886 auto* expect = R"(var_1 = 0u;
11887 loop {
11888 var_1 = 1u;
11889 var_1 = 2u;
11890 if (true) {
11891 var_1 = 3u;
11892 continue;
11893 }
11894 var_1 = 4u;
11895
11896 continuing {
11897 var_1 = 5u;
11898 }
11899 }
11900 var_1 = 6u;
11901 return;
11902 )";
11903 ASSERT_EQ(expect, got);
11904 }
11905
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Continue_Continue_AfterHeader_Conditional_EmptyContinuing)11906 TEST_F(
11907 SpvParserCFGTest,
11908 EmitBody_BranchConditional_Continue_Continue_AfterHeader_Conditional_EmptyContinuing) { // NOLINT
11909 // Like the previous tests, but with an empty continuing clause.
11910 auto p = parser(test::Assemble(CommonTypes() + R"(
11911 %100 = OpFunction %void None %voidfn
11912
11913 %10 = OpLabel
11914 OpStore %var %uint_0
11915 OpBranch %20
11916
11917 %20 = OpLabel
11918 OpStore %var %uint_1
11919 OpLoopMerge %99 %80 None
11920 OpBranch %30
11921
11922 %30 = OpLabel
11923 OpStore %var %uint_2
11924 OpSelectionMerge %50 None
11925 OpBranchConditional %cond2 %40 %50
11926
11927 %40 = OpLabel
11928 OpStore %var %uint_3
11929 OpBranchConditional %cond3 %80 %80 ; to continue
11930
11931 %50 = OpLabel ; merge for selection
11932 OpStore %var %uint_4
11933 OpBranch %80
11934
11935 %80 = OpLabel ; continue target
11936 ; no statements here.
11937 OpBranch %20
11938
11939 %99 = OpLabel
11940 OpStore %var %uint_6
11941 OpReturn
11942
11943 OpFunctionEnd
11944 )"));
11945 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
11946 auto fe = p->function_emitter(100);
11947 EXPECT_TRUE(fe.EmitBody()) << p->error();
11948 auto ast_body = fe.ast_body();
11949 auto got = test::ToString(p->program(), ast_body);
11950 auto* expect = R"(var_1 = 0u;
11951 loop {
11952 var_1 = 1u;
11953 var_1 = 2u;
11954 if (true) {
11955 var_1 = 3u;
11956 continue;
11957 }
11958 var_1 = 4u;
11959 }
11960 var_1 = 6u;
11961 return;
11962 )";
11963 ASSERT_EQ(expect, got);
11964 }
11965
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_LoopContinue_FromSwitch)11966 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_LoopContinue_FromSwitch) {
11967 auto p = parser(test::Assemble(CommonTypes() + R"(
11968 %100 = OpFunction %void None %voidfn
11969
11970 %10 = OpLabel
11971 OpStore %var %uint_1
11972 OpBranch %20
11973
11974 %20 = OpLabel
11975 OpStore %var %uint_2
11976 OpLoopMerge %99 %80 None
11977 OpBranch %30
11978
11979 %30 = OpLabel
11980 OpStore %var %uint_3
11981 OpSelectionMerge %79 None
11982 OpSwitch %selector %79 40 %40
11983
11984 %40 = OpLabel
11985 OpStore %var %uint_4
11986 OpBranchConditional %cond2 %80 %80; dup continue edge
11987
11988 %79 = OpLabel ; switch merge
11989 OpStore %var %uint_5
11990 OpBranch %80
11991
11992 %80 = OpLabel ; continue target
11993 OpStore %var %uint_6
11994 OpBranch %20
11995
11996 %99 = OpLabel
11997 OpStore %var %uint_7
11998 OpReturn
11999
12000 OpFunctionEnd
12001 )"));
12002 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12003 auto fe = p->function_emitter(100);
12004 EXPECT_TRUE(fe.EmitBody()) << p->error();
12005 auto ast_body = fe.ast_body();
12006 auto got = test::ToString(p->program(), ast_body);
12007 auto* expect = R"(var_1 = 1u;
12008 loop {
12009 var_1 = 2u;
12010 var_1 = 3u;
12011 switch(42u) {
12012 case 40u: {
12013 var_1 = 4u;
12014 continue;
12015 }
12016 default: {
12017 }
12018 }
12019 var_1 = 5u;
12020
12021 continuing {
12022 var_1 = 6u;
12023 }
12024 }
12025 var_1 = 7u;
12026 return;
12027 )";
12028 ASSERT_EQ(expect, got);
12029 }
12030
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Continue_IfBreak_OnTrue)12031 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Continue_IfBreak_OnTrue) {
12032 auto p = parser(test::Assemble(CommonTypes() + R"(
12033 %100 = OpFunction %void None %voidfn
12034
12035 %10 = OpLabel
12036 OpStore %var %uint_0
12037 OpBranch %20
12038
12039 %20 = OpLabel
12040 OpStore %var %uint_1
12041 OpLoopMerge %99 %80 None
12042 OpBranch %30
12043
12044 %30 = OpLabel
12045 OpStore %var %uint_2
12046 OpSelectionMerge %50 None
12047 OpBranchConditional %cond2 %40 %50
12048
12049 %40 = OpLabel
12050 OpStore %var %uint_3
12051 ; true to if's merge; false to continue
12052 OpBranchConditional %cond3 %50 %80
12053
12054 %50 = OpLabel ; merge for selection
12055 OpStore %var %uint_4
12056 OpBranch %80
12057
12058 %80 = OpLabel ; continue target
12059 OpStore %var %uint_5
12060 OpBranch %20
12061
12062 %99 = OpLabel
12063 OpStore %var %uint_6
12064 OpReturn
12065
12066 OpFunctionEnd
12067 )"));
12068 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12069 auto fe = p->function_emitter(100);
12070 EXPECT_TRUE(fe.EmitBody()) << p->error();
12071 auto ast_body = fe.ast_body();
12072 auto got = test::ToString(p->program(), ast_body);
12073 auto* expect = R"(var_1 = 0u;
12074 loop {
12075 var_1 = 1u;
12076 var_1 = 2u;
12077 if (true) {
12078 var_1 = 3u;
12079 if (false) {
12080 } else {
12081 continue;
12082 }
12083 }
12084 var_1 = 4u;
12085
12086 continuing {
12087 var_1 = 5u;
12088 }
12089 }
12090 var_1 = 6u;
12091 return;
12092 )";
12093 ASSERT_EQ(expect, got);
12094 }
12095
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Continue_IfBreak_OnFalse)12096 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Continue_IfBreak_OnFalse) {
12097 auto p = parser(test::Assemble(CommonTypes() + R"(
12098 %100 = OpFunction %void None %voidfn
12099
12100 %10 = OpLabel
12101 OpStore %var %uint_0
12102 OpBranch %20
12103
12104 %20 = OpLabel
12105 OpStore %var %uint_1
12106 OpLoopMerge %99 %80 None
12107 OpBranch %30
12108
12109 %30 = OpLabel
12110 OpStore %var %uint_2
12111 OpSelectionMerge %50 None
12112 OpBranchConditional %cond2 %40 %50
12113
12114 %40 = OpLabel
12115 OpStore %var %uint_3
12116 ; false to if's merge; true to continue
12117 OpBranchConditional %cond3 %80 %50
12118
12119 %50 = OpLabel ; merge for selection
12120 OpStore %var %uint_4
12121 OpBranch %80
12122
12123 %80 = OpLabel ; continue target
12124 OpStore %var %uint_5
12125 OpBranch %20
12126
12127 %99 = OpLabel
12128 OpStore %var %uint_6
12129 OpReturn
12130
12131 OpFunctionEnd
12132 )"));
12133 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12134 auto fe = p->function_emitter(100);
12135 EXPECT_TRUE(fe.EmitBody()) << p->error();
12136 auto ast_body = fe.ast_body();
12137 auto got = test::ToString(p->program(), ast_body);
12138 auto* expect = R"(var_1 = 0u;
12139 loop {
12140 var_1 = 1u;
12141 var_1 = 2u;
12142 if (true) {
12143 var_1 = 3u;
12144 if (false) {
12145 continue;
12146 }
12147 }
12148 var_1 = 4u;
12149
12150 continuing {
12151 var_1 = 5u;
12152 }
12153 }
12154 var_1 = 6u;
12155 return;
12156 )";
12157 ASSERT_EQ(expect, got);
12158 }
12159
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Continue_Fallthrough_OnTrue)12160 TEST_F(SpvParserCFGTest,
12161 EmitBody_BranchConditional_Continue_Fallthrough_OnTrue) {
12162 auto p = parser(test::Assemble(CommonTypes() + R"(
12163 %100 = OpFunction %void None %voidfn
12164
12165 %10 = OpLabel
12166 OpStore %var %uint_0
12167 OpBranch %20
12168
12169 %20 = OpLabel
12170 OpStore %var %uint_1
12171 OpLoopMerge %99 %80 None
12172 OpBranch %30
12173
12174 %30 = OpLabel
12175 OpStore %var %uint_2
12176 OpSelectionMerge %79 None
12177 OpSwitch %selector %79 40 %40 50 %50
12178
12179 %40 = OpLabel
12180 OpStore %var %uint_40
12181 OpBranchConditional %cond %50 %80 ; loop continue; fall through on true
12182
12183 %50 = OpLabel
12184 OpStore %var %uint_50
12185 OpBranch %79
12186
12187 %79 = OpLabel ; switch merge
12188 OpStore %var %uint_3
12189 OpBranch %80
12190
12191 %80 = OpLabel ; continue target
12192 OpStore %var %uint_4
12193 OpBranch %20
12194
12195 %99 = OpLabel
12196 OpStore %var %uint_5
12197 OpReturn
12198
12199 OpFunctionEnd
12200 )"));
12201 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12202 auto fe = p->function_emitter(100);
12203 EXPECT_TRUE(fe.EmitBody()) << p->error();
12204 auto ast_body = fe.ast_body();
12205 auto got = test::ToString(p->program(), ast_body);
12206 auto* expect = R"(var_1 = 0u;
12207 loop {
12208 var_1 = 1u;
12209 var_1 = 2u;
12210 switch(42u) {
12211 case 40u: {
12212 var_1 = 40u;
12213 if (false) {
12214 } else {
12215 continue;
12216 }
12217 fallthrough;
12218 }
12219 case 50u: {
12220 var_1 = 50u;
12221 }
12222 default: {
12223 }
12224 }
12225 var_1 = 3u;
12226
12227 continuing {
12228 var_1 = 4u;
12229 }
12230 }
12231 var_1 = 5u;
12232 return;
12233 )";
12234 ASSERT_EQ(expect, got);
12235 }
12236
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Continue_Fallthrough_OnFalse)12237 TEST_F(SpvParserCFGTest,
12238 EmitBody_BranchConditional_Continue_Fallthrough_OnFalse) {
12239 auto p = parser(test::Assemble(CommonTypes() + R"(
12240 %100 = OpFunction %void None %voidfn
12241
12242 %10 = OpLabel
12243 OpStore %var %uint_0
12244 OpBranch %20
12245
12246 %20 = OpLabel
12247 OpStore %var %uint_1
12248 OpLoopMerge %99 %80 None
12249 OpBranch %30
12250
12251 %30 = OpLabel
12252 OpStore %var %uint_2
12253 OpSelectionMerge %79 None
12254 OpSwitch %selector %79 40 %40 50 %50
12255
12256 %40 = OpLabel
12257 OpStore %var %uint_40
12258 OpBranchConditional %cond %80 %50 ; loop continue; fall through on false
12259
12260 %50 = OpLabel
12261 OpStore %var %uint_50
12262 OpBranch %79
12263
12264 %79 = OpLabel ; switch merge
12265 OpStore %var %uint_3
12266 OpBranch %80
12267
12268 %80 = OpLabel ; continue target
12269 OpStore %var %uint_4
12270 OpBranch %20
12271
12272 %99 = OpLabel
12273 OpStore %var %uint_5
12274 OpReturn
12275
12276 OpFunctionEnd
12277 )"));
12278 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12279 auto fe = p->function_emitter(100);
12280 EXPECT_TRUE(fe.EmitBody()) << p->error();
12281 auto ast_body = fe.ast_body();
12282 auto got = test::ToString(p->program(), ast_body);
12283 auto* expect = R"(var_1 = 0u;
12284 loop {
12285 var_1 = 1u;
12286 var_1 = 2u;
12287 switch(42u) {
12288 case 40u: {
12289 var_1 = 40u;
12290 if (false) {
12291 continue;
12292 }
12293 fallthrough;
12294 }
12295 case 50u: {
12296 var_1 = 50u;
12297 }
12298 default: {
12299 }
12300 }
12301 var_1 = 3u;
12302
12303 continuing {
12304 var_1 = 4u;
12305 }
12306 }
12307 var_1 = 5u;
12308 return;
12309 )";
12310 ASSERT_EQ(expect, got);
12311 }
12312
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Continue_Forward_OnTrue)12313 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Continue_Forward_OnTrue) {
12314 auto p = parser(test::Assemble(CommonTypes() + R"(
12315 %100 = OpFunction %void None %voidfn
12316
12317 %10 = OpLabel
12318 OpStore %var %uint_0
12319 OpBranch %20
12320
12321 %20 = OpLabel
12322 OpStore %var %uint_1
12323 OpLoopMerge %99 %80 None
12324 OpBranch %30
12325
12326 %30 = OpLabel
12327 OpStore %var %uint_2
12328 ; continue; forward on true
12329 OpBranchConditional %cond %40 %80
12330
12331 %40 = OpLabel
12332 OpStore %var %uint_3
12333 OpBranch %80
12334
12335 %80 = OpLabel ; continue target
12336 OpStore %var %uint_4
12337 OpBranch %20
12338
12339 %99 = OpLabel
12340 OpStore %var %uint_5
12341 OpReturn
12342
12343 OpFunctionEnd
12344 )"));
12345 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12346 auto fe = p->function_emitter(100);
12347 EXPECT_TRUE(fe.EmitBody()) << p->error();
12348 auto ast_body = fe.ast_body();
12349 auto got = test::ToString(p->program(), ast_body);
12350 auto* expect = R"(var_1 = 0u;
12351 loop {
12352 var_1 = 1u;
12353 var_1 = 2u;
12354 if (false) {
12355 } else {
12356 continue;
12357 }
12358 var_1 = 3u;
12359
12360 continuing {
12361 var_1 = 4u;
12362 }
12363 }
12364 var_1 = 5u;
12365 return;
12366 )";
12367 ASSERT_EQ(expect, got);
12368 }
12369
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Continue_Forward_OnFalse)12370 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Continue_Forward_OnFalse) {
12371 auto p = parser(test::Assemble(CommonTypes() + R"(
12372 %100 = OpFunction %void None %voidfn
12373
12374 %10 = OpLabel
12375 OpStore %var %uint_0
12376 OpBranch %20
12377
12378 %20 = OpLabel
12379 OpStore %var %uint_1
12380 OpLoopMerge %99 %80 None
12381 OpBranch %30
12382
12383 %30 = OpLabel
12384 OpStore %var %uint_2
12385 ; continue; forward on true
12386 OpBranchConditional %cond %80 %40
12387
12388 %40 = OpLabel
12389 OpStore %var %uint_3
12390 OpBranch %80
12391
12392 %80 = OpLabel ; continue target
12393 OpStore %var %uint_4
12394 OpBranch %20
12395
12396 %99 = OpLabel
12397 OpStore %var %uint_5
12398 OpReturn
12399
12400 OpFunctionEnd
12401 )"));
12402 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12403 auto fe = p->function_emitter(100);
12404 EXPECT_TRUE(fe.EmitBody()) << p->error();
12405 auto ast_body = fe.ast_body();
12406 auto got = test::ToString(p->program(), ast_body);
12407 auto* expect = R"(var_1 = 0u;
12408 loop {
12409 var_1 = 1u;
12410 var_1 = 2u;
12411 if (false) {
12412 continue;
12413 }
12414 var_1 = 3u;
12415
12416 continuing {
12417 var_1 = 4u;
12418 }
12419 }
12420 var_1 = 5u;
12421 return;
12422 )";
12423 ASSERT_EQ(expect, got);
12424 }
12425
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_IfBreak_IfBreak_Same)12426 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_IfBreak_IfBreak_Same) {
12427 auto p = parser(test::Assemble(CommonTypes() + R"(
12428 %100 = OpFunction %void None %voidfn
12429
12430 %10 = OpLabel
12431 OpStore %var %uint_0
12432 OpSelectionMerge %99 None
12433 OpBranchConditional %cond %99 %99
12434
12435 %20 = OpLabel ; dead
12436 OpStore %var %uint_1
12437 OpBranch %99
12438
12439 %99 = OpLabel
12440 OpStore %var %uint_5
12441 OpReturn
12442
12443 OpFunctionEnd
12444 )"));
12445 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12446 auto fe = p->function_emitter(100);
12447 EXPECT_TRUE(fe.EmitBody()) << p->error();
12448 auto ast_body = fe.ast_body();
12449 auto got = test::ToString(p->program(), ast_body);
12450 auto* expect = R"(var_1 = 0u;
12451 if (false) {
12452 }
12453 var_1 = 5u;
12454 return;
12455 )";
12456 ASSERT_EQ(expect, got);
12457 }
12458
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_IfBreak_IfBreak_DifferentIsError)12459 TEST_F(SpvParserCFGTest,
12460 EmitBody_BranchConditional_IfBreak_IfBreak_DifferentIsError) {
12461 auto p = parser(test::Assemble(CommonTypes() + R"(
12462 %100 = OpFunction %void None %voidfn
12463
12464 %10 = OpLabel
12465 OpStore %var %uint_0
12466 OpSelectionMerge %99 None
12467 OpBranchConditional %cond %20 %99
12468
12469 %20 = OpLabel
12470 OpStore %var %uint_1
12471 OpSelectionMerge %89 None
12472 OpBranchConditional %cond %30 %89
12473
12474 %30 = OpLabel
12475 OpStore %var %uint_2
12476 OpBranchConditional %cond %89 %99 ; invalid divergence
12477
12478 %89 = OpLabel ; inner if-merge
12479 OpBranch %99
12480
12481 %99 = OpLabel ; outer if-merge
12482 OpStore %var %uint_5
12483 OpReturn
12484
12485 OpFunctionEnd
12486 )"));
12487 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12488 auto fe = p->function_emitter(100);
12489 EXPECT_FALSE(FlowClassifyCFGEdges(&fe));
12490 EXPECT_THAT(
12491 p->error(),
12492 Eq("Branch from block 30 to block 99 is an invalid exit from construct "
12493 "starting at block 20; branch bypasses merge block 89"));
12494 }
12495
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Fallthrough_Fallthrough_Same)12496 TEST_F(SpvParserCFGTest,
12497 EmitBody_BranchConditional_Fallthrough_Fallthrough_Same) {
12498 auto p = parser(test::Assemble(CommonTypes() + R"(
12499 %100 = OpFunction %void None %voidfn
12500
12501 %10 = OpLabel
12502 OpStore %var %uint_1
12503 OpSelectionMerge %99 None
12504 OpSwitch %selector %99 20 %20 30 %30
12505
12506 %20 = OpLabel
12507 OpStore %var %uint_20
12508 OpBranchConditional %cond %30 %30 ; fallthrough fallthrough
12509
12510 %30 = OpLabel
12511 OpStore %var %uint_30
12512 OpBranch %99
12513
12514 %99 = OpLabel
12515 OpStore %var %uint_7
12516 OpReturn
12517
12518 OpFunctionEnd
12519 )"));
12520 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12521 auto fe = p->function_emitter(100);
12522 EXPECT_TRUE(fe.EmitBody()) << p->error();
12523
12524 auto ast_body = fe.ast_body();
12525 auto got = test::ToString(p->program(), ast_body);
12526 auto* expect = R"(var_1 = 1u;
12527 switch(42u) {
12528 case 20u: {
12529 var_1 = 20u;
12530 fallthrough;
12531 }
12532 case 30u: {
12533 var_1 = 30u;
12534 }
12535 default: {
12536 }
12537 }
12538 var_1 = 7u;
12539 return;
12540 )";
12541 ASSERT_EQ(expect, got);
12542 }
12543
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Fallthrough_NotLastInCase_IsError)12544 TEST_F(SpvParserCFGTest,
12545 EmitBody_BranchConditional_Fallthrough_NotLastInCase_IsError) {
12546 // See also
12547 // ClassifyCFGEdges_Fallthrough_BranchConditionalWith_Forward_IsError.
12548 auto p = parser(test::Assemble(CommonTypes() + R"(
12549 %100 = OpFunction %void None %voidfn
12550
12551 %10 = OpLabel
12552 OpSelectionMerge %99 None
12553 OpSwitch %selector %99 20 %20 40 %40
12554
12555 %20 = OpLabel ; case 30
12556 OpSelectionMerge %39 None
12557 OpBranchConditional %cond %40 %30 ; fallthrough and forward
12558
12559 %30 = OpLabel
12560 OpBranch %39
12561
12562 %39 = OpLabel
12563 OpBranch %99
12564
12565 %40 = OpLabel ; case 40
12566 OpBranch %99
12567
12568 %99 = OpLabel
12569 OpReturn
12570
12571 OpFunctionEnd
12572 )"));
12573 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12574 auto fe = p->function_emitter(100);
12575 EXPECT_FALSE(fe.EmitBody());
12576 // The weird forward branch pulls in 40 as part of the selection rather than
12577 // as a case.
12578 EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 40, 30, 39, 99));
12579 EXPECT_THAT(
12580 p->error(),
12581 Eq("Branch from 10 to 40 bypasses header 20 (dominance rule violated)"));
12582 }
12583
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Forward_Forward_Same)12584 TEST_F(SpvParserCFGTest, EmitBody_BranchConditional_Forward_Forward_Same) {
12585 auto p = parser(test::Assemble(CommonTypes() + R"(
12586 %100 = OpFunction %void None %voidfn
12587
12588 %10 = OpLabel
12589 OpStore %var %uint_1
12590 OpBranchConditional %cond %99 %99; forward
12591
12592 %99 = OpLabel
12593 OpStore %var %uint_2
12594 OpReturn
12595
12596 OpFunctionEnd
12597 )"));
12598 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12599 auto fe = p->function_emitter(100);
12600 EXPECT_TRUE(fe.EmitBody()) << p->error();
12601 auto ast_body = fe.ast_body();
12602 auto got = test::ToString(p->program(), ast_body);
12603 auto* expect = R"(var_1 = 1u;
12604 var_1 = 2u;
12605 return;
12606 )";
12607 ASSERT_EQ(expect, got);
12608 }
12609
TEST_F(SpvParserCFGTest,EmitBody_BranchConditional_Forward_Forward_Different_IsError)12610 TEST_F(SpvParserCFGTest,
12611 EmitBody_BranchConditional_Forward_Forward_Different_IsError) {
12612 auto p = parser(test::Assemble(CommonTypes() + R"(
12613 %100 = OpFunction %void None %voidfn
12614
12615 %10 = OpLabel
12616 OpBranchConditional %cond %20 %99
12617
12618 %20 = OpLabel
12619 OpReturn
12620
12621 %99 = OpLabel
12622 OpStore %var %uint_2
12623 OpReturn
12624
12625 OpFunctionEnd
12626 )"));
12627 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12628 auto fe = p->function_emitter(100);
12629 EXPECT_FALSE(fe.EmitBody());
12630 EXPECT_THAT(p->error(),
12631 Eq("Control flow diverges at block 10 (to 20, 99) but it is not "
12632 "a structured header (it has no merge instruction)"));
12633 }
12634
TEST_F(SpvParserCFGTest,Switch_NotAsSelectionHeader_Simple)12635 TEST_F(SpvParserCFGTest, Switch_NotAsSelectionHeader_Simple) {
12636 auto assembly = CommonTypes() + R"(
12637 %100 = OpFunction %void None %voidfn
12638
12639 %10 = OpLabel
12640 OpSwitch %uint_0 %99
12641
12642 %99 = OpLabel
12643 OpReturn
12644
12645 OpFunctionEnd
12646 )";
12647 auto p = parser(test::Assemble(assembly));
12648 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12649 auto fe = p->function_emitter(100);
12650 EXPECT_FALSE(fe.EmitBody());
12651 EXPECT_THAT(
12652 p->error(),
12653 HasSubstr("invalid structured control flow: found an OpSwitch that "
12654 "is not preceded by an OpSelectionMerge:"));
12655 }
12656
TEST_F(SpvParserCFGTest,Switch_NotAsSelectionHeader_NonDefaultBranchesAreContinue)12657 TEST_F(SpvParserCFGTest,
12658 Switch_NotAsSelectionHeader_NonDefaultBranchesAreContinue) {
12659 // Adapted from SPIRV-Tools test MissingMergeOneUnseenTargetSwitchBad
12660 auto p = parser(test::Assemble(CommonTypes() + R"(
12661 %100 = OpFunction %void None %voidfn
12662 %entry = OpLabel
12663 OpBranch %loop
12664 %loop = OpLabel
12665 OpLoopMerge %merge %cont None
12666 OpBranchConditional %cond %merge %b1
12667
12668 ; Here an OpSwitch is used with only one "unseen-so-far" target
12669 ; so it doesn't need an OpSelectionMerge.
12670 ; The %cont target can be implemented via "continue". So we can
12671 ; generate:
12672 ; if ((selector != 1) && (selector != 3)) { continue; }
12673 %b1 = OpLabel
12674 OpSwitch %selector %b2 0 %b2 1 %cont 2 %b2 3 %cont
12675
12676 %b2 = OpLabel ; the one unseen target
12677 OpBranch %cont
12678 %cont = OpLabel
12679 OpBranchConditional %cond2 %merge %loop
12680 %merge = OpLabel
12681 OpReturn
12682 OpFunctionEnd
12683 )"));
12684 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12685 auto fe = p->function_emitter(100);
12686 EXPECT_FALSE(fe.EmitBody());
12687 EXPECT_THAT(
12688 p->error(),
12689 HasSubstr("invalid structured control flow: found an OpSwitch that "
12690 "is not preceded by an OpSelectionMerge:"));
12691 }
12692
TEST_F(SpvParserCFGTest,Switch_NotAsSelectionHeader_DefaultBranchIsContinue)12693 TEST_F(SpvParserCFGTest, Switch_NotAsSelectionHeader_DefaultBranchIsContinue) {
12694 // Adapted from SPIRV-Tools test MissingMergeOneUnseenTargetSwitchBad
12695 auto p = parser(test::Assemble(CommonTypes() + R"(
12696 %100 = OpFunction %void None %voidfn
12697 %entry = OpLabel
12698 OpBranch %loop
12699 %loop = OpLabel
12700 OpLoopMerge %merge %cont None
12701 OpBranchConditional %cond %merge %b1
12702
12703 ; Here an OpSwitch is used with only one "unseen-so-far" target
12704 ; so it doesn't need an OpSelectionMerge.
12705 ; The %cont target can be implemented via "continue". So we can
12706 ; generate:
12707 ; if (!(selector == 0 || selector == 2)) {continue;}
12708 %b1 = OpLabel
12709 OpSwitch %selector %cont 0 %b2 1 %cont 2 %b2
12710
12711 %b2 = OpLabel ; the one unseen target
12712 OpBranch %cont
12713 %cont = OpLabel
12714 OpBranchConditional %cond2 %merge %loop
12715 %merge = OpLabel
12716 OpReturn
12717 OpFunctionEnd
12718 )"));
12719 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12720 auto fe = p->function_emitter(100);
12721 EXPECT_FALSE(fe.EmitBody());
12722 EXPECT_THAT(
12723 p->error(),
12724 HasSubstr("invalid structured control flow: found an OpSwitch that "
12725 "is not preceded by an OpSelectionMerge:"));
12726 }
12727
TEST_F(SpvParserCFGTest,SiblingLoopConstruct_Null)12728 TEST_F(SpvParserCFGTest, SiblingLoopConstruct_Null) {
12729 auto assembly = CommonTypes() + R"(
12730 %100 = OpFunction %void None %voidfn
12731 %10 = OpLabel
12732 OpReturn
12733 OpFunctionEnd
12734 )";
12735 auto p = parser(test::Assemble(assembly));
12736 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12737 auto fe = p->function_emitter(100);
12738 EXPECT_EQ(fe.SiblingLoopConstruct(nullptr), nullptr);
12739 }
12740
TEST_F(SpvParserCFGTest,SiblingLoopConstruct_NotAContinue)12741 TEST_F(SpvParserCFGTest, SiblingLoopConstruct_NotAContinue) {
12742 auto assembly = CommonTypes() + R"(
12743 %100 = OpFunction %void None %voidfn
12744
12745 %10 = OpLabel
12746 OpReturn
12747
12748 OpFunctionEnd
12749 )";
12750 auto p = parser(test::Assemble(assembly));
12751 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12752 auto fe = p->function_emitter(100);
12753 ASSERT_TRUE(FlowLabelControlFlowConstructs(&fe)) << p->error();
12754 const Construct* c = fe.GetBlockInfo(10)->construct;
12755 EXPECT_NE(c, nullptr);
12756 EXPECT_EQ(fe.SiblingLoopConstruct(c), nullptr);
12757 }
12758
TEST_F(SpvParserCFGTest,SiblingLoopConstruct_SingleBlockLoop)12759 TEST_F(SpvParserCFGTest, SiblingLoopConstruct_SingleBlockLoop) {
12760 auto assembly = CommonTypes() + R"(
12761 %100 = OpFunction %void None %voidfn
12762
12763 %10 = OpLabel
12764 OpBranch %20
12765
12766 %20 = OpLabel
12767 OpLoopMerge %99 %20 None
12768 OpBranchConditional %cond %20 %99
12769
12770 %99 = OpLabel
12771 OpReturn
12772
12773 OpFunctionEnd
12774 )";
12775 auto p = parser(test::Assemble(assembly));
12776 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12777 auto fe = p->function_emitter(100);
12778 ASSERT_TRUE(FlowLabelControlFlowConstructs(&fe)) << p->error();
12779 const Construct* c = fe.GetBlockInfo(20)->construct;
12780 EXPECT_EQ(c->kind, Construct::kContinue);
12781 EXPECT_EQ(fe.SiblingLoopConstruct(c), nullptr);
12782 }
12783
TEST_F(SpvParserCFGTest,SiblingLoopConstruct_ContinueIsWholeMultiBlockLoop)12784 TEST_F(SpvParserCFGTest, SiblingLoopConstruct_ContinueIsWholeMultiBlockLoop) {
12785 auto assembly = CommonTypes() + R"(
12786 %100 = OpFunction %void None %voidfn
12787
12788 %10 = OpLabel
12789 OpBranch %20
12790
12791 %20 = OpLabel
12792 OpLoopMerge %99 %20 None ; continue target is also loop header
12793 OpBranch %30
12794
12795 %30 = OpLabel
12796 OpBranchConditional %cond %20 %99
12797
12798 %99 = OpLabel
12799 OpReturn
12800
12801 OpFunctionEnd
12802 )";
12803 auto p = parser(test::Assemble(assembly));
12804 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
12805 << p->error() << assembly;
12806 auto fe = p->function_emitter(100);
12807 ASSERT_TRUE(FlowLabelControlFlowConstructs(&fe)) << p->error();
12808 const Construct* c = fe.GetBlockInfo(20)->construct;
12809 EXPECT_EQ(c->kind, Construct::kContinue);
12810 EXPECT_EQ(fe.SiblingLoopConstruct(c), nullptr);
12811 }
12812
TEST_F(SpvParserCFGTest,SiblingLoopConstruct_HasSiblingLoop)12813 TEST_F(SpvParserCFGTest, SiblingLoopConstruct_HasSiblingLoop) {
12814 auto assembly = CommonTypes() + R"(
12815 %100 = OpFunction %void None %voidfn
12816
12817 %10 = OpLabel
12818 OpBranch %20
12819
12820 %20 = OpLabel
12821 OpLoopMerge %99 %30 None
12822 OpBranchConditional %cond %30 %99
12823
12824 %30 = OpLabel ; continue target
12825 OpBranch %20
12826
12827 %99 = OpLabel
12828 OpReturn
12829
12830 OpFunctionEnd
12831 )";
12832 auto p = parser(test::Assemble(assembly));
12833 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12834 auto fe = p->function_emitter(100);
12835 ASSERT_TRUE(FlowLabelControlFlowConstructs(&fe)) << p->error();
12836 const Construct* c = fe.GetBlockInfo(30)->construct;
12837 EXPECT_EQ(c->kind, Construct::kContinue);
12838 EXPECT_THAT(ToString(fe.SiblingLoopConstruct(c)),
12839 Eq("Construct{ Loop [1,2) begin_id:20 end_id:30 depth:1 "
12840 "parent:Function@10 scope:[1,3) in-l:Loop@20 }"));
12841 }
12842
TEST_F(SpvParserCFGTest,EmitBody_IfSelection_TrueBranch_LoopBreak)12843 TEST_F(SpvParserCFGTest, EmitBody_IfSelection_TrueBranch_LoopBreak) {
12844 // crbug.com/tint/243
12845 auto assembly = CommonTypes() + R"(
12846 %100 = OpFunction %void None %voidfn
12847
12848 %5 = OpLabel
12849 OpBranch %10
12850
12851 %10 = OpLabel
12852 OpLoopMerge %99 %90 None
12853 OpBranch %20
12854
12855 %20 = OpLabel
12856 OpSelectionMerge %40 None
12857 OpBranchConditional %cond %99 %30 ; true branch breaking is ok
12858
12859 %30 = OpLabel
12860 OpBranch %40
12861
12862 %40 = OpLabel ; selection merge
12863 OpBranch %90
12864
12865 %90 = OpLabel ; continue target
12866 OpBranch %10 ; backedge
12867
12868 %99 = OpLabel ; loop merge
12869 OpReturn
12870 OpFunctionEnd
12871 )";
12872 auto p = parser(test::Assemble(assembly));
12873 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12874 auto fe = p->function_emitter(100);
12875 EXPECT_TRUE(fe.EmitBody()) << p->error();
12876
12877 auto ast_body = fe.ast_body();
12878 auto got = test::ToString(p->program(), ast_body);
12879 auto* expect = R"(loop {
12880 if (false) {
12881 break;
12882 }
12883 }
12884 return;
12885 )";
12886 ASSERT_EQ(expect, got);
12887 }
12888
TEST_F(SpvParserCFGTest,EmitBody_TrueBranch_LoopContinue)12889 TEST_F(SpvParserCFGTest, EmitBody_TrueBranch_LoopContinue) {
12890 // crbug.com/tint/243
12891 auto assembly = CommonTypes() + R"(
12892 %100 = OpFunction %void None %voidfn
12893
12894 %5 = OpLabel
12895 OpBranch %10
12896
12897 %10 = OpLabel
12898 OpLoopMerge %99 %90 None
12899 OpBranch %20
12900
12901 %20 = OpLabel
12902 OpSelectionMerge %40 None
12903 OpBranchConditional %cond %90 %30 ; true branch continue is ok
12904
12905 %30 = OpLabel
12906 OpBranch %40
12907
12908 %40 = OpLabel ; selection merge
12909 OpBranch %90
12910
12911 %90 = OpLabel ; continue target
12912 OpBranch %10 ; backedge
12913
12914 %99 = OpLabel ; loop merge
12915 OpReturn
12916
12917 OpFunctionEnd
12918 )";
12919 auto p = parser(test::Assemble(assembly));
12920 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12921 auto fe = p->function_emitter(100);
12922 EXPECT_TRUE(fe.EmitBody()) << p->error();
12923 auto ast_body = fe.ast_body();
12924 auto got = test::ToString(p->program(), ast_body);
12925 auto* expect = R"(loop {
12926 if (false) {
12927 continue;
12928 }
12929 }
12930 return;
12931 )";
12932 ASSERT_EQ(expect, got);
12933 }
12934
TEST_F(SpvParserCFGTest,EmitBody_TrueBranch_SwitchBreak)12935 TEST_F(SpvParserCFGTest, EmitBody_TrueBranch_SwitchBreak) {
12936 // crbug.com/tint/243
12937 auto assembly = CommonTypes() + R"(
12938 %100 = OpFunction %void None %voidfn
12939
12940 %10 = OpLabel
12941 OpSelectionMerge %99 None
12942 OpSwitch %uint_20 %99 20 %20
12943
12944 %20 = OpLabel
12945 OpSelectionMerge %40 None
12946 OpBranchConditional %cond %99 %30 ; true branch switch break is ok
12947
12948 %30 = OpLabel
12949 OpBranch %40
12950
12951 %40 = OpLabel ; if-selection merge
12952 OpBranch %99
12953
12954 %99 = OpLabel ; switch merge
12955 OpReturn
12956
12957 OpFunctionEnd
12958 )";
12959 auto p = parser(test::Assemble(assembly));
12960 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
12961 auto fe = p->function_emitter(100);
12962 EXPECT_TRUE(fe.EmitBody()) << p->error();
12963 auto ast_body = fe.ast_body();
12964 auto got = test::ToString(p->program(), ast_body);
12965 auto* expect = R"(switch(20u) {
12966 case 20u: {
12967 if (false) {
12968 break;
12969 }
12970 }
12971 default: {
12972 }
12973 }
12974 return;
12975 )";
12976 ASSERT_EQ(expect, got);
12977 }
12978
TEST_F(SpvParserCFGTest,EmitBody_FalseBranch_LoopBreak)12979 TEST_F(SpvParserCFGTest, EmitBody_FalseBranch_LoopBreak) {
12980 // crbug.com/tint/243
12981 auto assembly = CommonTypes() + R"(
12982 %100 = OpFunction %void None %voidfn
12983
12984 %5 = OpLabel
12985 OpBranch %10
12986
12987 %10 = OpLabel
12988 OpLoopMerge %99 %90 None
12989 OpBranch %20
12990
12991 %20 = OpLabel
12992 OpSelectionMerge %40 None
12993 OpBranchConditional %cond %30 %99 ; false branch breaking is ok
12994
12995 %30 = OpLabel
12996 OpBranch %40
12997
12998 %40 = OpLabel ; selection merge
12999 OpBranch %90
13000
13001 %90 = OpLabel ; continue target
13002 OpBranch %10 ; backedge
13003
13004 %99 = OpLabel ; loop merge
13005 OpReturn
13006
13007 OpFunctionEnd
13008 )";
13009 auto p = parser(test::Assemble(assembly));
13010 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
13011 auto fe = p->function_emitter(100);
13012 EXPECT_TRUE(fe.EmitBody()) << p->error();
13013 auto ast_body = fe.ast_body();
13014 auto got = test::ToString(p->program(), ast_body);
13015 auto* expect = R"(loop {
13016 if (false) {
13017 } else {
13018 break;
13019 }
13020 }
13021 return;
13022 )";
13023 ASSERT_EQ(expect, got);
13024 }
13025
TEST_F(SpvParserCFGTest,EmitBody_FalseBranch_LoopContinue)13026 TEST_F(SpvParserCFGTest, EmitBody_FalseBranch_LoopContinue) {
13027 // crbug.com/tint/243
13028 auto assembly = CommonTypes() + R"(
13029 %100 = OpFunction %void None %voidfn
13030
13031 %5 = OpLabel
13032 OpBranch %10
13033
13034 %10 = OpLabel
13035 OpLoopMerge %99 %90 None
13036 OpBranch %20
13037
13038 %20 = OpLabel
13039 OpSelectionMerge %40 None
13040 OpBranchConditional %cond %30 %90 ; false branch continue is ok
13041
13042 %30 = OpLabel
13043 OpBranch %40
13044
13045 %40 = OpLabel ; selection merge
13046 OpBranch %90
13047
13048 %90 = OpLabel ; continue target
13049 OpBranch %10 ; backedge
13050
13051 %99 = OpLabel ; loop merge
13052 OpReturn
13053
13054 OpFunctionEnd
13055 )";
13056 auto p = parser(test::Assemble(assembly));
13057 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
13058 auto fe = p->function_emitter(100);
13059 EXPECT_TRUE(fe.EmitBody()) << p->error();
13060 auto ast_body = fe.ast_body();
13061 auto got = test::ToString(p->program(), ast_body);
13062 auto* expect = R"(loop {
13063 if (false) {
13064 } else {
13065 continue;
13066 }
13067 }
13068 return;
13069 )";
13070 ASSERT_EQ(expect, got) << p->error();
13071 }
13072
TEST_F(SpvParserCFGTest,EmitBody_FalseBranch_SwitchBreak)13073 TEST_F(SpvParserCFGTest, EmitBody_FalseBranch_SwitchBreak) {
13074 // crbug.com/tint/243
13075 auto assembly = CommonTypes() + R"(
13076 %100 = OpFunction %void None %voidfn
13077
13078 %10 = OpLabel
13079 OpSelectionMerge %99 None
13080 OpSwitch %uint_20 %99 20 %20
13081
13082 %20 = OpLabel
13083 OpSelectionMerge %40 None
13084 OpBranchConditional %cond %30 %99 ; false branch switch break is ok
13085
13086 %30 = OpLabel
13087 OpBranch %40
13088
13089 %40 = OpLabel ; if-selection merge
13090 OpBranch %99
13091
13092 %99 = OpLabel ; switch merge
13093 OpReturn
13094
13095 OpFunctionEnd
13096 )";
13097 auto p = parser(test::Assemble(assembly));
13098 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
13099 auto fe = p->function_emitter(100);
13100 EXPECT_TRUE(fe.EmitBody()) << p->error();
13101 auto ast_body = fe.ast_body();
13102 auto got = test::ToString(p->program(), ast_body);
13103 auto* expect = R"(switch(20u) {
13104 case 20u: {
13105 if (false) {
13106 } else {
13107 break;
13108 }
13109 }
13110 default: {
13111 }
13112 }
13113 return;
13114 )";
13115 ASSERT_EQ(expect, got);
13116 }
13117
TEST_F(SpvParserCFGTest,EmitBody_LoopInternallyDiverge_Simple)13118 TEST_F(SpvParserCFGTest, EmitBody_LoopInternallyDiverge_Simple) {
13119 // crbug.com/tint/524
13120 auto assembly = CommonTypes() + R"(
13121 %100 = OpFunction %void None %voidfn
13122 %10 = OpLabel
13123 OpStore %var %uint_10
13124 OpBranch %20
13125
13126 %20 = OpLabel
13127 OpStore %var %uint_20
13128 OpLoopMerge %99 %90 None
13129 OpBranchConditional %cond %30 %40 ; divergence
13130
13131 %30 = OpLabel
13132 OpStore %var %uint_30
13133 OpBranch %90
13134
13135 %40 = OpLabel
13136 OpStore %var %uint_40
13137 OpBranch %90
13138
13139 %90 = OpLabel ; continue target
13140 OpStore %var %uint_90
13141 OpBranch %20
13142
13143 %99 = OpLabel ; loop merge
13144 OpStore %var %uint_99
13145 OpReturn
13146
13147 OpFunctionEnd
13148 )";
13149 auto p = parser(test::Assemble(assembly));
13150 ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
13151 auto fe = p->function_emitter(100);
13152 EXPECT_TRUE(fe.EmitBody()) << p->error();
13153 auto ast_body = fe.ast_body();
13154 auto got = test::ToString(p->program(), ast_body);
13155 auto* expect = R"(var_1 = 10u;
13156 loop {
13157 var_1 = 20u;
13158 if (false) {
13159 var_1 = 30u;
13160 continue;
13161 } else {
13162 var_1 = 40u;
13163 }
13164
13165 continuing {
13166 var_1 = 90u;
13167 }
13168 }
13169 var_1 = 99u;
13170 return;
13171 )";
13172 ASSERT_EQ(expect, got) << got;
13173 }
13174
TEST_F(SpvParserCFGTest,EmitBody_ContinueFromSingleBlockLoopToOuterLoop_IsError)13175 TEST_F(SpvParserCFGTest,
13176 EmitBody_ContinueFromSingleBlockLoopToOuterLoop_IsError) {
13177 // crbug.com/tint/793
13178 // This is invalid SPIR-V but the validator was only recently upgraded
13179 // to catch it.
13180 auto assembly = CommonTypes() + R"(
13181 %100 = OpFunction %void None %voidfn
13182 %5 = OpLabel
13183 OpBranch %10
13184
13185 %10 = OpLabel ; outer loop header
13186 OpLoopMerge %99 %89 None
13187 OpBranchConditional %cond %99 %20
13188
13189 %20 = OpLabel ; inner loop single block loop
13190 OpLoopMerge %79 %20 None
13191
13192 ; true -> continue to outer loop
13193 ; false -> self-loop
13194 ; The true branch is invalid because a "continue block", i.e. the block
13195 ; containing the branch to the continue target, "is valid only for the
13196 ; innermost loop it is nested inside of".
13197 ; So it can't branch to the continue target of an outer loop.
13198 OpBranchConditional %cond %89 %20
13199
13200 %79 = OpLabel ; merge for outer loop
13201 OpUnreachable
13202
13203 %89 = OpLabel
13204 OpBranch %10 ; backedge for outer loop
13205
13206 %99 = OpLabel ; merge for outer
13207 OpReturn
13208
13209 OpFunctionEnd
13210
13211 )";
13212 auto p = parser(test::Assemble(assembly));
13213 EXPECT_FALSE(p->Parse());
13214 EXPECT_FALSE(p->success());
13215 EXPECT_THAT(p->error(),
13216 HasSubstr("block <ID> 20[%20] exits the continue headed by <ID> "
13217 "20[%20], but not via a structured exit"))
13218 << p->error();
13219 }
13220
13221 } // namespace
13222 } // namespace spirv
13223 } // namespace reader
13224 } // namespace tint
13225