11cb0ef41Sopenharmony_ci// Copyright 2014 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/graph-reducer.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include <functional>
81cb0ef41Sopenharmony_ci#include <limits>
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ci#include "src/codegen/tick-counter.h"
111cb0ef41Sopenharmony_ci#include "src/compiler/graph.h"
121cb0ef41Sopenharmony_ci#include "src/compiler/js-heap-broker.h"
131cb0ef41Sopenharmony_ci#include "src/compiler/node-observer.h"
141cb0ef41Sopenharmony_ci#include "src/compiler/node-properties.h"
151cb0ef41Sopenharmony_ci#include "src/compiler/node.h"
161cb0ef41Sopenharmony_ci#include "src/compiler/verifier.h"
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_cinamespace v8 {
191cb0ef41Sopenharmony_cinamespace internal {
201cb0ef41Sopenharmony_cinamespace compiler {
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_cienum class GraphReducer::State : uint8_t {
231cb0ef41Sopenharmony_ci  kUnvisited,
241cb0ef41Sopenharmony_ci  kRevisit,
251cb0ef41Sopenharmony_ci  kOnStack,
261cb0ef41Sopenharmony_ci  kVisited
271cb0ef41Sopenharmony_ci};
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_civoid Reducer::Finalize() {}
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_ciReduction Reducer::Reduce(Node* node,
331cb0ef41Sopenharmony_ci                          ObserveNodeManager* observe_node_manager) {
341cb0ef41Sopenharmony_ci  Reduction reduction = Reduce(node);
351cb0ef41Sopenharmony_ci  if (V8_UNLIKELY(observe_node_manager && reduction.Changed())) {
361cb0ef41Sopenharmony_ci    observe_node_manager->OnNodeChanged(reducer_name(), node,
371cb0ef41Sopenharmony_ci                                        reduction.replacement());
381cb0ef41Sopenharmony_ci  }
391cb0ef41Sopenharmony_ci  return reduction;
401cb0ef41Sopenharmony_ci}
411cb0ef41Sopenharmony_ci
421cb0ef41Sopenharmony_ciGraphReducer::GraphReducer(Zone* zone, Graph* graph, TickCounter* tick_counter,
431cb0ef41Sopenharmony_ci                           JSHeapBroker* broker, Node* dead,
441cb0ef41Sopenharmony_ci                           ObserveNodeManager* observe_node_manager)
451cb0ef41Sopenharmony_ci    : graph_(graph),
461cb0ef41Sopenharmony_ci      dead_(dead),
471cb0ef41Sopenharmony_ci      state_(graph, 4),
481cb0ef41Sopenharmony_ci      reducers_(zone),
491cb0ef41Sopenharmony_ci      revisit_(zone),
501cb0ef41Sopenharmony_ci      stack_(zone),
511cb0ef41Sopenharmony_ci      tick_counter_(tick_counter),
521cb0ef41Sopenharmony_ci      broker_(broker),
531cb0ef41Sopenharmony_ci      observe_node_manager_(observe_node_manager) {
541cb0ef41Sopenharmony_ci  if (dead != nullptr) {
551cb0ef41Sopenharmony_ci    NodeProperties::SetType(dead_, Type::None());
561cb0ef41Sopenharmony_ci  }
571cb0ef41Sopenharmony_ci}
581cb0ef41Sopenharmony_ci
591cb0ef41Sopenharmony_ciGraphReducer::~GraphReducer() = default;
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_civoid GraphReducer::AddReducer(Reducer* reducer) {
631cb0ef41Sopenharmony_ci  reducers_.push_back(reducer);
641cb0ef41Sopenharmony_ci}
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_civoid GraphReducer::ReduceNode(Node* node) {
681cb0ef41Sopenharmony_ci  DCHECK(stack_.empty());
691cb0ef41Sopenharmony_ci  DCHECK(revisit_.empty());
701cb0ef41Sopenharmony_ci  Push(node);
711cb0ef41Sopenharmony_ci  for (;;) {
721cb0ef41Sopenharmony_ci    if (!stack_.empty()) {
731cb0ef41Sopenharmony_ci      // Process the node on the top of the stack, potentially pushing more or
741cb0ef41Sopenharmony_ci      // popping the node off the stack.
751cb0ef41Sopenharmony_ci      ReduceTop();
761cb0ef41Sopenharmony_ci    } else if (!revisit_.empty()) {
771cb0ef41Sopenharmony_ci      // If the stack becomes empty, revisit any nodes in the revisit queue.
781cb0ef41Sopenharmony_ci      node = revisit_.front();
791cb0ef41Sopenharmony_ci      revisit_.pop();
801cb0ef41Sopenharmony_ci      if (state_.Get(node) == State::kRevisit) {
811cb0ef41Sopenharmony_ci        // state can change while in queue.
821cb0ef41Sopenharmony_ci        Push(node);
831cb0ef41Sopenharmony_ci      }
841cb0ef41Sopenharmony_ci    } else {
851cb0ef41Sopenharmony_ci      // Run all finalizers.
861cb0ef41Sopenharmony_ci      for (Reducer* const reducer : reducers_) reducer->Finalize();
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci      // Check if we have new nodes to revisit.
891cb0ef41Sopenharmony_ci      if (revisit_.empty()) break;
901cb0ef41Sopenharmony_ci    }
911cb0ef41Sopenharmony_ci  }
921cb0ef41Sopenharmony_ci  DCHECK(revisit_.empty());
931cb0ef41Sopenharmony_ci  DCHECK(stack_.empty());
941cb0ef41Sopenharmony_ci}
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_civoid GraphReducer::ReduceGraph() { ReduceNode(graph()->end()); }
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ciReduction GraphReducer::Reduce(Node* const node) {
1011cb0ef41Sopenharmony_ci  auto skip = reducers_.end();
1021cb0ef41Sopenharmony_ci  for (auto i = reducers_.begin(); i != reducers_.end();) {
1031cb0ef41Sopenharmony_ci    if (i != skip) {
1041cb0ef41Sopenharmony_ci      tick_counter_->TickAndMaybeEnterSafepoint();
1051cb0ef41Sopenharmony_ci      Reduction reduction = (*i)->Reduce(node, observe_node_manager_);
1061cb0ef41Sopenharmony_ci      if (!reduction.Changed()) {
1071cb0ef41Sopenharmony_ci        // No change from this reducer.
1081cb0ef41Sopenharmony_ci      } else if (reduction.replacement() == node) {
1091cb0ef41Sopenharmony_ci        // {replacement} == {node} represents an in-place reduction. Rerun
1101cb0ef41Sopenharmony_ci        // all the other reducers for this node, as now there may be more
1111cb0ef41Sopenharmony_ci        // opportunities for reduction.
1121cb0ef41Sopenharmony_ci        if (FLAG_trace_turbo_reduction) {
1131cb0ef41Sopenharmony_ci          UnparkedScopeIfNeeded unparked(broker_);
1141cb0ef41Sopenharmony_ci          // TODO(neis): Disallow racy handle dereference once we stop
1151cb0ef41Sopenharmony_ci          // supporting --no-local-heaps --no-concurrent-inlining.
1161cb0ef41Sopenharmony_ci          AllowHandleDereference allow_deref;
1171cb0ef41Sopenharmony_ci          StdoutStream{} << "- In-place update of #" << *node << " by reducer "
1181cb0ef41Sopenharmony_ci                         << (*i)->reducer_name() << std::endl;
1191cb0ef41Sopenharmony_ci        }
1201cb0ef41Sopenharmony_ci        skip = i;
1211cb0ef41Sopenharmony_ci        i = reducers_.begin();
1221cb0ef41Sopenharmony_ci        continue;
1231cb0ef41Sopenharmony_ci      } else {
1241cb0ef41Sopenharmony_ci        // {node} was replaced by another node.
1251cb0ef41Sopenharmony_ci        if (FLAG_trace_turbo_reduction) {
1261cb0ef41Sopenharmony_ci          UnparkedScopeIfNeeded unparked(broker_);
1271cb0ef41Sopenharmony_ci          // TODO(neis): Disallow racy handle dereference once we stop
1281cb0ef41Sopenharmony_ci          // supporting --no-local-heaps --no-concurrent-inlining.
1291cb0ef41Sopenharmony_ci          AllowHandleDereference allow_deref;
1301cb0ef41Sopenharmony_ci          StdoutStream{} << "- Replacement of #" << *node << " with #"
1311cb0ef41Sopenharmony_ci                         << *(reduction.replacement()) << " by reducer "
1321cb0ef41Sopenharmony_ci                         << (*i)->reducer_name() << std::endl;
1331cb0ef41Sopenharmony_ci        }
1341cb0ef41Sopenharmony_ci        return reduction;
1351cb0ef41Sopenharmony_ci      }
1361cb0ef41Sopenharmony_ci    }
1371cb0ef41Sopenharmony_ci    ++i;
1381cb0ef41Sopenharmony_ci  }
1391cb0ef41Sopenharmony_ci  if (skip == reducers_.end()) {
1401cb0ef41Sopenharmony_ci    // No change from any reducer.
1411cb0ef41Sopenharmony_ci    return Reducer::NoChange();
1421cb0ef41Sopenharmony_ci  }
1431cb0ef41Sopenharmony_ci  // At least one reducer did some in-place reduction.
1441cb0ef41Sopenharmony_ci  return Reducer::Changed(node);
1451cb0ef41Sopenharmony_ci}
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci
1481cb0ef41Sopenharmony_civoid GraphReducer::ReduceTop() {
1491cb0ef41Sopenharmony_ci  NodeState& entry = stack_.top();
1501cb0ef41Sopenharmony_ci  Node* node = entry.node;
1511cb0ef41Sopenharmony_ci  DCHECK_EQ(State::kOnStack, state_.Get(node));
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_ci  if (node->IsDead()) return Pop();  // Node was killed while on stack.
1541cb0ef41Sopenharmony_ci
1551cb0ef41Sopenharmony_ci  Node::Inputs node_inputs = node->inputs();
1561cb0ef41Sopenharmony_ci
1571cb0ef41Sopenharmony_ci  // Recurse on an input if necessary.
1581cb0ef41Sopenharmony_ci  int start = entry.input_index < node_inputs.count() ? entry.input_index : 0;
1591cb0ef41Sopenharmony_ci  for (int i = start; i < node_inputs.count(); ++i) {
1601cb0ef41Sopenharmony_ci    Node* input = node_inputs[i];
1611cb0ef41Sopenharmony_ci    if (input != node && Recurse(input)) {
1621cb0ef41Sopenharmony_ci      entry.input_index = i + 1;
1631cb0ef41Sopenharmony_ci      return;
1641cb0ef41Sopenharmony_ci    }
1651cb0ef41Sopenharmony_ci  }
1661cb0ef41Sopenharmony_ci  for (int i = 0; i < start; ++i) {
1671cb0ef41Sopenharmony_ci    Node* input = node_inputs[i];
1681cb0ef41Sopenharmony_ci    if (input != node && Recurse(input)) {
1691cb0ef41Sopenharmony_ci      entry.input_index = i + 1;
1701cb0ef41Sopenharmony_ci      return;
1711cb0ef41Sopenharmony_ci    }
1721cb0ef41Sopenharmony_ci  }
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci  // Remember the max node id before reduction.
1751cb0ef41Sopenharmony_ci  NodeId const max_id = static_cast<NodeId>(graph()->NodeCount() - 1);
1761cb0ef41Sopenharmony_ci
1771cb0ef41Sopenharmony_ci  // All inputs should be visited or on stack. Apply reductions to node.
1781cb0ef41Sopenharmony_ci  Reduction reduction = Reduce(node);
1791cb0ef41Sopenharmony_ci
1801cb0ef41Sopenharmony_ci  // If there was no reduction, pop {node} and continue.
1811cb0ef41Sopenharmony_ci  if (!reduction.Changed()) return Pop();
1821cb0ef41Sopenharmony_ci
1831cb0ef41Sopenharmony_ci  // Check if the reduction is an in-place update of the {node}.
1841cb0ef41Sopenharmony_ci  Node* const replacement = reduction.replacement();
1851cb0ef41Sopenharmony_ci  if (replacement == node) {
1861cb0ef41Sopenharmony_ci    for (Node* const user : node->uses()) {
1871cb0ef41Sopenharmony_ci      DCHECK_IMPLIES(user == node, state_.Get(node) != State::kVisited);
1881cb0ef41Sopenharmony_ci      Revisit(user);
1891cb0ef41Sopenharmony_ci    }
1901cb0ef41Sopenharmony_ci
1911cb0ef41Sopenharmony_ci    // In-place update of {node}, may need to recurse on an input.
1921cb0ef41Sopenharmony_ci    node_inputs = node->inputs();
1931cb0ef41Sopenharmony_ci    for (int i = 0; i < node_inputs.count(); ++i) {
1941cb0ef41Sopenharmony_ci      Node* input = node_inputs[i];
1951cb0ef41Sopenharmony_ci      if (input != node && Recurse(input)) {
1961cb0ef41Sopenharmony_ci        entry.input_index = i + 1;
1971cb0ef41Sopenharmony_ci        return;
1981cb0ef41Sopenharmony_ci      }
1991cb0ef41Sopenharmony_ci    }
2001cb0ef41Sopenharmony_ci  }
2011cb0ef41Sopenharmony_ci
2021cb0ef41Sopenharmony_ci  // After reducing the node, pop it off the stack.
2031cb0ef41Sopenharmony_ci  Pop();
2041cb0ef41Sopenharmony_ci
2051cb0ef41Sopenharmony_ci  // Check if we have a new replacement.
2061cb0ef41Sopenharmony_ci  if (replacement != node) {
2071cb0ef41Sopenharmony_ci    Replace(node, replacement, max_id);
2081cb0ef41Sopenharmony_ci  }
2091cb0ef41Sopenharmony_ci}
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci
2121cb0ef41Sopenharmony_civoid GraphReducer::Replace(Node* node, Node* replacement) {
2131cb0ef41Sopenharmony_ci  Replace(node, replacement, std::numeric_limits<NodeId>::max());
2141cb0ef41Sopenharmony_ci}
2151cb0ef41Sopenharmony_ci
2161cb0ef41Sopenharmony_ci
2171cb0ef41Sopenharmony_civoid GraphReducer::Replace(Node* node, Node* replacement, NodeId max_id) {
2181cb0ef41Sopenharmony_ci  if (node == graph()->start()) graph()->SetStart(replacement);
2191cb0ef41Sopenharmony_ci  if (node == graph()->end()) graph()->SetEnd(replacement);
2201cb0ef41Sopenharmony_ci  if (replacement->id() <= max_id) {
2211cb0ef41Sopenharmony_ci    // {replacement} is an old node, so unlink {node} and assume that
2221cb0ef41Sopenharmony_ci    // {replacement} was already reduced and finish.
2231cb0ef41Sopenharmony_ci    for (Edge edge : node->use_edges()) {
2241cb0ef41Sopenharmony_ci      Node* const user = edge.from();
2251cb0ef41Sopenharmony_ci      Verifier::VerifyEdgeInputReplacement(edge, replacement);
2261cb0ef41Sopenharmony_ci      edge.UpdateTo(replacement);
2271cb0ef41Sopenharmony_ci      // Don't revisit this node if it refers to itself.
2281cb0ef41Sopenharmony_ci      if (user != node) Revisit(user);
2291cb0ef41Sopenharmony_ci    }
2301cb0ef41Sopenharmony_ci    node->Kill();
2311cb0ef41Sopenharmony_ci  } else {
2321cb0ef41Sopenharmony_ci    // Replace all old uses of {node} with {replacement}, but allow new nodes
2331cb0ef41Sopenharmony_ci    // created by this reduction to use {node}.
2341cb0ef41Sopenharmony_ci    for (Edge edge : node->use_edges()) {
2351cb0ef41Sopenharmony_ci      Node* const user = edge.from();
2361cb0ef41Sopenharmony_ci      if (user->id() <= max_id) {
2371cb0ef41Sopenharmony_ci        edge.UpdateTo(replacement);
2381cb0ef41Sopenharmony_ci        // Don't revisit this node if it refers to itself.
2391cb0ef41Sopenharmony_ci        if (user != node) Revisit(user);
2401cb0ef41Sopenharmony_ci      }
2411cb0ef41Sopenharmony_ci    }
2421cb0ef41Sopenharmony_ci    // Unlink {node} if it's no longer used.
2431cb0ef41Sopenharmony_ci    if (node->uses().empty()) node->Kill();
2441cb0ef41Sopenharmony_ci
2451cb0ef41Sopenharmony_ci    // If there was a replacement, reduce it after popping {node}.
2461cb0ef41Sopenharmony_ci    Recurse(replacement);
2471cb0ef41Sopenharmony_ci  }
2481cb0ef41Sopenharmony_ci}
2491cb0ef41Sopenharmony_ci
2501cb0ef41Sopenharmony_ci
2511cb0ef41Sopenharmony_civoid GraphReducer::ReplaceWithValue(Node* node, Node* value, Node* effect,
2521cb0ef41Sopenharmony_ci                                    Node* control) {
2531cb0ef41Sopenharmony_ci  if (effect == nullptr && node->op()->EffectInputCount() > 0) {
2541cb0ef41Sopenharmony_ci    effect = NodeProperties::GetEffectInput(node);
2551cb0ef41Sopenharmony_ci  }
2561cb0ef41Sopenharmony_ci  if (control == nullptr && node->op()->ControlInputCount() > 0) {
2571cb0ef41Sopenharmony_ci    control = NodeProperties::GetControlInput(node);
2581cb0ef41Sopenharmony_ci  }
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_ci  // Requires distinguishing between value, effect and control edges.
2611cb0ef41Sopenharmony_ci  for (Edge edge : node->use_edges()) {
2621cb0ef41Sopenharmony_ci    Node* const user = edge.from();
2631cb0ef41Sopenharmony_ci    DCHECK(!user->IsDead());
2641cb0ef41Sopenharmony_ci    if (NodeProperties::IsControlEdge(edge)) {
2651cb0ef41Sopenharmony_ci      if (user->opcode() == IrOpcode::kIfSuccess) {
2661cb0ef41Sopenharmony_ci        Replace(user, control);
2671cb0ef41Sopenharmony_ci      } else if (user->opcode() == IrOpcode::kIfException) {
2681cb0ef41Sopenharmony_ci        DCHECK_NOT_NULL(dead_);
2691cb0ef41Sopenharmony_ci        edge.UpdateTo(dead_);
2701cb0ef41Sopenharmony_ci        Revisit(user);
2711cb0ef41Sopenharmony_ci      } else {
2721cb0ef41Sopenharmony_ci        DCHECK_NOT_NULL(control);
2731cb0ef41Sopenharmony_ci        edge.UpdateTo(control);
2741cb0ef41Sopenharmony_ci        Revisit(user);
2751cb0ef41Sopenharmony_ci      }
2761cb0ef41Sopenharmony_ci    } else if (NodeProperties::IsEffectEdge(edge)) {
2771cb0ef41Sopenharmony_ci      DCHECK_NOT_NULL(effect);
2781cb0ef41Sopenharmony_ci      edge.UpdateTo(effect);
2791cb0ef41Sopenharmony_ci      Revisit(user);
2801cb0ef41Sopenharmony_ci    } else {
2811cb0ef41Sopenharmony_ci      DCHECK_NOT_NULL(value);
2821cb0ef41Sopenharmony_ci      edge.UpdateTo(value);
2831cb0ef41Sopenharmony_ci      Revisit(user);
2841cb0ef41Sopenharmony_ci    }
2851cb0ef41Sopenharmony_ci  }
2861cb0ef41Sopenharmony_ci}
2871cb0ef41Sopenharmony_ci
2881cb0ef41Sopenharmony_ci
2891cb0ef41Sopenharmony_civoid GraphReducer::Pop() {
2901cb0ef41Sopenharmony_ci  Node* node = stack_.top().node;
2911cb0ef41Sopenharmony_ci  state_.Set(node, State::kVisited);
2921cb0ef41Sopenharmony_ci  stack_.pop();
2931cb0ef41Sopenharmony_ci}
2941cb0ef41Sopenharmony_ci
2951cb0ef41Sopenharmony_ci
2961cb0ef41Sopenharmony_civoid GraphReducer::Push(Node* const node) {
2971cb0ef41Sopenharmony_ci  DCHECK_NE(State::kOnStack, state_.Get(node));
2981cb0ef41Sopenharmony_ci  state_.Set(node, State::kOnStack);
2991cb0ef41Sopenharmony_ci  stack_.push({node, 0});
3001cb0ef41Sopenharmony_ci}
3011cb0ef41Sopenharmony_ci
3021cb0ef41Sopenharmony_ci
3031cb0ef41Sopenharmony_cibool GraphReducer::Recurse(Node* node) {
3041cb0ef41Sopenharmony_ci  if (state_.Get(node) > State::kRevisit) return false;
3051cb0ef41Sopenharmony_ci  Push(node);
3061cb0ef41Sopenharmony_ci  return true;
3071cb0ef41Sopenharmony_ci}
3081cb0ef41Sopenharmony_ci
3091cb0ef41Sopenharmony_ci
3101cb0ef41Sopenharmony_civoid GraphReducer::Revisit(Node* node) {
3111cb0ef41Sopenharmony_ci  if (state_.Get(node) == State::kVisited) {
3121cb0ef41Sopenharmony_ci    state_.Set(node, State::kRevisit);
3131cb0ef41Sopenharmony_ci    revisit_.push(node);
3141cb0ef41Sopenharmony_ci  }
3151cb0ef41Sopenharmony_ci}
3161cb0ef41Sopenharmony_ci
3171cb0ef41Sopenharmony_ci}  // namespace compiler
3181cb0ef41Sopenharmony_ci}  // namespace internal
3191cb0ef41Sopenharmony_ci}  // namespace v8
320