Commit 0dfe759f authored by Toon Verwaest's avatar Toon Verwaest Committed by Commit Bot

[runtime] Use SequentialStringKey to implement LookupStringIfExists_NoAllocate

This is a step towards reducing the number of StringTableKeys to the absolute
minimum so we can better optimize how they work. This always flattens
ConsStrings into a buffer to avoid expensive comparison with cons string (as
well as hash computation).

Change-Id: I6dcf0bdd2a722f490dad02b7f887083e1ac46000
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1598707Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61289}
parent 1a0ea54d
...@@ -6479,7 +6479,7 @@ class RegExpKey : public HashTableKey { ...@@ -6479,7 +6479,7 @@ class RegExpKey : public HashTableKey {
}; };
// InternalizedStringKey carries a string/internalized-string object as key. // InternalizedStringKey carries a string/internalized-string object as key.
class InternalizedStringKey : public StringTableKey { class InternalizedStringKey final : public StringTableKey {
public: public:
explicit InternalizedStringKey(Handle<String> string) explicit InternalizedStringKey(Handle<String> string)
: StringTableKey(0, string->length()), string_(string) { : StringTableKey(0, string->length()), string_(string) {
...@@ -6911,147 +6911,68 @@ Handle<StringTable> StringTable::CautiousShrink(Isolate* isolate, ...@@ -6911,147 +6911,68 @@ Handle<StringTable> StringTable::CautiousShrink(Isolate* isolate,
namespace { namespace {
class StringTableNoAllocateKey : public StringTableKey { template <typename Char>
public: Address LookupString(Isolate* isolate, String string) {
StringTableNoAllocateKey(String string, uint64_t seed) DisallowHeapAllocation no_gc;
: StringTableKey(0, string.length()), string_(string) { StringTable table = isolate->heap()->string_table();
StringShape shape(string); uint64_t seed = HashSeed(isolate);
one_byte_ = shape.encoding_tag() == kOneByteStringTag;
DCHECK(!shape.IsInternalized());
DCHECK(!shape.IsThin());
int length = string->length();
if (shape.IsCons() && length <= String::kMaxHashCalcLength) {
special_flattening_ = true;
uint32_t hash_field = 0;
if (one_byte_) {
if (V8_LIKELY(length <=
static_cast<int>(arraysize(one_byte_buffer_)))) {
one_byte_content_ = one_byte_buffer_;
} else {
one_byte_content_ = new uint8_t[length];
}
String::WriteToFlat(string, one_byte_content_, 0, length);
hash_field =
StringHasher::HashSequentialString(one_byte_content_, length, seed);
} else {
if (V8_LIKELY(length <=
static_cast<int>(arraysize(two_byte_buffer_)))) {
two_byte_content_ = two_byte_buffer_;
} else {
two_byte_content_ = new uint16_t[length];
}
String::WriteToFlat(string, two_byte_content_, 0, length);
hash_field =
StringHasher::HashSequentialString(two_byte_content_, length, seed);
}
string->set_hash_field(hash_field);
} else {
special_flattening_ = false;
one_byte_content_ = nullptr;
string->Hash();
}
DCHECK(string->HasHashCode()); int length = string.length();
set_hash_field(string->hash_field());
}
~StringTableNoAllocateKey() override { std::unique_ptr<Char[]> buffer;
if (one_byte_) { const Char* chars;
if (one_byte_content_ != one_byte_buffer_) delete[] one_byte_content_;
} else { if (string.IsConsString()) {
if (two_byte_content_ != two_byte_buffer_) delete[] two_byte_content_; buffer.reset(new Char[length]);
String::WriteToFlat(string, buffer.get(), 0, length);
chars = buffer.get();
} else {
String source = string;
size_t start = 0;
if (source.IsSlicedString()) {
SlicedString sliced = SlicedString::cast(source);
start = sliced.offset();
source = sliced.parent();
} }
if (source.IsThinString()) source = ThinString::cast(source).actual();
chars = source.GetChars<Char>(no_gc) + start;
} }
// TODO(verwaest): Internalize to one-byte when possible.
SequentialStringKey<Char> key(Vector<const Char>(chars, length), seed);
bool IsMatch(String other) override { // String could be an array index.
DCHECK(other->IsInternalizedString()); uint32_t hash_field = key.hash_field();
DCHECK(other->IsFlat());
DisallowHeapAllocation no_gc;
if (!special_flattening_) {
if (string_->Get(0) != other->Get(0)) return false;
if (string_->IsFlat()) {
StringShape shape1(string_);
StringShape shape2(other);
if (shape1.encoding_tag() == kOneByteStringTag &&
shape2.encoding_tag() == kOneByteStringTag) {
String::FlatContent flat1 = string_->GetFlatContent(no_gc);
String::FlatContent flat2 = other->GetFlatContent(no_gc);
return CompareRawStringContents(flat1.ToOneByteVector().begin(),
flat2.ToOneByteVector().begin(),
length());
}
if (shape1.encoding_tag() == kTwoByteStringTag &&
shape2.encoding_tag() == kTwoByteStringTag) {
String::FlatContent flat1 = string_->GetFlatContent(no_gc);
String::FlatContent flat2 = other->GetFlatContent(no_gc);
return CompareRawStringContents(flat1.ToUC16Vector().begin(),
flat2.ToUC16Vector().begin(),
length());
}
}
StringComparator comparator;
return comparator.Equals(string_, other);
}
String::FlatContent flat_content = other->GetFlatContent(no_gc); if (Name::ContainsCachedArrayIndex(hash_field)) {
if (one_byte_) { return Smi::FromInt(String::ArrayIndexValueBits::decode(hash_field)).ptr();
if (flat_content.IsOneByte()) {
return CompareRawStringContents(one_byte_content_,
flat_content.ToOneByteVector().begin(),
length());
} else {
DCHECK(flat_content.IsTwoByte());
for (int i = 0; i < length(); i++) {
if (flat_content.Get(i) != one_byte_content_[i]) return false;
}
return true;
}
} else {
if (flat_content.IsTwoByte()) {
return CompareRawStringContents(
two_byte_content_, flat_content.ToUC16Vector().begin(), length());
} else {
DCHECK(flat_content.IsOneByte());
for (int i = 0; i < length(); i++) {
if (flat_content.Get(i) != two_byte_content_[i]) return false;
}
return true;
}
}
} }
V8_WARN_UNUSED_RESULT Handle<String> AsHandle(Isolate* isolate) override { if ((hash_field & Name::kIsNotArrayIndexMask) == 0) {
UNREACHABLE(); // It is an indexed, but it's not cached.
return Smi::FromInt(ResultSentinel::kUnsupported).ptr();
} }
private: int entry = table->FindEntry(ReadOnlyRoots(isolate), &key, key.hash());
String string_; if (entry == kNotFound) {
bool one_byte_; // A string that's not an array index, and not in the string table,
bool special_flattening_; // cannot have been used as a property name before.
union { return Smi::FromInt(ResultSentinel::kNotFound).ptr();
uint8_t* one_byte_content_; }
uint16_t* two_byte_content_;
}; String internalized = String::cast(table->KeyAt(entry));
union { if (FLAG_thin_strings) {
uint8_t one_byte_buffer_[256]; MakeStringThin(string, internalized, isolate);
uint16_t two_byte_buffer_[128]; }
}; return internalized.ptr();
}; }
} // namespace } // namespace
// static // static
Address StringTable::LookupStringIfExists_NoAllocate(Isolate* isolate, Address StringTable::LookupStringIfExists_NoAllocate(Isolate* isolate,
Address raw_string) { Address raw_string) {
DisallowHeapAllocation no_gc;
String string = String::cast(Object(raw_string)); String string = String::cast(Object(raw_string));
Heap* heap = isolate->heap(); DCHECK(!string.IsInternalizedString());
StringTable table = heap->string_table();
StringTableNoAllocateKey key(string, HashSeed(isolate));
// String could be an array index.
uint32_t hash = string->hash_field();
// Valid array indices are >= 0, so they cannot be mixed up with any of // Valid array indices are >= 0, so they cannot be mixed up with any of
// the result sentinels, which are negative. // the result sentinels, which are negative.
...@@ -7060,26 +6981,10 @@ Address StringTable::LookupStringIfExists_NoAllocate(Isolate* isolate, ...@@ -7060,26 +6981,10 @@ Address StringTable::LookupStringIfExists_NoAllocate(Isolate* isolate,
STATIC_ASSERT( STATIC_ASSERT(
!String::ArrayIndexValueBits::is_valid(ResultSentinel::kNotFound)); !String::ArrayIndexValueBits::is_valid(ResultSentinel::kNotFound));
if (Name::ContainsCachedArrayIndex(hash)) { if (string.IsOneByteRepresentation()) {
return Smi::FromInt(String::ArrayIndexValueBits::decode(hash)).ptr(); return i::LookupString<uint8_t>(isolate, string);
}
if ((hash & Name::kIsNotArrayIndexMask) == 0) {
// It is an indexed, but it's not cached.
return Smi::FromInt(ResultSentinel::kUnsupported).ptr();
}
DCHECK(!string->IsInternalizedString());
int entry = table->FindEntry(ReadOnlyRoots(isolate), &key, key.hash());
if (entry != kNotFound) {
String internalized = String::cast(table->KeyAt(entry));
if (FLAG_thin_strings) {
MakeStringThin(string, internalized, isolate);
}
return internalized.ptr();
} }
// A string that's not an array index, and not in the string table, return i::LookupString<uint16_t>(isolate, string);
// cannot have been used as a property name before.
return Smi::FromInt(ResultSentinel::kNotFound).ptr();
} }
String StringTable::ForwardStringIfExists(Isolate* isolate, StringTableKey* key, String StringTable::ForwardStringIfExists(Isolate* isolate, StringTableKey* key,
......
...@@ -532,7 +532,9 @@ void String::WriteToFlat(String src, sinkchar* sink, int f, int t) { ...@@ -532,7 +532,9 @@ void String::WriteToFlat(String src, sinkchar* sink, int f, int t) {
int from = f; int from = f;
int to = t; int to = t;
while (true) { while (true) {
DCHECK(0 <= from && from <= to && to <= source->length()); DCHECK_LE(0, from);
DCHECK_LE(from, to);
DCHECK_LE(to, source->length());
switch (StringShape(source).full_representation_tag()) { switch (StringShape(source).full_representation_tag()) {
case kOneByteStringTag | kExternalStringTag: { case kOneByteStringTag | kExternalStringTag: {
CopyChars(sink, ExternalOneByteString::cast(source)->GetChars() + from, CopyChars(sink, ExternalOneByteString::cast(source)->GetChars() + from,
......
...@@ -186,7 +186,7 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer { ...@@ -186,7 +186,7 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
}; };
// Used to insert a deserialized internalized string into the string table. // Used to insert a deserialized internalized string into the string table.
class StringTableInsertionKey : public StringTableKey { class StringTableInsertionKey final : public StringTableKey {
public: public:
explicit StringTableInsertionKey(String string); explicit StringTableInsertionKey(String string);
......
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