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