"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",
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_;
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.
//===========================================================================
} // 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;
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);
namespace internal {
namespace compiler {
-bool Reducer::Finish() { return true; }
-
-
enum class GraphReducer::State : uint8_t {
kUnvisited,
kRevisit,
}
-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) {
// 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); }
--- /dev/null
+// 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
--- /dev/null
+// 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_
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
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);
};
#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"
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());
}
frame->SetOsrStackSlotCount(static_cast<int>(UnoptimizedFrameSlots()));
}
-
} // namespace compiler
} // namespace internal
} // namespace v8
#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"
};
+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"; }
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());
// 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();
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.
};
-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;
--- /dev/null
+// 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
'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',
'../../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',