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/compiler-dispatcher/optimizing-compile-dispatcher.h" 6 7#include "src/base/atomicops.h" 8#include "src/compilation-info.h" 9#include "src/compiler.h" 10#include "src/full-codegen/full-codegen.h" 11#include "src/isolate.h" 12#include "src/objects-inl.h" 13#include "src/tracing/trace-event.h" 14#include "src/v8.h" 15 16namespace v8 { 17namespace internal { 18 19namespace { 20 21void DisposeCompilationJob(CompilationJob* job, bool restore_function_code) { 22 if (restore_function_code) { 23 Handle<JSFunction> function = job->info()->closure(); 24 function->ReplaceCode(function->shared()->code()); 25 // TODO(mvstanton): We can't call ensureliterals here due to allocation, 26 // but we probably shouldn't call ReplaceCode either, as this 27 // sometimes runs on the worker thread! 28 // JSFunction::EnsureLiterals(function); 29 } 30 delete job; 31} 32 33} // namespace 34 35class OptimizingCompileDispatcher::CompileTask : public v8::Task { 36 public: 37 explicit CompileTask(Isolate* isolate, 38 OptimizingCompileDispatcher* dispatcher) 39 : isolate_(isolate), dispatcher_(dispatcher) { 40 base::LockGuard<base::Mutex> lock_guard(&dispatcher_->ref_count_mutex_); 41 ++dispatcher_->ref_count_; 42 } 43 44 virtual ~CompileTask() {} 45 46 private: 47 // v8::Task overrides. 48 void Run() override { 49 DisallowHeapAllocation no_allocation; 50 DisallowHandleAllocation no_handles; 51 DisallowHandleDereference no_deref; 52 53 { 54 TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_); 55 56 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), 57 "V8.RecompileConcurrent"); 58 59 if (dispatcher_->recompilation_delay_ != 0) { 60 base::OS::Sleep(base::TimeDelta::FromMilliseconds( 61 dispatcher_->recompilation_delay_)); 62 } 63 64 dispatcher_->CompileNext(dispatcher_->NextInput(true)); 65 } 66 { 67 base::LockGuard<base::Mutex> lock_guard(&dispatcher_->ref_count_mutex_); 68 if (--dispatcher_->ref_count_ == 0) { 69 dispatcher_->ref_count_zero_.NotifyOne(); 70 } 71 } 72 } 73 74 Isolate* isolate_; 75 OptimizingCompileDispatcher* dispatcher_; 76 77 DISALLOW_COPY_AND_ASSIGN(CompileTask); 78}; 79 80OptimizingCompileDispatcher::~OptimizingCompileDispatcher() { 81#ifdef DEBUG 82 { 83 base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_); 84 DCHECK_EQ(0, ref_count_); 85 } 86#endif 87 DCHECK_EQ(0, input_queue_length_); 88 DeleteArray(input_queue_); 89} 90 91CompilationJob* OptimizingCompileDispatcher::NextInput(bool check_if_flushing) { 92 base::LockGuard<base::Mutex> access_input_queue_(&input_queue_mutex_); 93 if (input_queue_length_ == 0) return NULL; 94 CompilationJob* job = input_queue_[InputQueueIndex(0)]; 95 DCHECK_NOT_NULL(job); 96 input_queue_shift_ = InputQueueIndex(1); 97 input_queue_length_--; 98 if (check_if_flushing) { 99 if (static_cast<ModeFlag>(base::Acquire_Load(&mode_)) == FLUSH) { 100 AllowHandleDereference allow_handle_dereference; 101 DisposeCompilationJob(job, true); 102 return NULL; 103 } 104 } 105 return job; 106} 107 108void OptimizingCompileDispatcher::CompileNext(CompilationJob* job) { 109 if (!job) return; 110 111 // The function may have already been optimized by OSR. Simply continue. 112 CompilationJob::Status status = job->ExecuteJob(); 113 USE(status); // Prevent an unused-variable error. 114 115 // The function may have already been optimized by OSR. Simply continue. 116 // Use a mutex to make sure that functions marked for install 117 // are always also queued. 118 base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_); 119 output_queue_.push(job); 120 isolate_->stack_guard()->RequestInstallCode(); 121} 122 123void OptimizingCompileDispatcher::FlushOutputQueue(bool restore_function_code) { 124 for (;;) { 125 CompilationJob* job = NULL; 126 { 127 base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_); 128 if (output_queue_.empty()) return; 129 job = output_queue_.front(); 130 output_queue_.pop(); 131 } 132 133 DisposeCompilationJob(job, restore_function_code); 134 } 135} 136 137void OptimizingCompileDispatcher::Flush(BlockingBehavior blocking_behavior) { 138 if (FLAG_block_concurrent_recompilation) Unblock(); 139 if (blocking_behavior == BlockingBehavior::kDontBlock) { 140 base::LockGuard<base::Mutex> access_input_queue_(&input_queue_mutex_); 141 while (input_queue_length_ > 0) { 142 CompilationJob* job = input_queue_[InputQueueIndex(0)]; 143 DCHECK_NOT_NULL(job); 144 input_queue_shift_ = InputQueueIndex(1); 145 input_queue_length_--; 146 DisposeCompilationJob(job, true); 147 } 148 FlushOutputQueue(true); 149 if (FLAG_trace_concurrent_recompilation) { 150 PrintF(" ** Flushed concurrent recompilation queues (not blocking).\n"); 151 } 152 return; 153 } 154 base::Release_Store(&mode_, static_cast<base::AtomicWord>(FLUSH)); 155 if (FLAG_block_concurrent_recompilation) Unblock(); 156 { 157 base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_); 158 while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_); 159 base::Release_Store(&mode_, static_cast<base::AtomicWord>(COMPILE)); 160 } 161 FlushOutputQueue(true); 162 if (FLAG_trace_concurrent_recompilation) { 163 PrintF(" ** Flushed concurrent recompilation queues.\n"); 164 } 165} 166 167void OptimizingCompileDispatcher::Stop() { 168 base::Release_Store(&mode_, static_cast<base::AtomicWord>(FLUSH)); 169 if (FLAG_block_concurrent_recompilation) Unblock(); 170 { 171 base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_); 172 while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_); 173 base::Release_Store(&mode_, static_cast<base::AtomicWord>(COMPILE)); 174 } 175 176 if (recompilation_delay_ != 0) { 177 // At this point the optimizing compiler thread's event loop has stopped. 178 // There is no need for a mutex when reading input_queue_length_. 179 while (input_queue_length_ > 0) CompileNext(NextInput()); 180 InstallOptimizedFunctions(); 181 } else { 182 FlushOutputQueue(false); 183 } 184} 185 186void OptimizingCompileDispatcher::InstallOptimizedFunctions() { 187 HandleScope handle_scope(isolate_); 188 189 for (;;) { 190 CompilationJob* job = NULL; 191 { 192 base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_); 193 if (output_queue_.empty()) return; 194 job = output_queue_.front(); 195 output_queue_.pop(); 196 } 197 CompilationInfo* info = job->info(); 198 Handle<JSFunction> function(*info->closure()); 199 if (function->IsOptimized()) { 200 if (FLAG_trace_concurrent_recompilation) { 201 PrintF(" ** Aborting compilation for "); 202 function->ShortPrint(); 203 PrintF(" as it has already been optimized.\n"); 204 } 205 DisposeCompilationJob(job, false); 206 } else { 207 Compiler::FinalizeCompilationJob(job); 208 } 209 } 210} 211 212void OptimizingCompileDispatcher::QueueForOptimization(CompilationJob* job) { 213 DCHECK(IsQueueAvailable()); 214 { 215 // Add job to the back of the input queue. 216 base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_); 217 DCHECK_LT(input_queue_length_, input_queue_capacity_); 218 input_queue_[InputQueueIndex(input_queue_length_)] = job; 219 input_queue_length_++; 220 } 221 if (FLAG_block_concurrent_recompilation) { 222 blocked_jobs_++; 223 } else { 224 V8::GetCurrentPlatform()->CallOnBackgroundThread( 225 new CompileTask(isolate_, this), v8::Platform::kShortRunningTask); 226 } 227} 228 229void OptimizingCompileDispatcher::Unblock() { 230 while (blocked_jobs_ > 0) { 231 V8::GetCurrentPlatform()->CallOnBackgroundThread( 232 new CompileTask(isolate_, this), v8::Platform::kShortRunningTask); 233 blocked_jobs_--; 234 } 235} 236 237} // namespace internal 238} // namespace v8 239