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 "effcee/effcee.h"
20 #include "gmock/gmock.h"
21 #include "source/opt/ir_builder.h"
22 #include "source/opt/loop_descriptor.h"
23 #include "source/opt/loop_peeling.h"
24 #include "test/opt/pass_fixture.h"
25
26 namespace spvtools {
27 namespace opt {
28 namespace {
29
30 using PeelingTest = PassTest<::testing::Test>;
31
Validate(const std::vector<uint32_t> & bin)32 bool Validate(const std::vector<uint32_t>& bin) {
33 spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2;
34 spv_context spvContext = spvContextCreate(target_env);
35 spv_diagnostic diagnostic = nullptr;
36 spv_const_binary_t binary = {bin.data(), bin.size()};
37 spv_result_t error = spvValidate(spvContext, &binary, &diagnostic);
38 if (error != 0) spvDiagnosticPrint(diagnostic);
39 spvDiagnosticDestroy(diagnostic);
40 spvContextDestroy(spvContext);
41 return error == 0;
42 }
43
Match(const std::string & checks,IRContext * context)44 void Match(const std::string& checks, IRContext* context) {
45 // Silence unused warnings with !defined(SPIRV_EFFCE)
46 (void)checks;
47
48 std::vector<uint32_t> bin;
49 context->module()->ToBinary(&bin, true);
50 EXPECT_TRUE(Validate(bin));
51 std::string assembly;
52 SpirvTools tools(SPV_ENV_UNIVERSAL_1_2);
53 EXPECT_TRUE(
54 tools.Disassemble(bin, &assembly, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER))
55 << "Disassembling failed for shader:\n"
56 << assembly << std::endl;
57 auto match_result = effcee::Match(assembly, checks);
58 EXPECT_EQ(effcee::Result::Status::Ok, match_result.status())
59 << match_result.message() << "\nChecking result:\n"
60 << assembly;
61 }
62
63 /*
64 Generated from the following GLSL + --eliminate-local-multi-store
65
66 First test:
67 #version 330 core
68 void main() {
69 for(int i = 0; i < 10; ++i) {
70 if (i < 4)
71 break;
72 }
73 }
74
75 Second test (with a common sub-expression elimination):
76 #version 330 core
77 void main() {
78 for(int i = 0; i + 1 < 10; ++i) {
79 }
80 }
81
82 Third test:
83 #version 330 core
84 void main() {
85 int a[10];
86 for (int i = 0; a[i] != 0; i++) {}
87 }
88
89 Forth test:
90 #version 330 core
91 void main() {
92 for (long i = 0; i < 10; i++) {}
93 }
94
95 Fifth test:
96 #version 330 core
97 void main() {
98 for (float i = 0; i < 10; i++) {}
99 }
100
101 Sixth test:
102 #version 450
103 layout(location = 0)out float o;
104 void main() {
105 o = 0.0;
106 for( int i = 0; true; i++ ) {
107 o += 1.0;
108 if (i > 10) break;
109 }
110 }
111 */
TEST_F(PeelingTest,CannotPeel)112 TEST_F(PeelingTest, CannotPeel) {
113 // Build the given SPIR-V program in |text|, take the first loop in the first
114 // function and test that it is not peelable. |loop_count_id| is the id
115 // representing the loop count, if equals to 0, then the function build a 10
116 // constant as loop count.
117 auto test_cannot_peel = [](const std::string& text, uint32_t loop_count_id) {
118 std::unique_ptr<IRContext> context =
119 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
120 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
121 Module* module = context->module();
122 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
123 << text << std::endl;
124 Function& f = *module->begin();
125 LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
126
127 EXPECT_EQ(ld.NumLoops(), 1u);
128
129 Instruction* loop_count = nullptr;
130 if (loop_count_id) {
131 loop_count = context->get_def_use_mgr()->GetDef(loop_count_id);
132 } else {
133 InstructionBuilder builder(context.get(), &*f.begin());
134 // Exit condition.
135 loop_count = builder.GetSintConstant(10);
136 }
137
138 LoopPeeling peel(&*ld.begin(), loop_count);
139 EXPECT_FALSE(peel.CanPeelLoop());
140 };
141 {
142 SCOPED_TRACE("loop with break");
143
144 const std::string text = R"(
145 OpCapability Shader
146 %1 = OpExtInstImport "GLSL.std.450"
147 OpMemoryModel Logical GLSL450
148 OpEntryPoint Fragment %main "main"
149 OpExecutionMode %main OriginLowerLeft
150 OpSource GLSL 330
151 OpName %main "main"
152 %void = OpTypeVoid
153 %3 = OpTypeFunction %void
154 %int = OpTypeInt 32 1
155 %_ptr_Function_int = OpTypePointer Function %int
156 %int_0 = OpConstant %int 0
157 %int_10 = OpConstant %int 10
158 %bool = OpTypeBool
159 %int_4 = OpConstant %int 4
160 %int_1 = OpConstant %int 1
161 %main = OpFunction %void None %3
162 %5 = OpLabel
163 OpBranch %10
164 %10 = OpLabel
165 %28 = OpPhi %int %int_0 %5 %27 %13
166 OpLoopMerge %12 %13 None
167 OpBranch %14
168 %14 = OpLabel
169 %18 = OpSLessThan %bool %28 %int_10
170 OpBranchConditional %18 %11 %12
171 %11 = OpLabel
172 %21 = OpSLessThan %bool %28 %int_4
173 OpSelectionMerge %23 None
174 OpBranchConditional %21 %22 %23
175 %22 = OpLabel
176 OpBranch %12
177 %23 = OpLabel
178 OpBranch %13
179 %13 = OpLabel
180 %27 = OpIAdd %int %28 %int_1
181 OpBranch %10
182 %12 = OpLabel
183 OpReturn
184 OpFunctionEnd
185 )";
186 test_cannot_peel(text, 0);
187 }
188
189 {
190 SCOPED_TRACE("Ambiguous iterator update");
191
192 const std::string text = R"(
193 OpCapability Shader
194 %1 = OpExtInstImport "GLSL.std.450"
195 OpMemoryModel Logical GLSL450
196 OpEntryPoint Fragment %main "main"
197 OpExecutionMode %main OriginLowerLeft
198 OpSource GLSL 330
199 OpName %main "main"
200 %void = OpTypeVoid
201 %3 = OpTypeFunction %void
202 %int = OpTypeInt 32 1
203 %_ptr_Function_int = OpTypePointer Function %int
204 %int_0 = OpConstant %int 0
205 %int_1 = OpConstant %int 1
206 %int_10 = OpConstant %int 10
207 %bool = OpTypeBool
208 %main = OpFunction %void None %3
209 %5 = OpLabel
210 OpBranch %10
211 %10 = OpLabel
212 %23 = OpPhi %int %int_0 %5 %17 %13
213 OpLoopMerge %12 %13 None
214 OpBranch %14
215 %14 = OpLabel
216 %17 = OpIAdd %int %23 %int_1
217 %20 = OpSLessThan %bool %17 %int_10
218 OpBranchConditional %20 %11 %12
219 %11 = OpLabel
220 OpBranch %13
221 %13 = OpLabel
222 OpBranch %10
223 %12 = OpLabel
224 OpReturn
225 OpFunctionEnd
226 )";
227
228 test_cannot_peel(text, 0);
229 }
230
231 {
232 SCOPED_TRACE("No loop static bounds");
233
234 const std::string text = R"(
235 OpCapability Shader
236 %1 = OpExtInstImport "GLSL.std.450"
237 OpMemoryModel Logical GLSL450
238 OpEntryPoint Fragment %main "main"
239 OpExecutionMode %main OriginLowerLeft
240 OpSource GLSL 330
241 OpName %main "main"
242 OpName %i "i"
243 OpName %a "a"
244 %void = OpTypeVoid
245 %3 = OpTypeFunction %void
246 %int = OpTypeInt 32 1
247 %_ptr_Function_int = OpTypePointer Function %int
248 %int_0 = OpConstant %int 0
249 %uint = OpTypeInt 32 0
250 %uint_10 = OpConstant %uint 10
251 %_arr_int_uint_10 = OpTypeArray %int %uint_10
252 %_ptr_Function__arr_int_uint_10 = OpTypePointer Function %_arr_int_uint_10
253 %bool = OpTypeBool
254 %int_1 = OpConstant %int 1
255 %main = OpFunction %void None %3
256 %5 = OpLabel
257 %i = OpVariable %_ptr_Function_int Function
258 %a = OpVariable %_ptr_Function__arr_int_uint_10 Function
259 OpStore %i %int_0
260 OpBranch %10
261 %10 = OpLabel
262 %28 = OpPhi %int %int_0 %5 %27 %13
263 OpLoopMerge %12 %13 None
264 OpBranch %14
265 %14 = OpLabel
266 %21 = OpAccessChain %_ptr_Function_int %a %28
267 %22 = OpLoad %int %21
268 %24 = OpINotEqual %bool %22 %int_0
269 OpBranchConditional %24 %11 %12
270 %11 = OpLabel
271 OpBranch %13
272 %13 = OpLabel
273 %27 = OpIAdd %int %28 %int_1
274 OpStore %i %27
275 OpBranch %10
276 %12 = OpLabel
277 OpReturn
278 OpFunctionEnd
279 )";
280
281 test_cannot_peel(text, 22);
282 }
283 {
284 SCOPED_TRACE("Int 64 type for conditions");
285
286 const std::string text = R"(
287 OpCapability Shader
288 %1 = OpExtInstImport "GLSL.std.450"
289 OpMemoryModel Logical GLSL450
290 OpEntryPoint Fragment %2 "main"
291 OpExecutionMode %2 OriginLowerLeft
292 OpSource GLSL 330
293 OpName %2 "main"
294 OpName %4 "i"
295 %6 = OpTypeVoid
296 %3 = OpTypeFunction %6
297 %7 = OpTypeInt 64 1
298 %8 = OpTypePointer Function %7
299 %9 = OpConstant %7 0
300 %15 = OpConstant %7 10
301 %16 = OpTypeBool
302 %17 = OpConstant %7 1
303 %2 = OpFunction %6 None %3
304 %5 = OpLabel
305 %4 = OpVariable %8 Function
306 OpStore %4 %9
307 OpBranch %10
308 %10 = OpLabel
309 %22 = OpPhi %7 %9 %5 %21 %13
310 OpLoopMerge %12 %13 None
311 OpBranch %14
312 %14 = OpLabel
313 %18 = OpSLessThan %16 %22 %15
314 OpBranchConditional %18 %11 %12
315 %11 = OpLabel
316 OpBranch %13
317 %13 = OpLabel
318 %21 = OpIAdd %7 %22 %17
319 OpStore %4 %21
320 OpBranch %10
321 %12 = OpLabel
322 OpReturn
323 OpFunctionEnd
324 )";
325 // %15 is a constant for a 64 int. Currently rejected.
326 test_cannot_peel(text, 15);
327 }
328 {
329 SCOPED_TRACE("Float type for conditions");
330
331 const std::string text = R"(
332 OpCapability Shader
333 %1 = OpExtInstImport "GLSL.std.450"
334 OpMemoryModel Logical GLSL450
335 OpEntryPoint Fragment %2 "main"
336 OpExecutionMode %2 OriginLowerLeft
337 OpSource GLSL 330
338 OpName %2 "main"
339 OpName %4 "i"
340 %6 = OpTypeVoid
341 %3 = OpTypeFunction %6
342 %7 = OpTypeFloat 32
343 %8 = OpTypePointer Function %7
344 %9 = OpConstant %7 0
345 %15 = OpConstant %7 10
346 %16 = OpTypeBool
347 %17 = OpConstant %7 1
348 %2 = OpFunction %6 None %3
349 %5 = OpLabel
350 %4 = OpVariable %8 Function
351 OpStore %4 %9
352 OpBranch %10
353 %10 = OpLabel
354 %22 = OpPhi %7 %9 %5 %21 %13
355 OpLoopMerge %12 %13 None
356 OpBranch %14
357 %14 = OpLabel
358 %18 = OpFOrdLessThan %16 %22 %15
359 OpBranchConditional %18 %11 %12
360 %11 = OpLabel
361 OpBranch %13
362 %13 = OpLabel
363 %21 = OpFAdd %7 %22 %17
364 OpStore %4 %21
365 OpBranch %10
366 %12 = OpLabel
367 OpReturn
368 OpFunctionEnd
369 )";
370 // %15 is a constant for a float. Currently rejected.
371 test_cannot_peel(text, 15);
372 }
373 {
374 SCOPED_TRACE("Side effect before exit");
375
376 const std::string text = R"(
377 OpCapability Shader
378 %1 = OpExtInstImport "GLSL.std.450"
379 OpMemoryModel Logical GLSL450
380 OpEntryPoint Fragment %main "main" %o
381 OpExecutionMode %main OriginLowerLeft
382 OpSource GLSL 450
383 OpName %main "main"
384 OpName %o "o"
385 OpName %i "i"
386 OpDecorate %o Location 0
387 %void = OpTypeVoid
388 %3 = OpTypeFunction %void
389 %float = OpTypeFloat 32
390 %_ptr_Output_float = OpTypePointer Output %float
391 %o = OpVariable %_ptr_Output_float Output
392 %float_0 = OpConstant %float 0
393 %int = OpTypeInt 32 1
394 %_ptr_Function_int = OpTypePointer Function %int
395 %int_0 = OpConstant %int 0
396 %bool = OpTypeBool
397 %true = OpConstantTrue %bool
398 %float_1 = OpConstant %float 1
399 %int_10 = OpConstant %int 10
400 %int_1 = OpConstant %int 1
401 %main = OpFunction %void None %3
402 %5 = OpLabel
403 %i = OpVariable %_ptr_Function_int Function
404 OpStore %o %float_0
405 OpStore %i %int_0
406 OpBranch %14
407 %14 = OpLabel
408 %33 = OpPhi %int %int_0 %5 %32 %17
409 OpLoopMerge %16 %17 None
410 OpBranch %15
411 %15 = OpLabel
412 %22 = OpLoad %float %o
413 %23 = OpFAdd %float %22 %float_1
414 OpStore %o %23
415 %26 = OpSGreaterThan %bool %33 %int_10
416 OpSelectionMerge %28 None
417 OpBranchConditional %26 %27 %28
418 %27 = OpLabel
419 OpBranch %16
420 %28 = OpLabel
421 OpBranch %17
422 %17 = OpLabel
423 %32 = OpIAdd %int %33 %int_1
424 OpStore %i %32
425 OpBranch %14
426 %16 = OpLabel
427 OpReturn
428 OpFunctionEnd
429 )";
430 test_cannot_peel(text, 0);
431 }
432 }
433
434 /*
435 Generated from the following GLSL + --eliminate-local-multi-store
436
437 #version 330 core
438 void main() {
439 int i = 0;
440 for (; i < 10; i++) {}
441 }
442 */
TEST_F(PeelingTest,SimplePeeling)443 TEST_F(PeelingTest, SimplePeeling) {
444 const std::string text = R"(
445 OpCapability Shader
446 %1 = OpExtInstImport "GLSL.std.450"
447 OpMemoryModel Logical GLSL450
448 OpEntryPoint Fragment %main "main"
449 OpExecutionMode %main OriginLowerLeft
450 OpSource GLSL 330
451 OpName %main "main"
452 %void = OpTypeVoid
453 %3 = OpTypeFunction %void
454 %int = OpTypeInt 32 1
455 %_ptr_Function_int = OpTypePointer Function %int
456 %int_0 = OpConstant %int 0
457 %int_10 = OpConstant %int 10
458 %bool = OpTypeBool
459 %int_1 = OpConstant %int 1
460 %main = OpFunction %void None %3
461 %5 = OpLabel
462 OpBranch %10
463 %10 = OpLabel
464 %22 = OpPhi %int %int_0 %5 %21 %13
465 OpLoopMerge %12 %13 None
466 OpBranch %14
467 %14 = OpLabel
468 %18 = OpSLessThan %bool %22 %int_10
469 OpBranchConditional %18 %11 %12
470 %11 = OpLabel
471 OpBranch %13
472 %13 = OpLabel
473 %21 = OpIAdd %int %22 %int_1
474 OpBranch %10
475 %12 = OpLabel
476 OpReturn
477 OpFunctionEnd
478 )";
479
480 // Peel before.
481 {
482 SCOPED_TRACE("Peel before");
483
484 std::unique_ptr<IRContext> context =
485 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
486 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
487 Module* module = context->module();
488 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
489 << text << std::endl;
490 Function& f = *module->begin();
491 LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
492
493 EXPECT_EQ(ld.NumLoops(), 1u);
494
495 InstructionBuilder builder(context.get(), &*f.begin());
496 // Exit condition.
497 Instruction* ten_cst = builder.GetSintConstant(10);
498
499 LoopPeeling peel(&*ld.begin(), ten_cst);
500 EXPECT_TRUE(peel.CanPeelLoop());
501 peel.PeelBefore(2);
502
503 const std::string check = R"(
504 CHECK: [[CST_TEN:%\w+]] = OpConstant {{%\w+}} 10
505 CHECK: [[CST_TWO:%\w+]] = OpConstant {{%\w+}} 2
506 CHECK: OpFunction
507 CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
508 CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} [[CST_TWO]] [[CST_TEN]]
509 CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] [[CST_TWO]] [[CST_TEN]]
510 CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
511 CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
512 CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
513 CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
514 CHECK: [[COND_BLOCK:%\w+]] = OpLabel
515 CHECK-NEXT: OpSLessThan
516 CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[DUMMY_IT]]
517 CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]]
518 CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
519 CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
520 CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
521
522 CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
523 CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
524 CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
525
526 CHECK: [[AFTER_LOOP]] = OpLabel
527 CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]]
528 CHECK-NEXT: OpLoopMerge
529 )";
530
531 Match(check, context.get());
532 }
533
534 // Peel after.
535 {
536 SCOPED_TRACE("Peel after");
537
538 std::unique_ptr<IRContext> context =
539 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
540 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
541 Module* module = context->module();
542 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
543 << text << std::endl;
544 Function& f = *module->begin();
545 LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
546
547 EXPECT_EQ(ld.NumLoops(), 1u);
548
549 InstructionBuilder builder(context.get(), &*f.begin());
550 // Exit condition.
551 Instruction* ten_cst = builder.GetSintConstant(10);
552
553 LoopPeeling peel(&*ld.begin(), ten_cst);
554 EXPECT_TRUE(peel.CanPeelLoop());
555 peel.PeelAfter(2);
556
557 const std::string check = R"(
558 CHECK: OpFunction
559 CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
560 CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}}
561 CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
562 CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
563 CHECK: [[BEFORE_LOOP]] = OpLabel
564 CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
565 CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
566 CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
567 CHECK: [[COND_BLOCK:%\w+]] = OpLabel
568 CHECK-NEXT: OpSLessThan
569 CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT]] {{%\w+}}
570 CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]]
571 CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]]
572 CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
573 CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
574 CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
575
576 CHECK: [[IF_MERGE]] = OpLabel
577 CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]]
578 CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
579
580 CHECK: [[AFTER_LOOP]] = OpLabel
581 CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
582 CHECK-NEXT: OpLoopMerge
583
584 )";
585
586 Match(check, context.get());
587 }
588
589 // Same as above, but reuse the induction variable.
590 // Peel before.
591 {
592 SCOPED_TRACE("Peel before with IV reuse");
593
594 std::unique_ptr<IRContext> context =
595 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
596 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
597 Module* module = context->module();
598 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
599 << text << std::endl;
600 Function& f = *module->begin();
601 LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
602
603 EXPECT_EQ(ld.NumLoops(), 1u);
604
605 InstructionBuilder builder(context.get(), &*f.begin());
606 // Exit condition.
607 Instruction* ten_cst = builder.GetSintConstant(10);
608
609 LoopPeeling peel(&*ld.begin(), ten_cst,
610 context->get_def_use_mgr()->GetDef(22));
611 EXPECT_TRUE(peel.CanPeelLoop());
612 peel.PeelBefore(2);
613
614 const std::string check = R"(
615 CHECK: [[CST_TEN:%\w+]] = OpConstant {{%\w+}} 10
616 CHECK: [[CST_TWO:%\w+]] = OpConstant {{%\w+}} 2
617 CHECK: OpFunction
618 CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
619 CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} [[CST_TWO]] [[CST_TEN]]
620 CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] [[CST_TWO]] [[CST_TEN]]
621 CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
622 CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE:%\w+]]
623 CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
624 CHECK: [[COND_BLOCK:%\w+]] = OpLabel
625 CHECK-NEXT: OpSLessThan
626 CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[i]]
627 CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]]
628 CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
629 CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
630
631 CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
632 CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
633 CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
634
635 CHECK: [[AFTER_LOOP]] = OpLabel
636 CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]]
637 CHECK-NEXT: OpLoopMerge
638 )";
639
640 Match(check, context.get());
641 }
642
643 // Peel after.
644 {
645 SCOPED_TRACE("Peel after IV reuse");
646
647 std::unique_ptr<IRContext> context =
648 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
649 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
650 Module* module = context->module();
651 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
652 << text << std::endl;
653 Function& f = *module->begin();
654 LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
655
656 EXPECT_EQ(ld.NumLoops(), 1u);
657
658 InstructionBuilder builder(context.get(), &*f.begin());
659 // Exit condition.
660 Instruction* ten_cst = builder.GetSintConstant(10);
661
662 LoopPeeling peel(&*ld.begin(), ten_cst,
663 context->get_def_use_mgr()->GetDef(22));
664 EXPECT_TRUE(peel.CanPeelLoop());
665 peel.PeelAfter(2);
666
667 const std::string check = R"(
668 CHECK: OpFunction
669 CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
670 CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}}
671 CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
672 CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
673 CHECK: [[BEFORE_LOOP]] = OpLabel
674 CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE:%\w+]]
675 CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
676 CHECK: [[COND_BLOCK:%\w+]] = OpLabel
677 CHECK-NEXT: OpSLessThan
678 CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[I]] {{%\w+}}
679 CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]]
680 CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]]
681 CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
682 CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
683
684 CHECK: [[IF_MERGE]] = OpLabel
685 CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]]
686 CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
687
688 CHECK: [[AFTER_LOOP]] = OpLabel
689 CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
690 CHECK-NEXT: OpLoopMerge
691
692 )";
693
694 Match(check, context.get());
695 }
696 }
697
698 /*
699 Generated from the following GLSL + --eliminate-local-multi-store
700
701 #version 330 core
702 void main() {
703 int a[10];
704 int n = a[0];
705 for(int i = 0; i < n; ++i) {}
706 }
707 */
TEST_F(PeelingTest,PeelingUncountable)708 TEST_F(PeelingTest, PeelingUncountable) {
709 const std::string text = R"(
710 OpCapability Shader
711 %1 = OpExtInstImport "GLSL.std.450"
712 OpMemoryModel Logical GLSL450
713 OpEntryPoint Fragment %main "main"
714 OpExecutionMode %main OriginLowerLeft
715 OpSource GLSL 330
716 OpName %main "main"
717 OpName %a "a"
718 %void = OpTypeVoid
719 %3 = OpTypeFunction %void
720 %int = OpTypeInt 32 1
721 %_ptr_Function_int = OpTypePointer Function %int
722 %uint = OpTypeInt 32 0
723 %uint_10 = OpConstant %uint 10
724 %_arr_int_uint_10 = OpTypeArray %int %uint_10
725 %_ptr_Function__arr_int_uint_10 = OpTypePointer Function %_arr_int_uint_10
726 %int_0 = OpConstant %int 0
727 %bool = OpTypeBool
728 %int_1 = OpConstant %int 1
729 %main = OpFunction %void None %3
730 %5 = OpLabel
731 %a = OpVariable %_ptr_Function__arr_int_uint_10 Function
732 %15 = OpAccessChain %_ptr_Function_int %a %int_0
733 %16 = OpLoad %int %15
734 OpBranch %18
735 %18 = OpLabel
736 %30 = OpPhi %int %int_0 %5 %29 %21
737 OpLoopMerge %20 %21 None
738 OpBranch %22
739 %22 = OpLabel
740 %26 = OpSLessThan %bool %30 %16
741 OpBranchConditional %26 %19 %20
742 %19 = OpLabel
743 OpBranch %21
744 %21 = OpLabel
745 %29 = OpIAdd %int %30 %int_1
746 OpBranch %18
747 %20 = OpLabel
748 OpReturn
749 OpFunctionEnd
750 )";
751
752 // Peel before.
753 {
754 SCOPED_TRACE("Peel before");
755
756 std::unique_ptr<IRContext> context =
757 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
758 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
759 Module* module = context->module();
760 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
761 << text << std::endl;
762 Function& f = *module->begin();
763 LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
764
765 EXPECT_EQ(ld.NumLoops(), 1u);
766
767 Instruction* loop_count = context->get_def_use_mgr()->GetDef(16);
768 EXPECT_EQ(loop_count->opcode(), spv::Op::OpLoad);
769
770 LoopPeeling peel(&*ld.begin(), loop_count);
771 EXPECT_TRUE(peel.CanPeelLoop());
772 peel.PeelBefore(1);
773
774 const std::string check = R"(
775 CHECK: OpFunction
776 CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
777 CHECK: [[LOOP_COUNT:%\w+]] = OpLoad
778 CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} {{%\w+}} [[LOOP_COUNT]]
779 CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] {{%\w+}} [[LOOP_COUNT]]
780 CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
781 CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
782 CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
783 CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
784 CHECK: [[COND_BLOCK:%\w+]] = OpLabel
785 CHECK-NEXT: OpSLessThan
786 CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[DUMMY_IT]]
787 CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]]
788 CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
789 CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
790 CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
791
792 CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
793 CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
794 CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
795
796 CHECK: [[AFTER_LOOP]] = OpLabel
797 CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]]
798 CHECK-NEXT: OpLoopMerge
799 )";
800
801 Match(check, context.get());
802 }
803
804 // Peel after.
805 {
806 SCOPED_TRACE("Peel after");
807
808 std::unique_ptr<IRContext> context =
809 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
810 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
811 Module* module = context->module();
812 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
813 << text << std::endl;
814 Function& f = *module->begin();
815 LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
816
817 EXPECT_EQ(ld.NumLoops(), 1u);
818
819 Instruction* loop_count = context->get_def_use_mgr()->GetDef(16);
820 EXPECT_EQ(loop_count->opcode(), spv::Op::OpLoad);
821
822 LoopPeeling peel(&*ld.begin(), loop_count);
823 EXPECT_TRUE(peel.CanPeelLoop());
824 peel.PeelAfter(1);
825
826 const std::string check = R"(
827 CHECK: OpFunction
828 CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
829 CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}}
830 CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
831 CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
832 CHECK: [[BEFORE_LOOP]] = OpLabel
833 CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
834 CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
835 CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
836 CHECK: [[COND_BLOCK:%\w+]] = OpLabel
837 CHECK-NEXT: OpSLessThan
838 CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT]] {{%\w+}}
839 CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]]
840 CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]]
841 CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
842 CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
843 CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
844
845 CHECK: [[IF_MERGE]] = OpLabel
846 CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]]
847 CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
848
849 CHECK: [[AFTER_LOOP]] = OpLabel
850 CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
851 CHECK-NEXT: OpLoopMerge
852
853 )";
854
855 Match(check, context.get());
856 }
857 }
858
859 /*
860 Generated from the following GLSL + --eliminate-local-multi-store
861
862 #version 330 core
863 void main() {
864 int i = 0;
865 do {
866 i++;
867 } while (i < 10);
868 }
869 */
TEST_F(PeelingTest,DoWhilePeeling)870 TEST_F(PeelingTest, DoWhilePeeling) {
871 const std::string text = R"(
872 OpCapability Shader
873 %1 = OpExtInstImport "GLSL.std.450"
874 OpMemoryModel Logical GLSL450
875 OpEntryPoint Fragment %main "main"
876 OpExecutionMode %main OriginLowerLeft
877 OpSource GLSL 330
878 OpName %main "main"
879 %void = OpTypeVoid
880 %3 = OpTypeFunction %void
881 %int = OpTypeInt 32 1
882 %_ptr_Function_int = OpTypePointer Function %int
883 %int_0 = OpConstant %int 0
884 %int_1 = OpConstant %int 1
885 %int_10 = OpConstant %int 10
886 %bool = OpTypeBool
887 %main = OpFunction %void None %3
888 %5 = OpLabel
889 OpBranch %10
890 %10 = OpLabel
891 %21 = OpPhi %int %int_0 %5 %16 %13
892 OpLoopMerge %12 %13 None
893 OpBranch %11
894 %11 = OpLabel
895 %16 = OpIAdd %int %21 %int_1
896 OpBranch %13
897 %13 = OpLabel
898 %20 = OpSLessThan %bool %16 %int_10
899 OpBranchConditional %20 %10 %12
900 %12 = OpLabel
901 OpReturn
902 OpFunctionEnd
903 )";
904
905 // Peel before.
906 {
907 SCOPED_TRACE("Peel before");
908
909 std::unique_ptr<IRContext> context =
910 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
911 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
912 Module* module = context->module();
913 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
914 << text << std::endl;
915 Function& f = *module->begin();
916 LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
917
918 EXPECT_EQ(ld.NumLoops(), 1u);
919 InstructionBuilder builder(context.get(), &*f.begin());
920 // Exit condition.
921 Instruction* ten_cst = builder.GetUintConstant(10);
922
923 LoopPeeling peel(&*ld.begin(), ten_cst);
924 EXPECT_TRUE(peel.CanPeelLoop());
925 peel.PeelBefore(2);
926
927 const std::string check = R"(
928 CHECK: OpFunction
929 CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
930 CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpULessThan {{%\w+}}
931 CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]]
932 CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
933 CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
934 CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
935 CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
936 CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
937 CHECK: [[BE]] = OpLabel
938 CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
939 CHECK-NEXT: [[EXIT_COND:%\w+]] = OpULessThan {{%\w+}} [[DUMMY_IT_1]]
940 CHECK-NEXT: OpBranchConditional [[EXIT_COND]] [[BEFORE_LOOP]] [[AFTER_LOOP_PREHEADER]]
941
942 CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
943 CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
944 CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
945
946 CHECK: [[AFTER_LOOP]] = OpLabel
947 CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[I_1]] [[AFTER_LOOP_PREHEADER]]
948 CHECK-NEXT: OpLoopMerge
949 )";
950
951 Match(check, context.get());
952 }
953
954 // Peel after.
955 {
956 SCOPED_TRACE("Peel after");
957
958 std::unique_ptr<IRContext> context =
959 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
960 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
961 Module* module = context->module();
962 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
963 << text << std::endl;
964 Function& f = *module->begin();
965 LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
966
967 EXPECT_EQ(ld.NumLoops(), 1u);
968
969 InstructionBuilder builder(context.get(), &*f.begin());
970 // Exit condition.
971 Instruction* ten_cst = builder.GetUintConstant(10);
972
973 LoopPeeling peel(&*ld.begin(), ten_cst);
974 EXPECT_TRUE(peel.CanPeelLoop());
975 peel.PeelAfter(2);
976
977 const std::string check = R"(
978 CHECK: OpFunction
979 CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
980 CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpULessThan {{%\w+}}
981 CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
982 CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
983 CHECK: [[BEFORE_LOOP]] = OpLabel
984 CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
985 CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
986 CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
987 CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
988 CHECK: [[BE]] = OpLabel
989 CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
990 CHECK-NEXT: [[EXIT_VAL:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT_1]]
991 CHECK-NEXT: [[EXIT_COND:%\w+]] = OpULessThan {{%\w+}} [[EXIT_VAL]]
992 CHECK-NEXT: OpBranchConditional [[EXIT_COND]] [[BEFORE_LOOP]] [[BEFORE_LOOP_MERGE]]
993
994 CHECK: [[IF_MERGE]] = OpLabel
995 CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I_1]] [[BEFORE_LOOP_MERGE]]
996 CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
997
998 CHECK: [[AFTER_LOOP]] = OpLabel
999 CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
1000 CHECK-NEXT: OpLoopMerge
1001 )";
1002
1003 Match(check, context.get());
1004 }
1005 }
1006
1007 /*
1008 Generated from the following GLSL + --eliminate-local-multi-store
1009
1010 #version 330 core
1011 void main() {
1012 int a[10];
1013 int n = a[0];
1014 for(int i = 0; i < n; ++i) {}
1015 }
1016 */
TEST_F(PeelingTest,PeelingLoopWithStore)1017 TEST_F(PeelingTest, PeelingLoopWithStore) {
1018 const std::string text = R"(
1019 OpCapability Shader
1020 %1 = OpExtInstImport "GLSL.std.450"
1021 OpMemoryModel Logical GLSL450
1022 OpEntryPoint Fragment %main "main" %o %n
1023 OpExecutionMode %main OriginLowerLeft
1024 OpSource GLSL 450
1025 OpName %main "main"
1026 OpName %o "o"
1027 OpName %end "end"
1028 OpName %n "n"
1029 OpName %i "i"
1030 OpDecorate %o Location 0
1031 OpDecorate %n Flat
1032 OpDecorate %n Location 0
1033 %void = OpTypeVoid
1034 %3 = OpTypeFunction %void
1035 %float = OpTypeFloat 32
1036 %_ptr_Output_float = OpTypePointer Output %float
1037 %o = OpVariable %_ptr_Output_float Output
1038 %float_0 = OpConstant %float 0
1039 %int = OpTypeInt 32 1
1040 %_ptr_Function_int = OpTypePointer Function %int
1041 %_ptr_Input_int = OpTypePointer Input %int
1042 %n = OpVariable %_ptr_Input_int Input
1043 %int_0 = OpConstant %int 0
1044 %bool = OpTypeBool
1045 %float_1 = OpConstant %float 1
1046 %int_1 = OpConstant %int 1
1047 %main = OpFunction %void None %3
1048 %5 = OpLabel
1049 %end = OpVariable %_ptr_Function_int Function
1050 %i = OpVariable %_ptr_Function_int Function
1051 OpStore %o %float_0
1052 %15 = OpLoad %int %n
1053 OpStore %end %15
1054 OpStore %i %int_0
1055 OpBranch %18
1056 %18 = OpLabel
1057 %33 = OpPhi %int %int_0 %5 %32 %21
1058 OpLoopMerge %20 %21 None
1059 OpBranch %22
1060 %22 = OpLabel
1061 %26 = OpSLessThan %bool %33 %15
1062 OpBranchConditional %26 %19 %20
1063 %19 = OpLabel
1064 %28 = OpLoad %float %o
1065 %29 = OpFAdd %float %28 %float_1
1066 OpStore %o %29
1067 OpBranch %21
1068 %21 = OpLabel
1069 %32 = OpIAdd %int %33 %int_1
1070 OpStore %i %32
1071 OpBranch %18
1072 %20 = OpLabel
1073 OpReturn
1074 OpFunctionEnd
1075 )";
1076
1077 // Peel before.
1078 {
1079 SCOPED_TRACE("Peel before");
1080
1081 std::unique_ptr<IRContext> context =
1082 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1083 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1084 Module* module = context->module();
1085 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
1086 << text << std::endl;
1087 Function& f = *module->begin();
1088 LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
1089
1090 EXPECT_EQ(ld.NumLoops(), 1u);
1091
1092 Instruction* loop_count = context->get_def_use_mgr()->GetDef(15);
1093 EXPECT_EQ(loop_count->opcode(), spv::Op::OpLoad);
1094
1095 LoopPeeling peel(&*ld.begin(), loop_count);
1096 EXPECT_TRUE(peel.CanPeelLoop());
1097 peel.PeelBefore(1);
1098
1099 const std::string check = R"(
1100 CHECK: OpFunction
1101 CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
1102 CHECK: [[LOOP_COUNT:%\w+]] = OpLoad
1103 CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} {{%\w+}} [[LOOP_COUNT]]
1104 CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] {{%\w+}} [[LOOP_COUNT]]
1105 CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
1106 CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
1107 CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
1108 CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
1109 CHECK: [[COND_BLOCK:%\w+]] = OpLabel
1110 CHECK-NEXT: OpSLessThan
1111 CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[DUMMY_IT]]
1112 CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]]
1113 CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
1114 CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
1115 CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
1116
1117 CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
1118 CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
1119 CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
1120
1121 CHECK: [[AFTER_LOOP]] = OpLabel
1122 CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]]
1123 CHECK-NEXT: OpLoopMerge
1124 )";
1125
1126 Match(check, context.get());
1127 }
1128
1129 // Peel after.
1130 {
1131 SCOPED_TRACE("Peel after");
1132
1133 std::unique_ptr<IRContext> context =
1134 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1135 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1136 Module* module = context->module();
1137 EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
1138 << text << std::endl;
1139 Function& f = *module->begin();
1140 LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
1141
1142 EXPECT_EQ(ld.NumLoops(), 1u);
1143
1144 Instruction* loop_count = context->get_def_use_mgr()->GetDef(15);
1145 EXPECT_EQ(loop_count->opcode(), spv::Op::OpLoad);
1146
1147 LoopPeeling peel(&*ld.begin(), loop_count);
1148 EXPECT_TRUE(peel.CanPeelLoop());
1149 peel.PeelAfter(1);
1150
1151 const std::string check = R"(
1152 CHECK: OpFunction
1153 CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
1154 CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}}
1155 CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
1156 CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
1157 CHECK: [[BEFORE_LOOP]] = OpLabel
1158 CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
1159 CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
1160 CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
1161 CHECK: [[COND_BLOCK:%\w+]] = OpLabel
1162 CHECK-NEXT: OpSLessThan
1163 CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT]] {{%\w+}}
1164 CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]]
1165 CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]]
1166 CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
1167 CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
1168 CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
1169
1170 CHECK: [[IF_MERGE]] = OpLabel
1171 CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]]
1172 CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
1173
1174 CHECK: [[AFTER_LOOP]] = OpLabel
1175 CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
1176 CHECK-NEXT: OpLoopMerge
1177
1178 )";
1179
1180 Match(check, context.get());
1181 }
1182 }
1183
1184 } // namespace
1185 } // namespace opt
1186 } // namespace spvtools
1187