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

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

Issue 1424733002: [turbofan] Improve deferred code handling for polymorphic property access. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Undo unrelated change. Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/compiler/js-native-context-specialization.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/compiler/js-native-context-specialization.cc
diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc
index 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);
}
« no previous file with comments | « src/compiler/js-native-context-specialization.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698