[turbofan] Move graph trimming functionality to dedicated GraphTrimmer.
Up until now that was still mixed with control reduction in the ControlReducer. This separation allows us to remove the horrible Reducer::Finish hack and also do graph trimming at more appropriate places in the pipeline (i.e. trim dead nodes after generic lowering, which can also make nodes dead). R=jarin@chromium.org,mstarzinger@chromium.org Review URL: https://codereview.chromium.org/1188433010 Cr-Commit-Position: refs/heads/master@{#29077}
This commit is contained in:
parent
e61a957b2a
commit
80a6e53935
2
BUILD.gn
2
BUILD.gn
@ -642,6 +642,8 @@ source_set("v8_base") {
|
||||
"src/compiler/graph-reducer.h",
|
||||
"src/compiler/graph-replay.cc",
|
||||
"src/compiler/graph-replay.h",
|
||||
"src/compiler/graph-trimmer.cc",
|
||||
"src/compiler/graph-trimmer.h",
|
||||
"src/compiler/graph-visualizer.cc",
|
||||
"src/compiler/graph-visualizer.h",
|
||||
"src/compiler/graph.cc",
|
||||
|
@ -21,27 +21,8 @@ namespace compiler {
|
||||
if (FLAG_trace_turbo_reduction) PrintF(__VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
enum VisitState { kUnvisited = 0, kOnStack = 1, kRevisit = 2, kVisited = 3 };
|
||||
enum Decision { kFalse, kUnknown, kTrue };
|
||||
|
||||
class ReachabilityMarker : public NodeMarker<uint8_t> {
|
||||
public:
|
||||
explicit ReachabilityMarker(Graph* graph) : NodeMarker<uint8_t>(graph, 8) {}
|
||||
bool SetReachableFromEnd(Node* node) {
|
||||
uint8_t before = Get(node);
|
||||
Set(node, before | kFromEnd);
|
||||
return before & kFromEnd;
|
||||
}
|
||||
bool IsReachableFromEnd(Node* node) { return Get(node) & kFromEnd; }
|
||||
void Push(Node* node) { Set(node, Get(node) | kFwStack); }
|
||||
void Pop(Node* node) { Set(node, Get(node) & ~kFwStack); }
|
||||
bool IsOnStack(Node* node) { return Get(node) & kFwStack; }
|
||||
|
||||
private:
|
||||
enum Bit { kFromEnd = 1, kFwStack = 2 };
|
||||
};
|
||||
|
||||
|
||||
class ControlReducerImpl final : public AdvancedReducer {
|
||||
public:
|
||||
Zone* zone_;
|
||||
@ -58,83 +39,6 @@ class ControlReducerImpl final : public AdvancedReducer {
|
||||
CommonOperatorBuilder* common() { return jsgraph_->common(); }
|
||||
Node* dead() { return jsgraph_->DeadControl(); }
|
||||
|
||||
// Finish reducing the graph by trimming nodes.
|
||||
bool Finish() final {
|
||||
// TODO(bmeurer): Move this to the GraphReducer.
|
||||
Trim();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddNodesReachableFromRoots(ReachabilityMarker& marked,
|
||||
NodeVector& nodes) {
|
||||
jsgraph_->GetCachedNodes(&nodes); // Consider cached nodes roots.
|
||||
Node* end = graph()->end();
|
||||
marked.SetReachableFromEnd(end);
|
||||
if (!end->IsDead()) nodes.push_back(end); // Consider end to be a root.
|
||||
for (Node* node : nodes) marked.SetReachableFromEnd(node);
|
||||
AddBackwardsReachableNodes(marked, nodes, 0);
|
||||
}
|
||||
|
||||
void AddBackwardsReachableNodes(ReachabilityMarker& marked, NodeVector& nodes,
|
||||
size_t cursor) {
|
||||
while (cursor < nodes.size()) {
|
||||
Node* node = nodes[cursor++];
|
||||
for (Node* const input : node->inputs()) {
|
||||
if (!marked.SetReachableFromEnd(input)) {
|
||||
nodes.push_back(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trim() {
|
||||
// Gather all nodes backwards-reachable from end through inputs.
|
||||
ReachabilityMarker marked(graph());
|
||||
NodeVector nodes(zone_);
|
||||
jsgraph_->GetCachedNodes(&nodes);
|
||||
AddNodesReachableFromRoots(marked, nodes);
|
||||
TrimNodes(marked, nodes);
|
||||
}
|
||||
|
||||
void TrimNodes(ReachabilityMarker& marked, NodeVector& nodes) {
|
||||
// Remove dead->live edges.
|
||||
for (size_t j = 0; j < nodes.size(); j++) {
|
||||
Node* node = nodes[j];
|
||||
for (Edge edge : node->use_edges()) {
|
||||
Node* use = edge.from();
|
||||
if (!marked.IsReachableFromEnd(use)) {
|
||||
TRACE("DeadLink: #%d:%s(%d) -> #%d:%s\n", use->id(),
|
||||
use->op()->mnemonic(), edge.index(), node->id(),
|
||||
node->op()->mnemonic());
|
||||
edge.UpdateTo(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
// Verify that no inputs to live nodes are NULL.
|
||||
for (Node* node : nodes) {
|
||||
for (int index = 0; index < node->InputCount(); index++) {
|
||||
Node* input = node->InputAt(index);
|
||||
if (input == nullptr) {
|
||||
std::ostringstream str;
|
||||
str << "GraphError: node #" << node->id() << ":" << *node->op()
|
||||
<< "(input @" << index << ") == null";
|
||||
FATAL(str.str().c_str());
|
||||
}
|
||||
if (input->opcode() == IrOpcode::kDead) {
|
||||
std::ostringstream str;
|
||||
str << "GraphError: node #" << node->id() << ":" << *node->op()
|
||||
<< "(input @" << index << ") == dead";
|
||||
FATAL(str.str().c_str());
|
||||
}
|
||||
}
|
||||
for (Node* use : node->uses()) {
|
||||
CHECK(marked.IsReachableFromEnd(use));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Reducer implementation: perform reductions on a node.
|
||||
//===========================================================================
|
||||
@ -443,13 +347,6 @@ class DummyEditor final : public AdvancedReducer::Editor {
|
||||
} // namespace
|
||||
|
||||
|
||||
void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) {
|
||||
DummyEditor editor;
|
||||
ControlReducerImpl impl(&editor, zone, jsgraph);
|
||||
impl.Trim();
|
||||
}
|
||||
|
||||
|
||||
Node* ControlReducer::ReduceMerge(JSGraph* jsgraph, Node* node,
|
||||
int max_phis_for_select) {
|
||||
Zone zone;
|
||||
|
@ -25,9 +25,6 @@ class ControlReducer {
|
||||
static void ReduceGraph(Zone* zone, JSGraph* graph,
|
||||
int max_phis_for_select = 0);
|
||||
|
||||
// Trim nodes in the graph that are not reachable from end.
|
||||
static void TrimGraph(Zone* zone, JSGraph* graph);
|
||||
|
||||
// Reduces a single merge node and attached phis.
|
||||
static Node* ReduceMerge(JSGraph* graph, Node* node,
|
||||
int max_phis_for_select = 0);
|
||||
|
@ -14,9 +14,6 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
bool Reducer::Finish() { return true; }
|
||||
|
||||
|
||||
enum class GraphReducer::State : uint8_t {
|
||||
kUnvisited,
|
||||
kRevisit,
|
||||
@ -70,23 +67,7 @@ void GraphReducer::ReduceNode(Node* node) {
|
||||
}
|
||||
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
void GraphReducer::ReduceGraph() { ReduceNode(graph()->end()); }
|
||||
|
||||
|
||||
Reduction GraphReducer::Reduce(Node* const node) {
|
||||
|
@ -47,13 +47,6 @@ class Reducer {
|
||||
// Try to reduce a node if possible.
|
||||
virtual Reduction Reduce(Node* node) = 0;
|
||||
|
||||
// Ask this reducer to finish operation, returns {true} if the reducer is
|
||||
// done, while {false} indicates that the graph might need to be reduced
|
||||
// again.
|
||||
// TODO(turbofan): Remove this once the dead node trimming is in the
|
||||
// GraphReducer.
|
||||
virtual bool Finish();
|
||||
|
||||
// Helper functions for subclasses to produce reductions for a node.
|
||||
static Reduction NoChange() { return Reduction(); }
|
||||
static Reduction Replace(Node* node) { return Reduction(node); }
|
||||
|
54
src/compiler/graph-trimmer.cc
Normal file
54
src/compiler/graph-trimmer.cc
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2015 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 "src/compiler/graph-trimmer.h"
|
||||
|
||||
#include "src/compiler/graph.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
GraphTrimmer::GraphTrimmer(Zone* zone, Graph* graph)
|
||||
: graph_(graph), is_live_(graph, 2), live_(zone) {
|
||||
live_.reserve(graph->NodeCount());
|
||||
}
|
||||
|
||||
|
||||
GraphTrimmer::~GraphTrimmer() {}
|
||||
|
||||
|
||||
void GraphTrimmer::TrimGraph() {
|
||||
// Mark end node as live.
|
||||
MarkAsLive(graph()->end());
|
||||
// Compute transitive closure of live nodes.
|
||||
for (size_t i = 0; i < live_.size(); ++i) {
|
||||
for (Node* const input : live_[i]->inputs()) {
|
||||
DCHECK_EQ(IsLive(input),
|
||||
std::find(live_.begin(), live_.end(), input) != live_.end());
|
||||
MarkAsLive(input);
|
||||
}
|
||||
}
|
||||
// Remove dead->live edges.
|
||||
for (Node* const live : live_) {
|
||||
DCHECK(IsLive(live));
|
||||
for (Edge edge : live->use_edges()) {
|
||||
Node* const user = edge.from();
|
||||
DCHECK_EQ(IsLive(user),
|
||||
std::find(live_.begin(), live_.end(), user) != live_.end());
|
||||
if (!IsLive(user)) {
|
||||
if (FLAG_trace_turbo_reduction) {
|
||||
OFStream os(stdout);
|
||||
os << "DeadLink: " << *user << "(" << edge.index() << ") -> " << *live
|
||||
<< std::endl;
|
||||
}
|
||||
edge.UpdateTo(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
57
src/compiler/graph-trimmer.h
Normal file
57
src/compiler/graph-trimmer.h
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
#ifndef V8_COMPILER_GRAPH_TRIMMER_H_
|
||||
#define V8_COMPILER_GRAPH_TRIMMER_H_
|
||||
|
||||
#include "src/compiler/node-marker.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
// Forward declarations.
|
||||
class Graph;
|
||||
|
||||
|
||||
// Trims dead nodes from the node graph.
|
||||
class GraphTrimmer final {
|
||||
public:
|
||||
GraphTrimmer(Zone* zone, Graph* graph);
|
||||
~GraphTrimmer();
|
||||
|
||||
// Trim nodes in the {graph} that are not reachable from {graph->end()}.
|
||||
void TrimGraph();
|
||||
|
||||
// Trim nodes in the {graph} that are not reachable from either {graph->end()}
|
||||
// or any of the roots in the sequence [{begin},{end}[.
|
||||
template <typename ForwardIterator>
|
||||
void TrimGraph(ForwardIterator begin, ForwardIterator end) {
|
||||
while (begin != end) MarkAsLive(*begin++);
|
||||
TrimGraph();
|
||||
}
|
||||
|
||||
private:
|
||||
V8_INLINE bool IsLive(Node* const node) { return is_live_.Get(node); }
|
||||
V8_INLINE void MarkAsLive(Node* const node) {
|
||||
if (!node->IsDead() && !IsLive(node)) {
|
||||
is_live_.Set(node, true);
|
||||
live_.push_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
Graph* graph() const { return graph_; }
|
||||
|
||||
Graph* const graph_;
|
||||
NodeMarker<bool> is_live_;
|
||||
NodeVector live_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GraphTrimmer);
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_COMPILER_GRAPH_TRIMMER_H_
|
@ -16,14 +16,6 @@ NodeMarkerBase::NodeMarkerBase(Graph* graph, uint32_t num_states)
|
||||
DCHECK_LT(mark_min_, mark_max_); // check for wraparound.
|
||||
}
|
||||
|
||||
|
||||
void NodeMarkerBase::Reset(Graph* graph) {
|
||||
uint32_t const num_states = mark_max_ - mark_min_;
|
||||
mark_min_ = graph->mark_max_;
|
||||
mark_max_ = graph->mark_max_ += num_states;
|
||||
DCHECK_LT(mark_min_, mark_max_); // check for wraparound.
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -34,11 +34,10 @@ class NodeMarkerBase {
|
||||
DCHECK_LT(node->mark(), mark_max_);
|
||||
node->set_mark(mark + mark_min_);
|
||||
}
|
||||
void Reset(Graph* graph);
|
||||
|
||||
private:
|
||||
Mark mark_min_;
|
||||
Mark mark_max_;
|
||||
Mark const mark_min_;
|
||||
Mark const mark_max_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NodeMarkerBase);
|
||||
};
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "src/compiler/control-reducer.h"
|
||||
#include "src/compiler/frame.h"
|
||||
#include "src/compiler/graph.h"
|
||||
#include "src/compiler/graph-trimmer.h"
|
||||
#include "src/compiler/graph-visualizer.h"
|
||||
#include "src/compiler/js-graph.h"
|
||||
#include "src/compiler/loop-analysis.h"
|
||||
@ -337,9 +338,12 @@ void OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
|
||||
Node* node = ControlReducer::ReduceMerge(jsgraph, osr_loop);
|
||||
if (node != osr_loop) osr_loop->ReplaceUses(node);
|
||||
|
||||
// Run the normal control reduction, which naturally trims away the dead
|
||||
// parts of the graph.
|
||||
// Run control reduction and graph trimming.
|
||||
ControlReducer::ReduceGraph(tmp_zone, jsgraph);
|
||||
GraphTrimmer trimmer(tmp_zone, jsgraph->graph());
|
||||
NodeVector roots(tmp_zone);
|
||||
jsgraph->GetCachedNodes(&roots);
|
||||
trimmer.TrimGraph(roots.begin(), roots.end());
|
||||
}
|
||||
|
||||
|
||||
@ -351,7 +355,6 @@ void OsrHelper::SetupFrame(Frame* frame) {
|
||||
frame->SetOsrStackSlotCount(static_cast<int>(UnoptimizedFrameSlots()));
|
||||
}
|
||||
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "src/compiler/control-reducer.h"
|
||||
#include "src/compiler/frame-elider.h"
|
||||
#include "src/compiler/graph-replay.h"
|
||||
#include "src/compiler/graph-trimmer.h"
|
||||
#include "src/compiler/graph-visualizer.h"
|
||||
#include "src/compiler/greedy-allocator.h"
|
||||
#include "src/compiler/instruction.h"
|
||||
@ -653,6 +654,28 @@ struct LateControlReductionPhase {
|
||||
};
|
||||
|
||||
|
||||
struct EarlyGraphTrimmingPhase {
|
||||
static const char* phase_name() { return "early graph trimming"; }
|
||||
void Run(PipelineData* data, Zone* temp_zone) {
|
||||
GraphTrimmer trimmer(temp_zone, data->graph());
|
||||
NodeVector roots(temp_zone);
|
||||
data->jsgraph()->GetCachedNodes(&roots);
|
||||
trimmer.TrimGraph(roots.begin(), roots.end());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct LateGraphTrimmingPhase {
|
||||
static const char* phase_name() { return "late graph trimming"; }
|
||||
void Run(PipelineData* data, Zone* temp_zone) {
|
||||
GraphTrimmer trimmer(temp_zone, data->graph());
|
||||
NodeVector roots(temp_zone);
|
||||
data->jsgraph()->GetCachedNodes(&roots);
|
||||
trimmer.TrimGraph(roots.begin(), roots.end());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct StressLoopPeelingPhase {
|
||||
static const char* phase_name() { return "stress loop peeling"; }
|
||||
|
||||
@ -1024,6 +1047,9 @@ Handle<Code> Pipeline::GenerateCode() {
|
||||
Run<InliningPhase>();
|
||||
RunPrintAndVerify("Inlined", true);
|
||||
|
||||
Run<EarlyGraphTrimmingPhase>();
|
||||
RunPrintAndVerify("Early trimmed", true);
|
||||
|
||||
if (FLAG_print_turbo_replay) {
|
||||
// Print a replay of the initial graph.
|
||||
GraphReplayPrinter::PrintReplay(data.graph());
|
||||
@ -1090,6 +1116,10 @@ Handle<Code> Pipeline::GenerateCode() {
|
||||
// TODO(jarin, rossberg): Remove UNTYPED once machine typing works.
|
||||
RunPrintAndVerify("Lowered generic", true);
|
||||
|
||||
Run<LateGraphTrimmingPhase>();
|
||||
// TODO(jarin, rossberg): Remove UNTYPED once machine typing works.
|
||||
RunPrintAndVerify("Late trimmed", true);
|
||||
|
||||
BeginPhaseKind("block building");
|
||||
|
||||
data.source_positions()->RemoveDecorator();
|
||||
|
@ -141,8 +141,6 @@ class ControlReducerTester : HandleAndZoneScope {
|
||||
return effect ? common.EffectPhi(count) : common.Phi(kMachAnyTagged, count);
|
||||
}
|
||||
|
||||
void Trim() { ControlReducer::TrimGraph(main_zone(), &jsgraph); }
|
||||
|
||||
void ReduceGraph() { ControlReducer::ReduceGraph(main_zone(), &jsgraph); }
|
||||
|
||||
// Checks one-step reduction of a phi.
|
||||
@ -311,213 +309,6 @@ struct DeadChecker {
|
||||
};
|
||||
|
||||
|
||||
TEST(Trim1_live) {
|
||||
ControlReducerTester T;
|
||||
CHECK(IsUsedBy(T.start, T.p0));
|
||||
T.graph.SetEnd(T.p0);
|
||||
T.Trim();
|
||||
CHECK(IsUsedBy(T.start, T.p0));
|
||||
CheckInputs(T.p0, T.start);
|
||||
}
|
||||
|
||||
|
||||
TEST(Trim1_dead) {
|
||||
ControlReducerTester T;
|
||||
CHECK(IsUsedBy(T.start, T.p0));
|
||||
T.Trim();
|
||||
CHECK(!IsUsedBy(T.start, T.p0));
|
||||
CHECK(!T.p0->InputAt(0));
|
||||
}
|
||||
|
||||
|
||||
TEST(Trim2_live) {
|
||||
ControlReducerTester T;
|
||||
Node* phi =
|
||||
T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), T.one, T.half, T.start);
|
||||
CHECK(IsUsedBy(T.one, phi));
|
||||
CHECK(IsUsedBy(T.half, phi));
|
||||
CHECK(IsUsedBy(T.start, phi));
|
||||
T.graph.SetEnd(phi);
|
||||
T.Trim();
|
||||
CHECK(IsUsedBy(T.one, phi));
|
||||
CHECK(IsUsedBy(T.half, phi));
|
||||
CHECK(IsUsedBy(T.start, phi));
|
||||
CheckInputs(phi, T.one, T.half, T.start);
|
||||
}
|
||||
|
||||
|
||||
TEST(Trim2_dead) {
|
||||
ControlReducerTester T;
|
||||
Node* phi =
|
||||
T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), T.one, T.half, T.start);
|
||||
CHECK(IsUsedBy(T.one, phi));
|
||||
CHECK(IsUsedBy(T.half, phi));
|
||||
CHECK(IsUsedBy(T.start, phi));
|
||||
T.Trim();
|
||||
CHECK(!IsUsedBy(T.one, phi));
|
||||
CHECK(!IsUsedBy(T.half, phi));
|
||||
CHECK(!IsUsedBy(T.start, phi));
|
||||
CHECK(!phi->InputAt(0));
|
||||
CHECK(!phi->InputAt(1));
|
||||
CHECK(!phi->InputAt(2));
|
||||
}
|
||||
|
||||
|
||||
TEST(Trim_chain1) {
|
||||
ControlReducerTester T;
|
||||
const int kDepth = 15;
|
||||
Node* live[kDepth];
|
||||
Node* dead[kDepth];
|
||||
Node* end = T.start;
|
||||
for (int i = 0; i < kDepth; i++) {
|
||||
live[i] = end = T.graph.NewNode(T.common.Merge(1), end);
|
||||
dead[i] = T.graph.NewNode(T.common.Merge(1), end);
|
||||
}
|
||||
// end -> live[last] -> live[last-1] -> ... -> start
|
||||
// dead[last] ^ dead[last-1] ^ ... ^
|
||||
T.graph.SetEnd(end);
|
||||
T.Trim();
|
||||
for (int i = 0; i < kDepth; i++) {
|
||||
CHECK(!IsUsedBy(live[i], dead[i]));
|
||||
CHECK(!dead[i]->InputAt(0));
|
||||
CHECK_EQ(i == 0 ? T.start : live[i - 1], live[i]->InputAt(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Trim_chain2) {
|
||||
ControlReducerTester T;
|
||||
const int kDepth = 15;
|
||||
Node* live[kDepth];
|
||||
Node* dead[kDepth];
|
||||
Node* l = T.start;
|
||||
Node* d = T.start;
|
||||
for (int i = 0; i < kDepth; i++) {
|
||||
live[i] = l = T.graph.NewNode(T.common.Merge(1), l);
|
||||
dead[i] = d = T.graph.NewNode(T.common.Merge(1), d);
|
||||
}
|
||||
// end -> live[last] -> live[last-1] -> ... -> start
|
||||
// dead[last] -> dead[last-1] -> ... -> start
|
||||
T.graph.SetEnd(l);
|
||||
T.Trim();
|
||||
CHECK(!IsUsedBy(T.start, dead[0]));
|
||||
for (int i = 0; i < kDepth; i++) {
|
||||
CHECK_EQ(i == 0 ? NULL : dead[i - 1], dead[i]->InputAt(0));
|
||||
CHECK_EQ(i == 0 ? T.start : live[i - 1], live[i]->InputAt(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Trim_cycle1) {
|
||||
ControlReducerTester T;
|
||||
Node* loop = T.graph.NewNode(T.common.Loop(1), T.start, T.start);
|
||||
loop->ReplaceInput(1, loop);
|
||||
Node* end = T.graph.NewNode(T.common.End(1), loop);
|
||||
T.graph.SetEnd(end);
|
||||
|
||||
CHECK(IsUsedBy(T.start, loop));
|
||||
CHECK(IsUsedBy(loop, end));
|
||||
CHECK(IsUsedBy(loop, loop));
|
||||
|
||||
T.Trim();
|
||||
|
||||
// nothing should have happened to the loop itself.
|
||||
CHECK(IsUsedBy(T.start, loop));
|
||||
CHECK(IsUsedBy(loop, end));
|
||||
CHECK(IsUsedBy(loop, loop));
|
||||
CheckInputs(loop, T.start, loop);
|
||||
CheckInputs(end, loop);
|
||||
}
|
||||
|
||||
|
||||
TEST(Trim_cycle2) {
|
||||
ControlReducerTester T;
|
||||
Node* loop = T.graph.NewNode(T.common.Loop(2), T.start, T.start);
|
||||
loop->ReplaceInput(1, loop);
|
||||
Node* end = T.graph.NewNode(T.common.End(1), loop);
|
||||
Node* phi =
|
||||
T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), T.one, T.half, loop);
|
||||
T.graph.SetEnd(end);
|
||||
|
||||
CHECK(IsUsedBy(T.start, loop));
|
||||
CHECK(IsUsedBy(loop, end));
|
||||
CHECK(IsUsedBy(loop, loop));
|
||||
CHECK(IsUsedBy(loop, phi));
|
||||
CHECK(IsUsedBy(T.one, phi));
|
||||
CHECK(IsUsedBy(T.half, phi));
|
||||
|
||||
T.Trim();
|
||||
|
||||
// nothing should have happened to the loop itself.
|
||||
CHECK(IsUsedBy(T.start, loop));
|
||||
CHECK(IsUsedBy(loop, end));
|
||||
CHECK(IsUsedBy(loop, loop));
|
||||
CheckInputs(loop, T.start, loop);
|
||||
CheckInputs(end, loop);
|
||||
|
||||
// phi should have been trimmed away.
|
||||
CHECK(!IsUsedBy(loop, phi));
|
||||
CHECK(!IsUsedBy(T.one, phi));
|
||||
CHECK(!IsUsedBy(T.half, phi));
|
||||
CHECK(!phi->InputAt(0));
|
||||
CHECK(!phi->InputAt(1));
|
||||
CHECK(!phi->InputAt(2));
|
||||
}
|
||||
|
||||
|
||||
void CheckTrimConstant(ControlReducerTester* T, Node* k) {
|
||||
Node* phi = T->graph.NewNode(T->common.Phi(kMachInt32, 1), k, T->start);
|
||||
CHECK(IsUsedBy(k, phi));
|
||||
T->Trim();
|
||||
CHECK(!IsUsedBy(k, phi));
|
||||
CHECK(!phi->InputAt(0));
|
||||
CHECK(!phi->InputAt(1));
|
||||
}
|
||||
|
||||
|
||||
TEST(Trim_constants) {
|
||||
ControlReducerTester T;
|
||||
int32_t int32_constants[] = {
|
||||
0, -1, -2, 2, 2, 3, 3, 4, 4, 5, 5, 4, 5, 6, 6, 7, 8, 7, 8, 9,
|
||||
0, -11, -12, 12, 12, 13, 13, 14, 14, 15, 15, 14, 15, 6, 6, 7, 8, 7, 8, 9};
|
||||
|
||||
for (size_t i = 0; i < arraysize(int32_constants); i++) {
|
||||
CheckTrimConstant(&T, T.jsgraph.Int32Constant(int32_constants[i]));
|
||||
CheckTrimConstant(&T, T.jsgraph.Float64Constant(int32_constants[i]));
|
||||
CheckTrimConstant(&T, T.jsgraph.Constant(int32_constants[i]));
|
||||
}
|
||||
|
||||
Node* other_constants[] = {
|
||||
T.jsgraph.UndefinedConstant(), T.jsgraph.TheHoleConstant(),
|
||||
T.jsgraph.TrueConstant(), T.jsgraph.FalseConstant(),
|
||||
T.jsgraph.NullConstant(), T.jsgraph.ZeroConstant(),
|
||||
T.jsgraph.OneConstant(), T.jsgraph.NaNConstant(),
|
||||
T.jsgraph.Constant(21), T.jsgraph.Constant(22.2)};
|
||||
|
||||
for (size_t i = 0; i < arraysize(other_constants); i++) {
|
||||
CheckTrimConstant(&T, other_constants[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Trim_EmptyFrameState1) {
|
||||
ControlReducerTester T;
|
||||
|
||||
Node* node = T.jsgraph.EmptyFrameState();
|
||||
T.Trim();
|
||||
|
||||
for (Node* input : node->inputs()) {
|
||||
CHECK_NOT_NULL(input);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Trim_EmptyFrameState2) {
|
||||
ControlReducerTester T;
|
||||
CheckTrimConstant(&T, T.jsgraph.EmptyFrameState());
|
||||
}
|
||||
|
||||
|
||||
TEST(CReducePhi1) {
|
||||
ControlReducerTester R;
|
||||
|
||||
|
85
test/unittests/compiler/graph-trimmer-unittest.cc
Normal file
85
test/unittests/compiler/graph-trimmer-unittest.cc
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright 2015 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 "src/compiler/graph-trimmer.h"
|
||||
#include "test/unittests/compiler/graph-unittest.h"
|
||||
#include "testing/gmock-support.h"
|
||||
|
||||
using testing::ElementsAre;
|
||||
using testing::UnorderedElementsAre;
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
class GraphTrimmerTest : public GraphTest {
|
||||
public:
|
||||
GraphTrimmerTest() : GraphTest(1) {}
|
||||
|
||||
protected:
|
||||
void TrimGraph(Node* root) {
|
||||
Node* const roots[1] = {root};
|
||||
GraphTrimmer trimmer(zone(), graph());
|
||||
trimmer.TrimGraph(&roots[0], &roots[arraysize(roots)]);
|
||||
}
|
||||
void TrimGraph() {
|
||||
GraphTrimmer trimmer(zone(), graph());
|
||||
trimmer.TrimGraph();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
const Operator kDead0(IrOpcode::kDead, Operator::kNoProperties, "Dead0", 0, 0,
|
||||
1, 0, 0, 0);
|
||||
const Operator kLive0(IrOpcode::kDead, Operator::kNoProperties, "Live0", 0, 0,
|
||||
1, 0, 0, 1);
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
TEST_F(GraphTrimmerTest, Empty) {
|
||||
Node* const start = graph()->NewNode(common()->Start(0));
|
||||
Node* const end = graph()->NewNode(common()->End(1), start);
|
||||
graph()->SetStart(start);
|
||||
graph()->SetEnd(end);
|
||||
TrimGraph();
|
||||
EXPECT_EQ(end, graph()->end());
|
||||
EXPECT_EQ(start, graph()->start());
|
||||
EXPECT_EQ(start, end->InputAt(0));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GraphTrimmerTest, DeadUseOfStart) {
|
||||
Node* const dead0 = graph()->NewNode(&kDead0, graph()->start());
|
||||
graph()->SetEnd(graph()->NewNode(common()->End(1), graph()->start()));
|
||||
TrimGraph();
|
||||
EXPECT_THAT(dead0->inputs(), ElementsAre(nullptr));
|
||||
EXPECT_THAT(graph()->start()->uses(), ElementsAre(graph()->end()));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GraphTrimmerTest, DeadAndLiveUsesOfStart) {
|
||||
Node* const dead0 = graph()->NewNode(&kDead0, graph()->start());
|
||||
Node* const live0 = graph()->NewNode(&kLive0, graph()->start());
|
||||
graph()->SetEnd(graph()->NewNode(common()->End(1), live0));
|
||||
TrimGraph();
|
||||
EXPECT_THAT(dead0->inputs(), ElementsAre(nullptr));
|
||||
EXPECT_THAT(graph()->start()->uses(), ElementsAre(live0));
|
||||
EXPECT_THAT(live0->uses(), ElementsAre(graph()->end()));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GraphTrimmerTest, Roots) {
|
||||
Node* const live0 = graph()->NewNode(&kLive0, graph()->start());
|
||||
Node* const live1 = graph()->NewNode(&kLive0, graph()->start());
|
||||
graph()->SetEnd(graph()->NewNode(common()->End(1), live0));
|
||||
TrimGraph(live1);
|
||||
EXPECT_THAT(graph()->start()->uses(), UnorderedElementsAre(live0, live1));
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -48,6 +48,7 @@
|
||||
'compiler/diamond-unittest.cc',
|
||||
'compiler/graph-reducer-unittest.cc',
|
||||
'compiler/graph-reducer-unittest.h',
|
||||
'compiler/graph-trimmer-unittest.cc',
|
||||
'compiler/graph-unittest.cc',
|
||||
'compiler/graph-unittest.h',
|
||||
'compiler/instruction-selector-unittest.cc',
|
||||
|
@ -475,6 +475,8 @@
|
||||
'../../src/compiler/graph-reducer.h',
|
||||
'../../src/compiler/graph-replay.cc',
|
||||
'../../src/compiler/graph-replay.h',
|
||||
'../../src/compiler/graph-trimmer.cc',
|
||||
'../../src/compiler/graph-trimmer.h',
|
||||
'../../src/compiler/graph-visualizer.cc',
|
||||
'../../src/compiler/graph-visualizer.h',
|
||||
'../../src/compiler/graph.cc',
|
||||
|
Loading…
Reference in New Issue
Block a user