- If V8 snapshots are enabled then the hash is only randomized at build time.
- Breaks MIPS
---
Backport hash collision workaround to 3.6.
This is made up of 9956, 10351, 10338 and 10330.
This change bakes the string hash key into the snapshot, so
it is determined at build time for shapshot configs.
Review URL: http://codereview.chromium.org/9124004
// Set up the context from the function argument.
__ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
- // Set up the roots register.
- ExternalReference roots_address =
- ExternalReference::roots_address(masm->isolate());
- __ mov(r10, Operand(roots_address));
+ __ InitializeRootRegister();
// Push the function and the receiver onto the stack.
__ push(r1);
}
-class StringHelper : public AllStatic {
- public:
- // Generate code for copying characters using a simple loop. This should only
- // be used in places where the number of characters is small and the
- // additional setup and checking in GenerateCopyCharactersLong adds too much
- // overhead. Copying of overlapping regions is not supported.
- // Dest register ends at the position after the last character written.
- static void GenerateCopyCharacters(MacroAssembler* masm,
- Register dest,
- Register src,
- Register count,
- Register scratch,
- bool ascii);
-
- // Generate code for copying a large number of characters. This function
- // is allowed to spend extra time setting up conditions to make copying
- // faster. Copying of overlapping regions is not supported.
- // Dest register ends at the position after the last character written.
- static void GenerateCopyCharactersLong(MacroAssembler* masm,
- Register dest,
- Register src,
- Register count,
- Register scratch1,
- Register scratch2,
- Register scratch3,
- Register scratch4,
- Register scratch5,
- int flags);
-
-
- // Probe the symbol table for a two character string. If the string is
- // not found by probing a jump to the label not_found is performed. This jump
- // does not guarantee that the string is not in the symbol table. If the
- // string is found the code falls through with the string in register r0.
- // Contents of both c1 and c2 registers are modified. At the exit c1 is
- // guaranteed to contain halfword with low and high bytes equal to
- // initial contents of c1 and c2 respectively.
- static void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
- Register c1,
- Register c2,
- Register scratch1,
- Register scratch2,
- Register scratch3,
- Register scratch4,
- Register scratch5,
- Label* not_found);
-
- // Generate string hash.
- static void GenerateHashInit(MacroAssembler* masm,
- Register hash,
- Register character);
-
- static void GenerateHashAddCharacter(MacroAssembler* masm,
- Register hash,
- Register character);
-
- static void GenerateHashGetHash(MacroAssembler* masm,
- Register hash);
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
-};
-
-
void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
Register dest,
Register src,
static const int kProbes = 4;
Label found_in_symbol_table;
Label next_probe[kProbes];
+ Register candidate = scratch5; // Scratch register contains candidate.
for (int i = 0; i < kProbes; i++) {
- Register candidate = scratch5; // Scratch register contains candidate.
-
// Calculate entry in symbol table.
if (i > 0) {
__ add(candidate, hash, Operand(SymbolTable::GetProbeOffset(i)));
__ jmp(not_found);
// Scratch register contains result when we fall through to here.
- Register result = scratch;
+ Register result = candidate;
__ bind(&found_in_symbol_table);
__ Move(r0, result);
}
Register hash,
Register character) {
// hash = character + (character << 10);
- __ add(hash, character, Operand(character, LSL, 10));
+ __ LoadRoot(hash, Heap::kStringHashSeedRootIndex);
+ // Untag smi seed and add the character.
+ __ add(hash, character, Operand(hash, LSR, kSmiTagSize));
+ // hash += hash << 10;
+ __ add(hash, hash, Operand(hash, LSL, 10));
// hash ^= hash >> 6;
- __ eor(hash, hash, Operand(hash, ASR, 6));
+ __ eor(hash, hash, Operand(hash, LSR, 6));
}
// hash += hash << 10;
__ add(hash, hash, Operand(hash, LSL, 10));
// hash ^= hash >> 6;
- __ eor(hash, hash, Operand(hash, ASR, 6));
+ __ eor(hash, hash, Operand(hash, LSR, 6));
}
// hash += hash << 3;
__ add(hash, hash, Operand(hash, LSL, 3));
// hash ^= hash >> 11;
- __ eor(hash, hash, Operand(hash, ASR, 11));
+ __ eor(hash, hash, Operand(hash, LSR, 11));
// hash += hash << 15;
__ add(hash, hash, Operand(hash, LSL, 15), SetCC);
+ uint32_t kHashShiftCutOffMask = (1 << (32 - String::kHashShift)) - 1;
+ __ and_(hash, hash, Operand(kHashShiftCutOffMask));
+
// if (hash == 0) hash = 27;
- __ mov(hash, Operand(27), LeaveCC, ne);
+ __ mov(hash, Operand(27), LeaveCC, eq);
}
};
+class StringHelper : public AllStatic {
+ public:
+ // Generate code for copying characters using a simple loop. This should only
+ // be used in places where the number of characters is small and the
+ // additional setup and checking in GenerateCopyCharactersLong adds too much
+ // overhead. Copying of overlapping regions is not supported.
+ // Dest register ends at the position after the last character written.
+ static void GenerateCopyCharacters(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch,
+ bool ascii);
+
+ // Generate code for copying a large number of characters. This function
+ // is allowed to spend extra time setting up conditions to make copying
+ // faster. Copying of overlapping regions is not supported.
+ // Dest register ends at the position after the last character written.
+ static void GenerateCopyCharactersLong(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Register scratch5,
+ int flags);
+
+
+ // Probe the symbol table for a two character string. If the string is
+ // not found by probing a jump to the label not_found is performed. This jump
+ // does not guarantee that the string is not in the symbol table. If the
+ // string is found the code falls through with the string in register r0.
+ // Contents of both c1 and c2 registers are modified. At the exit c1 is
+ // guaranteed to contain halfword with low and high bytes equal to
+ // initial contents of c1 and c2 respectively.
+ static void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Register scratch5,
+ Label* not_found);
+
+ // Generate string hash.
+ static void GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character);
+
+ static void GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character);
+
+ static void GenerateHashGetHash(MacroAssembler* masm,
+ Register hash);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
+};
+
+
// Flag that indicates how to generate code for the stub StringAddStub.
enum StringAddFlags {
NO_STRING_ADD_FLAGS = 0,
__ pop(ip); // remove sp
__ pop(ip); // remove lr
- // Set up the roots register.
- ExternalReference roots_address = ExternalReference::roots_address(isolate);
- __ mov(r10, Operand(roots_address));
+ __ InitializeRootRegister();
__ pop(ip); // remove pc
__ pop(r7); // get continuation, leave pc on stack
void MacroAssembler::LoadRoot(Register destination,
Heap::RootListIndex index,
Condition cond) {
- ldr(destination, MemOperand(roots, index << kPointerSizeLog2), cond);
+ ldr(destination, MemOperand(kRootRegister, index << kPointerSizeLog2), cond);
}
void MacroAssembler::StoreRoot(Register source,
Heap::RootListIndex index,
Condition cond) {
- str(source, MemOperand(roots, index << kPointerSizeLog2), cond);
+ str(source, MemOperand(kRootRegister, index << kPointerSizeLog2), cond);
}
// Give alias names to registers
const Register cp = { 8 }; // JavaScript context pointer
-const Register roots = { 10 }; // Roots array pointer.
+const Register kRootRegister = { 10 }; // Roots array pointer.
// Flags used for the AllocateInNewSpace functions.
enum AllocationFlags {
Register map,
Register scratch);
+ void InitializeRootRegister() {
+ ExternalReference roots_address =
+ ExternalReference::roots_address(isolate());
+ mov(kRootRegister, Operand(roots_address));
+ }
+
// ---------------------------------------------------------------------------
// JavaScript invokes
#endif // V8_SHARED
// Initialize the global objects
Handle<ObjectTemplate> global_template = CreateGlobalTemplate();
+
+ v8::TryCatch try_catch;
Persistent<Context> context = Context::New(NULL, global_template);
- ASSERT(!context.IsEmpty());
+ if (context.IsEmpty()) {
+ v8::Local<v8::Value> st = try_catch.StackTrace();
+ ASSERT(!context.IsEmpty());
+ }
Context::Scope scope(context);
#ifndef V8_SHARED
"print stack trace when throwing exceptions")
DEFINE_bool(preallocate_message_memory, false,
"preallocate some memory to build stack traces.")
+DEFINE_bool(randomize_string_hashes,
+ true,
+ "randomize string hashes to avoid predictable hash collisions "
+ "(with snapshots this option cannot override the baked-in seed)")
+DEFINE_int(string_hash_seed,
+ 0,
+ "Fixed seed to use to string hashing (0 means random)"
+ "(with snapshots this option cannot override the baked-in seed)")
// v8.cc
DEFINE_bool(preemption, false,
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(
+ Smi::FromInt(V8::RandomPrivate(isolate()) & 0x3fffffff));
+ } else {
+ set_string_hash_seed(Smi::FromInt(FLAG_string_hash_seed));
+ }
+ }
+
if (create_heap_objects) {
// Create initial maps.
if (!CreateInitialMaps()) return false;
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(FixedArray, empty_fixed_array, EmptyFixedArray) \
V(ByteArray, empty_byte_array, EmptyByteArray) \
V(FixedDoubleArray, empty_fixed_double_array, EmptyFixedDoubleArray) \
// Please note this function does not perform a garbage collection.
MUST_USE_RESULT MaybeObject* LookupSymbol(Vector<const char> str);
MUST_USE_RESULT MaybeObject* LookupAsciiSymbol(Vector<const char> str);
- MUST_USE_RESULT MaybeObject* LookupTwoByteSymbol(
- Vector<const uc16> str);
+ MUST_USE_RESULT MaybeObject* LookupTwoByteSymbol(Vector<const uc16> str);
MUST_USE_RESULT MaybeObject* LookupAsciiSymbol(const char* str) {
return LookupSymbol(CStrVector(str));
}
if (global_gc_epilogue_callback_ != NULL) global_gc_epilogue_callback_();
}
+ uint32_t StringHashSeed() {
+ uint32_t seed = static_cast<uint32_t>(string_hash_seed()->value());
+ ASSERT(FLAG_randomize_string_hashes || seed == 0);
+ return seed;
+ }
+
private:
Heap();
static const int kProbes = 4;
Label found_in_symbol_table;
Label next_probe[kProbes], next_probe_pop_mask[kProbes];
+ Register candidate = scratch; // Scratch register contains candidate.
for (int i = 0; i < kProbes; i++) {
// Calculate entry in symbol table.
__ mov(scratch, hash);
__ and_(scratch, Operand(mask));
// Load the entry from the symbol table.
- Register candidate = scratch; // Scratch register contains candidate.
STATIC_ASSERT(SymbolTable::kEntrySize == 1);
__ mov(candidate,
FieldOperand(symbol_table,
__ jmp(not_found);
// Scratch register contains result when we fall through to here.
- Register result = scratch;
+ Register result = candidate;
__ bind(&found_in_symbol_table);
__ pop(mask); // Pop saved mask from the stack.
if (!result.is(eax)) {
Register hash,
Register character,
Register scratch) {
- // hash = character + (character << 10);
- __ mov(hash, character);
- __ shl(hash, 10);
- __ add(hash, Operand(character));
+ // hash = (seed + character) + ((seed + character) << 10);
+ if (Serializer::enabled()) {
+ ExternalReference roots_address =
+ ExternalReference::roots_address(masm->isolate());
+ __ mov(scratch, Immediate(Heap::kStringHashSeedRootIndex));
+ __ mov(scratch, Operand::StaticArray(scratch,
+ times_pointer_size,
+ roots_address));
+ __ add(scratch, Operand(character));
+ __ mov(hash, scratch);
+ __ shl(scratch, 10);
+ __ add(hash, Operand(scratch));
+ } else {
+ int32_t seed = masm->isolate()->heap()->StringHashSeed();
+ __ lea(scratch, Operand(character, seed));
+ __ shl(scratch, 10);
+ __ lea(hash, Operand(scratch, character, times_1, seed));
+ }
// hash ^= hash >> 6;
__ mov(scratch, hash);
- __ sar(scratch, 6);
+ __ shr(scratch, 6);
__ xor_(hash, Operand(scratch));
}
__ add(hash, Operand(scratch));
// hash ^= hash >> 6;
__ mov(scratch, hash);
- __ sar(scratch, 6);
+ __ shr(scratch, 6);
__ xor_(hash, Operand(scratch));
}
__ add(hash, Operand(scratch));
// hash ^= hash >> 11;
__ mov(scratch, hash);
- __ sar(scratch, 11);
+ __ shr(scratch, 11);
__ xor_(hash, Operand(scratch));
// hash += hash << 15;
__ mov(scratch, hash);
__ shl(scratch, 15);
__ add(hash, Operand(scratch));
+ uint32_t kHashShiftCutOffMask = (1 << (32 - String::kHashShift)) - 1;
+ __ and_(hash, kHashShiftCutOffMask);
+
// if (hash == 0) hash = 27;
Label hash_not_zero;
__ test(hash, Operand(hash));
}
-
-
void MacroAssembler::Drop(int stack_elements) {
if (stack_elements > 0) {
add(Operand(esp), Immediate(stack_elements * kPointerSize));
void StringHelper::GenerateHashInit(MacroAssembler* masm,
- Register hash,
- Register character) {
- // hash = character + (character << 10);
- __ sll(hash, character, 10);
+ Register hash,
+ Register character) {
+ // hash = seed + character + ((seed + character) << 10);
+ __ LoadRoot(hash, Heap::kStringHashSeedRootIndex);
+ // Untag smi seed and add the character.
+ __ SmiUntag(hash);
__ addu(hash, hash, character);
+ __ sll(at, hash, 10);
+ __ addu(hash, hash, at);
// hash ^= hash >> 6;
__ sra(at, hash, 6);
__ xor_(hash, hash, at);
void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
- Register hash,
- Register character) {
+ Register hash,
+ Register character) {
// hash += character;
__ addu(hash, hash, character);
// hash += hash << 10;
void DebugBreak();
#endif
+ void InitializeRootRegister() {
+ ExternalReference roots_address =
+ ExternalReference::roots_address(isolate());
+ li(kRootRegister, Operand(roots_address));
+ }
// -------------------------------------------------------------------------
// Exception handling.
// EnsureCapacity will guarantee the hash table is never full.
while (true) {
Object* element = KeyAt(entry);
- if (element == isolate->heap()->undefined_value()) break; // Empty entry.
- if (element != isolate->heap()->null_value() &&
+ // Empty entry.
+ if (element == isolate->heap()->raw_unchecked_undefined_value()) break;
+ if (element != isolate->heap()->raw_unchecked_null_value() &&
Shape::IsMatch(key, element)) return entry;
entry = NextProbe(entry, count++, capacity);
}
}
-StringHasher::StringHasher(int length)
+StringHasher::StringHasher(int length, uint32_t seed)
: length_(length),
- raw_running_hash_(0),
+ raw_running_hash_(seed),
array_index_(0),
is_array_index_(0 < length_ && length_ <= String::kMaxArrayIndexSize),
is_first_char_(true),
- is_valid_(true) { }
+ is_valid_(true) {
+ ASSERT(FLAG_randomize_string_hashes || raw_running_hash_ == 0);
+}
bool StringHasher::has_trivial_hash() {
result += (result << 3);
result ^= (result >> 11);
result += (result << 15);
- if (result == 0) {
+ if ((result & String::kHashBitMask) == 0) {
result = 27;
}
return result;
template <typename schar>
-uint32_t HashSequentialString(const schar* chars, int length) {
- StringHasher hasher(length);
+uint32_t HashSequentialString(const schar* chars, int length, uint32_t seed) {
+ StringHasher hasher(length, seed);
if (!hasher.has_trivial_hash()) {
int i;
for (i = 0; hasher.is_array_index() && (i < length); i++) {
// Fast check: if hash code is computed for both strings
// a fast negative check can be performed.
if (HasHashCode() && other->HasHashCode()) {
+#ifdef DEBUG
+ if (FLAG_enable_slow_asserts) {
+ if (Hash() != other->Hash()) {
+ bool found_difference = false;
+ for (int i = 0; i < len; i++) {
+ if (Get(i) != other->Get(i)) {
+ found_difference = true;
+ break;
+ }
+ }
+ ASSERT(found_difference);
+ }
+ }
+#endif
if (Hash() != other->Hash()) return false;
}
// Compute the hash code.
uint32_t field = 0;
if (StringShape(this).IsSequentialAscii()) {
- field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), len);
+ field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(),
+ len,
+ GetHeap()->StringHashSeed());
} else if (StringShape(this).IsSequentialTwoByte()) {
- field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), len);
+ field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(),
+ len,
+ GetHeap()->StringHashSeed());
} else {
StringInputBuffer buffer(this);
- field = ComputeHashField(&buffer, len);
+ field = ComputeHashField(&buffer, len, GetHeap()->StringHashSeed());
}
// Store the hash code in the object.
uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
- int length) {
- StringHasher hasher(length);
+ int length,
+ uint32_t seed) {
+ StringHasher hasher(length, seed);
// Very long strings have a trivial hash that doesn't inspect the
// string contents.
// Utf8SymbolKey carries a vector of chars as key.
class Utf8SymbolKey : public HashTableKey {
public:
- explicit Utf8SymbolKey(Vector<const char> string)
- : string_(string), hash_field_(0) { }
+ explicit Utf8SymbolKey(Vector<const char> string, uint32_t seed)
+ : string_(string), hash_field_(0), seed_(seed) { }
bool IsMatch(Object* string) {
return String::cast(string)->IsEqualTo(string_);
unibrow::Utf8InputBuffer<> buffer(string_.start(),
static_cast<unsigned>(string_.length()));
chars_ = buffer.Length();
- hash_field_ = String::ComputeHashField(&buffer, chars_);
+ hash_field_ = String::ComputeHashField(&buffer, chars_, seed_);
uint32_t result = hash_field_ >> String::kHashShift;
ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
return result;
Vector<const char> string_;
uint32_t hash_field_;
int chars_; // Caches the number of characters when computing the hash code.
+ uint32_t seed_;
};
template <typename Char>
class SequentialSymbolKey : public HashTableKey {
public:
- explicit SequentialSymbolKey(Vector<const Char> string)
- : string_(string), hash_field_(0) { }
+ explicit SequentialSymbolKey(Vector<const Char> string, uint32_t seed)
+ : string_(string), hash_field_(0), seed_(seed) { }
uint32_t Hash() {
- StringHasher hasher(string_.length());
+ StringHasher hasher(string_.length(), seed_);
// Very long strings have a trivial hash that doesn't inspect the
// string contents.
Vector<const Char> string_;
uint32_t hash_field_;
+ uint32_t seed_;
};
class AsciiSymbolKey : public SequentialSymbolKey<char> {
public:
- explicit AsciiSymbolKey(Vector<const char> str)
- : SequentialSymbolKey<char>(str) { }
+ AsciiSymbolKey(Vector<const char> str, uint32_t seed)
+ : SequentialSymbolKey<char>(str, seed) { }
bool IsMatch(Object* string) {
return String::cast(string)->IsAsciiEqualTo(string_);
public:
explicit SubStringAsciiSymbolKey(Handle<SeqAsciiString> string,
int from,
- int length)
- : string_(string), from_(from), length_(length) { }
+ int length,
+ uint32_t seed)
+ : string_(string), from_(from), length_(length), seed_(seed) { }
uint32_t Hash() {
ASSERT(length_ >= 0);
ASSERT(from_ + length_ <= string_->length());
- StringHasher hasher(length_);
+ StringHasher hasher(length_, string_->GetHeap()->StringHashSeed());
// Very long strings have a trivial hash that doesn't inspect the
// string contents.
int from_;
int length_;
uint32_t hash_field_;
+ uint32_t seed_;
};
class TwoByteSymbolKey : public SequentialSymbolKey<uc16> {
public:
- explicit TwoByteSymbolKey(Vector<const uc16> str)
- : SequentialSymbolKey<uc16>(str) { }
+ explicit TwoByteSymbolKey(Vector<const uc16> str, uint32_t seed)
+ : SequentialSymbolKey<uc16>(str, seed) { }
bool IsMatch(Object* string) {
return String::cast(string)->IsTwoByteEqualTo(string_);
// algorithm.
class TwoCharHashTableKey : public HashTableKey {
public:
- TwoCharHashTableKey(uint32_t c1, uint32_t c2)
+ TwoCharHashTableKey(uint32_t c1, uint32_t c2, uint32_t seed)
: c1_(c1), c2_(c2) {
// Char 1.
- uint32_t hash = c1 + (c1 << 10);
+ uint32_t hash = seed;
+ hash += c1;
+ hash += hash << 10;
hash ^= hash >> 6;
// Char 2.
hash += c2;
hash += hash << 3;
hash ^= hash >> 11;
hash += hash << 15;
- if (hash == 0) hash = 27;
+ if ((hash & String::kHashBitMask) == 0) hash = 27;
#ifdef DEBUG
- StringHasher hasher(2);
+ StringHasher hasher(2, seed);
hasher.AddCharacter(c1);
hasher.AddCharacter(c2);
// If this assert fails then we failed to reproduce the two-character
bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
uint32_t c2,
String** symbol) {
- TwoCharHashTableKey key(c1, c2);
+ TwoCharHashTableKey key(c1, c2, GetHeap()->StringHashSeed());
int entry = FindEntry(&key);
if (entry == kNotFound) {
return false;
}
-MaybeObject* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
- Utf8SymbolKey key(str);
+MaybeObject* SymbolTable::LookupSymbol(Vector<const char> str,
+ Object** s) {
+ Utf8SymbolKey key(str, GetHeap()->StringHashSeed());
return LookupKey(&key, s);
}
MaybeObject* SymbolTable::LookupAsciiSymbol(Vector<const char> str,
Object** s) {
- AsciiSymbolKey key(str);
+ AsciiSymbolKey key(str, GetHeap()->StringHashSeed());
return LookupKey(&key, s);
}
int from,
int length,
Object** s) {
- SubStringAsciiSymbolKey key(str, from, length);
+ SubStringAsciiSymbolKey key(str, from, length, GetHeap()->StringHashSeed());
return LookupKey(&key, s);
}
MaybeObject* SymbolTable::LookupTwoByteSymbol(Vector<const uc16> str,
Object** s) {
- TwoByteSymbolKey key(str);
+ TwoByteSymbolKey key(str, GetHeap()->StringHashSeed());
return LookupKey(&key, s);
}
class StringHasher {
public:
- explicit inline StringHasher(int length);
+ explicit inline StringHasher(int length, uint32_t seed);
// Returns true if the hash of this string can be computed without
// looking at the contents.
// Calculates string hash.
template <typename schar>
-inline uint32_t HashSequentialString(const schar* chars, int length);
+inline uint32_t HashSequentialString(const schar* chars,
+ int length,
+ uint32_t seed);
// The characteristics of a string are stored in its map. Retrieving these
inline uint32_t Hash();
static uint32_t ComputeHashField(unibrow::CharacterStream* buffer,
- int length);
+ int length,
+ uint32_t seed);
static bool ComputeArrayIndex(unibrow::CharacterStream* buffer,
uint32_t* index,
// Shift constant retrieving hash code from hash field.
static const int kHashShift = kNofHashBitFields;
+ // Only these bits are relevant in the hash, since the top two are shifted
+ // out.
+ static const uint32_t kHashBitMask = 0xffffffffu >> kHashShift;
+
// Array index strings this short can keep their index in the hash
// field.
static const int kMaxCachedArrayIndexLength = 7;
Vector<char> dst = Vector<char>::New(len + 1);
OS::StrNCpy(dst, src, len);
dst[len] = '\0';
- uint32_t hash = HashSequentialString(dst.start(), len);
+ uint32_t hash =
+ HashSequentialString(dst.start(), len, HEAP->StringHashSeed());
return AddOrDisposeString(dst.start(), hash);
}
DeleteArray(str.start());
return format;
}
- uint32_t hash = HashSequentialString(str.start(), len);
+ uint32_t hash = HashSequentialString(
+ str.start(), len, HEAP->StringHashSeed());
return AddOrDisposeString(str.start(), hash);
}
uint64_t HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
uint64_t id = static_cast<uint64_t>(info->GetHash());
const char* label = info->GetLabel();
- id ^= HashSequentialString(label, static_cast<int>(strlen(label)));
+ id ^= HashSequentialString(label,
+ static_cast<int>(strlen(label)),
+ HEAP->StringHashSeed());
intptr_t element_count = info->GetElementCount();
if (element_count != -1)
id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count));
static const int kProbes = 4;
Label found_in_symbol_table;
Label next_probe[kProbes];
+ Register candidate = scratch; // Scratch register contains candidate.
for (int i = 0; i < kProbes; i++) {
// Calculate entry in symbol table.
__ movl(scratch, hash);
__ andl(scratch, mask);
// Load the entry from the symbol table.
- Register candidate = scratch; // Scratch register contains candidate.
STATIC_ASSERT(SymbolTable::kEntrySize == 1);
__ movq(candidate,
FieldOperand(symbol_table,
__ jmp(not_found);
// Scratch register contains result when we fall through to here.
- Register result = scratch;
+ Register result = candidate;
__ bind(&found_in_symbol_table);
if (!result.is(rax)) {
__ movq(rax, result);
Register hash,
Register character,
Register scratch) {
- // hash = character + (character << 10);
- __ movl(hash, character);
- __ shll(hash, Immediate(10));
- __ addl(hash, character);
+ // hash = (seed + character) + ((seed + character) << 10);
+ __ LoadRoot(scratch, Heap::kStringHashSeedRootIndex);
+ __ SmiToInteger32(scratch, scratch);
+ __ addl(scratch, character);
+ __ movl(hash, scratch);
+ __ shll(scratch, Immediate(10));
+ __ addl(hash, scratch);
// hash ^= hash >> 6;
__ movl(scratch, hash);
- __ sarl(scratch, Immediate(6));
+ __ shrl(scratch, Immediate(6));
__ xorl(hash, scratch);
}
__ addl(hash, scratch);
// hash ^= hash >> 6;
__ movl(scratch, hash);
- __ sarl(scratch, Immediate(6));
+ __ shrl(scratch, Immediate(6));
__ xorl(hash, scratch);
}
__ leal(hash, Operand(hash, hash, times_8, 0));
// hash ^= hash >> 11;
__ movl(scratch, hash);
- __ sarl(scratch, Immediate(11));
+ __ shrl(scratch, Immediate(11));
__ xorl(hash, scratch);
// hash += hash << 15;
__ movl(scratch, hash);
__ shll(scratch, Immediate(15));
__ addl(hash, scratch);
+ uint32_t kHashShiftCutOffMask = (1 << (32 - String::kHashShift)) - 1;
+ __ andl(hash, Immediate(kHashShiftCutOffMask));
+
// if (hash == 0) hash = 27;
Label hash_not_zero;
__ j(not_zero, &hash_not_zero);
'test-fixed-dtoa.cc',
'test-flags.cc',
'test-func-name-inference.cc',
+ 'test-hashing.cc',
'test-hashmap.cc',
'test-heap-profiler.cc',
'test-heap.cc',
--- /dev/null
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdlib.h>
+
+#include "v8.h"
+
+#include "factory.h"
+#include "macro-assembler.h"
+#include "cctest.h"
+#include "code-stubs.h"
+#include "objects.h"
+
+#ifdef USE_SIMULATOR
+#include "simulator.h"
+#endif
+
+using namespace v8::internal;
+
+
+typedef uint32_t (*HASH_FUNCTION)();
+
+static v8::Persistent<v8::Context> env;
+
+#define __ masm->
+
+
+void generate(MacroAssembler* masm, i::Vector<const char> string) {
+ // GenerateHashInit takes the first character as an argument so it can't
+ // handle the zero length string.
+ ASSERT(string.length() > 0);
+#ifdef V8_TARGET_ARCH_IA32
+ __ push(ebx);
+ __ push(ecx);
+ __ mov(eax, Immediate(0));
+ __ mov(ebx, Immediate(string.at(0)));
+ StringHelper::GenerateHashInit(masm, eax, ebx, ecx);
+ for (int i = 1; i < string.length(); i++) {
+ __ mov(ebx, Immediate(string.at(i)));
+ StringHelper::GenerateHashAddCharacter(masm, eax, ebx, ecx);
+ }
+ StringHelper::GenerateHashGetHash(masm, eax, ecx);
+ __ pop(ecx);
+ __ pop(ebx);
+ __ Ret();
+#elif V8_TARGET_ARCH_X64
+ __ push(kRootRegister);
+ __ InitializeRootRegister();
+ __ push(rbx);
+ __ push(rcx);
+ __ movq(rax, Immediate(0));
+ __ movq(rbx, Immediate(string.at(0)));
+ StringHelper::GenerateHashInit(masm, rax, rbx, rcx);
+ for (int i = 1; i < string.length(); i++) {
+ __ movq(rbx, Immediate(string.at(i)));
+ StringHelper::GenerateHashAddCharacter(masm, rax, rbx, rcx);
+ }
+ StringHelper::GenerateHashGetHash(masm, rax, rcx);
+ __ pop(rcx);
+ __ pop(rbx);
+ __ pop(kRootRegister);
+ __ Ret();
+#elif V8_TARGET_ARCH_ARM
+ __ push(kRootRegister);
+ __ InitializeRootRegister();
+
+ __ mov(r0, Operand(0));
+ __ mov(ip, Operand(string.at(0)));
+ StringHelper::GenerateHashInit(masm, r0, ip);
+ for (int i = 1; i < string.length(); i++) {
+ __ mov(ip, Operand(string.at(i)));
+ StringHelper::GenerateHashAddCharacter(masm, r0, ip);
+ }
+ StringHelper::GenerateHashGetHash(masm, r0);
+ __ pop(kRootRegister);
+ __ mov(pc, Operand(lr));
+#elif V8_TARGET_ARCH_MIPS
+ __ push(kRootRegister);
+ __ InitializeRootRegister();
+
+ __ li(v0, Operand(0));
+ __ li(t1, Operand(string.at(0)));
+ StringHelper::GenerateHashInit(masm, v0, t1);
+ for (int i = 1; i < string.length(); i++) {
+ __ li(t1, Operand(string.at(i)));
+ StringHelper::GenerateHashAddCharacter(masm, v0, t1);
+ }
+ StringHelper::GenerateHashGetHash(masm, v0);
+ __ pop(kRootRegister);
+ __ jr(ra);
+ __ nop();
+#endif
+}
+
+
+void check(i::Vector<const char> string) {
+ v8::HandleScope scope;
+ v8::internal::byte buffer[2048];
+ MacroAssembler masm(Isolate::Current(), buffer, sizeof buffer);
+
+ generate(&masm, string);
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ Code* code = Code::cast(HEAP->CreateCode(
+ desc,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked());
+ CHECK(code->IsCode());
+
+ HASH_FUNCTION hash = FUNCTION_CAST<HASH_FUNCTION>(code->entry());
+ Handle<String> v8_string = FACTORY->NewStringFromAscii(string);
+ v8_string->set_hash_field(String::kEmptyHashField);
+#ifdef USE_SIMULATOR
+ uint32_t codegen_hash =
+ reinterpret_cast<uint32_t>(CALL_GENERATED_CODE(hash, 0, 0, 0, 0, 0));
+#else
+ uint32_t codegen_hash = hash();
+#endif
+ uint32_t runtime_hash = v8_string->Hash();
+ CHECK(runtime_hash == codegen_hash);
+}
+
+
+void check_twochars(char a, char b) {
+ char ab[2] = {a, b};
+ check(i::Vector<const char>(ab, 2));
+}
+
+
+TEST(StringHash) {
+ if (env.IsEmpty()) env = v8::Context::New();
+ for (int a = 0; a < String::kMaxAsciiCharCode; a++) {
+ // Numbers are hashed differently.
+ if (a >= '0' && a <= '9') continue;
+ for (int b = 0; b < String::kMaxAsciiCharCode; b++) {
+ if (b >= '0' && b <= '9') continue;
+ check_twochars(static_cast<char>(a), static_cast<char>(b));
+ }
+ }
+ check(i::Vector<const char>("*", 1));
+ check(i::Vector<const char>(".zZ", 3));
+ check(i::Vector<const char>("muc", 3));
+ check(i::Vector<const char>("(>'_')>", 7));
+ check(i::Vector<const char>("-=[ vee eight ftw ]=-", 21));
+}
+
+#undef __
var expected_y = (i + 1) * 2 + 2 + ((i + 1) * 2 + 2) / 100;
// All frames except the bottom one has normal variables a and b.
- assertEquals('a', frame.localName(0));
- assertEquals('b', frame.localName(1));
- assertEquals(expected_a, frame.localValue(0).value());
- assertEquals(expected_b, frame.localValue(1).value());
+ var a = ('a' === frame.localName(0)) ? 0 : 1;
+ var b = 1 - a;
+ assertEquals('a', frame.localName(a));
+ assertEquals('b', frame.localName(b));
+ assertEquals(expected_a, frame.localValue(a).value());
+ assertEquals(expected_b, frame.localValue(b).value());
// All frames except the bottom one has arguments variables x and y.
assertEquals('x', frame.argumentName(0));
var expected_y = (i + 1) * 2 + 2;
// All frames except the bottom one has normal variables a and b.
- assertEquals('a', frame.localName(0));
- assertEquals('b', frame.localName(1));
- assertEquals(expected_a, frame.localValue(0).value());
- assertEquals(expected_b, frame.localValue(1).value());
+ var a = ('a' === frame.localName(0)) ? 0 : 1;
+ var b = 1 - a;
+ assertEquals('a', frame.localName(a));
+ assertEquals('b', frame.localName(b));
+ assertEquals(expected_a, frame.localValue(a).value());
+ assertEquals(expected_b, frame.localValue(b).value());
// All frames except the bottom one has arguments variables x and y.
assertEquals('x', frame.argumentName(0));
listenerComplete = true;
}
} catch (e) {
- exception = e
+ exception = e.stack;
};
};