Commit 35a6eeec authored by Leszek Swirski's avatar Leszek Swirski Committed by V8 LUCI CQ

[off-thread] Allow off-thread top-level IIFE finalization

Allow off-thread finalization for parallel compile tasks (i.e. for top-
level IIFEs).

This allows us to merge the code paths in BackgroundCompileTask, and
re-enable the compiler dispatcher tests under the off-thread
finalization flag. Indeed, we can simplify further and get rid of that
flag entirely (it has been on-by-default for several releases now).

Change-Id: I54f361997d651667fa813ec09790a6aab4d26774
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3226780Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77615}
parent afd15549
......@@ -37,6 +37,7 @@
#include "src/execution/runtime-profiler.h"
#include "src/execution/vm-state-inl.h"
#include "src/handles/maybe-handles.h"
#include "src/handles/persistent-handles.h"
#include "src/heap/heap-inl.h"
#include "src/heap/local-factory-inl.h"
#include "src/heap/local-heap-inl.h"
......@@ -754,11 +755,23 @@ bool IterativelyExecuteAndFinalizeUnoptimizedCompilationJobs(
std::vector<FunctionLiteral*> functions_to_compile;
functions_to_compile.push_back(parse_info->literal());
bool is_first = true;
while (!functions_to_compile.empty()) {
FunctionLiteral* literal = functions_to_compile.back();
functions_to_compile.pop_back();
Handle<SharedFunctionInfo> shared_info =
Compiler::GetSharedFunctionInfo(literal, script, isolate);
Handle<SharedFunctionInfo> shared_info;
if (is_first) {
// We get the first SharedFunctionInfo directly as outer_shared_info
// rather than with Compiler::GetSharedFunctionInfo, to support
// placeholder SharedFunctionInfos that aren't on the script's SFI list.
DCHECK_EQ(literal->function_literal_id(),
outer_shared_info->function_literal_id());
shared_info = outer_shared_info;
is_first = false;
} else {
shared_info = Compiler::GetSharedFunctionInfo(literal, script, isolate);
}
if (shared_info->is_compiled()) continue;
std::unique_ptr<UnoptimizedCompilationJob> job =
......@@ -809,44 +822,6 @@ bool IterativelyExecuteAndFinalizeUnoptimizedCompilationJobs(
return true;
}
bool FinalizeAllUnoptimizedCompilationJobs(
ParseInfo* parse_info, Isolate* isolate, Handle<Script> script,
UnoptimizedCompilationJobList* compilation_jobs,
FinalizeUnoptimizedCompilationDataList*
finalize_unoptimized_compilation_data_list) {
DCHECK(AllowCompilation::IsAllowed(isolate));
DCHECK(!compilation_jobs->empty());
// TODO(rmcilroy): Clear native context in debug once AsmJS generates doesn't
// rely on accessing native context during finalization.
// Allocate scope infos for the literal.
DeclarationScope::AllocateScopeInfos(parse_info, isolate);
// Finalize the functions' compilation jobs.
for (auto&& job : *compilation_jobs) {
FunctionLiteral* literal = job->compilation_info()->literal();
Handle<SharedFunctionInfo> shared_info =
Compiler::GetSharedFunctionInfo(literal, script, isolate);
// The inner function might be compiled already if compiling for debug.
if (shared_info->is_compiled()) continue;
UpdateSharedFunctionFlagsAfterCompilation(literal, *shared_info);
if (FinalizeSingleUnoptimizedCompilationJob(
job.get(), shared_info, isolate,
finalize_unoptimized_compilation_data_list) !=
CompilationJob::SUCCEEDED) {
return false;
}
}
// Report any warnings generated during compilation.
if (parse_info->pending_error_handler()->has_pending_warnings()) {
parse_info->pending_error_handler()->PrepareWarnings(isolate);
}
return true;
}
bool FinalizeDeferredUnoptimizedCompilationJobs(
Isolate* isolate, Handle<Script> script,
DeferredFinalizationJobDataList* deferred_jobs,
......@@ -1373,8 +1348,6 @@ MaybeHandle<SharedFunctionInfo> CompileToplevel(
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
parse_info->flags().is_eval() ? "V8.CompileEval" : "V8.Compile");
// Prepare and execute compilation of the outer-most function.
// Create the SharedFunctionInfo and add it to the script's list.
Handle<SharedFunctionInfo> shared_info =
CreateTopLevelSharedFunctionInfo(parse_info, script, isolate);
......@@ -1382,6 +1355,7 @@ MaybeHandle<SharedFunctionInfo> CompileToplevel(
FinalizeUnoptimizedCompilationDataList
finalize_unoptimized_compilation_data_list;
// Prepare and execute compilation of the outer-most function.
if (!IterativelyExecuteAndFinalizeUnoptimizedCompilationJobs(
isolate, shared_info, script, parse_info, isolate->allocator(),
is_compiled_scope, &finalize_unoptimized_compilation_data_list,
......@@ -1418,57 +1392,6 @@ RuntimeCallCounterId RuntimeCallCounterIdForCompileBackground(
}
#endif // V8_RUNTIME_CALL_STATS
MaybeHandle<SharedFunctionInfo> CompileAndFinalizeOnBackgroundThread(
ParseInfo* parse_info, AccountingAllocator* allocator,
Handle<Script> script, LocalIsolate* isolate,
FinalizeUnoptimizedCompilationDataList*
finalize_unoptimized_compilation_data_list,
DeferredFinalizationJobDataList* jobs_to_retry_finalization_on_main_thread,
IsCompiledScope* is_compiled_scope) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.CompileCodeBackground");
RCS_SCOPE(parse_info->runtime_call_stats(),
RuntimeCallCounterIdForCompileBackground(parse_info));
Handle<SharedFunctionInfo> shared_info =
CreateTopLevelSharedFunctionInfo(parse_info, script, isolate);
if (!IterativelyExecuteAndFinalizeUnoptimizedCompilationJobs(
isolate, shared_info, script, parse_info, allocator,
is_compiled_scope, finalize_unoptimized_compilation_data_list,
jobs_to_retry_finalization_on_main_thread)) {
return kNullMaybeHandle;
}
// Character stream shouldn't be used again.
parse_info->ResetCharacterStream();
return shared_info;
}
// TODO(leszeks): Remove this once off-thread finalization is always on.
void CompileOnBackgroundThread(ParseInfo* parse_info,
AccountingAllocator* allocator,
UnoptimizedCompilationJobList* jobs) {
DisallowHeapAccess no_heap_access;
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.CompileCodeBackground");
RCS_SCOPE(parse_info->runtime_call_stats(),
RuntimeCallCounterIdForCompileBackground(parse_info));
// Generate the unoptimized bytecode or asm-js data.
DCHECK(jobs->empty());
bool success = RecursivelyExecuteUnoptimizedCompilationJobs(
parse_info, parse_info->literal(), allocator, jobs);
USE(success);
DCHECK_EQ(success, !jobs->empty());
// Character stream shouldn't be used again.
parse_info->ResetCharacterStream();
}
} // namespace
CompilationHandleScope::~CompilationHandleScope() {
......@@ -1494,19 +1417,19 @@ DeferredFinalizationJobData::DeferredFinalizationJobData(
BackgroundCompileTask::BackgroundCompileTask(ScriptStreamingData* streamed_data,
Isolate* isolate, ScriptType type)
: flags_(UnoptimizedCompileFlags::ForToplevelCompile(
: isolate_for_local_isolate_(isolate),
flags_(UnoptimizedCompileFlags::ForToplevelCompile(
isolate, true, construct_language_mode(FLAG_use_strict),
REPLMode::kNo, type, FLAG_lazy_streaming)),
compile_state_(isolate),
info_(std::make_unique<ParseInfo>(isolate, flags_, &compile_state_)),
isolate_for_local_isolate_(isolate),
start_position_(0),
end_position_(0),
function_literal_id_(kFunctionLiteralIdTopLevel),
stack_size_(i::FLAG_stack_size),
worker_thread_runtime_call_stats_(
isolate->counters()->worker_thread_runtime_call_stats()),
timer_(isolate->counters()->compile_script_on_background()),
start_position_(0),
end_position_(0),
function_literal_id_(kFunctionLiteralIdTopLevel),
language_mode_(info_->language_mode()) {
VMState<PARSER> state(isolate);
......@@ -1522,22 +1445,22 @@ BackgroundCompileTask::BackgroundCompileTask(ScriptStreamingData* streamed_data,
}
BackgroundCompileTask::BackgroundCompileTask(
const ParseInfo* outer_parse_info, const AstRawString* function_name,
const FunctionLiteral* function_literal,
Isolate* isolate, const ParseInfo* outer_parse_info,
const AstRawString* function_name, const FunctionLiteral* function_literal,
WorkerThreadRuntimeCallStats* worker_thread_runtime_stats,
TimedHistogram* timer, int max_stack_size)
: flags_(UnoptimizedCompileFlags::ForToplevelFunction(
: isolate_for_local_isolate_(isolate),
flags_(UnoptimizedCompileFlags::ForToplevelFunction(
outer_parse_info->flags(), function_literal)),
compile_state_(*outer_parse_info->state()),
info_(ParseInfo::ForToplevelFunction(flags_, &compile_state_,
function_literal, function_name)),
isolate_for_local_isolate_(nullptr),
start_position_(function_literal->start_position()),
end_position_(function_literal->end_position()),
function_literal_id_(function_literal->function_literal_id()),
stack_size_(max_stack_size),
worker_thread_runtime_call_stats_(worker_thread_runtime_stats),
timer_(timer),
start_position_(function_literal->start_position()),
end_position_(function_literal->end_position()),
function_literal_id_(function_literal->function_literal_id()),
language_mode_(info_->language_mode()) {
DCHECK_EQ(outer_parse_info->parameters_end_pos(), kNoSourcePosition);
DCHECK_NULL(outer_parse_info->extension());
......@@ -1550,6 +1473,11 @@ BackgroundCompileTask::BackgroundCompileTask(
character_stream->Seek(start_position_);
info_->set_character_stream(std::move(character_stream));
// Get the script out of the outer ParseInfo and turn it into a persistent
// handle we can transfer to the background thread.
persistent_handles_ = std::make_unique<PersistentHandles>(isolate);
script_ = persistent_handles_->NewHandle(outer_parse_info->script());
// Get preparsed scope data from the function literal.
if (function_literal->produced_preparse_data()) {
ZonePreparseData* serialized_data =
......@@ -1563,135 +1491,244 @@ BackgroundCompileTask::~BackgroundCompileTask() = default;
namespace {
// A scope object that ensures a parse info's runtime call stats and stack limit
// are set correctly during worker-thread compile, and restores it after going
// out of scope.
class V8_NODISCARD OffThreadParseInfoScope {
public:
OffThreadParseInfoScope(
ParseInfo* parse_info,
WorkerThreadRuntimeCallStats* worker_thread_runtime_stats, int stack_size)
: parse_info_(parse_info),
original_stack_limit_(parse_info_->stack_limit()),
original_runtime_call_stats_(parse_info_->runtime_call_stats()),
worker_thread_scope_(worker_thread_runtime_stats) {
parse_info_->SetPerThreadState(GetCurrentStackPosition() - stack_size * KB,
worker_thread_scope_.Get());
void SetScriptFieldsFromDetails(Isolate* isolate, Script script,
ScriptDetails script_details,
DisallowGarbageCollection* no_gc) {
Handle<Object> script_name;
if (script_details.name_obj.ToHandle(&script_name)) {
script.set_name(*script_name);
script.set_line_offset(script_details.line_offset);
script.set_column_offset(script_details.column_offset);
}
OffThreadParseInfoScope(const OffThreadParseInfoScope&) = delete;
OffThreadParseInfoScope& operator=(const OffThreadParseInfoScope&) = delete;
~OffThreadParseInfoScope() {
DCHECK_NOT_NULL(parse_info_);
parse_info_->SetPerThreadState(original_stack_limit_,
original_runtime_call_stats_);
// The API can provide a source map URL, but a source map URL could also have
// been inferred by the parser from a magic comment. The latter takes
// preference over the former, so we don't want to override the source mapping
// URL if it already exists.
Handle<Object> source_map_url;
if (script_details.source_map_url.ToHandle(&source_map_url) &&
script.source_mapping_url(isolate).IsUndefined(isolate)) {
script.set_source_mapping_url(*source_map_url);
}
private:
ParseInfo* parse_info_;
uintptr_t original_stack_limit_;
RuntimeCallStats* original_runtime_call_stats_;
WorkerThreadRuntimeCallStatsScope worker_thread_scope_;
};
Handle<FixedArray> host_defined_options;
if (script_details.host_defined_options.ToHandle(&host_defined_options)) {
script.set_host_defined_options(*host_defined_options);
}
}
} // namespace
void BackgroundCompileTask::Run() {
TimedHistogramScope timer(timer_);
base::Optional<OffThreadParseInfoScope> off_thread_scope(
base::in_place, info_.get(), worker_thread_runtime_call_stats_,
stack_size_);
WorkerThreadRuntimeCallStatsScope worker_thread_scope(
worker_thread_runtime_call_stats_);
// Update the per-thread state of the ParseInfo to the off-thread state.
// TODO(leszeks): Fully initialize the ParseInfo here rather than passing it
// across from the main thread.
info_->SetPerThreadState(GetCurrentStackPosition() - stack_size_ * KB,
worker_thread_scope.Get());
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"BackgroundCompileTask::Run");
RCS_SCOPE(info_->runtime_call_stats(),
RuntimeCallCounterId::kCompileBackgroundCompileTask);
bool toplevel_script_compilation = flags_.is_toplevel();
LocalIsolate isolate(isolate_for_local_isolate_, ThreadKind::kBackground);
LocalHandleScope handle_scope(&isolate);
if (toplevel_script_compilation) {
DCHECK_NULL(persistent_handles_);
UnparkedScope unparked_scope(&isolate);
// We don't have the script source, origin, or details yet, so use default
// values for them. These will be fixed up during the main-thread merge.
info_->InitializeScript(
&isolate, isolate.factory()->empty_string(), kNullMaybeHandle,
ScriptOriginOptions(false, false, false, info_->flags().is_module()));
script_ = isolate.heap()->NewPersistentHandle(info_->script());
} else {
DCHECK_NOT_NULL(persistent_handles_);
isolate.heap()->AttachPersistentHandles(std::move(persistent_handles_));
#ifdef DEBUG
{
UnparkedScope unparked_scope(&isolate);
DCHECK(!script_->is_null());
}
#endif
}
// Update the character stream's runtime call stats.
info_->character_stream()->set_runtime_call_stats(
info_->runtime_call_stats());
// Parser needs to stay alive for finalizing the parsing on the main
// thread.
parser_.reset(new Parser(info_.get()));
parser_->InitializeEmptyScopeChain(info_.get());
Parser parser(info_.get());
parser.InitializeEmptyScopeChain(info_.get());
parser_->ParseOnBackground(info_.get(), start_position_, end_position_,
function_literal_id_);
parser.ParseOnBackground(info_.get(), start_position_, end_position_,
function_literal_id_);
// Save the language mode.
language_mode_ = info_->language_mode();
if (!FLAG_finalize_streaming_on_background) {
if (info_->literal() != nullptr) {
CompileOnBackgroundThread(info_.get(), compile_state_.allocator(),
&compilation_jobs_);
}
} else {
DCHECK(info_->flags().is_toplevel());
{
LocalIsolate isolate(isolate_for_local_isolate_, ThreadKind::kBackground);
UnparkedScope unparked_scope(&isolate);
LocalHandleScope handle_scope(&isolate);
{
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.CompileCodeBackground");
RCS_SCOPE(info_->runtime_call_stats(),
RuntimeCallCounterIdForCompileBackground(info_.get()));
info_->ast_value_factory()->Internalize(&isolate);
UnparkedScope unparked_scope(&isolate);
// We don't have the script source, origin, or details yet, so use default
// values for them. These will be fixed up during the main-thread merge.
Handle<Script> script = info_->CreateScript(
&isolate, isolate.factory()->empty_string(), kNullMaybeHandle,
ScriptOriginOptions(false, false, false, info_->flags().is_module()));
info_->ast_value_factory()->Internalize(&isolate);
parser_->UpdateStatistics(script, use_counts_, &total_preparse_skipped_);
parser_->HandleSourceURLComments(&isolate, script);
if (toplevel_script_compilation) {
// The script is new, so we can mutate it here.
parser.HandleSourceURLComments(&isolate, script_);
} else {
info_->CheckFlagsForFunctionFromScript(*script_);
}
MaybeHandle<SharedFunctionInfo> maybe_result;
if (info_->literal() != nullptr) {
maybe_result = CompileAndFinalizeOnBackgroundThread(
info_.get(), compile_state_.allocator(), script, &isolate,
&finalize_unoptimized_compilation_data_,
&jobs_to_retry_finalization_on_main_thread_, &is_compiled_scope_);
MaybeHandle<SharedFunctionInfo> maybe_result;
if (info_->literal() != nullptr) {
Handle<SharedFunctionInfo> shared_info;
if (toplevel_script_compilation) {
shared_info =
CreateTopLevelSharedFunctionInfo(info_.get(), script_, &isolate);
} else {
DCHECK(compile_state_.pending_error_handler()->has_pending_error());
PreparePendingException(&isolate, info_.get());
// Create a second, placeholder SFI for storing the results.
shared_info =
isolate.factory()->NewPlaceholderSharedFunctionInfoForLazyLiteral(
info_->literal(), script_);
}
outer_function_sfi_ =
isolate.heap()->NewPersistentMaybeHandle(maybe_result);
script_ = isolate.heap()->NewPersistentHandle(script);
persistent_handles_ = isolate.heap()->DetachPersistentHandles();
if (IterativelyExecuteAndFinalizeUnoptimizedCompilationJobs(
&isolate, shared_info, script_, info_.get(),
compile_state_.allocator(), &is_compiled_scope_,
&finalize_unoptimized_compilation_data_,
&jobs_to_retry_finalization_on_main_thread_)) {
maybe_result = shared_info;
}
}
{
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.FinalizeCodeBackground.ReleaseParser");
DCHECK_EQ(language_mode_, info_->language_mode());
off_thread_scope.reset();
parser_.reset();
info_.reset();
if (maybe_result.is_null()) {
PreparePendingException(&isolate, info_.get());
}
outer_function_sfi_ =
isolate.heap()->NewPersistentMaybeHandle(maybe_result);
parser.UpdateStatistics(script_, use_counts_, &total_preparse_skipped_);
}
DCHECK_EQ(language_mode_, info_->language_mode());
info_.reset();
persistent_handles_ = isolate.heap()->DetachPersistentHandles();
}
MaybeHandle<SharedFunctionInfo> BackgroundCompileTask::FinalizeScript(
Isolate* isolate, Handle<String> source,
const ScriptDetails& script_details) {
ScriptOriginOptions origin_options = script_details.origin_options;
DCHECK(flags_.is_toplevel());
DCHECK_EQ(flags_.is_module(), origin_options.IsModule());
MaybeHandle<SharedFunctionInfo> maybe_result;
// We might not have been able to finalize all jobs on the background
// thread (e.g. asm.js jobs), so finalize those deferred jobs now.
if (FinalizeDeferredUnoptimizedCompilationJobs(
isolate, script_, &jobs_to_retry_finalization_on_main_thread_,
compile_state_.pending_error_handler(),
&finalize_unoptimized_compilation_data_)) {
maybe_result = outer_function_sfi_;
}
script_->set_source(*source);
script_->set_origin_options(origin_options);
// The one post-hoc fix-up: Add the script to the script list.
Handle<WeakArrayList> scripts = isolate->factory()->script_list();
scripts =
WeakArrayList::Append(isolate, scripts, MaybeObjectHandle::Weak(script_));
isolate->heap()->SetRootScriptList(*scripts);
// Set the script fields after finalization, to keep this path the same
// between main-thread and off-thread finalization.
{
DisallowGarbageCollection no_gc;
SetScriptFieldsFromDetails(isolate, *script_, script_details, &no_gc);
LOG(isolate, ScriptDetails(*script_));
}
ReportStatistics(isolate);
Handle<SharedFunctionInfo> result;
if (!maybe_result.ToHandle(&result)) {
FailWithPreparedPendingException(isolate, script_,
compile_state_.pending_error_handler());
return kNullMaybeHandle;
}
FinalizeUnoptimizedScriptCompilation(isolate, script_, flags_,
&compile_state_,
finalize_unoptimized_compilation_data_);
return handle(*result, isolate);
}
MaybeHandle<SharedFunctionInfo> BackgroundCompileTask::GetOuterFunctionSfi(
Isolate* isolate) {
// outer_function_sfi_ is a persistent Handle, tied to the lifetime of the
// persistent_handles_ member, so create a new Handle to let it outlive
// the BackgroundCompileTask.
bool BackgroundCompileTask::FinalizeFunction(
Isolate* isolate, Handle<SharedFunctionInfo> shared_info,
Compiler::ClearExceptionFlag flag) {
DCHECK(!flags_.is_toplevel());
MaybeHandle<SharedFunctionInfo> maybe_result;
// We might not have been able to finalize all jobs on the background
// thread (e.g. asm.js jobs), so finalize those deferred jobs now.
if (FinalizeDeferredUnoptimizedCompilationJobs(
isolate, script_, &jobs_to_retry_finalization_on_main_thread_,
compile_state_.pending_error_handler(),
&finalize_unoptimized_compilation_data_)) {
maybe_result = outer_function_sfi_;
}
ReportStatistics(isolate);
Handle<SharedFunctionInfo> result;
if (outer_function_sfi_.ToHandle(&result)) {
return handle(*result, isolate);
if (!maybe_result.ToHandle(&result)) {
if (flag == Compiler::KEEP_EXCEPTION) {
FailWithPreparedPendingException(isolate, script_,
compile_state_.pending_error_handler());
}
return false;
}
return kNullMaybeHandle;
FinalizeUnoptimizedCompilation(isolate, script_, flags_, &compile_state_,
finalize_unoptimized_compilation_data_);
// Move the compiled data from the placeholder SFI to the real SFI.
shared_info->set_function_data(result->function_data(kAcquireLoad),
kReleaseStore);
shared_info->set_feedback_metadata(result->feedback_metadata(kAcquireLoad),
kReleaseStore);
return true;
}
Handle<Script> BackgroundCompileTask::GetScript(Isolate* isolate) {
// script_ is a persistent Handle, tied to the lifetime of the
// persistent_handles_ member, so create a new Handle to let it outlive
// the BackgroundCompileTask.
return handle(*script_, isolate);
void BackgroundCompileTask::ReportStatistics(Isolate* isolate) {
// Update use-counts.
for (int i = 0; i < static_cast<int>(v8::Isolate::kUseCounterFeatureCount);
++i) {
v8::Isolate::UseCounterFeature feature =
static_cast<v8::Isolate::UseCounterFeature>(i);
isolate->CountUsage(feature, use_counts_[i]);
}
isolate->counters()->total_preparse_skipped()->Increment(
total_preparse_skipped_);
}
BackgroundDeserializeTask::BackgroundDeserializeTask(
......@@ -2064,39 +2101,15 @@ MaybeHandle<SharedFunctionInfo> Compiler::CompileToplevel(
bool Compiler::FinalizeBackgroundCompileTask(
BackgroundCompileTask* task, Handle<SharedFunctionInfo> shared_info,
Isolate* isolate, ClearExceptionFlag flag) {
DCHECK(!FLAG_finalize_streaming_on_background);
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.FinalizeBackgroundCompileTask");
RCS_SCOPE(isolate,
RuntimeCallCounterId::kCompileFinalizeBackgroundCompileTask);
HandleScope scope(isolate);
ParseInfo* parse_info = task->info();
DCHECK(!parse_info->flags().is_toplevel());
DCHECK(!shared_info->is_compiled());
Handle<Script> script(Script::cast(shared_info->script()), isolate);
parse_info->CheckFlagsForFunctionFromScript(*script);
task->parser()->UpdateStatistics(isolate, script);
task->parser()->HandleSourceURLComments(isolate, script);
if (task->compilation_jobs()->empty()) {
// Parsing or compile failed on background thread - report error messages.
return FailWithPendingException(isolate, script, parse_info, flag);
}
// Parsing has succeeded - finalize compilation.
parse_info->ast_value_factory()->Internalize(isolate);
if (!FinalizeAllUnoptimizedCompilationJobs(
parse_info, isolate, script, task->compilation_jobs(),
task->finalize_unoptimized_compilation_data())) {
// Finalization failed - throw an exception.
return FailWithPendingException(isolate, script, parse_info, flag);
}
FinalizeUnoptimizedCompilation(
isolate, script, parse_info->flags(), parse_info->state(),
*task->finalize_unoptimized_compilation_data());
if (!task->FinalizeFunction(isolate, shared_info, flag)) return false;
DCHECK(!isolate->has_pending_exception());
DCHECK(shared_info->is_compiled());
......@@ -2212,9 +2225,9 @@ MaybeHandle<JSFunction> Compiler::GetFunctionFromEval(
if (!context->IsNativeContext()) {
maybe_outer_scope_info = handle(context->scope_info(), isolate);
}
script =
parse_info.CreateScript(isolate, source, kNullMaybeHandle,
parse_info.InitializeScript(isolate, source, kNullMaybeHandle,
OriginOptionsForEval(outer_info->script()));
script = parse_info.script();
script->set_eval_from_shared(*outer_info);
if (eval_position == kNoSourcePosition) {
// If the position is missing, attempt to get the code offset by
......@@ -2650,38 +2663,14 @@ struct ScriptCompileTimerScope {
}
};
void SetScriptFieldsFromDetails(Isolate* isolate, Script script,
ScriptDetails script_details,
DisallowGarbageCollection* no_gc) {
Handle<Object> script_name;
if (script_details.name_obj.ToHandle(&script_name)) {
script.set_name(*script_name);
script.set_line_offset(script_details.line_offset);
script.set_column_offset(script_details.column_offset);
}
// The API can provide a source map URL, but a source map URL could also have
// been inferred by the parser from a magic comment. The latter takes
// preference over the former, so we don't want to override the source mapping
// URL if it already exists.
Handle<Object> source_map_url;
if (script_details.source_map_url.ToHandle(&source_map_url) &&
script.source_mapping_url(isolate).IsUndefined(isolate)) {
script.set_source_mapping_url(*source_map_url);
}
Handle<FixedArray> host_defined_options;
if (script_details.host_defined_options.ToHandle(&host_defined_options)) {
script.set_host_defined_options(*host_defined_options);
}
}
Handle<Script> NewScript(
Isolate* isolate, ParseInfo* parse_info, Handle<String> source,
ScriptDetails script_details, NativesFlag natives,
MaybeHandle<FixedArray> maybe_wrapped_arguments = kNullMaybeHandle) {
// Create a script object describing the script to be compiled.
Handle<Script> script =
parse_info->CreateScript(isolate, source, maybe_wrapped_arguments,
parse_info->InitializeScript(isolate, source, maybe_wrapped_arguments,
script_details.origin_options, natives);
Handle<Script> script = parse_info->script();
DisallowGarbageCollection no_gc;
SetScriptFieldsFromDetails(isolate, *script, script_details, &no_gc);
LOG(isolate, ScriptDetails(*script));
......@@ -3123,8 +3112,7 @@ MaybeHandle<SharedFunctionInfo>
Compiler::GetSharedFunctionInfoForStreamedScript(
Isolate* isolate, Handle<String> source,
const ScriptDetails& script_details, ScriptStreamingData* streaming_data) {
ScriptOriginOptions origin_options = script_details.origin_options;
DCHECK(!origin_options.IsWasm());
DCHECK(!script_details.origin_options.IsWasm());
ScriptCompileTimerScope compile_timer(
isolate, ScriptCompiler::kNoCacheBecauseStreamingSource);
......@@ -3153,94 +3141,15 @@ Compiler::GetSharedFunctionInfoForStreamedScript(
if (maybe_result.is_null()) {
// No cache entry found, finalize compilation of the script and add it to
// the isolate cache.
DCHECK_EQ(task->flags().is_module(), origin_options.IsModule());
Handle<Script> script;
if (FLAG_finalize_streaming_on_background) {
RCS_SCOPE(isolate,
RuntimeCallCounterId::kCompilePublishBackgroundFinalization);
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.OffThreadFinalization.Publish");
script = task->GetScript(isolate);
// We might not have been able to finalize all jobs on the background
// thread (e.g. asm.js jobs), so finalize those deferred jobs now.
if (FinalizeDeferredUnoptimizedCompilationJobs(
isolate, script,
task->jobs_to_retry_finalization_on_main_thread(),
task->compile_state()->pending_error_handler(),
task->finalize_unoptimized_compilation_data())) {
maybe_result = task->GetOuterFunctionSfi(isolate);
}
script->set_source(*source);
script->set_origin_options(origin_options);
// The one post-hoc fix-up: Add the script to the script list.
Handle<WeakArrayList> scripts = isolate->factory()->script_list();
scripts = WeakArrayList::Append(isolate, scripts,
MaybeObjectHandle::Weak(script));
isolate->heap()->SetRootScriptList(*scripts);
for (int i = 0;
i < static_cast<int>(v8::Isolate::kUseCounterFeatureCount); ++i) {
v8::Isolate::UseCounterFeature feature =
static_cast<v8::Isolate::UseCounterFeature>(i);
isolate->CountUsage(feature, task->use_count(feature));
}
isolate->counters()->total_preparse_skipped()->Increment(
task->total_preparse_skipped());
} else {
ParseInfo* parse_info = task->info();
DCHECK_EQ(parse_info->flags().is_module(), origin_options.IsModule());
DCHECK(parse_info->flags().is_toplevel());
script = parse_info->CreateScript(isolate, source, kNullMaybeHandle,
origin_options);
task->parser()->UpdateStatistics(isolate, script);
task->parser()->HandleSourceURLComments(isolate, script);
if (!task->compilation_jobs()->empty()) {
// Off-thread parse & compile has succeeded - finalize compilation.
DCHECK_NOT_NULL(parse_info->literal());
parse_info->ast_value_factory()->Internalize(isolate);
Handle<SharedFunctionInfo> shared_info =
CreateTopLevelSharedFunctionInfo(parse_info, script, isolate);
if (FinalizeAllUnoptimizedCompilationJobs(
parse_info, isolate, script, task->compilation_jobs(),
task->finalize_unoptimized_compilation_data())) {
maybe_result = shared_info;
}
}
if (maybe_result.is_null()) {
// Compilation failed - prepare to throw an exception after script
// fields have been set.
PreparePendingException(isolate, parse_info);
}
}
RCS_SCOPE(isolate,
RuntimeCallCounterId::kCompilePublishBackgroundFinalization);
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.OffThreadFinalization.Publish");
// Set the script fields after finalization, to keep this path the same
// between main-thread and off-thread finalization.
{
DisallowGarbageCollection no_gc;
SetScriptFieldsFromDetails(isolate, *script, script_details, &no_gc);
LOG(isolate, ScriptDetails(*script));
}
maybe_result = task->FinalizeScript(isolate, source, script_details);
Handle<SharedFunctionInfo> result;
if (!maybe_result.ToHandle(&result)) {
FailWithPreparedPendingException(
isolate, script, task->compile_state()->pending_error_handler());
} else {
FinalizeUnoptimizedScriptCompilation(
isolate, script, task->flags(), task->compile_state(),
*task->finalize_unoptimized_compilation_data());
if (maybe_result.ToHandle(&result)) {
// Add compiled code to the isolate cache.
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.StreamingFinalization.AddToCache");
......@@ -3252,7 +3161,7 @@ Compiler::GetSharedFunctionInfoForStreamedScript(
"V8.StreamingFinalization.Release");
streaming_data->Release();
return maybe_result;
}
} // namespace internal
// static
Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfoForWebSnapshot(
......
......@@ -8,6 +8,7 @@
#include <forward_list>
#include <memory>
#include "src/ast/ast-value-factory.h"
#include "src/base/platform/elapsed-timer.h"
#include "src/codegen/bailout-reason.h"
#include "src/common/globals.h"
......@@ -495,8 +496,7 @@ using DeferredFinalizationJobDataList =
class V8_EXPORT_PRIVATE BackgroundCompileTask {
public:
// Creates a new task that when run will parse and compile the streamed
// script associated with |data| and can be finalized with
// Compiler::GetSharedFunctionInfoForStreamedScript.
// script associated with |data| and can be finalized with FinalizeScript.
// Note: does not take ownership of |data|.
BackgroundCompileTask(ScriptStreamingData* data, Isolate* isolate,
v8::ScriptType type);
......@@ -505,68 +505,44 @@ class V8_EXPORT_PRIVATE BackgroundCompileTask {
~BackgroundCompileTask();
// Creates a new task that when run will parse and compile the
// |function_literal| and can be finalized with
// |function_literal| and can be finalized with FinalizeFunction
// Compiler::FinalizeBackgroundCompileTask.
BackgroundCompileTask(
const ParseInfo* outer_parse_info, const AstRawString* function_name,
Isolate* isolate, const ParseInfo* outer_parse_info,
const AstRawString* function_name,
const FunctionLiteral* function_literal,
WorkerThreadRuntimeCallStats* worker_thread_runtime_stats,
TimedHistogram* timer, int max_stack_size);
void Run();
ParseInfo* info() {
DCHECK_NOT_NULL(info_);
return info_.get();
}
Parser* parser() { return parser_.get(); }
UnoptimizedCompilationJobList* compilation_jobs() {
return &compilation_jobs_;
}
UnoptimizedCompileFlags flags() const { return flags_; }
UnoptimizedCompileState* compile_state() { return &compile_state_; }
LanguageMode language_mode() { return language_mode_; }
FinalizeUnoptimizedCompilationDataList*
finalize_unoptimized_compilation_data() {
return &finalize_unoptimized_compilation_data_;
}
int use_count(v8::Isolate::UseCounterFeature feature) const {
return use_counts_[static_cast<int>(feature)];
}
int total_preparse_skipped() const { return total_preparse_skipped_; }
MaybeHandle<SharedFunctionInfo> FinalizeScript(
Isolate* isolate, Handle<String> source,
const ScriptDetails& script_details);
// Jobs which could not be finalized in the background task, and need to be
// finalized on the main thread.
DeferredFinalizationJobDataList* jobs_to_retry_finalization_on_main_thread() {
return &jobs_to_retry_finalization_on_main_thread_;
}
bool FinalizeFunction(Isolate* isolate,
Handle<SharedFunctionInfo> shared_info,
Compiler::ClearExceptionFlag flag);
// Getters for the off-thread finalization results, that create main-thread
// handles to the objects.
MaybeHandle<SharedFunctionInfo> GetOuterFunctionSfi(Isolate* isolate);
Handle<Script> GetScript(Isolate* isolate);
UnoptimizedCompileFlags flags() const { return flags_; }
LanguageMode language_mode() const { return language_mode_; }
private:
// Data needed for parsing, and data needed to to be passed between thread
// between parsing and compilation. These need to be initialized before the
// compilation starts.
void ReportStatistics(Isolate* isolate);
// Data needed for parsing and compilation. These need to be initialized
// before the compilation starts.
Isolate* isolate_for_local_isolate_;
UnoptimizedCompileFlags flags_;
UnoptimizedCompileState compile_state_;
std::unique_ptr<ParseInfo> info_;
std::unique_ptr<Parser> parser_;
// Data needed for finalizing compilation after background compilation.
UnoptimizedCompilationJobList compilation_jobs_;
int stack_size_;
WorkerThreadRuntimeCallStats* worker_thread_runtime_call_stats_;
TimedHistogram* timer_;
// Data needed for merging onto the main thread after background finalization.
// TODO(leszeks): When these are available, the above fields are not. We
// should add some stricter type-safety or DCHECKs to ensure that the user of
// the task knows this.
Isolate* isolate_for_local_isolate_;
// Data needed for merging onto the main thread.
std::unique_ptr<PersistentHandles> persistent_handles_;
MaybeHandle<SharedFunctionInfo> outer_function_sfi_;
Handle<Script> script_;
IsCompiledScope is_compiled_scope_;
FinalizeUnoptimizedCompilationDataList finalize_unoptimized_compilation_data_;
DeferredFinalizationJobDataList jobs_to_retry_finalization_on_main_thread_;
......@@ -574,13 +550,11 @@ class V8_EXPORT_PRIVATE BackgroundCompileTask {
int total_preparse_skipped_ = 0;
// Single function data for top-level function compilation.
Handle<Script> script_;
int start_position_;
int end_position_;
int function_literal_id_;
int stack_size_;
WorkerThreadRuntimeCallStats* worker_thread_runtime_call_stats_;
TimedHistogram* timer_;
LanguageMode language_mode_;
};
......
......@@ -67,7 +67,7 @@ base::Optional<LazyCompileDispatcher::JobId> LazyCompileDispatcher::Enqueue(
if (!IsEnabled()) return base::nullopt;
std::unique_ptr<Job> job = std::make_unique<Job>(new BackgroundCompileTask(
outer_parse_info, function_name, function_literal,
isolate_, outer_parse_info, function_name, function_literal,
worker_thread_runtime_call_stats_, background_compile_timer_,
static_cast<int>(max_stack_size_)));
JobMap::const_iterator it = InsertJob(std::move(job));
......
......@@ -668,8 +668,9 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
i::ParseInfo parse_info(i_isolate, flags, &compile_state);
i::Handle<i::Script> script = parse_info.CreateScript(
i_isolate, str, i::kNullMaybeHandle, ScriptOriginOptions());
parse_info.InitializeScript(i_isolate, str, i::kNullMaybeHandle,
ScriptOriginOptions());
i::Handle<i::Script> script = parse_info.script();
if (!i::parsing::ParseProgram(&parse_info, script, i_isolate,
i::parsing::ReportStatisticsMode::kYes)) {
parse_info.pending_error_handler()->PrepareErrors(
......
......@@ -445,8 +445,8 @@ DEFINE_NEG_IMPLICATION(enable_third_party_heap, allocation_site_pretenuring)
DEFINE_NEG_IMPLICATION(enable_third_party_heap, turbo_allocation_folding)
DEFINE_NEG_IMPLICATION(enable_third_party_heap, concurrent_recompilation)
DEFINE_NEG_IMPLICATION(enable_third_party_heap, concurrent_inlining)
DEFINE_NEG_IMPLICATION(enable_third_party_heap,
finalize_streaming_on_background)
DEFINE_NEG_IMPLICATION(enable_third_party_heap, script_streaming)
DEFINE_NEG_IMPLICATION(enable_third_party_heap, parallel_compile_tasks)
DEFINE_NEG_IMPLICATION(enable_third_party_heap, use_marking_progress_bar)
DEFINE_NEG_IMPLICATION(enable_third_party_heap, move_object_start)
DEFINE_NEG_IMPLICATION(enable_third_party_heap, concurrent_marking)
......@@ -1474,14 +1474,8 @@ DEFINE_BOOL(enable_regexp_unaligned_accesses, true,
DEFINE_BOOL(script_streaming, true, "enable parsing on background")
DEFINE_BOOL(stress_background_compile, false,
"stress test parsing on background")
DEFINE_BOOL(
finalize_streaming_on_background, true,
"perform the script streaming finalization on the background thread")
DEFINE_BOOL(concurrent_cache_deserialization, true,
"enable deserializing code caches on background")
// TODO(leszeks): Parallel compile tasks currently don't support off-thread
// finalization.
DEFINE_NEG_IMPLICATION(parallel_compile_tasks, finalize_streaming_on_background)
DEFINE_BOOL(disable_old_api_accessors, false,
"Disable old-style API accessors whose setters trigger through the "
"prototype chain")
......
......@@ -305,6 +305,24 @@ Handle<SharedFunctionInfo> FactoryBase<Impl>::NewSharedFunctionInfoForLiteral(
return shared;
}
template <typename Impl>
Handle<SharedFunctionInfo>
FactoryBase<Impl>::NewPlaceholderSharedFunctionInfoForLazyLiteral(
FunctionLiteral* literal, Handle<Script> script) {
FunctionKind kind = literal->kind();
Handle<SharedFunctionInfo> shared =
NewSharedFunctionInfo(literal->GetName(isolate()), MaybeHandle<Code>(),
Builtin::kCompileLazy, kind);
// Don't fully initialise the SFI from the function literal, since we e.g.
// might not have the scope info, but initialise just enough to work for
// compilation/finalization.
shared->set_function_literal_id(literal->function_literal_id());
// Set the script on the SFI, but don't make the script's SFI list point back
// to this SFI.
shared->set_script(*script);
return shared;
}
template <typename Impl>
Handle<PreparseData> FactoryBase<Impl>::NewPreparseData(int data_length,
int children_length) {
......
......@@ -162,6 +162,9 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) FactoryBase
Handle<SharedFunctionInfo> NewSharedFunctionInfoForLiteral(
FunctionLiteral* literal, Handle<Script> script, bool is_toplevel);
Handle<SharedFunctionInfo> NewPlaceholderSharedFunctionInfoForLazyLiteral(
FunctionLiteral* literal, Handle<Script> script);
Handle<PreparseData> NewPreparseData(int data_length, int children_length);
Handle<UncompiledDataWithoutPreparseData>
......
......@@ -240,7 +240,7 @@ ParseInfo::~ParseInfo() = default;
DeclarationScope* ParseInfo::scope() const { return literal()->scope(); }
template <typename IsolateT>
Handle<Script> ParseInfo::CreateScript(
void ParseInfo::InitializeScript(
IsolateT* isolate, Handle<String> source,
MaybeHandle<FixedArray> maybe_wrapped_arguments,
ScriptOriginOptions origin_options, NativesFlag natives) {
......@@ -271,19 +271,18 @@ Handle<Script> ParseInfo::CreateScript(
CheckFlagsForToplevelCompileFromScript(*script,
isolate->is_collecting_type_profile());
return script;
script_ = script;
}
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
Handle<Script> ParseInfo::CreateScript(
Isolate* isolate, Handle<String> source,
MaybeHandle<FixedArray> maybe_wrapped_arguments,
ScriptOriginOptions origin_options, NativesFlag natives);
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
Handle<Script> ParseInfo::CreateScript(
LocalIsolate* isolate, Handle<String> source,
MaybeHandle<FixedArray> maybe_wrapped_arguments,
ScriptOriginOptions origin_options, NativesFlag natives);
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void ParseInfo::
InitializeScript(Isolate* isolate, Handle<String> source,
MaybeHandle<FixedArray> maybe_wrapped_arguments,
ScriptOriginOptions origin_options, NativesFlag natives);
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void ParseInfo::
InitializeScript(LocalIsolate* isolate, Handle<String> source,
MaybeHandle<FixedArray> maybe_wrapped_arguments,
ScriptOriginOptions origin_options, NativesFlag natives);
AstValueFactory* ParseInfo::GetOrCreateAstValueFactory() {
if (!ast_value_factory_.get()) {
......
......@@ -225,10 +225,10 @@ class V8_EXPORT_PRIVATE ParseInfo {
template <typename IsolateT>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
Handle<Script> CreateScript(IsolateT* isolate, Handle<String> source,
MaybeHandle<FixedArray> maybe_wrapped_arguments,
ScriptOriginOptions origin_options,
NativesFlag natives = NOT_NATIVES_CODE);
void InitializeScript(IsolateT* isolate, Handle<String> source,
MaybeHandle<FixedArray> maybe_wrapped_arguments,
ScriptOriginOptions origin_options,
NativesFlag natives = NOT_NATIVES_CODE);
// Either returns the ast-value-factory associcated with this ParseInfo, or
// creates and returns a new factory if none exists.
......@@ -274,6 +274,9 @@ class V8_EXPORT_PRIVATE ParseInfo {
LanguageMode language_mode() const { return language_mode_; }
void set_language_mode(LanguageMode value) { language_mode_ = value; }
Handle<Script> script() const { return script_; }
void set_script(Handle<Script> script) { script_ = script; }
Utf16CharacterStream* character_stream() const {
return character_stream_.get();
}
......@@ -352,6 +355,7 @@ class V8_EXPORT_PRIVATE ParseInfo {
int max_function_literal_id_;
//----------- Inputs+Outputs of parsing and scope analysis -----------------
Handle<Script> script_;
std::unique_ptr<Utf16CharacterStream> character_stream_;
std::unique_ptr<ConsumedPreparseData> consumed_preparse_data_;
std::unique_ptr<AstValueFactory> ast_value_factory_;
......
......@@ -38,7 +38,6 @@ class LazyCompilerDispatcherTestFlags {
FLAG_single_threaded = true;
FlagList::EnforceFlagImplications();
FLAG_lazy_compile_dispatcher = true;
FLAG_finalize_streaming_on_background = false;
}
static void RestoreFlags() {
......@@ -106,15 +105,6 @@ class LazyCompilerDispatcherTest : public TestWithNativeContext {
return dispatcher->Enqueue(outer_parse_info.get(), function_name,
function_literal);
}
protected:
void SetUp() override {
// TODO(leszeks): Support background finalization in compiler dispatcher.
if (FLAG_finalize_streaming_on_background) {
GTEST_SKIP_(
"Parallel compile tasks don't yet support background finalization");
}
}
};
namespace {
......
......@@ -88,9 +88,10 @@ class LocalFactoryTest : public TestWithIsolateAndZone {
parse_info()->ast_value_factory()->Internalize(local_isolate());
DeclarationScope::AllocateScopeInfos(parse_info(), local_isolate());
script_ = parse_info_.CreateScript(local_isolate(),
local_factory()->empty_string(),
kNullMaybeHandle, ScriptOriginOptions());
parse_info_.InitializeScript(local_isolate(),
local_factory()->empty_string(),
kNullMaybeHandle, ScriptOriginOptions());
script_ = parse_info_.script();
// Create the SFI list on the script so that SFI SetScript works.
Handle<WeakFixedArray> infos = local_factory()->NewWeakFixedArray(
......
......@@ -80,20 +80,11 @@ class BackgroundCompileTaskTest : public TestWithNativeContext {
shared->function_literal_id(), nullptr);
return new BackgroundCompileTask(
outer_parse_info.get(), function_name, function_literal,
isolate, outer_parse_info.get(), function_name, function_literal,
isolate->counters()->worker_thread_runtime_call_stats(),
isolate->counters()->compile_function_on_background(), FLAG_stack_size);
}
protected:
void SetUp() override {
// TODO(leszeks): Support background finalization in compiler dispatcher.
if (FLAG_finalize_streaming_on_background) {
GTEST_SKIP_(
"Parallel compile tasks don't yet support background finalization");
}
}
private:
AccountingAllocator* allocator_;
static SaveFlags* save_flags_;
......
......@@ -67,6 +67,8 @@ std::unique_ptr<ParseInfo> OuterParseInfoForShared(
ScannerStream::For(isolate, source));
result->set_character_stream(std::move(stream));
result->set_script(handle(script, isolate));
return result;
}
......
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