1// Copyright 2012 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/compiler-dispatcher/optimizing-compile-dispatcher.h" 6 7#include "src/base/atomicops.h" 8#include "src/codegen/compiler.h" 9#include "src/codegen/optimized-compilation-info.h" 10#include "src/execution/isolate.h" 11#include "src/execution/local-isolate.h" 12#include "src/handles/handles-inl.h" 13#include "src/heap/local-heap.h" 14#include "src/heap/parked-scope.h" 15#include "src/init/v8.h" 16#include "src/logging/counters.h" 17#include "src/logging/log.h" 18#include "src/logging/runtime-call-stats-scope.h" 19#include "src/objects/js-function.h" 20#include "src/tasks/cancelable-task.h" 21#include "src/tracing/trace-event.h" 22 23namespace v8 { 24namespace internal { 25 26class OptimizingCompileDispatcher::CompileTask : public CancelableTask { 27 public: 28 explicit CompileTask(Isolate* isolate, 29 OptimizingCompileDispatcher* dispatcher) 30 : CancelableTask(isolate), 31 isolate_(isolate), 32 worker_thread_runtime_call_stats_( 33 isolate->counters()->worker_thread_runtime_call_stats()), 34 dispatcher_(dispatcher) { 35 ++dispatcher_->ref_count_; 36 } 37 38 CompileTask(const CompileTask&) = delete; 39 CompileTask& operator=(const CompileTask&) = delete; 40 41 ~CompileTask() override = default; 42 43 private: 44 // v8::Task overrides. 45 void RunInternal() override { 46 LocalIsolate local_isolate(isolate_, ThreadKind::kBackground); 47 DCHECK(local_isolate.heap()->IsParked()); 48 49 { 50 RCS_SCOPE(&local_isolate, 51 RuntimeCallCounterId::kOptimizeBackgroundDispatcherJob); 52 53 TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_); 54 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), 55 "V8.OptimizeBackground"); 56 57 if (dispatcher_->recompilation_delay_ != 0) { 58 base::OS::Sleep(base::TimeDelta::FromMilliseconds( 59 dispatcher_->recompilation_delay_)); 60 } 61 62 dispatcher_->CompileNext(dispatcher_->NextInput(&local_isolate), 63 &local_isolate); 64 } 65 { 66 base::MutexGuard lock_guard(&dispatcher_->ref_count_mutex_); 67 if (--dispatcher_->ref_count_ == 0) { 68 dispatcher_->ref_count_zero_.NotifyOne(); 69 } 70 } 71 } 72 73 Isolate* isolate_; 74 WorkerThreadRuntimeCallStats* worker_thread_runtime_call_stats_; 75 OptimizingCompileDispatcher* dispatcher_; 76}; 77 78OptimizingCompileDispatcher::~OptimizingCompileDispatcher() { 79 DCHECK_EQ(0, ref_count_); 80 DCHECK_EQ(0, input_queue_length_); 81 DeleteArray(input_queue_); 82} 83 84TurbofanCompilationJob* OptimizingCompileDispatcher::NextInput( 85 LocalIsolate* local_isolate) { 86 base::MutexGuard access_input_queue_(&input_queue_mutex_); 87 if (input_queue_length_ == 0) return nullptr; 88 TurbofanCompilationJob* job = input_queue_[InputQueueIndex(0)]; 89 DCHECK_NOT_NULL(job); 90 input_queue_shift_ = InputQueueIndex(1); 91 input_queue_length_--; 92 return job; 93} 94 95void OptimizingCompileDispatcher::CompileNext(TurbofanCompilationJob* job, 96 LocalIsolate* local_isolate) { 97 if (!job) return; 98 99 // The function may have already been optimized by OSR. Simply continue. 100 CompilationJob::Status status = 101 job->ExecuteJob(local_isolate->runtime_call_stats(), local_isolate); 102 USE(status); // Prevent an unused-variable error. 103 104 { 105 // The function may have already been optimized by OSR. Simply continue. 106 // Use a mutex to make sure that functions marked for install 107 // are always also queued. 108 base::MutexGuard access_output_queue_(&output_queue_mutex_); 109 output_queue_.push(job); 110 } 111 112 if (finalize()) isolate_->stack_guard()->RequestInstallCode(); 113} 114 115void OptimizingCompileDispatcher::FlushOutputQueue(bool restore_function_code) { 116 for (;;) { 117 std::unique_ptr<TurbofanCompilationJob> job; 118 { 119 base::MutexGuard access_output_queue_(&output_queue_mutex_); 120 if (output_queue_.empty()) return; 121 job.reset(output_queue_.front()); 122 output_queue_.pop(); 123 } 124 125 Compiler::DisposeTurbofanCompilationJob(job.get(), restore_function_code); 126 } 127} 128 129void OptimizingCompileDispatcher::FlushInputQueue() { 130 base::MutexGuard access_input_queue_(&input_queue_mutex_); 131 while (input_queue_length_ > 0) { 132 std::unique_ptr<TurbofanCompilationJob> job( 133 input_queue_[InputQueueIndex(0)]); 134 DCHECK_NOT_NULL(job); 135 input_queue_shift_ = InputQueueIndex(1); 136 input_queue_length_--; 137 Compiler::DisposeTurbofanCompilationJob(job.get(), true); 138 } 139} 140 141void OptimizingCompileDispatcher::AwaitCompileTasks() { 142 { 143 base::MutexGuard lock_guard(&ref_count_mutex_); 144 while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_); 145 } 146 147#ifdef DEBUG 148 base::MutexGuard access_input_queue(&input_queue_mutex_); 149 CHECK_EQ(input_queue_length_, 0); 150#endif // DEBUG 151} 152 153void OptimizingCompileDispatcher::FlushQueues( 154 BlockingBehavior blocking_behavior, bool restore_function_code) { 155 FlushInputQueue(); 156 if (blocking_behavior == BlockingBehavior::kBlock) { 157 base::MutexGuard lock_guard(&ref_count_mutex_); 158 while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_); 159 } 160 FlushOutputQueue(restore_function_code); 161} 162 163void OptimizingCompileDispatcher::Flush(BlockingBehavior blocking_behavior) { 164 HandleScope handle_scope(isolate_); 165 FlushQueues(blocking_behavior, true); 166 if (FLAG_trace_concurrent_recompilation) { 167 PrintF(" ** Flushed concurrent recompilation queues. (mode: %s)\n", 168 (blocking_behavior == BlockingBehavior::kBlock) ? "blocking" 169 : "non blocking"); 170 } 171} 172 173void OptimizingCompileDispatcher::Stop() { 174 HandleScope handle_scope(isolate_); 175 FlushQueues(BlockingBehavior::kBlock, false); 176 // At this point the optimizing compiler thread's event loop has stopped. 177 // There is no need for a mutex when reading input_queue_length_. 178 DCHECK_EQ(input_queue_length_, 0); 179} 180 181void OptimizingCompileDispatcher::InstallOptimizedFunctions() { 182 HandleScope handle_scope(isolate_); 183 184 for (;;) { 185 std::unique_ptr<TurbofanCompilationJob> job; 186 { 187 base::MutexGuard access_output_queue_(&output_queue_mutex_); 188 if (output_queue_.empty()) return; 189 job.reset(output_queue_.front()); 190 output_queue_.pop(); 191 } 192 OptimizedCompilationInfo* info = job->compilation_info(); 193 Handle<JSFunction> function(*info->closure(), isolate_); 194 195 // If another racing task has already finished compiling and installing the 196 // requested code kind on the function, throw out the current job. 197 if (!info->is_osr() && function->HasAvailableCodeKind(info->code_kind())) { 198 if (FLAG_trace_concurrent_recompilation) { 199 PrintF(" ** Aborting compilation for "); 200 function->ShortPrint(); 201 PrintF(" as it has already been optimized.\n"); 202 } 203 Compiler::DisposeTurbofanCompilationJob(job.get(), false); 204 continue; 205 } 206 207 Compiler::FinalizeTurbofanCompilationJob(job.get(), isolate_); 208 } 209} 210 211bool OptimizingCompileDispatcher::HasJobs() { 212 DCHECK_EQ(ThreadId::Current(), isolate_->thread_id()); 213 // Note: This relies on {output_queue_} being mutated by a background thread 214 // only when {ref_count_} is not zero. Also, {ref_count_} is never incremented 215 // by a background thread. 216 return ref_count_ != 0 || !output_queue_.empty(); 217} 218 219void OptimizingCompileDispatcher::QueueForOptimization( 220 TurbofanCompilationJob* job) { 221 DCHECK(IsQueueAvailable()); 222 { 223 // Add job to the back of the input queue. 224 base::MutexGuard access_input_queue(&input_queue_mutex_); 225 DCHECK_LT(input_queue_length_, input_queue_capacity_); 226 input_queue_[InputQueueIndex(input_queue_length_)] = job; 227 input_queue_length_++; 228 } 229 V8::GetCurrentPlatform()->CallOnWorkerThread( 230 std::make_unique<CompileTask>(isolate_, this)); 231} 232 233} // namespace internal 234} // namespace v8 235