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
15namespace v8 {
16namespace internal {
17
18OptimizingCompilerThread::~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
32void 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
87OptimizedCompileJob* 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
98void 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
115static 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
136void 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
150void 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
161void 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
171void 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
185void 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
218void 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
256void 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
285void OptimizingCompilerThread::Unblock() {
286  DCHECK(!IsOptimizerThread());
287  while (blocked_jobs_ > 0) {
288    input_queue_semaphore_.Signal();
289    blocked_jobs_--;
290  }
291}
292
293
294OptimizedCompileJob* 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
311bool 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
325bool 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
337void 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
364bool OptimizingCompilerThread::IsOptimizerThread(Isolate* isolate) {
365  return isolate->concurrent_recompilation_enabled() &&
366         isolate->optimizing_compiler_thread()->IsOptimizerThread();
367}
368
369
370bool 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