diff --git a/deps/v8/src/arm/code-stubs-arm.cc b/deps/v8/src/arm/code-stubs-arm.cc index f07f20f032..a59b6fd633 100644 --- a/deps/v8/src/arm/code-stubs-arm.cc +++ b/deps/v8/src/arm/code-stubs-arm.cc @@ -5363,7 +5363,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, Register hash, Register character) { // hash = character + (character << 10); - __ LoadRoot(hash, Heap::kStringHashSeedRootIndex); + __ LoadRoot(hash, Heap::kHashSeedRootIndex); // Untag smi seed and add the character. __ add(hash, character, Operand(hash, LSR, kSmiTagSize)); // hash += hash << 10; diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index 2f68eb9b63..7a1f802450 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -1337,6 +1337,34 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +void MacroAssembler::GetNumberHash(Register t0, Register scratch) { + // First of all we assign the hash seed to scratch. + LoadRoot(scratch, Heap::kHashSeedRootIndex); + SmiUntag(scratch); + + // Xor original key with a seed. + eor(t0, t0, Operand(scratch)); + + // Compute the hash code from the untagged key. This must be kept in sync + // with ComputeIntegerHash in utils.h. + // + // hash = ~hash + (hash << 15); + mvn(scratch, Operand(t0)); + add(t0, scratch, Operand(t0, LSL, 15)); + // hash = hash ^ (hash >> 12); + eor(t0, t0, Operand(t0, LSR, 12)); + // hash = hash + (hash << 2); + add(t0, t0, Operand(t0, LSL, 2)); + // hash = hash ^ (hash >> 4); + eor(t0, t0, Operand(t0, LSR, 4)); + // hash = hash * 2057; + mov(scratch, Operand(2057)); + mul(t0, t0, scratch); + // hash = hash ^ (hash >> 16); + eor(t0, t0, Operand(t0, LSR, 16)); +} + + void MacroAssembler::LoadFromNumberDictionary(Label* miss, Register elements, Register key, @@ -1366,26 +1394,10 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, // t2 - used for the index into the dictionary. Label done; - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash << 15); - mvn(t1, Operand(t0)); - add(t0, t1, Operand(t0, LSL, 15)); - // hash = hash ^ (hash >> 12); - eor(t0, t0, Operand(t0, LSR, 12)); - // hash = hash + (hash << 2); - add(t0, t0, Operand(t0, LSL, 2)); - // hash = hash ^ (hash >> 4); - eor(t0, t0, Operand(t0, LSR, 4)); - // hash = hash * 2057; - mov(t1, Operand(2057)); - mul(t0, t0, t1); - // hash = hash ^ (hash >> 16); - eor(t0, t0, Operand(t0, LSR, 16)); + GetNumberHash(t0, t1); // Compute the capacity mask. - ldr(t1, FieldMemOperand(elements, NumberDictionary::kCapacityOffset)); + ldr(t1, FieldMemOperand(elements, SeededNumberDictionary::kCapacityOffset)); mov(t1, Operand(t1, ASR, kSmiTagSize)); // convert smi to int sub(t1, t1, Operand(1)); @@ -1396,17 +1408,17 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, mov(t2, t0); // Compute the masked index: (hash + i + i * i) & mask. if (i > 0) { - add(t2, t2, Operand(NumberDictionary::GetProbeOffset(i))); + add(t2, t2, Operand(SeededNumberDictionary::GetProbeOffset(i))); } and_(t2, t2, Operand(t1)); // Scale the index by multiplying by the element size. - ASSERT(NumberDictionary::kEntrySize == 3); + ASSERT(SeededNumberDictionary::kEntrySize == 3); add(t2, t2, Operand(t2, LSL, 1)); // t2 = t2 * 3 // Check if the key is identical to the name. add(t2, elements, Operand(t2, LSL, kPointerSizeLog2)); - ldr(ip, FieldMemOperand(t2, NumberDictionary::kElementsStartOffset)); + ldr(ip, FieldMemOperand(t2, SeededNumberDictionary::kElementsStartOffset)); cmp(key, Operand(ip)); if (i != kProbes - 1) { b(eq, &done); @@ -1419,14 +1431,14 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, // Check that the value is a normal property. // t2: elements + (index * kPointerSize) const int kDetailsOffset = - NumberDictionary::kElementsStartOffset + 2 * kPointerSize; + SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize; ldr(t1, FieldMemOperand(t2, kDetailsOffset)); tst(t1, Operand(Smi::FromInt(PropertyDetails::TypeField::kMask))); b(ne, miss); // Get the value at the masked, scaled index and return. const int kValueOffset = - NumberDictionary::kElementsStartOffset + kPointerSize; + SeededNumberDictionary::kElementsStartOffset + kPointerSize; ldr(result, FieldMemOperand(t2, kValueOffset)); } diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h index 4f695fff20..0546e6a15e 100644 --- a/deps/v8/src/arm/macro-assembler-arm.h +++ b/deps/v8/src/arm/macro-assembler-arm.h @@ -441,6 +441,7 @@ class MacroAssembler: public Assembler { Register scratch, Label* miss); + void GetNumberHash(Register t0, Register scratch); void LoadFromNumberDictionary(Label* miss, Register elements, diff --git a/deps/v8/src/code-stubs.cc b/deps/v8/src/code-stubs.cc index 00da4cba62..724445e849 100644 --- a/deps/v8/src/code-stubs.cc +++ b/deps/v8/src/code-stubs.cc @@ -40,7 +40,7 @@ namespace internal { bool CodeStub::FindCodeInCache(Code** code_out) { Heap* heap = Isolate::Current()->heap(); int index = heap->code_stubs()->FindEntry(GetKey()); - if (index != NumberDictionary::kNotFound) { + if (index != UnseededNumberDictionary::kNotFound) { *code_out = Code::cast(heap->code_stubs()->ValueAt(index)); return true; } @@ -121,9 +121,9 @@ Handle CodeStub::GetCode() { FinishCode(*new_object); // Update the dictionary and the root in Heap. - Handle dict = + Handle dict = factory->DictionaryAtNumberPut( - Handle(heap->code_stubs()), + Handle(heap->code_stubs()), GetKey(), new_object); heap->public_set_code_stubs(*dict); @@ -165,7 +165,7 @@ MaybeObject* CodeStub::TryGetCode() { MaybeObject* maybe_new_object = heap->code_stubs()->AtNumberPut(GetKey(), code); if (maybe_new_object->ToObject(&new_object)) { - heap->public_set_code_stubs(NumberDictionary::cast(new_object)); + heap->public_set_code_stubs(UnseededNumberDictionary::cast(new_object)); } } diff --git a/deps/v8/src/debug.h b/deps/v8/src/debug.h index a098040c0d..a5083eb4fe 100644 --- a/deps/v8/src/debug.h +++ b/deps/v8/src/debug.h @@ -178,7 +178,9 @@ class ScriptCache : private HashMap { private: // Calculate the hash value from the key (script id). - static uint32_t Hash(int key) { return ComputeIntegerHash(key); } + static uint32_t Hash(int key) { + return ComputeIntegerHash(key, v8::internal::kZeroHashSeed); + } // Scripts match if their keys (script id) match. static bool ScriptMatch(void* key1, void* key2) { return key1 == key2; } diff --git a/deps/v8/src/elements.cc b/deps/v8/src/elements.cc index e4ecfe8dd6..0454644617 100644 --- a/deps/v8/src/elements.cc +++ b/deps/v8/src/elements.cc @@ -392,7 +392,7 @@ class PixelElementsAccessor class DictionaryElementsAccessor : public ElementsAccessorBase { + SeededNumberDictionary> { public: static MaybeObject* DeleteCommon(JSObject* obj, uint32_t key, @@ -405,9 +405,10 @@ class DictionaryElementsAccessor if (is_arguments) { backing_store = FixedArray::cast(backing_store->get(1)); } - NumberDictionary* dictionary = NumberDictionary::cast(backing_store); + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(backing_store); int entry = dictionary->FindEntry(key); - if (entry != NumberDictionary::kNotFound) { + if (entry != SeededNumberDictionary::kNotFound) { Object* result = dictionary->DeleteProperty(entry, mode); if (result == heap->true_value()) { MaybeObject* maybe_elements = dictionary->Shrink(key); @@ -440,7 +441,7 @@ class DictionaryElementsAccessor protected: friend class ElementsAccessorBase; + SeededNumberDictionary>; virtual MaybeObject* Delete(JSObject* obj, uint32_t key, @@ -448,12 +449,12 @@ class DictionaryElementsAccessor return DeleteCommon(obj, key, mode); } - static MaybeObject* Get(NumberDictionary* backing_store, + static MaybeObject* Get(SeededNumberDictionary* backing_store, uint32_t key, JSObject* obj, Object* receiver) { int entry = backing_store->FindEntry(key); - if (entry != NumberDictionary::kNotFound) { + if (entry != SeededNumberDictionary::kNotFound) { Object* element = backing_store->ValueAt(entry); PropertyDetails details = backing_store->DetailsAt(entry); if (details.type() == CALLBACKS) { @@ -468,7 +469,7 @@ class DictionaryElementsAccessor return obj->GetHeap()->the_hole_value(); } - static uint32_t GetKeyForIndex(NumberDictionary* dict, + static uint32_t GetKeyForIndex(SeededNumberDictionary* dict, uint32_t index) { Object* key = dict->KeyAt(index); return Smi::cast(key)->value(); diff --git a/deps/v8/src/factory.cc b/deps/v8/src/factory.cc index 97289266e3..971f9f9cee 100644 --- a/deps/v8/src/factory.cc +++ b/deps/v8/src/factory.cc @@ -77,11 +77,21 @@ Handle Factory::NewStringDictionary(int at_least_space_for) { } -Handle Factory::NewNumberDictionary(int at_least_space_for) { +Handle Factory::NewSeededNumberDictionary( + int at_least_space_for) { ASSERT(0 <= at_least_space_for); CALL_HEAP_FUNCTION(isolate(), - NumberDictionary::Allocate(at_least_space_for), - NumberDictionary); + SeededNumberDictionary::Allocate(at_least_space_for), + SeededNumberDictionary); +} + + +Handle Factory::NewUnseededNumberDictionary( + int at_least_space_for) { + ASSERT(0 <= at_least_space_for); + CALL_HEAP_FUNCTION(isolate(), + UnseededNumberDictionary::Allocate(at_least_space_for), + UnseededNumberDictionary); } @@ -990,13 +1000,23 @@ Handle Factory::NumberToString(Handle number) { } -Handle Factory::DictionaryAtNumberPut( - Handle dictionary, +Handle Factory::DictionaryAtNumberPut( + Handle dictionary, + uint32_t key, + Handle value) { + CALL_HEAP_FUNCTION(isolate(), + dictionary->AtNumberPut(key, *value), + SeededNumberDictionary); +} + + +Handle Factory::DictionaryAtNumberPut( + Handle dictionary, uint32_t key, Handle value) { CALL_HEAP_FUNCTION(isolate(), dictionary->AtNumberPut(key, *value), - NumberDictionary); + UnseededNumberDictionary); } diff --git a/deps/v8/src/factory.h b/deps/v8/src/factory.h index 71ae750b38..c9817fefec 100644 --- a/deps/v8/src/factory.h +++ b/deps/v8/src/factory.h @@ -54,7 +54,11 @@ class Factory { int size, PretenureFlag pretenure = NOT_TENURED); - Handle NewNumberDictionary(int at_least_space_for); + Handle NewSeededNumberDictionary( + int at_least_space_for); + + Handle NewUnseededNumberDictionary( + int at_least_space_for); Handle NewStringDictionary(int at_least_space_for); @@ -412,8 +416,13 @@ class Factory { Handle stack_trace, Handle stack_frames); - Handle DictionaryAtNumberPut( - Handle, + Handle DictionaryAtNumberPut( + Handle, + uint32_t key, + Handle value); + + Handle DictionaryAtNumberPut( + Handle, uint32_t key, Handle value); diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index 53cc4856f3..e8f6349e0e 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -319,13 +319,13 @@ DEFINE_bool(trace_exception, false, "print stack trace when throwing exceptions") DEFINE_bool(preallocate_message_memory, false, "preallocate some memory to build stack traces.") -DEFINE_bool(randomize_string_hashes, +DEFINE_bool(randomize_hashes, true, - "randomize string hashes to avoid predictable hash collisions " + "randomize hashes to avoid predictable hash collisions " "(with snapshots this option cannot override the baked-in seed)") -DEFINE_int(string_hash_seed, +DEFINE_int(hash_seed, 0, - "Fixed seed to use to string hashing (0 means random)" + "Fixed seed to use to hash property keys (0 means random)" "(with snapshots this option cannot override the baked-in seed)") // v8.cc diff --git a/deps/v8/src/frames.cc b/deps/v8/src/frames.cc index bebd10a806..60b1aadfca 100644 --- a/deps/v8/src/frames.cc +++ b/deps/v8/src/frames.cc @@ -1187,7 +1187,8 @@ PcToCodeCache::PcToCodeCacheEntry* PcToCodeCache::GetCacheEntry(Address pc) { isolate_->counters()->pc_to_code()->Increment(); ASSERT(IsPowerOf2(kPcToCodeCacheSize)); uint32_t hash = ComputeIntegerHash( - static_cast(reinterpret_cast(pc))); + static_cast(reinterpret_cast(pc)), + v8::internal::kZeroHashSeed); uint32_t index = hash & (kPcToCodeCacheSize - 1); PcToCodeCacheEntry* entry = cache(index); if (entry->pc == pc) { diff --git a/deps/v8/src/handles.cc b/deps/v8/src/handles.cc index 35c363c10c..c482fa6f68 100644 --- a/deps/v8/src/handles.cc +++ b/deps/v8/src/handles.cc @@ -214,10 +214,10 @@ void NormalizeProperties(Handle object, } -Handle NormalizeElements(Handle object) { +Handle NormalizeElements(Handle object) { CALL_HEAP_FUNCTION(object->GetIsolate(), object->NormalizeElements(), - NumberDictionary); + SeededNumberDictionary); } @@ -229,14 +229,14 @@ void TransformToFastProperties(Handle object, } -Handle NumberDictionarySet( - Handle dictionary, +Handle SeededNumberDictionarySet( + Handle dictionary, uint32_t index, Handle value, PropertyDetails details) { CALL_HEAP_FUNCTION(dictionary->GetIsolate(), dictionary->Set(index, *value, details), - NumberDictionary); + SeededNumberDictionary); } diff --git a/deps/v8/src/handles.h b/deps/v8/src/handles.h index 7eaf4de927..5674120643 100644 --- a/deps/v8/src/handles.h +++ b/deps/v8/src/handles.h @@ -170,11 +170,11 @@ class HandleScope { void NormalizeProperties(Handle object, PropertyNormalizationMode mode, int expected_additional_properties); -Handle NormalizeElements(Handle object); +Handle NormalizeElements(Handle object); void TransformToFastProperties(Handle object, int unused_property_fields); -MUST_USE_RESULT Handle NumberDictionarySet( - Handle dictionary, +MUST_USE_RESULT Handle SeededNumberDictionarySet( + Handle dictionary, uint32_t index, Handle value, PropertyDetails details); diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index 6815c2da47..c91f7691b0 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -2181,17 +2181,17 @@ bool Heap::CreateInitialObjects() { // Allocate the code_stubs dictionary. The initial size is set to avoid // expanding the dictionary during bootstrapping. - { MaybeObject* maybe_obj = NumberDictionary::Allocate(128); + { MaybeObject* maybe_obj = UnseededNumberDictionary::Allocate(128); if (!maybe_obj->ToObject(&obj)) return false; } - set_code_stubs(NumberDictionary::cast(obj)); + set_code_stubs(UnseededNumberDictionary::cast(obj)); // Allocate the non_monomorphic_cache used in stub-cache.cc. The initial size // is set to avoid expanding the dictionary during bootstrapping. - { MaybeObject* maybe_obj = NumberDictionary::Allocate(64); + { MaybeObject* maybe_obj = UnseededNumberDictionary::Allocate(64); if (!maybe_obj->ToObject(&obj)) return false; } - set_non_monomorphic_cache(NumberDictionary::cast(obj)); + set_non_monomorphic_cache(UnseededNumberDictionary::cast(obj)); { MaybeObject* maybe_obj = AllocatePolymorphicCodeCache(); if (!maybe_obj->ToObject(&obj)) return false; @@ -5362,14 +5362,14 @@ bool Heap::Setup(bool create_heap_objects) { if (lo_space_ == NULL) return false; if (!lo_space_->Setup()) return false; - // Set up the seed that is used to randomize the string hash function. - ASSERT(string_hash_seed() == 0); - if (FLAG_randomize_string_hashes) { - if (FLAG_string_hash_seed == 0) { - set_string_hash_seed( + // Setup the seed that is used to randomize the string hash function. + ASSERT(hash_seed() == 0); + if (FLAG_randomize_hashes) { + if (FLAG_hash_seed == 0) { + set_hash_seed( Smi::FromInt(V8::RandomPrivate(isolate()) & 0x3fffffff)); } else { - set_string_hash_seed(Smi::FromInt(FLAG_string_hash_seed)); + set_hash_seed(Smi::FromInt(FLAG_hash_seed)); } } diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index 26d8722e21..b1948a933c 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -79,7 +79,7 @@ inline Heap* _inline_get_heap_(); V(FixedArray, single_character_string_cache, SingleCharacterStringCache) \ V(FixedArray, string_split_cache, StringSplitCache) \ V(Object, termination_exception, TerminationException) \ - V(Smi, string_hash_seed, StringHashSeed) \ + V(Smi, hash_seed, HashSeed) \ V(FixedArray, empty_fixed_array, EmptyFixedArray) \ V(ByteArray, empty_byte_array, EmptyByteArray) \ V(FixedDoubleArray, empty_fixed_double_array, EmptyFixedDoubleArray) \ @@ -128,8 +128,8 @@ inline Heap* _inline_get_heap_(); V(Map, neander_map, NeanderMap) \ V(JSObject, message_listeners, MessageListeners) \ V(Foreign, prototype_accessors, PrototypeAccessors) \ - V(NumberDictionary, code_stubs, CodeStubs) \ - V(NumberDictionary, non_monomorphic_cache, NonMonomorphicCache) \ + V(UnseededNumberDictionary, code_stubs, CodeStubs) \ + V(UnseededNumberDictionary, non_monomorphic_cache, NonMonomorphicCache) \ V(PolymorphicCodeCache, polymorphic_code_cache, PolymorphicCodeCache) \ V(Code, js_entry_code, JsEntryCode) \ V(Code, js_construct_entry_code, JsConstructEntryCode) \ @@ -1037,7 +1037,7 @@ class Heap { inline AllocationSpace TargetSpaceId(InstanceType type); // Sets the stub_cache_ (only used when expanding the dictionary). - void public_set_code_stubs(NumberDictionary* value) { + void public_set_code_stubs(UnseededNumberDictionary* value) { roots_[kCodeStubsRootIndex] = value; } @@ -1049,7 +1049,7 @@ class Heap { } // Sets the non_monomorphic_cache_ (only used when expanding the dictionary). - void public_set_non_monomorphic_cache(NumberDictionary* value) { + void public_set_non_monomorphic_cache(UnseededNumberDictionary* value) { roots_[kNonMonomorphicCacheRootIndex] = value; } @@ -1301,9 +1301,9 @@ class Heap { if (global_gc_epilogue_callback_ != NULL) global_gc_epilogue_callback_(); } - uint32_t StringHashSeed() { - uint32_t seed = static_cast(string_hash_seed()->value()); - ASSERT(FLAG_randomize_string_hashes || seed == 0); + uint32_t HashSeed() { + uint32_t seed = static_cast(hash_seed()->value()); + ASSERT(FLAG_randomize_hashes || seed == 0); return seed; } diff --git a/deps/v8/src/ia32/code-stubs-ia32.cc b/deps/v8/src/ia32/code-stubs-ia32.cc index d67a9ba25e..dd0b8e8b65 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.cc +++ b/deps/v8/src/ia32/code-stubs-ia32.cc @@ -5610,7 +5610,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, if (Serializer::enabled()) { ExternalReference roots_address = ExternalReference::roots_address(masm->isolate()); - __ mov(scratch, Immediate(Heap::kStringHashSeedRootIndex)); + __ mov(scratch, Immediate(Heap::kHashSeedRootIndex)); __ mov(scratch, Operand::StaticArray(scratch, times_pointer_size, roots_address)); @@ -5619,7 +5619,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, __ shl(scratch, 10); __ add(hash, Operand(scratch)); } else { - int32_t seed = masm->isolate()->heap()->StringHashSeed(); + int32_t seed = masm->isolate()->heap()->HashSeed(); __ lea(scratch, Operand(character, seed)); __ shl(scratch, 10); __ lea(hash, Operand(scratch, character, times_1, seed)); diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index 5a3b9836dd..ce6d6a6ea0 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -752,6 +752,51 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +// Compute the hash code from the untagged key. This must be kept in sync +// with ComputeIntegerHash in utils.h. +// +// Note: r0 will contain hash code +void MacroAssembler::GetNumberHash(Register r0, Register scratch) { + // Xor original key with a seed. + if (Serializer::enabled()) { + ExternalReference roots_address = + ExternalReference::roots_address(isolate()); + mov(scratch, Immediate(Heap::kHashSeedRootIndex)); + mov(scratch, Operand::StaticArray(scratch, + times_pointer_size, + roots_address)); + SmiUntag(scratch); + xor_(r0, Operand(scratch)); + } else { + int32_t seed = isolate()->heap()->HashSeed(); + xor_(r0, seed); + } + + // hash = ~hash + (hash << 15); + mov(scratch, r0); + not_(r0); + shl(scratch, 15); + add(r0, Operand(scratch)); + // hash = hash ^ (hash >> 12); + mov(scratch, r0); + shr(scratch, 12); + xor_(r0, Operand(scratch)); + // hash = hash + (hash << 2); + lea(r0, Operand(r0, r0, times_4, 0)); + // hash = hash ^ (hash >> 4); + mov(scratch, r0); + shr(scratch, 4); + xor_(r0, Operand(scratch)); + // hash = hash * 2057; + imul(r0, r0, 2057); + // hash = hash ^ (hash >> 16); + mov(scratch, r0); + shr(scratch, 16); + xor_(r0, Operand(scratch)); +} + + + void MacroAssembler::LoadFromNumberDictionary(Label* miss, Register elements, Register key, @@ -777,33 +822,10 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, Label done; - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash << 15); - mov(r1, r0); - not_(r0); - shl(r1, 15); - add(r0, Operand(r1)); - // hash = hash ^ (hash >> 12); - mov(r1, r0); - shr(r1, 12); - xor_(r0, Operand(r1)); - // hash = hash + (hash << 2); - lea(r0, Operand(r0, r0, times_4, 0)); - // hash = hash ^ (hash >> 4); - mov(r1, r0); - shr(r1, 4); - xor_(r0, Operand(r1)); - // hash = hash * 2057; - imul(r0, r0, 2057); - // hash = hash ^ (hash >> 16); - mov(r1, r0); - shr(r1, 16); - xor_(r0, Operand(r1)); + GetNumberHash(r0, r1); // Compute capacity mask. - mov(r1, FieldOperand(elements, NumberDictionary::kCapacityOffset)); + mov(r1, FieldOperand(elements, SeededNumberDictionary::kCapacityOffset)); shr(r1, kSmiTagSize); // convert smi to int dec(r1); @@ -814,19 +836,19 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, mov(r2, r0); // Compute the masked index: (hash + i + i * i) & mask. if (i > 0) { - add(Operand(r2), Immediate(NumberDictionary::GetProbeOffset(i))); + add(Operand(r2), Immediate(SeededNumberDictionary::GetProbeOffset(i))); } and_(r2, Operand(r1)); // Scale the index by multiplying by the entry size. - ASSERT(NumberDictionary::kEntrySize == 3); + ASSERT(SeededNumberDictionary::kEntrySize == 3); lea(r2, Operand(r2, r2, times_2, 0)); // r2 = r2 * 3 // Check if the key matches. cmp(key, FieldOperand(elements, r2, times_pointer_size, - NumberDictionary::kElementsStartOffset)); + SeededNumberDictionary::kElementsStartOffset)); if (i != (kProbes - 1)) { j(equal, &done); } else { @@ -837,7 +859,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, bind(&done); // Check that the value is a normal propety. const int kDetailsOffset = - NumberDictionary::kElementsStartOffset + 2 * kPointerSize; + SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize; ASSERT_EQ(NORMAL, 0); test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset), Immediate(PropertyDetails::TypeField::kMask << kSmiTagSize)); @@ -845,7 +867,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, // Get the value at the masked, scaled index. const int kValueOffset = - NumberDictionary::kElementsStartOffset + kPointerSize; + SeededNumberDictionary::kElementsStartOffset + kPointerSize; mov(result, FieldOperand(elements, r2, times_pointer_size, kValueOffset)); } diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h index 1906644c35..8c5f5e9421 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/ia32/macro-assembler-ia32.h @@ -354,6 +354,7 @@ class MacroAssembler: public Assembler { Register scratch, Label* miss); + void GetNumberHash(Register r0, Register scratch); void LoadFromNumberDictionary(Label* miss, Register elements, diff --git a/deps/v8/src/mips/code-stubs-mips.cc b/deps/v8/src/mips/code-stubs-mips.cc index 3500ba7530..6838e54478 100644 --- a/deps/v8/src/mips/code-stubs-mips.cc +++ b/deps/v8/src/mips/code-stubs-mips.cc @@ -5580,7 +5580,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, Register hash, Register character) { // hash = seed + character + ((seed + character) << 10); - __ LoadRoot(hash, Heap::kStringHashSeedRootIndex); + __ LoadRoot(hash, Heap::kHashSeedRootIndex); // Untag smi seed and add the character. __ SmiUntag(hash); __ addu(hash, hash, character); diff --git a/deps/v8/src/mips/macro-assembler-mips.cc b/deps/v8/src/mips/macro-assembler-mips.cc index 4c48ef183c..1c0af5d629 100644 --- a/deps/v8/src/mips/macro-assembler-mips.cc +++ b/deps/v8/src/mips/macro-assembler-mips.cc @@ -343,6 +343,44 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +void MacroAssembler::GetNumberHash(Register reg0, Register scratch) { + // First of all we assign the hash seed to scratch. + LoadRoot(scratch, Heap::kHashSeedRootIndex); + SmiUntag(scratch); + + // Xor original key with a seed. + xor_(reg0, reg0, scratch); + + // Compute the hash code from the untagged key. This must be kept in sync + // with ComputeIntegerHash in utils.h. + // + // hash = ~hash + (hash << 15); + nor(scratch, reg0, zero_reg); + sll(at, reg0, 15); + addu(reg0, scratch, at); + + // hash = hash ^ (hash >> 12); + srl(at, reg0, 12); + xor_(reg0, reg0, at); + + // hash = hash + (hash << 2); + sll(at, reg0, 2); + addu(reg0, reg0, at); + + // hash = hash ^ (hash >> 4); + srl(at, reg0, 4); + xor_(reg0, reg0, at); + + // hash = hash * 2057; + li(scratch, Operand(2057)); + mul(reg0, reg0, scratch); + + // hash = hash ^ (hash >> 16); + srl(at, reg0, 16); + xor_(reg0, reg0, at); +} + + void MacroAssembler::LoadFromNumberDictionary(Label* miss, Register elements, Register key, @@ -374,36 +412,10 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, // at - Temporary (avoid MacroAssembler instructions also using 'at'). Label done; - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash << 15); - nor(reg1, reg0, zero_reg); - sll(at, reg0, 15); - addu(reg0, reg1, at); - - // hash = hash ^ (hash >> 12); - srl(at, reg0, 12); - xor_(reg0, reg0, at); - - // hash = hash + (hash << 2); - sll(at, reg0, 2); - addu(reg0, reg0, at); - - // hash = hash ^ (hash >> 4); - srl(at, reg0, 4); - xor_(reg0, reg0, at); - - // hash = hash * 2057; - li(reg1, Operand(2057)); - mul(reg0, reg0, reg1); - - // hash = hash ^ (hash >> 16); - srl(at, reg0, 16); - xor_(reg0, reg0, at); + GetNumberHash(reg0, reg1); // Compute the capacity mask. - lw(reg1, FieldMemOperand(elements, NumberDictionary::kCapacityOffset)); + lw(reg1, FieldMemOperand(elements, SeededNumberDictionary::kCapacityOffset)); sra(reg1, reg1, kSmiTagSize); Subu(reg1, reg1, Operand(1)); @@ -414,12 +426,12 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, mov(reg2, reg0); // Compute the masked index: (hash + i + i * i) & mask. if (i > 0) { - Addu(reg2, reg2, Operand(NumberDictionary::GetProbeOffset(i))); + Addu(reg2, reg2, Operand(SeededNumberDictionary::GetProbeOffset(i))); } and_(reg2, reg2, reg1); // Scale the index by multiplying by the element size. - ASSERT(NumberDictionary::kEntrySize == 3); + ASSERT(SeededNumberDictionary::kEntrySize == 3); sll(at, reg2, 1); // 2x. addu(reg2, reg2, at); // reg2 = reg2 * 3. @@ -427,7 +439,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, sll(at, reg2, kPointerSizeLog2); addu(reg2, elements, at); - lw(at, FieldMemOperand(reg2, NumberDictionary::kElementsStartOffset)); + lw(at, FieldMemOperand(reg2, SeededNumberDictionary::kElementsStartOffset)); if (i != kProbes - 1) { Branch(&done, eq, key, Operand(at)); } else { @@ -439,14 +451,14 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, // Check that the value is a normal property. // reg2: elements + (index * kPointerSize). const int kDetailsOffset = - NumberDictionary::kElementsStartOffset + 2 * kPointerSize; + SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize; lw(reg1, FieldMemOperand(reg2, kDetailsOffset)); And(at, reg1, Operand(Smi::FromInt(PropertyDetails::TypeField::kMask))); Branch(miss, ne, at, Operand(zero_reg)); // Get the value at the masked, scaled index and return. const int kValueOffset = - NumberDictionary::kElementsStartOffset + kPointerSize; + SeededNumberDictionary::kElementsStartOffset + kPointerSize; lw(result, FieldMemOperand(reg2, kValueOffset)); } diff --git a/deps/v8/src/mips/macro-assembler-mips.h b/deps/v8/src/mips/macro-assembler-mips.h index ce19a4eff6..c968ffcd15 100644 --- a/deps/v8/src/mips/macro-assembler-mips.h +++ b/deps/v8/src/mips/macro-assembler-mips.h @@ -266,6 +266,7 @@ class MacroAssembler: public Assembler { Register scratch, Label* miss); + void GetNumberHash(Register reg0, Register scratch); void LoadFromNumberDictionary(Label* miss, Register elements, diff --git a/deps/v8/src/objects-debug.cc b/deps/v8/src/objects-debug.cc index 8de7162ab2..e9ca6c0907 100644 --- a/deps/v8/src/objects-debug.cc +++ b/deps/v8/src/objects-debug.cc @@ -700,7 +700,7 @@ void JSObject::IncrementSpillStatistics(SpillInformation* info) { break; } case DICTIONARY_ELEMENTS: { - NumberDictionary* dict = element_dictionary(); + SeededNumberDictionary* dict = element_dictionary(); info->number_of_slow_used_elements_ += dict->NumberOfElements(); info->number_of_slow_unused_elements_ += dict->Capacity() - dict->NumberOfElements(); diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index bc4ce79db2..e7b6a34296 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -1789,7 +1789,7 @@ void FixedDoubleArray::Initialize(FixedArray* from) { } -void FixedDoubleArray::Initialize(NumberDictionary* from) { +void FixedDoubleArray::Initialize(SeededNumberDictionary* from) { int offset = kHeaderSize; for (int current = 0; current < length(); ++current) { WRITE_DOUBLE_FIELD(this, offset, hole_nan_as_double()); @@ -2077,7 +2077,7 @@ int HashTable::FindEntry(Key key) { template int HashTable::FindEntry(Isolate* isolate, Key key) { uint32_t capacity = Capacity(); - uint32_t entry = FirstProbe(Shape::Hash(key), capacity); + uint32_t entry = FirstProbe(HashTable::Hash(key), capacity); uint32_t count = 1; // EnsureCapacity will guarantee the hash table is never full. while (true) { @@ -2092,14 +2092,14 @@ int HashTable::FindEntry(Isolate* isolate, Key key) { } -bool NumberDictionary::requires_slow_elements() { +bool SeededNumberDictionary::requires_slow_elements() { Object* max_index_object = get(kMaxNumberKeyIndex); if (!max_index_object->IsSmi()) return false; return 0 != (Smi::cast(max_index_object)->value() & kRequiresSlowElementsMask); } -uint32_t NumberDictionary::max_number_key() { +uint32_t SeededNumberDictionary::max_number_key() { ASSERT(!requires_slow_elements()); Object* max_index_object = get(kMaxNumberKeyIndex); if (!max_index_object->IsSmi()) return 0; @@ -2107,7 +2107,7 @@ uint32_t NumberDictionary::max_number_key() { return value >> kRequiresSlowElementsTagSize; } -void NumberDictionary::set_requires_slow_elements() { +void SeededNumberDictionary::set_requires_slow_elements() { set(kMaxNumberKeyIndex, Smi::FromInt(kRequiresSlowElementsMask)); } @@ -4211,9 +4211,9 @@ StringDictionary* JSObject::property_dictionary() { } -NumberDictionary* JSObject::element_dictionary() { +SeededNumberDictionary* JSObject::element_dictionary() { ASSERT(HasDictionaryElements()); - return NumberDictionary::cast(elements()); + return SeededNumberDictionary::cast(elements()); } @@ -4243,7 +4243,7 @@ StringHasher::StringHasher(int length, uint32_t seed) is_array_index_(0 < length_ && length_ <= String::kMaxArrayIndexSize), is_first_char_(true), is_valid_(true) { - ASSERT(FLAG_randomize_string_hashes || raw_running_hash_ == 0); + ASSERT(FLAG_randomize_hashes || raw_running_hash_ == 0); } @@ -4476,16 +4476,27 @@ bool NumberDictionaryShape::IsMatch(uint32_t key, Object* other) { } -uint32_t NumberDictionaryShape::Hash(uint32_t key) { - return ComputeIntegerHash(key); +uint32_t UnseededNumberDictionaryShape::Hash(uint32_t key) { + return ComputeIntegerHash(key, 0); } -uint32_t NumberDictionaryShape::HashForObject(uint32_t key, Object* other) { +uint32_t UnseededNumberDictionaryShape::HashForObject(uint32_t key, + Object* other) { ASSERT(other->IsNumber()); - return ComputeIntegerHash(static_cast(other->Number())); + return ComputeIntegerHash(static_cast(other->Number()), 0); } +uint32_t SeededNumberDictionaryShape::SeededHash(uint32_t key, uint32_t seed) { + return ComputeIntegerHash(key, seed); +} + +uint32_t SeededNumberDictionaryShape::SeededHashForObject(uint32_t key, + uint32_t seed, + Object* other) { + ASSERT(other->IsNumber()); + return ComputeIntegerHash(static_cast(other->Number()), seed); +} MaybeObject* NumberDictionaryShape::AsObject(uint32_t key) { return Isolate::Current()->heap()->NumberFromUint32(key); diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 595459681f..42245d48b0 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -1951,9 +1951,10 @@ MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes( if (!JSObject::cast(pt)->HasDictionaryElements()) { continue; } - NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary(); + SeededNumberDictionary* dictionary = + JSObject::cast(pt)->element_dictionary(); int entry = dictionary->FindEntry(index); - if (entry != NumberDictionary::kNotFound) { + if (entry != SeededNumberDictionary::kNotFound) { PropertyDetails details = dictionary->DetailsAt(entry); if (details.type() == CALLBACKS) { *found = true; @@ -2928,11 +2929,11 @@ MaybeObject* JSObject::NormalizeElements() { int old_capacity = 0; int used_elements = 0; GetElementsCapacityAndUsage(&old_capacity, &used_elements); - NumberDictionary* dictionary = NULL; + SeededNumberDictionary* dictionary = NULL; { Object* object; - MaybeObject* maybe = NumberDictionary::Allocate(used_elements); + MaybeObject* maybe = SeededNumberDictionary::Allocate(used_elements); if (!maybe->ToObject(&object)) return maybe; - dictionary = NumberDictionary::cast(object); + dictionary = SeededNumberDictionary::cast(object); } // Copy the elements to the new backing store. @@ -2962,7 +2963,7 @@ MaybeObject* JSObject::NormalizeElements() { MaybeObject* maybe_result = dictionary->AddNumberEntry(i, value, details); if (!maybe_result->ToObject(&result)) return maybe_result; - dictionary = NumberDictionary::cast(result); + dictionary = SeededNumberDictionary::cast(result); } } @@ -3277,7 +3278,8 @@ bool JSObject::ReferencesObjectFromElements(FixedArray* elements, if (!element->IsTheHole() && element == object) return true; } } else { - Object* key = NumberDictionary::cast(elements)->SlowReverseLookup(object); + Object* key = + SeededNumberDictionary::cast(elements)->SlowReverseLookup(object); if (!key->IsUndefined()) return true; } return false; @@ -3416,9 +3418,9 @@ MaybeObject* JSObject::PreventExtensions() { } // If there are fast elements we normalize. - NumberDictionary* dictionary = NULL; + SeededNumberDictionary* dictionary = NULL; { MaybeObject* maybe = NormalizeElements(); - if (!maybe->To(&dictionary)) return maybe; + if (!maybe->To(&dictionary)) return maybe; } ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); // Make sure that we never go back to fast case. @@ -3581,11 +3583,11 @@ void JSObject::LookupCallback(String* name, LookupResult* result) { // undefined if the element is read-only, or the getter/setter pair (fixed // array) if there is an existing one, or the hole value if the element does // not exist or is a normal non-getter/setter data element. -static Object* FindGetterSetterInDictionary(NumberDictionary* dictionary, +static Object* FindGetterSetterInDictionary(SeededNumberDictionary* dictionary, uint32_t index, Heap* heap) { int entry = dictionary->FindEntry(index); - if (entry != NumberDictionary::kNotFound) { + if (entry != SeededNumberDictionary::kNotFound) { Object* result = dictionary->ValueAt(entry); PropertyDetails details = dictionary->DetailsAt(entry); if (details.IsReadOnly()) return heap->undefined_value(); @@ -3647,7 +3649,8 @@ MaybeObject* JSObject::DefineGetterSetter(String* name, if (probe == NULL || probe->IsTheHole()) { FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); if (arguments->IsDictionary()) { - NumberDictionary* dictionary = NumberDictionary::cast(arguments); + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(arguments); probe = FindGetterSetterInDictionary(dictionary, index, heap); if (!probe->IsTheHole()) return probe; } @@ -3716,11 +3719,11 @@ MaybeObject* JSObject::SetElementCallback(uint32_t index, PropertyDetails details = PropertyDetails(attributes, CALLBACKS); // Normalize elements to make this operation simple. - NumberDictionary* dictionary = NULL; + SeededNumberDictionary* dictionary = NULL; { Object* result; MaybeObject* maybe = NormalizeElements(); if (!maybe->ToObject(&result)) return maybe; - dictionary = NumberDictionary::cast(result); + dictionary = SeededNumberDictionary::cast(result); } ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); @@ -3728,7 +3731,7 @@ MaybeObject* JSObject::SetElementCallback(uint32_t index, { Object* result; MaybeObject* maybe = dictionary->Set(index, structure, details); if (!maybe->ToObject(&result)) return maybe; - dictionary = NumberDictionary::cast(result); + dictionary = SeededNumberDictionary::cast(result); } dictionary->set_requires_slow_elements(); @@ -3933,9 +3936,9 @@ Object* JSObject::LookupAccessor(String* name, bool is_getter) { obj = JSObject::cast(obj)->GetPrototype()) { JSObject* js_object = JSObject::cast(obj); if (js_object->HasDictionaryElements()) { - NumberDictionary* dictionary = js_object->element_dictionary(); + SeededNumberDictionary* dictionary = js_object->element_dictionary(); int entry = dictionary->FindEntry(index); - if (entry != NumberDictionary::kNotFound) { + if (entry != SeededNumberDictionary::kNotFound) { Object* element = dictionary->ValueAt(entry); PropertyDetails details = dictionary->DetailsAt(entry); if (details.type() == CALLBACKS) { @@ -6077,14 +6080,14 @@ uint32_t String::ComputeAndSetHash() { if (StringShape(this).IsSequentialAscii()) { field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len, - GetHeap()->StringHashSeed()); + GetHeap()->HashSeed()); } else if (StringShape(this).IsSequentialTwoByte()) { field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len, - GetHeap()->StringHashSeed()); + GetHeap()->HashSeed()); } else { StringInputBuffer buffer(this); - field = ComputeHashField(&buffer, len, GetHeap()->StringHashSeed()); + field = ComputeHashField(&buffer, len, GetHeap()->HashSeed()); } // Store the hash code in the object. @@ -7276,7 +7279,7 @@ static void CopyFastElementsToFast(FixedArray* source, } -static void CopySlowElementsToFast(NumberDictionary* source, +static void CopySlowElementsToFast(SeededNumberDictionary* source, FixedArray* destination, WriteBarrierMode mode) { for (int i = 0; i < source->Capacity(); ++i) { @@ -7324,7 +7327,7 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, case DICTIONARY_ELEMENTS: { AssertNoAllocation no_gc; WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc); - CopySlowElementsToFast(NumberDictionary::cast(elements()), + CopySlowElementsToFast(SeededNumberDictionary::cast(elements()), new_elements, mode); set_map(new_map); @@ -7339,7 +7342,7 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, FixedArray* parameter_map = FixedArray::cast(elements()); FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); if (arguments->IsDictionary()) { - CopySlowElementsToFast(NumberDictionary::cast(arguments), + CopySlowElementsToFast(SeededNumberDictionary::cast(arguments), new_elements, mode); } else { @@ -7426,7 +7429,7 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( break; } case DICTIONARY_ELEMENTS: { - elems->Initialize(NumberDictionary::cast(elements())); + elems->Initialize(SeededNumberDictionary::cast(elements())); break; } default: @@ -7861,7 +7864,7 @@ bool JSObject::HasElementPostInterceptor(JSReceiver* receiver, uint32_t index) { } case DICTIONARY_ELEMENTS: { if (element_dictionary()->FindEntry(index) - != NumberDictionary::kNotFound) { + != SeededNumberDictionary::kNotFound) { return true; } break; @@ -7993,7 +7996,7 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { } case DICTIONARY_ELEMENTS: { if (element_dictionary()->FindEntry(index) != - NumberDictionary::kNotFound) { + SeededNumberDictionary::kNotFound) { return DICTIONARY_ELEMENT; } break; @@ -8010,8 +8013,9 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { // If not aliased, check the arguments. FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); if (arguments->IsDictionary()) { - NumberDictionary* dictionary = NumberDictionary::cast(arguments); - if (dictionary->FindEntry(index) != NumberDictionary::kNotFound) { + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(arguments); + if (dictionary->FindEntry(index) != SeededNumberDictionary::kNotFound) { return DICTIONARY_ELEMENT; } } else { @@ -8040,8 +8044,8 @@ bool JSObject::HasElementInElements(FixedArray* elements, return true; } } else { - if (NumberDictionary::cast(elements)->FindEntry(index) != - NumberDictionary::kNotFound) { + if (SeededNumberDictionary::cast(elements)->FindEntry(index) != + SeededNumberDictionary::kNotFound) { return true; } } @@ -8107,7 +8111,7 @@ bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) { } case DICTIONARY_ELEMENTS: { if (element_dictionary()->FindEntry(index) - != NumberDictionary::kNotFound) { + != SeededNumberDictionary::kNotFound) { return true; } break; @@ -8387,15 +8391,15 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, FixedArray* elements = FixedArray::cast(this->elements()); bool is_arguments = (elements->map() == heap->non_strict_arguments_elements_map()); - NumberDictionary* dictionary = NULL; + SeededNumberDictionary* dictionary = NULL; if (is_arguments) { - dictionary = NumberDictionary::cast(elements->get(1)); + dictionary = SeededNumberDictionary::cast(elements->get(1)); } else { - dictionary = NumberDictionary::cast(elements); + dictionary = SeededNumberDictionary::cast(elements); } int entry = dictionary->FindEntry(index); - if (entry != NumberDictionary::kNotFound) { + if (entry != SeededNumberDictionary::kNotFound) { Object* element = dictionary->ValueAt(entry); PropertyDetails details = dictionary->DetailsAt(entry); if (details.type() == CALLBACKS) { @@ -8440,13 +8444,13 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, FixedArrayBase* new_dictionary; MaybeObject* maybe = dictionary->AtNumberPut(index, value); if (!maybe->To(&new_dictionary)) return maybe; - if (dictionary != NumberDictionary::cast(new_dictionary)) { + if (dictionary != SeededNumberDictionary::cast(new_dictionary)) { if (is_arguments) { elements->set(1, new_dictionary); } else { set_elements(new_dictionary); } - dictionary = NumberDictionary::cast(new_dictionary); + dictionary = SeededNumberDictionary::cast(new_dictionary); } } @@ -8767,7 +8771,8 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { FixedArray::cast(FixedArray::cast(backing_store_base)->get(1)); backing_store = FixedArray::cast(backing_store_base); if (backing_store->IsDictionary()) { - NumberDictionary* dictionary = NumberDictionary::cast(backing_store); + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(backing_store); *capacity = dictionary->Capacity(); *used = dictionary->NumberOfElements(); break; @@ -8781,8 +8786,8 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { } break; case DICTIONARY_ELEMENTS: { - NumberDictionary* dictionary = - NumberDictionary::cast(FixedArray::cast(elements())); + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(FixedArray::cast(elements())); *capacity = dictionary->Capacity(); *used = dictionary->NumberOfElements(); break; @@ -8827,8 +8832,8 @@ bool JSObject::ShouldConvertToSlowElements(int new_capacity) { int old_capacity = 0; int used_elements = 0; GetElementsCapacityAndUsage(&old_capacity, &used_elements); - int dictionary_size = NumberDictionary::ComputeCapacity(used_elements) * - NumberDictionary::kEntrySize; + int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) * + SeededNumberDictionary::kEntrySize; return 3 * dictionary_size <= new_capacity; } @@ -8842,11 +8847,11 @@ bool JSObject::ShouldConvertToFastElements() { if (IsAccessCheckNeeded()) return false; FixedArray* elements = FixedArray::cast(this->elements()); - NumberDictionary* dictionary = NULL; + SeededNumberDictionary* dictionary = NULL; if (elements->map() == GetHeap()->non_strict_arguments_elements_map()) { - dictionary = NumberDictionary::cast(elements->get(1)); + dictionary = SeededNumberDictionary::cast(elements->get(1)); } else { - dictionary = NumberDictionary::cast(elements); + dictionary = SeededNumberDictionary::cast(elements); } // If an element has been added at a very high index in the elements // dictionary, we cannot go back to fast case. @@ -8861,7 +8866,7 @@ bool JSObject::ShouldConvertToFastElements() { array_size = dictionary->max_number_key(); } uint32_t dictionary_size = static_cast(dictionary->Capacity()) * - NumberDictionary::kEntrySize; + SeededNumberDictionary::kEntrySize; return 2 * dictionary_size >= array_size; } @@ -8869,7 +8874,8 @@ bool JSObject::ShouldConvertToFastElements() { bool JSObject::CanConvertToFastDoubleElements() { if (FLAG_unbox_double_arrays) { ASSERT(HasDictionaryElements()); - NumberDictionary* dictionary = NumberDictionary::cast(elements()); + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(elements()); for (int i = 0; i < dictionary->Capacity(); i++) { Object* key = dictionary->KeyAt(i); if (key->IsNumber()) { @@ -9082,7 +9088,7 @@ bool JSObject::HasRealElementProperty(uint32_t index) { } case DICTIONARY_ELEMENTS: { return element_dictionary()->FindEntry(index) - != NumberDictionary::kNotFound; + != SeededNumberDictionary::kNotFound; } case NON_STRICT_ARGUMENTS_ELEMENTS: UNIMPLEMENTED(); @@ -9350,7 +9356,7 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, if (storage != NULL) { element_dictionary()->CopyKeysTo(storage, filter, - NumberDictionary::SORTED); + SeededNumberDictionary::SORTED); } counter += element_dictionary()->NumberOfElementsFilterAttributes(filter); break; @@ -9362,9 +9368,11 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, if (arguments->IsDictionary()) { // Copy the keys from arguments first, because Dictionary::CopyKeysTo // will insert in storage starting at index 0. - NumberDictionary* dictionary = NumberDictionary::cast(arguments); + SeededNumberDictionary* dictionary = + SeededNumberDictionary::cast(arguments); if (storage != NULL) { - dictionary->CopyKeysTo(storage, filter, NumberDictionary::UNSORTED); + dictionary->CopyKeysTo( + storage, filter, SeededNumberDictionary::UNSORTED); } counter += dictionary->NumberOfElementsFilterAttributes(filter); for (int i = 0; i < mapped_length; ++i) { @@ -9671,7 +9679,7 @@ class SubStringAsciiSymbolKey : public HashTableKey { uint32_t Hash() { ASSERT(length_ >= 0); ASSERT(from_ + length_ <= string_->length()); - StringHasher hasher(length_, string_->GetHeap()->StringHashSeed()); + StringHasher hasher(length_, string_->GetHeap()->HashSeed()); // Very long strings have a trivial hash that doesn't inspect the // string contents. @@ -9880,7 +9888,7 @@ MaybeObject* HashTable::Rehash(HashTable* new_table, Key key) { uint32_t from_index = EntryToIndex(i); Object* k = get(from_index); if (IsKey(k)) { - uint32_t hash = Shape::HashForObject(key, k); + uint32_t hash = HashTable::HashForObject(key, k); uint32_t insertion_index = EntryToIndex(new_table->FindInsertionEntry(hash)); for (int j = 0; j < Shape::kEntrySize; j++) { @@ -9976,38 +9984,46 @@ template class HashTable; template class Dictionary; -template class Dictionary; +template class Dictionary; -template MaybeObject* Dictionary::Allocate( - int); +template class Dictionary; + +template MaybeObject* Dictionary:: + Allocate(int at_least_space_for); + +template MaybeObject* Dictionary:: + Allocate(int at_least_space_for); template MaybeObject* Dictionary::Allocate( int); -template MaybeObject* Dictionary::AtPut( +template MaybeObject* Dictionary::AtPut( uint32_t, Object*); -template Object* Dictionary::SlowReverseLookup( - Object*); +template MaybeObject* Dictionary:: + AtPut(uint32_t, Object*); + +template Object* Dictionary:: + SlowReverseLookup(Object* value); template Object* Dictionary::SlowReverseLookup( Object*); -template void Dictionary::CopyKeysTo( +template void Dictionary::CopyKeysTo( FixedArray*, PropertyAttributes, - Dictionary::SortMode); + Dictionary::SortMode); template Object* Dictionary::DeleteProperty( int, JSObject::DeleteMode); -template Object* Dictionary::DeleteProperty( - int, JSObject::DeleteMode); +template Object* Dictionary:: + DeleteProperty(int, JSObject::DeleteMode); template MaybeObject* Dictionary::Shrink( String*); -template MaybeObject* Dictionary::Shrink( +template MaybeObject* Dictionary::Shrink( uint32_t); template void Dictionary::CopyKeysTo( @@ -10026,32 +10042,41 @@ template MaybeObject* Dictionary::GenerateNewEnumerationIndices(); template int -Dictionary::NumberOfElementsFilterAttributes( - PropertyAttributes); +Dictionary:: + NumberOfElementsFilterAttributes(PropertyAttributes); + +template MaybeObject* Dictionary::Add( + uint32_t, Object*, PropertyDetails); -template MaybeObject* Dictionary::Add( +template MaybeObject* Dictionary::Add( uint32_t, Object*, PropertyDetails); -template MaybeObject* Dictionary:: +template MaybeObject* Dictionary:: + EnsureCapacity(int, uint32_t); + +template MaybeObject* Dictionary:: EnsureCapacity(int, uint32_t); template MaybeObject* Dictionary:: EnsureCapacity(int, String*); -template MaybeObject* Dictionary::AddEntry( - uint32_t, Object*, PropertyDetails, uint32_t); +template MaybeObject* Dictionary:: + AddEntry(uint32_t, Object*, PropertyDetails, uint32_t); + +template MaybeObject* Dictionary:: + AddEntry(uint32_t, Object*, PropertyDetails, uint32_t); template MaybeObject* Dictionary::AddEntry( String*, Object*, PropertyDetails, uint32_t); template -int Dictionary::NumberOfEnumElements(); +int Dictionary::NumberOfEnumElements(); template int Dictionary::NumberOfEnumElements(); template -int HashTable::FindEntry(uint32_t); +int HashTable::FindEntry(uint32_t); // Collates undefined and unexisting elements below limit from position @@ -10061,7 +10086,7 @@ MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) { // Must stay in dictionary mode, either because of requires_slow_elements, // or because we are not going to sort (and therefore compact) all of the // elements. - NumberDictionary* dict = element_dictionary(); + SeededNumberDictionary* dict = element_dictionary(); HeapNumber* result_double = NULL; if (limit > static_cast(Smi::kMaxValue)) { // Allocate space for result before we start mutating the object. @@ -10074,10 +10099,10 @@ MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) { Object* obj; { MaybeObject* maybe_obj = - NumberDictionary::Allocate(dict->NumberOfElements()); + SeededNumberDictionary::Allocate(dict->NumberOfElements()); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } - NumberDictionary* new_dict = NumberDictionary::cast(obj); + SeededNumberDictionary* new_dict = SeededNumberDictionary::cast(obj); AssertNoAllocation no_alloc; @@ -10163,7 +10188,7 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { if (HasDictionaryElements()) { // Convert to fast elements containing only the existing properties. // Ordering is irrelevant, since we are going to sort anyway. - NumberDictionary* dict = element_dictionary(); + SeededNumberDictionary* dict = element_dictionary(); if (IsJSArray() || dict->requires_slow_elements() || dict->max_number_key() >= limit) { return PrepareSlowElementsForSort(limit); @@ -10576,7 +10601,7 @@ bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) { bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1, uint32_t c2, String** symbol) { - TwoCharHashTableKey key(c1, c2, GetHeap()->StringHashSeed()); + TwoCharHashTableKey key(c1, c2, GetHeap()->HashSeed()); int entry = FindEntry(&key); if (entry == kNotFound) { return false; @@ -10591,14 +10616,14 @@ bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1, MaybeObject* SymbolTable::LookupSymbol(Vector str, Object** s) { - Utf8SymbolKey key(str, GetHeap()->StringHashSeed()); + Utf8SymbolKey key(str, GetHeap()->HashSeed()); return LookupKey(&key, s); } MaybeObject* SymbolTable::LookupAsciiSymbol(Vector str, Object** s) { - AsciiSymbolKey key(str, GetHeap()->StringHashSeed()); + AsciiSymbolKey key(str, GetHeap()->HashSeed()); return LookupKey(&key, s); } @@ -10607,14 +10632,14 @@ MaybeObject* SymbolTable::LookupSubStringAsciiSymbol(Handle str, int from, int length, Object** s) { - SubStringAsciiSymbolKey key(str, from, length, GetHeap()->StringHashSeed()); + SubStringAsciiSymbolKey key(str, from, length, GetHeap()->HashSeed()); return LookupKey(&key, s); } MaybeObject* SymbolTable::LookupTwoByteSymbol(Vector str, Object** s) { - TwoByteSymbolKey key(str, GetHeap()->StringHashSeed()); + TwoByteSymbolKey key(str, GetHeap()->HashSeed()); return LookupKey(&key, s); } @@ -10905,7 +10930,7 @@ MaybeObject* Dictionary::EnsureCapacity(int n, Key key) { } -void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) { +void SeededNumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) { // Do nothing if the interval [from, to) is empty. if (from >= to) return; @@ -10971,8 +10996,9 @@ MaybeObject* Dictionary::AtPut(Key key, Object* value) { if (!maybe_k->ToObject(&k)) return maybe_k; } PropertyDetails details = PropertyDetails(NONE, NORMAL); - return Dictionary::cast(obj)-> - AddEntry(key, value, details, Shape::Hash(key)); + + return Dictionary::cast(obj)->AddEntry(key, value, details, + Dictionary::Hash(key)); } @@ -10987,8 +11013,9 @@ MaybeObject* Dictionary::Add(Key key, { MaybeObject* maybe_obj = EnsureCapacity(1, key); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } - return Dictionary::cast(obj)-> - AddEntry(key, value, details, Shape::Hash(key)); + + return Dictionary::cast(obj)->AddEntry(key, value, details, + Dictionary::Hash(key)); } @@ -11021,7 +11048,7 @@ MaybeObject* Dictionary::AddEntry(Key key, } -void NumberDictionary::UpdateMaxNumberKey(uint32_t key) { +void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key) { // If the dictionary requires slow elements an element has already // been added at a high index. if (requires_slow_elements()) return; @@ -11040,31 +11067,44 @@ void NumberDictionary::UpdateMaxNumberKey(uint32_t key) { } -MaybeObject* NumberDictionary::AddNumberEntry(uint32_t key, - Object* value, - PropertyDetails details) { +MaybeObject* SeededNumberDictionary::AddNumberEntry(uint32_t key, + Object* value, + PropertyDetails details) { UpdateMaxNumberKey(key); SLOW_ASSERT(this->FindEntry(key) == kNotFound); return Add(key, value, details); } -MaybeObject* NumberDictionary::AtNumberPut(uint32_t key, Object* value) { +MaybeObject* UnseededNumberDictionary::AddNumberEntry(uint32_t key, + Object* value) { + SLOW_ASSERT(this->FindEntry(key) == kNotFound); + return Add(key, value, PropertyDetails(NONE, NORMAL)); +} + + +MaybeObject* SeededNumberDictionary::AtNumberPut(uint32_t key, Object* value) { UpdateMaxNumberKey(key); return AtPut(key, value); } -MaybeObject* NumberDictionary::Set(uint32_t key, - Object* value, - PropertyDetails details) { +MaybeObject* UnseededNumberDictionary::AtNumberPut(uint32_t key, + Object* value) { + return AtPut(key, value); +} + + +MaybeObject* SeededNumberDictionary::Set(uint32_t key, + Object* value, + PropertyDetails details) { int entry = FindEntry(key); if (entry == kNotFound) return AddNumberEntry(key, value, details); // Preserve enumeration index. details = PropertyDetails(details.attributes(), details.type(), DetailsAt(entry).index()); - MaybeObject* maybe_object_key = NumberDictionaryShape::AsObject(key); + MaybeObject* maybe_object_key = SeededNumberDictionaryShape::AsObject(key); Object* object_key; if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key; SetEntry(entry, object_key, value, details); @@ -11072,6 +11112,18 @@ MaybeObject* NumberDictionary::Set(uint32_t key, } +MaybeObject* UnseededNumberDictionary::Set(uint32_t key, + Object* value) { + int entry = FindEntry(key); + if (entry == kNotFound) return AddNumberEntry(key, value); + MaybeObject* maybe_object_key = UnseededNumberDictionaryShape::AsObject(key); + Object* object_key; + if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key; + SetEntry(entry, object_key, value); + return this; +} + + template int Dictionary::NumberOfElementsFilterAttributes( diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 2fb2e00651..6a82a90812 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -1541,7 +1541,7 @@ class JSObject: public JSReceiver { bool HasFastArgumentsElements(); bool HasDictionaryArgumentsElements(); inline bool AllowsSetElementsLength(); - inline NumberDictionary* element_dictionary(); // Gets slow elements. + inline SeededNumberDictionary* element_dictionary(); // Gets slow elements. // Requires: HasFastElements(). MUST_USE_RESULT inline MaybeObject* EnsureWritableFastElements(); @@ -1902,8 +1902,6 @@ class JSObject: public JSReceiver { PropertyNormalizationMode mode, int expected_additional_properties); - // Convert and update the elements backing store to be a NumberDictionary - // dictionary. Returns the backing after conversion. MUST_USE_RESULT MaybeObject* NormalizeElements(); MUST_USE_RESULT MaybeObject* UpdateMapCodeCache(String* name, Code* code); @@ -2221,7 +2219,7 @@ class FixedDoubleArray: public FixedArrayBase { public: inline void Initialize(FixedArray* from); inline void Initialize(FixedDoubleArray* from); - inline void Initialize(NumberDictionary* from); + inline void Initialize(SeededNumberDictionary* from); // Setter and getter for elements. inline double get_scalar(int index); @@ -2514,9 +2512,46 @@ class DescriptorArray: public FixedArray { // beginning of the backing storage that can be used for non-element // information by subclasses. +template +class BaseShape { + public: + static const bool UsesSeed = false; + static uint32_t Hash(Key key) { return 0; } + static uint32_t SeededHash(Key key, uint32_t seed) { + ASSERT(UsesSeed); + return Hash(key); + } + static uint32_t HashForObject(Key key, Object* object) { return 0; } + static uint32_t SeededHashForObject(Key key, uint32_t seed, Object* object) { + // Won't be called if UsesSeed isn't overridden by child class. + return HashForObject(key, object); + } +}; + template class HashTable: public FixedArray { public: + // Wrapper methods + inline uint32_t Hash(Key key) { + if (Shape::UsesSeed) { + // I'm using map()->heap() to skip is_safe_to_read_maps assertion. + // That was done, because NumberDictionary is used inside GC. + return Shape::SeededHash(key, map()->heap()->HashSeed()); + } else { + return Shape::Hash(key); + } + } + + inline uint32_t HashForObject(Key key, Object* object) { + if (Shape::UsesSeed) { + // I'm using map()->heap() to skip is_safe_to_read_maps assertion. + // That was done, because NumberDictionary is used inside GC. + return Shape::SeededHashForObject(key, map()->heap()->HashSeed(), object); + } else { + return Shape::HashForObject(key, object); + } + } + // Returns the number of elements in the hash table. int NumberOfElements() { return Smi::cast(get(kNumberOfElementsIndex))->value(); @@ -2658,7 +2693,6 @@ class HashTable: public FixedArray { }; - // HashTableKey is an abstract superclass for virtual key behavior. class HashTableKey { public: @@ -2675,7 +2709,8 @@ class HashTableKey { virtual ~HashTableKey() {} }; -class SymbolTableShape { + +class SymbolTableShape : public BaseShape { public: static inline bool IsMatch(HashTableKey* key, Object* value) { return key->IsMatch(value); @@ -2734,7 +2769,7 @@ class SymbolTable: public HashTable { }; -class MapCacheShape { +class MapCacheShape : public BaseShape { public: static inline bool IsMatch(HashTableKey* key, Object* value) { return key->IsMatch(value); @@ -2890,7 +2925,7 @@ class Dictionary: public HashTable { }; -class StringDictionaryShape { +class StringDictionaryShape : public BaseShape { public: static inline bool IsMatch(String* key, Object* other); static inline uint32_t Hash(String* key); @@ -2923,23 +2958,42 @@ class StringDictionary: public Dictionary { }; -class NumberDictionaryShape { +class NumberDictionaryShape : public BaseShape { public: static inline bool IsMatch(uint32_t key, Object* other); - static inline uint32_t Hash(uint32_t key); - static inline uint32_t HashForObject(uint32_t key, Object* object); MUST_USE_RESULT static inline MaybeObject* AsObject(uint32_t key); - static const int kPrefixSize = 2; static const int kEntrySize = 3; static const bool kIsEnumerable = false; }; -class NumberDictionary: public Dictionary { +class SeededNumberDictionaryShape : public NumberDictionaryShape { public: - static NumberDictionary* cast(Object* obj) { + static const bool UsesSeed = true; + static const int kPrefixSize = 2; + + static inline uint32_t SeededHash(uint32_t key, uint32_t seed); + static inline uint32_t SeededHashForObject(uint32_t key, + uint32_t seed, + Object* object); +}; + + +class UnseededNumberDictionaryShape : public NumberDictionaryShape { + public: + static const int kPrefixSize = 0; + + static inline uint32_t Hash(uint32_t key); + static inline uint32_t HashForObject(uint32_t key, Object* object); +}; + + +class SeededNumberDictionary + : public Dictionary { + public: + static SeededNumberDictionary* cast(Object* obj) { ASSERT(obj->IsDictionary()); - return reinterpret_cast(obj); + return reinterpret_cast(obj); } // Type specific at put (default NONE attributes is used when adding). @@ -2978,7 +3032,24 @@ class NumberDictionary: public Dictionary { }; -class ObjectHashTableShape { +class UnseededNumberDictionary + : public Dictionary { + public: + static UnseededNumberDictionary* cast(Object* obj) { + ASSERT(obj->IsDictionary()); + return reinterpret_cast(obj); + } + + // Type specific at put (default NONE attributes is used when adding). + MUST_USE_RESULT MaybeObject* AtNumberPut(uint32_t key, Object* value); + MUST_USE_RESULT MaybeObject* AddNumberEntry(uint32_t key, Object* value); + + // Set an existing entry or add a new one if needed. + MUST_USE_RESULT MaybeObject* Set(uint32_t key, Object* value); +}; + + +class ObjectHashTableShape : public BaseShape { public: static inline bool IsMatch(JSObject* key, Object* other); static inline uint32_t Hash(JSObject* key); @@ -5543,7 +5614,7 @@ class JSRegExp: public JSObject { }; -class CompilationCacheShape { +class CompilationCacheShape : public BaseShape { public: static inline bool IsMatch(HashTableKey* key, Object* value) { return key->IsMatch(value); @@ -5643,7 +5714,7 @@ class CodeCache: public Struct { }; -class CodeCacheHashTableShape { +class CodeCacheHashTableShape : public BaseShape { public: static inline bool IsMatch(HashTableKey* key, Object* value) { return key->IsMatch(value); diff --git a/deps/v8/src/profile-generator.cc b/deps/v8/src/profile-generator.cc index ed1b90a2e7..e319efb043 100644 --- a/deps/v8/src/profile-generator.cc +++ b/deps/v8/src/profile-generator.cc @@ -111,7 +111,7 @@ const char* StringsStorage::GetCopy(const char* src) { OS::StrNCpy(dst, src, len); dst[len] = '\0'; uint32_t hash = - HashSequentialString(dst.start(), len, HEAP->StringHashSeed()); + HashSequentialString(dst.start(), len, HEAP->HashSeed()); return AddOrDisposeString(dst.start(), hash); } @@ -145,7 +145,7 @@ const char* StringsStorage::GetVFormatted(const char* format, va_list args) { return format; } uint32_t hash = HashSequentialString( - str.start(), len, HEAP->StringHashSeed()); + str.start(), len, HEAP->HashSeed()); return AddOrDisposeString(str.start(), hash); } @@ -178,18 +178,21 @@ void CodeEntry::CopyData(const CodeEntry& source) { uint32_t CodeEntry::GetCallUid() const { - uint32_t hash = ComputeIntegerHash(tag_); + uint32_t hash = ComputeIntegerHash(tag_, v8::internal::kZeroHashSeed); if (shared_id_ != 0) { - hash ^= ComputeIntegerHash( - static_cast(shared_id_)); + hash ^= ComputeIntegerHash(static_cast(shared_id_), + v8::internal::kZeroHashSeed); } else { hash ^= ComputeIntegerHash( - static_cast(reinterpret_cast(name_prefix_))); + static_cast(reinterpret_cast(name_prefix_)), + v8::internal::kZeroHashSeed); hash ^= ComputeIntegerHash( - static_cast(reinterpret_cast(name_))); + static_cast(reinterpret_cast(name_)), + v8::internal::kZeroHashSeed); hash ^= ComputeIntegerHash( - static_cast(reinterpret_cast(resource_name_))); - hash ^= ComputeIntegerHash(line_number_); + static_cast(reinterpret_cast(resource_name_)), + v8::internal::kZeroHashSeed); + hash ^= ComputeIntegerHash(line_number_, v8::internal::kZeroHashSeed); } return hash; } @@ -1213,7 +1216,7 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, entries_sorted_(false) { STATIC_ASSERT( sizeof(HeapGraphEdge) == - SnapshotSizeConstants::kExpectedHeapGraphEdgeSize); // NOLINT + SnapshotSizeConstants::kExpectedHeapGraphEdgeSize); STATIC_ASSERT( sizeof(HeapEntry) == SnapshotSizeConstants::kExpectedHeapEntrySize); // NOLINT @@ -1466,10 +1469,11 @@ uint64_t HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) { const char* label = info->GetLabel(); id ^= HashSequentialString(label, static_cast(strlen(label)), - HEAP->StringHashSeed()); + HEAP->HashSeed()); intptr_t element_count = info->GetElementCount(); if (element_count != -1) - id ^= ComputeIntegerHash(static_cast(element_count)); + id ^= ComputeIntegerHash(static_cast(element_count), + v8::internal::kZeroHashSeed); return id << 1; } @@ -2131,7 +2135,7 @@ void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj, } } } else if (js_obj->HasDictionaryElements()) { - NumberDictionary* dictionary = js_obj->element_dictionary(); + SeededNumberDictionary* dictionary = js_obj->element_dictionary(); int length = dictionary->Capacity(); for (int i = 0; i < length; ++i) { Object* k = dictionary->KeyAt(i); diff --git a/deps/v8/src/profile-generator.h b/deps/v8/src/profile-generator.h index da1fdc33ef..0beb109e24 100644 --- a/deps/v8/src/profile-generator.h +++ b/deps/v8/src/profile-generator.h @@ -735,7 +735,8 @@ class HeapObjectsMap { static uint32_t AddressHash(Address addr) { return ComputeIntegerHash( - static_cast(reinterpret_cast(addr))); + static_cast(reinterpret_cast(addr)), + v8::internal::kZeroHashSeed); } bool initial_fill_mode_; @@ -836,7 +837,8 @@ class HeapEntriesMap { static uint32_t Hash(HeapThing thing) { return ComputeIntegerHash( - static_cast(reinterpret_cast(thing))); + static_cast(reinterpret_cast(thing)), + v8::internal::kZeroHashSeed); } static bool HeapThingsMatch(HeapThing key1, HeapThing key2) { return key1 == key2; @@ -1018,7 +1020,8 @@ class NativeObjectsExplorer : public HeapEntriesAllocator { void VisitSubtreeWrapper(Object** p, uint16_t class_id); static uint32_t InfoHash(v8::RetainedObjectInfo* info) { - return ComputeIntegerHash(static_cast(info->GetHash())); + return ComputeIntegerHash(static_cast(info->GetHash()), + v8::internal::kZeroHashSeed); } static bool RetainedInfosMatch(void* key1, void* key2) { return key1 == key2 || @@ -1096,7 +1099,8 @@ class HeapSnapshotJSONSerializer { INLINE(static uint32_t ObjectHash(const void* key)) { return ComputeIntegerHash( - static_cast(reinterpret_cast(key))); + static_cast(reinterpret_cast(key)), + v8::internal::kZeroHashSeed); } void EnumerateNodes(); diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index 813f98f684..b1c4c102ec 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -202,7 +202,7 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, break; } case DICTIONARY_ELEMENTS: { - NumberDictionary* element_dictionary = copy->element_dictionary(); + SeededNumberDictionary* element_dictionary = copy->element_dictionary(); int capacity = element_dictionary->Capacity(); for (int i = 0; i < capacity; i++) { Object* k = element_dictionary->KeyAt(i); @@ -978,14 +978,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOwnProperty) { holder = Handle(JSObject::cast(proto)); } FixedArray* elements = FixedArray::cast(holder->elements()); - NumberDictionary* dictionary = NULL; + SeededNumberDictionary* dictionary = NULL; if (elements->map() == heap->non_strict_arguments_elements_map()) { - dictionary = NumberDictionary::cast(elements->get(1)); + dictionary = SeededNumberDictionary::cast(elements->get(1)); } else { - dictionary = NumberDictionary::cast(elements); + dictionary = SeededNumberDictionary::cast(elements); } int entry = dictionary->FindEntry(index); - ASSERT(entry != NumberDictionary::kNotFound); + ASSERT(entry != SeededNumberDictionary::kNotFound); PropertyDetails details = dictionary->DetailsAt(entry); switch (details.type()) { case CALLBACKS: { @@ -4342,12 +4342,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) { return isolate->Throw(*error); } - Handle dictionary = NormalizeElements(js_object); + Handle dictionary = NormalizeElements(js_object); // Make sure that we never go back to fast case. dictionary->set_requires_slow_elements(); PropertyDetails details = PropertyDetails(attr, NORMAL); - Handle extended_dictionary = - NumberDictionarySet(dictionary, index, obj_value, details); + Handle extended_dictionary = + SeededNumberDictionarySet(dictionary, index, obj_value, details); if (*extended_dictionary != *dictionary) { if (js_object->GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS) { FixedArray::cast(js_object->elements())->set(1, *extended_dictionary); @@ -4408,12 +4408,12 @@ static MaybeObject* NormalizeObjectSetElement(Isolate* isolate, Handle value, PropertyAttributes attr) { // Normalize the elements to enable attributes on the property. - Handle dictionary = NormalizeElements(js_object); + Handle dictionary = NormalizeElements(js_object); // Make sure that we never go back to fast case. dictionary->set_requires_slow_elements(); PropertyDetails details = PropertyDetails(attr, NORMAL); - Handle extended_dictionary = - NumberDictionarySet(dictionary, index, value, details); + Handle extended_dictionary = + SeededNumberDictionarySet(dictionary, index, value, details); if (*extended_dictionary != *dictionary) { js_object->set_elements(*extended_dictionary); } @@ -9485,8 +9485,9 @@ class ArrayConcatVisitor { // Fall-through to dictionary mode. } ASSERT(!fast_elements_); - Handle dict(NumberDictionary::cast(*storage_)); - Handle result = + Handle dict( + SeededNumberDictionary::cast(*storage_)); + Handle result = isolate_->factory()->DictionaryAtNumberPut(dict, index, elm); if (!result.is_identical_to(dict)) { // Dictionary needed to grow. @@ -9524,14 +9525,15 @@ class ArrayConcatVisitor { void SetDictionaryMode(uint32_t index) { ASSERT(fast_elements_); Handle current_storage(*storage_); - Handle slow_storage( - isolate_->factory()->NewNumberDictionary(current_storage->length())); + Handle slow_storage( + isolate_->factory()->NewSeededNumberDictionary( + current_storage->length())); uint32_t current_length = static_cast(current_storage->length()); for (uint32_t i = 0; i < current_length; i++) { HandleScope loop_scope; Handle element(current_storage->get(i)); if (!element->IsTheHole()) { - Handle new_storage = + Handle new_storage = isolate_->factory()->DictionaryAtNumberPut(slow_storage, i, element); if (!new_storage.is_identical_to(slow_storage)) { slow_storage = loop_scope.CloseAndEscape(new_storage); @@ -9578,8 +9580,8 @@ static uint32_t EstimateElementCount(Handle array) { break; } case DICTIONARY_ELEMENTS: { - Handle dictionary( - NumberDictionary::cast(array->elements())); + Handle dictionary( + SeededNumberDictionary::cast(array->elements())); int capacity = dictionary->Capacity(); for (int i = 0; i < capacity; i++) { Handle key(dictionary->KeyAt(i)); @@ -9667,7 +9669,8 @@ static void CollectElementIndices(Handle object, break; } case DICTIONARY_ELEMENTS: { - Handle dict(NumberDictionary::cast(object->elements())); + Handle dict( + SeededNumberDictionary::cast(object->elements())); uint32_t capacity = dict->Capacity(); for (uint32_t j = 0; j < capacity; j++) { HandleScope loop_scope; @@ -9796,7 +9799,7 @@ static bool IterateElements(Isolate* isolate, break; } case DICTIONARY_ELEMENTS: { - Handle dict(receiver->element_dictionary()); + Handle dict(receiver->element_dictionary()); List indices(dict->Capacity() / 2); // Collect all indices in the object and the prototypes less // than length. This might introduce duplicates in the indices list. @@ -9945,7 +9948,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayConcat) { uint32_t at_least_space_for = estimate_nof_elements + (estimate_nof_elements >> 2); storage = Handle::cast( - isolate->factory()->NewNumberDictionary(at_least_space_for)); + isolate->factory()->NewSeededNumberDictionary(at_least_space_for)); } ArrayConcatVisitor visitor(isolate, storage, fast_case); @@ -10031,7 +10034,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_EstimateNumberOfElements) { CONVERT_CHECKED(JSObject, object, args[0]); HeapObject* elements = object->elements(); if (elements->IsDictionary()) { - return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements()); + int result = SeededNumberDictionary::cast(elements)->NumberOfElements(); + return Smi::FromInt(result); } else if (object->IsJSArray()) { return JSArray::cast(object)->length(); } else { diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc index 55963303c4..cdb4874fcb 100644 --- a/deps/v8/src/stub-cache.cc +++ b/deps/v8/src/stub-cache.cc @@ -860,7 +860,7 @@ MaybeObject* StubCache::ComputeCallGlobal(int argc, static Object* GetProbeValue(Isolate* isolate, Code::Flags flags) { // Use raw_unchecked... so we don't get assert failures during GC. - NumberDictionary* dictionary = + UnseededNumberDictionary* dictionary = isolate->heap()->raw_unchecked_non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate, flags); if (entry != -1) return dictionary->ValueAt(entry); @@ -882,7 +882,8 @@ MUST_USE_RESULT static MaybeObject* ProbeCache(Isolate* isolate, heap->undefined_value()); if (!maybe_result->ToObject(&result)) return maybe_result; } - heap->public_set_non_monomorphic_cache(NumberDictionary::cast(result)); + heap->public_set_non_monomorphic_cache( + UnseededNumberDictionary::cast(result)); return probe; } diff --git a/deps/v8/src/type-info.cc b/deps/v8/src/type-info.cc index c64368e599..4df7ece086 100644 --- a/deps/v8/src/type-info.cc +++ b/deps/v8/src/type-info.cc @@ -69,7 +69,7 @@ TypeFeedbackOracle::TypeFeedbackOracle(Handle code, Handle TypeFeedbackOracle::GetInfo(unsigned ast_id) { int entry = dictionary_->FindEntry(ast_id); - return entry != NumberDictionary::kNotFound + return entry != UnseededNumberDictionary::kNotFound ? Handle(dictionary_->ValueAt(entry)) : Isolate::Current()->factory()->undefined_value(); } @@ -470,7 +470,7 @@ void TypeFeedbackOracle::CreateDictionary(Handle code, ZoneList* infos) { DisableAssertNoAllocation allocation_allowed; byte* old_start = code->instruction_start(); - dictionary_ = FACTORY->NewNumberDictionary(infos->length()); + dictionary_ = FACTORY->NewUnseededNumberDictionary(infos->length()); byte* new_start = code->instruction_start(); RelocateRelocInfos(infos, old_start, new_start); } @@ -536,7 +536,7 @@ void TypeFeedbackOracle::ProcessTarget(unsigned ast_id, Code* target) { void TypeFeedbackOracle::SetInfo(unsigned ast_id, Object* target) { - ASSERT(dictionary_->FindEntry(ast_id) == NumberDictionary::kNotFound); + ASSERT(dictionary_->FindEntry(ast_id) == UnseededNumberDictionary::kNotFound); MaybeObject* maybe_result = dictionary_->AtNumberPut(ast_id, target); USE(maybe_result); #ifdef DEBUG diff --git a/deps/v8/src/type-info.h b/deps/v8/src/type-info.h index 448e4c94e7..a0317402b0 100644 --- a/deps/v8/src/type-info.h +++ b/deps/v8/src/type-info.h @@ -280,7 +280,7 @@ class TypeFeedbackOracle BASE_EMBEDDED { Handle GetInfo(unsigned ast_id); Handle global_context_; - Handle dictionary_; + Handle dictionary_; DISALLOW_COPY_AND_ASSIGN(TypeFeedbackOracle); }; diff --git a/deps/v8/src/utils.h b/deps/v8/src/utils.h index 26c522b89f..cf7819e4a2 100644 --- a/deps/v8/src/utils.h +++ b/deps/v8/src/utils.h @@ -237,10 +237,13 @@ class BitField { // ---------------------------------------------------------------------------- // Hash function. +static const uint32_t kZeroHashSeed = 0; + // Thomas Wang, Integer Hash Functions. // http://www.concentric.net/~Ttwang/tech/inthash.htm -static inline uint32_t ComputeIntegerHash(uint32_t key) { +static inline uint32_t ComputeIntegerHash(uint32_t key, uint32_t seed) { uint32_t hash = key; + hash = hash ^ seed; hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1; hash = hash ^ (hash >> 12); hash = hash + (hash << 2); @@ -253,7 +256,8 @@ static inline uint32_t ComputeIntegerHash(uint32_t key) { static inline uint32_t ComputePointerHash(void* ptr) { return ComputeIntegerHash( - static_cast(reinterpret_cast(ptr))); + static_cast(reinterpret_cast(ptr)), + v8::internal::kZeroHashSeed); } diff --git a/deps/v8/src/v8globals.h b/deps/v8/src/v8globals.h index eb5c49d751..bf843e5f36 100644 --- a/deps/v8/src/v8globals.h +++ b/deps/v8/src/v8globals.h @@ -131,7 +131,8 @@ class FixedArray; class FunctionEntry; class FunctionLiteral; class FunctionTemplateInfo; -class NumberDictionary; +class SeededNumberDictionary; +class UnseededNumberDictionary; class StringDictionary; template class Handle; class Heap; diff --git a/deps/v8/src/x64/code-stubs-x64.cc b/deps/v8/src/x64/code-stubs-x64.cc index 947cda4a0e..347c17f799 100644 --- a/deps/v8/src/x64/code-stubs-x64.cc +++ b/deps/v8/src/x64/code-stubs-x64.cc @@ -4610,7 +4610,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, Register character, Register scratch) { // hash = (seed + character) + ((seed + character) << 10); - __ LoadRoot(scratch, Heap::kStringHashSeedRootIndex); + __ LoadRoot(scratch, Heap::kHashSeedRootIndex); __ SmiToInteger32(scratch, scratch); __ addl(scratch, character); __ movl(hash, scratch); diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc index 9cfc9b6588..8fcad23272 100644 --- a/deps/v8/src/x64/macro-assembler-x64.cc +++ b/deps/v8/src/x64/macro-assembler-x64.cc @@ -3210,6 +3210,42 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, } +void MacroAssembler::GetNumberHash(Register r0, Register scratch) { + // First of all we assign the hash seed to scratch. + LoadRoot(scratch, Heap::kHashSeedRootIndex); + SmiToInteger32(scratch, scratch); + + // Xor original key with a seed. + xorl(r0, scratch); + + // Compute the hash code from the untagged key. This must be kept in sync + // with ComputeIntegerHash in utils.h. + // + // hash = ~hash + (hash << 15); + movl(scratch, r0); + notl(r0); + shll(scratch, Immediate(15)); + addl(r0, scratch); + // hash = hash ^ (hash >> 12); + movl(scratch, r0); + shrl(scratch, Immediate(12)); + xorl(r0, scratch); + // hash = hash + (hash << 2); + leal(r0, Operand(r0, r0, times_4, 0)); + // hash = hash ^ (hash >> 4); + movl(scratch, r0); + shrl(scratch, Immediate(4)); + xorl(r0, scratch); + // hash = hash * 2057; + imull(r0, r0, Immediate(2057)); + // hash = hash ^ (hash >> 16); + movl(scratch, r0); + shrl(scratch, Immediate(16)); + xorl(r0, scratch); +} + + + void MacroAssembler::LoadFromNumberDictionary(Label* miss, Register elements, Register key, @@ -3240,34 +3276,11 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, Label done; - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash << 15); - movl(r1, r0); - notl(r0); - shll(r1, Immediate(15)); - addl(r0, r1); - // hash = hash ^ (hash >> 12); - movl(r1, r0); - shrl(r1, Immediate(12)); - xorl(r0, r1); - // hash = hash + (hash << 2); - leal(r0, Operand(r0, r0, times_4, 0)); - // hash = hash ^ (hash >> 4); - movl(r1, r0); - shrl(r1, Immediate(4)); - xorl(r0, r1); - // hash = hash * 2057; - imull(r0, r0, Immediate(2057)); - // hash = hash ^ (hash >> 16); - movl(r1, r0); - shrl(r1, Immediate(16)); - xorl(r0, r1); + GetNumberHash(r0, r1); // Compute capacity mask. - SmiToInteger32(r1, - FieldOperand(elements, NumberDictionary::kCapacityOffset)); + SmiToInteger32(r1, FieldOperand(elements, + SeededNumberDictionary::kCapacityOffset)); decl(r1); // Generate an unrolled loop that performs a few probes before giving up. @@ -3277,19 +3290,19 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, movq(r2, r0); // Compute the masked index: (hash + i + i * i) & mask. if (i > 0) { - addl(r2, Immediate(NumberDictionary::GetProbeOffset(i))); + addl(r2, Immediate(SeededNumberDictionary::GetProbeOffset(i))); } and_(r2, r1); // Scale the index by multiplying by the entry size. - ASSERT(NumberDictionary::kEntrySize == 3); + ASSERT(SeededNumberDictionary::kEntrySize == 3); lea(r2, Operand(r2, r2, times_2, 0)); // r2 = r2 * 3 // Check if the key matches. cmpq(key, FieldOperand(elements, r2, times_pointer_size, - NumberDictionary::kElementsStartOffset)); + SeededNumberDictionary::kElementsStartOffset)); if (i != (kProbes - 1)) { j(equal, &done); } else { @@ -3300,7 +3313,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, bind(&done); // Check that the value is a normal propety. const int kDetailsOffset = - NumberDictionary::kElementsStartOffset + 2 * kPointerSize; + SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize; ASSERT_EQ(NORMAL, 0); Test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset), Smi::FromInt(PropertyDetails::TypeField::kMask)); @@ -3308,7 +3321,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, // Get the value at the masked, scaled index. const int kValueOffset = - NumberDictionary::kElementsStartOffset + kPointerSize; + SeededNumberDictionary::kElementsStartOffset + kPointerSize; movq(result, FieldOperand(elements, r2, times_pointer_size, kValueOffset)); } diff --git a/deps/v8/src/x64/macro-assembler-x64.h b/deps/v8/src/x64/macro-assembler-x64.h index e7eb104c0b..ff6edc5b33 100644 --- a/deps/v8/src/x64/macro-assembler-x64.h +++ b/deps/v8/src/x64/macro-assembler-x64.h @@ -845,6 +845,7 @@ class MacroAssembler: public Assembler { Register scratch, Label* miss); + void GetNumberHash(Register r0, Register scratch); void LoadFromNumberDictionary(Label* miss, Register elements, diff --git a/deps/v8/test/cctest/test-hashing.cc b/deps/v8/test/cctest/test-hashing.cc index 9aa84614d6..a6265105eb 100644 --- a/deps/v8/test/cctest/test-hashing.cc +++ b/deps/v8/test/cctest/test-hashing.cc @@ -117,6 +117,41 @@ void generate(MacroAssembler* masm, i::Vector string) { } +void generate(MacroAssembler* masm, uint32_t key) { +#ifdef V8_TARGET_ARCH_IA32 + __ push(ebx); + __ mov(eax, Immediate(key)); + __ GetNumberHash(eax, ebx); + __ pop(ebx); + __ Ret(); +#elif V8_TARGET_ARCH_X64 + __ push(kRootRegister); + __ InitializeRootRegister(); + __ push(rbx); + __ movq(rax, Immediate(key)); + __ GetNumberHash(rax, rbx); + __ pop(rbx); + __ pop(kRootRegister); + __ Ret(); +#elif V8_TARGET_ARCH_ARM + __ push(kRootRegister); + __ InitializeRootRegister(); + __ mov(r0, Operand(key)); + __ GetNumberHash(r0, ip); + __ pop(kRootRegister); + __ mov(pc, Operand(lr)); +#elif V8_TARGET_ARCH_MIPS + __ push(kRootRegister); + __ InitializeRootRegister(); + __ li(v0, Operand(key)); + __ GetNumberHash(v0, t1); + __ pop(kRootRegister); + __ jr(ra); + __ nop(); +#endif +} + + void check(i::Vector string) { v8::HandleScope scope; v8::internal::byte buffer[2048]; @@ -146,12 +181,47 @@ void check(i::Vector string) { } +void check(uint32_t key) { + v8::HandleScope scope; + v8::internal::byte buffer[2048]; + MacroAssembler masm(Isolate::Current(), buffer, sizeof buffer); + + generate(&masm, key); + + CodeDesc desc; + masm.GetCode(&desc); + Code* code = Code::cast(HEAP->CreateCode( + desc, + Code::ComputeFlags(Code::STUB), + Handle(HEAP->undefined_value()))->ToObjectChecked()); + CHECK(code->IsCode()); + + HASH_FUNCTION hash = FUNCTION_CAST(code->entry()); +#ifdef USE_SIMULATOR + uint32_t codegen_hash = + reinterpret_cast(CALL_GENERATED_CODE(hash, 0, 0, 0, 0, 0)); +#else + uint32_t codegen_hash = hash(); +#endif + + uint32_t runtime_hash = ComputeIntegerHash( + key, + Isolate::Current()->heap()->HashSeed()); + CHECK(runtime_hash == codegen_hash); +} + + void check_twochars(char a, char b) { char ab[2] = {a, b}; check(i::Vector(ab, 2)); } +static uint32_t PseudoRandom(uint32_t i, uint32_t j) { + return ~(~((i * 781) ^ (j * 329))); +} + + TEST(StringHash) { if (env.IsEmpty()) env = v8::Context::New(); for (int a = 0; a < String::kMaxAsciiCharCode; a++) { @@ -169,4 +239,22 @@ TEST(StringHash) { check(i::Vector("-=[ vee eight ftw ]=-", 21)); } + +TEST(NumberHash) { + if (env.IsEmpty()) env = v8::Context::New(); + + // Some specific numbers + for (uint32_t key = 0; key < 42; key += 7) { + check(key); + } + + // Some pseudo-random numbers + static const uint32_t kLimit = 1000; + for (uint32_t i = 0; i < 5; i++) { + for (uint32_t j = 0; j < 5; j++) { + check(PseudoRandom(i, j) % kLimit); + } + } +} + #undef __