1// Copyright 2012 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "optimizing-compiler-thread.h"
29
30#include "v8.h"
31
32#include "full-codegen.h"
33#include "hydrogen.h"
34#include "isolate.h"
35#include "v8threads.h"
36
37namespace v8 {
38namespace internal {
39
40OptimizingCompilerThread::~OptimizingCompilerThread() {
41  ASSERT_EQ(0, input_queue_length_);
42  DeleteArray(input_queue_);
43  if (FLAG_concurrent_osr) {
44#ifdef DEBUG
45    for (int i = 0; i < osr_buffer_capacity_; i++) {
46      CHECK_EQ(NULL, osr_buffer_[i]);
47    }
48#endif
49    DeleteArray(osr_buffer_);
50  }
51}
52
53
54void OptimizingCompilerThread::Run() {
55#ifdef DEBUG
56  { LockGuard<Mutex> lock_guard(&thread_id_mutex_);
57    thread_id_ = ThreadId::Current().ToInteger();
58  }
59#endif
60  Isolate::SetIsolateThreadLocals(isolate_, NULL);
61  DisallowHeapAllocation no_allocation;
62  DisallowHandleAllocation no_handles;
63  DisallowHandleDereference no_deref;
64
65  ElapsedTimer total_timer;
66  if (FLAG_trace_concurrent_recompilation) total_timer.Start();
67
68  while (true) {
69    input_queue_semaphore_.Wait();
70    Logger::TimerEventScope timer(
71        isolate_, Logger::TimerEventScope::v8_recompile_concurrent);
72
73    if (FLAG_concurrent_recompilation_delay != 0) {
74      OS::Sleep(FLAG_concurrent_recompilation_delay);
75    }
76
77    switch (static_cast<StopFlag>(Acquire_Load(&stop_thread_))) {
78      case CONTINUE:
79        break;
80      case STOP:
81        if (FLAG_trace_concurrent_recompilation) {
82          time_spent_total_ = total_timer.Elapsed();
83        }
84        stop_semaphore_.Signal();
85        return;
86      case FLUSH:
87        // The main thread is blocked, waiting for the stop semaphore.
88        { AllowHandleDereference allow_handle_dereference;
89          FlushInputQueue(true);
90        }
91        Release_Store(&stop_thread_, static_cast<AtomicWord>(CONTINUE));
92        stop_semaphore_.Signal();
93        // Return to start of consumer loop.
94        continue;
95    }
96
97    ElapsedTimer compiling_timer;
98    if (FLAG_trace_concurrent_recompilation) compiling_timer.Start();
99
100    CompileNext();
101
102    if (FLAG_trace_concurrent_recompilation) {
103      time_spent_compiling_ += compiling_timer.Elapsed();
104    }
105  }
106}
107
108
109RecompileJob* OptimizingCompilerThread::NextInput() {
110  LockGuard<Mutex> access_input_queue_(&input_queue_mutex_);
111  if (input_queue_length_ == 0) return NULL;
112  RecompileJob* job = input_queue_[InputQueueIndex(0)];
113  ASSERT_NE(NULL, job);
114  input_queue_shift_ = InputQueueIndex(1);
115  input_queue_length_--;
116  return job;
117}
118
119
120void OptimizingCompilerThread::CompileNext() {
121  RecompileJob* job = NextInput();
122  ASSERT_NE(NULL, job);
123
124  // The function may have already been optimized by OSR.  Simply continue.
125  RecompileJob::Status status = job->OptimizeGraph();
126  USE(status);   // Prevent an unused-variable error in release mode.
127  ASSERT(status != RecompileJob::FAILED);
128
129  // The function may have already been optimized by OSR.  Simply continue.
130  // Use a mutex to make sure that functions marked for install
131  // are always also queued.
132  output_queue_.Enqueue(job);
133  isolate_->stack_guard()->RequestInstallCode();
134}
135
136
137static void DisposeRecompileJob(RecompileJob* job,
138                                bool restore_function_code) {
139  // The recompile job is allocated in the CompilationInfo's zone.
140  CompilationInfo* info = job->info();
141  if (restore_function_code) {
142    if (info->is_osr()) {
143      if (!job->IsWaitingForInstall()) BackEdgeTable::RemoveStackCheck(info);
144    } else {
145      Handle<JSFunction> function = info->closure();
146      function->ReplaceCode(function->shared()->code());
147    }
148  }
149  delete info;
150}
151
152
153void OptimizingCompilerThread::FlushInputQueue(bool restore_function_code) {
154  RecompileJob* job;
155  while ((job = NextInput())) {
156    // This should not block, since we have one signal on the input queue
157    // semaphore corresponding to each element in the input queue.
158    input_queue_semaphore_.Wait();
159    // OSR jobs are dealt with separately.
160    if (!job->info()->is_osr()) {
161      DisposeRecompileJob(job, restore_function_code);
162    }
163  }
164}
165
166
167void OptimizingCompilerThread::FlushOutputQueue(bool restore_function_code) {
168  RecompileJob* job;
169  while (output_queue_.Dequeue(&job)) {
170    // OSR jobs are dealt with separately.
171    if (!job->info()->is_osr()) {
172      DisposeRecompileJob(job, restore_function_code);
173    }
174  }
175}
176
177
178void OptimizingCompilerThread::FlushOsrBuffer(bool restore_function_code) {
179  for (int i = 0; i < osr_buffer_capacity_; i++) {
180    if (osr_buffer_[i] != NULL) {
181      DisposeRecompileJob(osr_buffer_[i], restore_function_code);
182      osr_buffer_[i] = NULL;
183    }
184  }
185}
186
187
188void OptimizingCompilerThread::Flush() {
189  ASSERT(!IsOptimizerThread());
190  Release_Store(&stop_thread_, static_cast<AtomicWord>(FLUSH));
191  if (FLAG_block_concurrent_recompilation) Unblock();
192  input_queue_semaphore_.Signal();
193  stop_semaphore_.Wait();
194  FlushOutputQueue(true);
195  if (FLAG_concurrent_osr) FlushOsrBuffer(true);
196  if (FLAG_trace_concurrent_recompilation) {
197    PrintF("  ** Flushed concurrent recompilation queues.\n");
198  }
199}
200
201
202void OptimizingCompilerThread::Stop() {
203  ASSERT(!IsOptimizerThread());
204  Release_Store(&stop_thread_, static_cast<AtomicWord>(STOP));
205  if (FLAG_block_concurrent_recompilation) Unblock();
206  input_queue_semaphore_.Signal();
207  stop_semaphore_.Wait();
208
209  if (FLAG_concurrent_recompilation_delay != 0) {
210    // At this point the optimizing compiler thread's event loop has stopped.
211    // There is no need for a mutex when reading input_queue_length_.
212    while (input_queue_length_ > 0) CompileNext();
213    InstallOptimizedFunctions();
214  } else {
215    FlushInputQueue(false);
216    FlushOutputQueue(false);
217  }
218
219  if (FLAG_concurrent_osr) FlushOsrBuffer(false);
220
221  if (FLAG_trace_concurrent_recompilation) {
222    double percentage = time_spent_compiling_.PercentOf(time_spent_total_);
223    PrintF("  ** Compiler thread did %.2f%% useful work\n", percentage);
224  }
225
226  if ((FLAG_trace_osr || FLAG_trace_concurrent_recompilation) &&
227      FLAG_concurrent_osr) {
228    PrintF("[COSR hit rate %d / %d]\n", osr_hits_, osr_attempts_);
229  }
230
231  Join();
232}
233
234
235void OptimizingCompilerThread::InstallOptimizedFunctions() {
236  ASSERT(!IsOptimizerThread());
237  HandleScope handle_scope(isolate_);
238
239  RecompileJob* job;
240  while (output_queue_.Dequeue(&job)) {
241    CompilationInfo* info = job->info();
242    if (info->is_osr()) {
243      if (FLAG_trace_osr) {
244        PrintF("[COSR - ");
245        info->closure()->PrintName();
246        PrintF(" is ready for install and entry at AST id %d]\n",
247               info->osr_ast_id().ToInt());
248      }
249      job->WaitForInstall();
250      BackEdgeTable::RemoveStackCheck(info);
251    } else {
252      Compiler::InstallOptimizedCode(job);
253    }
254  }
255}
256
257
258void OptimizingCompilerThread::QueueForOptimization(RecompileJob* job) {
259  ASSERT(IsQueueAvailable());
260  ASSERT(!IsOptimizerThread());
261  CompilationInfo* info = job->info();
262  if (info->is_osr()) {
263    if (FLAG_trace_concurrent_recompilation) {
264      PrintF("  ** Queueing ");
265      info->closure()->PrintName();
266      PrintF(" for concurrent on-stack replacement.\n");
267    }
268    osr_attempts_++;
269    BackEdgeTable::AddStackCheck(info);
270    AddToOsrBuffer(job);
271    // Add job to the front of the input queue.
272    LockGuard<Mutex> access_input_queue(&input_queue_mutex_);
273    ASSERT_LT(input_queue_length_, input_queue_capacity_);
274    // Move shift_ back by one.
275    input_queue_shift_ = InputQueueIndex(input_queue_capacity_ - 1);
276    input_queue_[InputQueueIndex(0)] = job;
277    input_queue_length_++;
278  } else {
279    info->closure()->MarkInRecompileQueue();
280    // Add job to the back of the input queue.
281    LockGuard<Mutex> access_input_queue(&input_queue_mutex_);
282    ASSERT_LT(input_queue_length_, input_queue_capacity_);
283    input_queue_[InputQueueIndex(input_queue_length_)] = job;
284    input_queue_length_++;
285  }
286  if (FLAG_block_concurrent_recompilation) {
287    blocked_jobs_++;
288  } else {
289    input_queue_semaphore_.Signal();
290  }
291}
292
293
294void OptimizingCompilerThread::Unblock() {
295  ASSERT(!IsOptimizerThread());
296  while (blocked_jobs_ > 0) {
297    input_queue_semaphore_.Signal();
298    blocked_jobs_--;
299  }
300}
301
302
303RecompileJob* OptimizingCompilerThread::FindReadyOSRCandidate(
304    Handle<JSFunction> function, uint32_t osr_pc_offset) {
305  ASSERT(!IsOptimizerThread());
306  for (int i = 0; i < osr_buffer_capacity_; i++) {
307    RecompileJob* current = osr_buffer_[i];
308    if (current != NULL &&
309        current->IsWaitingForInstall() &&
310        current->info()->HasSameOsrEntry(function, osr_pc_offset)) {
311      osr_hits_++;
312      osr_buffer_[i] = NULL;
313      return current;
314    }
315  }
316  return NULL;
317}
318
319
320bool OptimizingCompilerThread::IsQueuedForOSR(Handle<JSFunction> function,
321                                              uint32_t osr_pc_offset) {
322  ASSERT(!IsOptimizerThread());
323  for (int i = 0; i < osr_buffer_capacity_; i++) {
324    RecompileJob* current = osr_buffer_[i];
325    if (current != NULL &&
326        current->info()->HasSameOsrEntry(function, osr_pc_offset)) {
327      return !current->IsWaitingForInstall();
328    }
329  }
330  return false;
331}
332
333
334bool OptimizingCompilerThread::IsQueuedForOSR(JSFunction* function) {
335  ASSERT(!IsOptimizerThread());
336  for (int i = 0; i < osr_buffer_capacity_; i++) {
337    RecompileJob* current = osr_buffer_[i];
338    if (current != NULL && *current->info()->closure() == function) {
339      return !current->IsWaitingForInstall();
340    }
341  }
342  return false;
343}
344
345
346void OptimizingCompilerThread::AddToOsrBuffer(RecompileJob* job) {
347  ASSERT(!IsOptimizerThread());
348  // Find the next slot that is empty or has a stale job.
349  RecompileJob* stale = NULL;
350  while (true) {
351    stale = osr_buffer_[osr_buffer_cursor_];
352    if (stale == NULL || stale->IsWaitingForInstall()) break;
353    osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_;
354  }
355
356  // Add to found slot and dispose the evicted job.
357  if (stale != NULL) {
358    ASSERT(stale->IsWaitingForInstall());
359    CompilationInfo* info = stale->info();
360    if (FLAG_trace_osr) {
361      PrintF("[COSR - Discarded ");
362      info->closure()->PrintName();
363      PrintF(", AST id %d]\n", info->osr_ast_id().ToInt());
364    }
365    DisposeRecompileJob(stale, false);
366  }
367  osr_buffer_[osr_buffer_cursor_] = job;
368  osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_;
369}
370
371
372#ifdef DEBUG
373bool OptimizingCompilerThread::IsOptimizerThread(Isolate* isolate) {
374  return isolate->concurrent_recompilation_enabled() &&
375         isolate->optimizing_compiler_thread()->IsOptimizerThread();
376}
377
378
379bool OptimizingCompilerThread::IsOptimizerThread() {
380  LockGuard<Mutex> lock_guard(&thread_id_mutex_);
381  return ThreadId::Current().ToInteger() == thread_id_;
382}
383#endif
384
385
386} }  // namespace v8::internal
387