#ifndef V8_OBJECTS_NUMBER_STRING_CACHE_INL_H_
#define V8_OBJECTS_NUMBER_STRING_CACHE_INL_H_
#include "src/objects/number-string-cache.h"
#include "src/base/bits.h"
#include "src/common/globals.h"
#include "src/handles/handles-inl.h"
#include "src/objects/fixed-array-inl.h"
#include "src/objects/object-macros.h"
namespace v8::internal {
uint32_t SmiStringCache::capacity() const {
return Super::capacity() / kEntrySize;
}
InternalIndex SmiStringCache::GetEntryFor(Tagged<Smi> number) const {
uint32_t hash = static_cast<uint32_t>(number.value());
uint32_t mask = capacity() - 1;
return InternalIndex{hash & mask};
}
InternalIndex SmiStringCache::GetEntryFor(Isolate* isolate,
Tagged<Smi> number) {
return isolate->factory()->smi_string_cache()->GetEntryFor(number);
}
Handle<Object> SmiStringCache::Get(Isolate* isolate, InternalIndex entry,
Tagged<Smi> number) {
DisallowGarbageCollection no_gc;
Factory* factory = isolate->factory();
Tagged<SmiStringCache> cache = *factory->smi_string_cache();
uint32_t entry_index = entry.as_uint32() * kEntrySize;
Tagged<Object> key = cache->get(entry_index + kEntryKeyIndex);
const bool match = (key == number);
if (V8_UNLIKELY(v8_flags.native_code_counters)) {
Counters* counters = isolate->counters();
counters->number_string_cache_smi_probes()->Increment();
if (!match) {
counters->number_string_cache_smi_misses()->Increment();
}
}
if (match) {
auto string = Cast<String>(cache->get(entry_index + kEntryValueIndex));
return handle(string, isolate);
}
return factory->undefined_value();
}
void SmiStringCache::Set(Isolate* isolate, InternalIndex entry,
Tagged<Smi> number, DirectHandle<String> string) {
DCHECK_NE(number, kEmptySentinel);
DirectHandle<SmiStringCache> cache = isolate->factory()->smi_string_cache();
DCHECK_EQ(entry, cache->GetEntryFor(number));
uint32_t entry_index = entry.as_uint32() * kEntrySize;
if (cache->capacity() == kInitialSize &&
cache->get(entry_index + kEntryKeyIndex) != kEmptySentinel &&
!isolate->MemorySaverModeEnabled()) {
uint32_t full_size =
base::bits::RoundUpToPowerOfTwo32(v8_flags.smi_string_cache_size);
CHECK_LT(kInitialSize, full_size);
CHECK_LE(full_size, kMaxCapacity);
cache = SmiStringCache::New(isolate, full_size);
isolate->heap()->SetSmiStringCache(*cache);
entry = cache->GetEntryFor(number);
entry_index = entry.as_uint32() * kEntrySize;
}
DisallowGarbageCollection no_gc;
cache->set(entry_index + kEntryKeyIndex, number);
cache->set(entry_index + kEntryValueIndex, *string);
}
template <class IsolateT>
DirectHandle<SmiStringCache> SmiStringCache::New(IsolateT* isolate,
int capacity) {
std::optional<DisallowGarbageCollection> no_gc;
DirectHandle<SmiStringCache> result = Cast<SmiStringCache>(
Allocate(isolate, capacity * kEntrySize, &no_gc, AllocationType::kOld));
result->Clear();
return result;
}
void SmiStringCache::Clear() {
Relaxed_MemsetTagged(RawFieldOfFirstElement(), kEmptySentinel,
Super::capacity());
}
InternalIndex DoubleStringCache::GetEntryFor(uint64_t number_bits,
uint32_t capacity) {
uint32_t hash = static_cast<uint32_t>(number_bits) ^
static_cast<uint32_t>(number_bits >> 32);
uint32_t mask = capacity - 1;
return InternalIndex{hash & mask};
}
InternalIndex DoubleStringCache::GetEntryFor(uint64_t number_bits) const {
return GetEntryFor(number_bits, capacity());
}
InternalIndex DoubleStringCache::GetEntryFor(Isolate* isolate,
uint64_t number_bits) {
return isolate->factory()->double_string_cache()->GetEntryFor(number_bits);
}
Handle<Object> DoubleStringCache::Get(Isolate* isolate,
InternalIndex entry_index,
uint64_t number_bits) {
DisallowGarbageCollection no_gc;
Factory* factory = isolate->factory();
DirectHandle<DoubleStringCache> cache = factory->double_string_cache();
auto& entry = cache->entries()[entry_index.as_uint32()];
const bool match = entry.key_.value_as_bits() == number_bits;
if (V8_UNLIKELY(v8_flags.native_code_counters)) {
Counters* counters = isolate->counters();
counters->number_string_cache_double_probes()->Increment();
if (!match) {
counters->number_string_cache_double_misses()->Increment();
}
}
if (match) {
auto string = Cast<String>(entry.value_.load());
return handle(string, isolate);
}
return factory->undefined_value();
}
void DoubleStringCache::Set(Isolate* isolate, InternalIndex entry_index,
uint64_t number_bits, DirectHandle<String> string) {
DirectHandle<DoubleStringCache> cache =
isolate->factory()->double_string_cache();
DCHECK_EQ(entry_index, cache->GetEntryFor(number_bits));
auto* entry = &cache->entries()[entry_index.as_uint32()];
if (cache->capacity() == kInitialSize &&
entry->value_.load() != kEmptySentinel &&
!isolate->MemorySaverModeEnabled()) {
uint32_t full_size =
base::bits::RoundUpToPowerOfTwo32(v8_flags.double_string_cache_size);
CHECK_LT(kInitialSize, full_size);
CHECK_LE(full_size, kMaxCapacity);
cache = DoubleStringCache::New(isolate, full_size);
isolate->heap()->SetDoubleStringCache(*cache);
entry_index = cache->GetEntryFor(number_bits);
entry = &cache->entries()[entry_index.as_uint32()];
}
DisallowGarbageCollection no_gc;
entry->key_.set_value_as_bits(number_bits);
entry->value_.store(&**cache, *string);
}
template <class IsolateT>
DirectHandle<DoubleStringCache> DoubleStringCache::New(IsolateT* isolate,
int capacity) {
DCHECK_GE(capacity, kInitialSize);
DCHECK_LE(capacity, kMaxCapacity);
Tagged<DoubleStringCache> xs =
UncheckedCast<DoubleStringCache>(isolate->factory()->AllocateRawArray(
SizeFor(capacity), AllocationType::kOld));
ReadOnlyRoots roots{isolate};
xs->set_map_after_allocation(isolate, roots.double_string_cache_map(),
SKIP_WRITE_BARRIER);
xs->capacity_ = capacity;
#if TAGGED_SIZE_8_BYTES
xs->optional_padding_ = 0;
#endif
xs->Clear();
return handle(xs, isolate);
}
void DoubleStringCache::Clear() {
static_assert(sizeof(Entry) % kTaggedSize == 0);
auto* data = reinterpret_cast<Tagged_t*>(begin());
Relaxed_MemsetTagged(data, kEmptySentinel,
capacity() * (sizeof(Entry) / kTaggedSize));
}
}
#include "src/objects/object-macros-undef.h"
#endif