• 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 <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