From 80a6e53935a8205f0b5032759ecdf8aecb8d2a38 Mon Sep 17 00:00:00 2001 From: bmeurer Date: Wed, 17 Jun 2015 03:56:27 -0700 Subject: [PATCH] [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} --- BUILD.gn | 2 + src/compiler/control-reducer.cc | 103 --------- src/compiler/control-reducer.h | 3 - src/compiler/graph-reducer.cc | 21 +- src/compiler/graph-reducer.h | 7 - src/compiler/graph-trimmer.cc | 54 +++++ src/compiler/graph-trimmer.h | 57 +++++ src/compiler/node-marker.cc | 8 - src/compiler/node-marker.h | 5 +- src/compiler/osr.cc | 9 +- src/compiler/pipeline.cc | 30 +++ test/cctest/compiler/test-control-reducer.cc | 209 ------------------ .../compiler/graph-trimmer-unittest.cc | 85 +++++++ test/unittests/unittests.gyp | 1 + tools/gyp/v8.gyp | 2 + 15 files changed, 240 insertions(+), 356 deletions(-) create mode 100644 src/compiler/graph-trimmer.cc create mode 100644 src/compiler/graph-trimmer.h create mode 100644 test/unittests/compiler/graph-trimmer-unittest.cc diff --git a/BUILD.gn b/BUILD.gn index 3e652cf7c3..f48e276fc6 100644 --- a/BUILD.gn +++ b/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", diff --git a/src/compiler/control-reducer.cc b/src/compiler/control-reducer.cc index 05cf5ec897..4644440f9c 100644 --- a/src/compiler/control-reducer.cc +++ b/src/compiler/control-reducer.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 { - public: - explicit ReachabilityMarker(Graph* graph) : NodeMarker(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; diff --git a/src/compiler/control-reducer.h b/src/compiler/control-reducer.h index 06fb9e5df4..0fce0fb536 100644 --- a/src/compiler/control-reducer.h +++ b/src/compiler/control-reducer.h @@ -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); diff --git a/src/compiler/graph-reducer.cc b/src/compiler/graph-reducer.cc index 5435fb072c..33d400b61e 100644 --- a/src/compiler/graph-reducer.cc +++ b/src/compiler/graph-reducer.cc @@ -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) { diff --git a/src/compiler/graph-reducer.h b/src/compiler/graph-reducer.h index a8a907fa12..b4e9ef8d1c 100644 --- a/src/compiler/graph-reducer.h +++ b/src/compiler/graph-reducer.h @@ -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); } diff --git a/src/compiler/graph-trimmer.cc b/src/compiler/graph-trimmer.cc new file mode 100644 index 0000000000..ff2c036ea8 --- /dev/null +++ b/src/compiler/graph-trimmer.cc @@ -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 diff --git a/src/compiler/graph-trimmer.h b/src/compiler/graph-trimmer.h new file mode 100644 index 0000000000..d8258becc8 --- /dev/null +++ b/src/compiler/graph-trimmer.h @@ -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 + 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 is_live_; + NodeVector live_; + + DISALLOW_COPY_AND_ASSIGN(GraphTrimmer); +}; + +} // namespace compiler +} // namespace internal +} // namespace v8 + +#endif // V8_COMPILER_GRAPH_TRIMMER_H_ diff --git a/src/compiler/node-marker.cc b/src/compiler/node-marker.cc index 3e56a11271..fdfb22b21a 100644 --- a/src/compiler/node-marker.cc +++ b/src/compiler/node-marker.cc @@ -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 diff --git a/src/compiler/node-marker.h b/src/compiler/node-marker.h index 9ce03b054b..5ef2063f18 100644 --- a/src/compiler/node-marker.h +++ b/src/compiler/node-marker.h @@ -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); }; diff --git a/src/compiler/osr.cc b/src/compiler/osr.cc index 4d8583bdc1..fcd04768fd 100644 --- a/src/compiler/osr.cc +++ b/src/compiler/osr.cc @@ -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(UnoptimizedFrameSlots())); } - } // namespace compiler } // namespace internal } // namespace v8 diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc index 1dee5ae0c7..7b74592cdc 100644 --- a/src/compiler/pipeline.cc +++ b/src/compiler/pipeline.cc @@ -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 Pipeline::GenerateCode() { Run(); RunPrintAndVerify("Inlined", true); + Run(); + 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 Pipeline::GenerateCode() { // TODO(jarin, rossberg): Remove UNTYPED once machine typing works. RunPrintAndVerify("Lowered generic", true); + Run(); + // TODO(jarin, rossberg): Remove UNTYPED once machine typing works. + RunPrintAndVerify("Late trimmed", true); + BeginPhaseKind("block building"); data.source_positions()->RemoveDecorator(); diff --git a/test/cctest/compiler/test-control-reducer.cc b/test/cctest/compiler/test-control-reducer.cc index 81cb77a247..713090ff7c 100644 --- a/test/cctest/compiler/test-control-reducer.cc +++ b/test/cctest/compiler/test-control-reducer.cc @@ -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; diff --git a/test/unittests/compiler/graph-trimmer-unittest.cc b/test/unittests/compiler/graph-trimmer-unittest.cc new file mode 100644 index 0000000000..36892e6ee8 --- /dev/null +++ b/test/unittests/compiler/graph-trimmer-unittest.cc @@ -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 diff --git a/test/unittests/unittests.gyp b/test/unittests/unittests.gyp index a401833203..440b947c40 100644 --- a/test/unittests/unittests.gyp +++ b/test/unittests/unittests.gyp @@ -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', diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 51c1ae421b..6646838d28 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -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',