[turbofan] Add support for SOFT deopts and use that for property access.

Up until now we were unable to (re)optimize code when we hit
uninitialized (Keyed)Load/StoreICs in the code. We always put an IC
there (sharing the feedback vector with fullcodegen at least) and called
it a day. But we never deoptimized the code object when we gathered more
feedback. This doesn't work very well in practice, esp. with hot code
relying on this. So until we have a proper mechanism to express the need
to reoptimize after we gathered additional feedback from optimized code,
we follow the Crankshaft approach instead and install a SOFT deopt, so
we can not only learn but also utilize the new feedback.

R=mstarzinger@chromium.org
BUG=v8:4470
LOG=n

Review URL: https://codereview.chromium.org/1518013002

Cr-Commit-Position: refs/heads/master@{#34178}
This commit is contained in:
bmeurer 2016-02-20 11:05:47 -08:00 committed by Commit bot
parent be4597a29a
commit bda527b5ff
6 changed files with 99 additions and 20 deletions

View File

@ -421,10 +421,14 @@ OptimizedCompileJob::Status OptimizedCompileJob::CreateGraph() {
if (info()->shared_info()->asm_function()) {
if (info()->osr_frame()) info()->MarkAsFrameSpecializing();
info()->MarkAsFunctionContextSpecializing();
} else if (info()->has_global_object() &&
FLAG_native_context_specialization) {
info()->MarkAsNativeContextSpecializing();
info()->MarkAsTypingEnabled();
} else {
if (!FLAG_always_opt) {
info()->MarkAsBailoutOnUninitialized();
}
if (FLAG_native_context_specialization) {
info()->MarkAsNativeContextSpecializing();
info()->MarkAsTypingEnabled();
}
}
if (!info()->shared_info()->asm_function() ||
FLAG_turbo_asm_deoptimization) {

View File

@ -64,6 +64,7 @@ class CompilationInfo {
kDeoptimizationEnabled = 1 << 16,
kSourcePositionsEnabled = 1 << 17,
kFirstCompile = 1 << 18,
kBailoutOnUninitialized = 1 << 19,
};
explicit CompilationInfo(ParseInfo* parse_info);
@ -207,6 +208,12 @@ class CompilationInfo {
bool is_first_compile() const { return GetFlag(kFirstCompile); }
void MarkAsBailoutOnUninitialized() { SetFlag(kBailoutOnUninitialized); }
bool is_bailout_on_uninitialized() const {
return GetFlag(kBailoutOnUninitialized);
}
bool GeneratePreagedPrologue() const {
// Generate a pre-aged prologue if we are optimizing for size, which
// will make code flushing more aggressive. Only apply to Code::FUNCTION,

View File

@ -461,21 +461,49 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
}
Reduction JSNativeContextSpecialization::ReduceNamedAccess(
Node* node, Node* value, FeedbackNexus const& nexus, Handle<Name> name,
AccessMode access_mode, LanguageMode language_mode) {
DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
node->opcode() == IrOpcode::kJSStoreNamed);
// Check if the {nexus} reports type feedback for the IC.
if (nexus.IsUninitialized()) {
if ((flags() & kDeoptimizationEnabled) &&
(flags() & kBailoutOnUninitialized)) {
// TODO(turbofan): Implement all eager bailout points correctly in
// the graph builder.
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
if (!OpParameter<FrameStateInfo>(frame_state).bailout_id().IsNone()) {
return ReduceSoftDeoptimize(node);
}
}
return NoChange();
}
// Extract receiver maps from the IC using the {nexus}.
MapHandleList receiver_maps;
if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange();
DCHECK_LT(0, receiver_maps.length());
// Try to lower the named access based on the {receiver_maps}.
return ReduceNamedAccess(node, value, receiver_maps, name, access_mode,
language_mode);
}
Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
NamedAccess const& p = NamedAccessOf(node->op());
Node* const value = jsgraph()->Dead();
// Extract receiver maps from the LOAD_IC using the LoadICNexus.
MapHandleList receiver_maps;
if (!p.feedback().IsValid()) return NoChange();
LoadICNexus nexus(p.feedback().vector(), p.feedback().slot());
if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange();
DCHECK_LT(0, receiver_maps.length());
// Try to lower the named access based on the {receiver_maps}.
return ReduceNamedAccess(node, value, receiver_maps, p.name(),
AccessMode::kLoad, p.language_mode());
return ReduceNamedAccess(node, value, nexus, p.name(), AccessMode::kLoad,
p.language_mode());
}
@ -485,15 +513,12 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) {
Node* const value = NodeProperties::GetValueInput(node, 1);
// Extract receiver maps from the STORE_IC using the StoreICNexus.
MapHandleList receiver_maps;
if (!p.feedback().IsValid()) return NoChange();
StoreICNexus nexus(p.feedback().vector(), p.feedback().slot());
if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange();
DCHECK_LT(0, receiver_maps.length());
// Try to lower the named access based on the {receiver_maps}.
return ReduceNamedAccess(node, value, receiver_maps, p.name(),
AccessMode::kStore, p.language_mode());
return ReduceNamedAccess(node, value, nexus, p.name(), AccessMode::kStore,
p.language_mode());
}
@ -901,6 +926,20 @@ Reduction JSNativeContextSpecialization::ReduceKeyedAccess(
DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
node->opcode() == IrOpcode::kJSStoreProperty);
// Check if the {nexus} reports type feedback for the IC.
if (nexus.IsUninitialized()) {
if ((flags() & kDeoptimizationEnabled) &&
(flags() & kBailoutOnUninitialized)) {
// TODO(turbofan): Implement all eager bailout points correctly in
// the graph builder.
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
if (!OpParameter<FrameStateInfo>(frame_state).bailout_id().IsNone()) {
return ReduceSoftDeoptimize(node);
}
}
return NoChange();
}
// Extract receiver maps from the {nexus}.
MapHandleList receiver_maps;
if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange();
@ -940,6 +979,22 @@ Reduction JSNativeContextSpecialization::ReduceKeyedAccess(
}
Reduction JSNativeContextSpecialization::ReduceSoftDeoptimize(Node* node) {
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* deoptimize =
graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kSoft), 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);
}
Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode());
PropertyAccess const& p = PropertyAccessOf(node->op());

View File

@ -38,7 +38,8 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
// Flags that control the mode of operation.
enum Flag {
kNoFlags = 0u,
kDeoptimizationEnabled = 1u << 0,
kBailoutOnUninitialized = 1u << 0,
kDeoptimizationEnabled = 1u << 1,
};
typedef base::Flags<Flag> Flags;
@ -66,12 +67,18 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
AccessMode access_mode,
LanguageMode language_mode,
KeyedAccessStoreMode store_mode);
Reduction ReduceNamedAccess(Node* node, Node* value,
FeedbackNexus const& nexus, Handle<Name> name,
AccessMode access_mode,
LanguageMode language_mode);
Reduction ReduceNamedAccess(Node* node, Node* value,
MapHandleList const& receiver_maps,
Handle<Name> name, AccessMode access_mode,
LanguageMode language_mode,
Node* index = nullptr);
Reduction ReduceSoftDeoptimize(Node* node);
// Adds stability dependencies on all prototypes of every class in
// {receiver_type} up to (and including) the {holder}.
void AssumePrototypesStable(Type* receiver_type,

View File

@ -554,12 +554,17 @@ struct InliningPhase {
JSGlobalObjectSpecialization global_object_specialization(
&graph_reducer, data->jsgraph(), data->native_context(),
data->info()->dependencies());
JSNativeContextSpecialization::Flags flags =
JSNativeContextSpecialization::kNoFlags;
if (data->info()->is_bailout_on_uninitialized()) {
flags |= JSNativeContextSpecialization::kBailoutOnUninitialized;
}
if (data->info()->is_deoptimization_enabled()) {
flags |= JSNativeContextSpecialization::kDeoptimizationEnabled;
}
JSNativeContextSpecialization native_context_specialization(
&graph_reducer, data->jsgraph(),
data->info()->is_deoptimization_enabled()
? JSNativeContextSpecialization::kDeoptimizationEnabled
: JSNativeContextSpecialization::kNoFlags,
data->native_context(), data->info()->dependencies(), temp_zone);
&graph_reducer, data->jsgraph(), flags, data->native_context(),
data->info()->dependencies(), temp_zone);
JSInliningHeuristic inlining(&graph_reducer,
data->info()->is_inlining_enabled()
? JSInliningHeuristic::kGeneralInlining

View File

@ -343,6 +343,7 @@ class FeedbackNexus {
FeedbackVectorSlot slot() const { return slot_; }
InlineCacheState ic_state() const { return StateFromFeedback(); }
bool IsUninitialized() const { return StateFromFeedback() == UNINITIALIZED; }
Map* FindFirstMap() const {
MapHandleList maps;
ExtractMaps(&maps);