b971a6000f7eee7e33d2846b213b85d9a6eb90d1
[WebKit-https.git] / Source / JavaScriptCore / bytecode / CallLinkStatus.cpp
1 /*
2  * Copyright (C) 2012, 2013, 2014 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 "CallLinkStatus.h"
28
29 #include "CallLinkInfo.h"
30 #include "CodeBlock.h"
31 #include "DFGJITCode.h"
32 #include "LLIntCallLinkInfo.h"
33 #include "JSCInlines.h"
34 #include <wtf/CommaPrinter.h>
35 #include <wtf/ListDump.h>
36
37 namespace JSC {
38
39 static const bool verbose = false;
40
41 CallLinkStatus::CallLinkStatus(JSValue value)
42     : m_couldTakeSlowPath(false)
43     , m_isProved(false)
44 {
45     if (!value || !value.isCell()) {
46         m_couldTakeSlowPath = true;
47         return;
48     }
49     
50     m_edges.append(CallEdge(CallVariant(value.asCell()), 1));
51 }
52
53 CallLinkStatus CallLinkStatus::computeFromLLInt(const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, unsigned bytecodeIndex)
54 {
55     UNUSED_PARAM(profiledBlock);
56     UNUSED_PARAM(bytecodeIndex);
57 #if ENABLE(DFG_JIT)
58     if (profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCell))) {
59         // We could force this to be a closure call, but instead we'll just assume that it
60         // takes slow path.
61         return takesSlowPath();
62     }
63 #else
64     UNUSED_PARAM(locker);
65 #endif
66
67     VM& vm = *profiledBlock->vm();
68     
69     Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex;
70     OpcodeID op = vm.interpreter->getOpcodeID(instruction[0].u.opcode);
71     if (op != op_call && op != op_construct)
72         return CallLinkStatus();
73     
74     LLIntCallLinkInfo* callLinkInfo = instruction[5].u.callLinkInfo;
75     
76     return CallLinkStatus(callLinkInfo->lastSeenCallee.get());
77 }
78
79 CallLinkStatus CallLinkStatus::computeFor(
80     CodeBlock* profiledBlock, unsigned bytecodeIndex, const CallLinkInfoMap& map)
81 {
82     ConcurrentJITLocker locker(profiledBlock->m_lock);
83     
84     UNUSED_PARAM(profiledBlock);
85     UNUSED_PARAM(bytecodeIndex);
86     UNUSED_PARAM(map);
87 #if ENABLE(DFG_JIT)
88     ExitSiteData exitSiteData = computeExitSiteData(locker, profiledBlock, bytecodeIndex);
89     
90     CallLinkInfo* callLinkInfo = map.get(CodeOrigin(bytecodeIndex));
91     if (!callLinkInfo) {
92         if (exitSiteData.m_takesSlowPath)
93             return takesSlowPath();
94         return computeFromLLInt(locker, profiledBlock, bytecodeIndex);
95     }
96     
97     return computeFor(locker, profiledBlock, *callLinkInfo, exitSiteData);
98 #else
99     return CallLinkStatus();
100 #endif
101 }
102
103 CallLinkStatus::ExitSiteData CallLinkStatus::computeExitSiteData(
104     const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, unsigned bytecodeIndex,
105     ExitingJITType exitingJITType)
106 {
107     ExitSiteData exitSiteData;
108     
109 #if ENABLE(DFG_JIT)
110     exitSiteData.m_takesSlowPath =
111         profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadType, exitingJITType))
112         || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadExecutable, exitingJITType));
113     exitSiteData.m_badFunction =
114         profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCell, exitingJITType));
115 #else
116     UNUSED_PARAM(locker);
117     UNUSED_PARAM(profiledBlock);
118     UNUSED_PARAM(bytecodeIndex);
119     UNUSED_PARAM(exitingJITType);
120 #endif
121     
122     return exitSiteData;
123 }
124
125 #if ENABLE(JIT)
126 CallLinkStatus CallLinkStatus::computeFor(
127     const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, CallLinkInfo& callLinkInfo)
128 {
129     // We don't really need this, but anytime we have to debug this code, it becomes indispensable.
130     UNUSED_PARAM(profiledBlock);
131     
132     if (Options::callStatusShouldUseCallEdgeProfile()) {
133         // Always trust the call edge profile over anything else since this has precise counts.
134         // It can make the best possible decision because it never "forgets" what happened for any
135         // call, with the exception of fading out the counts of old calls (for example if the
136         // counter type is 16-bit then calls that happened more than 2^16 calls ago are given half
137         // weight, and this compounds for every 2^15 [sic] calls after that). The combination of
138         // high fidelity for recent calls and fading for older calls makes this the most useful
139         // mechamism of choosing how to optimize future calls.
140         CallEdgeProfile* edgeProfile = callLinkInfo.callEdgeProfile.get();
141         WTF::loadLoadFence();
142         if (edgeProfile) {
143             CallLinkStatus result = computeFromCallEdgeProfile(edgeProfile);
144             if (!!result)
145                 return result;
146         }
147     }
148     
149     return computeFromCallLinkInfo(locker, callLinkInfo);
150 }
151
152 CallLinkStatus CallLinkStatus::computeFromCallLinkInfo(
153     const ConcurrentJITLocker&, CallLinkInfo& callLinkInfo)
154 {
155     // Note that despite requiring that the locker is held, this code is racy with respect
156     // to the CallLinkInfo: it may get cleared while this code runs! This is because
157     // CallLinkInfo::unlink() may be called from a different CodeBlock than the one that owns
158     // the CallLinkInfo and currently we save space by not having CallLinkInfos know who owns
159     // them. So, there is no way for either the caller of CallLinkInfo::unlock() or unlock()
160     // itself to figure out which lock to lock.
161     //
162     // Fortunately, that doesn't matter. The only things we ask of CallLinkInfo - the slow
163     // path count, the stub, and the target - can all be asked racily. Stubs and targets can
164     // only be deleted at next GC, so if we load a non-null one, then it must contain data
165     // that is still marginally valid (i.e. the pointers ain't stale). This kind of raciness
166     // is probably OK for now.
167     
168     if (callLinkInfo.slowPathCount >= Options::couldTakeSlowCaseMinimumCount())
169         return takesSlowPath();
170     
171     if (ClosureCallStubRoutine* stub = callLinkInfo.stub.get())
172         return CallLinkStatus(stub->executable());
173     
174     JSFunction* target = callLinkInfo.lastSeenCallee.get();
175     if (!target)
176         return takesSlowPath();
177     
178     if (callLinkInfo.hasSeenClosure)
179         return CallLinkStatus(target->executable());
180
181     return CallLinkStatus(target);
182 }
183
184 CallLinkStatus CallLinkStatus::computeFromCallEdgeProfile(CallEdgeProfile* edgeProfile)
185 {
186     // In cases where the call edge profile saw nothing, use the CallLinkInfo instead.
187     if (!edgeProfile->totalCalls())
188         return CallLinkStatus();
189     
190     // To do anything meaningful, we require that the majority of calls are to something we
191     // know how to handle.
192     unsigned numCallsToKnown = edgeProfile->numCallsToKnownCells();
193     unsigned numCallsToUnknown = edgeProfile->numCallsToNotCell() + edgeProfile->numCallsToUnknownCell();
194     
195     // We require that the majority of calls were to something that we could possibly inline.
196     if (numCallsToKnown <= numCallsToUnknown)
197         return takesSlowPath();
198     
199     // We require that the number of such calls is greater than some minimal threshold, so that we
200     // avoid inlining completely cold calls.
201     if (numCallsToKnown < Options::frequentCallThreshold())
202         return takesSlowPath();
203     
204     CallLinkStatus result;
205     result.m_edges = edgeProfile->callEdges();
206     result.m_couldTakeSlowPath = !!numCallsToUnknown;
207     result.m_canTrustCounts = true;
208     
209     return result;
210 }
211
212 CallLinkStatus CallLinkStatus::computeFor(
213     const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, CallLinkInfo& callLinkInfo,
214     ExitSiteData exitSiteData)
215 {
216     CallLinkStatus result = computeFor(locker, profiledBlock, callLinkInfo);
217     if (exitSiteData.m_badFunction)
218         result.makeClosureCall();
219     if (exitSiteData.m_takesSlowPath)
220         result.m_couldTakeSlowPath = true;
221     
222     return result;
223 }
224 #endif
225
226 void CallLinkStatus::computeDFGStatuses(
227     CodeBlock* dfgCodeBlock, CallLinkStatus::ContextMap& map)
228 {
229 #if ENABLE(DFG_JIT)
230     RELEASE_ASSERT(dfgCodeBlock->jitType() == JITCode::DFGJIT);
231     CodeBlock* baselineCodeBlock = dfgCodeBlock->alternative();
232     for (auto iter = dfgCodeBlock->callLinkInfosBegin(); !!iter; ++iter) {
233         CallLinkInfo& info = **iter;
234         CodeOrigin codeOrigin = info.codeOrigin;
235         
236         // Check if we had already previously made a terrible mistake in the FTL for this
237         // code origin. Note that this is approximate because we could have a monovariant
238         // inline in the FTL that ended up failing. We should fix that at some point by
239         // having data structures to track the context of frequent exits. This is currently
240         // challenging because it would require creating a CodeOrigin-based database in
241         // baseline CodeBlocks, but those CodeBlocks don't really have a place to put the
242         // InlineCallFrames.
243         CodeBlock* currentBaseline =
244             baselineCodeBlockForOriginAndBaselineCodeBlock(codeOrigin, baselineCodeBlock);
245         ExitSiteData exitSiteData;
246         {
247             ConcurrentJITLocker locker(currentBaseline->m_lock);
248             exitSiteData = computeExitSiteData(
249                 locker, currentBaseline, codeOrigin.bytecodeIndex, ExitFromFTL);
250         }
251         
252         {
253             ConcurrentJITLocker locker(dfgCodeBlock->m_lock);
254             map.add(info.codeOrigin, computeFor(locker, dfgCodeBlock, info, exitSiteData));
255         }
256     }
257 #else
258     UNUSED_PARAM(dfgCodeBlock);
259 #endif // ENABLE(DFG_JIT)
260     
261     if (verbose) {
262         dataLog("Context map:\n");
263         ContextMap::iterator iter = map.begin();
264         ContextMap::iterator end = map.end();
265         for (; iter != end; ++iter) {
266             dataLog("    ", iter->key, ":\n");
267             dataLog("        ", iter->value, "\n");
268         }
269     }
270 }
271
272 CallLinkStatus CallLinkStatus::computeFor(
273     CodeBlock* profiledBlock, CodeOrigin codeOrigin,
274     const CallLinkInfoMap& baselineMap, const CallLinkStatus::ContextMap& dfgMap)
275 {
276     auto iter = dfgMap.find(codeOrigin);
277     if (iter != dfgMap.end())
278         return iter->value;
279     
280     return computeFor(profiledBlock, codeOrigin.bytecodeIndex, baselineMap);
281 }
282
283 bool CallLinkStatus::isClosureCall() const
284 {
285     for (unsigned i = m_edges.size(); i--;) {
286         if (m_edges[i].callee().isClosureCall())
287             return true;
288     }
289     return false;
290 }
291
292 void CallLinkStatus::makeClosureCall()
293 {
294     ASSERT(!m_isProved);
295     for (unsigned i = m_edges.size(); i--;)
296         m_edges[i] = m_edges[i].despecifiedClosure();
297     
298     if (!ASSERT_DISABLED) {
299         // Doing this should not have created duplicates, because the CallEdgeProfile
300         // should despecify closures if doing so would reduce the number of known callees.
301         for (unsigned i = 0; i < m_edges.size(); ++i) {
302             for (unsigned j = i + 1; j < m_edges.size(); ++j)
303                 ASSERT(m_edges[i].callee() != m_edges[j].callee());
304         }
305     }
306 }
307
308 void CallLinkStatus::dump(PrintStream& out) const
309 {
310     if (!isSet()) {
311         out.print("Not Set");
312         return;
313     }
314     
315     CommaPrinter comma;
316     
317     if (m_isProved)
318         out.print(comma, "Statically Proved");
319     
320     if (m_couldTakeSlowPath)
321         out.print(comma, "Could Take Slow Path");
322     
323     out.print(listDump(m_edges));
324 }
325
326 } // namespace JSC
327