Commit e1cdda25 authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[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: 's avatarJaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48208}
parent 6e68a28b
......@@ -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,70 @@ 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 NoChange();
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();
}
......
......@@ -16,11 +16,14 @@ 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} node for safety. 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 +40,8 @@ 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 RemoveLoopExit(Node* node);
......@@ -45,10 +50,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,14 @@ struct PendingEffectPhi {
: effect_phi(effect_phi), block(block) {}
};
Node* InsertUnreachable(Node* effect, Node* control, JSGraph* jsgraph) {
if (effect->opcode() == IrOpcode::kUnreachable) return effect;
return jsgraph->graph()->NewNode(jsgraph->common()->Unreachable(), effect,
control);
}
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 +95,12 @@ 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) {
effect = InsertUnreachable(effect, block_effect.current_control, jsgraph);
}
if (input != effect) {
node->ReplaceInput(i, effect);
}
}
}
......@@ -303,6 +314,31 @@ void TryCloneBranch(Node* node, BasicBlock* block, Zone* temp_zone,
cond->Kill();
merge->Kill();
}
Node* SentinelValue(JSGraph* graph, MachineRepresentation representation) {
switch (representation) {
case MachineRepresentation::kTagged:
case MachineRepresentation::kTaggedSigned:
return graph->SmiConstant(0xdead);
case MachineRepresentation::kTaggedPointer:
return graph->OptimizedOutConstant();
case MachineRepresentation::kFloat32:
return graph->Float32Constant(bit_cast<float>(0xdeaddead));
case MachineRepresentation::kFloat64:
return graph->Float32Constant(bit_cast<double>(0xdeaddeaddeaddead));
case MachineRepresentation::kBit:
case MachineRepresentation::kWord8:
return graph->Int32Constant(0);
case MachineRepresentation::kWord16:
case MachineRepresentation::kWord32:
return graph->Int32Constant(0xdead);
case MachineRepresentation::kWord64:
return graph->Int64Constant(0xdead);
default:
UNREACHABLE();
}
}
} // namespace
void EffectControlLinearizer::Run() {
......@@ -330,29 +366,38 @@ 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) {
MachineRepresentation representation =
PhiRepresentationOf(node->op());
NodeProperties::ReplaceValueInput(
node, SentinelValue(jsgraph(), representation), i);
BlockEffectControlData* data =
&block_effects.For(block->predecessors()[i], block);
data->dead = true;
if (data->current_effect) {
data->current_effect = InsertUnreachable(
data->current_effect, data->current_control, jsgraph());
}
}
}
} else if (node->opcode() == IrOpcode::kTerminate) {
DCHECK_NULL(terminate);
terminate = node;
......@@ -361,6 +406,19 @@ 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));
......@@ -399,7 +457,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 +538,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 +556,24 @@ 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}. After this assertion, it is safe to replace {DeadValue} with
// an arbitrary value.
if ((node->opcode() == IrOpcode::kBranch ||
node->opcode() == IrOpcode::kSwitch) &&
NodeProperties::GetValueInput(node, 0)->opcode() ==
IrOpcode::kDeadValue) {
*effect = InsertUnreachable(*effect, *control, jsgraph());
node->ReplaceInput(0, jsgraph()->Int32Constant(0));
}
if (node->opcode() == IrOpcode::kReturn &&
NodeProperties::GetValueInput(node, 1)->opcode() ==
IrOpcode::kDeadValue) {
*effect = InsertUnreachable(*effect, *control, jsgraph());
node->ReplaceInput(1, jsgraph()->SmiConstant(0));
}
// 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.
......
......@@ -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
}
......
......@@ -2905,6 +2905,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)
......@@ -2921,7 +2922,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__,
......
......@@ -873,6 +873,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.
......@@ -1786,7 +1791,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++) {
......@@ -1811,7 +1817,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