[parser] Split AstRawString and Parser zones

This allows us to reuse AstValueFactory's string table across multiple
parsers, while still releasing memory after each individual parse.

This is mild overkill for all the single parses that don't reuse
AstValueFactories, but there at least the AstRawStrings now end up
grouped together in memory, so that might have mild cache benefits.

Change-Id: I0b378760b601fa4ec6559a0dca5d7ed6f895e992
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3322764
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78338}
This commit is contained in:
Leszek Swirski 2021-12-10 16:24:19 +01:00 committed by V8 LUCI CQ
parent 4fae8b1680
commit ef0b2aabd9
8 changed files with 79 additions and 40 deletions

View File

@ -353,16 +353,18 @@ const AstRawString* AstValueFactory::GetString(
}
AstConsString* AstValueFactory::NewConsString() {
return zone()->New<AstConsString>();
return single_parse_zone()->New<AstConsString>();
}
AstConsString* AstValueFactory::NewConsString(const AstRawString* str) {
return NewConsString()->AddString(zone(), str);
return NewConsString()->AddString(single_parse_zone(), str);
}
AstConsString* AstValueFactory::NewConsString(const AstRawString* str1,
const AstRawString* str2) {
return NewConsString()->AddString(zone(), str1)->AddString(zone(), str2);
return NewConsString()
->AddString(single_parse_zone(), str1)
->AddString(single_parse_zone(), str2);
}
template <typename IsolateT>
@ -395,9 +397,9 @@ const AstRawString* AstValueFactory::GetString(
[&]() {
// Copy literal contents for later comparison.
int length = literal_bytes.length();
byte* new_literal_bytes = zone()->NewArray<byte>(length);
byte* new_literal_bytes = ast_raw_string_zone()->NewArray<byte>(length);
memcpy(new_literal_bytes, literal_bytes.begin(), length);
AstRawString* new_string = zone()->New<AstRawString>(
AstRawString* new_string = ast_raw_string_zone()->New<AstRawString>(
is_one_byte, base::Vector<const byte>(new_literal_bytes, length),
raw_hash_field);
CHECK_NOT_NULL(new_string);

View File

@ -311,24 +311,40 @@ class AstValueFactory {
public:
AstValueFactory(Zone* zone, const AstStringConstants* string_constants,
uint64_t hash_seed)
: AstValueFactory(zone, zone, string_constants, hash_seed) {}
AstValueFactory(Zone* ast_raw_string_zone, Zone* single_parse_zone,
const AstStringConstants* string_constants,
uint64_t hash_seed)
: string_table_(string_constants->string_table()),
strings_(nullptr),
strings_end_(&strings_),
string_constants_(string_constants),
empty_cons_string_(nullptr),
zone_(zone),
ast_raw_string_zone_(ast_raw_string_zone),
single_parse_zone_(single_parse_zone),
hash_seed_(hash_seed) {
DCHECK_NOT_NULL(zone_);
DCHECK_NOT_NULL(ast_raw_string_zone_);
DCHECK_NOT_NULL(single_parse_zone_);
DCHECK_EQ(hash_seed, string_constants->hash_seed());
std::fill(one_character_strings_,
one_character_strings_ + arraysize(one_character_strings_),
nullptr);
empty_cons_string_ = NewConsString();
// Allocate the empty ConsString in the AstRawString Zone instead of the
// single parse Zone like other ConsStrings, because unlike those it can be
// reused across parses.
empty_cons_string_ = ast_raw_string_zone_->New<AstConsString>();
}
Zone* zone() const {
DCHECK_NOT_NULL(zone_);
return zone_;
Zone* ast_raw_string_zone() const {
DCHECK_NOT_NULL(ast_raw_string_zone_);
return ast_raw_string_zone_;
}
Zone* single_parse_zone() const {
DCHECK_NOT_NULL(single_parse_zone_);
return single_parse_zone_;
}
const AstRawString* GetOneByteString(base::Vector<const uint8_t> literal) {
@ -394,7 +410,8 @@ class AstValueFactory {
static const int kMaxOneCharStringValue = 128;
const AstRawString* one_character_strings_[kMaxOneCharStringValue];
Zone* zone_;
Zone* ast_raw_string_zone_;
Zone* single_parse_zone_;
uint64_t hash_seed_;
};

View File

@ -166,17 +166,19 @@ DeclarationScope::DeclarationScope(Zone* zone, Scope* outer_scope,
ModuleScope::ModuleScope(DeclarationScope* script_scope,
AstValueFactory* avfactory)
: DeclarationScope(avfactory->zone(), script_scope, MODULE_SCOPE,
FunctionKind::kModule),
module_descriptor_(avfactory->zone()->New<SourceTextModuleDescriptor>(
avfactory->zone())) {
: DeclarationScope(avfactory->single_parse_zone(), script_scope,
MODULE_SCOPE, FunctionKind::kModule),
module_descriptor_(
avfactory->single_parse_zone()->New<SourceTextModuleDescriptor>(
avfactory->single_parse_zone())) {
set_language_mode(LanguageMode::kStrict);
DeclareThis(avfactory);
}
ModuleScope::ModuleScope(Handle<ScopeInfo> scope_info,
AstValueFactory* avfactory)
: DeclarationScope(avfactory->zone(), MODULE_SCOPE, avfactory, scope_info),
: DeclarationScope(avfactory->single_parse_zone(), MODULE_SCOPE, avfactory,
scope_info),
module_descriptor_(nullptr) {
set_language_mode(LanguageMode::kStrict);
}
@ -1623,7 +1625,7 @@ void DeclarationScope::ResetAfterPreparsing(AstValueFactory* ast_value_factory,
has_rest_ = false;
function_ = nullptr;
DCHECK_NE(zone(), ast_value_factory->zone());
DCHECK_NE(zone(), ast_value_factory->single_parse_zone());
// Make sure this scope and zone aren't used for allocation anymore.
{
// Get the zone, while variables_ is still valid
@ -1634,7 +1636,7 @@ void DeclarationScope::ResetAfterPreparsing(AstValueFactory* ast_value_factory,
if (aborted) {
// Prepare scope for use in the outer zone.
variables_ = VariableMap(ast_value_factory->zone());
variables_ = VariableMap(ast_value_factory->single_parse_zone());
if (!IsArrowFunction(function_kind_)) {
has_simple_parameters_ = true;
DeclareDefaultFunctionVariables(ast_value_factory);

View File

@ -60,7 +60,7 @@ AstConsString* FuncNameInferrer::MakeNameFromStack() {
continue;
}
// Add name. Separate names with ".".
Zone* zone = ast_value_factory_->zone();
Zone* zone = ast_value_factory_->single_parse_zone();
if (!result->IsEmpty()) {
result->AddString(zone, ast_value_factory_->dot_string());
}

View File

@ -176,9 +176,12 @@ ReusableUnoptimizedCompileState::ReusableUnoptimizedCompileState(
logger_(isolate->logger()),
dispatcher_(isolate->lazy_compile_dispatcher()),
ast_string_constants_(isolate->ast_string_constants()),
zone_(allocator_, "unoptimized-compile-zone"),
ast_raw_string_zone_(allocator_,
"unoptimized-compile-ast-raw-string-zone"),
single_parse_zone_(allocator_, "unoptimized-compile-parse-zone"),
ast_value_factory_(
new AstValueFactory(zone(), ast_string_constants(), hash_seed())) {}
new AstValueFactory(ast_raw_string_zone(), single_parse_zone(),
ast_string_constants(), hash_seed())) {}
ReusableUnoptimizedCompileState::ReusableUnoptimizedCompileState(
LocalIsolate* isolate)
@ -187,9 +190,12 @@ ReusableUnoptimizedCompileState::ReusableUnoptimizedCompileState(
logger_(isolate->main_thread_logger()),
dispatcher_(isolate->lazy_compile_dispatcher()),
ast_string_constants_(isolate->ast_string_constants()),
zone_(allocator_, "unoptimized-compile-zone"),
ast_raw_string_zone_(allocator_,
"unoptimized-compile-ast-raw-string-zone"),
single_parse_zone_(allocator_, "unoptimized-compile-parse-zone"),
ast_value_factory_(
new AstValueFactory(zone(), ast_string_constants(), hash_seed())) {}
new AstValueFactory(ast_raw_string_zone(), single_parse_zone(),
ast_string_constants(), hash_seed())) {}
ReusableUnoptimizedCompileState::~ReusableUnoptimizedCompileState() = default;
@ -235,7 +241,7 @@ ParseInfo::ParseInfo(LocalIsolate* isolate, const UnoptimizedCompileFlags flags,
: ParseInfo(flags, state, reusable_state, stack_limit,
isolate->runtime_call_stats()) {}
ParseInfo::~ParseInfo() = default;
ParseInfo::~ParseInfo() { reusable_state_->NotifySingleParseCompleted(); }
DeclarationScope* ParseInfo::scope() const { return literal()->scope(); }

View File

@ -184,7 +184,21 @@ class V8_EXPORT_PRIVATE ReusableUnoptimizedCompileState {
explicit ReusableUnoptimizedCompileState(LocalIsolate* isolate);
~ReusableUnoptimizedCompileState();
Zone* zone() { return &zone_; }
// The AstRawString Zone stores the AstRawStrings in the AstValueFactory that
// can be reused across parses, and thereforce should stay alive between
// parses that reuse this reusable state and its AstValueFactory.
Zone* ast_raw_string_zone() { return &ast_raw_string_zone_; }
// The single parse Zone stores the data of a single parse, and can be cleared
// when that parse completes.
//
// This is in "reusable" state despite being wiped per-parse, because it
// allows us to reuse the Zone itself, and e.g. keep the same single parse
// Zone pointer in the AstValueFactory.
Zone* single_parse_zone() { return &single_parse_zone_; }
void NotifySingleParseCompleted() { single_parse_zone_.ReleaseMemory(); }
AstValueFactory* ast_value_factory() const {
return ast_value_factory_.get();
}
@ -202,7 +216,8 @@ class V8_EXPORT_PRIVATE ReusableUnoptimizedCompileState {
Logger* logger_;
LazyCompileDispatcher* dispatcher_;
const AstStringConstants* ast_string_constants_;
Zone zone_;
Zone ast_raw_string_zone_;
Zone single_parse_zone_;
std::unique_ptr<AstValueFactory> ast_value_factory_;
};
@ -226,7 +241,7 @@ class V8_EXPORT_PRIVATE ParseInfo {
ScriptOriginOptions origin_options,
NativesFlag natives = NOT_NATIVES_CODE);
Zone* zone() const { return reusable_state_->zone(); }
Zone* zone() const { return reusable_state_->single_parse_zone(); }
const UnoptimizedCompileFlags& flags() const { return flags_; }

View File

@ -300,7 +300,7 @@ class ParserBase {
void ResetFunctionLiteralId() { function_literal_id_ = 0; }
// The Zone where the parsing outputs are stored.
Zone* main_zone() const { return ast_value_factory()->zone(); }
Zone* main_zone() const { return ast_value_factory()->single_parse_zone(); }
// The current Zone, which might be the main zone or a temporary Zone.
Zone* zone() const { return zone_; }

View File

@ -3657,7 +3657,6 @@ TEST(MaybeAssignedParameters) {
base::ScopedVector<char> program(Utf8LengthHelper(source) +
Utf8LengthHelper(suffix) + 1);
base::SNPrintF(program, "%s%s", source, suffix);
std::unique_ptr<i::ParseInfo> info;
printf("%s\n", program.begin());
v8::Local<v8::Value> v = CompileRun(program.begin());
i::Handle<i::Object> o = v8::Utils::OpenHandle(*v);
@ -3668,16 +3667,15 @@ TEST(MaybeAssignedParameters) {
i::UnoptimizedCompileFlags flags =
i::UnoptimizedCompileFlags::ForFunctionCompile(isolate, *shared);
flags.set_allow_lazy_parsing(allow_lazy);
info = std::make_unique<i::ParseInfo>(isolate, flags, &state,
&reusable_state);
CHECK_PARSE_FUNCTION(info.get(), shared, isolate);
i::ParseInfo info(isolate, flags, &state, &reusable_state);
CHECK_PARSE_FUNCTION(&info, shared, isolate);
i::Scope* scope = info->literal()->scope();
i::Scope* scope = info.literal()->scope();
CHECK(!scope->AsDeclarationScope()->was_lazily_parsed());
CHECK_NULL(scope->sibling());
CHECK(scope->is_function_scope());
const i::AstRawString* var_name =
info->ast_value_factory()->GetOneByteString("arg");
info.ast_value_factory()->GetOneByteString("arg");
i::Variable* var = scope->LookupForTesting(var_name);
CHECK(var->is_used() || !assigned);
bool is_maybe_assigned = var->maybe_assigned() == i::kMaybeAssigned;
@ -3708,12 +3706,11 @@ static void TestMaybeAssigned(Input input, const char* variable, bool module,
i::UnoptimizedCompileFlags::ForScriptCompile(isolate, *script);
flags.set_is_module(module);
flags.set_allow_lazy_parsing(allow_lazy_parsing);
std::unique_ptr<i::ParseInfo> info =
std::make_unique<i::ParseInfo>(isolate, flags, &state, &reusable_state);
i::ParseInfo info(isolate, flags, &state, &reusable_state);
CHECK_PARSE_PROGRAM(info.get(), script, isolate);
CHECK_PARSE_PROGRAM(&info, script, isolate);
i::Scope* scope = info->literal()->scope();
i::Scope* scope = info.literal()->scope();
CHECK(!scope->AsDeclarationScope()->was_lazily_parsed());
CHECK_NULL(scope->sibling());
CHECK(module ? scope->is_module_scope() : scope->is_script_scope());
@ -3723,7 +3720,7 @@ static void TestMaybeAssigned(Input input, const char* variable, bool module,
// Find the variable.
scope = i::ScopeTestHelper::FindScope(scope, input.location);
const i::AstRawString* var_name =
info->ast_value_factory()->GetOneByteString(variable);
info.ast_value_factory()->GetOneByteString(variable);
var = scope->LookupForTesting(var_name);
}