Chromium Code Reviews| Index: src/compiler.cc |
| diff --git a/src/compiler.cc b/src/compiler.cc |
| index c6dfa045b744a82da959c177e090546488322398..a84d263d72401e0ca89dd25010e880683ff0d38d 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,6 +276,25 @@ class HOptimizedGraphBuilderWithPositions: public HOptimizedGraphBuilder { |
| }; |
| +// 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 |
| +} |
| + |
| + |
| RecompileJob::Status RecompileJob::CreateGraph() { |
| ASSERT(isolate()->use_crankshaft()); |
| ASSERT(info()->IsOptimizing()); |
| @@ -376,9 +311,8 @@ 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 || IsDebuggerActive(isolate())) { |
| + return AbortOptimization(); |
| } |
| // Limit the number of times we re-compile a functions with |
| @@ -387,7 +321,7 @@ RecompileJob::Status RecompileJob::CreateGraph() { |
| 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(); |
| } |
| // Due to an encoding limit on LUnallocated operands in the Lithium |
| @@ -401,20 +335,19 @@ RecompileJob::Status RecompileJob::CreateGraph() { |
| Scope* scope = info()->scope(); |
| if ((scope->num_parameters() + 1) > parameter_limit) { |
| info()->set_bailout_reason(kTooManyParameters); |
| - return AbortOptimization(); |
| + return AbortAndDisableOptimization(); |
| } |
| 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(); |
| } |
| // Take --hydrogen-filter into account. |
| if (!info()->closure()->PassesFilter(FLAG_hydrogen_filter)) { |
|
titzer
2013/12/09 14:49:28
Do we also want to set a bailout reason and disabl
Yang
2013/12/10 11:22:04
Done.
|
| - info()->AbortOptimization(); |
| - return SetLastStatus(BAILED_OUT); |
| + return AbortOptimization(); |
| } |
| // Recompile the unoptimized version of the code if the current version |
| @@ -474,7 +407,6 @@ RecompileJob::Status RecompileJob::CreateGraph() { |
| graph_ = graph_builder_->CreateGraph(); |
| if (isolate()->has_pending_exception()) { |
| - info()->SetCode(Handle<Code>::null()); |
| return SetLastStatus(FAILED); |
| } |
| @@ -484,17 +416,15 @@ 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(); |
| } |
| return SetLastStatus(SUCCEEDED); |
| @@ -511,20 +441,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() { |
| +RecompileJob::Status RecompileJob::GenerateCode() { |
| ASSERT(last_status() == SUCCEEDED); |
| ASSERT(!info()->HasAbortedDueToDependencyChange()); |
| DisallowCodeDependencyChange no_dependency_change; |
| @@ -542,7 +471,7 @@ RecompileJob::Status RecompileJob::GenerateAndInstallCode() { |
| if (info()->bailout_reason() == kNoReason) { |
| info()->set_bailout_reason(kCodeGenerationFailed); |
| } |
| - return AbortOptimization(); |
| + return AbortAndDisableOptimization(); |
| } |
| info()->SetCode(optimized_code); |
| } |
| @@ -553,54 +482,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 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); |
| } |
| -} |
| - |
| - |
| -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,144 +546,373 @@ 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(); |
| - } |
| - } |
| - 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); |
| +// 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()); |
| +} |
| - // Compile the code. |
| - if (!MakeCode(info)) { |
| - if (!isolate->has_pending_exception()) isolate->StackOverflow(); |
| - 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())); |
| +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); |
| - 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)); |
| - } |
| + if (!FullCodeGenerator::MakeCode(info)) { |
| + Isolate* isolate = info->isolate(); |
| + if (!isolate->has_pending_exception()) isolate->StackOverflow(); |
| + return false; |
| + } |
| + return true; |
| +} |
| - // 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); |
| +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); |
| + |
| + 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(); |
| +} |
| + |
| + |
| +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()); |
| } |
| -#ifdef ENABLE_DEBUGGER_SUPPORT |
| - // Notify debugger |
| - isolate->debugger()->OnAfterCompile( |
| - script, Debugger::NO_AFTER_COMPILE_FLAGS); |
| -#endif |
| + CompilationInfoWithZone info(function); |
| + Handle<Code> result = GetUnoptimizedCodeCommon(&info); |
| + ASSERT_EQ(result.is_null(), info.isolate()->has_pending_exception()); |
| - live_edit_tracker.RecordFunctionInfo(result, lit, info->zone()); |
| + if (FLAG_always_opt && |
| + info.isolate()->use_crankshaft() && |
| + !info.shared_info()->optimization_disabled() && |
| + !info.isolate()->DebuggerHasBreakPoints()) { |
| + result = Compiler::GetOptimizedCode( |
| + function, result, Compiler::NOT_CONCURRENT); |
| + } |
| 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); |
| +Handle<Code> Compiler::GetUnoptimizedCode(Handle<SharedFunctionInfo> shared) { |
| + ASSERT(!shared->GetIsolate()->has_pending_exception()); |
| + ASSERT(!shared->is_compiled()); |
| - // The VM is in the COMPILER state until exiting this function. |
| - VMState<COMPILER> state(isolate); |
| + CompilationInfoWithZone info(shared); |
| + Handle<Code> result = GetUnoptimizedCodeCommon(&info); |
| + ASSERT_EQ(result.is_null(), info.isolate()->has_pending_exception()); |
| + return result; |
| +} |
| + |
| + |
| +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; |
| +} |
| + |
| + |
| +// 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); |
| + |
| + 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); |
| + |
| + 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(); |
| @@ -817,14 +961,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 +974,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()->LazyCompile(); |
| + 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 +1069,164 @@ 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; |
| +static bool GetOptimizedCodeNow(CompilationInfo* info) { |
| + if (!CompileOptimizedPrologue(info)) return false; |
| - // 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); |
| - |
| - // 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); |
| - } |
| - } |
| - } |
| + Logger::TimerEventScope timer( |
| + info->isolate(), Logger::TimerEventScope::v8_recompile_synchronous); |
| - ASSERT(info->code().is_null()); |
| - return false; |
| + RecompileJob job(info); |
| + if (job.CreateGraph() != RecompileJob::SUCCEEDED) return false; |
| + if (job.OptimizeGraph() != RecompileJob::SUCCEEDED) return false; |
| + if (job.GenerateCode() != RecompileJob::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. |
| - 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); |
| + Logger::TimerEventScope timer( |
| + isolate, Logger::TimerEventScope::v8_recompile_synchronous); |
| - if (FLAG_trace_osr) { |
| - PrintF("[COSR - attempt to queue "); |
| - closure->PrintName(); |
| - PrintF(" at AST id %d]\n", osr_ast_id.ToInt()); |
| + RecompileJob* job = new(info->zone()) RecompileJob(info); |
| + RecompileJob::Status status = job->CreateGraph(); |
| + if (status != RecompileJob::SUCCEEDED) return false; |
| + isolate->optimizing_compiler_thread()->QueueForOptimization(job); |
| + |
| + 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 Handle<Code>::null(); |
| } |
| + } 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>(shared->code()); |
| } |
| -Handle<Code> Compiler::InstallOptimizedCode(RecompileJob* job) { |
| +Handle<Code> Compiler::GetConcurrentlyOptimizedCode(RecompileJob* 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(); |
| + Handle<Code> fallback(shared->code()); |
| + fallback->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() != RecompileJob::SUCCEEDED || |
| + shared->optimization_disabled() || |
| + info->HasAbortedDueToDependencyChange() || |
| + isolate->DebuggerHasBreakPoints()) { |
| + return fallback; |
| } |
| - // 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() != RecompileJob::SUCCEEDED) { |
| + return fallback; |
| } |
| - // 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; |
| -} |
| + ASSERT(info->shared_info()->scope_info() != ScopeInfo::Empty(isolate)); |
|
titzer
2013/12/09 14:49:28
Don't know why this assert is here.
Yang
2013/12/10 11:22:04
Removed.
|
| + 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()); |
| } |
| @@ -1308,25 +1249,12 @@ void Compiler::RecordFunctionCompilation(Logger::LogEventsAndTags tag, |
| 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()), |