Revision f230a1cf deps/v8/src/optimizing-compiler-thread.cc
deps/v8/src/optimizing-compiler-thread.cc | ||
---|---|---|
29 | 29 |
|
30 | 30 |
#include "v8.h" |
31 | 31 |
|
32 |
#include "full-codegen.h" |
|
32 | 33 |
#include "hydrogen.h" |
33 | 34 |
#include "isolate.h" |
34 | 35 |
#include "v8threads.h" |
... | ... | |
36 | 37 |
namespace v8 { |
37 | 38 |
namespace internal { |
38 | 39 |
|
40 |
OptimizingCompilerThread::~OptimizingCompilerThread() { |
|
41 |
ASSERT_EQ(0, input_queue_length_); |
|
42 |
DeleteArray(input_queue_); |
|
43 |
if (FLAG_concurrent_osr) { |
|
44 |
#ifdef DEBUG |
|
45 |
for (int i = 0; i < osr_buffer_capacity_; i++) { |
|
46 |
CHECK_EQ(NULL, osr_buffer_[i]); |
|
47 |
} |
|
48 |
#endif |
|
49 |
DeleteArray(osr_buffer_); |
|
50 |
} |
|
51 |
} |
|
52 |
|
|
39 | 53 |
|
40 | 54 |
void OptimizingCompilerThread::Run() { |
41 | 55 |
#ifdef DEBUG |
... | ... | |
74 | 88 |
{ AllowHandleDereference allow_handle_dereference; |
75 | 89 |
FlushInputQueue(true); |
76 | 90 |
} |
77 |
Release_Store(&queue_length_, static_cast<AtomicWord>(0)); |
|
78 | 91 |
Release_Store(&stop_thread_, static_cast<AtomicWord>(CONTINUE)); |
79 | 92 |
stop_semaphore_.Signal(); |
80 | 93 |
// Return to start of consumer loop. |
... | ... | |
93 | 106 |
} |
94 | 107 |
|
95 | 108 |
|
109 |
RecompileJob* OptimizingCompilerThread::NextInput() { |
|
110 |
LockGuard<Mutex> access_input_queue_(&input_queue_mutex_); |
|
111 |
if (input_queue_length_ == 0) return NULL; |
|
112 |
RecompileJob* job = input_queue_[InputQueueIndex(0)]; |
|
113 |
ASSERT_NE(NULL, job); |
|
114 |
input_queue_shift_ = InputQueueIndex(1); |
|
115 |
input_queue_length_--; |
|
116 |
return job; |
|
117 |
} |
|
118 |
|
|
119 |
|
|
96 | 120 |
void OptimizingCompilerThread::CompileNext() { |
97 |
OptimizingCompiler* optimizing_compiler = NULL; |
|
98 |
bool result = input_queue_.Dequeue(&optimizing_compiler); |
|
99 |
USE(result); |
|
100 |
ASSERT(result); |
|
101 |
Barrier_AtomicIncrement(&queue_length_, static_cast<Atomic32>(-1)); |
|
121 |
RecompileJob* job = NextInput(); |
|
122 |
ASSERT_NE(NULL, job); |
|
102 | 123 |
|
103 | 124 |
// The function may have already been optimized by OSR. Simply continue. |
104 |
OptimizingCompiler::Status status = optimizing_compiler->OptimizeGraph();
|
|
125 |
RecompileJob::Status status = job->OptimizeGraph();
|
|
105 | 126 |
USE(status); // Prevent an unused-variable error in release mode. |
106 |
ASSERT(status != OptimizingCompiler::FAILED);
|
|
127 |
ASSERT(status != RecompileJob::FAILED);
|
|
107 | 128 |
|
108 | 129 |
// The function may have already been optimized by OSR. Simply continue. |
109 | 130 |
// Use a mutex to make sure that functions marked for install |
110 | 131 |
// are always also queued. |
111 |
if (!optimizing_compiler->info()->osr_ast_id().IsNone()) { |
|
112 |
ASSERT(FLAG_concurrent_osr); |
|
113 |
LockGuard<Mutex> access_osr_lists(&osr_list_mutex_); |
|
114 |
osr_candidates_.RemoveElement(optimizing_compiler); |
|
115 |
ready_for_osr_.Add(optimizing_compiler); |
|
116 |
} else { |
|
117 |
output_queue_.Enqueue(optimizing_compiler); |
|
118 |
isolate_->stack_guard()->RequestInstallCode(); |
|
132 |
output_queue_.Enqueue(job); |
|
133 |
isolate_->stack_guard()->RequestInstallCode(); |
|
134 |
} |
|
135 |
|
|
136 |
|
|
137 |
static void DisposeRecompileJob(RecompileJob* job, |
|
138 |
bool restore_function_code) { |
|
139 |
// The recompile job is allocated in the CompilationInfo's zone. |
|
140 |
CompilationInfo* info = job->info(); |
|
141 |
if (restore_function_code) { |
|
142 |
if (info->is_osr()) { |
|
143 |
if (!job->IsWaitingForInstall()) BackEdgeTable::RemoveStackCheck(info); |
|
144 |
} else { |
|
145 |
Handle<JSFunction> function = info->closure(); |
|
146 |
function->ReplaceCode(function->shared()->code()); |
|
147 |
} |
|
119 | 148 |
} |
149 |
delete info; |
|
120 | 150 |
} |
121 | 151 |
|
122 | 152 |
|
123 | 153 |
void OptimizingCompilerThread::FlushInputQueue(bool restore_function_code) { |
124 |
OptimizingCompiler* optimizing_compiler; |
|
125 |
// The optimizing compiler is allocated in the CompilationInfo's zone. |
|
126 |
while (input_queue_.Dequeue(&optimizing_compiler)) { |
|
154 |
RecompileJob* job; |
|
155 |
while ((job = NextInput())) { |
|
127 | 156 |
// This should not block, since we have one signal on the input queue |
128 | 157 |
// semaphore corresponding to each element in the input queue. |
129 | 158 |
input_queue_semaphore_.Wait(); |
130 |
CompilationInfo* info = optimizing_compiler->info(); |
|
131 |
if (restore_function_code) { |
|
132 |
Handle<JSFunction> function = info->closure(); |
|
133 |
function->ReplaceCode(function->shared()->code()); |
|
159 |
// OSR jobs are dealt with separately. |
|
160 |
if (!job->info()->is_osr()) { |
|
161 |
DisposeRecompileJob(job, restore_function_code); |
|
134 | 162 |
} |
135 |
delete info; |
|
136 | 163 |
} |
137 | 164 |
} |
138 | 165 |
|
139 | 166 |
|
140 | 167 |
void OptimizingCompilerThread::FlushOutputQueue(bool restore_function_code) { |
141 |
OptimizingCompiler* optimizing_compiler; |
|
142 |
// The optimizing compiler is allocated in the CompilationInfo's zone. |
|
143 |
while (output_queue_.Dequeue(&optimizing_compiler)) { |
|
144 |
CompilationInfo* info = optimizing_compiler->info(); |
|
145 |
if (restore_function_code) { |
|
146 |
Handle<JSFunction> function = info->closure(); |
|
147 |
function->ReplaceCode(function->shared()->code()); |
|
168 |
RecompileJob* job; |
|
169 |
while (output_queue_.Dequeue(&job)) { |
|
170 |
// OSR jobs are dealt with separately. |
|
171 |
if (!job->info()->is_osr()) { |
|
172 |
DisposeRecompileJob(job, restore_function_code); |
|
148 | 173 |
} |
149 |
delete info; |
|
150 | 174 |
} |
175 |
} |
|
176 |
|
|
151 | 177 |
|
152 |
osr_candidates_.Clear(); |
|
153 |
RemoveStaleOSRCandidates(0); |
|
178 |
void OptimizingCompilerThread::FlushOsrBuffer(bool restore_function_code) { |
|
179 |
for (int i = 0; i < osr_buffer_capacity_; i++) { |
|
180 |
if (osr_buffer_[i] != NULL) { |
|
181 |
DisposeRecompileJob(osr_buffer_[i], restore_function_code); |
|
182 |
osr_buffer_[i] = NULL; |
|
183 |
} |
|
184 |
} |
|
154 | 185 |
} |
155 | 186 |
|
156 | 187 |
|
157 | 188 |
void OptimizingCompilerThread::Flush() { |
158 | 189 |
ASSERT(!IsOptimizerThread()); |
159 | 190 |
Release_Store(&stop_thread_, static_cast<AtomicWord>(FLUSH)); |
191 |
if (FLAG_block_concurrent_recompilation) Unblock(); |
|
160 | 192 |
input_queue_semaphore_.Signal(); |
161 | 193 |
stop_semaphore_.Wait(); |
162 | 194 |
FlushOutputQueue(true); |
195 |
if (FLAG_concurrent_osr) FlushOsrBuffer(true); |
|
196 |
if (FLAG_trace_concurrent_recompilation) { |
|
197 |
PrintF(" ** Flushed concurrent recompilation queues.\n"); |
|
198 |
} |
|
163 | 199 |
} |
164 | 200 |
|
165 | 201 |
|
166 | 202 |
void OptimizingCompilerThread::Stop() { |
167 | 203 |
ASSERT(!IsOptimizerThread()); |
168 | 204 |
Release_Store(&stop_thread_, static_cast<AtomicWord>(STOP)); |
205 |
if (FLAG_block_concurrent_recompilation) Unblock(); |
|
169 | 206 |
input_queue_semaphore_.Signal(); |
170 | 207 |
stop_semaphore_.Wait(); |
171 | 208 |
|
172 | 209 |
if (FLAG_concurrent_recompilation_delay != 0) { |
173 |
// Barrier when loading queue length is not necessary since the write |
|
174 |
// happens in CompileNext on the same thread. |
|
175 |
// This is used only for testing. |
|
176 |
while (NoBarrier_Load(&queue_length_) > 0) CompileNext(); |
|
210 |
// At this point the optimizing compiler thread's event loop has stopped. |
|
211 |
// There is no need for a mutex when reading input_queue_length_. |
|
212 |
while (input_queue_length_ > 0) CompileNext(); |
|
177 | 213 |
InstallOptimizedFunctions(); |
178 | 214 |
} else { |
179 | 215 |
FlushInputQueue(false); |
180 | 216 |
FlushOutputQueue(false); |
181 | 217 |
} |
182 | 218 |
|
219 |
if (FLAG_concurrent_osr) FlushOsrBuffer(false); |
|
220 |
|
|
183 | 221 |
if (FLAG_trace_concurrent_recompilation) { |
184 | 222 |
double percentage = time_spent_compiling_.PercentOf(time_spent_total_); |
185 | 223 |
PrintF(" ** Compiler thread did %.2f%% useful work\n", percentage); |
186 | 224 |
} |
187 | 225 |
|
188 |
if (FLAG_trace_osr && FLAG_concurrent_osr) { |
|
226 |
if ((FLAG_trace_osr || FLAG_trace_concurrent_recompilation) && |
|
227 |
FLAG_concurrent_osr) { |
|
189 | 228 |
PrintF("[COSR hit rate %d / %d]\n", osr_hits_, osr_attempts_); |
190 | 229 |
} |
191 | 230 |
|
... | ... | |
196 | 235 |
void OptimizingCompilerThread::InstallOptimizedFunctions() { |
197 | 236 |
ASSERT(!IsOptimizerThread()); |
198 | 237 |
HandleScope handle_scope(isolate_); |
199 |
OptimizingCompiler* compiler; |
|
200 |
while (true) { |
|
201 |
if (!output_queue_.Dequeue(&compiler)) return; |
|
202 |
Compiler::InstallOptimizedCode(compiler); |
|
203 |
} |
|
204 | 238 |
|
205 |
// Remove the oldest OSR candidates that are ready so that we |
|
206 |
// only have limited number of them waiting. |
|
207 |
if (FLAG_concurrent_osr) RemoveStaleOSRCandidates(); |
|
239 |
RecompileJob* job; |
|
240 |
while (output_queue_.Dequeue(&job)) { |
|
241 |
CompilationInfo* info = job->info(); |
|
242 |
if (info->is_osr()) { |
|
243 |
if (FLAG_trace_osr) { |
|
244 |
PrintF("[COSR - "); |
|
245 |
info->closure()->PrintName(); |
|
246 |
PrintF(" is ready for install and entry at AST id %d]\n", |
|
247 |
info->osr_ast_id().ToInt()); |
|
248 |
} |
|
249 |
job->WaitForInstall(); |
|
250 |
BackEdgeTable::RemoveStackCheck(info); |
|
251 |
} else { |
|
252 |
Compiler::InstallOptimizedCode(job); |
|
253 |
} |
|
254 |
} |
|
208 | 255 |
} |
209 | 256 |
|
210 | 257 |
|
211 |
void OptimizingCompilerThread::QueueForOptimization( |
|
212 |
OptimizingCompiler* optimizing_compiler) { |
|
258 |
void OptimizingCompilerThread::QueueForOptimization(RecompileJob* job) { |
|
213 | 259 |
ASSERT(IsQueueAvailable()); |
214 | 260 |
ASSERT(!IsOptimizerThread()); |
215 |
Barrier_AtomicIncrement(&queue_length_, static_cast<Atomic32>(1)); |
|
216 |
if (optimizing_compiler->info()->osr_ast_id().IsNone()) { |
|
217 |
optimizing_compiler->info()->closure()->MarkInRecompileQueue(); |
|
218 |
} else { |
|
219 |
LockGuard<Mutex> access_osr_lists(&osr_list_mutex_); |
|
220 |
osr_candidates_.Add(optimizing_compiler); |
|
261 |
CompilationInfo* info = job->info(); |
|
262 |
if (info->is_osr()) { |
|
263 |
if (FLAG_trace_concurrent_recompilation) { |
|
264 |
PrintF(" ** Queueing "); |
|
265 |
info->closure()->PrintName(); |
|
266 |
PrintF(" for concurrent on-stack replacement.\n"); |
|
267 |
} |
|
221 | 268 |
osr_attempts_++; |
269 |
BackEdgeTable::AddStackCheck(info); |
|
270 |
AddToOsrBuffer(job); |
|
271 |
// Add job to the front of the input queue. |
|
272 |
LockGuard<Mutex> access_input_queue(&input_queue_mutex_); |
|
273 |
ASSERT_LT(input_queue_length_, input_queue_capacity_); |
|
274 |
// Move shift_ back by one. |
|
275 |
input_queue_shift_ = InputQueueIndex(input_queue_capacity_ - 1); |
|
276 |
input_queue_[InputQueueIndex(0)] = job; |
|
277 |
input_queue_length_++; |
|
278 |
} else { |
|
279 |
info->closure()->MarkInRecompileQueue(); |
|
280 |
// Add job to the back of the input queue. |
|
281 |
LockGuard<Mutex> access_input_queue(&input_queue_mutex_); |
|
282 |
ASSERT_LT(input_queue_length_, input_queue_capacity_); |
|
283 |
input_queue_[InputQueueIndex(input_queue_length_)] = job; |
|
284 |
input_queue_length_++; |
|
285 |
} |
|
286 |
if (FLAG_block_concurrent_recompilation) { |
|
287 |
blocked_jobs_++; |
|
288 |
} else { |
|
289 |
input_queue_semaphore_.Signal(); |
|
290 |
} |
|
291 |
} |
|
292 |
|
|
293 |
|
|
294 |
void OptimizingCompilerThread::Unblock() { |
|
295 |
ASSERT(!IsOptimizerThread()); |
|
296 |
while (blocked_jobs_ > 0) { |
|
297 |
input_queue_semaphore_.Signal(); |
|
298 |
blocked_jobs_--; |
|
222 | 299 |
} |
223 |
input_queue_.Enqueue(optimizing_compiler); |
|
224 |
input_queue_semaphore_.Signal(); |
|
225 | 300 |
} |
226 | 301 |
|
227 | 302 |
|
228 |
OptimizingCompiler* OptimizingCompilerThread::FindReadyOSRCandidate(
|
|
303 |
RecompileJob* OptimizingCompilerThread::FindReadyOSRCandidate(
|
|
229 | 304 |
Handle<JSFunction> function, uint32_t osr_pc_offset) { |
230 | 305 |
ASSERT(!IsOptimizerThread()); |
231 |
OptimizingCompiler* result = NULL;
|
|
232 |
{ LockGuard<Mutex> access_osr_lists(&osr_list_mutex_);
|
|
233 |
for (int i = 0; i < ready_for_osr_.length(); i++) {
|
|
234 |
if (ready_for_osr_[i]->info()->HasSameOsrEntry(function, osr_pc_offset)) {
|
|
235 |
osr_hits_++;
|
|
236 |
result = ready_for_osr_.Remove(i);
|
|
237 |
break;
|
|
238 |
}
|
|
306 |
for (int i = 0; i < osr_buffer_capacity_; i++) {
|
|
307 |
RecompileJob* current = osr_buffer_[i];
|
|
308 |
if (current != NULL &&
|
|
309 |
current->IsWaitingForInstall() &&
|
|
310 |
current->info()->HasSameOsrEntry(function, osr_pc_offset)) {
|
|
311 |
osr_hits_++;
|
|
312 |
osr_buffer_[i] = NULL;
|
|
313 |
return current;
|
|
239 | 314 |
} |
240 | 315 |
} |
241 |
RemoveStaleOSRCandidates(); |
|
242 |
return result; |
|
316 |
return NULL; |
|
243 | 317 |
} |
244 | 318 |
|
245 | 319 |
|
246 | 320 |
bool OptimizingCompilerThread::IsQueuedForOSR(Handle<JSFunction> function, |
247 | 321 |
uint32_t osr_pc_offset) { |
248 | 322 |
ASSERT(!IsOptimizerThread()); |
249 |
LockGuard<Mutex> access_osr_lists(&osr_list_mutex_); |
|
250 |
for (int i = 0; i < osr_candidates_.length(); i++) { |
|
251 |
if (osr_candidates_[i]->info()->HasSameOsrEntry(function, osr_pc_offset)) { |
|
252 |
return true; |
|
323 |
for (int i = 0; i < osr_buffer_capacity_; i++) { |
|
324 |
RecompileJob* current = osr_buffer_[i]; |
|
325 |
if (current != NULL && |
|
326 |
current->info()->HasSameOsrEntry(function, osr_pc_offset)) { |
|
327 |
return !current->IsWaitingForInstall(); |
|
253 | 328 |
} |
254 | 329 |
} |
255 | 330 |
return false; |
... | ... | |
258 | 333 |
|
259 | 334 |
bool OptimizingCompilerThread::IsQueuedForOSR(JSFunction* function) { |
260 | 335 |
ASSERT(!IsOptimizerThread()); |
261 |
LockGuard<Mutex> access_osr_lists(&osr_list_mutex_);
|
|
262 |
for (int i = 0; i < osr_candidates_.length(); i++) {
|
|
263 |
if (*osr_candidates_[i]->info()->closure() == function) {
|
|
264 |
return true;
|
|
336 |
for (int i = 0; i < osr_buffer_capacity_; i++) {
|
|
337 |
RecompileJob* current = osr_buffer_[i];
|
|
338 |
if (current != NULL && *current->info()->closure() == function) {
|
|
339 |
return !current->IsWaitingForInstall();
|
|
265 | 340 |
} |
266 | 341 |
} |
267 | 342 |
return false; |
268 | 343 |
} |
269 | 344 |
|
270 | 345 |
|
271 |
void OptimizingCompilerThread::RemoveStaleOSRCandidates(int limit) {
|
|
346 |
void OptimizingCompilerThread::AddToOsrBuffer(RecompileJob* job) {
|
|
272 | 347 |
ASSERT(!IsOptimizerThread()); |
273 |
LockGuard<Mutex> access_osr_lists(&osr_list_mutex_); |
|
274 |
while (ready_for_osr_.length() > limit) { |
|
275 |
OptimizingCompiler* compiler = ready_for_osr_.Remove(0); |
|
276 |
CompilationInfo* throw_away = compiler->info(); |
|
348 |
// Find the next slot that is empty or has a stale job. |
|
349 |
while (true) { |
|
350 |
RecompileJob* stale = osr_buffer_[osr_buffer_cursor_]; |
|
351 |
if (stale == NULL || stale->IsWaitingForInstall()) break; |
|
352 |
osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_; |
|
353 |
} |
|
354 |
|
|
355 |
// Add to found slot and dispose the evicted job. |
|
356 |
RecompileJob* evicted = osr_buffer_[osr_buffer_cursor_]; |
|
357 |
if (evicted != NULL) { |
|
358 |
ASSERT(evicted->IsWaitingForInstall()); |
|
359 |
CompilationInfo* info = evicted->info(); |
|
277 | 360 |
if (FLAG_trace_osr) { |
278 | 361 |
PrintF("[COSR - Discarded "); |
279 |
throw_away->closure()->PrintName(); |
|
280 |
PrintF(", AST id %d]\n", |
|
281 |
throw_away->osr_ast_id().ToInt()); |
|
362 |
info->closure()->PrintName(); |
|
363 |
PrintF(", AST id %d]\n", info->osr_ast_id().ToInt()); |
|
282 | 364 |
} |
283 |
delete throw_away;
|
|
365 |
DisposeRecompileJob(evicted, false);
|
|
284 | 366 |
} |
367 |
osr_buffer_[osr_buffer_cursor_] = job; |
|
368 |
osr_buffer_cursor_ = (osr_buffer_cursor_ + 1) % osr_buffer_capacity_; |
|
285 | 369 |
} |
286 | 370 |
|
287 | 371 |
|
Also available in: Unified diff