Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(99)

Unified Diff: src/compiler/js-native-context-specialization.cc

Issue 2232483002: [turbofan] Add support for keyed access to strings. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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());
+ }
}
}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698