blob: 7062db640d513341c48ad485412d6e343a5299a8 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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-compile-dispatcher.h"
6
7#include "src/base/atomicops.h"
8#include "src/full-codegen/full-codegen.h"
9#include "src/isolate.h"
10#include "src/v8.h"
11
12namespace v8 {
13namespace internal {
14
15namespace {
16
17void DisposeOptimizedCompileJob(OptimizedCompileJob* job,
18 bool restore_function_code) {
19 // The recompile job is allocated in the CompilationInfo's zone.
20 CompilationInfo* info = job->info();
21 if (restore_function_code) {
22 if (info->is_osr()) {
23 if (!job->IsWaitingForInstall()) {
24 // Remove stack check that guards OSR entry on original code.
25 Handle<Code> code = info->unoptimized_code();
26 uint32_t offset = code->TranslateAstIdToPcOffset(info->osr_ast_id());
27 BackEdgeTable::RemoveStackCheck(code, offset);
28 }
29 } else {
30 Handle<JSFunction> function = info->closure();
31 function->ReplaceCode(function->shared()->code());
32 }
33 }
34 delete info;
35}
36
37} // namespace
38
39
40class OptimizingCompileDispatcher::CompileTask : public v8::Task {
41 public:
42 explicit CompileTask(Isolate* isolate) : isolate_(isolate) {
43 OptimizingCompileDispatcher* dispatcher =
44 isolate_->optimizing_compile_dispatcher();
45 base::LockGuard<base::Mutex> lock_guard(&dispatcher->ref_count_mutex_);
46 ++dispatcher->ref_count_;
47 }
48
49 virtual ~CompileTask() {}
50
51 private:
52 // v8::Task overrides.
53 void Run() override {
54 DisallowHeapAllocation no_allocation;
55 DisallowHandleAllocation no_handles;
56 DisallowHandleDereference no_deref;
57
58 OptimizingCompileDispatcher* dispatcher =
59 isolate_->optimizing_compile_dispatcher();
60 {
61 TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_);
62
63 if (dispatcher->recompilation_delay_ != 0) {
64 base::OS::Sleep(base::TimeDelta::FromMilliseconds(
65 dispatcher->recompilation_delay_));
66 }
67
68 dispatcher->CompileNext(dispatcher->NextInput(true));
69 }
70 {
71 base::LockGuard<base::Mutex> lock_guard(&dispatcher->ref_count_mutex_);
72 if (--dispatcher->ref_count_ == 0) {
73 dispatcher->ref_count_zero_.NotifyOne();
74 }
75 }
76 }
77
78 Isolate* isolate_;
79
80 DISALLOW_COPY_AND_ASSIGN(CompileTask);
81};
82
83
84OptimizingCompileDispatcher::~OptimizingCompileDispatcher() {
85#ifdef DEBUG
86 {
87 base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_);
88 DCHECK_EQ(0, ref_count_);
89 }
90#endif
91 DCHECK_EQ(0, input_queue_length_);
92 DeleteArray(input_queue_);
93 if (FLAG_concurrent_osr) {
94#ifdef DEBUG
95 for (int i = 0; i < osr_buffer_capacity_; i++) {
96 CHECK_NULL(osr_buffer_[i]);
97 }
98#endif
99 DeleteArray(osr_buffer_);
100 }
101}
102
103
104OptimizedCompileJob* OptimizingCompileDispatcher::NextInput(
105 bool check_if_flushing) {
106 base::LockGuard<base::Mutex> access_input_queue_(&input_queue_mutex_);
107 if (input_queue_length_ == 0) return NULL;
108 OptimizedCompileJob* job = input_queue_[InputQueueIndex(0)];
109 DCHECK_NOT_NULL(job);
110 input_queue_shift_ = InputQueueIndex(1);
111 input_queue_length_--;
112 if (check_if_flushing) {
113 if (static_cast<ModeFlag>(base::Acquire_Load(&mode_)) == FLUSH) {
114 if (!job->info()->is_osr()) {
115 AllowHandleDereference allow_handle_dereference;
116 DisposeOptimizedCompileJob(job, true);
117 }
118 return NULL;
119 }
120 }
121 return job;
122}
123
124
125void OptimizingCompileDispatcher::CompileNext(OptimizedCompileJob* job) {
126 if (!job) return;
127
128 // The function may have already been optimized by OSR. Simply continue.
129 OptimizedCompileJob::Status status = job->OptimizeGraph();
130 USE(status); // Prevent an unused-variable error in release mode.
131 DCHECK(status != OptimizedCompileJob::FAILED);
132
133 // The function may have already been optimized by OSR. Simply continue.
134 // Use a mutex to make sure that functions marked for install
135 // are always also queued.
136 base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_);
137 output_queue_.push(job);
138 isolate_->stack_guard()->RequestInstallCode();
139}
140
141
142void OptimizingCompileDispatcher::FlushOutputQueue(bool restore_function_code) {
143 for (;;) {
144 OptimizedCompileJob* job = NULL;
145 {
146 base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_);
147 if (output_queue_.empty()) return;
148 job = output_queue_.front();
149 output_queue_.pop();
150 }
151
152 // OSR jobs are dealt with separately.
153 if (!job->info()->is_osr()) {
154 DisposeOptimizedCompileJob(job, restore_function_code);
155 }
156 }
157}
158
159
160void OptimizingCompileDispatcher::FlushOsrBuffer(bool restore_function_code) {
161 for (int i = 0; i < osr_buffer_capacity_; i++) {
162 if (osr_buffer_[i] != NULL) {
163 DisposeOptimizedCompileJob(osr_buffer_[i], restore_function_code);
164 osr_buffer_[i] = NULL;
165 }
166 }
167}
168
169
170void OptimizingCompileDispatcher::Flush() {
171 base::Release_Store(&mode_, static_cast<base::AtomicWord>(FLUSH));
172 if (FLAG_block_concurrent_recompilation) Unblock();
173 {
174 base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_);
175 while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_);
176 base::Release_Store(&mode_, static_cast<base::AtomicWord>(COMPILE));
177 }
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
186void OptimizingCompileDispatcher::Stop() {
187 base::Release_Store(&mode_, static_cast<base::AtomicWord>(FLUSH));
188 if (FLAG_block_concurrent_recompilation) Unblock();
189 {
190 base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_);
191 while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_);
192 base::Release_Store(&mode_, static_cast<base::AtomicWord>(COMPILE));
193 }
194
195 if (recompilation_delay_ != 0) {
196 // At this point the optimizing compiler thread's event loop has stopped.
197 // There is no need for a mutex when reading input_queue_length_.
198 while (input_queue_length_ > 0) CompileNext(NextInput());
199 InstallOptimizedFunctions();
200 } else {
201 FlushOutputQueue(false);
202 }
203
204 if (FLAG_concurrent_osr) FlushOsrBuffer(false);
205
206 if ((FLAG_trace_osr || FLAG_trace_concurrent_recompilation) &&
207 FLAG_concurrent_osr) {
208 PrintF("[COSR hit rate %d / %d]\n", osr_hits_, osr_attempts_);
209 }
210}
211
212
213void OptimizingCompileDispatcher::InstallOptimizedFunctions() {
214 HandleScope handle_scope(isolate_);
215
216 for (;;) {
217 OptimizedCompileJob* job = NULL;
218 {
219 base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_);
220 if (output_queue_.empty()) return;
221 job = output_queue_.front();
222 output_queue_.pop();
223 }
224 CompilationInfo* info = job->info();
225 Handle<JSFunction> function(*info->closure());
226 if (info->is_osr()) {
227 if (FLAG_trace_osr) {
228 PrintF("[COSR - ");
229 function->ShortPrint();
230 PrintF(" is ready for install and entry at AST id %d]\n",
231 info->osr_ast_id().ToInt());
232 }
233 job->WaitForInstall();
234 // Remove stack check that guards OSR entry on original code.
235 Handle<Code> code = info->unoptimized_code();
236 uint32_t offset = code->TranslateAstIdToPcOffset(info->osr_ast_id());
237 BackEdgeTable::RemoveStackCheck(code, offset);
238 } else {
239 if (function->IsOptimized()) {
240 if (FLAG_trace_concurrent_recompilation) {
241 PrintF(" ** Aborting compilation for ");
242 function->ShortPrint();
243 PrintF(" as it has already been optimized.\n");
244 }
245 DisposeOptimizedCompileJob(job, false);
246 } else {
247 Handle<Code> code = Compiler::GetConcurrentlyOptimizedCode(job);
248 function->ReplaceCode(code.is_null() ? function->shared()->code()
249 : *code);
250 }
251 }
252 }
253}
254
255
256void OptimizingCompileDispatcher::QueueForOptimization(
257 OptimizedCompileJob* job) {
258 DCHECK(IsQueueAvailable());
259 CompilationInfo* info = job->info();
260 if (info->is_osr()) {
261 osr_attempts_++;
262 AddToOsrBuffer(job);
263 // Add job to the front of the input queue.
264 base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_);
265 DCHECK_LT(input_queue_length_, input_queue_capacity_);
266 // Move shift_ back by one.
267 input_queue_shift_ = InputQueueIndex(input_queue_capacity_ - 1);
268 input_queue_[InputQueueIndex(0)] = job;
269 input_queue_length_++;
270 } else {
271 // Add job to the back of the input queue.
272 base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_);
273 DCHECK_LT(input_queue_length_, input_queue_capacity_);
274 input_queue_[InputQueueIndex(input_queue_length_)] = job;
275 input_queue_length_++;
276 }
277 if (FLAG_block_concurrent_recompilation) {
278 blocked_jobs_++;
279 } else {
280 V8::GetCurrentPlatform()->CallOnBackgroundThread(
281 new CompileTask(isolate_), v8::Platform::kShortRunningTask);
282 }
283}
284
285
286void OptimizingCompileDispatcher::Unblock() {
287 while (blocked_jobs_ > 0) {
288 V8::GetCurrentPlatform()->CallOnBackgroundThread(
289 new CompileTask(isolate_), v8::Platform::kShortRunningTask);
290 blocked_jobs_--;
291 }
292}
293
294
295OptimizedCompileJob* OptimizingCompileDispatcher::FindReadyOSRCandidate(
296 Handle<JSFunction> function, BailoutId osr_ast_id) {
297 for (int i = 0; i < osr_buffer_capacity_; i++) {
298 OptimizedCompileJob* current = osr_buffer_[i];
299 if (current != NULL && current->IsWaitingForInstall() &&
300 current->info()->HasSameOsrEntry(function, osr_ast_id)) {
301 osr_hits_++;
302 osr_buffer_[i] = NULL;
303 return current;
304 }
305 }
306 return NULL;
307}
308
309
310bool OptimizingCompileDispatcher::IsQueuedForOSR(Handle<JSFunction> function,
311 BailoutId osr_ast_id) {
312 for (int i = 0; i < osr_buffer_capacity_; i++) {
313 OptimizedCompileJob* current = osr_buffer_[i];
314 if (current != NULL &&
315 current->info()->HasSameOsrEntry(function, osr_ast_id)) {
316 return !current->IsWaitingForInstall();
317 }
318 }
319 return false;
320}
321
322
323bool OptimizingCompileDispatcher::IsQueuedForOSR(JSFunction* function) {
324 for (int i = 0; i < osr_buffer_capacity_; i++) {
325 OptimizedCompileJob* current = osr_buffer_[i];
326 if (current != NULL && *current->info()->closure() == function) {
327 return !current->IsWaitingForInstall();
328 }
329 }
330 return false;
331}
332
333
334void OptimizingCompileDispatcher::AddToOsrBuffer(OptimizedCompileJob* job) {
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 DCHECK(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} // namespace internal
358} // namespace v8