Commit 198deea2 authored by Leszek Swirski's avatar Leszek Swirski Committed by Commit Bot

[compiler] Off-thread finalize each function immediately

Allow "iterative" finalization when off-thread finalization is enabled,
meaning that each compiled function is finalized immediately after
compilation, rather than all functions being first compiled and then
finalized.

This is what we do on the main thread, and it reduces peak Zone memory
usage by being able to discard empty compilation Zones earlier.

One necessary functionality for this was being able to defer the
finalization of asm.js functions until the main thread pause, since
they can't be finalized off-thread -- previously we would just bail
out of doing the off-thread finalization if any inner function was
asm.js.

Bug: chromium:1011762
Change-Id: I21ff69d62eaa93b5ff908624b7115601e36f70f1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2282536Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69032}
parent fddd23c0
...@@ -187,8 +187,7 @@ class AsmJsCompilationJob final : public UnoptimizedCompilationJob { ...@@ -187,8 +187,7 @@ class AsmJsCompilationJob final : public UnoptimizedCompilationJob {
explicit AsmJsCompilationJob(ParseInfo* parse_info, FunctionLiteral* literal, explicit AsmJsCompilationJob(ParseInfo* parse_info, FunctionLiteral* literal,
AccountingAllocator* allocator) AccountingAllocator* allocator)
: UnoptimizedCompilationJob(parse_info->stack_limit(), parse_info, : UnoptimizedCompilationJob(parse_info->stack_limit(), parse_info,
&compilation_info_, &compilation_info_),
CanOffThreadFinalize::kNo),
allocator_(allocator), allocator_(allocator),
zone_(allocator, ZONE_NAME), zone_(allocator, ZONE_NAME),
compilation_info_(&zone_, parse_info, literal), compilation_info_(&zone_, parse_info, literal),
...@@ -203,7 +202,7 @@ class AsmJsCompilationJob final : public UnoptimizedCompilationJob { ...@@ -203,7 +202,7 @@ class AsmJsCompilationJob final : public UnoptimizedCompilationJob {
Isolate* isolate) final; Isolate* isolate) final;
Status FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info, Status FinalizeJobImpl(Handle<SharedFunctionInfo> shared_info,
OffThreadIsolate* isolate) final { OffThreadIsolate* isolate) final {
UNREACHABLE(); return CompilationJob::RETRY_ON_MAIN_THREAD;
} }
private: private:
...@@ -278,8 +277,8 @@ UnoptimizedCompilationJob::Status AsmJsCompilationJob::FinalizeJobImpl( ...@@ -278,8 +277,8 @@ UnoptimizedCompilationJob::Status AsmJsCompilationJob::FinalizeJobImpl(
RecordHistograms(isolate); RecordHistograms(isolate);
ReportCompilationSuccess(handle(Script::cast(shared_info->script()), isolate), ReportCompilationSuccess(handle(Script::cast(shared_info->script()), isolate),
compilation_info()->literal()->position(), shared_info->StartPosition(), compile_time_,
compile_time_, module_->size()); module_->size());
return SUCCEEDED; return SUCCEEDED;
} }
......
This diff is collapsed.
...@@ -212,7 +212,7 @@ class V8_EXPORT_PRIVATE Compiler : public AllStatic { ...@@ -212,7 +212,7 @@ class V8_EXPORT_PRIVATE Compiler : public AllStatic {
// thread. The current state of the job can be checked using {state()}. // thread. The current state of the job can be checked using {state()}.
class V8_EXPORT_PRIVATE CompilationJob { class V8_EXPORT_PRIVATE CompilationJob {
public: public:
enum Status { SUCCEEDED, FAILED }; enum Status { SUCCEEDED, FAILED, RETRY_ON_MAIN_THREAD };
enum class State { enum class State {
kReadyToPrepare, kReadyToPrepare,
kReadyToExecute, kReadyToExecute,
...@@ -234,10 +234,16 @@ class V8_EXPORT_PRIVATE CompilationJob { ...@@ -234,10 +234,16 @@ class V8_EXPORT_PRIVATE CompilationJob {
} }
V8_WARN_UNUSED_RESULT Status UpdateState(Status status, State next_state) { V8_WARN_UNUSED_RESULT Status UpdateState(Status status, State next_state) {
if (status == SUCCEEDED) { switch (status) {
case SUCCEEDED:
state_ = next_state; state_ = next_state;
} else { break;
case FAILED:
state_ = State::kFailed; state_ = State::kFailed;
break;
case RETRY_ON_MAIN_THREAD:
// Don't change the state, we'll re-try on the main thread.
break;
} }
return status; return status;
} }
...@@ -257,17 +263,12 @@ class V8_EXPORT_PRIVATE CompilationJob { ...@@ -257,17 +263,12 @@ class V8_EXPORT_PRIVATE CompilationJob {
// Either of phases can either fail or succeed. // Either of phases can either fail or succeed.
class UnoptimizedCompilationJob : public CompilationJob { class UnoptimizedCompilationJob : public CompilationJob {
public: public:
enum class CanOffThreadFinalize : bool { kYes = true, kNo = false };
UnoptimizedCompilationJob(uintptr_t stack_limit, ParseInfo* parse_info, UnoptimizedCompilationJob(uintptr_t stack_limit, ParseInfo* parse_info,
UnoptimizedCompilationInfo* compilation_info, UnoptimizedCompilationInfo* compilation_info)
CanOffThreadFinalize can_off_thread_finalize)
: CompilationJob(State::kReadyToExecute), : CompilationJob(State::kReadyToExecute),
stack_limit_(stack_limit), stack_limit_(stack_limit),
parse_info_(parse_info), parse_info_(parse_info),
compilation_info_(compilation_info), compilation_info_(compilation_info) {}
can_off_thread_finalize_(can_off_thread_finalize ==
CanOffThreadFinalize::kYes) {}
// Executes the compile job. Can be called on a background thread. // Executes the compile job. Can be called on a background thread.
V8_WARN_UNUSED_RESULT Status ExecuteJob(); V8_WARN_UNUSED_RESULT Status ExecuteJob();
...@@ -276,7 +277,9 @@ class UnoptimizedCompilationJob : public CompilationJob { ...@@ -276,7 +277,9 @@ class UnoptimizedCompilationJob : public CompilationJob {
V8_WARN_UNUSED_RESULT Status V8_WARN_UNUSED_RESULT Status
FinalizeJob(Handle<SharedFunctionInfo> shared_info, Isolate* isolate); FinalizeJob(Handle<SharedFunctionInfo> shared_info, Isolate* isolate);
// Finalizes the compile job. Can be called on a background thread. // Finalizes the compile job. Can be called on a background thread, and might
// return RETRY_ON_MAIN_THREAD if the finalization can't be run on the
// background thread, and should instead be retried on the foreground thread.
V8_WARN_UNUSED_RESULT Status FinalizeJob( V8_WARN_UNUSED_RESULT Status FinalizeJob(
Handle<SharedFunctionInfo> shared_info, OffThreadIsolate* isolate); Handle<SharedFunctionInfo> shared_info, OffThreadIsolate* isolate);
...@@ -285,7 +288,10 @@ class UnoptimizedCompilationJob : public CompilationJob { ...@@ -285,7 +288,10 @@ class UnoptimizedCompilationJob : public CompilationJob {
Handle<SharedFunctionInfo> shared, Handle<SharedFunctionInfo> shared,
Isolate* isolate) const; Isolate* isolate) const;
ParseInfo* parse_info() const { return parse_info_; } ParseInfo* parse_info() const {
DCHECK_NOT_NULL(parse_info_);
return parse_info_;
}
UnoptimizedCompilationInfo* compilation_info() const { UnoptimizedCompilationInfo* compilation_info() const {
return compilation_info_; return compilation_info_;
} }
...@@ -299,7 +305,7 @@ class UnoptimizedCompilationJob : public CompilationJob { ...@@ -299,7 +305,7 @@ class UnoptimizedCompilationJob : public CompilationJob {
return time_taken_to_finalize_; return time_taken_to_finalize_;
} }
bool can_off_thread_finalize() const { return can_off_thread_finalize_; } void ClearParseInfo() { parse_info_ = nullptr; }
protected: protected:
// Overridden by the actual implementation. // Overridden by the actual implementation.
...@@ -315,7 +321,6 @@ class UnoptimizedCompilationJob : public CompilationJob { ...@@ -315,7 +321,6 @@ class UnoptimizedCompilationJob : public CompilationJob {
UnoptimizedCompilationInfo* compilation_info_; UnoptimizedCompilationInfo* compilation_info_;
base::TimeDelta time_taken_to_execute_; base::TimeDelta time_taken_to_execute_;
base::TimeDelta time_taken_to_finalize_; base::TimeDelta time_taken_to_finalize_;
bool can_off_thread_finalize_;
}; };
// A base class for optimized compilation jobs. // A base class for optimized compilation jobs.
...@@ -426,6 +431,33 @@ class FinalizeUnoptimizedCompilationData { ...@@ -426,6 +431,33 @@ class FinalizeUnoptimizedCompilationData {
using FinalizeUnoptimizedCompilationDataList = using FinalizeUnoptimizedCompilationDataList =
std::vector<FinalizeUnoptimizedCompilationData>; std::vector<FinalizeUnoptimizedCompilationData>;
class DeferredFinalizationJobData {
public:
DeferredFinalizationJobData(Isolate* isolate,
Handle<SharedFunctionInfo> function_handle,
std::unique_ptr<UnoptimizedCompilationJob> job) {
UNREACHABLE();
}
DeferredFinalizationJobData(OffThreadIsolate* isolate,
Handle<SharedFunctionInfo> function_handle,
std::unique_ptr<UnoptimizedCompilationJob> job)
: function_transfer_handle_(isolate->TransferHandle(function_handle)),
job_(std::move(job)) {}
Handle<SharedFunctionInfo> function_handle() const {
return function_transfer_handle_.ToHandle();
}
UnoptimizedCompilationJob* job() const { return job_.get(); }
private:
OffThreadTransferHandle<SharedFunctionInfo> function_transfer_handle_;
std::unique_ptr<UnoptimizedCompilationJob> job_;
};
using DeferredFinalizationJobDataList =
std::vector<DeferredFinalizationJobData>;
class V8_EXPORT_PRIVATE BackgroundCompileTask { class V8_EXPORT_PRIVATE BackgroundCompileTask {
public: public:
// Creates a new task that when run will parse and compile the streamed // Creates a new task that when run will parse and compile the streamed
...@@ -451,16 +483,11 @@ class V8_EXPORT_PRIVATE BackgroundCompileTask { ...@@ -451,16 +483,11 @@ class V8_EXPORT_PRIVATE BackgroundCompileTask {
return info_.get(); return info_.get();
} }
Parser* parser() { return parser_.get(); } Parser* parser() { return parser_.get(); }
UnoptimizedCompilationJob* outer_function_job() { UnoptimizedCompilationJobList* compilation_jobs() {
return outer_function_job_.get(); return &compilation_jobs_;
}
UnoptimizedCompilationJobList* inner_function_jobs() {
return &inner_function_jobs_;
} }
UnoptimizedCompileFlags flags() const { return flags_; } UnoptimizedCompileFlags flags() const { return flags_; }
const UnoptimizedCompileState* compile_state() const { UnoptimizedCompileState* compile_state() { return &compile_state_; }
return &compile_state_;
}
LanguageMode language_mode() { return language_mode_; } LanguageMode language_mode() { return language_mode_; }
bool finalize_on_background_thread() { bool finalize_on_background_thread() {
return finalize_on_background_thread_; return finalize_on_background_thread_;
...@@ -479,6 +506,12 @@ class V8_EXPORT_PRIVATE BackgroundCompileTask { ...@@ -479,6 +506,12 @@ class V8_EXPORT_PRIVATE BackgroundCompileTask {
return &finalize_unoptimized_compilation_data_; return &finalize_unoptimized_compilation_data_;
} }
// 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_;
}
private: private:
// Data needed for parsing, and data needed to to be passed between thread // Data needed for parsing, and data needed to to be passed between thread
// between parsing and compilation. These need to be initialized before the // between parsing and compilation. These need to be initialized before the
...@@ -489,8 +522,7 @@ class V8_EXPORT_PRIVATE BackgroundCompileTask { ...@@ -489,8 +522,7 @@ class V8_EXPORT_PRIVATE BackgroundCompileTask {
std::unique_ptr<Parser> parser_; std::unique_ptr<Parser> parser_;
// Data needed for finalizing compilation after background compilation. // Data needed for finalizing compilation after background compilation.
std::unique_ptr<UnoptimizedCompilationJob> outer_function_job_; UnoptimizedCompilationJobList compilation_jobs_;
UnoptimizedCompilationJobList inner_function_jobs_;
// Data needed for merging onto the main thread after background finalization. // Data needed for merging onto the main thread after background finalization.
// TODO(leszeks): When these are available, the above fields are not. We // TODO(leszeks): When these are available, the above fields are not. We
...@@ -500,6 +532,7 @@ class V8_EXPORT_PRIVATE BackgroundCompileTask { ...@@ -500,6 +532,7 @@ class V8_EXPORT_PRIVATE BackgroundCompileTask {
OffThreadTransferMaybeHandle<SharedFunctionInfo> outer_function_sfi_; OffThreadTransferMaybeHandle<SharedFunctionInfo> outer_function_sfi_;
OffThreadTransferHandle<Script> script_; OffThreadTransferHandle<Script> script_;
FinalizeUnoptimizedCompilationDataList finalize_unoptimized_compilation_data_; FinalizeUnoptimizedCompilationDataList finalize_unoptimized_compilation_data_;
DeferredFinalizationJobDataList jobs_to_retry_finalization_on_main_thread_;
// Single function data for top-level function compilation. // Single function data for top-level function compilation.
int start_position_; int start_position_;
......
...@@ -43,6 +43,7 @@ class V8_EXPORT_PRIVATE UnoptimizedCompilationInfo final { ...@@ -43,6 +43,7 @@ class V8_EXPORT_PRIVATE UnoptimizedCompilationInfo final {
DCHECK_NOT_NULL(literal); DCHECK_NOT_NULL(literal);
literal_ = literal; literal_ = literal;
} }
void ClearLiteral() { literal_ = nullptr; }
DeclarationScope* scope() const; DeclarationScope* scope() const;
......
...@@ -149,7 +149,7 @@ InterpreterCompilationJob::InterpreterCompilationJob( ...@@ -149,7 +149,7 @@ InterpreterCompilationJob::InterpreterCompilationJob(
AccountingAllocator* allocator, AccountingAllocator* allocator,
std::vector<FunctionLiteral*>* eager_inner_literals) std::vector<FunctionLiteral*>* eager_inner_literals)
: UnoptimizedCompilationJob(parse_info->stack_limit(), parse_info, : UnoptimizedCompilationJob(parse_info->stack_limit(), parse_info,
&compilation_info_, CanOffThreadFinalize::kYes), &compilation_info_),
zone_(allocator, ZONE_NAME), zone_(allocator, ZONE_NAME),
compilation_info_(&zone_, parse_info, literal), compilation_info_(&zone_, parse_info, literal),
generator_(&zone_, &compilation_info_, parse_info->ast_string_constants(), generator_(&zone_, &compilation_info_, parse_info->ast_string_constants(),
......
...@@ -417,12 +417,15 @@ bool SharedFunctionInfo::is_compiled() const { ...@@ -417,12 +417,15 @@ bool SharedFunctionInfo::is_compiled() const {
!data.IsUncompiledData(); !data.IsUncompiledData();
} }
IsCompiledScope SharedFunctionInfo::is_compiled_scope(Isolate* isolate) const { template <typename LocalIsolate>
IsCompiledScope SharedFunctionInfo::is_compiled_scope(
LocalIsolate* isolate) const {
return IsCompiledScope(*this, isolate); return IsCompiledScope(*this, isolate);
} }
template <typename LocalIsolate>
IsCompiledScope::IsCompiledScope(const SharedFunctionInfo shared, IsCompiledScope::IsCompiledScope(const SharedFunctionInfo shared,
Isolate* isolate) LocalIsolate* isolate)
: retain_bytecode_(shared.HasBytecodeArray() : retain_bytecode_(shared.HasBytecodeArray()
? handle(shared.GetBytecodeArray(), isolate) ? handle(shared.GetBytecodeArray(), isolate)
: MaybeHandle<BytecodeArray>()), : MaybeHandle<BytecodeArray>()),
......
...@@ -260,7 +260,8 @@ class SharedFunctionInfo : public HeapObject { ...@@ -260,7 +260,8 @@ class SharedFunctionInfo : public HeapObject {
// Returns an IsCompiledScope which reports whether the function is compiled, // Returns an IsCompiledScope which reports whether the function is compiled,
// and if compiled, will avoid the function becoming uncompiled while it is // and if compiled, will avoid the function becoming uncompiled while it is
// held. // held.
inline IsCompiledScope is_compiled_scope(Isolate* isolate) const; template <typename LocalIsolate>
inline IsCompiledScope is_compiled_scope(LocalIsolate* isolate) const;
// [length]: The function length - usually the number of declared parameters. // [length]: The function length - usually the number of declared parameters.
// Use up to 2^16-2 parameters (16 bits of values, where one is reserved for // Use up to 2^16-2 parameters (16 bits of values, where one is reserved for
...@@ -700,7 +701,9 @@ struct SourceCodeOf { ...@@ -700,7 +701,9 @@ struct SourceCodeOf {
// the scope is retained. // the scope is retained.
class IsCompiledScope { class IsCompiledScope {
public: public:
inline IsCompiledScope(const SharedFunctionInfo shared, Isolate* isolate); template <typename LocalIsolate>
inline IsCompiledScope(const SharedFunctionInfo shared,
LocalIsolate* isolate);
inline IsCompiledScope() : retain_bytecode_(), is_compiled_(false) {} inline IsCompiledScope() : retain_bytecode_(), is_compiled_(false) {}
inline bool is_compiled() const { return is_compiled_; } inline bool is_compiled() const { return is_compiled_; }
......
...@@ -40,15 +40,19 @@ void PendingCompilationErrorHandler::MessageDetails::Prepare( ...@@ -40,15 +40,19 @@ void PendingCompilationErrorHandler::MessageDetails::Prepare(
switch (type_) { switch (type_) {
case kAstRawString: case kAstRawString:
return SetString(arg_->string(), isolate); return SetString(arg_->string(), isolate);
case kNone: case kNone:
case kConstCharString: case kConstCharString:
// We can delay allocation until ArgumentString(isolate). // We can delay allocation until ArgumentString(isolate).
// TODO(leszeks): We don't actually have to transfer this string, since // TODO(leszeks): We don't actually have to transfer this string, since
// it's a root. // it's a root.
return; return;
case kMainThreadHandle: case kMainThreadHandle:
case kOffThreadTransferHandle: case kOffThreadTransferHandle:
UNREACHABLE(); // The message details might already be prepared, so skip them if this is
// the case.
return;
} }
} }
......
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