We should support CreateThis in the FTL
[WebKit-https.git] / Source / JavaScriptCore / bytecode / CallLinkInfo.cpp
1 /*
2  * Copyright (C) 2012-2018 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 "CallLinkInfo.h"
28
29 #include "CallFrameShuffleData.h"
30 #include "DFGOperations.h"
31 #include "DFGThunks.h"
32 #include "FunctionCodeBlock.h"
33 #include "JSCInlines.h"
34 #include "MacroAssembler.h"
35 #include "Opcode.h"
36 #include "Repatch.h"
37 #include <wtf/ListDump.h>
38
39 #if ENABLE(JIT)
40 namespace JSC {
41
42 CallLinkInfo::CallType CallLinkInfo::callTypeFor(OpcodeID opcodeID)
43 {
44     if (opcodeID == op_call || opcodeID == op_call_eval)
45         return Call;
46     if (opcodeID == op_call_varargs)
47         return CallVarargs;
48     if (opcodeID == op_construct)
49         return Construct;
50     if (opcodeID == op_construct_varargs)
51         return ConstructVarargs;
52     if (opcodeID == op_tail_call)
53         return TailCall;
54     ASSERT(opcodeID == op_tail_call_varargs || opcodeID == op_tail_call_forward_arguments);
55     return TailCallVarargs;
56 }
57
58 CallLinkInfo::CallLinkInfo()
59     : m_hasSeenShouldRepatch(false)
60     , m_hasSeenClosure(false)
61     , m_clearedByGC(false)
62     , m_clearedByVirtual(false)
63     , m_allowStubs(true)
64     , m_isLinked(false)
65     , m_callType(None)
66     , m_calleeGPR(255)
67     , m_maxNumArguments(0)
68     , m_slowPathCount(0)
69 {
70 }
71
72 CallLinkInfo::~CallLinkInfo()
73 {
74     clearStub();
75     
76     if (isOnList())
77         remove();
78 }
79
80 void CallLinkInfo::clearStub()
81 {
82     if (!stub())
83         return;
84
85     m_stub->clearCallNodesFor(this);
86     m_stub = nullptr;
87 }
88
89 void CallLinkInfo::unlink(VM& vm)
90 {
91     // We could be called even if we're not linked anymore because of how polymorphic calls
92     // work. Each callsite within the polymorphic call stub may separately ask us to unlink().
93     if (isLinked())
94         unlinkFor(vm, *this);
95
96     // Either we were unlinked, in which case we should not have been on any list, or we unlinked
97     // ourselves so that we're not on any list anymore.
98     RELEASE_ASSERT(!isOnList());
99 }
100
101 CodeLocationNearCall<JSInternalPtrTag> CallLinkInfo::callReturnLocation()
102 {
103     RELEASE_ASSERT(!isDirect());
104     return CodeLocationNearCall<JSInternalPtrTag>(m_callReturnLocationOrPatchableJump, Regular);
105 }
106
107 CodeLocationJump<JSInternalPtrTag> CallLinkInfo::patchableJump()
108 {
109     RELEASE_ASSERT(callType() == DirectTailCall);
110     return CodeLocationJump<JSInternalPtrTag>(m_callReturnLocationOrPatchableJump);
111 }
112
113 CodeLocationDataLabelPtr<JSInternalPtrTag> CallLinkInfo::hotPathBegin()
114 {
115     RELEASE_ASSERT(!isDirect());
116     return CodeLocationDataLabelPtr<JSInternalPtrTag>(m_hotPathBeginOrSlowPathStart);
117 }
118
119 CodeLocationLabel<JSInternalPtrTag> CallLinkInfo::slowPathStart()
120 {
121     RELEASE_ASSERT(isDirect());
122     return m_hotPathBeginOrSlowPathStart;
123 }
124
125 void CallLinkInfo::setCallee(VM& vm, JSCell* owner, JSObject* callee)
126 {
127     RELEASE_ASSERT(!isDirect());
128     MacroAssembler::repatchPointer(hotPathBegin(), callee);
129     m_calleeOrCodeBlock.set(vm, owner, callee);
130     m_isLinked = true;
131 }
132
133 void CallLinkInfo::clearCallee()
134 {
135     RELEASE_ASSERT(!isDirect());
136     MacroAssembler::repatchPointer(hotPathBegin(), nullptr);
137     m_calleeOrCodeBlock.clear();
138     m_isLinked = false;
139 }
140
141 JSObject* CallLinkInfo::callee()
142 {
143     RELEASE_ASSERT(!isDirect());
144     return jsCast<JSObject*>(m_calleeOrCodeBlock.get());
145 }
146
147 void CallLinkInfo::setCodeBlock(VM& vm, JSCell* owner, FunctionCodeBlock* codeBlock)
148 {
149     RELEASE_ASSERT(isDirect());
150     m_calleeOrCodeBlock.setMayBeNull(vm, owner, codeBlock);
151     m_isLinked = true;
152 }
153
154 void CallLinkInfo::clearCodeBlock()
155 {
156     RELEASE_ASSERT(isDirect());
157     m_calleeOrCodeBlock.clear();
158     m_isLinked = false;
159 }
160
161 FunctionCodeBlock* CallLinkInfo::codeBlock()
162 {
163     RELEASE_ASSERT(isDirect());
164     return jsCast<FunctionCodeBlock*>(m_calleeOrCodeBlock.get());
165 }
166
167 void CallLinkInfo::setLastSeenCallee(VM& vm, const JSCell* owner, JSObject* callee)
168 {
169     RELEASE_ASSERT(!isDirect());
170     m_lastSeenCalleeOrExecutable.set(vm, owner, callee);
171 }
172
173 void CallLinkInfo::clearLastSeenCallee()
174 {
175     RELEASE_ASSERT(!isDirect());
176     m_lastSeenCalleeOrExecutable.clear();
177 }
178
179 JSObject* CallLinkInfo::lastSeenCallee()
180 {
181     RELEASE_ASSERT(!isDirect());
182     return jsCast<JSObject*>(m_lastSeenCalleeOrExecutable.get());
183 }
184
185 bool CallLinkInfo::haveLastSeenCallee()
186 {
187     RELEASE_ASSERT(!isDirect());
188     return !!m_lastSeenCalleeOrExecutable;
189 }
190
191 void CallLinkInfo::setExecutableDuringCompilation(ExecutableBase* executable)
192 {
193     RELEASE_ASSERT(isDirect());
194     m_lastSeenCalleeOrExecutable.setWithoutWriteBarrier(executable);
195 }
196
197 ExecutableBase* CallLinkInfo::executable()
198 {
199     RELEASE_ASSERT(isDirect());
200     return jsCast<ExecutableBase*>(m_lastSeenCalleeOrExecutable.get());
201 }
202
203 void CallLinkInfo::setMaxNumArguments(unsigned value)
204 {
205     RELEASE_ASSERT(isDirect());
206     RELEASE_ASSERT(value);
207     m_maxNumArguments = value;
208 }
209
210 void CallLinkInfo::visitWeak(VM& vm)
211 {
212     auto handleSpecificCallee = [&] (JSFunction* callee) {
213         if (Heap::isMarked(callee->executable()))
214             m_hasSeenClosure = true;
215         else
216             m_clearedByGC = true;
217     };
218     
219     if (isLinked()) {
220         if (stub()) {
221             if (!stub()->visitWeak(vm)) {
222                 if (Options::verboseOSR()) {
223                     dataLog(
224                         "At ", m_codeOrigin, ", ", RawPointer(this), ": clearing call stub to ",
225                         listDump(stub()->variants()), ", stub routine ", RawPointer(stub()),
226                         ".\n");
227                 }
228                 unlink(vm);
229                 m_clearedByGC = true;
230             }
231         } else if (!Heap::isMarked(m_calleeOrCodeBlock.get())) {
232             if (isDirect()) {
233                 if (Options::verboseOSR()) {
234                     dataLog(
235                         "Clearing call to ", RawPointer(codeBlock()), " (",
236                         pointerDump(codeBlock()), ").\n");
237                 }
238             } else {
239                 if (callee()->type() == JSFunctionType) {
240                     if (Options::verboseOSR()) {
241                         dataLog(
242                             "Clearing call to ",
243                             RawPointer(callee()), " (",
244                             static_cast<JSFunction*>(callee())->executable()->hashFor(specializationKind()),
245                             ").\n");
246                     }
247                     handleSpecificCallee(static_cast<JSFunction*>(callee()));
248                 } else {
249                     if (Options::verboseOSR())
250                         dataLog("Clearing call to ", RawPointer(callee()), ".\n");
251                     m_clearedByGC = true;
252                 }
253             }
254             unlink(vm);
255         } else if (isDirect() && !Heap::isMarked(m_lastSeenCalleeOrExecutable.get())) {
256             if (Options::verboseOSR()) {
257                 dataLog(
258                     "Clearing call to ", RawPointer(executable()),
259                     " because the executable is dead.\n");
260             }
261             unlink(vm);
262             // We should only get here once the owning CodeBlock is dying, since the executable must
263             // already be in the owner's weak references.
264             m_lastSeenCalleeOrExecutable.clear();
265         }
266     }
267     if (!isDirect() && haveLastSeenCallee() && !Heap::isMarked(lastSeenCallee())) {
268         if (lastSeenCallee()->type() == JSFunctionType)
269             handleSpecificCallee(jsCast<JSFunction*>(lastSeenCallee()));
270         else
271             m_clearedByGC = true;
272         clearLastSeenCallee();
273     }
274 }
275
276 void CallLinkInfo::setFrameShuffleData(const CallFrameShuffleData& shuffleData)
277 {
278     m_frameShuffleData = std::make_unique<CallFrameShuffleData>(shuffleData);
279 }
280
281 } // namespace JSC
282 #endif // ENABLE(JIT)
283