Home | History | Annotate | Download | only in src
      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/optimizing-compiler-thread.h"
      6 
      7 #include "src/v8.h"
      8 
      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"
     14 
     15 namespace v8 {
     16 namespace internal {
     17 
     18 OptimizingCompilerThread::~OptimizingCompilerThread() {
     19   DCHECK_EQ(0, input_queue_length_);
     20   DeleteArray(input_queue_);
     21   if (FLAG_concurrent_osr) {
     22 #ifdef DEBUG
     23     for (int i = 0; i < osr_buffer_capacity_; i++) {
     24       CHECK_EQ(NULL, osr_buffer_[i]);
     25     }
     26 #endif
     27     DeleteArray(osr_buffer_);
     28   }
     29 }
     30 
     31 
     32 void OptimizingCompilerThread::Run() {
     33 #ifdef DEBUG
     34   { base::LockGuard<base::Mutex> lock_guard(&thread_id_mutex_);
     35     thread_id_ = ThreadId::Current().ToInteger();
     36   }
     37 #endif
     38   Isolate::SetIsolateThreadLocals(isolate_, NULL);
     39   DisallowHeapAllocation no_allocation;
     40   DisallowHandleAllocation no_handles;
     41   DisallowHandleDereference no_deref;
     42 
     43   base::ElapsedTimer total_timer;
     44   if (FLAG_trace_concurrent_recompilation) total_timer.Start();
     45 
     46   while (true) {
     47     input_queue_semaphore_.Wait();
     48     TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_);
     49 
     50     if (FLAG_concurrent_recompilation_delay != 0) {
     51       base::OS::Sleep(FLAG_concurrent_recompilation_delay);
     52     }
     53 
     54     switch (static_cast<StopFlag>(base::Acquire_Load(&stop_thread_))) {
     55       case CONTINUE:
     56         break;
     57       case STOP:
     58         if (FLAG_trace_concurrent_recompilation) {
     59           time_spent_total_ = total_timer.Elapsed();
     60         }
     61         stop_semaphore_.Signal();
     62         return;
     63       case FLUSH:
     64         // The main thread is blocked, waiting for the stop semaphore.
     65         { AllowHandleDereference allow_handle_dereference;
     66           FlushInputQueue(true);
     67         }
     68         base::Release_Store(&stop_thread_,
     69                             static_cast<base::AtomicWord>(CONTINUE));
     70         stop_semaphore_.Signal();
     71         // Return to start of consumer loop.
     72         continue;
     73     }
     74 
     75     base::ElapsedTimer compiling_timer;
     76     if (FLAG_trace_concurrent_recompilation) compiling_timer.Start();
     77 
     78     CompileNext();
     79 
     80     if (FLAG_trace_concurrent_recompilation) {
     81       time_spent_compiling_ += compiling_timer.Elapsed();
     82     }
     83   }
     84 }
     85 
     86 
     87 OptimizedCompileJob* OptimizingCompilerThread::NextInput() {
     88   base::LockGuard<base::Mutex> access_input_queue_(&input_queue_mutex_);
     89   if (input_queue_length_ == 0) return NULL;
     90   OptimizedCompileJob* job = input_queue_[InputQueueIndex(0)];
     91   DCHECK_NE(NULL, job);
     92   input_queue_shift_ = InputQueueIndex(1);
     93   input_queue_length_--;
     94   return job;
     95 }
     96 
     97 
     98 void OptimizingCompilerThread::CompileNext() {
     99   OptimizedCompileJob* job = NextInput();
    100   DCHECK_NE(NULL, job);
    101 
    102   // The function may have already been optimized by OSR.  Simply continue.
    103   OptimizedCompileJob::Status status = job->OptimizeGraph();
    104   USE(status);   // Prevent an unused-variable error in release mode.
    105   DCHECK(status != OptimizedCompileJob::FAILED);
    106 
    107   // The function may have already been optimized by OSR.  Simply continue.
    108   // Use a mutex to make sure that functions marked for install
    109   // are always also queued.
    110   output_queue_.Enqueue(job);
    111   isolate_->stack_guard()->RequestInstallCode();
    112 }
    113 
    114 
    115 static void DisposeOptimizedCompileJob(OptimizedCompileJob* job,
    116                                        bool restore_function_code) {
    117   // The recompile job is allocated in the CompilationInfo's zone.
    118   CompilationInfo* info = job->info();
    119   if (restore_function_code) {
    120     if (info->is_osr()) {
    121       if (!job->IsWaitingForInstall()) {
    122         // Remove stack check that guards OSR entry on original code.
    123         Handle<Code> code = info->unoptimized_code();
    124         uint32_t offset = code->TranslateAstIdToPcOffset(info->osr_ast_id());
    125         BackEdgeTable::RemoveStackCheck(code, offset);
    126       }
    127     } else {
    128       Handle<JSFunction> function = info->closure();
    129       function->ReplaceCode(function->shared()->code());
    130     }
    131   }
    132   delete info;
    133 }
    134 
    135 
    136 void OptimizingCompilerThread::FlushInputQueue(bool restore_function_code) {
    137   OptimizedCompileJob* job;
    138   while ((job = NextInput())) {
    139     // This should not block, since we have one signal on the input queue
    140     // semaphore corresponding to each element in the input queue.
    141     input_queue_semaphore_.Wait();
    142     // OSR jobs are dealt with separately.
    143     if (!job->info()->is_osr()) {
    144       DisposeOptimizedCompileJob(job, restore_function_code);
    145     }
    146   }
    147 }
    148 
    149 
    150 void OptimizingCompilerThread::FlushOutputQueue(bool restore_function_code) {
    151   OptimizedCompileJob* job;
    152   while (output_queue_.Dequeue(&job)) {
    153     // OSR jobs are dealt with separately.
    154     if (!job->info()->is_osr()) {
    155       DisposeOptimizedCompileJob(job, restore_function_code);
    156     }
    157   }
    158 }
    159 
    160 
    161 void OptimizingCompilerThread::FlushOsrBuffer(bool restore_function_code) {
    162   for (int i = 0; i < osr_buffer_capacity_; i++) {
    163     if (osr_buffer_[i] != NULL) {
    164       DisposeOptimizedCompileJob(osr_buffer_[i], restore_function_code);
    165       osr_buffer_[i] = NULL;
    166     }
    167   }
    168 }
    169 
    170 
    171 void OptimizingCompilerThread::Flush() {
    172   DCHECK(!IsOptimizerThread());
    173   base::Release_Store(&stop_thread_, static_cast<base::AtomicWord>(FLUSH));
    174   if (FLAG_block_concurrent_recompilation) Unblock();
    175   input_queue_semaphore_.Signal();
    176   stop_semaphore_.Wait();
    177   FlushOutputQueue(true);
    178   if (FLAG_concurrent_osr) FlushOsrBuffer(true);
    179   if (FLAG_trace_concurrent_recompilation) {
    180     PrintF("  ** Flushed concurrent recompilation queues.\n");
    181   }
    182 }
    183 
    184 
    185 void OptimizingCompilerThread::Stop() {
    186   DCHECK(!IsOptimizerThread());
    187   base::Release_Store(&stop_thread_, static_cast<base::AtomicWord>(STOP));
    188   if (FLAG_block_concurrent_recompilation) Unblock();
    189   input_queue_semaphore_.Signal();
    190   stop_semaphore_.Wait();
    191 
    192   if (FLAG_concurrent_recompilation_delay != 0) {
    193     // At this point the optimizing compiler thread's event loop has stopped.
    194     // There is no need for a mutex when reading input_queue_length_.
    195     while (input_queue_length_ > 0) CompileNext();
    196     InstallOptimizedFunctions();
    197   } else {
    198     FlushInputQueue(false);
    199     FlushOutputQueue(false);
    200   }
    201 
    202   if (FLAG_concurrent_osr) FlushOsrBuffer(false);
    203 
    204   if (FLAG_trace_concurrent_recompilation) {
    205     double percentage = time_spent_compiling_.PercentOf(time_spent_total_);
    206     PrintF("  ** Compiler thread did %.2f%% useful work\n", percentage);
    207   }
    208 
    209   if ((FLAG_trace_osr || FLAG_trace_concurrent_recompilation) &&
    210       FLAG_concurrent_osr) {
    211     PrintF("[COSR hit rate %d / %d]\n", osr_hits_, osr_attempts_);
    212   }
    213 
    214   Join();
    215 }
    216 
    217 
    218 void OptimizingCompilerThread::InstallOptimizedFunctions() {
    219   DCHECK(!IsOptimizerThread());
    220   HandleScope handle_scope(isolate_);
    221 
    222   OptimizedCompileJob* job;
    223   while (output_queue_.Dequeue(&job)) {
    224     CompilationInfo* info = job->info();
    225     Handle<JSFunction> function(*info->closure());
    226     if (info->is_osr()) {
    227       if (FLAG_trace_osr) {
    228         PrintF("[COSR - ");
    229         function->ShortPrint();
    230         PrintF(" is ready for install and entry at AST id %d]\n",
    231                info->osr_ast_id().ToInt());
    232       }
    233       job->WaitForInstall();
    234       // Remove stack check that guards OSR entry on original code.
    235       Handle<Code> code = info->unoptimized_code();
    236       uint32_t offset = code->TranslateAstIdToPcOffset(info->osr_ast_id());
    237       BackEdgeTable::RemoveStackCheck(code, offset);
    238     } else {
    239       if (function->IsOptimized()) {
    240         if (FLAG_trace_concurrent_recompilation) {
    241           PrintF("  ** Aborting compilation for ");
    242           function->ShortPrint();
    243           PrintF(" as it has already been optimized.\n");
    244         }
    245         DisposeOptimizedCompileJob(job, false);
    246       } else {
    247         Handle<Code> code = Compiler::GetConcurrentlyOptimizedCode(job);
    248         function->ReplaceCode(
    249             code.is_null() ? function->shared()->code() : *code);
    250       }
    251     }
    252   }
    253 }
    254 
    255 
    256 void OptimizingCompilerThread::QueueForOptimization(OptimizedCompileJob* job) {
    257   DCHECK(IsQueueAvailable());
    258   DCHECK(!IsOptimizerThread());
    259   CompilationInfo* info = job->info();
    260   if (info->is_osr()) {
    261     osr_attempts_++;
    262     AddToOsrBuffer(job);
    263     // Add job to the front of the input queue.
    264     base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_);
    265     DCHECK_LT(input_queue_length_, input_queue_capacity_);
    266     // Move shift_ back by one.
    267     input_queue_shift_ = InputQueueIndex(input_queue_capacity_ - 1);
    268     input_queue_[InputQueueIndex(0)] = job;
    269     input_queue_length_++;
    270   } else {
    271     // Add job to the back of the input queue.
    272     base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_);
    273     DCHECK_LT(input_queue_length_, input_queue_capacity_);
    274     input_queue_[InputQueueIndex(input_queue_length_)] = job;
    275     input_queue_length_++;
    276   }
    277   if (FLAG_block_concurrent_recompilation) {
    278     blocked_jobs_++;
    279   } else {
    280     input_queue_semaphore_.Signal();
    281   }
    282 }
    283 
    284 
    285 void OptimizingCompilerThread::Unblock() {
    286   DCHECK(!IsOptimizerThread());
    287   while (blocked_jobs_ > 0) {
    288     input_queue_semaphore_.Signal();
    289     blocked_jobs_--;
    290   }
    291 }
    292 
    293 
    294 OptimizedCompileJob* OptimizingCompilerThread::FindReadyOSRCandidate(
    295     Handle<JSFunction> function, BailoutId osr_ast_id) {
    296   DCHECK(!IsOptimizerThread());
    297   for (int i = 0; i < osr_buffer_capacity_; i++) {
    298     OptimizedCompileJob* current = osr_buffer_[i];
    299     if (current != NULL &&
    300         current->IsWaitingForInstall() &&
    301         current->info()->HasSameOsrEntry(function, osr_ast_id)) {
    302       osr_hits_++;
    303       osr_buffer_[i] = NULL;
    304       return current;
    305     }
    306   }
    307   return NULL;
    308 }
    309 
    310 
    311 bool OptimizingCompilerThread::IsQueuedForOSR(Handle<JSFunction> function,
    312                                               BailoutId osr_ast_id) {
    313   DCHECK(!IsOptimizerThread());
    314   for (int i = 0; i < osr_buffer_capacity_; i++) {
    315     OptimizedCompileJob* current = osr_buffer_[i];
    316     if (current != NULL &&
    317         current->info()->HasSameOsrEntry(function, osr_ast_id)) {
    318       return !current->IsWaitingForInstall();
    319     }
    320   }
    321   return false;
    322 }
    323 
    324 
    325 bool OptimizingCompilerThread::IsQueuedForOSR(JSFunction* function) {
    326   DCHECK(!IsOptimizerThread());
    327   for (int i = 0; i < osr_buffer_capacity_; i++) {
    328     OptimizedCompileJob* current = osr_buffer_[i];
    329     if (current != NULL && *current->info()->closure() == function) {
    330       return !current->IsWaitingForInstall();
    331     }
    332   }
    333   return false;
    334 }
    335 
    336 
    337 void OptimizingCompilerThread::AddToOsrBuffer(OptimizedCompileJob* job) {
    338   DCHECK(!IsOptimizerThread());
    339   // Find the next slot that is empty or has a stale job.
    340   OptimizedCompileJob* stale = NULL;
    341   while (true) {
    342     stale = osr_buffer_[osr_buffer_cursor_];
    343     if (stale == NULL || stale->IsWaitingForInstall()) break;
    344     osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_;
    345   }
    346 
    347   // Add to found slot and dispose the evicted job.
    348   if (stale != NULL) {
    349     DCHECK(stale->IsWaitingForInstall());
    350     CompilationInfo* info = stale->info();
    351     if (FLAG_trace_osr) {
    352       PrintF("[COSR - Discarded ");
    353       info->closure()->PrintName();
    354       PrintF(", AST id %d]\n", info->osr_ast_id().ToInt());
    355     }
    356     DisposeOptimizedCompileJob(stale, false);
    357   }
    358   osr_buffer_[osr_buffer_cursor_] = job;
    359   osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_;
    360 }
    361 
    362 
    363 #ifdef DEBUG
    364 bool OptimizingCompilerThread::IsOptimizerThread(Isolate* isolate) {
    365   return isolate->concurrent_recompilation_enabled() &&
    366          isolate->optimizing_compiler_thread()->IsOptimizerThread();
    367 }
    368 
    369 
    370 bool OptimizingCompilerThread::IsOptimizerThread() {
    371   base::LockGuard<base::Mutex> lock_guard(&thread_id_mutex_);
    372   return ThreadId::Current().ToInteger() == thread_id_;
    373 }
    374 #endif
    375 
    376 
    377 } }  // namespace v8::internal
    378