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