| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 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 namespace { | 
|  | 19 | 
|  | 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) { | 
|  | 25     if (info->is_osr()) { | 
|  | 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); | 
|  | 31       } | 
|  | 32     } else { | 
|  | 33       Handle<JSFunction> function = info->closure(); | 
|  | 34       function->ReplaceCode(function->shared()->code()); | 
|  | 35     } | 
|  | 36   } | 
|  | 37   delete info; | 
|  | 38 } | 
|  | 39 | 
|  | 40 }  // namespace | 
|  | 41 | 
|  | 42 | 
|  | 43 class OptimizingCompilerThread::CompileTask : public v8::Task { | 
|  | 44  public: | 
|  | 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_); | 
|  | 48     ++thread->ref_count_; | 
|  | 49   } | 
|  | 50 | 
|  | 51   virtual ~CompileTask() {} | 
|  | 52 | 
|  | 53  private: | 
|  | 54   // v8::Task overrides. | 
|  | 55   void Run() OVERRIDE { | 
|  | 56     DisallowHeapAllocation no_allocation; | 
|  | 57     DisallowHandleAllocation no_handles; | 
|  | 58     DisallowHandleDereference no_deref; | 
|  | 59 | 
|  | 60     OptimizingCompilerThread* thread = isolate_->optimizing_compiler_thread(); | 
|  | 61     { | 
|  | 62       TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_); | 
|  | 63 | 
|  | 64       if (thread->recompilation_delay_ != 0) { | 
|  | 65         base::OS::Sleep(thread->recompilation_delay_); | 
|  | 66       } | 
|  | 67 | 
|  | 68       thread->CompileNext(thread->NextInput(true)); | 
|  | 69     } | 
|  | 70     { | 
|  | 71       base::LockGuard<base::Mutex> lock_guard(&thread->ref_count_mutex_); | 
|  | 72       if (--thread->ref_count_ == 0) { | 
|  | 73         thread->ref_count_zero_.NotifyOne(); | 
|  | 74       } | 
|  | 75     } | 
|  | 76   } | 
|  | 77 | 
|  | 78   Isolate* isolate_; | 
|  | 79 | 
|  | 80   DISALLOW_COPY_AND_ASSIGN(CompileTask); | 
|  | 81 }; | 
|  | 82 | 
|  | 83 | 
|  | 84 OptimizingCompilerThread::~OptimizingCompilerThread() { | 
|  | 85 #ifdef DEBUG | 
|  | 86   { | 
|  | 87     base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_); | 
|  | 88     DCHECK_EQ(0, ref_count_); | 
|  | 89   } | 
|  | 90 #endif | 
|  | 91   DCHECK_EQ(0, input_queue_length_); | 
|  | 92   DeleteArray(input_queue_); | 
|  | 93   if (FLAG_concurrent_osr) { | 
|  | 94 #ifdef DEBUG | 
|  | 95     for (int i = 0; i < osr_buffer_capacity_; i++) { | 
|  | 96       CHECK_NULL(osr_buffer_[i]); | 
|  | 97     } | 
|  | 98 #endif | 
|  | 99     DeleteArray(osr_buffer_); | 
|  | 100   } | 
|  | 101 } | 
|  | 102 | 
|  | 103 | 
|  | 104 void OptimizingCompilerThread::Run() { | 
|  | 105 #ifdef DEBUG | 
|  | 106   { base::LockGuard<base::Mutex> lock_guard(&thread_id_mutex_); | 
|  | 107     thread_id_ = ThreadId::Current().ToInteger(); | 
|  | 108   } | 
|  | 109 #endif | 
|  | 110   DisallowHeapAllocation no_allocation; | 
|  | 111   DisallowHandleAllocation no_handles; | 
|  | 112   DisallowHandleDereference no_deref; | 
|  | 113 | 
|  | 114   if (job_based_recompilation_) { | 
|  | 115     return; | 
|  | 116   } | 
|  | 117 | 
|  | 118   base::ElapsedTimer total_timer; | 
|  | 119   if (tracing_enabled_) total_timer.Start(); | 
|  | 120 | 
|  | 121   while (true) { | 
|  | 122     input_queue_semaphore_.Wait(); | 
|  | 123     TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_); | 
|  | 124 | 
|  | 125     if (recompilation_delay_ != 0) { | 
|  | 126       base::OS::Sleep(recompilation_delay_); | 
|  | 127     } | 
|  | 128 | 
|  | 129     switch (static_cast<StopFlag>(base::Acquire_Load(&stop_thread_))) { | 
|  | 130       case CONTINUE: | 
|  | 131         break; | 
|  | 132       case STOP: | 
|  | 133         if (tracing_enabled_) { | 
|  | 134           time_spent_total_ = total_timer.Elapsed(); | 
|  | 135         } | 
|  | 136         stop_semaphore_.Signal(); | 
|  | 137         return; | 
|  | 138       case FLUSH: | 
|  | 139         // The main thread is blocked, waiting for the stop semaphore. | 
|  | 140         { AllowHandleDereference allow_handle_dereference; | 
|  | 141           FlushInputQueue(true); | 
|  | 142         } | 
|  | 143         base::Release_Store(&stop_thread_, | 
|  | 144                             static_cast<base::AtomicWord>(CONTINUE)); | 
|  | 145         stop_semaphore_.Signal(); | 
|  | 146         // Return to start of consumer loop. | 
|  | 147         continue; | 
|  | 148     } | 
|  | 149 | 
|  | 150     base::ElapsedTimer compiling_timer; | 
|  | 151     if (tracing_enabled_) compiling_timer.Start(); | 
|  | 152 | 
|  | 153     CompileNext(NextInput()); | 
|  | 154 | 
|  | 155     if (tracing_enabled_) { | 
|  | 156       time_spent_compiling_ += compiling_timer.Elapsed(); | 
|  | 157     } | 
|  | 158   } | 
|  | 159 } | 
|  | 160 | 
|  | 161 | 
|  | 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); | 
|  | 175       } | 
|  | 176       return NULL; | 
|  | 177     } | 
|  | 178   } | 
|  | 179   return job; | 
|  | 180 } | 
|  | 181 | 
|  | 182 | 
|  | 183 void OptimizingCompilerThread::CompileNext(OptimizedCompileJob* job) { | 
|  | 184   if (!job) return; | 
|  | 185 | 
|  | 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); | 
|  | 190 | 
|  | 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(); | 
|  | 198 } | 
|  | 199 | 
|  | 200 | 
|  | 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); | 
|  | 211     } | 
|  | 212   } | 
|  | 213 } | 
|  | 214 | 
|  | 215 | 
|  | 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); | 
|  | 222     } | 
|  | 223   } | 
|  | 224 } | 
|  | 225 | 
|  | 226 | 
|  | 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; | 
|  | 232     } | 
|  | 233   } | 
|  | 234 } | 
|  | 235 | 
|  | 236 | 
|  | 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(); | 
|  | 244   } else { | 
|  | 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)); | 
|  | 248   } | 
|  | 249   FlushOutputQueue(true); | 
|  | 250   if (FLAG_concurrent_osr) FlushOsrBuffer(true); | 
|  | 251   if (tracing_enabled_) { | 
|  | 252     PrintF("  ** Flushed concurrent recompilation queues.\n"); | 
|  | 253   } | 
|  | 254 } | 
|  | 255 | 
|  | 256 | 
|  | 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(); | 
|  | 264   } else { | 
|  | 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)); | 
|  | 268   } | 
|  | 269 | 
|  | 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(); | 
|  | 275   } else { | 
|  | 276     FlushInputQueue(false); | 
|  | 277     FlushOutputQueue(false); | 
|  | 278   } | 
|  | 279 | 
|  | 280   if (FLAG_concurrent_osr) FlushOsrBuffer(false); | 
|  | 281 | 
|  | 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); | 
|  | 286   } | 
|  | 287 | 
|  | 288   if ((FLAG_trace_osr || tracing_enabled_) && FLAG_concurrent_osr) { | 
|  | 289     PrintF("[COSR hit rate %d / %d]\n", osr_hits_, osr_attempts_); | 
|  | 290   } | 
|  | 291 | 
|  | 292   Join(); | 
|  | 293 } | 
|  | 294 | 
|  | 295 | 
|  | 296 void OptimizingCompilerThread::InstallOptimizedFunctions() { | 
|  | 297   DCHECK(!IsOptimizerThread()); | 
|  | 298   HandleScope handle_scope(isolate_); | 
|  | 299 | 
|  | 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) { | 
|  | 306         PrintF("[COSR - "); | 
|  | 307         function->ShortPrint(); | 
|  | 308         PrintF(" is ready for install and entry at AST id %d]\n", | 
|  | 309                info->osr_ast_id().ToInt()); | 
|  | 310       } | 
|  | 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); | 
|  | 316     } else { | 
|  | 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"); | 
|  | 322         } | 
|  | 323         DisposeOptimizedCompileJob(job, false); | 
|  | 324       } else { | 
|  | 325         Handle<Code> code = Compiler::GetConcurrentlyOptimizedCode(job); | 
|  | 326         function->ReplaceCode( | 
|  | 327             code.is_null() ? function->shared()->code() : *code); | 
|  | 328       } | 
|  | 329     } | 
|  | 330   } | 
|  | 331 } | 
|  | 332 | 
|  | 333 | 
|  | 334 void OptimizingCompilerThread::QueueForOptimization(OptimizedCompileJob* job) { | 
|  | 335   DCHECK(IsQueueAvailable()); | 
|  | 336   DCHECK(!IsOptimizerThread()); | 
|  | 337   CompilationInfo* info = job->info(); | 
|  | 338   if (info->is_osr()) { | 
|  | 339     osr_attempts_++; | 
|  | 340     AddToOsrBuffer(job); | 
|  | 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_++; | 
|  | 348   } else { | 
|  | 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_++; | 
|  | 354   } | 
|  | 355   if (FLAG_block_concurrent_recompilation) { | 
|  | 356     blocked_jobs_++; | 
|  | 357   } else if (job_based_recompilation_) { | 
|  | 358     V8::GetCurrentPlatform()->CallOnBackgroundThread( | 
|  | 359         new CompileTask(isolate_), v8::Platform::kShortRunningTask); | 
|  | 360   } else { | 
|  | 361     input_queue_semaphore_.Signal(); | 
|  | 362   } | 
|  | 363 } | 
|  | 364 | 
|  | 365 | 
|  | 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); | 
|  | 372     } else { | 
|  | 373       input_queue_semaphore_.Signal(); | 
|  | 374     } | 
|  | 375     blocked_jobs_--; | 
|  | 376   } | 
|  | 377 } | 
|  | 378 | 
|  | 379 | 
|  | 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)) { | 
|  | 388       osr_hits_++; | 
|  | 389       osr_buffer_[i] = NULL; | 
|  | 390       return current; | 
|  | 391     } | 
|  | 392   } | 
|  | 393   return NULL; | 
|  | 394 } | 
|  | 395 | 
|  | 396 | 
|  | 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(); | 
|  | 405     } | 
|  | 406   } | 
|  | 407   return false; | 
|  | 408 } | 
|  | 409 | 
|  | 410 | 
|  | 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(); | 
|  | 417     } | 
|  | 418   } | 
|  | 419   return false; | 
|  | 420 } | 
|  | 421 | 
|  | 422 | 
|  | 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; | 
|  | 427   while (true) { | 
|  | 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_; | 
|  | 431   } | 
|  | 432 | 
|  | 433   // Add to found slot and dispose the evicted job. | 
|  | 434   if (stale != NULL) { | 
|  | 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()); | 
|  | 441     } | 
|  | 442     DisposeOptimizedCompileJob(stale, false); | 
|  | 443   } | 
|  | 444   osr_buffer_[osr_buffer_cursor_] = job; | 
|  | 445   osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_; | 
|  | 446 } | 
|  | 447 | 
|  | 448 | 
|  | 449 #ifdef DEBUG | 
|  | 450 bool OptimizingCompilerThread::IsOptimizerThread(Isolate* isolate) { | 
|  | 451   return isolate->concurrent_recompilation_enabled() && | 
|  | 452          isolate->optimizing_compiler_thread()->IsOptimizerThread(); | 
|  | 453 } | 
|  | 454 | 
|  | 455 | 
|  | 456 bool OptimizingCompilerThread::IsOptimizerThread() { | 
|  | 457   base::LockGuard<base::Mutex> lock_guard(&thread_id_mutex_); | 
|  | 458   return ThreadId::Current().ToInteger() == thread_id_; | 
|  | 459 } | 
|  | 460 #endif | 
|  | 461 | 
|  | 462 | 
|  | 463 } }  // namespace v8::internal | 
| OLD | NEW | 
|---|