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
36namespace JSC {
37
38static const bool verbose = false;
39
40CallLinkStatus::CallLinkStatus(JSValue value)
41    : m_callTarget(value)
42    , m_executable(0)
43    , m_structure(0)
44    , m_couldTakeSlowPath(false)
45    , m_isProved(false)
46{
47    if (!value || !value.isCell())
48        return;
49
50    m_structure = value.asCell()->structure();
51
52    if (!value.asCell()->inherits(JSFunction::info()))
53        return;
54
55    m_executable = jsCast<JSFunction*>(value.asCell())->executable();
56}
57
58JSFunction* CallLinkStatus::function() const
59{
60    if (!m_callTarget || !m_callTarget.isCell())
61        return 0;
62
63    if (!m_callTarget.asCell()->inherits(JSFunction::info()))
64        return 0;
65
66    return jsCast<JSFunction*>(m_callTarget.asCell());
67}
68
69InternalFunction* CallLinkStatus::internalFunction() const
70{
71    if (!m_callTarget || !m_callTarget.isCell())
72        return 0;
73
74    if (!m_callTarget.asCell()->inherits(InternalFunction::info()))
75        return 0;
76
77    return jsCast<InternalFunction*>(m_callTarget.asCell());
78}
79
80Intrinsic CallLinkStatus::intrinsicFor(CodeSpecializationKind kind) const
81{
82    if (!m_executable)
83        return NoIntrinsic;
84
85    return m_executable->intrinsicFor(kind);
86}
87
88CallLinkStatus CallLinkStatus::computeFromLLInt(const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, unsigned bytecodeIndex)
89{
90    UNUSED_PARAM(profiledBlock);
91    UNUSED_PARAM(bytecodeIndex);
92#if ENABLE(DFG_JIT)
93    if (profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadFunction))) {
94        // We could force this to be a closure call, but instead we'll just assume that it
95        // takes slow path.
96        return takesSlowPath();
97    }
98#else
99    UNUSED_PARAM(locker);
100#endif
101
102    VM& vm = *profiledBlock->vm();
103
104    Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex;
105    OpcodeID op = vm.interpreter->getOpcodeID(instruction[0].u.opcode);
106    if (op != op_call && op != op_construct)
107        return CallLinkStatus();
108
109    LLIntCallLinkInfo* callLinkInfo = instruction[5].u.callLinkInfo;
110
111    return CallLinkStatus(callLinkInfo->lastSeenCallee.get());
112}
113
114CallLinkStatus CallLinkStatus::computeFor(
115    CodeBlock* profiledBlock, unsigned bytecodeIndex, const CallLinkInfoMap& map)
116{
117    ConcurrentJITLocker locker(profiledBlock->m_lock);
118
119    UNUSED_PARAM(profiledBlock);
120    UNUSED_PARAM(bytecodeIndex);
121    UNUSED_PARAM(map);
122#if ENABLE(DFG_JIT)
123    if (profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCache))
124        || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCacheWatchpoint))
125        || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadExecutable)))
126        return takesSlowPath();
127
128    CallLinkInfo* callLinkInfo = map.get(CodeOrigin(bytecodeIndex));
129    if (!callLinkInfo)
130        return computeFromLLInt(locker, profiledBlock, bytecodeIndex);
131
132    CallLinkStatus result = computeFor(locker, *callLinkInfo);
133    if (!result)
134        return computeFromLLInt(locker, profiledBlock, bytecodeIndex);
135
136    if (profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadFunction)))
137        result.makeClosureCall();
138
139    return result;
140#else
141    return CallLinkStatus();
142#endif
143}
144
145#if ENABLE(JIT)
146CallLinkStatus CallLinkStatus::computeFor(const ConcurrentJITLocker&, CallLinkInfo& callLinkInfo)
147{
148    // Note that despite requiring that the locker is held, this code is racy with respect
149    // to the CallLinkInfo: it may get cleared while this code runs! This is because
150    // CallLinkInfo::unlink() may be called from a different CodeBlock than the one that owns
151    // the CallLinkInfo and currently we save space by not having CallLinkInfos know who owns
152    // them. So, there is no way for either the caller of CallLinkInfo::unlock() or unlock()
153    // itself to figure out which lock to lock.
154    //
155    // Fortunately, that doesn't matter. The only things we ask of CallLinkInfo - the slow
156    // path count, the stub, and the target - can all be asked racily. Stubs and targets can
157    // only be deleted at next GC, so if we load a non-null one, then it must contain data
158    // that is still marginally valid (i.e. the pointers ain't stale). This kind of raciness
159    // is probably OK for now.
160
161    if (callLinkInfo.slowPathCount >= Options::couldTakeSlowCaseMinimumCount())
162        return takesSlowPath();
163
164    if (ClosureCallStubRoutine* stub = callLinkInfo.stub.get())
165        return CallLinkStatus(stub->executable(), stub->structure());
166
167    JSFunction* target = callLinkInfo.lastSeenCallee.get();
168    if (!target)
169        return CallLinkStatus();
170
171    if (callLinkInfo.hasSeenClosure)
172        return CallLinkStatus(target->executable(), target->structure());
173
174    return CallLinkStatus(target);
175}
176#endif
177
178void CallLinkStatus::computeDFGStatuses(
179    CodeBlock* dfgCodeBlock, CallLinkStatus::ContextMap& map)
180{
181#if ENABLE(DFG_JIT)
182    RELEASE_ASSERT(dfgCodeBlock->jitType() == JITCode::DFGJIT);
183    CodeBlock* baselineCodeBlock = dfgCodeBlock->alternative();
184    for (auto iter = dfgCodeBlock->callLinkInfosBegin(); !!iter; ++iter) {
185        CallLinkInfo& info = **iter;
186        CodeOrigin codeOrigin = info.codeOrigin;
187
188        bool takeSlowPath;
189        bool badFunction;
190
191        // Check if we had already previously made a terrible mistake in the FTL for this
192        // code origin. Note that this is approximate because we could have a monovariant
193        // inline in the FTL that ended up failing. We should fix that at some point by
194        // having data structures to track the context of frequent exits. This is currently
195        // challenging because it would require creating a CodeOrigin-based database in
196        // baseline CodeBlocks, but those CodeBlocks don't really have a place to put the
197        // InlineCallFrames.
198        CodeBlock* currentBaseline =
199            baselineCodeBlockForOriginAndBaselineCodeBlock(codeOrigin, baselineCodeBlock);
200        {
201            ConcurrentJITLocker locker(currentBaseline->m_lock);
202            takeSlowPath =
203                currentBaseline->hasExitSite(locker, DFG::FrequentExitSite(codeOrigin.bytecodeIndex, BadCache, ExitFromFTL))
204                || currentBaseline->hasExitSite(locker, DFG::FrequentExitSite(codeOrigin.bytecodeIndex, BadCacheWatchpoint, ExitFromFTL))
205                || currentBaseline->hasExitSite(locker, DFG::FrequentExitSite(codeOrigin.bytecodeIndex, BadExecutable, ExitFromFTL));
206            badFunction =
207                currentBaseline->hasExitSite(locker, DFG::FrequentExitSite(codeOrigin.bytecodeIndex, BadFunction, ExitFromFTL));
208        }
209
210        {
211            ConcurrentJITLocker locker(dfgCodeBlock->m_lock);
212            if (takeSlowPath)
213                map.add(info.codeOrigin, takesSlowPath());
214            else {
215                CallLinkStatus status = computeFor(locker, info);
216                if (status.isSet()) {
217                    if (badFunction)
218                        status.makeClosureCall();
219                    map.add(info.codeOrigin, status);
220                }
221            }
222        }
223    }
224#else
225    UNUSED_PARAM(dfgCodeBlock);
226#endif // ENABLE(DFG_JIT)
227
228    if (verbose) {
229        dataLog("Context map:\n");
230        ContextMap::iterator iter = map.begin();
231        ContextMap::iterator end = map.end();
232        for (; iter != end; ++iter) {
233            dataLog("    ", iter->key, ":\n");
234            dataLog("        ", iter->value, "\n");
235        }
236    }
237}
238
239CallLinkStatus CallLinkStatus::computeFor(
240    CodeBlock* profiledBlock, CodeOrigin codeOrigin,
241    const CallLinkInfoMap& baselineMap, const CallLinkStatus::ContextMap& dfgMap)
242{
243    auto iter = dfgMap.find(codeOrigin);
244    if (iter != dfgMap.end())
245        return iter->value;
246
247    return computeFor(profiledBlock, codeOrigin.bytecodeIndex, baselineMap);
248}
249
250void CallLinkStatus::dump(PrintStream& out) const
251{
252    if (!isSet()) {
253        out.print("Not Set");
254        return;
255    }
256
257    CommaPrinter comma;
258
259    if (m_isProved)
260        out.print(comma, "Statically Proved");
261
262    if (m_couldTakeSlowPath)
263        out.print(comma, "Could Take Slow Path");
264
265    if (m_callTarget)
266        out.print(comma, "Known target: ", m_callTarget);
267
268    if (m_executable) {
269        out.print(comma, "Executable/CallHash: ", RawPointer(m_executable));
270        if (!isCompilationThread())
271            out.print("/", m_executable->hashFor(CodeForCall));
272    }
273
274    if (m_structure)
275        out.print(comma, "Structure: ", RawPointer(m_structure));
276}
277
278} // namespace JSC
279
280