Commit ddca3606 authored by Leszek Swirski's avatar Leszek Swirski Committed by Commit Bot

[ast] Allocate cons strings on-demand

Remove AstConsString "internalization", and instead make the conversion
to heap String be on-demand with an Allocate method. We never actually
need the heapified cons string more than once, so there's no need to do
the internalization walk or do the next/string union dance in the
AstConsString class.

This also allows us to specify how we want to allocate the String at the
call site. In particular, it allows us to allocate a flat SeqString rather
rather than a ConsString. This allows us to avoid allocating ConsStrings
which will just be passed to a flatten call, and especially avoid
allocating dead ConsStrings in the off-thread old space.

Bug: chromium:1011762
Bug: chromium:1043168
Change-Id: Id851f2f7529d92ad7e5388eb22823fd6d1959cd0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2020953Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66042}
parent 42ceebee
...@@ -160,36 +160,87 @@ bool AstRawString::Compare(void* a, void* b) { ...@@ -160,36 +160,87 @@ bool AstRawString::Compare(void* a, void* b) {
} }
template <typename Factory> template <typename Factory>
void AstConsString::Internalize(Factory* factory) { FactoryHandle<Factory, String> AstConsString::Allocate(Factory* factory) const {
if (IsEmpty()) { if (IsEmpty()) {
set_string(factory->empty_string()); return factory->empty_string();
return;
} }
// AstRawStrings are internalized before AstConsStrings, so // AstRawStrings are internalized before AstConsStrings are allocated, so
// AstRawString::string() will just work. // AstRawString::string() will just work.
FactoryHandle<Factory, String> tmp(segment_.string->string().get<Factory>()); FactoryHandle<Factory, String> tmp(segment_.string->string().get<Factory>());
for (AstConsString::Segment* current = segment_.next; current != nullptr; for (AstConsString::Segment* current = segment_.next; current != nullptr;
current = current->next) { current = current->next) {
tmp = tmp = factory
factory ->NewConsString(current->string->string().get<Factory>(), tmp,
->NewConsString( AllocationType::kOld)
current->string->string().get<Factory>(), tmp, .ToHandleChecked();
// TODO(leszeks): This is to avoid memory regressions while this }
// path is under development -- the off-thread factory doesn't return tmp;
// support young allocations. Figure out a way to avoid memory }
// regressions related to ConsStrings in the off-thread path. template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
std::is_same<Factory, OffThreadFactory>::value Handle<String> AstConsString::Allocate<Factory>(Factory* factory) const;
? AllocationType::kOld template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
: AllocationType::kYoung) OffThreadHandle<String> AstConsString::Allocate<OffThreadFactory>(
OffThreadFactory* factory) const;
template <typename Factory>
FactoryHandle<Factory, String> AstConsString::AllocateFlat(
Factory* factory) const {
if (IsEmpty()) {
return factory->empty_string();
}
if (!segment_.next) {
return segment_.string->string().get<Factory>();
}
int result_length = 0;
bool is_one_byte = true;
for (const AstConsString::Segment* current = &segment_; current != nullptr;
current = current->next) {
result_length += current->string->length();
is_one_byte = is_one_byte && current->string->is_one_byte();
}
if (is_one_byte) {
FactoryHandle<Factory, SeqOneByteString> result =
factory->NewRawOneByteString(result_length, AllocationType::kOld)
.ToHandleChecked(); .ToHandleChecked();
DisallowHeapAllocation no_gc;
uint8_t* dest = result->GetChars(no_gc) + result_length;
for (const AstConsString::Segment* current = &segment_; current != nullptr;
current = current->next) {
int length = current->string->length();
dest -= length;
CopyChars(dest, current->string->raw_data(), length);
}
DCHECK_EQ(dest, result->GetChars(no_gc));
return result;
}
FactoryHandle<Factory, SeqTwoByteString> result =
factory->NewRawTwoByteString(result_length, AllocationType::kOld)
.ToHandleChecked();
DisallowHeapAllocation no_gc;
uint16_t* dest = result->GetChars(no_gc) + result_length;
for (const AstConsString::Segment* current = &segment_; current != nullptr;
current = current->next) {
int length = current->string->length();
dest -= length;
if (current->string->is_one_byte()) {
CopyChars(dest, current->string->raw_data(), length);
} else {
CopyChars(dest,
reinterpret_cast<const uint16_t*>(current->string->raw_data()),
length);
}
} }
set_string(tmp); DCHECK_EQ(dest, result->GetChars(no_gc));
return result;
} }
template EXPORT_TEMPLATE_DEFINE( template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
V8_EXPORT_PRIVATE) void AstConsString::Internalize<Factory>(Factory* Handle<String> AstConsString::AllocateFlat<Factory>(Factory* factory) const;
factory); template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void AstConsString:: OffThreadHandle<String> AstConsString::AllocateFlat<OffThreadFactory>(
Internalize<OffThreadFactory>(OffThreadFactory* factory); OffThreadFactory* factory) const;
std::forward_list<const AstRawString*> AstConsString::ToRawStrings() const { std::forward_list<const AstRawString*> AstConsString::ToRawStrings() const {
std::forward_list<const AstRawString*> result; std::forward_list<const AstRawString*> result;
...@@ -275,10 +326,7 @@ const AstRawString* AstValueFactory::CloneFromOtherFactory( ...@@ -275,10 +326,7 @@ const AstRawString* AstValueFactory::CloneFromOtherFactory(
} }
AstConsString* AstValueFactory::NewConsString() { AstConsString* AstValueFactory::NewConsString() {
AstConsString* new_string = new (zone_) AstConsString; return new (zone_) AstConsString;
DCHECK_NOT_NULL(new_string);
AddConsString(new_string);
return new_string;
} }
AstConsString* AstValueFactory::NewConsString(const AstRawString* str) { AstConsString* AstValueFactory::NewConsString(const AstRawString* str) {
...@@ -300,13 +348,6 @@ void AstValueFactory::Internalize(Factory* factory) { ...@@ -300,13 +348,6 @@ void AstValueFactory::Internalize(Factory* factory) {
current = next; current = next;
} }
// AstConsStrings refer to AstRawStrings.
for (AstConsString* current = cons_strings_; current != nullptr;) {
AstConsString* next = current->next();
current->Internalize(factory);
current = next;
}
ResetStrings(); ResetStrings();
} }
template EXPORT_TEMPLATE_DEFINE( template EXPORT_TEMPLATE_DEFINE(
......
...@@ -144,13 +144,12 @@ class AstConsString final : public ZoneObject { ...@@ -144,13 +144,12 @@ class AstConsString final : public ZoneObject {
} }
template <typename Factory> template <typename Factory>
void Internalize(Factory* factory); EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
FactoryHandle<Factory, String> Allocate(Factory* factory) const;
// This function can be called after internalizing. template <typename Factory>
V8_INLINE HandleOrOffThreadHandle<String> string() const { EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
DCHECK(has_string_); FactoryHandle<Factory, String> AllocateFlat(Factory* factory) const;
return string_;
}
std::forward_list<const AstRawString*> ToRawStrings() const; std::forward_list<const AstRawString*> ToRawStrings() const;
...@@ -162,34 +161,19 @@ class AstConsString final : public ZoneObject { ...@@ -162,34 +161,19 @@ class AstConsString final : public ZoneObject {
AstConsString* next() const { return next_; } AstConsString* next() const { return next_; }
AstConsString** next_location() { return &next_; } AstConsString** next_location() { return &next_; }
void set_string(HandleOrOffThreadHandle<String> string) { AstConsString* next_;
DCHECK(!string.is_null());
DCHECK(!has_string_);
string_ = string;
#ifdef DEBUG
has_string_ = true;
#endif
}
// {string_} is stored as Address* instead of a Handle<String> so it can be
// stored in a union with {next_}.
union {
AstConsString* next_;
HandleOrOffThreadHandle<String> string_;
};
// A linked list of AstRawStrings of the contents of this AstConsString.
// This list has several properties:
//
// * For empty strings the string pointer is null,
// * Appended raw strings are added to the head of the list, so they are in
// reverse order
struct Segment { struct Segment {
const AstRawString* string; const AstRawString* string;
AstConsString::Segment* next; AstConsString::Segment* next;
}; };
Segment segment_; Segment segment_;
#ifdef DEBUG
// (Debug-only:) Verify the object life-cylce: Some functions may only be
// called after internalization (that is, after a v8::internal::String has
// been set); some only before.
bool has_string_ = false;
#endif
}; };
enum class AstSymbol : uint8_t { kHomeObjectSymbol }; enum class AstSymbol : uint8_t { kHomeObjectSymbol };
...@@ -294,8 +278,6 @@ class AstValueFactory { ...@@ -294,8 +278,6 @@ class AstValueFactory {
: string_table_(string_constants->string_table()), : string_table_(string_constants->string_table()),
strings_(nullptr), strings_(nullptr),
strings_end_(&strings_), strings_end_(&strings_),
cons_strings_(nullptr),
cons_strings_end_(&cons_strings_),
string_constants_(string_constants), string_constants_(string_constants),
empty_cons_string_(nullptr), empty_cons_string_(nullptr),
zone_(zone), zone_(zone),
...@@ -346,16 +328,9 @@ class AstValueFactory { ...@@ -346,16 +328,9 @@ class AstValueFactory {
strings_end_ = string->next_location(); strings_end_ = string->next_location();
return string; return string;
} }
AstConsString* AddConsString(AstConsString* string) {
*cons_strings_end_ = string;
cons_strings_end_ = string->next_location();
return string;
}
void ResetStrings() { void ResetStrings() {
strings_ = nullptr; strings_ = nullptr;
strings_end_ = &strings_; strings_end_ = &strings_;
cons_strings_ = nullptr;
cons_strings_end_ = &cons_strings_;
} }
V8_EXPORT_PRIVATE AstRawString* GetOneByteStringInternal( V8_EXPORT_PRIVATE AstRawString* GetOneByteStringInternal(
Vector<const uint8_t> literal); Vector<const uint8_t> literal);
...@@ -366,12 +341,8 @@ class AstValueFactory { ...@@ -366,12 +341,8 @@ class AstValueFactory {
// All strings are copied here, one after another (no zeroes inbetween). // All strings are copied here, one after another (no zeroes inbetween).
base::CustomMatcherHashMap string_table_; base::CustomMatcherHashMap string_table_;
// We need to keep track of strings_ in order since cons strings require their
// members to be internalized first.
AstRawString* strings_; AstRawString* strings_;
AstRawString** strings_end_; AstRawString** strings_end_;
AstConsString* cons_strings_;
AstConsString** cons_strings_end_;
// Holds constant string values which are shared across the isolate. // Holds constant string values which are shared across the isolate.
const AstStringConstants* string_constants_; const AstStringConstants* string_constants_;
......
...@@ -2218,9 +2218,8 @@ class FunctionLiteral final : public Expression { ...@@ -2218,9 +2218,8 @@ class FunctionLiteral final : public Expression {
// Empty handle means that the function does not have a shared name (i.e. // Empty handle means that the function does not have a shared name (i.e.
// the name will be set dynamically after creation of the function closure). // the name will be set dynamically after creation of the function closure).
MaybeHandle<String> name() const { MaybeHandle<String> GetName(Factory* factory) const {
return raw_name_ ? raw_name_->string().get<Factory>() return raw_name_ ? raw_name_->AllocateFlat(factory) : MaybeHandle<String>();
: MaybeHandle<String>();
} }
bool has_shared_name() const { return raw_name_ != nullptr; } bool has_shared_name() const { return raw_name_ != nullptr; }
const AstConsString* raw_name() const { return raw_name_; } const AstConsString* raw_name() const { return raw_name_; }
...@@ -2278,13 +2277,13 @@ class FunctionLiteral final : public Expression { ...@@ -2278,13 +2277,13 @@ class FunctionLiteral final : public Expression {
// Returns either name or inferred name as a cstring. // Returns either name or inferred name as a cstring.
std::unique_ptr<char[]> GetDebugName() const; std::unique_ptr<char[]> GetDebugName() const;
Handle<String> inferred_name() const { Handle<String> GetInferredName(Factory* factory) const {
if (!inferred_name_.is_null()) { if (!inferred_name_.is_null()) {
DCHECK_NULL(raw_inferred_name_); DCHECK_NULL(raw_inferred_name_);
return inferred_name_; return inferred_name_;
} }
if (raw_inferred_name_ != nullptr) { if (raw_inferred_name_ != nullptr) {
return raw_inferred_name_->string().get<Factory>(); return raw_inferred_name_->Allocate(factory);
} }
UNREACHABLE(); UNREACHABLE();
} }
......
...@@ -3214,7 +3214,7 @@ Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfoForLiteral( ...@@ -3214,7 +3214,7 @@ Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfoForLiteral(
FunctionLiteral* literal, Handle<Script> script, bool is_toplevel) { FunctionLiteral* literal, Handle<Script> script, bool is_toplevel) {
FunctionKind kind = literal->kind(); FunctionKind kind = literal->kind();
Handle<SharedFunctionInfo> shared = NewSharedFunctionInfoForBuiltin( Handle<SharedFunctionInfo> shared = NewSharedFunctionInfoForBuiltin(
literal->name(), Builtins::kCompileLazy, kind); literal->GetName(this), Builtins::kCompileLazy, kind);
SharedFunctionInfo::InitFromFunctionLiteral(isolate(), shared, literal, SharedFunctionInfo::InitFromFunctionLiteral(isolate(), shared, literal,
is_toplevel); is_toplevel);
shared->SetScript(ReadOnlyRoots(isolate()), *script, shared->SetScript(ReadOnlyRoots(isolate()), *script,
...@@ -3291,7 +3291,7 @@ Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo( ...@@ -3291,7 +3291,7 @@ Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(
Handle<String> shared_name; Handle<String> shared_name;
bool has_shared_name = maybe_name.ToHandle(&shared_name); bool has_shared_name = maybe_name.ToHandle(&shared_name);
if (has_shared_name) { if (has_shared_name) {
shared_name = String::Flatten(isolate(), shared_name, AllocationType::kOld); DCHECK(shared_name->IsFlat());
shared->set_name_or_scope_info(*shared_name); shared->set_name_or_scope_info(*shared_name);
} else { } else {
DCHECK_EQ(shared->name_or_scope_info(), DCHECK_EQ(shared->name_or_scope_info(),
...@@ -4061,6 +4061,8 @@ bool Factory::EmptyStringRootIsInitialized() { ...@@ -4061,6 +4061,8 @@ bool Factory::EmptyStringRootIsInitialized() {
NewFunctionArgs NewFunctionArgs::ForWasm( NewFunctionArgs NewFunctionArgs::ForWasm(
Handle<String> name, Handle<String> name,
Handle<WasmExportedFunctionData> exported_function_data, Handle<Map> map) { Handle<WasmExportedFunctionData> exported_function_data, Handle<Map> map) {
DCHECK(name->IsFlat());
NewFunctionArgs args; NewFunctionArgs args;
args.name_ = name; args.name_ = name;
args.maybe_map_ = map; args.maybe_map_ = map;
...@@ -4075,6 +4077,8 @@ NewFunctionArgs NewFunctionArgs::ForWasm( ...@@ -4075,6 +4077,8 @@ NewFunctionArgs NewFunctionArgs::ForWasm(
NewFunctionArgs NewFunctionArgs::ForWasm( NewFunctionArgs NewFunctionArgs::ForWasm(
Handle<String> name, Handle<WasmJSFunctionData> js_function_data, Handle<String> name, Handle<WasmJSFunctionData> js_function_data,
Handle<Map> map) { Handle<Map> map) {
DCHECK(name->IsFlat());
NewFunctionArgs args; NewFunctionArgs args;
args.name_ = name; args.name_ = name;
args.maybe_map_ = map; args.maybe_map_ = map;
...@@ -4089,6 +4093,7 @@ NewFunctionArgs NewFunctionArgs::ForWasm( ...@@ -4089,6 +4093,7 @@ NewFunctionArgs NewFunctionArgs::ForWasm(
NewFunctionArgs NewFunctionArgs::ForBuiltin(Handle<String> name, NewFunctionArgs NewFunctionArgs::ForBuiltin(Handle<String> name,
Handle<Map> map, int builtin_id) { Handle<Map> map, int builtin_id) {
DCHECK(Builtins::IsBuiltinId(builtin_id)); DCHECK(Builtins::IsBuiltinId(builtin_id));
DCHECK(name->IsFlat());
NewFunctionArgs args; NewFunctionArgs args;
args.name_ = name; args.name_ = name;
...@@ -4105,6 +4110,8 @@ NewFunctionArgs NewFunctionArgs::ForBuiltin(Handle<String> name, ...@@ -4105,6 +4110,8 @@ NewFunctionArgs NewFunctionArgs::ForBuiltin(Handle<String> name,
// static // static
NewFunctionArgs NewFunctionArgs::ForFunctionWithoutCode( NewFunctionArgs NewFunctionArgs::ForFunctionWithoutCode(
Handle<String> name, Handle<Map> map, LanguageMode language_mode) { Handle<String> name, Handle<Map> map, LanguageMode language_mode) {
DCHECK(name->IsFlat());
NewFunctionArgs args; NewFunctionArgs args;
args.name_ = name; args.name_ = name;
args.maybe_map_ = map; args.maybe_map_ = map;
...@@ -4123,6 +4130,7 @@ NewFunctionArgs NewFunctionArgs::ForBuiltinWithPrototype( ...@@ -4123,6 +4130,7 @@ NewFunctionArgs NewFunctionArgs::ForBuiltinWithPrototype(
int instance_size, int inobject_properties, int builtin_id, int instance_size, int inobject_properties, int builtin_id,
MutableMode prototype_mutability) { MutableMode prototype_mutability) {
DCHECK(Builtins::IsBuiltinId(builtin_id)); DCHECK(Builtins::IsBuiltinId(builtin_id));
DCHECK(name->IsFlat());
NewFunctionArgs args; NewFunctionArgs args;
args.name_ = name; args.name_ = name;
...@@ -4145,6 +4153,7 @@ NewFunctionArgs NewFunctionArgs::ForBuiltinWithPrototype( ...@@ -4145,6 +4153,7 @@ NewFunctionArgs NewFunctionArgs::ForBuiltinWithPrototype(
NewFunctionArgs NewFunctionArgs::ForBuiltinWithoutPrototype( NewFunctionArgs NewFunctionArgs::ForBuiltinWithoutPrototype(
Handle<String> name, int builtin_id, LanguageMode language_mode) { Handle<String> name, int builtin_id, LanguageMode language_mode) {
DCHECK(Builtins::IsBuiltinId(builtin_id)); DCHECK(Builtins::IsBuiltinId(builtin_id));
DCHECK(name->IsFlat());
NewFunctionArgs args; NewFunctionArgs args;
args.name_ = name; args.name_ = name;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "src/base/ieee754.h" #include "src/base/ieee754.h"
#include "src/builtins/accessors.h" #include "src/builtins/accessors.h"
#include "src/codegen/compiler.h" #include "src/codegen/compiler.h"
#include "src/common/globals.h"
#include "src/debug/debug.h" #include "src/debug/debug.h"
#include "src/execution/isolate-inl.h" #include "src/execution/isolate-inl.h"
#include "src/execution/microtask-queue.h" #include "src/execution/microtask-queue.h"
...@@ -443,6 +444,7 @@ V8_NOINLINE Handle<JSFunction> SimpleCreateFunction(Isolate* isolate, ...@@ -443,6 +444,7 @@ V8_NOINLINE Handle<JSFunction> SimpleCreateFunction(Isolate* isolate,
Builtins::Name call, Builtins::Name call,
int len, bool adapt) { int len, bool adapt) {
DCHECK(Builtins::HasJSLinkage(call)); DCHECK(Builtins::HasJSLinkage(call));
name = String::Flatten(isolate, name, AllocationType::kOld);
NewFunctionArgs args = NewFunctionArgs::ForBuiltinWithoutPrototype( NewFunctionArgs args = NewFunctionArgs::ForBuiltinWithoutPrototype(
name, call, LanguageMode::kStrict); name, call, LanguageMode::kStrict);
Handle<JSFunction> fun = isolate->factory()->NewFunction(args); Handle<JSFunction> fun = isolate->factory()->NewFunction(args);
......
...@@ -5385,11 +5385,12 @@ void SharedFunctionInfo::InitFromFunctionLiteral( ...@@ -5385,11 +5385,12 @@ void SharedFunctionInfo::InitFromFunctionLiteral(
scope_data->Serialize(shared_info->GetIsolate()); scope_data->Serialize(shared_info->GetIsolate());
data = isolate->factory()->NewUncompiledDataWithPreparseData( data = isolate->factory()->NewUncompiledDataWithPreparseData(
lit->inferred_name(), lit->start_position(), lit->end_position(), lit->GetInferredName(isolate->factory()), lit->start_position(),
preparse_data); lit->end_position(), preparse_data);
} else { } else {
data = isolate->factory()->NewUncompiledDataWithoutPreparseData( data = isolate->factory()->NewUncompiledDataWithoutPreparseData(
lit->inferred_name(), lit->start_position(), lit->end_position()); lit->GetInferredName(isolate->factory()), lit->start_position(),
lit->end_position());
} }
shared_info->set_uncompiled_data(*data); shared_info->set_uncompiled_data(*data);
......
...@@ -153,7 +153,7 @@ TEST_F(OffThreadFactoryTest, AstConsString_CreatesConsString) { ...@@ -153,7 +153,7 @@ TEST_F(OffThreadFactoryTest, AstConsString_CreatesConsString) {
ast_value_factory.Internalize(off_thread_factory()); ast_value_factory.Internalize(off_thread_factory());
OffThreadHandle<FixedArray> off_thread_wrapper = OffThreadHandle<FixedArray> off_thread_wrapper =
WrapString(foobar_string->string().get<OffThreadFactory>()); WrapString(foobar_string->Allocate(off_thread_factory()));
off_thread_factory()->FinishOffThread(); off_thread_factory()->FinishOffThread();
......
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