| 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()),
|
|
|