[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:
parent
be4597a29a
commit
bda527b5ff
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user