Commit 502dd40c authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Introduce CheckHole and CheckHoleNaN operators.

These simplified operators are used to perform the hole checks when
loading elements from a holey array. Depending on the CheckHoleMode,
they either return the hole as undefined or some NaN, or deoptimize
if the value is the hole or the hole NaN.

R=jarin@chromium.org

Review-Url: https://codereview.chromium.org/2066223002
Cr-Commit-Position: refs/heads/master@{#37001}
parent fd4d385b
......@@ -455,6 +455,12 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckIf:
state = LowerCheckIf(node, frame_state, *effect, *control);
break;
case IrOpcode::kCheckFloat64Hole:
state = LowerCheckFloat64Hole(node, frame_state, *effect, *control);
break;
case IrOpcode::kCheckTaggedHole:
state = LowerCheckTaggedHole(node, frame_state, *effect, *control);
break;
case IrOpcode::kPlainPrimitiveToNumber:
state = LowerPlainPrimitiveToNumber(node, *effect, *control);
break;
......@@ -1296,6 +1302,51 @@ EffectControlLinearizer::LowerCheckIf(Node* node, Node* frame_state,
return ValueEffectControl(node, node, node);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckFloat64Hole(Node* node, Node* frame_state,
Node* effect, Node* control) {
// If we reach this point w/o eliminating the {node} that's marked
// with allow-return-hole, we cannot do anything, so just deoptimize
// in case of the hole NaN (similar to Crankshaft).
Node* value = node->InputAt(0);
Node* check = graph()->NewNode(
machine()->Word32Equal(),
graph()->NewNode(machine()->Float64ExtractHighWord32(), value),
jsgraph()->Int32Constant(kHoleNanUpper32));
control = effect = graph()->NewNode(common()->DeoptimizeIf(), check,
frame_state, effect, control);
// Make sure the lowered node does not appear in any use lists.
node->TrimInputCount(0);
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckTaggedHole(Node* node, Node* frame_state,
Node* effect, Node* control) {
CheckTaggedHoleMode mode = CheckTaggedHoleModeOf(node->op());
Node* value = node->InputAt(0);
Node* check = graph()->NewNode(machine()->WordEqual(), value,
jsgraph()->TheHoleConstant());
switch (mode) {
case CheckTaggedHoleMode::kConvertHoleToUndefined:
value = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
check, jsgraph()->UndefinedConstant(), value);
break;
case CheckTaggedHoleMode::kNeverReturnHole:
control = effect = graph()->NewNode(common()->DeoptimizeIf(), check,
frame_state, effect, control);
break;
}
// Make sure the lowered node does not appear in any use lists.
node->TrimInputCount(0);
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::AllocateHeapNumberWithValue(Node* value, Node* effect,
Node* control) {
......
......@@ -89,6 +89,10 @@ class EffectControlLinearizer {
Node* control);
ValueEffectControl LowerStringFromCharCode(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerCheckFloat64Hole(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerCheckTaggedHole(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerCheckIf(Node* node, Node* frame_state, Node* effect,
Node* control);
ValueEffectControl LowerPlainPrimitiveToNumber(Node* node, Node* effect,
......
......@@ -652,6 +652,14 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
graph()->NewNode(common()->EffectPhi(this_control_count),
this_control_count + 1, &this_effects.front());
}
// TODO(turbofan): The effect/control linearization will not find a
// FrameState after the StoreField or Call that is generated for the
// elements kind transition above. This is because those operators
// don't have the kNoWrite flag on it, even though they are not
// observable by JavaScript.
this_effect = graph()->NewNode(common()->Checkpoint(), frame_state,
this_effect, this_control);
}
// Certain stores need a prototype chain check because shape changes
......@@ -762,42 +770,26 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
if (elements_kind == FAST_HOLEY_ELEMENTS ||
elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
// Perform the hole check on the result.
Node* check =
graph()->NewNode(simplified()->ReferenceEqual(element_access.type),
this_value, jsgraph()->TheHoleConstant());
CheckTaggedHoleMode mode = CheckTaggedHoleMode::kNeverReturnHole;
// Check if we are allowed to turn the hole into undefined.
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()) {
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check, this_control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
// Add a code dependency on the array protector cell.
AssumePrototypesStable(receiver_type, native_context,
isolate()->initial_object_prototype());
dependencies()->AssumePropertyCell(factory()->array_protector());
// Turn the hole into undefined.
this_control =
graph()->NewNode(common()->Merge(2), if_true, if_false);
this_value = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2),
jsgraph()->UndefinedConstant(), this_value, this_control);
element_type =
Type::Union(element_type, Type::Undefined(), graph()->zone());
} else {
// Deoptimize in case of the hole.
this_control = this_effect =
graph()->NewNode(common()->DeoptimizeIf(), check, frame_state,
this_effect, this_control);
mode = CheckTaggedHoleMode::kConvertHoleToUndefined;
}
// Rename the result to represent the actual type (not polluted by the
// hole).
this_value = graph()->NewNode(simplified()->TypeGuard(element_type),
this_value, this_control);
this_value = this_effect =
graph()->NewNode(simplified()->CheckTaggedHole(mode), this_value,
this_effect, this_control);
} else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
// Perform the hole check on the result.
CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole;
// 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)),
......@@ -808,18 +800,12 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
AssumePrototypesStable(receiver_type, native_context,
isolate()->initial_object_prototype());
dependencies()->AssumePropertyCell(factory()->array_protector());
// Turn the hole into undefined.
this_value = graph()->NewNode(simplified()->NumberConvertHoleNaN(),
this_value);
} else {
// Perform the hole check on the result.
Node* check =
graph()->NewNode(simplified()->NumberIsHoleNaN(), this_value);
// Deoptimize in case of the hole.
this_control = this_effect =
graph()->NewNode(common()->DeoptimizeIf(), check, frame_state,
this_effect, this_control);
// Return the signaling NaN hole directly if all uses are truncating.
mode = CheckFloat64HoleMode::kAllowReturnHole;
}
this_value = this_effect =
graph()->NewNode(simplified()->CheckFloat64Hole(mode), this_value,
this_effect, this_control);
}
} else {
DCHECK_EQ(AccessMode::kStore, access_mode);
......
......@@ -207,9 +207,7 @@
V(NumberTrunc) \
V(NumberToInt32) \
V(NumberToUint32) \
V(NumberIsHoleNaN) \
V(NumberSilenceNaN) \
V(NumberConvertHoleNaN) \
V(StringFromCharCode) \
V(StringToNumber) \
V(ChangeTaggedSignedToInt32) \
......@@ -226,6 +224,8 @@
V(CheckedFloat64ToInt32) \
V(CheckedTaggedToInt32) \
V(CheckedTaggedToFloat64) \
V(CheckFloat64Hole) \
V(CheckTaggedHole) \
V(CheckIf) \
V(TruncateTaggedToWord32) \
V(TruncateTaggedToFloat64) \
......
......@@ -1446,57 +1446,6 @@ class RepresentationSelector {
if (lower()) DeferReplacement(node, node->InputAt(0));
return;
}
case IrOpcode::kNumberIsHoleNaN: {
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kBit);
if (lower()) {
// NumberIsHoleNaN(x) => Word32Equal(Float64ExtractHighWord32(x),
// #HoleNanUpper32)
node->ReplaceInput(
0, jsgraph_->graph()->NewNode(
lowering->machine()->Float64ExtractHighWord32(),
node->InputAt(0)));
node->AppendInput(jsgraph_->zone(),
jsgraph_->Int32Constant(kHoleNanUpper32));
NodeProperties::ChangeOp(node, jsgraph_->machine()->Word32Equal());
}
return;
}
case IrOpcode::kNumberConvertHoleNaN: {
if (truncation.TruncatesToFloat64()) {
// NumberConvertHoleNaN(x) => x
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower()) DeferReplacement(node, node->InputAt(0));
} else {
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kTagged);
if (lower()) {
// NumberConvertHoleNaN(x) =>
// Select(Word32Equal(Float64ExtractHighWord32(x),
// #HoleNanUpper32),
// #Undefined,
// ChangeFloat64ToTagged(x))
Node* value = node->InputAt(0);
node->ReplaceInput(
0,
jsgraph_->graph()->NewNode(
jsgraph_->machine()->Word32Equal(),
jsgraph_->graph()->NewNode(
jsgraph_->machine()->Float64ExtractHighWord32(), value),
jsgraph_->Int32Constant(kHoleNanUpper32)));
node->AppendInput(jsgraph_->zone(), jsgraph_->UndefinedConstant());
node->AppendInput(
jsgraph_->zone(),
jsgraph_->graph()->NewNode(
jsgraph_->simplified()->ChangeFloat64ToTagged(), value));
NodeProperties::ChangeOp(
node, jsgraph_->common()->Select(MachineRepresentation::kTagged,
BranchHint::kFalse));
}
}
return;
}
case IrOpcode::kReferenceEqual: {
VisitBinop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
if (lower()) {
......@@ -1734,6 +1683,23 @@ class RepresentationSelector {
SetOutput(node, MachineRepresentation::kBit);
return;
}
case IrOpcode::kCheckFloat64Hole: {
CheckFloat64HoleMode mode = CheckFloat64HoleModeOf(node->op());
ProcessInput(node, 0, UseInfo::TruncatingFloat64());
ProcessRemainingInputs(node, 1);
SetOutput(node, MachineRepresentation::kFloat64);
if (truncation.TruncatesToFloat64() &&
mode == CheckFloat64HoleMode::kAllowReturnHole) {
if (lower()) DeferReplacement(node, node->InputAt(0));
}
return;
}
case IrOpcode::kCheckTaggedHole: {
ProcessInput(node, 0, UseInfo::AnyTagged());
ProcessRemainingInputs(node, 1);
SetOutput(node, MachineRepresentation::kTagged);
return;
}
//------------------------------------------------------------------
// Machine-level operators.
......
......@@ -172,6 +172,46 @@ const ElementAccess& ElementAccessOf(const Operator* op) {
return OpParameter<ElementAccess>(op);
}
size_t hash_value(CheckFloat64HoleMode mode) {
return static_cast<size_t>(mode);
}
std::ostream& operator<<(std::ostream& os, CheckFloat64HoleMode mode) {
switch (mode) {
case CheckFloat64HoleMode::kAllowReturnHole:
return os << "allow-return-hole";
case CheckFloat64HoleMode::kNeverReturnHole:
return os << "never-return-hole";
}
UNREACHABLE();
return os;
}
CheckFloat64HoleMode CheckFloat64HoleModeOf(const Operator* op) {
DCHECK_EQ(IrOpcode::kCheckFloat64Hole, op->opcode());
return OpParameter<CheckFloat64HoleMode>(op);
}
size_t hash_value(CheckTaggedHoleMode mode) {
return static_cast<size_t>(mode);
}
std::ostream& operator<<(std::ostream& os, CheckTaggedHoleMode mode) {
switch (mode) {
case CheckTaggedHoleMode::kConvertHoleToUndefined:
return os << "convert-hole-to-undefined";
case CheckTaggedHoleMode::kNeverReturnHole:
return os << "never-return-hole";
}
UNREACHABLE();
return os;
}
CheckTaggedHoleMode CheckTaggedHoleModeOf(const Operator* op) {
DCHECK_EQ(IrOpcode::kCheckTaggedHole, op->opcode());
return OpParameter<CheckTaggedHoleMode>(op);
}
Type* TypeOf(const Operator* op) {
DCHECK_EQ(IrOpcode::kTypeGuard, op->opcode());
return OpParameter<Type*>(op);
......@@ -214,9 +254,7 @@ BinaryOperationHints::Hint BinaryOperationHintOf(const Operator* op) {
V(NumberTrunc, Operator::kNoProperties, 1) \
V(NumberToInt32, Operator::kNoProperties, 1) \
V(NumberToUint32, Operator::kNoProperties, 1) \
V(NumberIsHoleNaN, Operator::kNoProperties, 1) \
V(NumberSilenceNaN, Operator::kNoProperties, 1) \
V(NumberConvertHoleNaN, Operator::kNoProperties, 1) \
V(StringFromCharCode, Operator::kNoProperties, 1) \
V(StringToNumber, Operator::kNoProperties, 1) \
V(PlainPrimitiveToNumber, Operator::kNoProperties, 1) \
......@@ -271,6 +309,31 @@ struct SimplifiedOperatorGlobalCache final {
CHECKED_OP_LIST(CHECKED)
#undef CHECKED
template <CheckFloat64HoleMode kMode>
struct CheckFloat64HoleNaNOperatortor final
: public Operator1<CheckFloat64HoleMode> {
CheckFloat64HoleNaNOperatortor()
: Operator1<CheckFloat64HoleMode>(
IrOpcode::kCheckFloat64Hole, Operator::kFoldable,
"CheckFloat64Hole", 1, 1, 1, 1, 1, 0, kMode) {}
};
CheckFloat64HoleNaNOperatortor<CheckFloat64HoleMode::kAllowReturnHole>
kCheckFloat64HoleAllowReturnHoleOperator;
CheckFloat64HoleNaNOperatortor<CheckFloat64HoleMode::kNeverReturnHole>
kCheckFloat64HoleNeverReturnHoleOperator;
template <CheckTaggedHoleMode kMode>
struct CheckTaggedHoleOperator final : public Operator1<CheckTaggedHoleMode> {
CheckTaggedHoleOperator()
: Operator1<CheckTaggedHoleMode>(IrOpcode::kCheckTaggedHole,
Operator::kFoldable, "CheckTaggedHole",
1, 1, 1, 1, 1, 0, kMode) {}
};
CheckTaggedHoleOperator<CheckTaggedHoleMode::kConvertHoleToUndefined>
kCheckTaggedHoleConvertHoleToUndefinedOperator;
CheckTaggedHoleOperator<CheckTaggedHoleMode::kNeverReturnHole>
kCheckTaggedHoleNeverReturnHoleOperator;
struct CheckIfOperator final : public Operator {
CheckIfOperator()
: Operator(IrOpcode::kCheckIf, Operator::kFoldable, "CheckIf", 1, 1, 1,
......@@ -326,6 +389,30 @@ PURE_OP_LIST(GET_FROM_CACHE)
CHECKED_OP_LIST(GET_FROM_CACHE)
#undef GET_FROM_CACHE
const Operator* SimplifiedOperatorBuilder::CheckFloat64Hole(
CheckFloat64HoleMode mode) {
switch (mode) {
case CheckFloat64HoleMode::kAllowReturnHole:
return &cache_.kCheckFloat64HoleAllowReturnHoleOperator;
case CheckFloat64HoleMode::kNeverReturnHole:
return &cache_.kCheckFloat64HoleNeverReturnHoleOperator;
}
UNREACHABLE();
return nullptr;
}
const Operator* SimplifiedOperatorBuilder::CheckTaggedHole(
CheckTaggedHoleMode mode) {
switch (mode) {
case CheckTaggedHoleMode::kConvertHoleToUndefined:
return &cache_.kCheckTaggedHoleConvertHoleToUndefinedOperator;
case CheckTaggedHoleMode::kNeverReturnHole:
return &cache_.kCheckTaggedHoleNeverReturnHoleOperator;
}
UNREACHABLE();
return nullptr;
}
const Operator* SimplifiedOperatorBuilder::CheckIf() {
return &cache_.kCheckIf;
}
......
......@@ -103,6 +103,28 @@ std::ostream& operator<<(std::ostream&, ElementAccess const&);
ElementAccess const& ElementAccessOf(const Operator* op) WARN_UNUSED_RESULT;
enum class CheckFloat64HoleMode : uint8_t {
kNeverReturnHole, // Never return the hole (deoptimize instead).
kAllowReturnHole // Allow to return the hole (signaling NaN).
};
size_t hash_value(CheckFloat64HoleMode);
std::ostream& operator<<(std::ostream&, CheckFloat64HoleMode);
CheckFloat64HoleMode CheckFloat64HoleModeOf(const Operator*) WARN_UNUSED_RESULT;
enum class CheckTaggedHoleMode : uint8_t {
kNeverReturnHole, // Never return the hole (deoptimize instead).
kConvertHoleToUndefined // Convert the hole to undefined.
};
size_t hash_value(CheckTaggedHoleMode);
std::ostream& operator<<(std::ostream&, CheckTaggedHoleMode);
CheckTaggedHoleMode CheckTaggedHoleModeOf(const Operator*) WARN_UNUSED_RESULT;
Type* TypeOf(const Operator* op) WARN_UNUSED_RESULT;
BinaryOperationHints::Hint BinaryOperationHintOf(const Operator* op);
......@@ -164,8 +186,6 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* NumberTrunc();
const Operator* NumberToInt32();
const Operator* NumberToUint32();
const Operator* NumberIsHoleNaN();
const Operator* NumberConvertHoleNaN();
const Operator* NumberSilenceNaN();
......@@ -202,6 +222,8 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* CheckedTaggedToInt32();
const Operator* CheckedTaggedToFloat64();
const Operator* CheckFloat64Hole(CheckFloat64HoleMode);
const Operator* CheckTaggedHole(CheckTaggedHoleMode);
const Operator* CheckIf();
const Operator* ObjectIsCallable();
......
......@@ -248,7 +248,6 @@ class Typer::Visitor : public Reducer {
static Type* NumberTrunc(Type*, Typer*);
static Type* NumberToInt32(Type*, Typer*);
static Type* NumberToUint32(Type*, Typer*);
static Type* NumberConvertHoleNaN(Type*, Typer*);
static Type* ObjectIsCallable(Type*, Typer*);
static Type* ObjectIsNumber(Type*, Typer*);
......@@ -557,10 +556,6 @@ Type* Typer::Visitor::NumberToUint32(Type* type, Typer* t) {
return Type::Unsigned32();
}
Type* Typer::Visitor::NumberConvertHoleNaN(Type* type, Typer* t) {
return Type::Union(type, Type::Undefined(), t->zone());
}
// Type checks.
Type* Typer::Visitor::ObjectIsCallable(Type* type, Typer* t) {
......@@ -1818,14 +1813,6 @@ Type* Typer::Visitor::TypeNumberToUint32(Node* node) {
}
Type* Typer::Visitor::TypeNumberIsHoleNaN(Node* node) {
return Type::Boolean();
}
Type* Typer::Visitor::TypeNumberConvertHoleNaN(Node* node) {
return TypeUnaryOp(node, NumberConvertHoleNaN);
}
// static
Type* Typer::Visitor::ReferenceEqualTyper(Type* lhs, Type* rhs, Typer* t) {
if (lhs->IsConstant() && rhs->Is(lhs)) {
......@@ -1974,6 +1961,29 @@ Type* Typer::Visitor::TypeCheckedTaggedToFloat64(Node* node) {
return Type::Number();
}
Type* Typer::Visitor::TypeCheckFloat64Hole(Node* node) {
Type* type = Operand(node, 0);
return type;
}
Type* Typer::Visitor::TypeCheckTaggedHole(Node* node) {
CheckTaggedHoleMode mode = CheckTaggedHoleModeOf(node->op());
Type* type = Operand(node, 0);
type = Type::Intersect(type, Type::NonInternal(), zone());
switch (mode) {
case CheckTaggedHoleMode::kConvertHoleToUndefined: {
// The hole is turned into undefined.
type = Type::Union(type, Type::Undefined(), zone());
break;
}
case CheckTaggedHoleMode::kNeverReturnHole: {
// We deoptimize in case of the hole.
break;
}
}
return type;
}
Type* Typer::Visitor::TypeCheckIf(Node* node) {
UNREACHABLE();
return nullptr;
......
......@@ -766,16 +766,6 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 0, Type::Number());
CheckUpperIs(node, Type::Unsigned32());
break;
case IrOpcode::kNumberIsHoleNaN:
// Number -> Boolean
CheckValueInputIs(node, 0, Type::Number());
CheckUpperIs(node, Type::Boolean());
break;
case IrOpcode::kNumberConvertHoleNaN:
// Number -> Number \/ Undefined
CheckValueInputIs(node, 0, Type::Number());
CheckUpperIs(node, Type::NumberOrUndefined());
break;
case IrOpcode::kPlainPrimitiveToNumber:
// Type is Number.
CheckUpperIs(node, Type::Number());
......@@ -940,6 +930,15 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kCheckedTaggedToFloat64:
break;
case IrOpcode::kCheckFloat64Hole:
CheckValueInputIs(node, 0, Type::Number());
CheckUpperIs(node, Type::Number());
break;
case IrOpcode::kCheckTaggedHole:
CheckValueInputIs(node, 0, Type::Any());
CheckUpperIs(node, Type::Any());
break;
case IrOpcode::kLoadField:
// Object -> fieldtype
// TODO(rossberg): activate once machine ops are typed.
......
......@@ -231,6 +231,7 @@ namespace internal {
V(StringOrReceiver, kString | kReceiver) \
V(Unique, kBoolean | kUniqueName | kNull | kUndefined | \
kReceiver) \
V(NonInternal, kPrimitive | kReceiver) \
V(NonNumber, kUnique | kString | kInternal) \
V(Any, 0xfffffffeu)
......
......@@ -841,6 +841,8 @@
'harmony/generators-turbo': [PASS, FAST_VARIANTS],
'regress/regress-crbug-352058': [SKIP],
# TODO(jarin): No truncations on CheckFloat64Hole.
'getters-on-elements': [SKIP],
}], # ignition or ignition_turbofan
['(ignition or ignition_turbofan) and arch == arm64', {
......
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