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

[turbofan] Utilize the fact that empty string is canonicalized.

Since the empty string is canonical HeapObject now, we can use
this fact to optimize

  - strict equality comparisons with the empty string to a
    simple ReferenceEqual operation, and
  - optimize ToBoolean to avoid instance type checks completely.

Drive-by-fix: Allow InternalizedString for Type::HeapConstant
in the type system. This is safe, since InternalizedStrings
can be compared to other heap constants by reference (except
for non-InternalizedStrings, which are excluded from the
HeapConstant type).

BUG=v8:5267
R=yangguo@chromium.org

Review-Url: https://codereview.chromium.org/2681273002
Cr-Commit-Position: refs/heads/master@{#43050}
parent 1a989bde
......@@ -912,11 +912,10 @@ Node* CodeStubAssembler::IsRegularHeapObjectSize(Node* size) {
void CodeStubAssembler::BranchIfToBooleanIsTrue(Node* value, Label* if_true,
Label* if_false) {
Label if_valueissmi(this), if_valueisnotsmi(this), if_valueisstring(this),
if_valueisheapnumber(this), if_valueisother(this);
Label if_valueissmi(this), if_valueisnotsmi(this),
if_valueisheapnumber(this, Label::kDeferred);
// Fast check for Boolean {value}s (common case).
GotoIf(WordEqual(value, BooleanConstant(true)), if_true);
// Rule out false {value}.
GotoIf(WordEqual(value, BooleanConstant(false)), if_false);
// Check if {value} is a Smi or a HeapObject.
......@@ -930,27 +929,24 @@ void CodeStubAssembler::BranchIfToBooleanIsTrue(Node* value, Label* if_true,
Bind(&if_valueisnotsmi);
{
// Check if {value} is the empty string.
GotoIf(IsEmptyString(value), if_false);
// The {value} is a HeapObject, load its map.
Node* value_map = LoadMap(value);
// Load the {value}s instance type.
Node* value_instance_type = LoadMapInstanceType(value_map);
// Dispatch based on the instance type; we distinguish all String instance
// types, the HeapNumber type and everything else.
GotoIf(Word32Equal(value_instance_type, Int32Constant(HEAP_NUMBER_TYPE)),
&if_valueisheapnumber);
Branch(IsStringInstanceType(value_instance_type), &if_valueisstring,
&if_valueisother);
// Only null, undefined and document.all have the undetectable bit set,
// so we can return false immediately when that bit is set.
Node* value_map_bitfield = LoadMapBitField(value_map);
Node* value_map_undetectable =
Word32And(value_map_bitfield, Int32Constant(1 << Map::kIsUndetectable));
Bind(&if_valueisstring);
{
// Load the string length field of the {value}.
Node* value_length = LoadObjectField(value, String::kLengthOffset);
// Check if the {value} is undetectable.
GotoUnless(Word32Equal(value_map_undetectable, Int32Constant(0)), if_false);
// Check if the {value} is the empty string.
BranchIfSmiEqual(value_length, SmiConstant(0), if_false, if_true);
}
// We still need to handle numbers specially, but all other {value}s
// that make it here yield true.
Branch(IsHeapNumberMap(value_map), &if_valueisheapnumber, if_true);
Bind(&if_valueisheapnumber);
{
......@@ -962,22 +958,6 @@ void CodeStubAssembler::BranchIfToBooleanIsTrue(Node* value, Label* if_true,
Branch(Float64LessThan(Float64Constant(0.0), Float64Abs(value_value)),
if_true, if_false);
}
Bind(&if_valueisother);
{
// Load the bit field from the {value}s map. The {value} is now either
// Null or Undefined, which have the undetectable bit set (so we always
// return false for those), or a Symbol or Simd128Value, whose maps never
// have the undetectable bit set (so we always return true for those), or
// a JSReceiver, which may or may not have the undetectable bit set.
Node* value_map_bitfield = LoadMapBitField(value_map);
Node* value_map_undetectable = Word32And(
value_map_bitfield, Int32Constant(1 << Map::kIsUndetectable));
// Check if the {value} is undetectable.
Branch(Word32Equal(value_map_undetectable, Int32Constant(0)), if_true,
if_false);
}
}
}
......
......@@ -922,70 +922,56 @@ Node* EffectControlLinearizer::LowerTruncateTaggedToBit(Node* node) {
Node* value = node->InputAt(0);
auto if_smi = __ MakeDeferredLabel<1>();
auto if_not_oddball = __ MakeDeferredLabel<1>();
auto if_not_string = __ MakeDeferredLabel<1>();
auto if_not_heapnumber = __ MakeDeferredLabel<1>();
auto done = __ MakeLabel<5>(MachineRepresentation::kBit);
auto if_heapnumber = __ MakeDeferredLabel<1>();
auto done = __ MakeLabel<6>(MachineRepresentation::kBit);
Node* zero = __ Int32Constant(0);
Node* fzero = __ Float64Constant(0.0);
// Check if {value} is false.
__ GotoIf(__ WordEqual(value, __ FalseConstant()), &done, zero);
// Check if {value} is a Smi.
Node* check_smi = ObjectIsSmi(value);
__ GotoIf(check_smi, &if_smi);
// Load the map instance type of {value}.
// Check if {value} is the empty string.
__ GotoIf(__ WordEqual(value, __ EmptyStringConstant()), &done, zero);
// Load the map of {value}.
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
Node* value_instance_type =
__ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
// Check if {value} is an Oddball.
Node* check_oddball =
__ Word32Equal(value_instance_type, __ Int32Constant(ODDBALL_TYPE));
__ GotoUnless(check_oddball, &if_not_oddball);
// The only Oddball {value} that is trueish is true itself.
__ Goto(&done, __ WordEqual(value, __ TrueConstant()));
__ Bind(&if_not_oddball);
// Check if {value} is a String.
Node* check_string = __ Int32LessThan(value_instance_type,
__ Int32Constant(FIRST_NONSTRING_TYPE));
__ GotoUnless(check_string, &if_not_string);
// For String {value}, we need to check that the length is not zero.
Node* value_length = __ LoadField(AccessBuilder::ForStringLength(), value);
__ Goto(&done, __ Word32Equal(
__ WordEqual(value_length, __ IntPtrConstant(0)), zero));
__ Bind(&if_not_string);
// Check if {value} is a HeapNumber.
Node* check_heapnumber =
__ Word32Equal(value_instance_type, __ Int32Constant(HEAP_NUMBER_TYPE));
__ GotoUnless(check_heapnumber, &if_not_heapnumber);
// For HeapNumber {value}, just check that its value is not 0.0, -0.0 or
// NaN.
// Load the raw value of {value}.
Node* value_value = __ LoadField(AccessBuilder::ForHeapNumberValue(), value);
__ Goto(&done, __ Float64LessThan(fzero, __ Float64Abs(value_value)));
// The {value} is either a JSReceiver, a Symbol or some Simd128Value. In
// those cases we can just the undetectable bit on the map, which will only
// be set for certain JSReceivers, i.e. document.all.
__ Bind(&if_not_heapnumber);
// Load the {value} map bit field.
// Check if the {value} is undetectable and immediately return false.
Node* value_map_bitfield =
__ LoadField(AccessBuilder::ForMapBitField(), value_map);
__ Goto(&done, __ Word32Equal(
__ Word32And(value_map_bitfield,
__ GotoUnless(
__ Word32Equal(__ Word32And(value_map_bitfield,
__ Int32Constant(1 << Map::kIsUndetectable)),
zero));
zero),
&done, zero);
// Check if {value} is a HeapNumber.
__ GotoIf(__ WordEqual(value_map, __ HeapNumberMapConstant()),
&if_heapnumber);
// All other values that reach here are true.
__ Goto(&done, __ Int32Constant(1));
__ Bind(&if_heapnumber);
{
// For HeapNumber {value}, just check that its value is not 0.0, -0.0 or
// NaN.
Node* value_value =
__ LoadField(AccessBuilder::ForHeapNumberValue(), value);
__ Goto(&done, __ Float64LessThan(fzero, __ Float64Abs(value_value)));
}
__ Bind(&if_smi);
// If {value} is a Smi, then we only need to check that it's not zero.
__ Goto(&done,
__ Word32Equal(__ WordEqual(value, __ IntPtrConstant(0)), zero));
{
// If {value} is a Smi, then we only need to check that it's not zero.
__ Goto(&done,
__ Word32Equal(__ WordEqual(value, __ IntPtrConstant(0)), zero));
}
__ Bind(&done);
return done.PhiAt(0);
......
......@@ -528,6 +528,13 @@ JSTypedLowering::JSTypedLowering(Editor* editor,
dependencies_(dependencies),
flags_(flags),
jsgraph_(jsgraph),
pointer_comparable_type_(Type::Union(
Type::Oddball(),
Type::Union(
Type::SymbolOrReceiver(),
Type::HeapConstant(factory()->empty_string(), graph()->zone()),
graph()->zone()),
graph()->zone())),
type_cache_(TypeCache::Get()) {
for (size_t k = 0; k < arraysize(shifted_int32_ranges_); ++k) {
double min = kMinInt / (1 << k);
......@@ -1010,7 +1017,7 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) {
if (r.BothInputsAre(Type::Unique())) {
return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
}
if (r.OneInputIs(Type::NonStringUniqueOrHole())) {
if (r.OneInputIs(pointer_comparable_type_)) {
return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
}
if (r.IsInternalizedStringCompareOperation()) {
......@@ -1074,6 +1081,14 @@ Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) {
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->BooleanNot());
return Changed(node);
} else if (input_type->Is(Type::String())) {
// JSToBoolean(x:string) => BooleanNot(ReferenceEqual(x,""))
node->ReplaceInput(0,
graph()->NewNode(simplified()->ReferenceEqual(), input,
jsgraph()->EmptyStringConstant()));
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->BooleanNot());
return Changed(node);
}
return NoChange();
}
......
......@@ -99,6 +99,7 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
Flags flags_;
JSGraph* jsgraph_;
Type* shifted_int32_ranges_[4];
Type* pointer_comparable_type_;
TypeCache const& type_cache_;
};
......
......@@ -43,13 +43,14 @@ Typer::Typer(Isolate* isolate, Flags flags, Graph* graph)
Zone* zone = this->zone();
Factory* const factory = isolate->factory();
singleton_false_ = Type::HeapConstant(factory->false_value(), zone);
singleton_true_ = Type::HeapConstant(factory->true_value(), zone);
singleton_the_hole_ = Type::HeapConstant(factory->the_hole_value(), zone);
singleton_empty_string_ = Type::HeapConstant(factory->empty_string(), zone);
singleton_false_ = operation_typer_.singleton_false();
singleton_true_ = operation_typer_.singleton_true();
falsish_ = Type::Union(
Type::Undetectable(),
Type::Union(Type::Union(singleton_false_, cache_.kZeroish, zone),
singleton_the_hole_, zone),
Type::Union(singleton_empty_string_, Type::Hole(), zone),
zone),
zone);
truish_ = Type::Union(
singleton_true_,
......@@ -908,8 +909,7 @@ Type* Typer::Visitor::JSStrictEqualTyper(Type* lhs, Type* rhs, Typer* t) {
(lhs->Max() < rhs->Min() || lhs->Min() > rhs->Max())) {
return t->singleton_false_;
}
if ((lhs->Is(t->singleton_the_hole_) || rhs->Is(t->singleton_the_hole_)) &&
!lhs->Maybe(rhs)) {
if ((lhs->Is(Type::Hole()) || rhs->Is(Type::Hole())) && !lhs->Maybe(rhs)) {
return t->singleton_false_;
}
if (lhs->IsHeapConstant() && rhs->Is(lhs)) {
......
......@@ -50,9 +50,9 @@ class V8_EXPORT_PRIVATE Typer {
TypeCache const& cache_;
OperationTyper operation_typer_;
Type* singleton_empty_string_;
Type* singleton_false_;
Type* singleton_true_;
Type* singleton_the_hole_;
Type* falsish_;
Type* truish_;
......
......@@ -464,7 +464,7 @@ HeapConstantType::HeapConstantType(BitsetType::bitset bitset,
i::Handle<i::HeapObject> object)
: TypeBase(kHeapConstant), bitset_(bitset), object_(object) {
DCHECK(!object->IsHeapNumber());
DCHECK(!object->IsString());
DCHECK_IMPLIES(object->IsString(), object->IsInternalizedString());
}
// -----------------------------------------------------------------------------
......@@ -840,17 +840,8 @@ Type* Type::NewConstant(i::Handle<i::Object> value, Zone* zone) {
return Range(v, v, zone);
} else if (value->IsHeapNumber()) {
return NewConstant(value->Number(), zone);
} else if (value->IsString()) {
bitset b = BitsetType::Lub(*value);
DCHECK(b == BitsetType::kInternalizedString ||
b == BitsetType::kOtherString);
if (b == BitsetType::kInternalizedString) {
return Type::InternalizedString();
} else if (b == BitsetType::kOtherString) {
return Type::OtherString();
} else {
UNREACHABLE();
}
} else if (value->IsString() && !value->IsInternalizedString()) {
return Type::OtherString();
}
return HeapConstant(i::Handle<i::HeapObject>::cast(value), zone);
}
......
......@@ -152,6 +152,7 @@ namespace compiler {
V(BooleanOrNumber, kBoolean | kNumber) \
V(BooleanOrNullOrNumber, kBooleanOrNumber | kNull) \
V(BooleanOrNullOrUndefined, kBoolean | kNull | kUndefined) \
V(Oddball, kBooleanOrNullOrUndefined | kHole) \
V(NullOrNumber, kNull | kNumber) \
V(NullOrUndefined, kNull | kUndefined) \
V(Undetectable, kNullOrUndefined | kOtherUndetectable) \
......@@ -178,11 +179,10 @@ namespace compiler {
V(Receiver, kObject | kProxy) \
V(ReceiverOrUndefined, kReceiver | kUndefined) \
V(ReceiverOrNullOrUndefined, kReceiver | kNull | kUndefined) \
V(SymbolOrReceiver, kSymbol | kReceiver) \
V(StringOrReceiver, kString | kReceiver) \
V(Unique, kBoolean | kUniqueName | kNull | \
kUndefined | kReceiver) \
V(NonStringUniqueOrHole, kBoolean | kHole | kNull | kReceiver | \
kSymbol | kUndefined) \
V(Internal, kHole | kExternalPointer | kOtherInternal) \
V(NonInternal, kPrimitive | kReceiver) \
V(NonNumber, kUnique | kString | kInternal) \
......
......@@ -130,6 +130,17 @@ TEST_F(JSTypedLoweringTest, JSToBooleanWithReceiverOrNullOrUndefined) {
EXPECT_THAT(r.replacement(), IsBooleanNot(IsObjectIsUndetectable(input)));
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithString) {
Node* input = Parameter(Type::String(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsBooleanNot(IsReferenceEqual(
input, IsHeapConstant(factory()->empty_string()))));
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithAny) {
Node* input = Parameter(Type::Any(), 0);
Node* context = Parameter(Type::Any(), 1);
......
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