1fd4e5da5Sopenharmony_ci// Copyright (c) 2018 Google LLC.
2fd4e5da5Sopenharmony_ci//
3fd4e5da5Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
4fd4e5da5Sopenharmony_ci// you may not use this file except in compliance with the License.
5fd4e5da5Sopenharmony_ci// You may obtain a copy of the License at
6fd4e5da5Sopenharmony_ci//
7fd4e5da5Sopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
8fd4e5da5Sopenharmony_ci//
9fd4e5da5Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software
10fd4e5da5Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
11fd4e5da5Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fd4e5da5Sopenharmony_ci// See the License for the specific language governing permissions and
13fd4e5da5Sopenharmony_ci// limitations under the License.
14fd4e5da5Sopenharmony_ci
15fd4e5da5Sopenharmony_ci#ifndef SOURCE_OPT_LOOP_PEELING_H_
16fd4e5da5Sopenharmony_ci#define SOURCE_OPT_LOOP_PEELING_H_
17fd4e5da5Sopenharmony_ci
18fd4e5da5Sopenharmony_ci#include <algorithm>
19fd4e5da5Sopenharmony_ci#include <limits>
20fd4e5da5Sopenharmony_ci#include <memory>
21fd4e5da5Sopenharmony_ci#include <tuple>
22fd4e5da5Sopenharmony_ci#include <unordered_map>
23fd4e5da5Sopenharmony_ci#include <unordered_set>
24fd4e5da5Sopenharmony_ci#include <utility>
25fd4e5da5Sopenharmony_ci#include <vector>
26fd4e5da5Sopenharmony_ci
27fd4e5da5Sopenharmony_ci#include "source/opt/ir_context.h"
28fd4e5da5Sopenharmony_ci#include "source/opt/loop_descriptor.h"
29fd4e5da5Sopenharmony_ci#include "source/opt/loop_utils.h"
30fd4e5da5Sopenharmony_ci#include "source/opt/pass.h"
31fd4e5da5Sopenharmony_ci#include "source/opt/scalar_analysis.h"
32fd4e5da5Sopenharmony_ci
33fd4e5da5Sopenharmony_cinamespace spvtools {
34fd4e5da5Sopenharmony_cinamespace opt {
35fd4e5da5Sopenharmony_ci
36fd4e5da5Sopenharmony_ci// Utility class to perform the peeling of a given loop.
37fd4e5da5Sopenharmony_ci// The loop peeling transformation make a certain amount of a loop iterations to
38fd4e5da5Sopenharmony_ci// be executed either before (peel before) or after (peel after) the transformed
39fd4e5da5Sopenharmony_ci// loop.
40fd4e5da5Sopenharmony_ci//
41fd4e5da5Sopenharmony_ci// For peeling cases the transformation does the following steps:
42fd4e5da5Sopenharmony_ci//   - It clones the loop and inserts the cloned loop before the original loop;
43fd4e5da5Sopenharmony_ci//   - It connects all iterating values of the cloned loop with the
44fd4e5da5Sopenharmony_ci//     corresponding original loop values so that the second loop starts with
45fd4e5da5Sopenharmony_ci//     the appropriate values.
46fd4e5da5Sopenharmony_ci//   - It inserts a new induction variable "i" is inserted into the cloned that
47fd4e5da5Sopenharmony_ci//     starts with the value 0 and increment by step of one.
48fd4e5da5Sopenharmony_ci//
49fd4e5da5Sopenharmony_ci// The last step is specific to each case:
50fd4e5da5Sopenharmony_ci//   - Peel before: the transformation is to peel the "N" first iterations.
51fd4e5da5Sopenharmony_ci//     The exit condition of the cloned loop is changed so that the loop
52fd4e5da5Sopenharmony_ci//     exits when "i < N" becomes false. The original loop is then protected to
53fd4e5da5Sopenharmony_ci//     only execute if there is any iteration left to do.
54fd4e5da5Sopenharmony_ci//   - Peel after: the transformation is to peel the "N" last iterations,
55fd4e5da5Sopenharmony_ci//     then the exit condition of the cloned loop is changed so that the loop
56fd4e5da5Sopenharmony_ci//     exits when "i + N < max_iteration" becomes false, where "max_iteration"
57fd4e5da5Sopenharmony_ci//     is the upper bound of the loop. The cloned loop is then protected to
58fd4e5da5Sopenharmony_ci//     only execute if there is any iteration left to do no covered by the
59fd4e5da5Sopenharmony_ci//     second.
60fd4e5da5Sopenharmony_ci//
61fd4e5da5Sopenharmony_ci// To be peelable:
62fd4e5da5Sopenharmony_ci//   - The loop must be in LCSSA form;
63fd4e5da5Sopenharmony_ci//   - The loop must not contain any breaks;
64fd4e5da5Sopenharmony_ci//   - The loop must not have any ambiguous iterators updates (see
65fd4e5da5Sopenharmony_ci//     "CanPeelLoop").
66fd4e5da5Sopenharmony_ci// The method "CanPeelLoop" checks that those constrained are met.
67fd4e5da5Sopenharmony_ciclass LoopPeeling {
68fd4e5da5Sopenharmony_ci public:
69fd4e5da5Sopenharmony_ci  // LoopPeeling constructor.
70fd4e5da5Sopenharmony_ci  // |loop| is the loop to peel.
71fd4e5da5Sopenharmony_ci  // |loop_iteration_count| is the instruction holding the |loop| iteration
72fd4e5da5Sopenharmony_ci  // count, must be invariant for |loop| and must be of an int 32 type (signed
73fd4e5da5Sopenharmony_ci  // or unsigned).
74fd4e5da5Sopenharmony_ci  // |canonical_induction_variable| is an induction variable that can be used to
75fd4e5da5Sopenharmony_ci  // count the number of iterations, must be of the same type as
76fd4e5da5Sopenharmony_ci  // |loop_iteration_count| and start at 0 and increase by step of one at each
77fd4e5da5Sopenharmony_ci  // iteration. The value nullptr is interpreted as no suitable variable exists
78fd4e5da5Sopenharmony_ci  // and one will be created.
79fd4e5da5Sopenharmony_ci  LoopPeeling(Loop* loop, Instruction* loop_iteration_count,
80fd4e5da5Sopenharmony_ci              Instruction* canonical_induction_variable = nullptr)
81fd4e5da5Sopenharmony_ci      : context_(loop->GetContext()),
82fd4e5da5Sopenharmony_ci        loop_utils_(loop->GetContext(), loop),
83fd4e5da5Sopenharmony_ci        loop_(loop),
84fd4e5da5Sopenharmony_ci        loop_iteration_count_(!loop->IsInsideLoop(loop_iteration_count)
85fd4e5da5Sopenharmony_ci                                  ? loop_iteration_count
86fd4e5da5Sopenharmony_ci                                  : nullptr),
87fd4e5da5Sopenharmony_ci        int_type_(nullptr),
88fd4e5da5Sopenharmony_ci        original_loop_canonical_induction_variable_(
89fd4e5da5Sopenharmony_ci            canonical_induction_variable),
90fd4e5da5Sopenharmony_ci        canonical_induction_variable_(nullptr) {
91fd4e5da5Sopenharmony_ci    if (loop_iteration_count_) {
92fd4e5da5Sopenharmony_ci      int_type_ = context_->get_type_mgr()
93fd4e5da5Sopenharmony_ci                      ->GetType(loop_iteration_count_->type_id())
94fd4e5da5Sopenharmony_ci                      ->AsInteger();
95fd4e5da5Sopenharmony_ci      if (canonical_induction_variable_) {
96fd4e5da5Sopenharmony_ci        assert(canonical_induction_variable_->type_id() ==
97fd4e5da5Sopenharmony_ci                   loop_iteration_count_->type_id() &&
98fd4e5da5Sopenharmony_ci               "loop_iteration_count and canonical_induction_variable do not "
99fd4e5da5Sopenharmony_ci               "have the same type");
100fd4e5da5Sopenharmony_ci      }
101fd4e5da5Sopenharmony_ci    }
102fd4e5da5Sopenharmony_ci    GetIteratingExitValues();
103fd4e5da5Sopenharmony_ci  }
104fd4e5da5Sopenharmony_ci
105fd4e5da5Sopenharmony_ci  // Returns true if the loop can be peeled.
106fd4e5da5Sopenharmony_ci  // To be peelable, all operation involved in the update of the loop iterators
107fd4e5da5Sopenharmony_ci  // must not dominates the exit condition. This restriction is a work around to
108fd4e5da5Sopenharmony_ci  // not miss compile code like:
109fd4e5da5Sopenharmony_ci  //
110fd4e5da5Sopenharmony_ci  //   for (int i = 0; i + 1 < N; i++) {}
111fd4e5da5Sopenharmony_ci  //   for (int i = 0; ++i < N; i++) {}
112fd4e5da5Sopenharmony_ci  //
113fd4e5da5Sopenharmony_ci  // The increment will happen before the test on the exit condition leading to
114fd4e5da5Sopenharmony_ci  // very look-a-like code.
115fd4e5da5Sopenharmony_ci  //
116fd4e5da5Sopenharmony_ci  // This restriction will not apply if a loop rotate is applied before (i.e.
117fd4e5da5Sopenharmony_ci  // becomes a do-while loop).
118fd4e5da5Sopenharmony_ci  bool CanPeelLoop() const {
119fd4e5da5Sopenharmony_ci    CFG& cfg = *context_->cfg();
120fd4e5da5Sopenharmony_ci
121fd4e5da5Sopenharmony_ci    if (!loop_iteration_count_) {
122fd4e5da5Sopenharmony_ci      return false;
123fd4e5da5Sopenharmony_ci    }
124fd4e5da5Sopenharmony_ci    if (!int_type_) {
125fd4e5da5Sopenharmony_ci      return false;
126fd4e5da5Sopenharmony_ci    }
127fd4e5da5Sopenharmony_ci    if (int_type_->width() != 32) {
128fd4e5da5Sopenharmony_ci      return false;
129fd4e5da5Sopenharmony_ci    }
130fd4e5da5Sopenharmony_ci    if (!loop_->IsLCSSA()) {
131fd4e5da5Sopenharmony_ci      return false;
132fd4e5da5Sopenharmony_ci    }
133fd4e5da5Sopenharmony_ci    if (!loop_->GetMergeBlock()) {
134fd4e5da5Sopenharmony_ci      return false;
135fd4e5da5Sopenharmony_ci    }
136fd4e5da5Sopenharmony_ci    if (cfg.preds(loop_->GetMergeBlock()->id()).size() != 1) {
137fd4e5da5Sopenharmony_ci      return false;
138fd4e5da5Sopenharmony_ci    }
139fd4e5da5Sopenharmony_ci    if (!IsConditionCheckSideEffectFree()) {
140fd4e5da5Sopenharmony_ci      return false;
141fd4e5da5Sopenharmony_ci    }
142fd4e5da5Sopenharmony_ci
143fd4e5da5Sopenharmony_ci    return !std::any_of(exit_value_.cbegin(), exit_value_.cend(),
144fd4e5da5Sopenharmony_ci                        [](std::pair<uint32_t, Instruction*> it) {
145fd4e5da5Sopenharmony_ci                          return it.second == nullptr;
146fd4e5da5Sopenharmony_ci                        });
147fd4e5da5Sopenharmony_ci  }
148fd4e5da5Sopenharmony_ci
149fd4e5da5Sopenharmony_ci  // Moves the execution of the |factor| first iterations of the loop into a
150fd4e5da5Sopenharmony_ci  // dedicated loop.
151fd4e5da5Sopenharmony_ci  void PeelBefore(uint32_t factor);
152fd4e5da5Sopenharmony_ci
153fd4e5da5Sopenharmony_ci  // Moves the execution of the |factor| last iterations of the loop into a
154fd4e5da5Sopenharmony_ci  // dedicated loop.
155fd4e5da5Sopenharmony_ci  void PeelAfter(uint32_t factor);
156fd4e5da5Sopenharmony_ci
157fd4e5da5Sopenharmony_ci  // Returns the cloned loop.
158fd4e5da5Sopenharmony_ci  Loop* GetClonedLoop() { return cloned_loop_; }
159fd4e5da5Sopenharmony_ci  // Returns the original loop.
160fd4e5da5Sopenharmony_ci  Loop* GetOriginalLoop() { return loop_; }
161fd4e5da5Sopenharmony_ci
162fd4e5da5Sopenharmony_ci private:
163fd4e5da5Sopenharmony_ci  IRContext* context_;
164fd4e5da5Sopenharmony_ci  LoopUtils loop_utils_;
165fd4e5da5Sopenharmony_ci  // The original loop.
166fd4e5da5Sopenharmony_ci  Loop* loop_;
167fd4e5da5Sopenharmony_ci  // The initial |loop_| upper bound.
168fd4e5da5Sopenharmony_ci  Instruction* loop_iteration_count_;
169fd4e5da5Sopenharmony_ci  // The int type to use for the canonical_induction_variable_.
170fd4e5da5Sopenharmony_ci  analysis::Integer* int_type_;
171fd4e5da5Sopenharmony_ci  // The cloned loop.
172fd4e5da5Sopenharmony_ci  Loop* cloned_loop_;
173fd4e5da5Sopenharmony_ci  // This is set to true when the exit and back-edge branch instruction is the
174fd4e5da5Sopenharmony_ci  // same.
175fd4e5da5Sopenharmony_ci  bool do_while_form_;
176fd4e5da5Sopenharmony_ci  // The canonical induction variable from the original loop if it exists.
177fd4e5da5Sopenharmony_ci  Instruction* original_loop_canonical_induction_variable_;
178fd4e5da5Sopenharmony_ci  // The canonical induction variable of the cloned loop. The induction variable
179fd4e5da5Sopenharmony_ci  // is initialized to 0 and incremented by step of 1.
180fd4e5da5Sopenharmony_ci  Instruction* canonical_induction_variable_;
181fd4e5da5Sopenharmony_ci  // Map between loop iterators and exit values. Loop iterators
182fd4e5da5Sopenharmony_ci  std::unordered_map<uint32_t, Instruction*> exit_value_;
183fd4e5da5Sopenharmony_ci
184fd4e5da5Sopenharmony_ci  // Duplicate |loop_| and place the new loop before the cloned loop. Iterating
185fd4e5da5Sopenharmony_ci  // values from the cloned loop are then connected to the original loop as
186fd4e5da5Sopenharmony_ci  // initializer.
187fd4e5da5Sopenharmony_ci  void DuplicateAndConnectLoop(LoopUtils::LoopCloningResult* clone_results);
188fd4e5da5Sopenharmony_ci
189fd4e5da5Sopenharmony_ci  // Insert the canonical induction variable into the first loop as a simplified
190fd4e5da5Sopenharmony_ci  // counter.
191fd4e5da5Sopenharmony_ci  void InsertCanonicalInductionVariable(
192fd4e5da5Sopenharmony_ci      LoopUtils::LoopCloningResult* clone_results);
193fd4e5da5Sopenharmony_ci
194fd4e5da5Sopenharmony_ci  // Fixes the exit condition of the before loop. The function calls
195fd4e5da5Sopenharmony_ci  // |condition_builder| to get the condition to use in the conditional branch
196fd4e5da5Sopenharmony_ci  // of the loop exit. The loop will be exited if the condition evaluate to
197fd4e5da5Sopenharmony_ci  // true. |condition_builder| takes an Instruction* that represent the
198fd4e5da5Sopenharmony_ci  // insertion point.
199fd4e5da5Sopenharmony_ci  void FixExitCondition(
200fd4e5da5Sopenharmony_ci      const std::function<uint32_t(Instruction*)>& condition_builder);
201fd4e5da5Sopenharmony_ci
202fd4e5da5Sopenharmony_ci  // Gathers all operations involved in the update of |iterator| into
203fd4e5da5Sopenharmony_ci  // |operations|.
204fd4e5da5Sopenharmony_ci  void GetIteratorUpdateOperations(
205fd4e5da5Sopenharmony_ci      const Loop* loop, Instruction* iterator,
206fd4e5da5Sopenharmony_ci      std::unordered_set<Instruction*>* operations);
207fd4e5da5Sopenharmony_ci
208fd4e5da5Sopenharmony_ci  // Gathers exiting iterator values. The function builds a map between each
209fd4e5da5Sopenharmony_ci  // iterating value in the loop (a phi instruction in the loop header) and its
210fd4e5da5Sopenharmony_ci  // SSA value when it exit the loop. If no exit value can be accurately found,
211fd4e5da5Sopenharmony_ci  // it is map to nullptr (see comment on CanPeelLoop).
212fd4e5da5Sopenharmony_ci  void GetIteratingExitValues();
213fd4e5da5Sopenharmony_ci
214fd4e5da5Sopenharmony_ci  // Returns true if a for-loop has no instruction with effects before the
215fd4e5da5Sopenharmony_ci  // condition check.
216fd4e5da5Sopenharmony_ci  bool IsConditionCheckSideEffectFree() const;
217fd4e5da5Sopenharmony_ci
218fd4e5da5Sopenharmony_ci  // Creates a new basic block and insert it between |bb| and the predecessor of
219fd4e5da5Sopenharmony_ci  // |bb|.
220fd4e5da5Sopenharmony_ci  BasicBlock* CreateBlockBefore(BasicBlock* bb);
221fd4e5da5Sopenharmony_ci
222fd4e5da5Sopenharmony_ci  // Inserts code to only execute |loop| only if the given |condition| is true.
223fd4e5da5Sopenharmony_ci  // |if_merge| is a suitable basic block to be used by the if condition as
224fd4e5da5Sopenharmony_ci  // merge block.
225fd4e5da5Sopenharmony_ci  // The function returns the if block protecting the loop.
226fd4e5da5Sopenharmony_ci  BasicBlock* ProtectLoop(Loop* loop, Instruction* condition,
227fd4e5da5Sopenharmony_ci                          BasicBlock* if_merge);
228fd4e5da5Sopenharmony_ci};
229fd4e5da5Sopenharmony_ci
230fd4e5da5Sopenharmony_ci// Implements a loop peeling optimization.
231fd4e5da5Sopenharmony_ci// For each loop, the pass will try to peel it if there is conditions that
232fd4e5da5Sopenharmony_ci// are true for the "N" first or last iterations of the loop.
233fd4e5da5Sopenharmony_ci// To avoid code size explosion, too large loops will not be peeled.
234fd4e5da5Sopenharmony_ciclass LoopPeelingPass : public Pass {
235fd4e5da5Sopenharmony_ci public:
236fd4e5da5Sopenharmony_ci  // Describes the peeling direction.
237fd4e5da5Sopenharmony_ci  enum class PeelDirection {
238fd4e5da5Sopenharmony_ci    kNone,    // Cannot peel
239fd4e5da5Sopenharmony_ci    kBefore,  // Can peel before
240fd4e5da5Sopenharmony_ci    kAfter    // Can peel last
241fd4e5da5Sopenharmony_ci  };
242fd4e5da5Sopenharmony_ci
243fd4e5da5Sopenharmony_ci  // Holds some statistics about peeled function.
244fd4e5da5Sopenharmony_ci  struct LoopPeelingStats {
245fd4e5da5Sopenharmony_ci    std::vector<std::tuple<const Loop*, PeelDirection, uint32_t>> peeled_loops_;
246fd4e5da5Sopenharmony_ci  };
247fd4e5da5Sopenharmony_ci
248fd4e5da5Sopenharmony_ci  LoopPeelingPass(LoopPeelingStats* stats = nullptr) : stats_(stats) {}
249fd4e5da5Sopenharmony_ci
250fd4e5da5Sopenharmony_ci  // Sets the loop peeling growth threshold. If the code size increase is above
251fd4e5da5Sopenharmony_ci  // |code_grow_threshold|, the loop will not be peeled. The code size is
252fd4e5da5Sopenharmony_ci  // measured in terms of SPIR-V instructions.
253fd4e5da5Sopenharmony_ci  static void SetLoopPeelingThreshold(size_t code_grow_threshold) {
254fd4e5da5Sopenharmony_ci    code_grow_threshold_ = code_grow_threshold;
255fd4e5da5Sopenharmony_ci  }
256fd4e5da5Sopenharmony_ci
257fd4e5da5Sopenharmony_ci  // Returns the loop peeling code growth threshold.
258fd4e5da5Sopenharmony_ci  static size_t GetLoopPeelingThreshold() { return code_grow_threshold_; }
259fd4e5da5Sopenharmony_ci
260fd4e5da5Sopenharmony_ci  const char* name() const override { return "loop-peeling"; }
261fd4e5da5Sopenharmony_ci
262fd4e5da5Sopenharmony_ci  // Processes the given |module|. Returns Status::Failure if errors occur when
263fd4e5da5Sopenharmony_ci  // processing. Returns the corresponding Status::Success if processing is
264fd4e5da5Sopenharmony_ci  // successful to indicate whether changes have been made to the module.
265fd4e5da5Sopenharmony_ci  Pass::Status Process() override;
266fd4e5da5Sopenharmony_ci
267fd4e5da5Sopenharmony_ci private:
268fd4e5da5Sopenharmony_ci  // Describes the peeling direction.
269fd4e5da5Sopenharmony_ci  enum class CmpOperator {
270fd4e5da5Sopenharmony_ci    kLT,  // less than
271fd4e5da5Sopenharmony_ci    kGT,  // greater than
272fd4e5da5Sopenharmony_ci    kLE,  // less than or equal
273fd4e5da5Sopenharmony_ci    kGE,  // greater than or equal
274fd4e5da5Sopenharmony_ci  };
275fd4e5da5Sopenharmony_ci
276fd4e5da5Sopenharmony_ci  class LoopPeelingInfo {
277fd4e5da5Sopenharmony_ci   public:
278fd4e5da5Sopenharmony_ci    using Direction = std::pair<PeelDirection, uint32_t>;
279fd4e5da5Sopenharmony_ci
280fd4e5da5Sopenharmony_ci    LoopPeelingInfo(Loop* loop, size_t loop_max_iterations,
281fd4e5da5Sopenharmony_ci                    ScalarEvolutionAnalysis* scev_analysis)
282fd4e5da5Sopenharmony_ci        : context_(loop->GetContext()),
283fd4e5da5Sopenharmony_ci          loop_(loop),
284fd4e5da5Sopenharmony_ci          scev_analysis_(scev_analysis),
285fd4e5da5Sopenharmony_ci          loop_max_iterations_(loop_max_iterations) {}
286fd4e5da5Sopenharmony_ci
287fd4e5da5Sopenharmony_ci    // Returns by how much and to which direction a loop should be peeled to
288fd4e5da5Sopenharmony_ci    // make the conditional branch of the basic block |bb| an unconditional
289fd4e5da5Sopenharmony_ci    // branch. If |bb|'s terminator is not a conditional branch or the condition
290fd4e5da5Sopenharmony_ci    // is not workable then it returns PeelDirection::kNone and a 0 factor.
291fd4e5da5Sopenharmony_ci    Direction GetPeelingInfo(BasicBlock* bb) const;
292fd4e5da5Sopenharmony_ci
293fd4e5da5Sopenharmony_ci   private:
294fd4e5da5Sopenharmony_ci    // Returns the id of the loop invariant operand of the conditional
295fd4e5da5Sopenharmony_ci    // expression |condition|. It returns if no operand is invariant.
296fd4e5da5Sopenharmony_ci    uint32_t GetFirstLoopInvariantOperand(Instruction* condition) const;
297fd4e5da5Sopenharmony_ci    // Returns the id of the non loop invariant operand of the conditional
298fd4e5da5Sopenharmony_ci    // expression |condition|. It returns if all operands are invariant.
299fd4e5da5Sopenharmony_ci    uint32_t GetFirstNonLoopInvariantOperand(Instruction* condition) const;
300fd4e5da5Sopenharmony_ci
301fd4e5da5Sopenharmony_ci    // Returns the value of |rec| at the first loop iteration.
302fd4e5da5Sopenharmony_ci    SExpression GetValueAtFirstIteration(SERecurrentNode* rec) const;
303fd4e5da5Sopenharmony_ci    // Returns the value of |rec| at the given |iteration|.
304fd4e5da5Sopenharmony_ci    SExpression GetValueAtIteration(SERecurrentNode* rec,
305fd4e5da5Sopenharmony_ci                                    int64_t iteration) const;
306fd4e5da5Sopenharmony_ci    // Returns the value of |rec| at the last loop iteration.
307fd4e5da5Sopenharmony_ci    SExpression GetValueAtLastIteration(SERecurrentNode* rec) const;
308fd4e5da5Sopenharmony_ci
309fd4e5da5Sopenharmony_ci    bool EvalOperator(CmpOperator cmp_op, SExpression lhs, SExpression rhs,
310fd4e5da5Sopenharmony_ci                      bool* result) const;
311fd4e5da5Sopenharmony_ci
312fd4e5da5Sopenharmony_ci    Direction HandleEquality(SExpression lhs, SExpression rhs) const;
313fd4e5da5Sopenharmony_ci    Direction HandleInequality(CmpOperator cmp_op, SExpression lhs,
314fd4e5da5Sopenharmony_ci                               SERecurrentNode* rhs) const;
315fd4e5da5Sopenharmony_ci
316fd4e5da5Sopenharmony_ci    static Direction GetNoneDirection() {
317fd4e5da5Sopenharmony_ci      return Direction{LoopPeelingPass::PeelDirection::kNone, 0};
318fd4e5da5Sopenharmony_ci    }
319fd4e5da5Sopenharmony_ci    IRContext* context_;
320fd4e5da5Sopenharmony_ci    Loop* loop_;
321fd4e5da5Sopenharmony_ci    ScalarEvolutionAnalysis* scev_analysis_;
322fd4e5da5Sopenharmony_ci    size_t loop_max_iterations_;
323fd4e5da5Sopenharmony_ci  };
324fd4e5da5Sopenharmony_ci  // Peel profitable loops in |f|.
325fd4e5da5Sopenharmony_ci  bool ProcessFunction(Function* f);
326fd4e5da5Sopenharmony_ci  // Peel |loop| if profitable.
327fd4e5da5Sopenharmony_ci  std::pair<bool, Loop*> ProcessLoop(Loop* loop, CodeMetrics* loop_size);
328fd4e5da5Sopenharmony_ci
329fd4e5da5Sopenharmony_ci  static size_t code_grow_threshold_;
330fd4e5da5Sopenharmony_ci  LoopPeelingStats* stats_;
331fd4e5da5Sopenharmony_ci};
332fd4e5da5Sopenharmony_ci
333fd4e5da5Sopenharmony_ci}  // namespace opt
334fd4e5da5Sopenharmony_ci}  // namespace spvtools
335fd4e5da5Sopenharmony_ci
336fd4e5da5Sopenharmony_ci#endif  // SOURCE_OPT_LOOP_PEELING_H_
337