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. |