| OLD | NEW |
| 1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/compiler/js-native-context-specialization.h" | 5 #include "src/compiler/js-native-context-specialization.h" |
| 6 | 6 |
| 7 #include "src/accessors.h" | 7 #include "src/accessors.h" |
| 8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
| 9 #include "src/compilation-dependencies.h" | 9 #include "src/compilation-dependencies.h" |
| 10 #include "src/compiler/access-builder.h" | 10 #include "src/compiler/access-builder.h" |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 103 | 103 |
| 104 Reduction JSNativeContextSpecialization::ReduceNamedAccess( | 104 Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
| 105 Node* node, Node* value, MapHandleList const& receiver_maps, | 105 Node* node, Node* value, MapHandleList const& receiver_maps, |
| 106 Handle<Name> name, AccessMode access_mode, LanguageMode language_mode, | 106 Handle<Name> name, AccessMode access_mode, LanguageMode language_mode, |
| 107 Node* index) { | 107 Node* index) { |
| 108 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || | 108 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || |
| 109 node->opcode() == IrOpcode::kJSStoreNamed || | 109 node->opcode() == IrOpcode::kJSStoreNamed || |
| 110 node->opcode() == IrOpcode::kJSLoadProperty || | 110 node->opcode() == IrOpcode::kJSLoadProperty || |
| 111 node->opcode() == IrOpcode::kJSStoreProperty); | 111 node->opcode() == IrOpcode::kJSStoreProperty); |
| 112 Node* receiver = NodeProperties::GetValueInput(node, 0); | 112 Node* receiver = NodeProperties::GetValueInput(node, 0); |
| 113 Node* context = NodeProperties::GetContextInput(node); |
| 114 Node* frame_state_eager = NodeProperties::FindFrameStateBefore(node); |
| 115 Node* frame_state_lazy = NodeProperties::GetFrameStateInput(node, 0); |
| 113 Node* effect = NodeProperties::GetEffectInput(node); | 116 Node* effect = NodeProperties::GetEffectInput(node); |
| 114 Node* control = NodeProperties::GetControlInput(node); | 117 Node* control = NodeProperties::GetControlInput(node); |
| 115 | 118 |
| 116 // Not much we can do if deoptimization support is disabled. | 119 // Not much we can do if deoptimization support is disabled. |
| 117 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | 120 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
| 118 | 121 |
| 119 // Retrieve the native context from the given {node}. | 122 // Retrieve the native context from the given {node}. |
| 120 Handle<Context> native_context; | 123 Handle<Context> native_context; |
| 121 if (!GetNativeContext(node).ToHandle(&native_context)) return NoChange(); | 124 if (!GetNativeContext(node).ToHandle(&native_context)) return NoChange(); |
| 122 | 125 |
| 123 // Compute property access infos for the receiver maps. | 126 // Compute property access infos for the receiver maps. |
| 124 AccessInfoFactory access_info_factory(dependencies(), native_context, | 127 AccessInfoFactory access_info_factory(dependencies(), native_context, |
| 125 graph()->zone()); | 128 graph()->zone()); |
| 126 ZoneVector<PropertyAccessInfo> access_infos(zone()); | 129 ZoneVector<PropertyAccessInfo> access_infos(zone()); |
| 127 if (!access_info_factory.ComputePropertyAccessInfos( | 130 if (!access_info_factory.ComputePropertyAccessInfos( |
| 128 receiver_maps, name, access_mode, &access_infos)) { | 131 receiver_maps, name, access_mode, &access_infos)) { |
| 129 return NoChange(); | 132 return NoChange(); |
| 130 } | 133 } |
| 131 | 134 |
| 135 // TODO(turbofan): Add support for inlining into try blocks. |
| 136 if (NodeProperties::IsExceptionalCall(node) || |
| 137 !(flags() & kAccessorInliningEnabled)) { |
| 138 for (auto access_info : access_infos) { |
| 139 if (access_info.IsAccessorConstant()) return NoChange(); |
| 140 } |
| 141 } |
| 142 |
| 132 // Nothing to do if we have no non-deprecated maps. | 143 // Nothing to do if we have no non-deprecated maps. |
| 133 if (access_infos.empty()) { | 144 if (access_infos.empty()) { |
| 134 return ReduceSoftDeoptimize( | 145 return ReduceSoftDeoptimize( |
| 135 node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess); | 146 node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess); |
| 136 } | 147 } |
| 137 | 148 |
| 138 // Ensure that {index} matches the specified {name} (if {index} is given). | 149 // Ensure that {index} matches the specified {name} (if {index} is given). |
| 139 if (index != nullptr) { | 150 if (index != nullptr) { |
| 140 Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Name()), | 151 Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Name()), |
| 141 index, jsgraph()->HeapConstant(name)); | 152 index, jsgraph()->HeapConstant(name)); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 155 receiver = effect = graph()->NewNode(simplified()->CheckNumber(), | 166 receiver = effect = graph()->NewNode(simplified()->CheckNumber(), |
| 156 receiver, effect, control); | 167 receiver, effect, control); |
| 157 } else { | 168 } else { |
| 158 // Monomorphic property access. | 169 // Monomorphic property access. |
| 159 effect = BuildCheckTaggedPointer(receiver, effect, control); | 170 effect = BuildCheckTaggedPointer(receiver, effect, control); |
| 160 effect = BuildCheckMaps(receiver, effect, control, | 171 effect = BuildCheckMaps(receiver, effect, control, |
| 161 access_info.receiver_maps()); | 172 access_info.receiver_maps()); |
| 162 } | 173 } |
| 163 | 174 |
| 164 // Generate the actual property access. | 175 // Generate the actual property access. |
| 165 ValueEffectControl continuation = | 176 ValueEffectControl continuation = BuildPropertyAccess( |
| 166 BuildPropertyAccess(receiver, value, effect, control, name, | 177 receiver, value, context, frame_state_lazy, effect, control, name, |
| 167 native_context, access_info, access_mode); | 178 native_context, access_info, access_mode); |
| 168 value = continuation.value(); | 179 value = continuation.value(); |
| 169 effect = continuation.effect(); | 180 effect = continuation.effect(); |
| 170 control = continuation.control(); | 181 control = continuation.control(); |
| 171 } else { | 182 } else { |
| 172 // The final states for every polymorphic branch. We join them with | 183 // The final states for every polymorphic branch. We join them with |
| 173 // Merge+Phi+EffectPhi at the bottom. | 184 // Merge+Phi+EffectPhi at the bottom. |
| 174 ZoneVector<Node*> values(zone()); | 185 ZoneVector<Node*> values(zone()); |
| 175 ZoneVector<Node*> effects(zone()); | 186 ZoneVector<Node*> effects(zone()); |
| 176 ZoneVector<Node*> controls(zone()); | 187 ZoneVector<Node*> controls(zone()); |
| 177 | 188 |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 243 // The Number case requires special treatment to also deal with Smis. | 254 // The Number case requires special treatment to also deal with Smis. |
| 244 if (HasNumberMaps(receiver_maps)) { | 255 if (HasNumberMaps(receiver_maps)) { |
| 245 // Join this check with the "receiver is smi" check above. | 256 // Join this check with the "receiver is smi" check above. |
| 246 DCHECK_NOT_NULL(receiverissmi_effect); | 257 DCHECK_NOT_NULL(receiverissmi_effect); |
| 247 DCHECK_NOT_NULL(receiverissmi_control); | 258 DCHECK_NOT_NULL(receiverissmi_control); |
| 248 this_effects.push_back(receiverissmi_effect); | 259 this_effects.push_back(receiverissmi_effect); |
| 249 this_controls.push_back(receiverissmi_control); | 260 this_controls.push_back(receiverissmi_control); |
| 250 receiverissmi_effect = receiverissmi_control = nullptr; | 261 receiverissmi_effect = receiverissmi_control = nullptr; |
| 251 } | 262 } |
| 252 | 263 |
| 253 // Create dominating Merge+EffectPhi for this {receiver} type. | 264 // Create single chokepoint for the control. |
| 254 int const this_control_count = static_cast<int>(this_controls.size()); | 265 int const this_control_count = static_cast<int>(this_controls.size()); |
| 255 this_control = | 266 if (this_control_count == 1) { |
| 256 (this_control_count == 1) | 267 this_control = this_controls.front(); |
| 257 ? this_controls.front() | 268 this_effect = this_effects.front(); |
| 258 : graph()->NewNode(common()->Merge(this_control_count), | 269 } else { |
| 259 this_control_count, &this_controls.front()); | 270 this_control = |
| 260 this_effects.push_back(this_control); | 271 graph()->NewNode(common()->Merge(this_control_count), |
| 261 int const this_effect_count = static_cast<int>(this_effects.size()); | 272 this_control_count, &this_controls.front()); |
| 262 this_effect = | 273 this_effects.push_back(this_control); |
| 263 (this_control_count == 1) | 274 this_effect = |
| 264 ? this_effects.front() | 275 graph()->NewNode(common()->EffectPhi(this_control_count), |
| 265 : graph()->NewNode(common()->EffectPhi(this_control_count), | 276 this_control_count + 1, &this_effects.front()); |
| 266 this_effect_count, &this_effects.front()); | 277 |
| 278 // TODO(turbofan): The effect/control linearization will not find a |
| 279 // FrameState after the EffectPhi that is generated above. |
| 280 this_effect = |
| 281 graph()->NewNode(common()->Checkpoint(), frame_state_eager, |
| 282 this_effect, this_control); |
| 283 } |
| 267 } | 284 } |
| 268 | 285 |
| 269 // Generate the actual property access. | 286 // Generate the actual property access. |
| 270 ValueEffectControl continuation = BuildPropertyAccess( | 287 ValueEffectControl continuation = BuildPropertyAccess( |
| 271 this_receiver, this_value, this_effect, this_control, name, | 288 this_receiver, this_value, context, frame_state_lazy, this_effect, |
| 272 native_context, access_info, access_mode); | 289 this_control, name, native_context, access_info, access_mode); |
| 273 values.push_back(continuation.value()); | 290 values.push_back(continuation.value()); |
| 274 effects.push_back(continuation.effect()); | 291 effects.push_back(continuation.effect()); |
| 275 controls.push_back(continuation.control()); | 292 controls.push_back(continuation.control()); |
| 276 } | 293 } |
| 277 | 294 |
| 278 DCHECK_NULL(fallthrough_control); | 295 DCHECK_NULL(fallthrough_control); |
| 279 | 296 |
| 280 // Generate the final merge point for all (polymorphic) branches. | 297 // Generate the final merge point for all (polymorphic) branches. |
| 281 int const control_count = static_cast<int>(controls.size()); | 298 int const control_count = static_cast<int>(controls.size()); |
| 282 if (control_count == 0) { | 299 if (control_count == 0) { |
| (...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 548 } else { | 565 } else { |
| 549 this_control = | 566 this_control = |
| 550 graph()->NewNode(common()->Merge(this_control_count), | 567 graph()->NewNode(common()->Merge(this_control_count), |
| 551 this_control_count, &this_controls.front()); | 568 this_control_count, &this_controls.front()); |
| 552 this_effects.push_back(this_control); | 569 this_effects.push_back(this_control); |
| 553 this_effect = | 570 this_effect = |
| 554 graph()->NewNode(common()->EffectPhi(this_control_count), | 571 graph()->NewNode(common()->EffectPhi(this_control_count), |
| 555 this_control_count + 1, &this_effects.front()); | 572 this_control_count + 1, &this_effects.front()); |
| 556 | 573 |
| 557 // TODO(turbofan): The effect/control linearization will not find a | 574 // TODO(turbofan): The effect/control linearization will not find a |
| 558 // FrameState after the StoreField or Call that is generated for the | 575 // FrameState after the EffectPhi that is generated above. |
| 559 // elements kind transition above. This is because those operators | |
| 560 // don't have the kNoWrite flag on it, even though they are not | |
| 561 // observable by JavaScript. | |
| 562 this_effect = graph()->NewNode(common()->Checkpoint(), frame_state, | 576 this_effect = graph()->NewNode(common()->Checkpoint(), frame_state, |
| 563 this_effect, this_control); | 577 this_effect, this_control); |
| 564 } | 578 } |
| 565 } | 579 } |
| 566 | 580 |
| 567 // Certain stores need a prototype chain check because shape changes | 581 // Certain stores need a prototype chain check because shape changes |
| 568 // could allow callbacks on elements in the prototype chain that are | 582 // could allow callbacks on elements in the prototype chain that are |
| 569 // not compatible with (monomorphic) keyed stores. | 583 // not compatible with (monomorphic) keyed stores. |
| 570 Handle<JSObject> holder; | 584 Handle<JSObject> holder; |
| 571 if (access_info.holder().ToHandle(&holder)) { | 585 if (access_info.holder().ToHandle(&holder)) { |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 727 // Extract the keyed access store mode from the KEYED_STORE_IC. | 741 // Extract the keyed access store mode from the KEYED_STORE_IC. |
| 728 KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode(); | 742 KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode(); |
| 729 | 743 |
| 730 // Try to lower the keyed access based on the {nexus}. | 744 // Try to lower the keyed access based on the {nexus}. |
| 731 return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kStore, | 745 return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kStore, |
| 732 p.language_mode(), store_mode); | 746 p.language_mode(), store_mode); |
| 733 } | 747 } |
| 734 | 748 |
| 735 JSNativeContextSpecialization::ValueEffectControl | 749 JSNativeContextSpecialization::ValueEffectControl |
| 736 JSNativeContextSpecialization::BuildPropertyAccess( | 750 JSNativeContextSpecialization::BuildPropertyAccess( |
| 737 Node* receiver, Node* value, Node* effect, Node* control, Handle<Name> name, | 751 Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect, |
| 738 Handle<Context> native_context, PropertyAccessInfo const& access_info, | 752 Node* control, Handle<Name> name, Handle<Context> native_context, |
| 739 AccessMode access_mode) { | 753 PropertyAccessInfo const& access_info, AccessMode access_mode) { |
| 740 // Determine actual holder and perform prototype chain checks. | 754 // Determine actual holder and perform prototype chain checks. |
| 741 Handle<JSObject> holder; | 755 Handle<JSObject> holder; |
| 742 if (access_info.holder().ToHandle(&holder)) { | 756 if (access_info.holder().ToHandle(&holder)) { |
| 743 AssumePrototypesStable(access_info.receiver_maps(), native_context, holder); | 757 AssumePrototypesStable(access_info.receiver_maps(), native_context, holder); |
| 744 } | 758 } |
| 745 | 759 |
| 746 // Generate the actual property access. | 760 // Generate the actual property access. |
| 747 if (access_info.IsNotFound()) { | 761 if (access_info.IsNotFound()) { |
| 748 DCHECK_EQ(AccessMode::kLoad, access_mode); | 762 DCHECK_EQ(AccessMode::kLoad, access_mode); |
| 749 value = jsgraph()->UndefinedConstant(); | 763 value = jsgraph()->UndefinedConstant(); |
| 750 } else if (access_info.IsDataConstant()) { | 764 } else if (access_info.IsDataConstant()) { |
| 751 value = jsgraph()->Constant(access_info.constant()); | 765 value = jsgraph()->Constant(access_info.constant()); |
| 752 if (access_mode == AccessMode::kStore) { | 766 if (access_mode == AccessMode::kStore) { |
| 753 Node* check = graph()->NewNode( | 767 Node* check = graph()->NewNode( |
| 754 simplified()->ReferenceEqual(Type::Tagged()), value, value); | 768 simplified()->ReferenceEqual(Type::Tagged()), value, value); |
| 755 effect = | 769 effect = |
| 756 graph()->NewNode(simplified()->CheckIf(), check, effect, control); | 770 graph()->NewNode(simplified()->CheckIf(), check, effect, control); |
| 757 } | 771 } |
| 772 } else if (access_info.IsAccessorConstant()) { |
| 773 // TODO(bmeurer): Properly rewire the IfException edge here if there's any. |
| 774 Node* target = jsgraph()->Constant(access_info.constant()); |
| 775 FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state); |
| 776 Handle<SharedFunctionInfo> shared_info = |
| 777 frame_info.shared_info().ToHandleChecked(); |
| 778 switch (access_mode) { |
| 779 case AccessMode::kLoad: { |
| 780 // We need a FrameState for the getter stub to restore the correct |
| 781 // context before returning to fullcodegen. |
| 782 FrameStateFunctionInfo const* frame_info0 = |
| 783 common()->CreateFrameStateFunctionInfo(FrameStateType::kGetterStub, |
| 784 1, 0, shared_info); |
| 785 Node* frame_state0 = graph()->NewNode( |
| 786 common()->FrameState(BailoutId::None(), |
| 787 OutputFrameStateCombine::Ignore(), |
| 788 frame_info0), |
| 789 graph()->NewNode(common()->StateValues(1), receiver), |
| 790 jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), |
| 791 context, target, frame_state); |
| 792 |
| 793 // Introduce the call to the getter function. |
| 794 value = effect = graph()->NewNode( |
| 795 javascript()->CallFunction( |
| 796 2, VectorSlotPair(), ConvertReceiverMode::kNotNullOrUndefined), |
| 797 target, receiver, context, frame_state0, effect, control); |
| 798 control = graph()->NewNode(common()->IfSuccess(), value); |
| 799 break; |
| 800 } |
| 801 case AccessMode::kStore: { |
| 802 // We need a FrameState for the setter stub to restore the correct |
| 803 // context and return the appropriate value to fullcodegen. |
| 804 FrameStateFunctionInfo const* frame_info0 = |
| 805 common()->CreateFrameStateFunctionInfo(FrameStateType::kSetterStub, |
| 806 2, 0, shared_info); |
| 807 Node* frame_state0 = graph()->NewNode( |
| 808 common()->FrameState(BailoutId::None(), |
| 809 OutputFrameStateCombine::Ignore(), |
| 810 frame_info0), |
| 811 graph()->NewNode(common()->StateValues(2), receiver, value), |
| 812 jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), |
| 813 context, target, frame_state); |
| 814 |
| 815 // Introduce the call to the setter function. |
| 816 effect = graph()->NewNode( |
| 817 javascript()->CallFunction( |
| 818 3, VectorSlotPair(), ConvertReceiverMode::kNotNullOrUndefined), |
| 819 target, receiver, value, context, frame_state0, effect, control); |
| 820 control = graph()->NewNode(common()->IfSuccess(), effect); |
| 821 break; |
| 822 } |
| 823 } |
| 758 } else { | 824 } else { |
| 759 DCHECK(access_info.IsDataField()); | 825 DCHECK(access_info.IsDataField()); |
| 760 FieldIndex const field_index = access_info.field_index(); | 826 FieldIndex const field_index = access_info.field_index(); |
| 761 Type* const field_type = access_info.field_type(); | 827 Type* const field_type = access_info.field_type(); |
| 762 if (access_mode == AccessMode::kLoad && | 828 if (access_mode == AccessMode::kLoad && |
| 763 access_info.holder().ToHandle(&holder)) { | 829 access_info.holder().ToHandle(&holder)) { |
| 764 receiver = jsgraph()->Constant(holder); | 830 receiver = jsgraph()->Constant(holder); |
| 765 } | 831 } |
| 766 Node* storage = receiver; | 832 Node* storage = receiver; |
| 767 if (!field_index.is_inobject()) { | 833 if (!field_index.is_inobject()) { |
| (...skipping 425 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1193 } | 1259 } |
| 1194 | 1260 |
| 1195 | 1261 |
| 1196 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 1262 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { |
| 1197 return jsgraph()->simplified(); | 1263 return jsgraph()->simplified(); |
| 1198 } | 1264 } |
| 1199 | 1265 |
| 1200 } // namespace compiler | 1266 } // namespace compiler |
| 1201 } // namespace internal | 1267 } // namespace internal |
| 1202 } // namespace v8 | 1268 } // namespace v8 |
| OLD | NEW |