2014-07-30 13:54:45 +00:00
|
|
|
// Copyright 2014 the V8 project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
#include <functional>
|
2015-05-06 10:12:47 +00:00
|
|
|
#include <limits>
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2015-01-26 18:35:04 +00:00
|
|
|
#include "src/compiler/graph.h"
|
|
|
|
#include "src/compiler/graph-reducer.h"
|
|
|
|
#include "src/compiler/node.h"
|
2015-05-12 12:41:41 +00:00
|
|
|
#include "src/compiler/node-properties.h"
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace compiler {
|
|
|
|
|
2015-05-06 10:12:47 +00:00
|
|
|
bool Reducer::Finish() { return true; }
|
|
|
|
|
|
|
|
|
2014-11-17 12:12:24 +00:00
|
|
|
enum class GraphReducer::State : uint8_t {
|
|
|
|
kUnvisited,
|
|
|
|
kRevisit,
|
|
|
|
kOnStack,
|
|
|
|
kVisited
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-06-05 12:01:55 +00:00
|
|
|
GraphReducer::GraphReducer(Zone* zone, Graph* graph, Node* dead_value,
|
|
|
|
Node* dead_control)
|
2014-11-17 12:12:24 +00:00
|
|
|
: graph_(graph),
|
2015-06-05 12:01:55 +00:00
|
|
|
dead_value_(dead_value),
|
|
|
|
dead_control_(dead_control),
|
2014-11-28 13:04:49 +00:00
|
|
|
state_(graph, 4),
|
2014-11-17 12:12:24 +00:00
|
|
|
reducers_(zone),
|
|
|
|
revisit_(zone),
|
2014-11-28 13:04:49 +00:00
|
|
|
stack_(zone) {}
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
|
2015-05-06 10:12:47 +00:00
|
|
|
GraphReducer::~GraphReducer() {}
|
|
|
|
|
|
|
|
|
2014-11-17 12:12:24 +00:00
|
|
|
void GraphReducer::AddReducer(Reducer* reducer) {
|
|
|
|
reducers_.push_back(reducer);
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GraphReducer::ReduceNode(Node* node) {
|
2014-11-17 12:12:24 +00:00
|
|
|
DCHECK(stack_.empty());
|
|
|
|
DCHECK(revisit_.empty());
|
|
|
|
Push(node);
|
|
|
|
for (;;) {
|
|
|
|
if (!stack_.empty()) {
|
|
|
|
// Process the node on the top of the stack, potentially pushing more or
|
|
|
|
// popping the node off the stack.
|
|
|
|
ReduceTop();
|
|
|
|
} else if (!revisit_.empty()) {
|
|
|
|
// If the stack becomes empty, revisit any nodes in the revisit queue.
|
|
|
|
Node* const node = revisit_.top();
|
|
|
|
revisit_.pop();
|
2014-11-28 13:04:49 +00:00
|
|
|
if (state_.Get(node) == State::kRevisit) {
|
2014-11-17 12:12:24 +00:00
|
|
|
// state can change while in queue.
|
|
|
|
Push(node);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DCHECK(revisit_.empty());
|
|
|
|
DCHECK(stack_.empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-06 10:12:47 +00:00
|
|
|
void GraphReducer::ReduceGraph() {
|
|
|
|
for (;;) {
|
|
|
|
ReduceNode(graph()->end());
|
|
|
|
// TODO(turbofan): Remove this once the dead node trimming is in the
|
|
|
|
// GraphReducer.
|
|
|
|
bool done = true;
|
|
|
|
for (Reducer* const reducer : reducers_) {
|
|
|
|
if (!reducer->Finish()) {
|
|
|
|
done = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (done) break;
|
|
|
|
// Reset all marks on the graph in preparation to re-reduce the graph.
|
|
|
|
state_.Reset(graph());
|
|
|
|
}
|
|
|
|
}
|
2014-11-17 12:12:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
Reduction GraphReducer::Reduce(Node* const node) {
|
|
|
|
auto skip = reducers_.end();
|
|
|
|
for (auto i = reducers_.begin(); i != reducers_.end();) {
|
|
|
|
if (i != skip) {
|
2014-07-30 13:54:45 +00:00
|
|
|
Reduction reduction = (*i)->Reduce(node);
|
2014-11-17 12:12:24 +00:00
|
|
|
if (!reduction.Changed()) {
|
2014-07-30 13:54:45 +00:00
|
|
|
// No change from this reducer.
|
2014-11-17 12:12:24 +00:00
|
|
|
} else if (reduction.replacement() == node) {
|
|
|
|
// {replacement} == {node} represents an in-place reduction. Rerun
|
|
|
|
// all the other reducers for this node, as now there may be more
|
2014-09-26 06:40:07 +00:00
|
|
|
// opportunities for reduction.
|
2014-11-17 12:12:24 +00:00
|
|
|
skip = i;
|
|
|
|
i = reducers_.begin();
|
|
|
|
continue;
|
2014-07-30 13:54:45 +00:00
|
|
|
} else {
|
2014-11-17 12:12:24 +00:00
|
|
|
// {node} was replaced by another node.
|
|
|
|
return reduction;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
if (skip == reducers_.end()) {
|
|
|
|
// No change from any reducer.
|
|
|
|
return Reducer::NoChange();
|
|
|
|
}
|
|
|
|
// At least one reducer did some in-place reduction.
|
|
|
|
return Reducer::Changed(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GraphReducer::ReduceTop() {
|
2014-11-27 16:24:08 +00:00
|
|
|
NodeState& entry = stack_.top();
|
|
|
|
Node* node = entry.node;
|
2014-11-28 13:04:49 +00:00
|
|
|
DCHECK(state_.Get(node) == State::kOnStack);
|
2014-11-27 16:24:08 +00:00
|
|
|
|
2014-11-17 12:12:24 +00:00
|
|
|
if (node->IsDead()) return Pop(); // Node was killed while on stack.
|
|
|
|
|
|
|
|
// Recurse on an input if necessary.
|
2014-11-27 16:24:08 +00:00
|
|
|
int start = entry.input_index < node->InputCount() ? entry.input_index : 0;
|
|
|
|
for (int i = start; i < node->InputCount(); i++) {
|
|
|
|
Node* input = node->InputAt(i);
|
|
|
|
entry.input_index = i + 1;
|
|
|
|
if (input != node && Recurse(input)) return;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < start; i++) {
|
|
|
|
Node* input = node->InputAt(i);
|
|
|
|
entry.input_index = i + 1;
|
2014-11-17 12:12:24 +00:00
|
|
|
if (input != node && Recurse(input)) return;
|
|
|
|
}
|
|
|
|
|
2015-05-06 10:12:47 +00:00
|
|
|
// Remember the max node id before reduction.
|
|
|
|
NodeId const max_id = graph()->NodeCount() - 1;
|
2014-11-17 12:12:24 +00:00
|
|
|
|
|
|
|
// All inputs should be visited or on stack. Apply reductions to node.
|
|
|
|
Reduction reduction = Reduce(node);
|
|
|
|
|
2014-12-17 10:31:43 +00:00
|
|
|
// If there was no reduction, pop {node} and continue.
|
|
|
|
if (!reduction.Changed()) return Pop();
|
|
|
|
|
|
|
|
// Check if the reduction is an in-place update of the {node}.
|
|
|
|
Node* const replacement = reduction.replacement();
|
|
|
|
if (replacement == node) {
|
|
|
|
// In-place update of {node}, may need to recurse on an input.
|
|
|
|
for (int i = 0; i < node->InputCount(); ++i) {
|
|
|
|
Node* input = node->InputAt(i);
|
|
|
|
entry.input_index = i + 1;
|
|
|
|
if (input != node && Recurse(input)) return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-17 12:12:24 +00:00
|
|
|
// After reducing the node, pop it off the stack.
|
|
|
|
Pop();
|
|
|
|
|
2014-12-17 10:31:43 +00:00
|
|
|
// Check if we have a new replacement.
|
|
|
|
if (replacement != node) {
|
2015-05-06 10:12:47 +00:00
|
|
|
Replace(node, replacement, max_id);
|
|
|
|
} else {
|
|
|
|
// Revisit all uses of the node.
|
|
|
|
for (Node* const user : node->uses()) {
|
|
|
|
// Don't revisit this node if it refers to itself.
|
|
|
|
if (user != node) Revisit(user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GraphReducer::Replace(Node* node, Node* replacement) {
|
|
|
|
Replace(node, replacement, std::numeric_limits<NodeId>::max());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GraphReducer::Replace(Node* node, Node* replacement, NodeId max_id) {
|
|
|
|
if (node == graph()->start()) graph()->SetStart(replacement);
|
|
|
|
if (node == graph()->end()) graph()->SetEnd(replacement);
|
|
|
|
if (replacement->id() <= max_id) {
|
|
|
|
// {replacement} is an old node, so unlink {node} and assume that
|
2014-12-17 10:31:43 +00:00
|
|
|
// {replacement} was already reduced and finish.
|
2015-05-06 10:12:47 +00:00
|
|
|
for (Edge edge : node->use_edges()) {
|
|
|
|
Node* const user = edge.from();
|
|
|
|
edge.UpdateTo(replacement);
|
|
|
|
// Don't revisit this node if it refers to itself.
|
|
|
|
if (user != node) Revisit(user);
|
|
|
|
}
|
|
|
|
node->Kill();
|
|
|
|
} else {
|
|
|
|
// Replace all old uses of {node} with {replacement}, but allow new nodes
|
|
|
|
// created by this reduction to use {node}.
|
|
|
|
for (Edge edge : node->use_edges()) {
|
|
|
|
Node* const user = edge.from();
|
|
|
|
if (user->id() <= max_id) {
|
|
|
|
edge.UpdateTo(replacement);
|
|
|
|
// Don't revisit this node if it refers to itself.
|
|
|
|
if (user != node) Revisit(user);
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
}
|
2015-05-06 10:12:47 +00:00
|
|
|
// Unlink {node} if it's no longer used.
|
|
|
|
if (node->uses().empty()) node->Kill();
|
|
|
|
|
|
|
|
// If there was a replacement, reduce it after popping {node}.
|
|
|
|
Recurse(replacement);
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-12 12:41:41 +00:00
|
|
|
void GraphReducer::ReplaceWithValue(Node* node, Node* value, Node* effect,
|
|
|
|
Node* control) {
|
2015-06-05 12:01:55 +00:00
|
|
|
if (effect == nullptr && node->op()->EffectInputCount() > 0) {
|
2015-05-12 12:41:41 +00:00
|
|
|
effect = NodeProperties::GetEffectInput(node);
|
|
|
|
}
|
|
|
|
if (control == nullptr && node->op()->ControlInputCount() > 0) {
|
|
|
|
control = NodeProperties::GetControlInput(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Requires distinguishing between value, effect and control edges.
|
|
|
|
for (Edge edge : node->use_edges()) {
|
|
|
|
Node* user = edge.from();
|
|
|
|
if (NodeProperties::IsControlEdge(edge)) {
|
|
|
|
if (user->opcode() == IrOpcode::kIfSuccess) {
|
|
|
|
Replace(user, control);
|
|
|
|
} else if (user->opcode() == IrOpcode::kIfException) {
|
2015-06-05 12:01:55 +00:00
|
|
|
for (Edge e : user->use_edges()) {
|
|
|
|
if (NodeProperties::IsValueEdge(e)) e.UpdateTo(dead_value_);
|
|
|
|
if (NodeProperties::IsControlEdge(e)) e.UpdateTo(dead_control_);
|
|
|
|
}
|
|
|
|
user->Kill();
|
2015-05-12 12:41:41 +00:00
|
|
|
} else {
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
} else if (NodeProperties::IsEffectEdge(edge)) {
|
|
|
|
DCHECK_NOT_NULL(effect);
|
|
|
|
edge.UpdateTo(effect);
|
|
|
|
Revisit(user);
|
|
|
|
} else {
|
|
|
|
edge.UpdateTo(value);
|
|
|
|
Revisit(user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-17 12:12:24 +00:00
|
|
|
void GraphReducer::Pop() {
|
2014-11-28 13:04:49 +00:00
|
|
|
Node* node = stack_.top().node;
|
|
|
|
state_.Set(node, State::kVisited);
|
2014-11-17 12:12:24 +00:00
|
|
|
stack_.pop();
|
|
|
|
}
|
2014-11-14 08:00:24 +00:00
|
|
|
|
|
|
|
|
2014-11-17 12:12:24 +00:00
|
|
|
void GraphReducer::Push(Node* const node) {
|
2014-11-28 13:04:49 +00:00
|
|
|
DCHECK(state_.Get(node) != State::kOnStack);
|
|
|
|
state_.Set(node, State::kOnStack);
|
2014-11-27 16:24:08 +00:00
|
|
|
stack_.push({node, 0});
|
2014-11-17 12:12:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-28 13:04:49 +00:00
|
|
|
bool GraphReducer::Recurse(Node* node) {
|
|
|
|
if (state_.Get(node) > State::kRevisit) return false;
|
2014-11-17 12:12:24 +00:00
|
|
|
Push(node);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-28 13:04:49 +00:00
|
|
|
void GraphReducer::Revisit(Node* node) {
|
|
|
|
if (state_.Get(node) == State::kVisited) {
|
|
|
|
state_.Set(node, State::kRevisit);
|
2014-11-17 12:12:24 +00:00
|
|
|
revisit_.push(node);
|
|
|
|
}
|
|
|
|
}
|
2014-09-04 09:22:10 +00:00
|
|
|
|
|
|
|
} // namespace compiler
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|