Commit 877de376 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Introduce FindOrderedHashMapEntryForReceiverKey operator.

This optimizes Map#get and Map#has for the case where the key is known
to be a JSReceiver. This generalizes the existing logic for the
FindOrderedHashMapEntryForSigned32Key operator to also deal with
receivers. This gives a nice 33% boost on the map-set-lookup-es6 test
of the six-speed benchmark suite.

Drive-by-fix: Rename the FindOrderedHashMapEntryForInt32Key operator to
FindOrderedHashMapEntryForSigned32Key to match the naming of the types.

R=jarin@chromium.org

Bug: v8:5267, v8:7001
Change-Id: Ifab8414f26adee7ec833d8cb94ae0ac49f2c3d35
Reviewed-on: https://chromium-review.googlesource.com/738180Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48938}
parent a7a4732f
......@@ -862,8 +862,11 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kFindOrderedHashMapEntry:
result = LowerFindOrderedHashMapEntry(node);
break;
case IrOpcode::kFindOrderedHashMapEntryForInt32Key:
result = LowerFindOrderedHashMapEntryForInt32Key(node);
case IrOpcode::kFindOrderedHashMapEntryForReceiverKey:
result = LowerFindOrderedHashMapEntryForReceiverKey(node);
break;
case IrOpcode::kFindOrderedHashMapEntryForSigned32Key:
result = LowerFindOrderedHashMapEntryForSigned32Key(node);
break;
case IrOpcode::kTransitionAndStoreNumberElement:
LowerTransitionAndStoreNumberElement(node);
......@@ -3958,44 +3961,9 @@ Maybe<Node*> EffectControlLinearizer::LowerFloat64RoundTruncate(Node* node) {
return Just(done.PhiAt(0));
}
Node* EffectControlLinearizer::LowerFindOrderedHashMapEntry(Node* node) {
Node* table = NodeProperties::GetValueInput(node, 0);
Node* key = NodeProperties::GetValueInput(node, 1);
{
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kFindOrderedHashMapEntry);
Operator::Properties const properties = node->op()->properties();
CallDescriptor::Flags const flags = CallDescriptor::kNoFlags;
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags,
properties);
return __ Call(desc, __ HeapConstant(callable.code()), table, key,
__ NoContextConstant());
}
}
Node* EffectControlLinearizer::ComputeIntegerHash(Node* value) {
// See v8::internal::ComputeIntegerHash()
value = __ Int32Add(__ Word32Xor(value, __ Int32Constant(0xffffffff)),
__ Word32Shl(value, __ Int32Constant(15)));
value = __ Word32Xor(value, __ Word32Shr(value, __ Int32Constant(12)));
value = __ Int32Add(value, __ Word32Shl(value, __ Int32Constant(2)));
value = __ Word32Xor(value, __ Word32Shr(value, __ Int32Constant(4)));
value = __ Int32Mul(value, __ Int32Constant(2057));
value = __ Word32Xor(value, __ Word32Shr(value, __ Int32Constant(16)));
value = __ Word32And(value, __ Int32Constant(0x3fffffff));
return value;
}
Node* EffectControlLinearizer::LowerFindOrderedHashMapEntryForInt32Key(
Node* node) {
Node* table = NodeProperties::GetValueInput(node, 0);
Node* key = NodeProperties::GetValueInput(node, 1);
// Compute the integer hash code.
Node* hash = ChangeUint32ToUintPtr(ComputeIntegerHash(key));
template <typename Predicate>
Node* EffectControlLinearizer::BuildFindOrderedHashMapEntry(
Node* table, Node* hash, Predicate const& predicate) {
Node* number_of_buckets = ChangeSmiToIntPtr(__ LoadField(
AccessBuilder::ForOrderedHashTableBaseNumberOfBuckets(), table));
hash = __ WordAnd(hash, __ IntSub(number_of_buckets, __ IntPtrConstant(1)));
......@@ -4026,20 +3994,7 @@ Node* EffectControlLinearizer::LowerFindOrderedHashMapEntryForInt32Key(
auto if_match = __ MakeLabel();
auto if_notmatch = __ MakeLabel();
auto if_notsmi = __ MakeDeferredLabel();
__ GotoIfNot(ObjectIsSmi(candidate_key), &if_notsmi);
__ Branch(__ Word32Equal(ChangeSmiToInt32(candidate_key), key), &if_match,
&if_notmatch);
__ Bind(&if_notsmi);
__ GotoIfNot(
__ WordEqual(__ LoadField(AccessBuilder::ForMap(), candidate_key),
__ HeapNumberMapConstant()),
&if_notmatch);
__ Branch(__ Float64Equal(__ LoadField(AccessBuilder::ForHeapNumberValue(),
candidate_key),
__ ChangeInt32ToFloat64(key)),
&if_match, &if_notmatch);
predicate(candidate_key, &if_match, &if_notmatch);
__ Bind(&if_match);
{
......@@ -4064,6 +4019,138 @@ Node* EffectControlLinearizer::LowerFindOrderedHashMapEntryForInt32Key(
return done.PhiAt(0);
}
Node* EffectControlLinearizer::LowerFindOrderedHashMapEntry(Node* node) {
Node* table = NodeProperties::GetValueInput(node, 0);
Node* key = NodeProperties::GetValueInput(node, 1);
{
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kFindOrderedHashMapEntry);
Operator::Properties const properties = node->op()->properties();
CallDescriptor::Flags const flags = CallDescriptor::kNoFlags;
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags,
properties);
return __ Call(desc, __ HeapConstant(callable.code()), table, key,
__ NoContextConstant());
}
}
Node* EffectControlLinearizer::GetExistingHashForReceiver(Node* receiver) {
auto done = __ MakeLabel(MachineType::PointerRepresentation());
auto if_smi = __ MakeLabel();
auto if_fixed_array = __ MakeLabel();
auto if_property_array = __ MakeLabel();
auto if_property_dictionary = __ MakeLabel();
// Dispatch depending on the {receiver}s "properties or hash" field.
Node* properties_or_hash =
__ LoadField(AccessBuilder::ForJSObjectPropertiesOrHash(), receiver);
__ GotoIf(ObjectIsSmi(properties_or_hash), &if_smi);
Node* properties_map =
__ LoadField(AccessBuilder::ForMap(), properties_or_hash);
Node* properties_instance_type =
__ LoadField(AccessBuilder::ForMapInstanceType(), properties_map);
__ GotoIf(__ Word32Equal(properties_instance_type,
__ Int32Constant(PROPERTY_ARRAY_TYPE)),
&if_property_array);
__ Branch(__ Word32Equal(properties_instance_type,
__ Int32Constant(HASH_TABLE_TYPE)),
&if_property_dictionary, &if_fixed_array);
__ Bind(&if_smi);
{
Node* hash = ChangeSmiToIntPtr(properties_or_hash);
__ Goto(&done, hash);
}
__ Bind(&if_fixed_array);
{
Node* hash = __ Int32Constant(PropertyArray::kNoHashSentinel);
__ Goto(&done, hash);
}
__ Bind(&if_property_array);
{
Node* length_and_hash = ChangeSmiToIntPtr(__ LoadField(
AccessBuilder::ForPropertyArrayLengthAndHash(), properties_or_hash));
Node* hash = __ WordShr(
length_and_hash, __ IntPtrConstant(PropertyArray::HashField::kShift));
__ Goto(&done, hash);
}
__ Bind(&if_property_dictionary);
{
Node* hash = ChangeSmiToIntPtr(__ LoadField(
AccessBuilder::ForDictionaryObjectHashIndex(), properties_or_hash));
__ Goto(&done, hash);
}
__ Bind(&done);
return done.PhiAt(0);
}
Node* EffectControlLinearizer::LowerFindOrderedHashMapEntryForReceiverKey(
Node* node) {
Node* table = NodeProperties::GetValueInput(node, 0);
Node* key = NodeProperties::GetValueInput(node, 1);
// Compute the hash code for the {key}.
Node* hash = GetExistingHashForReceiver(key);
// Search for the entry with the given {key}.
return BuildFindOrderedHashMapEntry(
table, hash,
[=](Node* candidate_key, GraphAssemblerLabel<0>* if_match,
GraphAssemblerLabel<0>* if_notmatch) {
__ Branch(__ WordEqual(candidate_key, key), if_match, if_notmatch);
});
}
Node* EffectControlLinearizer::ComputeIntegerHash(Node* value) {
// See v8::internal::ComputeIntegerHash()
value = __ Int32Add(__ Word32Xor(value, __ Int32Constant(0xffffffff)),
__ Word32Shl(value, __ Int32Constant(15)));
value = __ Word32Xor(value, __ Word32Shr(value, __ Int32Constant(12)));
value = __ Int32Add(value, __ Word32Shl(value, __ Int32Constant(2)));
value = __ Word32Xor(value, __ Word32Shr(value, __ Int32Constant(4)));
value = __ Int32Mul(value, __ Int32Constant(2057));
value = __ Word32Xor(value, __ Word32Shr(value, __ Int32Constant(16)));
value = __ Word32And(value, __ Int32Constant(0x3fffffff));
return value;
}
Node* EffectControlLinearizer::LowerFindOrderedHashMapEntryForSigned32Key(
Node* node) {
Node* table = NodeProperties::GetValueInput(node, 0);
Node* key = NodeProperties::GetValueInput(node, 1);
// Compute the integer hash code for the {key}.
Node* hash = ChangeUint32ToUintPtr(ComputeIntegerHash(key));
// Search for the entry with the given {key}.
return BuildFindOrderedHashMapEntry(
table, hash,
[=](Node* candidate_key, GraphAssemblerLabel<0>* if_match,
GraphAssemblerLabel<0>* if_notmatch) {
auto if_notsmi = __ MakeDeferredLabel();
__ GotoIfNot(ObjectIsSmi(candidate_key), &if_notsmi);
__ Branch(__ Word32Equal(ChangeSmiToInt32(candidate_key), key),
if_match, if_notmatch);
__ Bind(&if_notsmi);
__ GotoIfNot(
__ WordEqual(__ LoadField(AccessBuilder::ForMap(), candidate_key),
__ HeapNumberMapConstant()),
if_notmatch);
__ Branch(
__ Float64Equal(__ LoadField(AccessBuilder::ForHeapNumberValue(),
candidate_key),
__ ChangeInt32ToFloat64(key)),
if_match, if_notmatch);
});
}
#undef __
Factory* EffectControlLinearizer::factory() const {
......
......@@ -134,7 +134,8 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
void LowerStoreTypedElement(Node* node);
void LowerStoreSignedSmallElement(Node* node);
Node* LowerFindOrderedHashMapEntry(Node* node);
Node* LowerFindOrderedHashMapEntryForInt32Key(Node* node);
Node* LowerFindOrderedHashMapEntryForReceiverKey(Node* node);
Node* LowerFindOrderedHashMapEntryForSigned32Key(Node* node);
void LowerTransitionAndStoreElement(Node* node);
void LowerTransitionAndStoreNumberElement(Node* node);
void LowerTransitionAndStoreNonNumberElement(Node* node);
......@@ -153,7 +154,11 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* value,
Node* frame_state);
Node* BuildFloat64RoundDown(Node* value);
template <typename Predicate>
Node* BuildFindOrderedHashMapEntry(Node* table, Node* hash,
Predicate const& predicate);
Node* ComputeIntegerHash(Node* value);
Node* GetExistingHashForReceiver(Node* receiver);
Node* LowerStringComparison(Callable const& callable, Node* node);
Node* IsElementsKindGreaterThan(Node* kind, ElementsKind reference_kind);
......
......@@ -34,6 +34,7 @@ namespace compiler {
#define PURE_ASSEMBLER_MACH_BINOP_LIST(V) \
V(WordShl) \
V(WordSar) \
V(WordShr) \
V(WordAnd) \
V(Word32Or) \
V(Word32And) \
......
......@@ -309,76 +309,77 @@
#define SIMPLIFIED_SPECULATIVE_NUMBER_UNOP_LIST(V) V(SpeculativeToNumber)
#define SIMPLIFIED_OTHER_OP_LIST(V) \
V(PlainPrimitiveToNumber) \
V(PlainPrimitiveToWord32) \
V(PlainPrimitiveToFloat64) \
V(BooleanNot) \
V(StringToNumber) \
V(StringCharAt) \
V(StringCharCodeAt) \
V(SeqStringCharCodeAt) \
V(StringFromCharCode) \
V(StringFromCodePoint) \
V(StringIndexOf) \
V(StringToLowerCaseIntl) \
V(StringToUpperCaseIntl) \
V(CheckBounds) \
V(CheckIf) \
V(CheckMaps) \
V(CheckNumber) \
V(CheckInternalizedString) \
V(CheckReceiver) \
V(CheckString) \
V(CheckSeqString) \
V(CheckSymbol) \
V(CheckSmi) \
V(CheckHeapObject) \
V(CheckFloat64Hole) \
V(CheckNotTaggedHole) \
V(CheckEqualsInternalizedString) \
V(CheckEqualsSymbol) \
V(CompareMaps) \
V(ConvertTaggedHoleToUndefined) \
V(TypeOf) \
V(ClassOf) \
V(Allocate) \
V(LoadFieldByIndex) \
V(LoadField) \
V(LoadElement) \
V(LoadTypedElement) \
V(StoreField) \
V(StoreElement) \
V(StoreTypedElement) \
V(StoreSignedSmallElement) \
V(TransitionAndStoreElement) \
V(TransitionAndStoreNumberElement) \
V(TransitionAndStoreNonNumberElement) \
V(ToBoolean) \
V(ObjectIsArrayBufferView) \
V(ObjectIsCallable) \
V(ObjectIsConstructor) \
V(ObjectIsDetectableCallable) \
V(ObjectIsMinusZero) \
V(ObjectIsNaN) \
V(ObjectIsNonCallable) \
V(ObjectIsNumber) \
V(ObjectIsReceiver) \
V(ObjectIsSmi) \
V(ObjectIsString) \
V(ObjectIsSymbol) \
V(ObjectIsUndetectable) \
V(ArgumentsFrame) \
V(ArgumentsLength) \
V(NewDoubleElements) \
V(NewSmiOrObjectElements) \
V(NewArgumentsElements) \
V(ArrayBufferWasNeutered) \
V(EnsureWritableFastElements) \
V(MaybeGrowFastElements) \
V(TransitionElementsKind) \
V(FindOrderedHashMapEntry) \
V(FindOrderedHashMapEntryForInt32Key) \
#define SIMPLIFIED_OTHER_OP_LIST(V) \
V(PlainPrimitiveToNumber) \
V(PlainPrimitiveToWord32) \
V(PlainPrimitiveToFloat64) \
V(BooleanNot) \
V(StringToNumber) \
V(StringCharAt) \
V(StringCharCodeAt) \
V(SeqStringCharCodeAt) \
V(StringFromCharCode) \
V(StringFromCodePoint) \
V(StringIndexOf) \
V(StringToLowerCaseIntl) \
V(StringToUpperCaseIntl) \
V(CheckBounds) \
V(CheckIf) \
V(CheckMaps) \
V(CheckNumber) \
V(CheckInternalizedString) \
V(CheckReceiver) \
V(CheckString) \
V(CheckSeqString) \
V(CheckSymbol) \
V(CheckSmi) \
V(CheckHeapObject) \
V(CheckFloat64Hole) \
V(CheckNotTaggedHole) \
V(CheckEqualsInternalizedString) \
V(CheckEqualsSymbol) \
V(CompareMaps) \
V(ConvertTaggedHoleToUndefined) \
V(TypeOf) \
V(ClassOf) \
V(Allocate) \
V(LoadFieldByIndex) \
V(LoadField) \
V(LoadElement) \
V(LoadTypedElement) \
V(StoreField) \
V(StoreElement) \
V(StoreTypedElement) \
V(StoreSignedSmallElement) \
V(TransitionAndStoreElement) \
V(TransitionAndStoreNumberElement) \
V(TransitionAndStoreNonNumberElement) \
V(ToBoolean) \
V(ObjectIsArrayBufferView) \
V(ObjectIsCallable) \
V(ObjectIsConstructor) \
V(ObjectIsDetectableCallable) \
V(ObjectIsMinusZero) \
V(ObjectIsNaN) \
V(ObjectIsNonCallable) \
V(ObjectIsNumber) \
V(ObjectIsReceiver) \
V(ObjectIsSmi) \
V(ObjectIsString) \
V(ObjectIsSymbol) \
V(ObjectIsUndetectable) \
V(ArgumentsFrame) \
V(ArgumentsLength) \
V(NewDoubleElements) \
V(NewSmiOrObjectElements) \
V(NewArgumentsElements) \
V(ArrayBufferWasNeutered) \
V(EnsureWritableFastElements) \
V(MaybeGrowFastElements) \
V(TransitionElementsKind) \
V(FindOrderedHashMapEntry) \
V(FindOrderedHashMapEntryForReceiverKey) \
V(FindOrderedHashMapEntryForSigned32Key) \
V(RuntimeAbort)
#define SIMPLIFIED_OP_LIST(V) \
......
......@@ -2962,13 +2962,21 @@ class RepresentationSelector {
case IrOpcode::kFindOrderedHashMapEntry: {
Type* const key_type = TypeOf(node->InputAt(1));
if (key_type->Is(Type::Signed32OrMinusZero())) {
if (key_type->Is(Type::Receiver())) {
VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TaggedPointer(),
MachineRepresentation::kWord32);
if (lower()) {
NodeProperties::ChangeOp(
node, lowering->simplified()
->FindOrderedHashMapEntryForReceiverKey());
}
} else if (key_type->Is(Type::Signed32OrMinusZero())) {
VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
if (lower()) {
NodeProperties::ChangeOp(
node,
lowering->simplified()->FindOrderedHashMapEntryForInt32Key());
node, lowering->simplified()
->FindOrderedHashMapEntryForSigned32Key());
}
} else {
VisitBinop(node, UseInfo::AnyTagged(),
......
......@@ -722,14 +722,23 @@ struct SimplifiedOperatorGlobalCache final {
};
FindOrderedHashMapEntryOperator kFindOrderedHashMapEntry;
struct FindOrderedHashMapEntryForInt32KeyOperator final : public Operator {
FindOrderedHashMapEntryForInt32KeyOperator()
: Operator(IrOpcode::kFindOrderedHashMapEntryForInt32Key,
struct FindOrderedHashMapEntryForReceiverKeyOperator final : public Operator {
FindOrderedHashMapEntryForReceiverKeyOperator()
: Operator(IrOpcode::kFindOrderedHashMapEntryForReceiverKey,
Operator::kEliminatable,
"FindOrderedHashMapEntryForInt32Key", 2, 1, 1, 1, 1, 0) {}
"FindOrderedHashMapEntryForReceiverKey", 2, 1, 1, 1, 1, 0) {}
};
FindOrderedHashMapEntryForInt32KeyOperator
kFindOrderedHashMapEntryForInt32Key;
FindOrderedHashMapEntryForReceiverKeyOperator
kFindOrderedHashMapEntryForReceiverKey;
struct FindOrderedHashMapEntryForSigned32KeyOperator final : public Operator {
FindOrderedHashMapEntryForSigned32KeyOperator()
: Operator(IrOpcode::kFindOrderedHashMapEntryForSigned32Key,
Operator::kEliminatable,
"FindOrderedHashMapEntryForSigned32Key", 2, 1, 1, 1, 1, 0) {}
};
FindOrderedHashMapEntryForSigned32KeyOperator
kFindOrderedHashMapEntryForSigned32Key;
struct ArgumentsFrameOperator final : public Operator {
ArgumentsFrameOperator()
......@@ -908,7 +917,8 @@ CHECKED_OP_LIST(GET_FROM_CACHE)
GET_FROM_CACHE(ArrayBufferWasNeutered)
GET_FROM_CACHE(ArgumentsFrame)
GET_FROM_CACHE(FindOrderedHashMapEntry)
GET_FROM_CACHE(FindOrderedHashMapEntryForInt32Key)
GET_FROM_CACHE(FindOrderedHashMapEntryForReceiverKey)
GET_FROM_CACHE(FindOrderedHashMapEntryForSigned32Key)
GET_FROM_CACHE(LoadFieldByIndex)
#undef GET_FROM_CACHE
......
......@@ -408,7 +408,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* StringToUpperCaseIntl();
const Operator* FindOrderedHashMapEntry();
const Operator* FindOrderedHashMapEntryForInt32Key();
const Operator* FindOrderedHashMapEntryForReceiverKey();
const Operator* FindOrderedHashMapEntryForSigned32Key();
const Operator* SpeculativeToNumber(NumberOperationHint hint);
......
......@@ -2085,7 +2085,11 @@ Type* Typer::Visitor::TypeFindOrderedHashMapEntry(Node* node) {
return Type::Range(-1.0, FixedArray::kMaxLength, zone());
}
Type* Typer::Visitor::TypeFindOrderedHashMapEntryForInt32Key(Node* node) {
Type* Typer::Visitor::TypeFindOrderedHashMapEntryForReceiverKey(Node* node) {
return Type::Range(-1.0, FixedArray::kMaxLength, zone());
}
Type* Typer::Visitor::TypeFindOrderedHashMapEntryForSigned32Key(Node* node) {
return Type::Range(-1.0, FixedArray::kMaxLength, zone());
}
......
......@@ -1053,9 +1053,14 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::SignedSmall());
break;
case IrOpcode::kFindOrderedHashMapEntryForInt32Key:
case IrOpcode::kFindOrderedHashMapEntryForReceiverKey:
CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Signed32());
CheckValueInputIs(node, 1, Type::Receiver());
CheckTypeIs(node, Type::SignedSmall());
break;
case IrOpcode::kFindOrderedHashMapEntryForSigned32Key:
CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Signed32OrMinusZero());
CheckTypeIs(node, Type::SignedSmall());
break;
case IrOpcode::kArgumentsLength:
......
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