Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / v8 / src / optimizing-compiler-thread.cc
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
15 namespace v8 {
16 namespace internal {
17
18 class OptimizingCompilerThread::CompileTask : public v8::Task {
19  public:
20   CompileTask(Isolate* isolate, OptimizedCompileJob* job)
21       : isolate_(isolate), job_(job) {}
22
23   virtual ~CompileTask() {}
24
25  private:
26   // v8::Task overrides.
27   virtual void Run() OVERRIDE {
28     DisallowHeapAllocation no_allocation;
29     DisallowHandleAllocation no_handles;
30     DisallowHandleDereference no_deref;
31
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);
36
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.
40     {
41       base::LockGuard<base::Mutex> lock_guard(
42           &isolate_->optimizing_compiler_thread()->output_queue_mutex_);
43       isolate_->optimizing_compiler_thread()->output_queue_.Enqueue(job_);
44     }
45     isolate_->stack_guard()->RequestInstallCode();
46     {
47       base::LockGuard<base::Mutex> lock_guard(
48           &isolate_->optimizing_compiler_thread()->input_queue_mutex_);
49       isolate_->optimizing_compiler_thread()->input_queue_length_--;
50     }
51     isolate_->optimizing_compiler_thread()->input_queue_semaphore_.Signal();
52   }
53
54   Isolate* isolate_;
55   OptimizedCompileJob* job_;
56
57   DISALLOW_COPY_AND_ASSIGN(CompileTask);
58 };
59
60
61 OptimizingCompilerThread::~OptimizingCompilerThread() {
62   DCHECK_EQ(0, input_queue_length_);
63   DeleteArray(input_queue_);
64   if (FLAG_concurrent_osr) {
65 #ifdef DEBUG
66     for (int i = 0; i < osr_buffer_capacity_; i++) {
67       CHECK_EQ(NULL, osr_buffer_[i]);
68     }
69 #endif
70     DeleteArray(osr_buffer_);
71   }
72 }
73
74
75 void OptimizingCompilerThread::Run() {
76 #ifdef DEBUG
77   { base::LockGuard<base::Mutex> lock_guard(&thread_id_mutex_);
78     thread_id_ = ThreadId::Current().ToInteger();
79   }
80 #endif
81   DisallowHeapAllocation no_allocation;
82   DisallowHandleAllocation no_handles;
83   DisallowHandleDereference no_deref;
84
85   if (job_based_recompilation_) {
86     return;
87   }
88
89   base::ElapsedTimer total_timer;
90   if (tracing_enabled_) total_timer.Start();
91
92   while (true) {
93     input_queue_semaphore_.Wait();
94     TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_);
95
96     if (FLAG_concurrent_recompilation_delay != 0) {
97       base::OS::Sleep(FLAG_concurrent_recompilation_delay);
98     }
99
100     switch (static_cast<StopFlag>(base::Acquire_Load(&stop_thread_))) {
101       case CONTINUE:
102         break;
103       case STOP:
104         if (tracing_enabled_) {
105           time_spent_total_ = total_timer.Elapsed();
106         }
107         stop_semaphore_.Signal();
108         return;
109       case FLUSH:
110         // The main thread is blocked, waiting for the stop semaphore.
111         { AllowHandleDereference allow_handle_dereference;
112           FlushInputQueue(true);
113         }
114         base::Release_Store(&stop_thread_,
115                             static_cast<base::AtomicWord>(CONTINUE));
116         stop_semaphore_.Signal();
117         // Return to start of consumer loop.
118         continue;
119     }
120
121     base::ElapsedTimer compiling_timer;
122     if (tracing_enabled_) compiling_timer.Start();
123
124     CompileNext();
125
126     if (tracing_enabled_) {
127       time_spent_compiling_ += compiling_timer.Elapsed();
128     }
129   }
130 }
131
132
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_--;
141   return job;
142 }
143
144
145 void OptimizingCompilerThread::CompileNext() {
146   OptimizedCompileJob* job = NextInput();
147   DCHECK_NE(NULL, job);
148
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);
153
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();
159 }
160
161
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);
173       }
174     } else {
175       Handle<JSFunction> function = info->closure();
176       function->ReplaceCode(function->shared()->code());
177     }
178   }
179   delete info;
180 }
181
182
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);
193     }
194   }
195 }
196
197
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);
204     }
205   }
206 }
207
208
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;
214     }
215   }
216 }
217
218
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();
226   }
227   FlushOutputQueue(true);
228   if (FLAG_concurrent_osr) FlushOsrBuffer(true);
229   if (tracing_enabled_) {
230     PrintF("  ** Flushed concurrent recompilation queues.\n");
231   }
232 }
233
234
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();
242   }
243
244   if (job_based_recompilation_) {
245     while (true) {
246       {
247         base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_);
248         if (!input_queue_length_) break;
249       }
250       input_queue_semaphore_.Wait();
251     }
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();
257   } else {
258     FlushInputQueue(false);
259     FlushOutputQueue(false);
260   }
261
262   if (FLAG_concurrent_osr) FlushOsrBuffer(false);
263
264   if (tracing_enabled_) {
265     double percentage = time_spent_compiling_.PercentOf(time_spent_total_);
266     PrintF("  ** Compiler thread did %.2f%% useful work\n", percentage);
267   }
268
269   if ((FLAG_trace_osr || tracing_enabled_) && FLAG_concurrent_osr) {
270     PrintF("[COSR hit rate %d / %d]\n", osr_hits_, osr_attempts_);
271   }
272
273   Join();
274 }
275
276
277 void OptimizingCompilerThread::InstallOptimizedFunctions() {
278   DCHECK(!IsOptimizerThread());
279   HandleScope handle_scope(isolate_);
280
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) {
287         PrintF("[COSR - ");
288         function->ShortPrint();
289         PrintF(" is ready for install and entry at AST id %d]\n",
290                info->osr_ast_id().ToInt());
291       }
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);
297     } else {
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");
303         }
304         DisposeOptimizedCompileJob(job, false);
305       } else {
306         Handle<Code> code = Compiler::GetConcurrentlyOptimizedCode(job);
307         function->ReplaceCode(
308             code.is_null() ? function->shared()->code() : *code);
309       }
310     }
311   }
312 }
313
314
315 void OptimizingCompilerThread::QueueForOptimization(OptimizedCompileJob* job) {
316   DCHECK(IsQueueAvailable());
317   DCHECK(!IsOptimizerThread());
318   CompilationInfo* info = job->info();
319   if (info->is_osr()) {
320     osr_attempts_++;
321     AddToOsrBuffer(job);
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_++;
329   } else {
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_++;
335   }
336   if (job_based_recompilation_) {
337     V8::GetCurrentPlatform()->CallOnBackgroundThread(
338         new CompileTask(isolate_, job), v8::Platform::kShortRunningTask);
339   } else if (FLAG_block_concurrent_recompilation) {
340     blocked_jobs_++;
341   } else {
342     input_queue_semaphore_.Signal();
343   }
344 }
345
346
347 void OptimizingCompilerThread::Unblock() {
348   DCHECK(!IsOptimizerThread());
349   if (job_based_recompilation_) {
350     return;
351   }
352   while (blocked_jobs_ > 0) {
353     input_queue_semaphore_.Signal();
354     blocked_jobs_--;
355   }
356 }
357
358
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)) {
367       osr_hits_++;
368       osr_buffer_[i] = NULL;
369       return current;
370     }
371   }
372   return NULL;
373 }
374
375
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();
384     }
385   }
386   return false;
387 }
388
389
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();
396     }
397   }
398   return false;
399 }
400
401
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;
406   while (true) {
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_;
410   }
411
412   // Add to found slot and dispose the evicted job.
413   if (stale != NULL) {
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());
420     }
421     DisposeOptimizedCompileJob(stale, false);
422   }
423   osr_buffer_[osr_buffer_cursor_] = job;
424   osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_;
425 }
426
427
428 #ifdef DEBUG
429 bool OptimizingCompilerThread::IsOptimizerThread(Isolate* isolate) {
430   return isolate->concurrent_recompilation_enabled() &&
431          isolate->optimizing_compiler_thread()->IsOptimizerThread();
432 }
433
434
435 bool OptimizingCompilerThread::IsOptimizerThread() {
436   base::LockGuard<base::Mutex> lock_guard(&thread_id_mutex_);
437   return ThreadId::Current().ToInteger() == thread_id_;
438 }
439 #endif
440
441
442 } }  // namespace v8::internal