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

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

Issue 2936673005: [turbofan] Refactor property access building. (Closed)
Patch Set: Address reviewer comments Created 3 years, 6 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/property-access-builder.h » ('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 7ca9cdc1713eddb0bbfcc76a460cfe1816c6954a..48421c9b442b4bc6d8399fa4341707c4dc1a9b2b 100644
--- a/src/compiler/js-native-context-specialization.cc
+++ b/src/compiler/js-native-context-specialization.cc
@@ -13,6 +13,7 @@
#include "src/compiler/js-operator.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
+#include "src/compiler/property-access-builder.h"
#include "src/compiler/type-cache.h"
#include "src/feedback-vector.h"
#include "src/field-index-inl.h"
@@ -38,13 +39,6 @@ bool HasOnlyJSArrayMaps(MapHandles const& maps) {
return true;
}
-bool HasOnlyNumberMaps(MapHandles const& maps) {
- for (auto map : maps) {
- if (map->instance_type() != HEAP_NUMBER_TYPE) return false;
- }
- return true;
-}
-
bool HasOnlyStringMaps(MapHandles const& maps) {
for (auto map : maps) {
if (!map->IsStringMap()) return false;
@@ -52,16 +46,6 @@ bool HasOnlyStringMaps(MapHandles const& maps) {
return true;
}
-bool HasOnlySequentialStringMaps(MapHandles const& maps) {
- for (auto map : maps) {
- if (!map->IsStringMap()) return false;
- if (!StringShape(map->instance_type()).IsSequential()) {
- return false;
- }
- }
- return true;
-}
-
} // namespace
struct JSNativeContextSpecialization::ScriptContextTableLookupResult {
@@ -193,6 +177,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
return NoChange();
}
+ PropertyAccessBuilder access_builder(jsgraph(), dependencies());
+
if (access_info.IsNotFound()) {
// If there's no @@hasInstance handler, the OrdinaryHasInstance operation
// takes over, but that requires the {receiver} to be callable.
@@ -200,12 +186,13 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
// Determine actual holder and perform prototype chain checks.
Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) {
- AssumePrototypesStable(access_info.receiver_maps(), holder);
+ access_builder.AssumePrototypesStable(
+ native_context(), access_info.receiver_maps(), holder);
}
// Monomorphic property access.
- effect = BuildCheckMaps(constructor, effect, control,
- access_info.receiver_maps());
+ access_builder.BuildCheckMaps(constructor, &effect, control,
+ access_info.receiver_maps());
// Lower to OrdinaryHasInstance(C, O).
NodeProperties::ReplaceValueInput(node, constructor, 0);
@@ -220,7 +207,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
// Determine actual holder and perform prototype chain checks.
Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) {
- AssumePrototypesStable(access_info.receiver_maps(), holder);
+ access_builder.AssumePrototypesStable(
+ native_context(), access_info.receiver_maps(), holder);
} else {
holder = receiver;
}
@@ -241,8 +229,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
DCHECK(constant->IsCallable());
// Monomorphic property access.
- effect = BuildCheckMaps(constructor, effect, control,
- access_info.receiver_maps());
+ access_builder.BuildCheckMaps(constructor, &effect, control,
+ access_info.receiver_maps());
// Call the @@hasInstance handler.
Node* target = jsgraph()->Constant(constant);
@@ -641,29 +629,21 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
if_exceptions = &if_exception_nodes;
}
+ PropertyAccessBuilder access_builder(jsgraph(), dependencies());
+
// Check for the monomorphic cases.
if (access_infos.size() == 1) {
PropertyAccessInfo access_info = access_infos.front();
- if (HasOnlyStringMaps(access_info.receiver_maps())) {
- if (HasOnlySequentialStringMaps(access_info.receiver_maps())) {
- receiver = effect = graph()->NewNode(simplified()->CheckSeqString(),
- receiver, effect, control);
- } else {
- // 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.
- receiver = BuildCheckHeapObject(receiver, &effect, control);
- effect = BuildCheckMaps(receiver, effect, control,
- access_info.receiver_maps());
+ // Try to build string check or number check if possible.
+ // Otherwise build a map check.
+ if (!access_builder.TryBuildStringCheck(access_info.receiver_maps(),
+ &receiver, &effect, control) &&
+ !access_builder.TryBuildNumberCheck(access_info.receiver_maps(),
+ &receiver, &effect, control)) {
+ receiver =
+ access_builder.BuildCheckHeapObject(receiver, &effect, control);
+ access_builder.BuildCheckMaps(receiver, &effect, control,
+ access_info.receiver_maps());
}
// Generate the actual property access.
@@ -699,7 +679,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
receiverissmi_effect = effect;
} else {
- receiver = BuildCheckHeapObject(receiver, &effect, control);
+ receiver =
+ access_builder.BuildCheckHeapObject(receiver, &effect, control);
}
// Load the {receiver} map. The resulting effect is the dominating effect
@@ -726,8 +707,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
if (j == access_infos.size() - 1) {
// Last map check on the fallthrough control path, do a
// conditional eager deoptimization exit here.
- this_effect = BuildCheckMaps(receiver, this_effect, this_control,
- receiver_maps);
+ access_builder.BuildCheckMaps(receiver, &this_effect, this_control,
+ receiver_maps);
this_effects.push_back(this_effect);
this_controls.push_back(fallthrough_control);
fallthrough_control = nullptr;
@@ -1030,7 +1011,8 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
}
// Ensure that {receiver} is a heap object.
- receiver = BuildCheckHeapObject(receiver, &effect, control);
+ PropertyAccessBuilder access_builder(jsgraph(), dependencies());
+ receiver = access_builder.BuildCheckHeapObject(receiver, &effect, control);
// Check for the monomorphic case.
if (access_infos.size() == 1) {
@@ -1059,8 +1041,8 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
control);
// Perform map check on the {receiver}.
- effect = BuildCheckMaps(receiver, effect, control,
- access_info.receiver_maps());
+ access_builder.BuildCheckMaps(receiver, &effect, control,
+ access_info.receiver_maps());
// Access the actual element.
ValueEffectControl continuation =
@@ -1111,8 +1093,8 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
if (j == access_infos.size() - 1) {
// Last map check on the fallthrough control path, do a
// conditional eager deoptimization exit here.
- this_effect = BuildCheckMaps(receiver, this_effect, this_control,
- receiver_maps);
+ access_builder.BuildCheckMaps(receiver, &this_effect, this_control,
+ receiver_maps);
fallthrough_control = nullptr;
} else {
ZoneVector<Node*> this_controls(zone());
@@ -1347,169 +1329,236 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) {
p.language_mode(), store_mode);
}
+Node* JSNativeContextSpecialization::InlinePropertyGetterCall(
+ Node* receiver, Node* context, Node* frame_state, Node** effect,
+ Node** control, ZoneVector<Node*>* if_exceptions,
+ PropertyAccessInfo const& access_info) {
+ Node* target = jsgraph()->Constant(access_info.constant());
+ FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
+ Handle<SharedFunctionInfo> shared_info =
+ frame_info.shared_info().ToHandleChecked();
+ // We need a FrameState for the getter stub to restore the correct
+ // context before returning to fullcodegen.
+ FrameStateFunctionInfo const* frame_info0 =
+ common()->CreateFrameStateFunctionInfo(FrameStateType::kGetterStub, 1, 0,
+ shared_info);
+ Node* frame_state0 = graph()->NewNode(
+ common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
+ frame_info0),
+ graph()->NewNode(common()->StateValues(1, SparseInputMask::Dense()),
+ receiver),
+ jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), context,
+ target, frame_state);
+
+ // Introduce the call to the getter function.
+ Node* value;
+ if (access_info.constant()->IsJSFunction()) {
+ value = *effect = *control = graph()->NewNode(
+ jsgraph()->javascript()->Call(2, CallFrequency(), VectorSlotPair(),
+ ConvertReceiverMode::kNotNullOrUndefined),
+ target, receiver, context, frame_state0, *effect, *control);
+ } else {
+ DCHECK(access_info.constant()->IsFunctionTemplateInfo());
+ Handle<FunctionTemplateInfo> function_template_info(
+ Handle<FunctionTemplateInfo>::cast(access_info.constant()));
+ DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
+ value = InlineApiCall(receiver, context, target, frame_state0, nullptr,
+ effect, control, shared_info, function_template_info);
+ }
+ // Remember to rewire the IfException edge if this is inside a try-block.
+ if (if_exceptions != nullptr) {
+ // Create the appropriate IfException/IfSuccess projections.
+ Node* const if_exception =
+ graph()->NewNode(common()->IfException(), *control, *effect);
+ Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control);
+ if_exceptions->push_back(if_exception);
+ *control = if_success;
+ }
+ return value;
+}
+
+Node* JSNativeContextSpecialization::InlinePropertySetterCall(
+ Node* receiver, Node* value, Node* context, Node* frame_state,
+ Node** effect, Node** control, ZoneVector<Node*>* if_exceptions,
+ PropertyAccessInfo const& access_info) {
+ Node* target = jsgraph()->Constant(access_info.constant());
+ FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
+ Handle<SharedFunctionInfo> shared_info =
+ frame_info.shared_info().ToHandleChecked();
+ // We need a FrameState for the setter stub to restore the correct
+ // context and return the appropriate value to fullcodegen.
+ FrameStateFunctionInfo const* frame_info0 =
+ common()->CreateFrameStateFunctionInfo(FrameStateType::kSetterStub, 2, 0,
+ shared_info);
+ Node* frame_state0 = graph()->NewNode(
+ common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
+ frame_info0),
+ graph()->NewNode(common()->StateValues(2, SparseInputMask::Dense()),
+ receiver, value),
+ jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), context,
+ target, frame_state);
+
+ // Introduce the call to the setter function.
+ if (access_info.constant()->IsJSFunction()) {
+ *effect = *control = graph()->NewNode(
+ jsgraph()->javascript()->Call(3, CallFrequency(), VectorSlotPair(),
+ ConvertReceiverMode::kNotNullOrUndefined),
+ target, receiver, value, context, frame_state0, *effect, *control);
+ } else {
+ DCHECK(access_info.constant()->IsFunctionTemplateInfo());
+ Handle<FunctionTemplateInfo> function_template_info(
+ Handle<FunctionTemplateInfo>::cast(access_info.constant()));
+ DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
+ value = InlineApiCall(receiver, context, target, frame_state0, value,
+ effect, control, shared_info, function_template_info);
+ }
+ // Remember to rewire the IfException edge if this is inside a try-block.
+ if (if_exceptions != nullptr) {
+ // Create the appropriate IfException/IfSuccess projections.
+ Node* const if_exception =
+ graph()->NewNode(common()->IfException(), *control, *effect);
+ Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control);
+ if_exceptions->push_back(if_exception);
+ *control = if_success;
+ }
+ return value;
+}
+
+Node* JSNativeContextSpecialization::InlineApiCall(
+ Node* receiver, Node* context, Node* target, Node* frame_state, Node* value,
+ Node** effect, Node** control, Handle<SharedFunctionInfo> shared_info,
+ Handle<FunctionTemplateInfo> function_template_info) {
+ Handle<CallHandlerInfo> call_handler_info = handle(
+ CallHandlerInfo::cast(function_template_info->call_code()), isolate());
+ Handle<Object> call_data_object(call_handler_info->data(), isolate());
+
+ // Only setters have a value.
+ int const argc = value == nullptr ? 0 : 1;
+ // The stub always expects the receiver as the first param on the stack.
+ CallApiCallbackStub stub(
+ isolate(), argc,
+ true /* FunctionTemplateInfo doesn't have an associated context. */);
+ CallInterfaceDescriptor call_interface_descriptor =
+ stub.GetCallInterfaceDescriptor();
+ CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
+ isolate(), graph()->zone(), call_interface_descriptor,
+ call_interface_descriptor.GetStackParameterCount() + argc +
+ 1 /* implicit receiver */,
+ CallDescriptor::kNeedsFrameState, Operator::kNoProperties,
+ MachineType::AnyTagged(), 1);
+
+ Node* data = jsgraph()->Constant(call_data_object);
+ ApiFunction function(v8::ToCData<Address>(call_handler_info->callback()));
+ Node* function_reference =
+ graph()->NewNode(common()->ExternalConstant(ExternalReference(
+ &function, ExternalReference::DIRECT_API_CALL, isolate())));
+ Node* code = jsgraph()->HeapConstant(stub.GetCode());
+
+ // Add CallApiCallbackStub's register argument as well.
+ Node* inputs[11] = {
+ code, target, data, receiver /* holder */, function_reference, receiver};
+ int index = 6 + argc;
+ inputs[index++] = context;
+ inputs[index++] = frame_state;
+ inputs[index++] = *effect;
+ inputs[index++] = *control;
+ // This needs to stay here because of the edge case described in
+ // http://crbug.com/675648.
+ if (value != nullptr) {
+ inputs[6] = value;
+ }
+
+ return *effect = *control =
+ graph()->NewNode(common()->Call(call_descriptor), index, inputs);
+}
+
+JSNativeContextSpecialization::ValueEffectControl
+JSNativeContextSpecialization::BuildPropertyLoad(
+ Node* receiver, Node* context, Node* frame_state, Node* effect,
+ Node* control, Handle<Name> name, ZoneVector<Node*>* if_exceptions,
+ PropertyAccessInfo const& access_info, LanguageMode language_mode) {
+ // Determine actual holder and perform prototype chain checks.
+ Handle<JSObject> holder;
+ PropertyAccessBuilder access_builder(jsgraph(), dependencies());
+ if (access_info.holder().ToHandle(&holder)) {
+ access_builder.AssumePrototypesStable(native_context(),
+ access_info.receiver_maps(), holder);
+ }
+
+ // Generate the actual property access.
+ Node* value;
+ if (access_info.IsNotFound()) {
+ value = jsgraph()->UndefinedConstant();
+ } else if (access_info.IsDataConstant()) {
+ DCHECK(!FLAG_track_constant_fields);
+ value = jsgraph()->Constant(access_info.constant());
+ } else if (access_info.IsAccessorConstant()) {
+ value = InlinePropertyGetterCall(receiver, context, frame_state, &effect,
+ &control, if_exceptions, access_info);
+ } else {
+ DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
+ value = access_builder.BuildLoadDataField(name, access_info, receiver,
+ &effect, &control);
+ }
+
+ return ValueEffectControl(value, effect, control);
+}
+
JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::BuildPropertyAccess(
Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect,
Node* control, Handle<Name> name, ZoneVector<Node*>* if_exceptions,
PropertyAccessInfo const& access_info, AccessMode access_mode,
LanguageMode language_mode) {
+ switch (access_mode) {
+ case AccessMode::kLoad:
+ return BuildPropertyLoad(receiver, context, frame_state, effect, control,
+ name, if_exceptions, access_info, language_mode);
+ case AccessMode::kStore:
+ case AccessMode::kStoreInLiteral:
+ return BuildPropertyStore(receiver, value, context, frame_state, effect,
+ control, name, if_exceptions, access_info,
+ access_mode, language_mode);
+ }
+ UNREACHABLE();
+ return ValueEffectControl();
+}
+
+JSNativeContextSpecialization::ValueEffectControl
+JSNativeContextSpecialization::BuildPropertyStore(
+ Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect,
+ Node* control, Handle<Name> name, ZoneVector<Node*>* if_exceptions,
+ PropertyAccessInfo const& access_info, AccessMode access_mode,
+ LanguageMode language_mode) {
// Determine actual holder and perform prototype chain checks.
Handle<JSObject> holder;
+ PropertyAccessBuilder access_builder(jsgraph(), dependencies());
if (access_info.holder().ToHandle(&holder)) {
DCHECK_NE(AccessMode::kStoreInLiteral, access_mode);
- AssumePrototypesStable(access_info.receiver_maps(), holder);
+ access_builder.AssumePrototypesStable(native_context(),
+ access_info.receiver_maps(), holder);
}
+ DCHECK(!access_info.IsNotFound());
+
// Generate the actual property access.
- if (access_info.IsNotFound()) {
- DCHECK_EQ(AccessMode::kLoad, access_mode);
- value = jsgraph()->UndefinedConstant();
- } else if (access_info.IsDataConstant()) {
+ if (access_info.IsDataConstant()) {
DCHECK(!FLAG_track_constant_fields);
Node* constant_value = jsgraph()->Constant(access_info.constant());
- if (access_mode == AccessMode::kStore) {
- Node* check = graph()->NewNode(simplified()->ReferenceEqual(), value,
- constant_value);
- effect =
- graph()->NewNode(simplified()->CheckIf(), check, effect, control);
- }
+ Node* check =
+ graph()->NewNode(simplified()->ReferenceEqual(), value, constant_value);
+ effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
value = constant_value;
} else if (access_info.IsAccessorConstant()) {
- Node* target = jsgraph()->Constant(access_info.constant());
- FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
- Handle<SharedFunctionInfo> shared_info =
- frame_info.shared_info().ToHandleChecked();
- switch (access_mode) {
- case AccessMode::kLoad: {
- // We need a FrameState for the getter stub to restore the correct
- // context before returning to unoptimized code.
- FrameStateFunctionInfo const* frame_info0 =
- common()->CreateFrameStateFunctionInfo(FrameStateType::kGetterStub,
- 1, 0, shared_info);
- Node* frame_state0 = graph()->NewNode(
- common()->FrameState(BailoutId::None(),
- OutputFrameStateCombine::Ignore(),
- frame_info0),
- graph()->NewNode(common()->StateValues(1, SparseInputMask::Dense()),
- receiver),
- jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(),
- context, target, frame_state);
-
- // Introduce the call to the getter function.
- if (access_info.constant()->IsJSFunction()) {
- value = effect = control = graph()->NewNode(
- javascript()->Call(2, CallFrequency(), VectorSlotPair(),
- ConvertReceiverMode::kNotNullOrUndefined),
- target, receiver, context, frame_state0, effect, control);
- } else {
- DCHECK(access_info.constant()->IsFunctionTemplateInfo());
- Handle<FunctionTemplateInfo> function_template_info(
- Handle<FunctionTemplateInfo>::cast(access_info.constant()));
- DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
- ValueEffectControl value_effect_control = InlineApiCall(
- receiver, context, target, frame_state0, nullptr, effect, control,
- shared_info, function_template_info);
- value = value_effect_control.value();
- effect = value_effect_control.effect();
- control = value_effect_control.control();
- }
- break;
- }
- case AccessMode::kStore: {
- // We need a FrameState for the setter stub to restore the correct
- // context and return the appropriate value to unoptimized code.
- FrameStateFunctionInfo const* frame_info0 =
- common()->CreateFrameStateFunctionInfo(FrameStateType::kSetterStub,
- 2, 0, shared_info);
- Node* frame_state0 = graph()->NewNode(
- common()->FrameState(BailoutId::None(),
- OutputFrameStateCombine::Ignore(),
- frame_info0),
- graph()->NewNode(common()->StateValues(2, SparseInputMask::Dense()),
- receiver, value),
- jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(),
- context, target, frame_state);
-
- // Introduce the call to the setter function.
- if (access_info.constant()->IsJSFunction()) {
- effect = control = graph()->NewNode(
- javascript()->Call(3, CallFrequency(), VectorSlotPair(),
- ConvertReceiverMode::kNotNullOrUndefined),
- target, receiver, value, context, frame_state0, effect, control);
- } else {
- DCHECK(access_info.constant()->IsFunctionTemplateInfo());
- Handle<FunctionTemplateInfo> function_template_info(
- Handle<FunctionTemplateInfo>::cast(access_info.constant()));
- DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
- ValueEffectControl value_effect_control = InlineApiCall(
- receiver, context, target, frame_state0, value, effect, control,
- shared_info, function_template_info);
- value = value_effect_control.value();
- effect = value_effect_control.effect();
- control = value_effect_control.control();
- }
- break;
- }
- case AccessMode::kStoreInLiteral: {
- UNREACHABLE();
- break;
- }
- }
- // Remember to rewire the IfException edge if this is inside a try-block.
- if (if_exceptions != nullptr) {
- // Create the appropriate IfException/IfSuccess projections.
- Node* const if_exception =
- graph()->NewNode(common()->IfException(), control, effect);
- Node* const if_success = graph()->NewNode(common()->IfSuccess(), control);
- if_exceptions->push_back(if_exception);
- control = if_success;
- }
+ value =
+ InlinePropertySetterCall(receiver, value, context, frame_state, &effect,
+ &control, if_exceptions, access_info);
} else {
DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
FieldIndex const field_index = access_info.field_index();
Type* const field_type = access_info.field_type();
MachineRepresentation const field_representation =
access_info.field_representation();
- if (access_mode == AccessMode::kLoad) {
- if (access_info.holder().ToHandle(&holder)) {
- receiver = jsgraph()->Constant(holder);
- }
- // Optimize immutable property loads.
- HeapObjectMatcher m(receiver);
- if (m.HasValue() && m.Value()->IsJSObject()) {
- // TODO(ishell): Use something simpler like
- //
- // Handle<Object> value =
- // JSObject::FastPropertyAt(Handle<JSObject>::cast(m.Value()),
- // Representation::Tagged(), field_index);
- //
- // here, once we have the immutable bit in the access_info.
-
- // TODO(turbofan): Given that we already have the field_index here, we
- // might be smarter in the future and not rely on the LookupIterator,
- // but for now let's just do what Crankshaft does.
- LookupIterator it(m.Value(), name,
- LookupIterator::OWN_SKIP_INTERCEPTOR);
- if (it.state() == LookupIterator::DATA) {
- bool is_reaonly_non_configurable =
- it.IsReadOnly() && !it.IsConfigurable();
- if (is_reaonly_non_configurable ||
- (FLAG_track_constant_fields &&
- access_info.IsDataConstantField())) {
- Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
- if (!is_reaonly_non_configurable) {
- // It's necessary to add dependency on the map that introduced
- // the field.
- DCHECK(access_info.IsDataConstantField());
- DCHECK(!it.is_dictionary_holder());
- Handle<Map> field_owner_map = it.GetFieldOwnerMap();
- dependencies()->AssumeFieldOwner(field_owner_map);
- }
- return ValueEffectControl(value, effect, control);
- }
- }
- }
- }
Node* storage = receiver;
if (!field_index.is_inobject()) {
storage = effect = graph()->NewNode(
@@ -1524,193 +1573,158 @@ JSNativeContextSpecialization::BuildPropertyAccess(
field_type,
MachineType::TypeForRepresentation(field_representation),
kFullWriteBarrier};
- if (access_mode == AccessMode::kLoad) {
- if (field_representation == MachineRepresentation::kFloat64) {
+ bool store_to_constant_field = FLAG_track_constant_fields &&
+ (access_mode == AccessMode::kStore) &&
+ access_info.IsDataConstantField();
+
+ DCHECK(access_mode == AccessMode::kStore ||
+ access_mode == AccessMode::kStoreInLiteral);
+ switch (field_representation) {
+ case MachineRepresentation::kFloat64: {
+ value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
+ effect, control);
if (!field_index.is_inobject() || field_index.is_hidden_field() ||
!FLAG_unbox_double_fields) {
- FieldAccess const storage_access = {kTaggedBase,
- field_index.offset(),
- name,
- MaybeHandle<Map>(),
- Type::OtherInternal(),
- MachineType::TaggedPointer(),
- kPointerWriteBarrier};
- storage = effect =
- graph()->NewNode(simplified()->LoadField(storage_access), storage,
- effect, control);
- field_access.offset = HeapNumber::kValueOffset;
- field_access.name = MaybeHandle<Name>();
- }
- } else if (field_representation ==
- MachineRepresentation::kTaggedPointer) {
- // Remember the map of the field value, if its map is stable. This is
- // used by the LoadElimination to eliminate map checks on the result.
- Handle<Map> field_map;
- if (access_info.field_map().ToHandle(&field_map)) {
- if (field_map->is_stable()) {
- dependencies()->AssumeMapStable(field_map);
- field_access.map = field_map;
+ 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(Type::OtherInternal(), 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::Any();
+ field_access.machine_type = MachineType::TaggedPointer();
+ field_access.write_barrier_kind = kPointerWriteBarrier;
+ } else {
+ // We just store directly to the MutableHeapNumber.
+ FieldAccess const storage_access = {kTaggedBase,
+ field_index.offset(),
+ name,
+ MaybeHandle<Map>(),
+ Type::OtherInternal(),
+ MachineType::TaggedPointer(),
+ kPointerWriteBarrier};
+ storage = effect =
+ graph()->NewNode(simplified()->LoadField(storage_access),
+ storage, effect, control);
+ field_access.offset = HeapNumber::kValueOffset;
+ field_access.name = MaybeHandle<Name>();
+ field_access.machine_type = MachineType::Float64();
}
}
+ if (store_to_constant_field) {
+ DCHECK(!access_info.HasTransitionMap());
+ // If the field is constant check that the value we are going
+ // to store matches current value.
+ Node* current_value = effect = graph()->NewNode(
+ simplified()->LoadField(field_access), storage, effect, control);
+
+ Node* check = graph()->NewNode(simplified()->NumberEqual(),
+ current_value, value);
+ effect =
+ graph()->NewNode(simplified()->CheckIf(), check, effect, control);
+ return ValueEffectControl(value, effect, control);
+ }
+ break;
}
- value = effect = graph()->NewNode(simplified()->LoadField(field_access),
- storage, effect, control);
- } else {
- bool store_to_constant_field = FLAG_track_constant_fields &&
- (access_mode == AccessMode::kStore) &&
- access_info.IsDataConstantField();
-
- DCHECK(access_mode == AccessMode::kStore ||
- access_mode == AccessMode::kStoreInLiteral);
- switch (field_representation) {
- case MachineRepresentation::kFloat64: {
- 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(Type::OtherInternal(), 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::Any();
- field_access.machine_type = MachineType::TaggedPointer();
- field_access.write_barrier_kind = kPointerWriteBarrier;
- } else {
- // We just store directly to the MutableHeapNumber.
- FieldAccess const storage_access = {kTaggedBase,
- field_index.offset(),
- name,
- MaybeHandle<Map>(),
- Type::OtherInternal(),
- MachineType::TaggedPointer(),
- kPointerWriteBarrier};
- storage = effect =
- graph()->NewNode(simplified()->LoadField(storage_access),
- storage, effect, control);
- field_access.offset = HeapNumber::kValueOffset;
- field_access.name = MaybeHandle<Name>();
- field_access.machine_type = MachineType::Float64();
- }
- }
- if (store_to_constant_field) {
- DCHECK(!access_info.HasTransitionMap());
- // If the field is constant check that the value we are going
- // to store matches current value.
- Node* current_value = effect =
- graph()->NewNode(simplified()->LoadField(field_access), storage,
- effect, control);
-
- Node* check = graph()->NewNode(simplified()->NumberEqual(),
- current_value, value);
- effect = graph()->NewNode(simplified()->CheckIf(), check, effect,
- control);
- return ValueEffectControl(value, effect, control);
- }
- break;
+ case MachineRepresentation::kTaggedSigned:
+ case MachineRepresentation::kTaggedPointer:
+ case MachineRepresentation::kTagged:
+ if (store_to_constant_field) {
+ DCHECK(!access_info.HasTransitionMap());
+ // If the field is constant check that the value we are going
+ // to store matches current value.
+ Node* current_value = effect = graph()->NewNode(
+ simplified()->LoadField(field_access), storage, effect, control);
+
+ Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
+ current_value, value);
+ effect =
+ graph()->NewNode(simplified()->CheckIf(), check, effect, control);
+ return ValueEffectControl(value, effect, control);
}
- case MachineRepresentation::kTaggedSigned:
- case MachineRepresentation::kTaggedPointer:
- case MachineRepresentation::kTagged:
- if (store_to_constant_field) {
- DCHECK(!access_info.HasTransitionMap());
- // If the field is constant check that the value we are going
- // to store matches current value.
- Node* current_value = effect =
- graph()->NewNode(simplified()->LoadField(field_access), storage,
- effect, control);
-
- Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
- current_value, value);
- effect = graph()->NewNode(simplified()->CheckIf(), check, effect,
- control);
- return ValueEffectControl(value, effect, control);
- }
- if (field_representation == MachineRepresentation::kTaggedSigned) {
- value = effect = graph()->NewNode(simplified()->CheckSmi(), value,
- effect, control);
- field_access.write_barrier_kind = kNoWriteBarrier;
-
- } else if (field_representation ==
- MachineRepresentation::kTaggedPointer) {
- // Ensure that {value} is a HeapObject.
- value = BuildCheckHeapObject(value, &effect, control);
- Handle<Map> field_map;
- if (access_info.field_map().ToHandle(&field_map)) {
- // Emit a map check for the value.
- effect = graph()->NewNode(
- simplified()->CheckMaps(CheckMapsFlag::kNone,
- ZoneHandleSet<Map>(field_map)),
- value, effect, control);
- }
- field_access.write_barrier_kind = kPointerWriteBarrier;
-
- } else {
- DCHECK_EQ(MachineRepresentation::kTagged, field_representation);
+ if (field_representation == MachineRepresentation::kTaggedSigned) {
+ value = effect = graph()->NewNode(simplified()->CheckSmi(), value,
+ effect, control);
+ field_access.write_barrier_kind = kNoWriteBarrier;
+
+ } else if (field_representation ==
+ MachineRepresentation::kTaggedPointer) {
+ // Ensure that {value} is a HeapObject.
+ value = access_builder.BuildCheckHeapObject(value, &effect, control);
+ Handle<Map> field_map;
+ if (access_info.field_map().ToHandle(&field_map)) {
+ // Emit a map check for the value.
+ effect = graph()->NewNode(
+ simplified()->CheckMaps(CheckMapsFlag::kNone,
+ ZoneHandleSet<Map>(field_map)),
+ value, effect, control);
}
- break;
- case MachineRepresentation::kNone:
- case MachineRepresentation::kBit:
- case MachineRepresentation::kWord8:
- case MachineRepresentation::kWord16:
- case MachineRepresentation::kWord32:
- case MachineRepresentation::kWord64:
- case MachineRepresentation::kFloat32:
- case MachineRepresentation::kSimd128:
- UNREACHABLE();
- break;
- }
- // Check if we need to perform a transitioning store.
- Handle<Map> transition_map;
- if (access_info.transition_map().ToHandle(&transition_map)) {
- // Check if we need to grow the properties backing store
- // with this transitioning store.
- Handle<Map> original_map(Map::cast(transition_map->GetBackPointer()),
- isolate());
- if (original_map->unused_property_fields() == 0) {
- DCHECK(!field_index.is_inobject());
-
- // Reallocate the properties {storage}.
- storage = effect = BuildExtendPropertiesBackingStore(
- original_map, storage, effect, control);
-
- // Perform the actual store.
- effect = graph()->NewNode(simplified()->StoreField(field_access),
- storage, value, effect, control);
-
- // Atomically switch to the new properties below.
- field_access = AccessBuilder::ForJSObjectProperties();
- value = storage;
- storage = receiver;
+ field_access.write_barrier_kind = kPointerWriteBarrier;
+
+ } else {
+ DCHECK_EQ(MachineRepresentation::kTagged, field_representation);
}
- 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);
- effect = graph()->NewNode(common()->FinishRegion(),
- jsgraph()->UndefinedConstant(), effect);
- } else {
- // Regular non-transitioning field store.
+ break;
+ case MachineRepresentation::kNone:
+ case MachineRepresentation::kBit:
+ case MachineRepresentation::kWord8:
+ case MachineRepresentation::kWord16:
+ case MachineRepresentation::kWord32:
+ case MachineRepresentation::kWord64:
+ case MachineRepresentation::kFloat32:
+ case MachineRepresentation::kSimd128:
+ UNREACHABLE();
+ break;
+ }
+ // Check if we need to perform a transitioning store.
+ Handle<Map> transition_map;
+ if (access_info.transition_map().ToHandle(&transition_map)) {
+ // Check if we need to grow the properties backing store
+ // with this transitioning store.
+ Handle<Map> original_map(Map::cast(transition_map->GetBackPointer()),
+ isolate());
+ if (original_map->unused_property_fields() == 0) {
+ DCHECK(!field_index.is_inobject());
+
+ // Reallocate the properties {storage}.
+ storage = effect = BuildExtendPropertiesBackingStore(
+ original_map, storage, effect, control);
+
+ // Perform the actual store.
effect = graph()->NewNode(simplified()->StoreField(field_access),
storage, value, effect, control);
+
+ // Atomically switch to the new properties below.
+ field_access = AccessBuilder::ForJSObjectProperties();
+ value = storage;
+ storage = receiver;
}
+ 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);
+ effect = graph()->NewNode(common()->FinishRegion(),
+ jsgraph()->UndefinedConstant(), effect);
+ } else {
+ // Regular non-transitioning field store.
+ effect = graph()->NewNode(simplified()->StoreField(field_access), storage,
+ value, effect, control);
}
}
@@ -1763,10 +1777,10 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreDataPropertyInLiteral(
Node* control = NodeProperties::GetControlInput(node);
// Monomorphic property access.
- receiver = BuildCheckHeapObject(receiver, &effect, control);
-
- effect =
- BuildCheckMaps(receiver, effect, control, access_info.receiver_maps());
+ PropertyAccessBuilder access_builder(jsgraph(), dependencies());
+ receiver = access_builder.BuildCheckHeapObject(receiver, &effect, control);
+ access_builder.BuildCheckMaps(receiver, &effect, control,
+ access_info.receiver_maps());
// Ensure that {name} matches the cached name.
Node* name = NodeProperties::GetValueInput(node, 1);
@@ -2107,112 +2121,6 @@ JSNativeContextSpecialization::BuildElementAccess(
return ValueEffectControl(value, effect, control);
}
-JSNativeContextSpecialization::ValueEffectControl
-JSNativeContextSpecialization::InlineApiCall(
- Node* receiver, Node* context, Node* target, Node* frame_state, Node* value,
- Node* effect, Node* control, Handle<SharedFunctionInfo> shared_info,
- Handle<FunctionTemplateInfo> function_template_info) {
- Handle<CallHandlerInfo> call_handler_info = handle(
- CallHandlerInfo::cast(function_template_info->call_code()), isolate());
- Handle<Object> call_data_object(call_handler_info->data(), isolate());
-
- // Only setters have a value.
- int const argc = value == nullptr ? 0 : 1;
- // The stub always expects the receiver as the first param on the stack.
- CallApiCallbackStub stub(
- isolate(), argc,
- true /* FunctionTemplateInfo doesn't have an associated context. */);
- CallInterfaceDescriptor call_interface_descriptor =
- stub.GetCallInterfaceDescriptor();
- CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
- isolate(), graph()->zone(), call_interface_descriptor,
- call_interface_descriptor.GetStackParameterCount() + argc +
- 1 /* implicit receiver */,
- CallDescriptor::kNeedsFrameState, Operator::kNoProperties,
- MachineType::AnyTagged(), 1);
-
- Node* data = jsgraph()->Constant(call_data_object);
- ApiFunction function(v8::ToCData<Address>(call_handler_info->callback()));
- Node* function_reference =
- graph()->NewNode(common()->ExternalConstant(ExternalReference(
- &function, ExternalReference::DIRECT_API_CALL, isolate())));
- Node* code = jsgraph()->HeapConstant(stub.GetCode());
-
- // Add CallApiCallbackStub's register argument as well.
- Node* inputs[11] = {
- code, target, data, receiver /* holder */, function_reference, receiver};
- int index = 6 + argc;
- inputs[index++] = context;
- inputs[index++] = frame_state;
- inputs[index++] = effect;
- inputs[index++] = control;
- // This needs to stay here because of the edge case described in
- // http://crbug.com/675648.
- if (value != nullptr) {
- inputs[6] = value;
- }
-
- Node* control0;
- Node* effect0;
- Node* value0 = effect0 = control0 =
- graph()->NewNode(common()->Call(call_descriptor), index, inputs);
- return ValueEffectControl(value0, effect0, control0);
-}
-
-Node* JSNativeContextSpecialization::BuildCheckHeapObject(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 receiver;
- }
- default: {
- return *effect = graph()->NewNode(simplified()->CheckHeapObject(),
- receiver, *effect, control);
- }
- }
-}
-
-Node* JSNativeContextSpecialization::BuildCheckMaps(
- Node* receiver, Node* effect, Node* control,
- MapHandles const& receiver_maps) {
- HeapObjectMatcher m(receiver);
- if (m.HasValue()) {
- Handle<Map> receiver_map(m.Value()->map(), isolate());
- if (receiver_map->is_stable()) {
- for (Handle<Map> map : receiver_maps) {
- if (map.is_identical_to(receiver_map)) {
- dependencies()->AssumeMapStable(receiver_map);
- return effect;
- }
- }
- }
- }
- ZoneHandleSet<Map> maps;
- CheckMapsFlags flags = CheckMapsFlag::kNone;
- for (Handle<Map> map : receiver_maps) {
- maps.insert(map, graph()->zone());
- if (map->is_migration_target()) {
- flags |= CheckMapsFlag::kTryMigrateInstance;
- }
- }
- return graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver,
- effect, control);
-}
-
Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
Handle<Map> map, Node* properties, Node* effect, Node* control) {
// TODO(bmeurer/jkummerow): Property deletions can undo map transitions
@@ -2261,21 +2169,6 @@ Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
return graph()->NewNode(common()->FinishRegion(), new_properties, effect);
}
-void JSNativeContextSpecialization::AssumePrototypesStable(
- MapHandles const& receiver_maps, Handle<JSObject> holder) {
- // Determine actual holder and perform prototype chain checks.
- 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;
- if (Map::GetConstructorFunction(map, native_context())
- .ToHandle(&constructor)) {
- map = handle(constructor->initial_map(), isolate());
- }
- dependencies()->AssumePrototypeMapsStable(map, holder);
- }
-}
-
bool JSNativeContextSpecialization::CanTreatHoleAsUndefined(
MapHandles const& receiver_maps) {
// Check if the array prototype chain is intact.
« no previous file with comments | « src/compiler/js-native-context-specialization.h ('k') | src/compiler/property-access-builder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698