Index: src/compiler.cc |
diff --git a/src/compiler.cc b/src/compiler.cc |
index c6dfa045b744a82da959c177e090546488322398..b9fe9870d6750a5383c72b955195bb9b4c2a4bb0 100644 |
--- a/src/compiler.cc |
+++ b/src/compiler.cc |
@@ -59,7 +59,6 @@ CompilationInfo::CompilationInfo(Handle<Script> script, |
: flags_(LanguageModeField::encode(CLASSIC_MODE)), |
script_(script), |
osr_ast_id_(BailoutId::None()), |
- osr_pc_offset_(0), |
parameter_count_(0) { |
Initialize(script->GetIsolate(), BASE, zone); |
} |
@@ -71,7 +70,6 @@ CompilationInfo::CompilationInfo(Handle<SharedFunctionInfo> shared_info, |
shared_info_(shared_info), |
script_(Handle<Script>(Script::cast(shared_info->script()))), |
osr_ast_id_(BailoutId::None()), |
- osr_pc_offset_(0), |
parameter_count_(0) { |
Initialize(script_->GetIsolate(), BASE, zone); |
} |
@@ -85,7 +83,6 @@ CompilationInfo::CompilationInfo(Handle<JSFunction> closure, |
script_(Handle<Script>(Script::cast(shared_info_->script()))), |
context_(closure->context()), |
osr_ast_id_(BailoutId::None()), |
- osr_pc_offset_(0), |
parameter_count_(0) { |
Initialize(script_->GetIsolate(), BASE, zone); |
} |
@@ -97,7 +94,6 @@ CompilationInfo::CompilationInfo(HydrogenCodeStub* stub, |
: flags_(LanguageModeField::encode(CLASSIC_MODE) | |
IsLazy::encode(true)), |
osr_ast_id_(BailoutId::None()), |
- osr_pc_offset_(0), |
parameter_count_(0) { |
Initialize(isolate, STUB, zone); |
code_stub_ = stub; |
@@ -243,86 +239,6 @@ bool CompilationInfo::ShouldSelfOptimize() { |
} |
-// Determine whether to use the full compiler for all code. If the flag |
-// --always-full-compiler is specified this is the case. For the virtual frame |
-// based compiler the full compiler is also used if a debugger is connected, as |
-// the code from the full compiler supports mode precise break points. For the |
-// crankshaft adaptive compiler debugging the optimized code is not possible at |
-// all. However crankshaft support recompilation of functions, so in this case |
-// the full compiler need not be be used if a debugger is attached, but only if |
-// break points has actually been set. |
-static bool IsDebuggerActive(Isolate* isolate) { |
-#ifdef ENABLE_DEBUGGER_SUPPORT |
- return isolate->use_crankshaft() ? |
- isolate->debug()->has_break_points() : |
- isolate->debugger()->IsDebuggerActive(); |
-#else |
- return false; |
-#endif |
-} |
- |
- |
-static bool AlwaysFullCompiler(Isolate* isolate) { |
- return FLAG_always_full_compiler || IsDebuggerActive(isolate); |
-} |
- |
- |
-void RecompileJob::RecordOptimizationStats() { |
- 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_create_graph_.InMillisecondsF(); |
- double ms_optimize = time_taken_to_optimize_.InMillisecondsF(); |
- double ms_codegen = time_taken_to_codegen_.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_create_graph_, |
- time_taken_to_optimize_, |
- time_taken_to_codegen_); |
- } |
-} |
- |
- |
-// A return value of true indicates the compilation pipeline is still |
-// going, not necessarily that we optimized the code. |
-static bool MakeCrankshaftCode(CompilationInfo* info) { |
- RecompileJob job(info); |
- RecompileJob::Status status = job.CreateGraph(); |
- |
- if (status != RecompileJob::SUCCEEDED) { |
- return status != RecompileJob::FAILED; |
- } |
- status = job.OptimizeGraph(); |
- if (status != RecompileJob::SUCCEEDED) { |
- status = job.AbortOptimization(); |
- return status != RecompileJob::FAILED; |
- } |
- status = job.GenerateAndInstallCode(); |
- return status != RecompileJob::FAILED; |
-} |
- |
- |
class HOptimizedGraphBuilderWithPositions: public HOptimizedGraphBuilder { |
public: |
explicit HOptimizedGraphBuilderWithPositions(CompilationInfo* info) |
@@ -360,7 +276,26 @@ class HOptimizedGraphBuilderWithPositions: public HOptimizedGraphBuilder { |
}; |
-RecompileJob::Status RecompileJob::CreateGraph() { |
+// Determine whether to use the full compiler for all code. If the flag |
+// --always-full-compiler is specified this is the case. For the virtual frame |
+// based compiler the full compiler is also used if a debugger is connected, as |
+// the code from the full compiler supports mode precise break points. For the |
+// crankshaft adaptive compiler debugging the optimized code is not possible at |
+// all. However crankshaft support recompilation of functions, so in this case |
+// the full compiler need not be be used if a debugger is attached, but only if |
+// break points has actually been set. |
+static bool IsDebuggerActive(Isolate* isolate) { |
+#ifdef ENABLE_DEBUGGER_SUPPORT |
+ return isolate->use_crankshaft() ? |
+ isolate->debug()->has_break_points() : |
+ isolate->debugger()->IsDebuggerActive(); |
+#else |
+ return false; |
+#endif |
+} |
+ |
+ |
+OptimizedCompileJob::Status OptimizedCompileJob::CreateGraph() { |
ASSERT(isolate()->use_crankshaft()); |
ASSERT(info()->IsOptimizing()); |
ASSERT(!info()->IsCompilingForDebugging()); |
@@ -376,18 +311,15 @@ RecompileJob::Status RecompileJob::CreateGraph() { |
// Fall back to using the full code generator if it's not possible |
// to use the Hydrogen-based optimizing compiler. We already have |
// generated code for this from the shared function object. |
- if (AlwaysFullCompiler(isolate())) { |
- info()->AbortOptimization(); |
- return SetLastStatus(BAILED_OUT); |
- } |
+ if (FLAG_always_full_compiler) return AbortOptimization(); |
+ if (IsDebuggerActive(isolate())) return AbortOptimization(kDebuggerIsActive); |
// Limit the number of times we re-compile a functions with |
// the optimizing compiler. |
const int kMaxOptCount = |
FLAG_deopt_every_n_times == 0 ? FLAG_max_opt_count : 1000; |
if (info()->opt_count() > kMaxOptCount) { |
- info()->set_bailout_reason(kOptimizedTooManyTimes); |
- return AbortOptimization(); |
+ return AbortAndDisableOptimization(kOptimizedTooManyTimes); |
} |
// Due to an encoding limit on LUnallocated operands in the Lithium |
@@ -400,21 +332,18 @@ RecompileJob::Status RecompileJob::CreateGraph() { |
const int parameter_limit = -LUnallocated::kMinFixedSlotIndex; |
Scope* scope = info()->scope(); |
if ((scope->num_parameters() + 1) > parameter_limit) { |
- info()->set_bailout_reason(kTooManyParameters); |
- return AbortOptimization(); |
+ return AbortAndDisableOptimization(kTooManyParameters); |
} |
const int locals_limit = LUnallocated::kMaxFixedSlotIndex; |
if (info()->is_osr() && |
scope->num_parameters() + 1 + scope->num_stack_slots() > locals_limit) { |
- info()->set_bailout_reason(kTooManyParametersLocals); |
- return AbortOptimization(); |
+ return AbortAndDisableOptimization(kTooManyParametersLocals); |
} |
// Take --hydrogen-filter into account. |
if (!info()->closure()->PassesFilter(FLAG_hydrogen_filter)) { |
- info()->AbortOptimization(); |
- return SetLastStatus(BAILED_OUT); |
+ return AbortOptimization(kHydrogenFilter); |
} |
// Recompile the unoptimized version of the code if the current version |
@@ -474,7 +403,6 @@ RecompileJob::Status RecompileJob::CreateGraph() { |
graph_ = graph_builder_->CreateGraph(); |
if (isolate()->has_pending_exception()) { |
- info()->SetCode(Handle<Code>::null()); |
return SetLastStatus(FAILED); |
} |
@@ -484,24 +412,21 @@ RecompileJob::Status RecompileJob::CreateGraph() { |
ASSERT(!graph_builder_->inline_bailout() || graph_ == NULL); |
if (graph_ == NULL) { |
if (graph_builder_->inline_bailout()) { |
- info_->AbortOptimization(); |
- return SetLastStatus(BAILED_OUT); |
- } else { |
return AbortOptimization(); |
+ } else { |
+ return AbortAndDisableOptimization(); |
} |
} |
if (info()->HasAbortedDueToDependencyChange()) { |
- info_->set_bailout_reason(kBailedOutDueToDependencyChange); |
- info_->AbortOptimization(); |
- return SetLastStatus(BAILED_OUT); |
+ return AbortOptimization(kBailedOutDueToDependencyChange); |
} |
return SetLastStatus(SUCCEEDED); |
} |
-RecompileJob::Status RecompileJob::OptimizeGraph() { |
+OptimizedCompileJob::Status OptimizedCompileJob::OptimizeGraph() { |
DisallowHeapAllocation no_allocation; |
DisallowHandleAllocation no_handles; |
DisallowHandleDereference no_deref; |
@@ -511,20 +436,19 @@ RecompileJob::Status RecompileJob::OptimizeGraph() { |
Timer t(this, &time_taken_to_optimize_); |
ASSERT(graph_ != NULL); |
BailoutReason bailout_reason = kNoReason; |
- if (!graph_->Optimize(&bailout_reason)) { |
- if (bailout_reason != kNoReason) graph_builder_->Bailout(bailout_reason); |
- return SetLastStatus(BAILED_OUT); |
- } else { |
+ |
+ if (graph_->Optimize(&bailout_reason)) { |
chunk_ = LChunk::NewChunk(graph_); |
- if (chunk_ == NULL) { |
- return SetLastStatus(BAILED_OUT); |
- } |
+ if (chunk_ != NULL) return SetLastStatus(SUCCEEDED); |
+ } else if (bailout_reason != kNoReason) { |
+ graph_builder_->Bailout(bailout_reason); |
} |
- return SetLastStatus(SUCCEEDED); |
+ |
+ return AbortOptimization(); |
} |
-RecompileJob::Status RecompileJob::GenerateAndInstallCode() { |
+OptimizedCompileJob::Status OptimizedCompileJob::GenerateCode() { |
ASSERT(last_status() == SUCCEEDED); |
ASSERT(!info()->HasAbortedDueToDependencyChange()); |
DisallowCodeDependencyChange no_dependency_change; |
@@ -540,9 +464,9 @@ RecompileJob::Status RecompileJob::GenerateAndInstallCode() { |
Handle<Code> optimized_code = chunk_->Codegen(); |
if (optimized_code.is_null()) { |
if (info()->bailout_reason() == kNoReason) { |
- info()->set_bailout_reason(kCodeGenerationFailed); |
+ info_->set_bailout_reason(kCodeGenerationFailed); |
} |
- return AbortOptimization(); |
+ return AbortAndDisableOptimization(); |
} |
info()->SetCode(optimized_code); |
} |
@@ -553,54 +477,40 @@ RecompileJob::Status RecompileJob::GenerateAndInstallCode() { |
} |
-static bool GenerateCode(CompilationInfo* info) { |
- bool is_optimizing = info->isolate()->use_crankshaft() && |
- !info->IsCompilingForDebugging() && |
- info->IsOptimizing(); |
- if (is_optimizing) { |
- Logger::TimerEventScope timer( |
- info->isolate(), Logger::TimerEventScope::v8_recompile_synchronous); |
- return MakeCrankshaftCode(info); |
- } else { |
- if (info->IsOptimizing()) { |
- // Have the CompilationInfo decide if the compilation should be |
- // BASE or NONOPT. |
- info->DisableOptimization(); |
- } |
- Logger::TimerEventScope timer( |
- info->isolate(), Logger::TimerEventScope::v8_compile_full_code); |
- return FullCodeGenerator::MakeCode(info); |
+void OptimizedCompileJob::RecordOptimizationStats() { |
+ 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); |
} |
-} |
- |
- |
-static bool MakeCode(CompilationInfo* info) { |
- // Precondition: code has been parsed. Postcondition: the code field in |
- // the compilation info is set if compilation succeeded. |
- ASSERT(info->function() != NULL); |
- return Rewriter::Rewrite(info) && Scope::Analyze(info) && GenerateCode(info); |
-} |
- |
- |
-#ifdef ENABLE_DEBUGGER_SUPPORT |
-bool Compiler::MakeCodeForLiveEdit(CompilationInfo* info) { |
- // Precondition: code has been parsed. Postcondition: the code field in |
- // the compilation info is set if compilation succeeded. |
- bool succeeded = MakeCode(info); |
- if (!info->shared_info().is_null()) { |
- Handle<ScopeInfo> scope_info = ScopeInfo::Create(info->scope(), |
- info->zone()); |
- info->shared_info()->set_scope_info(*scope_info); |
+ double ms_creategraph = time_taken_to_create_graph_.InMillisecondsF(); |
+ double ms_optimize = time_taken_to_optimize_.InMillisecondsF(); |
+ double ms_codegen = time_taken_to_codegen_.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); |
} |
- return succeeded; |
-} |
-#endif |
- |
+ if (FLAG_trace_opt_stats) { |
+ static double compilation_time = 0.0; |
+ static int compiled_functions = 0; |
+ static int code_size = 0; |
-static bool DebuggerWantsEagerCompilation(CompilationInfo* info, |
- bool allow_lazy_without_ctx = false) { |
- return LiveEditFunctionTracker::IsActive(info->isolate()) || |
- (info->isolate()->DebuggerHasBreakPoints() && !allow_lazy_without_ctx); |
+ 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_create_graph_, |
+ time_taken_to_optimize_, |
+ time_taken_to_codegen_); |
+ } |
} |
@@ -631,148 +541,378 @@ void SetExpectedNofPropertiesFromEstimate(Handle<SharedFunctionInfo> shared, |
} |
-static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) { |
- Isolate* isolate = info->isolate(); |
- PostponeInterruptsScope postpone(isolate); |
+static void UpdateSharedFunctionInfo(CompilationInfo* info) { |
+ // Update the shared function info with the compiled code and the |
+ // scope info. Please note, that the order of the shared function |
+ // info initialization is important since set_scope_info might |
+ // trigger a GC, causing the ASSERT below to be invalid if the code |
+ // was flushed. By setting the code object last we avoid this. |
+ Handle<SharedFunctionInfo> shared = info->shared_info(); |
+ Handle<ScopeInfo> scope_info = |
+ ScopeInfo::Create(info->scope(), info->zone()); |
+ shared->set_scope_info(*scope_info); |
- ASSERT(!isolate->native_context().is_null()); |
- Handle<Script> script = info->script(); |
- // TODO(svenpanne) Obscure place for this, perhaps move to OnBeforeCompile? |
- FixedArray* array = isolate->native_context()->embedder_data(); |
- script->set_context_data(array->get(0)); |
+ Handle<Code> code = info->code(); |
+ CHECK(code->kind() == Code::FUNCTION); |
+ shared->ReplaceCode(*code); |
+ if (shared->optimization_disabled()) code->set_optimizable(false); |
-#ifdef ENABLE_DEBUGGER_SUPPORT |
- if (info->is_eval()) { |
- script->set_compilation_type(Script::COMPILATION_TYPE_EVAL); |
- // For eval scripts add information on the function from which eval was |
- // called. |
- if (info->is_eval()) { |
- StackTraceFrameIterator it(isolate); |
- if (!it.done()) { |
- script->set_eval_from_shared(it.frame()->function()->shared()); |
- Code* code = it.frame()->LookupCode(); |
- int offset = static_cast<int>( |
- it.frame()->pc() - code->instruction_start()); |
- script->set_eval_from_instructions_offset(Smi::FromInt(offset)); |
- } |
- } |
- } |
+ // Set the expected number of properties for instances. |
+ FunctionLiteral* lit = info->function(); |
+ int expected = lit->expected_property_count(); |
+ SetExpectedNofPropertiesFromEstimate(shared, expected); |
- // Notify debugger |
- isolate->debugger()->OnBeforeCompile(script); |
-#endif |
+ // Check the function has compiled code. |
+ ASSERT(shared->is_compiled()); |
+ shared->set_dont_optimize_reason(lit->dont_optimize_reason()); |
+ shared->set_dont_inline(lit->flags()->Contains(kDontInline)); |
+ shared->set_ast_node_count(lit->ast_node_count()); |
+ shared->set_language_mode(lit->language_mode()); |
+} |
- // Only allow non-global compiles for eval. |
- ASSERT(info->is_eval() || info->is_global()); |
- { |
- Parser parser(info); |
- if ((info->pre_parse_data() != NULL || |
- String::cast(script->source())->length() > FLAG_min_preparse_length) && |
- !DebuggerWantsEagerCompilation(info)) |
- parser.set_allow_lazy(true); |
- if (!parser.Parse()) { |
- return Handle<SharedFunctionInfo>::null(); |
- } |
+ |
+// Sets the function info on a function. |
+// The start_position points to the first '(' character after the function name |
+// in the full script source. When counting characters in the script source the |
+// the first character is number 0 (not 1). |
+static void SetFunctionInfo(Handle<SharedFunctionInfo> function_info, |
+ FunctionLiteral* lit, |
+ bool is_toplevel, |
+ Handle<Script> script) { |
+ function_info->set_length(lit->parameter_count()); |
+ function_info->set_formal_parameter_count(lit->parameter_count()); |
+ function_info->set_script(*script); |
+ function_info->set_function_token_position(lit->function_token_position()); |
+ function_info->set_start_position(lit->start_position()); |
+ function_info->set_end_position(lit->end_position()); |
+ function_info->set_is_expression(lit->is_expression()); |
+ function_info->set_is_anonymous(lit->is_anonymous()); |
+ function_info->set_is_toplevel(is_toplevel); |
+ function_info->set_inferred_name(*lit->inferred_name()); |
+ function_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation()); |
+ function_info->set_allows_lazy_compilation_without_context( |
+ lit->AllowsLazyCompilationWithoutContext()); |
+ function_info->set_language_mode(lit->language_mode()); |
+ function_info->set_uses_arguments(lit->scope()->arguments() != NULL); |
+ function_info->set_has_duplicate_parameters(lit->has_duplicate_parameters()); |
+ function_info->set_ast_node_count(lit->ast_node_count()); |
+ function_info->set_is_function(lit->is_function()); |
+ function_info->set_dont_optimize_reason(lit->dont_optimize_reason()); |
+ function_info->set_dont_inline(lit->flags()->Contains(kDontInline)); |
+ function_info->set_dont_cache(lit->flags()->Contains(kDontCache)); |
+ function_info->set_is_generator(lit->is_generator()); |
+} |
+ |
+ |
+static bool CompileUnoptimizedCode(CompilationInfo* info) { |
+ ASSERT(info->function() != NULL); |
+ if (!Rewriter::Rewrite(info)) return false; |
+ if (!Scope::Analyze(info)) return false; |
+ ASSERT(info->scope() != NULL); |
+ |
+ if (!FullCodeGenerator::MakeCode(info)) { |
+ Isolate* isolate = info->isolate(); |
+ if (!isolate->has_pending_exception()) isolate->StackOverflow(); |
+ return false; |
} |
+ return true; |
+} |
- FunctionLiteral* lit = info->function(); |
- LiveEditFunctionTracker live_edit_tracker(isolate, lit); |
- Handle<SharedFunctionInfo> result; |
- { |
- // Measure how long it takes to do the compilation; only take the |
- // rest of the function into account to avoid overlap with the |
- // parsing statistics. |
- HistogramTimer* rate = info->is_eval() |
- ? info->isolate()->counters()->compile_eval() |
- : info->isolate()->counters()->compile(); |
- HistogramTimerScope timer(rate); |
- // Compile the code. |
- if (!MakeCode(info)) { |
- if (!isolate->has_pending_exception()) isolate->StackOverflow(); |
- return Handle<SharedFunctionInfo>::null(); |
- } |
+static Handle<Code> GetUnoptimizedCodeCommon(CompilationInfo* info) { |
+ VMState<COMPILER> state(info->isolate()); |
+ PostponeInterruptsScope postpone(info->isolate()); |
+ if (!Parser::Parse(info)) return Handle<Code>::null(); |
+ LanguageMode language_mode = info->function()->language_mode(); |
+ info->SetLanguageMode(language_mode); |
- // Allocate function. |
- ASSERT(!info->code().is_null()); |
- result = |
- isolate->factory()->NewSharedFunctionInfo( |
- lit->name(), |
- lit->materialized_literal_count(), |
- lit->is_generator(), |
- info->code(), |
- ScopeInfo::Create(info->scope(), info->zone())); |
+ if (!CompileUnoptimizedCode(info)) return Handle<Code>::null(); |
+ Compiler::RecordFunctionCompilation( |
+ Logger::LAZY_COMPILE_TAG, info, info->shared_info()); |
+ UpdateSharedFunctionInfo(info); |
+ ASSERT_EQ(Code::FUNCTION, info->code()->kind()); |
+ return info->code(); |
+} |
- ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position()); |
- Compiler::SetFunctionInfo(result, lit, true, script); |
- |
- if (script->name()->IsString()) { |
- PROFILE(isolate, CodeCreateEvent( |
- info->is_eval() |
- ? Logger::EVAL_TAG |
- : Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script), |
- *info->code(), |
- *result, |
- info, |
- String::cast(script->name()))); |
- GDBJIT(AddCode(Handle<String>(String::cast(script->name())), |
- script, |
- info->code(), |
- info)); |
- } else { |
- PROFILE(isolate, CodeCreateEvent( |
- info->is_eval() |
- ? Logger::EVAL_TAG |
- : Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script), |
- *info->code(), |
- *result, |
- info, |
- isolate->heap()->empty_string())); |
- GDBJIT(AddCode(Handle<String>(), script, info->code(), info)); |
- } |
- // Hint to the runtime system used when allocating space for initial |
- // property space by setting the expected number of properties for |
- // the instances of the function. |
- SetExpectedNofPropertiesFromEstimate(result, |
- lit->expected_property_count()); |
+Handle<Code> Compiler::GetUnoptimizedCode(Handle<JSFunction> function) { |
+ ASSERT(!function->GetIsolate()->has_pending_exception()); |
+ ASSERT(!function->is_compiled()); |
+ if (function->shared()->is_compiled()) { |
+ return Handle<Code>(function->shared()->code()); |
+ } |
- script->set_compilation_state(Script::COMPILATION_STATE_COMPILED); |
+ CompilationInfoWithZone info(function); |
+ Handle<Code> result = GetUnoptimizedCodeCommon(&info); |
+ ASSERT_EQ(result.is_null(), info.isolate()->has_pending_exception()); |
+ |
+ if (FLAG_always_opt && |
+ info.isolate()->use_crankshaft() && |
+ !info.shared_info()->optimization_disabled() && |
+ !info.isolate()->DebuggerHasBreakPoints()) { |
+ Handle<Code> opt_code = Compiler::GetOptimizedCode( |
+ function, result, Compiler::NOT_CONCURRENT); |
+ if (!opt_code.is_null()) result = opt_code; |
} |
-#ifdef ENABLE_DEBUGGER_SUPPORT |
- // Notify debugger |
- isolate->debugger()->OnAfterCompile( |
- script, Debugger::NO_AFTER_COMPILE_FLAGS); |
-#endif |
+ return result; |
+} |
- live_edit_tracker.RecordFunctionInfo(result, lit, info->zone()); |
+Handle<Code> Compiler::GetUnoptimizedCode(Handle<SharedFunctionInfo> shared) { |
+ ASSERT(!shared->GetIsolate()->has_pending_exception()); |
+ ASSERT(!shared->is_compiled()); |
+ |
+ CompilationInfoWithZone info(shared); |
+ Handle<Code> result = GetUnoptimizedCodeCommon(&info); |
+ ASSERT_EQ(result.is_null(), info.isolate()->has_pending_exception()); |
return result; |
} |
-Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, |
- Handle<Object> script_name, |
- int line_offset, |
- int column_offset, |
- bool is_shared_cross_origin, |
- Handle<Context> context, |
- v8::Extension* extension, |
- ScriptDataImpl* pre_data, |
- Handle<Object> script_data, |
- NativesFlag natives) { |
- Isolate* isolate = source->GetIsolate(); |
- int source_length = source->length(); |
- isolate->counters()->total_load_size()->Increment(source_length); |
- isolate->counters()->total_compile_size()->Increment(source_length); |
+bool Compiler::EnsureCompiled(Handle<JSFunction> function, |
+ ClearExceptionFlag flag) { |
+ if (function->is_compiled()) return true; |
+ Handle<Code> code = Compiler::GetUnoptimizedCode(function); |
+ if (code.is_null()) { |
+ if (flag == CLEAR_EXCEPTION) { |
+ function->GetIsolate()->clear_pending_exception(); |
+ } |
+ return false; |
+ } |
+ function->ReplaceCode(*code); |
+ ASSERT(function->is_compiled()); |
+ return true; |
+} |
- // The VM is in the COMPILER state until exiting this function. |
+ |
+// Compile full code for debugging. This code will have debug break slots |
+// and deoptimization information. Deoptimization information is required |
+// in case that an optimized version of this function is still activated on |
+// the stack. It will also make sure that the full code is compiled with |
+// the same flags as the previous version, that is flags which can change |
+// the code generated. The current method of mapping from already compiled |
+// full code without debug break slots to full code with debug break slots |
+// depends on the generated code is otherwise exactly the same. |
+// If compilation fails, just keep the existing code. |
+Handle<Code> Compiler::GetCodeForDebugging(Handle<JSFunction> function) { |
+ CompilationInfoWithZone info(function); |
+ Isolate* isolate = info.isolate(); |
VMState<COMPILER> state(isolate); |
- CompilationCache* compilation_cache = isolate->compilation_cache(); |
+ ASSERT(!isolate->has_pending_exception()); |
+ Handle<Code> old_code(function->shared()->code()); |
+ ASSERT(old_code->kind() == Code::FUNCTION); |
+ ASSERT(!old_code->has_debug_break_slots()); |
+ |
+ info.MarkCompilingForDebugging(); |
+ if (old_code->is_compiled_optimizable()) { |
+ info.EnableDeoptimizationSupport(); |
+ } else { |
+ info.MarkNonOptimizable(); |
+ } |
+ Handle<Code> new_code = GetUnoptimizedCodeCommon(&info); |
+ if (new_code.is_null()) { |
+ isolate->clear_pending_exception(); |
+ } else { |
+ ASSERT_EQ(old_code->is_compiled_optimizable(), |
+ new_code->is_compiled_optimizable()); |
+ } |
+ return new_code; |
+} |
+ |
+ |
+#ifdef ENABLE_DEBUGGER_SUPPORT |
+void Compiler::CompileForLiveEdit(Handle<Script> script) { |
+ // TODO(635): support extensions. |
+ CompilationInfoWithZone info(script); |
+ VMState<COMPILER> state(info.isolate()); |
+ |
+ info.MarkAsGlobal(); |
+ if (!Parser::Parse(&info)) return; |
+ LanguageMode language_mode = info.function()->language_mode(); |
+ info.SetLanguageMode(language_mode); |
+ |
+ LiveEditFunctionTracker tracker(info.isolate(), info.function()); |
+ if (!CompileUnoptimizedCode(&info)) return; |
+ if (!info.shared_info().is_null()) { |
+ Handle<ScopeInfo> scope_info = ScopeInfo::Create(info.scope(), |
+ info.zone()); |
+ info.shared_info()->set_scope_info(*scope_info); |
+ } |
+ tracker.RecordRootFunctionInfo(info.code()); |
+} |
+#endif |
+ |
+ |
+static bool DebuggerWantsEagerCompilation(CompilationInfo* info, |
+ bool allow_lazy_without_ctx = false) { |
+ return LiveEditFunctionTracker::IsActive(info->isolate()) || |
+ (info->isolate()->DebuggerHasBreakPoints() && !allow_lazy_without_ctx); |
+} |
+ |
+ |
+static Handle<SharedFunctionInfo> CompileToplevel(CompilationInfo* info) { |
+ Isolate* isolate = info->isolate(); |
+ ASSERT(!isolate->native_context().is_null()); |
+ Handle<Script> script = info->script(); |
+ |
+ // TODO(svenpanne) Obscure place for this, perhaps move to OnBeforeCompile? |
+ FixedArray* array = isolate->native_context()->embedder_data(); |
+ script->set_context_data(array->get(0)); |
+ |
+#ifdef ENABLE_DEBUGGER_SUPPORT |
+ isolate->debugger()->OnBeforeCompile(script); |
+#endif |
+ |
+ ASSERT(info->is_eval() || info->is_global()); |
+ |
+ bool parse_allow_lazy = |
+ (info->pre_parse_data() != NULL || |
+ String::cast(script->source())->length() > FLAG_min_preparse_length) && |
+ !DebuggerWantsEagerCompilation(info); |
- // Do a lookup in the compilation cache but not for extensions. |
+ Handle<SharedFunctionInfo> result; |
+ |
+ { VMState<COMPILER> state(info->isolate()); |
+ if (!Parser::Parse(info, parse_allow_lazy)) { |
+ return Handle<SharedFunctionInfo>::null(); |
+ } |
+ |
+ FunctionLiteral* lit = info->function(); |
+ LiveEditFunctionTracker live_edit_tracker(isolate, lit); |
+ |
+ // Measure how long it takes to do the compilation; only take the |
+ // rest of the function into account to avoid overlap with the |
+ // parsing statistics. |
+ HistogramTimer* rate = info->is_eval() |
+ ? info->isolate()->counters()->compile_eval() |
+ : info->isolate()->counters()->compile(); |
+ HistogramTimerScope timer(rate); |
+ |
+ // Compile the code. |
+ if (!CompileUnoptimizedCode(info)) { |
+ return Handle<SharedFunctionInfo>::null(); |
+ } |
+ |
+ // Allocate function. |
+ ASSERT(!info->code().is_null()); |
+ result = isolate->factory()->NewSharedFunctionInfo( |
+ lit->name(), |
+ lit->materialized_literal_count(), |
+ lit->is_generator(), |
+ info->code(), |
+ ScopeInfo::Create(info->scope(), info->zone())); |
+ |
+ ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position()); |
+ SetFunctionInfo(result, lit, true, script); |
+ |
+ Handle<String> script_name = script->name()->IsString() |
+ ? Handle<String>(String::cast(script->name())) |
+ : isolate->factory()->empty_string(); |
+ Logger::LogEventsAndTags log_tag = info->is_eval() |
+ ? Logger::EVAL_TAG |
+ : Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script); |
+ |
+ PROFILE(isolate, CodeCreateEvent( |
+ log_tag, *info->code(), *result, info, *script_name)); |
+ GDBJIT(AddCode(script_name, script, info->code(), info)); |
+ |
+ // Hint to the runtime system used when allocating space for initial |
+ // property space by setting the expected number of properties for |
+ // the instances of the function. |
+ SetExpectedNofPropertiesFromEstimate(result, |
+ lit->expected_property_count()); |
+ |
+ script->set_compilation_state(Script::COMPILATION_STATE_COMPILED); |
+ |
+ live_edit_tracker.RecordFunctionInfo(result, lit, info->zone()); |
+ } |
+ |
+#ifdef ENABLE_DEBUGGER_SUPPORT |
+ isolate->debugger()->OnAfterCompile(script, Debugger::NO_AFTER_COMPILE_FLAGS); |
+#endif |
+ |
+ return result; |
+} |
+ |
+ |
+Handle<JSFunction> Compiler::GetFunctionFromEval(Handle<String> source, |
+ Handle<Context> context, |
+ LanguageMode language_mode, |
+ ParseRestriction restriction, |
+ int scope_position) { |
+ Isolate* isolate = source->GetIsolate(); |
+ int source_length = source->length(); |
+ isolate->counters()->total_eval_size()->Increment(source_length); |
+ isolate->counters()->total_compile_size()->Increment(source_length); |
+ |
+ CompilationCache* compilation_cache = isolate->compilation_cache(); |
+ Handle<SharedFunctionInfo> shared_info = compilation_cache->LookupEval( |
+ source, context, language_mode, scope_position); |
+ |
+ if (shared_info.is_null()) { |
+ Handle<Script> script = isolate->factory()->NewScript(source); |
+ CompilationInfoWithZone info(script); |
+ info.MarkAsEval(); |
+ if (context->IsNativeContext()) info.MarkAsGlobal(); |
+ info.SetLanguageMode(language_mode); |
+ info.SetParseRestriction(restriction); |
+ info.SetContext(context); |
+ |
+#if ENABLE_DEBUGGER_SUPPORT |
+ Debug::RecordEvalCaller(script); |
+#endif // ENABLE_DEBUGGER_SUPPORT |
+ |
+ shared_info = CompileToplevel(&info); |
+ |
+ if (shared_info.is_null()) { |
+ return Handle<JSFunction>::null(); |
+ } else { |
+ // Explicitly disable optimization for eval code. We're not yet prepared |
+ // to handle eval-code in the optimizing compiler. |
+ shared_info->DisableOptimization(kEval); |
+ |
+ // If caller is strict mode, the result must be in strict mode or |
+ // extended mode as well, but not the other way around. Consider: |
+ // eval("'use strict'; ..."); |
+ ASSERT(language_mode != STRICT_MODE || !shared_info->is_classic_mode()); |
+ // If caller is in extended mode, the result must also be in |
+ // extended mode. |
+ ASSERT(language_mode != EXTENDED_MODE || |
+ shared_info->is_extended_mode()); |
+ if (!shared_info->dont_cache()) { |
+ compilation_cache->PutEval( |
+ source, context, shared_info, scope_position); |
+ } |
+ } |
+ } else if (shared_info->ic_age() != isolate->heap()->global_ic_age()) { |
+ shared_info->ResetForNewContext(isolate->heap()->global_ic_age()); |
+ } |
+ |
+ return isolate->factory()->NewFunctionFromSharedFunctionInfo( |
+ shared_info, context, NOT_TENURED); |
+} |
+ |
+ |
+Handle<SharedFunctionInfo> Compiler::CompileScript(Handle<String> source, |
+ Handle<Object> script_name, |
+ int line_offset, |
+ int column_offset, |
+ bool is_shared_cross_origin, |
+ Handle<Context> context, |
+ v8::Extension* extension, |
+ ScriptDataImpl* pre_data, |
+ Handle<Object> script_data, |
+ NativesFlag natives) { |
+ Isolate* isolate = source->GetIsolate(); |
+ int source_length = source->length(); |
+ isolate->counters()->total_load_size()->Increment(source_length); |
+ isolate->counters()->total_compile_size()->Increment(source_length); |
+ |
+ CompilationCache* compilation_cache = isolate->compilation_cache(); |
+ |
+ // Do a lookup in the compilation cache but not for extensions. |
Handle<SharedFunctionInfo> result; |
if (extension == NULL) { |
result = compilation_cache->LookupScript(source, |
@@ -817,14 +957,12 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, |
if (FLAG_use_strict) { |
info.SetLanguageMode(FLAG_harmony_scoping ? EXTENDED_MODE : STRICT_MODE); |
} |
- result = MakeFunctionInfo(&info); |
+ result = CompileToplevel(&info); |
if (extension == NULL && !result.is_null() && !result->dont_cache()) { |
compilation_cache->PutScript(source, context, result); |
} |
- } else { |
- if (result->ic_age() != isolate->heap()->global_ic_age()) { |
+ } else if (result->ic_age() != isolate->heap()->global_ic_age()) { |
result->ResetForNewContext(isolate->heap()->global_ic_age()); |
- } |
} |
if (result.is_null()) isolate->ReportPendingMessages(); |
@@ -832,130 +970,82 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, |
} |
-Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source, |
- Handle<Context> context, |
- bool is_global, |
- LanguageMode language_mode, |
- ParseRestriction restriction, |
- int scope_position) { |
- Isolate* isolate = source->GetIsolate(); |
- int source_length = source->length(); |
- isolate->counters()->total_eval_size()->Increment(source_length); |
- isolate->counters()->total_compile_size()->Increment(source_length); |
- |
- // The VM is in the COMPILER state until exiting this function. |
- VMState<COMPILER> state(isolate); |
- |
- // Do a lookup in the compilation cache; if the entry is not there, invoke |
- // the compiler and add the result to the cache. |
- Handle<SharedFunctionInfo> result; |
- CompilationCache* compilation_cache = isolate->compilation_cache(); |
- result = compilation_cache->LookupEval(source, |
- context, |
- is_global, |
- language_mode, |
- scope_position); |
+Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal, |
+ Handle<Script> script) { |
+ // Precondition: code has been parsed and scopes have been analyzed. |
+ CompilationInfoWithZone info(script); |
+ info.SetFunction(literal); |
+ info.SetScope(literal->scope()); |
+ info.SetLanguageMode(literal->scope()->language_mode()); |
- if (result.is_null()) { |
- // Create a script object describing the script to be compiled. |
- Handle<Script> script = isolate->factory()->NewScript(source); |
- CompilationInfoWithZone info(script); |
- info.MarkAsEval(); |
- if (is_global) info.MarkAsGlobal(); |
- info.SetLanguageMode(language_mode); |
- info.SetParseRestriction(restriction); |
- info.SetContext(context); |
- result = MakeFunctionInfo(&info); |
- if (!result.is_null()) { |
- // Explicitly disable optimization for eval code. We're not yet prepared |
- // to handle eval-code in the optimizing compiler. |
- result->DisableOptimization(kEval); |
+ Isolate* isolate = info.isolate(); |
+ Factory* factory = isolate->factory(); |
+ LiveEditFunctionTracker live_edit_tracker(isolate, literal); |
+ // Determine if the function can be lazily compiled. This is necessary to |
+ // allow some of our builtin JS files to be lazily compiled. These |
+ // builtins cannot be handled lazily by the parser, since we have to know |
+ // if a function uses the special natives syntax, which is something the |
+ // parser records. |
+ // If the debugger requests compilation for break points, we cannot be |
+ // aggressive about lazy compilation, because it might trigger compilation |
+ // of functions without an outer context when setting a breakpoint through |
+ // Debug::FindSharedFunctionInfoInScript. |
+ bool allow_lazy_without_ctx = literal->AllowsLazyCompilationWithoutContext(); |
+ bool allow_lazy = literal->AllowsLazyCompilation() && |
+ !DebuggerWantsEagerCompilation(&info, allow_lazy_without_ctx); |
- // If caller is strict mode, the result must be in strict mode or |
- // extended mode as well, but not the other way around. Consider: |
- // eval("'use strict'; ..."); |
- ASSERT(language_mode != STRICT_MODE || !result->is_classic_mode()); |
- // If caller is in extended mode, the result must also be in |
- // extended mode. |
- ASSERT(language_mode != EXTENDED_MODE || |
- result->is_extended_mode()); |
- if (!result->dont_cache()) { |
- compilation_cache->PutEval( |
- source, context, is_global, result, scope_position); |
- } |
- } |
+ // Generate code |
+ Handle<ScopeInfo> scope_info; |
+ if (FLAG_lazy && allow_lazy && !literal->is_parenthesized()) { |
+ Handle<Code> code = isolate->builtins()->CompileUnoptimized(); |
+ info.SetCode(code); |
+ scope_info = Handle<ScopeInfo>(ScopeInfo::Empty(isolate)); |
+ } else if (FullCodeGenerator::MakeCode(&info)) { |
+ ASSERT(!info.code().is_null()); |
+ scope_info = ScopeInfo::Create(info.scope(), info.zone()); |
} else { |
- if (result->ic_age() != isolate->heap()->global_ic_age()) { |
- result->ResetForNewContext(isolate->heap()->global_ic_age()); |
- } |
+ return Handle<SharedFunctionInfo>::null(); |
} |
+ // Create a shared function info object. |
+ Handle<SharedFunctionInfo> result = |
+ factory->NewSharedFunctionInfo(literal->name(), |
+ literal->materialized_literal_count(), |
+ literal->is_generator(), |
+ info.code(), |
+ scope_info); |
+ SetFunctionInfo(result, literal, false, script); |
+ RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, result); |
+ result->set_allows_lazy_compilation(allow_lazy); |
+ result->set_allows_lazy_compilation_without_context(allow_lazy_without_ctx); |
+ |
+ // Set the expected number of properties for instances and return |
+ // the resulting function. |
+ SetExpectedNofPropertiesFromEstimate(result, |
+ literal->expected_property_count()); |
+ live_edit_tracker.RecordFunctionInfo(result, literal, info.zone()); |
return result; |
} |
-static bool InstallFullCode(CompilationInfo* info) { |
- // Update the shared function info with the compiled code and the |
- // scope info. Please note, that the order of the shared function |
- // info initialization is important since set_scope_info might |
- // trigger a GC, causing the ASSERT below to be invalid if the code |
- // was flushed. By setting the code object last we avoid this. |
- Handle<SharedFunctionInfo> shared = info->shared_info(); |
- Handle<Code> code = info->code(); |
- CHECK(code->kind() == Code::FUNCTION); |
- Handle<JSFunction> function = info->closure(); |
- Handle<ScopeInfo> scope_info = |
- ScopeInfo::Create(info->scope(), info->zone()); |
- shared->set_scope_info(*scope_info); |
- shared->ReplaceCode(*code); |
- if (!function.is_null()) { |
- function->ReplaceCode(*code); |
- ASSERT(!function->IsOptimized()); |
- } |
- |
- // Set the expected number of properties for instances. |
- FunctionLiteral* lit = info->function(); |
- int expected = lit->expected_property_count(); |
- SetExpectedNofPropertiesFromEstimate(shared, expected); |
- |
- // Check the function has compiled code. |
- ASSERT(shared->is_compiled()); |
- shared->set_dont_optimize_reason(lit->dont_optimize_reason()); |
- shared->set_dont_inline(lit->flags()->Contains(kDontInline)); |
- shared->set_ast_node_count(lit->ast_node_count()); |
- |
- if (info->isolate()->use_crankshaft() && |
- !function.is_null() && |
- !shared->optimization_disabled()) { |
- // If we're asked to always optimize, we compile the optimized |
- // version of the function right away - unless the debugger is |
- // active as it makes no sense to compile optimized code then. |
- if (FLAG_always_opt && |
- !info->isolate()->DebuggerHasBreakPoints()) { |
- CompilationInfoWithZone optimized(function); |
- optimized.SetOptimizing(BailoutId::None()); |
- return Compiler::CompileLazy(&optimized); |
+static Handle<Code> GetCodeFromOptimizedCodeMap(Handle<JSFunction> function) { |
+ if (FLAG_cache_optimized_code) { |
+ Handle<SharedFunctionInfo> shared(function->shared()); |
+ DisallowHeapAllocation no_gc; |
+ int index = shared->SearchOptimizedCodeMap( |
+ function->context()->native_context()); |
+ if (index > 0) { |
+ if (FLAG_trace_opt) { |
+ PrintF("[found optimized code for "); |
+ function->ShortPrint(); |
+ PrintF("]\n"); |
+ } |
+ FixedArray* literals = shared->GetLiteralsFromOptimizedCodeMap(index); |
+ if (literals != NULL) function->set_literals(literals); |
+ return Handle<Code>(shared->GetCodeFromOptimizedCodeMap(index)); |
} |
} |
- return true; |
-} |
- |
- |
-static void InstallCodeCommon(CompilationInfo* info) { |
- Handle<SharedFunctionInfo> shared = info->shared_info(); |
- Handle<Code> code = info->code(); |
- ASSERT(!code.is_null()); |
- |
- // Set optimizable to false if this is disallowed by the shared |
- // function info, e.g., we might have flushed the code and must |
- // reset this bit when lazy compiling the code again. |
- if (shared->optimization_disabled()) code->set_optimizable(false); |
- |
- if (shared->code() == *code) { |
- // Do not send compilation event for the same code twice. |
- return; |
- } |
- Compiler::RecordFunctionCompilation(Logger::LAZY_COMPILE_TAG, info, shared); |
+ return Handle<Code>::null(); |
} |
@@ -975,317 +1065,162 @@ static void InsertCodeIntoOptimizedCodeMap(CompilationInfo* info) { |
} |
-static bool InstallCodeFromOptimizedCodeMap(CompilationInfo* info) { |
- if (!info->IsOptimizing()) return false; // Nothing to look up. |
+static bool CompileOptimizedPrologue(CompilationInfo* info) { |
+ if (!Parser::Parse(info)) return false; |
+ LanguageMode language_mode = info->function()->language_mode(); |
+ info->SetLanguageMode(language_mode); |
- // Lookup non-OSR optimized code. |
- if (FLAG_cache_optimized_code && !info->is_osr()) { |
- Handle<SharedFunctionInfo> shared = info->shared_info(); |
- Handle<JSFunction> function = info->closure(); |
- ASSERT(!function.is_null()); |
- Handle<Context> native_context(function->context()->native_context()); |
- int index = shared->SearchOptimizedCodeMap(*native_context); |
- if (index > 0) { |
- if (FLAG_trace_opt) { |
- PrintF("[found optimized code for "); |
- function->ShortPrint(); |
- PrintF("]\n"); |
- } |
- // Caching of optimized code enabled and optimized code found. |
- shared->InstallFromOptimizedCodeMap(*function, index); |
- return true; |
- } |
- } |
- return false; |
+ if (!Rewriter::Rewrite(info)) return false; |
+ if (!Scope::Analyze(info)) return false; |
+ ASSERT(info->scope() != NULL); |
+ return true; |
} |
-bool Compiler::CompileLazy(CompilationInfo* info) { |
- Isolate* isolate = info->isolate(); |
- |
- // The VM is in the COMPILER state until exiting this function. |
- VMState<COMPILER> state(isolate); |
- |
- PostponeInterruptsScope postpone(isolate); |
- |
- Handle<SharedFunctionInfo> shared = info->shared_info(); |
- int compiled_size = shared->end_position() - shared->start_position(); |
- isolate->counters()->total_compile_size()->Increment(compiled_size); |
- |
- if (InstallCodeFromOptimizedCodeMap(info)) return true; |
- |
- // Generate the AST for the lazily compiled function. |
- if (Parser::Parse(info)) { |
- // Measure how long it takes to do the lazy compilation; only take the |
- // rest of the function into account to avoid overlap with the lazy |
- // parsing statistics. |
- HistogramTimerScope timer(isolate->counters()->compile_lazy()); |
- |
- // After parsing we know the function's language mode. Remember it. |
- LanguageMode language_mode = info->function()->language_mode(); |
- info->SetLanguageMode(language_mode); |
- shared->set_language_mode(language_mode); |
+static bool GetOptimizedCodeNow(CompilationInfo* info) { |
+ if (!CompileOptimizedPrologue(info)) return false; |
- // Compile the code. |
- if (!MakeCode(info)) { |
- if (!isolate->has_pending_exception()) { |
- isolate->StackOverflow(); |
- } |
- } else { |
- InstallCodeCommon(info); |
- |
- if (info->IsOptimizing()) { |
- // Optimized code successfully created. |
- Handle<Code> code = info->code(); |
- ASSERT(shared->scope_info() != ScopeInfo::Empty(isolate)); |
- // TODO(titzer): Only replace the code if it was not an OSR compile. |
- info->closure()->ReplaceCode(*code); |
- InsertCodeIntoOptimizedCodeMap(info); |
- return true; |
- } else if (!info->is_osr()) { |
- // Compilation failed. Replace with full code if not OSR compile. |
- return InstallFullCode(info); |
- } |
- } |
- } |
- |
- ASSERT(info->code().is_null()); |
- return false; |
+ Logger::TimerEventScope timer( |
+ info->isolate(), Logger::TimerEventScope::v8_recompile_synchronous); |
+ |
+ OptimizedCompileJob job(info); |
+ if (job.CreateGraph() != OptimizedCompileJob::SUCCEEDED) return false; |
+ if (job.OptimizeGraph() != OptimizedCompileJob::SUCCEEDED) return false; |
+ if (job.GenerateCode() != OptimizedCompileJob::SUCCEEDED) return false; |
+ |
+ // Success! |
+ ASSERT(!info->isolate()->has_pending_exception()); |
+ InsertCodeIntoOptimizedCodeMap(info); |
+ Compiler::RecordFunctionCompilation( |
+ Logger::LAZY_COMPILE_TAG, info, info->shared_info()); |
+ return true; |
} |
-bool Compiler::RecompileConcurrent(Handle<JSFunction> closure, |
- Handle<Code> unoptimized, |
- uint32_t osr_pc_offset) { |
- bool compiling_for_osr = (osr_pc_offset != 0); |
- |
- Isolate* isolate = closure->GetIsolate(); |
- // Here we prepare compile data for the concurrent recompilation thread, but |
- // this still happens synchronously and interrupts execution. |
- Logger::TimerEventScope timer( |
- isolate, Logger::TimerEventScope::v8_recompile_synchronous); |
- |
+static bool GetOptimizedCodeLater(CompilationInfo* info) { |
+ Isolate* isolate = info->isolate(); |
if (!isolate->optimizing_compiler_thread()->IsQueueAvailable()) { |
if (FLAG_trace_concurrent_recompilation) { |
PrintF(" ** Compilation queue full, will retry optimizing "); |
- closure->PrintName(); |
- PrintF(" on next run.\n"); |
+ info->closure()->PrintName(); |
+ PrintF(" later.\n"); |
} |
return false; |
} |
- SmartPointer<CompilationInfo> info(new CompilationInfoWithZone(closure)); |
- Handle<SharedFunctionInfo> shared = info->shared_info(); |
+ CompilationHandleScope handle_scope(info); |
+ if (!CompileOptimizedPrologue(info)) return false; |
+ info->SaveHandles(); // Copy handles to the compilation handle scope. |
+ |
+ Logger::TimerEventScope timer( |
+ isolate, Logger::TimerEventScope::v8_recompile_synchronous); |
- if (compiling_for_osr) { |
- BailoutId osr_ast_id = unoptimized->TranslatePcOffsetToAstId(osr_pc_offset); |
- ASSERT(!osr_ast_id.IsNone()); |
- info->SetOptimizing(osr_ast_id); |
- info->SetOsrInfo(unoptimized, osr_pc_offset); |
+ OptimizedCompileJob* job = new(info->zone()) OptimizedCompileJob(info); |
+ OptimizedCompileJob::Status status = job->CreateGraph(); |
+ if (status != OptimizedCompileJob::SUCCEEDED) return false; |
+ isolate->optimizing_compiler_thread()->QueueForOptimization(job); |
- if (FLAG_trace_osr) { |
- PrintF("[COSR - attempt to queue "); |
- closure->PrintName(); |
- PrintF(" at AST id %d]\n", osr_ast_id.ToInt()); |
+ if (FLAG_trace_concurrent_recompilation) { |
+ PrintF(" ** Queued "); |
+ info->closure()->PrintName(); |
+ if (info->is_osr()) { |
+ PrintF(" for concurrent OSR at %d.\n", info->osr_ast_id().ToInt()); |
+ } else { |
+ PrintF(" for concurrent optimization.\n"); |
} |
- } else { |
- info->SetOptimizing(BailoutId::None()); |
} |
+ return true; |
+} |
+ |
+Handle<Code> Compiler::GetOptimizedCode(Handle<JSFunction> function, |
+ Handle<Code> current_code, |
+ ConcurrencyMode mode, |
+ BailoutId osr_ast_id) { |
+ if (osr_ast_id.IsNone()) { // No cache for OSR. |
+ Handle<Code> cached_code = GetCodeFromOptimizedCodeMap(function); |
+ if (!cached_code.is_null()) return cached_code; |
+ } |
+ |
+ SmartPointer<CompilationInfo> info(new CompilationInfoWithZone(function)); |
+ Isolate* isolate = info->isolate(); |
VMState<COMPILER> state(isolate); |
+ ASSERT(!isolate->has_pending_exception()); |
PostponeInterruptsScope postpone(isolate); |
+ Handle<SharedFunctionInfo> shared = info->shared_info(); |
+ ASSERT_NE(ScopeInfo::Empty(isolate), shared->scope_info()); |
int compiled_size = shared->end_position() - shared->start_position(); |
isolate->counters()->total_compile_size()->Increment(compiled_size); |
+ current_code->set_profiler_ticks(0); |
- { |
- CompilationHandleScope handle_scope(info.get()); |
+ info->SetOptimizing(osr_ast_id, current_code); |
- if (!compiling_for_osr && InstallCodeFromOptimizedCodeMap(info.get())) { |
- return true; |
+ if (mode == CONCURRENT) { |
+ if (GetOptimizedCodeLater(info.get())) { |
+ info.Detach(); // The background recompile job owns this now. |
+ return isolate->builtins()->InOptimizationQueue(); |
} |
+ } else { |
+ if (GetOptimizedCodeNow(info.get())) return info->code(); |
+ } |
- if (Parser::Parse(info.get())) { |
- LanguageMode language_mode = info->function()->language_mode(); |
- info->SetLanguageMode(language_mode); |
- shared->set_language_mode(language_mode); |
- info->SaveHandles(); |
- |
- if (Rewriter::Rewrite(info.get()) && Scope::Analyze(info.get())) { |
- RecompileJob* job = new(info->zone()) RecompileJob(info.get()); |
- RecompileJob::Status status = job->CreateGraph(); |
- if (status == RecompileJob::SUCCEEDED) { |
- info.Detach(); |
- unoptimized->set_profiler_ticks(0); |
- isolate->optimizing_compiler_thread()->QueueForOptimization(job); |
- ASSERT(!isolate->has_pending_exception()); |
- return true; |
- } else if (status == RecompileJob::BAILED_OUT) { |
- isolate->clear_pending_exception(); |
- InstallFullCode(info.get()); |
- } |
- } |
- } |
+ // Failed. |
+ if (FLAG_trace_opt) { |
+ PrintF("[failed to optimize "); |
+ function->PrintName(); |
+ PrintF("]\n"); |
} |
if (isolate->has_pending_exception()) isolate->clear_pending_exception(); |
- return false; |
+ return Handle<Code>::null(); |
} |
-Handle<Code> Compiler::InstallOptimizedCode(RecompileJob* job) { |
+Handle<Code> Compiler::GetConcurrentlyOptimizedCode(OptimizedCompileJob* job) { |
+ // Take ownership of compilation info. Deleting compilation info |
+ // also tears down the zone and the recompile job. |
SmartPointer<CompilationInfo> info(job->info()); |
- // The function may have already been optimized by OSR. Simply continue. |
- // Except when OSR already disabled optimization for some reason. |
- if (info->shared_info()->optimization_disabled()) { |
- info->AbortOptimization(); |
- InstallFullCode(info.get()); |
- if (FLAG_trace_concurrent_recompilation) { |
- PrintF(" ** aborting optimization for "); |
- info->closure()->PrintName(); |
- PrintF(" as it has been disabled.\n"); |
- } |
- ASSERT(!info->closure()->IsInRecompileQueue()); |
- return Handle<Code>::null(); |
- } |
- |
Isolate* isolate = info->isolate(); |
+ |
VMState<COMPILER> state(isolate); |
Logger::TimerEventScope timer( |
isolate, Logger::TimerEventScope::v8_recompile_synchronous); |
- // If crankshaft succeeded, install the optimized code else install |
- // the unoptimized code. |
- RecompileJob::Status status = job->last_status(); |
- if (info->HasAbortedDueToDependencyChange()) { |
- info->set_bailout_reason(kBailedOutDueToDependencyChange); |
- status = job->AbortOptimization(); |
- } else if (status != RecompileJob::SUCCEEDED) { |
- info->set_bailout_reason(kFailedBailedOutLastTime); |
- status = job->AbortOptimization(); |
- } else if (isolate->DebuggerHasBreakPoints()) { |
- info->set_bailout_reason(kDebuggerIsActive); |
- status = job->AbortOptimization(); |
- } else { |
- status = job->GenerateAndInstallCode(); |
- ASSERT(status == RecompileJob::SUCCEEDED || |
- status == RecompileJob::BAILED_OUT); |
- } |
- InstallCodeCommon(info.get()); |
- if (status == RecompileJob::SUCCEEDED) { |
- Handle<Code> code = info->code(); |
- ASSERT(info->shared_info()->scope_info() != ScopeInfo::Empty(isolate)); |
- info->closure()->ReplaceCode(*code); |
- if (info->shared_info()->SearchOptimizedCodeMap( |
- info->closure()->context()->native_context()) == -1) { |
- InsertCodeIntoOptimizedCodeMap(info.get()); |
- } |
- if (FLAG_trace_concurrent_recompilation) { |
- PrintF(" ** Optimized code for "); |
- info->closure()->PrintName(); |
- PrintF(" installed.\n"); |
- } |
- } else { |
- info->AbortOptimization(); |
- InstallFullCode(info.get()); |
+ Handle<SharedFunctionInfo> shared = info->shared_info(); |
+ shared->code()->set_profiler_ticks(0); |
+ |
+ // 1) Optimization 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) Debugger may have been activated. |
+ |
+ if (job->last_status() != OptimizedCompileJob::SUCCEEDED || |
+ shared->optimization_disabled() || |
+ info->HasAbortedDueToDependencyChange() || |
+ isolate->DebuggerHasBreakPoints()) { |
+ return Handle<Code>::null(); |
} |
- // Optimized code is finally replacing unoptimized code. Reset the latter's |
- // profiler ticks to prevent too soon re-opt after a deopt. |
- info->shared_info()->code()->set_profiler_ticks(0); |
- ASSERT(!info->closure()->IsInRecompileQueue()); |
- return (status == RecompileJob::SUCCEEDED) ? info->code() |
- : Handle<Code>::null(); |
-} |
- |
-Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal, |
- Handle<Script> script) { |
- // Precondition: code has been parsed and scopes have been analyzed. |
- CompilationInfoWithZone info(script); |
- info.SetFunction(literal); |
- info.SetScope(literal->scope()); |
- info.SetLanguageMode(literal->scope()->language_mode()); |
- |
- Isolate* isolate = info.isolate(); |
- Factory* factory = isolate->factory(); |
- LiveEditFunctionTracker live_edit_tracker(isolate, literal); |
- // Determine if the function can be lazily compiled. This is necessary to |
- // allow some of our builtin JS files to be lazily compiled. These |
- // builtins cannot be handled lazily by the parser, since we have to know |
- // if a function uses the special natives syntax, which is something the |
- // parser records. |
- // If the debugger requests compilation for break points, we cannot be |
- // aggressive about lazy compilation, because it might trigger compilation |
- // of functions without an outer context when setting a breakpoint through |
- // Debug::FindSharedFunctionInfoInScript. |
- bool allow_lazy_without_ctx = literal->AllowsLazyCompilationWithoutContext(); |
- bool allow_lazy = literal->AllowsLazyCompilation() && |
- !DebuggerWantsEagerCompilation(&info, allow_lazy_without_ctx); |
- |
- Handle<ScopeInfo> scope_info(ScopeInfo::Empty(isolate)); |
- |
- // Generate code |
- if (FLAG_lazy && allow_lazy && !literal->is_parenthesized()) { |
- Handle<Code> code = isolate->builtins()->LazyCompile(); |
- info.SetCode(code); |
- } else if (GenerateCode(&info)) { |
- ASSERT(!info.code().is_null()); |
- scope_info = ScopeInfo::Create(info.scope(), info.zone()); |
- } else { |
- return Handle<SharedFunctionInfo>::null(); |
+ if (job->GenerateCode() != OptimizedCompileJob::SUCCEEDED) { |
+ return Handle<Code>::null(); |
} |
- // Create a shared function info object. |
- Handle<SharedFunctionInfo> result = |
- factory->NewSharedFunctionInfo(literal->name(), |
- literal->materialized_literal_count(), |
- literal->is_generator(), |
- info.code(), |
- scope_info); |
- SetFunctionInfo(result, literal, false, script); |
- RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, result); |
- result->set_allows_lazy_compilation(allow_lazy); |
- result->set_allows_lazy_compilation_without_context(allow_lazy_without_ctx); |
- |
- // Set the expected number of properties for instances and return |
- // the resulting function. |
- SetExpectedNofPropertiesFromEstimate(result, |
- literal->expected_property_count()); |
- live_edit_tracker.RecordFunctionInfo(result, literal, info.zone()); |
- return result; |
-} |
+ Compiler::RecordFunctionCompilation( |
+ Logger::LAZY_COMPILE_TAG, info.get(), shared); |
+ if (info->shared_info()->SearchOptimizedCodeMap( |
+ info->context()->native_context()) == -1) { |
+ InsertCodeIntoOptimizedCodeMap(info.get()); |
+ } |
+ if (FLAG_trace_concurrent_recompilation) { |
+ PrintF(" ** Optimized code for "); |
+ info->closure()->PrintName(); |
+ PrintF(" generated.\n"); |
+ } |
-// Sets the function info on a function. |
-// The start_position points to the first '(' character after the function name |
-// in the full script source. When counting characters in the script source the |
-// the first character is number 0 (not 1). |
-void Compiler::SetFunctionInfo(Handle<SharedFunctionInfo> function_info, |
- FunctionLiteral* lit, |
- bool is_toplevel, |
- Handle<Script> script) { |
- function_info->set_length(lit->parameter_count()); |
- function_info->set_formal_parameter_count(lit->parameter_count()); |
- function_info->set_script(*script); |
- function_info->set_function_token_position(lit->function_token_position()); |
- function_info->set_start_position(lit->start_position()); |
- function_info->set_end_position(lit->end_position()); |
- function_info->set_is_expression(lit->is_expression()); |
- function_info->set_is_anonymous(lit->is_anonymous()); |
- function_info->set_is_toplevel(is_toplevel); |
- function_info->set_inferred_name(*lit->inferred_name()); |
- function_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation()); |
- function_info->set_allows_lazy_compilation_without_context( |
- lit->AllowsLazyCompilationWithoutContext()); |
- function_info->set_language_mode(lit->language_mode()); |
- function_info->set_uses_arguments(lit->scope()->arguments() != NULL); |
- function_info->set_has_duplicate_parameters(lit->has_duplicate_parameters()); |
- function_info->set_ast_node_count(lit->ast_node_count()); |
- function_info->set_is_function(lit->is_function()); |
- function_info->set_dont_optimize_reason(lit->dont_optimize_reason()); |
- function_info->set_dont_inline(lit->flags()->Contains(kDontInline)); |
- function_info->set_dont_cache(lit->flags()->Contains(kDontCache)); |
- function_info->set_is_generator(lit->is_generator()); |
+ return Handle<Code>(*info->code()); |
} |
@@ -1302,31 +1237,18 @@ void Compiler::RecordFunctionCompilation(Logger::LogEventsAndTags tag, |
info->isolate()->cpu_profiler()->is_profiling()) { |
Handle<Script> script = info->script(); |
Handle<Code> code = info->code(); |
- if (*code == info->isolate()->builtins()->builtin(Builtins::kLazyCompile)) |
+ if (code.is_identical_to(info->isolate()->builtins()->CompileUnoptimized())) |
return; |
int line_num = GetScriptLineNumber(script, shared->start_position()) + 1; |
int column_num = |
GetScriptColumnNumber(script, shared->start_position()) + 1; |
USE(line_num); |
- if (script->name()->IsString()) { |
- PROFILE(info->isolate(), |
- CodeCreateEvent(Logger::ToNativeByScript(tag, *script), |
- *code, |
- *shared, |
- info, |
- String::cast(script->name()), |
- line_num, |
- column_num)); |
- } else { |
- PROFILE(info->isolate(), |
- CodeCreateEvent(Logger::ToNativeByScript(tag, *script), |
- *code, |
- *shared, |
- info, |
- info->isolate()->heap()->empty_string(), |
- line_num, |
- column_num)); |
- } |
+ String* script_name = script->name()->IsString() |
+ ? String::cast(script->name()) |
+ : info->isolate()->heap()->empty_string(); |
+ Logger::LogEventsAndTags log_tag = Logger::ToNativeByScript(tag, *script); |
+ PROFILE(info->isolate(), CodeCreateEvent( |
+ log_tag, *code, *shared, info, script_name, line_num, column_num)); |
} |
GDBJIT(AddCode(Handle<String>(shared->DebugName()), |