Line data Source code
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 :
16 : namespace v8 {
17 : namespace internal {
18 :
19 : namespace {
20 :
21 11391 : void DisposeCompilationJob(CompilationJob* job, bool restore_function_code) {
22 8094 : if (restore_function_code) {
23 : Handle<JSFunction> function = job->info()->closure();
24 3297 : 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 8094 : delete job;
31 8094 : }
32 :
33 : } // namespace
34 :
35 : class OptimizingCompileDispatcher::CompileTask : public v8::Task {
36 : public:
37 51653 : explicit CompileTask(Isolate* isolate,
38 : OptimizingCompileDispatcher* dispatcher)
39 51653 : : isolate_(isolate), dispatcher_(dispatcher) {
40 51653 : base::LockGuard<base::Mutex> lock_guard(&dispatcher_->ref_count_mutex_);
41 51653 : ++dispatcher_->ref_count_;
42 51653 : }
43 :
44 103306 : virtual ~CompileTask() {}
45 :
46 : private:
47 : // v8::Task overrides.
48 51653 : void Run() override {
49 : DisallowHeapAllocation no_allocation;
50 : DisallowHandleAllocation no_handles;
51 : DisallowHandleDereference no_deref;
52 :
53 : {
54 51653 : TimerEventScope<TimerEventRecompileConcurrent> timer(isolate_);
55 :
56 154959 : TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
57 : "V8.RecompileConcurrent");
58 :
59 51653 : if (dispatcher_->recompilation_delay_ != 0) {
60 : base::OS::Sleep(base::TimeDelta::FromMilliseconds(
61 0 : dispatcher_->recompilation_delay_));
62 : }
63 :
64 51653 : dispatcher_->CompileNext(dispatcher_->NextInput(true));
65 : }
66 : {
67 51653 : base::LockGuard<base::Mutex> lock_guard(&dispatcher_->ref_count_mutex_);
68 51653 : if (--dispatcher_->ref_count_ == 0) {
69 45671 : dispatcher_->ref_count_zero_.NotifyOne();
70 : }
71 : }
72 51653 : }
73 :
74 : Isolate* isolate_;
75 : OptimizingCompileDispatcher* dispatcher_;
76 :
77 : DISALLOW_COPY_AND_ASSIGN(CompileTask);
78 : };
79 :
80 177407 : OptimizingCompileDispatcher::~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 59135 : DeleteArray(input_queue_);
89 59136 : }
90 :
91 51653 : CompilationJob* OptimizingCompileDispatcher::NextInput(bool check_if_flushing) {
92 51653 : base::LockGuard<base::Mutex> access_input_queue_(&input_queue_mutex_);
93 51653 : if (input_queue_length_ == 0) return NULL;
94 103282 : CompilationJob* job = input_queue_[InputQueueIndex(0)];
95 : DCHECK_NOT_NULL(job);
96 51641 : input_queue_shift_ = InputQueueIndex(1);
97 51641 : input_queue_length_--;
98 51641 : if (check_if_flushing) {
99 103282 : if (static_cast<ModeFlag>(base::Acquire_Load(&mode_)) == FLUSH) {
100 : AllowHandleDereference allow_handle_dereference;
101 44 : DisposeCompilationJob(job, true);
102 : return NULL;
103 : }
104 : }
105 51597 : return job;
106 : }
107 :
108 51653 : void OptimizingCompileDispatcher::CompileNext(CompilationJob* job) {
109 103306 : if (!job) return;
110 :
111 : // The function may have already been optimized by OSR. Simply continue.
112 51597 : 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 51588 : base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_);
119 : output_queue_.push(job);
120 51597 : isolate_->stack_guard()->RequestInstallCode();
121 : }
122 :
123 83701 : void OptimizingCompileDispatcher::FlushOutputQueue(bool restore_function_code) {
124 : for (;;) {
125 : CompilationJob* job = NULL;
126 : {
127 87066 : base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_);
128 170769 : if (output_queue_.empty()) return;
129 3365 : job = output_queue_.front();
130 : output_queue_.pop();
131 : }
132 :
133 3365 : DisposeCompilationJob(job, restore_function_code);
134 3365 : }
135 : }
136 :
137 24567 : void OptimizingCompileDispatcher::Flush(BlockingBehavior blocking_behavior) {
138 24567 : if (blocking_behavior == BlockingBehavior::kDontBlock) {
139 12852 : if (FLAG_block_concurrent_recompilation) Unblock();
140 12852 : base::LockGuard<base::Mutex> access_input_queue_(&input_queue_mutex_);
141 12864 : while (input_queue_length_ > 0) {
142 24 : CompilationJob* job = input_queue_[InputQueueIndex(0)];
143 : DCHECK_NOT_NULL(job);
144 12 : input_queue_shift_ = InputQueueIndex(1);
145 12 : input_queue_length_--;
146 12 : DisposeCompilationJob(job, true);
147 : }
148 12852 : FlushOutputQueue(true);
149 12852 : if (FLAG_trace_concurrent_recompilation) {
150 0 : PrintF(" ** Flushed concurrent recompilation queues (not blocking).\n");
151 : }
152 24567 : return;
153 : }
154 11715 : base::Release_Store(&mode_, static_cast<base::AtomicWord>(FLUSH));
155 11715 : if (FLAG_block_concurrent_recompilation) Unblock();
156 : {
157 11715 : base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_);
158 23 : while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_);
159 : base::Release_Store(&mode_, static_cast<base::AtomicWord>(COMPILE));
160 : }
161 11715 : FlushOutputQueue(true);
162 11715 : if (FLAG_trace_concurrent_recompilation) {
163 0 : PrintF(" ** Flushed concurrent recompilation queues.\n");
164 : }
165 : }
166 :
167 59135 : void OptimizingCompileDispatcher::Stop() {
168 59135 : base::Release_Store(&mode_, static_cast<base::AtomicWord>(FLUSH));
169 59135 : if (FLAG_block_concurrent_recompilation) Unblock();
170 : {
171 59135 : base::LockGuard<base::Mutex> lock_guard(&ref_count_mutex_);
172 60 : while (ref_count_ > 0) ref_count_zero_.Wait(&ref_count_mutex_);
173 : base::Release_Store(&mode_, static_cast<base::AtomicWord>(COMPILE));
174 : }
175 :
176 59134 : 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 0 : while (input_queue_length_ > 0) CompileNext(NextInput());
180 0 : InstallOptimizedFunctions();
181 : } else {
182 59134 : FlushOutputQueue(false);
183 : }
184 59135 : }
185 :
186 63646 : void OptimizingCompileDispatcher::InstallOptimizedFunctions() {
187 63646 : HandleScope handle_scope(isolate_);
188 :
189 : for (;;) {
190 48218 : CompilationJob* job = NULL;
191 : {
192 111864 : base::LockGuard<base::Mutex> access_output_queue_(&output_queue_mutex_);
193 175510 : if (output_queue_.empty()) return;
194 48218 : job = output_queue_.front();
195 : output_queue_.pop();
196 : }
197 : CompilationInfo* info = job->info();
198 : Handle<JSFunction> function(*info->closure());
199 48218 : if (function->IsOptimized()) {
200 4673 : if (FLAG_trace_concurrent_recompilation) {
201 0 : PrintF(" ** Aborting compilation for ");
202 0 : function->ShortPrint();
203 0 : PrintF(" as it has already been optimized.\n");
204 : }
205 4673 : DisposeCompilationJob(job, false);
206 : } else {
207 43545 : Compiler::FinalizeCompilationJob(job);
208 : }
209 : }
210 : }
211 :
212 51653 : void OptimizingCompileDispatcher::QueueForOptimization(CompilationJob* job) {
213 : DCHECK(IsQueueAvailable());
214 : {
215 : // Add job to the back of the input queue.
216 51653 : base::LockGuard<base::Mutex> access_input_queue(&input_queue_mutex_);
217 : DCHECK_LT(input_queue_length_, input_queue_capacity_);
218 103306 : input_queue_[InputQueueIndex(input_queue_length_)] = job;
219 51653 : input_queue_length_++;
220 : }
221 51653 : if (FLAG_block_concurrent_recompilation) {
222 92 : blocked_jobs_++;
223 : } else {
224 51561 : V8::GetCurrentPlatform()->CallOnBackgroundThread(
225 51561 : new CompileTask(isolate_, this), v8::Platform::kShortRunningTask);
226 : }
227 51653 : }
228 :
229 170 : void OptimizingCompileDispatcher::Unblock() {
230 432 : while (blocked_jobs_ > 0) {
231 92 : V8::GetCurrentPlatform()->CallOnBackgroundThread(
232 92 : new CompileTask(isolate_, this), v8::Platform::kShortRunningTask);
233 92 : blocked_jobs_--;
234 : }
235 170 : }
236 :
237 : } // namespace internal
238 : } // namespace v8
|