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 42920f74e0665fbc92173072180e4ce123e2f291..e83ca073d79bf2cd5005b991739c000fbede3312 100644 |
--- a/src/compiler/js-native-context-specialization.cc |
+++ b/src/compiler/js-native-context-specialization.cc |
@@ -477,10 +477,11 @@ bool JSNativeContextSpecialization::ComputePropertyAccessInfos( |
} |
-Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
- DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); |
- NamedAccess const& p = NamedAccessOf(node->op()); |
- Handle<Name> name = p.name(); |
+Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
+ Node* node, Node* value, MapHandleList const& receiver_maps, |
+ Handle<Name> name, PropertyAccessMode access_mode) { |
+ DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || |
+ node->opcode() == IrOpcode::kJSStoreNamed); |
Node* receiver = NodeProperties::GetValueInput(node, 0); |
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); |
Node* effect = NodeProperties::GetEffectInput(node); |
@@ -489,16 +490,10 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
// Not much we can do if deoptimization support is disabled. |
if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
- // Extract receiver maps from the LOAD_IC using the LoadICNexus. |
- MapHandleList receiver_maps; |
- if (!p.feedback().IsValid()) return NoChange(); |
- LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
- if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); |
- DCHECK_LT(0, receiver_maps.length()); |
- |
// Compute property access infos for the receiver maps. |
ZoneVector<PropertyAccessInfo> access_infos(zone()); |
- if (!ComputePropertyAccessInfos(receiver_maps, name, kLoad, &access_infos)) { |
+ if (!ComputePropertyAccessInfos(receiver_maps, name, access_mode, |
+ &access_infos)) { |
return NoChange(); |
} |
@@ -506,7 +501,7 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
if (access_infos.empty()) return NoChange(); |
// The final states for every polymorphic branch. We join them with |
- // Merge+Phi+EffectPhi at the bottom. |
+ // Merge++Phi+EffectPhi at the bottom. |
ZoneVector<Node*> values(zone()); |
ZoneVector<Node*> effects(zone()); |
ZoneVector<Node*> controls(zone()); |
@@ -532,7 +527,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
// Generate code for the various different property access patterns. |
Node* fallthrough_control = control; |
for (PropertyAccessInfo const& access_info : access_infos) { |
- Node* this_value = receiver; |
+ Node* this_value = value; |
+ Node* this_receiver = receiver; |
Node* this_effect = effect; |
Node* this_control; |
@@ -546,8 +542,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
Node* check = |
graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type, |
jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE)); |
- Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
- check, fallthrough_control); |
+ Node* branch = |
+ graph()->NewNode(common()->Branch(), check, fallthrough_control); |
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
this_control = graph()->NewNode(common()->IfTrue(), branch); |
} else { |
@@ -559,8 +555,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
Node* check = |
graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), |
receiver_map, jsgraph()->Constant(map)); |
- Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
- check, fallthrough_control); |
+ Node* branch = |
+ graph()->NewNode(common()->Branch(), check, fallthrough_control); |
this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
} |
@@ -575,44 +571,98 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
// Determine actual holder and perform prototype chain checks. |
Handle<JSObject> holder; |
if (access_info.holder().ToHandle(&holder)) { |
- this_value = jsgraph()->Constant(holder); |
AssumePrototypesStable(receiver_type, holder); |
+ if (access_mode == kLoad) { |
+ this_receiver = jsgraph()->Constant(holder); |
+ } |
} |
// Generate the actual property access. |
if (access_info.IsDataConstant()) { |
this_value = jsgraph()->Constant(access_info.constant()); |
+ if (access_mode == kStore) { |
+ Node* check = graph()->NewNode( |
+ simplified()->ReferenceEqual(Type::Tagged()), value, this_value); |
+ Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
+ check, this_control); |
+ exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
+ this_control = graph()->NewNode(common()->IfTrue(), branch); |
+ } |
} else { |
- // TODO(bmeurer): This is sort of adhoc, and must be refactored into some |
- // common code once we also have support for stores. |
DCHECK(access_info.IsDataField()); |
FieldIndex const field_index = access_info.field_index(); |
Type* const field_type = access_info.field_type(); |
if (!field_index.is_inobject()) { |
- this_value = this_effect = graph()->NewNode( |
+ this_receiver = this_effect = graph()->NewNode( |
simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), |
- this_value, this_effect, this_control); |
+ this_receiver, this_effect, this_control); |
} |
- FieldAccess field_access; |
- field_access.base_is_tagged = kTaggedBase; |
- field_access.offset = field_index.offset(); |
- field_access.name = name; |
- field_access.type = field_type; |
- field_access.machine_type = kMachAnyTagged; |
- if (field_type->Is(Type::UntaggedFloat64())) { |
- if (!field_index.is_inobject() || field_index.is_hidden_field() || |
- !FLAG_unbox_double_fields) { |
- this_value = this_effect = |
- graph()->NewNode(simplified()->LoadField(field_access), |
- this_value, this_effect, this_control); |
- field_access.offset = HeapNumber::kValueOffset; |
- field_access.name = MaybeHandle<Name>(); |
+ FieldAccess field_access = {kTaggedBase, field_index.offset(), name, |
+ field_type, kMachAnyTagged}; |
+ if (access_mode == kLoad) { |
+ if (field_type->Is(Type::UntaggedFloat64())) { |
+ if (!field_index.is_inobject() || field_index.is_hidden_field() || |
+ !FLAG_unbox_double_fields) { |
+ this_receiver = this_effect = |
+ graph()->NewNode(simplified()->LoadField(field_access), |
+ this_receiver, this_effect, this_control); |
+ field_access.offset = HeapNumber::kValueOffset; |
+ field_access.name = MaybeHandle<Name>(); |
+ } |
+ field_access.machine_type = kMachFloat64; |
+ } |
+ this_value = this_effect = |
+ graph()->NewNode(simplified()->LoadField(field_access), |
+ this_receiver, this_effect, this_control); |
+ } else { |
+ DCHECK_EQ(kStore, access_mode); |
+ if (field_type->Is(Type::TaggedSigned())) { |
+ Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); |
+ Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
+ check, this_control); |
+ exit_controls.push_back( |
+ graph()->NewNode(common()->IfFalse(), branch)); |
+ this_control = graph()->NewNode(common()->IfTrue(), branch); |
+ } else if (field_type->Is(Type::TaggedPointer())) { |
+ Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); |
+ Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
+ check, this_control); |
+ exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
+ this_control = graph()->NewNode(common()->IfFalse(), branch); |
+ if (field_type->NumClasses() > 0) { |
+ // Emit a (sequence of) map checks for the value. |
+ ZoneVector<Node*> this_controls(zone()); |
+ Node* value_map = this_effect = graph()->NewNode( |
+ simplified()->LoadField(AccessBuilder::ForMap()), value, |
+ this_effect, this_control); |
+ for (auto i = field_type->Classes(); !i.Done(); i.Advance()) { |
+ Handle<Map> field_map(i.Current()); |
+ check = graph()->NewNode( |
+ simplified()->ReferenceEqual(Type::Internal()), value_map, |
+ jsgraph()->Constant(field_map)); |
+ branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
+ check, this_control); |
+ this_control = graph()->NewNode(common()->IfFalse(), branch); |
+ this_controls.push_back( |
+ graph()->NewNode(common()->IfTrue(), branch)); |
+ } |
+ exit_controls.push_back(this_control); |
+ int const this_control_count = |
+ static_cast<int>(this_controls.size()); |
+ this_control = |
+ (this_control_count == 1) |
+ ? this_controls.front() |
+ : graph()->NewNode(common()->Merge(this_control_count), |
+ this_control_count, |
+ &this_controls.front()); |
+ } |
+ } else { |
+ DCHECK(field_type->Is(Type::Tagged())); |
} |
- field_access.machine_type = kMachFloat64; |
+ this_effect = graph()->NewNode(simplified()->StoreField(field_access), |
+ this_receiver, this_value, this_effect, |
+ this_control); |
} |
- this_value = this_effect = |
- graph()->NewNode(simplified()->LoadField(field_access), this_value, |
- this_effect, this_control); |
} |
// Remember the final state for this property access. |
@@ -622,6 +672,17 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
} |
// Collect the fallthru control as final "exit" control. |
+ if (fallthrough_control != control) { |
+ // Mark the last fallthru branch as deferred. |
+ Node* branch = NodeProperties::GetControlInput(fallthrough_control); |
+ DCHECK_EQ(IrOpcode::kBranch, branch->opcode()); |
+ if (fallthrough_control->opcode() == IrOpcode::kIfTrue) { |
+ NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kFalse)); |
+ } else { |
+ DCHECK_EQ(IrOpcode::kIfFalse, fallthrough_control->opcode()); |
+ NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kTrue)); |
+ } |
+ } |
exit_controls.push_back(fallthrough_control); |
// Generate the single "exit" point, where we get if either all map/instance |
@@ -641,7 +702,6 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); |
// Generate the final merge point for all (polymorphic) branches. |
- Node* value; |
int const control_count = static_cast<int>(controls.size()); |
if (control_count == 1) { |
value = values.front(); |
@@ -661,215 +721,37 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
} |
-Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { |
- DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); |
+Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
+ DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); |
NamedAccess const& p = NamedAccessOf(node->op()); |
- Handle<Name> name = p.name(); |
- Node* receiver = NodeProperties::GetValueInput(node, 0); |
- Node* value = NodeProperties::GetValueInput(node, 1); |
- Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); |
- Node* effect = NodeProperties::GetEffectInput(node); |
- Node* control = NodeProperties::GetControlInput(node); |
- |
- // Not much we can do if deoptimization support is disabled. |
- if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
+ Node* const value = jsgraph()->Dead(); |
- // Extract receiver maps from the STORE_IC using the StoreICNexus. |
+ // Extract receiver maps from the LOAD_IC using the LoadICNexus. |
MapHandleList receiver_maps; |
if (!p.feedback().IsValid()) return NoChange(); |
- StoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
+ LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); |
DCHECK_LT(0, receiver_maps.length()); |
- // Compute property access infos for the receiver maps. |
- ZoneVector<PropertyAccessInfo> access_infos(zone()); |
- if (!ComputePropertyAccessInfos(receiver_maps, name, kStore, &access_infos)) { |
- return NoChange(); |
- } |
- |
- // Nothing to do if we have no non-deprecated maps. |
- if (access_infos.empty()) return NoChange(); |
- |
- // The final states for every polymorphic branch. We join them with |
- // Merge+EffectPhi at the bottom. |
- ZoneVector<Node*> effects(zone()); |
- ZoneVector<Node*> controls(zone()); |
- |
- // The list of "exiting" controls, which currently go to a single deoptimize. |
- // TODO(bmeurer): Consider using an IC as fallback. |
- Node* const exit_effect = effect; |
- ZoneVector<Node*> exit_controls(zone()); |
- |
- // Ensure that {receiver} is a heap object. |
- Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); |
- Node* branch = |
- graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
- exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
- control = graph()->NewNode(common()->IfFalse(), branch); |
- |
- // Load the {receiver} map. The resulting effect is the dominating effect for |
- // all (polymorphic) branches. |
- Node* receiver_map = effect = |
- graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
- receiver, effect, control); |
- |
- // Generate code for the various different property access patterns. |
- Node* fallthrough_control = control; |
- for (PropertyAccessInfo const& access_info : access_infos) { |
- Node* this_receiver = receiver; |
- Node* this_effect = effect; |
- Node* this_control; |
- |
- // Perform map check on {receiver}. |
- Type* receiver_type = access_info.receiver_type(); |
- if (receiver_type->Is(Type::String())) { |
- // Emit an instance type check for strings. |
- Node* receiver_instance_type = this_effect = graph()->NewNode( |
- simplified()->LoadField(AccessBuilder::ForMapInstanceType()), |
- receiver_map, this_effect, fallthrough_control); |
- Node* check = |
- graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type, |
- jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE)); |
- Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
- check, fallthrough_control); |
- fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
- this_control = graph()->NewNode(common()->IfTrue(), branch); |
- } else { |
- // Emit a (sequence of) map checks for other properties. |
- ZoneVector<Node*> this_controls(zone()); |
- for (auto i = access_info.receiver_type()->Classes(); !i.Done(); |
- i.Advance()) { |
- Handle<Map> map = i.Current(); |
- Node* check = |
- graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), |
- receiver_map, jsgraph()->Constant(map)); |
- Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
- check, fallthrough_control); |
- this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
- fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
- } |
- int const this_control_count = static_cast<int>(this_controls.size()); |
- this_control = |
- (this_control_count == 1) |
- ? this_controls.front() |
- : graph()->NewNode(common()->Merge(this_control_count), |
- this_control_count, &this_controls.front()); |
- } |
- |
- // Determine actual holder and perform prototype chain checks. |
- Handle<JSObject> holder; |
- if (access_info.holder().ToHandle(&holder)) { |
- AssumePrototypesStable(receiver_type, holder); |
- } |
- |
- // Generate the actual property access. |
- if (access_info.IsDataConstant()) { |
- Node* check = |
- graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()), value, |
- jsgraph()->Constant(access_info.constant())); |
- Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
- check, this_control); |
- exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
- this_control = graph()->NewNode(common()->IfTrue(), branch); |
- } else { |
- // TODO(bmeurer): This is sort of adhoc, and must be refactored into some |
- // common code once we also have support for stores. |
- DCHECK(access_info.IsDataField()); |
- FieldIndex const field_index = access_info.field_index(); |
- Type* const field_type = access_info.field_type(); |
- if (!field_index.is_inobject()) { |
- this_receiver = this_effect = graph()->NewNode( |
- simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), |
- this_receiver, this_effect, this_control); |
- } |
- FieldAccess field_access; |
- field_access.base_is_tagged = kTaggedBase; |
- field_access.offset = field_index.offset(); |
- field_access.name = name; |
- field_access.type = field_type; |
- field_access.machine_type = kMachAnyTagged; |
- if (field_type->Is(Type::TaggedSigned())) { |
- Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); |
- Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
- check, this_control); |
- exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
- this_control = graph()->NewNode(common()->IfTrue(), branch); |
- } else if (field_type->Is(Type::TaggedPointer())) { |
- Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); |
- Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
- check, this_control); |
- exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
- this_control = graph()->NewNode(common()->IfFalse(), branch); |
- if (field_type->NumClasses() > 0) { |
- // Emit a (sequence of) map checks for the value. |
- ZoneVector<Node*> this_controls(zone()); |
- Node* value_map = this_effect = |
- graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
- value, this_effect, this_control); |
- for (auto i = field_type->Classes(); !i.Done(); i.Advance()) { |
- Handle<Map> field_map(i.Current()); |
- check = |
- graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), |
- value_map, jsgraph()->Constant(field_map)); |
- branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
- check, this_control); |
- this_control = graph()->NewNode(common()->IfFalse(), branch); |
- this_controls.push_back( |
- graph()->NewNode(common()->IfTrue(), branch)); |
- } |
- exit_controls.push_back(this_control); |
- int const this_control_count = static_cast<int>(this_controls.size()); |
- this_control = (this_control_count == 1) |
- ? this_controls.front() |
- : graph()->NewNode( |
- common()->Merge(this_control_count), |
- this_control_count, &this_controls.front()); |
- } |
- } else { |
- DCHECK(field_type->Is(Type::Tagged())); |
- } |
- this_effect = |
- graph()->NewNode(simplified()->StoreField(field_access), |
- this_receiver, value, this_effect, this_control); |
- } |
+ // Try to lower the named access based on the {receiver_maps}. |
+ return ReduceNamedAccess(node, value, receiver_maps, p.name(), kLoad); |
+} |
- // Remember the final state for this property access. |
- effects.push_back(this_effect); |
- controls.push_back(this_control); |
- } |
- // Collect the fallthru control as final "exit" control. |
- exit_controls.push_back(fallthrough_control); |
+Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { |
+ DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); |
+ NamedAccess const& p = NamedAccessOf(node->op()); |
+ Node* const value = NodeProperties::GetValueInput(node, 1); |
- // Generate the single "exit" point, where we get if either all map/instance |
- // type checks failed, or one of the assumptions inside one of the cases |
- // failes (i.e. failing prototype chain check). |
- // TODO(bmeurer): Consider falling back to IC here if deoptimization is |
- // disabled. |
- int const exit_control_count = static_cast<int>(exit_controls.size()); |
- Node* exit_control = |
- (exit_control_count == 1) |
- ? exit_controls.front() |
- : graph()->NewNode(common()->Merge(exit_control_count), |
- exit_control_count, &exit_controls.front()); |
- Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, |
- exit_effect, exit_control); |
- // TODO(bmeurer): This should be on the AdvancedReducer somehow. |
- NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); |
+ // Extract receiver maps from the STORE_IC using the StoreICNexus. |
+ MapHandleList receiver_maps; |
+ if (!p.feedback().IsValid()) return NoChange(); |
+ StoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
+ if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); |
+ DCHECK_LT(0, receiver_maps.length()); |
- // Generate the final merge point for all (polymorphic) branches. |
- int const control_count = static_cast<int>(controls.size()); |
- if (control_count == 1) { |
- effect = effects.front(); |
- control = controls.front(); |
- } else { |
- control = graph()->NewNode(common()->Merge(control_count), control_count, |
- &controls.front()); |
- effects.push_back(control); |
- effect = graph()->NewNode(common()->EffectPhi(control_count), |
- control_count + 1, &effects.front()); |
- } |
- return Replace(node, value, effect, control); |
+ // Try to lower the named access based on the {receiver_maps}. |
+ return ReduceNamedAccess(node, value, receiver_maps, p.name(), kStore); |
} |