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 <string>
16
17#include "effcee/effcee.h"
18#include "gmock/gmock.h"
19#include "test/opt/pass_fixture.h"
20
21namespace spvtools {
22namespace opt {
23namespace {
24
25using UnswitchTest = PassTest<::testing::Test>;
26
27/*
28Generated from the following GLSL + --eliminate-local-multi-store
29
30#version 450 core
31uniform vec4 c;
32void main() {
33  int i = 0;
34  int j = 0;
35  bool cond = c[0] == 0;
36  for (; i < 10; i++, j++) {
37    if (cond) {
38      i++;
39    }
40    else {
41      j++;
42    }
43  }
44}
45*/
46TEST_F(UnswitchTest, SimpleUnswitch) {
47  const std::string text = R"(
48; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
49; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
50; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
51
52; Loop specialized for false.
53; CHECK: [[loop_f]] = OpLabel
54; CHECK-NEXT: OpBranch [[loop:%\w+]]
55; CHECK: [[loop]] = OpLabel
56; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_i:%\w+]] [[continue:%\w+]]
57; CHECK-NEXT: [[phi_j:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_j:%\w+]] [[continue]]
58; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
59; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
60; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
61; [[loop_body]] = OpLabel
62; CHECK: OpSelectionMerge [[sel_merge:%\w+]] None
63; CHECK: OpBranchConditional %false [[bb1:%\w+]] [[bb2:%\w+]]
64; CHECK: [[bb2]] = OpLabel
65; CHECK-NEXT: [[inc_j:%\w+]] = OpIAdd %int [[phi_j]] %int_1
66; CHECK-NEXT: OpBranch [[sel_merge]]
67; CHECK: [[bb1]] = OpLabel
68; CHECK-NEXT: [[inc_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1
69; CHECK-NEXT: OpBranch [[sel_merge]]
70; CHECK: [[sel_merge]] = OpLabel
71; CHECK: OpBranch [[if_merge]]
72
73; Loop specialized for true.
74; CHECK: [[loop_t]] = OpLabel
75; CHECK-NEXT: OpBranch [[loop:%\w+]]
76; CHECK: [[loop]] = OpLabel
77; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]]
78; CHECK-NEXT: [[phi_j:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_j:%\w+]] [[continue]]
79; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
80; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
81; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
82; [[loop_body]] = OpLabel
83; CHECK: OpSelectionMerge [[sel_merge:%\w+]] None
84; CHECK: OpBranchConditional %true [[bb1:%\w+]] [[bb2:%\w+]]
85; CHECK: [[bb1]] = OpLabel
86; CHECK-NEXT: [[inc_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1
87; CHECK-NEXT: OpBranch [[sel_merge]]
88; CHECK: [[bb2]] = OpLabel
89; CHECK-NEXT: [[inc_j:%\w+]] = OpIAdd %int [[phi_j]] %int_1
90; CHECK-NEXT: OpBranch [[sel_merge]]
91; CHECK: [[sel_merge]] = OpLabel
92; CHECK: OpBranch [[if_merge]]
93
94; CHECK: [[if_merge]] = OpLabel
95; CHECK-NEXT: OpReturn
96
97               OpCapability Shader
98          %1 = OpExtInstImport "GLSL.std.450"
99               OpMemoryModel Logical GLSL450
100               OpEntryPoint Fragment %main "main"
101               OpExecutionMode %main OriginLowerLeft
102               OpSource GLSL 450
103               OpName %main "main"
104               OpName %c "c"
105               OpDecorate %c Location 0
106               OpDecorate %c DescriptorSet 0
107       %void = OpTypeVoid
108          %3 = OpTypeFunction %void
109        %int = OpTypeInt 32 1
110%_ptr_Function_int = OpTypePointer Function %int
111      %int_0 = OpConstant %int 0
112       %bool = OpTypeBool
113%_ptr_Function_bool = OpTypePointer Function %bool
114      %float = OpTypeFloat 32
115    %v4float = OpTypeVector %float 4
116%_ptr_UniformConstant_v4float = OpTypePointer UniformConstant %v4float
117          %c = OpVariable %_ptr_UniformConstant_v4float UniformConstant
118       %uint = OpTypeInt 32 0
119     %uint_0 = OpConstant %uint 0
120%_ptr_UniformConstant_float = OpTypePointer UniformConstant %float
121    %float_0 = OpConstant %float 0
122     %int_10 = OpConstant %int 10
123      %int_1 = OpConstant %int 1
124       %main = OpFunction %void None %3
125          %5 = OpLabel
126         %21 = OpAccessChain %_ptr_UniformConstant_float %c %uint_0
127         %22 = OpLoad %float %21
128         %24 = OpFOrdEqual %bool %22 %float_0
129               OpBranch %25
130         %25 = OpLabel
131         %46 = OpPhi %int %int_0 %5 %43 %28
132         %47 = OpPhi %int %int_0 %5 %45 %28
133               OpLoopMerge %27 %28 None
134               OpBranch %29
135         %29 = OpLabel
136         %32 = OpSLessThan %bool %46 %int_10
137               OpBranchConditional %32 %26 %27
138         %26 = OpLabel
139               OpSelectionMerge %35 None
140               OpBranchConditional %24 %34 %39
141         %34 = OpLabel
142         %38 = OpIAdd %int %46 %int_1
143               OpBranch %35
144         %39 = OpLabel
145         %41 = OpIAdd %int %47 %int_1
146               OpBranch %35
147         %35 = OpLabel
148         %48 = OpPhi %int %38 %34 %46 %39
149         %49 = OpPhi %int %47 %34 %41 %39
150               OpBranch %28
151         %28 = OpLabel
152         %43 = OpIAdd %int %48 %int_1
153         %45 = OpIAdd %int %49 %int_1
154               OpBranch %25
155         %27 = OpLabel
156               OpReturn
157               OpFunctionEnd
158  )";
159
160  SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
161}
162
163/*
164Generated from the following GLSL + --eliminate-local-multi-store
165
166#version 330 core
167in vec4 c;
168void main() {
169  int i = 0;
170  bool cond = c[0] == 0;
171  for (; i < 10; i++) {
172    if (cond) {
173      i++;
174    }
175    else {
176      return;
177    }
178  }
179}
180*/
181TEST_F(UnswitchTest, UnswitchExit) {
182  const std::string text = R"(
183; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
184; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
185; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
186
187; Loop specialized for false.
188; CHECK: [[loop_f]] = OpLabel
189; CHECK: OpReturn
190
191; Loop specialized for true.
192; CHECK: [[loop_t]] = OpLabel
193; CHECK-NEXT: OpBranch [[loop:%\w+]]
194; CHECK: [[loop]] = OpLabel
195; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]]
196; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
197; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
198; CHECK-NEXT: OpBranchConditional [[loop_exit]] {{%\w+}} [[merge]]
199; Check that we have i+=2.
200; CHECK: [[phi_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1
201; CHECK: [[iv_i]] = OpIAdd %int [[phi_i]] %int_1
202; CHECK: [[merge]] = OpLabel
203; CHECK-NEXT: OpBranch [[if_merge]]
204
205; CHECK: [[if_merge]] = OpLabel
206; CHECK-NEXT: OpReturn
207
208               OpCapability Shader
209          %1 = OpExtInstImport "GLSL.std.450"
210               OpMemoryModel Logical GLSL450
211               OpEntryPoint Fragment %main "main" %c
212               OpExecutionMode %main OriginUpperLeft
213               OpSource GLSL 330
214               OpName %main "main"
215               OpName %c "c"
216               OpDecorate %c Location 0
217               OpDecorate %23 Uniform
218       %void = OpTypeVoid
219          %3 = OpTypeFunction %void
220        %int = OpTypeInt 32 1
221%_ptr_Function_int = OpTypePointer Function %int
222      %int_0 = OpConstant %int 0
223       %bool = OpTypeBool
224%_ptr_Function_bool = OpTypePointer Function %bool
225      %float = OpTypeFloat 32
226    %v4float = OpTypeVector %float 4
227%_ptr_Input_v4float = OpTypePointer Input %v4float
228          %c = OpVariable %_ptr_Input_v4float Input
229       %uint = OpTypeInt 32 0
230     %uint_0 = OpConstant %uint 0
231%_ptr_Input_float = OpTypePointer Input %float
232    %float_0 = OpConstant %float 0
233     %int_10 = OpConstant %int 10
234      %int_1 = OpConstant %int 1
235       %main = OpFunction %void None %3
236          %5 = OpLabel
237         %20 = OpAccessChain %_ptr_Input_float %c %uint_0
238         %21 = OpLoad %float %20
239         %23 = OpFOrdEqual %bool %21 %float_0
240               OpBranch %24
241         %24 = OpLabel
242         %42 = OpPhi %int %int_0 %5 %41 %27
243               OpLoopMerge %26 %27 None
244               OpBranch %28
245         %28 = OpLabel
246         %31 = OpSLessThan %bool %42 %int_10
247               OpBranchConditional %31 %25 %26
248         %25 = OpLabel
249               OpSelectionMerge %34 None
250               OpBranchConditional %23 %33 %38
251         %33 = OpLabel
252         %37 = OpIAdd %int %42 %int_1
253               OpBranch %34
254         %38 = OpLabel
255               OpReturn
256         %34 = OpLabel
257               OpBranch %27
258         %27 = OpLabel
259         %41 = OpIAdd %int %37 %int_1
260               OpBranch %24
261         %26 = OpLabel
262               OpReturn
263               OpFunctionEnd
264  )";
265
266  SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
267}
268
269/*
270Generated from the following GLSL + --eliminate-local-multi-store
271
272#version 330 core
273in vec4 c;
274void main() {
275  int i = 0;
276  bool cond = c[0] == 0;
277  for (; i < 10; i++) {
278    if (cond) {
279      continue;
280    }
281    else {
282      i++;
283    }
284  }
285}
286*/
287TEST_F(UnswitchTest, UnswitchContinue) {
288  const std::string text = R"(
289; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
290; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
291; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
292
293; Loop specialized for false.
294; CHECK: [[loop_f]] = OpLabel
295; CHECK-NEXT: OpBranch [[loop:%\w+]]
296; CHECK: [[loop]] = OpLabel
297; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_i:%\w+]] [[continue:%\w+]]
298; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
299; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
300; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
301; CHECK: [[loop_body:%\w+]] = OpLabel
302; CHECK-NEXT: OpSelectionMerge
303; CHECK-NEXT: OpBranchConditional %false
304; CHECK: [[merge]] = OpLabel
305; CHECK-NEXT: OpBranch [[if_merge]]
306
307; Loop specialized for true.
308; CHECK: [[loop_t]] = OpLabel
309; CHECK-NEXT: OpBranch [[loop:%\w+]]
310; CHECK: [[loop]] = OpLabel
311; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]]
312; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
313; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
314; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
315; CHECK: [[loop_body:%\w+]] = OpLabel
316; CHECK-NEXT: OpSelectionMerge
317; CHECK-NEXT: OpBranchConditional %true
318; CHECK: [[merge]] = OpLabel
319; CHECK-NEXT: OpBranch [[if_merge]]
320
321; CHECK: [[if_merge]] = OpLabel
322; CHECK-NEXT: OpReturn
323
324               OpCapability Shader
325          %1 = OpExtInstImport "GLSL.std.450"
326               OpMemoryModel Logical GLSL450
327               OpEntryPoint Fragment %main "main" %c
328               OpExecutionMode %main OriginUpperLeft
329               OpSource GLSL 330
330               OpName %main "main"
331               OpName %c "c"
332               OpDecorate %c Location 0
333               OpDecorate %23 Uniform
334       %void = OpTypeVoid
335          %3 = OpTypeFunction %void
336        %int = OpTypeInt 32 1
337%_ptr_Function_int = OpTypePointer Function %int
338      %int_0 = OpConstant %int 0
339       %bool = OpTypeBool
340%_ptr_Function_bool = OpTypePointer Function %bool
341      %float = OpTypeFloat 32
342    %v4float = OpTypeVector %float 4
343%_ptr_Input_v4float = OpTypePointer Input %v4float
344          %c = OpVariable %_ptr_Input_v4float Input
345       %uint = OpTypeInt 32 0
346     %uint_0 = OpConstant %uint 0
347%_ptr_Input_float = OpTypePointer Input %float
348    %float_0 = OpConstant %float 0
349     %int_10 = OpConstant %int 10
350      %int_1 = OpConstant %int 1
351       %main = OpFunction %void None %3
352          %5 = OpLabel
353         %20 = OpAccessChain %_ptr_Input_float %c %uint_0
354         %21 = OpLoad %float %20
355         %23 = OpFOrdEqual %bool %21 %float_0
356               OpBranch %24
357         %24 = OpLabel
358         %42 = OpPhi %int %int_0 %5 %41 %27
359               OpLoopMerge %26 %27 None
360               OpBranch %28
361         %28 = OpLabel
362         %31 = OpSLessThan %bool %42 %int_10
363               OpBranchConditional %31 %25 %26
364         %25 = OpLabel
365               OpSelectionMerge %34 None
366               OpBranchConditional %23 %33 %36
367         %33 = OpLabel
368               OpBranch %27
369         %36 = OpLabel
370         %39 = OpIAdd %int %42 %int_1
371               OpBranch %34
372         %34 = OpLabel
373               OpBranch %27
374         %27 = OpLabel
375         %43 = OpPhi %int %42 %33 %39 %34
376         %41 = OpIAdd %int %43 %int_1
377               OpBranch %24
378         %26 = OpLabel
379               OpReturn
380               OpFunctionEnd
381  )";
382
383  SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
384}
385
386/*
387Generated from the following GLSL + --eliminate-local-multi-store
388
389#version 330 core
390in vec4 c;
391void main() {
392  int i = 0;
393  bool cond = c[0] == 0;
394  for (; i < 10; i++) {
395    if (cond) {
396      i++;
397    }
398    else {
399      break;
400    }
401  }
402}
403*/
404TEST_F(UnswitchTest, UnswitchKillLoop) {
405  const std::string text = R"(
406; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
407; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
408; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
409
410; Loop specialized for false.
411; CHECK: [[loop_f]] = OpLabel
412; CHECK: OpBranch [[if_merge]]
413
414; Loop specialized for true.
415; CHECK: [[loop_t]] = OpLabel
416; CHECK-NEXT: OpBranch [[loop:%\w+]]
417; CHECK: [[loop]] = OpLabel
418; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]]
419; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
420; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
421; CHECK-NEXT: OpBranchConditional [[loop_exit]] {{%\w+}} [[merge]]
422; Check that we have i+=2.
423; CHECK: [[phi_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1
424; CHECK: [[iv_i]] = OpIAdd %int [[phi_i]] %int_1
425; CHECK: [[merge]] = OpLabel
426; CHECK-NEXT: OpBranch [[if_merge]]
427
428; CHECK: [[if_merge]] = OpLabel
429; CHECK-NEXT: OpReturn
430
431               OpCapability Shader
432          %1 = OpExtInstImport "GLSL.std.450"
433               OpMemoryModel Logical GLSL450
434               OpEntryPoint Fragment %main "main" %c
435               OpExecutionMode %main OriginUpperLeft
436               OpSource GLSL 330
437               OpName %main "main"
438               OpName %c "c"
439               OpDecorate %c Location 0
440               OpDecorate %23 Uniform
441       %void = OpTypeVoid
442          %3 = OpTypeFunction %void
443        %int = OpTypeInt 32 1
444%_ptr_Function_int = OpTypePointer Function %int
445      %int_0 = OpConstant %int 0
446       %bool = OpTypeBool
447%_ptr_Function_bool = OpTypePointer Function %bool
448      %float = OpTypeFloat 32
449    %v4float = OpTypeVector %float 4
450%_ptr_Input_v4float = OpTypePointer Input %v4float
451          %c = OpVariable %_ptr_Input_v4float Input
452       %uint = OpTypeInt 32 0
453     %uint_0 = OpConstant %uint 0
454%_ptr_Input_float = OpTypePointer Input %float
455    %float_0 = OpConstant %float 0
456     %int_10 = OpConstant %int 10
457      %int_1 = OpConstant %int 1
458       %main = OpFunction %void None %3
459          %5 = OpLabel
460         %20 = OpAccessChain %_ptr_Input_float %c %uint_0
461         %21 = OpLoad %float %20
462         %23 = OpFOrdEqual %bool %21 %float_0
463               OpBranch %24
464         %24 = OpLabel
465         %42 = OpPhi %int %int_0 %5 %41 %27
466               OpLoopMerge %26 %27 None
467               OpBranch %28
468         %28 = OpLabel
469         %31 = OpSLessThan %bool %42 %int_10
470               OpBranchConditional %31 %25 %26
471         %25 = OpLabel
472               OpSelectionMerge %34 None
473               OpBranchConditional %23 %33 %38
474         %33 = OpLabel
475         %37 = OpIAdd %int %42 %int_1
476               OpBranch %34
477         %38 = OpLabel
478               OpBranch %26
479         %34 = OpLabel
480               OpBranch %27
481         %27 = OpLabel
482         %41 = OpIAdd %int %37 %int_1
483               OpBranch %24
484         %26 = OpLabel
485               OpReturn
486               OpFunctionEnd
487  )";
488
489  SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
490}
491
492/*
493Generated from the following GLSL + --eliminate-local-multi-store
494
495#version 330 core
496in vec4 c;
497void main() {
498  int i = 0;
499  int cond = int(c[0]);
500  for (; i < 10; i++) {
501    switch (cond) {
502      case 0:
503        return;
504      case 1:
505        discard;
506      case 2:
507        break;
508      default:
509        break;
510    }
511  }
512  bool cond2 = i == 9;
513}
514*/
515TEST_F(UnswitchTest, UnswitchSwitch) {
516  const std::string text = R"(
517; CHECK: [[cst_cond:%\w+]] = OpConvertFToS
518; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
519; CHECK-NEXT: OpSwitch [[cst_cond]] [[default:%\w+]] 0 [[loop_0:%\w+]] 1 [[loop_1:%\w+]] 2 [[loop_2:%\w+]]
520
521; Loop specialized for 2.
522; CHECK: [[loop_2]] = OpLabel
523; CHECK-NEXT: OpBranch [[loop:%\w+]]
524; CHECK: [[loop]] = OpLabel
525; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_2]] [[iv_i:%\w+]] [[continue:%\w+]]
526; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
527; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
528; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
529; CHECK: [[loop_body]] = OpLabel
530; CHECK-NEXT: OpSelectionMerge
531; CHECK-NEXT: OpSwitch %int_2
532; CHECK: [[merge]] = OpLabel
533; CHECK-NEXT: OpBranch [[if_merge]]
534
535; Loop specialized for 1.
536; CHECK: [[loop_1]] = OpLabel
537; CHECK-NEXT: OpBranch [[loop:%\w+]]
538; CHECK: [[loop]] = OpLabel
539; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_1]] [[iv_i:%\w+]] [[continue:%\w+]]
540; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
541; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
542; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
543; CHECK: [[loop_body]] = OpLabel
544; CHECK-NEXT: OpSelectionMerge
545; CHECK-NEXT: OpSwitch %int_1
546; CHECK: [[merge]] = OpLabel
547; CHECK-NEXT: OpBranch [[if_merge]]
548
549; Loop specialized for 0.
550; CHECK: [[loop_0]] = OpLabel
551; CHECK-NEXT: OpBranch [[loop:%\w+]]
552; CHECK: [[loop]] = OpLabel
553; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_0]] [[iv_i:%\w+]] [[continue:%\w+]]
554; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
555; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
556; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
557; CHECK: [[loop_body]] = OpLabel
558; CHECK-NEXT: OpSelectionMerge
559; CHECK-NEXT: OpSwitch %int_0
560; CHECK: [[merge]] = OpLabel
561; CHECK-NEXT: OpBranch [[if_merge]]
562
563; Loop specialized for the default case.
564; CHECK: [[default]] = OpLabel
565; CHECK-NEXT: OpBranch [[loop:%\w+]]
566; CHECK: [[loop]] = OpLabel
567; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[default]] [[iv_i:%\w+]] [[continue:%\w+]]
568; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
569; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}}
570; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]]
571; CHECK: [[loop_body]] = OpLabel
572; CHECK-NEXT: OpSelectionMerge
573; CHECK-NEXT: OpSwitch %uint_3
574; CHECK: [[merge]] = OpLabel
575; CHECK-NEXT: OpBranch [[if_merge]]
576
577; CHECK: [[if_merge]] = OpLabel
578; CHECK-NEXT: OpReturn
579               OpCapability Shader
580          %1 = OpExtInstImport "GLSL.std.450"
581               OpMemoryModel Logical GLSL450
582               OpEntryPoint Fragment %main "main" %c
583               OpExecutionMode %main OriginUpperLeft
584               OpSource GLSL 330
585               OpName %main "main"
586               OpName %c "c"
587               OpDecorate %c Location 0
588               OpDecorate %20 Uniform
589       %void = OpTypeVoid
590          %3 = OpTypeFunction %void
591        %int = OpTypeInt 32 1
592%_ptr_Function_int = OpTypePointer Function %int
593      %int_0 = OpConstant %int 0
594      %float = OpTypeFloat 32
595    %v4float = OpTypeVector %float 4
596%_ptr_Input_v4float = OpTypePointer Input %v4float
597          %c = OpVariable %_ptr_Input_v4float Input
598       %uint = OpTypeInt 32 0
599     %uint_0 = OpConstant %uint 0
600%_ptr_Input_float = OpTypePointer Input %float
601     %int_10 = OpConstant %int 10
602       %bool = OpTypeBool
603      %int_1 = OpConstant %int 1
604%_ptr_Function_bool = OpTypePointer Function %bool
605       %main = OpFunction %void None %3
606          %5 = OpLabel
607         %18 = OpAccessChain %_ptr_Input_float %c %uint_0
608         %19 = OpLoad %float %18
609         %20 = OpConvertFToS %int %19
610               OpBranch %21
611         %21 = OpLabel
612         %49 = OpPhi %int %int_0 %5 %43 %24
613               OpLoopMerge %23 %24 None
614               OpBranch %25
615         %25 = OpLabel
616         %29 = OpSLessThan %bool %49 %int_10
617               OpBranchConditional %29 %22 %23
618         %22 = OpLabel
619               OpSelectionMerge %35 None
620               OpSwitch %20 %34 0 %31 1 %32 2 %33
621         %34 = OpLabel
622               OpBranch %35
623         %31 = OpLabel
624               OpReturn
625         %32 = OpLabel
626               OpKill
627         %33 = OpLabel
628               OpBranch %35
629         %35 = OpLabel
630               OpBranch %24
631         %24 = OpLabel
632         %43 = OpIAdd %int %49 %int_1
633               OpBranch %21
634         %23 = OpLabel
635               OpReturn
636               OpFunctionEnd
637  )";
638
639  SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
640}
641
642/*
643Generated from the following GLSL + --eliminate-local-multi-store
644
645#version 440 core
646layout(location = 0)in vec4 c;
647void main() {
648  int i = 0;
649  int j = 0;
650  int k = 0;
651  bool cond = c[0] == 0;
652  for (; i < 10; i++) {
653    for (; j < 10; j++) {
654      if (cond) {
655        i++;
656      } else {
657        j++;
658      }
659    }
660  }
661}
662*/
663TEST_F(UnswitchTest, UnSwitchNested) {
664  // Test that an branch can be unswitched out of two nested loops.
665  const std::string text = R"(
666; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual
667; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None
668; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]]
669
670; Loop specialized for false
671; CHECK: [[loop_f]] = OpLabel
672; CHECK-NEXT: OpBranch [[loop:%\w+]]
673; CHECK: [[loop]] = OpLabel
674; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_f]] {{%\w+}} [[continue:%\w+]]
675; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_f]] {{%\w+}} [[continue]]
676; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
677; CHECK-NOT: [[merge]] = OpLabel
678; CHECK: OpLoopMerge
679; CHECK-NEXT: OpBranch [[bb1:%\w+]]
680; CHECK: [[bb1]] = OpLabel
681; CHECK-NEXT: OpSLessThan
682; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb2:%\w+]]
683; CHECK: [[bb2]] = OpLabel
684; CHECK-NEXT: OpSelectionMerge
685; CHECK-NEXT: OpBranchConditional %false
686; CHECK: [[merge]] = OpLabel
687
688; Loop specialized for true.  Same as first loop except the branch condition is true.
689; CHECK: [[loop_t]] = OpLabel
690; CHECK-NEXT: OpBranch [[loop:%\w+]]
691; CHECK: [[loop]] = OpLabel
692; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_t]] {{%\w+}} [[continue:%\w+]]
693; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_t]] {{%\w+}} [[continue]]
694; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
695; CHECK-NOT: [[merge]] = OpLabel
696; CHECK: OpLoopMerge
697; CHECK-NEXT: OpBranch [[bb1:%\w+]]
698; CHECK: [[bb1]] = OpLabel
699; CHECK-NEXT: OpSLessThan
700; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb2:%\w+]]
701; CHECK: [[bb2]] = OpLabel
702; CHECK-NEXT: OpSelectionMerge
703; CHECK-NEXT: OpBranchConditional %true
704; CHECK: [[merge]] = OpLabel
705
706               OpCapability Shader
707          %1 = OpExtInstImport "GLSL.std.450"
708               OpMemoryModel Logical GLSL450
709               OpEntryPoint Fragment %main "main" %c
710               OpExecutionMode %main OriginUpperLeft
711               OpSource GLSL 440
712               OpName %main "main"
713               OpName %c "c"
714               OpDecorate %c Location 0
715               OpDecorate %25 Uniform
716       %void = OpTypeVoid
717          %3 = OpTypeFunction %void
718        %int = OpTypeInt 32 1
719%_ptr_Function_int = OpTypePointer Function %int
720      %int_0 = OpConstant %int 0
721       %bool = OpTypeBool
722%_ptr_Function_bool = OpTypePointer Function %bool
723      %float = OpTypeFloat 32
724    %v4float = OpTypeVector %float 4
725%_ptr_Input_v4float = OpTypePointer Input %v4float
726          %c = OpVariable %_ptr_Input_v4float Input
727       %uint = OpTypeInt 32 0
728     %uint_0 = OpConstant %uint 0
729%_ptr_Input_float = OpTypePointer Input %float
730    %float_0 = OpConstant %float 0
731     %int_10 = OpConstant %int 10
732      %int_1 = OpConstant %int 1
733       %main = OpFunction %void None %3
734          %5 = OpLabel
735         %22 = OpAccessChain %_ptr_Input_float %c %uint_0
736         %23 = OpLoad %float %22
737         %25 = OpFOrdEqual %bool %23 %float_0
738               OpBranch %26
739         %26 = OpLabel
740         %67 = OpPhi %int %int_0 %5 %52 %29
741         %68 = OpPhi %int %int_0 %5 %70 %29
742               OpLoopMerge %28 %29 None
743               OpBranch %30
744         %30 = OpLabel
745         %33 = OpSLessThan %bool %67 %int_10
746               OpBranchConditional %33 %27 %28
747         %27 = OpLabel
748               OpBranch %34
749         %34 = OpLabel
750         %69 = OpPhi %int %67 %27 %46 %37
751         %70 = OpPhi %int %68 %27 %50 %37
752               OpLoopMerge %36 %37 None
753               OpBranch %38
754         %38 = OpLabel
755         %40 = OpSLessThan %bool %70 %int_10
756               OpBranchConditional %40 %35 %36
757         %35 = OpLabel
758               OpSelectionMerge %43 None
759               OpBranchConditional %25 %42 %47
760         %42 = OpLabel
761         %46 = OpIAdd %int %69 %int_1
762               OpBranch %43
763         %47 = OpLabel
764               OpReturn
765         %43 = OpLabel
766               OpBranch %37
767         %37 = OpLabel
768         %50 = OpIAdd %int %70 %int_1
769               OpBranch %34
770         %36 = OpLabel
771               OpBranch %29
772         %29 = OpLabel
773         %52 = OpIAdd %int %69 %int_1
774               OpBranch %26
775         %28 = OpLabel
776               OpReturn
777               OpFunctionEnd
778)";
779
780  SinglePassRunAndMatch<LoopUnswitchPass>(text, true);
781}
782
783/*
784Generated from the following GLSL + --eliminate-local-multi-store
785
786#version 330 core
787in vec4 c;
788void main() {
789  bool cond = false;
790  if (c[0] == 0) {
791     cond = c[1] == 0;
792  } else {
793     cond = c[2] == 0;
794  }
795  for (int i = 0; i < 10; i++) {
796    if (cond) {
797      i++;
798    }
799  }
800}
801*/
802TEST_F(UnswitchTest, UnswitchNotUniform) {
803  // Check that the unswitch is not triggered (condition loop invariant but not
804  // uniform)
805  const std::string text = R"(
806               OpCapability Shader
807          %1 = OpExtInstImport "GLSL.std.450"
808               OpMemoryModel Logical GLSL450
809               OpEntryPoint Fragment %main "main" %c
810               OpExecutionMode %main OriginUpperLeft
811               OpSource GLSL 330
812               OpName %main "main"
813               OpName %c "c"
814               OpDecorate %c Location 0
815       %void = OpTypeVoid
816          %3 = OpTypeFunction %void
817       %bool = OpTypeBool
818%_ptr_Function_bool = OpTypePointer Function %bool
819      %float = OpTypeFloat 32
820    %v4float = OpTypeVector %float 4
821%_ptr_Input_v4float = OpTypePointer Input %v4float
822          %c = OpVariable %_ptr_Input_v4float Input
823       %uint = OpTypeInt 32 0
824     %uint_0 = OpConstant %uint 0
825%_ptr_Input_float = OpTypePointer Input %float
826    %float_0 = OpConstant %float 0
827     %uint_1 = OpConstant %uint 1
828     %uint_2 = OpConstant %uint 2
829        %int = OpTypeInt 32 1
830%_ptr_Function_int = OpTypePointer Function %int
831      %int_0 = OpConstant %int 0
832     %int_10 = OpConstant %int 10
833      %int_1 = OpConstant %int 1
834       %main = OpFunction %void None %3
835          %5 = OpLabel
836         %17 = OpAccessChain %_ptr_Input_float %c %uint_0
837         %18 = OpLoad %float %17
838         %20 = OpFOrdEqual %bool %18 %float_0
839               OpSelectionMerge %22 None
840               OpBranchConditional %20 %21 %27
841         %21 = OpLabel
842         %24 = OpAccessChain %_ptr_Input_float %c %uint_1
843         %25 = OpLoad %float %24
844         %26 = OpFOrdEqual %bool %25 %float_0
845               OpBranch %22
846         %27 = OpLabel
847         %29 = OpAccessChain %_ptr_Input_float %c %uint_2
848         %30 = OpLoad %float %29
849         %31 = OpFOrdEqual %bool %30 %float_0
850               OpBranch %22
851         %22 = OpLabel
852         %52 = OpPhi %bool %26 %21 %31 %27
853               OpBranch %36
854         %36 = OpLabel
855         %53 = OpPhi %int %int_0 %22 %51 %39
856               OpLoopMerge %38 %39 None
857               OpBranch %40
858         %40 = OpLabel
859         %43 = OpSLessThan %bool %53 %int_10
860               OpBranchConditional %43 %37 %38
861         %37 = OpLabel
862               OpSelectionMerge %46 None
863               OpBranchConditional %52 %45 %46
864         %45 = OpLabel
865         %49 = OpIAdd %int %53 %int_1
866               OpBranch %46
867         %46 = OpLabel
868         %54 = OpPhi %int %53 %37 %49 %45
869               OpBranch %39
870         %39 = OpLabel
871         %51 = OpIAdd %int %54 %int_1
872               OpBranch %36
873         %38 = OpLabel
874               OpReturn
875               OpFunctionEnd
876  )";
877
878  auto result =
879      SinglePassRunAndDisassemble<LoopUnswitchPass>(text, true, false);
880
881  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
882}
883
884TEST_F(UnswitchTest, DontUnswitchLatch) {
885  // Check that the unswitch is not triggered for the latch branch.
886  const std::string text = R"(
887         OpCapability Shader
888    %1 = OpExtInstImport "GLSL.std.450"
889         OpMemoryModel Logical GLSL450
890         OpEntryPoint Fragment %4 "main"
891         OpExecutionMode %4 OriginUpperLeft
892         OpSource ESSL 310
893 %void = OpTypeVoid
894    %3 = OpTypeFunction %void
895 %bool = OpTypeBool
896%false = OpConstantFalse %bool
897    %4 = OpFunction %void None %3
898    %5 = OpLabel
899         OpBranch %6
900    %6 = OpLabel
901         OpLoopMerge %8 %9 None
902         OpBranch %7
903    %7 = OpLabel
904         OpBranch %9
905    %9 = OpLabel
906         OpBranchConditional %false %6 %8
907    %8 = OpLabel
908         OpReturn
909         OpFunctionEnd
910  )";
911
912  auto result =
913      SinglePassRunAndDisassemble<LoopUnswitchPass>(text, true, false);
914  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
915}
916
917TEST_F(UnswitchTest, DontUnswitchConstantCondition) {
918  const std::string text = R"(
919               OpCapability Shader
920          %1 = OpExtInstImport "GLSL.std.450"
921               OpMemoryModel Logical GLSL450
922               OpEntryPoint Fragment %main "main"
923               OpExecutionMode %main OriginLowerLeft
924               OpSource GLSL 450
925               OpName %main "main"
926       %void = OpTypeVoid
927          %4 = OpTypeFunction %void
928        %int = OpTypeInt 32 1
929      %int_0 = OpConstant %int 0
930       %bool = OpTypeBool
931       %true = OpConstantTrue %bool
932      %int_1 = OpConstant %int 1
933       %main = OpFunction %void None %4
934         %10 = OpLabel
935               OpBranch %11
936         %11 = OpLabel
937         %12 = OpPhi %int %int_0 %10 %13 %14
938               OpLoopMerge %15 %14 None
939               OpBranch %16
940         %16 = OpLabel
941         %17 = OpSLessThan %bool %12 %int_1
942               OpBranchConditional %17 %18 %15
943         %18 = OpLabel
944               OpSelectionMerge %19 None
945               OpBranchConditional %true %20 %19
946         %20 = OpLabel
947         %21 = OpIAdd %int %12 %int_1
948               OpBranch %19
949         %19 = OpLabel
950         %22 = OpPhi %int %21 %20 %12 %18
951               OpBranch %14
952         %14 = OpLabel
953         %13 = OpIAdd %int %22 %int_1
954               OpBranch %11
955         %15 = OpLabel
956               OpReturn
957               OpFunctionEnd
958  )";
959
960  auto result =
961      SinglePassRunAndDisassemble<LoopUnswitchPass>(text, true, false);
962  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
963}
964
965}  // namespace
966}  // namespace opt
967}  // namespace spvtools
968