From 909500aa1db9789b68e101045a6359a7fcb30e83 Mon Sep 17 00:00:00 2001 From: adamk Date: Mon, 6 Apr 2015 17:11:48 -0700 Subject: [PATCH] Reimplement Maps and Sets in JS Previously, the only optimized code path for Maps and Sets was for String keys. This was achieved through an implementation of various complex operations in Hydrogen. This approach was neither scalable nor forward-compatible. This patch adds the necessary intrinsics to implement Maps and Sets almost entirely in JS. The added intrinsics are: %_FixedArrayGet %_FixedArraySet %_TheHole %_JSCollectionGetTable %_StringGetRawHashField With these additions, as well as a few changes to what's exposed as runtime functions, most of the C++ code backing Maps and Sets is gone (including both runtime code in objects.cc and Crankshaft in hydrogen.cc). Review URL: https://codereview.chromium.org/947683002 Cr-Commit-Position: refs/heads/master@{#27605} --- src/arm/code-stubs-arm.cc | 10 - src/collection.js | 236 ++++++++++-- src/hydrogen.cc | 512 +------------------------ src/hydrogen.h | 14 +- src/ia32/code-stubs-ia32.cc | 10 - src/macros.py | 27 ++ src/mips/code-stubs-mips.cc | 10 - src/mips64/code-stubs-mips64.cc | 10 - src/objects.cc | 155 +------- src/objects.h | 48 +-- src/ppc/code-stubs-ppc.cc | 10 - src/runtime/runtime-collections.cc | 146 ++++--- src/runtime/runtime.h | 21 +- src/templates.js | 6 +- src/x64/code-stubs-x64.cc | 10 - test/cctest/cctest.gyp | 1 - test/cctest/test-dictionary.cc | 12 - test/cctest/test-ordered-hash-table.cc | 179 --------- 18 files changed, 353 insertions(+), 1064 deletions(-) delete mode 100644 test/cctest/test-ordered-hash-table.cc diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index a111427960..ffe5fe346a 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -1005,16 +1005,6 @@ void CEntryStub::Generate(MacroAssembler* masm) { __ VFPEnsureFPSCRState(r2); - // Runtime functions should not return 'the hole'. Allowing it to escape may - // lead to crashes in the IC code later. - if (FLAG_debug_code) { - Label okay; - __ CompareRoot(r0, Heap::kTheHoleValueRootIndex); - __ b(ne, &okay); - __ stop("The hole escaped"); - __ bind(&okay); - } - // Check result for exception sentinel. Label exception_returned; __ CompareRoot(r0, Heap::kExceptionRootIndex); diff --git a/src/collection.js b/src/collection.js index c8cf639f97..537dbd69cb 100644 --- a/src/collection.js +++ b/src/collection.js @@ -12,6 +12,86 @@ var $Set = global.Set; var $Map = global.Map; +// Used by harmony-templates.js +var $MapGet; +var $MapSet; + + +(function() { + + +function HashToEntry(table, hash, numBuckets) { + var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets); + return ORDERED_HASH_TABLE_BUCKET_AT(table, bucket); +} +%SetInlineBuiltinFlag(HashToEntry); + + +function SetFindEntry(table, numBuckets, key, hash) { + var keyIsNaN = IS_NUMBER(key) && NUMBER_IS_NAN(key); + for (var entry = HashToEntry(table, hash, numBuckets); + entry !== NOT_FOUND; + entry = ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets)) { + var candidate = ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets); + if (key === candidate) { + return entry; + } + if (keyIsNaN && IS_NUMBER(candidate) && NUMBER_IS_NAN(candidate)) { + return entry; + } + } + return NOT_FOUND; +} +%SetInlineBuiltinFlag(SetFindEntry); + + +function MapFindEntry(table, numBuckets, key, hash) { + var keyIsNaN = IS_NUMBER(key) && NUMBER_IS_NAN(key); + for (var entry = HashToEntry(table, hash, numBuckets); + entry !== NOT_FOUND; + entry = ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets)) { + var candidate = ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets); + if (key === candidate) { + return entry; + } + if (keyIsNaN && IS_NUMBER(candidate) && NUMBER_IS_NAN(candidate)) { + return entry; + } + } + return NOT_FOUND; +} +%SetInlineBuiltinFlag(MapFindEntry); + + +function ComputeIntegerHash(key, seed) { + var hash = key; + hash = hash ^ seed; + hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1; + hash = hash ^ (hash >>> 12); + hash = hash + (hash << 2); + hash = hash ^ (hash >>> 4); + hash = (hash * 2057) | 0; // hash = (hash + (hash << 3)) + (hash << 11); + hash = hash ^ (hash >>> 16); + return hash; +} +%SetInlineBuiltinFlag(ComputeIntegerHash); + + +function GetHash(key) { + if (%_IsSmi(key)) { + return ComputeIntegerHash(key, 0); + } + if (IS_STRING(key)) { + var field = %_StringGetRawHashField(key); + if ((field & 1 /* Name::kHashNotComputedMask */) === 0) { + return field >>> 2 /* Name::kHashShift */; + } + } + return %GenericHash(key); +} +%SetInlineBuiltinFlag(GetHash); + + // ------------------------------------------------------------------- // Harmony Set @@ -35,7 +115,7 @@ function SetConstructor(iterable) { } -function SetAddJS(key) { +function SetAdd(key) { if (!IS_SET(this)) { throw MakeTypeError('incompatible_method_receiver', ['Set.prototype.add', this]); @@ -47,34 +127,76 @@ function SetAddJS(key) { if (key === 0) { key = 0; } - return %_SetAdd(this, key); + var table = %_JSCollectionGetTable(this); + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table); + var hash = GetHash(key); + if (SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND) return this; + + var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table); + var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table); + var capacity = numBuckets << 1; + if ((nof + nod) >= capacity) { + // Need to grow, bail out to runtime. + %SetGrow(this); + // Re-load state from the grown backing store. + table = %_JSCollectionGetTable(this); + numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table); + nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table); + nod = ORDERED_HASH_TABLE_DELETED_COUNT(table); + } + var entry = nof + nod; + var index = ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets); + var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets); + var chainEntry = ORDERED_HASH_TABLE_BUCKET_AT(table, bucket); + ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry); + ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof + 1); + FIXED_ARRAY_SET(table, index, key); + FIXED_ARRAY_SET_SMI(table, index + 1, chainEntry); + return this; } -function SetHasJS(key) { +function SetHas(key) { if (!IS_SET(this)) { throw MakeTypeError('incompatible_method_receiver', ['Set.prototype.has', this]); } - return %_SetHas(this, key); + var table = %_JSCollectionGetTable(this); + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table); + var hash = GetHash(key); + return SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND; } -function SetDeleteJS(key) { +function SetDelete(key) { if (!IS_SET(this)) { throw MakeTypeError('incompatible_method_receiver', ['Set.prototype.delete', this]); } - return %_SetDelete(this, key); + var table = %_JSCollectionGetTable(this); + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table); + var hash = GetHash(key); + var entry = SetFindEntry(table, numBuckets, key, hash); + if (entry === NOT_FOUND) return false; + + var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table) - 1; + var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table) + 1; + var index = ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets); + FIXED_ARRAY_SET(table, index, %_TheHole()); + ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof); + ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, nod); + if (nof < (numBuckets >>> 1)) %SetShrink(this); + return true; } -function SetGetSizeJS() { +function SetGetSize() { if (!IS_SET(this)) { throw MakeTypeError('incompatible_method_receiver', ['Set.prototype.size', this]); } - return %_SetGetSize(this); + var table = %_JSCollectionGetTable(this); + return ORDERED_HASH_TABLE_ELEMENT_COUNT(table); } @@ -130,11 +252,11 @@ function SetUpSet() { %FunctionSetLength(SetForEach, 1); // Set up the non-enumerable functions on the Set prototype object. - InstallGetter($Set.prototype, "size", SetGetSizeJS); + InstallGetter($Set.prototype, "size", SetGetSize); InstallFunctions($Set.prototype, DONT_ENUM, $Array( - "add", SetAddJS, - "has", SetHasJS, - "delete", SetDeleteJS, + "add", SetAdd, + "has", SetHas, + "delete", SetDelete, "clear", SetClearJS, "forEach", SetForEach )); @@ -169,16 +291,21 @@ function MapConstructor(iterable) { } -function MapGetJS(key) { +function MapGet(key) { if (!IS_MAP(this)) { throw MakeTypeError('incompatible_method_receiver', ['Map.prototype.get', this]); } - return %_MapGet(this, key); + var table = %_JSCollectionGetTable(this); + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table); + var hash = GetHash(key); + var entry = MapFindEntry(table, numBuckets, key, hash); + if (entry === NOT_FOUND) return UNDEFINED; + return ORDERED_HASH_MAP_VALUE_AT(table, entry, numBuckets); } -function MapSetJS(key, value) { +function MapSet(key, value) { if (!IS_MAP(this)) { throw MakeTypeError('incompatible_method_receiver', ['Map.prototype.set', this]); @@ -190,34 +317,84 @@ function MapSetJS(key, value) { if (key === 0) { key = 0; } - return %_MapSet(this, key, value); + + var table = %_JSCollectionGetTable(this); + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table); + var hash = GetHash(key); + var entry = MapFindEntry(table, numBuckets, key, hash); + if (entry !== NOT_FOUND) { + var existingIndex = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets); + FIXED_ARRAY_SET(table, existingIndex + 1, value); + return this; + } + + var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table); + var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table); + var capacity = numBuckets << 1; + if ((nof + nod) >= capacity) { + // Need to grow, bail out to runtime. + %MapGrow(this); + // Re-load state from the grown backing store. + table = %_JSCollectionGetTable(this); + numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table); + nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table); + nod = ORDERED_HASH_TABLE_DELETED_COUNT(table); + } + entry = nof + nod; + var index = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets); + var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets); + var chainEntry = ORDERED_HASH_TABLE_BUCKET_AT(table, bucket); + ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry); + ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof + 1); + FIXED_ARRAY_SET(table, index, key); + FIXED_ARRAY_SET(table, index + 1, value); + FIXED_ARRAY_SET(table, index + 2, chainEntry); + return this; } -function MapHasJS(key) { +function MapHas(key) { if (!IS_MAP(this)) { throw MakeTypeError('incompatible_method_receiver', ['Map.prototype.has', this]); } - return %_MapHas(this, key); + var table = %_JSCollectionGetTable(this); + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table); + var hash = GetHash(key); + return MapFindEntry(table, numBuckets, key, hash) !== NOT_FOUND; } -function MapDeleteJS(key) { +function MapDelete(key) { if (!IS_MAP(this)) { throw MakeTypeError('incompatible_method_receiver', ['Map.prototype.delete', this]); } - return %_MapDelete(this, key); + var table = %_JSCollectionGetTable(this); + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table); + var hash = GetHash(key); + var entry = MapFindEntry(table, numBuckets, key, hash); + if (entry === NOT_FOUND) return false; + + var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table) - 1; + var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table) + 1; + var index = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets); + FIXED_ARRAY_SET(table, index, %_TheHole()); + FIXED_ARRAY_SET(table, index + 1, %_TheHole()); + ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof); + ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, nod); + if (nof < (numBuckets >>> 1)) %MapShrink(this); + return true; } -function MapGetSizeJS() { +function MapGetSize() { if (!IS_MAP(this)) { throw MakeTypeError('incompatible_method_receiver', ['Map.prototype.size', this]); } - return %_MapGetSize(this); + var table = %_JSCollectionGetTable(this); + return ORDERED_HASH_TABLE_ELEMENT_COUNT(table); } @@ -271,15 +448,20 @@ function SetUpMap() { %FunctionSetLength(MapForEach, 1); // Set up the non-enumerable functions on the Map prototype object. - InstallGetter($Map.prototype, "size", MapGetSizeJS); + InstallGetter($Map.prototype, "size", MapGetSize); InstallFunctions($Map.prototype, DONT_ENUM, $Array( - "get", MapGetJS, - "set", MapSetJS, - "has", MapHasJS, - "delete", MapDeleteJS, + "get", MapGet, + "set", MapSet, + "has", MapHas, + "delete", MapDelete, "clear", MapClearJS, "forEach", MapForEach )); + + $MapGet = MapGet; + $MapSet = MapSet; } SetUpMap(); + +})(); diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 40f0ca4601..06a75b5ca3 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -12055,528 +12055,54 @@ void HOptimizedGraphBuilder::GenerateUnlikely(CallRuntime* call) { } -HValue* HOptimizedGraphBuilder::BuildOrderedHashTableHashToBucket( - HValue* hash, HValue* num_buckets) { - HValue* mask = AddUncasted(num_buckets, graph()->GetConstant1()); - mask->ChangeRepresentation(Representation::Integer32()); - mask->ClearFlag(HValue::kCanOverflow); - return AddUncasted(Token::BIT_AND, hash, mask); -} - - -template -HValue* HOptimizedGraphBuilder::BuildOrderedHashTableHashToEntry( - HValue* table, HValue* hash, HValue* num_buckets) { - HValue* bucket = BuildOrderedHashTableHashToBucket(hash, num_buckets); - HValue* entry_index = AddUncasted( - bucket, Add(CollectionType::kHashTableStartIndex)); - entry_index->ClearFlag(HValue::kCanOverflow); - HValue* entry = Add(table, entry_index, nullptr, FAST_ELEMENTS); - entry->set_type(HType::Smi()); - return entry; -} - - -template -HValue* HOptimizedGraphBuilder::BuildOrderedHashTableEntryToIndex( - HValue* entry, HValue* num_buckets) { - HValue* index = - AddUncasted(entry, Add(CollectionType::kEntrySize)); - index->ClearFlag(HValue::kCanOverflow); - index = AddUncasted(index, num_buckets); - index->ClearFlag(HValue::kCanOverflow); - index = AddUncasted( - index, Add(CollectionType::kHashTableStartIndex)); - index->ClearFlag(HValue::kCanOverflow); - return index; -} - - -template -HValue* HOptimizedGraphBuilder::BuildOrderedHashTableFindEntry(HValue* table, - HValue* key, - HValue* hash) { - HValue* num_buckets = Add( - table, nullptr, - HObjectAccess::ForOrderedHashTableNumberOfBuckets()); - - HValue* entry = BuildOrderedHashTableHashToEntry(table, hash, - num_buckets); - - Push(entry); - - LoopBuilder loop(this); - loop.BeginBody(1); - - entry = Pop(); - - { - IfBuilder if_not_found(this); - if_not_found.If( - entry, Add(CollectionType::kNotFound), Token::EQ); - if_not_found.Then(); - Push(entry); - loop.Break(); - } - - HValue* key_index = - BuildOrderedHashTableEntryToIndex(entry, num_buckets); - HValue* candidate_key = - Add(table, key_index, nullptr, FAST_ELEMENTS); - - { - IfBuilder if_keys_equal(this); - if_keys_equal.If(candidate_key); - if_keys_equal.AndIf(candidate_key, key, - Token::EQ_STRICT); - if_keys_equal.Then(); - Push(key_index); - loop.Break(); - } - - // BuildChainAt - HValue* chain_index = AddUncasted( - key_index, Add(CollectionType::kChainOffset)); - chain_index->ClearFlag(HValue::kCanOverflow); - entry = Add(table, chain_index, nullptr, FAST_ELEMENTS); - entry->set_type(HType::Smi()); - Push(entry); - - loop.EndBody(); - - return Pop(); -} - - -void HOptimizedGraphBuilder::GenerateMapGet(CallRuntime* call) { +void HOptimizedGraphBuilder::GenerateFixedArrayGet(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( - receiver, nullptr, HObjectAccess::ForJSCollectionTable()); - HValue* key_index = - BuildOrderedHashTableFindEntry(table, key, hash); - IfBuilder if_found(this); - if_found.If( - key_index, Add(OrderedHashMap::kNotFound), Token::NE); - if_found.Then(); - { - HValue* value_index = AddUncasted( - key_index, Add(OrderedHashMap::kValueOffset)); - value_index->ClearFlag(HValue::kCanOverflow); - Push(Add(table, value_index, nullptr, FAST_ELEMENTS)); - } - if_found.Else(); - Push(graph()->GetConstantUndefined()); - if_found.End(); - } - string_checker.Else(); - { - Add(receiver, key); - Push(Add(call->name(), - Runtime::FunctionForId(Runtime::kMapGet), 2)); - } - } - - return ast_context()->ReturnValue(Pop()); + HValue* index = Pop(); + HValue* object = Pop(); + HInstruction* result = New( + object, index, nullptr, FAST_HOLEY_ELEMENTS, ALLOW_RETURN_HOLE); + return ast_context()->ReturnInstruction(result, call->id()); } -HValue* HOptimizedGraphBuilder::BuildStringHashLoadIfIsStringAndHashComputed( - HValue* object, HIfContinuation* continuation) { - IfBuilder string_checker(this); - string_checker.If(object); - string_checker.And(); - HValue* hash = Add(object, nullptr, - HObjectAccess::ForStringHashField()); - HValue* hash_not_computed_mask = Add(String::kHashNotComputedMask); - HValue* hash_computed_test = - AddUncasted(Token::BIT_AND, hash, hash_not_computed_mask); - string_checker.If( - hash_computed_test, graph()->GetConstant0(), Token::EQ); - string_checker.Then(); - HValue* shifted_hash = - AddUncasted(hash, Add(String::kHashShift)); - string_checker.CaptureContinuation(continuation); - return shifted_hash; -} - - -template -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( - receiver, nullptr, HObjectAccess::ForJSCollectionTable()); - HValue* key_index = - BuildOrderedHashTableFindEntry(table, key, hash); - { - IfBuilder if_found(this); - if_found.If( - key_index, Add(CollectionType::kNotFound), Token::NE); - if_found.Then(); - Push(graph()->GetConstantTrue()); - if_found.Else(); - Push(graph()->GetConstantFalse()); - } - } - string_checker.Else(); - { - Add(receiver, key); - Push(Add(call->name(), c_function, 2)); - } - } - - return ast_context()->ReturnValue(Pop()); -} - - -void HOptimizedGraphBuilder::GenerateMapHas(CallRuntime* call) { - BuildJSCollectionHas( - call, Runtime::FunctionForId(Runtime::kMapHas)); -} - - -void HOptimizedGraphBuilder::GenerateSetHas(CallRuntime* call) { - BuildJSCollectionHas( - call, Runtime::FunctionForId(Runtime::kSetHas)); -} - - -template -HValue* HOptimizedGraphBuilder::BuildOrderedHashTableAddEntry( - HValue* table, HValue* key, HValue* hash, - HIfContinuation* join_continuation) { - HValue* num_buckets = Add( - table, nullptr, - HObjectAccess::ForOrderedHashTableNumberOfBuckets()); - HValue* capacity = AddUncasted( - num_buckets, Add(CollectionType::kLoadFactor)); - capacity->ClearFlag(HValue::kCanOverflow); - HValue* num_elements = Add( - table, nullptr, - HObjectAccess::ForOrderedHashTableNumberOfElements()); - HValue* num_deleted = Add( - table, nullptr, HObjectAccess::ForOrderedHashTableNumberOfDeletedElements< - CollectionType>()); - HValue* used = AddUncasted(num_elements, num_deleted); - used->ClearFlag(HValue::kCanOverflow); - IfBuilder if_space_available(this); - if_space_available.If(capacity, used, Token::GT); - if_space_available.Then(); - HValue* bucket = BuildOrderedHashTableHashToBucket(hash, num_buckets); - HValue* entry = used; - HValue* key_index = - BuildOrderedHashTableEntryToIndex(entry, num_buckets); - - HValue* bucket_index = AddUncasted( - bucket, Add(CollectionType::kHashTableStartIndex)); - bucket_index->ClearFlag(HValue::kCanOverflow); - HValue* chain_entry = - Add(table, bucket_index, nullptr, FAST_ELEMENTS); - chain_entry->set_type(HType::Smi()); - - HValue* chain_index = AddUncasted( - key_index, Add(CollectionType::kChainOffset)); - chain_index->ClearFlag(HValue::kCanOverflow); - - Add(table, bucket_index, entry, FAST_ELEMENTS); - Add(table, chain_index, chain_entry, FAST_ELEMENTS); - Add(table, key_index, key, FAST_ELEMENTS); - - HValue* new_num_elements = - AddUncasted(num_elements, graph()->GetConstant1()); - new_num_elements->ClearFlag(HValue::kCanOverflow); - Add( - table, - HObjectAccess::ForOrderedHashTableNumberOfElements(), - new_num_elements); - if_space_available.JoinContinuation(join_continuation); - return key_index; -} - - -void HOptimizedGraphBuilder::GenerateMapSet(CallRuntime* call) { +void HOptimizedGraphBuilder::GenerateFixedArraySet(CallRuntime* call) { DCHECK(call->arguments()->length() == 3); CHECK_ALIVE(VisitForValue(call->arguments()->at(0))); CHECK_ALIVE(VisitForValue(call->arguments()->at(1))); CHECK_ALIVE(VisitForValue(call->arguments()->at(2))); HValue* value = Pop(); - HValue* key = Pop(); - HValue* receiver = Pop(); - + HValue* index = Pop(); + HValue* object = Pop(); NoObservableSideEffectsScope no_effects(this); - - HIfContinuation return_or_call_runtime_continuation( - graph()->CreateBasicBlock(), graph()->CreateBasicBlock()); - HIfContinuation got_string_hash; - HValue* hash = - BuildStringHashLoadIfIsStringAndHashComputed(key, &got_string_hash); - IfBuilder string_checker(this, &got_string_hash); - string_checker.Then(); - { - HValue* table = Add(receiver, nullptr, - HObjectAccess::ForJSCollectionTable()); - HValue* key_index = - BuildOrderedHashTableFindEntry(table, key, hash); - { - IfBuilder if_found(this); - if_found.If( - key_index, Add(OrderedHashMap::kNotFound), Token::NE); - if_found.Then(); - { - HValue* value_index = AddUncasted( - key_index, Add(OrderedHashMap::kValueOffset)); - value_index->ClearFlag(HValue::kCanOverflow); - Add(table, value_index, value, FAST_ELEMENTS); - } - if_found.Else(); - { - HIfContinuation did_add(graph()->CreateBasicBlock(), - graph()->CreateBasicBlock()); - HValue* key_index = BuildOrderedHashTableAddEntry( - table, key, hash, &did_add); - IfBuilder if_did_add(this, &did_add); - if_did_add.Then(); - { - HValue* value_index = AddUncasted( - key_index, Add(OrderedHashMap::kValueOffset)); - value_index->ClearFlag(HValue::kCanOverflow); - Add(table, value_index, value, FAST_ELEMENTS); - } - if_did_add.JoinContinuation(&return_or_call_runtime_continuation); - } - } - } - string_checker.JoinContinuation(&return_or_call_runtime_continuation); - - { - IfBuilder return_or_call_runtime(this, - &return_or_call_runtime_continuation); - return_or_call_runtime.Then(); - Push(receiver); - return_or_call_runtime.Else(); - Add(receiver, key, value); - Push(Add(call->name(), - Runtime::FunctionForId(Runtime::kMapSet), 3)); - } - - return ast_context()->ReturnValue(Pop()); + Add(object, index, value, FAST_HOLEY_ELEMENTS); + return ast_context()->ReturnValue(graph()->GetConstantUndefined()); } -void HOptimizedGraphBuilder::GenerateSetAdd(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 return_or_call_runtime_continuation( - graph()->CreateBasicBlock(), graph()->CreateBasicBlock()); - HIfContinuation got_string_hash; - HValue* hash = - BuildStringHashLoadIfIsStringAndHashComputed(key, &got_string_hash); - IfBuilder string_checker(this, &got_string_hash); - string_checker.Then(); - { - HValue* table = Add(receiver, nullptr, - HObjectAccess::ForJSCollectionTable()); - HValue* key_index = - BuildOrderedHashTableFindEntry(table, key, hash); - { - IfBuilder if_not_found(this); - if_not_found.If( - key_index, Add(OrderedHashSet::kNotFound), Token::EQ); - if_not_found.Then(); - BuildOrderedHashTableAddEntry( - table, key, hash, &return_or_call_runtime_continuation); - } - } - string_checker.JoinContinuation(&return_or_call_runtime_continuation); - - { - IfBuilder return_or_call_runtime(this, - &return_or_call_runtime_continuation); - return_or_call_runtime.Then(); - Push(receiver); - return_or_call_runtime.Else(); - Add(receiver, key); - Push(Add(call->name(), - Runtime::FunctionForId(Runtime::kSetAdd), 2)); - } - - return ast_context()->ReturnValue(Pop()); +void HOptimizedGraphBuilder::GenerateTheHole(CallRuntime* call) { + DCHECK(call->arguments()->length() == 0); + return ast_context()->ReturnValue(graph()->GetConstantHole()); } -template -void HOptimizedGraphBuilder::BuildJSCollectionDelete( - 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 return_or_call_runtime_continuation( - graph()->CreateBasicBlock(), graph()->CreateBasicBlock()); - HIfContinuation got_string_hash; - HValue* hash = - BuildStringHashLoadIfIsStringAndHashComputed(key, &got_string_hash); - IfBuilder string_checker(this, &got_string_hash); - string_checker.Then(); - { - HValue* table = Add(receiver, nullptr, - HObjectAccess::ForJSCollectionTable()); - HValue* key_index = - BuildOrderedHashTableFindEntry(table, key, hash); - { - IfBuilder if_found(this); - if_found.If( - key_index, Add(CollectionType::kNotFound), Token::NE); - if_found.Then(); - { - // If we're removing an element, we might need to shrink. - // If we do need to shrink, we'll be bailing out to the runtime. - HValue* num_elements = Add( - table, nullptr, HObjectAccess::ForOrderedHashTableNumberOfElements< - CollectionType>()); - num_elements = AddUncasted(num_elements, graph()->GetConstant1()); - num_elements->ClearFlag(HValue::kCanOverflow); - - HValue* num_buckets = Add( - table, nullptr, HObjectAccess::ForOrderedHashTableNumberOfBuckets< - CollectionType>()); - // threshold is capacity >> 2; we simplify this to num_buckets >> 1 - // since kLoadFactor is 2. - STATIC_ASSERT(CollectionType::kLoadFactor == 2); - HValue* threshold = - AddUncasted(num_buckets, graph()->GetConstant1()); - - IfBuilder if_need_not_shrink(this); - if_need_not_shrink.If(num_elements, threshold, - Token::GTE); - if_need_not_shrink.Then(); - { - Add(table, key_index, graph()->GetConstantHole(), - FAST_ELEMENTS); - - // For maps, also need to clear the value. - if (CollectionType::kChainOffset > 1) { - HValue* value_index = - AddUncasted(key_index, graph()->GetConstant1()); - value_index->ClearFlag(HValue::kCanOverflow); - Add(table, value_index, graph()->GetConstantHole(), - FAST_ELEMENTS); - } - STATIC_ASSERT(CollectionType::kChainOffset <= 2); - - HValue* num_deleted = Add( - table, nullptr, - HObjectAccess::ForOrderedHashTableNumberOfDeletedElements< - CollectionType>()); - num_deleted = AddUncasted(num_deleted, graph()->GetConstant1()); - num_deleted->ClearFlag(HValue::kCanOverflow); - Add( - table, HObjectAccess::ForOrderedHashTableNumberOfElements< - CollectionType>(), - num_elements); - Add( - table, HObjectAccess::ForOrderedHashTableNumberOfDeletedElements< - CollectionType>(), - num_deleted); - Push(graph()->GetConstantTrue()); - } - if_need_not_shrink.JoinContinuation( - &return_or_call_runtime_continuation); - } - if_found.Else(); - { - // Not found, so we're done. - Push(graph()->GetConstantFalse()); - } - } - } - string_checker.JoinContinuation(&return_or_call_runtime_continuation); - - { - IfBuilder return_or_call_runtime(this, - &return_or_call_runtime_continuation); - return_or_call_runtime.Then(); - return_or_call_runtime.Else(); - Add(receiver, key); - Push(Add(call->name(), c_function, 2)); - } - - return ast_context()->ReturnValue(Pop()); -} - - -void HOptimizedGraphBuilder::GenerateMapDelete(CallRuntime* call) { - BuildJSCollectionDelete( - call, Runtime::FunctionForId(Runtime::kMapDelete)); -} - - -void HOptimizedGraphBuilder::GenerateSetDelete(CallRuntime* call) { - BuildJSCollectionDelete( - call, Runtime::FunctionForId(Runtime::kSetDelete)); -} - - -void HOptimizedGraphBuilder::GenerateSetGetSize(CallRuntime* call) { +void HOptimizedGraphBuilder::GenerateJSCollectionGetTable(CallRuntime* call) { DCHECK(call->arguments()->length() == 1); CHECK_ALIVE(VisitForValue(call->arguments()->at(0))); HValue* receiver = Pop(); - HValue* table = Add(receiver, nullptr, - HObjectAccess::ForJSCollectionTable()); HInstruction* result = New( - table, nullptr, - HObjectAccess::ForOrderedHashTableNumberOfElements()); + receiver, nullptr, HObjectAccess::ForJSCollectionTable()); return ast_context()->ReturnInstruction(result, call->id()); } -void HOptimizedGraphBuilder::GenerateMapGetSize(CallRuntime* call) { +void HOptimizedGraphBuilder::GenerateStringGetRawHashField(CallRuntime* call) { DCHECK(call->arguments()->length() == 1); CHECK_ALIVE(VisitForValue(call->arguments()->at(0))); - HValue* receiver = Pop(); - HValue* table = Add(receiver, nullptr, - HObjectAccess::ForJSCollectionTable()); + HValue* object = Pop(); HInstruction* result = New( - table, nullptr, - HObjectAccess::ForOrderedHashTableNumberOfElements()); + object, nullptr, HObjectAccess::ForStringHashField()); return ast_context()->ReturnInstruction(result, call->id()); } diff --git a/src/hydrogen.h b/src/hydrogen.h index f4c1439128..c70ea2d96c 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -2222,18 +2222,14 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { F(MathLogRT) \ /* ES6 Collections */ \ F(MapClear) \ - F(MapDelete) \ - F(MapGet) \ - F(MapGetSize) \ - F(MapHas) \ F(MapInitialize) \ - F(MapSet) \ - F(SetAdd) \ F(SetClear) \ - F(SetDelete) \ - F(SetGetSize) \ - F(SetHas) \ F(SetInitialize) \ + F(FixedArrayGet) \ + F(FixedArraySet) \ + F(JSCollectionGetTable) \ + F(StringGetRawHashField) \ + F(TheHole) \ /* Arrays */ \ F(HasFastPackedElements) \ F(GetPrototype) \ diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index c640a29c10..30852adfa9 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -2494,16 +2494,6 @@ void CEntryStub::Generate(MacroAssembler* masm) { __ call(ebx); // Result is in eax or edx:eax - do not destroy these registers! - // Runtime functions should not return 'the hole'. Allowing it to escape may - // lead to crashes in the IC code later. - if (FLAG_debug_code) { - Label okay; - __ cmp(eax, isolate()->factory()->the_hole_value()); - __ j(not_equal, &okay, Label::kNear); - __ int3(); - __ bind(&okay); - } - // Check result for exception sentinel. Label exception_returned; __ cmp(eax, isolate()->factory()->exception()); diff --git a/src/macros.py b/src/macros.py index 324702bb3a..2dab3b2bae 100644 --- a/src/macros.py +++ b/src/macros.py @@ -276,6 +276,33 @@ const ITERATOR_KIND_KEYS = 1; const ITERATOR_KIND_VALUES = 2; const ITERATOR_KIND_ENTRIES = 3; +macro FIXED_ARRAY_GET(array, index) = (%_FixedArrayGet(array, (index) | 0)); +macro FIXED_ARRAY_SET(array, index, value) = (%_FixedArraySet(array, (index) | 0, value)); +# TODO(adamk): Find a more robust way to force Smi representation. +macro FIXED_ARRAY_SET_SMI(array, index, value) = (FIXED_ARRAY_SET(array, index, (value) | 0)); + +macro ORDERED_HASH_TABLE_BUCKET_COUNT(table) = (FIXED_ARRAY_GET(table, 0)); +macro ORDERED_HASH_TABLE_ELEMENT_COUNT(table) = (FIXED_ARRAY_GET(table, 1)); +macro ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, count) = (FIXED_ARRAY_SET_SMI(table, 1, count)); +macro ORDERED_HASH_TABLE_DELETED_COUNT(table) = (FIXED_ARRAY_GET(table, 2)); +macro ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, count) = (FIXED_ARRAY_SET_SMI(table, 2, count)); +macro ORDERED_HASH_TABLE_BUCKET_AT(table, bucket) = (FIXED_ARRAY_GET(table, 3 + (bucket))); +macro ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry) = (FIXED_ARRAY_SET(table, 3 + (bucket), entry)); + +macro ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets) = (hash & ((numBuckets) - 1)); + +macro ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets) = (3 + (numBuckets) + ((entry) << 1)); +macro ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets))); +macro ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets) + 1)); + +macro ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets) = (3 + (numBuckets) + ((entry) * 3)); +macro ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets))); +macro ORDERED_HASH_MAP_VALUE_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets) + 1)); +macro ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets) + 2)); + +# Must match OrderedHashTable::kNotFound. +const NOT_FOUND = -1; + # Check whether debug is active. const DEBUG_IS_ACTIVE = (%_DebugIsActive() != 0); macro DEBUG_IS_STEPPING(function) = (%_DebugIsActive() != 0 && %DebugCallbackSupportsStepping(function)); diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc index f0314e4d84..c7a040ebf9 100644 --- a/src/mips/code-stubs-mips.cc +++ b/src/mips/code-stubs-mips.cc @@ -1108,16 +1108,6 @@ void CEntryStub::Generate(MacroAssembler* masm) { } - // Runtime functions should not return 'the hole'. Allowing it to escape may - // lead to crashes in the IC code later. - if (FLAG_debug_code) { - Label okay; - __ LoadRoot(t0, Heap::kTheHoleValueRootIndex); - __ Branch(&okay, ne, v0, Operand(t0)); - __ stop("The hole escaped"); - __ bind(&okay); - } - // Check result for exception sentinel. Label exception_returned; __ LoadRoot(t0, Heap::kExceptionRootIndex); diff --git a/src/mips64/code-stubs-mips64.cc b/src/mips64/code-stubs-mips64.cc index ae99442316..34a61ff037 100644 --- a/src/mips64/code-stubs-mips64.cc +++ b/src/mips64/code-stubs-mips64.cc @@ -1103,16 +1103,6 @@ void CEntryStub::Generate(MacroAssembler* masm) { masm->InstructionsGeneratedSince(&find_ra)); } - // Runtime functions should not return 'the hole'. Allowing it to escape may - // lead to crashes in the IC code later. - if (FLAG_debug_code) { - Label okay; - __ LoadRoot(a4, Heap::kTheHoleValueRootIndex); - __ Branch(&okay, ne, v0, Operand(a4)); - __ stop("The hole escaped"); - __ bind(&okay); - } - // Check result for exception sentinel. Label exception_returned; __ LoadRoot(a4, Heap::kExceptionRootIndex); diff --git a/src/objects.cc b/src/objects.cc index f3fad4d0c5..891aaac995 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -807,17 +807,19 @@ Map* Object::GetRootMap(Isolate* isolate) { Object* Object::GetHash() { - // The object is either a number, a name, an odd-ball, + // The object is either a Smi, a HeapNumber, a name, an odd-ball, // a real JS object, or a Harmony proxy. if (IsSmi()) { - int num = Smi::cast(this)->value(); - uint32_t hash = ComputeLongHash(double_to_uint64(static_cast(num))); + uint32_t hash = ComputeIntegerHash(Smi::cast(this)->value(), kZeroHashSeed); return Smi::FromInt(hash & Smi::kMaxValue); } if (IsHeapNumber()) { double num = HeapNumber::cast(this)->value(); if (std::isnan(num)) return Smi::FromInt(Smi::kMaxValue); if (i::IsMinusZero(num)) num = 0; + if (IsSmiDouble(num)) { + return Smi::FromInt(FastD2I(num))->GetHash(); + } uint32_t hash = ComputeLongHash(double_to_uint64(num)); return Smi::FromInt(hash & Smi::kMaxValue); } @@ -16257,20 +16259,6 @@ Handle OrderedHashTable::Clear( } -template -Handle OrderedHashTable::Remove( - Handle table, Handle key, bool* was_present) { - int entry = table->FindEntry(key); - if (entry == kNotFound) { - *was_present = false; - return table; - } - *was_present = true; - table->RemoveEntry(entry); - return Shrink(table); -} - - template Handle OrderedHashTable::Rehash( Handle table, int new_capacity) { @@ -16316,61 +16304,6 @@ Handle OrderedHashTable::Rehash( } -template -int OrderedHashTable::FindEntry( - Handle key, int hash) { - DCHECK(!IsObsolete()); - - DisallowHeapAllocation no_gc; - DCHECK(!key->IsTheHole()); - for (int entry = HashToEntry(hash); entry != kNotFound; - entry = ChainAt(entry)) { - Object* candidate = KeyAt(entry); - if (candidate->SameValueZero(*key)) - return entry; - } - return kNotFound; -} - - -template -int OrderedHashTable::FindEntry( - Handle key) { - DisallowHeapAllocation no_gc; - Object* hash = key->GetHash(); - if (!hash->IsSmi()) return kNotFound; - return FindEntry(key, Smi::cast(hash)->value()); -} - - -template -int OrderedHashTable::AddEntry(int hash) { - DCHECK(!IsObsolete()); - - int entry = UsedCapacity(); - int bucket = HashToBucket(hash); - int index = EntryToIndex(entry); - Object* chain_entry = get(kHashTableStartIndex + bucket); - set(kHashTableStartIndex + bucket, Smi::FromInt(entry)); - set(index + kChainOffset, chain_entry); - SetNumberOfElements(NumberOfElements() + 1); - return index; -} - - -template -void OrderedHashTable::RemoveEntry(int entry) { - DCHECK(!IsObsolete()); - - int index = EntryToIndex(entry); - for (int i = 0; i < entrysize; ++i) { - set_the_hole(index + i); - } - SetNumberOfElements(NumberOfElements() - 1); - SetNumberOfDeletedElements(NumberOfDeletedElements() + 1); -} - - template Handle OrderedHashTable::Allocate( Isolate* isolate, int capacity, PretenureFlag pretenure); @@ -16387,21 +16320,6 @@ template Handle OrderedHashTable::Clear( Handle table); -template Handle -OrderedHashTable::Remove( - Handle table, Handle key, bool* was_present); - -template int OrderedHashTable::FindEntry( - Handle key, int hash); -template int OrderedHashTable::FindEntry( - Handle key); - -template int -OrderedHashTable::AddEntry(int hash); - -template void -OrderedHashTable::RemoveEntry(int entry); - template Handle OrderedHashTable::Allocate( @@ -16419,69 +16337,6 @@ template Handle OrderedHashTable::Clear( Handle table); -template Handle -OrderedHashTable::Remove( - Handle table, Handle key, bool* was_present); - -template int OrderedHashTable::FindEntry( - Handle key, int hash); -template int OrderedHashTable::FindEntry( - Handle key); - -template int -OrderedHashTable::AddEntry(int hash); - -template void -OrderedHashTable::RemoveEntry(int entry); - - -bool OrderedHashSet::Contains(Handle key) { - return FindEntry(key) != kNotFound; -} - - -Handle OrderedHashSet::Add(Handle table, - Handle key) { - int hash = GetOrCreateHash(table->GetIsolate(), key)->value(); - if (table->FindEntry(key, hash) != kNotFound) return table; - - table = EnsureGrowable(table); - - int index = table->AddEntry(hash); - table->set(index, *key); - return table; -} - - -Object* OrderedHashMap::Lookup(Handle key) { - DisallowHeapAllocation no_gc; - int entry = FindEntry(key); - if (entry == kNotFound) return GetHeap()->the_hole_value(); - return ValueAt(entry); -} - - -Handle OrderedHashMap::Put(Handle table, - Handle key, - Handle value) { - DCHECK(!key->IsTheHole()); - - int hash = GetOrCreateHash(table->GetIsolate(), key)->value(); - int entry = table->FindEntry(key, hash); - - if (entry != kNotFound) { - table->set(table->EntryToIndex(entry) + kValueOffset, *value); - return table; - } - - table = EnsureGrowable(table); - - int index = table->AddEntry(hash); - table->set(index, *key); - table->set(index + kValueOffset, *value); - return table; -} - template void OrderedHashTableIterator::Transition() { diff --git a/src/objects.h b/src/objects.h index 5faf62b6ca..49ad48c673 100644 --- a/src/objects.h +++ b/src/objects.h @@ -3974,17 +3974,6 @@ class OrderedHashTable: public FixedArray { // exisiting iterators can be updated. static Handle Clear(Handle table); - // Returns an OrderedHashTable (possibly |table|) where |key| has been - // removed. - static Handle Remove(Handle table, Handle key, - bool* was_present); - - // Returns kNotFound if the key isn't present. - int FindEntry(Handle key, int hash); - - // Like the above, but doesn't require the caller to provide a hash. - int FindEntry(Handle key); - int NumberOfElements() { return Smi::cast(get(kNumberOfElementsIndex))->value(); } @@ -3999,15 +3988,6 @@ class OrderedHashTable: public FixedArray { return Smi::cast(get(kNumberOfBucketsIndex))->value(); } - // Returns the index into the data table where the new entry - // should be placed. The table is assumed to have enough space - // for a new entry. - int AddEntry(int hash); - - // Removes the entry, and puts the_hole in entrysize pointers - // (leaving the hash table chain intact). - void RemoveEntry(int entry); - // Returns an index into |this| for the given entry. int EntryToIndex(int entry) { return kHashTableStartIndex + NumberOfBuckets() + (entry * kEntrySize); @@ -4078,20 +4058,6 @@ class OrderedHashTable: public FixedArray { return NumberOfBuckets() * kLoadFactor; } - // Returns the next entry for the given entry. - int ChainAt(int entry) { - return Smi::cast(get(EntryToIndex(entry) + kChainOffset))->value(); - } - - int HashToBucket(int hash) { - return hash & (NumberOfBuckets() - 1); - } - - int HashToEntry(int hash) { - int bucket = HashToBucket(hash); - return Smi::cast(get(kHashTableStartIndex + bucket))->value(); - } - void SetNextTable(Derived* next_table) { set(kNextTableIndex, next_table); } @@ -4115,27 +4081,17 @@ class OrderedHashSet: public OrderedHashTable< OrderedHashSet, JSSetIterator, 1> { public: DECLARE_CAST(OrderedHashSet) - - bool Contains(Handle key); - static Handle Add( - Handle table, Handle key); }; class JSMapIterator; -class OrderedHashMap:public OrderedHashTable< - OrderedHashMap, JSMapIterator, 2> { +class OrderedHashMap + : public OrderedHashTable { public: DECLARE_CAST(OrderedHashMap) - Object* Lookup(Handle key); - static Handle Put( - Handle table, - Handle key, - Handle value); - Object* ValueAt(int entry) { return get(EntryToIndex(entry) + kValueOffset); } diff --git a/src/ppc/code-stubs-ppc.cc b/src/ppc/code-stubs-ppc.cc index 6c958668ef..a50c39c108 100644 --- a/src/ppc/code-stubs-ppc.cc +++ b/src/ppc/code-stubs-ppc.cc @@ -1086,16 +1086,6 @@ void CEntryStub::Generate(MacroAssembler* masm) { } #endif - // Runtime functions should not return 'the hole'. Allowing it to escape may - // lead to crashes in the IC code later. - if (FLAG_debug_code) { - Label okay; - __ CompareRoot(r3, Heap::kTheHoleValueRootIndex); - __ bne(&okay); - __ stop("The hole escaped"); - __ bind(&okay); - } - // Check result for exception sentinel. Label exception_returned; __ CompareRoot(r3, Heap::kExceptionRootIndex); diff --git a/src/runtime/runtime-collections.cc b/src/runtime/runtime-collections.cc index ffffbdd2f2..bacd1c606f 100644 --- a/src/runtime/runtime-collections.cc +++ b/src/runtime/runtime-collections.cc @@ -11,6 +11,60 @@ namespace v8 { namespace internal { + +RUNTIME_FUNCTION(Runtime_StringGetRawHashField) { + HandleScope scope(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(String, string, 0); + return *isolate->factory()->NewNumberFromUint(string->hash_field()); +} + + +RUNTIME_FUNCTION(Runtime_TheHole) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 0); + return isolate->heap()->the_hole_value(); +} + + +RUNTIME_FUNCTION(Runtime_FixedArrayGet) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 2); + CONVERT_ARG_CHECKED(FixedArray, object, 0); + CONVERT_SMI_ARG_CHECKED(index, 1); + return object->get(index); +} + + +RUNTIME_FUNCTION(Runtime_FixedArraySet) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 3); + CONVERT_ARG_CHECKED(FixedArray, object, 0); + CONVERT_SMI_ARG_CHECKED(index, 1); + CONVERT_ARG_CHECKED(Object, value, 2); + object->set(index, value); + return isolate->heap()->undefined_value(); +} + + +RUNTIME_FUNCTION(Runtime_JSCollectionGetTable) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_CHECKED(JSObject, object, 0); + RUNTIME_ASSERT(object->IsJSSet() || object->IsJSMap()); + return static_cast(object)->table(); +} + + +RUNTIME_FUNCTION(Runtime_GenericHash) { + HandleScope scope(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(Object, object, 0); + Handle hash = Object::GetOrCreateHash(isolate, object); + return *hash; +} + + RUNTIME_FUNCTION(Runtime_SetInitialize) { HandleScope scope(isolate); DCHECK(args.length() == 1); @@ -21,38 +75,25 @@ RUNTIME_FUNCTION(Runtime_SetInitialize) { } -RUNTIME_FUNCTION(Runtime_SetAdd) { +RUNTIME_FUNCTION(Runtime_SetGrow) { HandleScope scope(isolate); - DCHECK(args.length() == 2); + DCHECK(args.length() == 1); CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); Handle table(OrderedHashSet::cast(holder->table())); - table = OrderedHashSet::Add(table, key); + table = OrderedHashSet::EnsureGrowable(table); holder->set_table(*table); - return *holder; + return isolate->heap()->undefined_value(); } -RUNTIME_FUNCTION(Runtime_SetHas) { +RUNTIME_FUNCTION(Runtime_SetShrink) { HandleScope scope(isolate); - DCHECK(args.length() == 2); + DCHECK(args.length() == 1); CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); Handle table(OrderedHashSet::cast(holder->table())); - return isolate->heap()->ToBoolean(table->Contains(key)); -} - - -RUNTIME_FUNCTION(Runtime_SetDelete) { - HandleScope scope(isolate); - DCHECK(args.length() == 2); - CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); - Handle table(OrderedHashSet::cast(holder->table())); - bool was_present = false; - table = OrderedHashSet::Remove(table, key, &was_present); + table = OrderedHashSet::Shrink(table); holder->set_table(*table); - return isolate->heap()->ToBoolean(was_present); + return isolate->heap()->undefined_value(); } @@ -67,15 +108,6 @@ RUNTIME_FUNCTION(Runtime_SetClear) { } -RUNTIME_FUNCTION(Runtime_SetGetSize) { - HandleScope scope(isolate); - DCHECK(args.length() == 1); - CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); - Handle table(OrderedHashSet::cast(holder->table())); - return Smi::FromInt(table->NumberOfElements()); -} - - RUNTIME_FUNCTION(Runtime_SetIteratorInitialize) { HandleScope scope(isolate); DCHECK(args.length() == 3); @@ -141,39 +173,14 @@ RUNTIME_FUNCTION(Runtime_MapInitialize) { } -RUNTIME_FUNCTION(Runtime_MapGet) { +RUNTIME_FUNCTION(Runtime_MapShrink) { HandleScope scope(isolate); - DCHECK(args.length() == 2); + DCHECK(args.length() == 1); CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); Handle table(OrderedHashMap::cast(holder->table())); - Handle lookup(table->Lookup(key), isolate); - return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup; -} - - -RUNTIME_FUNCTION(Runtime_MapHas) { - HandleScope scope(isolate); - DCHECK(args.length() == 2); - CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); - Handle table(OrderedHashMap::cast(holder->table())); - Handle lookup(table->Lookup(key), isolate); - return isolate->heap()->ToBoolean(!lookup->IsTheHole()); -} - - -RUNTIME_FUNCTION(Runtime_MapDelete) { - HandleScope scope(isolate); - DCHECK(args.length() == 2); - CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); - Handle table(OrderedHashMap::cast(holder->table())); - bool was_present = false; - Handle new_table = - OrderedHashMap::Remove(table, key, &was_present); - holder->set_table(*new_table); - return isolate->heap()->ToBoolean(was_present); + table = OrderedHashMap::Shrink(table); + holder->set_table(*table); + return isolate->heap()->undefined_value(); } @@ -188,25 +195,14 @@ RUNTIME_FUNCTION(Runtime_MapClear) { } -RUNTIME_FUNCTION(Runtime_MapSet) { - HandleScope scope(isolate); - DCHECK(args.length() == 3); - CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); - CONVERT_ARG_HANDLE_CHECKED(Object, value, 2); - Handle table(OrderedHashMap::cast(holder->table())); - Handle new_table = OrderedHashMap::Put(table, key, value); - holder->set_table(*new_table); - return *holder; -} - - -RUNTIME_FUNCTION(Runtime_MapGetSize) { +RUNTIME_FUNCTION(Runtime_MapGrow) { HandleScope scope(isolate); DCHECK(args.length() == 1); CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0); Handle table(OrderedHashMap::cast(holder->table())); - return Smi::FromInt(table->NumberOfElements()); + table = OrderedHashMap::EnsureGrowable(table); + holder->set_table(*table); + return isolate->heap()->undefined_value(); } diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 4fb487037c..e9e4879328 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -301,6 +301,13 @@ namespace internal { F(GetConstructTrap, 1, 1) \ F(Fix, 1, 1) \ \ + /* ES6 collections */ \ + F(MapGrow, 1, 1) \ + F(MapShrink, 1, 1) \ + F(SetGrow, 1, 1) \ + F(SetShrink, 1, 1) \ + F(GenericHash, 1, 1) \ + \ /* ES6 collection iterators */ \ F(SetIteratorInitialize, 3, 1) \ F(SetIteratorClone, 1, 1) \ @@ -705,18 +712,14 @@ namespace internal { F(MathLogRT, 1, 1) \ /* ES6 Collections */ \ F(MapClear, 1, 1) \ - F(MapDelete, 2, 1) \ - F(MapGet, 2, 1) \ - F(MapGetSize, 1, 1) \ - F(MapHas, 2, 1) \ F(MapInitialize, 1, 1) \ - F(MapSet, 3, 1) \ - F(SetAdd, 2, 1) \ F(SetClear, 1, 1) \ - F(SetDelete, 2, 1) \ - F(SetGetSize, 1, 1) \ - F(SetHas, 2, 1) \ F(SetInitialize, 1, 1) \ + F(FixedArrayGet, 2, 1) \ + F(FixedArraySet, 3, 1) \ + F(JSCollectionGetTable, 1, 1) \ + F(StringGetRawHashField, 1, 1) \ + F(TheHole, 0, 1) \ /* Arrays */ \ F(HasFastPackedElements, 1, 1) \ F(GetPrototype, 1, 1) \ diff --git a/src/templates.js b/src/templates.js index 20f8af5f68..feb5b0645b 100644 --- a/src/templates.js +++ b/src/templates.js @@ -21,7 +21,7 @@ function SameCallSiteElements(rawStrings, other) { function GetCachedCallSite(siteObj, hash) { - var obj = %MapGet(callSiteCache, hash); + var obj = %_CallFunction(callSiteCache, hash, $MapGet); if (IS_UNDEFINED(obj)) return; @@ -33,13 +33,13 @@ function GetCachedCallSite(siteObj, hash) { function SetCachedCallSite(siteObj, hash) { - var obj = %MapGet(callSiteCache, hash); + var obj = %_CallFunction(callSiteCache, hash, $MapGet); var array; if (IS_UNDEFINED(obj)) { array = new InternalArray(1); array[0] = siteObj; - %MapSet(callSiteCache, hash, array); + %_CallFunction(callSiteCache, hash, array, $MapSet); } else { obj.push(siteObj); } diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 49904297fe..0ca316e536 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -2415,16 +2415,6 @@ void CEntryStub::Generate(MacroAssembler* masm) { } #endif // _WIN64 - // Runtime functions should not return 'the hole'. Allowing it to escape may - // lead to crashes in the IC code later. - if (FLAG_debug_code) { - Label okay; - __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); - __ j(not_equal, &okay, Label::kNear); - __ int3(); - __ bind(&okay); - } - // Check result for exception sentinel. Label exception_returned; __ CompareRoot(rax, Heap::kExceptionRootIndex); diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index 9a1e572230..1603275987 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -141,7 +141,6 @@ 'test-mementos.cc', 'test-migrations.cc', 'test-object-observe.cc', - 'test-ordered-hash-table.cc', 'test-parsing.cc', 'test-platform.cc', 'test-profile-generator.cc', diff --git a/test/cctest/test-dictionary.cc b/test/cctest/test-dictionary.cc index 14e5d69d43..4f041ba6f5 100644 --- a/test/cctest/test-dictionary.cc +++ b/test/cctest/test-dictionary.cc @@ -110,7 +110,6 @@ TEST(HashMap) { v8::HandleScope scope(context->GetIsolate()); Isolate* isolate = CcTest::i_isolate(); TestHashMap(ObjectHashTable::New(isolate, 23)); - TestHashMap(isolate->factory()->NewOrderedHashMap()); } @@ -199,15 +198,6 @@ static void TestHashSetCausesGC(Handle table) { table = HashSet::Add(table, key); CHECK(gc_count < isolate->heap()->gc_count()); } - - -TEST(ObjectHashSetCausesGC) { - i::FLAG_stress_compaction = false; - LocalContext context; - v8::HandleScope scope(context->GetIsolate()); - Isolate* isolate = CcTest::i_isolate(); - TestHashSetCausesGC(isolate->factory()->NewOrderedHashSet()); -} #endif @@ -246,9 +236,7 @@ TEST(ObjectHashTableCausesGC) { v8::HandleScope scope(context->GetIsolate()); Isolate* isolate = CcTest::i_isolate(); TestHashMapCausesGC(ObjectHashTable::New(isolate, 1)); - TestHashMapCausesGC(isolate->factory()->NewOrderedHashMap()); } #endif - } diff --git a/test/cctest/test-ordered-hash-table.cc b/test/cctest/test-ordered-hash-table.cc deleted file mode 100644 index 9578936317..0000000000 --- a/test/cctest/test-ordered-hash-table.cc +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2014 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include - -#include "src/v8.h" - -#include "src/factory.h" -#include "test/cctest/cctest.h" - -namespace { - -using namespace v8::internal; - - -TEST(Set) { - LocalContext context; - Isolate* isolate = CcTest::i_isolate(); - Factory* factory = isolate->factory(); - HandleScope scope(isolate); - Handle ordered_set = factory->NewOrderedHashSet(); - CHECK_EQ(2, ordered_set->NumberOfBuckets()); - CHECK_EQ(0, ordered_set->NumberOfElements()); - CHECK_EQ(0, ordered_set->NumberOfDeletedElements()); - - Handle map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); - Handle obj = factory->NewJSObjectFromMap(map); - CHECK(!ordered_set->Contains(obj)); - ordered_set = OrderedHashSet::Add(ordered_set, obj); - CHECK_EQ(1, ordered_set->NumberOfElements()); - CHECK(ordered_set->Contains(obj)); - bool was_present = false; - ordered_set = OrderedHashSet::Remove(ordered_set, obj, &was_present); - CHECK(was_present); - CHECK_EQ(0, ordered_set->NumberOfElements()); - CHECK(!ordered_set->Contains(obj)); - - // Removing a not-present object should set was_present to false. - ordered_set = OrderedHashSet::Remove(ordered_set, obj, &was_present); - CHECK(!was_present); - - // Test for collisions/chaining - Handle obj1 = factory->NewJSObjectFromMap(map); - ordered_set = OrderedHashSet::Add(ordered_set, obj1); - Handle obj2 = factory->NewJSObjectFromMap(map); - ordered_set = OrderedHashSet::Add(ordered_set, obj2); - Handle obj3 = factory->NewJSObjectFromMap(map); - ordered_set = OrderedHashSet::Add(ordered_set, obj3); - CHECK_EQ(3, ordered_set->NumberOfElements()); - CHECK(ordered_set->Contains(obj1)); - CHECK(ordered_set->Contains(obj2)); - CHECK(ordered_set->Contains(obj3)); - - // Test growth - ordered_set = OrderedHashSet::Add(ordered_set, obj); - Handle obj4 = factory->NewJSObjectFromMap(map); - ordered_set = OrderedHashSet::Add(ordered_set, obj4); - CHECK(ordered_set->Contains(obj)); - CHECK(ordered_set->Contains(obj1)); - CHECK(ordered_set->Contains(obj2)); - CHECK(ordered_set->Contains(obj3)); - CHECK(ordered_set->Contains(obj4)); - CHECK_EQ(5, ordered_set->NumberOfElements()); - CHECK_EQ(0, ordered_set->NumberOfDeletedElements()); - CHECK_EQ(4, ordered_set->NumberOfBuckets()); - - // Test shrinking - ordered_set = OrderedHashSet::Remove(ordered_set, obj, &was_present); - CHECK(was_present); - ordered_set = OrderedHashSet::Remove(ordered_set, obj1, &was_present); - CHECK(was_present); - ordered_set = OrderedHashSet::Remove(ordered_set, obj2, &was_present); - CHECK(was_present); - ordered_set = OrderedHashSet::Remove(ordered_set, obj3, &was_present); - CHECK(was_present); - CHECK_EQ(1, ordered_set->NumberOfElements()); - CHECK_EQ(2, ordered_set->NumberOfBuckets()); -} - - -TEST(Map) { - LocalContext context; - Isolate* isolate = CcTest::i_isolate(); - Factory* factory = isolate->factory(); - HandleScope scope(isolate); - Handle ordered_map = factory->NewOrderedHashMap(); - CHECK_EQ(2, ordered_map->NumberOfBuckets()); - CHECK_EQ(0, ordered_map->NumberOfElements()); - CHECK_EQ(0, ordered_map->NumberOfDeletedElements()); - - Handle map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); - Handle obj = factory->NewJSObjectFromMap(map); - Handle val = factory->NewJSObjectFromMap(map); - CHECK(ordered_map->Lookup(obj)->IsTheHole()); - ordered_map = OrderedHashMap::Put(ordered_map, obj, val); - CHECK_EQ(1, ordered_map->NumberOfElements()); - Object* lookup = ordered_map->Lookup(obj); - CHECK(lookup->SameValue(*val)); - bool was_present = false; - ordered_map = OrderedHashMap::Remove(ordered_map, obj, &was_present); - CHECK(was_present); - CHECK_EQ(0, ordered_map->NumberOfElements()); - CHECK(ordered_map->Lookup(obj)->IsTheHole()); - - // Test for collisions/chaining - Handle obj1 = factory->NewJSObjectFromMap(map); - Handle obj2 = factory->NewJSObjectFromMap(map); - Handle obj3 = factory->NewJSObjectFromMap(map); - Handle val1 = factory->NewJSObjectFromMap(map); - Handle val2 = factory->NewJSObjectFromMap(map); - Handle val3 = factory->NewJSObjectFromMap(map); - ordered_map = OrderedHashMap::Put(ordered_map, obj1, val1); - ordered_map = OrderedHashMap::Put(ordered_map, obj2, val2); - ordered_map = OrderedHashMap::Put(ordered_map, obj3, val3); - CHECK_EQ(3, ordered_map->NumberOfElements()); - lookup = ordered_map->Lookup(obj1); - CHECK(lookup->SameValue(*val1)); - lookup = ordered_map->Lookup(obj2); - CHECK(lookup->SameValue(*val2)); - lookup = ordered_map->Lookup(obj3); - CHECK(lookup->SameValue(*val3)); - - // Test growth - ordered_map = OrderedHashMap::Put(ordered_map, obj, val); - Handle obj4 = factory->NewJSObjectFromMap(map); - Handle val4 = factory->NewJSObjectFromMap(map); - ordered_map = OrderedHashMap::Put(ordered_map, obj4, val4); - lookup = ordered_map->Lookup(obj); - CHECK(lookup->SameValue(*val)); - lookup = ordered_map->Lookup(obj1); - CHECK(lookup->SameValue(*val1)); - lookup = ordered_map->Lookup(obj2); - CHECK(lookup->SameValue(*val2)); - lookup = ordered_map->Lookup(obj3); - CHECK(lookup->SameValue(*val3)); - lookup = ordered_map->Lookup(obj4); - CHECK(lookup->SameValue(*val4)); - CHECK_EQ(5, ordered_map->NumberOfElements()); - CHECK_EQ(4, ordered_map->NumberOfBuckets()); - - // Test shrinking - ordered_map = OrderedHashMap::Remove(ordered_map, obj, &was_present); - CHECK(was_present); - ordered_map = OrderedHashMap::Remove(ordered_map, obj1, &was_present); - CHECK(was_present); - ordered_map = OrderedHashMap::Remove(ordered_map, obj2, &was_present); - CHECK(was_present); - ordered_map = OrderedHashMap::Remove(ordered_map, obj3, &was_present); - CHECK(was_present); - CHECK_EQ(1, ordered_map->NumberOfElements()); - CHECK_EQ(2, ordered_map->NumberOfBuckets()); -} - - -}