From ef0b2aabd9439e0efe823b6f46bf553a5d5f8b73 Mon Sep 17 00:00:00 2001 From: Leszek Swirski Date: Fri, 10 Dec 2021 16:24:19 +0100 Subject: [PATCH] [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 Commit-Queue: Leszek Swirski Auto-Submit: Leszek Swirski Cr-Commit-Position: refs/heads/main@{#78338} --- src/ast/ast-value-factory.cc | 12 +++++++----- src/ast/ast-value-factory.h | 31 ++++++++++++++++++++++++------- src/ast/scopes.cc | 16 +++++++++------- src/parsing/func-name-inferrer.cc | 2 +- src/parsing/parse-info.cc | 16 +++++++++++----- src/parsing/parse-info.h | 21 ++++++++++++++++++--- src/parsing/parser-base.h | 2 +- test/cctest/test-parsing.cc | 19 ++++++++----------- 8 files changed, 79 insertions(+), 40 deletions(-) diff --git a/src/ast/ast-value-factory.cc b/src/ast/ast-value-factory.cc index 4dab59fdae..94d364ce54 100644 --- a/src/ast/ast-value-factory.cc +++ b/src/ast/ast-value-factory.cc @@ -353,16 +353,18 @@ const AstRawString* AstValueFactory::GetString( } AstConsString* AstValueFactory::NewConsString() { - return zone()->New(); + return single_parse_zone()->New(); } 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 @@ -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(length); + byte* new_literal_bytes = ast_raw_string_zone()->NewArray(length); memcpy(new_literal_bytes, literal_bytes.begin(), length); - AstRawString* new_string = zone()->New( + AstRawString* new_string = ast_raw_string_zone()->New( is_one_byte, base::Vector(new_literal_bytes, length), raw_hash_field); CHECK_NOT_NULL(new_string); diff --git a/src/ast/ast-value-factory.h b/src/ast/ast-value-factory.h index d036d99604..aa6a99852f 100644 --- a/src/ast/ast-value-factory.h +++ b/src/ast/ast-value-factory.h @@ -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(); } - 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 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_; }; diff --git a/src/ast/scopes.cc b/src/ast/scopes.cc index 1375c26f15..a68c6349c9 100644 --- a/src/ast/scopes.cc +++ b/src/ast/scopes.cc @@ -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( - avfactory->zone())) { + : DeclarationScope(avfactory->single_parse_zone(), script_scope, + MODULE_SCOPE, FunctionKind::kModule), + module_descriptor_( + avfactory->single_parse_zone()->New( + avfactory->single_parse_zone())) { set_language_mode(LanguageMode::kStrict); DeclareThis(avfactory); } ModuleScope::ModuleScope(Handle 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); diff --git a/src/parsing/func-name-inferrer.cc b/src/parsing/func-name-inferrer.cc index b61224fbbb..aff064b8c8 100644 --- a/src/parsing/func-name-inferrer.cc +++ b/src/parsing/func-name-inferrer.cc @@ -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()); } diff --git a/src/parsing/parse-info.cc b/src/parsing/parse-info.cc index 048948ed3c..e706704d26 100644 --- a/src/parsing/parse-info.cc +++ b/src/parsing/parse-info.cc @@ -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(); } diff --git a/src/parsing/parse-info.h b/src/parsing/parse-info.h index 8ceccdd514..b497bfb752 100644 --- a/src/parsing/parse-info.h +++ b/src/parsing/parse-info.h @@ -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 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_; } diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h index b44a5407a6..f15cce5421 100644 --- a/src/parsing/parser-base.h +++ b/src/parsing/parser-base.h @@ -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_; } diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index f2b3842774..5501c5f80a 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -3657,7 +3657,6 @@ TEST(MaybeAssignedParameters) { base::ScopedVector program(Utf8LengthHelper(source) + Utf8LengthHelper(suffix) + 1); base::SNPrintF(program, "%s%s", source, suffix); - std::unique_ptr info; printf("%s\n", program.begin()); v8::Local v = CompileRun(program.begin()); i::Handle 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(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 info = - std::make_unique(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); }