Commit 22b9ec0b authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Improve deferred code handling for polymorphic property access.

Only mark the last fallthrough control as deferred, otherwise the
splintering will ruin the code generation for the (maybe likely)
polymorphic cases.

Drive-by-fix: Reduce overall code duplication between JSLoadNamed
and JSStoreNamed specialization.

R=jarin@chromium.org
BUG=v8:4470
LOG=n

Review URL: https://codereview.chromium.org/1424733002

Cr-Commit-Position: refs/heads/master@{#31623}
parent 8da26dd6
...@@ -477,10 +477,11 @@ bool JSNativeContextSpecialization::ComputePropertyAccessInfos( ...@@ -477,10 +477,11 @@ bool JSNativeContextSpecialization::ComputePropertyAccessInfos(
} }
Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { Reduction JSNativeContextSpecialization::ReduceNamedAccess(
DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); Node* node, Node* value, MapHandleList const& receiver_maps,
NamedAccess const& p = NamedAccessOf(node->op()); Handle<Name> name, PropertyAccessMode access_mode) {
Handle<Name> name = p.name(); DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
node->opcode() == IrOpcode::kJSStoreNamed);
Node* receiver = NodeProperties::GetValueInput(node, 0); Node* receiver = NodeProperties::GetValueInput(node, 0);
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
...@@ -489,16 +490,10 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { ...@@ -489,16 +490,10 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
// Not much we can do if deoptimization support is disabled. // Not much we can do if deoptimization support is disabled.
if (!(flags() & kDeoptimizationEnabled)) return NoChange(); 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. // Compute property access infos for the receiver maps.
ZoneVector<PropertyAccessInfo> access_infos(zone()); ZoneVector<PropertyAccessInfo> access_infos(zone());
if (!ComputePropertyAccessInfos(receiver_maps, name, kLoad, &access_infos)) { if (!ComputePropertyAccessInfos(receiver_maps, name, access_mode,
&access_infos)) {
return NoChange(); return NoChange();
} }
...@@ -506,7 +501,7 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { ...@@ -506,7 +501,7 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
if (access_infos.empty()) return NoChange(); if (access_infos.empty()) return NoChange();
// The final states for every polymorphic branch. We join them with // 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*> values(zone());
ZoneVector<Node*> effects(zone()); ZoneVector<Node*> effects(zone());
ZoneVector<Node*> controls(zone()); ZoneVector<Node*> controls(zone());
...@@ -532,7 +527,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { ...@@ -532,7 +527,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
// Generate code for the various different property access patterns. // Generate code for the various different property access patterns.
Node* fallthrough_control = control; Node* fallthrough_control = control;
for (PropertyAccessInfo const& access_info : access_infos) { 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_effect = effect;
Node* this_control; Node* this_control;
...@@ -546,8 +542,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { ...@@ -546,8 +542,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
Node* check = Node* check =
graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type, graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type,
jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE)); jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE));
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), Node* branch =
check, fallthrough_control); graph()->NewNode(common()->Branch(), check, fallthrough_control);
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
this_control = graph()->NewNode(common()->IfTrue(), branch); this_control = graph()->NewNode(common()->IfTrue(), branch);
} else { } else {
...@@ -559,8 +555,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { ...@@ -559,8 +555,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
Node* check = Node* check =
graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()),
receiver_map, jsgraph()->Constant(map)); receiver_map, jsgraph()->Constant(map));
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), Node* branch =
check, fallthrough_control); graph()->NewNode(common()->Branch(), check, fallthrough_control);
this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
} }
...@@ -575,44 +571,98 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { ...@@ -575,44 +571,98 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
// Determine actual holder and perform prototype chain checks. // Determine actual holder and perform prototype chain checks.
Handle<JSObject> holder; Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) { if (access_info.holder().ToHandle(&holder)) {
this_value = jsgraph()->Constant(holder);
AssumePrototypesStable(receiver_type, holder); AssumePrototypesStable(receiver_type, holder);
if (access_mode == kLoad) {
this_receiver = jsgraph()->Constant(holder);
}
} }
// Generate the actual property access. // Generate the actual property access.
if (access_info.IsDataConstant()) { if (access_info.IsDataConstant()) {
this_value = jsgraph()->Constant(access_info.constant()); 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 { } 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()); DCHECK(access_info.IsDataField());
FieldIndex const field_index = access_info.field_index(); FieldIndex const field_index = access_info.field_index();
Type* const field_type = access_info.field_type(); Type* const field_type = access_info.field_type();
if (!field_index.is_inobject()) { if (!field_index.is_inobject()) {
this_value = this_effect = graph()->NewNode( this_receiver = this_effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), simplified()->LoadField(AccessBuilder::ForJSObjectProperties()),
this_value, this_effect, this_control); this_receiver, this_effect, this_control);
} }
FieldAccess field_access; FieldAccess field_access = {kTaggedBase, field_index.offset(), name,
field_access.base_is_tagged = kTaggedBase; field_type, kMachAnyTagged};
field_access.offset = field_index.offset(); if (access_mode == kLoad) {
field_access.name = name; if (field_type->Is(Type::UntaggedFloat64())) {
field_access.type = field_type; if (!field_index.is_inobject() || field_index.is_hidden_field() ||
field_access.machine_type = kMachAnyTagged; !FLAG_unbox_double_fields) {
if (field_type->Is(Type::UntaggedFloat64())) { this_receiver = this_effect =
if (!field_index.is_inobject() || field_index.is_hidden_field() || graph()->NewNode(simplified()->LoadField(field_access),
!FLAG_unbox_double_fields) { this_receiver, this_effect, this_control);
this_value = this_effect = field_access.offset = HeapNumber::kValueOffset;
graph()->NewNode(simplified()->LoadField(field_access), field_access.name = MaybeHandle<Name>();
this_value, this_effect, this_control); }
field_access.offset = HeapNumber::kValueOffset; field_access.machine_type = kMachFloat64;
field_access.name = MaybeHandle<Name>(); }
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. // Remember the final state for this property access.
...@@ -622,6 +672,17 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { ...@@ -622,6 +672,17 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
} }
// Collect the fallthru control as final "exit" control. // 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); exit_controls.push_back(fallthrough_control);
// Generate the single "exit" point, where we get if either all map/instance // Generate the single "exit" point, where we get if either all map/instance
...@@ -641,7 +702,6 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { ...@@ -641,7 +702,6 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
// Generate the final merge point for all (polymorphic) branches. // Generate the final merge point for all (polymorphic) branches.
Node* value;
int const control_count = static_cast<int>(controls.size()); int const control_count = static_cast<int>(controls.size());
if (control_count == 1) { if (control_count == 1) {
value = values.front(); value = values.front();
...@@ -661,215 +721,37 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { ...@@ -661,215 +721,37 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
} }
Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
NamedAccess const& p = NamedAccessOf(node->op()); NamedAccess const& p = NamedAccessOf(node->op());
Handle<Name> name = p.name(); Node* const value = jsgraph()->Dead();
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();
// Extract receiver maps from the STORE_IC using the StoreICNexus. // Extract receiver maps from the LOAD_IC using the LoadICNexus.
MapHandleList receiver_maps; MapHandleList receiver_maps;
if (!p.feedback().IsValid()) return NoChange(); 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(); if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange();
DCHECK_LT(0, receiver_maps.length()); DCHECK_LT(0, receiver_maps.length());
// Compute property access infos for the receiver maps. // Try to lower the named access based on the {receiver_maps}.
ZoneVector<PropertyAccessInfo> access_infos(zone()); return ReduceNamedAccess(node, value, receiver_maps, p.name(), kLoad);
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);
}
// 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. Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) {
exit_controls.push_back(fallthrough_control); 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 // Extract receiver maps from the STORE_IC using the StoreICNexus.
// type checks failed, or one of the assumptions inside one of the cases MapHandleList receiver_maps;
// failes (i.e. failing prototype chain check). if (!p.feedback().IsValid()) return NoChange();
// TODO(bmeurer): Consider falling back to IC here if deoptimization is StoreICNexus nexus(p.feedback().vector(), p.feedback().slot());
// disabled. if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange();
int const exit_control_count = static_cast<int>(exit_controls.size()); DCHECK_LT(0, receiver_maps.length());
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);
// Generate the final merge point for all (polymorphic) branches. // Try to lower the named access based on the {receiver_maps}.
int const control_count = static_cast<int>(controls.size()); return ReduceNamedAccess(node, value, receiver_maps, p.name(), kStore);
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);
} }
......
...@@ -68,6 +68,11 @@ class JSNativeContextSpecialization final : public AdvancedReducer { ...@@ -68,6 +68,11 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
PropertyAccessMode access_mode, PropertyAccessMode access_mode,
ZoneVector<PropertyAccessInfo>* access_infos); ZoneVector<PropertyAccessInfo>* access_infos);
Reduction ReduceNamedAccess(Node* node, Node* value,
MapHandleList const& receiver_maps,
Handle<Name> name,
PropertyAccessMode access_mode);
struct ScriptContextTableLookupResult; struct ScriptContextTableLookupResult;
bool LookupInScriptContextTable(Handle<Name> name, bool LookupInScriptContextTable(Handle<Name> name,
ScriptContextTableLookupResult* result); ScriptContextTableLookupResult* result);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment