1 // Copyright (c) 2018 Google LLC
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 "source/opt/struct_cfg_analysis.h"
16
17 #include <string>
18
19 #include "gmock/gmock.h"
20 #include "test/opt/assembly_builder.h"
21 #include "test/opt/pass_fixture.h"
22 #include "test/opt/pass_utils.h"
23
24 namespace spvtools {
25 namespace opt {
26 namespace {
27
28 using StructCFGAnalysisTest = PassTest<::testing::Test>;
29 using ::testing::UnorderedElementsAre;
30
TEST_F(StructCFGAnalysisTest,BBInSelection)31 TEST_F(StructCFGAnalysisTest, BBInSelection) {
32 const std::string text = R"(
33 OpCapability Shader
34 OpMemoryModel Logical GLSL450
35 OpEntryPoint Fragment %main "main"
36 %void = OpTypeVoid
37 %bool = OpTypeBool
38 %bool_undef = OpUndef %bool
39 %uint = OpTypeInt 32 0
40 %uint_undef = OpUndef %uint
41 %void_func = OpTypeFunction %void
42 %main = OpFunction %void None %void_func
43 %1 = OpLabel
44 OpSelectionMerge %3 None
45 OpBranchConditional %undef_bool %2 %3
46 %2 = OpLabel
47 OpBranch %3
48 %3 = OpLabel
49 OpReturn
50 OpFunctionEnd
51 )";
52
53 std::unique_ptr<IRContext> context =
54 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
55 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
56
57 StructuredCFGAnalysis analysis(context.get());
58
59 // The header is not in the construct.
60 EXPECT_EQ(analysis.ContainingConstruct(1), 0);
61 EXPECT_EQ(analysis.ContainingLoop(1), 0);
62 EXPECT_EQ(analysis.MergeBlock(1), 0);
63 EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
64 EXPECT_EQ(analysis.ContainingSwitch(1), 0);
65 EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
66 EXPECT_FALSE(analysis.IsContinueBlock(1));
67 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
68 EXPECT_FALSE(analysis.IsInContinueConstruct(1));
69 EXPECT_FALSE(analysis.IsMergeBlock(1));
70
71 // BB2 is in the construct.
72 EXPECT_EQ(analysis.ContainingConstruct(2), 1);
73 EXPECT_EQ(analysis.ContainingLoop(2), 0);
74 EXPECT_EQ(analysis.MergeBlock(2), 3);
75 EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
76 EXPECT_EQ(analysis.ContainingSwitch(2), 0);
77 EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
78 EXPECT_FALSE(analysis.IsContinueBlock(2));
79 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
80 EXPECT_FALSE(analysis.IsInContinueConstruct(2));
81 EXPECT_FALSE(analysis.IsMergeBlock(2));
82
83 // The merge node is not in the construct.
84 EXPECT_EQ(analysis.ContainingConstruct(3), 0);
85 EXPECT_EQ(analysis.ContainingLoop(3), 0);
86 EXPECT_EQ(analysis.MergeBlock(3), 0);
87 EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
88 EXPECT_EQ(analysis.ContainingSwitch(3), 0);
89 EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
90 EXPECT_FALSE(analysis.IsContinueBlock(3));
91 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
92 EXPECT_FALSE(analysis.IsInContinueConstruct(3));
93 EXPECT_TRUE(analysis.IsMergeBlock(3));
94 }
95
TEST_F(StructCFGAnalysisTest,BBInLoop)96 TEST_F(StructCFGAnalysisTest, BBInLoop) {
97 const std::string text = R"(
98 OpCapability Shader
99 OpMemoryModel Logical GLSL450
100 OpEntryPoint Fragment %main "main"
101 %void = OpTypeVoid
102 %bool = OpTypeBool
103 %bool_undef = OpUndef %bool
104 %uint = OpTypeInt 32 0
105 %uint_undef = OpUndef %uint
106 %void_func = OpTypeFunction %void
107 %main = OpFunction %void None %void_func
108 %entry_lab = OpLabel
109 OpBranch %1
110 %1 = OpLabel
111 OpLoopMerge %3 %4 None
112 OpBranchConditional %undef_bool %2 %3
113 %2 = OpLabel
114 OpBranch %3
115 %4 = OpLabel
116 OpBranch %1
117 %3 = OpLabel
118 OpReturn
119 OpFunctionEnd
120 )";
121
122 std::unique_ptr<IRContext> context =
123 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
124 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
125
126 StructuredCFGAnalysis analysis(context.get());
127
128 // The header is not in the construct.
129 EXPECT_EQ(analysis.ContainingConstruct(1), 0);
130 EXPECT_EQ(analysis.ContainingLoop(1), 0);
131 EXPECT_EQ(analysis.MergeBlock(1), 0);
132 EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
133 EXPECT_EQ(analysis.ContainingSwitch(1), 0);
134 EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
135 EXPECT_FALSE(analysis.IsContinueBlock(1));
136 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
137 EXPECT_FALSE(analysis.IsInContinueConstruct(1));
138 EXPECT_FALSE(analysis.IsMergeBlock(1));
139
140 // BB2 is in the construct.
141 EXPECT_EQ(analysis.ContainingConstruct(2), 1);
142 EXPECT_EQ(analysis.ContainingLoop(2), 1);
143 EXPECT_EQ(analysis.MergeBlock(2), 3);
144 EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
145 EXPECT_EQ(analysis.ContainingSwitch(2), 0);
146 EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
147 EXPECT_FALSE(analysis.IsContinueBlock(2));
148 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
149 EXPECT_FALSE(analysis.IsInContinueConstruct(2));
150 EXPECT_FALSE(analysis.IsMergeBlock(2));
151
152 // The merge node is not in the construct.
153 EXPECT_EQ(analysis.ContainingConstruct(3), 0);
154 EXPECT_EQ(analysis.ContainingLoop(3), 0);
155 EXPECT_EQ(analysis.MergeBlock(3), 0);
156 EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
157 EXPECT_EQ(analysis.ContainingSwitch(3), 0);
158 EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
159 EXPECT_FALSE(analysis.IsContinueBlock(3));
160 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
161 EXPECT_FALSE(analysis.IsInContinueConstruct(3));
162 EXPECT_TRUE(analysis.IsMergeBlock(3));
163
164 // The continue block is in the construct.
165 EXPECT_EQ(analysis.ContainingConstruct(4), 1);
166 EXPECT_EQ(analysis.ContainingLoop(4), 1);
167 EXPECT_EQ(analysis.MergeBlock(4), 3);
168 EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
169 EXPECT_EQ(analysis.ContainingSwitch(4), 0);
170 EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
171 EXPECT_TRUE(analysis.IsContinueBlock(4));
172 EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
173 EXPECT_TRUE(analysis.IsInContinueConstruct(4));
174 EXPECT_FALSE(analysis.IsMergeBlock(4));
175 }
176
TEST_F(StructCFGAnalysisTest,SelectionInLoop)177 TEST_F(StructCFGAnalysisTest, SelectionInLoop) {
178 const std::string text = R"(
179 OpCapability Shader
180 OpMemoryModel Logical GLSL450
181 OpEntryPoint Fragment %main "main"
182 %void = OpTypeVoid
183 %bool = OpTypeBool
184 %bool_undef = OpUndef %bool
185 %uint = OpTypeInt 32 0
186 %uint_undef = OpUndef %uint
187 %void_func = OpTypeFunction %void
188 %main = OpFunction %void None %void_func
189 %entry_lab = OpLabel
190 OpBranch %1
191 %1 = OpLabel
192 OpLoopMerge %3 %4 None
193 OpBranchConditional %undef_bool %2 %3
194 %2 = OpLabel
195 OpSelectionMerge %6 None
196 OpBranchConditional %undef_bool %5 %6
197 %5 = OpLabel
198 OpBranch %6
199 %6 = OpLabel
200 OpBranch %3
201 %4 = OpLabel
202 OpBranch %1
203 %3 = OpLabel
204 OpReturn
205 OpFunctionEnd
206 )";
207
208 std::unique_ptr<IRContext> context =
209 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
210 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
211
212 StructuredCFGAnalysis analysis(context.get());
213
214 // The loop header is not in either construct.
215 EXPECT_EQ(analysis.ContainingConstruct(1), 0);
216 EXPECT_EQ(analysis.ContainingLoop(1), 0);
217 EXPECT_EQ(analysis.MergeBlock(1), 0);
218 EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
219 EXPECT_EQ(analysis.ContainingSwitch(1), 0);
220 EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
221 EXPECT_FALSE(analysis.IsContinueBlock(1));
222 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
223 EXPECT_FALSE(analysis.IsInContinueConstruct(1));
224 EXPECT_FALSE(analysis.IsMergeBlock(1));
225
226 // Selection header is in the loop only.
227 EXPECT_EQ(analysis.ContainingConstruct(2), 1);
228 EXPECT_EQ(analysis.ContainingLoop(2), 1);
229 EXPECT_EQ(analysis.MergeBlock(2), 3);
230 EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
231 EXPECT_EQ(analysis.ContainingSwitch(2), 0);
232 EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
233 EXPECT_FALSE(analysis.IsContinueBlock(2));
234 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
235 EXPECT_FALSE(analysis.IsInContinueConstruct(2));
236 EXPECT_FALSE(analysis.IsMergeBlock(2));
237
238 // The loop merge node is not in either construct.
239 EXPECT_EQ(analysis.ContainingConstruct(3), 0);
240 EXPECT_EQ(analysis.ContainingLoop(3), 0);
241 EXPECT_EQ(analysis.MergeBlock(3), 0);
242 EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
243 EXPECT_EQ(analysis.ContainingSwitch(3), 0);
244 EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
245 EXPECT_FALSE(analysis.IsContinueBlock(3));
246 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
247 EXPECT_FALSE(analysis.IsInContinueConstruct(3));
248 EXPECT_TRUE(analysis.IsMergeBlock(3));
249
250 // The continue block is in the loop only.
251 EXPECT_EQ(analysis.ContainingConstruct(4), 1);
252 EXPECT_EQ(analysis.ContainingLoop(4), 1);
253 EXPECT_EQ(analysis.MergeBlock(4), 3);
254 EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
255 EXPECT_EQ(analysis.ContainingSwitch(4), 0);
256 EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
257 EXPECT_TRUE(analysis.IsContinueBlock(4));
258 EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
259 EXPECT_TRUE(analysis.IsInContinueConstruct(4));
260 EXPECT_FALSE(analysis.IsMergeBlock(4));
261
262 // BB5 is in the selection and the loop.
263 EXPECT_EQ(analysis.ContainingConstruct(5), 2);
264 EXPECT_EQ(analysis.ContainingLoop(5), 1);
265 EXPECT_EQ(analysis.MergeBlock(5), 6);
266 EXPECT_EQ(analysis.LoopMergeBlock(5), 3);
267 EXPECT_EQ(analysis.ContainingSwitch(5), 0);
268 EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
269 EXPECT_FALSE(analysis.IsContinueBlock(5));
270 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
271 EXPECT_FALSE(analysis.IsInContinueConstruct(5));
272 EXPECT_FALSE(analysis.IsMergeBlock(5));
273
274 // The selection merge is in the loop only.
275 EXPECT_EQ(analysis.ContainingConstruct(6), 1);
276 EXPECT_EQ(analysis.ContainingLoop(6), 1);
277 EXPECT_EQ(analysis.MergeBlock(6), 3);
278 EXPECT_EQ(analysis.LoopMergeBlock(6), 3);
279 EXPECT_EQ(analysis.ContainingSwitch(6), 0);
280 EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
281 EXPECT_FALSE(analysis.IsContinueBlock(6));
282 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
283 EXPECT_FALSE(analysis.IsInContinueConstruct(6));
284 EXPECT_TRUE(analysis.IsMergeBlock(6));
285 }
286
TEST_F(StructCFGAnalysisTest,LoopInSelection)287 TEST_F(StructCFGAnalysisTest, LoopInSelection) {
288 const std::string text = R"(
289 OpCapability Shader
290 OpMemoryModel Logical GLSL450
291 OpEntryPoint Fragment %main "main"
292 %void = OpTypeVoid
293 %bool = OpTypeBool
294 %bool_undef = OpUndef %bool
295 %uint = OpTypeInt 32 0
296 %uint_undef = OpUndef %uint
297 %void_func = OpTypeFunction %void
298 %main = OpFunction %void None %void_func
299 %entry_lab = OpLabel
300 OpBranch %1
301 %1 = OpLabel
302 OpSelectionMerge %3 None
303 OpBranchConditional %undef_bool %2 %3
304 %2 = OpLabel
305 OpLoopMerge %4 %5 None
306 OpBranchConditional %undef_bool %4 %6
307 %5 = OpLabel
308 OpBranch %2
309 %6 = OpLabel
310 OpBranch %4
311 %4 = OpLabel
312 OpBranch %3
313 %3 = OpLabel
314 OpReturn
315 OpFunctionEnd
316 )";
317
318 std::unique_ptr<IRContext> context =
319 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
320 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
321
322 StructuredCFGAnalysis analysis(context.get());
323
324 // The selection header is not in either construct.
325 EXPECT_EQ(analysis.ContainingConstruct(1), 0);
326 EXPECT_EQ(analysis.ContainingLoop(1), 0);
327 EXPECT_EQ(analysis.MergeBlock(1), 0);
328 EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
329 EXPECT_EQ(analysis.ContainingSwitch(1), 0);
330 EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
331 EXPECT_FALSE(analysis.IsContinueBlock(1));
332 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
333 EXPECT_FALSE(analysis.IsInContinueConstruct(1));
334 EXPECT_FALSE(analysis.IsMergeBlock(1));
335
336 // Loop header is in the selection only.
337 EXPECT_EQ(analysis.ContainingConstruct(2), 1);
338 EXPECT_EQ(analysis.ContainingLoop(2), 0);
339 EXPECT_EQ(analysis.MergeBlock(2), 3);
340 EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
341 EXPECT_EQ(analysis.ContainingSwitch(2), 0);
342 EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
343 EXPECT_FALSE(analysis.IsContinueBlock(2));
344 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
345 EXPECT_FALSE(analysis.IsInContinueConstruct(2));
346 EXPECT_FALSE(analysis.IsMergeBlock(2));
347
348 // The selection merge node is not in either construct.
349 EXPECT_EQ(analysis.ContainingConstruct(3), 0);
350 EXPECT_EQ(analysis.ContainingLoop(3), 0);
351 EXPECT_EQ(analysis.MergeBlock(3), 0);
352 EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
353 EXPECT_EQ(analysis.ContainingSwitch(3), 0);
354 EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
355 EXPECT_FALSE(analysis.IsContinueBlock(3));
356 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
357 EXPECT_FALSE(analysis.IsInContinueConstruct(3));
358 EXPECT_TRUE(analysis.IsMergeBlock(3));
359
360 // The loop merge is in the selection only.
361 EXPECT_EQ(analysis.ContainingConstruct(4), 1);
362 EXPECT_EQ(analysis.ContainingLoop(4), 0);
363 EXPECT_EQ(analysis.MergeBlock(4), 3);
364 EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
365 EXPECT_EQ(analysis.ContainingSwitch(4), 0);
366 EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
367 EXPECT_FALSE(analysis.IsContinueBlock(4));
368 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
369 EXPECT_FALSE(analysis.IsInContinueConstruct(4));
370 EXPECT_TRUE(analysis.IsMergeBlock(4));
371
372 // The loop continue target is in the loop.
373 EXPECT_EQ(analysis.ContainingConstruct(5), 2);
374 EXPECT_EQ(analysis.ContainingLoop(5), 2);
375 EXPECT_EQ(analysis.MergeBlock(5), 4);
376 EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
377 EXPECT_EQ(analysis.ContainingSwitch(5), 0);
378 EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
379 EXPECT_TRUE(analysis.IsContinueBlock(5));
380 EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
381 EXPECT_TRUE(analysis.IsInContinueConstruct(5));
382 EXPECT_FALSE(analysis.IsMergeBlock(5));
383
384 // BB6 is in the loop.
385 EXPECT_EQ(analysis.ContainingConstruct(6), 2);
386 EXPECT_EQ(analysis.ContainingLoop(6), 2);
387 EXPECT_EQ(analysis.MergeBlock(6), 4);
388 EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
389 EXPECT_EQ(analysis.ContainingSwitch(6), 0);
390 EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
391 EXPECT_FALSE(analysis.IsContinueBlock(6));
392 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
393 EXPECT_FALSE(analysis.IsInContinueConstruct(6));
394 EXPECT_FALSE(analysis.IsMergeBlock(6));
395 }
396
TEST_F(StructCFGAnalysisTest,SelectionInSelection)397 TEST_F(StructCFGAnalysisTest, SelectionInSelection) {
398 const std::string text = R"(
399 OpCapability Shader
400 OpMemoryModel Logical GLSL450
401 OpEntryPoint Fragment %main "main"
402 %void = OpTypeVoid
403 %bool = OpTypeBool
404 %bool_undef = OpUndef %bool
405 %uint = OpTypeInt 32 0
406 %uint_undef = OpUndef %uint
407 %void_func = OpTypeFunction %void
408 %main = OpFunction %void None %void_func
409 %entry_lab = OpLabel
410 OpBranch %1
411 %1 = OpLabel
412 OpSelectionMerge %3 None
413 OpBranchConditional %undef_bool %2 %3
414 %2 = OpLabel
415 OpSelectionMerge %4 None
416 OpBranchConditional %undef_bool %4 %5
417 %5 = OpLabel
418 OpBranch %4
419 %4 = OpLabel
420 OpBranch %3
421 %3 = OpLabel
422 OpReturn
423 OpFunctionEnd
424 )";
425
426 std::unique_ptr<IRContext> context =
427 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
428 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
429
430 StructuredCFGAnalysis analysis(context.get());
431
432 // The outer selection header is not in either construct.
433 EXPECT_EQ(analysis.ContainingConstruct(1), 0);
434 EXPECT_EQ(analysis.ContainingLoop(1), 0);
435 EXPECT_EQ(analysis.MergeBlock(1), 0);
436 EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
437 EXPECT_EQ(analysis.ContainingSwitch(1), 0);
438 EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
439 EXPECT_FALSE(analysis.IsContinueBlock(1));
440 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
441 EXPECT_FALSE(analysis.IsInContinueConstruct(1));
442 EXPECT_FALSE(analysis.IsMergeBlock(1));
443
444 // The inner header is in the outer selection.
445 EXPECT_EQ(analysis.ContainingConstruct(2), 1);
446 EXPECT_EQ(analysis.ContainingLoop(2), 0);
447 EXPECT_EQ(analysis.MergeBlock(2), 3);
448 EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
449 EXPECT_EQ(analysis.ContainingSwitch(2), 0);
450 EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
451 EXPECT_FALSE(analysis.IsContinueBlock(2));
452 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
453 EXPECT_FALSE(analysis.IsInContinueConstruct(2));
454 EXPECT_FALSE(analysis.IsMergeBlock(2));
455
456 // The outer merge node is not in either construct.
457 EXPECT_EQ(analysis.ContainingConstruct(3), 0);
458 EXPECT_EQ(analysis.ContainingLoop(3), 0);
459 EXPECT_EQ(analysis.MergeBlock(3), 0);
460 EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
461 EXPECT_EQ(analysis.ContainingSwitch(3), 0);
462 EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
463 EXPECT_FALSE(analysis.IsContinueBlock(3));
464 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
465 EXPECT_FALSE(analysis.IsInContinueConstruct(3));
466 EXPECT_TRUE(analysis.IsMergeBlock(3));
467
468 // The inner merge is in the outer selection.
469 EXPECT_EQ(analysis.ContainingConstruct(4), 1);
470 EXPECT_EQ(analysis.ContainingLoop(4), 0);
471 EXPECT_EQ(analysis.MergeBlock(4), 3);
472 EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
473 EXPECT_EQ(analysis.ContainingSwitch(4), 0);
474 EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
475 EXPECT_FALSE(analysis.IsContinueBlock(4));
476 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
477 EXPECT_FALSE(analysis.IsInContinueConstruct(4));
478 EXPECT_TRUE(analysis.IsMergeBlock(4));
479
480 // BB5 is in the inner selection.
481 EXPECT_EQ(analysis.ContainingConstruct(5), 2);
482 EXPECT_EQ(analysis.ContainingLoop(5), 0);
483 EXPECT_EQ(analysis.MergeBlock(5), 4);
484 EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
485 EXPECT_EQ(analysis.ContainingSwitch(5), 0);
486 EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
487 EXPECT_FALSE(analysis.IsContinueBlock(5));
488 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
489 EXPECT_FALSE(analysis.IsInContinueConstruct(5));
490 EXPECT_FALSE(analysis.IsMergeBlock(5));
491 }
492
TEST_F(StructCFGAnalysisTest,LoopInLoop)493 TEST_F(StructCFGAnalysisTest, LoopInLoop) {
494 const std::string text = R"(
495 OpCapability Shader
496 OpMemoryModel Logical GLSL450
497 OpEntryPoint Fragment %main "main"
498 %void = OpTypeVoid
499 %bool = OpTypeBool
500 %bool_undef = OpUndef %bool
501 %uint = OpTypeInt 32 0
502 %uint_undef = OpUndef %uint
503 %void_func = OpTypeFunction %void
504 %main = OpFunction %void None %void_func
505 %entry_lab = OpLabel
506 OpBranch %1
507 %1 = OpLabel
508 OpLoopMerge %3 %7 None
509 OpBranchConditional %undef_bool %2 %3
510 %2 = OpLabel
511 OpLoopMerge %4 %5 None
512 OpBranchConditional %undef_bool %4 %6
513 %5 = OpLabel
514 OpBranch %2
515 %6 = OpLabel
516 OpBranch %4
517 %4 = OpLabel
518 OpBranch %3
519 %7 = OpLabel
520 OpBranch %1
521 %3 = OpLabel
522 OpReturn
523 OpFunctionEnd
524 )";
525
526 std::unique_ptr<IRContext> context =
527 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
528 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
529
530 StructuredCFGAnalysis analysis(context.get());
531
532 // The outer loop header is not in either construct.
533 EXPECT_EQ(analysis.ContainingConstruct(1), 0);
534 EXPECT_EQ(analysis.ContainingLoop(1), 0);
535 EXPECT_EQ(analysis.MergeBlock(1), 0);
536 EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
537 EXPECT_EQ(analysis.ContainingSwitch(1), 0);
538 EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
539 EXPECT_FALSE(analysis.IsContinueBlock(1));
540 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
541 EXPECT_FALSE(analysis.IsInContinueConstruct(1));
542 EXPECT_FALSE(analysis.IsMergeBlock(1));
543
544 // The inner loop header is in the outer loop.
545 EXPECT_EQ(analysis.ContainingConstruct(2), 1);
546 EXPECT_EQ(analysis.ContainingLoop(2), 1);
547 EXPECT_EQ(analysis.MergeBlock(2), 3);
548 EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
549 EXPECT_EQ(analysis.ContainingSwitch(2), 0);
550 EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
551 EXPECT_FALSE(analysis.IsContinueBlock(2));
552 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
553 EXPECT_FALSE(analysis.IsInContinueConstruct(2));
554 EXPECT_FALSE(analysis.IsMergeBlock(2));
555
556 // The outer merge node is not in either construct.
557 EXPECT_EQ(analysis.ContainingConstruct(3), 0);
558 EXPECT_EQ(analysis.ContainingLoop(3), 0);
559 EXPECT_EQ(analysis.MergeBlock(3), 0);
560 EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
561 EXPECT_EQ(analysis.ContainingSwitch(3), 0);
562 EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
563 EXPECT_FALSE(analysis.IsContinueBlock(3));
564 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
565 EXPECT_FALSE(analysis.IsInContinueConstruct(3));
566 EXPECT_TRUE(analysis.IsMergeBlock(3));
567
568 // The inner merge is in the outer loop.
569 EXPECT_EQ(analysis.ContainingConstruct(4), 1);
570 EXPECT_EQ(analysis.ContainingLoop(4), 1);
571 EXPECT_EQ(analysis.MergeBlock(4), 3);
572 EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
573 EXPECT_EQ(analysis.ContainingSwitch(4), 0);
574 EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
575 EXPECT_FALSE(analysis.IsContinueBlock(4));
576 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
577 EXPECT_FALSE(analysis.IsInContinueConstruct(4));
578 EXPECT_TRUE(analysis.IsMergeBlock(4));
579
580 // The inner continue target is in the inner loop.
581 EXPECT_EQ(analysis.ContainingConstruct(5), 2);
582 EXPECT_EQ(analysis.ContainingLoop(5), 2);
583 EXPECT_EQ(analysis.MergeBlock(5), 4);
584 EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
585 EXPECT_EQ(analysis.ContainingSwitch(5), 0);
586 EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
587 EXPECT_TRUE(analysis.IsContinueBlock(5));
588 EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
589 EXPECT_TRUE(analysis.IsInContinueConstruct(5));
590 EXPECT_FALSE(analysis.IsMergeBlock(5));
591
592 // BB6 is in the loop.
593 EXPECT_EQ(analysis.ContainingConstruct(6), 2);
594 EXPECT_EQ(analysis.ContainingLoop(6), 2);
595 EXPECT_EQ(analysis.MergeBlock(6), 4);
596 EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
597 EXPECT_EQ(analysis.ContainingSwitch(6), 0);
598 EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
599 EXPECT_FALSE(analysis.IsContinueBlock(6));
600 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
601 EXPECT_FALSE(analysis.IsInContinueConstruct(6));
602 EXPECT_FALSE(analysis.IsMergeBlock(6));
603
604 // The outer continue target is in the outer loop.
605 EXPECT_EQ(analysis.ContainingConstruct(7), 1);
606 EXPECT_EQ(analysis.ContainingLoop(7), 1);
607 EXPECT_EQ(analysis.MergeBlock(7), 3);
608 EXPECT_EQ(analysis.LoopMergeBlock(7), 3);
609 EXPECT_EQ(analysis.ContainingSwitch(7), 0);
610 EXPECT_EQ(analysis.SwitchMergeBlock(7), 0);
611 EXPECT_TRUE(analysis.IsContinueBlock(7));
612 EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(7));
613 EXPECT_TRUE(analysis.IsInContinueConstruct(7));
614 EXPECT_FALSE(analysis.IsMergeBlock(7));
615 }
616
TEST_F(StructCFGAnalysisTest,KernelTest)617 TEST_F(StructCFGAnalysisTest, KernelTest) {
618 const std::string text = R"(
619 OpCapability Kernel
620 OpMemoryModel Logical GLSL450
621 OpEntryPoint Fragment %main "main"
622 %void = OpTypeVoid
623 %bool = OpTypeBool
624 %bool_undef = OpUndef %bool
625 %void_func = OpTypeFunction %void
626 %main = OpFunction %void None %void_func
627 %1 = OpLabel
628 OpBranchConditional %undef_bool %2 %3
629 %2 = OpLabel
630 OpBranch %3
631 %3 = OpLabel
632 OpReturn
633 OpFunctionEnd
634 )";
635
636 std::unique_ptr<IRContext> context =
637 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
638 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
639
640 StructuredCFGAnalysis analysis(context.get());
641
642 // No structured control flow, so none of the basic block are in any
643 // construct.
644 for (uint32_t i = 1; i <= 3; i++) {
645 EXPECT_EQ(analysis.ContainingConstruct(i), 0);
646 EXPECT_EQ(analysis.ContainingLoop(i), 0);
647 EXPECT_EQ(analysis.MergeBlock(i), 0);
648 EXPECT_EQ(analysis.LoopMergeBlock(i), 0);
649 EXPECT_EQ(analysis.ContainingSwitch(i), 0);
650 EXPECT_EQ(analysis.SwitchMergeBlock(i), 0);
651 EXPECT_FALSE(analysis.IsContinueBlock(i));
652 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(i));
653 EXPECT_FALSE(analysis.IsInContinueConstruct(i));
654 EXPECT_FALSE(analysis.IsMergeBlock(i));
655 }
656 }
657
TEST_F(StructCFGAnalysisTest,EmptyFunctionTest)658 TEST_F(StructCFGAnalysisTest, EmptyFunctionTest) {
659 const std::string text = R"(
660 OpCapability Shader
661 OpCapability Linkage
662 OpMemoryModel Logical GLSL450
663 OpDecorate %func LinkageAttributes "x" Import
664 %void = OpTypeVoid
665 %void_fn = OpTypeFunction %void
666 %func = OpFunction %void None %void_fn
667 OpFunctionEnd
668 )";
669
670 std::unique_ptr<IRContext> context =
671 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
672 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
673
674 // #2451: This segfaulted on empty functions.
675 StructuredCFGAnalysis analysis(context.get());
676 }
677
TEST_F(StructCFGAnalysisTest,BBInSwitch)678 TEST_F(StructCFGAnalysisTest, BBInSwitch) {
679 const std::string text = R"(
680 OpCapability Shader
681 OpMemoryModel Logical GLSL450
682 OpEntryPoint Fragment %main "main"
683 %void = OpTypeVoid
684 %bool = OpTypeBool
685 %bool_undef = OpUndef %bool
686 %uint = OpTypeInt 32 0
687 %uint_undef = OpUndef %uint
688 %void_func = OpTypeFunction %void
689 %main = OpFunction %void None %void_func
690 %1 = OpLabel
691 OpSelectionMerge %3 None
692 OpSwitch %uint_undef %2 0 %3
693 %2 = OpLabel
694 OpBranch %3
695 %3 = OpLabel
696 OpReturn
697 OpFunctionEnd
698 )";
699
700 std::unique_ptr<IRContext> context =
701 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
702 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
703
704 StructuredCFGAnalysis analysis(context.get());
705
706 // The header is not in the construct.
707 EXPECT_EQ(analysis.ContainingConstruct(1), 0);
708 EXPECT_EQ(analysis.ContainingLoop(1), 0);
709 EXPECT_EQ(analysis.MergeBlock(1), 0);
710 EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
711 EXPECT_EQ(analysis.ContainingSwitch(1), 0);
712 EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
713 EXPECT_FALSE(analysis.IsContinueBlock(1));
714 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
715 EXPECT_FALSE(analysis.IsInContinueConstruct(1));
716 EXPECT_FALSE(analysis.IsMergeBlock(1));
717
718 // BB2 is in the construct.
719 EXPECT_EQ(analysis.ContainingConstruct(2), 1);
720 EXPECT_EQ(analysis.ContainingLoop(2), 0);
721 EXPECT_EQ(analysis.MergeBlock(2), 3);
722 EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
723 EXPECT_EQ(analysis.ContainingSwitch(2), 1);
724 EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
725 EXPECT_FALSE(analysis.IsContinueBlock(2));
726 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
727 EXPECT_FALSE(analysis.IsInContinueConstruct(2));
728 EXPECT_FALSE(analysis.IsMergeBlock(2));
729
730 // The merge node is not in the construct.
731 EXPECT_EQ(analysis.ContainingConstruct(3), 0);
732 EXPECT_EQ(analysis.ContainingLoop(3), 0);
733 EXPECT_EQ(analysis.MergeBlock(3), 0);
734 EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
735 EXPECT_EQ(analysis.ContainingSwitch(3), 0);
736 EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
737 EXPECT_FALSE(analysis.IsContinueBlock(3));
738 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
739 EXPECT_FALSE(analysis.IsInContinueConstruct(3));
740 EXPECT_TRUE(analysis.IsMergeBlock(3));
741 }
742
TEST_F(StructCFGAnalysisTest,LoopInSwitch)743 TEST_F(StructCFGAnalysisTest, LoopInSwitch) {
744 const std::string text = R"(
745 OpCapability Shader
746 OpMemoryModel Logical GLSL450
747 OpEntryPoint Fragment %main "main"
748 %void = OpTypeVoid
749 %bool = OpTypeBool
750 %bool_undef = OpUndef %bool
751 %uint = OpTypeInt 32 0
752 %uint_undef = OpUndef %uint
753 %void_func = OpTypeFunction %void
754 %main = OpFunction %void None %void_func
755 %entry_lab = OpLabel
756 OpBranch %1
757 %1 = OpLabel
758 OpSelectionMerge %3 None
759 OpSwitch %uint_undef %2 1 %3
760 %2 = OpLabel
761 OpLoopMerge %4 %5 None
762 OpBranchConditional %undef_bool %4 %6
763 %5 = OpLabel
764 OpBranch %2
765 %6 = OpLabel
766 OpBranch %4
767 %4 = OpLabel
768 OpBranch %3
769 %3 = OpLabel
770 OpReturn
771 OpFunctionEnd
772 )";
773
774 std::unique_ptr<IRContext> context =
775 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
776 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
777
778 StructuredCFGAnalysis analysis(context.get());
779
780 // The selection header is not in either construct.
781 EXPECT_EQ(analysis.ContainingConstruct(1), 0);
782 EXPECT_EQ(analysis.ContainingLoop(1), 0);
783 EXPECT_EQ(analysis.MergeBlock(1), 0);
784 EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
785 EXPECT_EQ(analysis.ContainingSwitch(1), 0);
786 EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
787 EXPECT_FALSE(analysis.IsContinueBlock(1));
788 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
789 EXPECT_FALSE(analysis.IsInContinueConstruct(1));
790 EXPECT_FALSE(analysis.IsMergeBlock(1));
791
792 // Loop header is in the selection only.
793 EXPECT_EQ(analysis.ContainingConstruct(2), 1);
794 EXPECT_EQ(analysis.ContainingLoop(2), 0);
795 EXPECT_EQ(analysis.MergeBlock(2), 3);
796 EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
797 EXPECT_EQ(analysis.ContainingSwitch(2), 1);
798 EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
799 EXPECT_FALSE(analysis.IsContinueBlock(2));
800 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
801 EXPECT_FALSE(analysis.IsInContinueConstruct(2));
802 EXPECT_FALSE(analysis.IsMergeBlock(2));
803
804 // The selection merge node is not in either construct.
805 EXPECT_EQ(analysis.ContainingConstruct(3), 0);
806 EXPECT_EQ(analysis.ContainingLoop(3), 0);
807 EXPECT_EQ(analysis.MergeBlock(3), 0);
808 EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
809 EXPECT_EQ(analysis.ContainingSwitch(3), 0);
810 EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
811 EXPECT_FALSE(analysis.IsContinueBlock(3));
812 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
813 EXPECT_FALSE(analysis.IsInContinueConstruct(3));
814 EXPECT_TRUE(analysis.IsMergeBlock(3));
815
816 // The loop merge is in the selection only.
817 EXPECT_EQ(analysis.ContainingConstruct(4), 1);
818 EXPECT_EQ(analysis.ContainingLoop(4), 0);
819 EXPECT_EQ(analysis.MergeBlock(4), 3);
820 EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
821 EXPECT_EQ(analysis.ContainingSwitch(4), 1);
822 EXPECT_EQ(analysis.SwitchMergeBlock(4), 3);
823 EXPECT_FALSE(analysis.IsContinueBlock(4));
824 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
825 EXPECT_FALSE(analysis.IsInContinueConstruct(4));
826 EXPECT_TRUE(analysis.IsMergeBlock(4));
827
828 // The loop continue target is in the loop.
829 EXPECT_EQ(analysis.ContainingConstruct(5), 2);
830 EXPECT_EQ(analysis.ContainingLoop(5), 2);
831 EXPECT_EQ(analysis.MergeBlock(5), 4);
832 EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
833 EXPECT_EQ(analysis.ContainingSwitch(5), 0);
834 EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
835 EXPECT_TRUE(analysis.IsContinueBlock(5));
836 EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
837 EXPECT_TRUE(analysis.IsInContinueConstruct(5));
838 EXPECT_FALSE(analysis.IsMergeBlock(5));
839
840 // BB6 is in the loop.
841 EXPECT_EQ(analysis.ContainingConstruct(6), 2);
842 EXPECT_EQ(analysis.ContainingLoop(6), 2);
843 EXPECT_EQ(analysis.MergeBlock(6), 4);
844 EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
845 EXPECT_EQ(analysis.ContainingSwitch(6), 0);
846 EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
847 EXPECT_FALSE(analysis.IsContinueBlock(6));
848 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
849 EXPECT_FALSE(analysis.IsInContinueConstruct(6));
850 EXPECT_FALSE(analysis.IsMergeBlock(6));
851 }
852
TEST_F(StructCFGAnalysisTest,SelectionInSwitch)853 TEST_F(StructCFGAnalysisTest, SelectionInSwitch) {
854 const std::string text = R"(
855 OpCapability Shader
856 OpMemoryModel Logical GLSL450
857 OpEntryPoint Fragment %main "main"
858 %void = OpTypeVoid
859 %bool = OpTypeBool
860 %bool_undef = OpUndef %bool
861 %uint = OpTypeInt 32 0
862 %uint_undef = OpUndef %uint
863 %void_func = OpTypeFunction %void
864 %main = OpFunction %void None %void_func
865 %entry_lab = OpLabel
866 OpBranch %1
867 %1 = OpLabel
868 OpSelectionMerge %3 None
869 OpSwitch %uint_undef %2 10 %3
870 %2 = OpLabel
871 OpSelectionMerge %4 None
872 OpBranchConditional %undef_bool %4 %5
873 %5 = OpLabel
874 OpBranch %4
875 %4 = OpLabel
876 OpBranch %3
877 %3 = OpLabel
878 OpReturn
879 OpFunctionEnd
880 )";
881
882 std::unique_ptr<IRContext> context =
883 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
884 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
885
886 StructuredCFGAnalysis analysis(context.get());
887
888 // The outer selection header is not in either construct.
889 EXPECT_EQ(analysis.ContainingConstruct(1), 0);
890 EXPECT_EQ(analysis.ContainingLoop(1), 0);
891 EXPECT_EQ(analysis.MergeBlock(1), 0);
892 EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
893 EXPECT_EQ(analysis.ContainingSwitch(1), 0);
894 EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
895 EXPECT_FALSE(analysis.IsContinueBlock(1));
896 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
897 EXPECT_FALSE(analysis.IsInContinueConstruct(1));
898 EXPECT_FALSE(analysis.IsMergeBlock(1));
899
900 // The inner header is in the outer selection.
901 EXPECT_EQ(analysis.ContainingConstruct(2), 1);
902 EXPECT_EQ(analysis.ContainingLoop(2), 0);
903 EXPECT_EQ(analysis.MergeBlock(2), 3);
904 EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
905 EXPECT_EQ(analysis.ContainingSwitch(2), 1);
906 EXPECT_EQ(analysis.SwitchMergeBlock(2), 3);
907 EXPECT_FALSE(analysis.IsContinueBlock(2));
908 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
909 EXPECT_FALSE(analysis.IsInContinueConstruct(2));
910 EXPECT_FALSE(analysis.IsMergeBlock(2));
911
912 // The outer merge node is not in either construct.
913 EXPECT_EQ(analysis.ContainingConstruct(3), 0);
914 EXPECT_EQ(analysis.ContainingLoop(3), 0);
915 EXPECT_EQ(analysis.MergeBlock(3), 0);
916 EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
917 EXPECT_EQ(analysis.ContainingSwitch(3), 0);
918 EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
919 EXPECT_FALSE(analysis.IsContinueBlock(3));
920 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
921 EXPECT_FALSE(analysis.IsInContinueConstruct(3));
922 EXPECT_TRUE(analysis.IsMergeBlock(3));
923
924 // The inner merge is in the outer selection.
925 EXPECT_EQ(analysis.ContainingConstruct(4), 1);
926 EXPECT_EQ(analysis.ContainingLoop(4), 0);
927 EXPECT_EQ(analysis.MergeBlock(4), 3);
928 EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
929 EXPECT_EQ(analysis.ContainingSwitch(4), 1);
930 EXPECT_EQ(analysis.SwitchMergeBlock(4), 3);
931 EXPECT_FALSE(analysis.IsContinueBlock(4));
932 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
933 EXPECT_FALSE(analysis.IsInContinueConstruct(4));
934 EXPECT_TRUE(analysis.IsMergeBlock(4));
935
936 // BB5 is in the inner selection.
937 EXPECT_EQ(analysis.ContainingConstruct(5), 2);
938 EXPECT_EQ(analysis.ContainingLoop(5), 0);
939 EXPECT_EQ(analysis.MergeBlock(5), 4);
940 EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
941 EXPECT_EQ(analysis.ContainingSwitch(5), 1);
942 EXPECT_EQ(analysis.SwitchMergeBlock(5), 3);
943 EXPECT_FALSE(analysis.IsContinueBlock(5));
944 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
945 EXPECT_FALSE(analysis.IsInContinueConstruct(5));
946 EXPECT_FALSE(analysis.IsMergeBlock(5));
947 }
948
TEST_F(StructCFGAnalysisTest,SwitchInSelection)949 TEST_F(StructCFGAnalysisTest, SwitchInSelection) {
950 const std::string text = R"(
951 OpCapability Shader
952 OpMemoryModel Logical GLSL450
953 OpEntryPoint Fragment %main "main"
954 %void = OpTypeVoid
955 %bool = OpTypeBool
956 %bool_undef = OpUndef %bool
957 %uint = OpTypeInt 32 0
958 %uint_undef = OpUndef %uint
959 %void_func = OpTypeFunction %void
960 %main = OpFunction %void None %void_func
961 %entry_lab = OpLabel
962 OpBranch %1
963 %1 = OpLabel
964 OpSelectionMerge %3 None
965 OpBranchConditional %undef_bool %2 %3
966 %2 = OpLabel
967 OpSelectionMerge %4 None
968 OpSwitch %uint_undef %4 7 %5
969 %5 = OpLabel
970 OpBranch %4
971 %4 = OpLabel
972 OpBranch %3
973 %3 = OpLabel
974 OpReturn
975 OpFunctionEnd
976 )";
977
978 std::unique_ptr<IRContext> context =
979 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
980 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
981
982 StructuredCFGAnalysis analysis(context.get());
983
984 // The outer selection header is not in either construct.
985 EXPECT_EQ(analysis.ContainingConstruct(1), 0);
986 EXPECT_EQ(analysis.ContainingLoop(1), 0);
987 EXPECT_EQ(analysis.MergeBlock(1), 0);
988 EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
989 EXPECT_EQ(analysis.ContainingSwitch(1), 0);
990 EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
991 EXPECT_FALSE(analysis.IsContinueBlock(1));
992 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
993 EXPECT_FALSE(analysis.IsInContinueConstruct(1));
994 EXPECT_FALSE(analysis.IsMergeBlock(1));
995
996 // The inner header is in the outer selection.
997 EXPECT_EQ(analysis.ContainingConstruct(2), 1);
998 EXPECT_EQ(analysis.ContainingLoop(2), 0);
999 EXPECT_EQ(analysis.MergeBlock(2), 3);
1000 EXPECT_EQ(analysis.LoopMergeBlock(2), 0);
1001 EXPECT_EQ(analysis.ContainingSwitch(2), 0);
1002 EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
1003 EXPECT_FALSE(analysis.IsContinueBlock(2));
1004 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
1005 EXPECT_FALSE(analysis.IsInContinueConstruct(2));
1006 EXPECT_FALSE(analysis.IsMergeBlock(2));
1007
1008 // The outer merge node is not in either construct.
1009 EXPECT_EQ(analysis.ContainingConstruct(3), 0);
1010 EXPECT_EQ(analysis.ContainingLoop(3), 0);
1011 EXPECT_EQ(analysis.MergeBlock(3), 0);
1012 EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
1013 EXPECT_EQ(analysis.ContainingSwitch(3), 0);
1014 EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
1015 EXPECT_FALSE(analysis.IsContinueBlock(3));
1016 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
1017 EXPECT_FALSE(analysis.IsInContinueConstruct(3));
1018 EXPECT_TRUE(analysis.IsMergeBlock(3));
1019
1020 // The inner merge is in the outer selection.
1021 EXPECT_EQ(analysis.ContainingConstruct(4), 1);
1022 EXPECT_EQ(analysis.ContainingLoop(4), 0);
1023 EXPECT_EQ(analysis.MergeBlock(4), 3);
1024 EXPECT_EQ(analysis.LoopMergeBlock(4), 0);
1025 EXPECT_EQ(analysis.ContainingSwitch(4), 0);
1026 EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
1027 EXPECT_FALSE(analysis.IsContinueBlock(4));
1028 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4));
1029 EXPECT_FALSE(analysis.IsInContinueConstruct(4));
1030 EXPECT_TRUE(analysis.IsMergeBlock(4));
1031
1032 // BB5 is in the inner selection.
1033 EXPECT_EQ(analysis.ContainingConstruct(5), 2);
1034 EXPECT_EQ(analysis.ContainingLoop(5), 0);
1035 EXPECT_EQ(analysis.MergeBlock(5), 4);
1036 EXPECT_EQ(analysis.LoopMergeBlock(5), 0);
1037 EXPECT_EQ(analysis.ContainingSwitch(5), 2);
1038 EXPECT_EQ(analysis.SwitchMergeBlock(5), 4);
1039 EXPECT_FALSE(analysis.IsContinueBlock(5));
1040 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5));
1041 EXPECT_FALSE(analysis.IsInContinueConstruct(5));
1042 EXPECT_FALSE(analysis.IsMergeBlock(5));
1043 }
1044
TEST_F(StructCFGAnalysisTest,SelectionInContinue)1045 TEST_F(StructCFGAnalysisTest, SelectionInContinue) {
1046 const std::string text = R"(
1047 OpCapability Shader
1048 OpMemoryModel Logical GLSL450
1049 OpEntryPoint Fragment %main "main"
1050 %void = OpTypeVoid
1051 %bool = OpTypeBool
1052 %bool_undef = OpUndef %bool
1053 %uint = OpTypeInt 32 0
1054 %uint_undef = OpUndef %uint
1055 %void_func = OpTypeFunction %void
1056 %main = OpFunction %void None %void_func
1057 %entry_lab = OpLabel
1058 OpBranch %1
1059 %1 = OpLabel
1060 OpLoopMerge %3 %4 None
1061 OpBranchConditional %undef_bool %2 %3
1062 %2 = OpLabel
1063 OpBranch %3
1064 %4 = OpLabel
1065 OpSelectionMerge %6 None
1066 OpBranchConditional %undef_bool %5 %6
1067 %5 = OpLabel
1068 OpBranch %6
1069 %6 = OpLabel
1070 OpBranch %1
1071 %3 = OpLabel
1072 OpReturn
1073 OpFunctionEnd
1074 )";
1075
1076 std::unique_ptr<IRContext> context =
1077 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1078 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1079
1080 StructuredCFGAnalysis analysis(context.get());
1081
1082 // The loop header is not in either construct.
1083 EXPECT_EQ(analysis.ContainingConstruct(1), 0);
1084 EXPECT_EQ(analysis.ContainingLoop(1), 0);
1085 EXPECT_EQ(analysis.MergeBlock(1), 0);
1086 EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
1087 EXPECT_EQ(analysis.ContainingSwitch(1), 0);
1088 EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
1089 EXPECT_FALSE(analysis.IsContinueBlock(1));
1090 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
1091 EXPECT_FALSE(analysis.IsInContinueConstruct(1));
1092 EXPECT_FALSE(analysis.IsMergeBlock(1));
1093
1094 // Selection header is in the loop only.
1095 EXPECT_EQ(analysis.ContainingConstruct(2), 1);
1096 EXPECT_EQ(analysis.ContainingLoop(2), 1);
1097 EXPECT_EQ(analysis.MergeBlock(2), 3);
1098 EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
1099 EXPECT_EQ(analysis.ContainingSwitch(2), 0);
1100 EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
1101 EXPECT_FALSE(analysis.IsContinueBlock(2));
1102 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
1103 EXPECT_FALSE(analysis.IsInContinueConstruct(2));
1104 EXPECT_FALSE(analysis.IsMergeBlock(2));
1105
1106 // The loop merge node is not in either construct.
1107 EXPECT_EQ(analysis.ContainingConstruct(3), 0);
1108 EXPECT_EQ(analysis.ContainingLoop(3), 0);
1109 EXPECT_EQ(analysis.MergeBlock(3), 0);
1110 EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
1111 EXPECT_EQ(analysis.ContainingSwitch(3), 0);
1112 EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
1113 EXPECT_FALSE(analysis.IsContinueBlock(3));
1114 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
1115 EXPECT_FALSE(analysis.IsInContinueConstruct(3));
1116 EXPECT_TRUE(analysis.IsMergeBlock(3));
1117
1118 // The continue block is in the loop only.
1119 EXPECT_EQ(analysis.ContainingConstruct(4), 1);
1120 EXPECT_EQ(analysis.ContainingLoop(4), 1);
1121 EXPECT_EQ(analysis.MergeBlock(4), 3);
1122 EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
1123 EXPECT_EQ(analysis.ContainingSwitch(4), 0);
1124 EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
1125 EXPECT_TRUE(analysis.IsContinueBlock(4));
1126 EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
1127 EXPECT_TRUE(analysis.IsInContinueConstruct(4));
1128 EXPECT_FALSE(analysis.IsMergeBlock(4));
1129
1130 // BB5 is in the selection and the continue for the loop.
1131 EXPECT_EQ(analysis.ContainingConstruct(5), 4);
1132 EXPECT_EQ(analysis.ContainingLoop(5), 1);
1133 EXPECT_EQ(analysis.MergeBlock(5), 6);
1134 EXPECT_EQ(analysis.LoopMergeBlock(5), 3);
1135 EXPECT_EQ(analysis.ContainingSwitch(5), 0);
1136 EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
1137 EXPECT_FALSE(analysis.IsContinueBlock(5));
1138 EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
1139 EXPECT_TRUE(analysis.IsInContinueConstruct(5));
1140 EXPECT_FALSE(analysis.IsMergeBlock(5));
1141
1142 // BB5 is in the continue for the loop.
1143 EXPECT_EQ(analysis.ContainingConstruct(6), 1);
1144 EXPECT_EQ(analysis.ContainingLoop(6), 1);
1145 EXPECT_EQ(analysis.MergeBlock(6), 3);
1146 EXPECT_EQ(analysis.LoopMergeBlock(6), 3);
1147 EXPECT_EQ(analysis.ContainingSwitch(6), 0);
1148 EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
1149 EXPECT_FALSE(analysis.IsContinueBlock(6));
1150 EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(6));
1151 EXPECT_TRUE(analysis.IsInContinueConstruct(6));
1152 EXPECT_TRUE(analysis.IsMergeBlock(6));
1153 }
1154
TEST_F(StructCFGAnalysisTest,LoopInContinue)1155 TEST_F(StructCFGAnalysisTest, LoopInContinue) {
1156 const std::string text = R"(
1157 OpCapability Shader
1158 OpMemoryModel Logical GLSL450
1159 OpEntryPoint Fragment %main "main"
1160 %void = OpTypeVoid
1161 %bool = OpTypeBool
1162 %bool_undef = OpUndef %bool
1163 %uint = OpTypeInt 32 0
1164 %uint_undef = OpUndef %uint
1165 %void_func = OpTypeFunction %void
1166 %main = OpFunction %void None %void_func
1167 %entry_lab = OpLabel
1168 OpBranch %1
1169 %1 = OpLabel
1170 OpLoopMerge %3 %7 None
1171 OpBranchConditional %undef_bool %2 %3
1172 %2 = OpLabel
1173 OpBranchConditional %undef_bool %3 %7
1174 %7 = OpLabel
1175 OpLoopMerge %4 %5 None
1176 OpBranchConditional %undef_bool %4 %6
1177 %5 = OpLabel
1178 OpBranch %7
1179 %6 = OpLabel
1180 OpBranch %4
1181 %4 = OpLabel
1182 OpBranch %1
1183 %3 = OpLabel
1184 OpReturn
1185 OpFunctionEnd
1186 )";
1187
1188 std::unique_ptr<IRContext> context =
1189 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1190 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1191
1192 StructuredCFGAnalysis analysis(context.get());
1193
1194 // The outer loop header is not in either construct.
1195 EXPECT_EQ(analysis.ContainingConstruct(1), 0);
1196 EXPECT_EQ(analysis.ContainingLoop(1), 0);
1197 EXPECT_EQ(analysis.MergeBlock(1), 0);
1198 EXPECT_EQ(analysis.LoopMergeBlock(1), 0);
1199 EXPECT_EQ(analysis.ContainingSwitch(1), 0);
1200 EXPECT_EQ(analysis.SwitchMergeBlock(1), 0);
1201 EXPECT_FALSE(analysis.IsContinueBlock(1));
1202 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1));
1203 EXPECT_FALSE(analysis.IsInContinueConstruct(1));
1204 EXPECT_FALSE(analysis.IsMergeBlock(1));
1205
1206 // BB2 is a regular block in the inner loop.
1207 EXPECT_EQ(analysis.ContainingConstruct(2), 1);
1208 EXPECT_EQ(analysis.ContainingLoop(2), 1);
1209 EXPECT_EQ(analysis.MergeBlock(2), 3);
1210 EXPECT_EQ(analysis.LoopMergeBlock(2), 3);
1211 EXPECT_EQ(analysis.ContainingSwitch(2), 0);
1212 EXPECT_EQ(analysis.SwitchMergeBlock(2), 0);
1213 EXPECT_FALSE(analysis.IsContinueBlock(2));
1214 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2));
1215 EXPECT_FALSE(analysis.IsInContinueConstruct(2));
1216 EXPECT_FALSE(analysis.IsMergeBlock(2));
1217
1218 // The outer merge node is not in either construct.
1219 EXPECT_EQ(analysis.ContainingConstruct(3), 0);
1220 EXPECT_EQ(analysis.ContainingLoop(3), 0);
1221 EXPECT_EQ(analysis.MergeBlock(3), 0);
1222 EXPECT_EQ(analysis.LoopMergeBlock(3), 0);
1223 EXPECT_EQ(analysis.ContainingSwitch(3), 0);
1224 EXPECT_EQ(analysis.SwitchMergeBlock(3), 0);
1225 EXPECT_FALSE(analysis.IsContinueBlock(3));
1226 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3));
1227 EXPECT_FALSE(analysis.IsInContinueConstruct(3));
1228 EXPECT_TRUE(analysis.IsMergeBlock(3));
1229
1230 // The inner merge is in the continue of the outer loop.
1231 EXPECT_EQ(analysis.ContainingConstruct(4), 1);
1232 EXPECT_EQ(analysis.ContainingLoop(4), 1);
1233 EXPECT_EQ(analysis.MergeBlock(4), 3);
1234 EXPECT_EQ(analysis.LoopMergeBlock(4), 3);
1235 EXPECT_EQ(analysis.ContainingSwitch(4), 0);
1236 EXPECT_EQ(analysis.SwitchMergeBlock(4), 0);
1237 EXPECT_FALSE(analysis.IsContinueBlock(4));
1238 EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4));
1239 EXPECT_TRUE(analysis.IsInContinueConstruct(4));
1240 EXPECT_TRUE(analysis.IsMergeBlock(4));
1241
1242 // The inner continue target is in the inner loop.
1243 EXPECT_EQ(analysis.ContainingConstruct(5), 7);
1244 EXPECT_EQ(analysis.ContainingLoop(5), 7);
1245 EXPECT_EQ(analysis.MergeBlock(5), 4);
1246 EXPECT_EQ(analysis.LoopMergeBlock(5), 4);
1247 EXPECT_EQ(analysis.ContainingSwitch(5), 0);
1248 EXPECT_EQ(analysis.SwitchMergeBlock(5), 0);
1249 EXPECT_TRUE(analysis.IsContinueBlock(5));
1250 EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5));
1251 EXPECT_TRUE(analysis.IsInContinueConstruct(5));
1252 EXPECT_FALSE(analysis.IsMergeBlock(5));
1253
1254 // BB6 is a regular block in the inner loop.
1255 EXPECT_EQ(analysis.ContainingConstruct(6), 7);
1256 EXPECT_EQ(analysis.ContainingLoop(6), 7);
1257 EXPECT_EQ(analysis.MergeBlock(6), 4);
1258 EXPECT_EQ(analysis.LoopMergeBlock(6), 4);
1259 EXPECT_EQ(analysis.ContainingSwitch(6), 0);
1260 EXPECT_EQ(analysis.SwitchMergeBlock(6), 0);
1261 EXPECT_FALSE(analysis.IsContinueBlock(6));
1262 EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6));
1263 EXPECT_TRUE(analysis.IsInContinueConstruct(6));
1264 EXPECT_FALSE(analysis.IsMergeBlock(6));
1265
1266 // The outer continue target is in the outer loop.
1267 EXPECT_EQ(analysis.ContainingConstruct(7), 1);
1268 EXPECT_EQ(analysis.ContainingLoop(7), 1);
1269 EXPECT_EQ(analysis.MergeBlock(7), 3);
1270 EXPECT_EQ(analysis.LoopMergeBlock(7), 3);
1271 EXPECT_EQ(analysis.ContainingSwitch(7), 0);
1272 EXPECT_EQ(analysis.SwitchMergeBlock(7), 0);
1273 EXPECT_TRUE(analysis.IsContinueBlock(7));
1274 EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(7));
1275 EXPECT_TRUE(analysis.IsInContinueConstruct(7));
1276 EXPECT_FALSE(analysis.IsMergeBlock(7));
1277 }
1278
TEST_F(StructCFGAnalysisTest,FuncCallInContinueDirect)1279 TEST_F(StructCFGAnalysisTest, FuncCallInContinueDirect) {
1280 const std::string text = R"(
1281 OpCapability Shader
1282 OpMemoryModel Logical GLSL450
1283 OpEntryPoint Fragment %1 "main"
1284 %void = OpTypeVoid
1285 %bool = OpTypeBool
1286 %4 = OpUndef %bool
1287 %uint = OpTypeInt 32 0
1288 %6 = OpUndef %uint
1289 %7 = OpTypeFunction %void
1290 %1 = OpFunction %void None %7
1291 %8 = OpLabel
1292 OpBranch %9
1293 %9 = OpLabel
1294 OpLoopMerge %10 %11 None
1295 OpBranchConditional %12 %10 %11
1296 %11 = OpLabel
1297 %13 = OpFunctionCall %void %14
1298 OpBranch %9
1299 %10 = OpLabel
1300 %15 = OpFunctionCall %void %16
1301 OpReturn
1302 OpFunctionEnd
1303 %14 = OpFunction %void None %7
1304 %17 = OpLabel
1305 OpReturn
1306 OpFunctionEnd
1307 %16 = OpFunction %void None %7
1308 %18 = OpLabel
1309 OpReturn
1310 OpFunctionEnd
1311 )";
1312
1313 std::unique_ptr<IRContext> context =
1314 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1315 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1316
1317 StructuredCFGAnalysis analysis(context.get());
1318
1319 auto c = analysis.FindFuncsCalledFromContinue();
1320 EXPECT_THAT(c, UnorderedElementsAre(14u));
1321 }
1322
TEST_F(StructCFGAnalysisTest,FuncCallInContinueIndirect)1323 TEST_F(StructCFGAnalysisTest, FuncCallInContinueIndirect) {
1324 const std::string text = R"(
1325 OpCapability Shader
1326 OpMemoryModel Logical GLSL450
1327 OpEntryPoint Fragment %1 "main"
1328 %void = OpTypeVoid
1329 %bool = OpTypeBool
1330 %4 = OpUndef %bool
1331 %uint = OpTypeInt 32 0
1332 %6 = OpUndef %uint
1333 %7 = OpTypeFunction %void
1334 %1 = OpFunction %void None %7
1335 %8 = OpLabel
1336 OpBranch %9
1337 %9 = OpLabel
1338 OpLoopMerge %10 %11 None
1339 OpBranchConditional %12 %10 %11
1340 %11 = OpLabel
1341 %13 = OpFunctionCall %void %14
1342 OpBranch %9
1343 %10 = OpLabel
1344 %15 = OpFunctionCall %void %16
1345 OpReturn
1346 OpFunctionEnd
1347 %14 = OpFunction %void None %7
1348 %17 = OpLabel
1349 %19 = OpFunctionCall %void %16
1350 OpReturn
1351 OpFunctionEnd
1352 %16 = OpFunction %void None %7
1353 %18 = OpLabel
1354 %20 = OpFunctionCall %void %21
1355 OpReturn
1356 OpFunctionEnd
1357 %21 = OpFunction %void None %7
1358 %22 = OpLabel
1359 OpReturn
1360 OpFunctionEnd
1361 )";
1362
1363 std::unique_ptr<IRContext> context =
1364 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1365 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1366
1367 StructuredCFGAnalysis analysis(context.get());
1368
1369 auto c = analysis.FindFuncsCalledFromContinue();
1370 EXPECT_THAT(c, UnorderedElementsAre(14u, 16u, 21u));
1371 }
1372 } // namespace
1373 } // namespace opt
1374 } // namespace spvtools
1375