Commit 4ea4e729 authored by bmeurer's avatar bmeurer Committed by Commit Bot

[turbofan] Replace uninitialized JSCall nodes with SOFT deopt.

Uninitialized property accesses are replaced with SOFT deopts in
TurboFan, but uninitialized JSCall nodes are not, and instead they
just stick around and are also not being inlined because the heurstic
in TurboFan doesn't consider those candidates since their call frequency
is below the threshold. This unifies the behavior and also replaces
uninitialized calls with SOFT deopts, addressing some inconsistency in
optimization behavior as discovered by Brian White of Node for example
here: https://twitter.com/mscdexdotexe/status/879005026202640385

R=jarin@chromium.org
BUG=v8:4551, v8:5267

Review-Url: https://codereview.chromium.org/2956843002
Cr-Commit-Position: refs/heads/master@{#46231}
parent 68c6ce67
......@@ -1374,10 +1374,17 @@ void BytecodeGraphBuilder::BuildCall(TailCallMode tail_call_mode,
VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
CallFrequency frequency = ComputeCallFrequency(slot_id);
const Operator* call = javascript()->Call(arg_count, frequency, feedback,
receiver_mode, tail_call_mode);
Node* value = ProcessCallArguments(call, args, static_cast<int>(arg_count));
environment()->BindAccumulator(value, Environment::kAttachFrameState);
const Operator* op = javascript()->Call(arg_count, frequency, feedback,
receiver_mode, tail_call_mode);
Node* node = nullptr;
if (Node* simplified = TryBuildSimplifiedCall(
op, args, static_cast<int>(arg_count), feedback.slot())) {
if (environment() == nullptr) return;
node = simplified;
} else {
node = ProcessCallArguments(op, args, static_cast<int>(arg_count));
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::BuildCallVarArgs(TailCallMode tail_call_mode,
......@@ -2663,6 +2670,25 @@ Node* BytecodeGraphBuilder::TryBuildSimplifiedToPrimitiveToString(
return nullptr;
}
Node* BytecodeGraphBuilder::TryBuildSimplifiedCall(const Operator* op,
Node* const* args,
int arg_count,
FeedbackSlot slot) {
// TODO(mstarzinger,6112): This is a workaround for OSR loop entries being
// pruned from the graph by a soft-deopt. It can happen that a CallIC that
// control-dominates the OSR entry is still in "uninitialized" state.
if (!osr_ast_id_.IsNone()) return nullptr;
Node* effect = environment()->GetEffectDependency();
Node* control = environment()->GetControlDependency();
Reduction early_reduction = type_hint_lowering().ReduceCallOperation(
op, args, arg_count, effect, control, slot);
if (early_reduction.Changed()) {
ApplyEarlyReduction(early_reduction);
return early_reduction.replacement();
}
return nullptr;
}
Node* BytecodeGraphBuilder::TryBuildSimplifiedLoadNamed(const Operator* op,
Node* receiver,
FeedbackSlot slot) {
......
......@@ -188,6 +188,8 @@ class BytecodeGraphBuilder {
FeedbackSlot slot);
Node* TryBuildSimplifiedToNumber(Node* input, FeedbackSlot slot);
Node* TryBuildSimplifiedToPrimitiveToString(Node* input, FeedbackSlot slot);
Node* TryBuildSimplifiedCall(const Operator* op, Node* const* args,
int arg_count, FeedbackSlot slot);
Node* TryBuildSimplifiedLoadNamed(const Operator* op, Node* receiver,
FeedbackSlot slot);
Node* TryBuildSimplifiedLoadKeyed(const Operator* op, Node* receiver,
......
......@@ -949,27 +949,22 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
if (!p.feedback().IsValid()) return NoChange();
CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
if (nexus.IsUninitialized()) {
// TODO(turbofan): Tail-calling to a CallIC stub is not supported.
if (p.tail_call_mode() == TailCallMode::kAllow) return NoChange();
// Insert a CallIC here to collect feedback for uninitialized calls.
int const arg_count = static_cast<int>(p.arity() - 2);
Callable callable = CodeFactory::CallIC(isolate(), p.convert_mode());
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), arg_count + 1,
flags);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* stub_arity = jsgraph()->Constant(arg_count);
Node* slot_index =
jsgraph()->Constant(FeedbackVector::GetIndex(p.feedback().slot()));
Node* feedback_vector = jsgraph()->HeapConstant(p.feedback().vector());
node->InsertInput(graph()->zone(), 0, stub_code);
node->InsertInput(graph()->zone(), 2, stub_arity);
node->InsertInput(graph()->zone(), 3, slot_index);
node->InsertInput(graph()->zone(), 4, feedback_vector);
NodeProperties::ChangeOp(node, common()->Call(desc));
return Changed(node);
if (flags() & kBailoutOnUninitialized) {
// Introduce a SOFT deopt if the call {node} wasn't executed so far.
Node* frame_state = NodeProperties::FindFrameStateBefore(node);
Node* deoptimize = graph()->NewNode(
common()->Deoptimize(
DeoptimizeKind::kSoft,
DeoptimizeReason::kInsufficientTypeFeedbackForCall),
frame_state, effect, control);
// TODO(bmeurer): This should be on the AdvancedReducer somehow.
NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
Revisit(graph()->end());
node->TrimInputCount(0);
NodeProperties::ChangeOp(node, common()->Dead());
return Changed(node);
}
return NoChange();
}
Handle<Object> feedback(nexus.GetFeedback(), isolate());
......
......@@ -28,11 +28,16 @@ class SimplifiedOperatorBuilder;
// which might allow inlining or other optimizations to be performed afterwards.
class JSCallReducer final : public AdvancedReducer {
public:
JSCallReducer(Editor* editor, JSGraph* jsgraph,
// Flags that control the mode of operation.
enum Flag { kNoFlags = 0u, kBailoutOnUninitialized = 1u << 0 };
typedef base::Flags<Flag> Flags;
JSCallReducer(Editor* editor, JSGraph* jsgraph, Flags flags,
Handle<Context> native_context,
CompilationDependencies* dependencies)
: AdvancedReducer(editor),
jsgraph_(jsgraph),
flags_(flags),
native_context_(native_context),
dependencies_(dependencies) {}
......@@ -74,9 +79,11 @@ class JSCallReducer final : public AdvancedReducer {
CommonOperatorBuilder* common() const;
JSOperatorBuilder* javascript() const;
SimplifiedOperatorBuilder* simplified() const;
Flags flags() const { return flags_; }
CompilationDependencies* dependencies() const { return dependencies_; }
JSGraph* const jsgraph_;
Flags const flags_;
Handle<Context> const native_context_;
CompilationDependencies* const dependencies_;
};
......
......@@ -274,6 +274,22 @@ Reduction JSTypeHintLowering::ReduceToPrimitiveToStringOperation(
return Reduction();
}
Reduction JSTypeHintLowering::ReduceCallOperation(const Operator* op,
Node* const* args,
int arg_count, Node* effect,
Node* control,
FeedbackSlot slot) const {
DCHECK_EQ(IrOpcode::kJSCall, op->opcode());
DCHECK(!slot.IsInvalid());
CallICNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
return Reduction(node);
}
return Reduction();
}
Reduction JSTypeHintLowering::ReduceLoadNamedOperation(
const Operator* op, Node* obj, Node* effect, Node* control,
FeedbackSlot slot) const {
......
......@@ -64,6 +64,11 @@ class JSTypeHintLowering {
Node* control,
FeedbackSlot slot) const;
// Potential reduction of call operations.
Reduction ReduceCallOperation(const Operator* op, Node* const* args,
int arg_count, Node* effect, Node* control,
FeedbackSlot slot) const;
// Potential reduction of property access operations.
Reduction ReduceLoadNamedOperation(const Operator* op, Node* obj,
Node* effect, Node* control,
......
......@@ -909,6 +909,9 @@ struct InliningPhase {
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
data->common(), data->machine());
JSCallReducer call_reducer(&graph_reducer, data->jsgraph(),
data->info()->is_bailout_on_uninitialized()
? JSCallReducer::kBailoutOnUninitialized
: JSCallReducer::kNoFlags,
data->native_context(),
data->info()->dependencies());
JSContextSpecialization context_specialization(
......
......@@ -17,6 +17,7 @@ namespace internal {
namespace compiler {
TEST(RunUnwindingInfo) {
FLAG_always_opt = true;
FLAG_perf_prof_unwinding_info = true;
FunctionTester tester(
......
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