Commit 733a2463 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Proper dead code elimination as regular reducer.

The three different concerns that the ControlReducer used to deal with
are now properly separated into

  a.) DeadCodeElimination, which is a regular AdvancedReducer, that
      propagates Dead via control edges,
  b.) CommonOperatorReducer, which does strength reduction on common
      operators (i.e. Branch, Phi, and friends), and
  c.) GraphTrimming, which removes dead->live edges from the graph.

This will make it possible to run the DeadCodeElimination together with
other passes that actually introduce Dead nodes, i.e. typed lowering;
and it opens the door for general inlining without two stage fix point
iteration.

To make the DeadCodeElimination easier and more uniform, we basically
reverted the introduction of DeadValue and DeadEffect, and changed the
Dead operator to produce control, value and effect. Note however that
this is not a requirement, but merely a way to make dead propagation
easier and more uniform. We could always go back and decide to have
different Dead operators if some other change requires that.

Note that there are several additional opportunities for cleanup now,
i.e. OSR deconstruction could be a regular reducer now, and we don't
need to use TheHole as dead value marker in the GraphReducer. And we can
actually run the dead code elimination together with the other passes
instead of using separate passes over the graph.  We will do this in
follow up CLs.

R=jarin@chromium.org, mstarzinger@chromium.org

Review URL: https://codereview.chromium.org/1193833002

Cr-Commit-Position: refs/heads/master@{#29146}
parent f82ccff5
...@@ -627,8 +627,8 @@ source_set("v8_base") { ...@@ -627,8 +627,8 @@ source_set("v8_base") {
"src/compiler/control-equivalence.h", "src/compiler/control-equivalence.h",
"src/compiler/control-flow-optimizer.cc", "src/compiler/control-flow-optimizer.cc",
"src/compiler/control-flow-optimizer.h", "src/compiler/control-flow-optimizer.h",
"src/compiler/control-reducer.cc", "src/compiler/dead-code-elimination.cc",
"src/compiler/control-reducer.h", "src/compiler/dead-code-elimination.h",
"src/compiler/diamond.h", "src/compiler/diamond.h",
"src/compiler/frame.h", "src/compiler/frame.h",
"src/compiler/frame-elider.cc", "src/compiler/frame-elider.cc",
......
...@@ -403,7 +403,7 @@ class AstGraphBuilder::FrameStateBeforeAndAfter { ...@@ -403,7 +403,7 @@ class AstGraphBuilder::FrameStateBeforeAndAfter {
if (count >= 1) { if (count >= 1) {
// Add the frame state for after the operation. // Add the frame state for after the operation.
DCHECK_EQ(IrOpcode::kDeadValue, DCHECK_EQ(IrOpcode::kDead,
NodeProperties::GetFrameStateInput(node, 0)->opcode()); NodeProperties::GetFrameStateInput(node, 0)->opcode());
Node* frame_state_after = Node* frame_state_after =
...@@ -416,7 +416,7 @@ class AstGraphBuilder::FrameStateBeforeAndAfter { ...@@ -416,7 +416,7 @@ class AstGraphBuilder::FrameStateBeforeAndAfter {
if (count >= 2) { if (count >= 2) {
// Add the frame state for before the operation. // Add the frame state for before the operation.
DCHECK_EQ(IrOpcode::kDeadValue, DCHECK_EQ(IrOpcode::kDead,
NodeProperties::GetFrameStateInput(node, 1)->opcode()); NodeProperties::GetFrameStateInput(node, 1)->opcode());
NodeProperties::ReplaceFrameStateInput(node, 1, frame_state_before_); NodeProperties::ReplaceFrameStateInput(node, 1, frame_state_before_);
} }
...@@ -3761,7 +3761,8 @@ void AstGraphBuilder::PrepareFrameState(Node* node, BailoutId ast_id, ...@@ -3761,7 +3761,8 @@ void AstGraphBuilder::PrepareFrameState(Node* node, BailoutId ast_id,
OutputFrameStateCombine combine) { OutputFrameStateCombine combine) {
if (OperatorProperties::GetFrameStateInputCount(node->op()) > 0) { if (OperatorProperties::GetFrameStateInputCount(node->op()) > 0) {
DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op())); DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
DCHECK_EQ(IrOpcode::kDeadValue,
DCHECK_EQ(IrOpcode::kDead,
NodeProperties::GetFrameStateInput(node, 0)->opcode()); NodeProperties::GetFrameStateInput(node, 0)->opcode());
NodeProperties::ReplaceFrameStateInput( NodeProperties::ReplaceFrameStateInput(
node, 0, environment()->Checkpoint(ast_id, combine)); node, 0, environment()->Checkpoint(ast_id, combine));
...@@ -3816,9 +3817,9 @@ Node* AstGraphBuilder::MakeNode(const Operator* op, int value_input_count, ...@@ -3816,9 +3817,9 @@ Node* AstGraphBuilder::MakeNode(const Operator* op, int value_input_count,
} }
for (int i = 0; i < frame_state_count; i++) { for (int i = 0; i < frame_state_count; i++) {
// The frame state will be inserted later. Here we misuse // The frame state will be inserted later. Here we misuse
// the {DeadValue} node as a sentinel to be later overwritten // the {Dead} node as a sentinel to be later overwritten
// with the real frame state. // with the real frame state.
*current_input++ = jsgraph()->DeadValue(); *current_input++ = jsgraph()->Dead();
} }
if (has_effect) { if (has_effect) {
*current_input++ = environment_->GetEffectDependency(); *current_input++ = environment_->GetEffectDependency();
......
...@@ -485,11 +485,11 @@ class AstGraphBuilder::Environment : public ZoneObject { ...@@ -485,11 +485,11 @@ class AstGraphBuilder::Environment : public ZoneObject {
// Mark this environment as being unreachable. // Mark this environment as being unreachable.
void MarkAsUnreachable() { void MarkAsUnreachable() {
UpdateControlDependency(builder()->jsgraph()->DeadControl()); UpdateControlDependency(builder()->jsgraph()->Dead());
liveness_block_ = nullptr; liveness_block_ = nullptr;
} }
bool IsMarkedAsUnreachable() { bool IsMarkedAsUnreachable() {
return GetControlDependency()->opcode() == IrOpcode::kDeadControl; return GetControlDependency()->opcode() == IrOpcode::kDead;
} }
// Merge another environment into this one. // Merge another environment into this one.
......
...@@ -16,8 +16,39 @@ namespace v8 { ...@@ -16,8 +16,39 @@ namespace v8 {
namespace internal { namespace internal {
namespace compiler { namespace compiler {
namespace {
enum class Decision { kUnknown, kTrue, kFalse };
Decision DecideCondition(Node* const cond) {
switch (cond->opcode()) {
case IrOpcode::kInt32Constant: {
Int32Matcher mcond(cond);
return mcond.Value() ? Decision::kTrue : Decision::kFalse;
}
case IrOpcode::kInt64Constant: {
Int64Matcher mcond(cond);
return mcond.Value() ? Decision::kTrue : Decision::kFalse;
}
case IrOpcode::kHeapConstant: {
HeapObjectMatcher<HeapObject> mcond(cond);
return mcond.Value().handle()->BooleanValue() ? Decision::kTrue
: Decision::kFalse;
}
default:
return Decision::kUnknown;
}
}
} // namespace
Reduction CommonOperatorReducer::Reduce(Node* node) { Reduction CommonOperatorReducer::Reduce(Node* node) {
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kBranch:
return ReduceBranch(node);
case IrOpcode::kMerge:
return ReduceMerge(node);
case IrOpcode::kEffectPhi: case IrOpcode::kEffectPhi:
return ReduceEffectPhi(node); return ReduceEffectPhi(node);
case IrOpcode::kPhi: case IrOpcode::kPhi:
...@@ -31,6 +62,66 @@ Reduction CommonOperatorReducer::Reduce(Node* node) { ...@@ -31,6 +62,66 @@ Reduction CommonOperatorReducer::Reduce(Node* node) {
} }
Reduction CommonOperatorReducer::ReduceBranch(Node* node) {
DCHECK_EQ(IrOpcode::kBranch, node->opcode());
Node* const cond = node->InputAt(0);
Decision const decision = DecideCondition(cond);
if (decision == Decision::kUnknown) return NoChange();
Node* const control = node->InputAt(1);
node->set_op(common()->Dead());
node->TrimInputCount(0);
for (Node* const use : node->uses()) {
switch (use->opcode()) {
case IrOpcode::kIfTrue:
Replace(use, (decision == Decision::kTrue) ? control : node);
break;
case IrOpcode::kIfFalse:
Replace(use, (decision == Decision::kFalse) ? control : node);
break;
default:
UNREACHABLE();
}
}
return Changed(node);
}
Reduction CommonOperatorReducer::ReduceMerge(Node* node) {
DCHECK_EQ(IrOpcode::kMerge, node->opcode());
//
// Check if this is a merge that belongs to an unused diamond, which means
// that:
//
// a) the {Merge} has no {Phi} or {EffectPhi} uses, and
// b) the {Merge} has two inputs, one {IfTrue} and one {IfFalse}, which are
// both owned by the Merge, and
// c) and the {IfTrue} and {IfFalse} nodes point to the same {Branch}.
//
if (node->InputCount() == 2) {
for (Node* const use : node->uses()) {
if (IrOpcode::IsPhiOpcode(use->opcode())) return NoChange();
}
Node* if_true = node->InputAt(0);
Node* if_false = node->InputAt(1);
if (if_true->opcode() != IrOpcode::kIfTrue) std::swap(if_true, if_false);
if (if_true->opcode() == IrOpcode::kIfTrue &&
if_false->opcode() == IrOpcode::kIfFalse &&
if_true->InputAt(0) == if_false->InputAt(0) && if_true->OwnedBy(node) &&
if_false->OwnedBy(node)) {
Node* const branch = if_true->InputAt(0);
DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
DCHECK(branch->OwnedBy(if_true, if_false));
Node* const control = branch->InputAt(1);
// Mark the {branch} as {Dead}.
branch->set_op(common()->Dead());
branch->TrimInputCount(0);
return Replace(control);
}
}
return NoChange();
}
Reduction CommonOperatorReducer::ReduceEffectPhi(Node* node) { Reduction CommonOperatorReducer::ReduceEffectPhi(Node* node) {
DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode()); DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode());
int const input_count = node->InputCount() - 1; int const input_count = node->InputCount() - 1;
...@@ -146,11 +237,15 @@ Reduction CommonOperatorReducer::ReduceSelect(Node* node) { ...@@ -146,11 +237,15 @@ Reduction CommonOperatorReducer::ReduceSelect(Node* node) {
Node* const vtrue = node->InputAt(1); Node* const vtrue = node->InputAt(1);
Node* const vfalse = node->InputAt(2); Node* const vfalse = node->InputAt(2);
if (vtrue == vfalse) return Replace(vtrue); if (vtrue == vfalse) return Replace(vtrue);
switch (DecideCondition(cond)) {
case Decision::kTrue:
return Replace(vtrue);
case Decision::kFalse:
return Replace(vfalse);
case Decision::kUnknown:
break;
}
switch (cond->opcode()) { switch (cond->opcode()) {
case IrOpcode::kHeapConstant: {
HeapObjectMatcher<HeapObject> mcond(cond);
return Replace(mcond.Value().handle()->BooleanValue() ? vtrue : vfalse);
}
case IrOpcode::kFloat32LessThan: { case IrOpcode::kFloat32LessThan: {
Float32BinopMatcher mcond(cond); Float32BinopMatcher mcond(cond);
if (mcond.left().Is(0.0) && mcond.right().Equals(vtrue) && if (mcond.left().Is(0.0) && mcond.right().Equals(vtrue) &&
......
...@@ -33,6 +33,8 @@ class CommonOperatorReducer final : public AdvancedReducer { ...@@ -33,6 +33,8 @@ class CommonOperatorReducer final : public AdvancedReducer {
Reduction Reduce(Node* node) final; Reduction Reduce(Node* node) final;
private: private:
Reduction ReduceBranch(Node* node);
Reduction ReduceMerge(Node* node);
Reduction ReduceEffectPhi(Node* node); Reduction ReduceEffectPhi(Node* node);
Reduction ReducePhi(Node* node); Reduction ReducePhi(Node* node);
Reduction ReduceSelect(Node* node); Reduction ReduceSelect(Node* node);
......
...@@ -116,9 +116,7 @@ std::ostream& operator<<(std::ostream& os, ParameterInfo const& i) { ...@@ -116,9 +116,7 @@ std::ostream& operator<<(std::ostream& os, ParameterInfo const& i) {
#define CACHED_OP_LIST(V) \ #define CACHED_OP_LIST(V) \
V(DeadValue, Operator::kPure, 0, 0, 0, 1, 0, 0) \ V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
V(DeadEffect, Operator::kPure, 0, 0, 0, 0, 1, 0) \
V(DeadControl, Operator::kFoldable, 0, 0, 0, 0, 0, 1) \
V(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \ V(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \ V(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \ V(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
......
...@@ -96,6 +96,7 @@ class CommonOperatorBuilder final : public ZoneObject { ...@@ -96,6 +96,7 @@ class CommonOperatorBuilder final : public ZoneObject {
public: public:
explicit CommonOperatorBuilder(Zone* zone); explicit CommonOperatorBuilder(Zone* zone);
const Operator* Dead();
const Operator* End(size_t control_input_count); const Operator* End(size_t control_input_count);
const Operator* Branch(BranchHint = BranchHint::kNone); const Operator* Branch(BranchHint = BranchHint::kNone);
const Operator* IfTrue(); const Operator* IfTrue();
...@@ -110,10 +111,6 @@ class CommonOperatorBuilder final : public ZoneObject { ...@@ -110,10 +111,6 @@ class CommonOperatorBuilder final : public ZoneObject {
const Operator* Return(); const Operator* Return();
const Operator* Terminate(); const Operator* Terminate();
const Operator* DeadValue();
const Operator* DeadEffect();
const Operator* DeadControl();
const Operator* Start(int num_formal_parameters); const Operator* Start(int num_formal_parameters);
const Operator* Loop(int control_input_count); const Operator* Loop(int control_input_count);
const Operator* Merge(int control_input_count); const Operator* Merge(int control_input_count);
......
This diff is collapsed.
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_COMPILER_CONTROL_REDUCER_H_
#define V8_COMPILER_CONTROL_REDUCER_H_
namespace v8 {
namespace internal {
// Forward declarations.
class Zone;
namespace compiler {
// Forward declarations.
class JSGraph;
class CommonOperatorBuilder;
class Node;
class ControlReducer {
public:
// Perform branch folding and dead code elimination on the graph.
static void ReduceGraph(Zone* zone, JSGraph* graph,
int max_phis_for_select = 0);
// Reduces a single merge node and attached phis.
static Node* ReduceMerge(JSGraph* graph, Node* node,
int max_phis_for_select = 0);
// Testing interface.
static Node* ReduceIfNodeForTesting(JSGraph* graph, Node* node);
};
}
}
} // namespace v8::internal::compiler
#endif // V8_COMPILER_CONTROL_REDUCER_H_
// 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/dead-code-elimination.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/graph.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
namespace v8 {
namespace internal {
namespace compiler {
DeadCodeElimination::DeadCodeElimination(Editor* editor, Graph* graph,
CommonOperatorBuilder* common)
: AdvancedReducer(editor),
graph_(graph),
common_(common),
dead_(graph->NewNode(common->Dead())) {}
Reduction DeadCodeElimination::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kEnd:
return ReduceEnd(node);
case IrOpcode::kLoop:
case IrOpcode::kMerge:
return ReduceLoopOrMerge(node);
default:
return ReduceNode(node);
}
UNREACHABLE();
return NoChange();
}
Reduction DeadCodeElimination::ReduceEnd(Node* node) {
DCHECK_EQ(IrOpcode::kEnd, node->opcode());
int const input_count = node->InputCount();
DCHECK_LE(1, input_count);
int live_input_count = 0;
for (int i = 0; i < input_count; ++i) {
Node* const input = node->InputAt(i);
// Skip dead inputs.
if (input->opcode() == IrOpcode::kDead) continue;
// Compact live inputs.
if (i != live_input_count) node->ReplaceInput(live_input_count, input);
++live_input_count;
}
if (live_input_count == 0) {
return Replace(dead());
} else if (live_input_count < input_count) {
node->set_op(common()->End(live_input_count));
node->TrimInputCount(live_input_count);
return Changed(node);
}
DCHECK_EQ(input_count, live_input_count);
return NoChange();
}
Reduction DeadCodeElimination::ReduceLoopOrMerge(Node* node) {
DCHECK(IrOpcode::IsMergeOpcode(node->opcode()));
int const input_count = node->InputCount();
DCHECK_LE(1, input_count);
// Count the number of live inputs to {node} and compact them on the fly, also
// compacting the inputs of the associated {Phi} and {EffectPhi} uses at the
// same time. We consider {Loop}s dead even if only the first control input
// is dead.
int live_input_count = 0;
if (node->opcode() != IrOpcode::kLoop ||
node->InputAt(0)->opcode() != IrOpcode::kDead) {
for (int i = 0; i < input_count; ++i) {
Node* const input = node->InputAt(i);
// Skip dead inputs.
if (input->opcode() == IrOpcode::kDead) continue;
// Compact live inputs.
if (live_input_count != i) {
node->ReplaceInput(live_input_count, input);
for (Node* const use : node->uses()) {
if (NodeProperties::IsPhi(use)) {
DCHECK_EQ(input_count + 1, use->InputCount());
use->ReplaceInput(live_input_count, use->InputAt(i));
}
}
}
++live_input_count;
}
}
if (live_input_count == 0) {
return Replace(dead());
} else if (live_input_count == 1) {
// Due to compaction above, the live input is at offset 0.
for (Node* const use : node->uses()) {
if (NodeProperties::IsPhi(use)) {
Replace(use, use->InputAt(0));
} else if (use->opcode() == IrOpcode::kTerminate) {
DCHECK_EQ(IrOpcode::kLoop, node->opcode());
Replace(use, dead());
}
}
return Replace(node->InputAt(0));
}
DCHECK_LE(2, live_input_count);
DCHECK_LE(live_input_count, input_count);
// Trim input count for the {Merge} or {Loop} node.
if (live_input_count < input_count) {
// Trim input counts for all phi uses and revisit them.
for (Node* const use : node->uses()) {
if (NodeProperties::IsPhi(use)) {
use->ReplaceInput(live_input_count, node);
TrimMergeOrPhi(use, live_input_count);
Revisit(use);
}
}
TrimMergeOrPhi(node, live_input_count);
return Changed(node);
}
return NoChange();
}
Reduction DeadCodeElimination::ReduceNode(Node* node) {
// If {node} has exactly one control input and this is {Dead},
// replace {node} with {Dead}.
int const control_input_count = node->op()->ControlInputCount();
if (control_input_count == 0) return NoChange();
DCHECK_EQ(1, control_input_count);
Node* control = NodeProperties::GetControlInput(node);
if (control->opcode() == IrOpcode::kDead) return Replace(control);
return NoChange();
}
void DeadCodeElimination::TrimMergeOrPhi(Node* node, int size) {
const Operator* const op = common()->ResizeMergeOrPhi(node->op(), size);
node->TrimInputCount(OperatorProperties::GetTotalInputCount(op));
node->set_op(op);
}
} // 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_DEAD_CODE_ELIMINATION_H_
#define V8_COMPILER_DEAD_CODE_ELIMINATION_H_
#include "src/compiler/graph-reducer.h"
namespace v8 {
namespace internal {
namespace compiler {
// Forward declarations.
class CommonOperatorBuilder;
// Propagates {Dead} control through the graph and thereby removes dead code.
// Note that this does not include trimming dead uses from the graph, and it
// also does not include detecting dead code by any other means than seeing a
// {Dead} control input; that is left to other reducers.
class DeadCodeElimination final : public AdvancedReducer {
public:
DeadCodeElimination(Editor* editor, Graph* graph,
CommonOperatorBuilder* common);
~DeadCodeElimination() final {}
Reduction Reduce(Node* node) final;
private:
Reduction ReduceEnd(Node* node);
Reduction ReduceLoopOrMerge(Node* node);
Reduction ReduceNode(Node* node);
void TrimMergeOrPhi(Node* node, int size);
Graph* graph() const { return graph_; }
CommonOperatorBuilder* common() const { return common_; }
Node* dead() const { return dead_; }
Graph* const graph_;
CommonOperatorBuilder* const common_;
Node* const dead_;
DISALLOW_COPY_AND_ASSIGN(DeadCodeElimination);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_DEAD_CODE_ELIMINATION_H_
...@@ -256,7 +256,7 @@ void GraphVisualizer::PrintNode(Node* node, bool gray) { ...@@ -256,7 +256,7 @@ void GraphVisualizer::PrintNode(Node* node, bool gray) {
os_ << " shape=\"record\"\n"; os_ << " shape=\"record\"\n";
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kEnd: case IrOpcode::kEnd:
case IrOpcode::kDeadControl: case IrOpcode::kDead:
case IrOpcode::kStart: case IrOpcode::kStart:
os_ << " style=\"diagonals\"\n"; os_ << " style=\"diagonals\"\n";
break; break;
......
...@@ -203,13 +203,8 @@ Node* JSGraph::EmptyFrameState() { ...@@ -203,13 +203,8 @@ Node* JSGraph::EmptyFrameState() {
} }
Node* JSGraph::DeadValue() { Node* JSGraph::Dead() {
return CACHED(kDeadValue, graph()->NewNode(common()->DeadValue())); return CACHED(kDead, graph()->NewNode(common()->Dead()));
}
Node* JSGraph::DeadControl() {
return CACHED(kDeadControl, graph()->NewNode(common()->DeadControl()));
} }
......
...@@ -117,11 +117,8 @@ class JSGraph : public ZoneObject { ...@@ -117,11 +117,8 @@ class JSGraph : public ZoneObject {
// cannot deopt. // cannot deopt.
Node* EmptyFrameState(); Node* EmptyFrameState();
// Creates a value node that servers as value input for dead nodes. // Create a control node that serves as dependency for dead nodes.
Node* DeadValue(); Node* Dead();
// Creates a control node that serves as control dependency for dead nodes.
Node* DeadControl();
JSOperatorBuilder* javascript() const { return javascript_; } JSOperatorBuilder* javascript() const { return javascript_; }
CommonOperatorBuilder* common() const { return common_; } CommonOperatorBuilder* common() const { return common_; }
...@@ -145,8 +142,7 @@ class JSGraph : public ZoneObject { ...@@ -145,8 +142,7 @@ class JSGraph : public ZoneObject {
kOneConstant, kOneConstant,
kNaNConstant, kNaNConstant,
kEmptyFrameState, kEmptyFrameState,
kDeadValue, kDead,
kDeadControl,
kNumCachedNodes // Must remain last. kNumCachedNodes // Must remain last.
}; };
......
...@@ -62,8 +62,8 @@ class JSCallFunctionAccessor { ...@@ -62,8 +62,8 @@ class JSCallFunctionAccessor {
class CopyVisitor { class CopyVisitor {
public: public:
CopyVisitor(Graph* source_graph, Graph* target_graph, Zone* temp_zone) CopyVisitor(Graph* source_graph, Graph* target_graph, Zone* temp_zone)
: sentinel_op_(IrOpcode::kDeadControl, Operator::kNoProperties, : sentinel_op_(IrOpcode::kDead, Operator::kNoProperties, "Sentinel", 0, 0,
"Sentinel", 0, 0, 0, 0, 0, 0), 0, 0, 0, 0),
sentinel_(target_graph->NewNode(&sentinel_op_)), sentinel_(target_graph->NewNode(&sentinel_op_)),
copies_(source_graph->NodeCount(), sentinel_, temp_zone), copies_(source_graph->NodeCount(), sentinel_, temp_zone),
source_graph_(source_graph), source_graph_(source_graph),
......
...@@ -212,7 +212,7 @@ PeeledIteration* LoopPeeler::Peel(Graph* graph, CommonOperatorBuilder* common, ...@@ -212,7 +212,7 @@ PeeledIteration* LoopPeeler::Peel(Graph* graph, CommonOperatorBuilder* common,
5 + (loop->TotalSize() + exits.size() + rets.size()) * 2; 5 + (loop->TotalSize() + exits.size() + rets.size()) * 2;
Peeling peeling(graph, tmp_zone, estimated_peeled_size, &iter->node_pairs_); Peeling peeling(graph, tmp_zone, estimated_peeled_size, &iter->node_pairs_);
Node* dead = graph->NewNode(common->DeadControl()); Node* dead = graph->NewNode(common->Dead());
// Map the loop header nodes to their entry values. // Map the loop header nodes to their entry values.
for (Node* node : loop_tree->HeaderNodes(loop)) { for (Node* node : loop_tree->HeaderNodes(loop)) {
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
V(Terminate) \ V(Terminate) \
V(OsrNormalEntry) \ V(OsrNormalEntry) \
V(OsrLoopEntry) \ V(OsrLoopEntry) \
V(DeadControl) \
V(Throw) \ V(Throw) \
V(End) V(End)
...@@ -58,9 +57,7 @@ ...@@ -58,9 +57,7 @@
#define COMMON_OP_LIST(V) \ #define COMMON_OP_LIST(V) \
CONSTANT_OP_LIST(V) \ CONSTANT_OP_LIST(V) \
INNER_OP_LIST(V) \ INNER_OP_LIST(V) \
V(DeadEffect) \ V(Dead)
V(DeadValue)
// Opcodes for JavaScript operators. // Opcodes for JavaScript operators.
#define JS_COMPARE_BINOP_LIST(V) \ #define JS_COMPARE_BINOP_LIST(V) \
...@@ -321,7 +318,7 @@ class IrOpcode { ...@@ -321,7 +318,7 @@ class IrOpcode {
// Returns true if opcode for common operator. // Returns true if opcode for common operator.
static bool IsCommonOpcode(Value value) { static bool IsCommonOpcode(Value value) {
return kStart <= value && value <= kDeadValue; return kStart <= value && value <= kDead;
} }
// Returns true if opcode for control operator. // Returns true if opcode for control operator.
......
...@@ -117,7 +117,7 @@ int OperatorProperties::GetTotalInputCount(const Operator* op) { ...@@ -117,7 +117,7 @@ int OperatorProperties::GetTotalInputCount(const Operator* op) {
bool OperatorProperties::IsBasicBlockBegin(const Operator* op) { bool OperatorProperties::IsBasicBlockBegin(const Operator* op) {
Operator::Opcode const opcode = op->opcode(); Operator::Opcode const opcode = op->opcode();
return opcode == IrOpcode::kStart || opcode == IrOpcode::kEnd || return opcode == IrOpcode::kStart || opcode == IrOpcode::kEnd ||
opcode == IrOpcode::kDeadControl || opcode == IrOpcode::kLoop || opcode == IrOpcode::kDead || opcode == IrOpcode::kLoop ||
opcode == IrOpcode::kMerge || opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kMerge || opcode == IrOpcode::kIfTrue ||
opcode == IrOpcode::kIfFalse || opcode == IrOpcode::kIfSuccess || opcode == IrOpcode::kIfFalse || opcode == IrOpcode::kIfSuccess ||
opcode == IrOpcode::kIfException || opcode == IrOpcode::kIfValue || opcode == IrOpcode::kIfException || opcode == IrOpcode::kIfValue ||
......
...@@ -5,9 +5,11 @@ ...@@ -5,9 +5,11 @@
#include "src/compiler.h" #include "src/compiler.h"
#include "src/compiler/all-nodes.h" #include "src/compiler/all-nodes.h"
#include "src/compiler/common-operator.h" #include "src/compiler/common-operator.h"
#include "src/compiler/control-reducer.h" #include "src/compiler/common-operator-reducer.h"
#include "src/compiler/dead-code-elimination.h"
#include "src/compiler/frame.h" #include "src/compiler/frame.h"
#include "src/compiler/graph.h" #include "src/compiler/graph.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/graph-trimmer.h" #include "src/compiler/graph-trimmer.h"
#include "src/compiler/graph-visualizer.h" #include "src/compiler/graph-visualizer.h"
#include "src/compiler/js-graph.h" #include "src/compiler/js-graph.h"
...@@ -317,7 +319,7 @@ void OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common, ...@@ -317,7 +319,7 @@ void OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
// Analyze the graph to determine how deeply nested the OSR loop is. // Analyze the graph to determine how deeply nested the OSR loop is.
LoopTree* loop_tree = LoopFinder::BuildLoopTree(graph, tmp_zone); LoopTree* loop_tree = LoopFinder::BuildLoopTree(graph, tmp_zone);
Node* dead = jsgraph->DeadControl(); Node* dead = jsgraph->Dead();
LoopTree::Loop* loop = loop_tree->ContainingLoop(osr_loop); LoopTree::Loop* loop = loop_tree->ContainingLoop(osr_loop);
if (loop->depth() > 0) { if (loop->depth() > 0) {
PeelOuterLoopsForOsr(graph, common, tmp_zone, dead, loop_tree, loop, PeelOuterLoopsForOsr(graph, common, tmp_zone, dead, loop_tree, loop,
...@@ -331,16 +333,28 @@ void OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common, ...@@ -331,16 +333,28 @@ void OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
osr_loop_entry->ReplaceUses(graph->start()); osr_loop_entry->ReplaceUses(graph->start());
osr_loop_entry->Kill(); osr_loop_entry->Kill();
// Normally the control reducer removes loops whose first input is dead, // Remove the first input to the {osr_loop}.
// but we need to avoid that because the osr_loop is reachable through int const live_input_count = osr_loop->InputCount() - 1;
// the second input, so reduce it and its phis manually. CHECK_NE(0, live_input_count);
osr_loop->ReplaceInput(0, dead); for (Node* const use : osr_loop->uses()) {
Node* node = ControlReducer::ReduceMerge(jsgraph, osr_loop); if (NodeProperties::IsPhi(use)) {
if (node != osr_loop) osr_loop->ReplaceUses(node); use->set_op(common->ResizeMergeOrPhi(use->op(), live_input_count));
use->RemoveInput(0);
}
}
osr_loop->set_op(common->ResizeMergeOrPhi(osr_loop->op(), live_input_count));
osr_loop->RemoveInput(0);
// Run control reduction and graph trimming. // Run control reduction and graph trimming.
ControlReducer::ReduceGraph(tmp_zone, jsgraph); // TODO(bmeurer): The OSR deconstruction could be a regular reducer and play
GraphTrimmer trimmer(tmp_zone, jsgraph->graph()); // nice together with the rest, instead of having this custom stuff here.
GraphReducer graph_reducer(tmp_zone, graph);
DeadCodeElimination dce(&graph_reducer, graph, common);
CommonOperatorReducer cor(&graph_reducer, graph, common, jsgraph->machine());
graph_reducer.AddReducer(&dce);
graph_reducer.AddReducer(&cor);
graph_reducer.ReduceGraph();
GraphTrimmer trimmer(tmp_zone, graph);
NodeVector roots(tmp_zone); NodeVector roots(tmp_zone);
jsgraph->GetCachedNodes(&roots); jsgraph->GetCachedNodes(&roots);
trimmer.TrimGraph(roots.begin(), roots.end()); trimmer.TrimGraph(roots.begin(), roots.end());
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#include "src/compiler/code-generator.h" #include "src/compiler/code-generator.h"
#include "src/compiler/common-operator-reducer.h" #include "src/compiler/common-operator-reducer.h"
#include "src/compiler/control-flow-optimizer.h" #include "src/compiler/control-flow-optimizer.h"
#include "src/compiler/control-reducer.h" #include "src/compiler/dead-code-elimination.h"
#include "src/compiler/frame-elider.h" #include "src/compiler/frame-elider.h"
#include "src/compiler/graph-replay.h" #include "src/compiler/graph-replay.h"
#include "src/compiler/graph-trimmer.h" #include "src/compiler/graph-trimmer.h"
...@@ -407,8 +407,8 @@ class SourcePositionWrapper final : public Reducer { ...@@ -407,8 +407,8 @@ class SourcePositionWrapper final : public Reducer {
class JSGraphReducer final : public GraphReducer { class JSGraphReducer final : public GraphReducer {
public: public:
JSGraphReducer(JSGraph* jsgraph, Zone* zone) JSGraphReducer(JSGraph* jsgraph, Zone* zone)
: GraphReducer(zone, jsgraph->graph(), jsgraph->DeadValue(), : GraphReducer(zone, jsgraph->graph(), jsgraph->TheHoleConstant(),
jsgraph->DeadControl()) {} jsgraph->Dead()) {}
~JSGraphReducer() final {} ~JSGraphReducer() final {}
}; };
...@@ -640,7 +640,13 @@ struct ChangeLoweringPhase { ...@@ -640,7 +640,13 @@ struct ChangeLoweringPhase {
struct EarlyControlReductionPhase { struct EarlyControlReductionPhase {
static const char* phase_name() { return "early control reduction"; } static const char* phase_name() { return "early control reduction"; }
void Run(PipelineData* data, Zone* temp_zone) { void Run(PipelineData* data, Zone* temp_zone) {
ControlReducer::ReduceGraph(temp_zone, data->jsgraph(), 0); GraphReducer graph_reducer(temp_zone, data->graph());
DeadCodeElimination dce(&graph_reducer, data->graph(), data->common());
CommonOperatorReducer common(&graph_reducer, data->graph(), data->common(),
data->machine());
graph_reducer.AddReducer(&dce);
graph_reducer.AddReducer(&common);
graph_reducer.ReduceGraph();
} }
}; };
...@@ -648,7 +654,13 @@ struct EarlyControlReductionPhase { ...@@ -648,7 +654,13 @@ struct EarlyControlReductionPhase {
struct LateControlReductionPhase { struct LateControlReductionPhase {
static const char* phase_name() { return "late control reduction"; } static const char* phase_name() { return "late control reduction"; }
void Run(PipelineData* data, Zone* temp_zone) { void Run(PipelineData* data, Zone* temp_zone) {
ControlReducer::ReduceGraph(temp_zone, data->jsgraph(), 0); GraphReducer graph_reducer(temp_zone, data->graph());
DeadCodeElimination dce(&graph_reducer, data->graph(), data->common());
CommonOperatorReducer common(&graph_reducer, data->graph(), data->common(),
data->machine());
graph_reducer.AddReducer(&dce);
graph_reducer.AddReducer(&common);
graph_reducer.ReduceGraph();
} }
}; };
......
...@@ -497,7 +497,7 @@ class RepresentationSelector { ...@@ -497,7 +497,7 @@ class RepresentationSelector {
// Common operators. // Common operators.
//------------------------------------------------------------------ //------------------------------------------------------------------
case IrOpcode::kStart: case IrOpcode::kStart:
case IrOpcode::kDeadControl: case IrOpcode::kDead:
return VisitLeaf(node, 0); return VisitLeaf(node, 0);
case IrOpcode::kParameter: { case IrOpcode::kParameter: {
// TODO(titzer): use representation from linkage. // TODO(titzer): use representation from linkage.
......
...@@ -268,7 +268,6 @@ class Typer::Visitor : public Reducer { ...@@ -268,7 +268,6 @@ class Typer::Visitor : public Reducer {
DECLARE_CASE(Terminate) DECLARE_CASE(Terminate)
DECLARE_CASE(OsrNormalEntry) DECLARE_CASE(OsrNormalEntry)
DECLARE_CASE(OsrLoopEntry) DECLARE_CASE(OsrLoopEntry)
DECLARE_CASE(DeadControl)
DECLARE_CASE(Throw) DECLARE_CASE(Throw)
DECLARE_CASE(End) DECLARE_CASE(End)
#undef DECLARE_CASE #undef DECLARE_CASE
...@@ -313,7 +312,6 @@ class Typer::Visitor : public Reducer { ...@@ -313,7 +312,6 @@ class Typer::Visitor : public Reducer {
DECLARE_CASE(Terminate) DECLARE_CASE(Terminate)
DECLARE_CASE(OsrNormalEntry) DECLARE_CASE(OsrNormalEntry)
DECLARE_CASE(OsrLoopEntry) DECLARE_CASE(OsrLoopEntry)
DECLARE_CASE(DeadControl)
DECLARE_CASE(Throw) DECLARE_CASE(Throw)
DECLARE_CASE(End) DECLARE_CASE(End)
#undef DECLARE_CASE #undef DECLARE_CASE
...@@ -742,17 +740,6 @@ Bounds Typer::Visitor::TypeFinish(Node* node) { ...@@ -742,17 +740,6 @@ Bounds Typer::Visitor::TypeFinish(Node* node) {
} }
Bounds Typer::Visitor::TypeDeadValue(Node* node) {
return Bounds(Type::None(zone()), Type::Any(zone()));
}
Bounds Typer::Visitor::TypeDeadEffect(Node* node) {
UNREACHABLE();
return Bounds();
}
Bounds Typer::Visitor::TypeFrameState(Node* node) { Bounds Typer::Visitor::TypeFrameState(Node* node) {
// TODO(rossberg): Ideally FrameState wouldn't have a value output. // TODO(rossberg): Ideally FrameState wouldn't have a value output.
return Bounds(Type::None(zone()), Type::Internal(zone())); return Bounds(Type::None(zone()), Type::Internal(zone()));
...@@ -780,6 +767,11 @@ Bounds Typer::Visitor::TypeProjection(Node* node) { ...@@ -780,6 +767,11 @@ Bounds Typer::Visitor::TypeProjection(Node* node) {
} }
Bounds Typer::Visitor::TypeDead(Node* node) {
return Bounds::Unbounded(zone());
}
// JS comparison operators. // JS comparison operators.
......
...@@ -192,9 +192,7 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -192,9 +192,7 @@ void Verifier::Visitor::Check(Node* node) {
// Type is empty. // Type is empty.
CheckNotTyped(node); CheckNotTyped(node);
break; break;
case IrOpcode::kDeadValue: case IrOpcode::kDead:
case IrOpcode::kDeadEffect:
case IrOpcode::kDeadControl:
// Dead is never connected to the graph. // Dead is never connected to the graph.
// TODO(mstarzinger): Make the GraphReducer immediately perform control // TODO(mstarzinger): Make the GraphReducer immediately perform control
// reduction in case control is killed. This will prevent {Dead} from // reduction in case control is killed. This will prevent {Dead} from
......
...@@ -54,7 +54,6 @@ ...@@ -54,7 +54,6 @@
'compiler/test-basic-block-profiler.cc', 'compiler/test-basic-block-profiler.cc',
'compiler/test-branch-combine.cc', 'compiler/test-branch-combine.cc',
'compiler/test-changes-lowering.cc', 'compiler/test-changes-lowering.cc',
'compiler/test-control-reducer.cc',
'compiler/test-gap-resolver.cc', 'compiler/test-gap-resolver.cc',
'compiler/test-graph-visualizer.cc', 'compiler/test-graph-visualizer.cc',
'compiler/test-instruction.cc', 'compiler/test-instruction.cc',
......
This diff is collapsed.
...@@ -47,7 +47,7 @@ class LoopFinderTester : HandleAndZoneScope { ...@@ -47,7 +47,7 @@ class LoopFinderTester : HandleAndZoneScope {
one(jsgraph.OneConstant()), one(jsgraph.OneConstant()),
half(jsgraph.Constant(0.5)), half(jsgraph.Constant(0.5)),
self(graph.NewNode(common.Int32Constant(0xaabbccdd))), self(graph.NewNode(common.Int32Constant(0xaabbccdd))),
dead(graph.NewNode(common.DeadControl())), dead(graph.NewNode(common.Dead())),
loop_tree(NULL) { loop_tree(NULL) {
graph.SetEnd(end); graph.SetEnd(end);
graph.SetStart(start); graph.SetStart(start);
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
#include "src/compiler/common-operator.h" #include "src/compiler/common-operator.h"
#include "src/compiler/common-operator-reducer.h" #include "src/compiler/common-operator-reducer.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/machine-operator.h" #include "src/compiler/machine-operator.h"
#include "src/compiler/machine-type.h" #include "src/compiler/machine-type.h"
#include "src/compiler/operator.h" #include "src/compiler/operator.h"
...@@ -28,7 +27,6 @@ class CommonOperatorReducerTest : public GraphTest { ...@@ -28,7 +27,6 @@ class CommonOperatorReducerTest : public GraphTest {
Reduction Reduce( Reduction Reduce(
AdvancedReducer::Editor* editor, Node* node, AdvancedReducer::Editor* editor, Node* node,
MachineOperatorBuilder::Flags flags = MachineOperatorBuilder::kNoFlags) { MachineOperatorBuilder::Flags flags = MachineOperatorBuilder::kNoFlags) {
JSOperatorBuilder javascript(zone());
MachineOperatorBuilder machine(zone(), kMachPtr, flags); MachineOperatorBuilder machine(zone(), kMachPtr, flags);
CommonOperatorReducer reducer(editor, graph(), common(), &machine); CommonOperatorReducer reducer(editor, graph(), common(), &machine);
return reducer.Reduce(node); return reducer.Reduce(node);
...@@ -65,6 +63,144 @@ const Operator kOp0(0, Operator::kNoProperties, "Op0", 0, 0, 0, 1, 1, 0); ...@@ -65,6 +63,144 @@ const Operator kOp0(0, Operator::kNoProperties, "Op0", 0, 0, 0, 1, 1, 0);
} // namespace } // namespace
// -----------------------------------------------------------------------------
// Branch
TEST_F(CommonOperatorReducerTest, BranchWithInt32ZeroConstant) {
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
Node* const control = graph()->start();
Node* const branch =
graph()->NewNode(common()->Branch(hint), Int32Constant(0), control);
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, Replace(if_true, IsDead()));
EXPECT_CALL(editor, Replace(if_false, control));
Reduction const r = Reduce(&editor, branch);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsDead());
}
}
TEST_F(CommonOperatorReducerTest, BranchWithInt32OneConstant) {
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
Node* const control = graph()->start();
Node* const branch =
graph()->NewNode(common()->Branch(hint), Int32Constant(1), control);
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, Replace(if_true, control));
EXPECT_CALL(editor, Replace(if_false, IsDead()));
Reduction const r = Reduce(&editor, branch);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsDead());
}
}
TEST_F(CommonOperatorReducerTest, BranchWithInt64ZeroConstant) {
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
Node* const control = graph()->start();
Node* const branch =
graph()->NewNode(common()->Branch(hint), Int64Constant(0), control);
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, Replace(if_true, IsDead()));
EXPECT_CALL(editor, Replace(if_false, control));
Reduction const r = Reduce(&editor, branch);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsDead());
}
}
TEST_F(CommonOperatorReducerTest, BranchWithInt64OneConstant) {
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
Node* const control = graph()->start();
Node* const branch =
graph()->NewNode(common()->Branch(hint), Int64Constant(1), control);
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, Replace(if_true, control));
EXPECT_CALL(editor, Replace(if_false, IsDead()));
Reduction const r = Reduce(&editor, branch);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsDead());
}
}
TEST_F(CommonOperatorReducerTest, BranchWithFalseConstant) {
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
Node* const control = graph()->start();
Node* const branch =
graph()->NewNode(common()->Branch(hint), FalseConstant(), control);
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, Replace(if_true, IsDead()));
EXPECT_CALL(editor, Replace(if_false, control));
Reduction const r = Reduce(&editor, branch);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsDead());
}
}
TEST_F(CommonOperatorReducerTest, BranchWithTrueConstant) {
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
Node* const control = graph()->start();
Node* const branch =
graph()->NewNode(common()->Branch(hint), TrueConstant(), control);
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, Replace(if_true, control));
EXPECT_CALL(editor, Replace(if_false, IsDead()));
Reduction const r = Reduce(&editor, branch);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsDead());
}
}
// -----------------------------------------------------------------------------
// Merge
TEST_F(CommonOperatorReducerTest, MergeOfUnusedDiamond0) {
Node* const value = Parameter(0);
Node* const control = graph()->start();
Node* const branch = graph()->NewNode(common()->Branch(), value, control);
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
Reduction const r =
Reduce(graph()->NewNode(common()->Merge(2), if_true, if_false));
ASSERT_TRUE(r.Changed());
EXPECT_EQ(control, r.replacement());
EXPECT_THAT(branch, IsDead());
}
TEST_F(CommonOperatorReducerTest, MergeOfUnusedDiamond1) {
Node* const value = Parameter(0);
Node* const control = graph()->start();
Node* const branch = graph()->NewNode(common()->Branch(), value, control);
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
Reduction const r =
Reduce(graph()->NewNode(common()->Merge(2), if_false, if_true));
ASSERT_TRUE(r.Changed());
EXPECT_EQ(control, r.replacement());
EXPECT_THAT(branch, IsDead());
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// EffectPhi // EffectPhi
...@@ -283,6 +419,50 @@ TEST_F(CommonOperatorReducerTest, SelectWithSameThenAndElse) { ...@@ -283,6 +419,50 @@ TEST_F(CommonOperatorReducerTest, SelectWithSameThenAndElse) {
} }
TEST_F(CommonOperatorReducerTest, SelectWithInt32ZeroConstant) {
Node* p0 = Parameter(0);
Node* p1 = Parameter(1);
Node* select = graph()->NewNode(common()->Select(kMachAnyTagged),
Int32Constant(0), p0, p1);
Reduction r = Reduce(select);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(p1, r.replacement());
}
TEST_F(CommonOperatorReducerTest, SelectWithInt32OneConstant) {
Node* p0 = Parameter(0);
Node* p1 = Parameter(1);
Node* select = graph()->NewNode(common()->Select(kMachAnyTagged),
Int32Constant(1), p0, p1);
Reduction r = Reduce(select);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(p0, r.replacement());
}
TEST_F(CommonOperatorReducerTest, SelectWithInt64ZeroConstant) {
Node* p0 = Parameter(0);
Node* p1 = Parameter(1);
Node* select = graph()->NewNode(common()->Select(kMachAnyTagged),
Int64Constant(0), p0, p1);
Reduction r = Reduce(select);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(p1, r.replacement());
}
TEST_F(CommonOperatorReducerTest, SelectWithInt64OneConstant) {
Node* p0 = Parameter(0);
Node* p1 = Parameter(1);
Node* select = graph()->NewNode(common()->Select(kMachAnyTagged),
Int64Constant(1), p0, p1);
Reduction r = Reduce(select);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(p0, r.replacement());
}
TEST_F(CommonOperatorReducerTest, SelectWithFalseConstant) { TEST_F(CommonOperatorReducerTest, SelectWithFalseConstant) {
Node* p0 = Parameter(0); Node* p0 = Parameter(0);
Node* p1 = Parameter(1); Node* p1 = Parameter(1);
......
...@@ -48,15 +48,13 @@ const SharedOperator kSharedOperators[] = { ...@@ -48,15 +48,13 @@ const SharedOperator kSharedOperators[] = {
value_input_count, effect_input_count, control_input_count, \ value_input_count, effect_input_count, control_input_count, \
value_output_count, effect_output_count, control_output_count \ value_output_count, effect_output_count, control_output_count \
} }
SHARED(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1),
SHARED(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1), SHARED(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
SHARED(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1), SHARED(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
SHARED(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1), SHARED(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
SHARED(Throw, Operator::kKontrol, 1, 1, 1, 0, 0, 1), SHARED(Throw, Operator::kKontrol, 1, 1, 1, 0, 0, 1),
SHARED(Return, Operator::kNoThrow, 1, 1, 1, 0, 0, 1), SHARED(Return, Operator::kNoThrow, 1, 1, 1, 0, 0, 1),
SHARED(Terminate, Operator::kKontrol, 0, 1, 1, 0, 0, 1), SHARED(Terminate, Operator::kKontrol, 0, 1, 1, 0, 0, 1)
SHARED(DeadValue, Operator::kPure, 0, 0, 0, 1, 0, 0),
SHARED(DeadEffect, Operator::kPure, 0, 0, 0, 0, 1, 0),
SHARED(DeadControl, Operator::kFoldable, 0, 0, 0, 0, 0, 1)
#undef SHARED #undef SHARED
}; };
......
// 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/control-reducer.h"
#include "src/compiler/diamond.h"
#include "src/compiler/graph-visualizer.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node.h"
#include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h"
#include "testing/gmock-support.h"
using testing::_;
using testing::AllOf;
using testing::Capture;
using testing::CaptureEq;
namespace v8 {
namespace internal {
namespace compiler {
class ControlReducerTest : public GraphTest {
public:
ControlReducerTest()
: GraphTest(1),
machine_(zone()),
javascript_(zone()),
jsgraph_(isolate(), graph(), common(), &javascript_, &machine_) {}
protected:
MachineOperatorBuilder machine_;
JSOperatorBuilder javascript_;
JSGraph jsgraph_;
void ReduceGraph(int max_phis_for_select = 0) {
if (FLAG_trace_turbo_graph) {
OFStream os(stdout);
os << "-- Graph before control reduction" << std::endl;
os << AsRPO(*graph());
}
ControlReducer::ReduceGraph(zone(), jsgraph(), max_phis_for_select);
if (FLAG_trace_turbo_graph) {
OFStream os(stdout);
os << "-- Graph after control reduction" << std::endl;
os << AsRPO(*graph());
}
}
JSGraph* jsgraph() { return &jsgraph_; }
};
TEST_F(ControlReducerTest, SelectPhi) {
Node* p0 = Parameter(0);
const MachineType kType = kMachInt32;
Diamond d(graph(), common(), p0);
Node* phi =
d.Phi(kType, jsgraph()->Int32Constant(1), jsgraph()->Int32Constant(2));
Node* ret =
graph()->NewNode(common()->Return(), phi, graph()->start(), d.merge);
graph()->end()->ReplaceInput(0, ret);
ReduceGraph(1);
// Phi should be replaced with a select.
EXPECT_THAT(graph()->end(),
IsEnd(IsReturn(
IsSelect(kType, p0, IsInt32Constant(1), IsInt32Constant(2)),
graph()->start(), graph()->start())));
}
TEST_F(ControlReducerTest, SelectPhis_fail) {
Node* p0 = Parameter(0);
const MachineType kType = kMachInt32;
Diamond d(graph(), common(), p0);
Node* phi =
d.Phi(kType, jsgraph()->Int32Constant(1), jsgraph()->Int32Constant(2));
Node* phi2 =
d.Phi(kType, jsgraph()->Int32Constant(11), jsgraph()->Int32Constant(22));
USE(phi2);
Node* ret =
graph()->NewNode(common()->Return(), phi, graph()->start(), d.merge);
graph()->end()->ReplaceInput(0, ret);
ReduceGraph(1);
// Diamond should not be replaced with a select (too many phis).
EXPECT_THAT(ret, IsReturn(phi, graph()->start(), d.merge));
EXPECT_THAT(graph()->end(), IsEnd(ret));
}
TEST_F(ControlReducerTest, SelectTwoPhis) {
Node* p0 = Parameter(0);
const MachineType kType = kMachInt32;
Diamond d(graph(), common(), p0);
Node* phi1 =
d.Phi(kType, jsgraph()->Int32Constant(1), jsgraph()->Int32Constant(2));
Node* phi2 =
d.Phi(kType, jsgraph()->Int32Constant(2), jsgraph()->Int32Constant(3));
MachineOperatorBuilder machine(zone());
Node* add = graph()->NewNode(machine.Int32Add(), phi1, phi2);
Node* ret =
graph()->NewNode(common()->Return(), add, graph()->start(), d.merge);
graph()->end()->ReplaceInput(0, ret);
ReduceGraph(2);
// Phis should be replaced with two selects.
EXPECT_THAT(
ret,
IsReturn(IsInt32Add(
IsSelect(kType, p0, IsInt32Constant(1), IsInt32Constant(2)),
IsSelect(kType, p0, IsInt32Constant(2), IsInt32Constant(3))),
graph()->start(), graph()->start()));
EXPECT_THAT(graph()->end(), IsEnd(ret));
}
} // namespace compiler
} // namespace internal
} // namespace v8
This diff is collapsed.
...@@ -275,11 +275,11 @@ struct ReplaceWithValueReducer final : public AdvancedReducer { ...@@ -275,11 +275,11 @@ struct ReplaceWithValueReducer final : public AdvancedReducer {
using AdvancedReducer::ReplaceWithValue; using AdvancedReducer::ReplaceWithValue;
}; };
const Operator kMockOperator(IrOpcode::kDeadControl, Operator::kNoProperties, const Operator kMockOperator(IrOpcode::kDead, Operator::kNoProperties,
"MockOperator", 0, 0, 0, 1, 0, 0); "MockOperator", 0, 0, 0, 1, 0, 0);
const Operator kMockOpEffect(IrOpcode::kDeadControl, Operator::kNoProperties, const Operator kMockOpEffect(IrOpcode::kDead, Operator::kNoProperties,
"MockOpEffect", 0, 1, 0, 1, 1, 0); "MockOpEffect", 0, 1, 0, 1, 1, 0);
const Operator kMockOpControl(IrOpcode::kDeadControl, Operator::kNoProperties, const Operator kMockOpControl(IrOpcode::kDead, Operator::kNoProperties,
"MockOpControl", 0, 0, 1, 1, 0, 1); "MockOpControl", 0, 0, 1, 1, 0, 1);
const IfExceptionHint kNoHint = IfExceptionHint::kLocallyCaught; const IfExceptionHint kNoHint = IfExceptionHint::kLocallyCaught;
......
...@@ -32,10 +32,10 @@ class GraphTrimmerTest : public GraphTest { ...@@ -32,10 +32,10 @@ class GraphTrimmerTest : public GraphTest {
namespace { namespace {
const Operator kDead0(IrOpcode::kDeadControl, Operator::kNoProperties, "Dead0", const Operator kDead0(IrOpcode::kDead, Operator::kNoProperties, "Dead0", 0, 0,
0, 0, 1, 0, 0, 0); 1, 0, 0, 0);
const Operator kLive0(IrOpcode::kDeadControl, Operator::kNoProperties, "Live0", const Operator kLive0(IrOpcode::kDead, Operator::kNoProperties, "Live0", 0, 0,
0, 0, 1, 0, 0, 1); 1, 0, 0, 1);
} // namespace } // namespace
......
...@@ -32,7 +32,7 @@ class NodePropertiesTest : public TestWithZone { ...@@ -32,7 +32,7 @@ class NodePropertiesTest : public TestWithZone {
namespace { namespace {
const Operator kMockOperator(IrOpcode::kDeadControl, Operator::kNoProperties, const Operator kMockOperator(IrOpcode::kDead, Operator::kNoProperties,
"MockOperator", 0, 0, 0, 1, 1, 2); "MockOperator", 0, 0, 0, 1, 1, 2);
const Operator kMockCallOperator(IrOpcode::kCall, Operator::kNoProperties, const Operator kMockCallOperator(IrOpcode::kCall, Operator::kNoProperties,
"MockCallOperator", 0, 0, 0, 0, 0, 2); "MockCallOperator", 0, 0, 0, 0, 0, 2);
......
...@@ -1378,6 +1378,11 @@ class IsUnopMatcher final : public NodeMatcher { ...@@ -1378,6 +1378,11 @@ class IsUnopMatcher final : public NodeMatcher {
} // namespace } // namespace
Matcher<Node*> IsDead() {
return MakeMatcher(new NodeMatcher(IrOpcode::kDead));
}
Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher) { Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher) {
return MakeMatcher(new IsControl1Matcher(IrOpcode::kEnd, control0_matcher)); return MakeMatcher(new IsControl1Matcher(IrOpcode::kEnd, control0_matcher));
} }
......
...@@ -36,6 +36,7 @@ class Node; ...@@ -36,6 +36,7 @@ class Node;
using ::testing::Matcher; using ::testing::Matcher;
Matcher<Node*> IsDead();
Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher); Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher);
Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher, Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher,
const Matcher<Node*>& control1_matcher); const Matcher<Node*>& control1_matcher);
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
'compiler/compiler-test-utils.h', 'compiler/compiler-test-utils.h',
'compiler/control-equivalence-unittest.cc', 'compiler/control-equivalence-unittest.cc',
'compiler/control-flow-optimizer-unittest.cc', 'compiler/control-flow-optimizer-unittest.cc',
'compiler/control-reducer-unittest.cc', 'compiler/dead-code-elimination-unittest.cc',
'compiler/diamond-unittest.cc', 'compiler/diamond-unittest.cc',
'compiler/graph-reducer-unittest.cc', 'compiler/graph-reducer-unittest.cc',
'compiler/graph-reducer-unittest.h', 'compiler/graph-reducer-unittest.h',
......
...@@ -460,8 +460,8 @@ ...@@ -460,8 +460,8 @@
'../../src/compiler/control-equivalence.h', '../../src/compiler/control-equivalence.h',
'../../src/compiler/control-flow-optimizer.cc', '../../src/compiler/control-flow-optimizer.cc',
'../../src/compiler/control-flow-optimizer.h', '../../src/compiler/control-flow-optimizer.h',
'../../src/compiler/control-reducer.cc', '../../src/compiler/dead-code-elimination.cc',
'../../src/compiler/control-reducer.h', '../../src/compiler/dead-code-elimination.h',
'../../src/compiler/diamond.h', '../../src/compiler/diamond.h',
'../../src/compiler/frame.h', '../../src/compiler/frame.h',
'../../src/compiler/frame-elider.cc', '../../src/compiler/frame-elider.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