// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/compiler.h" #include #include #include "src/asmjs/asm-js.h" #include "src/asmjs/asm-typer.h" #include "src/assembler-inl.h" #include "src/ast/ast-numbering.h" #include "src/ast/prettyprinter.h" #include "src/ast/scopes.h" #include "src/bootstrapper.h" #include "src/codegen.h" #include "src/compilation-cache.h" #include "src/compiler-dispatcher/compiler-dispatcher.h" #include "src/compiler-dispatcher/optimizing-compile-dispatcher.h" #include "src/compiler/pipeline.h" #include "src/crankshaft/hydrogen.h" #include "src/debug/debug.h" #include "src/debug/liveedit.h" #include "src/frames-inl.h" #include "src/full-codegen/full-codegen.h" #include "src/globals.h" #include "src/heap/heap.h" #include "src/interpreter/interpreter.h" #include "src/isolate-inl.h" #include "src/log-inl.h" #include "src/messages.h" #include "src/parsing/parsing.h" #include "src/parsing/rewriter.h" #include "src/parsing/scanner-character-streams.h" #include "src/runtime-profiler.h" #include "src/snapshot/code-serializer.h" #include "src/vm-state-inl.h" namespace v8 { namespace internal { // A wrapper around a ParseInfo that detaches the parser handles from the // underlying DeferredHandleScope and stores them in info_ on destruction. class ParseHandleScope final { public: explicit ParseHandleScope(ParseInfo* info) : deferred_(info->isolate()), info_(info) {} ~ParseHandleScope() { info_->set_deferred_handles(deferred_.Detach()); } private: DeferredHandleScope deferred_; ParseInfo* info_; }; // A wrapper around a CompilationInfo that detaches the Handles from // the underlying DeferredHandleScope and stores them in info_ on // destruction. class CompilationHandleScope final { public: explicit CompilationHandleScope(CompilationInfo* info) : deferred_(info->isolate()), info_(info) {} ~CompilationHandleScope() { info_->set_deferred_handles(deferred_.Detach()); } private: DeferredHandleScope deferred_; CompilationInfo* info_; }; // Helper that times a scoped region and records the elapsed time. struct ScopedTimer { explicit ScopedTimer(base::TimeDelta* location) : location_(location) { DCHECK(location_ != NULL); timer_.Start(); } ~ScopedTimer() { *location_ += timer_.Elapsed(); } base::ElapsedTimer timer_; base::TimeDelta* location_; }; // ---------------------------------------------------------------------------- // Implementation of CompilationJob CompilationJob::CompilationJob(Isolate* isolate, CompilationInfo* info, const char* compiler_name, State initial_state) : info_(info), isolate_thread_id_(isolate->thread_id()), compiler_name_(compiler_name), state_(initial_state), stack_limit_(isolate->stack_guard()->real_climit()), executed_on_background_thread_(false) {} CompilationJob::Status CompilationJob::PrepareJob() { DCHECK(ThreadId::Current().Equals(info()->isolate()->thread_id())); DisallowJavascriptExecution no_js(isolate()); if (FLAG_trace_opt && info()->IsOptimizing()) { OFStream os(stdout); os << "[compiling method " << Brief(*info()->closure()) << " using " << compiler_name_; if (info()->is_osr()) os << " OSR"; os << "]" << std::endl; } // Delegate to the underlying implementation. DCHECK(state() == State::kReadyToPrepare); ScopedTimer t(&time_taken_to_prepare_); return UpdateState(PrepareJobImpl(), State::kReadyToExecute); } CompilationJob::Status CompilationJob::ExecuteJob() { std::unique_ptr no_allocation; std::unique_ptr no_handles; std::unique_ptr no_deref; std::unique_ptr no_dependency_change; if (can_execute_on_background_thread()) { no_allocation.reset(new DisallowHeapAllocation()); no_handles.reset(new DisallowHandleAllocation()); no_deref.reset(new DisallowHandleDereference()); no_dependency_change.reset(new DisallowCodeDependencyChange()); executed_on_background_thread_ = !ThreadId::Current().Equals(isolate_thread_id_); } else { DCHECK(ThreadId::Current().Equals(isolate_thread_id_)); } // Delegate to the underlying implementation. DCHECK(state() == State::kReadyToExecute); ScopedTimer t(&time_taken_to_execute_); return UpdateState(ExecuteJobImpl(), State::kReadyToFinalize); } CompilationJob::Status CompilationJob::FinalizeJob() { DCHECK(ThreadId::Current().Equals(info()->isolate()->thread_id())); DisallowCodeDependencyChange no_dependency_change; DisallowJavascriptExecution no_js(isolate()); DCHECK(!info()->dependencies()->HasAborted()); // Delegate to the underlying implementation. DCHECK(state() == State::kReadyToFinalize); ScopedTimer t(&time_taken_to_finalize_); return UpdateState(FinalizeJobImpl(), State::kSucceeded); } CompilationJob::Status CompilationJob::RetryOptimization(BailoutReason reason) { DCHECK(info_->IsOptimizing()); info_->RetryOptimization(reason); state_ = State::kFailed; return FAILED; } CompilationJob::Status CompilationJob::AbortOptimization(BailoutReason reason) { DCHECK(info_->IsOptimizing()); info_->AbortOptimization(reason); state_ = State::kFailed; return FAILED; } void CompilationJob::RecordUnoptimizedCompilationStats() const { int code_size; if (info()->has_bytecode_array()) { code_size = info()->bytecode_array()->SizeIncludingMetadata(); } else { code_size = info()->code()->SizeIncludingMetadata(); } Counters* counters = isolate()->counters(); // TODO(4280): Rename counters from "baseline" to "unoptimized" eventually. counters->total_baseline_code_size()->Increment(code_size); counters->total_baseline_compile_count()->Increment(1); // TODO(5203): Add timers for each phase of compilation. } void CompilationJob::RecordOptimizedCompilationStats() const { DCHECK(info()->IsOptimizing()); Handle function = info()->closure(); if (!function->IsOptimized()) { // Concurrent recompilation and OSR may race. Increment only once. int opt_count = function->shared()->opt_count(); function->shared()->set_opt_count(opt_count + 1); } double ms_creategraph = time_taken_to_prepare_.InMillisecondsF(); double ms_optimize = time_taken_to_execute_.InMillisecondsF(); double ms_codegen = time_taken_to_finalize_.InMillisecondsF(); if (FLAG_trace_opt) { PrintF("[optimizing "); function->ShortPrint(); PrintF(" - took %0.3f, %0.3f, %0.3f ms]\n", ms_creategraph, ms_optimize, ms_codegen); } if (FLAG_trace_opt_stats) { static double compilation_time = 0.0; static int compiled_functions = 0; static int code_size = 0; compilation_time += (ms_creategraph + ms_optimize + ms_codegen); compiled_functions++; code_size += function->shared()->SourceSize(); PrintF("Compiled: %d functions with %d byte source size in %fms.\n", compiled_functions, code_size, compilation_time); } if (FLAG_hydrogen_stats) { isolate()->GetHStatistics()->IncrementSubtotals(time_taken_to_prepare_, time_taken_to_execute_, time_taken_to_finalize_); } } Isolate* CompilationJob::isolate() const { return info()->isolate(); } namespace { void AddWeakObjectToCodeDependency(Isolate* isolate, Handle object, Handle code) { Handle cell = Code::WeakCellFor(code); Heap* heap = isolate->heap(); if (heap->InNewSpace(*object)) { heap->AddWeakNewSpaceObjectToCodeDependency(object, cell); } else { Handle dep(heap->LookupWeakObjectToCodeDependency(object)); dep = DependentCode::InsertWeakCode(dep, DependentCode::kWeakCodeGroup, cell); heap->AddWeakObjectToCodeDependency(object, dep); } } } // namespace void CompilationJob::RegisterWeakObjectsInOptimizedCode(Handle code) { // TODO(turbofan): Move this to pipeline.cc once Crankshaft dies. Isolate* const isolate = code->GetIsolate(); DCHECK(code->is_optimized_code()); std::vector> maps; std::vector> objects; { DisallowHeapAllocation no_gc; int const mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | RelocInfo::ModeMask(RelocInfo::CELL); for (RelocIterator it(*code, mode_mask); !it.done(); it.next()) { RelocInfo::Mode mode = it.rinfo()->rmode(); if (mode == RelocInfo::CELL && code->IsWeakObjectInOptimizedCode(it.rinfo()->target_cell())) { objects.push_back(handle(it.rinfo()->target_cell(), isolate)); } else if (mode == RelocInfo::EMBEDDED_OBJECT && code->IsWeakObjectInOptimizedCode( it.rinfo()->target_object())) { Handle object(HeapObject::cast(it.rinfo()->target_object()), isolate); if (object->IsMap()) { maps.push_back(Handle::cast(object)); } else { objects.push_back(object); } } } } for (Handle map : maps) { if (map->dependent_code()->IsEmpty(DependentCode::kWeakCodeGroup)) { isolate->heap()->AddRetainedMap(map); } Map::AddDependentCode(map, DependentCode::kWeakCodeGroup, code); } for (Handle object : objects) { AddWeakObjectToCodeDependency(isolate, object, code); } code->set_can_have_weak_objects(true); } // ---------------------------------------------------------------------------- // Local helper methods that make up the compilation pipeline. namespace { void RecordFunctionCompilation(CodeEventListener::LogEventsAndTags tag, CompilationInfo* info) { // Log the code generation. If source information is available include // script name and line number. Check explicitly whether logging is // enabled as finding the line number is not free. if (info->isolate()->logger()->is_logging_code_events() || info->isolate()->is_profiling()) { Handle shared = info->shared_info(); Handle