Unreviewed, rolling out r154804.
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGWorklist.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "DFGWorklist.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "CodeBlock.h"
32 #include "DeferGC.h"
33 #include "DFGLongLivedState.h"
34
35 namespace JSC { namespace DFG {
36
37 Worklist::Worklist()
38     : m_numberOfActiveThreads(0)
39 {
40 }
41
42 Worklist::~Worklist()
43 {
44     {
45         MutexLocker locker(m_lock);
46         for (unsigned i = m_threads.size(); i--;)
47             m_queue.append(RefPtr<Plan>(0)); // Use null plan to indicate that we want the thread to terminate.
48         m_planEnqueued.broadcast();
49     }
50     for (unsigned i = m_threads.size(); i--;)
51         waitForThreadCompletion(m_threads[i]);
52     ASSERT(!m_numberOfActiveThreads);
53 }
54
55 void Worklist::finishCreation(unsigned numberOfThreads)
56 {
57     RELEASE_ASSERT(numberOfThreads);
58     for (unsigned i = numberOfThreads; i--;)
59         m_threads.append(createThread(threadFunction, this, "JSC Compilation Thread"));
60 }
61
62 PassRefPtr<Worklist> Worklist::create(unsigned numberOfThreads)
63 {
64     RefPtr<Worklist> result = adoptRef(new Worklist());
65     result->finishCreation(numberOfThreads);
66     return result;
67 }
68
69 void Worklist::enqueue(PassRefPtr<Plan> passedPlan)
70 {
71     RefPtr<Plan> plan = passedPlan;
72     MutexLocker locker(m_lock);
73     if (Options::verboseCompilationQueue()) {
74         dump(locker, WTF::dataFile());
75         dataLog(": Enqueueing plan to optimize ", *plan->key(), "\n");
76     }
77     ASSERT(m_plans.find(plan->key()) == m_plans.end());
78     m_plans.add(plan->key(), plan);
79     m_queue.append(plan);
80     m_planEnqueued.signal();
81 }
82
83 Worklist::State Worklist::compilationState(CodeBlock* profiledBlock)
84 {
85     MutexLocker locker(m_lock);
86     PlanMap::iterator iter = m_plans.find(profiledBlock);
87     if (iter == m_plans.end())
88         return NotKnown;
89     return iter->value->isCompiled ? Compiled : Compiling;
90 }
91
92 void Worklist::waitUntilAllPlansForVMAreReady(VM& vm)
93 {
94     DeferGC deferGC(vm.heap);
95     // Wait for all of the plans for the given VM to complete. The idea here
96     // is that we want all of the caller VM's plans to be done. We don't care
97     // about any other VM's plans, and we won't attempt to wait on those.
98     // After we release this lock, we know that although other VMs may still
99     // be adding plans, our VM will not be.
100     
101     MutexLocker locker(m_lock);
102     
103     if (Options::verboseCompilationQueue()) {
104         dump(locker, WTF::dataFile());
105         dataLog(": Waiting for all in VM to complete.\n");
106     }
107     
108     for (;;) {
109         bool allAreCompiled = true;
110         PlanMap::iterator end = m_plans.end();
111         for (PlanMap::iterator iter = m_plans.begin(); iter != end; ++iter) {
112             if (&iter->value->vm != &vm)
113                 continue;
114             if (!iter->value->isCompiled) {
115                 allAreCompiled = false;
116                 break;
117             }
118         }
119         
120         if (allAreCompiled)
121             break;
122         
123         m_planCompiled.wait(m_lock);
124     }
125 }
126
127 void Worklist::removeAllReadyPlansForVM(VM& vm, Vector<RefPtr<Plan>, 8>& myReadyPlans)
128 {
129     DeferGC deferGC(vm.heap);
130     MutexLocker locker(m_lock);
131     for (size_t i = 0; i < m_readyPlans.size(); ++i) {
132         RefPtr<Plan> plan = m_readyPlans[i];
133         if (&plan->vm != &vm)
134             continue;
135         if (!plan->isCompiled)
136             continue;
137         myReadyPlans.append(plan);
138         m_readyPlans[i--] = m_readyPlans.last();
139         m_readyPlans.removeLast();
140         m_plans.remove(plan->key());
141     }
142 }
143
144 void Worklist::removeAllReadyPlansForVM(VM& vm)
145 {
146     Vector<RefPtr<Plan>, 8> myReadyPlans;
147     removeAllReadyPlansForVM(vm, myReadyPlans);
148 }
149
150 Worklist::State Worklist::completeAllReadyPlansForVM(VM& vm, CodeBlock* requestedProfiledBlock)
151 {
152     DeferGC deferGC(vm.heap);
153     Vector<RefPtr<Plan>, 8> myReadyPlans;
154     
155     removeAllReadyPlansForVM(vm, myReadyPlans);
156     
157     State resultingState = NotKnown;
158
159     while (!myReadyPlans.isEmpty()) {
160         RefPtr<Plan> plan = myReadyPlans.takeLast();
161         CodeBlock* profiledBlock = plan->key();
162         
163         if (Options::verboseCompilationQueue())
164             dataLog(*this, ": Completing ", *profiledBlock, "\n");
165         
166         RELEASE_ASSERT(plan->isCompiled);
167         
168         CompilationResult compilationResult =
169             profiledBlock->replaceWithDeferredOptimizedCode(plan);
170         RELEASE_ASSERT(compilationResult != CompilationDeferred);
171         profiledBlock->setOptimizationThresholdBasedOnCompilationResult(compilationResult);
172         
173         if (profiledBlock == requestedProfiledBlock)
174             resultingState = Compiled;
175     }
176     
177     if (requestedProfiledBlock && resultingState == NotKnown) {
178         MutexLocker locker(m_lock);
179         if (m_plans.contains(requestedProfiledBlock))
180             resultingState = Compiling;
181     }
182     
183     return resultingState;
184 }
185
186 void Worklist::completeAllPlansForVM(VM& vm)
187 {
188     DeferGC deferGC(vm.heap);
189     waitUntilAllPlansForVMAreReady(vm);
190     completeAllReadyPlansForVM(vm);
191 }
192
193 size_t Worklist::queueLength()
194 {
195     MutexLocker locker(m_lock);
196     return m_queue.size();
197 }
198
199 void Worklist::dump(PrintStream& out) const
200 {
201     MutexLocker locker(m_lock);
202     dump(locker, out);
203 }
204
205 void Worklist::dump(const MutexLocker&, PrintStream& out) const
206 {
207     out.print(
208         "Worklist(", RawPointer(this), ")[Queue Length = ", m_queue.size(),
209         ", Map Size = ", m_plans.size(), ", Num Ready = ", m_readyPlans.size(),
210         ", Num Active Threads = ", m_numberOfActiveThreads, "/", m_threads.size(), "]");
211 }
212
213 void Worklist::runThread()
214 {
215     CompilationScope compilationScope;
216     
217     if (Options::verboseCompilationQueue())
218         dataLog(*this, ": Thread started\n");
219     
220     LongLivedState longLivedState;
221     
222     for (;;) {
223         RefPtr<Plan> plan;
224         {
225             MutexLocker locker(m_lock);
226             while (m_queue.isEmpty())
227                 m_planEnqueued.wait(m_lock);
228             plan = m_queue.takeFirst();
229             if (plan)
230                 m_numberOfActiveThreads++;
231         }
232         
233         if (!plan) {
234             if (Options::verboseCompilationQueue())
235                 dataLog(*this, ": Thread shutting down\n");
236             return;
237         }
238         
239         if (Options::verboseCompilationQueue())
240             dataLog(*this, ": Compiling ", *plan->key(), " asynchronously\n");
241         
242         plan->compileInThread(longLivedState);
243         
244         {
245             MutexLocker locker(m_lock);
246             plan->key()->forceOptimizationSlowPathConcurrently();
247             plan->isCompiled = true;
248             
249             if (Options::verboseCompilationQueue()) {
250                 dump(locker, WTF::dataFile());
251                 dataLog(": Compiled ", *plan->key(), " asynchronously\n");
252             }
253             
254             m_readyPlans.append(plan);
255             
256             m_planCompiled.broadcast();
257             m_numberOfActiveThreads--;
258         }
259     }
260 }
261
262 void Worklist::threadFunction(void* argument)
263 {
264     static_cast<Worklist*>(argument)->runThread();
265 }
266
267 static pthread_once_t initializeGlobalWorklistKeyOnce = PTHREAD_ONCE_INIT;
268 static Worklist* theGlobalWorklist;
269
270 static void initializeGlobalWorklistOnce()
271 {
272     unsigned numberOfThreads;
273     
274     if (Options::useExperimentalFTL())
275         numberOfThreads = 1; // We don't yet use LLVM in a thread-safe way.
276     else
277         numberOfThreads = Options::numberOfCompilerThreads();
278     
279     theGlobalWorklist = Worklist::create(numberOfThreads).leakRef();
280 }
281
282 Worklist* globalWorklist()
283 {
284     pthread_once(&initializeGlobalWorklistKeyOnce, initializeGlobalWorklistOnce);
285     return theGlobalWorklist;
286 }
287
288 } } // namespace JSC::DFG
289
290 #endif // ENABLE(DFG_JIT)
291