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 f7b044785f28dd5c92f9212c791dd8b376538115..26793cc85185ea395bf134c0a6395df17f475b7e 100644 |
--- a/src/compiler/js-native-context-specialization.cc |
+++ b/src/compiler/js-native-context-specialization.cc |
@@ -45,7 +45,8 @@ bool HasOnlyNumberMaps(MapList const& maps) { |
return true; |
} |
-bool HasOnlyStringMaps(MapList const& maps) { |
+template <typename T> |
+bool HasOnlyStringMaps(T const& maps) { |
for (auto map : maps) { |
if (!map->IsStringMap()) return false; |
} |
@@ -426,184 +427,213 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( |
// Not much we can do if deoptimization support is disabled. |
if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
- // Retrieve the native context from the given {node}. |
- Handle<Context> native_context; |
- if (!GetNativeContext(node).ToHandle(&native_context)) return NoChange(); |
+ // Check for keyed access to strings. |
+ if (HasOnlyStringMaps(receiver_maps)) { |
+ // Strings are immutable in JavaScript. |
+ if (access_mode == AccessMode::kStore) return NoChange(); |
- // Compute element access infos for the receiver maps. |
- AccessInfoFactory access_info_factory(dependencies(), native_context, |
- graph()->zone()); |
- ZoneVector<ElementAccessInfo> access_infos(zone()); |
- if (!access_info_factory.ComputeElementAccessInfos(receiver_maps, access_mode, |
- &access_infos)) { |
- return NoChange(); |
- } |
+ // Ensure that the {receiver} is actually a String. |
+ receiver = effect = graph()->NewNode(simplified()->CheckString(), receiver, |
+ effect, control); |
- // Nothing to do if we have no non-deprecated maps. |
- if (access_infos.empty()) { |
- return ReduceSoftDeoptimize( |
- node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess); |
- } |
+ // Determine the {receiver} length. |
+ Node* length = effect = graph()->NewNode( |
+ simplified()->LoadField(AccessBuilder::ForStringLength()), receiver, |
+ effect, control); |
- // Ensure that {receiver} is a heap object. |
- effect = BuildCheckTaggedPointer(receiver, effect, control); |
+ // Ensure that {index} is less than {receiver} length. |
+ index = effect = graph()->NewNode(simplified()->CheckBounds(), index, |
+ length, 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; |
- 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), effect, control); |
- } |
+ // Load the character from the {receiver}. |
+ value = graph()->NewNode(simplified()->StringCharCodeAt(), receiver, index, |
+ control); |
- // 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); |
+ // Return it as a single character string. |
+ value = graph()->NewNode(simplified()->StringFromCharCode(), value); |
+ } else { |
+ // Retrieve the native context from the given {node}. |
+ Handle<Context> native_context; |
+ if (!GetNativeContext(node).ToHandle(&native_context)) return NoChange(); |
+ |
+ // Compute element access infos for the receiver maps. |
+ AccessInfoFactory access_info_factory(dependencies(), native_context, |
+ graph()->zone()); |
+ ZoneVector<ElementAccessInfo> access_infos(zone()); |
+ if (!access_info_factory.ComputeElementAccessInfos( |
+ receiver_maps, access_mode, &access_infos)) { |
+ return NoChange(); |
+ } |
- // Perform map check on the {receiver}. |
- effect = |
- BuildCheckMaps(receiver, effect, control, access_info.receiver_maps()); |
+ // Nothing to do if we have no non-deprecated maps. |
+ if (access_infos.empty()) { |
+ return ReduceSoftDeoptimize( |
+ node, |
+ DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess); |
+ } |
- // Access the actual element. |
- ValueEffectControl continuation = BuildElementAccess( |
- receiver, index, value, effect, control, native_context, access_info, |
- access_mode, store_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()); |
+ // Ensure that {receiver} is a heap object. |
+ effect = BuildCheckTaggedPointer(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; |
+ // 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); |
+ // 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(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(), 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); |
- } |
+ // 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, store_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); |
} |
- // 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()); |
+ // 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(), 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); |
+ } |
+ } |
- // TODO(turbofan): The effect/control linearization will not find a |
- // FrameState after the EffectPhi that is generated above. |
- 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 EffectPhi that is generated above. |
+ this_effect = graph()->NewNode(common()->Checkpoint(), frame_state, |
+ this_effect, this_control); |
+ } |
} |
- } |
- // Access the actual element. |
- ValueEffectControl continuation = BuildElementAccess( |
- this_receiver, this_index, this_value, this_effect, this_control, |
- native_context, access_info, access_mode, store_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, store_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()); |
+ } |
} |
} |