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 <memory>
16 #include <string>
17 #include <vector>
18
19 #include "gmock/gmock.h"
20 #include "source/opt/loop_unroller.h"
21 #include "source/opt/loop_utils.h"
22 #include "source/opt/pass.h"
23 #include "test/opt/assembly_builder.h"
24 #include "test/opt/function_utils.h"
25 #include "test/opt/pass_fixture.h"
26 #include "test/opt/pass_utils.h"
27
28 namespace spvtools {
29 namespace opt {
30 namespace {
31
32 using ::testing::UnorderedElementsAre;
33 using PassClassTest = PassTest<::testing::Test>;
34
35 /*
36 Generated from the following GLSL
37 #version 330 core
38 layout(location = 0) out vec4 c;
39 void main() {
40 float x[4];
41 for (int i = 0; i < 4; ++i) {
42 x[i] = 1.0f;
43 }
44 }
45 */
TEST_F(PassClassTest,SimpleFullyUnrollTest)46 TEST_F(PassClassTest, SimpleFullyUnrollTest) {
47 // With LocalMultiStoreElimPass
48 const std::string text = R"(
49 OpCapability Shader
50 %1 = OpExtInstImport "GLSL.std.450"
51 OpMemoryModel Logical GLSL450
52 OpEntryPoint Fragment %2 "main" %3
53 OpExecutionMode %2 OriginUpperLeft
54 OpSource GLSL 330
55 OpName %2 "main"
56 OpName %5 "x"
57 OpName %3 "c"
58 OpDecorate %3 Location 0
59 %6 = OpTypeVoid
60 %7 = OpTypeFunction %6
61 %8 = OpTypeInt 32 1
62 %9 = OpTypePointer Function %8
63 %10 = OpConstant %8 0
64 %11 = OpConstant %8 4
65 %12 = OpTypeBool
66 %13 = OpTypeFloat 32
67 %14 = OpTypeInt 32 0
68 %15 = OpConstant %14 4
69 %16 = OpTypeArray %13 %15
70 %17 = OpTypePointer Function %16
71 %18 = OpConstant %13 1
72 %19 = OpTypePointer Function %13
73 %20 = OpConstant %8 1
74 %21 = OpTypeVector %13 4
75 %22 = OpTypePointer Output %21
76 %3 = OpVariable %22 Output
77 %2 = OpFunction %6 None %7
78 %23 = OpLabel
79 %5 = OpVariable %17 Function
80 OpBranch %24
81 %24 = OpLabel
82 %35 = OpPhi %8 %10 %23 %34 %26
83 OpLoopMerge %25 %26 Unroll
84 OpBranch %27
85 %27 = OpLabel
86 %29 = OpSLessThan %12 %35 %11
87 OpBranchConditional %29 %30 %25
88 %30 = OpLabel
89 %32 = OpAccessChain %19 %5 %35
90 OpStore %32 %18
91 OpBranch %26
92 %26 = OpLabel
93 %34 = OpIAdd %8 %35 %20
94 OpBranch %24
95 %25 = OpLabel
96 OpReturn
97 OpFunctionEnd
98 )";
99
100 const std::string output = R"(OpCapability Shader
101 %1 = OpExtInstImport "GLSL.std.450"
102 OpMemoryModel Logical GLSL450
103 OpEntryPoint Fragment %2 "main" %3
104 OpExecutionMode %2 OriginUpperLeft
105 OpSource GLSL 330
106 OpName %2 "main"
107 OpName %4 "x"
108 OpName %3 "c"
109 OpDecorate %3 Location 0
110 %5 = OpTypeVoid
111 %6 = OpTypeFunction %5
112 %7 = OpTypeInt 32 1
113 %8 = OpTypePointer Function %7
114 %9 = OpConstant %7 0
115 %10 = OpConstant %7 4
116 %11 = OpTypeBool
117 %12 = OpTypeFloat 32
118 %13 = OpTypeInt 32 0
119 %14 = OpConstant %13 4
120 %15 = OpTypeArray %12 %14
121 %16 = OpTypePointer Function %15
122 %17 = OpConstant %12 1
123 %18 = OpTypePointer Function %12
124 %19 = OpConstant %7 1
125 %20 = OpTypeVector %12 4
126 %21 = OpTypePointer Output %20
127 %3 = OpVariable %21 Output
128 %2 = OpFunction %5 None %6
129 %22 = OpLabel
130 %4 = OpVariable %16 Function
131 OpBranch %23
132 %23 = OpLabel
133 OpBranch %28
134 %28 = OpLabel
135 %29 = OpSLessThan %11 %9 %10
136 OpBranch %30
137 %30 = OpLabel
138 %31 = OpAccessChain %18 %4 %9
139 OpStore %31 %17
140 OpBranch %26
141 %26 = OpLabel
142 %25 = OpIAdd %7 %9 %19
143 OpBranch %32
144 %32 = OpLabel
145 OpBranch %34
146 %34 = OpLabel
147 %35 = OpSLessThan %11 %25 %10
148 OpBranch %36
149 %36 = OpLabel
150 %37 = OpAccessChain %18 %4 %25
151 OpStore %37 %17
152 OpBranch %38
153 %38 = OpLabel
154 %39 = OpIAdd %7 %25 %19
155 OpBranch %40
156 %40 = OpLabel
157 OpBranch %42
158 %42 = OpLabel
159 %43 = OpSLessThan %11 %39 %10
160 OpBranch %44
161 %44 = OpLabel
162 %45 = OpAccessChain %18 %4 %39
163 OpStore %45 %17
164 OpBranch %46
165 %46 = OpLabel
166 %47 = OpIAdd %7 %39 %19
167 OpBranch %48
168 %48 = OpLabel
169 OpBranch %50
170 %50 = OpLabel
171 %51 = OpSLessThan %11 %47 %10
172 OpBranch %52
173 %52 = OpLabel
174 %53 = OpAccessChain %18 %4 %47
175 OpStore %53 %17
176 OpBranch %54
177 %54 = OpLabel
178 %55 = OpIAdd %7 %47 %19
179 OpBranch %27
180 %27 = OpLabel
181 OpReturn
182 OpFunctionEnd
183 )";
184
185 std::unique_ptr<IRContext> context =
186 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
187 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
188 Module* module = context->module();
189 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
190 << text << std::endl;
191
192 LoopUnroller loop_unroller;
193 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
194 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
195 }
196
197 /*
198 Generated from the following GLSL
199 #version 330 core
200 layout(location = 0) out vec4 c;
201 void main() {
202 float x[4];
203 for (int i = 0; i < 4; ++i) {
204 x[i] = 1.0f;
205 }
206 }
207 */
TEST_F(PassClassTest,SimpleFullyUnrollWithDebugInstructions)208 TEST_F(PassClassTest, SimpleFullyUnrollWithDebugInstructions) {
209 // We must preserve the debug information including OpenCL.DebugInfo.100
210 // instructions and OpLine instructions. Only the first block has
211 // DebugDeclare and DebugValue used for the declaration (i.e., DebugValue
212 // with Deref). Other blocks unrolled from the loop must not contain them.
213 const std::string text = R"(
214 OpCapability Shader
215 %1 = OpExtInstImport "GLSL.std.450"
216 %ext = OpExtInstImport "OpenCL.DebugInfo.100"
217 OpMemoryModel Logical GLSL450
218 OpEntryPoint Fragment %2 "main" %3
219 OpExecutionMode %2 OriginUpperLeft
220 OpSource GLSL 330
221 %file_name = OpString "test"
222 %float_name = OpString "float"
223 %main_name = OpString "main"
224 %f_name = OpString "f"
225 %i_name = OpString "i"
226 OpName %2 "main"
227 OpName %5 "x"
228 OpName %3 "c"
229 OpDecorate %3 Location 0
230 %6 = OpTypeVoid
231 %7 = OpTypeFunction %6
232 %8 = OpTypeInt 32 1
233 %9 = OpTypePointer Function %8
234 %10 = OpConstant %8 0
235 %11 = OpConstant %8 4
236 %12 = OpTypeBool
237 %13 = OpTypeFloat 32
238 %14 = OpTypeInt 32 0
239 %uint_32 = OpConstant %14 32
240 %15 = OpConstant %14 4
241 %16 = OpTypeArray %13 %15
242 %17 = OpTypePointer Function %16
243 %18 = OpConstant %13 1
244 %19 = OpTypePointer Function %13
245 %20 = OpConstant %8 1
246 %21 = OpTypeVector %13 4
247 %22 = OpTypePointer Output %21
248 %3 = OpVariable %22 Output
249 %null_expr = OpExtInst %6 %ext DebugExpression
250 %deref = OpExtInst %6 %ext DebugOperation Deref
251 %deref_expr = OpExtInst %6 %ext DebugExpression %deref
252 %src = OpExtInst %6 %ext DebugSource %file_name
253 %cu = OpExtInst %6 %ext DebugCompilationUnit 1 4 %src HLSL
254 %dbg_tf = OpExtInst %6 %ext DebugTypeBasic %float_name %uint_32 Float
255 %dbg_v4f = OpExtInst %6 %ext DebugTypeVector %dbg_tf 4
256 %main_ty = OpExtInst %6 %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
257 %dbg_main = OpExtInst %6 %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %2
258 %bb = OpExtInst %6 %ext DebugLexicalBlock %src 0 0 %dbg_main
259 %dbg_f = OpExtInst %6 %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal
260 %dbg_i = OpExtInst %6 %ext DebugLocalVariable %i_name %dbg_v4f %src 1 0 %bb FlagIsLocal
261
262 ; CHECK: [[f:%\w+]] = OpString "f"
263 ; CHECK: [[i:%\w+]] = OpString "i"
264 ; CHECK: [[int_0:%\w+]] = OpConstant {{%\w+}} 0
265
266 ; CHECK: [[null_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression
267 ; CHECK: [[deref:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugOperation Deref
268 ; CHECK: [[deref_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression [[deref]]
269 ; CHECK: [[dbg_fn:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugFunction
270 ; CHECK: [[dbg_bb:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLexicalBlock
271 ; CHECK: [[dbg_f:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[f]] {{%\w+}} {{%\w+}} 0 0 [[dbg_fn]]
272 ; CHECK: [[dbg_i:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[i]] {{%\w+}} {{%\w+}} 1 0 [[dbg_bb]]
273
274 %2 = OpFunction %6 None %7
275 %23 = OpLabel
276
277 ; The first block has DebugDeclare and DebugValue with Deref
278 ;
279 ; CHECK: OpLabel
280 ; CHECK: DebugScope [[dbg_fn]]
281 ; CHECK: [[x:%\w+]] = OpVariable {{%\w+}} Function
282 ; CHECK: OpLine {{%\w+}} 0 0
283 ; CHECK: OpBranch
284 ; CHECK: OpLabel
285 ; CHECK: DebugScope [[dbg_fn]]
286 ; CHECK: DebugValue [[dbg_f]] [[int_0]] [[null_expr]]
287 ; CHECK: OpBranch
288 ; CHECK: DebugScope [[dbg_fn]]
289 ; CHECK: OpLine {{%\w+}} 1 1
290 ; CHECK: OpSLessThan
291 ; CHECK: OpLine {{%\w+}} 2 0
292 ; CHECK: OpBranch
293 ; CHECK: OpLabel
294 ; CHECK: DebugScope [[dbg_bb]]
295 ; CHECK: DebugDeclare [[dbg_f]] [[x]] [[null_expr]]
296 ; CHECK: DebugValue [[dbg_i]] [[x]] [[deref_expr]]
297 ; CHECK: OpLine {{%\w+}} 3 0
298 ;
299 ; CHECK: OpLine {{%\w+}} 6 0
300 ; CHECK: [[add:%\w+]] = OpIAdd
301 ; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]]
302 ; CHECK: OpLine {{%\w+}} 7 0
303
304 ; Other blocks do not have DebugDeclare and DebugValue with Deref
305 ;
306 ; CHECK: DebugScope [[dbg_fn]]
307 ; CHECK: OpLine {{%\w+}} 1 1
308 ; CHECK: OpSLessThan
309 ; CHECK: OpLine {{%\w+}} 2 0
310 ; CHECK: OpBranch
311 ; CHECK: OpLabel
312 ;
313 ; CHECK: DebugScope [[dbg_bb]]
314 ; CHECK-NOT: DebugDeclare [[dbg_f]] [[x]] [[null_expr]]
315 ; CHECK-NOT: DebugValue [[dbg_i]] [[x]] [[deref_expr]]
316 ; CHECK: OpLine {{%\w+}} 3 0
317 ;
318 ; CHECK: OpLine {{%\w+}} 6 0
319 ; CHECK: [[add:%\w+]] = OpIAdd
320 ; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]]
321 ; CHECK: OpLine {{%\w+}} 7 0
322 ;
323 ; CHECK-NOT: DebugDeclare [[dbg_f]] [[x]] [[null_expr]]
324 ; CHECK-NOT: DebugValue [[dbg_i]] [[x]] [[deref_expr]]
325 ; CHECK: DebugScope [[dbg_fn]]
326 ; CHECK: OpLine {{%\w+}} 8 0
327 ; CHECK: OpReturn
328
329 %s0 = OpExtInst %6 %ext DebugScope %dbg_main
330 %5 = OpVariable %17 Function
331 OpLine %file_name 0 0
332 OpBranch %24
333 %24 = OpLabel
334 %s1 = OpExtInst %6 %ext DebugScope %dbg_main
335 %35 = OpPhi %8 %10 %23 %34 %26
336 %value0 = OpExtInst %6 %ext DebugValue %dbg_f %35 %null_expr
337 OpLine %file_name 1 0
338 OpLoopMerge %25 %26 Unroll
339 OpBranch %27
340 %27 = OpLabel
341 %s2 = OpExtInst %6 %ext DebugScope %dbg_main
342 OpLine %file_name 1 1
343 %29 = OpSLessThan %12 %35 %11
344 OpLine %file_name 2 0
345 OpBranchConditional %29 %30 %25
346 %30 = OpLabel
347 %s3 = OpExtInst %6 %ext DebugScope %bb
348 %decl0 = OpExtInst %6 %ext DebugDeclare %dbg_f %5 %null_expr
349 %decl1 = OpExtInst %6 %ext DebugValue %dbg_i %5 %deref_expr
350 OpLine %file_name 3 0
351 %32 = OpAccessChain %19 %5 %35
352 OpLine %file_name 4 0
353 OpStore %32 %18
354 OpLine %file_name 5 0
355 OpBranch %26
356 %26 = OpLabel
357 %s4 = OpExtInst %6 %ext DebugScope %dbg_main
358 OpLine %file_name 6 0
359 %34 = OpIAdd %8 %35 %20
360 %value1 = OpExtInst %6 %ext DebugValue %dbg_f %34 %null_expr
361 OpLine %file_name 7 0
362 OpBranch %24
363 %25 = OpLabel
364 %s5 = OpExtInst %6 %ext DebugScope %dbg_main
365 OpLine %file_name 8 0
366 OpReturn
367 OpFunctionEnd)";
368
369 std::unique_ptr<IRContext> context =
370 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
371 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
372 Module* module = context->module();
373 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
374 << text << std::endl;
375
376 LoopUnroller loop_unroller;
377 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
378 SinglePassRunAndMatch<LoopUnroller>(text, true);
379 }
380
381 template <int factor>
382 class PartialUnrollerTestPass : public Pass {
383 public:
PartialUnrollerTestPass()384 PartialUnrollerTestPass() : Pass() {}
385
name() const386 const char* name() const override { return "Loop unroller"; }
387
Process()388 Status Process() override {
389 for (Function& f : *context()->module()) {
390 LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f);
391 for (auto& loop : loop_descriptor) {
392 LoopUtils loop_utils{context(), &loop};
393 loop_utils.PartiallyUnroll(factor);
394 }
395 }
396
397 return Pass::Status::SuccessWithChange;
398 }
399 };
400
401 /*
402 Generated from the following GLSL
403 #version 330 core
404 layout(location = 0) out vec4 c;
405 void main() {
406 float x[10];
407 for (int i = 0; i < 10; ++i) {
408 x[i] = 1.0f;
409 }
410 }
411 */
TEST_F(PassClassTest,SimplePartialUnroll)412 TEST_F(PassClassTest, SimplePartialUnroll) {
413 // With LocalMultiStoreElimPass
414 const std::string text = R"(
415 OpCapability Shader
416 %1 = OpExtInstImport "GLSL.std.450"
417 OpMemoryModel Logical GLSL450
418 OpEntryPoint Fragment %2 "main" %3
419 OpExecutionMode %2 OriginUpperLeft
420 OpSource GLSL 330
421 OpName %2 "main"
422 OpName %5 "x"
423 OpName %3 "c"
424 OpDecorate %3 Location 0
425 %6 = OpTypeVoid
426 %7 = OpTypeFunction %6
427 %8 = OpTypeInt 32 1
428 %9 = OpTypePointer Function %8
429 %10 = OpConstant %8 0
430 %11 = OpConstant %8 10
431 %12 = OpTypeBool
432 %13 = OpTypeFloat 32
433 %14 = OpTypeInt 32 0
434 %15 = OpConstant %14 10
435 %16 = OpTypeArray %13 %15
436 %17 = OpTypePointer Function %16
437 %18 = OpConstant %13 1
438 %19 = OpTypePointer Function %13
439 %20 = OpConstant %8 1
440 %21 = OpTypeVector %13 4
441 %22 = OpTypePointer Output %21
442 %3 = OpVariable %22 Output
443 %2 = OpFunction %6 None %7
444 %23 = OpLabel
445 %5 = OpVariable %17 Function
446 OpBranch %24
447 %24 = OpLabel
448 %35 = OpPhi %8 %10 %23 %34 %26
449 OpLoopMerge %25 %26 Unroll
450 OpBranch %27
451 %27 = OpLabel
452 %29 = OpSLessThan %12 %35 %11
453 OpBranchConditional %29 %30 %25
454 %30 = OpLabel
455 %32 = OpAccessChain %19 %5 %35
456 OpStore %32 %18
457 OpBranch %26
458 %26 = OpLabel
459 %34 = OpIAdd %8 %35 %20
460 OpBranch %24
461 %25 = OpLabel
462 OpReturn
463 OpFunctionEnd
464 )";
465
466 const std::string output = R"(OpCapability Shader
467 %1 = OpExtInstImport "GLSL.std.450"
468 OpMemoryModel Logical GLSL450
469 OpEntryPoint Fragment %2 "main" %3
470 OpExecutionMode %2 OriginUpperLeft
471 OpSource GLSL 330
472 OpName %2 "main"
473 OpName %4 "x"
474 OpName %3 "c"
475 OpDecorate %3 Location 0
476 %5 = OpTypeVoid
477 %6 = OpTypeFunction %5
478 %7 = OpTypeInt 32 1
479 %8 = OpTypePointer Function %7
480 %9 = OpConstant %7 0
481 %10 = OpConstant %7 10
482 %11 = OpTypeBool
483 %12 = OpTypeFloat 32
484 %13 = OpTypeInt 32 0
485 %14 = OpConstant %13 10
486 %15 = OpTypeArray %12 %14
487 %16 = OpTypePointer Function %15
488 %17 = OpConstant %12 1
489 %18 = OpTypePointer Function %12
490 %19 = OpConstant %7 1
491 %20 = OpTypeVector %12 4
492 %21 = OpTypePointer Output %20
493 %3 = OpVariable %21 Output
494 %2 = OpFunction %5 None %6
495 %22 = OpLabel
496 %4 = OpVariable %16 Function
497 OpBranch %23
498 %23 = OpLabel
499 %24 = OpPhi %7 %9 %22 %39 %38
500 OpLoopMerge %27 %38 DontUnroll
501 OpBranch %28
502 %28 = OpLabel
503 %29 = OpSLessThan %11 %24 %10
504 OpBranchConditional %29 %30 %27
505 %30 = OpLabel
506 %31 = OpAccessChain %18 %4 %24
507 OpStore %31 %17
508 OpBranch %26
509 %26 = OpLabel
510 %25 = OpIAdd %7 %24 %19
511 OpBranch %32
512 %32 = OpLabel
513 OpBranch %34
514 %34 = OpLabel
515 %35 = OpSLessThan %11 %25 %10
516 OpBranch %36
517 %36 = OpLabel
518 %37 = OpAccessChain %18 %4 %25
519 OpStore %37 %17
520 OpBranch %38
521 %38 = OpLabel
522 %39 = OpIAdd %7 %25 %19
523 OpBranch %23
524 %27 = OpLabel
525 OpReturn
526 OpFunctionEnd
527 )";
528
529 std::unique_ptr<IRContext> context =
530 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
531 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
532 Module* module = context->module();
533 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
534 << text << std::endl;
535
536 LoopUnroller loop_unroller;
537 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
538 SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(text, output, false);
539 }
540
541 /*
542 Generated from the following GLSL
543 #version 330 core
544 layout(location = 0) out vec4 c;
545 void main() {
546 float x[10];
547 for (int i = 0; i < 10; ++i) {
548 x[i] = 1.0f;
549 }
550 }
551 */
TEST_F(PassClassTest,SimpleUnevenPartialUnroll)552 TEST_F(PassClassTest, SimpleUnevenPartialUnroll) {
553 // With LocalMultiStoreElimPass
554 const std::string text = R"(
555 OpCapability Shader
556 %1 = OpExtInstImport "GLSL.std.450"
557 OpMemoryModel Logical GLSL450
558 OpEntryPoint Fragment %2 "main" %3
559 OpExecutionMode %2 OriginUpperLeft
560 OpSource GLSL 330
561 OpName %2 "main"
562 OpName %5 "x"
563 OpName %3 "c"
564 OpDecorate %3 Location 0
565 %6 = OpTypeVoid
566 %7 = OpTypeFunction %6
567 %8 = OpTypeInt 32 1
568 %9 = OpTypePointer Function %8
569 %10 = OpConstant %8 0
570 %11 = OpConstant %8 10
571 %12 = OpTypeBool
572 %13 = OpTypeFloat 32
573 %14 = OpTypeInt 32 0
574 %15 = OpConstant %14 10
575 %16 = OpTypeArray %13 %15
576 %17 = OpTypePointer Function %16
577 %18 = OpConstant %13 1
578 %19 = OpTypePointer Function %13
579 %20 = OpConstant %8 1
580 %21 = OpTypeVector %13 4
581 %22 = OpTypePointer Output %21
582 %3 = OpVariable %22 Output
583 %2 = OpFunction %6 None %7
584 %23 = OpLabel
585 %5 = OpVariable %17 Function
586 OpBranch %24
587 %24 = OpLabel
588 %35 = OpPhi %8 %10 %23 %34 %26
589 OpLoopMerge %25 %26 Unroll
590 OpBranch %27
591 %27 = OpLabel
592 %29 = OpSLessThan %12 %35 %11
593 OpBranchConditional %29 %30 %25
594 %30 = OpLabel
595 %32 = OpAccessChain %19 %5 %35
596 OpStore %32 %18
597 OpBranch %26
598 %26 = OpLabel
599 %34 = OpIAdd %8 %35 %20
600 OpBranch %24
601 %25 = OpLabel
602 OpReturn
603 OpFunctionEnd
604 )";
605
606 const std::string output = R"(OpCapability Shader
607 %1 = OpExtInstImport "GLSL.std.450"
608 OpMemoryModel Logical GLSL450
609 OpEntryPoint Fragment %2 "main" %3
610 OpExecutionMode %2 OriginUpperLeft
611 OpSource GLSL 330
612 OpName %2 "main"
613 OpName %4 "x"
614 OpName %3 "c"
615 OpDecorate %3 Location 0
616 %5 = OpTypeVoid
617 %6 = OpTypeFunction %5
618 %7 = OpTypeInt 32 1
619 %8 = OpTypePointer Function %7
620 %9 = OpConstant %7 0
621 %10 = OpConstant %7 10
622 %11 = OpTypeBool
623 %12 = OpTypeFloat 32
624 %13 = OpTypeInt 32 0
625 %14 = OpConstant %13 10
626 %15 = OpTypeArray %12 %14
627 %16 = OpTypePointer Function %15
628 %17 = OpConstant %12 1
629 %18 = OpTypePointer Function %12
630 %19 = OpConstant %7 1
631 %20 = OpTypeVector %12 4
632 %21 = OpTypePointer Output %20
633 %3 = OpVariable %21 Output
634 %58 = OpConstant %13 1
635 %2 = OpFunction %5 None %6
636 %22 = OpLabel
637 %4 = OpVariable %16 Function
638 OpBranch %23
639 %23 = OpLabel
640 %24 = OpPhi %7 %9 %22 %25 %26
641 OpLoopMerge %32 %26 Unroll
642 OpBranch %28
643 %28 = OpLabel
644 %29 = OpSLessThan %11 %24 %58
645 OpBranchConditional %29 %30 %32
646 %30 = OpLabel
647 %31 = OpAccessChain %18 %4 %24
648 OpStore %31 %17
649 OpBranch %26
650 %26 = OpLabel
651 %25 = OpIAdd %7 %24 %19
652 OpBranch %23
653 %32 = OpLabel
654 OpBranch %33
655 %33 = OpLabel
656 %34 = OpPhi %7 %24 %32 %57 %56
657 OpLoopMerge %41 %56 DontUnroll
658 OpBranch %35
659 %35 = OpLabel
660 %36 = OpSLessThan %11 %34 %10
661 OpBranchConditional %36 %37 %41
662 %37 = OpLabel
663 %38 = OpAccessChain %18 %4 %34
664 OpStore %38 %17
665 OpBranch %39
666 %39 = OpLabel
667 %40 = OpIAdd %7 %34 %19
668 OpBranch %42
669 %42 = OpLabel
670 OpBranch %44
671 %44 = OpLabel
672 %45 = OpSLessThan %11 %40 %10
673 OpBranch %46
674 %46 = OpLabel
675 %47 = OpAccessChain %18 %4 %40
676 OpStore %47 %17
677 OpBranch %48
678 %48 = OpLabel
679 %49 = OpIAdd %7 %40 %19
680 OpBranch %50
681 %50 = OpLabel
682 OpBranch %52
683 %52 = OpLabel
684 %53 = OpSLessThan %11 %49 %10
685 OpBranch %54
686 %54 = OpLabel
687 %55 = OpAccessChain %18 %4 %49
688 OpStore %55 %17
689 OpBranch %56
690 %56 = OpLabel
691 %57 = OpIAdd %7 %49 %19
692 OpBranch %33
693 %41 = OpLabel
694 OpReturn
695 %27 = 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 Module* module = context->module();
704 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
705 << text << std::endl;
706
707 LoopUnroller loop_unroller;
708 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
709 // By unrolling by a factor that doesn't divide evenly into the number of loop
710 // iterations we perfom an additional transform when partially unrolling to
711 // account for the remainder.
712 SinglePassRunAndCheck<PartialUnrollerTestPass<3>>(text, output, false);
713 }
714
715 /* Generated from
716 #version 410 core
717 layout(location=0) flat in int upper_bound;
718 void main() {
719 float x[10];
720 for (int i = 2; i < 8; i+=2) {
721 x[i] = i;
722 }
723 }
724 */
TEST_F(PassClassTest,SimpleLoopIterationsCheck)725 TEST_F(PassClassTest, SimpleLoopIterationsCheck) {
726 // With LocalMultiStoreElimPass
727 const std::string text = R"(
728 OpCapability Shader
729 %1 = OpExtInstImport "GLSL.std.450"
730 OpMemoryModel Logical GLSL450
731 OpEntryPoint Fragment %2 "main" %3
732 OpExecutionMode %2 OriginUpperLeft
733 OpSource GLSL 410
734 OpName %2 "main"
735 OpName %5 "x"
736 OpName %3 "upper_bound"
737 OpDecorate %3 Flat
738 OpDecorate %3 Location 0
739 %6 = OpTypeVoid
740 %7 = OpTypeFunction %6
741 %8 = OpTypeInt 32 1
742 %9 = OpTypePointer Function %8
743 %10 = OpConstant %8 2
744 %11 = OpConstant %8 8
745 %12 = OpTypeBool
746 %13 = OpTypeFloat 32
747 %14 = OpTypeInt 32 0
748 %15 = OpConstant %14 10
749 %16 = OpTypeArray %13 %15
750 %17 = OpTypePointer Function %16
751 %18 = OpTypePointer Function %13
752 %19 = OpTypePointer Input %8
753 %3 = OpVariable %19 Input
754 %2 = OpFunction %6 None %7
755 %20 = OpLabel
756 %5 = OpVariable %17 Function
757 OpBranch %21
758 %21 = OpLabel
759 %34 = OpPhi %8 %10 %20 %33 %23
760 OpLoopMerge %22 %23 Unroll
761 OpBranch %24
762 %24 = OpLabel
763 %26 = OpSLessThan %12 %34 %11
764 OpBranchConditional %26 %27 %22
765 %27 = OpLabel
766 %30 = OpConvertSToF %13 %34
767 %31 = OpAccessChain %18 %5 %34
768 OpStore %31 %30
769 OpBranch %23
770 %23 = OpLabel
771 %33 = OpIAdd %8 %34 %10
772 OpBranch %21
773 %22 = OpLabel
774 OpReturn
775 OpFunctionEnd
776 )";
777
778 std::unique_ptr<IRContext> context =
779 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
780 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
781 Module* module = context->module();
782 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
783 << text << std::endl;
784
785 Function* f = spvtest::GetFunction(module, 2);
786
787 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
788 EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
789
790 Loop& loop = loop_descriptor.GetLoopByIndex(0);
791
792 EXPECT_TRUE(loop.HasUnrollLoopControl());
793
794 BasicBlock* condition = loop.FindConditionBlock();
795 EXPECT_EQ(condition->id(), 24u);
796
797 Instruction* induction = loop.FindConditionVariable(condition);
798 EXPECT_EQ(induction->result_id(), 34u);
799
800 LoopUtils loop_utils{context.get(), &loop};
801 EXPECT_TRUE(loop_utils.CanPerformUnroll());
802
803 size_t iterations = 0;
804 EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(),
805 &iterations));
806 EXPECT_EQ(iterations, 3u);
807 }
808
809 /* Generated from
810 #version 410 core
811 void main() {
812 float x[10];
813 for (int i = -1; i < 6; i+=3) {
814 x[i] = i;
815 }
816 }
817 */
TEST_F(PassClassTest,SimpleLoopIterationsCheckSignedInit)818 TEST_F(PassClassTest, SimpleLoopIterationsCheckSignedInit) {
819 // With LocalMultiStoreElimPass
820 const std::string text = R"(
821 OpCapability Shader
822 %1 = OpExtInstImport "GLSL.std.450"
823 OpMemoryModel Logical GLSL450
824 OpEntryPoint Fragment %2 "main" %3
825 OpExecutionMode %2 OriginUpperLeft
826 OpSource GLSL 410
827 OpName %2 "main"
828 OpName %5 "x"
829 OpName %3 "upper_bound"
830 OpDecorate %3 Flat
831 OpDecorate %3 Location 0
832 %6 = OpTypeVoid
833 %7 = OpTypeFunction %6
834 %8 = OpTypeInt 32 1
835 %9 = OpTypePointer Function %8
836 %10 = OpConstant %8 -1
837 %11 = OpConstant %8 6
838 %12 = OpTypeBool
839 %13 = OpTypeFloat 32
840 %14 = OpTypeInt 32 0
841 %15 = OpConstant %14 10
842 %16 = OpTypeArray %13 %15
843 %17 = OpTypePointer Function %16
844 %18 = OpTypePointer Function %13
845 %19 = OpConstant %8 3
846 %20 = OpTypePointer Input %8
847 %3 = OpVariable %20 Input
848 %2 = OpFunction %6 None %7
849 %21 = OpLabel
850 %5 = OpVariable %17 Function
851 OpBranch %22
852 %22 = OpLabel
853 %35 = OpPhi %8 %10 %21 %34 %24
854 OpLoopMerge %23 %24 None
855 OpBranch %25
856 %25 = OpLabel
857 %27 = OpSLessThan %12 %35 %11
858 OpBranchConditional %27 %28 %23
859 %28 = OpLabel
860 %31 = OpConvertSToF %13 %35
861 %32 = OpAccessChain %18 %5 %35
862 OpStore %32 %31
863 OpBranch %24
864 %24 = OpLabel
865 %34 = OpIAdd %8 %35 %19
866 OpBranch %22
867 %23 = OpLabel
868 OpReturn
869 OpFunctionEnd
870 )";
871
872 std::unique_ptr<IRContext> context =
873 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
874 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
875 Module* module = context->module();
876 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
877 << text << std::endl;
878
879 Function* f = spvtest::GetFunction(module, 2);
880
881 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
882
883 EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
884
885 Loop& loop = loop_descriptor.GetLoopByIndex(0);
886
887 EXPECT_FALSE(loop.HasUnrollLoopControl());
888
889 BasicBlock* condition = loop.FindConditionBlock();
890 EXPECT_EQ(condition->id(), 25u);
891
892 Instruction* induction = loop.FindConditionVariable(condition);
893 EXPECT_EQ(induction->result_id(), 35u);
894
895 LoopUtils loop_utils{context.get(), &loop};
896 EXPECT_TRUE(loop_utils.CanPerformUnroll());
897
898 size_t iterations = 0;
899 EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(),
900 &iterations));
901 EXPECT_EQ(iterations, 3u);
902 }
903
904 /*
905 Generated from the following GLSL
906 #version 410 core
907 void main() {
908 float out_array[6];
909 for (uint i = 0; i < 2; i++) {
910 for (int x = 0; x < 3; ++x) {
911 out_array[x + i*3] = i;
912 }
913 }
914 }
915 */
TEST_F(PassClassTest,UnrollNestedLoops)916 TEST_F(PassClassTest, UnrollNestedLoops) {
917 // With LocalMultiStoreElimPass
918 const std::string text = R"(
919 OpCapability Shader
920 %1 = OpExtInstImport "GLSL.std.450"
921 OpMemoryModel Logical GLSL450
922 OpEntryPoint Fragment %4 "main"
923 OpExecutionMode %4 OriginUpperLeft
924 OpSource GLSL 410
925 OpName %4 "main"
926 OpName %35 "out_array"
927 %2 = OpTypeVoid
928 %3 = OpTypeFunction %2
929 %6 = OpTypeInt 32 0
930 %7 = OpTypePointer Function %6
931 %9 = OpConstant %6 0
932 %16 = OpConstant %6 2
933 %17 = OpTypeBool
934 %19 = OpTypeInt 32 1
935 %20 = OpTypePointer Function %19
936 %22 = OpConstant %19 0
937 %29 = OpConstant %19 3
938 %31 = OpTypeFloat 32
939 %32 = OpConstant %6 6
940 %33 = OpTypeArray %31 %32
941 %34 = OpTypePointer Function %33
942 %39 = OpConstant %6 3
943 %44 = OpTypePointer Function %31
944 %47 = OpConstant %19 1
945 %4 = OpFunction %2 None %3
946 %5 = OpLabel
947 %35 = OpVariable %34 Function
948 OpBranch %10
949 %10 = OpLabel
950 %51 = OpPhi %6 %9 %5 %50 %13
951 OpLoopMerge %12 %13 Unroll
952 OpBranch %14
953 %14 = OpLabel
954 %18 = OpULessThan %17 %51 %16
955 OpBranchConditional %18 %11 %12
956 %11 = OpLabel
957 OpBranch %23
958 %23 = OpLabel
959 %54 = OpPhi %19 %22 %11 %48 %26
960 OpLoopMerge %25 %26 Unroll
961 OpBranch %27
962 %27 = OpLabel
963 %30 = OpSLessThan %17 %54 %29
964 OpBranchConditional %30 %24 %25
965 %24 = OpLabel
966 %37 = OpBitcast %6 %54
967 %40 = OpIMul %6 %51 %39
968 %41 = OpIAdd %6 %37 %40
969 %43 = OpConvertUToF %31 %51
970 %45 = OpAccessChain %44 %35 %41
971 OpStore %45 %43
972 OpBranch %26
973 %26 = OpLabel
974 %48 = OpIAdd %19 %54 %47
975 OpBranch %23
976 %25 = OpLabel
977 OpBranch %13
978 %13 = OpLabel
979 %50 = OpIAdd %6 %51 %47
980 OpBranch %10
981 %12 = OpLabel
982 OpReturn
983 OpFunctionEnd
984 )";
985
986 const std::string output = R"(OpCapability Shader
987 %1 = OpExtInstImport "GLSL.std.450"
988 OpMemoryModel Logical GLSL450
989 OpEntryPoint Fragment %2 "main"
990 OpExecutionMode %2 OriginUpperLeft
991 OpSource GLSL 410
992 OpName %2 "main"
993 OpName %3 "out_array"
994 %4 = OpTypeVoid
995 %5 = OpTypeFunction %4
996 %6 = OpTypeInt 32 0
997 %7 = OpTypePointer Function %6
998 %8 = OpConstant %6 0
999 %9 = OpConstant %6 2
1000 %10 = OpTypeBool
1001 %11 = OpTypeInt 32 1
1002 %12 = OpTypePointer Function %11
1003 %13 = OpConstant %11 0
1004 %14 = OpConstant %11 3
1005 %15 = OpTypeFloat 32
1006 %16 = OpConstant %6 6
1007 %17 = OpTypeArray %15 %16
1008 %18 = OpTypePointer Function %17
1009 %19 = OpConstant %6 3
1010 %20 = OpTypePointer Function %15
1011 %21 = OpConstant %11 1
1012 %2 = OpFunction %4 None %5
1013 %22 = OpLabel
1014 %3 = OpVariable %18 Function
1015 OpBranch %23
1016 %23 = OpLabel
1017 OpBranch %28
1018 %28 = OpLabel
1019 %29 = OpULessThan %10 %8 %9
1020 OpBranch %30
1021 %30 = OpLabel
1022 OpBranch %31
1023 %31 = OpLabel
1024 OpBranch %36
1025 %36 = OpLabel
1026 %37 = OpSLessThan %10 %13 %14
1027 OpBranch %38
1028 %38 = OpLabel
1029 %39 = OpBitcast %6 %13
1030 %40 = OpIMul %6 %8 %19
1031 %41 = OpIAdd %6 %39 %40
1032 %42 = OpConvertUToF %15 %8
1033 %43 = OpAccessChain %20 %3 %41
1034 OpStore %43 %42
1035 OpBranch %34
1036 %34 = OpLabel
1037 %33 = OpIAdd %11 %13 %21
1038 OpBranch %44
1039 %44 = OpLabel
1040 OpBranch %46
1041 %46 = OpLabel
1042 %47 = OpSLessThan %10 %33 %14
1043 OpBranch %48
1044 %48 = OpLabel
1045 %49 = OpBitcast %6 %33
1046 %50 = OpIMul %6 %8 %19
1047 %51 = OpIAdd %6 %49 %50
1048 %52 = OpConvertUToF %15 %8
1049 %53 = OpAccessChain %20 %3 %51
1050 OpStore %53 %52
1051 OpBranch %54
1052 %54 = OpLabel
1053 %55 = OpIAdd %11 %33 %21
1054 OpBranch %56
1055 %56 = OpLabel
1056 OpBranch %58
1057 %58 = OpLabel
1058 %59 = OpSLessThan %10 %55 %14
1059 OpBranch %60
1060 %60 = OpLabel
1061 %61 = OpBitcast %6 %55
1062 %62 = OpIMul %6 %8 %19
1063 %63 = OpIAdd %6 %61 %62
1064 %64 = OpConvertUToF %15 %8
1065 %65 = OpAccessChain %20 %3 %63
1066 OpStore %65 %64
1067 OpBranch %66
1068 %66 = OpLabel
1069 %67 = OpIAdd %11 %55 %21
1070 OpBranch %35
1071 %35 = OpLabel
1072 OpBranch %26
1073 %26 = OpLabel
1074 %25 = OpIAdd %6 %8 %21
1075 OpBranch %68
1076 %68 = OpLabel
1077 OpBranch %70
1078 %70 = OpLabel
1079 %71 = OpULessThan %10 %25 %9
1080 OpBranch %72
1081 %72 = OpLabel
1082 OpBranch %73
1083 %73 = OpLabel
1084 OpBranch %74
1085 %74 = OpLabel
1086 %75 = OpSLessThan %10 %13 %14
1087 OpBranch %76
1088 %76 = OpLabel
1089 %77 = OpBitcast %6 %13
1090 %78 = OpIMul %6 %25 %19
1091 %79 = OpIAdd %6 %77 %78
1092 %80 = OpConvertUToF %15 %25
1093 %81 = OpAccessChain %20 %3 %79
1094 OpStore %81 %80
1095 OpBranch %82
1096 %82 = OpLabel
1097 %83 = OpIAdd %11 %13 %21
1098 OpBranch %84
1099 %84 = OpLabel
1100 OpBranch %85
1101 %85 = OpLabel
1102 %86 = OpSLessThan %10 %83 %14
1103 OpBranch %87
1104 %87 = OpLabel
1105 %88 = OpBitcast %6 %83
1106 %89 = OpIMul %6 %25 %19
1107 %90 = OpIAdd %6 %88 %89
1108 %91 = OpConvertUToF %15 %25
1109 %92 = OpAccessChain %20 %3 %90
1110 OpStore %92 %91
1111 OpBranch %93
1112 %93 = OpLabel
1113 %94 = OpIAdd %11 %83 %21
1114 OpBranch %95
1115 %95 = OpLabel
1116 OpBranch %96
1117 %96 = OpLabel
1118 %97 = OpSLessThan %10 %94 %14
1119 OpBranch %98
1120 %98 = OpLabel
1121 %99 = OpBitcast %6 %94
1122 %100 = OpIMul %6 %25 %19
1123 %101 = OpIAdd %6 %99 %100
1124 %102 = OpConvertUToF %15 %25
1125 %103 = OpAccessChain %20 %3 %101
1126 OpStore %103 %102
1127 OpBranch %104
1128 %104 = OpLabel
1129 %105 = OpIAdd %11 %94 %21
1130 OpBranch %106
1131 %106 = OpLabel
1132 OpBranch %107
1133 %107 = OpLabel
1134 %108 = OpIAdd %6 %25 %21
1135 OpBranch %27
1136 %27 = OpLabel
1137 OpReturn
1138 OpFunctionEnd
1139 )";
1140
1141 std::unique_ptr<IRContext> context =
1142 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1143 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1144 Module* module = context->module();
1145 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1146 << text << std::endl;
1147 LoopUnroller loop_unroller;
1148 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1149 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
1150 }
1151
1152 /*
1153 Generated from the following GLSL
1154 #version 410 core
1155 void main() {
1156 float out_array[2];
1157 for (int i = -3; i < -1; i++) {
1158 out_array[3 + i] = i;
1159 }
1160 }
1161 */
TEST_F(PassClassTest,NegativeConditionAndInit)1162 TEST_F(PassClassTest, NegativeConditionAndInit) {
1163 // With LocalMultiStoreElimPass
1164 const std::string text = R"(
1165 OpCapability Shader
1166 %1 = OpExtInstImport "GLSL.std.450"
1167 OpMemoryModel Logical GLSL450
1168 OpEntryPoint Fragment %4 "main"
1169 OpExecutionMode %4 OriginUpperLeft
1170 OpSource GLSL 410
1171 OpName %4 "main"
1172 OpName %23 "out_array"
1173 %2 = OpTypeVoid
1174 %3 = OpTypeFunction %2
1175 %6 = OpTypeInt 32 1
1176 %7 = OpTypePointer Function %6
1177 %9 = OpConstant %6 -3
1178 %16 = OpConstant %6 -1
1179 %17 = OpTypeBool
1180 %19 = OpTypeInt 32 0
1181 %20 = OpConstant %19 2
1182 %21 = OpTypeArray %6 %20
1183 %22 = OpTypePointer Function %21
1184 %25 = OpConstant %6 3
1185 %30 = OpConstant %6 1
1186 %4 = OpFunction %2 None %3
1187 %5 = OpLabel
1188 %23 = OpVariable %22 Function
1189 OpBranch %10
1190 %10 = OpLabel
1191 %32 = OpPhi %6 %9 %5 %31 %13
1192 OpLoopMerge %12 %13 Unroll
1193 OpBranch %14
1194 %14 = OpLabel
1195 %18 = OpSLessThan %17 %32 %16
1196 OpBranchConditional %18 %11 %12
1197 %11 = OpLabel
1198 %26 = OpIAdd %6 %32 %25
1199 %28 = OpAccessChain %7 %23 %26
1200 OpStore %28 %32
1201 OpBranch %13
1202 %13 = OpLabel
1203 %31 = OpIAdd %6 %32 %30
1204 OpBranch %10
1205 %12 = OpLabel
1206 OpReturn
1207 OpFunctionEnd
1208 )";
1209
1210 const std::string expected = R"(OpCapability Shader
1211 %1 = OpExtInstImport "GLSL.std.450"
1212 OpMemoryModel Logical GLSL450
1213 OpEntryPoint Fragment %2 "main"
1214 OpExecutionMode %2 OriginUpperLeft
1215 OpSource GLSL 410
1216 OpName %2 "main"
1217 OpName %3 "out_array"
1218 %4 = OpTypeVoid
1219 %5 = OpTypeFunction %4
1220 %6 = OpTypeInt 32 1
1221 %7 = OpTypePointer Function %6
1222 %8 = OpConstant %6 -3
1223 %9 = OpConstant %6 -1
1224 %10 = OpTypeBool
1225 %11 = OpTypeInt 32 0
1226 %12 = OpConstant %11 2
1227 %13 = OpTypeArray %6 %12
1228 %14 = OpTypePointer Function %13
1229 %15 = OpConstant %6 3
1230 %16 = OpConstant %6 1
1231 %2 = OpFunction %4 None %5
1232 %17 = OpLabel
1233 %3 = OpVariable %14 Function
1234 OpBranch %18
1235 %18 = OpLabel
1236 OpBranch %23
1237 %23 = OpLabel
1238 %24 = OpSLessThan %10 %8 %9
1239 OpBranch %25
1240 %25 = OpLabel
1241 %26 = OpIAdd %6 %8 %15
1242 %27 = OpAccessChain %7 %3 %26
1243 OpStore %27 %8
1244 OpBranch %21
1245 %21 = OpLabel
1246 %20 = OpIAdd %6 %8 %16
1247 OpBranch %28
1248 %28 = OpLabel
1249 OpBranch %30
1250 %30 = OpLabel
1251 %31 = OpSLessThan %10 %20 %9
1252 OpBranch %32
1253 %32 = OpLabel
1254 %33 = OpIAdd %6 %20 %15
1255 %34 = OpAccessChain %7 %3 %33
1256 OpStore %34 %20
1257 OpBranch %35
1258 %35 = OpLabel
1259 %36 = OpIAdd %6 %20 %16
1260 OpBranch %22
1261 %22 = OpLabel
1262 OpReturn
1263 OpFunctionEnd
1264 )";
1265
1266 std::unique_ptr<IRContext> context =
1267 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1268 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1269 Module* module = context->module();
1270 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1271 << text << std::endl;
1272
1273 LoopUnroller loop_unroller;
1274 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1275 // SinglePassRunAndCheck<LoopUnroller>(text, expected, false);
1276
1277 Function* f = spvtest::GetFunction(module, 4);
1278
1279 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
1280 EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
1281
1282 Loop& loop = loop_descriptor.GetLoopByIndex(0);
1283
1284 EXPECT_TRUE(loop.HasUnrollLoopControl());
1285
1286 BasicBlock* condition = loop.FindConditionBlock();
1287 EXPECT_EQ(condition->id(), 14u);
1288
1289 Instruction* induction = loop.FindConditionVariable(condition);
1290 EXPECT_EQ(induction->result_id(), 32u);
1291
1292 LoopUtils loop_utils{context.get(), &loop};
1293 EXPECT_TRUE(loop_utils.CanPerformUnroll());
1294
1295 size_t iterations = 0;
1296 EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(),
1297 &iterations));
1298 EXPECT_EQ(iterations, 2u);
1299 SinglePassRunAndCheck<LoopUnroller>(text, expected, false);
1300 }
1301
1302 /*
1303 Generated from the following GLSL
1304 #version 410 core
1305 void main() {
1306 float out_array[9];
1307 for (int i = -10; i < -1; i++) {
1308 out_array[i] = i;
1309 }
1310 }
1311 */
TEST_F(PassClassTest,NegativeConditionAndInitResidualUnroll)1312 TEST_F(PassClassTest, NegativeConditionAndInitResidualUnroll) {
1313 // With LocalMultiStoreElimPass
1314 const std::string text = R"(
1315 OpCapability Shader
1316 %1 = OpExtInstImport "GLSL.std.450"
1317 OpMemoryModel Logical GLSL450
1318 OpEntryPoint Fragment %4 "main"
1319 OpExecutionMode %4 OriginUpperLeft
1320 OpSource GLSL 410
1321 OpName %4 "main"
1322 OpName %23 "out_array"
1323 %2 = OpTypeVoid
1324 %3 = OpTypeFunction %2
1325 %6 = OpTypeInt 32 1
1326 %7 = OpTypePointer Function %6
1327 %9 = OpConstant %6 -10
1328 %16 = OpConstant %6 -1
1329 %17 = OpTypeBool
1330 %19 = OpTypeInt 32 0
1331 %20 = OpConstant %19 9
1332 %21 = OpTypeArray %6 %20
1333 %22 = OpTypePointer Function %21
1334 %25 = OpConstant %6 10
1335 %30 = OpConstant %6 1
1336 %4 = OpFunction %2 None %3
1337 %5 = OpLabel
1338 %23 = OpVariable %22 Function
1339 OpBranch %10
1340 %10 = OpLabel
1341 %32 = OpPhi %6 %9 %5 %31 %13
1342 OpLoopMerge %12 %13 Unroll
1343 OpBranch %14
1344 %14 = OpLabel
1345 %18 = OpSLessThan %17 %32 %16
1346 OpBranchConditional %18 %11 %12
1347 %11 = OpLabel
1348 %26 = OpIAdd %6 %32 %25
1349 %28 = OpAccessChain %7 %23 %26
1350 OpStore %28 %32
1351 OpBranch %13
1352 %13 = OpLabel
1353 %31 = OpIAdd %6 %32 %30
1354 OpBranch %10
1355 %12 = OpLabel
1356 OpReturn
1357 OpFunctionEnd
1358 )";
1359
1360 const std::string expected = R"(OpCapability Shader
1361 %1 = OpExtInstImport "GLSL.std.450"
1362 OpMemoryModel Logical GLSL450
1363 OpEntryPoint Fragment %2 "main"
1364 OpExecutionMode %2 OriginUpperLeft
1365 OpSource GLSL 410
1366 OpName %2 "main"
1367 OpName %3 "out_array"
1368 %4 = OpTypeVoid
1369 %5 = OpTypeFunction %4
1370 %6 = OpTypeInt 32 1
1371 %7 = OpTypePointer Function %6
1372 %8 = OpConstant %6 -10
1373 %9 = OpConstant %6 -1
1374 %10 = OpTypeBool
1375 %11 = OpTypeInt 32 0
1376 %12 = OpConstant %11 9
1377 %13 = OpTypeArray %6 %12
1378 %14 = OpTypePointer Function %13
1379 %15 = OpConstant %6 10
1380 %16 = OpConstant %6 1
1381 %48 = OpConstant %6 -9
1382 %2 = OpFunction %4 None %5
1383 %17 = OpLabel
1384 %3 = OpVariable %14 Function
1385 OpBranch %18
1386 %18 = OpLabel
1387 %19 = OpPhi %6 %8 %17 %20 %21
1388 OpLoopMerge %28 %21 Unroll
1389 OpBranch %23
1390 %23 = OpLabel
1391 %24 = OpSLessThan %10 %19 %48
1392 OpBranchConditional %24 %25 %28
1393 %25 = OpLabel
1394 %26 = OpIAdd %6 %19 %15
1395 %27 = OpAccessChain %7 %3 %26
1396 OpStore %27 %19
1397 OpBranch %21
1398 %21 = OpLabel
1399 %20 = OpIAdd %6 %19 %16
1400 OpBranch %18
1401 %28 = OpLabel
1402 OpBranch %29
1403 %29 = OpLabel
1404 %30 = OpPhi %6 %19 %28 %47 %46
1405 OpLoopMerge %38 %46 DontUnroll
1406 OpBranch %31
1407 %31 = OpLabel
1408 %32 = OpSLessThan %10 %30 %9
1409 OpBranchConditional %32 %33 %38
1410 %33 = OpLabel
1411 %34 = OpIAdd %6 %30 %15
1412 %35 = OpAccessChain %7 %3 %34
1413 OpStore %35 %30
1414 OpBranch %36
1415 %36 = OpLabel
1416 %37 = OpIAdd %6 %30 %16
1417 OpBranch %39
1418 %39 = OpLabel
1419 OpBranch %41
1420 %41 = OpLabel
1421 %42 = OpSLessThan %10 %37 %9
1422 OpBranch %43
1423 %43 = OpLabel
1424 %44 = OpIAdd %6 %37 %15
1425 %45 = OpAccessChain %7 %3 %44
1426 OpStore %45 %37
1427 OpBranch %46
1428 %46 = OpLabel
1429 %47 = OpIAdd %6 %37 %16
1430 OpBranch %29
1431 %38 = OpLabel
1432 OpReturn
1433 %22 = OpLabel
1434 OpReturn
1435 OpFunctionEnd
1436 )";
1437
1438 std::unique_ptr<IRContext> context =
1439 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1440 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1441 Module* module = context->module();
1442 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1443 << text << std::endl;
1444
1445 LoopUnroller loop_unroller;
1446 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1447
1448 Function* f = spvtest::GetFunction(module, 4);
1449
1450 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
1451 EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
1452
1453 Loop& loop = loop_descriptor.GetLoopByIndex(0);
1454
1455 EXPECT_TRUE(loop.HasUnrollLoopControl());
1456
1457 BasicBlock* condition = loop.FindConditionBlock();
1458 EXPECT_EQ(condition->id(), 14u);
1459
1460 Instruction* induction = loop.FindConditionVariable(condition);
1461 EXPECT_EQ(induction->result_id(), 32u);
1462
1463 LoopUtils loop_utils{context.get(), &loop};
1464 EXPECT_TRUE(loop_utils.CanPerformUnroll());
1465
1466 size_t iterations = 0;
1467 EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(),
1468 &iterations));
1469 EXPECT_EQ(iterations, 9u);
1470 SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(text, expected, false);
1471 }
1472
1473 /*
1474 Generated from the following GLSL
1475 #version 410 core
1476 void main() {
1477 float out_array[10];
1478 for (uint i = 0; i < 2; i++) {
1479 for (int x = 0; x < 5; ++x) {
1480 out_array[x + i*5] = i;
1481 }
1482 }
1483 }
1484 */
TEST_F(PassClassTest,UnrollNestedLoopsValidateDescriptor)1485 TEST_F(PassClassTest, UnrollNestedLoopsValidateDescriptor) {
1486 // With LocalMultiStoreElimPass
1487 const std::string text = R"(
1488 OpCapability Shader
1489 %1 = OpExtInstImport "GLSL.std.450"
1490 OpMemoryModel Logical GLSL450
1491 OpEntryPoint Fragment %4 "main"
1492 OpExecutionMode %4 OriginUpperLeft
1493 OpSource GLSL 410
1494 OpName %4 "main"
1495 OpName %35 "out_array"
1496 %2 = OpTypeVoid
1497 %3 = OpTypeFunction %2
1498 %6 = OpTypeInt 32 0
1499 %7 = OpTypePointer Function %6
1500 %9 = OpConstant %6 0
1501 %16 = OpConstant %6 2
1502 %17 = OpTypeBool
1503 %19 = OpTypeInt 32 1
1504 %20 = OpTypePointer Function %19
1505 %22 = OpConstant %19 0
1506 %29 = OpConstant %19 5
1507 %31 = OpTypeFloat 32
1508 %32 = OpConstant %6 10
1509 %33 = OpTypeArray %31 %32
1510 %34 = OpTypePointer Function %33
1511 %39 = OpConstant %6 5
1512 %44 = OpTypePointer Function %31
1513 %47 = OpConstant %19 1
1514 %4 = OpFunction %2 None %3
1515 %5 = OpLabel
1516 %35 = OpVariable %34 Function
1517 OpBranch %10
1518 %10 = OpLabel
1519 %51 = OpPhi %6 %9 %5 %50 %13
1520 OpLoopMerge %12 %13 Unroll
1521 OpBranch %14
1522 %14 = OpLabel
1523 %18 = OpULessThan %17 %51 %16
1524 OpBranchConditional %18 %11 %12
1525 %11 = OpLabel
1526 OpBranch %23
1527 %23 = OpLabel
1528 %54 = OpPhi %19 %22 %11 %48 %26
1529 OpLoopMerge %25 %26 Unroll
1530 OpBranch %27
1531 %27 = OpLabel
1532 %30 = OpSLessThan %17 %54 %29
1533 OpBranchConditional %30 %24 %25
1534 %24 = OpLabel
1535 %37 = OpBitcast %6 %54
1536 %40 = OpIMul %6 %51 %39
1537 %41 = OpIAdd %6 %37 %40
1538 %43 = OpConvertUToF %31 %51
1539 %45 = OpAccessChain %44 %35 %41
1540 OpStore %45 %43
1541 OpBranch %26
1542 %26 = OpLabel
1543 %48 = OpIAdd %19 %54 %47
1544 OpBranch %23
1545 %25 = OpLabel
1546 OpBranch %13
1547 %13 = OpLabel
1548 %50 = OpIAdd %6 %51 %47
1549 OpBranch %10
1550 %12 = OpLabel
1551 OpReturn
1552 OpFunctionEnd
1553 )";
1554
1555 { // Test fully unroll
1556 std::unique_ptr<IRContext> context =
1557 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1558 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1559 Module* module = context->module();
1560 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1561 << text << std::endl;
1562 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1563
1564 Function* f = spvtest::GetFunction(module, 4);
1565 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
1566 EXPECT_EQ(loop_descriptor.NumLoops(), 2u);
1567
1568 Loop& outer_loop = loop_descriptor.GetLoopByIndex(1);
1569
1570 EXPECT_TRUE(outer_loop.HasUnrollLoopControl());
1571
1572 Loop& inner_loop = loop_descriptor.GetLoopByIndex(0);
1573
1574 EXPECT_TRUE(inner_loop.HasUnrollLoopControl());
1575
1576 EXPECT_EQ(outer_loop.GetBlocks().size(), 9u);
1577
1578 EXPECT_EQ(inner_loop.GetBlocks().size(), 4u);
1579 EXPECT_EQ(outer_loop.NumImmediateChildren(), 1u);
1580 EXPECT_EQ(inner_loop.NumImmediateChildren(), 0u);
1581
1582 {
1583 LoopUtils loop_utils{context.get(), &inner_loop};
1584 loop_utils.FullyUnroll();
1585 loop_utils.Finalize();
1586 }
1587
1588 EXPECT_EQ(loop_descriptor.NumLoops(), 1u);
1589 EXPECT_EQ(outer_loop.GetBlocks().size(), 25u);
1590 EXPECT_EQ(outer_loop.NumImmediateChildren(), 0u);
1591 {
1592 LoopUtils loop_utils{context.get(), &outer_loop};
1593 loop_utils.FullyUnroll();
1594 loop_utils.Finalize();
1595 }
1596 EXPECT_EQ(loop_descriptor.NumLoops(), 0u);
1597 }
1598
1599 { // Test partially unroll
1600 std::unique_ptr<IRContext> context =
1601 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1602 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1603 Module* module = context->module();
1604 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1605 << text << std::endl;
1606 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1607
1608 Function* f = spvtest::GetFunction(module, 4);
1609 LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f);
1610 EXPECT_EQ(loop_descriptor.NumLoops(), 2u);
1611
1612 Loop& outer_loop = loop_descriptor.GetLoopByIndex(1);
1613
1614 EXPECT_TRUE(outer_loop.HasUnrollLoopControl());
1615
1616 Loop& inner_loop = loop_descriptor.GetLoopByIndex(0);
1617
1618 EXPECT_TRUE(inner_loop.HasUnrollLoopControl());
1619
1620 EXPECT_EQ(outer_loop.GetBlocks().size(), 9u);
1621
1622 EXPECT_EQ(inner_loop.GetBlocks().size(), 4u);
1623
1624 EXPECT_EQ(outer_loop.NumImmediateChildren(), 1u);
1625 EXPECT_EQ(inner_loop.NumImmediateChildren(), 0u);
1626
1627 LoopUtils loop_utils{context.get(), &inner_loop};
1628 loop_utils.PartiallyUnroll(2);
1629 loop_utils.Finalize();
1630
1631 // The number of loops should actually grow.
1632 EXPECT_EQ(loop_descriptor.NumLoops(), 3u);
1633 EXPECT_EQ(outer_loop.GetBlocks().size(), 18u);
1634 EXPECT_EQ(outer_loop.NumImmediateChildren(), 2u);
1635 }
1636 }
1637
1638 /*
1639 Generated from the following GLSL
1640 #version 410 core
1641 void main() {
1642 float out_array[3];
1643 for (int i = 3; i > 0; --i) {
1644 out_array[i] = i;
1645 }
1646 }
1647 */
TEST_F(PassClassTest,FullyUnrollNegativeStepLoopTest)1648 TEST_F(PassClassTest, FullyUnrollNegativeStepLoopTest) {
1649 // With LocalMultiStoreElimPass
1650 const std::string text = R"(
1651 OpCapability Shader
1652 %1 = OpExtInstImport "GLSL.std.450"
1653 OpMemoryModel Logical GLSL450
1654 OpEntryPoint Fragment %4 "main"
1655 OpExecutionMode %4 OriginUpperLeft
1656 OpSource GLSL 410
1657 OpName %4 "main"
1658 OpName %24 "out_array"
1659 %2 = OpTypeVoid
1660 %3 = OpTypeFunction %2
1661 %6 = OpTypeInt 32 1
1662 %7 = OpTypePointer Function %6
1663 %9 = OpConstant %6 3
1664 %16 = OpConstant %6 0
1665 %17 = OpTypeBool
1666 %19 = OpTypeFloat 32
1667 %20 = OpTypeInt 32 0
1668 %21 = OpConstant %20 3
1669 %22 = OpTypeArray %19 %21
1670 %23 = OpTypePointer Function %22
1671 %28 = OpTypePointer Function %19
1672 %31 = OpConstant %6 1
1673 %4 = OpFunction %2 None %3
1674 %5 = OpLabel
1675 %24 = OpVariable %23 Function
1676 OpBranch %10
1677 %10 = OpLabel
1678 %33 = OpPhi %6 %9 %5 %32 %13
1679 OpLoopMerge %12 %13 Unroll
1680 OpBranch %14
1681 %14 = OpLabel
1682 %18 = OpSGreaterThan %17 %33 %16
1683 OpBranchConditional %18 %11 %12
1684 %11 = OpLabel
1685 %27 = OpConvertSToF %19 %33
1686 %29 = OpAccessChain %28 %24 %33
1687 OpStore %29 %27
1688 OpBranch %13
1689 %13 = OpLabel
1690 %32 = OpISub %6 %33 %31
1691 OpBranch %10
1692 %12 = OpLabel
1693 OpReturn
1694 OpFunctionEnd
1695 )";
1696
1697 const std::string output = R"(OpCapability Shader
1698 %1 = OpExtInstImport "GLSL.std.450"
1699 OpMemoryModel Logical GLSL450
1700 OpEntryPoint Fragment %2 "main"
1701 OpExecutionMode %2 OriginUpperLeft
1702 OpSource GLSL 410
1703 OpName %2 "main"
1704 OpName %3 "out_array"
1705 %4 = OpTypeVoid
1706 %5 = OpTypeFunction %4
1707 %6 = OpTypeInt 32 1
1708 %7 = OpTypePointer Function %6
1709 %8 = OpConstant %6 3
1710 %9 = OpConstant %6 0
1711 %10 = OpTypeBool
1712 %11 = OpTypeFloat 32
1713 %12 = OpTypeInt 32 0
1714 %13 = OpConstant %12 3
1715 %14 = OpTypeArray %11 %13
1716 %15 = OpTypePointer Function %14
1717 %16 = OpTypePointer Function %11
1718 %17 = OpConstant %6 1
1719 %2 = OpFunction %4 None %5
1720 %18 = OpLabel
1721 %3 = OpVariable %15 Function
1722 OpBranch %19
1723 %19 = OpLabel
1724 OpBranch %24
1725 %24 = OpLabel
1726 %25 = OpSGreaterThan %10 %8 %9
1727 OpBranch %26
1728 %26 = OpLabel
1729 %27 = OpConvertSToF %11 %8
1730 %28 = OpAccessChain %16 %3 %8
1731 OpStore %28 %27
1732 OpBranch %22
1733 %22 = OpLabel
1734 %21 = OpISub %6 %8 %17
1735 OpBranch %29
1736 %29 = OpLabel
1737 OpBranch %31
1738 %31 = OpLabel
1739 %32 = OpSGreaterThan %10 %21 %9
1740 OpBranch %33
1741 %33 = OpLabel
1742 %34 = OpConvertSToF %11 %21
1743 %35 = OpAccessChain %16 %3 %21
1744 OpStore %35 %34
1745 OpBranch %36
1746 %36 = OpLabel
1747 %37 = OpISub %6 %21 %17
1748 OpBranch %38
1749 %38 = OpLabel
1750 OpBranch %40
1751 %40 = OpLabel
1752 %41 = OpSGreaterThan %10 %37 %9
1753 OpBranch %42
1754 %42 = OpLabel
1755 %43 = OpConvertSToF %11 %37
1756 %44 = OpAccessChain %16 %3 %37
1757 OpStore %44 %43
1758 OpBranch %45
1759 %45 = OpLabel
1760 %46 = OpISub %6 %37 %17
1761 OpBranch %23
1762 %23 = OpLabel
1763 OpReturn
1764 OpFunctionEnd
1765 )";
1766
1767 std::unique_ptr<IRContext> context =
1768 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1769 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1770 Module* module = context->module();
1771 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1772 << text << std::endl;
1773
1774 LoopUnroller loop_unroller;
1775 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1776 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
1777 }
1778
1779 /*
1780 Generated from the following GLSL
1781 #version 410 core
1782 void main() {
1783 float out_array[3];
1784 for (int i = 9; i > 0; i-=3) {
1785 out_array[i] = i;
1786 }
1787 }
1788 */
TEST_F(PassClassTest,FullyUnrollNegativeNonOneStepLoop)1789 TEST_F(PassClassTest, FullyUnrollNegativeNonOneStepLoop) {
1790 // With LocalMultiStoreElimPass
1791 const std::string text = R"(
1792 OpCapability Shader
1793 %1 = OpExtInstImport "GLSL.std.450"
1794 OpMemoryModel Logical GLSL450
1795 OpEntryPoint Fragment %4 "main"
1796 OpExecutionMode %4 OriginUpperLeft
1797 OpSource GLSL 410
1798 OpName %4 "main"
1799 OpName %24 "out_array"
1800 %2 = OpTypeVoid
1801 %3 = OpTypeFunction %2
1802 %6 = OpTypeInt 32 1
1803 %7 = OpTypePointer Function %6
1804 %9 = OpConstant %6 9
1805 %16 = OpConstant %6 0
1806 %17 = OpTypeBool
1807 %19 = OpTypeFloat 32
1808 %20 = OpTypeInt 32 0
1809 %21 = OpConstant %20 3
1810 %22 = OpTypeArray %19 %21
1811 %23 = OpTypePointer Function %22
1812 %28 = OpTypePointer Function %19
1813 %30 = OpConstant %6 3
1814 %4 = OpFunction %2 None %3
1815 %5 = OpLabel
1816 %24 = OpVariable %23 Function
1817 OpBranch %10
1818 %10 = OpLabel
1819 %33 = OpPhi %6 %9 %5 %32 %13
1820 OpLoopMerge %12 %13 Unroll
1821 OpBranch %14
1822 %14 = OpLabel
1823 %18 = OpSGreaterThan %17 %33 %16
1824 OpBranchConditional %18 %11 %12
1825 %11 = OpLabel
1826 %27 = OpConvertSToF %19 %33
1827 %29 = OpAccessChain %28 %24 %33
1828 OpStore %29 %27
1829 OpBranch %13
1830 %13 = OpLabel
1831 %32 = OpISub %6 %33 %30
1832 OpBranch %10
1833 %12 = OpLabel
1834 OpReturn
1835 OpFunctionEnd
1836 )";
1837
1838 const std::string output = R"(OpCapability Shader
1839 %1 = OpExtInstImport "GLSL.std.450"
1840 OpMemoryModel Logical GLSL450
1841 OpEntryPoint Fragment %2 "main"
1842 OpExecutionMode %2 OriginUpperLeft
1843 OpSource GLSL 410
1844 OpName %2 "main"
1845 OpName %3 "out_array"
1846 %4 = OpTypeVoid
1847 %5 = OpTypeFunction %4
1848 %6 = OpTypeInt 32 1
1849 %7 = OpTypePointer Function %6
1850 %8 = OpConstant %6 9
1851 %9 = OpConstant %6 0
1852 %10 = OpTypeBool
1853 %11 = OpTypeFloat 32
1854 %12 = OpTypeInt 32 0
1855 %13 = OpConstant %12 3
1856 %14 = OpTypeArray %11 %13
1857 %15 = OpTypePointer Function %14
1858 %16 = OpTypePointer Function %11
1859 %17 = OpConstant %6 3
1860 %2 = OpFunction %4 None %5
1861 %18 = OpLabel
1862 %3 = OpVariable %15 Function
1863 OpBranch %19
1864 %19 = OpLabel
1865 OpBranch %24
1866 %24 = OpLabel
1867 %25 = OpSGreaterThan %10 %8 %9
1868 OpBranch %26
1869 %26 = OpLabel
1870 %27 = OpConvertSToF %11 %8
1871 %28 = OpAccessChain %16 %3 %8
1872 OpStore %28 %27
1873 OpBranch %22
1874 %22 = OpLabel
1875 %21 = OpISub %6 %8 %17
1876 OpBranch %29
1877 %29 = OpLabel
1878 OpBranch %31
1879 %31 = OpLabel
1880 %32 = OpSGreaterThan %10 %21 %9
1881 OpBranch %33
1882 %33 = OpLabel
1883 %34 = OpConvertSToF %11 %21
1884 %35 = OpAccessChain %16 %3 %21
1885 OpStore %35 %34
1886 OpBranch %36
1887 %36 = OpLabel
1888 %37 = OpISub %6 %21 %17
1889 OpBranch %38
1890 %38 = OpLabel
1891 OpBranch %40
1892 %40 = OpLabel
1893 %41 = OpSGreaterThan %10 %37 %9
1894 OpBranch %42
1895 %42 = OpLabel
1896 %43 = OpConvertSToF %11 %37
1897 %44 = OpAccessChain %16 %3 %37
1898 OpStore %44 %43
1899 OpBranch %45
1900 %45 = OpLabel
1901 %46 = OpISub %6 %37 %17
1902 OpBranch %23
1903 %23 = OpLabel
1904 OpReturn
1905 OpFunctionEnd
1906 )";
1907
1908 std::unique_ptr<IRContext> context =
1909 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1910 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1911 Module* module = context->module();
1912 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
1913 << text << std::endl;
1914
1915 LoopUnroller loop_unroller;
1916 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
1917 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
1918 }
1919
1920 /*
1921 Generated from the following GLSL
1922 #version 410 core
1923 void main() {
1924 float out_array[3];
1925 for (int i = 0; i < 7; i+=3) {
1926 out_array[i] = i;
1927 }
1928 }
1929 */
TEST_F(PassClassTest,FullyUnrollNonDivisibleStepLoop)1930 TEST_F(PassClassTest, FullyUnrollNonDivisibleStepLoop) {
1931 // With LocalMultiStoreElimPass
1932 const std::string text = R"(OpCapability Shader
1933 %1 = OpExtInstImport "GLSL.std.450"
1934 OpMemoryModel Logical GLSL450
1935 OpEntryPoint Fragment %4 "main"
1936 OpExecutionMode %4 OriginUpperLeft
1937 OpSource GLSL 410
1938 OpName %4 "main"
1939 OpName %24 "out_array"
1940 %2 = OpTypeVoid
1941 %3 = OpTypeFunction %2
1942 %6 = OpTypeInt 32 1
1943 %7 = OpTypePointer Function %6
1944 %9 = OpConstant %6 0
1945 %16 = OpConstant %6 7
1946 %17 = OpTypeBool
1947 %19 = OpTypeFloat 32
1948 %20 = OpTypeInt 32 0
1949 %21 = OpConstant %20 3
1950 %22 = OpTypeArray %19 %21
1951 %23 = OpTypePointer Function %22
1952 %28 = OpTypePointer Function %19
1953 %30 = OpConstant %6 3
1954 %4 = OpFunction %2 None %3
1955 %5 = OpLabel
1956 %24 = OpVariable %23 Function
1957 OpBranch %10
1958 %10 = OpLabel
1959 %33 = OpPhi %6 %9 %5 %32 %13
1960 OpLoopMerge %12 %13 Unroll
1961 OpBranch %14
1962 %14 = OpLabel
1963 %18 = OpSLessThan %17 %33 %16
1964 OpBranchConditional %18 %11 %12
1965 %11 = OpLabel
1966 %27 = OpConvertSToF %19 %33
1967 %29 = OpAccessChain %28 %24 %33
1968 OpStore %29 %27
1969 OpBranch %13
1970 %13 = OpLabel
1971 %32 = OpIAdd %6 %33 %30
1972 OpBranch %10
1973 %12 = OpLabel
1974 OpReturn
1975 OpFunctionEnd
1976 )";
1977
1978 const std::string output = R"(OpCapability Shader
1979 %1 = OpExtInstImport "GLSL.std.450"
1980 OpMemoryModel Logical GLSL450
1981 OpEntryPoint Fragment %2 "main"
1982 OpExecutionMode %2 OriginUpperLeft
1983 OpSource GLSL 410
1984 OpName %2 "main"
1985 OpName %3 "out_array"
1986 %4 = OpTypeVoid
1987 %5 = OpTypeFunction %4
1988 %6 = OpTypeInt 32 1
1989 %7 = OpTypePointer Function %6
1990 %8 = OpConstant %6 0
1991 %9 = OpConstant %6 7
1992 %10 = OpTypeBool
1993 %11 = OpTypeFloat 32
1994 %12 = OpTypeInt 32 0
1995 %13 = OpConstant %12 3
1996 %14 = OpTypeArray %11 %13
1997 %15 = OpTypePointer Function %14
1998 %16 = OpTypePointer Function %11
1999 %17 = OpConstant %6 3
2000 %2 = OpFunction %4 None %5
2001 %18 = OpLabel
2002 %3 = OpVariable %15 Function
2003 OpBranch %19
2004 %19 = OpLabel
2005 OpBranch %24
2006 %24 = OpLabel
2007 %25 = OpSLessThan %10 %8 %9
2008 OpBranch %26
2009 %26 = OpLabel
2010 %27 = OpConvertSToF %11 %8
2011 %28 = OpAccessChain %16 %3 %8
2012 OpStore %28 %27
2013 OpBranch %22
2014 %22 = OpLabel
2015 %21 = OpIAdd %6 %8 %17
2016 OpBranch %29
2017 %29 = OpLabel
2018 OpBranch %31
2019 %31 = OpLabel
2020 %32 = OpSLessThan %10 %21 %9
2021 OpBranch %33
2022 %33 = OpLabel
2023 %34 = OpConvertSToF %11 %21
2024 %35 = OpAccessChain %16 %3 %21
2025 OpStore %35 %34
2026 OpBranch %36
2027 %36 = OpLabel
2028 %37 = OpIAdd %6 %21 %17
2029 OpBranch %38
2030 %38 = OpLabel
2031 OpBranch %40
2032 %40 = OpLabel
2033 %41 = OpSLessThan %10 %37 %9
2034 OpBranch %42
2035 %42 = OpLabel
2036 %43 = OpConvertSToF %11 %37
2037 %44 = OpAccessChain %16 %3 %37
2038 OpStore %44 %43
2039 OpBranch %45
2040 %45 = OpLabel
2041 %46 = OpIAdd %6 %37 %17
2042 OpBranch %23
2043 %23 = OpLabel
2044 OpReturn
2045 OpFunctionEnd
2046 )";
2047
2048 std::unique_ptr<IRContext> context =
2049 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
2050 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2051 Module* module = context->module();
2052 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2053 << text << std::endl;
2054
2055 LoopUnroller loop_unroller;
2056 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2057 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
2058 }
2059
2060 /*
2061 Generated from the following GLSL
2062 #version 410 core
2063 void main() {
2064 float out_array[4];
2065 for (int i = 11; i > 0; i-=3) {
2066 out_array[i] = i;
2067 }
2068 }
2069 */
TEST_F(PassClassTest,FullyUnrollNegativeNonDivisibleStepLoop)2070 TEST_F(PassClassTest, FullyUnrollNegativeNonDivisibleStepLoop) {
2071 // With LocalMultiStoreElimPass
2072 const std::string text = R"(OpCapability Shader
2073 %1 = OpExtInstImport "GLSL.std.450"
2074 OpMemoryModel Logical GLSL450
2075 OpEntryPoint Fragment %4 "main"
2076 OpExecutionMode %4 OriginUpperLeft
2077 OpSource GLSL 410
2078 OpName %4 "main"
2079 OpName %24 "out_array"
2080 %2 = OpTypeVoid
2081 %3 = OpTypeFunction %2
2082 %6 = OpTypeInt 32 1
2083 %7 = OpTypePointer Function %6
2084 %9 = OpConstant %6 11
2085 %16 = OpConstant %6 0
2086 %17 = OpTypeBool
2087 %19 = OpTypeFloat 32
2088 %20 = OpTypeInt 32 0
2089 %21 = OpConstant %20 4
2090 %22 = OpTypeArray %19 %21
2091 %23 = OpTypePointer Function %22
2092 %28 = OpTypePointer Function %19
2093 %30 = OpConstant %6 3
2094 %4 = OpFunction %2 None %3
2095 %5 = OpLabel
2096 %24 = OpVariable %23 Function
2097 OpBranch %10
2098 %10 = OpLabel
2099 %33 = OpPhi %6 %9 %5 %32 %13
2100 OpLoopMerge %12 %13 Unroll
2101 OpBranch %14
2102 %14 = OpLabel
2103 %18 = OpSGreaterThan %17 %33 %16
2104 OpBranchConditional %18 %11 %12
2105 %11 = OpLabel
2106 %27 = OpConvertSToF %19 %33
2107 %29 = OpAccessChain %28 %24 %33
2108 OpStore %29 %27
2109 OpBranch %13
2110 %13 = OpLabel
2111 %32 = OpISub %6 %33 %30
2112 OpBranch %10
2113 %12 = OpLabel
2114 OpReturn
2115 OpFunctionEnd
2116 )";
2117
2118 const std::string output = R"(OpCapability Shader
2119 %1 = OpExtInstImport "GLSL.std.450"
2120 OpMemoryModel Logical GLSL450
2121 OpEntryPoint Fragment %2 "main"
2122 OpExecutionMode %2 OriginUpperLeft
2123 OpSource GLSL 410
2124 OpName %2 "main"
2125 OpName %3 "out_array"
2126 %4 = OpTypeVoid
2127 %5 = OpTypeFunction %4
2128 %6 = OpTypeInt 32 1
2129 %7 = OpTypePointer Function %6
2130 %8 = OpConstant %6 11
2131 %9 = OpConstant %6 0
2132 %10 = OpTypeBool
2133 %11 = OpTypeFloat 32
2134 %12 = OpTypeInt 32 0
2135 %13 = OpConstant %12 4
2136 %14 = OpTypeArray %11 %13
2137 %15 = OpTypePointer Function %14
2138 %16 = OpTypePointer Function %11
2139 %17 = OpConstant %6 3
2140 %2 = OpFunction %4 None %5
2141 %18 = OpLabel
2142 %3 = OpVariable %15 Function
2143 OpBranch %19
2144 %19 = OpLabel
2145 OpBranch %24
2146 %24 = OpLabel
2147 %25 = OpSGreaterThan %10 %8 %9
2148 OpBranch %26
2149 %26 = OpLabel
2150 %27 = OpConvertSToF %11 %8
2151 %28 = OpAccessChain %16 %3 %8
2152 OpStore %28 %27
2153 OpBranch %22
2154 %22 = OpLabel
2155 %21 = OpISub %6 %8 %17
2156 OpBranch %29
2157 %29 = OpLabel
2158 OpBranch %31
2159 %31 = OpLabel
2160 %32 = OpSGreaterThan %10 %21 %9
2161 OpBranch %33
2162 %33 = OpLabel
2163 %34 = OpConvertSToF %11 %21
2164 %35 = OpAccessChain %16 %3 %21
2165 OpStore %35 %34
2166 OpBranch %36
2167 %36 = OpLabel
2168 %37 = OpISub %6 %21 %17
2169 OpBranch %38
2170 %38 = OpLabel
2171 OpBranch %40
2172 %40 = OpLabel
2173 %41 = OpSGreaterThan %10 %37 %9
2174 OpBranch %42
2175 %42 = OpLabel
2176 %43 = OpConvertSToF %11 %37
2177 %44 = OpAccessChain %16 %3 %37
2178 OpStore %44 %43
2179 OpBranch %45
2180 %45 = OpLabel
2181 %46 = OpISub %6 %37 %17
2182 OpBranch %47
2183 %47 = OpLabel
2184 OpBranch %49
2185 %49 = OpLabel
2186 %50 = OpSGreaterThan %10 %46 %9
2187 OpBranch %51
2188 %51 = OpLabel
2189 %52 = OpConvertSToF %11 %46
2190 %53 = OpAccessChain %16 %3 %46
2191 OpStore %53 %52
2192 OpBranch %54
2193 %54 = OpLabel
2194 %55 = OpISub %6 %46 %17
2195 OpBranch %23
2196 %23 = OpLabel
2197 OpReturn
2198 OpFunctionEnd
2199 )";
2200
2201 std::unique_ptr<IRContext> context =
2202 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
2203 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2204 Module* module = context->module();
2205 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2206 << text << std::endl;
2207
2208 LoopUnroller loop_unroller;
2209 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2210 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
2211 }
2212
2213 // With LocalMultiStoreElimPass
2214 static const std::string multiple_phi_shader = R"(
2215 OpCapability Shader
2216 %1 = OpExtInstImport "GLSL.std.450"
2217 OpMemoryModel Logical GLSL450
2218 OpEntryPoint Fragment %4 "main"
2219 OpExecutionMode %4 OriginUpperLeft
2220 OpSource GLSL 410
2221 OpName %4 "main"
2222 OpName %8 "foo("
2223 %2 = OpTypeVoid
2224 %3 = OpTypeFunction %2
2225 %6 = OpTypeInt 32 1
2226 %7 = OpTypeFunction %6
2227 %10 = OpTypePointer Function %6
2228 %12 = OpConstant %6 0
2229 %14 = OpConstant %6 3
2230 %22 = OpConstant %6 6
2231 %23 = OpTypeBool
2232 %31 = OpConstant %6 1
2233 %4 = OpFunction %2 None %3
2234 %5 = OpLabel
2235 %40 = OpFunctionCall %6 %8
2236 OpReturn
2237 OpFunctionEnd
2238 %8 = OpFunction %6 None %7
2239 %9 = OpLabel
2240 OpBranch %16
2241 %16 = OpLabel
2242 %41 = OpPhi %6 %12 %9 %34 %19
2243 %42 = OpPhi %6 %14 %9 %29 %19
2244 %43 = OpPhi %6 %12 %9 %32 %19
2245 OpLoopMerge %18 %19 Unroll
2246 OpBranch %20
2247 %20 = OpLabel
2248 %24 = OpSLessThan %23 %43 %22
2249 OpBranchConditional %24 %17 %18
2250 %17 = OpLabel
2251 %27 = OpIMul %6 %43 %41
2252 %29 = OpIAdd %6 %42 %27
2253 OpBranch %19
2254 %19 = OpLabel
2255 %32 = OpIAdd %6 %43 %31
2256 %34 = OpISub %6 %41 %31
2257 OpBranch %16
2258 %18 = OpLabel
2259 %37 = OpIAdd %6 %42 %41
2260 OpReturnValue %37
2261 OpFunctionEnd
2262 )";
2263
TEST_F(PassClassTest,PartiallyUnrollResidualMultipleInductionVariables)2264 TEST_F(PassClassTest, PartiallyUnrollResidualMultipleInductionVariables) {
2265 const std::string output = R"(OpCapability Shader
2266 %1 = OpExtInstImport "GLSL.std.450"
2267 OpMemoryModel Logical GLSL450
2268 OpEntryPoint Fragment %2 "main"
2269 OpExecutionMode %2 OriginUpperLeft
2270 OpSource GLSL 410
2271 OpName %2 "main"
2272 OpName %3 "foo("
2273 %4 = OpTypeVoid
2274 %5 = OpTypeFunction %4
2275 %6 = OpTypeInt 32 1
2276 %7 = OpTypeFunction %6
2277 %8 = OpTypePointer Function %6
2278 %9 = OpConstant %6 0
2279 %10 = OpConstant %6 3
2280 %11 = OpConstant %6 6
2281 %12 = OpTypeBool
2282 %13 = OpConstant %6 1
2283 %82 = OpTypeInt 32 0
2284 %83 = OpConstant %82 2
2285 %2 = OpFunction %4 None %5
2286 %14 = OpLabel
2287 %15 = OpFunctionCall %6 %3
2288 OpReturn
2289 OpFunctionEnd
2290 %3 = OpFunction %6 None %7
2291 %16 = OpLabel
2292 OpBranch %17
2293 %17 = OpLabel
2294 %18 = OpPhi %6 %9 %16 %19 %20
2295 %21 = OpPhi %6 %10 %16 %22 %20
2296 %23 = OpPhi %6 %9 %16 %24 %20
2297 OpLoopMerge %31 %20 Unroll
2298 OpBranch %26
2299 %26 = OpLabel
2300 %27 = OpSLessThan %12 %23 %83
2301 OpBranchConditional %27 %28 %31
2302 %28 = OpLabel
2303 %29 = OpIMul %6 %23 %18
2304 %22 = OpIAdd %6 %21 %29
2305 OpBranch %20
2306 %20 = OpLabel
2307 %24 = OpIAdd %6 %23 %13
2308 %19 = OpISub %6 %18 %13
2309 OpBranch %17
2310 %31 = OpLabel
2311 OpBranch %32
2312 %32 = OpLabel
2313 %33 = OpPhi %6 %18 %31 %81 %79
2314 %34 = OpPhi %6 %21 %31 %78 %79
2315 %35 = OpPhi %6 %23 %31 %80 %79
2316 OpLoopMerge %44 %79 DontUnroll
2317 OpBranch %36
2318 %36 = OpLabel
2319 %37 = OpSLessThan %12 %35 %11
2320 OpBranchConditional %37 %38 %44
2321 %38 = OpLabel
2322 %39 = OpIMul %6 %35 %33
2323 %40 = OpIAdd %6 %34 %39
2324 OpBranch %41
2325 %41 = OpLabel
2326 %42 = OpIAdd %6 %35 %13
2327 %43 = OpISub %6 %33 %13
2328 OpBranch %46
2329 %46 = OpLabel
2330 OpBranch %50
2331 %50 = OpLabel
2332 %51 = OpSLessThan %12 %42 %11
2333 OpBranch %52
2334 %52 = OpLabel
2335 %53 = OpIMul %6 %42 %43
2336 %54 = OpIAdd %6 %40 %53
2337 OpBranch %55
2338 %55 = OpLabel
2339 %56 = OpIAdd %6 %42 %13
2340 %57 = OpISub %6 %43 %13
2341 OpBranch %58
2342 %58 = OpLabel
2343 OpBranch %62
2344 %62 = OpLabel
2345 %63 = OpSLessThan %12 %56 %11
2346 OpBranch %64
2347 %64 = OpLabel
2348 %65 = OpIMul %6 %56 %57
2349 %66 = OpIAdd %6 %54 %65
2350 OpBranch %67
2351 %67 = OpLabel
2352 %68 = OpIAdd %6 %56 %13
2353 %69 = OpISub %6 %57 %13
2354 OpBranch %70
2355 %70 = OpLabel
2356 OpBranch %74
2357 %74 = OpLabel
2358 %75 = OpSLessThan %12 %68 %11
2359 OpBranch %76
2360 %76 = OpLabel
2361 %77 = OpIMul %6 %68 %69
2362 %78 = OpIAdd %6 %66 %77
2363 OpBranch %79
2364 %79 = OpLabel
2365 %80 = OpIAdd %6 %68 %13
2366 %81 = OpISub %6 %69 %13
2367 OpBranch %32
2368 %44 = OpLabel
2369 %45 = OpIAdd %6 %34 %33
2370 OpReturnValue %45
2371 %25 = OpLabel
2372 %30 = OpIAdd %6 %34 %33
2373 OpReturnValue %30
2374 OpFunctionEnd
2375 )";
2376
2377 std::unique_ptr<IRContext> context =
2378 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader,
2379 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2380 Module* module = context->module();
2381 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2382 << multiple_phi_shader << std::endl;
2383
2384 LoopUnroller loop_unroller;
2385 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2386 SinglePassRunAndCheck<PartialUnrollerTestPass<4>>(multiple_phi_shader, output,
2387 false);
2388 }
2389
TEST_F(PassClassTest,PartiallyUnrollMultipleInductionVariables)2390 TEST_F(PassClassTest, PartiallyUnrollMultipleInductionVariables) {
2391 const std::string output = R"(OpCapability Shader
2392 %1 = OpExtInstImport "GLSL.std.450"
2393 OpMemoryModel Logical GLSL450
2394 OpEntryPoint Fragment %2 "main"
2395 OpExecutionMode %2 OriginUpperLeft
2396 OpSource GLSL 410
2397 OpName %2 "main"
2398 OpName %3 "foo("
2399 %4 = OpTypeVoid
2400 %5 = OpTypeFunction %4
2401 %6 = OpTypeInt 32 1
2402 %7 = OpTypeFunction %6
2403 %8 = OpTypePointer Function %6
2404 %9 = OpConstant %6 0
2405 %10 = OpConstant %6 3
2406 %11 = OpConstant %6 6
2407 %12 = OpTypeBool
2408 %13 = OpConstant %6 1
2409 %2 = OpFunction %4 None %5
2410 %14 = OpLabel
2411 %15 = OpFunctionCall %6 %3
2412 OpReturn
2413 OpFunctionEnd
2414 %3 = OpFunction %6 None %7
2415 %16 = OpLabel
2416 OpBranch %17
2417 %17 = OpLabel
2418 %18 = OpPhi %6 %9 %16 %42 %40
2419 %21 = OpPhi %6 %10 %16 %39 %40
2420 %23 = OpPhi %6 %9 %16 %41 %40
2421 OpLoopMerge %25 %40 DontUnroll
2422 OpBranch %26
2423 %26 = OpLabel
2424 %27 = OpSLessThan %12 %23 %11
2425 OpBranchConditional %27 %28 %25
2426 %28 = OpLabel
2427 %29 = OpIMul %6 %23 %18
2428 %22 = OpIAdd %6 %21 %29
2429 OpBranch %20
2430 %20 = OpLabel
2431 %24 = OpIAdd %6 %23 %13
2432 %19 = OpISub %6 %18 %13
2433 OpBranch %31
2434 %31 = OpLabel
2435 OpBranch %35
2436 %35 = OpLabel
2437 %36 = OpSLessThan %12 %24 %11
2438 OpBranch %37
2439 %37 = OpLabel
2440 %38 = OpIMul %6 %24 %19
2441 %39 = OpIAdd %6 %22 %38
2442 OpBranch %40
2443 %40 = OpLabel
2444 %41 = OpIAdd %6 %24 %13
2445 %42 = OpISub %6 %19 %13
2446 OpBranch %17
2447 %25 = OpLabel
2448 %30 = OpIAdd %6 %21 %18
2449 OpReturnValue %30
2450 OpFunctionEnd
2451 )";
2452
2453 std::unique_ptr<IRContext> context =
2454 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader,
2455 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2456 Module* module = context->module();
2457 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2458 << multiple_phi_shader << std::endl;
2459
2460 LoopUnroller loop_unroller;
2461 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2462 SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(multiple_phi_shader, output,
2463 false);
2464 }
2465
TEST_F(PassClassTest,FullyUnrollMultipleInductionVariables)2466 TEST_F(PassClassTest, FullyUnrollMultipleInductionVariables) {
2467 const std::string output = R"(OpCapability Shader
2468 %1 = OpExtInstImport "GLSL.std.450"
2469 OpMemoryModel Logical GLSL450
2470 OpEntryPoint Fragment %2 "main"
2471 OpExecutionMode %2 OriginUpperLeft
2472 OpSource GLSL 410
2473 OpName %2 "main"
2474 OpName %3 "foo("
2475 %4 = OpTypeVoid
2476 %5 = OpTypeFunction %4
2477 %6 = OpTypeInt 32 1
2478 %7 = OpTypeFunction %6
2479 %8 = OpTypePointer Function %6
2480 %9 = OpConstant %6 0
2481 %10 = OpConstant %6 3
2482 %11 = OpConstant %6 6
2483 %12 = OpTypeBool
2484 %13 = OpConstant %6 1
2485 %2 = OpFunction %4 None %5
2486 %14 = OpLabel
2487 %15 = OpFunctionCall %6 %3
2488 OpReturn
2489 OpFunctionEnd
2490 %3 = OpFunction %6 None %7
2491 %16 = OpLabel
2492 OpBranch %17
2493 %17 = OpLabel
2494 OpBranch %26
2495 %26 = OpLabel
2496 %27 = OpSLessThan %12 %9 %11
2497 OpBranch %28
2498 %28 = OpLabel
2499 %29 = OpIMul %6 %9 %9
2500 %22 = OpIAdd %6 %10 %29
2501 OpBranch %20
2502 %20 = OpLabel
2503 %24 = OpIAdd %6 %9 %13
2504 %19 = OpISub %6 %9 %13
2505 OpBranch %31
2506 %31 = OpLabel
2507 OpBranch %35
2508 %35 = OpLabel
2509 %36 = OpSLessThan %12 %24 %11
2510 OpBranch %37
2511 %37 = OpLabel
2512 %38 = OpIMul %6 %24 %19
2513 %39 = OpIAdd %6 %22 %38
2514 OpBranch %40
2515 %40 = OpLabel
2516 %41 = OpIAdd %6 %24 %13
2517 %42 = OpISub %6 %19 %13
2518 OpBranch %43
2519 %43 = OpLabel
2520 OpBranch %47
2521 %47 = OpLabel
2522 %48 = OpSLessThan %12 %41 %11
2523 OpBranch %49
2524 %49 = OpLabel
2525 %50 = OpIMul %6 %41 %42
2526 %51 = OpIAdd %6 %39 %50
2527 OpBranch %52
2528 %52 = OpLabel
2529 %53 = OpIAdd %6 %41 %13
2530 %54 = OpISub %6 %42 %13
2531 OpBranch %55
2532 %55 = OpLabel
2533 OpBranch %59
2534 %59 = OpLabel
2535 %60 = OpSLessThan %12 %53 %11
2536 OpBranch %61
2537 %61 = OpLabel
2538 %62 = OpIMul %6 %53 %54
2539 %63 = OpIAdd %6 %51 %62
2540 OpBranch %64
2541 %64 = OpLabel
2542 %65 = OpIAdd %6 %53 %13
2543 %66 = OpISub %6 %54 %13
2544 OpBranch %67
2545 %67 = OpLabel
2546 OpBranch %71
2547 %71 = OpLabel
2548 %72 = OpSLessThan %12 %65 %11
2549 OpBranch %73
2550 %73 = OpLabel
2551 %74 = OpIMul %6 %65 %66
2552 %75 = OpIAdd %6 %63 %74
2553 OpBranch %76
2554 %76 = OpLabel
2555 %77 = OpIAdd %6 %65 %13
2556 %78 = OpISub %6 %66 %13
2557 OpBranch %79
2558 %79 = OpLabel
2559 OpBranch %83
2560 %83 = OpLabel
2561 %84 = OpSLessThan %12 %77 %11
2562 OpBranch %85
2563 %85 = OpLabel
2564 %86 = OpIMul %6 %77 %78
2565 %87 = OpIAdd %6 %75 %86
2566 OpBranch %88
2567 %88 = OpLabel
2568 %89 = OpIAdd %6 %77 %13
2569 %90 = OpISub %6 %78 %13
2570 OpBranch %25
2571 %25 = OpLabel
2572 %30 = OpIAdd %6 %87 %90
2573 OpReturnValue %30
2574 OpFunctionEnd
2575 )";
2576
2577 std::unique_ptr<IRContext> context =
2578 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader,
2579 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2580 Module* module = context->module();
2581 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2582 << multiple_phi_shader << std::endl;
2583
2584 LoopUnroller loop_unroller;
2585 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2586 SinglePassRunAndCheck<LoopUnroller>(multiple_phi_shader, output, false);
2587 }
2588
2589 /*
2590 Generated from the following GLSL
2591 #version 440 core
2592 void main()
2593 {
2594 int j = 0;
2595 for (int i = 0; i <= 2; ++i)
2596 ++j;
2597
2598 for (int i = 1; i >= 0; --i)
2599 ++j;
2600 }
2601 */
TEST_F(PassClassTest,FullyUnrollEqualToOperations)2602 TEST_F(PassClassTest, FullyUnrollEqualToOperations) {
2603 // With LocalMultiStoreElimPass
2604 const std::string text = R"(
2605 OpCapability Shader
2606 %1 = OpExtInstImport "GLSL.std.450"
2607 OpMemoryModel Logical GLSL450
2608 OpEntryPoint Fragment %4 "main"
2609 OpExecutionMode %4 OriginUpperLeft
2610 OpSource GLSL 440
2611 OpName %4 "main"
2612 %2 = OpTypeVoid
2613 %3 = OpTypeFunction %2
2614 %6 = OpTypeInt 32 1
2615 %7 = OpTypePointer Function %6
2616 %9 = OpConstant %6 0
2617 %17 = OpConstant %6 2
2618 %18 = OpTypeBool
2619 %21 = OpConstant %6 1
2620 %4 = OpFunction %2 None %3
2621 %5 = OpLabel
2622 OpBranch %11
2623 %11 = OpLabel
2624 %37 = OpPhi %6 %9 %5 %22 %14
2625 %38 = OpPhi %6 %9 %5 %24 %14
2626 OpLoopMerge %13 %14 Unroll
2627 OpBranch %15
2628 %15 = OpLabel
2629 %19 = OpSLessThanEqual %18 %38 %17
2630 OpBranchConditional %19 %12 %13
2631 %12 = OpLabel
2632 %22 = OpIAdd %6 %37 %21
2633 OpBranch %14
2634 %14 = OpLabel
2635 %24 = OpIAdd %6 %38 %21
2636 OpBranch %11
2637 %13 = OpLabel
2638 OpBranch %26
2639 %26 = OpLabel
2640 %39 = OpPhi %6 %37 %13 %34 %29
2641 %40 = OpPhi %6 %21 %13 %36 %29
2642 OpLoopMerge %28 %29 Unroll
2643 OpBranch %30
2644 %30 = OpLabel
2645 %32 = OpSGreaterThanEqual %18 %40 %9
2646 OpBranchConditional %32 %27 %28
2647 %27 = OpLabel
2648 %34 = OpIAdd %6 %39 %21
2649 OpBranch %29
2650 %29 = OpLabel
2651 %36 = OpISub %6 %40 %21
2652 OpBranch %26
2653 %28 = OpLabel
2654 OpReturn
2655 OpFunctionEnd
2656 )";
2657
2658 const std::string output = R"(OpCapability Shader
2659 %1 = OpExtInstImport "GLSL.std.450"
2660 OpMemoryModel Logical GLSL450
2661 OpEntryPoint Fragment %2 "main"
2662 OpExecutionMode %2 OriginUpperLeft
2663 OpSource GLSL 440
2664 OpName %2 "main"
2665 %3 = OpTypeVoid
2666 %4 = OpTypeFunction %3
2667 %5 = OpTypeInt 32 1
2668 %6 = OpTypePointer Function %5
2669 %7 = OpConstant %5 0
2670 %8 = OpConstant %5 2
2671 %9 = OpTypeBool
2672 %10 = OpConstant %5 1
2673 %2 = OpFunction %3 None %4
2674 %11 = OpLabel
2675 OpBranch %12
2676 %12 = OpLabel
2677 OpBranch %19
2678 %19 = OpLabel
2679 %20 = OpSLessThanEqual %9 %7 %8
2680 OpBranch %21
2681 %21 = OpLabel
2682 %14 = OpIAdd %5 %7 %10
2683 OpBranch %15
2684 %15 = OpLabel
2685 %17 = OpIAdd %5 %7 %10
2686 OpBranch %41
2687 %41 = OpLabel
2688 OpBranch %44
2689 %44 = OpLabel
2690 %45 = OpSLessThanEqual %9 %17 %8
2691 OpBranch %46
2692 %46 = OpLabel
2693 %47 = OpIAdd %5 %14 %10
2694 OpBranch %48
2695 %48 = OpLabel
2696 %49 = OpIAdd %5 %17 %10
2697 OpBranch %50
2698 %50 = OpLabel
2699 OpBranch %53
2700 %53 = OpLabel
2701 %54 = OpSLessThanEqual %9 %49 %8
2702 OpBranch %55
2703 %55 = OpLabel
2704 %56 = OpIAdd %5 %47 %10
2705 OpBranch %57
2706 %57 = OpLabel
2707 %58 = OpIAdd %5 %49 %10
2708 OpBranch %18
2709 %18 = OpLabel
2710 OpBranch %22
2711 %22 = OpLabel
2712 OpBranch %29
2713 %29 = OpLabel
2714 %30 = OpSGreaterThanEqual %9 %10 %7
2715 OpBranch %31
2716 %31 = OpLabel
2717 %24 = OpIAdd %5 %56 %10
2718 OpBranch %25
2719 %25 = OpLabel
2720 %27 = OpISub %5 %10 %10
2721 OpBranch %32
2722 %32 = OpLabel
2723 OpBranch %35
2724 %35 = OpLabel
2725 %36 = OpSGreaterThanEqual %9 %27 %7
2726 OpBranch %37
2727 %37 = OpLabel
2728 %38 = OpIAdd %5 %24 %10
2729 OpBranch %39
2730 %39 = OpLabel
2731 %40 = OpISub %5 %27 %10
2732 OpBranch %28
2733 %28 = OpLabel
2734 OpReturn
2735 OpFunctionEnd
2736 )";
2737
2738 std::unique_ptr<IRContext> context =
2739 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
2740 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2741 Module* module = context->module();
2742 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2743 << text << std::endl;
2744
2745 LoopUnroller loop_unroller;
2746 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2747 SinglePassRunAndCheck<LoopUnroller>(text, output, false);
2748 }
2749
2750 // With LocalMultiStoreElimPass
2751 const std::string condition_in_header = R"(
2752 OpCapability Shader
2753 OpMemoryModel Logical GLSL450
2754 OpEntryPoint Fragment %main "main" %o
2755 OpExecutionMode %main OriginUpperLeft
2756 OpSource GLSL 430
2757 OpDecorate %o Location 0
2758 %void = OpTypeVoid
2759 %6 = OpTypeFunction %void
2760 %int = OpTypeInt 32 1
2761 %int_n2 = OpConstant %int -2
2762 %int_2 = OpConstant %int 2
2763 %bool = OpTypeBool
2764 %float = OpTypeFloat 32
2765 %_ptr_Output_float = OpTypePointer Output %float
2766 %o = OpVariable %_ptr_Output_float Output
2767 %float_1 = OpConstant %float 1
2768 %main = OpFunction %void None %6
2769 %15 = OpLabel
2770 OpBranch %16
2771 %16 = OpLabel
2772 %27 = OpPhi %int %int_n2 %15 %26 %18
2773 %21 = OpSLessThanEqual %bool %27 %int_2
2774 OpLoopMerge %17 %18 Unroll
2775 OpBranchConditional %21 %22 %17
2776 %22 = OpLabel
2777 %23 = OpLoad %float %o
2778 %24 = OpFAdd %float %23 %float_1
2779 OpStore %o %24
2780 OpBranch %18
2781 %18 = OpLabel
2782 %26 = OpIAdd %int %27 %int_2
2783 OpBranch %16
2784 %17 = OpLabel
2785 OpReturn
2786 OpFunctionEnd
2787 )";
2788
TEST_F(PassClassTest,FullyUnrollConditionIsInHeaderBlock)2789 TEST_F(PassClassTest, FullyUnrollConditionIsInHeaderBlock) {
2790 const std::string output = R"(OpCapability Shader
2791 OpMemoryModel Logical GLSL450
2792 OpEntryPoint Fragment %1 "main" %2
2793 OpExecutionMode %1 OriginUpperLeft
2794 OpSource GLSL 430
2795 OpDecorate %2 Location 0
2796 %3 = OpTypeVoid
2797 %4 = OpTypeFunction %3
2798 %5 = OpTypeInt 32 1
2799 %6 = OpConstant %5 -2
2800 %7 = OpConstant %5 2
2801 %8 = OpTypeBool
2802 %9 = OpTypeFloat 32
2803 %10 = OpTypePointer Output %9
2804 %2 = OpVariable %10 Output
2805 %11 = OpConstant %9 1
2806 %1 = OpFunction %3 None %4
2807 %12 = OpLabel
2808 OpBranch %13
2809 %13 = OpLabel
2810 %17 = OpSLessThanEqual %8 %6 %7
2811 OpBranch %19
2812 %19 = OpLabel
2813 %20 = OpLoad %9 %2
2814 %21 = OpFAdd %9 %20 %11
2815 OpStore %2 %21
2816 OpBranch %16
2817 %16 = OpLabel
2818 %15 = OpIAdd %5 %6 %7
2819 OpBranch %22
2820 %22 = OpLabel
2821 %24 = OpSLessThanEqual %8 %15 %7
2822 OpBranch %25
2823 %25 = OpLabel
2824 %26 = OpLoad %9 %2
2825 %27 = OpFAdd %9 %26 %11
2826 OpStore %2 %27
2827 OpBranch %28
2828 %28 = OpLabel
2829 %29 = OpIAdd %5 %15 %7
2830 OpBranch %30
2831 %30 = OpLabel
2832 %32 = OpSLessThanEqual %8 %29 %7
2833 OpBranch %33
2834 %33 = OpLabel
2835 %34 = OpLoad %9 %2
2836 %35 = OpFAdd %9 %34 %11
2837 OpStore %2 %35
2838 OpBranch %36
2839 %36 = OpLabel
2840 %37 = OpIAdd %5 %29 %7
2841 OpBranch %18
2842 %18 = OpLabel
2843 OpReturn
2844 OpFunctionEnd
2845 )";
2846
2847 std::unique_ptr<IRContext> context =
2848 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, condition_in_header,
2849 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2850 Module* module = context->module();
2851 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2852 << condition_in_header << std::endl;
2853
2854 LoopUnroller loop_unroller;
2855 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2856 SinglePassRunAndCheck<LoopUnroller>(condition_in_header, output, false);
2857 }
2858
TEST_F(PassClassTest,PartiallyUnrollResidualConditionIsInHeaderBlock)2859 TEST_F(PassClassTest, PartiallyUnrollResidualConditionIsInHeaderBlock) {
2860 const std::string output = R"(OpCapability Shader
2861 OpMemoryModel Logical GLSL450
2862 OpEntryPoint Fragment %1 "main" %2
2863 OpExecutionMode %1 OriginUpperLeft
2864 OpSource GLSL 430
2865 OpDecorate %2 Location 0
2866 %3 = OpTypeVoid
2867 %4 = OpTypeFunction %3
2868 %5 = OpTypeInt 32 1
2869 %6 = OpConstant %5 -2
2870 %7 = OpConstant %5 2
2871 %8 = OpTypeBool
2872 %9 = OpTypeFloat 32
2873 %10 = OpTypePointer Output %9
2874 %2 = OpVariable %10 Output
2875 %11 = OpConstant %9 1
2876 %40 = OpTypeInt 32 0
2877 %41 = OpConstant %40 1
2878 %1 = OpFunction %3 None %4
2879 %12 = OpLabel
2880 OpBranch %13
2881 %13 = OpLabel
2882 %14 = OpPhi %5 %6 %12 %15 %16
2883 %17 = OpSLessThanEqual %8 %14 %41
2884 OpLoopMerge %22 %16 Unroll
2885 OpBranchConditional %17 %19 %22
2886 %19 = OpLabel
2887 %20 = OpLoad %9 %2
2888 %21 = OpFAdd %9 %20 %11
2889 OpStore %2 %21
2890 OpBranch %16
2891 %16 = OpLabel
2892 %15 = OpIAdd %5 %14 %7
2893 OpBranch %13
2894 %22 = OpLabel
2895 OpBranch %23
2896 %23 = OpLabel
2897 %24 = OpPhi %5 %14 %22 %39 %38
2898 %25 = OpSLessThanEqual %8 %24 %7
2899 OpLoopMerge %31 %38 DontUnroll
2900 OpBranchConditional %25 %26 %31
2901 %26 = OpLabel
2902 %27 = OpLoad %9 %2
2903 %28 = OpFAdd %9 %27 %11
2904 OpStore %2 %28
2905 OpBranch %29
2906 %29 = OpLabel
2907 %30 = OpIAdd %5 %24 %7
2908 OpBranch %32
2909 %32 = OpLabel
2910 %34 = OpSLessThanEqual %8 %30 %7
2911 OpBranch %35
2912 %35 = OpLabel
2913 %36 = OpLoad %9 %2
2914 %37 = OpFAdd %9 %36 %11
2915 OpStore %2 %37
2916 OpBranch %38
2917 %38 = OpLabel
2918 %39 = OpIAdd %5 %30 %7
2919 OpBranch %23
2920 %31 = OpLabel
2921 OpReturn
2922 %18 = OpLabel
2923 OpReturn
2924 OpFunctionEnd
2925 )";
2926
2927 std::unique_ptr<IRContext> context =
2928 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, condition_in_header,
2929 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
2930 Module* module = context->module();
2931 EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n"
2932 << condition_in_header << std::endl;
2933
2934 LoopUnroller loop_unroller;
2935 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
2936 SinglePassRunAndCheck<PartialUnrollerTestPass<2>>(condition_in_header, output,
2937 false);
2938 }
2939
2940 /*
2941 Generated from following GLSL with latch block artificially inserted to be
2942 seperate from continue.
2943 #version 430
2944 void main(void) {
2945 float x[10];
2946 for (int i = 0; i < 10; ++i) {
2947 x[i] = i;
2948 }
2949 }
2950 */
TEST_F(PassClassTest,PartiallyUnrollLatchNotContinue)2951 TEST_F(PassClassTest, PartiallyUnrollLatchNotContinue) {
2952 const std::string text = R"(OpCapability Shader
2953 %1 = OpExtInstImport "GLSL.std.450"
2954 OpMemoryModel Logical GLSL450
2955 OpEntryPoint Fragment %2 "main"
2956 OpExecutionMode %2 OriginUpperLeft
2957 OpSource GLSL 430
2958 OpName %2 "main"
2959 OpName %3 "i"
2960 OpName %4 "x"
2961 %5 = OpTypeVoid
2962 %6 = OpTypeFunction %5
2963 %7 = OpTypeInt 32 1
2964 %8 = OpTypePointer Function %7
2965 %9 = OpConstant %7 0
2966 %10 = OpConstant %7 10
2967 %11 = OpTypeBool
2968 %12 = OpTypeFloat 32
2969 %13 = OpTypeInt 32 0
2970 %14 = OpConstant %13 10
2971 %15 = OpTypeArray %12 %14
2972 %16 = OpTypePointer Function %15
2973 %17 = OpTypePointer Function %12
2974 %18 = OpConstant %7 1
2975 %2 = OpFunction %5 None %6
2976 %19 = OpLabel
2977 %3 = OpVariable %8 Function
2978 %4 = OpVariable %16 Function
2979 OpStore %3 %9
2980 OpBranch %20
2981 %20 = OpLabel
2982 %21 = OpPhi %7 %9 %19 %22 %30
2983 OpLoopMerge %24 %23 Unroll
2984 OpBranch %25
2985 %25 = OpLabel
2986 %26 = OpSLessThan %11 %21 %10
2987 OpBranchConditional %26 %27 %24
2988 %27 = OpLabel
2989 %28 = OpConvertSToF %12 %21
2990 %29 = OpAccessChain %17 %4 %21
2991 OpStore %29 %28
2992 OpBranch %23
2993 %23 = OpLabel
2994 %22 = OpIAdd %7 %21 %18
2995 OpStore %3 %22
2996 OpBranch %30
2997 %30 = OpLabel
2998 OpBranch %20
2999 %24 = OpLabel
3000 OpReturn
3001 OpFunctionEnd
3002 )";
3003
3004 const std::string expected = R"(OpCapability Shader
3005 %1 = OpExtInstImport "GLSL.std.450"
3006 OpMemoryModel Logical GLSL450
3007 OpEntryPoint Fragment %2 "main"
3008 OpExecutionMode %2 OriginUpperLeft
3009 OpSource GLSL 430
3010 OpName %2 "main"
3011 OpName %3 "i"
3012 OpName %4 "x"
3013 %5 = OpTypeVoid
3014 %6 = OpTypeFunction %5
3015 %7 = OpTypeInt 32 1
3016 %8 = OpTypePointer Function %7
3017 %9 = OpConstant %7 0
3018 %10 = OpConstant %7 10
3019 %11 = OpTypeBool
3020 %12 = OpTypeFloat 32
3021 %13 = OpTypeInt 32 0
3022 %14 = OpConstant %13 10
3023 %15 = OpTypeArray %12 %14
3024 %16 = OpTypePointer Function %15
3025 %17 = OpTypePointer Function %12
3026 %18 = OpConstant %7 1
3027 %63 = OpConstant %13 1
3028 %2 = OpFunction %5 None %6
3029 %19 = OpLabel
3030 %3 = OpVariable %8 Function
3031 %4 = OpVariable %16 Function
3032 OpStore %3 %9
3033 OpBranch %20
3034 %20 = OpLabel
3035 %21 = OpPhi %7 %9 %19 %22 %23
3036 OpLoopMerge %31 %25 Unroll
3037 OpBranch %26
3038 %26 = OpLabel
3039 %27 = OpSLessThan %11 %21 %63
3040 OpBranchConditional %27 %28 %31
3041 %28 = OpLabel
3042 %29 = OpConvertSToF %12 %21
3043 %30 = OpAccessChain %17 %4 %21
3044 OpStore %30 %29
3045 OpBranch %25
3046 %25 = OpLabel
3047 %22 = OpIAdd %7 %21 %18
3048 OpStore %3 %22
3049 OpBranch %23
3050 %23 = OpLabel
3051 OpBranch %20
3052 %31 = OpLabel
3053 OpBranch %32
3054 %32 = OpLabel
3055 %33 = OpPhi %7 %21 %31 %61 %62
3056 OpLoopMerge %42 %60 DontUnroll
3057 OpBranch %34
3058 %34 = OpLabel
3059 %35 = OpSLessThan %11 %33 %10
3060 OpBranchConditional %35 %36 %42
3061 %36 = OpLabel
3062 %37 = OpConvertSToF %12 %33
3063 %38 = OpAccessChain %17 %4 %33
3064 OpStore %38 %37
3065 OpBranch %39
3066 %39 = OpLabel
3067 %40 = OpIAdd %7 %33 %18
3068 OpStore %3 %40
3069 OpBranch %41
3070 %41 = OpLabel
3071 OpBranch %43
3072 %43 = OpLabel
3073 OpBranch %45
3074 %45 = OpLabel
3075 %46 = OpSLessThan %11 %40 %10
3076 OpBranch %47
3077 %47 = OpLabel
3078 %48 = OpConvertSToF %12 %40
3079 %49 = OpAccessChain %17 %4 %40
3080 OpStore %49 %48
3081 OpBranch %50
3082 %50 = OpLabel
3083 %51 = OpIAdd %7 %40 %18
3084 OpStore %3 %51
3085 OpBranch %52
3086 %52 = OpLabel
3087 OpBranch %53
3088 %53 = OpLabel
3089 OpBranch %55
3090 %55 = OpLabel
3091 %56 = OpSLessThan %11 %51 %10
3092 OpBranch %57
3093 %57 = OpLabel
3094 %58 = OpConvertSToF %12 %51
3095 %59 = OpAccessChain %17 %4 %51
3096 OpStore %59 %58
3097 OpBranch %60
3098 %60 = OpLabel
3099 %61 = OpIAdd %7 %51 %18
3100 OpStore %3 %61
3101 OpBranch %62
3102 %62 = OpLabel
3103 OpBranch %32
3104 %42 = OpLabel
3105 OpReturn
3106 %24 = OpLabel
3107 OpReturn
3108 OpFunctionEnd
3109 )";
3110
3111 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
3112 SinglePassRunAndCheck<PartialUnrollerTestPass<3>>(text, expected, true);
3113
3114 // Make sure the latch block information is preserved and propagated correctly
3115 // by the pass.
3116 std::unique_ptr<IRContext> context =
3117 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
3118 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
3119
3120 PartialUnrollerTestPass<3> unroller;
3121 unroller.SetContextForTesting(context.get());
3122 unroller.Process();
3123
3124 Module* module = context->module();
3125 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
3126 << text << std::endl;
3127 const Function* f = spvtest::GetFunction(module, 2);
3128 LoopDescriptor ld{context.get(), f};
3129
3130 EXPECT_EQ(ld.NumLoops(), 2u);
3131
3132 Loop& loop_1 = ld.GetLoopByIndex(0u);
3133 EXPECT_NE(loop_1.GetLatchBlock(), loop_1.GetContinueBlock());
3134
3135 Loop& loop_2 = ld.GetLoopByIndex(1u);
3136 EXPECT_NE(loop_2.GetLatchBlock(), loop_2.GetContinueBlock());
3137 }
3138
3139 // Test that a loop with a self-referencing OpPhi instruction is handled
3140 // correctly.
TEST_F(PassClassTest,OpPhiSelfReference)3141 TEST_F(PassClassTest, OpPhiSelfReference) {
3142 const std::string text = R"(
3143 ; Find the two adds from the unrolled loop
3144 ; CHECK: OpIAdd
3145 ; CHECK: OpIAdd
3146 ; CHECK: OpIAdd %uint %uint_0 %uint_1
3147 ; CHECK-NEXT: OpReturn
3148 OpCapability Shader
3149 %1 = OpExtInstImport "GLSL.std.450"
3150 OpMemoryModel Logical GLSL450
3151 OpEntryPoint GLCompute %2 "main"
3152 OpExecutionMode %2 LocalSize 8 8 1
3153 OpSource HLSL 600
3154 %uint = OpTypeInt 32 0
3155 %void = OpTypeVoid
3156 %5 = OpTypeFunction %void
3157 %uint_0 = OpConstant %uint 0
3158 %uint_1 = OpConstant %uint 1
3159 %bool = OpTypeBool
3160 %true = OpConstantTrue %bool
3161 %2 = OpFunction %void None %5
3162 %10 = OpLabel
3163 OpBranch %19
3164 %19 = OpLabel
3165 %20 = OpPhi %uint %uint_0 %10 %20 %21
3166 %22 = OpPhi %uint %uint_0 %10 %23 %21
3167 %24 = OpULessThanEqual %bool %22 %uint_1
3168 OpLoopMerge %25 %21 Unroll
3169 OpBranchConditional %24 %21 %25
3170 %21 = OpLabel
3171 %23 = OpIAdd %uint %22 %uint_1
3172 OpBranch %19
3173 %25 = OpLabel
3174 %14 = OpIAdd %uint %20 %uint_1
3175 OpReturn
3176 OpFunctionEnd
3177 )";
3178
3179 const bool kFullyUnroll = true;
3180 const uint32_t kUnrollFactor = 0;
3181 SinglePassRunAndMatch<opt::LoopUnroller>(text, true, kFullyUnroll,
3182 kUnrollFactor);
3183 }
3184
3185 // Test that a loop containing an unreachable merge block can still be unrolled
3186 // correctly.
TEST_F(PassClassTest,UnreachableMerge)3187 TEST_F(PassClassTest, UnreachableMerge) {
3188 const std::string text = R"(
3189 ; Identify the first iteration of the unrolled loop, and make sure it contains
3190 ; the unreachable merge block.
3191 ; The first SelectionMerge corresponds to the original loop merge.
3192 ; The second is the branch in the loop.
3193 ; CHECK: OpSelectionMerge {{%\w+}} None
3194 ; CHECK: OpSelectionMerge [[unrch1:%\w+]] None
3195 ; CHECK: [[unrch1]] = OpLabel
3196 ; CHECK-NEXT: OpUnreachable
3197 ; Identify the second iteration of the unrolled loop, and make sure it contains
3198 ; the unreachable merge block.
3199 ; The first SelectionMerge corresponds to the original loop merge
3200 ; The second is the branch in the loop.
3201 ; CHECK: OpSelectionMerge {{%\w+}} None
3202 ; CHECK: OpSelectionMerge [[unrch2:%\w+]] None
3203 ; CHECK: [[unrch2]] = OpLabel
3204 ; CHECK-NEXT: OpUnreachable
3205
3206 OpCapability Shader
3207 OpMemoryModel Logical GLSL450
3208 OpEntryPoint GLCompute %main "main"
3209 OpExecutionMode %main LocalSize 64 1 1
3210 OpSource HLSL 600
3211 OpName %main "main"
3212 %uint = OpTypeInt 32 0
3213 %uint_0 = OpConstant %uint 0
3214 %uint_2 = OpConstant %uint 2
3215 %uint_1 = OpConstant %uint 1
3216 %bool = OpTypeBool
3217 %void = OpTypeVoid
3218 %18 = OpTypeFunction %void
3219 %main = OpFunction %void None %18
3220 %23 = OpLabel
3221 OpBranch %24
3222 %24 = OpLabel
3223 %28 = OpPhi %uint %uint_0 %23 %29 %27
3224 %30 = OpULessThan %bool %28 %uint_2
3225 OpLoopMerge %31 %27 Unroll
3226 OpBranchConditional %30 %32 %31
3227 %32 = OpLabel
3228 OpSelectionMerge %33 None
3229 OpSwitch %uint_0 %34
3230 %34 = OpLabel
3231 %35 = OpUndef %bool
3232 OpSelectionMerge %36 None
3233 OpBranchConditional %35 %37 %38
3234 %38 = OpLabel
3235 OpBranch %33
3236 %37 = OpLabel
3237 OpBranch %33
3238 %36 = OpLabel
3239 OpUnreachable
3240 %33 = OpLabel
3241 OpBranch %27
3242 %27 = OpLabel
3243 %29 = OpIAdd %uint %28 %uint_1
3244 OpBranch %24
3245 %31 = OpLabel
3246 OpReturn
3247 OpFunctionEnd
3248 )";
3249
3250 const bool kFullyUnroll = true;
3251 const uint32_t kUnrollFactor = 0;
3252 SinglePassRunAndMatch<opt::LoopUnroller>(text, true, kFullyUnroll,
3253 kUnrollFactor);
3254 }
3255
TEST_F(PassClassTest,InitValueIsConstantNull)3256 TEST_F(PassClassTest, InitValueIsConstantNull) {
3257 const std::string shader = R"(
3258 OpCapability Shader
3259 %1 = OpExtInstImport "GLSL.std.450"
3260 OpMemoryModel Logical GLSL450
3261 OpEntryPoint Fragment %4 "main"
3262 OpExecutionMode %4 OriginUpperLeft
3263 OpSource ESSL 320
3264 %2 = OpTypeVoid
3265 %3 = OpTypeFunction %2
3266 %6 = OpTypeInt 32 1
3267 %7 = OpConstantNull %6
3268 %13 = OpConstant %6 1
3269 %21 = OpConstant %6 1
3270 %10 = OpTypeBool
3271 %17 = OpTypePointer Function %6
3272 %4 = OpFunction %2 None %3
3273 %11 = OpLabel
3274 OpBranch %5
3275 %5 = OpLabel
3276 %23 = OpPhi %6 %7 %11 %20 %15
3277 OpLoopMerge %8 %15 Unroll
3278 OpBranch %14
3279 %14 = OpLabel
3280 %9 = OpSLessThan %10 %23 %13
3281 OpBranchConditional %9 %15 %8
3282 %15 = OpLabel
3283 %20 = OpIAdd %6 %23 %21
3284 OpBranch %5
3285 %8 = OpLabel
3286 OpReturn
3287 OpFunctionEnd
3288 )";
3289
3290 const std::string output = R"(OpCapability Shader
3291 %1 = OpExtInstImport "GLSL.std.450"
3292 OpMemoryModel Logical GLSL450
3293 OpEntryPoint Fragment %2 "main"
3294 OpExecutionMode %2 OriginUpperLeft
3295 OpSource ESSL 320
3296 %3 = OpTypeVoid
3297 %4 = OpTypeFunction %3
3298 %5 = OpTypeInt 32 1
3299 %6 = OpConstantNull %5
3300 %7 = OpConstant %5 1
3301 %8 = OpConstant %5 1
3302 %9 = OpTypeBool
3303 %10 = OpTypePointer Function %5
3304 %2 = OpFunction %3 None %4
3305 %11 = OpLabel
3306 OpBranch %12
3307 %12 = OpLabel
3308 OpBranch %17
3309 %17 = OpLabel
3310 %18 = OpSLessThan %9 %6 %7
3311 OpBranch %15
3312 %15 = OpLabel
3313 %14 = OpIAdd %5 %6 %8
3314 OpBranch %16
3315 %16 = OpLabel
3316 OpReturn
3317 OpFunctionEnd
3318 )";
3319
3320 auto context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, shader,
3321 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
3322 Module* module = context->module();
3323 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
3324 << shader << std::endl;
3325
3326 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
3327 SinglePassRunAndCheck<LoopUnroller>(shader, output, false);
3328 }
3329
TEST_F(PassClassTest,ConditionValueIsConstantNull)3330 TEST_F(PassClassTest, ConditionValueIsConstantNull) {
3331 const std::string shader = R"(
3332 OpCapability Shader
3333 %1 = OpExtInstImport "GLSL.std.450"
3334 OpMemoryModel Logical GLSL450
3335 OpEntryPoint Fragment %4 "main"
3336 OpExecutionMode %4 OriginUpperLeft
3337 OpSource ESSL 320
3338 %2 = OpTypeVoid
3339 %3 = OpTypeFunction %2
3340 %6 = OpTypeInt 32 1
3341 %7 = OpConstantNull %6
3342 %13 = OpConstant %6 1
3343 %21 = OpConstant %6 1
3344 %10 = OpTypeBool
3345 %17 = OpTypePointer Function %6
3346 %4 = OpFunction %2 None %3
3347 %11 = OpLabel
3348 OpBranch %5
3349 %5 = OpLabel
3350 %23 = OpPhi %6 %13 %11 %20 %15
3351 OpLoopMerge %8 %15 Unroll
3352 OpBranch %14
3353 %14 = OpLabel
3354 %9 = OpSGreaterThan %10 %23 %7
3355 OpBranchConditional %9 %15 %8
3356 %15 = OpLabel
3357 %20 = OpISub %6 %23 %21
3358 OpBranch %5
3359 %8 = OpLabel
3360 OpReturn
3361 OpFunctionEnd
3362 )";
3363
3364 const std::string output = R"(OpCapability Shader
3365 %1 = OpExtInstImport "GLSL.std.450"
3366 OpMemoryModel Logical GLSL450
3367 OpEntryPoint Fragment %2 "main"
3368 OpExecutionMode %2 OriginUpperLeft
3369 OpSource ESSL 320
3370 %3 = OpTypeVoid
3371 %4 = OpTypeFunction %3
3372 %5 = OpTypeInt 32 1
3373 %6 = OpConstantNull %5
3374 %7 = OpConstant %5 1
3375 %8 = OpConstant %5 1
3376 %9 = OpTypeBool
3377 %10 = OpTypePointer Function %5
3378 %2 = OpFunction %3 None %4
3379 %11 = OpLabel
3380 OpBranch %12
3381 %12 = OpLabel
3382 OpBranch %17
3383 %17 = OpLabel
3384 %18 = OpSGreaterThan %9 %7 %6
3385 OpBranch %15
3386 %15 = OpLabel
3387 %14 = OpISub %5 %7 %8
3388 OpBranch %16
3389 %16 = OpLabel
3390 OpReturn
3391 OpFunctionEnd
3392 )";
3393
3394 auto context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, shader,
3395 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
3396 Module* module = context->module();
3397 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
3398 << shader << std::endl;
3399
3400 SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
3401 SinglePassRunAndCheck<LoopUnroller>(shader, output, false);
3402 }
3403
3404 } // namespace
3405 } // namespace opt
3406 } // namespace spvtools
3407