blob: 687a24c4d9177cc4ba18230f700413fea75ad7c2 [file] [log] [blame]
fpizlo@apple.com1f8917f2012-01-22 01:36:23 +00001/*
fpizlo@apple.com8a5fd182015-02-02 18:38:08 +00002 * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
fpizlo@apple.com1f8917f2012-01-22 01:36:23 +00003 *
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
msaboff@apple.com95894332014-01-29 19:18:54 +000029#include "CallLinkInfo.h"
fpizlo@apple.com1f8917f2012-01-22 01:36:23 +000030#include "CodeBlock.h"
msaboff@apple.com95894332014-01-29 19:18:54 +000031#include "DFGJITCode.h"
ggaren@apple.com21cd7022015-08-18 18:28:54 +000032#include "InlineCallFrame.h"
fpizlo@apple.com7bbcaab2012-02-22 05:23:19 +000033#include "LLIntCallLinkInfo.h"
fpizlo@apple.comfb7eff22014-02-11 01:45:50 +000034#include "JSCInlines.h"
fpizlo@apple.com6bd61452013-01-03 22:04:39 +000035#include <wtf/CommaPrinter.h>
fpizlo@apple.com29abafe2014-08-28 19:09:48 +000036#include <wtf/ListDump.h>
fpizlo@apple.com1f8917f2012-01-22 01:36:23 +000037
38namespace JSC {
39
msaboff@apple.com95894332014-01-29 19:18:54 +000040static const bool verbose = false;
41
fpizlo@apple.com6bd61452013-01-03 22:04:39 +000042CallLinkStatus::CallLinkStatus(JSValue value)
fpizlo@apple.com29abafe2014-08-28 19:09:48 +000043 : m_couldTakeSlowPath(false)
fpizlo@apple.com6bd61452013-01-03 22:04:39 +000044 , m_isProved(false)
45{
fpizlo@apple.com29abafe2014-08-28 19:09:48 +000046 if (!value || !value.isCell()) {
47 m_couldTakeSlowPath = true;
fpizlo@apple.com6bd61452013-01-03 22:04:39 +000048 return;
fpizlo@apple.com29abafe2014-08-28 19:09:48 +000049 }
fpizlo@apple.com6bd61452013-01-03 22:04:39 +000050
fpizlo@apple.com8a5fd182015-02-02 18:38:08 +000051 m_variants.append(CallVariant(value.asCell()));
fpizlo@apple.com6bd61452013-01-03 22:04:39 +000052}
53
msaboff@apple.com95894332014-01-29 19:18:54 +000054CallLinkStatus CallLinkStatus::computeFromLLInt(const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, unsigned bytecodeIndex)
fpizlo@apple.com7bbcaab2012-02-22 05:23:19 +000055{
56 UNUSED_PARAM(profiledBlock);
57 UNUSED_PARAM(bytecodeIndex);
msaboff@apple.com95894332014-01-29 19:18:54 +000058#if ENABLE(DFG_JIT)
fpizlo@apple.com29abafe2014-08-28 19:09:48 +000059 if (profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCell))) {
msaboff@apple.com95894332014-01-29 19:18:54 +000060 // We could force this to be a closure call, but instead we'll just assume that it
61 // takes slow path.
62 return takesSlowPath();
63 }
64#else
65 UNUSED_PARAM(locker);
66#endif
67
68 VM& vm = *profiledBlock->vm();
69
fpizlo@apple.com7bbcaab2012-02-22 05:23:19 +000070 Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex;
msaboff@apple.com95894332014-01-29 19:18:54 +000071 OpcodeID op = vm.interpreter->getOpcodeID(instruction[0].u.opcode);
msaboff@apple.comc15ae7e2015-09-16 23:40:35 +000072 if (op != op_call && op != op_construct && op != op_tail_call)
msaboff@apple.com95894332014-01-29 19:18:54 +000073 return CallLinkStatus();
74
oliver@apple.comcf0e6c42013-07-25 04:01:45 +000075 LLIntCallLinkInfo* callLinkInfo = instruction[5].u.callLinkInfo;
fpizlo@apple.com7bbcaab2012-02-22 05:23:19 +000076
fpizlo@apple.com6bd61452013-01-03 22:04:39 +000077 return CallLinkStatus(callLinkInfo->lastSeenCallee.get());
fpizlo@apple.com7bbcaab2012-02-22 05:23:19 +000078}
79
fpizlo@apple.comba262b22014-03-23 04:34:38 +000080CallLinkStatus CallLinkStatus::computeFor(
81 CodeBlock* profiledBlock, unsigned bytecodeIndex, const CallLinkInfoMap& map)
fpizlo@apple.com1f8917f2012-01-22 01:36:23 +000082{
oliver@apple.comd2056662013-07-25 04:00:37 +000083 ConcurrentJITLocker locker(profiledBlock->m_lock);
oliver@apple.com0c1b13e2013-07-25 03:58:43 +000084
fpizlo@apple.com246741b2012-01-22 09:11:49 +000085 UNUSED_PARAM(profiledBlock);
86 UNUSED_PARAM(bytecodeIndex);
fpizlo@apple.comf7100b62014-03-24 17:29:51 +000087 UNUSED_PARAM(map);
commit-queue@webkit.orge9754872014-02-15 08:30:54 +000088#if ENABLE(DFG_JIT)
fpizlo@apple.com4c6b8ad2014-07-22 21:08:50 +000089 ExitSiteData exitSiteData = computeExitSiteData(locker, profiledBlock, bytecodeIndex);
msaboff@apple.com95894332014-01-29 19:18:54 +000090
fpizlo@apple.comba262b22014-03-23 04:34:38 +000091 CallLinkInfo* callLinkInfo = map.get(CodeOrigin(bytecodeIndex));
fpizlo@apple.comdc983c42015-01-28 05:48:59 +000092 if (!callLinkInfo) {
93 if (exitSiteData.m_takesSlowPath)
94 return takesSlowPath();
msaboff@apple.com95894332014-01-29 19:18:54 +000095 return computeFromLLInt(locker, profiledBlock, bytecodeIndex);
fpizlo@apple.comdc983c42015-01-28 05:48:59 +000096 }
fpizlo@apple.com7bbcaab2012-02-22 05:23:19 +000097
fpizlo@apple.com29abafe2014-08-28 19:09:48 +000098 return computeFor(locker, profiledBlock, *callLinkInfo, exitSiteData);
msaboff@apple.com95894332014-01-29 19:18:54 +000099#else
100 return CallLinkStatus();
101#endif
102}
103
fpizlo@apple.com4c6b8ad2014-07-22 21:08:50 +0000104CallLinkStatus::ExitSiteData CallLinkStatus::computeExitSiteData(
fpizlo@apple.comc1f69d72015-06-02 05:39:11 +0000105 const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, unsigned bytecodeIndex)
fpizlo@apple.com4c6b8ad2014-07-22 21:08:50 +0000106{
107 ExitSiteData exitSiteData;
108
109#if ENABLE(DFG_JIT)
110 exitSiteData.m_takesSlowPath =
fpizlo@apple.comc1f69d72015-06-02 05:39:11 +0000111 profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadType))
112 || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadExecutable));
fpizlo@apple.com4c6b8ad2014-07-22 21:08:50 +0000113 exitSiteData.m_badFunction =
fpizlo@apple.comc1f69d72015-06-02 05:39:11 +0000114 profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCell));
fpizlo@apple.com0d24eb32014-07-22 21:30:48 +0000115#else
116 UNUSED_PARAM(locker);
117 UNUSED_PARAM(profiledBlock);
118 UNUSED_PARAM(bytecodeIndex);
fpizlo@apple.com4c6b8ad2014-07-22 21:08:50 +0000119#endif
120
121 return exitSiteData;
122}
123
msaboff@apple.com95894332014-01-29 19:18:54 +0000124#if ENABLE(JIT)
fpizlo@apple.com29abafe2014-08-28 19:09:48 +0000125CallLinkStatus CallLinkStatus::computeFor(
126 const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, CallLinkInfo& callLinkInfo)
127{
128 // We don't really need this, but anytime we have to debug this code, it becomes indispensable.
129 UNUSED_PARAM(profiledBlock);
130
fpizlo@apple.com8fefdd32015-02-18 19:55:47 +0000131 CallLinkStatus result = computeFromCallLinkInfo(locker, callLinkInfo);
msaboff@apple.com203a56e2015-06-24 22:37:30 +0000132 result.m_maxNumArguments = callLinkInfo.maxNumArguments();
fpizlo@apple.com8fefdd32015-02-18 19:55:47 +0000133 return result;
fpizlo@apple.com29abafe2014-08-28 19:09:48 +0000134}
135
136CallLinkStatus CallLinkStatus::computeFromCallLinkInfo(
137 const ConcurrentJITLocker&, CallLinkInfo& callLinkInfo)
msaboff@apple.com95894332014-01-29 19:18:54 +0000138{
msaboff@apple.com203a56e2015-06-24 22:37:30 +0000139 if (callLinkInfo.clearedByGC())
fpizlo@apple.com23bc5842015-06-03 20:08:01 +0000140 return takesSlowPath();
141
fpizlo@apple.com2bcd60f2014-05-01 15:48:05 +0000142 // Note that despite requiring that the locker is held, this code is racy with respect
143 // to the CallLinkInfo: it may get cleared while this code runs! This is because
144 // CallLinkInfo::unlink() may be called from a different CodeBlock than the one that owns
145 // the CallLinkInfo and currently we save space by not having CallLinkInfos know who owns
146 // them. So, there is no way for either the caller of CallLinkInfo::unlock() or unlock()
147 // itself to figure out which lock to lock.
148 //
149 // Fortunately, that doesn't matter. The only things we ask of CallLinkInfo - the slow
150 // path count, the stub, and the target - can all be asked racily. Stubs and targets can
151 // only be deleted at next GC, so if we load a non-null one, then it must contain data
152 // that is still marginally valid (i.e. the pointers ain't stale). This kind of raciness
153 // is probably OK for now.
154
fpizlo@apple.com8a5fd182015-02-02 18:38:08 +0000155 // PolymorphicCallStubRoutine is a GCAwareJITStubRoutine, so if non-null, it will stay alive
156 // until next GC even if the CallLinkInfo is concurrently cleared. Also, the variants list is
157 // never mutated after the PolymorphicCallStubRoutine is instantiated. We have some conservative
158 // fencing in place to make sure that we see the variants list after construction.
msaboff@apple.com203a56e2015-06-24 22:37:30 +0000159 if (PolymorphicCallStubRoutine* stub = callLinkInfo.stub()) {
fpizlo@apple.com8a5fd182015-02-02 18:38:08 +0000160 WTF::loadLoadFence();
161
162 CallEdgeList edges = stub->edges();
163
164 // Now that we've loaded the edges list, there are no further concurrency concerns. We will
165 // just manipulate and prune this list to our liking - mostly removing entries that are too
166 // infrequent and ensuring that it's sorted in descending order of frequency.
167
168 RELEASE_ASSERT(edges.size());
169
170 std::sort(
171 edges.begin(), edges.end(),
172 [] (CallEdge a, CallEdge b) {
173 return a.count() > b.count();
174 });
175 RELEASE_ASSERT(edges.first().count() >= edges.last().count());
176
177 double totalCallsToKnown = 0;
msaboff@apple.com203a56e2015-06-24 22:37:30 +0000178 double totalCallsToUnknown = callLinkInfo.slowPathCount();
fpizlo@apple.com8a5fd182015-02-02 18:38:08 +0000179 CallVariantList variants;
180 for (size_t i = 0; i < edges.size(); ++i) {
181 CallEdge edge = edges[i];
182 // If the call is at the tail of the distribution, then we don't optimize it and we
183 // treat it as if it was a call to something unknown. We define the tail as being either
184 // a call that doesn't belong to the N most frequent callees (N =
185 // maxPolymorphicCallVariantsForInlining) or that has a total call count that is too
186 // small.
187 if (i >= Options::maxPolymorphicCallVariantsForInlining()
188 || edge.count() < Options::frequentCallThreshold())
189 totalCallsToUnknown += edge.count();
190 else {
191 totalCallsToKnown += edge.count();
192 variants.append(edge.callee());
193 }
194 }
195
196 // Bail if we didn't find any calls that qualified.
197 RELEASE_ASSERT(!!totalCallsToKnown == !!variants.size());
198 if (variants.isEmpty())
199 return takesSlowPath();
200
201 // We require that the distribution of callees is skewed towards a handful of common ones.
202 if (totalCallsToKnown / totalCallsToUnknown < Options::minimumCallToKnownRate())
203 return takesSlowPath();
204
205 RELEASE_ASSERT(totalCallsToKnown);
206 RELEASE_ASSERT(variants.size());
207
208 CallLinkStatus result;
209 result.m_variants = variants;
210 result.m_couldTakeSlowPath = !!totalCallsToUnknown;
211 return result;
212 }
213
fpizlo@apple.com481b5f12015-06-02 02:59:39 +0000214 CallLinkStatus result;
fpizlo@apple.comba262b22014-03-23 04:34:38 +0000215
msaboff@apple.com203a56e2015-06-24 22:37:30 +0000216 if (JSFunction* target = callLinkInfo.lastSeenCallee()) {
fpizlo@apple.com481b5f12015-06-02 02:59:39 +0000217 CallVariant variant(target);
msaboff@apple.com203a56e2015-06-24 22:37:30 +0000218 if (callLinkInfo.hasSeenClosure())
fpizlo@apple.com481b5f12015-06-02 02:59:39 +0000219 variant = variant.despecifiedClosure();
220 result.m_variants.append(variant);
221 }
fpizlo@apple.com806b5822013-01-08 01:23:38 +0000222
msaboff@apple.com203a56e2015-06-24 22:37:30 +0000223 result.m_couldTakeSlowPath = !!callLinkInfo.slowPathCount();
fpizlo@apple.com6bd61452013-01-03 22:04:39 +0000224
fpizlo@apple.com481b5f12015-06-02 02:59:39 +0000225 return result;
msaboff@apple.com95894332014-01-29 19:18:54 +0000226}
fpizlo@apple.com4c6b8ad2014-07-22 21:08:50 +0000227
fpizlo@apple.com29abafe2014-08-28 19:09:48 +0000228CallLinkStatus CallLinkStatus::computeFor(
229 const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, CallLinkInfo& callLinkInfo,
230 ExitSiteData exitSiteData)
231{
232 CallLinkStatus result = computeFor(locker, profiledBlock, callLinkInfo);
fpizlo@apple.com4c6b8ad2014-07-22 21:08:50 +0000233 if (exitSiteData.m_badFunction)
234 result.makeClosureCall();
fpizlo@apple.com29abafe2014-08-28 19:09:48 +0000235 if (exitSiteData.m_takesSlowPath)
236 result.m_couldTakeSlowPath = true;
fpizlo@apple.com4c6b8ad2014-07-22 21:08:50 +0000237
238 return result;
239}
fpizlo@apple.com1f8917f2012-01-22 01:36:23 +0000240#endif
msaboff@apple.com95894332014-01-29 19:18:54 +0000241
242void CallLinkStatus::computeDFGStatuses(
243 CodeBlock* dfgCodeBlock, CallLinkStatus::ContextMap& map)
244{
245#if ENABLE(DFG_JIT)
246 RELEASE_ASSERT(dfgCodeBlock->jitType() == JITCode::DFGJIT);
247 CodeBlock* baselineCodeBlock = dfgCodeBlock->alternative();
fpizlo@apple.comba262b22014-03-23 04:34:38 +0000248 for (auto iter = dfgCodeBlock->callLinkInfosBegin(); !!iter; ++iter) {
249 CallLinkInfo& info = **iter;
msaboff@apple.com203a56e2015-06-24 22:37:30 +0000250 CodeOrigin codeOrigin = info.codeOrigin();
msaboff@apple.com95894332014-01-29 19:18:54 +0000251
msaboff@apple.com95894332014-01-29 19:18:54 +0000252 // Check if we had already previously made a terrible mistake in the FTL for this
253 // code origin. Note that this is approximate because we could have a monovariant
254 // inline in the FTL that ended up failing. We should fix that at some point by
255 // having data structures to track the context of frequent exits. This is currently
256 // challenging because it would require creating a CodeOrigin-based database in
257 // baseline CodeBlocks, but those CodeBlocks don't really have a place to put the
258 // InlineCallFrames.
259 CodeBlock* currentBaseline =
260 baselineCodeBlockForOriginAndBaselineCodeBlock(codeOrigin, baselineCodeBlock);
fpizlo@apple.com4c6b8ad2014-07-22 21:08:50 +0000261 ExitSiteData exitSiteData;
msaboff@apple.com95894332014-01-29 19:18:54 +0000262 {
263 ConcurrentJITLocker locker(currentBaseline->m_lock);
fpizlo@apple.com4c6b8ad2014-07-22 21:08:50 +0000264 exitSiteData = computeExitSiteData(
fpizlo@apple.comc1f69d72015-06-02 05:39:11 +0000265 locker, currentBaseline, codeOrigin.bytecodeIndex);
msaboff@apple.com95894332014-01-29 19:18:54 +0000266 }
267
268 {
269 ConcurrentJITLocker locker(dfgCodeBlock->m_lock);
msaboff@apple.com203a56e2015-06-24 22:37:30 +0000270 map.add(info.codeOrigin(), computeFor(locker, dfgCodeBlock, info, exitSiteData));
msaboff@apple.com95894332014-01-29 19:18:54 +0000271 }
272 }
273#else
274 UNUSED_PARAM(dfgCodeBlock);
275#endif // ENABLE(DFG_JIT)
276
277 if (verbose) {
278 dataLog("Context map:\n");
279 ContextMap::iterator iter = map.begin();
280 ContextMap::iterator end = map.end();
281 for (; iter != end; ++iter) {
282 dataLog(" ", iter->key, ":\n");
283 dataLog(" ", iter->value, "\n");
284 }
285 }
286}
287
288CallLinkStatus CallLinkStatus::computeFor(
fpizlo@apple.comba262b22014-03-23 04:34:38 +0000289 CodeBlock* profiledBlock, CodeOrigin codeOrigin,
290 const CallLinkInfoMap& baselineMap, const CallLinkStatus::ContextMap& dfgMap)
msaboff@apple.com95894332014-01-29 19:18:54 +0000291{
fpizlo@apple.comba262b22014-03-23 04:34:38 +0000292 auto iter = dfgMap.find(codeOrigin);
293 if (iter != dfgMap.end())
msaboff@apple.com95894332014-01-29 19:18:54 +0000294 return iter->value;
295
fpizlo@apple.comba262b22014-03-23 04:34:38 +0000296 return computeFor(profiledBlock, codeOrigin.bytecodeIndex, baselineMap);
fpizlo@apple.com1f8917f2012-01-22 01:36:23 +0000297}
298
fpizlo@apple.com8fefdd32015-02-18 19:55:47 +0000299void CallLinkStatus::setProvenConstantCallee(CallVariant variant)
300{
301 m_variants = CallVariantList{ variant };
302 m_couldTakeSlowPath = false;
303 m_isProved = true;
304}
305
fpizlo@apple.com29abafe2014-08-28 19:09:48 +0000306bool CallLinkStatus::isClosureCall() const
307{
fpizlo@apple.com8a5fd182015-02-02 18:38:08 +0000308 for (unsigned i = m_variants.size(); i--;) {
309 if (m_variants[i].isClosureCall())
fpizlo@apple.com29abafe2014-08-28 19:09:48 +0000310 return true;
311 }
312 return false;
313}
314
315void CallLinkStatus::makeClosureCall()
316{
fpizlo@apple.com8a5fd182015-02-02 18:38:08 +0000317 m_variants = despecifiedVariantList(m_variants);
fpizlo@apple.com29abafe2014-08-28 19:09:48 +0000318}
319
fpizlo@apple.com5e2296a2013-01-07 02:24:58 +0000320void CallLinkStatus::dump(PrintStream& out) const
fpizlo@apple.com6bd61452013-01-03 22:04:39 +0000321{
322 if (!isSet()) {
323 out.print("Not Set");
324 return;
325 }
326
327 CommaPrinter comma;
328
329 if (m_isProved)
330 out.print(comma, "Statically Proved");
331
332 if (m_couldTakeSlowPath)
333 out.print(comma, "Could Take Slow Path");
334
fpizlo@apple.com8a5fd182015-02-02 18:38:08 +0000335 if (!m_variants.isEmpty())
336 out.print(comma, listDump(m_variants));
fpizlo@apple.com8fefdd32015-02-18 19:55:47 +0000337
338 if (m_maxNumArguments)
339 out.print(comma, "maxNumArguments = ", m_maxNumArguments);
fpizlo@apple.com6bd61452013-01-03 22:04:39 +0000340}
341
fpizlo@apple.com1f8917f2012-01-22 01:36:23 +0000342} // namespace JSC
343