Refactor Name::Hash

We introduce a new information type ForwardingIndex to be stored in
the Name::Hash field (to be used in the future).
To do so we use the 2 least significant bit to distinguish types
of information stored in the hash field (in contrast to only bit 1
to distinguis integer indicies from "real" hashes).
This motivated a refactor to use base::BitField for the hash field.

Bug: v8:12007
Change-Id: I651c86807edfc218792d0db12379374eaa50c930
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3432385
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Commit-Queue: Patrick Thier <pthier@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78975}
This commit is contained in:
Patrick Thier 2022-02-07 08:43:17 +00:00 committed by V8 LUCI CQ
parent 21f72f82ce
commit aa8cf1f0c2
22 changed files with 167 additions and 116 deletions

View File

@ -84,7 +84,7 @@ template EXPORT_TEMPLATE_DEFINE(
bool AstRawString::AsArrayIndex(uint32_t* index) const {
// The StringHasher will set up the hash. Bail out early if we know it
// can't be convertible to an array index.
if ((raw_hash_field_ & Name::kIsNotIntegerIndexMask) != 0) return false;
if (!IsIntegerIndex()) return false;
if (length() <= Name::kMaxCachedArrayIndexLength) {
*index = Name::ArrayIndexValueBits::decode(raw_hash_field_);
return true;
@ -97,7 +97,7 @@ bool AstRawString::AsArrayIndex(uint32_t* index) const {
}
bool AstRawString::IsIntegerIndex() const {
return (raw_hash_field_ & Name::kIsNotIntegerIndexMask) == 0;
return Name::IsIntegerIndex(raw_hash_field_);
}
bool AstRawString::IsOneByteEqualTo(const char* data) const {

View File

@ -80,7 +80,7 @@ class AstRawString final : public ZoneObject {
uint32_t Hash() const {
// Hash field must be computed.
DCHECK_EQ(raw_hash_field_ & Name::kHashNotComputedMask, 0);
return raw_hash_field_ >> Name::kHashShift;
return Name::HashBits::decode(raw_hash_field_);
}
// This function can be called after internalizing.

View File

@ -16,7 +16,7 @@ namespace base {
// BitField is a help template for encoding and decode bitfield with
// unsigned content.
// Instantiate them via 'using', which is cheaper than deriving a new class:
// using MyBitField = base::BitField<int, 4, 2, MyEnum>;
// using MyBitField = base::BitField<MyEnum, 4, 2>;
// The BitField class is final to enforce this style over derivation.
template <class T, int shift, int size, class U = uint32_t>

View File

@ -1929,6 +1929,18 @@ extern operator '[]' macro LoadWeakFixedArrayElement(
extern operator '[]' macro LoadUint8Ptr(RawPtr<uint8>, intptr): uint8;
extern enum HashFieldType extends uint32 constexpr 'Name::HashFieldType' {
kHash,
kIntegerIndex,
kForwardingIndex,
kEmpty
}
operator '==' macro HashFieldTypeEquals(
s1: HashFieldType, s2: HashFieldType): bool {
return Word32Equal(s1, s2);
}
const kNoHashSentinel:
constexpr int32 generates 'PropertyArray::kNoHashSentinel';
extern macro LoadNameHash(Name): uint32;

View File

@ -241,7 +241,7 @@ transitioning javascript builtin NumberParseFloat(
} label String(s: String) {
// Check if the string is a cached array index.
const hash: NameHash = s.raw_hash_field;
if (!hash.is_not_integer_index_mask &&
if (IsIntegerIndex(hash) &&
hash.array_index_length < kMaxCachedArrayIndexLength) {
const arrayIndex: uint32 = hash.array_index_value;
return SmiFromUint32(arrayIndex);
@ -292,7 +292,7 @@ transitioning builtin ParseInt(implicit context: Context)(
} label String(s: String) {
// Check if the string is a cached array index.
const hash: NameHash = s.raw_hash_field;
if (!hash.is_not_integer_index_mask &&
if (IsIntegerIndex(hash) &&
hash.array_index_length < kMaxCachedArrayIndexLength) {
const arrayIndex: uint32 = hash.array_index_value;
return SmiFromUint32(arrayIndex);

View File

@ -2109,7 +2109,7 @@ TNode<IntPtrT> CodeStubAssembler::LoadJSReceiverIdentityHash(
TNode<Uint32T> CodeStubAssembler::LoadNameHashAssumeComputed(TNode<Name> name) {
TNode<Uint32T> hash_field = LoadNameRawHashField(name);
CSA_DCHECK(this, IsClearWord32(hash_field, Name::kHashNotComputedMask));
return Unsigned(Word32Shr(hash_field, Int32Constant(Name::kHashShift)));
return DecodeWord32<Name::HashBits>(hash_field);
}
TNode<Uint32T> CodeStubAssembler::LoadNameHash(TNode<Name> name,
@ -2119,7 +2119,7 @@ TNode<Uint32T> CodeStubAssembler::LoadNameHash(TNode<Name> name,
GotoIf(IsSetWord32(raw_hash_field, Name::kHashNotComputedMask),
if_hash_not_computed);
}
return Unsigned(Word32Shr(raw_hash_field, Int32Constant(Name::kHashShift)));
return DecodeWord32<Name::HashBits>(raw_hash_field);
}
TNode<Smi> CodeStubAssembler::LoadStringLengthAsSmi(TNode<String> string) {
@ -6794,8 +6794,9 @@ TNode<BoolT> CodeStubAssembler::IsUniqueNameNoIndex(TNode<HeapObject> object) {
return Select<BoolT>(
IsInternalizedStringInstanceType(instance_type),
[=] {
return IsSetWord32(LoadNameRawHashField(CAST(object)),
Name::kIsNotIntegerIndexMask);
return IsNotEqualInWord32<Name::HashFieldTypeBits>(
LoadNameRawHashField(CAST(object)),
Name::HashFieldType::kIntegerIndex);
},
[=] { return IsSymbolInstanceType(instance_type); });
}
@ -8119,7 +8120,8 @@ void CodeStubAssembler::TryToName(TNode<Object> key, Label* if_keyisindex,
&if_has_cached_index);
// No cached array index. If the string knows that it contains an index,
// then it must be an uncacheable index. Handle this case in the runtime.
GotoIf(IsClearWord32(raw_hash_field, Name::kIsNotIntegerIndexMask),
GotoIf(IsEqualInWord32<Name::HashFieldTypeBits>(
raw_hash_field, Name::HashFieldType::kIntegerIndex),
if_bailout);
GotoIf(InstanceTypeEqual(var_instance_type.value(), THIN_STRING_TYPE),

View File

@ -2878,7 +2878,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
return Word32Equal(Word32And(word32, const_mask), const_mask);
}
// Returns true if the bit field |BitField| in |word32| is equal to a given.
// Returns true if the bit field |BitField| in |word32| is equal to a given
// constant |value|. Avoids a shift compared to using DecodeWord32.
template <typename BitField>
TNode<BoolT> IsEqualInWord32(TNode<Word32T> word32,
@ -2888,6 +2888,14 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
return Word32Equal(masked_word32, Int32Constant(BitField::encode(value)));
}
// Returns true if the bit field |BitField| in |word32| is not equal to a
// given constant |value|. Avoids a shift compared to using DecodeWord32.
template <typename BitField>
TNode<BoolT> IsNotEqualInWord32(TNode<Word32T> word32,
typename BitField::FieldType value) {
return Word32BinaryNot(IsEqualInWord32<BitField>(word32, value));
}
// Returns true if any of the |T|'s bits in given |word| are set.
template <typename T>
TNode<BoolT> IsSetWord(TNode<WordT> word) {

View File

@ -1112,9 +1112,9 @@ Symbol Factory::NewSymbolInternal(AllocationType allocation) {
Symbol::kSize, allocation, read_only_roots().symbol_map()));
DisallowGarbageCollection no_gc;
// Generate a random hash value.
int hash = isolate()->GenerateIdentityHash(Name::kHashBitMask);
symbol.set_raw_hash_field(Name::kIsNotIntegerIndexMask |
(hash << Name::kHashShift));
int hash = isolate()->GenerateIdentityHash(Name::HashBits::kMax);
symbol.set_raw_hash_field(
Name::CreateHashFieldValue(hash, Name::HashFieldType::kHash));
symbol.set_description(read_only_roots().undefined_value(),
SKIP_WRITE_BARRIER);
symbol.set_flags(0);

View File

@ -78,12 +78,12 @@ class V8_EXPORT_PRIVATE StubCache {
Isolate* isolate() { return isolate_; }
// Setting kCacheIndexShift to Name::kHashShift is convenient because it
// Setting kCacheIndexShift to Name::HashBits::kShift is convenient because it
// causes the bit field inside the hash field to get shifted out implicitly.
// Note that kCacheIndexShift must not get too large, because
// sizeof(Entry) needs to be a multiple of 1 << kCacheIndexShift (see
// the STATIC_ASSERT below, in {entry(...)}).
static const int kCacheIndexShift = Name::kHashShift;
static const int kCacheIndexShift = Name::HashBits::kShift;
static const int kPrimaryTableBits = 11;
static const int kPrimaryTableSize = (1 << kPrimaryTableBits);
@ -123,7 +123,7 @@ class V8_EXPORT_PRIVATE StubCache {
// Compute the entry for a given offset in exactly the same way as
// we do in generated code. We generate an hash code that already
// ends in Name::kHashShift 0s. Then we multiply it so it is a multiple
// ends in Name::HashBits::kShift 0s. Then we multiply it so it is a multiple
// of sizeof(Entry). This makes it easier to avoid making mistakes
// in the hashed offset computations.
static Entry* entry(Entry* table, int offset) {

View File

@ -88,12 +88,31 @@ bool Name::IsHashFieldComputed(uint32_t raw_hash_field) {
return (raw_hash_field & kHashNotComputedMask) == 0;
}
bool Name::IsHash(uint32_t raw_hash_field) {
return HashFieldTypeBits::decode(raw_hash_field) == HashFieldType::kHash;
}
bool Name::IsIntegerIndex(uint32_t raw_hash_field) {
return HashFieldTypeBits::decode(raw_hash_field) ==
HashFieldType::kIntegerIndex;
}
bool Name::IsForwardingIndex(uint32_t raw_hash_field) {
return HashFieldTypeBits::decode(raw_hash_field) ==
HashFieldType::kForwardingIndex;
}
uint32_t Name::CreateHashFieldValue(uint32_t hash, HashFieldType type) {
return HashBits::encode(hash & HashBits::kMax) |
HashFieldTypeBits::encode(type);
}
bool Name::HasHashCode() const { return IsHashFieldComputed(raw_hash_field()); }
uint32_t Name::EnsureHash() {
// Fast case: has hash code already been computed?
uint32_t field = raw_hash_field();
if (IsHashFieldComputed(field)) return field >> kHashShift;
if (IsHashFieldComputed(field)) return HashBits::decode(field);
// Slow case: compute hash code and set it. Has to be a string.
return String::cast(*this).ComputeAndSetHash();
}
@ -101,7 +120,7 @@ uint32_t Name::EnsureHash() {
uint32_t Name::EnsureHash(const SharedStringAccessGuardIfNeeded& access_guard) {
// Fast case: has hash code already been computed?
uint32_t field = raw_hash_field();
if (IsHashFieldComputed(field)) return field >> kHashShift;
if (IsHashFieldComputed(field)) return HashBits::decode(field);
// Slow case: compute hash code and set it. Has to be a string.
return String::cast(*this).ComputeAndSetHash(access_guard);
}
@ -109,7 +128,7 @@ uint32_t Name::EnsureHash(const SharedStringAccessGuardIfNeeded& access_guard) {
uint32_t Name::hash() const {
uint32_t field = raw_hash_field();
DCHECK(IsHashFieldComputed(field));
return field >> kHashShift;
return HashBits::decode(field);
}
DEF_GETTER(Name, IsInterestingSymbol, bool) {

View File

@ -89,21 +89,30 @@ class Name : public TorqueGeneratedName<Name, PrimitiveHeapObject> {
void NameShortPrint();
int NameShortPrint(base::Vector<char> str);
// Mask constant for checking if a name has a computed hash code
// and if it is a string that is an integer index. The least significant bit
// indicates whether a hash code has been computed. If the hash code has
// been computed the 2nd bit tells whether the string can be used as an
// integer index (up to MAX_SAFE_INTEGER).
static const int kHashNotComputedMask = 1;
static const int kIsNotIntegerIndexMask = 1 << 1;
static const int kNofHashBitFields = 2;
// Mask constant for checking if a name has a computed hash code and the type
// of information stored in the hash field. The least significant bit
// indicates whether the value can be used as a hash (i.e. different values
// imply different strings).
enum class HashFieldType : uint32_t {
kHash = 0b10,
kIntegerIndex = 0b00,
kForwardingIndex = 0b01,
kEmpty = 0b11
};
// Shift constant retrieving hash code from hash field.
static const int kHashShift = kNofHashBitFields;
using HashFieldTypeBits = base::BitField<HashFieldType, 0, 2>;
using HashBits =
HashFieldTypeBits::Next<uint32_t, kBitsPerInt - HashFieldTypeBits::kSize>;
// Only these bits are relevant in the hash, since the top two are shifted
// out.
static const uint32_t kHashBitMask = 0xffffffffu >> kHashShift;
static constexpr int kHashNotComputedMask = 1;
// Value of empty hash field indicating that the hash is not computed.
static constexpr int kEmptyHashField =
HashFieldTypeBits::encode(HashFieldType::kEmpty);
// Empty hash and forwarding indices can not be used as hash.
STATIC_ASSERT((kEmptyHashField & kHashNotComputedMask) != 0);
STATIC_ASSERT((HashFieldTypeBits::encode(HashFieldType::kForwardingIndex) &
kHashNotComputedMask) != 0);
// Array index strings this short can keep their index in the hash field.
static const int kMaxCachedArrayIndexLength = 7;
@ -124,16 +133,15 @@ class Name : public TorqueGeneratedName<Name, PrimitiveHeapObject> {
// the case for the string '0'. 24 bits are used for the array index value.
static const int kArrayIndexValueBits = 24;
static const int kArrayIndexLengthBits =
kBitsPerInt - kArrayIndexValueBits - kNofHashBitFields;
kBitsPerInt - kArrayIndexValueBits - HashFieldTypeBits::kSize;
STATIC_ASSERT(kArrayIndexLengthBits > 0);
STATIC_ASSERT(kMaxArrayIndexSize < (1 << kArrayIndexLengthBits));
using ArrayIndexValueBits =
base::BitField<unsigned int, kNofHashBitFields, kArrayIndexValueBits>;
HashFieldTypeBits::Next<unsigned int, kArrayIndexValueBits>;
using ArrayIndexLengthBits =
base::BitField<unsigned int, kNofHashBitFields + kArrayIndexValueBits,
kArrayIndexLengthBits>;
ArrayIndexValueBits::Next<unsigned int, kArrayIndexLengthBits>;
// Check that kMaxCachedArrayIndexLength + 1 is a power of two so we
// could use a mask to test if the length of string is less than or equal to
@ -143,16 +151,19 @@ class Name : public TorqueGeneratedName<Name, PrimitiveHeapObject> {
// When any of these bits is set then the hash field does not contain a cached
// array index.
STATIC_ASSERT(HashFieldTypeBits::encode(HashFieldType::kIntegerIndex) == 0);
static const unsigned int kDoesNotContainCachedArrayIndexMask =
(~static_cast<unsigned>(kMaxCachedArrayIndexLength)
<< ArrayIndexLengthBits::kShift) |
kIsNotIntegerIndexMask;
// Value of empty hash field indicating that the hash is not computed.
static const int kEmptyHashField =
kIsNotIntegerIndexMask | kHashNotComputedMask;
HashFieldTypeBits::kMask;
static inline bool IsHashFieldComputed(uint32_t raw_hash_field);
static inline bool IsHash(uint32_t raw_hash_field);
static inline bool IsIntegerIndex(uint32_t raw_hash_field);
static inline bool IsForwardingIndex(uint32_t raw_hash_field);
static inline uint32_t CreateHashFieldValue(uint32_t hash,
HashFieldType type);
TQ_OBJECT_CONSTRUCTORS(Name)
};

View File

@ -8,8 +8,7 @@ extern class Name extends PrimitiveHeapObject {
}
bitfield struct NameHash extends uint32 {
hash_not_computed: bool: 1 bit;
is_not_integer_index_mask: bool: 1 bit;
hash_field_type: HashFieldType: 2 bit;
array_index_value: uint32: 24 bit;
array_index_length: uint32: 6 bit;
}
@ -35,25 +34,21 @@ extern class Symbol extends Name {
type PublicSymbol extends Symbol;
type PrivateSymbol extends Symbol;
const kNameEmptyHashField: NameHash = NameHash{
hash_not_computed: true,
is_not_integer_index_mask: true,
array_index_value: 0,
array_index_length: 0
};
const kMaxCachedArrayIndexLength: constexpr uint32
generates 'Name::kMaxCachedArrayIndexLength';
const kMaxArrayIndexSize: constexpr uint32
generates 'Name::kMaxArrayIndexSize';
const kNofHashBitFields: constexpr int31
generates 'Name::kNofHashBitFields';
generates 'Name::HashFieldTypeBits::kSize';
const kArrayIndexValueBits: constexpr int31
generates 'Name::kArrayIndexValueBits';
const kDoesNotContainCachedArrayIndexMask: constexpr uint32
generates 'Name::kDoesNotContainCachedArrayIndexMask';
const kIsNotIntegerIndexMask: constexpr uint32
generates 'Name::kIsNotIntegerIndexMask';
const kNameEmptyHashField: NameHash = NameHash{
hash_field_type: HashFieldType::kEmpty,
array_index_value: 0,
array_index_length: 0
};
macro ContainsCachedArrayIndex(hash: uint32): bool {
return (hash & kDoesNotContainCachedArrayIndexMask) == 0;
@ -72,16 +67,22 @@ macro TenToThe(exponent: uint32): uint32 {
return Unsigned(answer);
}
macro IsIntegerIndex(hash: NameHash): bool {
return hash.hash_field_type == HashFieldType::kIntegerIndex;
}
macro MakeArrayIndexHash(value: uint32, length: uint32): NameHash {
// This is in sync with StringHasher::MakeArrayIndexHash.
dcheck(length <= kMaxArrayIndexSize);
const one: uint32 = 1;
dcheck(TenToThe(kMaxCachedArrayIndexLength) < (one << kArrayIndexValueBits));
let hash: uint32 = value;
hash = (hash << kArrayIndexValueBitsShift) |
let rawHash: uint32 = value;
rawHash = (rawHash << kArrayIndexValueBitsShift) |
(length << kArrayIndexLengthBitsShift);
dcheck((hash & kIsNotIntegerIndexMask) == 0);
dcheck(
(length <= kMaxCachedArrayIndexLength) == ContainsCachedArrayIndex(hash));
return %RawDownCast<NameHash>(hash);
(length <= kMaxCachedArrayIndexLength) ==
ContainsCachedArrayIndex(rawHash));
const hash: NameHash = %RawDownCast<NameHash>(rawHash);
dcheck(IsIntegerIndex(hash));
return hash;
}

View File

@ -4692,7 +4692,7 @@ uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
value <<= String::ArrayIndexValueBits::kShift;
value |= length << String::ArrayIndexLengthBits::kShift;
DCHECK_EQ(value & String::kIsNotIntegerIndexMask, 0);
DCHECK(String::IsIntegerIndex(value));
DCHECK_EQ(length <= String::kMaxCachedArrayIndexLength,
Name::ContainsCachedArrayIndex(value));
return value;

View File

@ -1363,7 +1363,7 @@ bool String::AsArrayIndex(uint32_t* index) {
*index = ArrayIndexValueBits::decode(field);
return true;
}
if (IsHashFieldComputed(field) && (field & kIsNotIntegerIndexMask)) {
if (IsHashFieldComputed(field) && !IsIntegerIndex(field)) {
return false;
}
return SlowAsArrayIndex(index);
@ -1375,7 +1375,7 @@ bool String::AsIntegerIndex(size_t* index) {
*index = ArrayIndexValueBits::decode(field);
return true;
}
if (IsHashFieldComputed(field) && (field & kIsNotIntegerIndexMask)) {
if (IsHashFieldComputed(field) && !IsIntegerIndex(field)) {
return false;
}
return SlowAsIntegerIndex(index);

View File

@ -21,7 +21,7 @@ void StringTableKey::set_raw_hash_field(uint32_t raw_hash_field) {
}
uint32_t StringTableKey::hash() const {
return raw_hash_field_ >> Name::kHashShift;
return Name::HashBits::decode(raw_hash_field_);
}
} // namespace internal

View File

@ -662,7 +662,7 @@ Address StringTable::Data::TryStringToIndexOrLookupExisting(Isolate* isolate,
.ptr();
}
if ((raw_hash_field & Name::kIsNotIntegerIndexMask) == 0) {
if (Name::IsIntegerIndex(raw_hash_field)) {
// It is an index, but it's not cached.
return Smi::FromInt(ResultSentinel::kUnsupported).ptr();
}

View File

@ -1632,7 +1632,7 @@ uint32_t String::ComputeAndSetHash(
// Check the hash code is there.
DCHECK(HasHashCode());
uint32_t result = raw_hash_field >> kHashShift;
uint32_t result = HashBits::decode(raw_hash_field);
DCHECK_NE(result, 0); // Ensure that the hash value of 0 is never computed.
return result;
}
@ -1643,7 +1643,7 @@ bool String::SlowAsArrayIndex(uint32_t* index) {
if (length <= kMaxCachedArrayIndexLength) {
EnsureHash(); // Force computation of hash code.
uint32_t field = raw_hash_field();
if ((field & kIsNotIntegerIndexMask) != 0) return false;
if (!IsIntegerIndex(field)) return false;
*index = ArrayIndexValueBits::decode(field);
return true;
}
@ -1658,7 +1658,7 @@ bool String::SlowAsIntegerIndex(size_t* index) {
if (length <= kMaxCachedArrayIndexLength) {
EnsureHash(); // Force computation of hash code.
uint32_t field = raw_hash_field();
if ((field & kIsNotIntegerIndexMask) != 0) return false;
if (!IsIntegerIndex(field)) return false;
*index = ArrayIndexValueBits::decode(field);
return true;
}

View File

@ -135,7 +135,7 @@ namespace {
inline uint32_t ComputeStringHash(const char* str, int len) {
uint32_t raw_hash_field =
StringHasher::HashSequentialString(str, len, kZeroHashSeed);
return raw_hash_field >> Name::kHashShift;
return Name::HashBits::decode(raw_hash_field);
}
} // namespace

View File

@ -30,20 +30,21 @@ uint32_t StringHasher::GetHashCore(uint32_t running_hash) {
running_hash += (running_hash << 3);
running_hash ^= (running_hash >> 11);
running_hash += (running_hash << 15);
int32_t hash = static_cast<int32_t>(running_hash & String::kHashBitMask);
int32_t hash = static_cast<int32_t>(running_hash & String::HashBits::kMax);
// Ensure that the hash is kZeroHash, if the computed value is 0.
int32_t mask = (hash - 1) >> 31;
return running_hash | (kZeroHash & mask);
running_hash |= (kZeroHash & mask);
return running_hash;
}
uint32_t StringHasher::GetTrivialHash(int length) {
DCHECK_GT(length, String::kMaxHashCalcLength);
// The hash of a large string is simply computed from the length.
// Ensure that the max length is small enough to be shifted without losing
// Ensure that the max length is small enough to be encoded without losing
// information.
STATIC_ASSERT(base::bits::CountLeadingZeros32(String::kMaxLength) >=
String::kHashShift);
STATIC_ASSERT(String::kMaxLength <= String::HashBits::kMax);
uint32_t hash = static_cast<uint32_t>(length);
return (hash << String::kHashShift) | String::kIsNotIntegerIndexMask;
return String::CreateHashFieldValue(hash, String::HashFieldType::kHash);
}
template <typename char_t>
@ -79,19 +80,19 @@ uint32_t StringHasher::HashSequentialString(const char_t* chars_raw, int length,
// Not an array index, but it could still be an integer index.
// Perform a regular hash computation, and additionally check
// if there are non-digit characters.
uint32_t is_integer_index = 0;
String::HashFieldType type = String::HashFieldType::kIntegerIndex;
uint32_t running_hash = static_cast<uint32_t>(seed);
uint64_t index_big = 0;
const uchar* end = &chars[length];
while (chars != end) {
if (is_integer_index == 0 &&
if (type == String::HashFieldType::kIntegerIndex &&
!TryAddIntegerIndexChar(&index_big, *chars)) {
is_integer_index = String::kIsNotIntegerIndexMask;
type = String::HashFieldType::kHash;
}
running_hash = AddCharacterCore(running_hash, *chars++);
}
uint32_t hash = (GetHashCore(running_hash) << String::kHashShift) |
is_integer_index;
uint32_t hash =
String::CreateHashFieldValue(GetHashCore(running_hash), type);
if (Name::ContainsCachedArrayIndex(hash)) {
// The hash accidentally looks like a cached index. Fix that by
// setting a bit that looks like a longer-than-cacheable string
@ -118,8 +119,8 @@ uint32_t StringHasher::HashSequentialString(const char_t* chars_raw, int length,
running_hash = AddCharacterCore(running_hash, *chars++);
}
return (GetHashCore(running_hash) << String::kHashShift) |
String::kIsNotIntegerIndexMask;
return String::CreateHashFieldValue(GetHashCore(running_hash),
String::HashFieldType::kHash);
}
std::size_t SeededStringHasher::operator()(const char* name) const {

View File

@ -25,15 +25,10 @@ using TVariable = compiler::TypedCodeAssemblerVariable<T>;
Handle<Name> NewNameWithHash(Isolate* isolate, const char* str, uint32_t hash,
bool is_integer) {
uint32_t hash_field = hash << Name::kHashShift;
uint32_t hash_field = Name::CreateHashFieldValue(
hash, is_integer ? Name::HashFieldType::kIntegerIndex
: Name::HashFieldType::kHash);
static_assert(Name::kNofHashBitFields == 2, "This test needs updating");
static_assert(Name::kHashNotComputedMask == 1, "This test needs updating");
static_assert(Name::kIsNotIntegerIndexMask == 2, "This test needs updating");
if (!is_integer) {
hash_field |= Name::kIsNotIntegerIndexMask;
}
Handle<Name> name = isolate->factory()->NewOneByteInternalizedString(
base::OneByteVector(str), hash_field);
name->set_raw_hash_field(hash_field);
@ -223,13 +218,15 @@ TEST(DescriptorArrayHashCollisionMassive) {
Isolate* isolate = CcTest::i_isolate();
HandleScope handle_scope(isolate);
static_assert(Name::kNofHashBitFields == 2, "This test needs updating");
static_assert(Name::HashFieldTypeBits::kSize == 2,
"This test might require updating if more HashFieldType values "
"are introduced");
std::vector<Handle<Name>> names;
// Use the same hash value for all names.
uint32_t hash =
static_cast<uint32_t>(isolate->GenerateIdentityHash(Name::kHashBitMask));
uint32_t hash = static_cast<uint32_t>(
isolate->GenerateIdentityHash(Name::HashBits::kMax));
for (int i = 0; i < kMaxNumberOfDescriptors / 2; ++i) {
// Add pairs of names having the same base hash value but having different
@ -269,7 +266,9 @@ TEST(DescriptorArrayHashCollision) {
Isolate* isolate = CcTest::i_isolate();
HandleScope handle_scope(isolate);
static_assert(Name::kNofHashBitFields == 2, "This test needs updating");
static_assert(Name::HashFieldTypeBits::kSize == 2,
"This test might require updating if more HashFieldType values "
"are introduced");
std::vector<Handle<Name>> names;
uint32_t hash = 0;
@ -278,7 +277,7 @@ TEST(DescriptorArrayHashCollision) {
if (i % 2 == 0) {
// Change hash value for every pair of names.
hash = static_cast<uint32_t>(
isolate->GenerateIdentityHash(Name::kHashBitMask));
isolate->GenerateIdentityHash(Name::HashBits::kMax));
}
// Add pairs of names having the same base hash value but having different
@ -318,13 +317,15 @@ TEST(TransitionArrayHashCollisionMassive) {
Isolate* isolate = CcTest::i_isolate();
HandleScope handle_scope(isolate);
static_assert(Name::kNofHashBitFields == 2, "This test needs updating");
static_assert(Name::HashFieldTypeBits::kSize == 2,
"This test might require updating if more HashFieldType values "
"are introduced");
std::vector<Handle<Name>> names;
// Use the same hash value for all names.
uint32_t hash =
static_cast<uint32_t>(isolate->GenerateIdentityHash(Name::kHashBitMask));
uint32_t hash = static_cast<uint32_t>(
isolate->GenerateIdentityHash(Name::HashBits::kMax));
for (int i = 0; i < TransitionsAccessor::kMaxNumberOfTransitions / 2; ++i) {
// Add pairs of names having the same base hash value but having different
@ -369,19 +370,21 @@ TEST(TransitionArrayHashCollision) {
Isolate* isolate = CcTest::i_isolate();
HandleScope handle_scope(isolate);
static_assert(Name::kNofHashBitFields == 2, "This test needs updating");
static_assert(Name::HashFieldTypeBits::kSize == 2,
"This test might require updating if more HashFieldType values "
"are introduced");
std::vector<Handle<Name>> names;
// Use the same hash value for all names.
uint32_t hash =
static_cast<uint32_t>(isolate->GenerateIdentityHash(Name::kHashBitMask));
uint32_t hash = static_cast<uint32_t>(
isolate->GenerateIdentityHash(Name::HashBits::kMax));
for (int i = 0; i < TransitionsAccessor::kMaxNumberOfTransitions / 2; ++i) {
if (i % 2 == 0) {
// Change hash value for every pair of names.
hash = static_cast<uint32_t>(
isolate->GenerateIdentityHash(Name::kHashBitMask));
isolate->GenerateIdentityHash(Name::HashBits::kMax));
}
// Add pairs of names having the same base hash value but having different
// values of is_integer bit.

View File

@ -1840,13 +1840,13 @@ void TestString(i::Isolate* isolate, const IndexData& data) {
CHECK(s->AsIntegerIndex(&index));
CHECK_EQ(data.integer_index, index);
s->EnsureHash();
CHECK_EQ(0, s->raw_hash_field() & String::kIsNotIntegerIndexMask);
CHECK(String::IsIntegerIndex(s->raw_hash_field()));
CHECK(s->HasHashCode());
}
if (!s->HasHashCode()) s->EnsureHash();
CHECK(s->HasHashCode());
if (!data.is_integer_index) {
CHECK_NE(0, s->raw_hash_field() & String::kIsNotIntegerIndexMask);
CHECK(String::IsHash(s->raw_hash_field()));
}
}
@ -1858,12 +1858,12 @@ TEST(HashArrayIndexStrings) {
v8::HandleScope scope(CcTest::isolate());
i::Isolate* isolate = CcTest::i_isolate();
CHECK_EQ(StringHasher::MakeArrayIndexHash(0 /* value */, 1 /* length */) >>
Name::kHashShift,
CHECK_EQ(Name::HashBits::decode(
StringHasher::MakeArrayIndexHash(0 /* value */, 1 /* length */)),
isolate->factory()->zero_string()->hash());
CHECK_EQ(StringHasher::MakeArrayIndexHash(1 /* value */, 1 /* length */) >>
Name::kHashShift,
CHECK_EQ(Name::HashBits::decode(
StringHasher::MakeArrayIndexHash(1 /* value */, 1 /* length */)),
isolate->factory()->one_string()->hash());
IndexData tests[] = {

View File

@ -100,15 +100,9 @@ Handle<Name> CreateKeyWithHash(Isolate* isolate, KeyCache& keys,
fake_hash |= swiss_table::H2(override_with);
}
// Ensure that just doing a shift below is correct.
static_assert(Name::kNofHashBitFields == 2, "This test needs updating");
static_assert(Name::kHashNotComputedMask == 1,
"This test needs updating");
static_assert(Name::kIsNotIntegerIndexMask == 2,
"This test needs updating");
// Prepare what to put into the hash field.
uint32_t hash_field = fake_hash << Name::kHashShift;
uint32_t hash_field =
Name::CreateHashFieldValue(fake_hash, Name::HashFieldType::kHash);
CHECK_NE(hash_field, 0);
key_symbol->set_raw_hash_field(hash_field);