Index: src/compiler/js-native-context-specialization.cc |
diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc |
index 2567a2571184ee9def9681fba338dc28dc5f4997..bbb9531495039648c43b008d3f13d72f6f31fb2b 100644 |
--- a/src/compiler/js-native-context-specialization.cc |
+++ b/src/compiler/js-native-context-specialization.cc |
@@ -143,30 +143,28 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
} |
// Check for the monomorphic cases. |
- if (access_infos.size() == 1 && |
- HasOnlyStringMaps(access_infos[0].receiver_maps())) { |
- // Monormorphic string access (ignoring the fact that there are multiple |
- // String maps). |
- receiver = effect = graph()->NewNode(simplified()->CheckString(), receiver, |
- effect, control); |
- |
- // Generate the actual property access. |
- ValueEffectControl continuation = |
- BuildPropertyAccess(receiver, value, effect, control, name, |
- native_context, access_infos[0], access_mode); |
- value = continuation.value(); |
- effect = continuation.effect(); |
- control = continuation.control(); |
- } else if (access_infos.size() == 1 && |
- HasOnlyNumberMaps(access_infos[0].receiver_maps())) { |
- // Monomorphic number access (we also deal with Smis here). |
- receiver = effect = graph()->NewNode(simplified()->CheckNumber(), receiver, |
- effect, control); |
+ if (access_infos.size() == 1) { |
+ PropertyAccessInfo access_info = access_infos.front(); |
+ if (HasOnlyStringMaps(access_info.receiver_maps())) { |
+ // Monormorphic string access (ignoring the fact that there are multiple |
+ // String maps). |
+ receiver = effect = graph()->NewNode(simplified()->CheckString(), |
+ receiver, effect, control); |
+ } else if (HasOnlyNumberMaps(access_info.receiver_maps())) { |
+ // Monomorphic number access (we also deal with Smis here). |
+ receiver = effect = graph()->NewNode(simplified()->CheckNumber(), |
+ receiver, effect, control); |
+ } else { |
+ // Monomorphic property access. |
+ effect = BuildCheckTaggedPointer(receiver, effect, control); |
+ effect = BuildCheckMaps(receiver, effect, control, |
+ access_info.receiver_maps()); |
+ } |
// Generate the actual property access. |
ValueEffectControl continuation = |
BuildPropertyAccess(receiver, value, effect, control, name, |
- native_context, access_infos[0], access_mode); |
+ native_context, access_info, access_mode); |
value = continuation.value(); |
effect = continuation.effect(); |
control = continuation.control(); |
@@ -196,8 +194,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch); |
receiverissmi_effect = effect; |
} else { |
- receiver = effect = graph()->NewNode(simplified()->CheckTaggedPointer(), |
- receiver, effect, control); |
+ effect = BuildCheckTaggedPointer(receiver, effect, control); |
} |
// Load the {receiver} map. The resulting effect is the dominating effect |
@@ -434,139 +431,179 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( |
node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess); |
} |
- // The final states for every polymorphic branch. We join them with |
- // Merge+Phi+EffectPhi at the bottom. |
- ZoneVector<Node*> values(zone()); |
- ZoneVector<Node*> effects(zone()); |
- ZoneVector<Node*> controls(zone()); |
- |
// Ensure that {receiver} is a heap object. |
- receiver = effect = graph()->NewNode(simplified()->CheckTaggedPointer(), |
- receiver, effect, control); |
- |
- // Generate code for the various different element access patterns. |
- Node* fallthrough_control = control; |
- for (size_t j = 0; j < access_infos.size(); ++j) { |
- ElementAccessInfo const& access_info = access_infos[j]; |
- Node* this_receiver = receiver; |
- Node* this_value = value; |
- Node* this_index = index; |
- Node* this_effect = effect; |
- Node* this_control = fallthrough_control; |
+ effect = BuildCheckTaggedPointer(receiver, effect, control); |
+ |
+ // Check for the monomorphic case. |
+ if (access_infos.size() == 1) { |
+ ElementAccessInfo access_info = access_infos.front(); |
// Perform possible elements kind transitions. |
for (auto transition : access_info.transitions()) { |
Handle<Map> const transition_source = transition.first; |
Handle<Map> const transition_target = transition.second; |
- this_effect = graph()->NewNode( |
+ effect = graph()->NewNode( |
simplified()->TransitionElementsKind( |
IsSimpleMapChangeTransition(transition_source->elements_kind(), |
transition_target->elements_kind()) |
? ElementsTransition::kFastTransition |
: ElementsTransition::kSlowTransition), |
receiver, jsgraph()->HeapConstant(transition_source), |
- jsgraph()->HeapConstant(transition_target), this_effect, |
- this_control); |
+ jsgraph()->HeapConstant(transition_target), effect, control); |
} |
- // Load the {receiver} map. |
- Node* receiver_map = this_effect = |
- graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
- receiver, this_effect, this_control); |
- |
- // Perform map check on {receiver}. |
- MapList const& receiver_maps = access_info.receiver_maps(); |
- { |
- ZoneVector<Node*> this_controls(zone()); |
- ZoneVector<Node*> this_effects(zone()); |
- size_t num_classes = receiver_maps.size(); |
- for (Handle<Map> map : receiver_maps) { |
- DCHECK_LT(0u, num_classes); |
- Node* check = |
- graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), |
- receiver_map, jsgraph()->Constant(map)); |
- if (--num_classes == 0 && j == access_infos.size() - 1) { |
- // Last map check on the fallthrough control path, do a conditional |
- // eager deoptimization exit here. |
- // TODO(turbofan): This is ugly as hell! We should probably introduce |
- // macro-ish operators for property access that encapsulate this whole |
- // mess. |
- check = graph()->NewNode(simplified()->CheckIf(), check, this_effect, |
- this_control); |
- this_controls.push_back(this_control); |
- this_effects.push_back(check); |
- fallthrough_control = nullptr; |
+ // TODO(turbofan): The effect/control linearization will not find a |
+ // FrameState after the StoreField or Call that is generated for the |
+ // elements kind transition above. This is because those operators |
+ // don't have the kNoWrite flag on it, even though they are not |
+ // observable by JavaScript. |
+ effect = |
+ graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); |
+ |
+ // Perform map check on the {receiver}. |
+ effect = |
+ BuildCheckMaps(receiver, effect, control, access_info.receiver_maps()); |
+ |
+ // Access the actual element. |
+ ValueEffectControl continuation = |
+ BuildElementAccess(receiver, index, value, effect, control, |
+ native_context, access_info, access_mode); |
+ value = continuation.value(); |
+ effect = continuation.effect(); |
+ control = continuation.control(); |
+ } else { |
+ // The final states for every polymorphic branch. We join them with |
+ // Merge+Phi+EffectPhi at the bottom. |
+ ZoneVector<Node*> values(zone()); |
+ ZoneVector<Node*> effects(zone()); |
+ ZoneVector<Node*> controls(zone()); |
+ |
+ // Generate code for the various different element access patterns. |
+ Node* fallthrough_control = control; |
+ for (size_t j = 0; j < access_infos.size(); ++j) { |
+ ElementAccessInfo const& access_info = access_infos[j]; |
+ Node* this_receiver = receiver; |
+ Node* this_value = value; |
+ Node* this_index = index; |
+ Node* this_effect = effect; |
+ Node* this_control = fallthrough_control; |
+ |
+ // Perform possible elements kind transitions. |
+ for (auto transition : access_info.transitions()) { |
+ Handle<Map> const transition_source = transition.first; |
+ Handle<Map> const transition_target = transition.second; |
+ this_effect = graph()->NewNode( |
+ simplified()->TransitionElementsKind( |
+ IsSimpleMapChangeTransition(transition_source->elements_kind(), |
+ transition_target->elements_kind()) |
+ ? ElementsTransition::kFastTransition |
+ : ElementsTransition::kSlowTransition), |
+ receiver, jsgraph()->HeapConstant(transition_source), |
+ jsgraph()->HeapConstant(transition_target), this_effect, |
+ this_control); |
+ } |
+ |
+ // Load the {receiver} map. |
+ Node* receiver_map = this_effect = |
+ graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
+ receiver, this_effect, this_control); |
+ |
+ // Perform map check(s) on {receiver}. |
+ MapList const& receiver_maps = access_info.receiver_maps(); |
+ { |
+ ZoneVector<Node*> this_controls(zone()); |
+ ZoneVector<Node*> this_effects(zone()); |
+ size_t num_classes = receiver_maps.size(); |
+ for (Handle<Map> map : receiver_maps) { |
+ DCHECK_LT(0u, num_classes); |
+ Node* check = |
+ graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), |
+ receiver_map, jsgraph()->Constant(map)); |
+ if (--num_classes == 0 && j == access_infos.size() - 1) { |
+ // Last map check on the fallthrough control path, do a conditional |
+ // eager deoptimization exit here. |
+ // TODO(turbofan): This is ugly as hell! We should probably |
+ // introduce macro-ish operators for property access that |
+ // encapsulate this whole mess. |
+ check = graph()->NewNode(simplified()->CheckIf(), check, |
+ this_effect, this_control); |
+ this_controls.push_back(this_control); |
+ this_effects.push_back(check); |
+ fallthrough_control = nullptr; |
+ } else { |
+ Node* branch = graph()->NewNode(common()->Branch(), check, |
+ fallthrough_control); |
+ this_controls.push_back( |
+ graph()->NewNode(common()->IfTrue(), branch)); |
+ this_effects.push_back(effect); |
+ fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
+ } |
+ } |
+ |
+ // Create single chokepoint for the control. |
epertoso
2016/07/29 09:34:08
nit: do you mean checkpoint?
Benedikt Meurer
2016/07/29 09:37:48
Yes.
|
+ int const this_control_count = static_cast<int>(this_controls.size()); |
+ if (this_control_count == 1) { |
+ this_control = this_controls.front(); |
+ this_effect = this_effects.front(); |
} else { |
- Node* branch = |
- graph()->NewNode(common()->Branch(), check, fallthrough_control); |
- this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
- this_effects.push_back(effect); |
- fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
+ this_control = |
+ graph()->NewNode(common()->Merge(this_control_count), |
+ this_control_count, &this_controls.front()); |
+ this_effects.push_back(this_control); |
+ this_effect = |
+ graph()->NewNode(common()->EffectPhi(this_control_count), |
+ this_control_count + 1, &this_effects.front()); |
+ |
+ // TODO(turbofan): The effect/control linearization will not find a |
+ // FrameState after the StoreField or Call that is generated for the |
+ // elements kind transition above. This is because those operators |
+ // don't have the kNoWrite flag on it, even though they are not |
+ // observable by JavaScript. |
+ this_effect = graph()->NewNode(common()->Checkpoint(), frame_state, |
+ this_effect, this_control); |
} |
} |
- // Create single chokepoint for the control. |
- int const this_control_count = static_cast<int>(this_controls.size()); |
- if (this_control_count == 1) { |
- this_control = this_controls.front(); |
- this_effect = this_effects.front(); |
- } else { |
- this_control = |
- graph()->NewNode(common()->Merge(this_control_count), |
- this_control_count, &this_controls.front()); |
- this_effects.push_back(this_control); |
- this_effect = |
- graph()->NewNode(common()->EffectPhi(this_control_count), |
- this_control_count + 1, &this_effects.front()); |
- |
- // TODO(turbofan): The effect/control linearization will not find a |
- // FrameState after the StoreField or Call that is generated for the |
- // elements kind transition above. This is because those operators |
- // don't have the kNoWrite flag on it, even though they are not |
- // observable by JavaScript. |
- this_effect = graph()->NewNode(common()->Checkpoint(), frame_state, |
- this_effect, this_control); |
+ // Certain stores need a prototype chain check because shape changes |
+ // could allow callbacks on elements in the prototype chain that are |
+ // not compatible with (monomorphic) keyed stores. |
+ Handle<JSObject> holder; |
+ if (access_info.holder().ToHandle(&holder)) { |
+ AssumePrototypesStable(receiver_maps, native_context, holder); |
} |
- } |
- // Certain stores need a prototype chain check because shape changes |
- // could allow callbacks on elements in the prototype chain that are |
- // not compatible with (monomorphic) keyed stores. |
- Handle<JSObject> holder; |
- if (access_info.holder().ToHandle(&holder)) { |
- AssumePrototypesStable(receiver_maps, native_context, holder); |
+ // Access the actual element. |
+ ValueEffectControl continuation = BuildElementAccess( |
+ this_receiver, this_index, this_value, this_effect, this_control, |
+ native_context, access_info, access_mode); |
+ values.push_back(continuation.value()); |
+ effects.push_back(continuation.effect()); |
+ controls.push_back(continuation.control()); |
} |
- // Access the actual element. |
- ValueEffectControl continuation = BuildElementAccess( |
- this_receiver, this_index, this_value, this_effect, this_control, |
- native_context, access_info, access_mode); |
- values.push_back(continuation.value()); |
- effects.push_back(continuation.effect()); |
- controls.push_back(continuation.control()); |
- } |
- |
- DCHECK_NULL(fallthrough_control); |
+ DCHECK_NULL(fallthrough_control); |
- // 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(); |
- } else if (control_count == 1) { |
- value = values.front(); |
- effect = effects.front(); |
- control = controls.front(); |
- } else { |
- control = graph()->NewNode(common()->Merge(control_count), control_count, |
- &controls.front()); |
- values.push_back(control); |
- value = graph()->NewNode( |
- common()->Phi(MachineRepresentation::kTagged, control_count), |
- control_count + 1, &values.front()); |
- effects.push_back(control); |
- effect = graph()->NewNode(common()->EffectPhi(control_count), |
- control_count + 1, &effects.front()); |
+ // 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(); |
+ } else if (control_count == 1) { |
+ value = values.front(); |
+ effect = effects.front(); |
+ control = controls.front(); |
+ } else { |
+ control = graph()->NewNode(common()->Merge(control_count), control_count, |
+ &controls.front()); |
+ values.push_back(control); |
+ value = graph()->NewNode( |
+ common()->Phi(MachineRepresentation::kTagged, control_count), |
+ control_count + 1, &values.front()); |
+ effects.push_back(control); |
+ effect = graph()->NewNode(common()->EffectPhi(control_count), |
+ control_count + 1, &effects.front()); |
+ } |
} |
+ |
ReplaceWithValue(node, value, effect, control); |
return Replace(value); |
} |
@@ -792,18 +829,15 @@ JSNativeContextSpecialization::BuildPropertyAccess( |
value = effect = graph()->NewNode(simplified()->CheckTaggedSigned(), |
value, effect, control); |
} else if (field_type->Is(Type::TaggedPointer())) { |
+ // Ensure that {value} is a HeapObject. |
value = effect = graph()->NewNode(simplified()->CheckTaggedPointer(), |
value, effect, control); |
if (field_type->NumClasses() == 1) { |
// Emit a map check for the value. |
- Node* value_map = effect = |
- graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
- value, effect, control); |
- Node* check = graph()->NewNode( |
- simplified()->ReferenceEqual(Type::Internal()), value_map, |
- jsgraph()->Constant(field_type->Classes().Current())); |
- effect = |
- graph()->NewNode(simplified()->CheckIf(), check, effect, control); |
+ Node* field_map = |
+ jsgraph()->Constant(field_type->Classes().Current()); |
+ effect = graph()->NewNode(simplified()->CheckMaps(1), value, |
+ field_map, effect, control); |
} else { |
DCHECK_EQ(0, field_type->NumClasses()); |
} |
@@ -967,6 +1001,61 @@ JSNativeContextSpecialization::BuildElementAccess( |
return ValueEffectControl(value, effect, control); |
} |
+Node* JSNativeContextSpecialization::BuildCheckMaps( |
+ Node* receiver, Node* effect, Node* control, |
+ std::vector<Handle<Map>> const& maps) { |
+ HeapObjectMatcher m(receiver); |
+ if (m.HasValue()) { |
+ Handle<Map> receiver_map(m.Value()->map(), isolate()); |
+ if (receiver_map->is_stable()) { |
+ for (Handle<Map> map : maps) { |
+ if (map.is_identical_to(receiver_map)) { |
+ dependencies()->AssumeMapStable(receiver_map); |
+ return effect; |
+ } |
+ } |
+ } |
+ } |
+ int const map_input_count = static_cast<int>(maps.size()); |
+ int const input_count = 1 + map_input_count + 1 + 1; |
+ Node** inputs = zone()->NewArray<Node*>(input_count); |
+ inputs[0] = receiver; |
+ for (int i = 0; i < map_input_count; ++i) { |
+ inputs[1 + i] = jsgraph()->HeapConstant(maps[i]); |
+ } |
+ inputs[input_count - 2] = effect; |
+ inputs[input_count - 1] = control; |
+ return graph()->NewNode(simplified()->CheckMaps(map_input_count), input_count, |
+ inputs); |
+} |
+ |
+Node* JSNativeContextSpecialization::BuildCheckTaggedPointer(Node* receiver, |
+ Node* effect, |
+ Node* control) { |
+ switch (receiver->opcode()) { |
+ case IrOpcode::kHeapConstant: |
+ case IrOpcode::kJSCreate: |
+ case IrOpcode::kJSCreateArguments: |
+ case IrOpcode::kJSCreateArray: |
+ case IrOpcode::kJSCreateClosure: |
+ case IrOpcode::kJSCreateIterResultObject: |
+ case IrOpcode::kJSCreateLiteralArray: |
+ case IrOpcode::kJSCreateLiteralObject: |
+ case IrOpcode::kJSCreateLiteralRegExp: |
+ case IrOpcode::kJSConvertReceiver: |
+ case IrOpcode::kJSToName: |
+ case IrOpcode::kJSToString: |
+ case IrOpcode::kJSToObject: |
+ case IrOpcode::kJSTypeOf: { |
+ return effect; |
+ } |
+ default: { |
+ return graph()->NewNode(simplified()->CheckTaggedPointer(), receiver, |
+ effect, control); |
+ } |
+ } |
+} |
+ |
void JSNativeContextSpecialization::AssumePrototypesStable( |
std::vector<Handle<Map>> const& receiver_maps, |
Handle<Context> native_context, Handle<JSObject> holder) { |
@@ -1011,8 +1100,11 @@ bool JSNativeContextSpecialization::ExtractReceiverMaps( |
MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverMap(Node* receiver, |
Node* effect) { |
- NodeMatcher m(receiver); |
- if (m.IsJSCreate()) { |
+ HeapObjectMatcher m(receiver); |
+ if (m.HasValue()) { |
+ Handle<Map> receiver_map(m.Value()->map(), isolate()); |
+ if (receiver_map->is_stable()) return receiver_map; |
+ } else if (m.IsJSCreate()) { |
HeapObjectMatcher mtarget(m.InputAt(0)); |
HeapObjectMatcher mnewtarget(m.InputAt(1)); |
if (mtarget.HasValue() && mnewtarget.HasValue()) { |
@@ -1036,6 +1128,7 @@ MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverMap(Node* receiver, |
} |
} |
} |
+ // TODO(turbofan): Go hunting for CheckMaps(receiver) in the effect chain? |
return MaybeHandle<Map>(); |
} |