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
~OptimizingCompilerThread()18 OptimizingCompilerThread::~OptimizingCompilerThread() {
19 ASSERT_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
Run()32 void OptimizingCompilerThread::Run() {
33 #ifdef DEBUG
34 { LockGuard<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 ElapsedTimer total_timer;
44 if (FLAG_trace_concurrent_recompilation) total_timer.Start();
45
46 while (true) {
47 input_queue_semaphore_.Wait();
48 Logger::TimerEventScope timer(
49 isolate_, Logger::TimerEventScope::v8_recompile_concurrent);
50
51 if (FLAG_concurrent_recompilation_delay != 0) {
52 OS::Sleep(FLAG_concurrent_recompilation_delay);
53 }
54
55 switch (static_cast<StopFlag>(base::Acquire_Load(&stop_thread_))) {
56 case CONTINUE:
57 break;
58 case STOP:
59 if (FLAG_trace_concurrent_recompilation) {
60 time_spent_total_ = total_timer.Elapsed();
61 }
62 stop_semaphore_.Signal();
63 return;
64 case FLUSH:
65 // The main thread is blocked, waiting for the stop semaphore.
66 { AllowHandleDereference allow_handle_dereference;
67 FlushInputQueue(true);
68 }
69 base::Release_Store(&stop_thread_,
70 static_cast<base::AtomicWord>(CONTINUE));
71 stop_semaphore_.Signal();
72 // Return to start of consumer loop.
73 continue;
74 }
75
76 ElapsedTimer compiling_timer;
77 if (FLAG_trace_concurrent_recompilation) compiling_timer.Start();
78
79 CompileNext();
80
81 if (FLAG_trace_concurrent_recompilation) {
82 time_spent_compiling_ += compiling_timer.Elapsed();
83 }
84 }
85 }
86
87
NextInput()88 OptimizedCompileJob* OptimizingCompilerThread::NextInput() {
89 LockGuard<Mutex> access_input_queue_(&input_queue_mutex_);
90 if (input_queue_length_ == 0) return NULL;
91 OptimizedCompileJob* job = input_queue_[InputQueueIndex(0)];
92 ASSERT_NE(NULL, job);
93 input_queue_shift_ = InputQueueIndex(1);
94 input_queue_length_--;
95 return job;
96 }
97
98
CompileNext()99 void OptimizingCompilerThread::CompileNext() {
100 OptimizedCompileJob* job = NextInput();
101 ASSERT_NE(NULL, job);
102
103 // The function may have already been optimized by OSR. Simply continue.
104 OptimizedCompileJob::Status status = job->OptimizeGraph();
105 USE(status); // Prevent an unused-variable error in release mode.
106 ASSERT(status != OptimizedCompileJob::FAILED);
107
108 // The function may have already been optimized by OSR. Simply continue.
109 // Use a mutex to make sure that functions marked for install
110 // are always also queued.
111 output_queue_.Enqueue(job);
112 isolate_->stack_guard()->RequestInstallCode();
113 }
114
115
DisposeOptimizedCompileJob(OptimizedCompileJob * job,bool restore_function_code)116 static void DisposeOptimizedCompileJob(OptimizedCompileJob* job,
117 bool restore_function_code) {
118 // The recompile job is allocated in the CompilationInfo's zone.
119 CompilationInfo* info = job->info();
120 if (restore_function_code) {
121 if (info->is_osr()) {
122 if (!job->IsWaitingForInstall()) {
123 // Remove stack check that guards OSR entry on original code.
124 Handle<Code> code = info->unoptimized_code();
125 uint32_t offset = code->TranslateAstIdToPcOffset(info->osr_ast_id());
126 BackEdgeTable::RemoveStackCheck(code, offset);
127 }
128 } else {
129 Handle<JSFunction> function = info->closure();
130 function->ReplaceCode(function->shared()->code());
131 }
132 }
133 delete info;
134 }
135
136
FlushInputQueue(bool restore_function_code)137 void OptimizingCompilerThread::FlushInputQueue(bool restore_function_code) {
138 OptimizedCompileJob* job;
139 while ((job = NextInput())) {
140 // This should not block, since we have one signal on the input queue
141 // semaphore corresponding to each element in the input queue.
142 input_queue_semaphore_.Wait();
143 // OSR jobs are dealt with separately.
144 if (!job->info()->is_osr()) {
145 DisposeOptimizedCompileJob(job, restore_function_code);
146 }
147 }
148 }
149
150
FlushOutputQueue(bool restore_function_code)151 void OptimizingCompilerThread::FlushOutputQueue(bool restore_function_code) {
152 OptimizedCompileJob* job;
153 while (output_queue_.Dequeue(&job)) {
154 // OSR jobs are dealt with separately.
155 if (!job->info()->is_osr()) {
156 DisposeOptimizedCompileJob(job, restore_function_code);
157 }
158 }
159 }
160
161
FlushOsrBuffer(bool restore_function_code)162 void OptimizingCompilerThread::FlushOsrBuffer(bool restore_function_code) {
163 for (int i = 0; i < osr_buffer_capacity_; i++) {
164 if (osr_buffer_[i] != NULL) {
165 DisposeOptimizedCompileJob(osr_buffer_[i], restore_function_code);
166 osr_buffer_[i] = NULL;
167 }
168 }
169 }
170
171
Flush()172 void OptimizingCompilerThread::Flush() {
173 ASSERT(!IsOptimizerThread());
174 base::Release_Store(&stop_thread_, static_cast<base::AtomicWord>(FLUSH));
175 if (FLAG_block_concurrent_recompilation) Unblock();
176 input_queue_semaphore_.Signal();
177 stop_semaphore_.Wait();
178 FlushOutputQueue(true);
179 if (FLAG_concurrent_osr) FlushOsrBuffer(true);
180 if (FLAG_trace_concurrent_recompilation) {
181 PrintF(" ** Flushed concurrent recompilation queues.\n");
182 }
183 }
184
185
Stop()186 void OptimizingCompilerThread::Stop() {
187 ASSERT(!IsOptimizerThread());
188 base::Release_Store(&stop_thread_, static_cast<base::AtomicWord>(STOP));
189 if (FLAG_block_concurrent_recompilation) Unblock();
190 input_queue_semaphore_.Signal();
191 stop_semaphore_.Wait();
192
193 if (FLAG_concurrent_recompilation_delay != 0) {
194 // At this point the optimizing compiler thread's event loop has stopped.
195 // There is no need for a mutex when reading input_queue_length_.
196 while (input_queue_length_ > 0) CompileNext();
197 InstallOptimizedFunctions();
198 } else {
199 FlushInputQueue(false);
200 FlushOutputQueue(false);
201 }
202
203 if (FLAG_concurrent_osr) FlushOsrBuffer(false);
204
205 if (FLAG_trace_concurrent_recompilation) {
206 double percentage = time_spent_compiling_.PercentOf(time_spent_total_);
207 PrintF(" ** Compiler thread did %.2f%% useful work\n", percentage);
208 }
209
210 if ((FLAG_trace_osr || FLAG_trace_concurrent_recompilation) &&
211 FLAG_concurrent_osr) {
212 PrintF("[COSR hit rate %d / %d]\n", osr_hits_, osr_attempts_);
213 }
214
215 Join();
216 }
217
218
InstallOptimizedFunctions()219 void OptimizingCompilerThread::InstallOptimizedFunctions() {
220 ASSERT(!IsOptimizerThread());
221 HandleScope handle_scope(isolate_);
222
223 OptimizedCompileJob* job;
224 while (output_queue_.Dequeue(&job)) {
225 CompilationInfo* info = job->info();
226 Handle<JSFunction> function(*info->closure());
227 if (info->is_osr()) {
228 if (FLAG_trace_osr) {
229 PrintF("[COSR - ");
230 info->closure()->PrintName();
231 PrintF(" is ready for install and entry at AST id %d]\n",
232 info->osr_ast_id().ToInt());
233 }
234 job->WaitForInstall();
235 // Remove stack check that guards OSR entry on original code.
236 Handle<Code> code = info->unoptimized_code();
237 uint32_t offset = code->TranslateAstIdToPcOffset(info->osr_ast_id());
238 BackEdgeTable::RemoveStackCheck(code, offset);
239 } else {
240 if (function->IsOptimized()) {
241 DisposeOptimizedCompileJob(job, false);
242 } else {
243 Handle<Code> code = Compiler::GetConcurrentlyOptimizedCode(job);
244 function->ReplaceCode(
245 code.is_null() ? function->shared()->code() : *code);
246 }
247 }
248 }
249 }
250
251
QueueForOptimization(OptimizedCompileJob * job)252 void OptimizingCompilerThread::QueueForOptimization(OptimizedCompileJob* job) {
253 ASSERT(IsQueueAvailable());
254 ASSERT(!IsOptimizerThread());
255 CompilationInfo* info = job->info();
256 if (info->is_osr()) {
257 osr_attempts_++;
258 AddToOsrBuffer(job);
259 // Add job to the front of the input queue.
260 LockGuard<Mutex> access_input_queue(&input_queue_mutex_);
261 ASSERT_LT(input_queue_length_, input_queue_capacity_);
262 // Move shift_ back by one.
263 input_queue_shift_ = InputQueueIndex(input_queue_capacity_ - 1);
264 input_queue_[InputQueueIndex(0)] = job;
265 input_queue_length_++;
266 } else {
267 // Add job to the back of the input queue.
268 LockGuard<Mutex> access_input_queue(&input_queue_mutex_);
269 ASSERT_LT(input_queue_length_, input_queue_capacity_);
270 input_queue_[InputQueueIndex(input_queue_length_)] = job;
271 input_queue_length_++;
272 }
273 if (FLAG_block_concurrent_recompilation) {
274 blocked_jobs_++;
275 } else {
276 input_queue_semaphore_.Signal();
277 }
278 }
279
280
Unblock()281 void OptimizingCompilerThread::Unblock() {
282 ASSERT(!IsOptimizerThread());
283 while (blocked_jobs_ > 0) {
284 input_queue_semaphore_.Signal();
285 blocked_jobs_--;
286 }
287 }
288
289
FindReadyOSRCandidate(Handle<JSFunction> function,BailoutId osr_ast_id)290 OptimizedCompileJob* OptimizingCompilerThread::FindReadyOSRCandidate(
291 Handle<JSFunction> function, BailoutId osr_ast_id) {
292 ASSERT(!IsOptimizerThread());
293 for (int i = 0; i < osr_buffer_capacity_; i++) {
294 OptimizedCompileJob* current = osr_buffer_[i];
295 if (current != NULL &&
296 current->IsWaitingForInstall() &&
297 current->info()->HasSameOsrEntry(function, osr_ast_id)) {
298 osr_hits_++;
299 osr_buffer_[i] = NULL;
300 return current;
301 }
302 }
303 return NULL;
304 }
305
306
IsQueuedForOSR(Handle<JSFunction> function,BailoutId osr_ast_id)307 bool OptimizingCompilerThread::IsQueuedForOSR(Handle<JSFunction> function,
308 BailoutId osr_ast_id) {
309 ASSERT(!IsOptimizerThread());
310 for (int i = 0; i < osr_buffer_capacity_; i++) {
311 OptimizedCompileJob* current = osr_buffer_[i];
312 if (current != NULL &&
313 current->info()->HasSameOsrEntry(function, osr_ast_id)) {
314 return !current->IsWaitingForInstall();
315 }
316 }
317 return false;
318 }
319
320
IsQueuedForOSR(JSFunction * function)321 bool OptimizingCompilerThread::IsQueuedForOSR(JSFunction* function) {
322 ASSERT(!IsOptimizerThread());
323 for (int i = 0; i < osr_buffer_capacity_; i++) {
324 OptimizedCompileJob* current = osr_buffer_[i];
325 if (current != NULL && *current->info()->closure() == function) {
326 return !current->IsWaitingForInstall();
327 }
328 }
329 return false;
330 }
331
332
AddToOsrBuffer(OptimizedCompileJob * job)333 void OptimizingCompilerThread::AddToOsrBuffer(OptimizedCompileJob* job) {
334 ASSERT(!IsOptimizerThread());
335 // Find the next slot that is empty or has a stale job.
336 OptimizedCompileJob* stale = NULL;
337 while (true) {
338 stale = osr_buffer_[osr_buffer_cursor_];
339 if (stale == NULL || stale->IsWaitingForInstall()) break;
340 osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_;
341 }
342
343 // Add to found slot and dispose the evicted job.
344 if (stale != NULL) {
345 ASSERT(stale->IsWaitingForInstall());
346 CompilationInfo* info = stale->info();
347 if (FLAG_trace_osr) {
348 PrintF("[COSR - Discarded ");
349 info->closure()->PrintName();
350 PrintF(", AST id %d]\n", info->osr_ast_id().ToInt());
351 }
352 DisposeOptimizedCompileJob(stale, false);
353 }
354 osr_buffer_[osr_buffer_cursor_] = job;
355 osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_;
356 }
357
358
359 #ifdef DEBUG
IsOptimizerThread(Isolate * isolate)360 bool OptimizingCompilerThread::IsOptimizerThread(Isolate* isolate) {
361 return isolate->concurrent_recompilation_enabled() &&
362 isolate->optimizing_compiler_thread()->IsOptimizerThread();
363 }
364
365
IsOptimizerThread()366 bool OptimizingCompilerThread::IsOptimizerThread() {
367 LockGuard<Mutex> lock_guard(&thread_id_mutex_);
368 return ThreadId::Current().ToInteger() == thread_id_;
369 }
370 #endif
371
372
373 } } // namespace v8::internal
374