Commit e61bd68e authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Introduce a dedicated ConvertTaggedHoleToUndefined operator.

Separate ConvertTaggedHoleToUndefined and CheckTaggedHole into two
separate operators, where the former is pure and just turns into
trivial control flow in the EffectControlLinearizer.

R=jarin@chromium.org

Review-Url: https://codereview.chromium.org/2236443004
Cr-Commit-Position: refs/heads/master@{#38559}
parent 73b0f157
......@@ -714,6 +714,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckTaggedHole:
state = LowerCheckTaggedHole(node, frame_state, *effect, *control);
break;
case IrOpcode::kConvertTaggedHoleToUndefined:
state = LowerConvertTaggedHoleToUndefined(node, *effect, *control);
break;
case IrOpcode::kPlainPrimitiveToNumber:
state = LowerPlainPrimitiveToNumber(node, *effect, *control);
break;
......@@ -2418,22 +2421,35 @@ EffectControlLinearizer::LowerCheckFloat64Hole(Node* node, Node* frame_state,
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(DeoptimizeReason::kHole),
check, frame_state, effect, control);
break;
}
control = effect =
graph()->NewNode(common()->DeoptimizeIf(DeoptimizeReason::kHole), check,
frame_state, effect, control);
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerConvertTaggedHoleToUndefined(Node* node,
Node* effect,
Node* control) {
Node* value = node->InputAt(0);
Node* check = graph()->NewNode(machine()->WordEqual(), value,
jsgraph()->TheHoleConstant());
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* vtrue = jsgraph()->UndefinedConstant();
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse = value;
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vtrue, vfalse, control);
return ValueEffectControl(value, effect, control);
}
......
......@@ -130,6 +130,8 @@ class EffectControlLinearizer {
Node* effect, Node* control);
ValueEffectControl LowerCheckTaggedHole(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerConvertTaggedHoleToUndefined(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerPlainPrimitiveToNumber(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerPlainPrimitiveToWord32(Node* node, Node* effect,
......
......@@ -1158,7 +1158,7 @@ JSNativeContextSpecialization::BuildElementAccess(
}
// Compute the element access.
Type* element_type = Type::Any();
Type* element_type = Type::NonInternal();
MachineType element_machine_type = MachineType::AnyTagged();
if (IsFastDoubleElementsKind(elements_kind)) {
element_type = Type::Number();
......@@ -1176,10 +1176,8 @@ JSNativeContextSpecialization::BuildElementAccess(
// of holey backing stores.
if (elements_kind == FAST_HOLEY_ELEMENTS ||
elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
element_access.type = Type::Union(
element_type,
Type::Constant(factory()->the_hole_value(), graph()->zone()),
graph()->zone());
element_access.type =
Type::Union(element_type, Type::Hole(), graph()->zone());
}
// Perform the actual backing store access.
value = effect =
......@@ -1189,15 +1187,16 @@ JSNativeContextSpecialization::BuildElementAccess(
// the hole to undefined if possible, or deoptimizing otherwise.
if (elements_kind == FAST_HOLEY_ELEMENTS ||
elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
// Perform the hole check on the result.
CheckTaggedHoleMode mode = CheckTaggedHoleMode::kNeverReturnHole;
// Check if we are allowed to turn the hole into undefined.
if (CanTreatHoleAsUndefined(receiver_maps, native_context)) {
// Turn the hole into undefined.
mode = CheckTaggedHoleMode::kConvertHoleToUndefined;
value = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(),
value);
} else {
// Bailout if we see the hole.
value = effect = graph()->NewNode(simplified()->CheckTaggedHole(),
value, effect, control);
}
value = effect = graph()->NewNode(simplified()->CheckTaggedHole(mode),
value, effect, control);
} else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
// Perform the hole check on the result.
CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole;
......
......@@ -290,6 +290,7 @@
V(CheckTaggedSigned) \
V(CheckFloat64Hole) \
V(CheckTaggedHole) \
V(ConvertTaggedHoleToUndefined) \
V(Allocate) \
V(LoadField) \
V(LoadBuffer) \
......
......@@ -2392,17 +2392,29 @@ class RepresentationSelector {
return;
}
case IrOpcode::kCheckTaggedHole: {
CheckTaggedHoleMode mode = CheckTaggedHoleModeOf(node->op());
if (truncation.IsUsedAsWord32() &&
mode == CheckTaggedHoleMode::kConvertHoleToUndefined) {
ProcessInput(node, 0, UseInfo::CheckedSigned32AsWord32());
ProcessRemainingInputs(node, 1);
SetOutput(node, MachineRepresentation::kWord32);
VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
return;
}
case IrOpcode::kConvertTaggedHoleToUndefined: {
if (InputIs(node, Type::NumberOrOddball()) &&
truncation.IsUsedAsWord32()) {
// Propagate the Word32 truncation.
VisitUnop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
if (lower()) DeferReplacement(node, node->InputAt(0));
} else if (InputIs(node, Type::NumberOrOddball()) &&
truncation.IsUsedAsFloat64()) {
// Propagate the Float64 truncation.
VisitUnop(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower()) DeferReplacement(node, node->InputAt(0));
} else if (InputIs(node, Type::NonInternal())) {
VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
if (lower()) DeferReplacement(node, node->InputAt(0));
} else {
ProcessInput(node, 0, UseInfo::AnyTagged());
ProcessRemainingInputs(node, 1);
SetOutput(node, MachineRepresentation::kTagged);
// TODO(turbofan): Add a (Tagged) truncation that identifies hole
// and undefined, i.e. for a[i] === obj cases.
VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
}
return;
}
......
This diff is collapsed.
......@@ -119,17 +119,6 @@ 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;
enum class CheckTaggedInputMode : uint8_t {
kNumber,
kNumberOrOddball,
......@@ -339,7 +328,8 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* CheckedTruncateTaggedToWord32();
const Operator* CheckFloat64Hole(CheckFloat64HoleMode);
const Operator* CheckTaggedHole(CheckTaggedHoleMode);
const Operator* CheckTaggedHole();
const Operator* ConvertTaggedHoleToUndefined();
const Operator* ObjectIsCallable();
const Operator* ObjectIsNumber();
......
......@@ -1234,7 +1234,7 @@ Type* Typer::Visitor::WrapContextTypeForInput(Node* node) {
if (outer->Is(Type::None())) {
return Type::None();
} else {
DCHECK(outer->Maybe(Type::Internal()));
DCHECK(outer->Maybe(Type::OtherInternal()));
return Type::Context(outer, zone());
}
}
......@@ -1583,19 +1583,17 @@ Type* Typer::Visitor::TypeCheckFloat64Hole(Node* node) {
}
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::TypeConvertTaggedHoleToUndefined(Node* node) {
Type* type = Operand(node, 0);
if (type->Maybe(Type::Hole())) {
// Turn "the hole" into undefined.
type = Type::Intersect(type, Type::NonInternal(), zone());
type = Type::Union(type, Type::Undefined(), zone());
}
return type;
}
......
......@@ -1042,7 +1042,11 @@ void Verifier::Visitor::Check(Node* node) {
break;
case IrOpcode::kCheckTaggedHole:
CheckValueInputIs(node, 0, Type::Any());
CheckUpperIs(node, Type::Any());
CheckUpperIs(node, Type::NonInternal());
break;
case IrOpcode::kConvertTaggedHoleToUndefined:
CheckValueInputIs(node, 0, Type::Any());
CheckUpperIs(node, Type::NonInternal());
break;
case IrOpcode::kLoadField:
......
......@@ -2609,6 +2609,8 @@ void Heap::CreateInitialObjects() {
set_nan_value(*factory->NewHeapNumber(
std::numeric_limits<double>::quiet_NaN(), IMMUTABLE, TENURED));
set_hole_nan_value(*factory->NewHeapNumber(bit_cast<double>(kHoleNanInt64),
IMMUTABLE, TENURED));
set_infinity_value(*factory->NewHeapNumber(V8_INFINITY, IMMUTABLE, TENURED));
set_minus_infinity_value(
*factory->NewHeapNumber(-V8_INFINITY, IMMUTABLE, TENURED));
......@@ -2629,7 +2631,7 @@ void Heap::CreateInitialObjects() {
// Initialize the_hole_value.
Oddball::Initialize(isolate(), factory->the_hole_value(), "hole",
handle(Smi::FromInt(-1), isolate()), "undefined",
factory->hole_nan_value(), "undefined",
Oddball::kTheHole);
// Initialize the true_value.
......
......@@ -166,6 +166,7 @@ using v8::MemoryPressureLevel;
V(Cell, species_protector, SpeciesProtector) \
/* Special numbers */ \
V(HeapNumber, nan_value, NanValue) \
V(HeapNumber, hole_nan_value, HoleNanValue) \
V(HeapNumber, infinity_value, InfinityValue) \
V(HeapNumber, minus_zero_value, MinusZeroValue) \
V(HeapNumber, minus_infinity_value, MinusInfinityValue) \
......
......@@ -601,7 +601,8 @@ void Oddball::OddballVerify() {
VerifyHeapPointer(to_string());
Object* number = to_number();
if (number->IsHeapObject()) {
CHECK(number == heap->nan_value());
CHECK(number == heap->nan_value() ||
number == heap->hole_nan_value());
} else {
CHECK(number->IsSmi());
int value = Smi::cast(number)->value();
......
......@@ -147,10 +147,10 @@ Type::bitset BitsetType::Lub(Type* type) {
if (type->IsClass()) return type->AsClass()->Lub();
if (type->IsConstant()) return type->AsConstant()->Lub();
if (type->IsRange()) return type->AsRange()->Lub();
if (type->IsContext()) return kInternal & kTaggedPointer;
if (type->IsContext()) return kOtherInternal & kTaggedPointer;
if (type->IsArray()) return kOtherObject;
if (type->IsFunction()) return kFunction;
if (type->IsTuple()) return kInternal;
if (type->IsTuple()) return kOtherInternal;
UNREACHABLE();
return kNone;
}
......@@ -187,14 +187,14 @@ Type::bitset BitsetType::Lub(i::Map* map) {
if (map == heap->undefined_map()) return kUndefined;
if (map == heap->null_map()) return kNull;
if (map == heap->boolean_map()) return kBoolean;
DCHECK(map == heap->the_hole_map() ||
map == heap->uninitialized_map() ||
if (map == heap->the_hole_map()) return kHole;
DCHECK(map == heap->uninitialized_map() ||
map == heap->no_interceptor_result_sentinel_map() ||
map == heap->termination_exception_map() ||
map == heap->arguments_marker_map() ||
map == heap->optimized_out_map() ||
map == heap->stale_register_map());
return kInternal & kTaggedPointer;
return kOtherInternal & kTaggedPointer;
}
case HEAP_NUMBER_TYPE:
return kNumber & kTaggedPointer;
......@@ -250,10 +250,10 @@ Type::bitset BitsetType::Lub(i::Map* map) {
case SCRIPT_TYPE:
case CODE_TYPE:
case PROPERTY_CELL_TYPE:
return kInternal & kTaggedPointer;
return kOtherInternal & kTaggedPointer;
// Remaining instance types are unsupported for now. If any of them do
// require bit set types, they should get kInternal & kTaggedPointer.
// require bit set types, they should get kOtherInternal & kTaggedPointer.
case MUTABLE_HEAP_NUMBER_TYPE:
case FREE_SPACE_TYPE:
#define FIXED_TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
......
......@@ -199,7 +199,8 @@ namespace internal {
V(OtherUndetectable, 1u << 16 | REPRESENTATION(kTaggedPointer)) \
V(Proxy, 1u << 18 | REPRESENTATION(kTaggedPointer)) \
V(Function, 1u << 19 | REPRESENTATION(kTaggedPointer)) \
V(Internal, 1u << 20 | REPRESENTATION(kTagged | kUntagged)) \
V(Hole, 1u << 20 | REPRESENTATION(kTaggedPointer)) \
V(OtherInternal, 1u << 21 | REPRESENTATION(kTagged | kUntagged)) \
\
V(Signed31, kUnsigned30 | kNegative31) \
V(Signed32, kSigned31 | kOtherUnsigned31 | kOtherSigned32) \
......@@ -225,7 +226,7 @@ namespace internal {
V(NullOrNumber, kNull | kNumber) \
V(NullOrUndefined, kNull | kUndefined) \
V(Undetectable, kNullOrUndefined | kOtherUndetectable) \
V(NumberOrOddball, kNumber | kNullOrUndefined | kBoolean) \
V(NumberOrOddball, kNumber | kNullOrUndefined | kBoolean | kHole) \
V(NumberOrSimdOrString, kNumber | kSimd | kString) \
V(NumberOrString, kNumber | kString) \
V(NumberOrUndefined, kNumber | kUndefined) \
......@@ -237,6 +238,7 @@ namespace internal {
V(StringOrReceiver, kString | kReceiver) \
V(Unique, kBoolean | kUniqueName | kNull | kUndefined | \
kReceiver) \
V(Internal, kHole | kOtherInternal) \
V(NonInternal, kPrimitive | kReceiver) \
V(NonNumber, kUnique | kString | kInternal) \
V(Any, 0xfffffffeu)
......
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