• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2018 Google LLC.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <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