Commit 34d9be2e authored by adamk's avatar adamk Committed by Commit bot

Optimize non-mutation Map and Set operations for String keys

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

Cr-Commit-Position: refs/heads/master@{#25625}
parent 881a921b
......@@ -65,7 +65,7 @@ function SetHasJS(key) {
throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.has', this]);
}
return %SetHas(this, key);
return %_SetHas(this, key);
}
......@@ -83,7 +83,7 @@ function SetGetSizeJS() {
throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.size', this]);
}
return %SetGetSize(this);
return %_SetGetSize(this);
}
......@@ -193,7 +193,7 @@ function MapGetJS(key) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.get', this]);
}
return %MapGet(this, key);
return %_MapGet(this, key);
}
......@@ -218,7 +218,7 @@ function MapHasJS(key) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.has', this]);
}
return %MapHas(this, key);
return %_MapHas(this, key);
}
......@@ -236,7 +236,7 @@ function MapGetSizeJS() {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.size', this]);
}
return %MapGetSize(this);
return %_MapGetSize(this);
}
......
......@@ -6339,6 +6339,23 @@ class HObjectAccess FINAL {
return HObjectAccess(kInobject, GlobalObject::kNativeContextOffset);
}
static HObjectAccess ForJSCollectionTable() {
return HObjectAccess::ForObservableJSObjectOffset(
JSCollection::kTableOffset);
}
template <typename CollectionType>
static HObjectAccess ForOrderedHashTableNumberOfBuckets() {
return HObjectAccess(kInobject, CollectionType::kNumberOfBucketsOffset,
Representation::Smi());
}
template <typename CollectionType>
static HObjectAccess ForOrderedHashTableNumberOfElements() {
return HObjectAccess(kInobject, CollectionType::kNumberOfElementsOffset,
Representation::Smi());
}
inline bool Equals(HObjectAccess that) const {
return value_ == that.value_; // portion and offset must match
}
......
......@@ -12113,6 +12113,240 @@ void HOptimizedGraphBuilder::GenerateMathSqrtRT(CallRuntime* call) {
}
template <typename CollectionType>
HValue* HOptimizedGraphBuilder::BuildOrderedHashTableFindEntry(HValue* table,
HValue* key,
HValue* hash) {
HValue* num_buckets = Add<HLoadNamedField>(
table, static_cast<HValue*>(NULL),
HObjectAccess::ForOrderedHashTableNumberOfBuckets<CollectionType>());
// Some things that won't change inside the loop
HValue* not_found = Add<HConstant>(CollectionType::kNotFound);
HValue* start_index = Add<HConstant>(CollectionType::kHashTableStartIndex);
HValue* entry_size = Add<HConstant>(CollectionType::kEntrySize);
HValue* chain_offset = Add<HConstant>(CollectionType::kChainOffset);
HValue* key_start = AddUncasted<HAdd>(start_index, num_buckets);
key_start->ClearFlag(HValue::kCanOverflow);
// BuildHashToBucket
HValue* mask = AddUncasted<HSub>(num_buckets, graph()->GetConstant1());
mask->ChangeRepresentation(Representation::Integer32());
mask->ClearFlag(HValue::kCanOverflow);
HValue* bucket = AddUncasted<HBitwise>(Token::BIT_AND, hash, mask);
// BuildHashToEntry
HValue* entry_index = AddUncasted<HAdd>(start_index, bucket);
entry_index->ClearFlag(HValue::kCanOverflow);
HValue* entry =
Add<HLoadKeyed>(table, entry_index, static_cast<HValue*>(NULL),
FAST_ELEMENTS, ALLOW_RETURN_HOLE);
entry->set_type(HType::Smi());
Push(entry);
LoopBuilder loop(this);
loop.BeginBody(1);
entry = Pop();
{
IfBuilder if_not_found(this);
if_not_found.If<HCompareNumericAndBranch>(entry, not_found, Token::EQ);
if_not_found.Then();
Push(entry);
loop.Break();
}
// BuildEntryToIndex
HValue* key_index = AddUncasted<HMul>(entry, entry_size);
key_index->ClearFlag(HValue::kCanOverflow);
key_index = AddUncasted<HAdd>(key_index, key_start);
key_index->ClearFlag(HValue::kCanOverflow);
// BuildKeyAt
HValue* candidate_key =
Add<HLoadKeyed>(table, key_index, static_cast<HValue*>(NULL),
FAST_ELEMENTS, ALLOW_RETURN_HOLE);
{
IfBuilder if_keys_equal(this);
if_keys_equal.If<HIsStringAndBranch>(candidate_key);
if_keys_equal.AndIf<HStringCompareAndBranch>(candidate_key, key,
Token::EQ_STRICT);
if_keys_equal.Then();
Push(key_index);
loop.Break();
}
// BuildChainAt
HValue* chain_index = AddUncasted<HMul>(entry, entry_size);
chain_index->ClearFlag(HValue::kCanOverflow);
chain_index = AddUncasted<HAdd>(chain_index, key_start);
chain_index->ClearFlag(HValue::kCanOverflow);
chain_index = AddUncasted<HAdd>(chain_index, chain_offset);
chain_index->ClearFlag(HValue::kCanOverflow);
entry = Add<HLoadKeyed>(table, chain_index, static_cast<HValue*>(NULL),
FAST_ELEMENTS, ALLOW_RETURN_HOLE);
entry->set_type(HType::Smi());
Push(entry);
loop.EndBody();
return Pop();
}
void HOptimizedGraphBuilder::GenerateMapGet(CallRuntime* call) {
DCHECK(call->arguments()->length() == 2);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* key = Pop();
HValue* receiver = Pop();
NoObservableSideEffectsScope no_effects(this);
HIfContinuation continuation;
HValue* hash =
BuildStringHashLoadIfIsStringAndHashComputed(key, &continuation);
{
IfBuilder string_checker(this, &continuation);
string_checker.Then();
{
HValue* table =
Add<HLoadNamedField>(receiver, static_cast<HValue*>(NULL),
HObjectAccess::ForJSCollectionTable());
HValue* key_index =
BuildOrderedHashTableFindEntry<OrderedHashMap>(table, key, hash);
IfBuilder if_found(this);
if_found.If<HCompareNumericAndBranch>(
key_index, Add<HConstant>(OrderedHashMap::kNotFound), Token::NE);
if_found.Then();
{
HValue* value_index = AddUncasted<HAdd>(
key_index, Add<HConstant>(OrderedHashMap::kValueOffset));
value_index->ClearFlag(HValue::kCanOverflow);
Push(Add<HLoadKeyed>(table, value_index, static_cast<HValue*>(NULL),
FAST_ELEMENTS));
}
if_found.Else();
Push(graph()->GetConstantUndefined());
if_found.End();
}
string_checker.Else();
{
Add<HPushArguments>(receiver, key);
Push(Add<HCallRuntime>(call->name(),
Runtime::FunctionForId(Runtime::kMapGet), 2));
}
}
return ast_context()->ReturnValue(Pop());
}
HValue* HOptimizedGraphBuilder::BuildStringHashLoadIfIsStringAndHashComputed(
HValue* object, HIfContinuation* continuation) {
IfBuilder string_checker(this);
string_checker.If<HIsStringAndBranch>(object);
string_checker.And();
HValue* hash = Add<HLoadNamedField>(object, static_cast<HValue*>(NULL),
HObjectAccess::ForStringHashField());
HValue* hash_not_computed_mask = Add<HConstant>(String::kHashNotComputedMask);
HValue* hash_computed_test =
AddUncasted<HBitwise>(Token::BIT_AND, hash, hash_not_computed_mask);
string_checker.If<HCompareNumericAndBranch>(
hash_computed_test, graph()->GetConstant0(), Token::EQ);
string_checker.Then();
HValue* shifted_hash =
AddUncasted<HShr>(hash, Add<HConstant>(String::kHashShift));
string_checker.CaptureContinuation(continuation);
return shifted_hash;
}
template <typename CollectionType>
void HOptimizedGraphBuilder::BuildJSCollectionHas(
CallRuntime* call, const Runtime::Function* c_function) {
DCHECK(call->arguments()->length() == 2);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* key = Pop();
HValue* receiver = Pop();
NoObservableSideEffectsScope no_effects(this);
HIfContinuation continuation;
HValue* hash =
BuildStringHashLoadIfIsStringAndHashComputed(key, &continuation);
{
IfBuilder string_checker(this, &continuation);
string_checker.Then();
{
HValue* table =
Add<HLoadNamedField>(receiver, static_cast<HValue*>(NULL),
HObjectAccess::ForJSCollectionTable());
HValue* key_index =
BuildOrderedHashTableFindEntry<CollectionType>(table, key, hash);
{
IfBuilder if_found(this);
if_found.If<HCompareNumericAndBranch>(
key_index, Add<HConstant>(CollectionType::kNotFound), Token::NE);
if_found.Then();
Push(graph()->GetConstantTrue());
if_found.Else();
Push(graph()->GetConstantFalse());
}
}
string_checker.Else();
{
Add<HPushArguments>(receiver, key);
Push(Add<HCallRuntime>(call->name(), c_function, 2));
}
}
return ast_context()->ReturnValue(Pop());
}
void HOptimizedGraphBuilder::GenerateMapHas(CallRuntime* call) {
BuildJSCollectionHas<OrderedHashMap>(
call, Runtime::FunctionForId(Runtime::kMapHas));
}
void HOptimizedGraphBuilder::GenerateSetHas(CallRuntime* call) {
BuildJSCollectionHas<OrderedHashSet>(
call, Runtime::FunctionForId(Runtime::kSetHas));
}
void HOptimizedGraphBuilder::GenerateSetGetSize(CallRuntime* call) {
DCHECK(call->arguments()->length() == 1);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* receiver = Pop();
HValue* table = Add<HLoadNamedField>(receiver, static_cast<HValue*>(NULL),
HObjectAccess::ForJSCollectionTable());
HInstruction* result = New<HLoadNamedField>(
table, static_cast<HValue*>(NULL),
HObjectAccess::ForOrderedHashTableNumberOfElements<OrderedHashSet>());
return ast_context()->ReturnInstruction(result, call->id());
}
void HOptimizedGraphBuilder::GenerateMapGetSize(CallRuntime* call) {
DCHECK(call->arguments()->length() == 1);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* receiver = Pop();
HValue* table = Add<HLoadNamedField>(receiver, static_cast<HValue*>(NULL),
HObjectAccess::ForJSCollectionTable());
HInstruction* result = New<HLoadNamedField>(
table, static_cast<HValue*>(NULL),
HObjectAccess::ForOrderedHashTableNumberOfElements<OrderedHashMap>());
return ast_context()->ReturnInstruction(result, call->id());
}
void HOptimizedGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
DCHECK(call->arguments()->length() == 1);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
......
......@@ -2417,6 +2417,17 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
ElementsKind fixed_elements_kind,
HValue* byte_length, HValue* length);
template <typename CollectionType>
HValue* BuildOrderedHashTableFindEntry(HValue* table, HValue* key,
HValue* hash);
template <typename CollectionType>
void BuildJSCollectionHas(CallRuntime* call,
const Runtime::Function* c_function);
HValue* BuildStringHashLoadIfIsStringAndHashComputed(
HValue* object, HIfContinuation* continuation);
Handle<JSFunction> array_function() {
return handle(isolate()->native_context()->array_function());
}
......
......@@ -3906,6 +3906,19 @@ class OrderedHashTable: public FixedArray {
static const int kNotFound = -1;
static const int kMinCapacity = 4;
static const int kNumberOfBucketsIndex = 0;
static const int kNumberOfElementsIndex = kNumberOfBucketsIndex + 1;
static const int kNumberOfDeletedElementsIndex = kNumberOfElementsIndex + 1;
static const int kHashTableStartIndex = kNumberOfDeletedElementsIndex + 1;
static const int kNumberOfBucketsOffset =
kHeaderSize + kNumberOfBucketsIndex * kPointerSize;
static const int kNumberOfElementsOffset =
kHeaderSize + kNumberOfElementsIndex * kPointerSize;
static const int kEntrySize = entrysize + 1;
static const int kChainOffset = entrysize;
private:
static Handle<Derived> Rehash(Handle<Derived> table, int new_capacity);
......@@ -3947,17 +3960,9 @@ class OrderedHashTable: public FixedArray {
return set(kRemovedHolesIndex + index, Smi::FromInt(removed_index));
}
static const int kNumberOfBucketsIndex = 0;
static const int kNumberOfElementsIndex = kNumberOfBucketsIndex + 1;
static const int kNumberOfDeletedElementsIndex = kNumberOfElementsIndex + 1;
static const int kHashTableStartIndex = kNumberOfDeletedElementsIndex + 1;
static const int kNextTableIndex = kNumberOfElementsIndex;
static const int kRemovedHolesIndex = kHashTableStartIndex;
static const int kEntrySize = entrysize + 1;
static const int kChainOffset = entrysize;
static const int kLoadFactor = 2;
static const int kMaxCapacity =
(FixedArray::kMaxLength - kHashTableStartIndex)
......@@ -3997,7 +4002,6 @@ class OrderedHashMap:public OrderedHashTable<
return get(EntryToIndex(entry) + kValueOffset);
}
private:
static const int kValueOffset = 1;
};
......
......@@ -304,10 +304,8 @@ namespace internal {
/* Harmony sets */ \
F(SetInitialize, 1, 1) \
F(SetAdd, 2, 1) \
F(SetHas, 2, 1) \
F(SetDelete, 2, 1) \
F(SetClear, 1, 1) \
F(SetGetSize, 1, 1) \
\
F(SetIteratorInitialize, 3, 1) \
F(SetIteratorClone, 1, 1) \
......@@ -316,12 +314,9 @@ namespace internal {
\
/* Harmony maps */ \
F(MapInitialize, 1, 1) \
F(MapGet, 2, 1) \
F(MapHas, 2, 1) \
F(MapDelete, 2, 1) \
F(MapClear, 1, 1) \
F(MapSet, 3, 1) \
F(MapGetSize, 1, 1) \
\
F(MapIteratorInitialize, 3, 1) \
F(MapIteratorClone, 1, 1) \
......@@ -731,7 +726,13 @@ namespace internal {
F(DoubleHi, 1, 1) \
F(DoubleLo, 1, 1) \
F(MathSqrtRT, 1, 1) \
F(MathLogRT, 1, 1)
F(MathLogRT, 1, 1) \
/* ES6 Collections */ \
F(MapGet, 2, 1) \
F(MapGetSize, 1, 1) \
F(MapHas, 2, 1) \
F(SetGetSize, 1, 1) \
F(SetHas, 2, 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