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