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