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.
5 #include "src/optimizing-compiler-thread.h"
9 #include "src/base/atomicops.h"
10 #include "src/full-codegen.h"
11 #include "src/hydrogen.h"
12 #include "src/isolate.h"
13 #include "src/v8threads.h"
20 void DisposeOptimizedCompileJob(OptimizedCompileJob* job,
21 bool restore_function_code) {
22 // The recompile job is allocated in the CompilationInfo's zone.
23 CompilationInfo* info = job->info();
24 if (restore_function_code) {
26 if (!job->IsWaitingForInstall()) {
27 // Remove stack check that guards OSR entry on original code.
28 Handle<Code> code = info->unoptimized_code();
29 uint32_t offset = code->TranslateAstIdToPcOffset(info->osr_ast_id());
30 BackEdgeTable::RemoveStackCheck(code, offset);
33 Handle<JSFunction> function = info->closure();
34 function->ReplaceCode(function->shared()->code());
43 class OptimizingCompilerThread::CompileTask : public v8::Task {
45 explicit CompileTask(Isolate* isolate) : isolate_(isolate) {
46 OptimizingCompilerThread* thread = isolate_->optimizing_compiler_thread();
47 base::LockGuard<base::Mutex> lock_guard(&thread->ref_count_mutex_);
51 virtual ~CompileTask() {}
54 // v8::Task overrides.
56 DisallowHeapAllocation no_allocation;
57 DisallowHandleAllocation no_handles;
58 DisallowHandleDereference no_deref;
60 OptimizingCompilerThread* thread = isolate_->optimizing_compiler_thread();
62 TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_);
64 if (thread->recompilation_delay_ != 0) {
65 base::OS::Sleep(thread->recompilation_delay_);
68 thread->CompileNext(thread->NextInput(true));
71 base::LockGuard<base::Mutex> lock_guard(&thread->ref_count_mutex_);
72 if (--thread->ref_count_ == 0) {
73 thread->ref_count_zero_.NotifyOne();
80 DISALLOW_COPY_AND_ASSIGN(CompileTask);
84 OptimizingCompilerThread::~OptimizingCompilerThread() {
87 base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_);
88 DCHECK_EQ(0, ref_count_);
91 DCHECK_EQ(0, input_queue_length_);
92 DeleteArray(input_queue_);
93 if (FLAG_concurrent_osr) {
95 for (int i = 0; i < osr_buffer_capacity_; i++) {
96 CHECK_NULL(osr_buffer_[i]);
99 DeleteArray(osr_buffer_);
104 void OptimizingCompilerThread::Run() {
106 { base::LockGuard<base::Mutex> lock_guard(&thread_id_mutex_);
107 thread_id_ = ThreadId::Current().ToInteger();
110 DisallowHeapAllocation no_allocation;
111 DisallowHandleAllocation no_handles;
112 DisallowHandleDereference no_deref;
114 if (job_based_recompilation_) {
118 base::ElapsedTimer total_timer;
119 if (tracing_enabled_) total_timer.Start();
122 input_queue_semaphore_.Wait();
123 TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_);
125 if (recompilation_delay_ != 0) {
126 base::OS::Sleep(recompilation_delay_);
129 switch (static_cast<StopFlag>(base::Acquire_Load(&stop_thread_))) {
133 if (tracing_enabled_) {
134 time_spent_total_ = total_timer.Elapsed();
136 stop_semaphore_.Signal();
139 // The main thread is blocked, waiting for the stop semaphore.
140 { AllowHandleDereference allow_handle_dereference;
141 FlushInputQueue(true);
143 base::Release_Store(&stop_thread_,
144 static_cast<base::AtomicWord>(CONTINUE));
145 stop_semaphore_.Signal();
146 // Return to start of consumer loop.
150 base::ElapsedTimer compiling_timer;
151 if (tracing_enabled_) compiling_timer.Start();
153 CompileNext(NextInput());
155 if (tracing_enabled_) {
156 time_spent_compiling_ += compiling_timer.Elapsed();
162 OptimizedCompileJob* OptimizingCompilerThread::NextInput(
163 bool check_if_flushing) {
164 base::LockGuard<base::Mutex> access_input_queue_(&input_queue_mutex_);
165 if (input_queue_length_ == 0) return NULL;
166 OptimizedCompileJob* job = input_queue_[InputQueueIndex(0)];
167 DCHECK_NOT_NULL(job);
168 input_queue_shift_ = InputQueueIndex(1);
169 input_queue_length_--;
170 if (check_if_flushing) {
171 if (static_cast<StopFlag>(base::Acquire_Load(&stop_thread_)) != CONTINUE) {
172 if (!job->info()->is_osr()) {
173 AllowHandleDereference allow_handle_dereference;
174 DisposeOptimizedCompileJob(job, true);
183 void OptimizingCompilerThread::CompileNext(OptimizedCompileJob* job) {
186 // The function may have already been optimized by OSR. Simply continue.
187 OptimizedCompileJob::Status status = job->OptimizeGraph();
188 USE(status); // Prevent an unused-variable error in release mode.
189 DCHECK(status != OptimizedCompileJob::FAILED);
191 // The function may have already been optimized by OSR. Simply continue.
192 // Use a mutex to make sure that functions marked for install
193 // are always also queued.
194 if (job_based_recompilation_) output_queue_mutex_.Lock();
195 output_queue_.Enqueue(job);
196 if (job_based_recompilation_) output_queue_mutex_.Unlock();
197 isolate_->stack_guard()->RequestInstallCode();
201 void OptimizingCompilerThread::FlushInputQueue(bool restore_function_code) {
202 OptimizedCompileJob* job;
203 while ((job = NextInput())) {
204 DCHECK(!job_based_recompilation_);
205 // This should not block, since we have one signal on the input queue
206 // semaphore corresponding to each element in the input queue.
207 input_queue_semaphore_.Wait();
208 // OSR jobs are dealt with separately.
209 if (!job->info()->is_osr()) {
210 DisposeOptimizedCompileJob(job, restore_function_code);
216 void OptimizingCompilerThread::FlushOutputQueue(bool restore_function_code) {
217 OptimizedCompileJob* job;
218 while (output_queue_.Dequeue(&job)) {
219 // OSR jobs are dealt with separately.
220 if (!job->info()->is_osr()) {
221 DisposeOptimizedCompileJob(job, restore_function_code);
227 void OptimizingCompilerThread::FlushOsrBuffer(bool restore_function_code) {
228 for (int i = 0; i < osr_buffer_capacity_; i++) {
229 if (osr_buffer_[i] != NULL) {
230 DisposeOptimizedCompileJob(osr_buffer_[i], restore_function_code);
231 osr_buffer_[i] = NULL;
237 void OptimizingCompilerThread::Flush() {
238 DCHECK(!IsOptimizerThread());
239 base::Release_Store(&stop_thread_, static_cast<base::AtomicWord>(FLUSH));
240 if (FLAG_block_concurrent_recompilation) Unblock();
241 if (!job_based_recompilation_) {
242 input_queue_semaphore_.Signal();
243 stop_semaphore_.Wait();
245 base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_);
246 while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_);
247 base::Release_Store(&stop_thread_, static_cast<base::AtomicWord>(CONTINUE));
249 FlushOutputQueue(true);
250 if (FLAG_concurrent_osr) FlushOsrBuffer(true);
251 if (tracing_enabled_) {
252 PrintF(" ** Flushed concurrent recompilation queues.\n");
257 void OptimizingCompilerThread::Stop() {
258 DCHECK(!IsOptimizerThread());
259 base::Release_Store(&stop_thread_, static_cast<base::AtomicWord>(STOP));
260 if (FLAG_block_concurrent_recompilation) Unblock();
261 if (!job_based_recompilation_) {
262 input_queue_semaphore_.Signal();
263 stop_semaphore_.Wait();
265 base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_);
266 while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_);
267 base::Release_Store(&stop_thread_, static_cast<base::AtomicWord>(CONTINUE));
270 if (recompilation_delay_ != 0) {
271 // At this point the optimizing compiler thread's event loop has stopped.
272 // There is no need for a mutex when reading input_queue_length_.
273 while (input_queue_length_ > 0) CompileNext(NextInput());
274 InstallOptimizedFunctions();
276 FlushInputQueue(false);
277 FlushOutputQueue(false);
280 if (FLAG_concurrent_osr) FlushOsrBuffer(false);
282 if (tracing_enabled_) {
283 double percentage = time_spent_compiling_.PercentOf(time_spent_total_);
284 if (job_based_recompilation_) percentage = 100.0;
285 PrintF(" ** Compiler thread did %.2f%% useful work\n", percentage);
288 if ((FLAG_trace_osr || tracing_enabled_) && FLAG_concurrent_osr) {
289 PrintF("[COSR hit rate %d / %d]\n", osr_hits_, osr_attempts_);
296 void OptimizingCompilerThread::InstallOptimizedFunctions() {
297 DCHECK(!IsOptimizerThread());
298 HandleScope handle_scope(isolate_);
300 OptimizedCompileJob* job;
301 while (output_queue_.Dequeue(&job)) {
302 CompilationInfo* info = job->info();
303 Handle<JSFunction> function(*info->closure());
304 if (info->is_osr()) {
305 if (FLAG_trace_osr) {
307 function->ShortPrint();
308 PrintF(" is ready for install and entry at AST id %d]\n",
309 info->osr_ast_id().ToInt());
311 job->WaitForInstall();
312 // Remove stack check that guards OSR entry on original code.
313 Handle<Code> code = info->unoptimized_code();
314 uint32_t offset = code->TranslateAstIdToPcOffset(info->osr_ast_id());
315 BackEdgeTable::RemoveStackCheck(code, offset);
317 if (function->IsOptimized()) {
318 if (tracing_enabled_) {
319 PrintF(" ** Aborting compilation for ");
320 function->ShortPrint();
321 PrintF(" as it has already been optimized.\n");
323 DisposeOptimizedCompileJob(job, false);
325 Handle<Code> code = Compiler::GetConcurrentlyOptimizedCode(job);
326 function->ReplaceCode(
327 code.is_null() ? function->shared()->code() : *code);
334 void OptimizingCompilerThread::QueueForOptimization(OptimizedCompileJob* job) {
335 DCHECK(IsQueueAvailable());
336 DCHECK(!IsOptimizerThread());
337 CompilationInfo* info = job->info();
338 if (info->is_osr()) {
341 // Add job to the front of the input queue.
342 base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_);
343 DCHECK_LT(input_queue_length_, input_queue_capacity_);
344 // Move shift_ back by one.
345 input_queue_shift_ = InputQueueIndex(input_queue_capacity_ - 1);
346 input_queue_[InputQueueIndex(0)] = job;
347 input_queue_length_++;
349 // Add job to the back of the input queue.
350 base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_);
351 DCHECK_LT(input_queue_length_, input_queue_capacity_);
352 input_queue_[InputQueueIndex(input_queue_length_)] = job;
353 input_queue_length_++;
355 if (FLAG_block_concurrent_recompilation) {
357 } else if (job_based_recompilation_) {
358 V8::GetCurrentPlatform()->CallOnBackgroundThread(
359 new CompileTask(isolate_), v8::Platform::kShortRunningTask);
361 input_queue_semaphore_.Signal();
366 void OptimizingCompilerThread::Unblock() {
367 DCHECK(!IsOptimizerThread());
368 while (blocked_jobs_ > 0) {
369 if (job_based_recompilation_) {
370 V8::GetCurrentPlatform()->CallOnBackgroundThread(
371 new CompileTask(isolate_), v8::Platform::kShortRunningTask);
373 input_queue_semaphore_.Signal();
380 OptimizedCompileJob* OptimizingCompilerThread::FindReadyOSRCandidate(
381 Handle<JSFunction> function, BailoutId osr_ast_id) {
382 DCHECK(!IsOptimizerThread());
383 for (int i = 0; i < osr_buffer_capacity_; i++) {
384 OptimizedCompileJob* current = osr_buffer_[i];
385 if (current != NULL &&
386 current->IsWaitingForInstall() &&
387 current->info()->HasSameOsrEntry(function, osr_ast_id)) {
389 osr_buffer_[i] = NULL;
397 bool OptimizingCompilerThread::IsQueuedForOSR(Handle<JSFunction> function,
398 BailoutId osr_ast_id) {
399 DCHECK(!IsOptimizerThread());
400 for (int i = 0; i < osr_buffer_capacity_; i++) {
401 OptimizedCompileJob* current = osr_buffer_[i];
402 if (current != NULL &&
403 current->info()->HasSameOsrEntry(function, osr_ast_id)) {
404 return !current->IsWaitingForInstall();
411 bool OptimizingCompilerThread::IsQueuedForOSR(JSFunction* function) {
412 DCHECK(!IsOptimizerThread());
413 for (int i = 0; i < osr_buffer_capacity_; i++) {
414 OptimizedCompileJob* current = osr_buffer_[i];
415 if (current != NULL && *current->info()->closure() == function) {
416 return !current->IsWaitingForInstall();
423 void OptimizingCompilerThread::AddToOsrBuffer(OptimizedCompileJob* job) {
424 DCHECK(!IsOptimizerThread());
425 // Find the next slot that is empty or has a stale job.
426 OptimizedCompileJob* stale = NULL;
428 stale = osr_buffer_[osr_buffer_cursor_];
429 if (stale == NULL || stale->IsWaitingForInstall()) break;
430 osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_;
433 // Add to found slot and dispose the evicted job.
435 DCHECK(stale->IsWaitingForInstall());
436 CompilationInfo* info = stale->info();
437 if (FLAG_trace_osr) {
438 PrintF("[COSR - Discarded ");
439 info->closure()->PrintName();
440 PrintF(", AST id %d]\n", info->osr_ast_id().ToInt());
442 DisposeOptimizedCompileJob(stale, false);
444 osr_buffer_[osr_buffer_cursor_] = job;
445 osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_;
450 bool OptimizingCompilerThread::IsOptimizerThread(Isolate* isolate) {
451 return isolate->concurrent_recompilation_enabled() &&
452 isolate->optimizing_compiler_thread()->IsOptimizerThread();
456 bool OptimizingCompilerThread::IsOptimizerThread() {
457 base::LockGuard<base::Mutex> lock_guard(&thread_id_mutex_);
458 return ThreadId::Current().ToInteger() == thread_id_;
463 } } // namespace v8::internal