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:
parent
21f72f82ce
commit
aa8cf1f0c2
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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[] = {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user