Commit 6ddb5e7d authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

Reland^2 "[turbofan] eagerly prune None types and deadness from the graph"

Now, the EffectControlLinearizer connects all occurrences of Unreachable to the 
graph end. This fixes issues with later phases running DeadCodeElimination and
introducing new DeadValue nodes when processing uses of Unreachable.

This is a reland of 3c4bc27f
Original change's description:
> Reland "[turbofan] eagerly prune None types and deadness from the graph"
> 
> This is a reland of e1cdda25
> Original change's description:
> > [turbofan] eagerly prune None types and deadness from the graph
> > 
> > In addition to using the {Dead} node to prune dead control nodes and nodes that 
> > depend on them, we introduce a {DeadValue} node representing an impossible value 
> > that can occur at any position in the graph. The extended {DeadCodeElimination}
> > prunes {DeadValue} and its uses, inserting a crashing {Unreachable} node into
> > the effect chain when possible. The remaining uses of {DeadValue} are handled
> > in {EffectControlLinearizer}, where we always have access to the effect chain.
> > In addition to explicitly introduced {DeadValue} nodes, we consider any value use
> > of a node with type {None} as dead.
> > 
> > Bug: chromium:741225
> > Change-Id: Icc4b636d1d018c452ba1a2fa7cd3e00e522f1655
> > Reviewed-on: https://chromium-review.googlesource.com/641250
> > Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
> > Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#48208}
> 
> Bug: chromium:741225
> Change-Id: I21316913dae02864f7a6d7c9269405a79f054138
> Reviewed-on: https://chromium-review.googlesource.com/692034
> Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
> Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#48232}

Bug: chromium:741225
Change-Id: I5702ec34856c075717162153adc765774453c45f
Reviewed-on: https://chromium-review.googlesource.com/702264Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48366}
parent bed88539
...@@ -18,9 +18,7 @@ BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph, ...@@ -18,9 +18,7 @@ BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph,
jsgraph_(js_graph), jsgraph_(js_graph),
node_conditions_(zone, js_graph->graph()->NodeCount()), node_conditions_(zone, js_graph->graph()->NodeCount()),
zone_(zone), zone_(zone),
dead_(js_graph->graph()->NewNode(js_graph->common()->Dead())) { dead_(js_graph->Dead()) {}
NodeProperties::SetType(dead_, Type::None());
}
BranchElimination::~BranchElimination() {} BranchElimination::~BranchElimination() {}
......
...@@ -348,6 +348,8 @@ ZoneVector<MachineType> const* MachineTypesOf(Operator const* op) { ...@@ -348,6 +348,8 @@ ZoneVector<MachineType> const* MachineTypesOf(Operator const* op) {
#define COMMON_CACHED_OP_LIST(V) \ #define COMMON_CACHED_OP_LIST(V) \
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \ V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
V(DeadValue, Operator::kFoldable, 0, 0, 0, 1, 0, 0) \
V(Unreachable, Operator::kFoldable, 0, 1, 1, 0, 1, 0) \
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) \
......
...@@ -344,6 +344,8 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final ...@@ -344,6 +344,8 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
explicit CommonOperatorBuilder(Zone* zone); explicit CommonOperatorBuilder(Zone* zone);
const Operator* Dead(); const Operator* Dead();
const Operator* DeadValue();
const Operator* Unreachable();
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();
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "src/compiler/common-operator.h" #include "src/compiler/common-operator.h"
#include "src/compiler/graph.h" #include "src/compiler/graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/node-properties.h" #include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h" #include "src/compiler/operator-properties.h"
...@@ -18,10 +19,32 @@ DeadCodeElimination::DeadCodeElimination(Editor* editor, Graph* graph, ...@@ -18,10 +19,32 @@ DeadCodeElimination::DeadCodeElimination(Editor* editor, Graph* graph,
: AdvancedReducer(editor), : AdvancedReducer(editor),
graph_(graph), graph_(graph),
common_(common), common_(common),
dead_(graph->NewNode(common->Dead())) { dead_(graph->NewNode(common->Dead())),
dead_value_(graph->NewNode(common->DeadValue())) {
NodeProperties::SetType(dead_, Type::None()); NodeProperties::SetType(dead_, Type::None());
NodeProperties::SetType(dead_value_, Type::None());
} }
namespace {
// True if we can guarantee that {node} will never actually produce a value or
// effect.
bool NoReturn(Node* node) {
return node->opcode() == IrOpcode::kDead ||
node->opcode() == IrOpcode::kUnreachable ||
node->opcode() == IrOpcode::kDeadValue ||
!NodeProperties::GetTypeOrAny(node)->IsInhabited();
}
bool HasDeadInput(Node* node) {
for (Node* input : node->inputs()) {
if (NoReturn(input)) return true;
}
return false;
}
} // namespace
Reduction DeadCodeElimination::Reduce(Node* node) { Reduction DeadCodeElimination::Reduce(Node* node) {
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kEnd: case IrOpcode::kEnd:
...@@ -140,13 +163,90 @@ Reduction DeadCodeElimination::RemoveLoopExit(Node* node) { ...@@ -140,13 +163,90 @@ Reduction DeadCodeElimination::RemoveLoopExit(Node* node) {
} }
Reduction DeadCodeElimination::ReduceNode(Node* node) { Reduction DeadCodeElimination::ReduceNode(Node* node) {
int const effect_input_count = node->op()->EffectInputCount();
int const control_input_count = node->op()->ControlInputCount();
if (control_input_count == 0 && effect_input_count == 0) {
return ReducePureNode(node);
}
if (control_input_count == 1) {
// If {node} has exactly one control input and this is {Dead}, // If {node} has exactly one control input and this is {Dead},
// replace {node} with {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); Node* control = NodeProperties::GetControlInput(node);
if (control->opcode() == IrOpcode::kDead) return Replace(control); if (control->opcode() == IrOpcode::kDead) return Replace(control);
if (node->opcode() == IrOpcode::kPhi &&
(PhiRepresentationOf(node->op()) == MachineRepresentation::kNone ||
!NodeProperties::GetTypeOrAny(node)->IsInhabited())) {
return Replace(dead_value());
}
}
if (effect_input_count > 0 && !NodeProperties::IsPhi(node))
return ReduceEffectNode(node);
return NoChange();
}
Reduction DeadCodeElimination::ReducePureNode(Node* node) {
DCHECK_EQ(0, node->op()->EffectInputCount());
DCHECK_EQ(0, node->op()->ControlInputCount());
int input_count = node->op()->ValueInputCount();
for (int i = 0; i < input_count; ++i) {
Node* input = NodeProperties::GetValueInput(node, i);
if (NoReturn(input)) {
return Replace(dead_value());
}
}
return NoChange();
}
Reduction DeadCodeElimination::ReduceEffectNode(Node* node) {
if (IrOpcode::IsGraphTerminator(node->opcode())) {
return ReduceGraphTerminator(node);
}
DCHECK_EQ(1, node->op()->EffectInputCount());
Node* effect = NodeProperties::GetEffectInput(node, 0);
if (effect->opcode() == IrOpcode::kDead) {
return Replace(effect);
}
if (HasDeadInput(node) && node->opcode() != IrOpcode::kIfException) {
if (effect->opcode() == IrOpcode::kUnreachable) {
RelaxEffectsAndControls(node);
return Replace(dead_value());
}
Node* control = node->op()->ControlInputCount() == 1
? NodeProperties::GetControlInput(node, 0)
: graph()->start();
node->TrimInputCount(0);
node->AppendInput(graph()->zone(), effect);
node->AppendInput(graph()->zone(), control);
ReplaceWithValue(node, dead_value(), node, control);
NodeProperties::RemoveType(node);
NodeProperties::ChangeOp(node, common()->Unreachable());
return Changed(node);
}
return NoChange();
}
Reduction DeadCodeElimination::ReduceGraphTerminator(Node* node) {
DCHECK(IrOpcode::IsGraphTerminator(node->opcode()));
if (node->opcode() == IrOpcode::kThrow) return NoChange();
if (HasDeadInput(node)) {
Node* effect = NodeProperties::GetEffectInput(node, 0);
Node* control = NodeProperties::GetControlInput(node, 0);
if (effect->opcode() != IrOpcode::kUnreachable) {
effect = graph()->NewNode(common()->Unreachable(), effect, control);
}
node->TrimInputCount(2);
node->ReplaceInput(0, effect);
node->ReplaceInput(1, control);
NodeProperties::ChangeOp(node, common()->Throw());
return Changed(node);
}
return NoChange(); return NoChange();
} }
......
...@@ -16,11 +16,15 @@ namespace compiler { ...@@ -16,11 +16,15 @@ namespace compiler {
// Forward declarations. // Forward declarations.
class CommonOperatorBuilder; class CommonOperatorBuilder;
// Propagates {Dead} control and {DeadValue} values through the graph and
// Propagates {Dead} control through the graph and thereby removes dead code. // thereby removes dead code. When {DeadValue} hits the effect chain, a crashing
// Note that this does not include trimming dead uses from the graph, and it // {Unreachable} node is inserted and the rest of the effect chain is collapsed.
// also does not include detecting dead code by any other means than seeing a // We detect dead values based on types, pruning uses of DeadValue except for
// {Dead} control input; that is left to other reducers. // uses by phi and control nodes without effect input. These remaining uses of
// {DeadValue} are eliminated in the {EffectControlLinearizer}, when the effect
// chain is readily available to insert an {Unreachable} and connect it to the
// {End} node directly. In contrast to this, {Dead} must not remain in the
// graph.
class V8_EXPORT_PRIVATE DeadCodeElimination final class V8_EXPORT_PRIVATE DeadCodeElimination final
: public NON_EXPORTED_BASE(AdvancedReducer) { : public NON_EXPORTED_BASE(AdvancedReducer) {
public: public:
...@@ -37,6 +41,9 @@ class V8_EXPORT_PRIVATE DeadCodeElimination final ...@@ -37,6 +41,9 @@ class V8_EXPORT_PRIVATE DeadCodeElimination final
Reduction ReduceLoopOrMerge(Node* node); Reduction ReduceLoopOrMerge(Node* node);
Reduction ReduceLoopExit(Node* node); Reduction ReduceLoopExit(Node* node);
Reduction ReduceNode(Node* node); Reduction ReduceNode(Node* node);
Reduction ReducePureNode(Node* node);
Reduction ReduceEffectNode(Node* node);
Reduction ReduceGraphTerminator(Node* node);
Reduction RemoveLoopExit(Node* node); Reduction RemoveLoopExit(Node* node);
...@@ -45,10 +52,12 @@ class V8_EXPORT_PRIVATE DeadCodeElimination final ...@@ -45,10 +52,12 @@ class V8_EXPORT_PRIVATE DeadCodeElimination final
Graph* graph() const { return graph_; } Graph* graph() const { return graph_; }
CommonOperatorBuilder* common() const { return common_; } CommonOperatorBuilder* common() const { return common_; }
Node* dead() const { return dead_; } Node* dead() const { return dead_; }
Node* dead_value() const { return dead_value_; }
Graph* const graph_; Graph* const graph_;
CommonOperatorBuilder* const common_; CommonOperatorBuilder* const common_;
Node* const dead_; Node* const dead_;
Node* const dead_value_;
DISALLOW_COPY_AND_ASSIGN(DeadCodeElimination); DISALLOW_COPY_AND_ASSIGN(DeadCodeElimination);
}; };
......
...@@ -46,6 +46,7 @@ struct BlockEffectControlData { ...@@ -46,6 +46,7 @@ struct BlockEffectControlData {
Node* current_effect = nullptr; // New effect. Node* current_effect = nullptr; // New effect.
Node* current_control = nullptr; // New control. Node* current_control = nullptr; // New control.
Node* current_frame_state = nullptr; // New frame state. Node* current_frame_state = nullptr; // New frame state.
bool dead = false; // This control edge can never be taken.
}; };
class BlockEffectControlMap { class BlockEffectControlMap {
...@@ -76,8 +77,19 @@ struct PendingEffectPhi { ...@@ -76,8 +77,19 @@ struct PendingEffectPhi {
: effect_phi(effect_phi), block(block) {} : effect_phi(effect_phi), block(block) {}
}; };
void ConnectUnreachableToEnd(Node* effect, Node* control, JSGraph* jsgraph) {
Graph* graph = jsgraph->graph();
CommonOperatorBuilder* common = jsgraph->common();
if (effect->opcode() == IrOpcode::kDead) return;
if (effect->opcode() != IrOpcode::kUnreachable) {
effect = graph->NewNode(common->Unreachable(), effect, control);
}
Node* throw_node = graph->NewNode(common->Throw(), effect, control);
NodeProperties::MergeControlToEnd(graph, common, throw_node);
}
void UpdateEffectPhi(Node* node, BasicBlock* block, void UpdateEffectPhi(Node* node, BasicBlock* block,
BlockEffectControlMap* block_effects) { BlockEffectControlMap* block_effects, JSGraph* jsgraph) {
// Update all inputs to an effect phi with the effects from the given // Update all inputs to an effect phi with the effects from the given
// block->effect map. // block->effect map.
DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode()); DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode());
...@@ -88,8 +100,15 @@ void UpdateEffectPhi(Node* node, BasicBlock* block, ...@@ -88,8 +100,15 @@ void UpdateEffectPhi(Node* node, BasicBlock* block,
BasicBlock* predecessor = block->PredecessorAt(static_cast<size_t>(i)); BasicBlock* predecessor = block->PredecessorAt(static_cast<size_t>(i));
const BlockEffectControlData& block_effect = const BlockEffectControlData& block_effect =
block_effects->For(predecessor, block); block_effects->For(predecessor, block);
if (input != block_effect.current_effect) { Node* effect = block_effect.current_effect;
node->ReplaceInput(i, block_effect.current_effect); if (block_effect.dead) {
ConnectUnreachableToEnd(effect, block_effect.current_control, jsgraph);
effect = jsgraph->Dead();
Node* merge = NodeProperties::GetControlInput(node);
NodeProperties::ReplaceControlInput(merge, jsgraph->Dead(), i);
}
if (input != effect) {
node->ReplaceInput(i, effect);
} }
} }
} }
...@@ -303,6 +322,7 @@ void TryCloneBranch(Node* node, BasicBlock* block, Zone* temp_zone, ...@@ -303,6 +322,7 @@ void TryCloneBranch(Node* node, BasicBlock* block, Zone* temp_zone,
cond->Kill(); cond->Kill();
merge->Kill(); merge->Kill();
} }
} // namespace } // namespace
void EffectControlLinearizer::Run() { void EffectControlLinearizer::Run() {
...@@ -330,40 +350,60 @@ void EffectControlLinearizer::Run() { ...@@ -330,40 +350,60 @@ void EffectControlLinearizer::Run() {
instr++; instr++;
// Iterate over the phis and update the effect phis. // Iterate over the phis and update the effect phis.
Node* effect = nullptr; Node* effect_phi = nullptr;
Node* terminate = nullptr; Node* terminate = nullptr;
int predecessor_count = static_cast<int>(block->PredecessorCount());
for (; instr < block->NodeCount(); instr++) { for (; instr < block->NodeCount(); instr++) {
Node* node = block->NodeAt(instr); Node* node = block->NodeAt(instr);
// Only go through the phis and effect phis. // Only go through the phis and effect phis.
if (node->opcode() == IrOpcode::kEffectPhi) { if (node->opcode() == IrOpcode::kEffectPhi) {
// There should be at most one effect phi in a block. // There should be at most one effect phi in a block.
DCHECK_NULL(effect); DCHECK_NULL(effect_phi);
// IfException blocks should not have effect phis. // IfException blocks should not have effect phis.
DCHECK_NE(IrOpcode::kIfException, control->opcode()); DCHECK_NE(IrOpcode::kIfException, control->opcode());
effect = node; effect_phi = node;
} else if (node->opcode() == IrOpcode::kPhi) {
DCHECK_EQ(predecessor_count, node->op()->ValueInputCount());
// If a value input of a phi node is {DeadValue}, then the
// corresponding control edge is dead.
for (int i = 0; i < predecessor_count; ++i) {
if (NodeProperties::GetValueInput(node, i)->opcode() ==
IrOpcode::kDeadValue) {
BlockEffectControlData* data =
&block_effects.For(block->predecessors()[i], block);
data->dead = true;
}
}
} else if (node->opcode() == IrOpcode::kTerminate) {
DCHECK_NULL(terminate);
terminate = node;
} else {
break;
}
}
if (effect_phi) {
// Make sure we update the inputs to the incoming blocks' effects. // Make sure we update the inputs to the incoming blocks' effects.
if (HasIncomingBackEdges(block)) { if (HasIncomingBackEdges(block)) {
// In case of loops, we do not update the effect phi immediately // In case of loops, we do not update the effect phi immediately
// because the back predecessor has not been handled yet. We just // because the back predecessor has not been handled yet. We just
// record the effect phi for later processing. // record the effect phi for later processing.
pending_effect_phis.push_back(PendingEffectPhi(node, block)); pending_effect_phis.push_back(PendingEffectPhi(effect_phi, block));
} else {
UpdateEffectPhi(node, block, &block_effects);
}
} else if (node->opcode() == IrOpcode::kPhi) {
// Just skip phis.
} else if (node->opcode() == IrOpcode::kTerminate) {
DCHECK_NULL(terminate);
terminate = node;
} else { } else {
break; UpdateEffectPhi(effect_phi, block, &block_effects, jsgraph());
} }
} }
Node* effect = effect_phi;
if (effect == nullptr) { if (effect == nullptr) {
// There was no effect phi. // There was no effect phi.
DCHECK(!HasIncomingBackEdges(block));
// Since a loop should have at least a StackCheck, only loops in
// unreachable code can have no effect phi.
DCHECK_IMPLIES(
HasIncomingBackEdges(block),
block_effects.For(block->PredecessorAt(0), block)
.current_effect->opcode() == IrOpcode::kUnreachable);
if (block == schedule()->start()) { if (block == schedule()->start()) {
// Start block => effect is start. // Start block => effect is start.
DCHECK_EQ(graph()->start(), control); DCHECK_EQ(graph()->start(), control);
...@@ -375,12 +415,14 @@ void EffectControlLinearizer::Run() { ...@@ -375,12 +415,14 @@ void EffectControlLinearizer::Run() {
effect = nullptr; effect = nullptr;
} else { } else {
// If all the predecessors have the same effect, we can use it as our // If all the predecessors have the same effect, we can use it as our
// current effect. // current effect. If there are dead incoming control paths, we always
effect = // insert an effect phi to break the effect chain there later in
block_effects.For(block->PredecessorAt(0), block).current_effect; // UpdateEffectPhi().
for (size_t i = 1; i < block->PredecessorCount(); ++i) { for (size_t i = 0; i < block->PredecessorCount(); ++i) {
if (block_effects.For(block->PredecessorAt(i), block) const BlockEffectControlData& data =
.current_effect != effect) { block_effects.For(block->PredecessorAt(i), block);
if (!effect) effect = data.current_effect;
if (data.current_effect != effect || data.dead) {
effect = nullptr; effect = nullptr;
break; break;
} }
...@@ -399,7 +441,7 @@ void EffectControlLinearizer::Run() { ...@@ -399,7 +441,7 @@ void EffectControlLinearizer::Run() {
if (control->opcode() == IrOpcode::kLoop) { if (control->opcode() == IrOpcode::kLoop) {
pending_effect_phis.push_back(PendingEffectPhi(effect, block)); pending_effect_phis.push_back(PendingEffectPhi(effect, block));
} else { } else {
UpdateEffectPhi(effect, block, &block_effects); UpdateEffectPhi(effect, block, &block_effects, jsgraph());
} }
} else if (control->opcode() == IrOpcode::kIfException) { } else if (control->opcode() == IrOpcode::kIfException) {
// The IfException is connected into the effect chain, so we need // The IfException is connected into the effect chain, so we need
...@@ -480,7 +522,7 @@ void EffectControlLinearizer::Run() { ...@@ -480,7 +522,7 @@ void EffectControlLinearizer::Run() {
// during the first pass (because they could have incoming back edges). // during the first pass (because they could have incoming back edges).
for (const PendingEffectPhi& pending_effect_phi : pending_effect_phis) { for (const PendingEffectPhi& pending_effect_phi : pending_effect_phis) {
UpdateEffectPhi(pending_effect_phi.effect_phi, pending_effect_phi.block, UpdateEffectPhi(pending_effect_phi.effect_phi, pending_effect_phi.block,
&block_effects); &block_effects, jsgraph());
} }
for (BasicBlock* pending_block_control : pending_block_controls) { for (BasicBlock* pending_block_control : pending_block_controls) {
UpdateBlockControl(pending_block_control, &block_effects); UpdateBlockControl(pending_block_control, &block_effects);
...@@ -498,6 +540,20 @@ void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state, ...@@ -498,6 +540,20 @@ void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state,
return; return;
} }
// {Branch} and {Switch} nodes can have a {DeadValue} condition not pruned in
// {DeadCodeElimination}. Now we have the effect chain at hand to insert
// {Unreachable} and break the current effect/control chain, connecting it to
// the graph end.
if (((node->opcode() == IrOpcode::kBranch ||
node->opcode() == IrOpcode::kSwitch) &&
NodeProperties::GetValueInput(node, 0)->opcode() ==
IrOpcode::kDeadValue) ||
node->opcode() == IrOpcode::kUnreachable) {
ConnectUnreachableToEnd(*effect, *control, jsgraph());
*effect = *control = jsgraph()->Dead();
return;
}
// If the node has a visible effect, then there must be a checkpoint in the // If the node has a visible effect, then there must be a checkpoint in the
// effect chain before we are allowed to place another eager deoptimization // effect chain before we are allowed to place another eager deoptimization
// point. We zap the frame state to ensure this invariant is maintained. // point. We zap the frame state to ensure this invariant is maintained.
......
...@@ -1120,6 +1120,9 @@ void InstructionSelector::VisitNode(Node* node) { ...@@ -1120,6 +1120,9 @@ void InstructionSelector::VisitNode(Node* node) {
case IrOpcode::kDebugBreak: case IrOpcode::kDebugBreak:
VisitDebugBreak(node); VisitDebugBreak(node);
return; return;
case IrOpcode::kUnreachable:
VisitUnreachable(node);
return;
case IrOpcode::kComment: case IrOpcode::kComment:
VisitComment(node); VisitComment(node);
return; return;
...@@ -2764,6 +2767,11 @@ void InstructionSelector::VisitDebugBreak(Node* node) { ...@@ -2764,6 +2767,11 @@ void InstructionSelector::VisitDebugBreak(Node* node) {
Emit(kArchDebugBreak, g.NoOutput()); Emit(kArchDebugBreak, g.NoOutput());
} }
void InstructionSelector::VisitUnreachable(Node* node) {
OperandGenerator g(this);
Emit(kArchDebugBreak, g.NoOutput());
}
void InstructionSelector::VisitComment(Node* node) { void InstructionSelector::VisitComment(Node* node) {
OperandGenerator g(this); OperandGenerator g(this);
InstructionOperand operand(g.UseImmediate(node)); InstructionOperand operand(g.UseImmediate(node));
......
...@@ -348,6 +348,7 @@ class V8_EXPORT_PRIVATE InstructionSelector final { ...@@ -348,6 +348,7 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
void VisitReturn(Node* ret); void VisitReturn(Node* ret);
void VisitThrow(Node* node); void VisitThrow(Node* node);
void VisitRetain(Node* node); void VisitRetain(Node* node);
void VisitUnreachable(Node* node);
void EmitPrepareArguments(ZoneVector<compiler::PushParameter>* arguments, void EmitPrepareArguments(ZoneVector<compiler::PushParameter>* arguments,
const CallDescriptor* descriptor, Node* node); const CallDescriptor* descriptor, Node* node);
......
...@@ -305,6 +305,9 @@ Node* JSGraph::Dead() { ...@@ -305,6 +305,9 @@ Node* JSGraph::Dead() {
return CACHED(kDead, graph()->NewNode(common()->Dead())); return CACHED(kDead, graph()->NewNode(common()->Dead()));
} }
Node* JSGraph::DeadValue() {
return CACHED(kDeadValue, graph()->NewNode(common()->DeadValue()));
}
void JSGraph::GetCachedNodes(NodeVector* nodes) { void JSGraph::GetCachedNodes(NodeVector* nodes) {
cache_.GetCachedNodes(nodes); cache_.GetCachedNodes(nodes);
......
...@@ -152,6 +152,9 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -152,6 +152,9 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
// Create a control node that serves as dependency for dead nodes. // Create a control node that serves as dependency for dead nodes.
Node* Dead(); Node* Dead();
// Sentinel for a value resulting from unreachable computations.
Node* DeadValue();
CommonOperatorBuilder* common() const { return common_; } CommonOperatorBuilder* common() const { return common_; }
JSOperatorBuilder* javascript() const { return javascript_; } JSOperatorBuilder* javascript() const { return javascript_; }
SimplifiedOperatorBuilder* simplified() const { return simplified_; } SimplifiedOperatorBuilder* simplified() const { return simplified_; }
...@@ -193,6 +196,7 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -193,6 +196,7 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
kEmptyStateValues, kEmptyStateValues,
kSingleDeadTypedStateValues, kSingleDeadTypedStateValues,
kDead, kDead,
kDeadValue,
kNumCachedNodes // Must remain last. kNumCachedNodes // Must remain last.
}; };
......
...@@ -100,6 +100,7 @@ void MemoryOptimizer::VisitNode(Node* node, AllocationState const* state) { ...@@ -100,6 +100,7 @@ void MemoryOptimizer::VisitNode(Node* node, AllocationState const* state) {
case IrOpcode::kRetain: case IrOpcode::kRetain:
case IrOpcode::kUnsafePointerAdd: case IrOpcode::kUnsafePointerAdd:
case IrOpcode::kDebugBreak: case IrOpcode::kDebugBreak:
case IrOpcode::kUnreachable:
return VisitOtherEffect(node, state); return VisitOtherEffect(node, state);
default: default:
break; break;
......
...@@ -79,6 +79,8 @@ ...@@ -79,6 +79,8 @@
#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(Unreachable) \
V(DeadValue) \
V(Dead) V(Dead)
// Opcodes for JavaScript operators. // Opcodes for JavaScript operators.
......
...@@ -1168,6 +1168,7 @@ struct EffectControlLinearizationPhase { ...@@ -1168,6 +1168,7 @@ struct EffectControlLinearizationPhase {
static const char* phase_name() { return "effect linearization"; } static const char* phase_name() { return "effect linearization"; }
void Run(PipelineData* data, Zone* temp_zone) { void Run(PipelineData* data, Zone* temp_zone) {
{
// The scheduler requires the graphs to be trimmed, so trim now. // The scheduler requires the graphs to be trimmed, so trim now.
// TODO(jarin) Remove the trimming once the scheduler can handle untrimmed // TODO(jarin) Remove the trimming once the scheduler can handle untrimmed
// graphs. // graphs.
...@@ -1194,16 +1195,12 @@ struct EffectControlLinearizationPhase { ...@@ -1194,16 +1195,12 @@ struct EffectControlLinearizationPhase {
data->source_positions()); data->source_positions());
linearizer.Run(); linearizer.Run();
} }
}; {
// The {EffectControlLinearizer} might leave {Dead} nodes behind, so we
// The store-store elimination greatly benefits from doing a common operator // run {DeadCodeElimination} to prune these parts of the graph.
// reducer and dead code elimination just before it, to eliminate conditional // Also, the following store-store elimination phase greatly benefits from
// deopts with a constant condition. // doing a common operator reducer and dead code elimination just before
// it, to eliminate conditional deopts with a constant condition.
struct DeadCodeEliminationPhase {
static const char* phase_name() { return "dead code elimination"; }
void Run(PipelineData* data, Zone* temp_zone) {
JSGraphReducer graph_reducer(data->jsgraph(), temp_zone); JSGraphReducer graph_reducer(data->jsgraph(), temp_zone);
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(), DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common()); data->common());
...@@ -1213,6 +1210,7 @@ struct DeadCodeEliminationPhase { ...@@ -1213,6 +1210,7 @@ struct DeadCodeEliminationPhase {
AddReducer(data, &graph_reducer, &common_reducer); AddReducer(data, &graph_reducer, &common_reducer);
graph_reducer.ReduceGraph(); graph_reducer.ReduceGraph();
} }
}
}; };
struct StoreStoreEliminationPhase { struct StoreStoreEliminationPhase {
...@@ -1721,9 +1719,6 @@ bool PipelineImpl::OptimizeGraph(Linkage* linkage) { ...@@ -1721,9 +1719,6 @@ bool PipelineImpl::OptimizeGraph(Linkage* linkage) {
Run<EffectControlLinearizationPhase>(); Run<EffectControlLinearizationPhase>();
RunPrintAndVerify("Effect and control linearized", true); RunPrintAndVerify("Effect and control linearized", true);
Run<DeadCodeEliminationPhase>();
RunPrintAndVerify("Dead code elimination", true);
if (FLAG_turbo_store_elimination) { if (FLAG_turbo_store_elimination) {
Run<StoreStoreEliminationPhase>(); Run<StoreStoreEliminationPhase>();
RunPrintAndVerify("Store-store elimination", true); RunPrintAndVerify("Store-store elimination", true);
......
...@@ -217,8 +217,7 @@ Node* RepresentationChanger::GetTaggedSignedRepresentationFor( ...@@ -217,8 +217,7 @@ Node* RepresentationChanger::GetTaggedSignedRepresentationFor(
const Operator* op; const Operator* op;
if (output_type->Is(Type::None())) { if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime. // This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here. return jsgraph()->DeadValue();
return jsgraph()->Constant(0);
} else if (IsWord(output_rep)) { } else if (IsWord(output_rep)) {
if (output_type->Is(Type::Signed31())) { if (output_type->Is(Type::Signed31())) {
op = simplified()->ChangeInt31ToTaggedSigned(); op = simplified()->ChangeInt31ToTaggedSigned();
...@@ -336,8 +335,7 @@ Node* RepresentationChanger::GetTaggedPointerRepresentationFor( ...@@ -336,8 +335,7 @@ Node* RepresentationChanger::GetTaggedPointerRepresentationFor(
Operator const* op; Operator const* op;
if (output_type->Is(Type::None())) { if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime. // This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here. return jsgraph()->DeadValue();
return jsgraph()->TheHoleConstant();
} else if (output_rep == MachineRepresentation::kBit) { } else if (output_rep == MachineRepresentation::kBit) {
if (output_type->Is(Type::Boolean())) { if (output_type->Is(Type::Boolean())) {
op = simplified()->ChangeBitToTagged(); op = simplified()->ChangeBitToTagged();
...@@ -414,8 +412,7 @@ Node* RepresentationChanger::GetTaggedRepresentationFor( ...@@ -414,8 +412,7 @@ Node* RepresentationChanger::GetTaggedRepresentationFor(
const Operator* op; const Operator* op;
if (output_type->Is(Type::None())) { if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime. // This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here. return jsgraph()->DeadValue();
return jsgraph()->TheHoleConstant();
} else if (output_rep == MachineRepresentation::kBit) { } else if (output_rep == MachineRepresentation::kBit) {
if (output_type->Is(Type::Boolean())) { if (output_type->Is(Type::Boolean())) {
op = simplified()->ChangeBitToTagged(); op = simplified()->ChangeBitToTagged();
...@@ -493,8 +490,7 @@ Node* RepresentationChanger::GetFloat32RepresentationFor( ...@@ -493,8 +490,7 @@ Node* RepresentationChanger::GetFloat32RepresentationFor(
const Operator* op = nullptr; const Operator* op = nullptr;
if (output_type->Is(Type::None())) { if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime. // This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here. return jsgraph()->DeadValue();
return jsgraph()->Float32Constant(0.0f);
} else if (IsWord(output_rep)) { } else if (IsWord(output_rep)) {
if (output_type->Is(Type::Signed32())) { if (output_type->Is(Type::Signed32())) {
// int32 -> float64 -> float32 // int32 -> float64 -> float32
...@@ -554,8 +550,7 @@ Node* RepresentationChanger::GetFloat64RepresentationFor( ...@@ -554,8 +550,7 @@ Node* RepresentationChanger::GetFloat64RepresentationFor(
const Operator* op = nullptr; const Operator* op = nullptr;
if (output_type->Is(Type::None())) { if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime. // This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here. return jsgraph()->DeadValue();
return jsgraph()->Float64Constant(0.0);
} else if (IsWord(output_rep)) { } else if (IsWord(output_rep)) {
if (output_type->Is(Type::Signed32())) { if (output_type->Is(Type::Signed32())) {
op = machine()->ChangeInt32ToFloat64(); op = machine()->ChangeInt32ToFloat64();
...@@ -632,8 +627,7 @@ Node* RepresentationChanger::GetWord32RepresentationFor( ...@@ -632,8 +627,7 @@ Node* RepresentationChanger::GetWord32RepresentationFor(
const Operator* op = nullptr; const Operator* op = nullptr;
if (output_type->Is(Type::None())) { if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime. // This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here. return jsgraph()->DeadValue();
return jsgraph()->Int32Constant(0);
} else if (output_rep == MachineRepresentation::kBit) { } else if (output_rep == MachineRepresentation::kBit) {
return node; // Sloppy comparison -> word32 return node; // Sloppy comparison -> word32
} else if (output_rep == MachineRepresentation::kFloat64) { } else if (output_rep == MachineRepresentation::kFloat64) {
...@@ -769,8 +763,7 @@ Node* RepresentationChanger::GetBitRepresentationFor( ...@@ -769,8 +763,7 @@ Node* RepresentationChanger::GetBitRepresentationFor(
const Operator* op; const Operator* op;
if (output_type->Is(Type::None())) { if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime. // This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here. return jsgraph()->DeadValue();
return jsgraph()->Int32Constant(0);
} else if (output_rep == MachineRepresentation::kTagged || } else if (output_rep == MachineRepresentation::kTagged ||
output_rep == MachineRepresentation::kTaggedPointer) { output_rep == MachineRepresentation::kTaggedPointer) {
if (output_type->Is(Type::BooleanOrNullOrUndefined())) { if (output_type->Is(Type::BooleanOrNullOrUndefined())) {
...@@ -815,8 +808,7 @@ Node* RepresentationChanger::GetWord64RepresentationFor( ...@@ -815,8 +808,7 @@ Node* RepresentationChanger::GetWord64RepresentationFor(
Node* node, MachineRepresentation output_rep, Type* output_type) { Node* node, MachineRepresentation output_rep, Type* output_type) {
if (output_type->Is(Type::None())) { if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime. // This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here. return jsgraph()->DeadValue();
return jsgraph()->Int64Constant(0);
} else if (output_rep == MachineRepresentation::kBit) { } else if (output_rep == MachineRepresentation::kBit) {
return node; // Sloppy comparison -> word64 return node; // Sloppy comparison -> word64
} }
......
...@@ -2955,6 +2955,7 @@ class RepresentationSelector { ...@@ -2955,6 +2955,7 @@ class RepresentationSelector {
case IrOpcode::kArgumentsElementsState: case IrOpcode::kArgumentsElementsState:
case IrOpcode::kArgumentsLengthState: case IrOpcode::kArgumentsLengthState:
case IrOpcode::kRuntimeAbort: case IrOpcode::kRuntimeAbort:
case IrOpcode::kUnreachable:
// All JavaScript operators except JSToNumber have uniform handling. // All JavaScript operators except JSToNumber have uniform handling.
#define OPCODE_CASE(name) case IrOpcode::k##name: #define OPCODE_CASE(name) case IrOpcode::k##name:
JS_SIMPLE_BINOP_LIST(OPCODE_CASE) JS_SIMPLE_BINOP_LIST(OPCODE_CASE)
...@@ -2971,7 +2972,8 @@ class RepresentationSelector { ...@@ -2971,7 +2972,8 @@ class RepresentationSelector {
VisitInputs(node); VisitInputs(node);
// Assume the output is tagged. // Assume the output is tagged.
return SetOutput(node, MachineRepresentation::kTagged); return SetOutput(node, MachineRepresentation::kTagged);
case IrOpcode::kDeadValue:
return SetOutput(node, MachineRepresentation::kNone);
default: default:
V8_Fatal( V8_Fatal(
__FILE__, __LINE__, __FILE__, __LINE__,
......
...@@ -887,6 +887,10 @@ Type* Typer::Visitor::TypeTypeGuard(Node* node) { ...@@ -887,6 +887,10 @@ Type* Typer::Visitor::TypeTypeGuard(Node* node) {
Type* Typer::Visitor::TypeDead(Node* node) { return Type::None(); } Type* Typer::Visitor::TypeDead(Node* node) { return Type::None(); }
Type* Typer::Visitor::TypeDeadValue(Node* node) { return Type::None(); }
Type* Typer::Visitor::TypeUnreachable(Node* node) { UNREACHABLE(); }
// JS comparison operators. // JS comparison operators.
......
...@@ -218,6 +218,11 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -218,6 +218,11 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kDead: case IrOpcode::kDead:
// Dead is never connected to the graph. // Dead is never connected to the graph.
UNREACHABLE(); UNREACHABLE();
case IrOpcode::kDeadValue:
CheckTypeIs(node, Type::None());
break;
case IrOpcode::kUnreachable:
CheckNotTyped(node);
break; break;
case IrOpcode::kBranch: { case IrOpcode::kBranch: {
// Branch uses are IfTrue and IfFalse. // Branch uses are IfTrue and IfFalse.
...@@ -1796,7 +1801,8 @@ void Verifier::VerifyNode(Node* node) { ...@@ -1796,7 +1801,8 @@ void Verifier::VerifyNode(Node* node) {
Node* input = NodeProperties::GetFrameStateInput(node); Node* input = NodeProperties::GetFrameStateInput(node);
DCHECK(input->opcode() == IrOpcode::kFrameState || DCHECK(input->opcode() == IrOpcode::kFrameState ||
input->opcode() == IrOpcode::kStart || input->opcode() == IrOpcode::kStart ||
input->opcode() == IrOpcode::kDead); input->opcode() == IrOpcode::kDead ||
input->opcode() == IrOpcode::kDeadValue);
} }
// Effect inputs should be effect-producing nodes (or sentinels). // Effect inputs should be effect-producing nodes (or sentinels).
for (int i = 0; i < node->op()->EffectInputCount(); i++) { for (int i = 0; i < node->op()->EffectInputCount(); i++) {
...@@ -1821,7 +1827,9 @@ void Verifier::VerifyEdgeInputReplacement(const Edge& edge, ...@@ -1821,7 +1827,9 @@ void Verifier::VerifyEdgeInputReplacement(const Edge& edge,
DCHECK(!NodeProperties::IsEffectEdge(edge) || DCHECK(!NodeProperties::IsEffectEdge(edge) ||
replacement->op()->EffectOutputCount() > 0); replacement->op()->EffectOutputCount() > 0);
DCHECK(!NodeProperties::IsFrameStateEdge(edge) || DCHECK(!NodeProperties::IsFrameStateEdge(edge) ||
replacement->opcode() == IrOpcode::kFrameState); replacement->opcode() == IrOpcode::kFrameState ||
replacement->opcode() == IrOpcode::kDead ||
replacement->opcode() == IrOpcode::kDeadValue);
} }
#endif // DEBUG #endif // DEBUG
......
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