| OLD | NEW |
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | 27 |
| 28 #include "optimizing-compiler-thread.h" | 28 #include "optimizing-compiler-thread.h" |
| 29 | 29 |
| 30 #include "v8.h" | 30 #include "v8.h" |
| 31 | 31 |
| 32 #include "full-codegen.h" |
| 32 #include "hydrogen.h" | 33 #include "hydrogen.h" |
| 33 #include "isolate.h" | 34 #include "isolate.h" |
| 34 #include "v8threads.h" | 35 #include "v8threads.h" |
| 35 | 36 |
| 36 namespace v8 { | 37 namespace v8 { |
| 37 namespace internal { | 38 namespace internal { |
| 38 | 39 |
| 39 | 40 |
| 40 void OptimizingCompilerThread::Run() { | 41 void OptimizingCompilerThread::Run() { |
| 41 #ifdef DEBUG | 42 #ifdef DEBUG |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 86 CompileNext(); | 87 CompileNext(); |
| 87 | 88 |
| 88 if (FLAG_trace_concurrent_recompilation) { | 89 if (FLAG_trace_concurrent_recompilation) { |
| 89 time_spent_compiling_ += compiling_timer.Elapsed(); | 90 time_spent_compiling_ += compiling_timer.Elapsed(); |
| 90 } | 91 } |
| 91 } | 92 } |
| 92 } | 93 } |
| 93 | 94 |
| 94 | 95 |
| 95 void OptimizingCompilerThread::CompileNext() { | 96 void OptimizingCompilerThread::CompileNext() { |
| 96 OptimizingCompiler* optimizing_compiler = NULL; | 97 RecompileJob* job = NULL; |
| 97 bool result = input_queue_.Dequeue(&optimizing_compiler); | 98 bool result = input_queue_.Dequeue(&job); |
| 98 USE(result); | 99 USE(result); |
| 99 ASSERT(result); | 100 ASSERT(result); |
| 100 Barrier_AtomicIncrement(&queue_length_, static_cast<Atomic32>(-1)); | 101 Barrier_AtomicIncrement(&queue_length_, static_cast<Atomic32>(-1)); |
| 101 | 102 |
| 102 // The function may have already been optimized by OSR. Simply continue. | 103 // The function may have already been optimized by OSR. Simply continue. |
| 103 OptimizingCompiler::Status status = optimizing_compiler->OptimizeGraph(); | 104 RecompileJob::Status status = job->OptimizeGraph(); |
| 104 USE(status); // Prevent an unused-variable error in release mode. | 105 USE(status); // Prevent an unused-variable error in release mode. |
| 105 ASSERT(status != OptimizingCompiler::FAILED); | 106 ASSERT(status != RecompileJob::FAILED); |
| 106 | 107 |
| 107 // The function may have already been optimized by OSR. Simply continue. | 108 // The function may have already been optimized by OSR. Simply continue. |
| 108 // Use a mutex to make sure that functions marked for install | 109 // Use a mutex to make sure that functions marked for install |
| 109 // are always also queued. | 110 // are always also queued. |
| 110 if (!optimizing_compiler->info()->osr_ast_id().IsNone()) { | 111 LockGuard<Mutex> access_queue(&queue_mutex_); |
| 111 ASSERT(FLAG_concurrent_osr); | 112 output_queue_.Enqueue(job); |
| 112 LockGuard<Mutex> access_osr_lists(&osr_list_mutex_); | 113 isolate_->stack_guard()->RequestInstallCode(); |
| 113 osr_candidates_.RemoveElement(optimizing_compiler); | 114 } |
| 114 ready_for_osr_.Add(optimizing_compiler); | 115 |
| 115 } else { | 116 |
| 116 LockGuard<Mutex> access_queue(&queue_mutex_); | 117 static void DisposeRecompileJob(RecompileJob* job, |
| 117 output_queue_.Enqueue(optimizing_compiler); | 118 bool restore_function_code) { |
| 118 isolate_->stack_guard()->RequestInstallCode(); | 119 // The recompile job is allocated in the CompilationInfo's zone. |
| 120 CompilationInfo* info = job->info(); |
| 121 if (restore_function_code) { |
| 122 if (info->is_osr()) { |
| 123 if (!job->IsWaitingForInstall()) BackEdgeTable::RemoveStackCheck(info); |
| 124 } else { |
| 125 Handle<JSFunction> function = info->closure(); |
| 126 function->ReplaceCode(function->shared()->code()); |
| 127 } |
| 128 } |
| 129 delete info; |
| 130 } |
| 131 |
| 132 |
| 133 void OptimizingCompilerThread::FlushInputQueue(bool restore_function_code) { |
| 134 RecompileJob* job; |
| 135 while (input_queue_.Dequeue(&job)) { |
| 136 // This should not block, since we have one signal on the input queue |
| 137 // semaphore corresponding to each element in the input queue. |
| 138 input_queue_semaphore_.Wait(); |
| 139 // OSR jobs are dealt with separately. |
| 140 if (!job->info()->is_osr()) { |
| 141 DisposeRecompileJob(job, restore_function_code); |
| 142 } |
| 143 } |
| 144 Release_Store(&queue_length_, static_cast<AtomicWord>(0)); |
| 145 } |
| 146 |
| 147 |
| 148 void OptimizingCompilerThread::FlushOutputQueue(bool restore_function_code) { |
| 149 RecompileJob* job; |
| 150 while (true) { |
| 151 { LockGuard<Mutex> access_queue(&queue_mutex_); |
| 152 if (!output_queue_.Dequeue(&job)) break; |
| 153 } |
| 154 // OSR jobs are dealt with separately. |
| 155 if (!job->info()->is_osr()) { |
| 156 DisposeRecompileJob(job, restore_function_code); |
| 157 } |
| 119 } | 158 } |
| 120 } | 159 } |
| 121 | 160 |
| 122 | 161 |
| 123 void OptimizingCompilerThread::FlushInputQueue(bool restore_function_code) { | 162 void OptimizingCompilerThread::FlushOsrBuffer(bool restore_function_code) { |
| 124 OptimizingCompiler* optimizing_compiler; | 163 RecompileJob* job; |
| 125 // The optimizing compiler is allocated in the CompilationInfo's zone. | 164 for (int i = 0; i < osr_buffer_size_; i++) { |
| 126 while (input_queue_.Dequeue(&optimizing_compiler)) { | 165 job = osr_buffer_[i]; |
| 127 // This should not block, since we have one signal on the input queue | 166 if (job != NULL) DisposeRecompileJob(job, restore_function_code); |
| 128 // semaphore corresponding to each element in the input queue. | |
| 129 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()); | |
| 134 } | |
| 135 delete info; | |
| 136 } | 167 } |
| 137 Release_Store(&queue_length_, static_cast<AtomicWord>(0)); | 168 osr_cursor_ = 0; |
| 138 | |
| 139 LockGuard<Mutex> access_osr_lists(&osr_list_mutex_); | |
| 140 osr_candidates_.Clear(); | |
| 141 } | 169 } |
| 142 | 170 |
| 143 | 171 |
| 144 void OptimizingCompilerThread::FlushOutputQueue(bool restore_function_code) { | |
| 145 OptimizingCompiler* optimizing_compiler; | |
| 146 // The optimizing compiler is allocated in the CompilationInfo's zone. | |
| 147 while (true) { | |
| 148 { LockGuard<Mutex> access_queue(&queue_mutex_); | |
| 149 if (!output_queue_.Dequeue(&optimizing_compiler)) break; | |
| 150 } | |
| 151 CompilationInfo* info = optimizing_compiler->info(); | |
| 152 if (restore_function_code) { | |
| 153 Handle<JSFunction> function = info->closure(); | |
| 154 function->ReplaceCode(function->shared()->code()); | |
| 155 } | |
| 156 delete info; | |
| 157 } | |
| 158 | |
| 159 RemoveStaleOSRCandidates(0); | |
| 160 } | |
| 161 | |
| 162 | |
| 163 void OptimizingCompilerThread::Flush() { | 172 void OptimizingCompilerThread::Flush() { |
| 164 ASSERT(!IsOptimizerThread()); | 173 ASSERT(!IsOptimizerThread()); |
| 165 Release_Store(&stop_thread_, static_cast<AtomicWord>(FLUSH)); | 174 Release_Store(&stop_thread_, static_cast<AtomicWord>(FLUSH)); |
| 166 input_queue_semaphore_.Signal(); | 175 input_queue_semaphore_.Signal(); |
| 167 stop_semaphore_.Wait(); | 176 stop_semaphore_.Wait(); |
| 168 FlushOutputQueue(true); | 177 FlushOutputQueue(true); |
| 178 if (FLAG_concurrent_osr) FlushOsrBuffer(true); |
| 179 if (FLAG_trace_concurrent_recompilation) { |
| 180 PrintF(" ** Flushed concurrent recompilation queues.\n"); |
| 181 } |
| 169 } | 182 } |
| 170 | 183 |
| 171 | 184 |
| 172 void OptimizingCompilerThread::Stop() { | 185 void OptimizingCompilerThread::Stop() { |
| 173 ASSERT(!IsOptimizerThread()); | 186 ASSERT(!IsOptimizerThread()); |
| 174 Release_Store(&stop_thread_, static_cast<AtomicWord>(STOP)); | 187 Release_Store(&stop_thread_, static_cast<AtomicWord>(STOP)); |
| 175 input_queue_semaphore_.Signal(); | 188 input_queue_semaphore_.Signal(); |
| 176 stop_semaphore_.Wait(); | 189 stop_semaphore_.Wait(); |
| 177 | 190 |
| 178 if (FLAG_concurrent_recompilation_delay != 0) { | 191 if (FLAG_concurrent_recompilation_delay != 0) { |
| 179 // Barrier when loading queue length is not necessary since the write | 192 // Barrier when loading queue length is not necessary since the write |
| 180 // happens in CompileNext on the same thread. | 193 // happens in CompileNext on the same thread. |
| 181 // This is used only for testing. | 194 // This is used only for testing. |
| 182 while (NoBarrier_Load(&queue_length_) > 0) CompileNext(); | 195 while (NoBarrier_Load(&queue_length_) > 0) CompileNext(); |
| 183 InstallOptimizedFunctions(); | 196 InstallOptimizedFunctions(); |
| 184 } else { | 197 } else { |
| 185 FlushInputQueue(false); | 198 FlushInputQueue(false); |
| 186 FlushOutputQueue(false); | 199 FlushOutputQueue(false); |
| 187 } | 200 } |
| 188 | 201 |
| 202 if (FLAG_concurrent_osr) FlushOsrBuffer(false); |
| 203 |
| 189 if (FLAG_trace_concurrent_recompilation) { | 204 if (FLAG_trace_concurrent_recompilation) { |
| 190 double percentage = time_spent_compiling_.PercentOf(time_spent_total_); | 205 double percentage = time_spent_compiling_.PercentOf(time_spent_total_); |
| 191 PrintF(" ** Compiler thread did %.2f%% useful work\n", percentage); | 206 PrintF(" ** Compiler thread did %.2f%% useful work\n", percentage); |
| 192 } | 207 } |
| 193 | 208 |
| 194 if (FLAG_trace_osr && FLAG_concurrent_osr) { | 209 if ((FLAG_trace_osr || FLAG_trace_concurrent_recompilation) && |
| 210 FLAG_concurrent_osr) { |
| 195 PrintF("[COSR hit rate %d / %d]\n", osr_hits_, osr_attempts_); | 211 PrintF("[COSR hit rate %d / %d]\n", osr_hits_, osr_attempts_); |
| 196 } | 212 } |
| 197 | 213 |
| 198 Join(); | 214 Join(); |
| 199 } | 215 } |
| 200 | 216 |
| 201 | 217 |
| 202 void OptimizingCompilerThread::InstallOptimizedFunctions() { | 218 void OptimizingCompilerThread::InstallOptimizedFunctions() { |
| 203 ASSERT(!IsOptimizerThread()); | 219 ASSERT(!IsOptimizerThread()); |
| 204 HandleScope handle_scope(isolate_); | 220 HandleScope handle_scope(isolate_); |
| 205 | 221 |
| 206 OptimizingCompiler* compiler; | 222 RecompileJob* job; |
| 207 while (true) { | 223 while (true) { |
| 208 { LockGuard<Mutex> access_queue(&queue_mutex_); | 224 { LockGuard<Mutex> access_queue(&queue_mutex_); |
| 209 if (!output_queue_.Dequeue(&compiler)) break; | 225 if (!output_queue_.Dequeue(&job)) break; |
| 210 } | 226 } |
| 211 Compiler::InstallOptimizedCode(compiler); | 227 CompilationInfo* info = job->info(); |
| 228 if (info->is_osr()) { |
| 229 if (FLAG_trace_osr) { |
| 230 PrintF("[COSR - "); |
| 231 info->closure()->PrintName(); |
| 232 PrintF(" is ready for install and entry at AST id %d]\n", |
| 233 info->osr_ast_id().ToInt()); |
| 234 } |
| 235 job->WaitForInstall(); |
| 236 BackEdgeTable::RemoveStackCheck(info); |
| 237 } else { |
| 238 Compiler::InstallOptimizedCode(job); |
| 239 } |
| 212 } | 240 } |
| 213 | |
| 214 // Remove the oldest OSR candidates that are ready so that we | |
| 215 // only have limited number of them waiting. | |
| 216 if (FLAG_concurrent_osr) RemoveStaleOSRCandidates(); | |
| 217 } | 241 } |
| 218 | 242 |
| 219 | 243 |
| 220 void OptimizingCompilerThread::QueueForOptimization( | 244 void OptimizingCompilerThread::QueueForOptimization(RecompileJob* job) { |
| 221 OptimizingCompiler* optimizing_compiler) { | |
| 222 ASSERT(IsQueueAvailable()); | 245 ASSERT(IsQueueAvailable()); |
| 223 ASSERT(!IsOptimizerThread()); | 246 ASSERT(!IsOptimizerThread()); |
| 224 Barrier_AtomicIncrement(&queue_length_, static_cast<Atomic32>(1)); | 247 Barrier_AtomicIncrement(&queue_length_, static_cast<Atomic32>(1)); |
| 225 if (optimizing_compiler->info()->osr_ast_id().IsNone()) { | 248 CompilationInfo* info = job->info(); |
| 226 optimizing_compiler->info()->closure()->MarkInRecompileQueue(); | 249 if (info->is_osr()) { |
| 250 if (FLAG_trace_concurrent_recompilation) { |
| 251 PrintF(" ** Queueing "); |
| 252 info->closure()->PrintName(); |
| 253 PrintF(" for concurrent on-stack replacement.\n"); |
| 254 } |
| 255 AddToOsrBuffer(job); |
| 256 osr_attempts_++; |
| 257 BackEdgeTable::AddStackCheck(info); |
| 227 } else { | 258 } else { |
| 228 LockGuard<Mutex> access_osr_lists(&osr_list_mutex_); | 259 info->closure()->MarkInRecompileQueue(); |
| 229 osr_candidates_.Add(optimizing_compiler); | |
| 230 osr_attempts_++; | |
| 231 } | 260 } |
| 232 input_queue_.Enqueue(optimizing_compiler); | 261 input_queue_.Enqueue(job); |
| 233 input_queue_semaphore_.Signal(); | 262 input_queue_semaphore_.Signal(); |
| 234 } | 263 } |
| 235 | 264 |
| 236 | 265 |
| 237 OptimizingCompiler* OptimizingCompilerThread::FindReadyOSRCandidate( | 266 RecompileJob* OptimizingCompilerThread::FindReadyOSRCandidate( |
| 238 Handle<JSFunction> function, uint32_t osr_pc_offset) { | 267 Handle<JSFunction> function, uint32_t osr_pc_offset) { |
| 239 ASSERT(!IsOptimizerThread()); | 268 ASSERT(!IsOptimizerThread()); |
| 240 OptimizingCompiler* result = NULL; | 269 RecompileJob* result = NULL; |
| 241 { LockGuard<Mutex> access_osr_lists(&osr_list_mutex_); | 270 for (int i = 0; i < osr_buffer_size_; i++) { |
| 242 for (int i = 0; i < ready_for_osr_.length(); i++) { | 271 result = osr_buffer_[i]; |
| 243 if (ready_for_osr_[i]->info()->HasSameOsrEntry(function, osr_pc_offset)) { | 272 if (result == NULL) continue; |
| 244 osr_hits_++; | 273 if (result->IsWaitingForInstall() && |
| 245 result = ready_for_osr_.Remove(i); | 274 result->info()->HasSameOsrEntry(function, osr_pc_offset)) { |
| 246 break; | 275 osr_hits_++; |
| 247 } | 276 osr_buffer_[i] = NULL; |
| 277 return result; |
| 248 } | 278 } |
| 249 } | 279 } |
| 250 RemoveStaleOSRCandidates(); | 280 return NULL; |
| 251 return result; | |
| 252 } | 281 } |
| 253 | 282 |
| 254 | 283 |
| 255 bool OptimizingCompilerThread::IsQueuedForOSR(Handle<JSFunction> function, | 284 bool OptimizingCompilerThread::IsQueuedForOSR(Handle<JSFunction> function, |
| 256 uint32_t osr_pc_offset) { | 285 uint32_t osr_pc_offset) { |
| 257 ASSERT(!IsOptimizerThread()); | 286 ASSERT(!IsOptimizerThread()); |
| 258 LockGuard<Mutex> access_osr_lists(&osr_list_mutex_); | 287 for (int i = 0; i < osr_buffer_size_; i++) { |
| 259 for (int i = 0; i < osr_candidates_.length(); i++) { | 288 if (osr_buffer_[i] != NULL && |
| 260 if (osr_candidates_[i]->info()->HasSameOsrEntry(function, osr_pc_offset)) { | 289 osr_buffer_[i]->info()->HasSameOsrEntry(function, osr_pc_offset)) { |
| 261 return true; | 290 return !osr_buffer_[i]->IsWaitingForInstall(); |
| 262 } | 291 } |
| 263 } | 292 } |
| 264 return false; | 293 return false; |
| 265 } | 294 } |
| 266 | 295 |
| 267 | 296 |
| 268 bool OptimizingCompilerThread::IsQueuedForOSR(JSFunction* function) { | 297 bool OptimizingCompilerThread::IsQueuedForOSR(JSFunction* function) { |
| 269 ASSERT(!IsOptimizerThread()); | 298 ASSERT(!IsOptimizerThread()); |
| 270 LockGuard<Mutex> access_osr_lists(&osr_list_mutex_); | 299 for (int i = 0; i < osr_buffer_size_; i++) { |
| 271 for (int i = 0; i < osr_candidates_.length(); i++) { | 300 if (osr_buffer_[i] != NULL && |
| 272 if (*osr_candidates_[i]->info()->closure() == function) { | 301 *osr_buffer_[i]->info()->closure() == function) { |
| 273 return true; | 302 return !osr_buffer_[i]->IsWaitingForInstall(); |
| 274 } | 303 } |
| 275 } | 304 } |
| 276 return false; | 305 return false; |
| 277 } | 306 } |
| 278 | 307 |
| 279 | 308 |
| 280 void OptimizingCompilerThread::RemoveStaleOSRCandidates(int limit) { | 309 void OptimizingCompilerThread::AddToOsrBuffer(RecompileJob* job) { |
| 281 ASSERT(!IsOptimizerThread()); | 310 ASSERT(!IsOptimizerThread()); |
| 282 LockGuard<Mutex> access_osr_lists(&osr_list_mutex_); | 311 // Store into next empty slot or replace next stale OSR job that's waiting |
| 283 while (ready_for_osr_.length() > limit) { | 312 // in vain. Dispose in the latter case. |
| 284 OptimizingCompiler* compiler = ready_for_osr_.Remove(0); | 313 RecompileJob* stale; |
| 285 CompilationInfo* throw_away = compiler->info(); | 314 while (true) { |
| 286 if (FLAG_trace_osr) { | 315 stale = osr_buffer_[osr_cursor_]; |
| 287 PrintF("[COSR - Discarded "); | 316 if (stale == NULL) break; |
| 288 throw_away->closure()->PrintName(); | 317 if (stale->IsWaitingForInstall()) { |
| 289 PrintF(", AST id %d]\n", | 318 CompilationInfo* info = stale->info(); |
| 290 throw_away->osr_ast_id().ToInt()); | 319 if (FLAG_trace_osr) { |
| 320 PrintF("[COSR - Discarded "); |
| 321 info->closure()->PrintName(); |
| 322 PrintF(", AST id %d]\n", info->osr_ast_id().ToInt()); |
| 323 } |
| 324 DisposeRecompileJob(stale, false); |
| 325 break; |
| 291 } | 326 } |
| 292 delete throw_away; | 327 AdvanceOsrCursor(); |
| 293 } | 328 } |
| 329 |
| 330 osr_buffer_[osr_cursor_] = job; |
| 331 AdvanceOsrCursor(); |
| 294 } | 332 } |
| 295 | 333 |
| 296 | 334 |
| 297 #ifdef DEBUG | 335 #ifdef DEBUG |
| 298 bool OptimizingCompilerThread::IsOptimizerThread() { | 336 bool OptimizingCompilerThread::IsOptimizerThread() { |
| 299 if (!FLAG_concurrent_recompilation) return false; | 337 if (!FLAG_concurrent_recompilation) return false; |
| 300 LockGuard<Mutex> lock_guard(&thread_id_mutex_); | 338 LockGuard<Mutex> lock_guard(&thread_id_mutex_); |
| 301 return ThreadId::Current().ToInteger() == thread_id_; | 339 return ThreadId::Current().ToInteger() == thread_id_; |
| 302 } | 340 } |
| 303 #endif | 341 #endif |
| 304 | 342 |
| 305 | 343 |
| 306 } } // namespace v8::internal | 344 } } // namespace v8::internal |
| OLD | NEW |