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") {
"src/compiler/control-equivalence.h",
"src/compiler/control-flow-optimizer.cc",
"src/compiler/control-flow-optimizer.h",
"src/compiler/control-reducer.cc",
"src/compiler/control-reducer.h",
"src/compiler/dead-code-elimination.cc",
"src/compiler/dead-code-elimination.h",
"src/compiler/diamond.h",
"src/compiler/frame.h",
"src/compiler/frame-elider.cc",
......
......@@ -403,7 +403,7 @@ class AstGraphBuilder::FrameStateBeforeAndAfter {
if (count >= 1) {
// Add the frame state for after the operation.
DCHECK_EQ(IrOpcode::kDeadValue,
DCHECK_EQ(IrOpcode::kDead,
NodeProperties::GetFrameStateInput(node, 0)->opcode());
Node* frame_state_after =
......@@ -416,7 +416,7 @@ class AstGraphBuilder::FrameStateBeforeAndAfter {
if (count >= 2) {
// Add the frame state for before the operation.
DCHECK_EQ(IrOpcode::kDeadValue,
DCHECK_EQ(IrOpcode::kDead,
NodeProperties::GetFrameStateInput(node, 1)->opcode());
NodeProperties::ReplaceFrameStateInput(node, 1, frame_state_before_);
}
......@@ -3761,7 +3761,8 @@ void AstGraphBuilder::PrepareFrameState(Node* node, BailoutId ast_id,
OutputFrameStateCombine combine) {
if (OperatorProperties::GetFrameStateInputCount(node->op()) > 0) {
DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
DCHECK_EQ(IrOpcode::kDeadValue,
DCHECK_EQ(IrOpcode::kDead,
NodeProperties::GetFrameStateInput(node, 0)->opcode());
NodeProperties::ReplaceFrameStateInput(
node, 0, environment()->Checkpoint(ast_id, combine));
......@@ -3816,9 +3817,9 @@ Node* AstGraphBuilder::MakeNode(const Operator* op, int value_input_count,
}
for (int i = 0; i < frame_state_count; i++) {
// 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.
*current_input++ = jsgraph()->DeadValue();
*current_input++ = jsgraph()->Dead();
}
if (has_effect) {
*current_input++ = environment_->GetEffectDependency();
......
......@@ -485,11 +485,11 @@ class AstGraphBuilder::Environment : public ZoneObject {
// Mark this environment as being unreachable.
void MarkAsUnreachable() {
UpdateControlDependency(builder()->jsgraph()->DeadControl());
UpdateControlDependency(builder()->jsgraph()->Dead());
liveness_block_ = nullptr;
}
bool IsMarkedAsUnreachable() {
return GetControlDependency()->opcode() == IrOpcode::kDeadControl;
return GetControlDependency()->opcode() == IrOpcode::kDead;
}
// Merge another environment into this one.
......
......@@ -16,8 +16,39 @@ namespace v8 {
namespace internal {
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) {
switch (node->opcode()) {
case IrOpcode::kBranch:
return ReduceBranch(node);
case IrOpcode::kMerge:
return ReduceMerge(node);
case IrOpcode::kEffectPhi:
return ReduceEffectPhi(node);
case IrOpcode::kPhi:
......@@ -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) {
DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode());
int const input_count = node->InputCount() - 1;
......@@ -146,11 +237,15 @@ Reduction CommonOperatorReducer::ReduceSelect(Node* node) {
Node* const vtrue = node->InputAt(1);
Node* const vfalse = node->InputAt(2);
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()) {
case IrOpcode::kHeapConstant: {
HeapObjectMatcher<HeapObject> mcond(cond);
return Replace(mcond.Value().handle()->BooleanValue() ? vtrue : vfalse);
}
case IrOpcode::kFloat32LessThan: {
Float32BinopMatcher mcond(cond);
if (mcond.left().Is(0.0) && mcond.right().Equals(vtrue) &&
......
......@@ -33,6 +33,8 @@ class CommonOperatorReducer final : public AdvancedReducer {
Reduction Reduce(Node* node) final;
private:
Reduction ReduceBranch(Node* node);
Reduction ReduceMerge(Node* node);
Reduction ReduceEffectPhi(Node* node);
Reduction ReducePhi(Node* node);
Reduction ReduceSelect(Node* node);
......
......@@ -116,9 +116,7 @@ std::ostream& operator<<(std::ostream& os, ParameterInfo const& i) {
#define CACHED_OP_LIST(V) \
V(DeadValue, Operator::kPure, 0, 0, 0, 1, 0, 0) \
V(DeadEffect, Operator::kPure, 0, 0, 0, 0, 1, 0) \
V(DeadControl, Operator::kFoldable, 0, 0, 0, 0, 0, 1) \
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
V(IfTrue, 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) \
......
......@@ -96,6 +96,7 @@ class CommonOperatorBuilder final : public ZoneObject {
public:
explicit CommonOperatorBuilder(Zone* zone);
const Operator* Dead();
const Operator* End(size_t control_input_count);
const Operator* Branch(BranchHint = BranchHint::kNone);
const Operator* IfTrue();
......@@ -110,10 +111,6 @@ class CommonOperatorBuilder final : public ZoneObject {
const Operator* Return();
const Operator* Terminate();
const Operator* DeadValue();
const Operator* DeadEffect();
const Operator* DeadControl();
const Operator* Start(int num_formal_parameters);
const Operator* Loop(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) {
os_ << " shape=\"record\"\n";
switch (node->opcode()) {
case IrOpcode::kEnd:
case IrOpcode::kDeadControl:
case IrOpcode::kDead:
case IrOpcode::kStart:
os_ << " style=\"diagonals\"\n";
break;
......
......@@ -203,13 +203,8 @@ Node* JSGraph::EmptyFrameState() {
}
Node* JSGraph::DeadValue() {
return CACHED(kDeadValue, graph()->NewNode(common()->DeadValue()));
}
Node* JSGraph::DeadControl() {
return CACHED(kDeadControl, graph()->NewNode(common()->DeadControl()));
Node* JSGraph::Dead() {
return CACHED(kDead, graph()->NewNode(common()->Dead()));
}
......
......@@ -117,11 +117,8 @@ class JSGraph : public ZoneObject {
// cannot deopt.
Node* EmptyFrameState();
// Creates a value node that servers as value input for dead nodes.
Node* DeadValue();
// Creates a control node that serves as control dependency for dead nodes.
Node* DeadControl();
// Create a control node that serves as dependency for dead nodes.
Node* Dead();
JSOperatorBuilder* javascript() const { return javascript_; }
CommonOperatorBuilder* common() const { return common_; }
......@@ -145,8 +142,7 @@ class JSGraph : public ZoneObject {
kOneConstant,
kNaNConstant,
kEmptyFrameState,
kDeadValue,
kDeadControl,
kDead,
kNumCachedNodes // Must remain last.
};
......
......@@ -62,8 +62,8 @@ class JSCallFunctionAccessor {
class CopyVisitor {
public:
CopyVisitor(Graph* source_graph, Graph* target_graph, Zone* temp_zone)
: sentinel_op_(IrOpcode::kDeadControl, Operator::kNoProperties,
"Sentinel", 0, 0, 0, 0, 0, 0),
: sentinel_op_(IrOpcode::kDead, Operator::kNoProperties, "Sentinel", 0, 0,
0, 0, 0, 0),
sentinel_(target_graph->NewNode(&sentinel_op_)),
copies_(source_graph->NodeCount(), sentinel_, temp_zone),
source_graph_(source_graph),
......
......@@ -212,7 +212,7 @@ PeeledIteration* LoopPeeler::Peel(Graph* graph, CommonOperatorBuilder* common,
5 + (loop->TotalSize() + exits.size() + rets.size()) * 2;
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.
for (Node* node : loop_tree->HeaderNodes(loop)) {
......
......@@ -26,7 +26,6 @@
V(Terminate) \
V(OsrNormalEntry) \
V(OsrLoopEntry) \
V(DeadControl) \
V(Throw) \
V(End)
......@@ -58,9 +57,7 @@
#define COMMON_OP_LIST(V) \
CONSTANT_OP_LIST(V) \
INNER_OP_LIST(V) \
V(DeadEffect) \
V(DeadValue)
V(Dead)
// Opcodes for JavaScript operators.
#define JS_COMPARE_BINOP_LIST(V) \
......@@ -321,7 +318,7 @@ class IrOpcode {
// Returns true if opcode for common operator.
static bool IsCommonOpcode(Value value) {
return kStart <= value && value <= kDeadValue;
return kStart <= value && value <= kDead;
}
// Returns true if opcode for control operator.
......
......@@ -117,7 +117,7 @@ int OperatorProperties::GetTotalInputCount(const Operator* op) {
bool OperatorProperties::IsBasicBlockBegin(const Operator* op) {
Operator::Opcode const opcode = op->opcode();
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::kIfFalse || opcode == IrOpcode::kIfSuccess ||
opcode == IrOpcode::kIfException || opcode == IrOpcode::kIfValue ||
......
......@@ -5,9 +5,11 @@
#include "src/compiler.h"
#include "src/compiler/all-nodes.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/graph.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/graph-trimmer.h"
#include "src/compiler/graph-visualizer.h"
#include "src/compiler/js-graph.h"
......@@ -317,7 +319,7 @@ void OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
// Analyze the graph to determine how deeply nested the OSR loop is.
LoopTree* loop_tree = LoopFinder::BuildLoopTree(graph, tmp_zone);
Node* dead = jsgraph->DeadControl();
Node* dead = jsgraph->Dead();
LoopTree::Loop* loop = loop_tree->ContainingLoop(osr_loop);
if (loop->depth() > 0) {
PeelOuterLoopsForOsr(graph, common, tmp_zone, dead, loop_tree, loop,
......@@ -331,16 +333,28 @@ void OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
osr_loop_entry->ReplaceUses(graph->start());
osr_loop_entry->Kill();
// Normally the control reducer removes loops whose first input is dead,
// but we need to avoid that because the osr_loop is reachable through
// the second input, so reduce it and its phis manually.
osr_loop->ReplaceInput(0, dead);
Node* node = ControlReducer::ReduceMerge(jsgraph, osr_loop);
if (node != osr_loop) osr_loop->ReplaceUses(node);
// Remove the first input to the {osr_loop}.
int const live_input_count = osr_loop->InputCount() - 1;
CHECK_NE(0, live_input_count);
for (Node* const use : osr_loop->uses()) {
if (NodeProperties::IsPhi(use)) {
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.
ControlReducer::ReduceGraph(tmp_zone, jsgraph);
GraphTrimmer trimmer(tmp_zone, jsgraph->graph());
// TODO(bmeurer): The OSR deconstruction could be a regular reducer and play
// 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);
jsgraph->GetCachedNodes(&roots);
trimmer.TrimGraph(roots.begin(), roots.end());
......
......@@ -16,7 +16,7 @@
#include "src/compiler/code-generator.h"
#include "src/compiler/common-operator-reducer.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/graph-replay.h"
#include "src/compiler/graph-trimmer.h"
......@@ -407,8 +407,8 @@ class SourcePositionWrapper final : public Reducer {
class JSGraphReducer final : public GraphReducer {
public:
JSGraphReducer(JSGraph* jsgraph, Zone* zone)
: GraphReducer(zone, jsgraph->graph(), jsgraph->DeadValue(),
jsgraph->DeadControl()) {}
: GraphReducer(zone, jsgraph->graph(), jsgraph->TheHoleConstant(),
jsgraph->Dead()) {}
~JSGraphReducer() final {}
};
......@@ -640,7 +640,13 @@ struct ChangeLoweringPhase {
struct EarlyControlReductionPhase {
static const char* phase_name() { return "early control reduction"; }
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 {
struct LateControlReductionPhase {
static const char* phase_name() { return "late control reduction"; }
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 {
// Common operators.
//------------------------------------------------------------------
case IrOpcode::kStart:
case IrOpcode::kDeadControl:
case IrOpcode::kDead:
return VisitLeaf(node, 0);
case IrOpcode::kParameter: {
// TODO(titzer): use representation from linkage.
......
......@@ -268,7 +268,6 @@ class Typer::Visitor : public Reducer {
DECLARE_CASE(Terminate)
DECLARE_CASE(OsrNormalEntry)
DECLARE_CASE(OsrLoopEntry)
DECLARE_CASE(DeadControl)
DECLARE_CASE(Throw)
DECLARE_CASE(End)
#undef DECLARE_CASE
......@@ -313,7 +312,6 @@ class Typer::Visitor : public Reducer {
DECLARE_CASE(Terminate)
DECLARE_CASE(OsrNormalEntry)
DECLARE_CASE(OsrLoopEntry)
DECLARE_CASE(DeadControl)
DECLARE_CASE(Throw)
DECLARE_CASE(End)
#undef DECLARE_CASE
......@@ -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) {
// TODO(rossberg): Ideally FrameState wouldn't have a value output.
return Bounds(Type::None(zone()), Type::Internal(zone()));
......@@ -780,6 +767,11 @@ Bounds Typer::Visitor::TypeProjection(Node* node) {
}
Bounds Typer::Visitor::TypeDead(Node* node) {
return Bounds::Unbounded(zone());
}
// JS comparison operators.
......
......@@ -192,9 +192,7 @@ void Verifier::Visitor::Check(Node* node) {
// Type is empty.
CheckNotTyped(node);
break;
case IrOpcode::kDeadValue:
case IrOpcode::kDeadEffect:
case IrOpcode::kDeadControl:
case IrOpcode::kDead:
// Dead is never connected to the graph.
// TODO(mstarzinger): Make the GraphReducer immediately perform control
// reduction in case control is killed. This will prevent {Dead} from
......
......@@ -54,7 +54,6 @@
'compiler/test-basic-block-profiler.cc',
'compiler/test-branch-combine.cc',
'compiler/test-changes-lowering.cc',
'compiler/test-control-reducer.cc',
'compiler/test-gap-resolver.cc',
'compiler/test-graph-visualizer.cc',
'compiler/test-instruction.cc',
......
This diff is collapsed.
......@@ -47,7 +47,7 @@ class LoopFinderTester : HandleAndZoneScope {
one(jsgraph.OneConstant()),
half(jsgraph.Constant(0.5)),
self(graph.NewNode(common.Int32Constant(0xaabbccdd))),
dead(graph.NewNode(common.DeadControl())),
dead(graph.NewNode(common.Dead())),
loop_tree(NULL) {
graph.SetEnd(end);
graph.SetStart(start);
......
......@@ -4,7 +4,6 @@
#include "src/compiler/common-operator.h"
#include "src/compiler/common-operator-reducer.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/machine-type.h"
#include "src/compiler/operator.h"
......@@ -28,7 +27,6 @@ class CommonOperatorReducerTest : public GraphTest {
Reduction Reduce(
AdvancedReducer::Editor* editor, Node* node,
MachineOperatorBuilder::Flags flags = MachineOperatorBuilder::kNoFlags) {
JSOperatorBuilder javascript(zone());
MachineOperatorBuilder machine(zone(), kMachPtr, flags);
CommonOperatorReducer reducer(editor, graph(), common(), &machine);
return reducer.Reduce(node);
......@@ -65,6 +63,144 @@ const Operator kOp0(0, Operator::kNoProperties, "Op0", 0, 0, 0, 1, 1, 0);
} // 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
......@@ -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) {
Node* p0 = Parameter(0);
Node* p1 = Parameter(1);
......
......@@ -48,15 +48,13 @@ const SharedOperator kSharedOperators[] = {
value_input_count, effect_input_count, control_input_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(IfFalse, 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(Return, Operator::kNoThrow, 1, 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)
SHARED(Terminate, Operator::kKontrol, 0, 1, 1, 0, 0, 1)
#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 {
using AdvancedReducer::ReplaceWithValue;
};
const Operator kMockOperator(IrOpcode::kDeadControl, Operator::kNoProperties,
const Operator kMockOperator(IrOpcode::kDead, Operator::kNoProperties,
"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);
const Operator kMockOpControl(IrOpcode::kDeadControl, Operator::kNoProperties,
const Operator kMockOpControl(IrOpcode::kDead, Operator::kNoProperties,
"MockOpControl", 0, 0, 1, 1, 0, 1);
const IfExceptionHint kNoHint = IfExceptionHint::kLocallyCaught;
......
......@@ -32,10 +32,10 @@ class GraphTrimmerTest : public GraphTest {
namespace {
const Operator kDead0(IrOpcode::kDeadControl, Operator::kNoProperties, "Dead0",
0, 0, 1, 0, 0, 0);
const Operator kLive0(IrOpcode::kDeadControl, Operator::kNoProperties, "Live0",
0, 0, 1, 0, 0, 1);
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
......
......@@ -32,7 +32,7 @@ class NodePropertiesTest : public TestWithZone {
namespace {
const Operator kMockOperator(IrOpcode::kDeadControl, Operator::kNoProperties,
const Operator kMockOperator(IrOpcode::kDead, Operator::kNoProperties,
"MockOperator", 0, 0, 0, 1, 1, 2);
const Operator kMockCallOperator(IrOpcode::kCall, Operator::kNoProperties,
"MockCallOperator", 0, 0, 0, 0, 0, 2);
......
......@@ -1378,6 +1378,11 @@ class IsUnopMatcher final : public NodeMatcher {
} // namespace
Matcher<Node*> IsDead() {
return MakeMatcher(new NodeMatcher(IrOpcode::kDead));
}
Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher) {
return MakeMatcher(new IsControl1Matcher(IrOpcode::kEnd, control0_matcher));
}
......
......@@ -36,6 +36,7 @@ class Node;
using ::testing::Matcher;
Matcher<Node*> IsDead();
Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher);
Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher,
const Matcher<Node*>& control1_matcher);
......
......@@ -44,7 +44,7 @@
'compiler/compiler-test-utils.h',
'compiler/control-equivalence-unittest.cc',
'compiler/control-flow-optimizer-unittest.cc',
'compiler/control-reducer-unittest.cc',
'compiler/dead-code-elimination-unittest.cc',
'compiler/diamond-unittest.cc',
'compiler/graph-reducer-unittest.cc',
'compiler/graph-reducer-unittest.h',
......
......@@ -460,8 +460,8 @@
'../../src/compiler/control-equivalence.h',
'../../src/compiler/control-flow-optimizer.cc',
'../../src/compiler/control-flow-optimizer.h',
'../../src/compiler/control-reducer.cc',
'../../src/compiler/control-reducer.h',
'../../src/compiler/dead-code-elimination.cc',
'../../src/compiler/dead-code-elimination.h',
'../../src/compiler/diamond.h',
'../../src/compiler/frame.h',
'../../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