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