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