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 <string>
16 #include <utility>
17 #include <vector>
18
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 class PeelingPassTest : public PassTest<::testing::Test> {
30 public:
31 // Generic routine to run the loop peeling pass and check
AssembleAndRunPeelingTest(const std::string & text_head,const std::string & text_tail,SpvOp opcode,const std::string & res_id,const std::string & op1,const std::string & op2)32 LoopPeelingPass::LoopPeelingStats AssembleAndRunPeelingTest(
33 const std::string& text_head, const std::string& text_tail, SpvOp opcode,
34 const std::string& res_id, const std::string& op1,
35 const std::string& op2) {
36 std::string opcode_str;
37 switch (opcode) {
38 case SpvOpSLessThan:
39 opcode_str = "OpSLessThan";
40 break;
41 case SpvOpSGreaterThan:
42 opcode_str = "OpSGreaterThan";
43 break;
44 case SpvOpSLessThanEqual:
45 opcode_str = "OpSLessThanEqual";
46 break;
47 case SpvOpSGreaterThanEqual:
48 opcode_str = "OpSGreaterThanEqual";
49 break;
50 case SpvOpIEqual:
51 opcode_str = "OpIEqual";
52 break;
53 case SpvOpINotEqual:
54 opcode_str = "OpINotEqual";
55 break;
56 default:
57 assert(false && "Unhandled");
58 break;
59 }
60 std::string test_cond =
61 res_id + " = " + opcode_str + " %bool " + op1 + " " + op2 + "\n";
62
63 LoopPeelingPass::LoopPeelingStats stats;
64 SinglePassRunAndDisassemble<LoopPeelingPass>(
65 text_head + test_cond + text_tail, true, true, &stats);
66
67 return stats;
68 }
69
70 // Generic routine to run the loop peeling pass and check
RunPeelingTest(const std::string & text_head,const std::string & text_tail,SpvOp opcode,const std::string & res_id,const std::string & op1,const std::string & op2,size_t nb_of_loops)71 LoopPeelingPass::LoopPeelingStats RunPeelingTest(
72 const std::string& text_head, const std::string& text_tail, SpvOp opcode,
73 const std::string& res_id, const std::string& op1, const std::string& op2,
74 size_t nb_of_loops) {
75 LoopPeelingPass::LoopPeelingStats stats = AssembleAndRunPeelingTest(
76 text_head, text_tail, opcode, res_id, op1, op2);
77
78 Function& f = *context()->module()->begin();
79 LoopDescriptor& ld = *context()->GetLoopDescriptor(&f);
80 EXPECT_EQ(ld.NumLoops(), nb_of_loops);
81
82 return stats;
83 }
84
85 using PeelTraceType =
86 std::vector<std::pair<LoopPeelingPass::PeelDirection, uint32_t>>;
87
BuildAndCheckTrace(const std::string & text_head,const std::string & text_tail,SpvOp opcode,const std::string & res_id,const std::string & op1,const std::string & op2,const PeelTraceType & expected_peel_trace,size_t expected_nb_of_loops)88 void BuildAndCheckTrace(const std::string& text_head,
89 const std::string& text_tail, SpvOp opcode,
90 const std::string& res_id, const std::string& op1,
91 const std::string& op2,
92 const PeelTraceType& expected_peel_trace,
93 size_t expected_nb_of_loops) {
94 auto stats = RunPeelingTest(text_head, text_tail, opcode, res_id, op1, op2,
95 expected_nb_of_loops);
96
97 EXPECT_EQ(stats.peeled_loops_.size(), expected_peel_trace.size());
98 if (stats.peeled_loops_.size() != expected_peel_trace.size()) {
99 return;
100 }
101
102 PeelTraceType::const_iterator expected_trace_it =
103 expected_peel_trace.begin();
104 decltype(stats.peeled_loops_)::const_iterator stats_it =
105 stats.peeled_loops_.begin();
106
107 while (expected_trace_it != expected_peel_trace.end()) {
108 EXPECT_EQ(expected_trace_it->first, std::get<1>(*stats_it));
109 EXPECT_EQ(expected_trace_it->second, std::get<2>(*stats_it));
110 ++expected_trace_it;
111 ++stats_it;
112 }
113 }
114 };
115
116 /*
117 Test are derivation of the following generated test from the following GLSL +
118 --eliminate-local-multi-store
119
120 #version 330 core
121 void main() {
122 int a = 0;
123 for(int i = 1; i < 10; i += 2) {
124 if (i < 3) {
125 a += 2;
126 }
127 }
128 }
129
130 The condition is interchanged to test < > <= >= == and peel before/after
131 opportunities.
132 */
TEST_F(PeelingPassTest,PeelingPassBasic)133 TEST_F(PeelingPassTest, PeelingPassBasic) {
134 const std::string text_head = R"(
135 OpCapability Shader
136 %1 = OpExtInstImport "GLSL.std.450"
137 OpMemoryModel Logical GLSL450
138 OpEntryPoint Fragment %main "main"
139 OpExecutionMode %main OriginLowerLeft
140 OpSource GLSL 330
141 OpName %main "main"
142 OpName %a "a"
143 OpName %i "i"
144 %void = OpTypeVoid
145 %3 = OpTypeFunction %void
146 %int = OpTypeInt 32 1
147 %_ptr_Function_int = OpTypePointer Function %int
148 %bool = OpTypeBool
149 %int_20 = OpConstant %int 20
150 %int_19 = OpConstant %int 19
151 %int_18 = OpConstant %int 18
152 %int_17 = OpConstant %int 17
153 %int_16 = OpConstant %int 16
154 %int_15 = OpConstant %int 15
155 %int_14 = OpConstant %int 14
156 %int_13 = OpConstant %int 13
157 %int_12 = OpConstant %int 12
158 %int_11 = OpConstant %int 11
159 %int_10 = OpConstant %int 10
160 %int_9 = OpConstant %int 9
161 %int_8 = OpConstant %int 8
162 %int_7 = OpConstant %int 7
163 %int_6 = OpConstant %int 6
164 %int_5 = OpConstant %int 5
165 %int_4 = OpConstant %int 4
166 %int_3 = OpConstant %int 3
167 %int_2 = OpConstant %int 2
168 %int_1 = OpConstant %int 1
169 %int_0 = OpConstant %int 0
170 %main = OpFunction %void None %3
171 %5 = OpLabel
172 %a = OpVariable %_ptr_Function_int Function
173 %i = OpVariable %_ptr_Function_int Function
174 OpStore %a %int_0
175 OpStore %i %int_0
176 OpBranch %11
177 %11 = OpLabel
178 %31 = OpPhi %int %int_0 %5 %33 %14
179 %32 = OpPhi %int %int_1 %5 %30 %14
180 OpLoopMerge %13 %14 None
181 OpBranch %15
182 %15 = OpLabel
183 %19 = OpSLessThan %bool %32 %int_20
184 OpBranchConditional %19 %12 %13
185 %12 = OpLabel
186 )";
187 const std::string text_tail = R"(
188 OpSelectionMerge %24 None
189 OpBranchConditional %22 %23 %24
190 %23 = OpLabel
191 %27 = OpIAdd %int %31 %int_2
192 OpStore %a %27
193 OpBranch %24
194 %24 = OpLabel
195 %33 = OpPhi %int %31 %12 %27 %23
196 OpBranch %14
197 %14 = OpLabel
198 %30 = OpIAdd %int %32 %int_2
199 OpStore %i %30
200 OpBranch %11
201 %13 = OpLabel
202 OpReturn
203 OpFunctionEnd
204 )";
205
206 auto run_test = [&text_head, &text_tail, this](SpvOp opcode,
207 const std::string& op1,
208 const std::string& op2) {
209 auto stats =
210 RunPeelingTest(text_head, text_tail, opcode, "%22", op1, op2, 2);
211
212 EXPECT_EQ(stats.peeled_loops_.size(), 1u);
213 if (stats.peeled_loops_.size() != 1u)
214 return std::pair<LoopPeelingPass::PeelDirection, uint32_t>{
215 LoopPeelingPass::PeelDirection::kNone, 0};
216
217 return std::pair<LoopPeelingPass::PeelDirection, uint32_t>{
218 std::get<1>(*stats.peeled_loops_.begin()),
219 std::get<2>(*stats.peeled_loops_.begin())};
220 };
221
222 // Test LT
223 // Peel before by a factor of 2.
224 {
225 SCOPED_TRACE("Peel before iv < 4");
226
227 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
228 run_test(SpvOpSLessThan, "%32", "%int_4");
229 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
230 EXPECT_EQ(peel_info.second, 2u);
231 }
232 {
233 SCOPED_TRACE("Peel before 4 > iv");
234
235 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
236 run_test(SpvOpSGreaterThan, "%int_4", "%32");
237 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
238 EXPECT_EQ(peel_info.second, 2u);
239 }
240 {
241 SCOPED_TRACE("Peel before iv < 5");
242
243 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
244 run_test(SpvOpSLessThan, "%32", "%int_5");
245 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
246 EXPECT_EQ(peel_info.second, 2u);
247 }
248 {
249 SCOPED_TRACE("Peel before 5 > iv");
250
251 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
252 run_test(SpvOpSGreaterThan, "%int_5", "%32");
253 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
254 EXPECT_EQ(peel_info.second, 2u);
255 }
256
257 // Peel after by a factor of 2.
258 {
259 SCOPED_TRACE("Peel after iv < 16");
260
261 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
262 run_test(SpvOpSLessThan, "%32", "%int_16");
263 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
264 EXPECT_EQ(peel_info.second, 2u);
265 }
266 {
267 SCOPED_TRACE("Peel after 16 > iv");
268
269 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
270 run_test(SpvOpSGreaterThan, "%int_16", "%32");
271 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
272 EXPECT_EQ(peel_info.second, 2u);
273 }
274 {
275 SCOPED_TRACE("Peel after iv < 17");
276
277 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
278 run_test(SpvOpSLessThan, "%32", "%int_17");
279 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
280 EXPECT_EQ(peel_info.second, 2u);
281 }
282 {
283 SCOPED_TRACE("Peel after 17 > iv");
284
285 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
286 run_test(SpvOpSGreaterThan, "%int_17", "%32");
287 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
288 EXPECT_EQ(peel_info.second, 2u);
289 }
290
291 // Test GT
292 // Peel before by a factor of 2.
293 {
294 SCOPED_TRACE("Peel before iv > 5");
295
296 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
297 run_test(SpvOpSGreaterThan, "%32", "%int_5");
298 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
299 EXPECT_EQ(peel_info.second, 2u);
300 }
301 {
302 SCOPED_TRACE("Peel before 5 < iv");
303
304 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
305 run_test(SpvOpSLessThan, "%int_5", "%32");
306 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
307 EXPECT_EQ(peel_info.second, 2u);
308 }
309 {
310 SCOPED_TRACE("Peel before iv > 4");
311
312 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
313 run_test(SpvOpSGreaterThan, "%32", "%int_4");
314 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
315 EXPECT_EQ(peel_info.second, 2u);
316 }
317 {
318 SCOPED_TRACE("Peel before 4 < iv");
319
320 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
321 run_test(SpvOpSLessThan, "%int_4", "%32");
322 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
323 EXPECT_EQ(peel_info.second, 2u);
324 }
325
326 // Peel after by a factor of 2.
327 {
328 SCOPED_TRACE("Peel after iv > 16");
329
330 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
331 run_test(SpvOpSGreaterThan, "%32", "%int_16");
332 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
333 EXPECT_EQ(peel_info.second, 2u);
334 }
335 {
336 SCOPED_TRACE("Peel after 16 < iv");
337
338 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
339 run_test(SpvOpSLessThan, "%int_16", "%32");
340 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
341 EXPECT_EQ(peel_info.second, 2u);
342 }
343 {
344 SCOPED_TRACE("Peel after iv > 17");
345
346 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
347 run_test(SpvOpSGreaterThan, "%32", "%int_17");
348 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
349 EXPECT_EQ(peel_info.second, 2u);
350 }
351 {
352 SCOPED_TRACE("Peel after 17 < iv");
353
354 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
355 run_test(SpvOpSLessThan, "%int_17", "%32");
356 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
357 EXPECT_EQ(peel_info.second, 2u);
358 }
359
360 // Test LE
361 // Peel before by a factor of 2.
362 {
363 SCOPED_TRACE("Peel before iv <= 4");
364
365 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
366 run_test(SpvOpSLessThanEqual, "%32", "%int_4");
367 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
368 EXPECT_EQ(peel_info.second, 2u);
369 }
370 {
371 SCOPED_TRACE("Peel before 4 => iv");
372
373 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
374 run_test(SpvOpSGreaterThanEqual, "%int_4", "%32");
375 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
376 EXPECT_EQ(peel_info.second, 2u);
377 }
378 {
379 SCOPED_TRACE("Peel before iv <= 3");
380
381 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
382 run_test(SpvOpSLessThanEqual, "%32", "%int_3");
383 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
384 EXPECT_EQ(peel_info.second, 2u);
385 }
386 {
387 SCOPED_TRACE("Peel before 3 => iv");
388
389 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
390 run_test(SpvOpSGreaterThanEqual, "%int_3", "%32");
391 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
392 EXPECT_EQ(peel_info.second, 2u);
393 }
394
395 // Peel after by a factor of 2.
396 {
397 SCOPED_TRACE("Peel after iv <= 16");
398
399 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
400 run_test(SpvOpSLessThanEqual, "%32", "%int_16");
401 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
402 EXPECT_EQ(peel_info.second, 2u);
403 }
404 {
405 SCOPED_TRACE("Peel after 16 => iv");
406
407 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
408 run_test(SpvOpSGreaterThanEqual, "%int_16", "%32");
409 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
410 EXPECT_EQ(peel_info.second, 2u);
411 }
412 {
413 SCOPED_TRACE("Peel after iv <= 15");
414
415 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
416 run_test(SpvOpSLessThanEqual, "%32", "%int_15");
417 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
418 EXPECT_EQ(peel_info.second, 2u);
419 }
420 {
421 SCOPED_TRACE("Peel after 15 => iv");
422
423 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
424 run_test(SpvOpSGreaterThanEqual, "%int_15", "%32");
425 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
426 EXPECT_EQ(peel_info.second, 2u);
427 }
428
429 // Test GE
430 // Peel before by a factor of 2.
431 {
432 SCOPED_TRACE("Peel before iv >= 5");
433
434 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
435 run_test(SpvOpSGreaterThanEqual, "%32", "%int_5");
436 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
437 EXPECT_EQ(peel_info.second, 2u);
438 }
439 {
440 SCOPED_TRACE("Peel before 35 >= iv");
441
442 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
443 run_test(SpvOpSLessThanEqual, "%int_5", "%32");
444 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
445 EXPECT_EQ(peel_info.second, 2u);
446 }
447 {
448 SCOPED_TRACE("Peel before iv >= 4");
449
450 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
451 run_test(SpvOpSGreaterThanEqual, "%32", "%int_4");
452 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
453 EXPECT_EQ(peel_info.second, 2u);
454 }
455 {
456 SCOPED_TRACE("Peel before 4 <= iv");
457
458 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
459 run_test(SpvOpSLessThanEqual, "%int_4", "%32");
460 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
461 EXPECT_EQ(peel_info.second, 2u);
462 }
463
464 // Peel after by a factor of 2.
465 {
466 SCOPED_TRACE("Peel after iv >= 17");
467
468 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
469 run_test(SpvOpSGreaterThanEqual, "%32", "%int_17");
470 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
471 EXPECT_EQ(peel_info.second, 2u);
472 }
473 {
474 SCOPED_TRACE("Peel after 17 <= iv");
475
476 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
477 run_test(SpvOpSLessThanEqual, "%int_17", "%32");
478 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
479 EXPECT_EQ(peel_info.second, 2u);
480 }
481 {
482 SCOPED_TRACE("Peel after iv >= 16");
483
484 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
485 run_test(SpvOpSGreaterThanEqual, "%32", "%int_16");
486 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
487 EXPECT_EQ(peel_info.second, 2u);
488 }
489 {
490 SCOPED_TRACE("Peel after 16 <= iv");
491
492 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
493 run_test(SpvOpSLessThanEqual, "%int_16", "%32");
494 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
495 EXPECT_EQ(peel_info.second, 2u);
496 }
497
498 // Test EQ
499 // Peel before by a factor of 1.
500 {
501 SCOPED_TRACE("Peel before iv == 1");
502
503 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
504 run_test(SpvOpIEqual, "%32", "%int_1");
505 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
506 EXPECT_EQ(peel_info.second, 1u);
507 }
508 {
509 SCOPED_TRACE("Peel before 1 == iv");
510
511 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
512 run_test(SpvOpIEqual, "%int_1", "%32");
513 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
514 EXPECT_EQ(peel_info.second, 1u);
515 }
516
517 // Peel after by a factor of 1.
518 {
519 SCOPED_TRACE("Peel after iv == 19");
520
521 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
522 run_test(SpvOpIEqual, "%32", "%int_19");
523 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
524 EXPECT_EQ(peel_info.second, 1u);
525 }
526 {
527 SCOPED_TRACE("Peel after 19 == iv");
528
529 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
530 run_test(SpvOpIEqual, "%int_19", "%32");
531 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
532 EXPECT_EQ(peel_info.second, 1u);
533 }
534
535 // Test NE
536 // Peel before by a factor of 1.
537 {
538 SCOPED_TRACE("Peel before iv != 1");
539
540 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
541 run_test(SpvOpINotEqual, "%32", "%int_1");
542 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
543 EXPECT_EQ(peel_info.second, 1u);
544 }
545 {
546 SCOPED_TRACE("Peel before 1 != iv");
547
548 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
549 run_test(SpvOpINotEqual, "%int_1", "%32");
550 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
551 EXPECT_EQ(peel_info.second, 1u);
552 }
553
554 // Peel after by a factor of 1.
555 {
556 SCOPED_TRACE("Peel after iv != 19");
557
558 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
559 run_test(SpvOpINotEqual, "%32", "%int_19");
560 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
561 EXPECT_EQ(peel_info.second, 1u);
562 }
563 {
564 SCOPED_TRACE("Peel after 19 != iv");
565
566 std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
567 run_test(SpvOpINotEqual, "%int_19", "%32");
568 EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
569 EXPECT_EQ(peel_info.second, 1u);
570 }
571
572 // No peel.
573 {
574 SCOPED_TRACE("No Peel: 20 => iv");
575
576 auto stats = RunPeelingTest(text_head, text_tail, SpvOpSLessThanEqual,
577 "%22", "%int_20", "%32", 1);
578
579 EXPECT_EQ(stats.peeled_loops_.size(), 0u);
580 }
581 }
582
583 /*
584 Test are derivation of the following generated test from the following GLSL +
585 --eliminate-local-multi-store
586
587 #version 330 core
588 void main() {
589 int a = 0;
590 for(int i = 0; i < 10; ++i) {
591 if (i < 3) {
592 a += 2;
593 }
594 if (i < 1) {
595 a += 2;
596 }
597 }
598 }
599
600 The condition is interchanged to test < > <= >= == and peel before/after
601 opportunities.
602 */
TEST_F(PeelingPassTest,MultiplePeelingPass)603 TEST_F(PeelingPassTest, MultiplePeelingPass) {
604 const std::string text_head = R"(
605 OpCapability Shader
606 %1 = OpExtInstImport "GLSL.std.450"
607 OpMemoryModel Logical GLSL450
608 OpEntryPoint Fragment %main "main"
609 OpExecutionMode %main OriginLowerLeft
610 OpSource GLSL 330
611 OpName %main "main"
612 OpName %a "a"
613 OpName %i "i"
614 %void = OpTypeVoid
615 %3 = OpTypeFunction %void
616 %int = OpTypeInt 32 1
617 %_ptr_Function_int = OpTypePointer Function %int
618 %bool = OpTypeBool
619 %int_10 = OpConstant %int 10
620 %int_9 = OpConstant %int 9
621 %int_8 = OpConstant %int 8
622 %int_7 = OpConstant %int 7
623 %int_6 = OpConstant %int 6
624 %int_5 = OpConstant %int 5
625 %int_4 = OpConstant %int 4
626 %int_3 = OpConstant %int 3
627 %int_2 = OpConstant %int 2
628 %int_1 = OpConstant %int 1
629 %int_0 = OpConstant %int 0
630 %main = OpFunction %void None %3
631 %5 = OpLabel
632 %a = OpVariable %_ptr_Function_int Function
633 %i = OpVariable %_ptr_Function_int Function
634 OpStore %a %int_0
635 OpStore %i %int_0
636 OpBranch %11
637 %11 = OpLabel
638 %37 = OpPhi %int %int_0 %5 %40 %14
639 %38 = OpPhi %int %int_0 %5 %36 %14
640 OpLoopMerge %13 %14 None
641 OpBranch %15
642 %15 = OpLabel
643 %19 = OpSLessThan %bool %38 %int_10
644 OpBranchConditional %19 %12 %13
645 %12 = OpLabel
646 )";
647 const std::string text_tail = R"(
648 OpSelectionMerge %24 None
649 OpBranchConditional %22 %23 %24
650 %23 = OpLabel
651 %27 = OpIAdd %int %37 %int_2
652 OpStore %a %27
653 OpBranch %24
654 %24 = OpLabel
655 %39 = OpPhi %int %37 %12 %27 %23
656 %30 = OpSLessThan %bool %38 %int_1
657 OpSelectionMerge %32 None
658 OpBranchConditional %30 %31 %32
659 %31 = OpLabel
660 %34 = OpIAdd %int %39 %int_2
661 OpStore %a %34
662 OpBranch %32
663 %32 = OpLabel
664 %40 = OpPhi %int %39 %24 %34 %31
665 OpBranch %14
666 %14 = OpLabel
667 %36 = OpIAdd %int %38 %int_1
668 OpStore %i %36
669 OpBranch %11
670 %13 = OpLabel
671 OpReturn
672 OpFunctionEnd
673 )";
674
675 auto run_test = [&text_head, &text_tail, this](
676 SpvOp opcode, const std::string& op1,
677 const std::string& op2,
678 const PeelTraceType& expected_peel_trace) {
679 BuildAndCheckTrace(text_head, text_tail, opcode, "%22", op1, op2,
680 expected_peel_trace, expected_peel_trace.size() + 1);
681 };
682
683 // Test LT
684 // Peel before by a factor of 3.
685 {
686 SCOPED_TRACE("Peel before iv < 3");
687
688 run_test(SpvOpSLessThan, "%38", "%int_3",
689 {{LoopPeelingPass::PeelDirection::kBefore, 3u}});
690 }
691 {
692 SCOPED_TRACE("Peel before 3 > iv");
693
694 run_test(SpvOpSGreaterThan, "%int_3", "%38",
695 {{LoopPeelingPass::PeelDirection::kBefore, 3u}});
696 }
697
698 // Peel after by a factor of 2.
699 {
700 SCOPED_TRACE("Peel after iv < 8");
701
702 run_test(SpvOpSLessThan, "%38", "%int_8",
703 {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
704 }
705 {
706 SCOPED_TRACE("Peel after 8 > iv");
707
708 run_test(SpvOpSGreaterThan, "%int_8", "%38",
709 {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
710 }
711
712 // Test GT
713 // Peel before by a factor of 2.
714 {
715 SCOPED_TRACE("Peel before iv > 2");
716
717 run_test(SpvOpSGreaterThan, "%38", "%int_2",
718 {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
719 }
720 {
721 SCOPED_TRACE("Peel before 2 < iv");
722
723 run_test(SpvOpSLessThan, "%int_2", "%38",
724 {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
725 }
726
727 // Peel after by a factor of 3.
728 {
729 SCOPED_TRACE("Peel after iv > 7");
730
731 run_test(SpvOpSGreaterThan, "%38", "%int_7",
732 {{LoopPeelingPass::PeelDirection::kAfter, 3u}});
733 }
734 {
735 SCOPED_TRACE("Peel after 7 < iv");
736
737 run_test(SpvOpSLessThan, "%int_7", "%38",
738 {{LoopPeelingPass::PeelDirection::kAfter, 3u}});
739 }
740
741 // Test LE
742 // Peel before by a factor of 2.
743 {
744 SCOPED_TRACE("Peel before iv <= 1");
745
746 run_test(SpvOpSLessThanEqual, "%38", "%int_1",
747 {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
748 }
749 {
750 SCOPED_TRACE("Peel before 1 => iv");
751
752 run_test(SpvOpSGreaterThanEqual, "%int_1", "%38",
753 {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
754 }
755
756 // Peel after by a factor of 2.
757 {
758 SCOPED_TRACE("Peel after iv <= 7");
759
760 run_test(SpvOpSLessThanEqual, "%38", "%int_7",
761 {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
762 }
763 {
764 SCOPED_TRACE("Peel after 7 => iv");
765
766 run_test(SpvOpSGreaterThanEqual, "%int_7", "%38",
767 {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
768 }
769
770 // Test GE
771 // Peel before by a factor of 2.
772 {
773 SCOPED_TRACE("Peel before iv >= 2");
774
775 run_test(SpvOpSGreaterThanEqual, "%38", "%int_2",
776 {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
777 }
778 {
779 SCOPED_TRACE("Peel before 2 <= iv");
780
781 run_test(SpvOpSLessThanEqual, "%int_2", "%38",
782 {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
783 }
784
785 // Peel after by a factor of 2.
786 {
787 SCOPED_TRACE("Peel after iv >= 8");
788
789 run_test(SpvOpSGreaterThanEqual, "%38", "%int_8",
790 {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
791 }
792 {
793 SCOPED_TRACE("Peel after 8 <= iv");
794
795 run_test(SpvOpSLessThanEqual, "%int_8", "%38",
796 {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
797 }
798 // Test EQ
799 // Peel before by a factor of 1.
800 {
801 SCOPED_TRACE("Peel before iv == 0");
802
803 run_test(SpvOpIEqual, "%38", "%int_0",
804 {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
805 }
806 {
807 SCOPED_TRACE("Peel before 0 == iv");
808
809 run_test(SpvOpIEqual, "%int_0", "%38",
810 {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
811 }
812
813 // Peel after by a factor of 1.
814 {
815 SCOPED_TRACE("Peel after iv == 9");
816
817 run_test(SpvOpIEqual, "%38", "%int_9",
818 {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
819 }
820 {
821 SCOPED_TRACE("Peel after 9 == iv");
822
823 run_test(SpvOpIEqual, "%int_9", "%38",
824 {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
825 }
826
827 // Test NE
828 // Peel before by a factor of 1.
829 {
830 SCOPED_TRACE("Peel before iv != 0");
831
832 run_test(SpvOpINotEqual, "%38", "%int_0",
833 {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
834 }
835 {
836 SCOPED_TRACE("Peel before 0 != iv");
837
838 run_test(SpvOpINotEqual, "%int_0", "%38",
839 {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
840 }
841
842 // Peel after by a factor of 1.
843 {
844 SCOPED_TRACE("Peel after iv != 9");
845
846 run_test(SpvOpINotEqual, "%38", "%int_9",
847 {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
848 }
849 {
850 SCOPED_TRACE("Peel after 9 != iv");
851
852 run_test(SpvOpINotEqual, "%int_9", "%38",
853 {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
854 }
855 }
856
857 /*
858 Test are derivation of the following generated test from the following GLSL +
859 --eliminate-local-multi-store
860
861 #version 330 core
862 void main() {
863 int a = 0;
864 for (int i = 0; i < 10; i++) {
865 for (int j = 0; j < 10; j++) {
866 if (i < 3) {
867 a += 2;
868 }
869 }
870 }
871 }
872 */
TEST_F(PeelingPassTest,PeelingNestedPass)873 TEST_F(PeelingPassTest, PeelingNestedPass) {
874 const std::string text_head = R"(
875 OpCapability Shader
876 %1 = OpExtInstImport "GLSL.std.450"
877 OpMemoryModel Logical GLSL450
878 OpEntryPoint Fragment %main "main"
879 OpExecutionMode %main OriginLowerLeft
880 OpSource GLSL 330
881 OpName %main "main"
882 OpName %a "a"
883 OpName %i "i"
884 OpName %j "j"
885 %void = OpTypeVoid
886 %3 = OpTypeFunction %void
887 %int = OpTypeInt 32 1
888 %_ptr_Function_int = OpTypePointer Function %int
889 %int_0 = OpConstant %int 0
890 %int_10 = OpConstant %int 10
891 %bool = OpTypeBool
892 %int_7 = OpConstant %int 7
893 %int_3 = OpConstant %int 3
894 %int_2 = OpConstant %int 2
895 %int_1 = OpConstant %int 1
896 %43 = OpUndef %int
897 %main = OpFunction %void None %3
898 %5 = OpLabel
899 %a = OpVariable %_ptr_Function_int Function
900 %i = OpVariable %_ptr_Function_int Function
901 %j = OpVariable %_ptr_Function_int Function
902 OpStore %a %int_0
903 OpStore %i %int_0
904 OpBranch %11
905 %11 = OpLabel
906 %41 = OpPhi %int %int_0 %5 %45 %14
907 %42 = OpPhi %int %int_0 %5 %40 %14
908 %44 = OpPhi %int %43 %5 %46 %14
909 OpLoopMerge %13 %14 None
910 OpBranch %15
911 %15 = OpLabel
912 %19 = OpSLessThan %bool %42 %int_10
913 OpBranchConditional %19 %12 %13
914 %12 = OpLabel
915 OpStore %j %int_0
916 OpBranch %21
917 %21 = OpLabel
918 %45 = OpPhi %int %41 %12 %47 %24
919 %46 = OpPhi %int %int_0 %12 %38 %24
920 OpLoopMerge %23 %24 None
921 OpBranch %25
922 %25 = OpLabel
923 %27 = OpSLessThan %bool %46 %int_10
924 OpBranchConditional %27 %22 %23
925 %22 = OpLabel
926 )";
927
928 const std::string text_tail = R"(
929 OpSelectionMerge %32 None
930 OpBranchConditional %30 %31 %32
931 %31 = OpLabel
932 %35 = OpIAdd %int %45 %int_2
933 OpStore %a %35
934 OpBranch %32
935 %32 = OpLabel
936 %47 = OpPhi %int %45 %22 %35 %31
937 OpBranch %24
938 %24 = OpLabel
939 %38 = OpIAdd %int %46 %int_1
940 OpStore %j %38
941 OpBranch %21
942 %23 = OpLabel
943 OpBranch %14
944 %14 = OpLabel
945 %40 = OpIAdd %int %42 %int_1
946 OpStore %i %40
947 OpBranch %11
948 %13 = OpLabel
949 OpReturn
950 OpFunctionEnd
951 )";
952
953 auto run_test =
954 [&text_head, &text_tail, this](
955 SpvOp opcode, const std::string& op1, const std::string& op2,
956 const PeelTraceType& expected_peel_trace, size_t nb_of_loops) {
957 BuildAndCheckTrace(text_head, text_tail, opcode, "%30", op1, op2,
958 expected_peel_trace, nb_of_loops);
959 };
960
961 // Peeling outer before by a factor of 3.
962 {
963 SCOPED_TRACE("Peel before iv_i < 3");
964
965 // Expect peel before by a factor of 3 and 4 loops at the end.
966 run_test(SpvOpSLessThan, "%42", "%int_3",
967 {{LoopPeelingPass::PeelDirection::kBefore, 3u}}, 4);
968 }
969 // Peeling outer loop after by a factor of 3.
970 {
971 SCOPED_TRACE("Peel after iv_i < 7");
972
973 // Expect peel after by a factor of 3 and 4 loops at the end.
974 run_test(SpvOpSLessThan, "%42", "%int_7",
975 {{LoopPeelingPass::PeelDirection::kAfter, 3u}}, 4);
976 }
977
978 // Peeling inner loop before by a factor of 3.
979 {
980 SCOPED_TRACE("Peel before iv_j < 3");
981
982 // Expect peel before by a factor of 3 and 3 loops at the end.
983 run_test(SpvOpSLessThan, "%46", "%int_3",
984 {{LoopPeelingPass::PeelDirection::kBefore, 3u}}, 3);
985 }
986 // Peeling inner loop after by a factor of 3.
987 {
988 SCOPED_TRACE("Peel after iv_j < 7");
989
990 // Expect peel after by a factor of 3 and 3 loops at the end.
991 run_test(SpvOpSLessThan, "%46", "%int_7",
992 {{LoopPeelingPass::PeelDirection::kAfter, 3u}}, 3);
993 }
994
995 // Not unworkable condition.
996 {
997 SCOPED_TRACE("No peel");
998
999 // Expect no peeling and 2 loops at the end.
1000 run_test(SpvOpSLessThan, "%46", "%42", {}, 2);
1001 }
1002
1003 // Could do a peeling of 3, but the goes over the threshold.
1004 {
1005 SCOPED_TRACE("Over threshold");
1006
1007 size_t current_threshold = LoopPeelingPass::GetLoopPeelingThreshold();
1008 LoopPeelingPass::SetLoopPeelingThreshold(1u);
1009 // Expect no peeling and 2 loops at the end.
1010 run_test(SpvOpSLessThan, "%46", "%int_7", {}, 2);
1011 LoopPeelingPass::SetLoopPeelingThreshold(current_threshold);
1012 }
1013 }
1014 /*
1015 Test are derivation of the following generated test from the following GLSL +
1016 --eliminate-local-multi-store
1017
1018 #version 330 core
1019 void main() {
1020 int a = 0;
1021 for (int i = 0, j = 0; i < 10; j++, i++) {
1022 if (i < j) {
1023 a += 2;
1024 }
1025 }
1026 }
1027 */
TEST_F(PeelingPassTest,PeelingNoChanges)1028 TEST_F(PeelingPassTest, PeelingNoChanges) {
1029 const std::string text = R"(
1030 OpCapability Shader
1031 %1 = OpExtInstImport "GLSL.std.450"
1032 OpMemoryModel Logical GLSL450
1033 OpEntryPoint Fragment %main "main"
1034 OpExecutionMode %main OriginLowerLeft
1035 OpSource GLSL 330
1036 OpName %main "main"
1037 OpName %a "a"
1038 OpName %i "i"
1039 OpName %j "j"
1040 %void = OpTypeVoid
1041 %3 = OpTypeFunction %void
1042 %int = OpTypeInt 32 1
1043 %_ptr_Function_int = OpTypePointer Function %int
1044 %int_0 = OpConstant %int 0
1045 %int_10 = OpConstant %int 10
1046 %bool = OpTypeBool
1047 %int_2 = OpConstant %int 2
1048 %int_1 = OpConstant %int 1
1049 %main = OpFunction %void None %3
1050 %5 = OpLabel
1051 %a = OpVariable %_ptr_Function_int Function
1052 %i = OpVariable %_ptr_Function_int Function
1053 %j = OpVariable %_ptr_Function_int Function
1054 OpStore %a %int_0
1055 OpStore %i %int_0
1056 OpStore %j %int_0
1057 OpBranch %12
1058 %12 = OpLabel
1059 %34 = OpPhi %int %int_0 %5 %37 %15
1060 %35 = OpPhi %int %int_0 %5 %33 %15
1061 %36 = OpPhi %int %int_0 %5 %31 %15
1062 OpLoopMerge %14 %15 None
1063 OpBranch %16
1064 %16 = OpLabel
1065 %20 = OpSLessThan %bool %35 %int_10
1066 OpBranchConditional %20 %13 %14
1067 %13 = OpLabel
1068 %23 = OpSLessThan %bool %35 %36
1069 OpSelectionMerge %25 None
1070 OpBranchConditional %23 %24 %25
1071 %24 = OpLabel
1072 %28 = OpIAdd %int %34 %int_2
1073 OpStore %a %28
1074 OpBranch %25
1075 %25 = OpLabel
1076 %37 = OpPhi %int %34 %13 %28 %24
1077 OpBranch %15
1078 %15 = OpLabel
1079 %31 = OpIAdd %int %36 %int_1
1080 OpStore %j %31
1081 %33 = OpIAdd %int %35 %int_1
1082 OpStore %i %33
1083 OpBranch %12
1084 %14 = OpLabel
1085 OpReturn
1086 OpFunctionEnd
1087 )";
1088
1089 {
1090 auto result =
1091 SinglePassRunAndDisassemble<LoopPeelingPass>(text, true, false);
1092
1093 EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
1094 }
1095 }
1096
1097 } // namespace
1098 } // namespace opt
1099 } // namespace spvtools
1100