• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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