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"
18 class OptimizingCompilerThread::CompileTask : public v8::Task {
20 CompileTask(Isolate* isolate, OptimizedCompileJob* job)
21 : isolate_(isolate), job_(job) {}
23 virtual ~CompileTask() {}
26 // v8::Task overrides.
27 virtual void Run() OVERRIDE {
28 DisallowHeapAllocation no_allocation;
29 DisallowHandleAllocation no_handles;
30 DisallowHandleDereference no_deref;
32 // The function may have already been optimized by OSR. Simply continue.
33 OptimizedCompileJob::Status status = job_->OptimizeGraph();
34 USE(status); // Prevent an unused-variable error in release mode.
35 DCHECK(status != OptimizedCompileJob::FAILED);
37 // The function may have already been optimized by OSR. Simply continue.
38 // Use a mutex to make sure that functions marked for install
39 // are always also queued.
41 base::LockGuard<base::Mutex> lock_guard(
42 &isolate_->optimizing_compiler_thread()->output_queue_mutex_);
43 isolate_->optimizing_compiler_thread()->output_queue_.Enqueue(job_);
45 isolate_->stack_guard()->RequestInstallCode();
47 base::LockGuard<base::Mutex> lock_guard(
48 &isolate_->optimizing_compiler_thread()->input_queue_mutex_);
49 isolate_->optimizing_compiler_thread()->input_queue_length_--;
51 isolate_->optimizing_compiler_thread()->input_queue_semaphore_.Signal();
55 OptimizedCompileJob* job_;
57 DISALLOW_COPY_AND_ASSIGN(CompileTask);
61 OptimizingCompilerThread::~OptimizingCompilerThread() {
62 DCHECK_EQ(0, input_queue_length_);
63 DeleteArray(input_queue_);
64 if (FLAG_concurrent_osr) {
66 for (int i = 0; i < osr_buffer_capacity_; i++) {
67 CHECK_EQ(NULL, osr_buffer_[i]);
70 DeleteArray(osr_buffer_);
75 void OptimizingCompilerThread::Run() {
77 { base::LockGuard<base::Mutex> lock_guard(&thread_id_mutex_);
78 thread_id_ = ThreadId::Current().ToInteger();
81 DisallowHeapAllocation no_allocation;
82 DisallowHandleAllocation no_handles;
83 DisallowHandleDereference no_deref;
85 if (job_based_recompilation_) {
89 base::ElapsedTimer total_timer;
90 if (tracing_enabled_) total_timer.Start();
93 input_queue_semaphore_.Wait();
94 TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_);
96 if (FLAG_concurrent_recompilation_delay != 0) {
97 base::OS::Sleep(FLAG_concurrent_recompilation_delay);
100 switch (static_cast<StopFlag>(base::Acquire_Load(&stop_thread_))) {
104 if (tracing_enabled_) {
105 time_spent_total_ = total_timer.Elapsed();
107 stop_semaphore_.Signal();
110 // The main thread is blocked, waiting for the stop semaphore.
111 { AllowHandleDereference allow_handle_dereference;
112 FlushInputQueue(true);
114 base::Release_Store(&stop_thread_,
115 static_cast<base::AtomicWord>(CONTINUE));
116 stop_semaphore_.Signal();
117 // Return to start of consumer loop.
121 base::ElapsedTimer compiling_timer;
122 if (tracing_enabled_) compiling_timer.Start();
126 if (tracing_enabled_) {
127 time_spent_compiling_ += compiling_timer.Elapsed();
133 OptimizedCompileJob* OptimizingCompilerThread::NextInput() {
134 base::LockGuard<base::Mutex> access_input_queue_(&input_queue_mutex_);
135 DCHECK(!job_based_recompilation_);
136 if (input_queue_length_ == 0) return NULL;
137 OptimizedCompileJob* job = input_queue_[InputQueueIndex(0)];
138 DCHECK_NE(NULL, job);
139 input_queue_shift_ = InputQueueIndex(1);
140 input_queue_length_--;
145 void OptimizingCompilerThread::CompileNext() {
146 OptimizedCompileJob* job = NextInput();
147 DCHECK_NE(NULL, job);
149 // The function may have already been optimized by OSR. Simply continue.
150 OptimizedCompileJob::Status status = job->OptimizeGraph();
151 USE(status); // Prevent an unused-variable error in release mode.
152 DCHECK(status != OptimizedCompileJob::FAILED);
154 // The function may have already been optimized by OSR. Simply continue.
155 // Use a mutex to make sure that functions marked for install
156 // are always also queued.
157 output_queue_.Enqueue(job);
158 isolate_->stack_guard()->RequestInstallCode();
162 static void DisposeOptimizedCompileJob(OptimizedCompileJob* job,
163 bool restore_function_code) {
164 // The recompile job is allocated in the CompilationInfo's zone.
165 CompilationInfo* info = job->info();
166 if (restore_function_code) {
167 if (info->is_osr()) {
168 if (!job->IsWaitingForInstall()) {
169 // Remove stack check that guards OSR entry on original code.
170 Handle<Code> code = info->unoptimized_code();
171 uint32_t offset = code->TranslateAstIdToPcOffset(info->osr_ast_id());
172 BackEdgeTable::RemoveStackCheck(code, offset);
175 Handle<JSFunction> function = info->closure();
176 function->ReplaceCode(function->shared()->code());
183 void OptimizingCompilerThread::FlushInputQueue(bool restore_function_code) {
184 DCHECK(!job_based_recompilation_);
185 OptimizedCompileJob* job;
186 while ((job = NextInput())) {
187 // This should not block, since we have one signal on the input queue
188 // semaphore corresponding to each element in the input queue.
189 input_queue_semaphore_.Wait();
190 // OSR jobs are dealt with separately.
191 if (!job->info()->is_osr()) {
192 DisposeOptimizedCompileJob(job, restore_function_code);
198 void OptimizingCompilerThread::FlushOutputQueue(bool restore_function_code) {
199 OptimizedCompileJob* job;
200 while (output_queue_.Dequeue(&job)) {
201 // OSR jobs are dealt with separately.
202 if (!job->info()->is_osr()) {
203 DisposeOptimizedCompileJob(job, restore_function_code);
209 void OptimizingCompilerThread::FlushOsrBuffer(bool restore_function_code) {
210 for (int i = 0; i < osr_buffer_capacity_; i++) {
211 if (osr_buffer_[i] != NULL) {
212 DisposeOptimizedCompileJob(osr_buffer_[i], restore_function_code);
213 osr_buffer_[i] = NULL;
219 void OptimizingCompilerThread::Flush() {
220 DCHECK(!IsOptimizerThread());
221 base::Release_Store(&stop_thread_, static_cast<base::AtomicWord>(FLUSH));
222 if (FLAG_block_concurrent_recompilation) Unblock();
223 if (!job_based_recompilation_) {
224 input_queue_semaphore_.Signal();
225 stop_semaphore_.Wait();
227 FlushOutputQueue(true);
228 if (FLAG_concurrent_osr) FlushOsrBuffer(true);
229 if (tracing_enabled_) {
230 PrintF(" ** Flushed concurrent recompilation queues.\n");
235 void OptimizingCompilerThread::Stop() {
236 DCHECK(!IsOptimizerThread());
237 base::Release_Store(&stop_thread_, static_cast<base::AtomicWord>(STOP));
238 if (FLAG_block_concurrent_recompilation) Unblock();
239 if (!job_based_recompilation_) {
240 input_queue_semaphore_.Signal();
241 stop_semaphore_.Wait();
244 if (job_based_recompilation_) {
247 base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_);
248 if (!input_queue_length_) break;
250 input_queue_semaphore_.Wait();
252 } else if (FLAG_concurrent_recompilation_delay != 0) {
253 // At this point the optimizing compiler thread's event loop has stopped.
254 // There is no need for a mutex when reading input_queue_length_.
255 while (input_queue_length_ > 0) CompileNext();
256 InstallOptimizedFunctions();
258 FlushInputQueue(false);
259 FlushOutputQueue(false);
262 if (FLAG_concurrent_osr) FlushOsrBuffer(false);
264 if (tracing_enabled_) {
265 double percentage = time_spent_compiling_.PercentOf(time_spent_total_);
266 PrintF(" ** Compiler thread did %.2f%% useful work\n", percentage);
269 if ((FLAG_trace_osr || tracing_enabled_) && FLAG_concurrent_osr) {
270 PrintF("[COSR hit rate %d / %d]\n", osr_hits_, osr_attempts_);
277 void OptimizingCompilerThread::InstallOptimizedFunctions() {
278 DCHECK(!IsOptimizerThread());
279 HandleScope handle_scope(isolate_);
281 OptimizedCompileJob* job;
282 while (output_queue_.Dequeue(&job)) {
283 CompilationInfo* info = job->info();
284 Handle<JSFunction> function(*info->closure());
285 if (info->is_osr()) {
286 if (FLAG_trace_osr) {
288 function->ShortPrint();
289 PrintF(" is ready for install and entry at AST id %d]\n",
290 info->osr_ast_id().ToInt());
292 job->WaitForInstall();
293 // Remove stack check that guards OSR entry on original code.
294 Handle<Code> code = info->unoptimized_code();
295 uint32_t offset = code->TranslateAstIdToPcOffset(info->osr_ast_id());
296 BackEdgeTable::RemoveStackCheck(code, offset);
298 if (function->IsOptimized()) {
299 if (tracing_enabled_) {
300 PrintF(" ** Aborting compilation for ");
301 function->ShortPrint();
302 PrintF(" as it has already been optimized.\n");
304 DisposeOptimizedCompileJob(job, false);
306 Handle<Code> code = Compiler::GetConcurrentlyOptimizedCode(job);
307 function->ReplaceCode(
308 code.is_null() ? function->shared()->code() : *code);
315 void OptimizingCompilerThread::QueueForOptimization(OptimizedCompileJob* job) {
316 DCHECK(IsQueueAvailable());
317 DCHECK(!IsOptimizerThread());
318 CompilationInfo* info = job->info();
319 if (info->is_osr()) {
322 // Add job to the front of the input queue.
323 base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_);
324 DCHECK_LT(input_queue_length_, input_queue_capacity_);
325 // Move shift_ back by one.
326 input_queue_shift_ = InputQueueIndex(input_queue_capacity_ - 1);
327 input_queue_[InputQueueIndex(0)] = job;
328 input_queue_length_++;
330 // Add job to the back of the input queue.
331 base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_);
332 DCHECK_LT(input_queue_length_, input_queue_capacity_);
333 input_queue_[InputQueueIndex(input_queue_length_)] = job;
334 input_queue_length_++;
336 if (job_based_recompilation_) {
337 V8::GetCurrentPlatform()->CallOnBackgroundThread(
338 new CompileTask(isolate_, job), v8::Platform::kShortRunningTask);
339 } else if (FLAG_block_concurrent_recompilation) {
342 input_queue_semaphore_.Signal();
347 void OptimizingCompilerThread::Unblock() {
348 DCHECK(!IsOptimizerThread());
349 if (job_based_recompilation_) {
352 while (blocked_jobs_ > 0) {
353 input_queue_semaphore_.Signal();
359 OptimizedCompileJob* OptimizingCompilerThread::FindReadyOSRCandidate(
360 Handle<JSFunction> function, BailoutId osr_ast_id) {
361 DCHECK(!IsOptimizerThread());
362 for (int i = 0; i < osr_buffer_capacity_; i++) {
363 OptimizedCompileJob* current = osr_buffer_[i];
364 if (current != NULL &&
365 current->IsWaitingForInstall() &&
366 current->info()->HasSameOsrEntry(function, osr_ast_id)) {
368 osr_buffer_[i] = NULL;
376 bool OptimizingCompilerThread::IsQueuedForOSR(Handle<JSFunction> function,
377 BailoutId osr_ast_id) {
378 DCHECK(!IsOptimizerThread());
379 for (int i = 0; i < osr_buffer_capacity_; i++) {
380 OptimizedCompileJob* current = osr_buffer_[i];
381 if (current != NULL &&
382 current->info()->HasSameOsrEntry(function, osr_ast_id)) {
383 return !current->IsWaitingForInstall();
390 bool OptimizingCompilerThread::IsQueuedForOSR(JSFunction* function) {
391 DCHECK(!IsOptimizerThread());
392 for (int i = 0; i < osr_buffer_capacity_; i++) {
393 OptimizedCompileJob* current = osr_buffer_[i];
394 if (current != NULL && *current->info()->closure() == function) {
395 return !current->IsWaitingForInstall();
402 void OptimizingCompilerThread::AddToOsrBuffer(OptimizedCompileJob* job) {
403 DCHECK(!IsOptimizerThread());
404 // Find the next slot that is empty or has a stale job.
405 OptimizedCompileJob* stale = NULL;
407 stale = osr_buffer_[osr_buffer_cursor_];
408 if (stale == NULL || stale->IsWaitingForInstall()) break;
409 osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_;
412 // Add to found slot and dispose the evicted job.
414 DCHECK(stale->IsWaitingForInstall());
415 CompilationInfo* info = stale->info();
416 if (FLAG_trace_osr) {
417 PrintF("[COSR - Discarded ");
418 info->closure()->PrintName();
419 PrintF(", AST id %d]\n", info->osr_ast_id().ToInt());
421 DisposeOptimizedCompileJob(stale, false);
423 osr_buffer_[osr_buffer_cursor_] = job;
424 osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_;
429 bool OptimizingCompilerThread::IsOptimizerThread(Isolate* isolate) {
430 return isolate->concurrent_recompilation_enabled() &&
431 isolate->optimizing_compiler_thread()->IsOptimizerThread();
435 bool OptimizingCompilerThread::IsOptimizerThread() {
436 base::LockGuard<base::Mutex> lock_guard(&thread_id_mutex_);
437 return ThreadId::Current().ToInteger() == thread_id_;
442 } } // namespace v8::internal