11cb0ef41Sopenharmony_ci// Copyright 2015 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/compiler/loop-peeling.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include "src/compiler/common-operator.h"
81cb0ef41Sopenharmony_ci#include "src/compiler/compiler-source-position-table.h"
91cb0ef41Sopenharmony_ci#include "src/compiler/graph.h"
101cb0ef41Sopenharmony_ci#include "src/compiler/loop-analysis.h"
111cb0ef41Sopenharmony_ci#include "src/compiler/node-marker.h"
121cb0ef41Sopenharmony_ci#include "src/compiler/node-origin-table.h"
131cb0ef41Sopenharmony_ci#include "src/compiler/node-properties.h"
141cb0ef41Sopenharmony_ci#include "src/compiler/node.h"
151cb0ef41Sopenharmony_ci#include "src/zone/zone.h"
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ci// Loop peeling is an optimization that copies the body of a loop, creating
181cb0ef41Sopenharmony_ci// a new copy of the body called the "peeled iteration" that represents the
191cb0ef41Sopenharmony_ci// first iteration. Beginning with a loop as follows:
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ci//             E
221cb0ef41Sopenharmony_ci//             |                 A
231cb0ef41Sopenharmony_ci//             |                 |                     (backedges)
241cb0ef41Sopenharmony_ci//             | +---------------|---------------------------------+
251cb0ef41Sopenharmony_ci//             | | +-------------|-------------------------------+ |
261cb0ef41Sopenharmony_ci//             | | |             | +--------+                    | |
271cb0ef41Sopenharmony_ci//             | | |             | | +----+ |                    | |
281cb0ef41Sopenharmony_ci//             | | |             | | |    | |                    | |
291cb0ef41Sopenharmony_ci//           ( Loop )<-------- ( phiA )   | |                    | |
301cb0ef41Sopenharmony_ci//              |                 |       | |                    | |
311cb0ef41Sopenharmony_ci//      ((======P=================U=======|=|=====))             | |
321cb0ef41Sopenharmony_ci//      ((                                | |     ))             | |
331cb0ef41Sopenharmony_ci//      ((        X <---------------------+ |     ))             | |
341cb0ef41Sopenharmony_ci//      ((                                  |     ))             | |
351cb0ef41Sopenharmony_ci//      ((     body                         |     ))             | |
361cb0ef41Sopenharmony_ci//      ((                                  |     ))             | |
371cb0ef41Sopenharmony_ci//      ((        Y <-----------------------+     ))             | |
381cb0ef41Sopenharmony_ci//      ((                                        ))             | |
391cb0ef41Sopenharmony_ci//      ((===K====L====M==========================))             | |
401cb0ef41Sopenharmony_ci//           |    |    |                                         | |
411cb0ef41Sopenharmony_ci//           |    |    +-----------------------------------------+ |
421cb0ef41Sopenharmony_ci//           |    +------------------------------------------------+
431cb0ef41Sopenharmony_ci//           |
441cb0ef41Sopenharmony_ci//          exit
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_ci// The body of the loop is duplicated so that all nodes considered "inside"
471cb0ef41Sopenharmony_ci// the loop (e.g. {P, U, X, Y, K, L, M}) have a corresponding copies in the
481cb0ef41Sopenharmony_ci// peeled iteration (e.g. {P', U', X', Y', K', L', M'}). What were considered
491cb0ef41Sopenharmony_ci// backedges of the loop correspond to edges from the peeled iteration to
501cb0ef41Sopenharmony_ci// the main loop body, with multiple backedges requiring a merge.
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ci// Similarly, any exits from the loop body need to be merged with "exits"
531cb0ef41Sopenharmony_ci// from the peeled iteration, resulting in the graph as follows:
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci//             E
561cb0ef41Sopenharmony_ci//             |                 A
571cb0ef41Sopenharmony_ci//             |                 |
581cb0ef41Sopenharmony_ci//      ((=====P'================U'===============))
591cb0ef41Sopenharmony_ci//      ((                                        ))
601cb0ef41Sopenharmony_ci//      ((        X'<-------------+               ))
611cb0ef41Sopenharmony_ci//      ((                        |               ))
621cb0ef41Sopenharmony_ci//      ((   peeled iteration     |               ))
631cb0ef41Sopenharmony_ci//      ((                        |               ))
641cb0ef41Sopenharmony_ci//      ((        Y'<-----------+ |               ))
651cb0ef41Sopenharmony_ci//      ((                      | |               ))
661cb0ef41Sopenharmony_ci//      ((===K'===L'====M'======|=|===============))
671cb0ef41Sopenharmony_ci//           |    |     |       | |
681cb0ef41Sopenharmony_ci//  +--------+    +-+ +-+       | |
691cb0ef41Sopenharmony_ci//  |               | |         | |
701cb0ef41Sopenharmony_ci//  |              Merge <------phi
711cb0ef41Sopenharmony_ci//  |                |           |
721cb0ef41Sopenharmony_ci//  |          +-----+           |
731cb0ef41Sopenharmony_ci//  |          |                 |                     (backedges)
741cb0ef41Sopenharmony_ci//  |          | +---------------|---------------------------------+
751cb0ef41Sopenharmony_ci//  |          | | +-------------|-------------------------------+ |
761cb0ef41Sopenharmony_ci//  |          | | |             | +--------+                    | |
771cb0ef41Sopenharmony_ci//  |          | | |             | | +----+ |                    | |
781cb0ef41Sopenharmony_ci//  |          | | |             | | |    | |                    | |
791cb0ef41Sopenharmony_ci//  |        ( Loop )<-------- ( phiA )   | |                    | |
801cb0ef41Sopenharmony_ci//  |           |                 |       | |                    | |
811cb0ef41Sopenharmony_ci//  |   ((======P=================U=======|=|=====))             | |
821cb0ef41Sopenharmony_ci//  |   ((                                | |     ))             | |
831cb0ef41Sopenharmony_ci//  |   ((        X <---------------------+ |     ))             | |
841cb0ef41Sopenharmony_ci//  |   ((                                  |     ))             | |
851cb0ef41Sopenharmony_ci//  |   ((     body                         |     ))             | |
861cb0ef41Sopenharmony_ci//  |   ((                                  |     ))             | |
871cb0ef41Sopenharmony_ci//  |   ((        Y <-----------------------+     ))             | |
881cb0ef41Sopenharmony_ci//  |   ((                                        ))             | |
891cb0ef41Sopenharmony_ci//  |   ((===K====L====M==========================))             | |
901cb0ef41Sopenharmony_ci//  |        |    |    |                                         | |
911cb0ef41Sopenharmony_ci//  |        |    |    +-----------------------------------------+ |
921cb0ef41Sopenharmony_ci//  |        |    +------------------------------------------------+
931cb0ef41Sopenharmony_ci//  |        |
941cb0ef41Sopenharmony_ci//  |        |
951cb0ef41Sopenharmony_ci//  +----+ +-+
961cb0ef41Sopenharmony_ci//       | |
971cb0ef41Sopenharmony_ci//      Merge
981cb0ef41Sopenharmony_ci//        |
991cb0ef41Sopenharmony_ci//      exit
1001cb0ef41Sopenharmony_ci
1011cb0ef41Sopenharmony_ci// Note that the boxes ((===)) above are not explicitly represented in the
1021cb0ef41Sopenharmony_ci// graph, but are instead computed by the {LoopFinder}.
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_cinamespace v8 {
1051cb0ef41Sopenharmony_cinamespace internal {
1061cb0ef41Sopenharmony_cinamespace compiler {
1071cb0ef41Sopenharmony_ci
1081cb0ef41Sopenharmony_ciclass PeeledIterationImpl : public PeeledIteration {
1091cb0ef41Sopenharmony_ci public:
1101cb0ef41Sopenharmony_ci  NodeVector node_pairs_;
1111cb0ef41Sopenharmony_ci  explicit PeeledIterationImpl(Zone* zone) : node_pairs_(zone) {}
1121cb0ef41Sopenharmony_ci};
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci
1151cb0ef41Sopenharmony_ciNode* PeeledIteration::map(Node* node) {
1161cb0ef41Sopenharmony_ci  // TODO(turbofan): we use a simple linear search, since the peeled iteration
1171cb0ef41Sopenharmony_ci  // is really only used in testing.
1181cb0ef41Sopenharmony_ci  PeeledIterationImpl* impl = static_cast<PeeledIterationImpl*>(this);
1191cb0ef41Sopenharmony_ci  for (size_t i = 0; i < impl->node_pairs_.size(); i += 2) {
1201cb0ef41Sopenharmony_ci    if (impl->node_pairs_[i] == node) return impl->node_pairs_[i + 1];
1211cb0ef41Sopenharmony_ci  }
1221cb0ef41Sopenharmony_ci  return node;
1231cb0ef41Sopenharmony_ci}
1241cb0ef41Sopenharmony_ci
1251cb0ef41Sopenharmony_ciPeeledIteration* LoopPeeler::Peel(LoopTree::Loop* loop) {
1261cb0ef41Sopenharmony_ci  if (!CanPeel(loop)) return nullptr;
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_ci  //============================================================================
1291cb0ef41Sopenharmony_ci  // Construct the peeled iteration.
1301cb0ef41Sopenharmony_ci  //============================================================================
1311cb0ef41Sopenharmony_ci  PeeledIterationImpl* iter = tmp_zone_->New<PeeledIterationImpl>(tmp_zone_);
1321cb0ef41Sopenharmony_ci  uint32_t estimated_peeled_size = 5 + loop->TotalSize() * 2;
1331cb0ef41Sopenharmony_ci  NodeCopier copier(graph_, estimated_peeled_size, &iter->node_pairs_, 1);
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci  Node* dead = graph_->NewNode(common_->Dead());
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ci  // Map the loop header nodes to their entry values.
1381cb0ef41Sopenharmony_ci  for (Node* node : loop_tree_->HeaderNodes(loop)) {
1391cb0ef41Sopenharmony_ci    copier.Insert(node, node->InputAt(kAssumedLoopEntryIndex));
1401cb0ef41Sopenharmony_ci  }
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_ci  // Copy all the nodes of loop body for the peeled iteration.
1431cb0ef41Sopenharmony_ci  copier.CopyNodes(graph_, tmp_zone_, dead, loop_tree_->BodyNodes(loop),
1441cb0ef41Sopenharmony_ci                   source_positions_, node_origins_);
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci  //============================================================================
1471cb0ef41Sopenharmony_ci  // Replace the entry to the loop with the output of the peeled iteration.
1481cb0ef41Sopenharmony_ci  //============================================================================
1491cb0ef41Sopenharmony_ci  Node* loop_node = loop_tree_->GetLoopControl(loop);
1501cb0ef41Sopenharmony_ci  Node* new_entry;
1511cb0ef41Sopenharmony_ci  int backedges = loop_node->InputCount() - 1;
1521cb0ef41Sopenharmony_ci  if (backedges > 1) {
1531cb0ef41Sopenharmony_ci    // Multiple backedges from original loop, therefore multiple output edges
1541cb0ef41Sopenharmony_ci    // from the peeled iteration.
1551cb0ef41Sopenharmony_ci    NodeVector inputs(tmp_zone_);
1561cb0ef41Sopenharmony_ci    for (int i = 1; i < loop_node->InputCount(); i++) {
1571cb0ef41Sopenharmony_ci      inputs.push_back(copier.map(loop_node->InputAt(i)));
1581cb0ef41Sopenharmony_ci    }
1591cb0ef41Sopenharmony_ci    Node* merge =
1601cb0ef41Sopenharmony_ci        graph_->NewNode(common_->Merge(backedges), backedges, &inputs[0]);
1611cb0ef41Sopenharmony_ci
1621cb0ef41Sopenharmony_ci    // Merge values from the multiple output edges of the peeled iteration.
1631cb0ef41Sopenharmony_ci    for (Node* node : loop_tree_->HeaderNodes(loop)) {
1641cb0ef41Sopenharmony_ci      if (node->opcode() == IrOpcode::kLoop) continue;  // already done.
1651cb0ef41Sopenharmony_ci      inputs.clear();
1661cb0ef41Sopenharmony_ci      for (int i = 0; i < backedges; i++) {
1671cb0ef41Sopenharmony_ci        inputs.push_back(copier.map(node->InputAt(1 + i)));
1681cb0ef41Sopenharmony_ci      }
1691cb0ef41Sopenharmony_ci      for (Node* input : inputs) {
1701cb0ef41Sopenharmony_ci        if (input != inputs[0]) {  // Non-redundant phi.
1711cb0ef41Sopenharmony_ci          inputs.push_back(merge);
1721cb0ef41Sopenharmony_ci          const Operator* op = common_->ResizeMergeOrPhi(node->op(), backedges);
1731cb0ef41Sopenharmony_ci          Node* phi = graph_->NewNode(op, backedges + 1, &inputs[0]);
1741cb0ef41Sopenharmony_ci          node->ReplaceInput(0, phi);
1751cb0ef41Sopenharmony_ci          break;
1761cb0ef41Sopenharmony_ci        }
1771cb0ef41Sopenharmony_ci      }
1781cb0ef41Sopenharmony_ci    }
1791cb0ef41Sopenharmony_ci    new_entry = merge;
1801cb0ef41Sopenharmony_ci  } else {
1811cb0ef41Sopenharmony_ci    // Only one backedge, simply replace the input to loop with output of
1821cb0ef41Sopenharmony_ci    // peeling.
1831cb0ef41Sopenharmony_ci    for (Node* node : loop_tree_->HeaderNodes(loop)) {
1841cb0ef41Sopenharmony_ci      node->ReplaceInput(0, copier.map(node->InputAt(1)));
1851cb0ef41Sopenharmony_ci    }
1861cb0ef41Sopenharmony_ci    new_entry = copier.map(loop_node->InputAt(1));
1871cb0ef41Sopenharmony_ci  }
1881cb0ef41Sopenharmony_ci  loop_node->ReplaceInput(0, new_entry);
1891cb0ef41Sopenharmony_ci
1901cb0ef41Sopenharmony_ci  //============================================================================
1911cb0ef41Sopenharmony_ci  // Change the exit and exit markers to merge/phi/effect-phi.
1921cb0ef41Sopenharmony_ci  //============================================================================
1931cb0ef41Sopenharmony_ci  for (Node* exit : loop_tree_->ExitNodes(loop)) {
1941cb0ef41Sopenharmony_ci    switch (exit->opcode()) {
1951cb0ef41Sopenharmony_ci      case IrOpcode::kLoopExit:
1961cb0ef41Sopenharmony_ci        // Change the loop exit node to a merge node.
1971cb0ef41Sopenharmony_ci        exit->ReplaceInput(1, copier.map(exit->InputAt(0)));
1981cb0ef41Sopenharmony_ci        NodeProperties::ChangeOp(exit, common_->Merge(2));
1991cb0ef41Sopenharmony_ci        break;
2001cb0ef41Sopenharmony_ci      case IrOpcode::kLoopExitValue:
2011cb0ef41Sopenharmony_ci        // Change exit marker to phi.
2021cb0ef41Sopenharmony_ci        exit->InsertInput(graph_->zone(), 1, copier.map(exit->InputAt(0)));
2031cb0ef41Sopenharmony_ci        NodeProperties::ChangeOp(
2041cb0ef41Sopenharmony_ci            exit, common_->Phi(LoopExitValueRepresentationOf(exit->op()), 2));
2051cb0ef41Sopenharmony_ci        break;
2061cb0ef41Sopenharmony_ci      case IrOpcode::kLoopExitEffect:
2071cb0ef41Sopenharmony_ci        // Change effect exit marker to effect phi.
2081cb0ef41Sopenharmony_ci        exit->InsertInput(graph_->zone(), 1, copier.map(exit->InputAt(0)));
2091cb0ef41Sopenharmony_ci        NodeProperties::ChangeOp(exit, common_->EffectPhi(2));
2101cb0ef41Sopenharmony_ci        break;
2111cb0ef41Sopenharmony_ci      default:
2121cb0ef41Sopenharmony_ci        break;
2131cb0ef41Sopenharmony_ci    }
2141cb0ef41Sopenharmony_ci  }
2151cb0ef41Sopenharmony_ci  return iter;
2161cb0ef41Sopenharmony_ci}
2171cb0ef41Sopenharmony_ci
2181cb0ef41Sopenharmony_civoid LoopPeeler::PeelInnerLoops(LoopTree::Loop* loop) {
2191cb0ef41Sopenharmony_ci  // If the loop has nested loops, peel inside those.
2201cb0ef41Sopenharmony_ci  if (!loop->children().empty()) {
2211cb0ef41Sopenharmony_ci    for (LoopTree::Loop* inner_loop : loop->children()) {
2221cb0ef41Sopenharmony_ci      PeelInnerLoops(inner_loop);
2231cb0ef41Sopenharmony_ci    }
2241cb0ef41Sopenharmony_ci    return;
2251cb0ef41Sopenharmony_ci  }
2261cb0ef41Sopenharmony_ci  // Only peel small-enough loops.
2271cb0ef41Sopenharmony_ci  if (loop->TotalSize() > LoopPeeler::kMaxPeeledNodes) return;
2281cb0ef41Sopenharmony_ci  if (FLAG_trace_turbo_loop) {
2291cb0ef41Sopenharmony_ci    PrintF("Peeling loop with header: ");
2301cb0ef41Sopenharmony_ci    for (Node* node : loop_tree_->HeaderNodes(loop)) {
2311cb0ef41Sopenharmony_ci      PrintF("%i ", node->id());
2321cb0ef41Sopenharmony_ci    }
2331cb0ef41Sopenharmony_ci    PrintF("\n");
2341cb0ef41Sopenharmony_ci  }
2351cb0ef41Sopenharmony_ci
2361cb0ef41Sopenharmony_ci  Peel(loop);
2371cb0ef41Sopenharmony_ci}
2381cb0ef41Sopenharmony_ci
2391cb0ef41Sopenharmony_civoid LoopPeeler::EliminateLoopExit(Node* node) {
2401cb0ef41Sopenharmony_ci  DCHECK_EQ(IrOpcode::kLoopExit, node->opcode());
2411cb0ef41Sopenharmony_ci  // The exit markers take the loop exit as input. We iterate over uses
2421cb0ef41Sopenharmony_ci  // and remove all the markers from the graph.
2431cb0ef41Sopenharmony_ci  for (Edge edge : node->use_edges()) {
2441cb0ef41Sopenharmony_ci    if (NodeProperties::IsControlEdge(edge)) {
2451cb0ef41Sopenharmony_ci      Node* marker = edge.from();
2461cb0ef41Sopenharmony_ci      if (marker->opcode() == IrOpcode::kLoopExitValue) {
2471cb0ef41Sopenharmony_ci        NodeProperties::ReplaceUses(marker, marker->InputAt(0));
2481cb0ef41Sopenharmony_ci        marker->Kill();
2491cb0ef41Sopenharmony_ci      } else if (marker->opcode() == IrOpcode::kLoopExitEffect) {
2501cb0ef41Sopenharmony_ci        NodeProperties::ReplaceUses(marker, nullptr,
2511cb0ef41Sopenharmony_ci                                    NodeProperties::GetEffectInput(marker));
2521cb0ef41Sopenharmony_ci        marker->Kill();
2531cb0ef41Sopenharmony_ci      }
2541cb0ef41Sopenharmony_ci    }
2551cb0ef41Sopenharmony_ci  }
2561cb0ef41Sopenharmony_ci  NodeProperties::ReplaceUses(node, nullptr, nullptr,
2571cb0ef41Sopenharmony_ci                              NodeProperties::GetControlInput(node, 0));
2581cb0ef41Sopenharmony_ci  node->Kill();
2591cb0ef41Sopenharmony_ci}
2601cb0ef41Sopenharmony_ci
2611cb0ef41Sopenharmony_civoid LoopPeeler::PeelInnerLoopsOfTree() {
2621cb0ef41Sopenharmony_ci  for (LoopTree::Loop* loop : loop_tree_->outer_loops()) {
2631cb0ef41Sopenharmony_ci    PeelInnerLoops(loop);
2641cb0ef41Sopenharmony_ci  }
2651cb0ef41Sopenharmony_ci
2661cb0ef41Sopenharmony_ci  EliminateLoopExits(graph_, tmp_zone_);
2671cb0ef41Sopenharmony_ci}
2681cb0ef41Sopenharmony_ci
2691cb0ef41Sopenharmony_ci// static
2701cb0ef41Sopenharmony_civoid LoopPeeler::EliminateLoopExits(Graph* graph, Zone* tmp_zone) {
2711cb0ef41Sopenharmony_ci  ZoneQueue<Node*> queue(tmp_zone);
2721cb0ef41Sopenharmony_ci  ZoneVector<bool> visited(graph->NodeCount(), false, tmp_zone);
2731cb0ef41Sopenharmony_ci  queue.push(graph->end());
2741cb0ef41Sopenharmony_ci  while (!queue.empty()) {
2751cb0ef41Sopenharmony_ci    Node* node = queue.front();
2761cb0ef41Sopenharmony_ci    queue.pop();
2771cb0ef41Sopenharmony_ci
2781cb0ef41Sopenharmony_ci    if (node->opcode() == IrOpcode::kLoopExit) {
2791cb0ef41Sopenharmony_ci      Node* control = NodeProperties::GetControlInput(node);
2801cb0ef41Sopenharmony_ci      EliminateLoopExit(node);
2811cb0ef41Sopenharmony_ci      if (!visited[control->id()]) {
2821cb0ef41Sopenharmony_ci        visited[control->id()] = true;
2831cb0ef41Sopenharmony_ci        queue.push(control);
2841cb0ef41Sopenharmony_ci      }
2851cb0ef41Sopenharmony_ci    } else {
2861cb0ef41Sopenharmony_ci      for (int i = 0; i < node->op()->ControlInputCount(); i++) {
2871cb0ef41Sopenharmony_ci        Node* control = NodeProperties::GetControlInput(node, i);
2881cb0ef41Sopenharmony_ci        if (!visited[control->id()]) {
2891cb0ef41Sopenharmony_ci          visited[control->id()] = true;
2901cb0ef41Sopenharmony_ci          queue.push(control);
2911cb0ef41Sopenharmony_ci        }
2921cb0ef41Sopenharmony_ci      }
2931cb0ef41Sopenharmony_ci    }
2941cb0ef41Sopenharmony_ci  }
2951cb0ef41Sopenharmony_ci}
2961cb0ef41Sopenharmony_ci
2971cb0ef41Sopenharmony_ci}  // namespace compiler
2981cb0ef41Sopenharmony_ci}  // namespace internal
2991cb0ef41Sopenharmony_ci}  // namespace v8
300