Chromium Code Reviews| 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..8010901c20ab536772b631c7ee05822f20931a7d 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,38 +1573,6 @@ JSNativeContextSpecialization::BuildPropertyAccess( |
| field_type, |
| MachineType::TypeForRepresentation(field_representation), |
| kFullWriteBarrier}; |
| - if (access_mode == AccessMode::kLoad) { |
| - if (field_representation == MachineRepresentation::kFloat64) { |
| - 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; |
| - } |
| - } |
| - } |
| - value = effect = graph()->NewNode(simplified()->LoadField(field_access), |
| - storage, effect, control); |
| - } else { |
| bool store_to_constant_field = FLAG_track_constant_fields && |
|
Michael Starzinger
2017/06/16 08:32:22
nit: Indentation looks off starting here, all the
Jarin
2017/06/16 09:32:27
Done.
|
| (access_mode == AccessMode::kStore) && |
| access_info.IsDataConstantField(); |
| @@ -1648,7 +1665,8 @@ JSNativeContextSpecialization::BuildPropertyAccess( |
| } else if (field_representation == |
| MachineRepresentation::kTaggedPointer) { |
| // Ensure that {value} is a HeapObject. |
| - value = BuildCheckHeapObject(value, &effect, control); |
| + 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. |
| @@ -1712,7 +1730,6 @@ JSNativeContextSpecialization::BuildPropertyAccess( |
| storage, value, effect, control); |
| } |
| } |
| - } |
| return ValueEffectControl(value, effect, control); |
| } |
| @@ -1763,10 +1780,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 +2124,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 +2172,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. |