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/compilation-info.h" 9 #include "src/compiler.h" 10 #include "src/full-codegen/full-codegen.h" 11 #include "src/isolate.h" 12 #include "src/tracing/trace-event.h" 13 #include "src/v8.h" 14 15 namespace v8 { 16 namespace internal { 17 18 namespace { 19 20 void DisposeCompilationJob(CompilationJob* job, bool restore_function_code) { 21 if (restore_function_code) { 22 Handle<JSFunction> function = job->info()->closure(); 23 function->ReplaceCode(function->shared()->code()); 24 // TODO(mvstanton): We can't call ensureliterals here due to allocation, 25 // but we probably shouldn't call ReplaceCode either, as this 26 // sometimes runs on the worker thread! 27 // JSFunction::EnsureLiterals(function); 28 } 29 delete job; 30 } 31 32 } // namespace 33 34 class OptimizingCompileDispatcher::CompileTask : public v8::Task { 35 public: 36 explicit CompileTask(Isolate* isolate) : isolate_(isolate) { 37 OptimizingCompileDispatcher* dispatcher = 38 isolate_->optimizing_compile_dispatcher(); 39 base::LockGuard<base::Mutex> lock_guard(&dispatcher->ref_count_mutex_); 40 ++dispatcher->ref_count_; 41 } 42 43 virtual ~CompileTask() {} 44 45 private: 46 // v8::Task overrides. 47 void Run() override { 48 DisallowHeapAllocation no_allocation; 49 DisallowHandleAllocation no_handles; 50 DisallowHandleDereference no_deref; 51 52 OptimizingCompileDispatcher* dispatcher = 53 isolate_->optimizing_compile_dispatcher(); 54 { 55 TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_); 56 57 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), 58 "V8.RecompileConcurrent"); 59 60 if (dispatcher->recompilation_delay_ != 0) { 61 base::OS::Sleep(base::TimeDelta::FromMilliseconds( 62 dispatcher->recompilation_delay_)); 63 } 64 65 dispatcher->CompileNext(dispatcher->NextInput(true)); 66 } 67 { 68 base::LockGuard<base::Mutex> lock_guard(&dispatcher->ref_count_mutex_); 69 if (--dispatcher->ref_count_ == 0) { 70 dispatcher->ref_count_zero_.NotifyOne(); 71 } 72 } 73 } 74 75 Isolate* isolate_; 76 77 DISALLOW_COPY_AND_ASSIGN(CompileTask); 78 }; 79 80 OptimizingCompileDispatcher::~OptimizingCompileDispatcher() { 81 #ifdef DEBUG 82 { 83 base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_); 84 DCHECK_EQ(0, ref_count_); 85 } 86 #endif 87 DCHECK_EQ(0, input_queue_length_); 88 DeleteArray(input_queue_); 89 } 90 91 CompilationJob* OptimizingCompileDispatcher::NextInput(bool check_if_flushing) { 92 base::LockGuard<base::Mutex> access_input_queue_(&input_queue_mutex_); 93 if (input_queue_length_ == 0) return NULL; 94 CompilationJob* job = input_queue_[InputQueueIndex(0)]; 95 DCHECK_NOT_NULL(job); 96 input_queue_shift_ = InputQueueIndex(1); 97 input_queue_length_--; 98 if (check_if_flushing) { 99 if (static_cast<ModeFlag>(base::Acquire_Load(&mode_)) == FLUSH) { 100 AllowHandleDereference allow_handle_dereference; 101 DisposeCompilationJob(job, true); 102 return NULL; 103 } 104 } 105 return job; 106 } 107 108 void OptimizingCompileDispatcher::CompileNext(CompilationJob* job) { 109 if (!job) return; 110 111 // The function may have already been optimized by OSR. Simply continue. 112 CompilationJob::Status status = job->ExecuteJob(); 113 USE(status); // Prevent an unused-variable error. 114 115 // The function may have already been optimized by OSR. Simply continue. 116 // Use a mutex to make sure that functions marked for install 117 // are always also queued. 118 base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_); 119 output_queue_.push(job); 120 isolate_->stack_guard()->RequestInstallCode(); 121 } 122 123 void OptimizingCompileDispatcher::FlushOutputQueue(bool restore_function_code) { 124 for (;;) { 125 CompilationJob* job = NULL; 126 { 127 base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_); 128 if (output_queue_.empty()) return; 129 job = output_queue_.front(); 130 output_queue_.pop(); 131 } 132 133 DisposeCompilationJob(job, restore_function_code); 134 } 135 } 136 137 void OptimizingCompileDispatcher::Flush(BlockingBehavior blocking_behavior) { 138 if (FLAG_block_concurrent_recompilation) Unblock(); 139 if (blocking_behavior == BlockingBehavior::kDontBlock) { 140 base::LockGuard<base::Mutex> access_input_queue_(&input_queue_mutex_); 141 while (input_queue_length_ > 0) { 142 CompilationJob* job = input_queue_[InputQueueIndex(0)]; 143 DCHECK_NOT_NULL(job); 144 input_queue_shift_ = InputQueueIndex(1); 145 input_queue_length_--; 146 DisposeCompilationJob(job, true); 147 } 148 FlushOutputQueue(true); 149 if (FLAG_trace_concurrent_recompilation) { 150 PrintF(" ** Flushed concurrent recompilation queues (not blocking).\n"); 151 } 152 return; 153 } 154 base::Release_Store(&mode_, static_cast<base::AtomicWord>(FLUSH)); 155 if (FLAG_block_concurrent_recompilation) Unblock(); 156 { 157 base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_); 158 while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_); 159 base::Release_Store(&mode_, static_cast<base::AtomicWord>(COMPILE)); 160 } 161 FlushOutputQueue(true); 162 if (FLAG_trace_concurrent_recompilation) { 163 PrintF(" ** Flushed concurrent recompilation queues.\n"); 164 } 165 } 166 167 void OptimizingCompileDispatcher::Stop() { 168 base::Release_Store(&mode_, static_cast<base::AtomicWord>(FLUSH)); 169 if (FLAG_block_concurrent_recompilation) Unblock(); 170 { 171 base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_); 172 while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_); 173 base::Release_Store(&mode_, static_cast<base::AtomicWord>(COMPILE)); 174 } 175 176 if (recompilation_delay_ != 0) { 177 // At this point the optimizing compiler thread's event loop has stopped. 178 // There is no need for a mutex when reading input_queue_length_. 179 while (input_queue_length_ > 0) CompileNext(NextInput()); 180 InstallOptimizedFunctions(); 181 } else { 182 FlushOutputQueue(false); 183 } 184 } 185 186 void OptimizingCompileDispatcher::InstallOptimizedFunctions() { 187 HandleScope handle_scope(isolate_); 188 189 for (;;) { 190 CompilationJob* job = NULL; 191 { 192 base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_); 193 if (output_queue_.empty()) return; 194 job = output_queue_.front(); 195 output_queue_.pop(); 196 } 197 CompilationInfo* info = job->info(); 198 Handle<JSFunction> function(*info->closure()); 199 if (function->IsOptimized()) { 200 if (FLAG_trace_concurrent_recompilation) { 201 PrintF(" ** Aborting compilation for "); 202 function->ShortPrint(); 203 PrintF(" as it has already been optimized.\n"); 204 } 205 DisposeCompilationJob(job, false); 206 } else { 207 Compiler::FinalizeCompilationJob(job); 208 } 209 } 210 } 211 212 void OptimizingCompileDispatcher::QueueForOptimization(CompilationJob* job) { 213 DCHECK(IsQueueAvailable()); 214 { 215 // Add job to the back of the input queue. 216 base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_); 217 DCHECK_LT(input_queue_length_, input_queue_capacity_); 218 input_queue_[InputQueueIndex(input_queue_length_)] = job; 219 input_queue_length_++; 220 } 221 if (FLAG_block_concurrent_recompilation) { 222 blocked_jobs_++; 223 } else { 224 V8::GetCurrentPlatform()->CallOnBackgroundThread( 225 new CompileTask(isolate_), v8::Platform::kShortRunningTask); 226 } 227 } 228 229 void OptimizingCompileDispatcher::Unblock() { 230 while (blocked_jobs_ > 0) { 231 V8::GetCurrentPlatform()->CallOnBackgroundThread( 232 new CompileTask(isolate_), v8::Platform::kShortRunningTask); 233 blocked_jobs_--; 234 } 235 } 236 237 } // namespace internal 238 } // namespace v8 239