Commit 53c11006 authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Compiler] Avoid blocking on inner function parallel compilation.

Don't block on inner function compilation before competing outer function
compilation. Instead wait for the compilation to complete when the function
is called.

BUG=v8:5203

Review-Url: https://codereview.chromium.org/2686673002
Cr-Commit-Position: refs/heads/master@{#43116}
parent 2bfd8a7c
......@@ -17,7 +17,6 @@
#include "src/parsing/scanner-character-streams.h"
#include "src/unicode-cache.h"
#include "src/utils.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
......@@ -75,8 +74,8 @@ CompilerDispatcherJob::CompilerDispatcherJob(Isolate* isolate,
isolate_->global_handles()->Create(*shared))),
max_stack_size_(max_stack_size),
trace_compiler_dispatcher_jobs_(FLAG_trace_compiler_dispatcher_jobs) {
DCHECK(!shared_->is_toplevel());
HandleScope scope(isolate_);
DCHECK(!shared_->outer_scope_info()->IsTheHole(isolate_));
Handle<Script> script(Script::cast(shared_->script()), isolate_);
Handle<String> source(String::cast(script->source()), isolate_);
if (trace_compiler_dispatcher_jobs_) {
......@@ -87,7 +86,7 @@ CompilerDispatcherJob::CompilerDispatcherJob(Isolate* isolate,
}
CompilerDispatcherJob::CompilerDispatcherJob(
Isolate* isolate, CompilerDispatcherTracer* tracer,
Isolate* isolate, CompilerDispatcherTracer* tracer, Handle<Script> script,
Handle<SharedFunctionInfo> shared, FunctionLiteral* literal,
std::shared_ptr<Zone> parse_zone,
std::shared_ptr<DeferredHandles> parse_handles,
......@@ -106,6 +105,7 @@ CompilerDispatcherJob::CompilerDispatcherJob(
Handle<JSFunction>::null())),
trace_compiler_dispatcher_jobs_(FLAG_trace_compiler_dispatcher_jobs) {
parse_info_->set_literal(literal);
parse_info_->set_script(script);
parse_info_->set_deferred_handles(parse_handles);
compile_info_->set_deferred_handles(compile_handles);
......@@ -224,12 +224,12 @@ void CompilerDispatcherJob::PrepareToParseOnMainThread() {
parse_info_->set_function_literal_id(shared_->function_literal_id());
parser_.reset(new Parser(parse_info_.get()));
Handle<ScopeInfo> outer_scope_info(
handle(ScopeInfo::cast(shared_->outer_scope_info())));
parser_->DeserializeScopeChain(parse_info_.get(),
outer_scope_info->length() > 0
? MaybeHandle<ScopeInfo>(outer_scope_info)
: MaybeHandle<ScopeInfo>());
MaybeHandle<ScopeInfo> outer_scope_info;
if (!shared_->outer_scope_info()->IsTheHole(isolate_) &&
ScopeInfo::cast(shared_->outer_scope_info())->length() > 0) {
outer_scope_info = handle(ScopeInfo::cast(shared_->outer_scope_info()));
}
parser_->DeserializeScopeChain(parse_info_.get(), outer_scope_info);
Handle<String> name(String::cast(shared_->name()));
parse_info_->set_function_name(
......@@ -295,9 +295,11 @@ bool CompilerDispatcherJob::FinalizeParsingOnMainThread() {
DeferredHandleScope scope(isolate_);
{
parse_info_->ReopenHandlesInNewHandleScope();
Handle<ScopeInfo> outer_scope_info(
handle(ScopeInfo::cast(shared_->outer_scope_info())));
if (outer_scope_info->length() > 0) {
if (!shared_->outer_scope_info()->IsTheHole(isolate_) &&
ScopeInfo::cast(shared_->outer_scope_info())->length() > 0) {
Handle<ScopeInfo> outer_scope_info(
handle(ScopeInfo::cast(shared_->outer_scope_info())));
parse_info_->set_outer_scope_info(outer_scope_info);
}
parse_info_->set_shared_info(shared_);
......
......@@ -50,6 +50,7 @@ class V8_EXPORT_PRIVATE CompilerDispatcherJob {
size_t max_stack_size);
// Creates a CompilerDispatcherJob in the analyzed state.
CompilerDispatcherJob(Isolate* isolate, CompilerDispatcherTracer* tracer,
Handle<Script> script,
Handle<SharedFunctionInfo> shared,
FunctionLiteral* literal,
std::shared_ptr<Zone> parse_zone,
......
......@@ -293,8 +293,8 @@ bool CompilerDispatcher::EnqueueAndStep(Handle<SharedFunctionInfo> function) {
}
bool CompilerDispatcher::Enqueue(
Handle<SharedFunctionInfo> function, FunctionLiteral* literal,
std::shared_ptr<Zone> parse_zone,
Handle<Script> script, Handle<SharedFunctionInfo> function,
FunctionLiteral* literal, std::shared_ptr<Zone> parse_zone,
std::shared_ptr<DeferredHandles> parse_handles,
std::shared_ptr<DeferredHandles> compile_handles) {
if (!CanEnqueue(function)) return false;
......@@ -307,8 +307,8 @@ bool CompilerDispatcher::Enqueue(
}
std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
isolate_, tracer_.get(), function, literal, parse_zone, parse_handles,
compile_handles, max_stack_size_));
isolate_, tracer_.get(), script, function, literal, parse_zone,
parse_handles, compile_handles, max_stack_size_));
std::pair<int, int> key(Script::cast(function->script())->id(),
function->function_literal_id());
jobs_.insert(std::make_pair(key, std::move(job)));
......@@ -317,11 +317,12 @@ bool CompilerDispatcher::Enqueue(
}
bool CompilerDispatcher::EnqueueAndStep(
Handle<SharedFunctionInfo> function, FunctionLiteral* literal,
std::shared_ptr<Zone> parse_zone,
Handle<Script> script, Handle<SharedFunctionInfo> function,
FunctionLiteral* literal, std::shared_ptr<Zone> parse_zone,
std::shared_ptr<DeferredHandles> parse_handles,
std::shared_ptr<DeferredHandles> compile_handles) {
if (!Enqueue(function, literal, parse_zone, parse_handles, compile_handles)) {
if (!Enqueue(script, function, literal, parse_zone, parse_handles,
compile_handles)) {
return false;
}
......@@ -345,6 +346,9 @@ bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const {
void CompilerDispatcher::WaitForJobIfRunningOnBackground(
CompilerDispatcherJob* job) {
RuntimeCallTimerScope runtimeTimer(
isolate_, &RuntimeCallStats::CompileWaitForDispatcher);
base::LockGuard<base::Mutex> lock(&mutex_);
if (running_background_jobs_.find(job) == running_background_jobs_.end()) {
pending_background_jobs_.erase(job);
......@@ -359,16 +363,6 @@ void CompilerDispatcher::WaitForJobIfRunningOnBackground(
DCHECK(running_background_jobs_.find(job) == running_background_jobs_.end());
}
bool CompilerDispatcher::FinishNow(CompilerDispatcherJob* job) {
WaitForJobIfRunningOnBackground(job);
while (!IsFinished(job)) {
DoNextStepOnMainThread(isolate_, job, ExceptionHandling::kThrow);
}
bool result = job->status() != CompileJobStatus::kFailed;
job->ResetOnMainThread();
return result;
}
bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
JobMap::const_iterator job = GetJobFor(function);
CHECK(job != jobs_.end());
......@@ -379,7 +373,12 @@ bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
PrintF(" now\n");
}
bool result = FinishNow(job->second.get());
WaitForJobIfRunningOnBackground(job->second.get());
while (!IsFinished(job->second.get())) {
DoNextStepOnMainThread(isolate_, job->second.get(),
ExceptionHandling::kThrow);
}
bool result = job->second->status() != CompileJobStatus::kFailed;
if (trace_compiler_dispatcher_) {
PrintF("CompilerDispatcher: finished working on ");
......@@ -388,6 +387,7 @@ bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
tracer_->DumpStatistics();
}
job->second->ResetOnMainThread();
jobs_.erase(job);
if (jobs_.empty()) {
base::LockGuard<base::Mutex> lock(&mutex_);
......@@ -396,30 +396,6 @@ bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
return result;
}
bool CompilerDispatcher::FinishAllNow() {
if (trace_compiler_dispatcher_) {
PrintF("CompilerDispatcher: finishing all jobs now\n");
}
bool result = true;
for (auto& it : jobs_) {
result &= FinishNow(it.second.get());
}
if (trace_compiler_dispatcher_) {
PrintF("CompilerDispatcher: finished all jobs\n");
}
jobs_.clear();
{
base::LockGuard<base::Mutex> lock(&mutex_);
DCHECK(pending_background_jobs_.empty());
DCHECK(running_background_jobs_.empty());
abort_ = false;
}
return result;
}
void CompilerDispatcher::AbortAll(BlockingBehavior blocking) {
bool background_tasks_running =
task_manager_->TryAbortAll() == CancelableTaskManager::kTaskRunning;
......
......@@ -84,15 +84,16 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
// Enqueue a job for compilation. Function must have already been parsed and
// analyzed and be ready for compilation. Returns true if a job was enqueued.
bool Enqueue(Handle<SharedFunctionInfo> function, FunctionLiteral* literal,
std::shared_ptr<Zone> parse_zone,
bool Enqueue(Handle<Script> script, Handle<SharedFunctionInfo> function,
FunctionLiteral* literal, std::shared_ptr<Zone> parse_zone,
std::shared_ptr<DeferredHandles> parse_handles,
std::shared_ptr<DeferredHandles> compile_handles);
// Like Enqueue, but also advances the job so that it can potentially
// continue running on a background thread (if at all possible). Returns
// true if the job was enqueued.
bool EnqueueAndStep(Handle<SharedFunctionInfo> function,
bool EnqueueAndStep(Handle<Script> script,
Handle<SharedFunctionInfo> function,
FunctionLiteral* literal,
std::shared_ptr<Zone> parse_zone,
std::shared_ptr<DeferredHandles> parse_handles,
......@@ -105,10 +106,6 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
// possible). Returns true if the compile job was successful.
bool FinishNow(Handle<SharedFunctionInfo> function);
// Blocks until all enqueued jobs have finished. Returns true if all the
// compile jobs were successful.
bool FinishAllNow();
// Aborts a given job. Blocks if requested.
void Abort(Handle<SharedFunctionInfo> function, BlockingBehavior blocking);
......@@ -141,7 +138,6 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
void AbortInactiveJobs();
bool CanEnqueue(Handle<SharedFunctionInfo> function);
JobMap::const_iterator GetJobFor(Handle<SharedFunctionInfo> shared) const;
bool FinishNow(CompilerDispatcherJob* job);
void ConsiderJobForBackgroundProcessing(CompilerDispatcherJob* job);
void ScheduleMoreBackgroundTasksIfNeeded();
void ScheduleIdleTaskFromAnyThread();
......
......@@ -538,7 +538,8 @@ bool CompileUnoptimizedInnerFunctions(
CompilerDispatcher* dispatcher = isolate->compiler_dispatcher();
if (UseCompilerDispatcher(inner_function_mode, dispatcher, literal->scope(),
shared, is_debug, will_serialize) &&
dispatcher->EnqueueAndStep(shared, literal, parse_zone,
dispatcher->EnqueueAndStep(outer_info->script(), shared, literal,
parse_zone,
outer_info->parse_info()->deferred_handles(),
outer_info->deferred_handles())) {
// If we have successfully queued up the function for compilation on the
......@@ -620,17 +621,6 @@ bool CompileUnoptimizedCode(CompilationInfo* info,
return false;
}
// TODO(rmcilroy): Remove this once the enqueued tasks can keep the parsed
// zone and handles alive and replace with a check in CompileLazy to finish
// the task itself.
RuntimeCallTimerScope runtimeTimer(
isolate, &RuntimeCallStats::CompileWaitForDispatcher);
if (isolate->compiler_dispatcher()->IsEnabled() &&
!isolate->compiler_dispatcher()->FinishAllNow()) {
if (!isolate->has_pending_exception()) isolate->StackOverflow();
return false;
}
return true;
}
......@@ -1249,13 +1239,25 @@ bool Compiler::Compile(Handle<JSFunction> function, ClearExceptionFlag flag) {
Isolate* isolate = function->GetIsolate();
DCHECK(AllowCompilation::IsAllowed(isolate));
// Start a compilation.
CompilerDispatcher* dispatcher = isolate->compiler_dispatcher();
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
Handle<Code> code;
if (!GetLazyCode(function).ToHandle(&code)) {
if (flag == CLEAR_EXCEPTION) {
isolate->clear_pending_exception();
if (dispatcher->IsEnqueued(shared)) {
if (!dispatcher->FinishNow(shared)) {
if (flag == CLEAR_EXCEPTION) {
isolate->clear_pending_exception();
}
return false;
}
code = handle(shared->code(), isolate);
} else {
// Start a compilation.
if (!GetLazyCode(function).ToHandle(&code)) {
if (flag == CLEAR_EXCEPTION) {
isolate->clear_pending_exception();
}
return false;
}
return false;
}
// Install code on closure.
......@@ -1378,7 +1380,10 @@ MaybeHandle<JSArray> Compiler::CompileForLiveEdit(Handle<Script> script) {
bool Compiler::EnsureBytecode(CompilationInfo* info) {
if (!info->shared_info()->is_compiled()) {
if (GetUnoptimizedCode(info, Compiler::NOT_CONCURRENT).is_null()) {
CompilerDispatcher* dispatcher = info->isolate()->compiler_dispatcher();
if (dispatcher->IsEnqueued(info->shared_info())) {
if (!dispatcher->FinishNow(info->shared_info())) return false;
} else if (GetUnoptimizedCode(info, Compiler::NOT_CONCURRENT).is_null()) {
return false;
}
}
......@@ -1396,6 +1401,12 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) {
DCHECK_NOT_NULL(info->literal());
DCHECK_NOT_NULL(info->scope());
Handle<SharedFunctionInfo> shared = info->shared_info();
CompilerDispatcher* dispatcher = info->isolate()->compiler_dispatcher();
if (dispatcher->IsEnqueued(shared)) {
if (!dispatcher->FinishNow(shared)) return false;
}
if (!shared->has_deoptimization_support()) {
Zone compile_zone(info->isolate()->allocator(), ZONE_NAME);
CompilationInfo unoptimized(&compile_zone, info->parse_info(),
......
......@@ -843,18 +843,19 @@ TEST_F(CompilerDispatcherTest, EnqueueParsed) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
const char script[] =
const char source[] =
"function g() { var y = 1; function f17(x) { return x * y }; return f17; "
"} g();";
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), source));
Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
Handle<Script> script(Script::cast(shared->script()), i_isolate());
ParseInfo parse_info(shared);
ASSERT_TRUE(Compiler::ParseAndAnalyze(&parse_info));
std::shared_ptr<DeferredHandles> handles;
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
ASSERT_TRUE(dispatcher.Enqueue(shared, parse_info.literal(),
ASSERT_TRUE(dispatcher.Enqueue(script, shared, parse_info.literal(),
parse_info.zone_shared(), handles, handles));
ASSERT_TRUE(dispatcher.IsEnqueued(shared));
......@@ -870,18 +871,19 @@ TEST_F(CompilerDispatcherTest, EnqueueAndStepParsed) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
const char script[] =
const char source[] =
"function g() { var y = 1; function f18(x) { return x * y }; return f18; "
"} g();";
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), source));
Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
Handle<Script> script(Script::cast(shared->script()), i_isolate());
ParseInfo parse_info(shared);
ASSERT_TRUE(Compiler::ParseAndAnalyze(&parse_info));
std::shared_ptr<DeferredHandles> handles;
ASSERT_FALSE(dispatcher.IsEnqueued(shared));
ASSERT_TRUE(dispatcher.EnqueueAndStep(shared, parse_info.literal(),
ASSERT_TRUE(dispatcher.EnqueueAndStep(script, shared, parse_info.literal(),
parse_info.zone_shared(), handles,
handles));
ASSERT_TRUE(dispatcher.IsEnqueued(shared));
......@@ -895,55 +897,16 @@ TEST_F(CompilerDispatcherTest, EnqueueAndStepParsed) {
platform.ClearBackgroundTasks();
}
TEST_F(CompilerDispatcherTest, FinishAllNow) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
const char script1[] =
"function g() { var y = 1; function f19(x) { return x + y }; return f19; "
"} g();";
Handle<JSFunction> f1 = Handle<JSFunction>::cast(RunJS(isolate(), script1));
Handle<SharedFunctionInfo> shared1(f1->shared(), i_isolate());
const char script2[] =
"function g() { var y = 1; function f20(x) { return x * y }; return f20; "
"} g();";
Handle<JSFunction> f2 = Handle<JSFunction>::cast(RunJS(isolate(), script2));
Handle<SharedFunctionInfo> shared2(f2->shared(), i_isolate());
ASSERT_FALSE(shared1->is_compiled());
ASSERT_FALSE(shared2->is_compiled());
// Enqueue shared1 as already parsed.
ParseInfo parse_info(shared1);
ASSERT_TRUE(Compiler::ParseAndAnalyze(&parse_info));
std::shared_ptr<DeferredHandles> handles;
ASSERT_TRUE(dispatcher.Enqueue(shared1, parse_info.literal(),
parse_info.zone_shared(), handles, handles));
// Enqueue shared2 for parsing and compiling
ASSERT_TRUE(dispatcher.Enqueue(shared2));
ASSERT_TRUE(dispatcher.FinishAllNow());
// Finishing removes the SFI from the queue.
ASSERT_FALSE(dispatcher.IsEnqueued(shared1));
ASSERT_FALSE(dispatcher.IsEnqueued(shared2));
ASSERT_TRUE(shared1->is_compiled());
ASSERT_TRUE(shared2->is_compiled());
ASSERT_TRUE(platform.IdleTaskPending());
platform.ClearIdleTask();
}
TEST_F(CompilerDispatcherTest, CompileParsedOutOfScope) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
const char script[] =
const char source[] =
"function g() { var y = 1; function f20(x) { return x + y }; return f20; "
"} g();";
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), source));
Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
Handle<Script> script(Script::cast(shared->script()), i_isolate());
{
HandleScope scope(i_isolate()); // Create handles scope for parsing.
......@@ -959,7 +922,7 @@ TEST_F(CompilerDispatcherTest, CompileParsedOutOfScope) {
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(dispatcher.Enqueue(
shared, parse_info.literal(), parse_info.zone_shared(),
script, shared, parse_info.literal(), parse_info.zone_shared(),
parse_info.deferred_handles(), compilation_handles));
ASSERT_TRUE(platform.IdleTaskPending());
}
......@@ -1033,7 +996,7 @@ TEST_F(CompilerDispatcherTestWithoutContext, CompileExtensionWithoutContext) {
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(dispatcher.Enqueue(
shared, parse_info.literal(), parse_info.zone_shared(),
script, shared, parse_info.literal(), parse_info.zone_shared(),
parse_info.deferred_handles(), compilation_handles));
ASSERT_TRUE(platform.IdleTaskPending());
}
......@@ -1047,5 +1010,55 @@ TEST_F(CompilerDispatcherTestWithoutContext, CompileExtensionWithoutContext) {
ASSERT_TRUE(shared->is_compiled());
}
TEST_F(CompilerDispatcherTest, CompileLazyFinishesDispatcherJob) {
// Use the real dispatcher so that CompileLazy checks the same one for
// enqueued functions.
CompilerDispatcher* dispatcher = i_isolate()->compiler_dispatcher();
const char source[] =
"function g() { var y = 1; function f16(x) { return x * y }; return f16; "
"} g();";
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), source));
Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
ASSERT_FALSE(shared->is_compiled());
ASSERT_FALSE(dispatcher->IsEnqueued(shared));
ASSERT_TRUE(dispatcher->Enqueue(shared));
ASSERT_TRUE(dispatcher->IsEnqueued(shared));
// Now force the function to run and ensure CompileLazy finished and dequeues
// it from the dispatcher.
RunJS(isolate(), "g()();");
ASSERT_TRUE(shared->is_compiled());
ASSERT_FALSE(dispatcher->IsEnqueued(shared));
}
TEST_F(CompilerDispatcherTest, CompileLazy2FinishesDispatcherJob) {
// Use the real dispatcher so that CompileLazy checks the same one for
// enqueued functions.
CompilerDispatcher* dispatcher = i_isolate()->compiler_dispatcher();
const char source2[] = "function lazy2() { return 42; }; lazy2;";
Handle<JSFunction> lazy2 =
Handle<JSFunction>::cast(RunJS(isolate(), source2));
Handle<SharedFunctionInfo> shared2(lazy2->shared(), i_isolate());
ASSERT_FALSE(shared2->is_compiled());
const char source1[] = "function lazy1() { return lazy2(); }; lazy1;";
Handle<JSFunction> lazy1 =
Handle<JSFunction>::cast(RunJS(isolate(), source1));
Handle<SharedFunctionInfo> shared1(lazy1->shared(), i_isolate());
ASSERT_FALSE(shared1->is_compiled());
ASSERT_TRUE(dispatcher->Enqueue(shared1));
ASSERT_TRUE(dispatcher->Enqueue(shared2));
RunJS(isolate(), "lazy1();");
ASSERT_TRUE(shared1->is_compiled());
ASSERT_TRUE(shared2->is_compiled());
ASSERT_FALSE(dispatcher->IsEnqueued(shared1));
ASSERT_FALSE(dispatcher->IsEnqueued(shared2));
}
} // namespace internal
} // namespace v8
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