• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 namespace v8 {
17 namespace internal {
18 
19 namespace {
20 
DisposeCompilationJob(CompilationJob * job,bool restore_function_code)21 void 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 
35 class OptimizingCompileDispatcher::CompileTask : public v8::Task {
36  public:
CompileTask(Isolate * isolate,OptimizingCompileDispatcher * dispatcher)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 
~CompileTask()44   virtual ~CompileTask() {}
45 
46  private:
47   // v8::Task overrides.
Run()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 
~OptimizingCompileDispatcher()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 
NextInput(bool check_if_flushing)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 
CompileNext(CompilationJob * job)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 
FlushOutputQueue(bool restore_function_code)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 
Flush(BlockingBehavior blocking_behavior)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 
Stop()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 
InstallOptimizedFunctions()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 
QueueForOptimization(CompilationJob * job)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_, this), v8::Platform::kShortRunningTask);
226   }
227 }
228 
Unblock()229 void 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