Commit c2d2d4d1 authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Compiler] Add compile operations to CompilerDispatcherJob.

Adds compile operations to the CompilerDispatcherJob interface. As such,
introduces Compiler::PrepareUnoptimizedCompilationJob and updates the
unoptimized compilation path to use CompilationJobs. Also unifies
FinalizeCompilationJob to deal with both optimized and unoptimized
compilation jobs.

A dummy FullCodegenCompilationJob is also introduced, where all the work
is done in the ExecuteJob phase, which cannot be run on a
background thread.

BUG=v8:5203

Review-Url: https://codereview.chromium.org/2251713002
Cr-Commit-Position: refs/heads/master@{#38897}
parent 25f3de99
......@@ -5,6 +5,7 @@
#include "src/compiler-dispatcher/compiler-dispatcher-job.h"
#include "src/assert-scope.h"
#include "src/compiler.h"
#include "src/global-handles.h"
#include "src/isolate.h"
#include "src/objects-inl.h"
......@@ -23,7 +24,8 @@ CompilerDispatcherJob::CompilerDispatcherJob(Isolate* isolate,
: isolate_(isolate),
function_(Handle<JSFunction>::cast(
isolate_->global_handles()->Create(*function))),
max_stack_size_(max_stack_size) {
max_stack_size_(max_stack_size),
can_compile_on_background_thread_(false) {
HandleScope scope(isolate_);
Handle<SharedFunctionInfo> shared(function_->shared(), isolate_);
Handle<Script> script(Script::cast(shared->script()), isolate_);
......@@ -131,7 +133,7 @@ bool CompilerDispatcherJob::FinalizeParsingOnMainThread() {
if (parse_info_->literal() == nullptr) {
status_ = CompileJobStatus::kFailed;
} else {
status_ = CompileJobStatus::kReadyToCompile;
status_ = CompileJobStatus::kReadyToAnalyse;
}
DeferredHandleScope scope(isolate_);
......@@ -146,6 +148,7 @@ bool CompilerDispatcherJob::FinalizeParsingOnMainThread() {
parse_info_->set_script(script);
parse_info_->set_context(handle(function_->context(), isolate_));
parse_info_->set_shared_info(handle(function_->shared(), isolate_));
// Do the parsing tasks which need to be done on the main thread. This will
// also handle parse errors.
......@@ -163,6 +166,79 @@ bool CompilerDispatcherJob::FinalizeParsingOnMainThread() {
return status_ != CompileJobStatus::kFailed;
}
bool CompilerDispatcherJob::PrepareToCompileOnMainThread() {
DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
DCHECK(status() == CompileJobStatus::kReadyToAnalyse);
compile_info_.reset(new CompilationInfo(parse_info_.get(), function_));
DeferredHandleScope scope(isolate_);
{
// Create a canonical handle scope before ast numbering if compiling
// bytecode. This is required for off-thread bytecode generation.
std::unique_ptr<CanonicalHandleScope> canonical;
if (FLAG_ignition) canonical.reset(new CanonicalHandleScope(isolate_));
if (Compiler::Analyze(parse_info_.get())) {
compile_job_.reset(
Compiler::PrepareUnoptimizedCompilationJob(compile_info_.get()));
}
}
compile_info_->set_deferred_handles(scope.Detach());
if (!compile_job_.get()) {
if (!isolate_->has_pending_exception()) isolate_->StackOverflow();
status_ = CompileJobStatus::kFailed;
return false;
}
can_compile_on_background_thread_ =
compile_job_->can_execute_on_background_thread();
status_ = CompileJobStatus::kReadyToCompile;
return true;
}
void CompilerDispatcherJob::Compile() {
DCHECK(status() == CompileJobStatus::kReadyToCompile);
DCHECK(can_compile_on_background_thread_ ||
ThreadId::Current().Equals(isolate_->thread_id()));
// Disallowing of handle dereference and heap access dealt with in
// CompilationJob::ExecuteJob.
uintptr_t stack_limit =
reinterpret_cast<uintptr_t>(&stack_limit) - max_stack_size_ * KB;
compile_job_->set_stack_limit(stack_limit);
CompilationJob::Status status = compile_job_->ExecuteJob();
USE(status);
// Always transition to kCompiled - errors will be reported by
// FinalizeCompilingOnMainThread.
status_ = CompileJobStatus::kCompiled;
}
bool CompilerDispatcherJob::FinalizeCompilingOnMainThread() {
DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
DCHECK(status() == CompileJobStatus::kCompiled);
if (compile_job_->state() == CompilationJob::State::kFailed ||
!Compiler::FinalizeCompilationJob(compile_job_.release())) {
if (!isolate_->has_pending_exception()) isolate_->StackOverflow();
status_ = CompileJobStatus::kFailed;
return false;
}
zone_.reset();
parse_info_.reset();
compile_info_.reset();
compile_job_.reset();
handles_from_parsing_.reset();
status_ = CompileJobStatus::kDone;
return true;
}
void CompilerDispatcherJob::ResetOnMainThread() {
DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
......@@ -172,6 +248,8 @@ void CompilerDispatcherJob::ResetOnMainThread() {
parse_info_.reset();
zone_.reset();
handles_from_parsing_.reset();
compile_info_.reset();
compile_job_.reset();
if (!source_.is_null()) {
i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location());
......
......@@ -15,6 +15,7 @@ namespace v8 {
namespace internal {
class CompilationInfo;
class CompilationJob;
class Isolate;
class JSFunction;
class ParseInfo;
......@@ -28,7 +29,9 @@ enum class CompileJobStatus {
kInitial,
kReadyToParse,
kParsed,
kReadyToAnalyse,
kReadyToCompile,
kCompiled,
kFailed,
kDone,
};
......@@ -43,6 +46,11 @@ class CompilerDispatcherJob {
bool can_parse_on_background_thread() const {
return can_parse_on_background_thread_;
}
// Should only be called after kReadyToCompile.
bool can_compile_on_background_thread() const {
DCHECK(compile_job_.get());
return can_compile_on_background_thread_;
}
// Transition from kInitial to kReadyToParse.
void PrepareToParseOnMainThread();
......@@ -50,10 +58,21 @@ class CompilerDispatcherJob {
// Transition from kReadyToParse to kParsed.
void Parse();
// Transition from kParsed to kReadyToCompile (or kFailed). Returns false
// Transition from kParsed to kReadyToAnalyse (or kFailed). Returns false
// when transitioning to kFailed. In that case, an exception is pending.
bool FinalizeParsingOnMainThread();
// Transition from kReadyToAnalyse to kReadyToCompile (or kFailed). Returns
// false when transitioning to kFailed. In that case, an exception is pending.
bool PrepareToCompileOnMainThread();
// Transition from kReadyToCompile to kCompiled.
void Compile();
// Transition from kCompiled to kDone (or kFailed). Returns false when
// transitioning to kFailed. In that case, an exception is pending.
bool FinalizeCompilingOnMainThread();
// Transition from any state to kInitial and free all resources.
void ResetOnMainThread();
......@@ -74,7 +93,12 @@ class CompilerDispatcherJob {
std::unique_ptr<Parser> parser_;
std::unique_ptr<DeferredHandles> handles_from_parsing_;
// Members required for compiling.
std::unique_ptr<CompilationInfo> compile_info_;
std::unique_ptr<CompilationJob> compile_job_;
bool can_parse_on_background_thread_;
bool can_compile_on_background_thread_;
DISALLOW_COPY_AND_ASSIGN(CompilerDispatcherJob);
};
......
......@@ -260,10 +260,18 @@ CompilationJob::Status CompilationJob::PrepareJob() {
}
CompilationJob::Status CompilationJob::ExecuteJob() {
DisallowHeapAllocation no_allocation;
DisallowHandleAllocation no_handles;
DisallowHandleDereference no_deref;
DisallowCodeDependencyChange no_dependency_change;
std::unique_ptr<DisallowHeapAllocation> no_allocation;
std::unique_ptr<DisallowHandleAllocation> no_handles;
std::unique_ptr<DisallowHandleDereference> no_deref;
std::unique_ptr<DisallowCodeDependencyChange> no_dependency_change;
if (can_execute_on_background_thread()) {
no_allocation.reset(new DisallowHeapAllocation());
no_handles.reset(new DisallowHandleAllocation());
no_deref.reset(new DisallowHandleDereference());
no_dependency_change.reset(new DisallowCodeDependencyChange());
} else {
DCHECK(ThreadId::Current().Equals(info()->isolate()->thread_id()));
}
// Delegate to the underlying implementation.
DCHECK(state() == State::kReadyToExecute);
......@@ -283,6 +291,57 @@ CompilationJob::Status CompilationJob::FinalizeJob() {
return UpdateState(FinalizeJobImpl(), State::kSucceeded);
}
void CompilationJob::RecordUnoptimizedCompilationStats() const {
int code_size;
if (info()->has_bytecode_array()) {
code_size = info()->bytecode_array()->SizeIncludingMetadata();
} else {
code_size = info()->code()->SizeIncludingMetadata();
}
Counters* counters = isolate()->counters();
// TODO(4280): Rename counters from "baseline" to "unoptimized" eventually.
counters->total_baseline_code_size()->Increment(code_size);
counters->total_baseline_compile_count()->Increment(1);
// TODO(5203): Add timers for each phase of compilation.
}
void CompilationJob::RecordOptimizedCompilationStats() const {
DCHECK(info()->IsOptimizing());
Handle<JSFunction> function = info()->closure();
if (!function->IsOptimized()) {
// Concurrent recompilation and OSR may race. Increment only once.
int opt_count = function->shared()->opt_count();
function->shared()->set_opt_count(opt_count + 1);
}
double ms_creategraph = time_taken_to_prepare_.InMillisecondsF();
double ms_optimize = time_taken_to_execute_.InMillisecondsF();
double ms_codegen = time_taken_to_finalize_.InMillisecondsF();
if (FLAG_trace_opt) {
PrintF("[optimizing ");
function->ShortPrint();
PrintF(" - took %0.3f, %0.3f, %0.3f ms]\n", ms_creategraph, ms_optimize,
ms_codegen);
}
if (FLAG_trace_opt_stats) {
static double compilation_time = 0.0;
static int compiled_functions = 0;
static int code_size = 0;
compilation_time += (ms_creategraph + ms_optimize + ms_codegen);
compiled_functions++;
code_size += function->shared()->SourceSize();
PrintF("Compiled: %d functions with %d byte source size in %fms.\n",
compiled_functions, code_size, compilation_time);
}
if (FLAG_hydrogen_stats) {
isolate()->GetHStatistics()->IncrementSubtotals(time_taken_to_prepare_,
time_taken_to_execute_,
time_taken_to_finalize_);
}
}
namespace {
void AddWeakObjectToCodeDependency(Isolate* isolate, Handle<HeapObject> object,
......@@ -341,41 +400,6 @@ void CompilationJob::RegisterWeakObjectsInOptimizedCode(Handle<Code> code) {
code->set_can_have_weak_objects(true);
}
void CompilationJob::RecordOptimizationStats() {
DCHECK(info()->IsOptimizing());
Handle<JSFunction> function = info()->closure();
if (!function->IsOptimized()) {
// Concurrent recompilation and OSR may race. Increment only once.
int opt_count = function->shared()->opt_count();
function->shared()->set_opt_count(opt_count + 1);
}
double ms_creategraph = time_taken_to_prepare_.InMillisecondsF();
double ms_optimize = time_taken_to_execute_.InMillisecondsF();
double ms_codegen = time_taken_to_finalize_.InMillisecondsF();
if (FLAG_trace_opt) {
PrintF("[optimizing ");
function->ShortPrint();
PrintF(" - took %0.3f, %0.3f, %0.3f ms]\n", ms_creategraph, ms_optimize,
ms_codegen);
}
if (FLAG_trace_opt_stats) {
static double compilation_time = 0.0;
static int compiled_functions = 0;
static int code_size = 0;
compilation_time += (ms_creategraph + ms_optimize + ms_codegen);
compiled_functions++;
code_size += function->shared()->SourceSize();
PrintF("Compiled: %d functions with %d byte source size in %fms.\n",
compiled_functions, code_size, compilation_time);
}
if (FLAG_hydrogen_stats) {
isolate()->GetHStatistics()->IncrementSubtotals(time_taken_to_prepare_,
time_taken_to_execute_,
time_taken_to_finalize_);
}
}
// ----------------------------------------------------------------------------
// Local helper methods that make up the compilation pipeline.
......@@ -466,18 +490,24 @@ bool ShouldUseIgnition(CompilationInfo* info) {
return info->shared_info()->PassesFilter(FLAG_ignition_filter);
}
int CodeAndMetadataSize(CompilationInfo* info) {
if (info->has_bytecode_array()) {
return info->bytecode_array()->SizeIncludingMetadata();
CompilationJob* GetUnoptimizedCompilationJob(CompilationInfo* info) {
// Function should have been parsed and analyzed before creating a compilation
// job.
DCHECK_NOT_NULL(info->literal());
DCHECK_NOT_NULL(info->scope());
EnsureFeedbackMetadata(info);
if (ShouldUseIgnition(info)) {
return interpreter::Interpreter::NewCompilationJob(info);
} else {
return FullCodeGenerator::NewCompilationJob(info);
}
return info->code()->SizeIncludingMetadata();
}
bool GenerateUnoptimizedCode(CompilationInfo* info) {
bool success;
EnsureFeedbackMetadata(info);
if (FLAG_validate_asm && info->scope()->asm_module() &&
!info->shared_info()->is_asm_wasm_broken()) {
EnsureFeedbackMetadata(info);
MaybeHandle<FixedArray> wasm_data;
wasm_data = AsmJs::ConvertAsmToWasm(info->parse_info());
if (!wasm_data.is_null()) {
......@@ -486,19 +516,13 @@ bool GenerateUnoptimizedCode(CompilationInfo* info) {
return true;
}
}
if (ShouldUseIgnition(info)) {
success = interpreter::Interpreter::MakeBytecode(info);
} else {
success = FullCodeGenerator::MakeCode(info);
}
if (success) {
Isolate* isolate = info->isolate();
Counters* counters = isolate->counters();
// TODO(4280): Rename counters from "baseline" to "unoptimized" eventually.
counters->total_baseline_code_size()->Increment(CodeAndMetadataSize(info));
counters->total_baseline_compile_count()->Increment(1);
}
return success;
std::unique_ptr<CompilationJob> job(GetUnoptimizedCompilationJob(info));
if (job->PrepareJob() != CompilationJob::SUCCEEDED) return false;
if (job->ExecuteJob() != CompilationJob::SUCCEEDED) return false;
if (job->FinalizeJob() != CompilationJob::SUCCEEDED) return false;
job->RecordUnoptimizedCompilationStats();
return true;
}
bool CompileUnoptimizedCode(CompilationInfo* info) {
......@@ -534,6 +558,19 @@ void InstallSharedCompilationResult(CompilationInfo* info,
}
}
void InstallUnoptimizedCode(CompilationInfo* info) {
Handle<SharedFunctionInfo> shared = info->shared_info();
// Update the shared function info with the scope info.
InstallSharedScopeInfo(info, shared);
// Install compilation result on the shared function info
InstallSharedCompilationResult(info, shared);
// Record the function compilation event.
RecordFunctionCompilation(CodeEventListener::LAZY_COMPILE_TAG, info);
}
MUST_USE_RESULT MaybeHandle<Code> GetUnoptimizedCode(CompilationInfo* info) {
VMState<COMPILER> state(info->isolate());
PostponeInterruptsScope postpone(info->isolate());
......@@ -545,24 +582,27 @@ MUST_USE_RESULT MaybeHandle<Code> GetUnoptimizedCode(CompilationInfo* info) {
// Parse and update CompilationInfo with the results.
if (!Parser::ParseStatic(info->parse_info())) return MaybeHandle<Code>();
Handle<SharedFunctionInfo> shared = info->shared_info();
DCHECK_EQ(shared->language_mode(), info->literal()->language_mode());
DCHECK_EQ(info->shared_info()->language_mode(),
info->literal()->language_mode());
// Compile either unoptimized code or bytecode for the interpreter.
if (!CompileUnoptimizedCode(info)) return MaybeHandle<Code>();
// Update the shared function info with the scope info.
InstallSharedScopeInfo(info, shared);
// Install compilation result on the shared function info
InstallSharedCompilationResult(info, shared);
// Record the function compilation event.
RecordFunctionCompilation(CodeEventListener::LAZY_COMPILE_TAG, info);
InstallUnoptimizedCode(info);
return info->code();
}
CompilationJob::Status FinalizeUnoptimizedCompilationJob(CompilationJob* job) {
CompilationJob::Status status = job->FinalizeJob();
if (status == CompilationJob::SUCCEEDED) {
DCHECK(!job->info()->shared_info()->is_compiled());
InstallUnoptimizedCode(job->info());
job->RecordUnoptimizedCompilationStats();
}
return status;
}
MUST_USE_RESULT MaybeHandle<Code> GetCodeFromOptimizedCodeMap(
Handle<JSFunction> function, BailoutId osr_ast_id) {
Handle<SharedFunctionInfo> shared(function->shared());
......@@ -691,7 +731,7 @@ bool GetOptimizedCodeNow(CompilationJob* job) {
}
// Success!
job->RecordOptimizationStats();
job->RecordOptimizedCompilationStats();
DCHECK(!isolate->has_pending_exception());
InsertCodeIntoOptimizedCodeMap(info);
RecordFunctionCompilation(CodeEventListener::LAZY_COMPILE_TAG, info);
......@@ -852,6 +892,60 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
return MaybeHandle<Code>();
}
CompilationJob::Status FinalizeOptimizedCompilationJob(CompilationJob* job) {
CompilationInfo* info = job->info();
Isolate* isolate = info->isolate();
TimerEventScope<TimerEventRecompileSynchronous> timer(info->isolate());
RuntimeCallTimerScope runtimeTimer(isolate,
&RuntimeCallStats::RecompileSynchronous);
TRACE_EVENT_RUNTIME_CALL_STATS_TRACING_SCOPED(
isolate, &tracing::TraceEventStatsTable::RecompileSynchronous);
Handle<SharedFunctionInfo> shared = info->shared_info();
shared->code()->set_profiler_ticks(0);
DCHECK(!shared->HasDebugInfo());
// 1) Optimization on the concurrent thread may have failed.
// 2) The function may have already been optimized by OSR. Simply continue.
// Except when OSR already disabled optimization for some reason.
// 3) The code may have already been invalidated due to dependency change.
// 4) Code generation may have failed.
if (job->state() == CompilationJob::State::kReadyToFinalize) {
if (shared->optimization_disabled()) {
job->RetryOptimization(kOptimizationDisabled);
} else if (info->dependencies()->HasAborted()) {
job->RetryOptimization(kBailedOutDueToDependencyChange);
} else if (job->FinalizeJob() == CompilationJob::SUCCEEDED) {
job->RecordOptimizedCompilationStats();
RecordFunctionCompilation(CodeEventListener::LAZY_COMPILE_TAG, info);
if (shared
->SearchOptimizedCodeMap(info->context()->native_context(),
info->osr_ast_id())
.code == nullptr) {
InsertCodeIntoOptimizedCodeMap(info);
}
if (FLAG_trace_opt) {
PrintF("[completed optimizing ");
info->closure()->ShortPrint();
PrintF("]\n");
}
info->closure()->ReplaceCode(*info->code());
return CompilationJob::SUCCEEDED;
}
}
DCHECK(job->state() == CompilationJob::State::kFailed);
if (FLAG_trace_opt) {
PrintF("[aborted optimizing ");
info->closure()->ShortPrint();
PrintF(" because: %s]\n", GetBailoutReason(info->bailout_reason()));
}
info->closure()->ReplaceCode(shared->code());
return CompilationJob::FAILED;
}
class InterpreterActivationsFinder : public ThreadVisitor,
public OptimizedFunctionVisitor {
public:
......@@ -1910,58 +2004,28 @@ MaybeHandle<Code> Compiler::GetOptimizedCodeForOSR(Handle<JSFunction> function,
return GetOptimizedCode(function, NOT_CONCURRENT, osr_ast_id, osr_frame);
}
void Compiler::FinalizeCompilationJob(CompilationJob* raw_job) {
CompilationJob* Compiler::PrepareUnoptimizedCompilationJob(
CompilationInfo* info) {
VMState<COMPILER> state(info->isolate());
std::unique_ptr<CompilationJob> job(GetUnoptimizedCompilationJob(info));
if (job->PrepareJob() != CompilationJob::SUCCEEDED) {
return nullptr;
}
return job.release();
}
bool Compiler::FinalizeCompilationJob(CompilationJob* raw_job) {
// Take ownership of compilation job. Deleting job also tears down the zone.
std::unique_ptr<CompilationJob> job(raw_job);
CompilationInfo* info = job->info();
Isolate* isolate = info->isolate();
VMState<COMPILER> state(isolate);
TimerEventScope<TimerEventRecompileSynchronous> timer(info->isolate());
RuntimeCallTimerScope runtimeTimer(isolate,
&RuntimeCallStats::RecompileSynchronous);
TRACE_EVENT_RUNTIME_CALL_STATS_TRACING_SCOPED(
isolate, &tracing::TraceEventStatsTable::RecompileSynchronous);
Handle<SharedFunctionInfo> shared = info->shared_info();
shared->code()->set_profiler_ticks(0);
DCHECK(!shared->HasDebugInfo());
// 1) Optimization on the concurrent thread may have failed.
// 2) The function may have already been optimized by OSR. Simply continue.
// Except when OSR already disabled optimization for some reason.
// 3) The code may have already been invalidated due to dependency change.
// 4) Code generation may have failed.
if (job->state() == CompilationJob::State::kReadyToFinalize) {
if (shared->optimization_disabled()) {
job->RetryOptimization(kOptimizationDisabled);
} else if (info->dependencies()->HasAborted()) {
job->RetryOptimization(kBailedOutDueToDependencyChange);
} else if (job->FinalizeJob() == CompilationJob::SUCCEEDED) {
job->RecordOptimizationStats();
RecordFunctionCompilation(CodeEventListener::LAZY_COMPILE_TAG, info);
if (shared->SearchOptimizedCodeMap(info->context()->native_context(),
info->osr_ast_id()).code == nullptr) {
InsertCodeIntoOptimizedCodeMap(info);
}
if (FLAG_trace_opt) {
PrintF("[completed optimizing ");
info->closure()->ShortPrint();
PrintF("]\n");
}
info->closure()->ReplaceCode(*info->code());
return;
}
}
DCHECK(job->state() == CompilationJob::State::kFailed);
if (FLAG_trace_opt) {
PrintF("[aborted optimizing ");
info->closure()->ShortPrint();
PrintF(" because: %s]\n", GetBailoutReason(info->bailout_reason()));
VMState<COMPILER> state(job->info()->isolate());
if (job->info()->IsOptimizing()) {
return FinalizeOptimizedCompilationJob(job.get()) ==
CompilationJob::SUCCEEDED;
} else {
return FinalizeUnoptimizedCompilationJob(job.get()) ==
CompilationJob::SUCCEEDED;
}
info->closure()->ReplaceCode(shared->code());
}
void Compiler::PostInstantiation(Handle<JSFunction> function,
......
......@@ -57,8 +57,12 @@ class Compiler : public AllStatic {
static bool CompileDebugCode(Handle<SharedFunctionInfo> shared);
static MaybeHandle<JSArray> CompileForLiveEdit(Handle<Script> script);
// Prepare a compilation job for unoptimized code. Requires ParseAndAnalyse.
static CompilationJob* PrepareUnoptimizedCompilationJob(
CompilationInfo* info);
// Generate and install code from previously queued compilation job.
static void FinalizeCompilationJob(CompilationJob* job);
static bool FinalizeCompilationJob(CompilationJob* job);
// Give the compiler a chance to perform low-latency initialization tasks of
// the given {function} on its instantiation. Note that only the runtime will
......@@ -557,15 +561,20 @@ class CompilationJob {
kFailed,
};
explicit CompilationJob(CompilationInfo* info, const char* compiler_name,
State initial_state = State::kReadyToPrepare)
: info_(info), compiler_name_(compiler_name), state_(initial_state) {}
CompilationJob(Isolate* isolate, CompilationInfo* info,
const char* compiler_name,
State initial_state = State::kReadyToPrepare)
: info_(info),
compiler_name_(compiler_name),
state_(initial_state),
stack_limit_(isolate->stack_guard()->real_climit()) {}
virtual ~CompilationJob() {}
// Prepare the compile job. Must be called on the main thread.
MUST_USE_RESULT Status PrepareJob();
// Executes the compile job. Can be called off the main thread.
// Executes the compile job. Can be called on a background thread if
// can_execute_on_background_thread() returns true.
MUST_USE_RESULT Status ExecuteJob();
// Finalizes the compile job. Must be called on the main thread.
......@@ -589,7 +598,13 @@ class CompilationJob {
return FAILED;
}
void RecordOptimizationStats();
void RecordOptimizedCompilationStats() const;
void RecordUnoptimizedCompilationStats() const;
virtual bool can_execute_on_background_thread() const { return true; }
void set_stack_limit(uintptr_t stack_limit) { stack_limit_ = stack_limit; }
uintptr_t stack_limit() const { return stack_limit_; }
State state() const { return state_; }
CompilationInfo* info() const { return info_; }
......@@ -612,6 +627,7 @@ class CompilationJob {
base::TimeDelta time_taken_to_finalize_;
const char* compiler_name_;
State state_;
uintptr_t stack_limit_;
MUST_USE_RESULT Status UpdateState(Status status, State next_state) {
if (status == SUCCEEDED) {
......
......@@ -562,7 +562,7 @@ class PipelineCompilationJob final : public CompilationJob {
PipelineCompilationJob(Isolate* isolate, Handle<JSFunction> function)
// Note that the CompilationInfo is not initialized at the time we pass it
// to the CompilationJob constructor, but it is not dereferenced there.
: CompilationJob(&info_, "TurboFan"),
: CompilationJob(isolate, &info_, "TurboFan"),
zone_(isolate->allocator()),
zone_pool_(isolate->allocator()),
parse_info_(&zone_, function),
......@@ -660,7 +660,8 @@ class PipelineWasmCompilationJob final : public CompilationJob {
explicit PipelineWasmCompilationJob(CompilationInfo* info, Graph* graph,
CallDescriptor* descriptor,
SourcePositionTable* source_positions)
: CompilationJob(info, "TurboFan", State::kReadyToExecute),
: CompilationJob(info->isolate(), info, "TurboFan",
State::kReadyToExecute),
zone_pool_(info->isolate()->allocator()),
data_(&zone_pool_, info, graph, source_positions),
pipeline_(&data_),
......
......@@ -35,7 +35,7 @@ class Scope;
class HCompilationJob final : public CompilationJob {
public:
explicit HCompilationJob(Handle<JSFunction> function)
: CompilationJob(&info_, "Crankshaft"),
: CompilationJob(function->GetIsolate(), &info_, "Crankshaft"),
zone_(function->GetIsolate()->allocator()),
parse_info_(&zone_, function),
info_(&parse_info_, function),
......
......@@ -25,7 +25,31 @@ namespace internal {
#define __ ACCESS_MASM(masm())
bool FullCodeGenerator::MakeCode(CompilationInfo* info) {
class FullCodegenCompilationJob final : public CompilationJob {
public:
explicit FullCodegenCompilationJob(CompilationInfo* info)
: CompilationJob(info->isolate(), info, "Full-Codegen") {}
bool can_execute_on_background_thread() const override { return false; }
CompilationJob::Status PrepareJobImpl() final { return SUCCEEDED; }
CompilationJob::Status ExecuteJobImpl() final {
DCHECK(ThreadId::Current().Equals(isolate()->thread_id()));
return FullCodeGenerator::MakeCode(info(), stack_limit()) ? SUCCEEDED
: FAILED;
}
CompilationJob::Status FinalizeJobImpl() final { return SUCCEEDED; }
};
// static
CompilationJob* FullCodeGenerator::NewCompilationJob(CompilationInfo* info) {
return new FullCodegenCompilationJob(info);
}
// static
bool FullCodeGenerator::MakeCode(CompilationInfo* info, uintptr_t stack_limit) {
Isolate* isolate = info->isolate();
DCHECK(!FLAG_minimal);
......@@ -47,7 +71,7 @@ bool FullCodeGenerator::MakeCode(CompilationInfo* info) {
CodeObjectRequired::kYes);
if (info->will_serialize()) masm.enable_serializer();
FullCodeGenerator cgen(&masm, info);
FullCodeGenerator cgen(&masm, info, stack_limit);
cgen.Generate();
if (cgen.HasStackOverflow()) {
DCHECK(!isolate->has_pending_exception());
......@@ -157,9 +181,8 @@ bool FullCodeGenerator::MustCreateArrayLiteralWithRuntime(
expr->values()->length() > JSArray::kInitialMaxFastElementArray;
}
void FullCodeGenerator::Initialize() {
InitializeAstVisitor(info_->isolate());
void FullCodeGenerator::Initialize(uintptr_t stack_limit) {
InitializeAstVisitor(stack_limit);
masm_->set_emit_debug_code(FLAG_debug_code);
masm_->set_predictable_code_size(true);
}
......
......@@ -29,7 +29,8 @@ class JumpPatchSite;
class FullCodeGenerator final : public AstVisitor<FullCodeGenerator> {
public:
FullCodeGenerator(MacroAssembler* masm, CompilationInfo* info)
FullCodeGenerator(MacroAssembler* masm, CompilationInfo* info,
uintptr_t stack_limit)
: masm_(masm),
info_(info),
isolate_(info->isolate()),
......@@ -50,12 +51,17 @@ class FullCodeGenerator final : public AstVisitor<FullCodeGenerator> {
info->SourcePositionRecordingMode()),
ic_total_count_(0) {
DCHECK(!info->IsStub());
Initialize();
Initialize(stack_limit);
}
void Initialize();
void Initialize(uintptr_t stack_limit);
static bool MakeCode(CompilationInfo* info);
static CompilationJob* NewCompilationJob(CompilationInfo* info);
static bool MakeCode(CompilationInfo* info, uintptr_t stack_limit);
static bool MakeCode(CompilationInfo* info) {
return MakeCode(info, info->isolate()->stack_guard()->real_climit());
}
// Encode bailout state and pc-offset as a BitField<type, start, size>.
// Only use 30 bits because we encode the result as a smi.
......
......@@ -683,7 +683,6 @@ BytecodeGenerator::BytecodeGenerator(CompilationInfo* info)
loop_depth_(0),
home_object_symbol_(info->isolate()->factory()->home_object_symbol()),
prototype_string_(info->isolate()->factory()->prototype_string()) {
InitializeAstVisitor(info->isolate()->stack_guard()->real_climit());
}
Handle<BytecodeArray> BytecodeGenerator::FinalizeBytecode(Isolate* isolate) {
......@@ -726,11 +725,13 @@ void BytecodeGenerator::AllocateDeferredConstants() {
}
}
void BytecodeGenerator::GenerateBytecode() {
void BytecodeGenerator::GenerateBytecode(uintptr_t stack_limit) {
DisallowHeapAllocation no_allocation;
DisallowHandleAllocation no_handles;
DisallowHandleDereference no_deref;
InitializeAstVisitor(stack_limit);
// Initialize the incoming context.
ContextScope incoming_context(this, scope(), false);
......
......@@ -24,7 +24,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
public:
explicit BytecodeGenerator(CompilationInfo* info);
void GenerateBytecode();
void GenerateBytecode(uintptr_t stack_limit);
Handle<BytecodeArray> FinalizeBytecode(Isolate* isolate);
#define DECLARE_VISIT(type) void Visit##type(type* node);
......
......@@ -150,14 +150,40 @@ int Interpreter::InterruptBudget() {
}
InterpreterCompilationJob::InterpreterCompilationJob(CompilationInfo* info)
: CompilationJob(info, "Ignition"), generator_(info) {}
: CompilationJob(info->isolate(), info, "Ignition"), generator_(info) {}
InterpreterCompilationJob::Status InterpreterCompilationJob::PrepareJobImpl() {
if (FLAG_print_bytecode || FLAG_print_ast) {
OFStream os(stdout);
std::unique_ptr<char[]> name = info()->GetDebugName();
os << "[generating bytecode for function: " << info()->GetDebugName().get()
<< "]" << std::endl
<< std::flush;
}
#ifdef DEBUG
if (info()->parse_info() && FLAG_print_ast) {
OFStream os(stdout);
os << "--- AST ---" << std::endl
<< AstPrinter(info()->isolate()).PrintProgram(info()->literal())
<< std::endl
<< std::flush;
}
#endif // DEBUG
return SUCCEEDED;
}
InterpreterCompilationJob::Status InterpreterCompilationJob::ExecuteJobImpl() {
generator()->GenerateBytecode();
// TODO(5203): These timers aren't thread safe, move to using the CompilerJob
// timers.
RuntimeCallTimerScope runtimeTimer(info()->isolate(),
&RuntimeCallStats::CompileIgnition);
TimerEventScope<TimerEventCompileIgnition> timer(info()->isolate());
TRACE_EVENT_RUNTIME_CALL_STATS_TRACING_SCOPED(
info()->isolate(), &tracing::TraceEventStatsTable::CompileIgnition);
generator()->GenerateBytecode(stack_limit());
if (generator()->HasStackOverflow()) {
return FAILED;
......@@ -182,34 +208,8 @@ InterpreterCompilationJob::Status InterpreterCompilationJob::FinalizeJobImpl() {
return SUCCEEDED;
}
bool Interpreter::MakeBytecode(CompilationInfo* info) {
RuntimeCallTimerScope runtimeTimer(info->isolate(),
&RuntimeCallStats::CompileIgnition);
TimerEventScope<TimerEventCompileIgnition> timer(info->isolate());
TRACE_EVENT_RUNTIME_CALL_STATS_TRACING_SCOPED(
info->isolate(), &tracing::TraceEventStatsTable::CompileIgnition);
if (FLAG_print_bytecode || FLAG_print_ast) {
OFStream os(stdout);
std::unique_ptr<char[]> name = info->GetDebugName();
os << "[generating bytecode for function: " << info->GetDebugName().get()
<< "]" << std::endl
<< std::flush;
}
#ifdef DEBUG
if (info->parse_info() && FLAG_print_ast) {
OFStream os(stdout);
os << "--- AST ---" << std::endl
<< AstPrinter(info->isolate()).PrintProgram(info->literal()) << std::endl
<< std::flush;
}
#endif // DEBUG
InterpreterCompilationJob job(info);
if (job.PrepareJob() != CompilationJob::SUCCEEDED) return false;
if (job.ExecuteJob() != CompilationJob::SUCCEEDED) return false;
return job.FinalizeJob() == CompilationJob::SUCCEEDED;
CompilationJob* Interpreter::NewCompilationJob(CompilationInfo* info) {
return new InterpreterCompilationJob(info);
}
bool Interpreter::IsDispatchTableInitialized() {
......
......@@ -22,6 +22,7 @@ namespace internal {
class Isolate;
class Callable;
class CompilationInfo;
class CompilationJob;
namespace compiler {
class Node;
......@@ -42,8 +43,8 @@ class Interpreter {
// Returns the interrupt budget which should be used for the profiler counter.
static int InterruptBudget();
// Generate bytecode for |info|.
static bool MakeBytecode(CompilationInfo* info);
// Creates a compilation job which will generate bytecode for |info|.
static CompilationJob* NewCompilationJob(CompilationInfo* info);
// Return bytecode handler for |bytecode|.
Code* GetBytecodeHandler(Bytecode bytecode, OperandScale operand_scale);
......
......@@ -52,8 +52,8 @@ Handle<JSFunction> CreateFunction(
}
Handle<Script> script = isolate->factory()->NewScript(source);
Handle<SharedFunctionInfo> shared = isolate->factory()->NewSharedFunctionInfo(
isolate->factory()->NewStringFromAsciiChecked("f"), MaybeHandle<Code>(),
false);
isolate->factory()->NewStringFromAsciiChecked("f"),
isolate->builtins()->CompileLazy(), false);
SharedFunctionInfo::SetScript(shared, script);
shared->set_end_position(source->length());
Handle<JSFunction> function =
......@@ -62,6 +62,17 @@ Handle<JSFunction> CreateFunction(
return scope.CloseAndEscape(function);
}
Handle<Object> RunJS(v8::Isolate* isolate, const char* script) {
return Utils::OpenHandle(
*v8::Script::Compile(
isolate->GetCurrentContext(),
v8::String::NewFromUtf8(isolate, script, v8::NewStringType::kNormal)
.ToLocalChecked())
.ToLocalChecked()
->Run(isolate->GetCurrentContext())
.ToLocalChecked());
}
} // namespace
TEST_F(CompilerDispatcherJobTest, Construct) {
......@@ -93,7 +104,13 @@ TEST_F(CompilerDispatcherJobTest, StateTransitions) {
job->Parse();
ASSERT_TRUE(job->status() == CompileJobStatus::kParsed);
ASSERT_TRUE(job->FinalizeParsingOnMainThread());
ASSERT_TRUE(job->status() == CompileJobStatus::kReadyToAnalyse);
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);
}
......@@ -119,14 +136,7 @@ TEST_F(CompilerDispatcherJobTest, ScopeChain) {
const char script[] =
"function g() { var g = 1; function f(x) { return x * g }; return f; } "
"g();";
Handle<JSFunction> f = Handle<JSFunction>::cast(Utils::OpenHandle(
*v8::Script::Compile(isolate()->GetCurrentContext(),
v8::String::NewFromUtf8(isolate(), script,
v8::NewStringType::kNormal)
.ToLocalChecked())
.ToLocalChecked()
->Run(isolate()->GetCurrentContext())
.ToLocalChecked()));
Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script));
std::unique_ptr<CompilerDispatcherJob> job(
new CompilerDispatcherJob(i_isolate(), f, FLAG_stack_size));
......@@ -134,7 +144,7 @@ TEST_F(CompilerDispatcherJobTest, ScopeChain) {
job->PrepareToParseOnMainThread();
job->Parse();
ASSERT_TRUE(job->FinalizeParsingOnMainThread());
ASSERT_TRUE(job->status() == CompileJobStatus::kReadyToCompile);
ASSERT_TRUE(job->status() == CompileJobStatus::kReadyToAnalyse);
const AstRawString* var_x =
job->parse_info_->ast_value_factory()->GetOneByteString("x");
......@@ -152,5 +162,80 @@ TEST_F(CompilerDispatcherJobTest, ScopeChain) {
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(), f, FLAG_stack_size));
job->PrepareToParseOnMainThread();
job->Parse();
job->FinalizeParsingOnMainThread();
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, CompileFailureToPrepare) {
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(), CreateFunction(i_isolate(), &script), 100));
job->PrepareToParseOnMainThread();
job->Parse();
job->FinalizeParsingOnMainThread();
ASSERT_FALSE(job->PrepareToCompileOnMainThread());
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(), CreateFunction(i_isolate(), &script), 100));
job->PrepareToParseOnMainThread();
job->Parse();
job->FinalizeParsingOnMainThread();
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);
}
} // 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