Commit 0a1f0e33 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by V8 LUCI CQ

[strings] Fix raw hash lookup for forwarded strings

Raw hashes may need to be looked up via the forwarding table when
internalized strings are forwarded to external resources. Notably, the
megamorphic ICs were not correctly fetching the raw hash.

Bug: v8:12007
Change-Id: Ibbc75de57e707788f544fbd1a0f8f0041350e29d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3885379Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarPatrick Thier <pthier@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83115}
parent 779da1d0
...@@ -1996,14 +1996,14 @@ TNode<IntPtrT> CodeStubAssembler::LoadJSReceiverIdentityHash( ...@@ -1996,14 +1996,14 @@ TNode<IntPtrT> CodeStubAssembler::LoadJSReceiverIdentityHash(
} }
TNode<Uint32T> CodeStubAssembler::LoadNameHashAssumeComputed(TNode<Name> name) { TNode<Uint32T> CodeStubAssembler::LoadNameHashAssumeComputed(TNode<Name> name) {
TNode<Uint32T> hash_field = LoadNameRawHashField(name); TNode<Uint32T> hash_field = LoadNameRawHash(name);
CSA_DCHECK(this, IsClearWord32(hash_field, Name::kHashNotComputedMask)); CSA_DCHECK(this, IsClearWord32(hash_field, Name::kHashNotComputedMask));
return DecodeWord32<Name::HashBits>(hash_field); return DecodeWord32<Name::HashBits>(hash_field);
} }
TNode<Uint32T> CodeStubAssembler::LoadNameHash(TNode<Name> name, TNode<Uint32T> CodeStubAssembler::LoadNameHash(TNode<Name> name,
Label* if_hash_not_computed) { Label* if_hash_not_computed) {
TNode<Uint32T> raw_hash_field = LoadNameRawHashField(name); TNode<Uint32T> raw_hash_field = LoadNameRawHash(name);
if (if_hash_not_computed != nullptr) { if (if_hash_not_computed != nullptr) {
GotoIf(IsSetWord32(raw_hash_field, Name::kHashNotComputedMask), GotoIf(IsSetWord32(raw_hash_field, Name::kHashNotComputedMask),
if_hash_not_computed); if_hash_not_computed);
...@@ -2011,6 +2011,43 @@ TNode<Uint32T> CodeStubAssembler::LoadNameHash(TNode<Name> name, ...@@ -2011,6 +2011,43 @@ TNode<Uint32T> CodeStubAssembler::LoadNameHash(TNode<Name> name,
return DecodeWord32<Name::HashBits>(raw_hash_field); return DecodeWord32<Name::HashBits>(raw_hash_field);
} }
TNode<Uint32T> CodeStubAssembler::LoadNameRawHash(TNode<Name> name) {
TVARIABLE(Uint32T, var_raw_hash);
Label if_forwarding_index(this), not_forwarding_index(this), done(this);
TNode<Uint32T> raw_hash_field = LoadNameRawHashField(name);
Branch(IsEqualInWord32<Name::HashFieldTypeBits>(
raw_hash_field, Name::HashFieldType::kForwardingIndex),
&if_forwarding_index, &not_forwarding_index);
BIND(&not_forwarding_index);
{
var_raw_hash = raw_hash_field;
Goto(&done);
}
BIND(&if_forwarding_index);
{
TNode<ExternalReference> function =
ExternalConstant(ExternalReference::raw_hash_from_forward_table());
const TNode<ExternalReference> isolate_ptr =
ExternalConstant(ExternalReference::isolate_address(isolate()));
TNode<Uint32T> result = UncheckedCast<Uint32T>(CallCFunction(
function, MachineType::Uint32(),
std::make_pair(MachineType::Pointer(), isolate_ptr),
std::make_pair(
MachineType::Int32(),
DecodeWord32<Name::ForwardingIndexValueBits>(raw_hash_field))));
var_raw_hash = result;
Goto(&done);
}
BIND(&done);
return var_raw_hash.value();
}
TNode<Smi> CodeStubAssembler::LoadStringLengthAsSmi(TNode<String> string) { TNode<Smi> CodeStubAssembler::LoadStringLengthAsSmi(TNode<String> string) {
return SmiFromIntPtr(LoadStringLengthAsWord(string)); return SmiFromIntPtr(LoadStringLengthAsWord(string));
} }
...@@ -6747,8 +6784,7 @@ TNode<BoolT> CodeStubAssembler::IsUniqueNameNoIndex(TNode<HeapObject> object) { ...@@ -6747,8 +6784,7 @@ TNode<BoolT> CodeStubAssembler::IsUniqueNameNoIndex(TNode<HeapObject> object) {
IsInternalizedStringInstanceType(instance_type), IsInternalizedStringInstanceType(instance_type),
[=] { [=] {
return IsNotEqualInWord32<Name::HashFieldTypeBits>( return IsNotEqualInWord32<Name::HashFieldTypeBits>(
LoadNameRawHashField(CAST(object)), LoadNameRawHash(CAST(object)), Name::HashFieldType::kIntegerIndex);
Name::HashFieldType::kIntegerIndex);
}, },
[=] { return IsSymbolInstanceType(instance_type); }); [=] { return IsSymbolInstanceType(instance_type); });
} }
...@@ -6765,7 +6801,7 @@ TNode<BoolT> CodeStubAssembler::IsUniqueNameNoCachedIndex( ...@@ -6765,7 +6801,7 @@ TNode<BoolT> CodeStubAssembler::IsUniqueNameNoCachedIndex(
return Select<BoolT>( return Select<BoolT>(
IsInternalizedStringInstanceType(instance_type), IsInternalizedStringInstanceType(instance_type),
[=] { [=] {
return IsSetWord32(LoadNameRawHashField(CAST(object)), return IsSetWord32(LoadNameRawHash(CAST(object)),
Name::kDoesNotContainCachedArrayIndexMask); Name::kDoesNotContainCachedArrayIndexMask);
}, },
[=] { return IsSymbolInstanceType(instance_type); }); [=] { return IsSymbolInstanceType(instance_type); });
...@@ -7251,7 +7287,7 @@ TNode<Number> CodeStubAssembler::StringToNumber(TNode<String> input) { ...@@ -7251,7 +7287,7 @@ TNode<Number> CodeStubAssembler::StringToNumber(TNode<String> input) {
TVARIABLE(Number, var_result); TVARIABLE(Number, var_result);
// Check if string has a cached array index. // Check if string has a cached array index.
TNode<Uint32T> raw_hash_field = LoadNameRawHashField(input); TNode<Uint32T> raw_hash_field = LoadNameRawHash(input);
GotoIf(IsSetWord32(raw_hash_field, Name::kDoesNotContainCachedArrayIndexMask), GotoIf(IsSetWord32(raw_hash_field, Name::kDoesNotContainCachedArrayIndexMask),
&runtime); &runtime);
......
...@@ -1451,6 +1451,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -1451,6 +1451,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
Label* if_hash_not_computed = nullptr); Label* if_hash_not_computed = nullptr);
TNode<Uint32T> LoadNameHashAssumeComputed(TNode<Name> name); TNode<Uint32T> LoadNameHashAssumeComputed(TNode<Name> name);
// Load the Name::RawHash() value of a name as an uint32 value. Follows
// through the forwarding table.
TNode<Uint32T> LoadNameRawHash(TNode<Name> name);
// Load length field of a String object as Smi value. // Load length field of a String object as Smi value.
TNode<Smi> LoadStringLengthAsSmi(TNode<String> string); TNode<Smi> LoadStringLengthAsSmi(TNode<String> string);
// Load length field of a String object as intptr_t value. // Load length field of a String object as intptr_t value.
......
...@@ -1024,6 +1024,8 @@ FUNCTION_REFERENCE(try_string_to_index_or_lookup_existing, ...@@ -1024,6 +1024,8 @@ FUNCTION_REFERENCE(try_string_to_index_or_lookup_existing,
StringTable::TryStringToIndexOrLookupExisting) StringTable::TryStringToIndexOrLookupExisting)
FUNCTION_REFERENCE(string_from_forward_table, FUNCTION_REFERENCE(string_from_forward_table,
StringForwardingTable::GetForwardStringAddress) StringForwardingTable::GetForwardStringAddress)
FUNCTION_REFERENCE(raw_hash_from_forward_table,
StringForwardingTable::GetRawHashStatic)
FUNCTION_REFERENCE(string_to_array_index_function, String::ToArrayIndex) FUNCTION_REFERENCE(string_to_array_index_function, String::ToArrayIndex)
FUNCTION_REFERENCE(array_indexof_includes_smi_or_object, FUNCTION_REFERENCE(array_indexof_includes_smi_or_object,
ArrayIndexOfIncludesSmiOrObject) ArrayIndexOfIncludesSmiOrObject)
......
...@@ -208,6 +208,7 @@ class StatsCounter; ...@@ -208,6 +208,7 @@ class StatsCounter;
V(try_string_to_index_or_lookup_existing, \ V(try_string_to_index_or_lookup_existing, \
"try_string_to_index_or_lookup_existing") \ "try_string_to_index_or_lookup_existing") \
V(string_from_forward_table, "string_from_forward_table") \ V(string_from_forward_table, "string_from_forward_table") \
V(raw_hash_from_forward_table, "raw_hash_from_forward_table") \
IF_WASM(V, wasm_call_trap_callback_for_testing, \ IF_WASM(V, wasm_call_trap_callback_for_testing, \
"wasm::call_trap_callback_for_testing") \ "wasm::call_trap_callback_for_testing") \
IF_WASM(V, wasm_f32_ceil, "wasm::f32_ceil_wrapper") \ IF_WASM(V, wasm_f32_ceil, "wasm::f32_ceil_wrapper") \
......
...@@ -2887,7 +2887,7 @@ enum AccessorAssembler::StubCacheTable : int { ...@@ -2887,7 +2887,7 @@ enum AccessorAssembler::StubCacheTable : int {
TNode<IntPtrT> AccessorAssembler::StubCachePrimaryOffset(TNode<Name> name, TNode<IntPtrT> AccessorAssembler::StubCachePrimaryOffset(TNode<Name> name,
TNode<Map> map) { TNode<Map> map) {
// Compute the hash of the name (use entire hash field). // Compute the hash of the name (use entire hash field).
TNode<Uint32T> raw_hash_field = LoadNameRawHashField(name); TNode<Uint32T> raw_hash_field = LoadNameRawHash(name);
CSA_DCHECK(this, CSA_DCHECK(this,
Word32Equal(Word32And(raw_hash_field, Word32Equal(Word32And(raw_hash_field,
Int32Constant(Name::kHashNotComputedMask)), Int32Constant(Name::kHashNotComputedMask)),
......
...@@ -50,7 +50,7 @@ void StubCache::Initialize() { ...@@ -50,7 +50,7 @@ void StubCache::Initialize() {
// is scaled by 1 << kCacheIndexShift. // is scaled by 1 << kCacheIndexShift.
int StubCache::PrimaryOffset(Name name, Map map) { int StubCache::PrimaryOffset(Name name, Map map) {
// Compute the hash of the name (use entire hash field). // Compute the hash of the name (use entire hash field).
uint32_t field = name.raw_hash_field(); uint32_t field = name.RawHash();
DCHECK(Name::IsHashFieldComputed(field)); DCHECK(Name::IsHashFieldComputed(field));
// Using only the low bits in 64-bit mode is unlikely to increase the // Using only the low bits in 64-bit mode is unlikely to increase the
// risk of collision even if the heap is spread over an area larger than // risk of collision even if the heap is spread over an area larger than
......
...@@ -190,6 +190,14 @@ uint32_t Name::EnsureRawHash( ...@@ -190,6 +190,14 @@ uint32_t Name::EnsureRawHash(
return String::cast(*this).ComputeAndSetRawHash(access_guard); return String::cast(*this).ComputeAndSetRawHash(access_guard);
} }
uint32_t Name::RawHash() {
uint32_t field = raw_hash_field(kAcquireLoad);
if (V8_UNLIKELY(IsForwardingIndex(field))) {
return GetRawHashFromForwardingTable(field);
}
return field;
}
uint32_t Name::EnsureHash() { return HashBits::decode(EnsureRawHash()); } uint32_t Name::EnsureHash() { return HashBits::decode(EnsureRawHash()); }
uint32_t Name::EnsureHash(const SharedStringAccessGuardIfNeeded& access_guard) { uint32_t Name::EnsureHash(const SharedStringAccessGuardIfNeeded& access_guard) {
......
...@@ -190,6 +190,7 @@ class Name : public TorqueGeneratedName<Name, PrimitiveHeapObject> { ...@@ -190,6 +190,7 @@ class Name : public TorqueGeneratedName<Name, PrimitiveHeapObject> {
// a forwarding index. // a forwarding index.
inline uint32_t EnsureRawHash(); inline uint32_t EnsureRawHash();
inline uint32_t EnsureRawHash(const SharedStringAccessGuardIfNeeded&); inline uint32_t EnsureRawHash(const SharedStringAccessGuardIfNeeded&);
inline uint32_t RawHash();
static inline bool IsHashFieldComputed(uint32_t raw_hash_field); static inline bool IsHashFieldComputed(uint32_t raw_hash_field);
static inline bool IsHash(uint32_t raw_hash_field); static inline bool IsHash(uint32_t raw_hash_field);
......
...@@ -261,6 +261,11 @@ uint32_t StringForwardingTable::GetRawHash(PtrComprCageBase cage_base, ...@@ -261,6 +261,11 @@ uint32_t StringForwardingTable::GetRawHash(PtrComprCageBase cage_base,
return block->record(index_in_block)->raw_hash(cage_base); return block->record(index_in_block)->raw_hash(cage_base);
} }
// static
uint32_t StringForwardingTable::GetRawHashStatic(Isolate* isolate, int index) {
return isolate->string_forwarding_table()->GetRawHash(isolate, index);
}
v8::String::ExternalStringResourceBase* v8::String::ExternalStringResourceBase*
StringForwardingTable::GetExternalResource(int index, bool* is_one_byte) const { StringForwardingTable::GetExternalResource(int index, bool* is_one_byte) const {
CHECK_LT(index, size()); CHECK_LT(index, size());
......
...@@ -56,6 +56,7 @@ class StringForwardingTable { ...@@ -56,6 +56,7 @@ class StringForwardingTable {
static Address GetForwardStringAddress(Isolate* isolate, int index); static Address GetForwardStringAddress(Isolate* isolate, int index);
V8_EXPORT_PRIVATE uint32_t GetRawHash(PtrComprCageBase cage_base, V8_EXPORT_PRIVATE uint32_t GetRawHash(PtrComprCageBase cage_base,
int index) const; int index) const;
static uint32_t GetRawHashStatic(Isolate* isolate, int index);
v8::String::ExternalStringResourceBase* GetExternalResource( v8::String::ExternalStringResourceBase* GetExternalResource(
int index, bool* is_one_byte) const; int index, bool* is_one_byte) const;
......
...@@ -1675,7 +1675,7 @@ uint32_t String::ComputeAndSetRawHash( ...@@ -1675,7 +1675,7 @@ uint32_t String::ComputeAndSetRawHash(
string = ThinString::cast(string).actual(cage_base); string = ThinString::cast(string).actual(cage_base);
shape = StringShape(string, cage_base); shape = StringShape(string, cage_base);
if (length() == string.length()) { if (length() == string.length()) {
uint32_t raw_hash = string.raw_hash_field(); uint32_t raw_hash = string.RawHash();
DCHECK(IsHashFieldComputed(raw_hash)); DCHECK(IsHashFieldComputed(raw_hash));
set_raw_hash_field(raw_hash); set_raw_hash_field(raw_hash);
return raw_hash; return raw_hash;
......
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --expose-externalize-string --shared-string-table
// Flags: --allow-natives-syntax
function set(o, ext_key) {
o[ext_key] = "bar";
}
function get(o, ext_key) {
o[ext_key];
}
%PrepareFunctionForOptimization(set);
%OptimizeFunctionOnNextCall(set);
%PrepareFunctionForOptimization(get);
%OptimizeFunctionOnNextCall(get);
(function test() {
let ext_key = "AAAAAAAAAAAAAAAAAAAAAA";
externalizeString(ext_key);
set({a:1}, ext_key);
set({b:2}, ext_key);
set({c:3}, ext_key);
set({d:4}, ext_key);
set({e:5}, ext_key);
set({f:6}, ext_key);
get({a:1}, ext_key);
get({b:2}, ext_key);
get({c:3}, ext_key);
get({d:4}, ext_key);
get({e:5}, ext_key);
get({f:6}, ext_key);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment