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 "test/opt/pass_fixture.h"
18#include "test/opt/pass_utils.h"
19
20namespace spvtools {
21namespace opt {
22namespace {
23
24using IfConversionTest = PassTest<::testing::Test>;
25
26TEST_F(IfConversionTest, TestSimpleIfThenElse) {
27  const std::string text = R"(
28; CHECK: OpSelectionMerge [[merge:%\w+]]
29; CHECK: [[merge]] = OpLabel
30; CHECK-NOT: OpPhi
31; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1
32; CHECK OpStore {{%\w+}} [[sel]]
33OpCapability Shader
34OpMemoryModel Logical GLSL450
35OpEntryPoint Vertex %1 "func" %2
36%void = OpTypeVoid
37%bool = OpTypeBool
38%true = OpConstantTrue %bool
39%uint = OpTypeInt 32 0
40%uint_0 = OpConstant %uint 0
41%uint_1 = OpConstant %uint 1
42%_ptr_Output_uint = OpTypePointer Output %uint
43%2 = OpVariable %_ptr_Output_uint Output
44%11 = OpTypeFunction %void
45%1 = OpFunction %void None %11
46%12 = OpLabel
47OpSelectionMerge %14 None
48OpBranchConditional %true %15 %16
49%15 = OpLabel
50OpBranch %14
51%16 = OpLabel
52OpBranch %14
53%14 = OpLabel
54%18 = OpPhi %uint %uint_0 %15 %uint_1 %16
55OpStore %2 %18
56OpReturn
57OpFunctionEnd
58)";
59
60  SinglePassRunAndMatch<IfConversion>(text, true);
61}
62
63TEST_F(IfConversionTest, TestSimpleHalfIfTrue) {
64  const std::string text = R"(
65; CHECK: OpSelectionMerge [[merge:%\w+]]
66; CHECK: [[merge]] = OpLabel
67; CHECK-NOT: OpPhi
68; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1
69; CHECK OpStore {{%\w+}} [[sel]]
70OpCapability Shader
71OpMemoryModel Logical GLSL450
72OpEntryPoint Vertex %1 "func" %2
73%void = OpTypeVoid
74%bool = OpTypeBool
75%true = OpConstantTrue %bool
76%uint = OpTypeInt 32 0
77%uint_0 = OpConstant %uint 0
78%uint_1 = OpConstant %uint 1
79%_ptr_Output_uint = OpTypePointer Output %uint
80%2 = OpVariable %_ptr_Output_uint Output
81%11 = OpTypeFunction %void
82%1 = OpFunction %void None %11
83%12 = OpLabel
84OpSelectionMerge %14 None
85OpBranchConditional %true %15 %14
86%15 = OpLabel
87OpBranch %14
88%14 = OpLabel
89%18 = OpPhi %uint %uint_0 %15 %uint_1 %12
90OpStore %2 %18
91OpReturn
92OpFunctionEnd
93)";
94
95  SinglePassRunAndMatch<IfConversion>(text, true);
96}
97
98TEST_F(IfConversionTest, TestSimpleHalfIfExtraBlock) {
99  const std::string text = R"(
100; CHECK: OpSelectionMerge [[merge:%\w+]]
101; CHECK: [[merge]] = OpLabel
102; CHECK-NOT: OpPhi
103; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1
104; CHECK OpStore {{%\w+}} [[sel]]
105OpCapability Shader
106OpMemoryModel Logical GLSL450
107OpEntryPoint Vertex %1 "func" %2
108%void = OpTypeVoid
109%bool = OpTypeBool
110%true = OpConstantTrue %bool
111%uint = OpTypeInt 32 0
112%uint_0 = OpConstant %uint 0
113%uint_1 = OpConstant %uint 1
114%_ptr_Output_uint = OpTypePointer Output %uint
115%2 = OpVariable %_ptr_Output_uint Output
116%11 = OpTypeFunction %void
117%1 = OpFunction %void None %11
118%12 = OpLabel
119OpSelectionMerge %14 None
120OpBranchConditional %true %15 %14
121%15 = OpLabel
122OpBranch %16
123%16 = OpLabel
124OpBranch %14
125%14 = OpLabel
126%18 = OpPhi %uint %uint_0 %15 %uint_1 %12
127OpStore %2 %18
128OpReturn
129OpFunctionEnd
130)";
131
132  SinglePassRunAndMatch<IfConversion>(text, true);
133}
134
135TEST_F(IfConversionTest, TestSimpleHalfIfFalse) {
136  const std::string text = R"(
137; CHECK: OpSelectionMerge [[merge:%\w+]]
138; CHECK: [[merge]] = OpLabel
139; CHECK-NOT: OpPhi
140; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1
141; CHECK OpStore {{%\w+}} [[sel]]
142OpCapability Shader
143OpMemoryModel Logical GLSL450
144OpEntryPoint Vertex %1 "func" %2
145%void = OpTypeVoid
146%bool = OpTypeBool
147%true = OpConstantTrue %bool
148%uint = OpTypeInt 32 0
149%uint_0 = OpConstant %uint 0
150%uint_1 = OpConstant %uint 1
151%_ptr_Output_uint = OpTypePointer Output %uint
152%2 = OpVariable %_ptr_Output_uint Output
153%11 = OpTypeFunction %void
154%1 = OpFunction %void None %11
155%12 = OpLabel
156OpSelectionMerge %14 None
157OpBranchConditional %true %14 %15
158%15 = OpLabel
159OpBranch %14
160%14 = OpLabel
161%18 = OpPhi %uint %uint_0 %12 %uint_1 %15
162OpStore %2 %18
163OpReturn
164OpFunctionEnd
165)";
166
167  SinglePassRunAndMatch<IfConversion>(text, true);
168}
169
170TEST_F(IfConversionTest, TestVectorSplat) {
171  const std::string text = R"(
172; CHECK: [[bool_vec:%\w+]] = OpTypeVector %bool 2
173; CHECK: OpSelectionMerge [[merge:%\w+]]
174; CHECK: [[merge]] = OpLabel
175; CHECK-NOT: OpPhi
176; CHECK: [[comp:%\w+]] = OpCompositeConstruct [[bool_vec]] %true %true
177; CHECK: [[sel:%\w+]] = OpSelect {{%\w+}} [[comp]]
178; CHECK OpStore {{%\w+}} [[sel]]
179OpCapability Shader
180OpMemoryModel Logical GLSL450
181OpEntryPoint Vertex %1 "func" %2
182%void = OpTypeVoid
183%bool = OpTypeBool
184%true = OpConstantTrue %bool
185%uint = OpTypeInt 32 0
186%uint_0 = OpConstant %uint 0
187%uint_1 = OpConstant %uint 1
188%uint_vec2 = OpTypeVector %uint 2
189%vec2_01 = OpConstantComposite %uint_vec2 %uint_0 %uint_1
190%vec2_10 = OpConstantComposite %uint_vec2 %uint_1 %uint_0
191%_ptr_Output_uint = OpTypePointer Output %uint_vec2
192%2 = OpVariable %_ptr_Output_uint Output
193%11 = OpTypeFunction %void
194%1 = OpFunction %void None %11
195%12 = OpLabel
196OpSelectionMerge %14 None
197OpBranchConditional %true %15 %16
198%15 = OpLabel
199OpBranch %14
200%16 = OpLabel
201OpBranch %14
202%14 = OpLabel
203%18 = OpPhi %uint_vec2 %vec2_01 %15 %vec2_10 %16
204OpStore %2 %18
205OpReturn
206OpFunctionEnd
207)";
208
209  SinglePassRunAndMatch<IfConversion>(text, true);
210}
211
212TEST_F(IfConversionTest, CodeMotionSameValue) {
213  const std::string text = R"(
214; CHECK: [[var:%\w+]] = OpVariable
215; CHECK: OpFunction
216; CHECK: OpLabel
217; CHECK-NOT: OpLabel
218; CHECK: [[add:%\w+]] = OpIAdd %uint %uint_0 %uint_1
219; CHECK: OpSelectionMerge [[merge_lab:%\w+]] None
220; CHECK-NEXT: OpBranchConditional
221; CHECK: [[merge_lab]] = OpLabel
222; CHECK-NOT: OpLabel
223; CHECK: OpStore [[var]] [[add]]
224                    OpCapability Shader
225                    OpMemoryModel Logical GLSL450
226                    OpEntryPoint Vertex %1 "func" %2
227            %void = OpTypeVoid
228            %uint = OpTypeInt 32 0
229          %uint_0 = OpConstant %uint 0
230          %uint_1 = OpConstant %uint 1
231%_ptr_Output_uint = OpTypePointer Output %uint
232               %2 = OpVariable %_ptr_Output_uint Output
233               %8 = OpTypeFunction %void
234            %bool = OpTypeBool
235            %true = OpConstantTrue %bool
236               %1 = OpFunction %void None %8
237              %11 = OpLabel
238                    OpSelectionMerge %12 None
239                    OpBranchConditional %true %13 %15
240              %13 = OpLabel
241              %14 = OpIAdd %uint %uint_0 %uint_1
242                    OpBranch %12
243              %15 = OpLabel
244              %16 = OpIAdd %uint %uint_0 %uint_1
245                    OpBranch %12
246              %12 = OpLabel
247              %17 = OpPhi %uint %16 %15 %14 %13
248                    OpStore %2 %17
249                    OpReturn
250                    OpFunctionEnd
251)";
252
253  SinglePassRunAndMatch<IfConversion>(text, true);
254}
255
256TEST_F(IfConversionTest, CodeMotionMultipleInstructions) {
257  const std::string text = R"(
258; CHECK: [[var:%\w+]] = OpVariable
259; CHECK: OpFunction
260; CHECK: OpLabel
261; CHECK-NOT: OpLabel
262; CHECK: [[a1:%\w+]] = OpIAdd %uint %uint_0 %uint_1
263; CHECK: [[a2:%\w+]] = OpIAdd %uint [[a1]] %uint_1
264; CHECK: OpSelectionMerge [[merge_lab:%\w+]] None
265; CHECK-NEXT: OpBranchConditional
266; CHECK: [[merge_lab]] = OpLabel
267; CHECK-NOT: OpLabel
268; CHECK: OpStore [[var]] [[a2]]
269                    OpCapability Shader
270                    OpMemoryModel Logical GLSL450
271                    OpEntryPoint Vertex %1 "func" %2
272            %void = OpTypeVoid
273            %uint = OpTypeInt 32 0
274          %uint_0 = OpConstant %uint 0
275          %uint_1 = OpConstant %uint 1
276%_ptr_Output_uint = OpTypePointer Output %uint
277               %2 = OpVariable %_ptr_Output_uint Output
278               %8 = OpTypeFunction %void
279            %bool = OpTypeBool
280            %true = OpConstantTrue %bool
281               %1 = OpFunction %void None %8
282              %11 = OpLabel
283                    OpSelectionMerge %12 None
284                    OpBranchConditional %true %13 %15
285              %13 = OpLabel
286              %a1 = OpIAdd %uint %uint_0 %uint_1
287              %a2 = OpIAdd %uint %a1 %uint_1
288                    OpBranch %12
289              %15 = OpLabel
290              %b1 = OpIAdd %uint %uint_0 %uint_1
291              %b2 = OpIAdd %uint %b1 %uint_1
292                    OpBranch %12
293              %12 = OpLabel
294              %17 = OpPhi %uint %b2 %15 %a2 %13
295                    OpStore %2 %17
296                    OpReturn
297                    OpFunctionEnd
298)";
299
300  SinglePassRunAndMatch<IfConversion>(text, true);
301}
302
303TEST_F(IfConversionTest, NoCommonDominator) {
304  const std::string text = R"(OpCapability Shader
305OpMemoryModel Logical GLSL450
306OpEntryPoint Vertex %1 "func" %2
307%void = OpTypeVoid
308%uint = OpTypeInt 32 0
309%uint_0 = OpConstant %uint 0
310%uint_1 = OpConstant %uint 1
311%_ptr_Output_uint = OpTypePointer Output %uint
312%2 = OpVariable %_ptr_Output_uint Output
313%8 = OpTypeFunction %void
314%1 = OpFunction %void None %8
315%9 = OpLabel
316OpBranch %10
317%11 = OpLabel
318OpBranch %10
319%10 = OpLabel
320%12 = OpPhi %uint %uint_0 %9 %uint_1 %11
321OpStore %2 %12
322OpReturn
323OpFunctionEnd
324)";
325
326  SinglePassRunAndCheck<IfConversion>(text, text, true, true);
327}
328
329TEST_F(IfConversionTest, DontFlatten) {
330  const std::string text = R"(OpCapability Shader
331OpMemoryModel Logical GLSL450
332OpEntryPoint Vertex %1 "func" %2
333%void = OpTypeVoid
334%bool = OpTypeBool
335%true = OpConstantTrue %bool
336%uint = OpTypeInt 32 0
337%uint_0 = OpConstant %uint 0
338%uint_1 = OpConstant %uint 1
339%v2uint = OpTypeVector %uint 2
340%10 = OpConstantComposite %v2uint %uint_0 %uint_1
341%11 = OpConstantComposite %v2uint %uint_1 %uint_0
342%_ptr_Output_v2uint = OpTypePointer Output %v2uint
343%2 = OpVariable %_ptr_Output_v2uint Output
344%13 = OpTypeFunction %void
345%1 = OpFunction %void None %13
346%14 = OpLabel
347OpSelectionMerge %15 DontFlatten
348OpBranchConditional %true %16 %17
349%16 = OpLabel
350OpBranch %15
351%17 = OpLabel
352OpBranch %15
353%15 = OpLabel
354%18 = OpPhi %v2uint %10 %16 %11 %17
355OpStore %2 %18
356OpReturn
357OpFunctionEnd
358)";
359
360  SinglePassRunAndCheck<IfConversion>(text, text, true, true);
361}
362
363TEST_F(IfConversionTest, LoopUntouched) {
364  const std::string text = R"(OpCapability Shader
365OpMemoryModel Logical GLSL450
366OpEntryPoint Vertex %1 "func" %2
367%void = OpTypeVoid
368%uint = OpTypeInt 32 0
369%uint_0 = OpConstant %uint 0
370%uint_1 = OpConstant %uint 1
371%_ptr_Output_uint = OpTypePointer Output %uint
372%2 = OpVariable %_ptr_Output_uint Output
373%8 = OpTypeFunction %void
374%bool = OpTypeBool
375%true = OpConstantTrue %bool
376%1 = OpFunction %void None %8
377%11 = OpLabel
378OpBranch %12
379%12 = OpLabel
380%13 = OpPhi %uint %uint_0 %11 %uint_1 %12
381OpLoopMerge %14 %12 None
382OpBranchConditional %true %14 %12
383%14 = OpLabel
384OpStore %2 %13
385OpReturn
386OpFunctionEnd
387)";
388
389  SinglePassRunAndCheck<IfConversion>(text, text, true, true);
390}
391
392TEST_F(IfConversionTest, TooManyPredecessors) {
393  const std::string text = R"(OpCapability Shader
394OpMemoryModel Logical GLSL450
395OpEntryPoint Vertex %1 "func" %2
396%void = OpTypeVoid
397%uint = OpTypeInt 32 0
398%uint_0 = OpConstant %uint 0
399%uint_1 = OpConstant %uint 1
400%_ptr_Output_uint = OpTypePointer Output %uint
401%2 = OpVariable %_ptr_Output_uint Output
402%8 = OpTypeFunction %void
403%bool = OpTypeBool
404%true = OpConstantTrue %bool
405%1 = OpFunction %void None %8
406%11 = OpLabel
407OpSelectionMerge %12 None
408OpBranchConditional %true %13 %12
409%13 = OpLabel
410OpBranchConditional %true %14 %12
411%14 = OpLabel
412OpBranch %12
413%12 = OpLabel
414%15 = OpPhi %uint %uint_0 %11 %uint_0 %13 %uint_1 %14
415OpStore %2 %15
416OpReturn
417OpFunctionEnd
418)";
419
420  SinglePassRunAndCheck<IfConversion>(text, text, true, true);
421}
422
423TEST_F(IfConversionTest, NoCodeMotion) {
424  const std::string text = R"(OpCapability Shader
425OpMemoryModel Logical GLSL450
426OpEntryPoint Vertex %1 "func" %2
427%void = OpTypeVoid
428%uint = OpTypeInt 32 0
429%uint_0 = OpConstant %uint 0
430%uint_1 = OpConstant %uint 1
431%_ptr_Output_uint = OpTypePointer Output %uint
432%2 = OpVariable %_ptr_Output_uint Output
433%8 = OpTypeFunction %void
434%bool = OpTypeBool
435%true = OpConstantTrue %bool
436%1 = OpFunction %void None %8
437%11 = OpLabel
438OpSelectionMerge %12 None
439OpBranchConditional %true %13 %12
440%13 = OpLabel
441%14 = OpIAdd %uint %uint_0 %uint_1
442OpBranch %12
443%12 = OpLabel
444%15 = OpPhi %uint %uint_0 %11 %14 %13
445OpStore %2 %15
446OpReturn
447OpFunctionEnd
448)";
449
450  SinglePassRunAndCheck<IfConversion>(text, text, true, true);
451}
452
453TEST_F(IfConversionTest, NoCodeMotionImmovableInst) {
454  const std::string text = R"(OpCapability Shader
455OpMemoryModel Logical GLSL450
456OpEntryPoint Vertex %1 "func" %2
457%void = OpTypeVoid
458%uint = OpTypeInt 32 0
459%uint_0 = OpConstant %uint 0
460%uint_1 = OpConstant %uint 1
461%_ptr_Output_uint = OpTypePointer Output %uint
462%2 = OpVariable %_ptr_Output_uint Output
463%8 = OpTypeFunction %void
464%bool = OpTypeBool
465%true = OpConstantTrue %bool
466%1 = OpFunction %void None %8
467%11 = OpLabel
468OpSelectionMerge %12 None
469OpBranchConditional %true %13 %14
470%13 = OpLabel
471OpSelectionMerge %15 None
472OpBranchConditional %true %16 %15
473%16 = OpLabel
474%17 = OpIAdd %uint %uint_0 %uint_1
475OpBranch %15
476%15 = OpLabel
477%18 = OpPhi %uint %uint_0 %13 %17 %16
478%19 = OpIAdd %uint %18 %uint_1
479OpBranch %12
480%14 = OpLabel
481OpSelectionMerge %20 None
482OpBranchConditional %true %21 %20
483%21 = OpLabel
484%22 = OpIAdd %uint %uint_0 %uint_1
485OpBranch %20
486%20 = OpLabel
487%23 = OpPhi %uint %uint_0 %14 %22 %21
488%24 = OpIAdd %uint %23 %uint_1
489OpBranch %12
490%12 = OpLabel
491%25 = OpPhi %uint %24 %20 %19 %15
492OpStore %2 %25
493OpReturn
494OpFunctionEnd
495)";
496
497  SinglePassRunAndCheck<IfConversion>(text, text, true, true);
498}
499
500TEST_F(IfConversionTest, InvalidCommonDominator) {
501  const std::string text = R"(OpCapability Shader
502OpCapability Linkage
503OpMemoryModel Logical GLSL450
504%void = OpTypeVoid
505%float = OpTypeFloat 32
506%float_0 = OpConstant %float 0
507%float_1 = OpConstant %float 1
508%bool = OpTypeBool
509%true = OpConstantTrue %bool
510%1 = OpTypeFunction %void
511%2 = OpFunction %void None %1
512%3 = OpLabel
513OpBranch %4
514%4 = OpLabel
515OpLoopMerge %5 %6 None
516OpBranch %7
517%7 = OpLabel
518OpSelectionMerge %8 None
519OpBranchConditional %true %8 %9
520%9 = OpLabel
521OpSelectionMerge %10 None
522OpBranchConditional %true %10 %5
523%10 = OpLabel
524OpBranch %8
525%8 = OpLabel
526OpBranch %6
527%6 = OpLabel
528OpBranchConditional %true %4 %5
529%5 = OpLabel
530%11 = OpPhi %float %float_0 %6 %float_1 %9
531OpReturn
532OpFunctionEnd
533)";
534
535  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
536  SinglePassRunAndCheck<IfConversion>(text, text, true, true);
537}
538
539TEST_F(IfConversionTest, DebugInfoSimpleIfThenElse) {
540  // When it replaces an OpPhi with OpSelect, the new OpSelect must have
541  // the same scope and line information with the OpPhi.
542  const std::string text = R"(
543; CHECK: OpSelectionMerge [[merge:%\w+]]
544; CHECK: [[merge]] = OpLabel
545; CHECK-NOT: OpPhi
546; CHECK: DebugScope
547; CHECK-NEXT: OpLine {{%\w+}} 3 7
548; CHECK-NEXT: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1
549; CHECK-NEXT: DebugValue {{%\w+}} [[sel]]
550; CHECK: OpStore {{%\w+}} [[sel]]
551OpCapability Shader
552%ext = OpExtInstImport "OpenCL.DebugInfo.100"
553OpMemoryModel Logical GLSL450
554OpEntryPoint Vertex %1 "func" %2
555%name = OpString "test"
556%void = OpTypeVoid
557%bool = OpTypeBool
558%true = OpConstantTrue %bool
559%uint = OpTypeInt 32 0
560%uint_0 = OpConstant %uint 0
561%uint_1 = OpConstant %uint 1
562%uint_32 = OpConstant %uint 32
563%_ptr_Output_uint = OpTypePointer Output %uint
564%2 = OpVariable %_ptr_Output_uint Output
565%11 = OpTypeFunction %void
566%null_expr = OpExtInst %void %ext DebugExpression
567%src = OpExtInst %void %ext DebugSource %name
568%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
569%dbg_tf = OpExtInst %void %ext DebugTypeBasic %name %uint_32 Float
570%dbg_f = OpExtInst %void %ext DebugLocalVariable %name %dbg_tf %src 0 0 %cu FlagIsLocal
571%1 = OpFunction %void None %11
572%12 = OpLabel
573OpSelectionMerge %14 None
574OpBranchConditional %true %15 %16
575%15 = OpLabel
576OpBranch %14
577%16 = OpLabel
578OpBranch %14
579%14 = OpLabel
580%scope = OpExtInst %void %ext DebugScope %cu
581OpLine %name 3 7
582%18 = OpPhi %uint %uint_0 %15 %uint_1 %16
583%value = OpExtInst %void %ext DebugValue %dbg_f %18 %null_expr
584OpStore %2 %18
585OpReturn
586OpFunctionEnd
587)";
588
589  SinglePassRunAndMatch<IfConversion>(text, true);
590}
591
592TEST_F(IfConversionTest, MultipleEdgesFromSameBlock) {
593  // If a block has two out going edges that go to the same block, then there
594  // can be an OpPhi instruction with fewer entries than the number of incoming
595  // edges.  This must be handled.
596  const std::string text = R"(OpCapability Shader
597%1 = OpExtInstImport "GLSL.std.450"
598OpMemoryModel Logical GLSL450
599OpEntryPoint Fragment %2 "main"
600OpExecutionMode %2 OriginUpperLeft
601%void = OpTypeVoid
602%4 = OpTypeFunction %void
603%bool = OpTypeBool
604%true = OpConstantTrue %bool
605%true_0 = OpConstantTrue %bool
606%2 = OpFunction %void None %4
607%8 = OpLabel
608OpSelectionMerge %9 None
609OpBranchConditional %true_0 %9 %9
610%9 = OpLabel
611%10 = OpPhi %bool %true %8
612OpReturn
613OpFunctionEnd
614)";
615
616  SinglePassRunAndCheck<IfConversion>(text, text, true, true);
617}
618
619}  // namespace
620}  // namespace opt
621}  // namespace spvtools
622