diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index b4367602c3..dc11e180ed 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -103,16 +103,20 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, // Generate an unrolled loop that performs a few probes before // giving up. Measurements done on Gmail indicate that 2 probes // cover ~93% of loads from dictionaries. - static const int kProbes = 4; - for (int i = 0; i < kProbes; i++) { - // Compute the masked index: (hash + i + i * i) & mask. + static const uint32_t kProbes = + HashTable::kNofFastProbes; + static const uint32_t kShift = + HashTable::kHashRotateShift; + + for (uint32_t i = 0; i < kProbes; i++) { + // Compute the masked index. __ ldr(t1, FieldMemOperand(r2, String::kLengthOffset)); __ mov(t1, Operand(t1, LSR, String::kHashShift)); if (i > 0) { - __ add(t1, t1, Operand(StringDictionary::GetProbeOffset(i))); + __ and_(t1, r3, Operand(t1, ROR, (kShift * i) % kBitsPerInt)); + } else { + __ and_(t1, t1, Operand(r3)); } - __ and_(t1, t1, Operand(r3)); - // Scale the index by multiplying by the element size. ASSERT(StringDictionary::kEntrySize == 3); __ add(t1, t1, Operand(t1, LSL, 1)); // t1 = t1 * 3 diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc index e5500aad59..58c4282096 100644 --- a/src/arm/simulator-arm.cc +++ b/src/arm/simulator-arm.cc @@ -45,6 +45,7 @@ using ::v8::internal::PrintF; using ::v8::internal::OS; using ::v8::internal::ReadLine; using ::v8::internal::DeleteArray; +using ::v8::internal::RotateRight; // This macro provides a platform independent use of sscanf. The reason for // SScanF not being implemented in a platform independent was through @@ -817,7 +818,12 @@ int32_t Simulator::GetShiftRm(Instr* instr, bool* carry_out) { } case ROR: { - UNIMPLEMENTED(); + ASSERT(shift_amount > 0); + uint32_t uresult = static_cast(result); + uresult = RotateRight(uresult, shift_amount - 1); + *carry_out = (uresult & 1) == 1; + uresult = RotateRight(uresult, 1); + result = static_cast(uresult); break; } diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index 02bde2ae39..5ff66b48ff 100644 --- a/src/ia32/assembler-ia32.cc +++ b/src/ia32/assembler-ia32.cc @@ -1240,6 +1240,16 @@ void Assembler::xor_(const Operand& dst, const Immediate& x) { } +void Assembler::ror(Register dst, uint32_t count) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + EMIT(0xC1); + emit_operand(ecx, Operand(dst)); + ASSERT(count < 32); + EMIT(count); +} + + void Assembler::bt(const Operand& dst, Register src) { EnsureSpace ensure_space(this); last_pc_ = pc_; diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index 70b510e722..86db7d476b 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -597,6 +597,9 @@ class Assembler : public Malloced { void xor_(const Operand& src, Register dst); void xor_(const Operand& dst, const Immediate& x); + // Rotate dist right count times, asserts count < 32. + void ror(Register dst, uint32_t count); + // Bit operations. void bt(const Operand& dst, Register src); void bts(const Operand& dst, Register src); diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index 5ffe13d3fb..56b02956bb 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -97,15 +97,20 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label, // Generate an unrolled loop that performs a few probes before // giving up. Measurements done on Gmail indicate that 2 probes // cover ~93% of loads from dictionaries. - static const int kProbes = 4; const int kElementsStartOffset = Array::kHeaderSize + StringDictionary::kElementsStartIndex * kPointerSize; - for (int i = 0; i < kProbes; i++) { - // Compute the masked index: (hash + i + i * i) & mask. + + static const uint32_t kProbes = + HashTable::kNofFastProbes; + static const uint32_t kShift = + HashTable::kHashRotateShift; + + for (uint32_t i = 0; i < kProbes; i++) { + // Compute the masked index. __ mov(r1, FieldOperand(name, String::kLengthOffset)); __ shr(r1, String::kHashShift); if (i > 0) { - __ add(Operand(r1), Immediate(StringDictionary::GetProbeOffset(i))); + __ ror(r1, (kShift * i) % kBitsPerInt); } __ and_(r1, Operand(r2)); diff --git a/src/objects.cc b/src/objects.cc index 72412c15c7..52438090fa 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -6502,7 +6502,8 @@ template Object* HashTable::Allocate( int at_least_space_for) { int capacity = RoundUpToPowerOf2(at_least_space_for); - if (capacity < 4) capacity = 4; // Guarantee min capacity. + static const int kMinCapacity = 16; + if (capacity < kMinCapacity) capacity = kMinCapacity; Object* obj = Heap::AllocateHashTable(EntryToIndex(capacity)); if (!obj->IsFailure()) { HashTable::cast(obj)->SetNumberOfElements(0); @@ -6516,26 +6517,25 @@ Object* HashTable::Allocate( // Find entry for key otherwise return -1. template int HashTable::FindEntry(Key key) { - uint32_t nof = NumberOfElements(); - if (nof == 0) return kNotFound; // Bail out if empty. - - uint32_t capacity = Capacity(); + uint32_t mask = Capacity() - 1; uint32_t hash = Shape::Hash(key); - uint32_t entry = GetProbe(hash, 0, capacity); - Object* element = KeyAt(entry); - uint32_t passed_elements = 0; - if (!element->IsNull()) { - if (!element->IsUndefined() && Shape::IsMatch(key, element)) return entry; - if (++passed_elements == nof) return kNotFound; + // For the first probes rotate the hash to ensure a proper spread. + for (uint32_t i = 0; i < kNofFastProbes; i++) { + int entry = hash & mask; + Object* element = KeyAt(entry); + if (element->IsUndefined()) return kNotFound; + if (!element->IsNull() && Shape::IsMatch(key, element)) return entry; + hash = RotateRight(hash, kHashRotateShift); } - for (uint32_t i = 1; !element->IsUndefined(); i++) { - entry = GetProbe(hash, i, capacity); - element = KeyAt(entry); - if (!element->IsNull()) { - if (!element->IsUndefined() && Shape::IsMatch(key, element)) return entry; - if (++passed_elements == nof) return kNotFound; - } + + + // In this unlikely event, do a linear scan. + for (uint32_t i = 1; i <= mask; i++) { + int entry = ++hash & mask; + Object* element = KeyAt(entry); + if (element->IsUndefined()) return kNotFound; + if (!element->IsNull() && Shape::IsMatch(key, element)) return entry; } return kNotFound; } @@ -6579,15 +6579,23 @@ Object* HashTable::EnsureCapacity(int n, Key key) { template uint32_t HashTable::FindInsertionEntry(uint32_t hash) { - uint32_t capacity = Capacity(); - uint32_t entry = GetProbe(hash, 0, capacity); - Object* element = KeyAt(entry); + uint32_t mask = Capacity() - 1; + int entry; + Object* element; - for (uint32_t i = 1; !(element->IsUndefined() || element->IsNull()); i++) { - entry = GetProbe(hash, i, capacity); + // For the first probes rotate the hash to ensure a proper spread. + for (uint32_t i = 0; i < kNofFastProbes; i++) { + entry = hash & mask; element = KeyAt(entry); + if (element->IsUndefined() || element->IsNull()) return entry; + hash = RotateRight(hash, kHashRotateShift); } + do { + entry = ++hash & mask; + element = KeyAt(entry); + } while(!(element->IsUndefined() || element->IsNull())); + return entry; } @@ -6666,6 +6674,10 @@ int Dictionary::NumberOfEnumElements(); template int Dictionary::NumberOfEnumElements(); +template +int HashTable::FindEntry(uint32_t key); + + // Collates undefined and unexisting elements below limit from position // zero of the elements. The object stays in Dictionary mode. Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) { @@ -7021,7 +7033,7 @@ class SymbolsKey : public HashTableKey { uint32_t HashForObject(Object* obj) { FixedArray* symbols = FixedArray::cast(obj); int len = symbols->length(); - uint32_t hash = 0; + uint32_t hash = 40617523; // In case the array is empty. for (int i = 0; i < len; i++) { hash ^= String::cast(symbols->get(i))->Hash(); } diff --git a/src/objects.h b/src/objects.h index 5c76e4a51b..adef97c2e4 100644 --- a/src/objects.h +++ b/src/objects.h @@ -2051,11 +2051,6 @@ class HashTable: public FixedArray { // Casting. static inline HashTable* cast(Object* obj); - // Compute the probe offset (quadratic probing). - INLINE(static uint32_t GetProbeOffset(uint32_t n)) { - return (n + n * n) >> 1; - } - static const int kNumberOfElementsIndex = 0; static const int kCapacityIndex = 1; static const int kPrefixStartIndex = 2; @@ -2071,6 +2066,9 @@ class HashTable: public FixedArray { // Find entry for key otherwise return -1. int FindEntry(Key key); + static const uint32_t kNofFastProbes = 4; + static const uint32_t kHashRotateShift = 3; + protected: // Find the entry at which to insert element with the given key that @@ -2096,13 +2094,6 @@ class HashTable: public FixedArray { fast_set(this, kCapacityIndex, Smi::FromInt(capacity)); } - - // Returns probe entry. - static uint32_t GetProbe(uint32_t hash, uint32_t number, uint32_t size) { - ASSERT(IsPowerOf2(size)); - return (hash + GetProbeOffset(number)) & (size - 1); - } - // Ensure enough space for n additional elements. Object* EnsureCapacity(int n, Key key); }; diff --git a/src/utils.h b/src/utils.h index 91662eea6c..f32951c708 100644 --- a/src/utils.h +++ b/src/utils.h @@ -211,6 +211,21 @@ inline byte* DecodeUnsignedIntBackward(byte* p, unsigned int* x) { uint32_t ComputeIntegerHash(uint32_t key); +// ---------------------------------------------------------------------------- +// Bitwise rotate word + +inline uint32_t RotateRight(uint32_t value, uint32_t n) { + ASSERT(n < 31); + return (value >> n) | (value << (32-n)); +} + + +inline uint32_t RotateLeft(uint32_t value, uint32_t n) { + ASSERT(n < 31); + return (value << n) | (value >> (32-n)); +} + + // ---------------------------------------------------------------------------- // I/O support.