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()); -} - - -}