Commit 5716426b authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Finish support for holey double elements backing stores.

Also properly support loading from holey double element backing stores
in JSNativeContextSpecialization. This adds a new simplified operator
NumberIsHoleNaN, which checks whether a certain value is the special NaN
that we use to encode "the hole" in holey double element backing stores.

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

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

Cr-Commit-Position: refs/heads/master@{#32039}
parent 401efe21
...@@ -141,11 +141,6 @@ bool AccessInfoFactory::ComputeElementAccessInfo( ...@@ -141,11 +141,6 @@ bool AccessInfoFactory::ComputeElementAccessInfo(
if (!CanInlineElementAccess(map)) return false; if (!CanInlineElementAccess(map)) return false;
ElementsKind const elements_kind = map->elements_kind(); ElementsKind const elements_kind = map->elements_kind();
if (access_mode == AccessMode::kLoad &&
elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
// TODO(bmeurer): Add support for holey loads.
return false;
}
// Certain (monomorphic) stores need a prototype chain check because shape // Certain (monomorphic) stores need a prototype chain check because shape
// changes could allow callbacks on elements in the chain that are not // changes could allow callbacks on elements in the chain that are not
......
...@@ -711,7 +711,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -711,7 +711,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
Node* if_false = graph()->NewNode(common()->IfFalse(), branch); Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
// Check if we are allowed to turn the hole into undefined. // Check if we are allowed to turn the hole into undefined.
Type* initial_holey_array_type = Type::Class( Type* initial_holey_array_type = Type::Class(
handle(isolate()->get_initial_js_array_map(FAST_HOLEY_ELEMENTS)), handle(isolate()->get_initial_js_array_map(elements_kind)),
graph()->zone()); graph()->zone());
if (receiver_type->NowIs(initial_holey_array_type) && if (receiver_type->NowIs(initial_holey_array_type) &&
isolate()->IsFastArrayConstructorPrototypeChainIntact()) { isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
...@@ -736,6 +736,31 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -736,6 +736,31 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// hole). // hole).
this_value = graph()->NewNode(common()->Guard(element_type), this_value, this_value = graph()->NewNode(common()->Guard(element_type), this_value,
this_control); this_control);
} else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
// Perform the hole check on the result.
Node* check =
graph()->NewNode(simplified()->NumberIsHoleNaN(), this_value);
// Check if we are allowed to return the hole directly.
Type* initial_holey_array_type = Type::Class(
handle(isolate()->get_initial_js_array_map(elements_kind)),
graph()->zone());
if (receiver_type->NowIs(initial_holey_array_type) &&
isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
// Add a code dependency on the array protector cell.
AssumePrototypesStable(receiver_type,
isolate()->initial_object_prototype());
dependencies()->AssumePropertyCell(factory()->array_protector());
// Turn the hole into undefined.
this_value = graph()->NewNode(
common()->Select(kMachAnyTagged, BranchHint::kFalse), check,
jsgraph()->UndefinedConstant(), this_value);
} else {
// Deoptimize in case of the hole.
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check, this_control);
this_control = graph()->NewNode(common()->IfFalse(), branch);
exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
}
} }
} else { } else {
DCHECK_EQ(AccessMode::kStore, access_mode); DCHECK_EQ(AccessMode::kStore, access_mode);
......
...@@ -747,6 +747,21 @@ Reduction JSTypedLowering::ReduceJSToNumberInput(Node* input) { ...@@ -747,6 +747,21 @@ Reduction JSTypedLowering::ReduceJSToNumberInput(Node* input) {
if (result.Changed()) return result; if (result.Changed()) return result;
return Changed(input); // JSToNumber(JSToNumber(x)) => JSToNumber(x) return Changed(input); // JSToNumber(JSToNumber(x)) => JSToNumber(x)
} }
// Check for ToNumber truncation of signaling NaN to undefined mapping.
if (input->opcode() == IrOpcode::kSelect) {
Node* check = NodeProperties::GetValueInput(input, 0);
Node* vtrue = NodeProperties::GetValueInput(input, 1);
Type* vtrue_type = NodeProperties::GetType(vtrue);
Node* vfalse = NodeProperties::GetValueInput(input, 2);
Type* vfalse_type = NodeProperties::GetType(vfalse);
if (vtrue_type->Is(Type::Undefined()) && vfalse_type->Is(Type::Number())) {
if (check->opcode() == IrOpcode::kNumberIsHoleNaN &&
check->InputAt(0) == vfalse) {
// JSToNumber(Select(NumberIsHoleNaN(x), y:undefined, x:number)) => x
return Replace(vfalse);
}
}
}
// Check if we have a cached conversion. // Check if we have a cached conversion.
Type* input_type = NodeProperties::GetType(input); Type* input_type = NodeProperties::GetType(input);
if (input_type->Is(Type::Number())) { if (input_type->Is(Type::Number())) {
......
...@@ -183,6 +183,7 @@ ...@@ -183,6 +183,7 @@
V(NumberShiftRightLogical) \ V(NumberShiftRightLogical) \
V(NumberToInt32) \ V(NumberToInt32) \
V(NumberToUint32) \ V(NumberToUint32) \
V(NumberIsHoleNaN) \
V(PlainPrimitiveToNumber) \ V(PlainPrimitiveToNumber) \
V(ChangeTaggedToInt32) \ V(ChangeTaggedToInt32) \
V(ChangeTaggedToUint32) \ V(ChangeTaggedToUint32) \
......
...@@ -776,6 +776,21 @@ class RepresentationSelector { ...@@ -776,6 +776,21 @@ class RepresentationSelector {
} }
break; break;
} }
case IrOpcode::kNumberIsHoleNaN: {
VisitUnop(node, kMachFloat64, kMachBool);
if (lower()) {
// NumberIsHoleNaN(x) => Word32Equal(Float64ExtractLowWord32(x),
// #HoleNaNLower32)
node->ReplaceInput(0,
jsgraph_->graph()->NewNode(
lowering->machine()->Float64ExtractLowWord32(),
node->InputAt(0)));
node->AppendInput(jsgraph_->zone(),
jsgraph_->Int32Constant(kHoleNanLower32));
NodeProperties::ChangeOp(node, jsgraph_->machine()->Word32Equal());
}
break;
}
case IrOpcode::kPlainPrimitiveToNumber: { case IrOpcode::kPlainPrimitiveToNumber: {
VisitUnop(node, kMachAnyTagged, kTypeNumber | kRepTagged); VisitUnop(node, kMachAnyTagged, kTypeNumber | kRepTagged);
if (lower()) { if (lower()) {
......
...@@ -176,6 +176,7 @@ const ElementAccess& ElementAccessOf(const Operator* op) { ...@@ -176,6 +176,7 @@ const ElementAccess& ElementAccessOf(const Operator* op) {
V(NumberShiftRightLogical, Operator::kNoProperties, 2) \ V(NumberShiftRightLogical, Operator::kNoProperties, 2) \
V(NumberToInt32, Operator::kNoProperties, 1) \ V(NumberToInt32, Operator::kNoProperties, 1) \
V(NumberToUint32, Operator::kNoProperties, 1) \ V(NumberToUint32, Operator::kNoProperties, 1) \
V(NumberIsHoleNaN, Operator::kNoProperties, 1) \
V(PlainPrimitiveToNumber, Operator::kNoProperties, 1) \ V(PlainPrimitiveToNumber, Operator::kNoProperties, 1) \
V(ChangeTaggedToInt32, Operator::kNoProperties, 1) \ V(ChangeTaggedToInt32, Operator::kNoProperties, 1) \
V(ChangeTaggedToUint32, Operator::kNoProperties, 1) \ V(ChangeTaggedToUint32, Operator::kNoProperties, 1) \
......
...@@ -148,6 +148,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject { ...@@ -148,6 +148,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* NumberShiftRightLogical(); const Operator* NumberShiftRightLogical();
const Operator* NumberToInt32(); const Operator* NumberToInt32();
const Operator* NumberToUint32(); const Operator* NumberToUint32();
const Operator* NumberIsHoleNaN();
const Operator* PlainPrimitiveToNumber(); const Operator* PlainPrimitiveToNumber();
......
...@@ -1677,6 +1677,11 @@ Type* Typer::Visitor::TypeNumberToUint32(Node* node) { ...@@ -1677,6 +1677,11 @@ Type* Typer::Visitor::TypeNumberToUint32(Node* node) {
} }
Type* Typer::Visitor::TypeNumberIsHoleNaN(Node* node) {
return Type::Boolean(zone());
}
Type* Typer::Visitor::TypePlainPrimitiveToNumber(Node* node) { Type* Typer::Visitor::TypePlainPrimitiveToNumber(Node* node) {
return TypeUnaryOp(node, ToNumber); return TypeUnaryOp(node, ToNumber);
} }
......
...@@ -673,6 +673,11 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -673,6 +673,11 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 0, Type::Number()); CheckValueInputIs(node, 0, Type::Number());
CheckUpperIs(node, Type::Unsigned32()); CheckUpperIs(node, Type::Unsigned32());
break; break;
case IrOpcode::kNumberIsHoleNaN:
// Number -> Boolean
CheckValueInputIs(node, 0, Type::Number());
CheckUpperIs(node, Type::Boolean());
break;
case IrOpcode::kPlainPrimitiveToNumber: case IrOpcode::kPlainPrimitiveToNumber:
// PlainPrimitive -> Number // PlainPrimitive -> Number
CheckValueInputIs(node, 0, Type::PlainPrimitive()); CheckValueInputIs(node, 0, Type::PlainPrimitive());
......
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