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/objects-inl.h"
13#include "src/tracing/trace-event.h"
14#include "src/v8.h"
15
16namespace v8 {
17namespace internal {
18
19namespace {
20
21void DisposeCompilationJob(CompilationJob* job, bool restore_function_code) {
22  if (restore_function_code) {
23    Handle<JSFunction> function = job->info()->closure();
24    function->ReplaceCode(function->shared()->code());
25    // TODO(mvstanton): We can't call ensureliterals here due to allocation,
26    // but we probably shouldn't call ReplaceCode either, as this
27    // sometimes runs on the worker thread!
28    // JSFunction::EnsureLiterals(function);
29  }
30  delete job;
31}
32
33}  // namespace
34
35class OptimizingCompileDispatcher::CompileTask : public v8::Task {
36 public:
37  explicit CompileTask(Isolate* isolate,
38                       OptimizingCompileDispatcher* dispatcher)
39      : isolate_(isolate), dispatcher_(dispatcher) {
40    base::LockGuard<base::Mutex> lock_guard(&dispatcher_->ref_count_mutex_);
41    ++dispatcher_->ref_count_;
42  }
43
44  virtual ~CompileTask() {}
45
46 private:
47  // v8::Task overrides.
48  void Run() override {
49    DisallowHeapAllocation no_allocation;
50    DisallowHandleAllocation no_handles;
51    DisallowHandleDereference no_deref;
52
53    {
54      TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_);
55
56      TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
57                   "V8.RecompileConcurrent");
58
59      if (dispatcher_->recompilation_delay_ != 0) {
60        base::OS::Sleep(base::TimeDelta::FromMilliseconds(
61            dispatcher_->recompilation_delay_));
62      }
63
64      dispatcher_->CompileNext(dispatcher_->NextInput(true));
65    }
66    {
67      base::LockGuard<base::Mutex> lock_guard(&dispatcher_->ref_count_mutex_);
68      if (--dispatcher_->ref_count_ == 0) {
69        dispatcher_->ref_count_zero_.NotifyOne();
70      }
71    }
72  }
73
74  Isolate* isolate_;
75  OptimizingCompileDispatcher* dispatcher_;
76
77  DISALLOW_COPY_AND_ASSIGN(CompileTask);
78};
79
80OptimizingCompileDispatcher::~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
91CompilationJob* 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
108void 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
123void 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
137void 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
167void 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
186void 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
212void 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_, this), v8::Platform::kShortRunningTask);
226  }
227}
228
229void OptimizingCompileDispatcher::Unblock() {
230  while (blocked_jobs_ > 0) {
231    V8::GetCurrentPlatform()->CallOnBackgroundThread(
232        new CompileTask(isolate_, this), v8::Platform::kShortRunningTask);
233    blocked_jobs_--;
234  }
235}
236
237}  // namespace internal
238}  // namespace v8
239