6d42c4504a
Enable enqueueing of eager inner function compilation onto the compiler dispatcher. This enables these tasks to be performed in parallel to compilation of the outer functio (only for Ignition functions). We currently synchronize to ensure all inner function compilations are complete before executing the outer function - future work will allow outer function execution to happenin parallel to inner function compilation. BUG=v8:5203,v8:5215 Review-Url: https://codereview.chromium.org/2611313002 Cr-Commit-Position: refs/heads/master@{#42667}
339 lines
11 KiB
C++
339 lines
11 KiB
C++
// Copyright 2016 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <memory>
|
|
|
|
#include "include/v8.h"
|
|
#include "src/api.h"
|
|
#include "src/ast/ast.h"
|
|
#include "src/ast/scopes.h"
|
|
#include "src/base/platform/semaphore.h"
|
|
#include "src/compiler-dispatcher/compiler-dispatcher-job.h"
|
|
#include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
|
|
#include "src/flags.h"
|
|
#include "src/isolate-inl.h"
|
|
#include "src/parsing/parse-info.h"
|
|
#include "src/v8.h"
|
|
#include "test/unittests/compiler-dispatcher/compiler-dispatcher-helper.h"
|
|
#include "test/unittests/test-utils.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
class CompilerDispatcherJobTest : public TestWithContext {
|
|
public:
|
|
CompilerDispatcherJobTest() : tracer_(i_isolate()) {}
|
|
~CompilerDispatcherJobTest() override {}
|
|
|
|
CompilerDispatcherTracer* tracer() { return &tracer_; }
|
|
|
|
static void SetUpTestCase() {
|
|
old_flag_ = i::FLAG_ignition;
|
|
i::FLAG_ignition = true;
|
|
TestWithContext::SetUpTestCase();
|
|
}
|
|
|
|
static void TearDownTestCase() {
|
|
TestWithContext::TearDownTestCase();
|
|
i::FLAG_ignition = old_flag_;
|
|
}
|
|
|
|
private:
|
|
CompilerDispatcherTracer tracer_;
|
|
static bool old_flag_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(CompilerDispatcherJobTest);
|
|
};
|
|
|
|
bool CompilerDispatcherJobTest::old_flag_;
|
|
|
|
namespace {
|
|
|
|
const char test_script[] = "(x) { x*x; }";
|
|
|
|
class ScriptResource : public v8::String::ExternalOneByteStringResource {
|
|
public:
|
|
ScriptResource(const char* data, size_t length)
|
|
: data_(data), length_(length) {}
|
|
~ScriptResource() override = default;
|
|
|
|
const char* data() const override { return data_; }
|
|
size_t length() const override { return length_; }
|
|
|
|
private:
|
|
const char* data_;
|
|
size_t length_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ScriptResource);
|
|
};
|
|
|
|
Handle<SharedFunctionInfo> CreateSharedFunctionInfo(
|
|
Isolate* isolate, ExternalOneByteString::Resource* maybe_resource) {
|
|
HandleScope scope(isolate);
|
|
Handle<String> source;
|
|
if (maybe_resource) {
|
|
source = isolate->factory()
|
|
->NewExternalStringFromOneByte(maybe_resource)
|
|
.ToHandleChecked();
|
|
} else {
|
|
source = isolate->factory()->NewStringFromAsciiChecked(test_script);
|
|
}
|
|
Handle<Script> script = isolate->factory()->NewScript(source);
|
|
Handle<FixedArray> infos = isolate->factory()->NewFixedArray(3);
|
|
script->set_shared_function_infos(*infos);
|
|
Handle<SharedFunctionInfo> shared = isolate->factory()->NewSharedFunctionInfo(
|
|
isolate->factory()->NewStringFromAsciiChecked("f"),
|
|
isolate->builtins()->CompileLazy(), false);
|
|
shared->set_end_position(source->length());
|
|
shared->set_outer_scope_info(ScopeInfo::Empty(isolate));
|
|
shared->set_function_literal_id(1);
|
|
SharedFunctionInfo::SetScript(shared, script);
|
|
return scope.CloseAndEscape(shared);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_F(CompilerDispatcherJobTest, Construct) {
|
|
std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
|
|
i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), nullptr),
|
|
FLAG_stack_size));
|
|
}
|
|
|
|
TEST_F(CompilerDispatcherJobTest, StateTransitions) {
|
|
std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
|
|
i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), nullptr),
|
|
FLAG_stack_size));
|
|
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
|
|
job->PrepareToParseOnMainThread();
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kReadyToParse);
|
|
job->Parse();
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kParsed);
|
|
ASSERT_TRUE(job->FinalizeParsingOnMainThread());
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kReadyToAnalyze);
|
|
ASSERT_TRUE(job->AnalyzeOnMainThread());
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kAnalyzed);
|
|
ASSERT_TRUE(job->PrepareToCompileOnMainThread());
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kReadyToCompile);
|
|
job->Compile();
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kCompiled);
|
|
ASSERT_TRUE(job->FinalizeCompilingOnMainThread());
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kDone);
|
|
job->ResetOnMainThread();
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
|
|
}
|
|
|
|
TEST_F(CompilerDispatcherJobTest, SyntaxError) {
|
|
ScriptResource script("^^^", strlen("^^^"));
|
|
std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
|
|
i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), &script),
|
|
FLAG_stack_size));
|
|
|
|
job->PrepareToParseOnMainThread();
|
|
job->Parse();
|
|
ASSERT_FALSE(job->FinalizeParsingOnMainThread());
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kFailed);
|
|
ASSERT_TRUE(i_isolate()->has_pending_exception());
|
|
|
|
i_isolate()->clear_pending_exception();
|
|
|
|
job->ResetOnMainThread();
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
|
|
}
|
|
|
|
TEST_F(CompilerDispatcherJobTest, ScopeChain) {
|
|
const char script[] =
|
|
"function g() { var y = 1; function f(x) { return x * y }; return f; } "
|
|
"g();";
|
|
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
|
|
|
|
std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
|
|
i_isolate(), tracer(), handle(f->shared()), FLAG_stack_size));
|
|
|
|
job->PrepareToParseOnMainThread();
|
|
job->Parse();
|
|
ASSERT_TRUE(job->FinalizeParsingOnMainThread());
|
|
ASSERT_TRUE(job->AnalyzeOnMainThread());
|
|
ASSERT_TRUE(job->PrepareToCompileOnMainThread());
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kReadyToCompile);
|
|
|
|
const AstRawString* var_x =
|
|
job->parse_info_->ast_value_factory()->GetOneByteString("x");
|
|
Variable* var = job->parse_info_->literal()->scope()->Lookup(var_x);
|
|
ASSERT_TRUE(var);
|
|
ASSERT_TRUE(var->IsParameter());
|
|
|
|
const AstRawString* var_y =
|
|
job->parse_info_->ast_value_factory()->GetOneByteString("y");
|
|
var = job->parse_info_->literal()->scope()->Lookup(var_y);
|
|
ASSERT_TRUE(var);
|
|
ASSERT_TRUE(var->IsContextSlot());
|
|
|
|
job->ResetOnMainThread();
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
|
|
}
|
|
|
|
TEST_F(CompilerDispatcherJobTest, CompileAndRun) {
|
|
const char script[] =
|
|
"function g() {\n"
|
|
" f = function(a) {\n"
|
|
" for (var i = 0; i < 3; i++) { a += 20; }\n"
|
|
" return a;\n"
|
|
" }\n"
|
|
" return f;\n"
|
|
"}\n"
|
|
"g();";
|
|
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
|
|
std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
|
|
i_isolate(), tracer(), handle(f->shared()), FLAG_stack_size));
|
|
|
|
job->PrepareToParseOnMainThread();
|
|
job->Parse();
|
|
job->FinalizeParsingOnMainThread();
|
|
job->AnalyzeOnMainThread();
|
|
job->PrepareToCompileOnMainThread();
|
|
job->Compile();
|
|
ASSERT_TRUE(job->FinalizeCompilingOnMainThread());
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kDone);
|
|
|
|
Smi* value = Smi::cast(*RunJS(isolate(), "f(100);"));
|
|
ASSERT_TRUE(value == Smi::FromInt(160));
|
|
|
|
job->ResetOnMainThread();
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
|
|
}
|
|
|
|
TEST_F(CompilerDispatcherJobTest, CompileFailureToAnalyse) {
|
|
std::string raw_script("() { var a = ");
|
|
for (int i = 0; i < 100000; i++) {
|
|
raw_script += "'x' + ";
|
|
}
|
|
raw_script += " 'x'; }";
|
|
ScriptResource script(raw_script.c_str(), strlen(raw_script.c_str()));
|
|
std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
|
|
i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), &script),
|
|
100));
|
|
|
|
job->PrepareToParseOnMainThread();
|
|
job->Parse();
|
|
job->FinalizeParsingOnMainThread();
|
|
ASSERT_FALSE(job->AnalyzeOnMainThread());
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kFailed);
|
|
ASSERT_TRUE(i_isolate()->has_pending_exception());
|
|
|
|
i_isolate()->clear_pending_exception();
|
|
job->ResetOnMainThread();
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
|
|
}
|
|
|
|
TEST_F(CompilerDispatcherJobTest, CompileFailureToFinalize) {
|
|
std::string raw_script("() { var a = ");
|
|
for (int i = 0; i < 1000; i++) {
|
|
raw_script += "'x' + ";
|
|
}
|
|
raw_script += " 'x'; }";
|
|
ScriptResource script(raw_script.c_str(), strlen(raw_script.c_str()));
|
|
std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
|
|
i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), &script),
|
|
50));
|
|
|
|
job->PrepareToParseOnMainThread();
|
|
job->Parse();
|
|
job->FinalizeParsingOnMainThread();
|
|
job->AnalyzeOnMainThread();
|
|
job->PrepareToCompileOnMainThread();
|
|
job->Compile();
|
|
ASSERT_FALSE(job->FinalizeCompilingOnMainThread());
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kFailed);
|
|
ASSERT_TRUE(i_isolate()->has_pending_exception());
|
|
|
|
i_isolate()->clear_pending_exception();
|
|
job->ResetOnMainThread();
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
|
|
}
|
|
|
|
class CompileTask : public Task {
|
|
public:
|
|
CompileTask(CompilerDispatcherJob* job, base::Semaphore* semaphore)
|
|
: job_(job), semaphore_(semaphore) {}
|
|
~CompileTask() override {}
|
|
|
|
void Run() override {
|
|
job_->Compile();
|
|
semaphore_->Signal();
|
|
}
|
|
|
|
private:
|
|
CompilerDispatcherJob* job_;
|
|
base::Semaphore* semaphore_;
|
|
DISALLOW_COPY_AND_ASSIGN(CompileTask);
|
|
};
|
|
|
|
TEST_F(CompilerDispatcherJobTest, CompileOnBackgroundThread) {
|
|
const char* raw_script =
|
|
"(a, b) {\n"
|
|
" var c = a + b;\n"
|
|
" function bar() { return b }\n"
|
|
" var d = { foo: 100, bar : bar() }\n"
|
|
" return bar;"
|
|
"}";
|
|
ScriptResource script(raw_script, strlen(raw_script));
|
|
std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
|
|
i_isolate(), tracer(), CreateSharedFunctionInfo(i_isolate(), &script),
|
|
100));
|
|
|
|
job->PrepareToParseOnMainThread();
|
|
job->Parse();
|
|
job->FinalizeParsingOnMainThread();
|
|
job->AnalyzeOnMainThread();
|
|
job->PrepareToCompileOnMainThread();
|
|
|
|
base::Semaphore semaphore(0);
|
|
CompileTask* background_task = new CompileTask(job.get(), &semaphore);
|
|
V8::GetCurrentPlatform()->CallOnBackgroundThread(background_task,
|
|
Platform::kShortRunningTask);
|
|
semaphore.Wait();
|
|
ASSERT_TRUE(job->FinalizeCompilingOnMainThread());
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kDone);
|
|
|
|
job->ResetOnMainThread();
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
|
|
}
|
|
|
|
TEST_F(CompilerDispatcherJobTest, LazyInnerFunctions) {
|
|
const char script[] =
|
|
"function g() {\n"
|
|
" f = function() {\n"
|
|
" e = (function() { return 42; });\n"
|
|
" return e;\n"
|
|
" };\n"
|
|
" return f;\n"
|
|
"}\n"
|
|
"g();";
|
|
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
|
|
|
|
std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
|
|
i_isolate(), tracer(), handle(f->shared()), FLAG_stack_size));
|
|
|
|
job->PrepareToParseOnMainThread();
|
|
job->Parse();
|
|
ASSERT_TRUE(job->FinalizeParsingOnMainThread());
|
|
ASSERT_TRUE(job->AnalyzeOnMainThread());
|
|
ASSERT_TRUE(job->PrepareToCompileOnMainThread());
|
|
job->Compile();
|
|
ASSERT_TRUE(job->FinalizeCompilingOnMainThread());
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kDone);
|
|
|
|
Handle<JSFunction> e = Handle<JSFunction>::cast(RunJS(isolate(), "f();"));
|
|
|
|
ASSERT_FALSE(e->shared()->HasBaselineCode());
|
|
|
|
job->ResetOnMainThread();
|
|
ASSERT_TRUE(job->status() == CompileJobStatus::kInitial);
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|