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