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,
jsgraph_(js_graph),
node_conditions_(zone, js_graph->graph()->NodeCount()),
zone_(zone),
dead_(js_graph->graph()->NewNode(js_graph->common()->Dead())) {
NodeProperties::SetType(dead_, Type::None());
}
dead_(js_graph->Dead()) {}
BranchElimination::~BranchElimination() {}
......
......@@ -348,6 +348,8 @@ ZoneVector<MachineType> const* MachineTypesOf(Operator const* op) {
#define COMMON_CACHED_OP_LIST(V) \
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(IfFalse, 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
explicit CommonOperatorBuilder(Zone* zone);
const Operator* Dead();
const Operator* DeadValue();
const Operator* Unreachable();
const Operator* End(size_t control_input_count);
const Operator* Branch(BranchHint = BranchHint::kNone);
const Operator* IfTrue();
......
......@@ -6,6 +6,7 @@
#include "src/compiler/common-operator.h"
#include "src/compiler/graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
......@@ -18,10 +19,32 @@ DeadCodeElimination::DeadCodeElimination(Editor* editor, Graph* graph,
: AdvancedReducer(editor),
graph_(graph),
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_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) {
switch (node->opcode()) {
case IrOpcode::kEnd:
......@@ -140,13 +163,90 @@ Reduction DeadCodeElimination::RemoveLoopExit(Node* node) {
}
Reduction DeadCodeElimination::ReduceNode(Node* node) {
// If {node} has exactly one control input and this is {Dead},
// replace {node} with {Dead}.
int const effect_input_count = node->op()->EffectInputCount();
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);
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},
// replace {node} with {Dead}.
Node* control = NodeProperties::GetControlInput(node);
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();
}
......
......@@ -16,11 +16,15 @@ 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.
// Propagates {Dead} control and {DeadValue} values through the graph and
// thereby removes dead code. When {DeadValue} hits the effect chain, a crashing
// {Unreachable} node is inserted and the rest of the effect chain is collapsed.
// We detect dead values based on types, pruning uses of DeadValue except for
// 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
: public NON_EXPORTED_BASE(AdvancedReducer) {
public:
......@@ -37,6 +41,9 @@ class V8_EXPORT_PRIVATE DeadCodeElimination final
Reduction ReduceLoopOrMerge(Node* node);
Reduction ReduceLoopExit(Node* node);
Reduction ReduceNode(Node* node);
Reduction ReducePureNode(Node* node);
Reduction ReduceEffectNode(Node* node);
Reduction ReduceGraphTerminator(Node* node);
Reduction RemoveLoopExit(Node* node);
......@@ -45,10 +52,12 @@ class V8_EXPORT_PRIVATE DeadCodeElimination final
Graph* graph() const { return graph_; }
CommonOperatorBuilder* common() const { return common_; }
Node* dead() const { return dead_; }
Node* dead_value() const { return dead_value_; }
Graph* const graph_;
CommonOperatorBuilder* const common_;
Node* const dead_;
Node* const dead_value_;
DISALLOW_COPY_AND_ASSIGN(DeadCodeElimination);
};
......
......@@ -46,6 +46,7 @@ struct BlockEffectControlData {
Node* current_effect = nullptr; // New effect.
Node* current_control = nullptr; // New control.
Node* current_frame_state = nullptr; // New frame state.
bool dead = false; // This control edge can never be taken.
};
class BlockEffectControlMap {
......@@ -76,8 +77,19 @@ struct PendingEffectPhi {
: 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,
BlockEffectControlMap* block_effects) {
BlockEffectControlMap* block_effects, JSGraph* jsgraph) {
// Update all inputs to an effect phi with the effects from the given
// block->effect map.
DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode());
......@@ -88,8 +100,15 @@ void UpdateEffectPhi(Node* node, BasicBlock* block,
BasicBlock* predecessor = block->PredecessorAt(static_cast<size_t>(i));
const BlockEffectControlData& block_effect =
block_effects->For(predecessor, block);
if (input != block_effect.current_effect) {
node->ReplaceInput(i, block_effect.current_effect);
Node* effect = 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,
cond->Kill();
merge->Kill();
}
} // namespace
void EffectControlLinearizer::Run() {
......@@ -330,29 +350,30 @@ void EffectControlLinearizer::Run() {
instr++;
// Iterate over the phis and update the effect phis.
Node* effect = nullptr;
Node* effect_phi = nullptr;
Node* terminate = nullptr;
int predecessor_count = static_cast<int>(block->PredecessorCount());
for (; instr < block->NodeCount(); instr++) {
Node* node = block->NodeAt(instr);
// Only go through the phis and effect phis.
if (node->opcode() == IrOpcode::kEffectPhi) {
// 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.
DCHECK_NE(IrOpcode::kIfException, control->opcode());
effect = node;
// Make sure we update the inputs to the incoming blocks' effects.
if (HasIncomingBackEdges(block)) {
// In case of loops, we do not update the effect phi immediately
// because the back predecessor has not been handled yet. We just
// record the effect phi for later processing.
pending_effect_phis.push_back(PendingEffectPhi(node, block));
} else {
UpdateEffectPhi(node, block, &block_effects);
}
effect_phi = node;
} else if (node->opcode() == IrOpcode::kPhi) {
// Just skip phis.
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;
......@@ -361,9 +382,28 @@ void EffectControlLinearizer::Run() {
}
}
if (effect_phi) {
// Make sure we update the inputs to the incoming blocks' effects.
if (HasIncomingBackEdges(block)) {
// In case of loops, we do not update the effect phi immediately
// because the back predecessor has not been handled yet. We just
// record the effect phi for later processing.
pending_effect_phis.push_back(PendingEffectPhi(effect_phi, block));
} else {
UpdateEffectPhi(effect_phi, block, &block_effects, jsgraph());
}
}
Node* effect = effect_phi;
if (effect == nullptr) {
// 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()) {
// Start block => effect is start.
DCHECK_EQ(graph()->start(), control);
......@@ -375,12 +415,14 @@ void EffectControlLinearizer::Run() {
effect = nullptr;
} else {
// If all the predecessors have the same effect, we can use it as our
// current effect.
effect =
block_effects.For(block->PredecessorAt(0), block).current_effect;
for (size_t i = 1; i < block->PredecessorCount(); ++i) {
if (block_effects.For(block->PredecessorAt(i), block)
.current_effect != effect) {
// current effect. If there are dead incoming control paths, we always
// insert an effect phi to break the effect chain there later in
// UpdateEffectPhi().
for (size_t i = 0; i < block->PredecessorCount(); ++i) {
const BlockEffectControlData& data =
block_effects.For(block->PredecessorAt(i), block);
if (!effect) effect = data.current_effect;
if (data.current_effect != effect || data.dead) {
effect = nullptr;
break;
}
......@@ -399,7 +441,7 @@ void EffectControlLinearizer::Run() {
if (control->opcode() == IrOpcode::kLoop) {
pending_effect_phis.push_back(PendingEffectPhi(effect, block));
} else {
UpdateEffectPhi(effect, block, &block_effects);
UpdateEffectPhi(effect, block, &block_effects, jsgraph());
}
} else if (control->opcode() == IrOpcode::kIfException) {
// The IfException is connected into the effect chain, so we need
......@@ -480,7 +522,7 @@ void EffectControlLinearizer::Run() {
// during the first pass (because they could have incoming back edges).
for (const PendingEffectPhi& pending_effect_phi : pending_effect_phis) {
UpdateEffectPhi(pending_effect_phi.effect_phi, pending_effect_phi.block,
&block_effects);
&block_effects, jsgraph());
}
for (BasicBlock* pending_block_control : pending_block_controls) {
UpdateBlockControl(pending_block_control, &block_effects);
......@@ -498,6 +540,20 @@ void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state,
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
// effect chain before we are allowed to place another eager deoptimization
// point. We zap the frame state to ensure this invariant is maintained.
......
......@@ -1120,6 +1120,9 @@ void InstructionSelector::VisitNode(Node* node) {
case IrOpcode::kDebugBreak:
VisitDebugBreak(node);
return;
case IrOpcode::kUnreachable:
VisitUnreachable(node);
return;
case IrOpcode::kComment:
VisitComment(node);
return;
......@@ -2764,6 +2767,11 @@ void InstructionSelector::VisitDebugBreak(Node* node) {
Emit(kArchDebugBreak, g.NoOutput());
}
void InstructionSelector::VisitUnreachable(Node* node) {
OperandGenerator g(this);
Emit(kArchDebugBreak, g.NoOutput());
}
void InstructionSelector::VisitComment(Node* node) {
OperandGenerator g(this);
InstructionOperand operand(g.UseImmediate(node));
......
......@@ -348,6 +348,7 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
void VisitReturn(Node* ret);
void VisitThrow(Node* node);
void VisitRetain(Node* node);
void VisitUnreachable(Node* node);
void EmitPrepareArguments(ZoneVector<compiler::PushParameter>* arguments,
const CallDescriptor* descriptor, Node* node);
......
......@@ -305,6 +305,9 @@ Node* JSGraph::Dead() {
return CACHED(kDead, graph()->NewNode(common()->Dead()));
}
Node* JSGraph::DeadValue() {
return CACHED(kDeadValue, graph()->NewNode(common()->DeadValue()));
}
void JSGraph::GetCachedNodes(NodeVector* nodes) {
cache_.GetCachedNodes(nodes);
......
......@@ -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.
Node* Dead();
// Sentinel for a value resulting from unreachable computations.
Node* DeadValue();
CommonOperatorBuilder* common() const { return common_; }
JSOperatorBuilder* javascript() const { return javascript_; }
SimplifiedOperatorBuilder* simplified() const { return simplified_; }
......@@ -193,6 +196,7 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
kEmptyStateValues,
kSingleDeadTypedStateValues,
kDead,
kDeadValue,
kNumCachedNodes // Must remain last.
};
......
......@@ -100,6 +100,7 @@ void MemoryOptimizer::VisitNode(Node* node, AllocationState const* state) {
case IrOpcode::kRetain:
case IrOpcode::kUnsafePointerAdd:
case IrOpcode::kDebugBreak:
case IrOpcode::kUnreachable:
return VisitOtherEffect(node, state);
default:
break;
......
......@@ -79,6 +79,8 @@
#define COMMON_OP_LIST(V) \
CONSTANT_OP_LIST(V) \
INNER_OP_LIST(V) \
V(Unreachable) \
V(DeadValue) \
V(Dead)
// Opcodes for JavaScript operators.
......
......@@ -1168,50 +1168,48 @@ struct EffectControlLinearizationPhase {
static const char* phase_name() { return "effect linearization"; }
void Run(PipelineData* data, Zone* temp_zone) {
// The scheduler requires the graphs to be trimmed, so trim now.
// TODO(jarin) Remove the trimming once the scheduler can handle untrimmed
// graphs.
GraphTrimmer trimmer(temp_zone, data->graph());
NodeVector roots(temp_zone);
data->jsgraph()->GetCachedNodes(&roots);
trimmer.TrimGraph(roots.begin(), roots.end());
// Schedule the graph without node splitting so that we can
// fix the effect and control flow for nodes with low-level side
// effects (such as changing representation to tagged or
// 'floating' allocation regions.)
Schedule* schedule = Scheduler::ComputeSchedule(temp_zone, data->graph(),
Scheduler::kTempSchedule);
if (FLAG_turbo_verify) ScheduleVerifier::Run(schedule);
TraceSchedule(data->info(), schedule);
// Post-pass for wiring the control/effects
// - connect allocating representation changes into the control&effect
// chains and lower them,
// - get rid of the region markers,
// - introduce effect phis and rewire effects to get SSA again.
EffectControlLinearizer linearizer(data->jsgraph(), schedule, temp_zone,
data->source_positions());
linearizer.Run();
}
};
// The store-store elimination greatly benefits from 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);
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common());
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
data->common(), data->machine());
AddReducer(data, &graph_reducer, &dead_code_elimination);
AddReducer(data, &graph_reducer, &common_reducer);
graph_reducer.ReduceGraph();
{
// The scheduler requires the graphs to be trimmed, so trim now.
// TODO(jarin) Remove the trimming once the scheduler can handle untrimmed
// graphs.
GraphTrimmer trimmer(temp_zone, data->graph());
NodeVector roots(temp_zone);
data->jsgraph()->GetCachedNodes(&roots);
trimmer.TrimGraph(roots.begin(), roots.end());
// Schedule the graph without node splitting so that we can
// fix the effect and control flow for nodes with low-level side
// effects (such as changing representation to tagged or
// 'floating' allocation regions.)
Schedule* schedule = Scheduler::ComputeSchedule(temp_zone, data->graph(),
Scheduler::kTempSchedule);
if (FLAG_turbo_verify) ScheduleVerifier::Run(schedule);
TraceSchedule(data->info(), schedule);
// Post-pass for wiring the control/effects
// - connect allocating representation changes into the control&effect
// chains and lower them,
// - get rid of the region markers,
// - introduce effect phis and rewire effects to get SSA again.
EffectControlLinearizer linearizer(data->jsgraph(), schedule, temp_zone,
data->source_positions());
linearizer.Run();
}
{
// The {EffectControlLinearizer} might leave {Dead} nodes behind, so we
// run {DeadCodeElimination} to prune these parts of the graph.
// Also, the following store-store elimination phase greatly benefits from
// doing a common operator reducer and dead code elimination just before
// it, to eliminate conditional deopts with a constant condition.
JSGraphReducer graph_reducer(data->jsgraph(), temp_zone);
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common());
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
data->common(), data->machine());
AddReducer(data, &graph_reducer, &dead_code_elimination);
AddReducer(data, &graph_reducer, &common_reducer);
graph_reducer.ReduceGraph();
}
}
};
......@@ -1721,9 +1719,6 @@ bool PipelineImpl::OptimizeGraph(Linkage* linkage) {
Run<EffectControlLinearizationPhase>();
RunPrintAndVerify("Effect and control linearized", true);
Run<DeadCodeEliminationPhase>();
RunPrintAndVerify("Dead code elimination", true);
if (FLAG_turbo_store_elimination) {
Run<StoreStoreEliminationPhase>();
RunPrintAndVerify("Store-store elimination", true);
......
......@@ -217,8 +217,7 @@ Node* RepresentationChanger::GetTaggedSignedRepresentationFor(
const Operator* op;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here.
return jsgraph()->Constant(0);
return jsgraph()->DeadValue();
} else if (IsWord(output_rep)) {
if (output_type->Is(Type::Signed31())) {
op = simplified()->ChangeInt31ToTaggedSigned();
......@@ -336,8 +335,7 @@ Node* RepresentationChanger::GetTaggedPointerRepresentationFor(
Operator const* op;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here.
return jsgraph()->TheHoleConstant();
return jsgraph()->DeadValue();
} else if (output_rep == MachineRepresentation::kBit) {
if (output_type->Is(Type::Boolean())) {
op = simplified()->ChangeBitToTagged();
......@@ -414,8 +412,7 @@ Node* RepresentationChanger::GetTaggedRepresentationFor(
const Operator* op;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here.
return jsgraph()->TheHoleConstant();
return jsgraph()->DeadValue();
} else if (output_rep == MachineRepresentation::kBit) {
if (output_type->Is(Type::Boolean())) {
op = simplified()->ChangeBitToTagged();
......@@ -493,8 +490,7 @@ Node* RepresentationChanger::GetFloat32RepresentationFor(
const Operator* op = nullptr;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here.
return jsgraph()->Float32Constant(0.0f);
return jsgraph()->DeadValue();
} else if (IsWord(output_rep)) {
if (output_type->Is(Type::Signed32())) {
// int32 -> float64 -> float32
......@@ -554,8 +550,7 @@ Node* RepresentationChanger::GetFloat64RepresentationFor(
const Operator* op = nullptr;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here.
return jsgraph()->Float64Constant(0.0);
return jsgraph()->DeadValue();
} else if (IsWord(output_rep)) {
if (output_type->Is(Type::Signed32())) {
op = machine()->ChangeInt32ToFloat64();
......@@ -632,8 +627,7 @@ Node* RepresentationChanger::GetWord32RepresentationFor(
const Operator* op = nullptr;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here.
return jsgraph()->Int32Constant(0);
return jsgraph()->DeadValue();
} else if (output_rep == MachineRepresentation::kBit) {
return node; // Sloppy comparison -> word32
} else if (output_rep == MachineRepresentation::kFloat64) {
......@@ -769,8 +763,7 @@ Node* RepresentationChanger::GetBitRepresentationFor(
const Operator* op;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here.
return jsgraph()->Int32Constant(0);
return jsgraph()->DeadValue();
} else if (output_rep == MachineRepresentation::kTagged ||
output_rep == MachineRepresentation::kTaggedPointer) {
if (output_type->Is(Type::BooleanOrNullOrUndefined())) {
......@@ -815,8 +808,7 @@ Node* RepresentationChanger::GetWord64RepresentationFor(
Node* node, MachineRepresentation output_rep, Type* output_type) {
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
// We just provide a dummy value here.
return jsgraph()->Int64Constant(0);
return jsgraph()->DeadValue();
} else if (output_rep == MachineRepresentation::kBit) {
return node; // Sloppy comparison -> word64
}
......
......@@ -2955,6 +2955,7 @@ class RepresentationSelector {
case IrOpcode::kArgumentsElementsState:
case IrOpcode::kArgumentsLengthState:
case IrOpcode::kRuntimeAbort:
case IrOpcode::kUnreachable:
// All JavaScript operators except JSToNumber have uniform handling.
#define OPCODE_CASE(name) case IrOpcode::k##name:
JS_SIMPLE_BINOP_LIST(OPCODE_CASE)
......@@ -2971,7 +2972,8 @@ class RepresentationSelector {
VisitInputs(node);
// Assume the output is tagged.
return SetOutput(node, MachineRepresentation::kTagged);
case IrOpcode::kDeadValue:
return SetOutput(node, MachineRepresentation::kNone);
default:
V8_Fatal(
__FILE__, __LINE__,
......
......@@ -887,6 +887,10 @@ Type* Typer::Visitor::TypeTypeGuard(Node* node) {
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.
......
......@@ -218,6 +218,11 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kDead:
// Dead is never connected to the graph.
UNREACHABLE();
case IrOpcode::kDeadValue:
CheckTypeIs(node, Type::None());
break;
case IrOpcode::kUnreachable:
CheckNotTyped(node);
break;
case IrOpcode::kBranch: {
// Branch uses are IfTrue and IfFalse.
......@@ -1796,7 +1801,8 @@ void Verifier::VerifyNode(Node* node) {
Node* input = NodeProperties::GetFrameStateInput(node);
DCHECK(input->opcode() == IrOpcode::kFrameState ||
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).
for (int i = 0; i < node->op()->EffectInputCount(); i++) {
......@@ -1821,7 +1827,9 @@ void Verifier::VerifyEdgeInputReplacement(const Edge& edge,
DCHECK(!NodeProperties::IsEffectEdge(edge) ||
replacement->op()->EffectOutputCount() > 0);
DCHECK(!NodeProperties::IsFrameStateEdge(edge) ||
replacement->opcode() == IrOpcode::kFrameState);
replacement->opcode() == IrOpcode::kFrameState ||
replacement->opcode() == IrOpcode::kDead ||
replacement->opcode() == IrOpcode::kDeadValue);
}
#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