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