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

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

Issue 2191823002: [turbofan] Refactor the lowering of element/property accesses. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 4 years, 5 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 | « src/compiler/js-native-context-specialization.h ('k') | src/compiler/load-elimination.cc » ('j') | 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 53d6232eaa34752688592f1114f539b7daf81926..5718dbb8cc167b29b1ef753e5d67ae3661b2ed08 100644
--- a/src/compiler/js-native-context-specialization.cc
+++ b/src/compiler/js-native-context-specialization.cc
@@ -22,6 +22,38 @@ namespace v8 {
namespace internal {
namespace compiler {
+namespace {
+
+bool HasNumberMaps(MapList const& maps) {
+ for (auto map : maps) {
+ if (map->instance_type() == HEAP_NUMBER_TYPE) return true;
+ }
+ return false;
+}
+
+bool HasOnlyJSArrayMaps(MapList const& maps) {
+ for (auto map : maps) {
+ if (!map->IsJSArrayMap()) return false;
+ }
+ return true;
+}
+
+bool HasOnlyNumberMaps(MapList const& maps) {
+ for (auto map : maps) {
+ if (map->instance_type() != HEAP_NUMBER_TYPE) return false;
+ }
+ return true;
+}
+
+bool HasOnlyStringMaps(MapList const& maps) {
+ for (auto map : maps) {
+ if (!map->IsStringMap()) return false;
+ }
+ return true;
+}
+
+} // namespace
+
JSNativeContextSpecialization::JSNativeContextSpecialization(
Editor* editor, JSGraph* jsgraph, Flags flags,
MaybeHandle<Context> native_context, CompilationDependencies* dependencies,
@@ -100,12 +132,6 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// Nothing to do if we have no non-deprecated maps.
if (access_infos.empty()) return NoChange();
- // 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 {index} matches the specified {name} (if {index} is given).
if (index != nullptr) {
Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Name()),
@@ -113,285 +139,168 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
}
- // Check if {receiver} may be a number.
- bool receiverissmi_possible = false;
- for (PropertyAccessInfo const& access_info : access_infos) {
- if (access_info.receiver_type()->Is(Type::Number())) {
- receiverissmi_possible = true;
- break;
- }
- }
-
- // Ensure that {receiver} is a heap object.
- Node* receiverissmi_control = nullptr;
- Node* receiverissmi_effect = effect;
- if (receiverissmi_possible) {
- Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
- Node* branch = graph()->NewNode(common()->Branch(), check, control);
- control = graph()->NewNode(common()->IfFalse(), branch);
- receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
- receiverissmi_effect = effect;
- } else if (access_infos.size() != 1 ||
- !access_infos[0].receiver_type()->Is(Type::String())) {
- // TODO(bmeurer): We omit the Smi check here if we are going to lower to
- // the CheckString below; make this less horrible and adhoc.
- receiver = effect = graph()->NewNode(simplified()->CheckTaggedPointer(),
- receiver, effect, control);
- }
-
- // Load the {receiver} map. The resulting effect is the dominating effect for
- // all (polymorphic) branches.
- Node* receiver_map = effect =
- graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
- receiver, effect, control);
-
- // Generate code for the various different property access patterns.
- Node* fallthrough_control = control;
- for (size_t j = 0; j < access_infos.size(); ++j) {
- PropertyAccessInfo const& access_info = access_infos[j];
- Node* this_value = value;
- Node* this_receiver = receiver;
- Node* this_effect = effect;
- Node* this_control;
+ // 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);
- // Perform map check on {receiver}.
- Type* receiver_type = access_info.receiver_type();
- if (receiver_type->Is(Type::String())) {
- if (j == access_infos.size() - 1) {
- this_receiver = this_effect =
- graph()->NewNode(simplified()->CheckString(), receiver, this_effect,
- fallthrough_control);
- this_control = fallthrough_control;
- fallthrough_control = nullptr;
- } else {
- Node* check =
- graph()->NewNode(simplified()->ObjectIsString(), receiver);
- Node* branch =
- graph()->NewNode(common()->Branch(), check, fallthrough_control);
- fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
- this_control = graph()->NewNode(common()->IfTrue(), branch);
- }
- } else {
- // Emit a (sequence of) map checks for other {receiver}s.
- ZoneVector<Node*> this_controls(zone());
- ZoneVector<Node*> this_effects(zone());
- int num_classes = access_info.receiver_type()->NumClasses();
- for (auto i = access_info.receiver_type()->Classes(); !i.Done();
- i.Advance()) {
- DCHECK_LT(0, num_classes);
- Handle<Map> map = i.Current();
- Node* check =
- graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()),
- receiver_map, jsgraph()->Constant(map));
- if (--num_classes == 0 && j == access_infos.size() - 1) {
- check = graph()->NewNode(simplified()->CheckIf(), check, this_effect,
- fallthrough_control);
- this_controls.push_back(fallthrough_control);
- this_effects.push_back(check);
- fallthrough_control = nullptr;
- } else {
- Node* branch =
- graph()->NewNode(common()->Branch(), check, fallthrough_control);
- fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
- this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
- this_effects.push_back(this_effect);
- }
- }
+ // 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);
- // The Number case requires special treatment to also deal with Smis.
- if (receiver_type->Is(Type::Number())) {
- // Join this check with the "receiver is smi" check above.
- DCHECK_NOT_NULL(receiverissmi_effect);
- DCHECK_NOT_NULL(receiverissmi_control);
- this_effects.push_back(receiverissmi_effect);
- this_controls.push_back(receiverissmi_control);
- receiverissmi_effect = receiverissmi_control = nullptr;
+ // 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 {
+ // 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());
+
+ // Check if {receiver} may be a number.
+ bool receiverissmi_possible = false;
+ for (PropertyAccessInfo const& access_info : access_infos) {
+ if (HasNumberMaps(access_info.receiver_maps())) {
+ receiverissmi_possible = true;
+ break;
}
-
- // Create dominating Merge+EffectPhi for this {receiver} type.
- int const this_control_count = static_cast<int>(this_controls.size());
- this_control =
- (this_control_count == 1)
- ? this_controls.front()
- : graph()->NewNode(common()->Merge(this_control_count),
- this_control_count, &this_controls.front());
- this_effects.push_back(this_control);
- int const this_effect_count = static_cast<int>(this_effects.size());
- this_effect =
- (this_control_count == 1)
- ? this_effects.front()
- : graph()->NewNode(common()->EffectPhi(this_control_count),
- this_effect_count, &this_effects.front());
}
- // Determine actual holder and perform prototype chain checks.
- Handle<JSObject> holder;
- if (access_info.holder().ToHandle(&holder)) {
- AssumePrototypesStable(receiver_type, native_context, holder);
+ // Ensure that {receiver} is a heap object.
+ Node* receiverissmi_control = nullptr;
+ Node* receiverissmi_effect = effect;
+ if (receiverissmi_possible) {
+ Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
+ Node* branch = graph()->NewNode(common()->Branch(), check, control);
+ control = graph()->NewNode(common()->IfFalse(), branch);
+ receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
+ receiverissmi_effect = effect;
+ } else {
+ receiver = effect = graph()->NewNode(simplified()->CheckTaggedPointer(),
+ receiver, effect, control);
}
- // Generate the actual property access.
- if (access_info.IsNotFound()) {
- DCHECK_EQ(AccessMode::kLoad, access_mode);
- this_value = jsgraph()->UndefinedConstant();
- } else if (access_info.IsDataConstant()) {
- this_value = jsgraph()->Constant(access_info.constant());
- if (access_mode == AccessMode::kStore) {
- Node* check = graph()->NewNode(
- simplified()->ReferenceEqual(Type::Tagged()), value, this_value);
- this_effect = graph()->NewNode(simplified()->CheckIf(), check,
- this_effect, this_control);
- }
- } else {
- DCHECK(access_info.IsDataField());
- FieldIndex const field_index = access_info.field_index();
- Type* const field_type = access_info.field_type();
- if (access_mode == AccessMode::kLoad &&
- access_info.holder().ToHandle(&holder)) {
- this_receiver = jsgraph()->Constant(holder);
- }
- Node* this_storage = this_receiver;
- if (!field_index.is_inobject()) {
- this_storage = this_effect = graph()->NewNode(
- simplified()->LoadField(AccessBuilder::ForJSObjectProperties()),
- this_storage, this_effect, this_control);
- }
- FieldAccess field_access = {
- kTaggedBase, field_index.offset(), name,
- field_type, MachineType::AnyTagged(), kFullWriteBarrier};
- if (access_mode == AccessMode::kLoad) {
- if (field_type->Is(Type::UntaggedFloat64())) {
- if (!field_index.is_inobject() || field_index.is_hidden_field() ||
- !FLAG_unbox_double_fields) {
- this_storage = this_effect =
- graph()->NewNode(simplified()->LoadField(field_access),
- this_storage, this_effect, this_control);
- field_access.offset = HeapNumber::kValueOffset;
- field_access.name = MaybeHandle<Name>();
- }
- field_access.machine_type = MachineType::Float64();
- }
- this_value = this_effect =
- graph()->NewNode(simplified()->LoadField(field_access),
- this_storage, this_effect, this_control);
- } else {
- DCHECK_EQ(AccessMode::kStore, access_mode);
- if (field_type->Is(Type::UntaggedFloat64())) {
- this_value = this_effect =
- graph()->NewNode(simplified()->CheckNumber(), this_value,
- this_effect, this_control);
-
- if (!field_index.is_inobject() || field_index.is_hidden_field() ||
- !FLAG_unbox_double_fields) {
- if (access_info.HasTransitionMap()) {
- // Allocate a MutableHeapNumber for the new property.
- this_effect = graph()->NewNode(
- common()->BeginRegion(RegionObservability::kNotObservable),
- this_effect);
- Node* this_box = this_effect =
- graph()->NewNode(simplified()->Allocate(NOT_TENURED),
- jsgraph()->Constant(HeapNumber::kSize),
- this_effect, this_control);
- this_effect = graph()->NewNode(
- simplified()->StoreField(AccessBuilder::ForMap()), this_box,
- jsgraph()->HeapConstant(factory()->mutable_heap_number_map()),
- this_effect, this_control);
- this_effect = graph()->NewNode(
- simplified()->StoreField(AccessBuilder::ForHeapNumberValue()),
- this_box, this_value, this_effect, this_control);
- this_value = this_effect = graph()->NewNode(
- common()->FinishRegion(), this_box, this_effect);
-
- field_access.type = Type::TaggedPointer();
- } else {
- // We just store directly to the MutableHeapNumber.
- this_storage = this_effect =
- graph()->NewNode(simplified()->LoadField(field_access),
- this_storage, this_effect, this_control);
- field_access.offset = HeapNumber::kValueOffset;
- field_access.name = MaybeHandle<Name>();
- field_access.machine_type = MachineType::Float64();
- }
- } else {
- // Unboxed double field, we store directly to the field.
- field_access.machine_type = MachineType::Float64();
- }
- } else if (field_type->Is(Type::TaggedSigned())) {
- this_value = this_effect =
- graph()->NewNode(simplified()->CheckTaggedSigned(), this_value,
- this_effect, this_control);
- } else if (field_type->Is(Type::TaggedPointer())) {
- this_value = this_effect =
- graph()->NewNode(simplified()->CheckTaggedPointer(), this_value,
- this_effect, this_control);
- if (field_type->NumClasses() == 1) {
- // Emit a map check for the value.
- Node* this_value_map = this_effect = graph()->NewNode(
- simplified()->LoadField(AccessBuilder::ForMap()), this_value,
- this_effect, this_control);
- Node* check = graph()->NewNode(
- simplified()->ReferenceEqual(Type::Internal()), this_value_map,
- jsgraph()->Constant(field_type->Classes().Current()));
- this_effect = graph()->NewNode(simplified()->CheckIf(), check,
- this_effect, this_control);
+ // Load the {receiver} map. The resulting effect is the dominating effect
+ // for all (polymorphic) branches.
+ Node* receiver_map = effect =
+ graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
+ receiver, effect, control);
+
+ // Generate code for the various different property access patterns.
+ Node* fallthrough_control = control;
+ for (size_t j = 0; j < access_infos.size(); ++j) {
+ PropertyAccessInfo const& access_info = access_infos[j];
+ Node* this_value = value;
+ Node* this_receiver = receiver;
+ Node* this_effect = effect;
+ Node* this_control;
+
+ // Perform map check on {receiver}.
+ MapList const& receiver_maps = access_info.receiver_maps();
+ {
+ // Emit a (sequence of) map checks for other {receiver}s.
+ ZoneVector<Node*> this_controls(zone());
+ ZoneVector<Node*> this_effects(zone());
+ size_t num_classes = receiver_maps.size();
+ for (auto map : receiver_maps) {
+ DCHECK_LT(0u, num_classes);
+ Node* check =
+ graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()),
+ receiver_map, jsgraph()->Constant(map));
+ if (--num_classes == 0 && j == access_infos.size() - 1) {
+ check = graph()->NewNode(simplified()->CheckIf(), check,
+ this_effect, fallthrough_control);
+ this_controls.push_back(fallthrough_control);
+ this_effects.push_back(check);
+ fallthrough_control = nullptr;
} else {
- DCHECK_EQ(0, field_type->NumClasses());
+ Node* branch = graph()->NewNode(common()->Branch(), check,
+ fallthrough_control);
+ fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
+ this_controls.push_back(
+ graph()->NewNode(common()->IfTrue(), branch));
+ this_effects.push_back(this_effect);
}
- } else {
- DCHECK(field_type->Is(Type::Tagged()));
- }
- Handle<Map> transition_map;
- if (access_info.transition_map().ToHandle(&transition_map)) {
- this_effect = graph()->NewNode(
- common()->BeginRegion(RegionObservability::kObservable),
- this_effect);
- this_effect = graph()->NewNode(
- simplified()->StoreField(AccessBuilder::ForMap()), this_receiver,
- jsgraph()->Constant(transition_map), this_effect, this_control);
}
- this_effect = graph()->NewNode(simplified()->StoreField(field_access),
- this_storage, this_value, this_effect,
- this_control);
- if (access_info.HasTransitionMap()) {
- this_effect =
- graph()->NewNode(common()->FinishRegion(),
- jsgraph()->UndefinedConstant(), this_effect);
+
+ // The Number case requires special treatment to also deal with Smis.
+ if (HasNumberMaps(receiver_maps)) {
+ // Join this check with the "receiver is smi" check above.
+ DCHECK_NOT_NULL(receiverissmi_effect);
+ DCHECK_NOT_NULL(receiverissmi_control);
+ this_effects.push_back(receiverissmi_effect);
+ this_controls.push_back(receiverissmi_control);
+ receiverissmi_effect = receiverissmi_control = nullptr;
}
+
+ // Create dominating Merge+EffectPhi for this {receiver} type.
+ int const this_control_count = static_cast<int>(this_controls.size());
+ this_control =
+ (this_control_count == 1)
+ ? this_controls.front()
+ : graph()->NewNode(common()->Merge(this_control_count),
+ this_control_count, &this_controls.front());
+ this_effects.push_back(this_control);
+ int const this_effect_count = static_cast<int>(this_effects.size());
+ this_effect =
+ (this_control_count == 1)
+ ? this_effects.front()
+ : graph()->NewNode(common()->EffectPhi(this_control_count),
+ this_effect_count, &this_effects.front());
}
- }
- // Remember the final state for this property access.
- values.push_back(this_value);
- effects.push_back(this_effect);
- controls.push_back(this_control);
- }
+ // Generate the actual property access.
+ ValueEffectControl continuation = BuildPropertyAccess(
+ this_receiver, this_value, this_effect, this_control, name,
+ 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);
}
-
Reduction JSNativeContextSpecialization::ReduceNamedAccess(
Node* node, Node* value, FeedbackNexus const& nexus, Handle<Name> name,
AccessMode access_mode, LanguageMode language_mode) {
@@ -560,16 +469,13 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
receiver, this_effect, this_control);
// Perform map check on {receiver}.
- Type* receiver_type = access_info.receiver_type();
- bool receiver_is_jsarray = true;
+ MapList const& receiver_maps = access_info.receiver_maps();
{
ZoneVector<Node*> this_controls(zone());
ZoneVector<Node*> this_effects(zone());
- int num_classes = access_info.receiver_type()->NumClasses();
- for (auto i = access_info.receiver_type()->Classes(); !i.Done();
- i.Advance()) {
- DCHECK_LT(0, num_classes);
- Handle<Map> map = i.Current();
+ 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));
@@ -591,7 +497,6 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
this_effects.push_back(effect);
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
}
- if (!map->IsJSArrayMap()) receiver_is_jsarray = false;
}
// Create single chokepoint for the control.
@@ -623,141 +528,16 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// not compatible with (monomorphic) keyed stores.
Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) {
- AssumePrototypesStable(receiver_type, native_context, holder);
+ AssumePrototypesStable(receiver_maps, native_context, holder);
}
- // TODO(bmeurer): We currently specialize based on elements kind. We should
- // also be able to properly support strings and other JSObjects here.
- ElementsKind elements_kind = access_info.elements_kind();
-
- // Load the elements for the {receiver}.
- Node* this_elements = this_effect = graph()->NewNode(
- simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
- this_receiver, this_effect, this_control);
-
- // Don't try to store to a copy-on-write backing store.
- if (access_mode == AccessMode::kStore &&
- IsFastSmiOrObjectElementsKind(elements_kind)) {
- Node* this_elements_map = this_effect =
- graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
- this_elements, this_effect, this_control);
- Node* check = graph()->NewNode(
- simplified()->ReferenceEqual(Type::Any()), this_elements_map,
- jsgraph()->HeapConstant(factory()->fixed_array_map()));
- this_effect = graph()->NewNode(simplified()->CheckIf(), check,
- this_effect, this_control);
- }
-
- // Load the length of the {receiver}.
- Node* this_length = this_effect =
- receiver_is_jsarray
- ? graph()->NewNode(
- simplified()->LoadField(
- AccessBuilder::ForJSArrayLength(elements_kind)),
- this_receiver, this_effect, this_control)
- : graph()->NewNode(
- simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
- this_elements, this_effect, this_control);
-
- // Check that the {index} is in the valid range for the {receiver}.
- this_index = this_effect =
- graph()->NewNode(simplified()->CheckBounds(), this_index, this_length,
- this_effect, this_control);
-
- // Compute the element access.
- Type* element_type = Type::Any();
- MachineType element_machine_type = MachineType::AnyTagged();
- if (IsFastDoubleElementsKind(elements_kind)) {
- element_type = Type::Number();
- element_machine_type = MachineType::Float64();
- } else if (IsFastSmiElementsKind(elements_kind)) {
- element_type = type_cache_.kSmi;
- }
- ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize,
- element_type, element_machine_type,
- kFullWriteBarrier};
-
// Access the actual element.
- // TODO(bmeurer): Refactor this into separate methods or even a separate
- // class that deals with the elements access.
- if (access_mode == AccessMode::kLoad) {
- // Compute the real element access type, which includes the hole in case
- // of holey backing stores.
- if (elements_kind == FAST_HOLEY_ELEMENTS ||
- elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
- element_access.type = Type::Union(
- element_type,
- Type::Constant(factory()->the_hole_value(), graph()->zone()),
- graph()->zone());
- }
- // Perform the actual backing store access.
- this_value = this_effect = graph()->NewNode(
- simplified()->LoadElement(element_access), this_elements, this_index,
- this_effect, this_control);
- // Handle loading from holey backing stores correctly, by either mapping
- // the hole to undefined if possible, or deoptimizing otherwise.
- if (elements_kind == FAST_HOLEY_ELEMENTS ||
- elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
- // Perform the hole check on the result.
- CheckTaggedHoleMode mode = CheckTaggedHoleMode::kNeverReturnHole;
- // Check if we are allowed to turn the hole into undefined.
- Type* initial_holey_array_type = Type::Class(
- handle(isolate()->get_initial_js_array_map(elements_kind)),
- graph()->zone());
- if (receiver_type->NowIs(initial_holey_array_type) &&
- isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
- // Add a code dependency on the array protector cell.
- AssumePrototypesStable(receiver_type, native_context,
- isolate()->initial_object_prototype());
- dependencies()->AssumePropertyCell(factory()->array_protector());
- // Turn the hole into undefined.
- mode = CheckTaggedHoleMode::kConvertHoleToUndefined;
- }
- this_value = this_effect =
- graph()->NewNode(simplified()->CheckTaggedHole(mode), this_value,
- this_effect, this_control);
- } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
- // Perform the hole check on the result.
- CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole;
- // Check if we are allowed to return the hole directly.
- Type* initial_holey_array_type = Type::Class(
- handle(isolate()->get_initial_js_array_map(elements_kind)),
- graph()->zone());
- if (receiver_type->NowIs(initial_holey_array_type) &&
- isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
- // Add a code dependency on the array protector cell.
- AssumePrototypesStable(receiver_type, native_context,
- isolate()->initial_object_prototype());
- dependencies()->AssumePropertyCell(factory()->array_protector());
- // Return the signaling NaN hole directly if all uses are truncating.
- mode = CheckFloat64HoleMode::kAllowReturnHole;
- }
- this_value = this_effect =
- graph()->NewNode(simplified()->CheckFloat64Hole(mode), this_value,
- this_effect, this_control);
- }
- } else {
- DCHECK_EQ(AccessMode::kStore, access_mode);
- if (IsFastSmiElementsKind(elements_kind)) {
- this_value = this_effect =
- graph()->NewNode(simplified()->CheckTaggedSigned(), this_value,
- this_effect, this_control);
- } else if (IsFastDoubleElementsKind(elements_kind)) {
- this_value = this_effect = graph()->NewNode(
- simplified()->CheckNumber(), this_value, this_effect, this_control);
- // Make sure we do not store signalling NaNs into double arrays.
- this_value =
- graph()->NewNode(simplified()->NumberSilenceNaN(), this_value);
- }
- this_effect = graph()->NewNode(simplified()->StoreElement(element_access),
- this_elements, this_index, this_value,
- this_effect, this_control);
- }
-
- // Remember the final state for this element access.
- values.push_back(this_value);
- effects.push_back(this_effect);
- controls.push_back(this_control);
+ 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);
@@ -904,13 +684,283 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) {
p.language_mode(), store_mode);
}
+JSNativeContextSpecialization::ValueEffectControl
+JSNativeContextSpecialization::BuildPropertyAccess(
+ Node* receiver, Node* value, Node* effect, Node* control, Handle<Name> name,
+ Handle<Context> native_context, PropertyAccessInfo const& access_info,
+ AccessMode access_mode) {
+ // Determine actual holder and perform prototype chain checks.
+ Handle<JSObject> holder;
+ if (access_info.holder().ToHandle(&holder)) {
+ AssumePrototypesStable(access_info.receiver_maps(), native_context, holder);
+ }
+
+ // Generate the actual property access.
+ if (access_info.IsNotFound()) {
+ DCHECK_EQ(AccessMode::kLoad, access_mode);
+ value = jsgraph()->UndefinedConstant();
+ } else if (access_info.IsDataConstant()) {
+ value = jsgraph()->Constant(access_info.constant());
+ if (access_mode == AccessMode::kStore) {
+ Node* check = graph()->NewNode(
+ simplified()->ReferenceEqual(Type::Tagged()), value, value);
+ effect =
+ graph()->NewNode(simplified()->CheckIf(), check, effect, control);
+ }
+ } else {
+ DCHECK(access_info.IsDataField());
+ FieldIndex const field_index = access_info.field_index();
+ Type* const field_type = access_info.field_type();
+ if (access_mode == AccessMode::kLoad &&
+ access_info.holder().ToHandle(&holder)) {
+ receiver = jsgraph()->Constant(holder);
+ }
+ Node* storage = receiver;
+ if (!field_index.is_inobject()) {
+ storage = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSObjectProperties()),
+ storage, effect, control);
+ }
+ FieldAccess field_access = {
+ kTaggedBase, field_index.offset(), name,
+ field_type, MachineType::AnyTagged(), kFullWriteBarrier};
+ if (access_mode == AccessMode::kLoad) {
+ if (field_type->Is(Type::UntaggedFloat64())) {
+ if (!field_index.is_inobject() || field_index.is_hidden_field() ||
+ !FLAG_unbox_double_fields) {
+ storage = effect = graph()->NewNode(
+ simplified()->LoadField(field_access), storage, effect, control);
+ field_access.offset = HeapNumber::kValueOffset;
+ field_access.name = MaybeHandle<Name>();
+ }
+ field_access.machine_type = MachineType::Float64();
+ }
+ value = effect = graph()->NewNode(simplified()->LoadField(field_access),
+ storage, effect, control);
+ } else {
+ DCHECK_EQ(AccessMode::kStore, access_mode);
+ if (field_type->Is(Type::UntaggedFloat64())) {
+ value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
+ effect, control);
+
+ if (!field_index.is_inobject() || field_index.is_hidden_field() ||
+ !FLAG_unbox_double_fields) {
+ if (access_info.HasTransitionMap()) {
+ // Allocate a MutableHeapNumber for the new property.
+ effect = graph()->NewNode(
+ common()->BeginRegion(RegionObservability::kNotObservable),
+ effect);
+ Node* box = effect = graph()->NewNode(
+ simplified()->Allocate(NOT_TENURED),
+ jsgraph()->Constant(HeapNumber::kSize), effect, control);
+ effect = graph()->NewNode(
+ simplified()->StoreField(AccessBuilder::ForMap()), box,
+ jsgraph()->HeapConstant(factory()->mutable_heap_number_map()),
+ effect, control);
+ effect = graph()->NewNode(
+ simplified()->StoreField(AccessBuilder::ForHeapNumberValue()),
+ box, value, effect, control);
+ value = effect =
+ graph()->NewNode(common()->FinishRegion(), box, effect);
+
+ field_access.type = Type::TaggedPointer();
+ } else {
+ // We just store directly to the MutableHeapNumber.
+ storage = effect =
+ graph()->NewNode(simplified()->LoadField(field_access), storage,
+ effect, control);
+ field_access.offset = HeapNumber::kValueOffset;
+ field_access.name = MaybeHandle<Name>();
+ field_access.machine_type = MachineType::Float64();
+ }
+ } else {
+ // Unboxed double field, we store directly to the field.
+ field_access.machine_type = MachineType::Float64();
+ }
+ } else if (field_type->Is(Type::TaggedSigned())) {
+ value = effect = graph()->NewNode(simplified()->CheckTaggedSigned(),
+ value, effect, control);
+ } else if (field_type->Is(Type::TaggedPointer())) {
+ 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);
+ } else {
+ DCHECK_EQ(0, field_type->NumClasses());
+ }
+ } else {
+ DCHECK(field_type->Is(Type::Tagged()));
+ }
+ Handle<Map> transition_map;
+ if (access_info.transition_map().ToHandle(&transition_map)) {
+ effect = graph()->NewNode(
+ common()->BeginRegion(RegionObservability::kObservable), effect);
+ effect = graph()->NewNode(
+ simplified()->StoreField(AccessBuilder::ForMap()), receiver,
+ jsgraph()->Constant(transition_map), effect, control);
+ }
+ effect = graph()->NewNode(simplified()->StoreField(field_access), storage,
+ value, effect, control);
+ if (access_info.HasTransitionMap()) {
+ effect = graph()->NewNode(common()->FinishRegion(),
+ jsgraph()->UndefinedConstant(), effect);
+ }
+ }
+ }
+
+ return ValueEffectControl(value, effect, control);
+}
+
+JSNativeContextSpecialization::ValueEffectControl
+JSNativeContextSpecialization::BuildElementAccess(
+ Node* receiver, Node* index, Node* value, Node* effect, Node* control,
+ Handle<Context> native_context, ElementAccessInfo const& access_info,
+ AccessMode access_mode) {
+ // Determine actual holder and perform prototype chain checks.
+ Handle<JSObject> holder;
+ if (access_info.holder().ToHandle(&holder)) {
+ AssumePrototypesStable(access_info.receiver_maps(), native_context, holder);
+ }
+
+ // TODO(bmeurer): We currently specialize based on elements kind. We should
+ // also be able to properly support strings and other JSObjects here.
+ ElementsKind elements_kind = access_info.elements_kind();
+ MapList const& receiver_maps = access_info.receiver_maps();
+
+ // Load the elements for the {receiver}.
+ Node* elements = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
+ effect, control);
+
+ // Don't try to store to a copy-on-write backing store.
+ if (access_mode == AccessMode::kStore &&
+ IsFastSmiOrObjectElementsKind(elements_kind)) {
+ Node* elements_map = effect =
+ graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
+ elements, effect, control);
+ Node* check = graph()->NewNode(
+ simplified()->ReferenceEqual(Type::Any()), elements_map,
+ jsgraph()->HeapConstant(factory()->fixed_array_map()));
+ effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
+ }
+
+ // Load the length of the {receiver}.
+ Node* length = effect =
+ HasOnlyJSArrayMaps(receiver_maps)
+ ? graph()->NewNode(
+ simplified()->LoadField(
+ AccessBuilder::ForJSArrayLength(elements_kind)),
+ receiver, effect, control)
+ : graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
+ elements, effect, control);
+
+ // Check that the {index} is in the valid range for the {receiver}.
+ index = effect = graph()->NewNode(simplified()->CheckBounds(), index, length,
+ effect, control);
+
+ // Compute the element access.
+ Type* element_type = Type::Any();
+ MachineType element_machine_type = MachineType::AnyTagged();
+ if (IsFastDoubleElementsKind(elements_kind)) {
+ element_type = Type::Number();
+ element_machine_type = MachineType::Float64();
+ } else if (IsFastSmiElementsKind(elements_kind)) {
+ element_type = type_cache_.kSmi;
+ }
+ ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize,
+ element_type, element_machine_type,
+ kFullWriteBarrier};
+
+ // Access the actual element.
+ // TODO(bmeurer): Refactor this into separate methods or even a separate
+ // class that deals with the elements access.
+ if (access_mode == AccessMode::kLoad) {
+ // Compute the real element access type, which includes the hole in case
+ // of holey backing stores.
+ if (elements_kind == FAST_HOLEY_ELEMENTS ||
+ elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
+ element_access.type = Type::Union(
+ element_type,
+ Type::Constant(factory()->the_hole_value(), graph()->zone()),
+ graph()->zone());
+ }
+ // Perform the actual backing store access.
+ value = effect = graph()->NewNode(simplified()->LoadElement(element_access),
+ elements, index, effect, control);
+ // Handle loading from holey backing stores correctly, by either mapping
+ // the hole to undefined if possible, or deoptimizing otherwise.
+ if (elements_kind == FAST_HOLEY_ELEMENTS ||
+ elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
+ // Perform the hole check on the result.
+ CheckTaggedHoleMode mode = CheckTaggedHoleMode::kNeverReturnHole;
+ // Check if we are allowed to turn the hole into undefined.
+ // TODO(bmeurer): We might check the JSArray map from a different
+ // context here; may need reinvestigation.
+ if (receiver_maps.size() == 1 &&
+ receiver_maps[0].is_identical_to(
+ handle(isolate()->get_initial_js_array_map(elements_kind))) &&
+ isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
+ // Add a code dependency on the array protector cell.
+ dependencies()->AssumePrototypeMapsStable(
+ receiver_maps[0], isolate()->initial_object_prototype());
+ dependencies()->AssumePropertyCell(factory()->array_protector());
+ // Turn the hole into undefined.
+ mode = CheckTaggedHoleMode::kConvertHoleToUndefined;
+ }
+ value = effect = graph()->NewNode(simplified()->CheckTaggedHole(mode),
+ value, effect, control);
+ } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
+ // Perform the hole check on the result.
+ CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole;
+ // Check if we are allowed to return the hole directly.
+ // TODO(bmeurer): We might check the JSArray map from a different
+ // context here; may need reinvestigation.
+ if (receiver_maps.size() == 1 &&
+ receiver_maps[0].is_identical_to(
+ handle(isolate()->get_initial_js_array_map(elements_kind))) &&
+ isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
+ // Add a code dependency on the array protector cell.
+ dependencies()->AssumePrototypeMapsStable(
+ receiver_maps[0], isolate()->initial_object_prototype());
+ dependencies()->AssumePropertyCell(factory()->array_protector());
+ // Return the signaling NaN hole directly if all uses are truncating.
+ mode = CheckFloat64HoleMode::kAllowReturnHole;
+ }
+ value = effect = graph()->NewNode(simplified()->CheckFloat64Hole(mode),
+ value, effect, control);
+ }
+ } else {
+ DCHECK_EQ(AccessMode::kStore, access_mode);
+ if (IsFastSmiElementsKind(elements_kind)) {
+ value = effect = graph()->NewNode(simplified()->CheckTaggedSigned(),
+ value, effect, control);
+ } else if (IsFastDoubleElementsKind(elements_kind)) {
+ value = effect =
+ graph()->NewNode(simplified()->CheckNumber(), value, effect, control);
+ // Make sure we do not store signalling NaNs into double arrays.
+ value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
+ }
+ effect = graph()->NewNode(simplified()->StoreElement(element_access),
+ elements, index, value, effect, control);
+ }
+
+ return ValueEffectControl(value, effect, control);
+}
void JSNativeContextSpecialization::AssumePrototypesStable(
- Type* receiver_type, Handle<Context> native_context,
- Handle<JSObject> holder) {
+ std::vector<Handle<Map>> const& receiver_maps,
+ Handle<Context> native_context, Handle<JSObject> holder) {
// Determine actual holder and perform prototype chain checks.
- for (auto i = receiver_type->Classes(); !i.Done(); i.Advance()) {
- Handle<Map> map = i.Current();
+ for (auto map : receiver_maps) {
// Perform the implicit ToObject for primitives here.
// Implemented according to ES6 section 7.3.2 GetV (V, P).
Handle<JSFunction> constructor;
« no previous file with comments | « src/compiler/js-native-context-specialization.h ('k') | src/compiler/load-elimination.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698