Commit 608ed2e2 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add support for named access to Number primitives.

Implement the missing bits for named access to Number values, which is
basically always done on the Number prototype.  Crankshaft only deals
with Number primitives in the polymorphic case, while we generally
support Numbers even for monomorphic access.

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

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

Cr-Commit-Position: refs/heads/master@{#31731}
parent 538197da
...@@ -28,8 +28,7 @@ bool CanInlineElementAccess(Handle<Map> map) { ...@@ -28,8 +28,7 @@ bool CanInlineElementAccess(Handle<Map> map) {
bool CanInlinePropertyAccess(Handle<Map> map) { bool CanInlinePropertyAccess(Handle<Map> map) {
// TODO(bmeurer): Add support for Number primitives. if (map->instance_type() == HEAP_NUMBER_TYPE) return true;
// if (map->instance_type() == HEAP_NUMBER_TYPE) return false;
if (map->instance_type() < FIRST_NONSTRING_TYPE) return true; if (map->instance_type() < FIRST_NONSTRING_TYPE) return true;
return map->IsJSObjectMap() && !map->is_dictionary_map() && return map->IsJSObjectMap() && !map->is_dictionary_map() &&
!map->has_named_interceptor() && !map->has_named_interceptor() &&
......
...@@ -354,10 +354,10 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -354,10 +354,10 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// Ensure that {receiver} is a heap object. // Ensure that {receiver} is a heap object.
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
Node* branch = Node* branch = graph()->NewNode(common()->Branch(), check, control);
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
control = graph()->NewNode(common()->IfFalse(), branch); control = graph()->NewNode(common()->IfFalse(), branch);
Node* receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
Node* receiverissmi_effect = effect;
// Load the {receiver} map. The resulting effect is the dominating effect for // Load the {receiver} map. The resulting effect is the dominating effect for
// all (polymorphic) branches. // all (polymorphic) branches.
...@@ -388,8 +388,9 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -388,8 +388,9 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
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 {
// Emit a (sequence of) map checks for other properties. // Emit a (sequence of) map checks for other {receiver}s.
ZoneVector<Node*> this_controls(zone()); ZoneVector<Node*> this_controls(zone());
ZoneVector<Node*> this_effects(zone());
for (auto i = access_info.receiver_type()->Classes(); !i.Done(); for (auto i = access_info.receiver_type()->Classes(); !i.Done();
i.Advance()) { i.Advance()) {
Handle<Map> map = i.Current(); Handle<Map> map = i.Current();
...@@ -398,15 +399,37 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -398,15 +399,37 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
receiver_map, jsgraph()->Constant(map)); receiver_map, jsgraph()->Constant(map));
Node* branch = Node* branch =
graph()->NewNode(common()->Branch(), check, fallthrough_control); graph()->NewNode(common()->Branch(), check, fallthrough_control);
this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
this_effects.push_back(this_effect);
} }
// The Number case requires special treatment to also deal with Smis.
if (receiver_type->Is(Type::Number())) {
// Join this check with the "receiver is smi" check above, and mark the
// "receiver is smi" check as "consumed" so that we don't deoptimize if
// the {receiver} is actually a Smi.
if (receiverissmi_control != nullptr) {
this_controls.push_back(receiverissmi_control);
this_effects.push_back(receiverissmi_effect);
receiverissmi_control = receiverissmi_effect = nullptr;
}
}
// Create dominating Merge+EffectPhi for this {receiver} type.
int const this_control_count = static_cast<int>(this_controls.size()); int const this_control_count = static_cast<int>(this_controls.size());
this_control = this_control =
(this_control_count == 1) (this_control_count == 1)
? this_controls.front() ? this_controls.front()
: graph()->NewNode(common()->Merge(this_control_count), : graph()->NewNode(common()->Merge(this_control_count),
this_control_count, &this_controls.front()); this_control_count, &this_controls.front());
this_effects.push_back(this_control);
int const this_effect_count = static_cast<int>(this_effects.size());
this_effect =
(this_control_count == 1)
? this_effects.front()
: graph()->NewNode(common()->EffectPhi(this_control_count),
this_effect_count, &this_effects.front());
} }
// Determine actual holder and perform prototype chain checks. // Determine actual holder and perform prototype chain checks.
...@@ -584,17 +607,19 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -584,17 +607,19 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// Collect the fallthrough control as final "exit" control. // Collect the fallthrough control as final "exit" control.
if (fallthrough_control != control) { if (fallthrough_control != control) {
// Mark the last fallthrough branch as deferred. // Mark the last fallthrough branch as deferred.
Node* branch = NodeProperties::GetControlInput(fallthrough_control); MarkAsDeferred(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);
// Also collect the "receiver is smi" control if we didn't handle the case of
// Number primitives in the polymorphic branches above.
if (receiverissmi_control != nullptr) {
// Mark the "receiver is smi" case as deferred.
MarkAsDeferred(receiverissmi_control);
DCHECK_EQ(exit_effect, receiverissmi_effect);
exit_controls.push_back(receiverissmi_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
// type checks failed, or one of the assumptions inside one of the cases // type checks failed, or one of the assumptions inside one of the cases
// failes (i.e. failing prototype chain check). // failes (i.e. failing prototype chain check).
...@@ -876,14 +901,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -876,14 +901,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// Collect the fallthrough control as final "exit" control. // Collect the fallthrough control as final "exit" control.
if (fallthrough_control != control) { if (fallthrough_control != control) {
// Mark the last fallthrough branch as deferred. // Mark the last fallthrough branch as deferred.
Node* branch = NodeProperties::GetControlInput(fallthrough_control); MarkAsDeferred(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);
...@@ -1053,6 +1071,18 @@ void JSNativeContextSpecialization::AssumePrototypesStable( ...@@ -1053,6 +1071,18 @@ void JSNativeContextSpecialization::AssumePrototypesStable(
} }
void JSNativeContextSpecialization::MarkAsDeferred(Node* if_projection) {
Node* branch = NodeProperties::GetControlInput(if_projection);
DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
if (if_projection->opcode() == IrOpcode::kIfTrue) {
NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kFalse));
} else {
DCHECK_EQ(IrOpcode::kIfFalse, if_projection->opcode());
NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kTrue));
}
}
Graph* JSNativeContextSpecialization::graph() const { Graph* JSNativeContextSpecialization::graph() const {
return jsgraph()->graph(); return jsgraph()->graph();
} }
......
...@@ -87,6 +87,10 @@ class JSNativeContextSpecialization final : public AdvancedReducer { ...@@ -87,6 +87,10 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
// {receiver_type} up to (and including) the {holder}. // {receiver_type} up to (and including) the {holder}.
void AssumePrototypesStable(Type* receiver_type, Handle<JSObject> holder); void AssumePrototypesStable(Type* receiver_type, Handle<JSObject> holder);
// Assuming that {if_projection} is either IfTrue or IfFalse, adds a hint on
// the dominating Branch that {if_projection} is the unlikely (deferred) case.
void MarkAsDeferred(Node* if_projection);
Graph* graph() const; Graph* graph() const;
JSGraph* jsgraph() const { return jsgraph_; } JSGraph* jsgraph() const { return jsgraph_; }
Isolate* isolate() const; Isolate* isolate() const;
......
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