1/*
2 * Copyright (C) 2008 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
28#if ENABLE(JIT)
29#if USE(JSVALUE32_64)
30#include "JIT.h"
31
32#include "CodeBlock.h"
33#include "Interpreter.h"
34#include "JITInlineMethods.h"
35#include "JITStubCall.h"
36#include "JSArray.h"
37#include "JSFunction.h"
38#include "ResultType.h"
39#include "SamplingTool.h"
40
41#ifndef NDEBUG
42#include <stdio.h>
43#endif
44
45using namespace std;
46
47namespace JSC {
48
49void JIT::compileOpCallInitializeCallFrame()
50{
51    // regT0 holds callee, regT1 holds argCount
52    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSFunction, m_scopeChain)), regT3); // scopeChain
53    emitPutIntToCallFrameHeader(regT1, RegisterFile::ArgumentCount);
54    emitPutCellToCallFrameHeader(regT0, RegisterFile::Callee);
55    emitPutCellToCallFrameHeader(regT3, RegisterFile::ScopeChain);
56}
57
58void JIT::emit_op_call_put_result(Instruction* instruction)
59{
60    int dst = instruction[1].u.operand;
61    emitStore(dst, regT1, regT0);
62}
63
64void JIT::compileOpCallVarargs(Instruction* instruction)
65{
66    int callee = instruction[1].u.operand;
67    int argCountRegister = instruction[2].u.operand;
68    int registerOffset = instruction[3].u.operand;
69
70    emitLoad(callee, regT1, regT0);
71    emitLoadPayload(argCountRegister, regT2); // argCount
72    addPtr(Imm32(registerOffset), regT2, regT3); // registerOffset
73
74    emitJumpSlowCaseIfNotJSCell(callee, regT1);
75    addSlowCase(branchPtr(NotEqual, Address(regT0), TrustedImmPtr(m_globalData->jsFunctionVPtr)));
76
77    // Speculatively roll the callframe, assuming argCount will match the arity.
78    mul32(TrustedImm32(sizeof(Register)), regT3, regT3);
79    addPtr(callFrameRegister, regT3);
80    store32(TrustedImm32(JSValue::CellTag), tagFor(RegisterFile::CallerFrame, regT3));
81    storePtr(callFrameRegister, payloadFor(RegisterFile::CallerFrame, regT3));
82    move(regT3, callFrameRegister);
83
84    move(regT2, regT1); // argCount
85
86    emitNakedCall(m_globalData->jitStubs->ctiVirtualCall());
87
88    sampleCodeBlock(m_codeBlock);
89}
90
91void JIT::compileOpCallVarargsSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter)
92{
93    int callee = instruction[1].u.operand;
94
95    linkSlowCaseIfNotJSCell(iter, callee);
96    linkSlowCase(iter);
97
98    JITStubCall stubCall(this, cti_op_call_NotJSFunction);
99    stubCall.addArgument(regT1, regT0);
100    stubCall.addArgument(regT3);
101    stubCall.addArgument(regT2);
102    stubCall.call();
103
104    sampleCodeBlock(m_codeBlock);
105}
106
107void JIT::emit_op_ret(Instruction* currentInstruction)
108{
109    unsigned dst = currentInstruction[1].u.operand;
110
111    emitLoad(dst, regT1, regT0);
112    emitGetFromCallFrameHeaderPtr(RegisterFile::ReturnPC, regT2);
113    emitGetFromCallFrameHeaderPtr(RegisterFile::CallerFrame, callFrameRegister);
114
115    restoreReturnAddressBeforeReturn(regT2);
116    ret();
117}
118
119void JIT::emit_op_ret_object_or_this(Instruction* currentInstruction)
120{
121    unsigned result = currentInstruction[1].u.operand;
122    unsigned thisReg = currentInstruction[2].u.operand;
123
124    emitLoad(result, regT1, regT0);
125    Jump notJSCell = branch32(NotEqual, regT1, TrustedImm32(JSValue::CellTag));
126    loadPtr(Address(regT0, JSCell::structureOffset()), regT2);
127    Jump notObject = branch8(NotEqual, Address(regT2, Structure::typeInfoTypeOffset()), TrustedImm32(ObjectType));
128
129    emitGetFromCallFrameHeaderPtr(RegisterFile::ReturnPC, regT2);
130    emitGetFromCallFrameHeaderPtr(RegisterFile::CallerFrame, callFrameRegister);
131
132    restoreReturnAddressBeforeReturn(regT2);
133    ret();
134
135    notJSCell.link(this);
136    notObject.link(this);
137    emitLoad(thisReg, regT1, regT0);
138
139    emitGetFromCallFrameHeaderPtr(RegisterFile::ReturnPC, regT2);
140    emitGetFromCallFrameHeaderPtr(RegisterFile::CallerFrame, callFrameRegister);
141
142    restoreReturnAddressBeforeReturn(regT2);
143    ret();
144}
145
146void JIT::emitSlow_op_call(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
147{
148    compileOpCallSlowCase(currentInstruction, iter, m_callLinkInfoIndex++, op_call);
149}
150
151void JIT::emitSlow_op_call_eval(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
152{
153    compileOpCallSlowCase(currentInstruction, iter, m_callLinkInfoIndex++, op_call_eval);
154}
155
156void JIT::emitSlow_op_call_varargs(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
157{
158    compileOpCallVarargsSlowCase(currentInstruction, iter);
159}
160
161void JIT::emitSlow_op_construct(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
162{
163    compileOpCallSlowCase(currentInstruction, iter, m_callLinkInfoIndex++, op_construct);
164}
165
166void JIT::emit_op_call(Instruction* currentInstruction)
167{
168    compileOpCall(op_call, currentInstruction, m_callLinkInfoIndex++);
169}
170
171void JIT::emit_op_call_eval(Instruction* currentInstruction)
172{
173    compileOpCall(op_call_eval, currentInstruction, m_callLinkInfoIndex++);
174}
175
176void JIT::emit_op_call_varargs(Instruction* currentInstruction)
177{
178    compileOpCallVarargs(currentInstruction);
179}
180
181void JIT::emit_op_construct(Instruction* currentInstruction)
182{
183    compileOpCall(op_construct, currentInstruction, m_callLinkInfoIndex++);
184}
185
186#if !ENABLE(JIT_OPTIMIZE_CALL)
187
188/* ------------------------------ BEGIN: !ENABLE(JIT_OPTIMIZE_CALL) ------------------------------ */
189
190void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned)
191{
192    int callee = instruction[1].u.operand;
193    int argCount = instruction[2].u.operand;
194    int registerOffset = instruction[3].u.operand;
195
196    Jump wasEval;
197    if (opcodeID == op_call_eval) {
198        JITStubCall stubCall(this, cti_op_call_eval);
199        stubCall.addArgument(callee);
200        stubCall.addArgument(JIT::Imm32(registerOffset));
201        stubCall.addArgument(JIT::Imm32(argCount));
202        stubCall.call();
203        wasEval = branch32(NotEqual, regT1, TrustedImm32(JSValue::EmptyValueTag));
204    }
205
206    emitLoad(callee, regT1, regT0);
207
208    emitJumpSlowCaseIfNotJSCell(callee, regT1);
209    addSlowCase(branchPtr(NotEqual, Address(regT0), TrustedImmPtr(m_globalData->jsFunctionVPtr)));
210
211    // Speculatively roll the callframe, assuming argCount will match the arity.
212    store32(TrustedImm32(JSValue::CellTag), tagFor(RegisterFile::CallerFrame + registerOffset, callFrameRegister));
213    storePtr(callFrameRegister, payloadFor(RegisterFile::CallerFrame + registerOffset, callFrameRegister));
214    addPtr(Imm32(registerOffset * static_cast<int>(sizeof(Register))), callFrameRegister);
215    move(TrustedImm32(argCount), regT1);
216
217    emitNakedCall(opcodeID == op_construct ? m_globalData->jitStubs->ctiVirtualConstruct() : m_globalData->jitStubs->ctiVirtualCall());
218
219    if (opcodeID == op_call_eval)
220        wasEval.link(this);
221
222    sampleCodeBlock(m_codeBlock);
223}
224
225void JIT::compileOpCallSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter, unsigned, OpcodeID opcodeID)
226{
227    int callee = instruction[1].u.operand;
228    int argCount = instruction[2].u.operand;
229    int registerOffset = instruction[3].u.operand;
230
231    linkSlowCaseIfNotJSCell(iter, callee);
232    linkSlowCase(iter);
233
234    JITStubCall stubCall(this, opcodeID == op_construct ? cti_op_construct_NotJSConstruct : cti_op_call_NotJSFunction);
235    stubCall.addArgument(callee);
236    stubCall.addArgument(JIT::Imm32(registerOffset));
237    stubCall.addArgument(JIT::Imm32(argCount));
238    stubCall.call();
239
240    sampleCodeBlock(m_codeBlock);
241}
242
243#else // !ENABLE(JIT_OPTIMIZE_CALL)
244
245/* ------------------------------ BEGIN: ENABLE(JIT_OPTIMIZE_CALL) ------------------------------ */
246
247void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned callLinkInfoIndex)
248{
249    int callee = instruction[1].u.operand;
250    int argCount = instruction[2].u.operand;
251    int registerOffset = instruction[3].u.operand;
252
253    Jump wasEval;
254    if (opcodeID == op_call_eval) {
255        JITStubCall stubCall(this, cti_op_call_eval);
256        stubCall.addArgument(callee);
257        stubCall.addArgument(JIT::Imm32(registerOffset));
258        stubCall.addArgument(JIT::Imm32(argCount));
259        stubCall.call();
260        wasEval = branch32(NotEqual, regT1, TrustedImm32(JSValue::EmptyValueTag));
261    }
262
263    emitLoad(callee, regT1, regT0);
264
265    DataLabelPtr addressOfLinkedFunctionCheck;
266
267    BEGIN_UNINTERRUPTED_SEQUENCE(sequenceOpCall);
268
269    Jump jumpToSlow = branchPtrWithPatch(NotEqual, regT0, addressOfLinkedFunctionCheck, TrustedImmPtr(0));
270
271    END_UNINTERRUPTED_SEQUENCE(sequenceOpCall);
272
273    addSlowCase(jumpToSlow);
274    ASSERT_JIT_OFFSET(differenceBetween(addressOfLinkedFunctionCheck, jumpToSlow), patchOffsetOpCallCompareToJump);
275    m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck;
276
277    addSlowCase(branch32(NotEqual, regT1, TrustedImm32(JSValue::CellTag)));
278
279    // The following is the fast case, only used whan a callee can be linked.
280
281    // Fast version of stack frame initialization, directly relative to edi.
282    // Note that this omits to set up RegisterFile::CodeBlock, which is set in the callee
283    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSFunction, m_scopeChain)), regT2);
284
285    store32(TrustedImm32(JSValue::Int32Tag), tagFor(registerOffset + RegisterFile::ArgumentCount));
286    store32(Imm32(argCount), payloadFor(registerOffset + RegisterFile::ArgumentCount));
287    storePtr(callFrameRegister, payloadFor(RegisterFile::CallerFrame + registerOffset, callFrameRegister));
288    emitStore(registerOffset + RegisterFile::Callee, regT1, regT0);
289    store32(TrustedImm32(JSValue::CellTag), tagFor(registerOffset + RegisterFile::ScopeChain));
290    store32(regT2, payloadFor(registerOffset + RegisterFile::ScopeChain));
291    addPtr(Imm32(registerOffset * sizeof(Register)), callFrameRegister);
292
293    // Call to the callee
294    m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedCall();
295
296    if (opcodeID == op_call_eval)
297        wasEval.link(this);
298
299    sampleCodeBlock(m_codeBlock);
300}
301
302void JIT::compileOpCallSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter, unsigned callLinkInfoIndex, OpcodeID opcodeID)
303{
304    int callee = instruction[1].u.operand;
305    int argCount = instruction[2].u.operand;
306    int registerOffset = instruction[3].u.operand;
307
308    linkSlowCase(iter);
309    linkSlowCase(iter);
310
311    // Fast check for JS function.
312    Jump callLinkFailNotObject = branch32(NotEqual, regT1, TrustedImm32(JSValue::CellTag));
313    Jump callLinkFailNotJSFunction = branchPtr(NotEqual, Address(regT0), TrustedImmPtr(m_globalData->jsFunctionVPtr));
314
315    // Speculatively roll the callframe, assuming argCount will match the arity.
316    store32(TrustedImm32(JSValue::CellTag), tagFor(RegisterFile::CallerFrame + registerOffset, callFrameRegister));
317    storePtr(callFrameRegister, payloadFor(RegisterFile::CallerFrame + registerOffset, callFrameRegister));
318    addPtr(Imm32(registerOffset * static_cast<int>(sizeof(Register))), callFrameRegister);
319    move(Imm32(argCount), regT1);
320
321    m_callStructureStubCompilationInfo[callLinkInfoIndex].callReturnLocation = emitNakedCall(opcodeID == op_construct ? m_globalData->jitStubs->ctiVirtualConstructLink() : m_globalData->jitStubs->ctiVirtualCallLink());
322
323    // Done! - return back to the hot path.
324    ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_call_eval));
325    ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct));
326    emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_call));
327
328    // This handles host functions
329    callLinkFailNotObject.link(this);
330    callLinkFailNotJSFunction.link(this);
331
332    JITStubCall stubCall(this, opcodeID == op_construct ? cti_op_construct_NotJSConstruct : cti_op_call_NotJSFunction);
333    stubCall.addArgument(callee);
334    stubCall.addArgument(JIT::Imm32(registerOffset));
335    stubCall.addArgument(JIT::Imm32(argCount));
336    stubCall.call();
337
338    sampleCodeBlock(m_codeBlock);
339}
340
341/* ------------------------------ END: !ENABLE / ENABLE(JIT_OPTIMIZE_CALL) ------------------------------ */
342
343#endif // !ENABLE(JIT_OPTIMIZE_CALL)
344
345} // namespace JSC
346
347#endif // USE(JSVALUE32_64)
348#endif // ENABLE(JIT)
349