Commit 80a6e539 authored by bmeurer's avatar bmeurer Committed by Commit bot

[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}
parent e61a957b
......@@ -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); }
......
// 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
// 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;
......
// 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',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment