[TurboFan] Diagnostic code to track down bug in representation selection

We need to characterize the types of dead (IrOpcode::kDead) nodes
introduced in compilation phases prior to representation selection.
Normally, a dead node isn't expected at the start of this phase. The
question is, which phase introduced the dead node and failed to
deal with it properly?

Bug: chromium:780658
Change-Id: Ief5b45480bb7d704a2d09dafd60b5d389e0fd42e
Reviewed-on: https://chromium-review.googlesource.com/765968
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49328}
This commit is contained in:
Mike Stanton 2017-11-13 15:12:26 +01:00 committed by Commit Bot
parent 2bc09c95fb
commit f010b28fbe
14 changed files with 107 additions and 29 deletions

View File

@ -3191,7 +3191,7 @@ Node* BytecodeGraphBuilder::MakeNode(const Operator* op, int value_input_count,
// The frame state will be inserted later. Here we misuse the {Dead} node
// as a sentinel to be later overwritten with the real frame state by the
// calls to {PrepareFrameState} within individual visitor methods.
*current_input++ = jsgraph()->Dead();
*current_input++ = jsgraph()->Dead(JSGraph::DeadCustomer::GraphBuilding);
}
if (has_effect) {
*current_input++ = environment()->GetEffectDependency();

View File

@ -390,7 +390,9 @@ void EffectControlLinearizer::Run() {
// The input blocks do not have the same effect. We have
// to create an effect phi node.
inputs_buffer.clear();
inputs_buffer.resize(block->PredecessorCount(), jsgraph()->Dead());
inputs_buffer.resize(
block->PredecessorCount(),
jsgraph()->Dead(JSGraph::DeadCustomer::Unspecified));
inputs_buffer.push_back(control);
effect = graph()->NewNode(
common()->EffectPhi(static_cast<int>(block->PredecessorCount())),

View File

@ -74,7 +74,7 @@ Reduction EscapeAnalysisReducer::Reduce(Node* node) {
DCHECK(node->opcode() != IrOpcode::kAllocate &&
node->opcode() != IrOpcode::kFinishRegion);
DCHECK_NE(replacement, node);
if (replacement != jsgraph()->Dead()) {
if (replacement != jsgraph()->Dead(JSGraph::DeadCustomer::EscapeAnalysis)) {
replacement = MaybeGuard(node, replacement);
}
RelaxEffectsAndControls(node);
@ -178,7 +178,7 @@ Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect,
Node* field =
analysis_result().GetVirtualObjectField(vobject, offset, effect);
CHECK_NOT_NULL(field);
if (field != jsgraph()->Dead()) {
if (field != jsgraph()->Dead(JSGraph::DeadCustomer::EscapeAnalysis)) {
inputs.push_back(ReduceDeoptState(field, effect, deduplicator));
}
}

View File

@ -216,7 +216,10 @@ class EscapeAnalysisTracker : public ZoneObject {
replacement->id());
}
void MarkForDeletion() { SetReplacement(tracker_->jsgraph_->Dead()); }
void MarkForDeletion() {
SetReplacement(
tracker_->jsgraph_->Dead(JSGraph::DeadCustomer::EscapeAnalysis));
}
~Scope() {
if (replacement_ != tracker_->replacements_[current_node()] ||
@ -430,7 +433,10 @@ VariableTracker::State VariableTracker::MergeInputs(Node* effect_phi) {
// must have been created by a previous reduction of this [effect_phi].
for (int i = 0; i < arity; ++i) {
NodeProperties::ReplaceValueInput(
old_value, buffer_[i] ? buffer_[i] : graph_->Dead(), i);
old_value,
buffer_[i] ? buffer_[i]
: graph_->Dead(JSGraph::DeadCustomer::EscapeAnalysis),
i);
// This change cannot affect the rest of the reducer, so there is no
// need to trigger additional revisitations.
}
@ -540,7 +546,8 @@ void ReduceNode(const Operator* op, EscapeAnalysisTracker::Scope* current,
if (const VirtualObject* vobject = current->InitVirtualObject(size_int)) {
// Initialize with dead nodes as a sentinel for uninitialized memory.
for (Variable field : *vobject) {
current->Set(field, jsgraph->Dead());
current->Set(field,
jsgraph->Dead(JSGraph::DeadCustomer::EscapeAnalysis));
}
}
break;

View File

@ -301,10 +301,30 @@ Node* JSGraph::SingleDeadTypedStateValues() {
SparseInputMask(SparseInputMask::kEndMarker << 1))));
}
Node* JSGraph::Dead() {
return CACHED(kDead, graph()->NewNode(common()->Dead()));
Node* JSGraph::Dead(DeadCustomer which) {
switch (which) {
#define RETURN_CUSTOMER_NODE(customer) \
case customer: \
return CACHED(kDead##customer, graph()->NewNode(common()->Dead()));
CUSTOMER_LIST(RETURN_CUSTOMER_NODE)
#undef RETURN_CUSTOMER_NODE
default:
V8_Fatal(__FILE__, __LINE__, "Unexpected Which dead node detected");
}
return nullptr;
}
const char* JSGraph::WhichDeadNode(Node* node) {
#define SEARCH_FOR_DEAD_CUSTOMER(name) \
if (node == cached_nodes_[kDead##name]) { \
return #name; \
}
CUSTOMER_LIST(SEARCH_FOR_DEAD_CUSTOMER)
#undef SEARCH_FOR_DEAD_CUSTOMER
return "Uncached dead node";
}
void JSGraph::GetCachedNodes(NodeVector* nodes) {
cache_.GetCachedNodes(nodes);
@ -315,6 +335,8 @@ void JSGraph::GetCachedNodes(NodeVector* nodes) {
}
}
#undef CACHED
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -152,8 +152,26 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
// dead accumulator.
Node* SingleDeadTypedStateValues();
// Create a control node that serves as dependency for dead nodes.
Node* Dead();
// Create a control node that serves as dependency for dead nodes.
// TODO(mvstanton): We distinguish between different types of dead nodes
// to track down bug chromium:780658. After fixing this bug, the
// enum can be removed.
#define CUSTOMER_LIST(V) \
V(GraphBuilding) \
V(TypeHint) \
V(Inlining) \
V(ContextSpecialization) \
V(EscapeAnalysis) \
V(GraphReducer) \
V(Unspecified)
enum DeadCustomer {
#define DEFINE_DEAD_CUSTOMER_ENUM(name) name,
CUSTOMER_LIST(DEFINE_DEAD_CUSTOMER_ENUM)
#undef DEFINE_DEAD_CUSTOMER_ENUM
};
Node* Dead(DeadCustomer which);
const char* WhichDeadNode(Node* node);
CommonOperatorBuilder* common() const { return common_; }
JSOperatorBuilder* javascript() const { return javascript_; }
@ -195,8 +213,10 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
kNaNConstant,
kEmptyStateValues,
kSingleDeadTypedStateValues,
kDead,
kNumCachedNodes // Must remain last.
#define DEFINE_CACHED_DEAD_CUSTOMER_ENUM(name) kDead##name,
CUSTOMER_LIST(DEFINE_CACHED_DEAD_CUSTOMER_ENUM)
#undef DEFINE_CACHED_DEAD_CUSTOMER_ENUM
kNumCachedNodes // Must remain last.
};
Isolate* isolate_;

View File

@ -540,11 +540,15 @@ bool JSInliningHeuristic::TryReuseDispatch(Node* node, Node* callee,
}
// Mark the control inputs dead, so that we can kill the merge.
node->ReplaceInput(input_count - 1, jsgraph()->Dead());
callee->ReplaceInput(num_calls, jsgraph()->Dead());
effect_phi->ReplaceInput(num_calls, jsgraph()->Dead());
node->ReplaceInput(input_count - 1,
jsgraph()->Dead(JSGraph::DeadCustomer::Inlining));
callee->ReplaceInput(num_calls,
jsgraph()->Dead(JSGraph::DeadCustomer::Inlining));
effect_phi->ReplaceInput(num_calls,
jsgraph()->Dead(JSGraph::DeadCustomer::Inlining));
if (checkpoint) {
checkpoint->ReplaceInput(2, jsgraph()->Dead());
checkpoint->ReplaceInput(2,
jsgraph()->Dead(JSGraph::DeadCustomer::Inlining));
}
merge->Kill();

View File

@ -179,7 +179,7 @@ Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context,
control_output);
} else {
ReplaceWithValue(exception_target, exception_target, exception_target,
jsgraph()->Dead());
jsgraph()->Dead(JSGraph::DeadCustomer::Inlining));
}
}
@ -224,8 +224,9 @@ Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context,
ReplaceWithValue(call, value_output, effect_output, control_output);
return Changed(value_output);
} else {
ReplaceWithValue(call, jsgraph()->Dead(), jsgraph()->Dead(),
jsgraph()->Dead());
ReplaceWithValue(call, jsgraph()->Dead(JSGraph::DeadCustomer::Inlining),
jsgraph()->Dead(JSGraph::DeadCustomer::Inlining),
jsgraph()->Dead(JSGraph::DeadCustomer::Inlining));
return Changed(call);
}
}

View File

@ -890,7 +890,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// Generate the final merge point for all (polymorphic) branches.
int const control_count = static_cast<int>(controls.size());
if (control_count == 0) {
value = effect = control = jsgraph()->Dead();
value = effect = control =
jsgraph()->Dead(JSGraph::DeadCustomer::ContextSpecialization);
} else if (control_count == 1) {
value = values.front();
effect = effects.front();
@ -976,7 +977,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
NamedAccess const& p = NamedAccessOf(node->op());
Node* const receiver = NodeProperties::GetValueInput(node, 0);
Node* const value = jsgraph()->Dead();
Node* const value =
jsgraph()->Dead(JSGraph::DeadCustomer::ContextSpecialization);
// Check if we have a constant receiver.
HeapObjectMatcher m(receiver);
@ -1243,7 +1245,8 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// Generate the final merge point for all (polymorphic) branches.
int const control_count = static_cast<int>(controls.size());
if (control_count == 0) {
value = effect = control = jsgraph()->Dead();
value = effect = control =
jsgraph()->Dead(JSGraph::DeadCustomer::ContextSpecialization);
} else if (control_count == 1) {
value = values.front();
effect = effects.front();
@ -1442,7 +1445,7 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) {
PropertyAccess const& p = PropertyAccessOf(node->op());
Node* receiver = NodeProperties::GetValueInput(node, 0);
Node* name = NodeProperties::GetValueInput(node, 1);
Node* value = jsgraph()->Dead();
Node* value = jsgraph()->Dead(JSGraph::DeadCustomer::ContextSpecialization);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);

View File

@ -428,7 +428,7 @@ Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus& nexus, Node* effect,
if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) {
Node* deoptimize = jsgraph()->graph()->NewNode(
jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason),
jsgraph()->Dead(), effect, control);
jsgraph()->Dead(JSGraph::DeadCustomer::TypeHint), effect, control);
Node* frame_state = NodeProperties::FindFrameStateBefore(deoptimize);
deoptimize->ReplaceInput(0, frame_state);
return deoptimize;

View File

@ -664,7 +664,8 @@ class SourcePositionWrapper final : public Reducer {
class JSGraphReducer final : public GraphReducer {
public:
JSGraphReducer(JSGraph* jsgraph, Zone* zone)
: GraphReducer(zone, jsgraph->graph(), jsgraph->Dead()) {}
: GraphReducer(zone, jsgraph->graph(),
jsgraph->Dead(JSGraph::DeadCustomer::GraphReducer)) {}
~JSGraphReducer() final {}
};

View File

@ -206,6 +206,16 @@ class InputUseInfos {
#endif // DEBUG
// TODO(mvstanton): Remove after fixing chromium:780658.
static void UnexpectedDeadOpcode(const char* file, int line,
const char* deadCode, Node* node) {
// Record the deadcode in the stack to ease minidump debugging.
const char* format_string = "Unexpected dead opcode %s, node #%i\n.";
char buffer[256];
snprintf(buffer, sizeof(buffer), format_string, deadCode, node->id());
V8_Fatal(file, line, format_string, deadCode, node->id());
}
bool CanOverflowSigned32(const Operator* op, Type* left, Type* right,
Zone* type_zone) {
// We assume the inputs are checked Signed32 (or known statically
@ -3047,6 +3057,11 @@ class RepresentationSelector {
// Assume the output is tagged.
return SetOutput(node, MachineRepresentation::kTagged);
case IrOpcode::kDead: {
const char* deadVersion = jsgraph_->WhichDeadNode(node);
UnexpectedDeadOpcode(__FILE__, __LINE__, deadVersion, node);
break;
}
default:
V8_Fatal(
__FILE__, __LINE__,
@ -3092,7 +3107,7 @@ class RepresentationSelector {
DCHECK_EQ(0, node->op()->EffectOutputCount());
}
node->ReplaceUses(jsgraph_->Dead());
node->ReplaceUses(jsgraph_->Dead(JSGraph::DeadCustomer::Unspecified));
node->NullAllInputs(); // The {node} is now dead.
}

View File

@ -94,7 +94,9 @@ WasmGraphBuilder::WasmGraphBuilder(
DCHECK_NOT_NULL(jsgraph_);
}
Node* WasmGraphBuilder::Error() { return jsgraph()->Dead(); }
Node* WasmGraphBuilder::Error() {
return jsgraph()->Dead(JSGraph::DeadCustomer::Unspecified);
}
Node* WasmGraphBuilder::Start(unsigned params) {
Node* start = graph()->NewNode(jsgraph()->common()->Start(params));

View File

@ -27,7 +27,8 @@ class BranchEliminationTest : public GraphTest {
JSOperatorBuilder javascript(zone());
JSGraph jsgraph(isolate(), graph(), common(), &javascript, nullptr,
machine());
GraphReducer graph_reducer(zone(), graph(), jsgraph.Dead());
GraphReducer graph_reducer(
zone(), graph(), jsgraph.Dead(JSGraph::DeadCustomer::Unspecified));
BranchElimination branch_condition_elimination(&graph_reducer, &jsgraph,
zone());
graph_reducer.AddReducer(&branch_condition_elimination);