• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2018 Google LLC.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <memory>
16 #include <string>
17 #include <vector>
18 
19 #include "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(), SpvOpLoad);
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(), SpvOpLoad);
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(), SpvOpLoad);
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(), SpvOpLoad);
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