blob: abdb5eddd0c8e6ee32c8fc2ab847da440e4f4aab [file] [log] [blame]
barraclough@apple.come367b002008-12-04 05:43:14 +00001/*
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#include "JIT.h"
28
29#if ENABLE(JIT)
30
31#include "CodeBlock.h"
32#include "JITInlineMethods.h"
ggaren@apple.com0e5741a2009-05-12 01:26:52 +000033#include "JITStubCall.h"
barraclough@apple.come367b002008-12-04 05:43:14 +000034#include "JSArray.h"
35#include "JSFunction.h"
36#include "Interpreter.h"
37#include "ResultType.h"
38#include "SamplingTool.h"
39
40#ifndef NDEBUG
41#include <stdio.h>
42#endif
43
44using namespace std;
45
46namespace JSC {
47
barraclough@apple.come367b002008-12-04 05:43:14 +000048void JIT::compileOpCallInitializeCallFrame()
49{
barraclough@apple.comea6b6092009-02-13 04:22:02 +000050 store32(regT1, Address(callFrameRegister, RegisterFile::ArgumentCount * static_cast<int>(sizeof(Register))));
barraclough@apple.come367b002008-12-04 05:43:14 +000051
barraclough@apple.com9c15d5a2009-06-20 01:29:36 +000052 loadPtr(Address(regT2, OBJECT_OFFSETOF(JSFunction, m_data) + OBJECT_OFFSETOF(ScopeChain, m_node)), regT1); // newScopeChain
barraclough@apple.come367b002008-12-04 05:43:14 +000053
ggaren@apple.comacea3582009-05-03 01:58:45 +000054 storePtr(ImmPtr(JSValue::encode(JSValue())), Address(callFrameRegister, RegisterFile::OptionalCalleeArguments * static_cast<int>(sizeof(Register))));
barraclough@apple.comea6b6092009-02-13 04:22:02 +000055 storePtr(regT2, Address(callFrameRegister, RegisterFile::Callee * static_cast<int>(sizeof(Register))));
56 storePtr(regT1, Address(callFrameRegister, RegisterFile::ScopeChain * static_cast<int>(sizeof(Register))));
barraclough@apple.come367b002008-12-04 05:43:14 +000057}
58
59void JIT::compileOpCallSetupArgs(Instruction* instruction)
60{
61 int argCount = instruction[3].u.operand;
62 int registerOffset = instruction[4].u.operand;
63
64 // ecx holds func
barraclough@apple.comea6b6092009-02-13 04:22:02 +000065 emitPutJITStubArg(regT2, 1);
barraclough@apple.com732c7bf2008-12-13 05:25:22 +000066 emitPutJITStubArgConstant(argCount, 3);
oliver@apple.com65e286e2009-04-08 23:08:28 +000067 emitPutJITStubArgConstant(registerOffset, 2);
68}
69
70void JIT::compileOpCallVarargsSetupArgs(Instruction* instruction)
71{
72 int registerOffset = instruction[4].u.operand;
73
74 // ecx holds func
75 emitPutJITStubArg(regT2, 1);
76 emitPutJITStubArg(regT1, 3);
77 addPtr(Imm32(registerOffset), regT1, regT0);
78 emitPutJITStubArg(regT0, 2);
barraclough@apple.come367b002008-12-04 05:43:14 +000079}
80
barraclough@apple.come367b002008-12-04 05:43:14 +000081void JIT::compileOpConstructSetupArgs(Instruction* instruction)
82{
83 int argCount = instruction[3].u.operand;
84 int registerOffset = instruction[4].u.operand;
85 int proto = instruction[5].u.operand;
86 int thisRegister = instruction[6].u.operand;
87
88 // ecx holds func
barraclough@apple.comea6b6092009-02-13 04:22:02 +000089 emitPutJITStubArg(regT2, 1);
barraclough@apple.com732c7bf2008-12-13 05:25:22 +000090 emitPutJITStubArgConstant(registerOffset, 2);
91 emitPutJITStubArgConstant(argCount, 3);
barraclough@apple.comea6b6092009-02-13 04:22:02 +000092 emitPutJITStubArgFromVirtualRegister(proto, 4, regT0);
barraclough@apple.com732c7bf2008-12-13 05:25:22 +000093 emitPutJITStubArgConstant(thisRegister, 5);
barraclough@apple.come367b002008-12-04 05:43:14 +000094}
95
ggaren@apple.com29496602009-05-12 04:20:29 +000096void JIT::compileOpCallVarargs(Instruction* instruction)
97{
98 int dst = instruction[1].u.operand;
99 int callee = instruction[2].u.operand;
100 int argCountRegister = instruction[3].u.operand;
101
102 emitGetVirtualRegister(argCountRegister, regT1);
103 emitGetVirtualRegister(callee, regT2);
104 compileOpCallVarargsSetupArgs(instruction);
105
106 // Check for JSFunctions.
107 emitJumpSlowCaseIfNotJSCell(regT2);
108 addSlowCase(branchPtr(NotEqual, Address(regT2), ImmPtr(m_globalData->jsFunctionVPtr)));
109
110 // Speculatively roll the callframe, assuming argCount will match the arity.
111 mul32(Imm32(sizeof(Register)), regT0, regT0);
112 intptr_t offset = (intptr_t)sizeof(Register) * (intptr_t)RegisterFile::CallerFrame;
113 addPtr(Imm32((int32_t)offset), regT0, regT3);
114 addPtr(callFrameRegister, regT3);
115 storePtr(callFrameRegister, regT3);
116 addPtr(regT0, callFrameRegister);
117 emitNakedCall(m_globalData->jitStubs.ctiVirtualCall());
118
119 // Put the return value in dst. In the interpreter, op_ret does this.
120 emitPutVirtualRegister(dst);
121
122 sampleCodeBlock(m_codeBlock);
123}
124
125void JIT::compileOpCallVarargsSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter)
126{
127 int dst = instruction[1].u.operand;
128
129 linkSlowCase(iter);
130 linkSlowCase(iter);
131 JITStubCall stubCall(this, JITStubs::cti_op_call_NotJSFunction);
132 stubCall.call(dst); // In the interpreter, the callee puts the return value in dst.
133
134 sampleCodeBlock(m_codeBlock);
135}
136
barraclough@apple.come367b002008-12-04 05:43:14 +0000137#if !ENABLE(JIT_OPTIMIZE_CALL)
138
ggaren@apple.com29496602009-05-12 04:20:29 +0000139/* ------------------------------ BEGIN: !ENABLE(JIT_OPTIMIZE_CALL) ------------------------------ */
140
barraclough@apple.com249befb2008-12-13 03:18:10 +0000141void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned)
barraclough@apple.come367b002008-12-04 05:43:14 +0000142{
143 int dst = instruction[1].u.operand;
144 int callee = instruction[2].u.operand;
145 int argCount = instruction[3].u.operand;
146 int registerOffset = instruction[4].u.operand;
147
148 // Handle eval
barraclough@apple.com4f46a502008-12-13 01:39:38 +0000149 Jump wasEval;
barraclough@apple.come367b002008-12-04 05:43:14 +0000150 if (opcodeID == op_call_eval) {
ggaren@apple.comd8fccca2009-05-08 20:51:53 +0000151 CallEvalJITStub(this, instruction).call();
ggaren@apple.comacea3582009-05-03 01:58:45 +0000152 wasEval = branchPtr(NotEqual, regT0, ImmPtr(JSValue::encode(JSValue())));
barraclough@apple.come367b002008-12-04 05:43:14 +0000153 }
154
barraclough@apple.comea6b6092009-02-13 04:22:02 +0000155 emitGetVirtualRegister(callee, regT2);
barraclough@apple.come367b002008-12-04 05:43:14 +0000156 // The arguments have been set up on the hot path for op_call_eval
157 if (opcodeID == op_call)
158 compileOpCallSetupArgs(instruction);
159 else if (opcodeID == op_construct)
160 compileOpConstructSetupArgs(instruction);
161
162 // Check for JSFunctions.
barraclough@apple.comea6b6092009-02-13 04:22:02 +0000163 emitJumpSlowCaseIfNotJSCell(regT2);
ggaren@apple.comc3343bd2009-02-24 03:58:09 +0000164 addSlowCase(branchPtr(NotEqual, Address(regT2), ImmPtr(m_globalData->jsFunctionVPtr)));
barraclough@apple.come367b002008-12-04 05:43:14 +0000165
166 // First, in the case of a construct, allocate the new object.
167 if (opcodeID == op_construct) {
barraclough@apple.com4027bab2009-05-15 03:44:10 +0000168 JITStubCall(this, JITStubs::cti_op_construct_JSConstruct).call(registerOffset - RegisterFile::CallFrameHeaderSize - argCount);
barraclough@apple.comea6b6092009-02-13 04:22:02 +0000169 emitGetVirtualRegister(callee, regT2);
barraclough@apple.come367b002008-12-04 05:43:14 +0000170 }
171
172 // Speculatively roll the callframe, assuming argCount will match the arity.
barraclough@apple.come0aa4b62008-12-18 03:38:10 +0000173 storePtr(callFrameRegister, Address(callFrameRegister, (RegisterFile::CallerFrame + registerOffset) * static_cast<int>(sizeof(Register))));
174 addPtr(Imm32(registerOffset * static_cast<int>(sizeof(Register))), callFrameRegister);
barraclough@apple.comea6b6092009-02-13 04:22:02 +0000175 move(Imm32(argCount), regT1);
barraclough@apple.come367b002008-12-04 05:43:14 +0000176
ggaren@apple.comc3343bd2009-02-24 03:58:09 +0000177 emitNakedCall(m_globalData->jitStubs.ctiVirtualCall());
barraclough@apple.come367b002008-12-04 05:43:14 +0000178
179 if (opcodeID == op_call_eval)
barraclough@apple.com4f46a502008-12-13 01:39:38 +0000180 wasEval.link(this);
barraclough@apple.come367b002008-12-04 05:43:14 +0000181
182 // Put the return value in dst. In the interpreter, op_ret does this.
183 emitPutVirtualRegister(dst);
184
barraclough@apple.com24a7abb2009-01-16 23:34:46 +0000185 sampleCodeBlock(m_codeBlock);
barraclough@apple.come367b002008-12-04 05:43:14 +0000186}
187
barraclough@apple.com249befb2008-12-13 03:18:10 +0000188void JIT::compileOpCallSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter, unsigned, OpcodeID opcodeID)
barraclough@apple.come367b002008-12-04 05:43:14 +0000189{
190 int dst = instruction[1].u.operand;
191
barraclough@apple.com4f46a502008-12-13 01:39:38 +0000192 linkSlowCase(iter);
193 linkSlowCase(iter);
barraclough@apple.com4027bab2009-05-15 03:44:10 +0000194 JITStubCall stubCall(this, opcodeID == op_construct ? JITStubs::cti_op_construct_NotJSConstruct : JITStubs::cti_op_call_NotJSFunction);
ggaren@apple.comd8fccca2009-05-08 20:51:53 +0000195 stubCall.call(dst); // In the interpreter, the callee puts the return value in dst.
barraclough@apple.come367b002008-12-04 05:43:14 +0000196
barraclough@apple.com24a7abb2009-01-16 23:34:46 +0000197 sampleCodeBlock(m_codeBlock);
barraclough@apple.come367b002008-12-04 05:43:14 +0000198}
199
ggaren@apple.com29496602009-05-12 04:20:29 +0000200#else // !ENABLE(JIT_OPTIMIZE_CALL)
201
202/* ------------------------------ BEGIN: ENABLE(JIT_OPTIMIZE_CALL) ------------------------------ */
barraclough@apple.come367b002008-12-04 05:43:14 +0000203
barraclough@apple.com249befb2008-12-13 03:18:10 +0000204void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned callLinkInfoIndex)
barraclough@apple.come367b002008-12-04 05:43:14 +0000205{
206 int dst = instruction[1].u.operand;
207 int callee = instruction[2].u.operand;
208 int argCount = instruction[3].u.operand;
209 int registerOffset = instruction[4].u.operand;
210
211 // Handle eval
barraclough@apple.com289318a2008-12-22 01:00:07 +0000212 Jump wasEval;
barraclough@apple.come367b002008-12-04 05:43:14 +0000213 if (opcodeID == op_call_eval) {
ggaren@apple.comd8fccca2009-05-08 20:51:53 +0000214 CallEvalJITStub(this, instruction).call();
ggaren@apple.comacea3582009-05-03 01:58:45 +0000215 wasEval = branchPtr(NotEqual, regT0, ImmPtr(JSValue::encode(JSValue())));
barraclough@apple.come367b002008-12-04 05:43:14 +0000216 }
217
218 // This plants a check for a cached JSFunction value, so we can plant a fast link to the callee.
219 // This deliberately leaves the callee in ecx, used when setting up the stack frame below
barraclough@apple.comea6b6092009-02-13 04:22:02 +0000220 emitGetVirtualRegister(callee, regT2);
barraclough@apple.com289318a2008-12-22 01:00:07 +0000221 DataLabelPtr addressOfLinkedFunctionCheck;
ggaren@apple.comacea3582009-05-03 01:58:45 +0000222 Jump jumpToSlow = branchPtrWithPatch(NotEqual, regT2, addressOfLinkedFunctionCheck, ImmPtr(JSValue::encode(JSValue())));
barraclough@apple.com289318a2008-12-22 01:00:07 +0000223 addSlowCase(jumpToSlow);
weinig@apple.com76b0f8a2008-12-22 21:21:18 +0000224 ASSERT(differenceBetween(addressOfLinkedFunctionCheck, jumpToSlow) == patchOffsetOpCallCompareToJump);
barraclough@apple.come367b002008-12-04 05:43:14 +0000225 m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck;
226
227 // The following is the fast case, only used whan a callee can be linked.
228
229 // In the case of OpConstruct, call out to a cti_ function to create the new object.
230 if (opcodeID == op_construct) {
231 int proto = instruction[5].u.operand;
232 int thisRegister = instruction[6].u.operand;
233
barraclough@apple.comea6b6092009-02-13 04:22:02 +0000234 emitPutJITStubArg(regT2, 1);
235 emitPutJITStubArgFromVirtualRegister(proto, 4, regT0);
ggaren@apple.comd8fccca2009-05-08 20:51:53 +0000236 JITStubCall stubCall(this, JITStubs::cti_op_construct_JSConstruct);
237 stubCall.call(thisRegister);
barraclough@apple.comea6b6092009-02-13 04:22:02 +0000238 emitGetVirtualRegister(callee, regT2);
barraclough@apple.come367b002008-12-04 05:43:14 +0000239 }
240
241 // Fast version of stack frame initialization, directly relative to edi.
242 // Note that this omits to set up RegisterFile::CodeBlock, which is set in the callee
ggaren@apple.comacea3582009-05-03 01:58:45 +0000243 storePtr(ImmPtr(JSValue::encode(JSValue())), Address(callFrameRegister, (registerOffset + RegisterFile::OptionalCalleeArguments) * static_cast<int>(sizeof(Register))));
barraclough@apple.comea6b6092009-02-13 04:22:02 +0000244 storePtr(regT2, Address(callFrameRegister, (registerOffset + RegisterFile::Callee) * static_cast<int>(sizeof(Register))));
barraclough@apple.com9c15d5a2009-06-20 01:29:36 +0000245 loadPtr(Address(regT2, OBJECT_OFFSETOF(JSFunction, m_data) + OBJECT_OFFSETOF(ScopeChain, m_node)), regT1); // newScopeChain
barraclough@apple.com289318a2008-12-22 01:00:07 +0000246 store32(Imm32(argCount), Address(callFrameRegister, (registerOffset + RegisterFile::ArgumentCount) * static_cast<int>(sizeof(Register))));
247 storePtr(callFrameRegister, Address(callFrameRegister, (registerOffset + RegisterFile::CallerFrame) * static_cast<int>(sizeof(Register))));
barraclough@apple.comea6b6092009-02-13 04:22:02 +0000248 storePtr(regT1, Address(callFrameRegister, (registerOffset + RegisterFile::ScopeChain) * static_cast<int>(sizeof(Register))));
barraclough@apple.com289318a2008-12-22 01:00:07 +0000249 addPtr(Imm32(registerOffset * sizeof(Register)), callFrameRegister);
barraclough@apple.come367b002008-12-04 05:43:14 +0000250
251 // Call to the callee
barraclough@apple.com97bacef2009-06-05 07:55:38 +0000252 m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedCall();
barraclough@apple.come367b002008-12-04 05:43:14 +0000253
254 if (opcodeID == op_call_eval)
barraclough@apple.com289318a2008-12-22 01:00:07 +0000255 wasEval.link(this);
barraclough@apple.come367b002008-12-04 05:43:14 +0000256
257 // Put the return value in dst. In the interpreter, op_ret does this.
258 emitPutVirtualRegister(dst);
259
barraclough@apple.com24a7abb2009-01-16 23:34:46 +0000260 sampleCodeBlock(m_codeBlock);
barraclough@apple.come367b002008-12-04 05:43:14 +0000261}
262
barraclough@apple.com249befb2008-12-13 03:18:10 +0000263void JIT::compileOpCallSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter, unsigned callLinkInfoIndex, OpcodeID opcodeID)
barraclough@apple.come367b002008-12-04 05:43:14 +0000264{
265 int dst = instruction[1].u.operand;
266 int callee = instruction[2].u.operand;
267 int argCount = instruction[3].u.operand;
268 int registerOffset = instruction[4].u.operand;
269
barraclough@apple.com4f46a502008-12-13 01:39:38 +0000270 linkSlowCase(iter);
barraclough@apple.come367b002008-12-04 05:43:14 +0000271
272 // The arguments have been set up on the hot path for op_call_eval
273 if (opcodeID == op_call)
274 compileOpCallSetupArgs(instruction);
275 else if (opcodeID == op_construct)
276 compileOpConstructSetupArgs(instruction);
277
278 // Fast check for JS function.
barraclough@apple.comea6b6092009-02-13 04:22:02 +0000279 Jump callLinkFailNotObject = emitJumpIfNotJSCell(regT2);
ggaren@apple.comc3343bd2009-02-24 03:58:09 +0000280 Jump callLinkFailNotJSFunction = branchPtr(NotEqual, Address(regT2), ImmPtr(m_globalData->jsFunctionVPtr));
barraclough@apple.come367b002008-12-04 05:43:14 +0000281
282 // First, in the case of a construct, allocate the new object.
283 if (opcodeID == op_construct) {
ggaren@apple.comd8fccca2009-05-08 20:51:53 +0000284 JITStubCall(this, JITStubs::cti_op_construct_JSConstruct).call(registerOffset - RegisterFile::CallFrameHeaderSize - argCount);
barraclough@apple.comea6b6092009-02-13 04:22:02 +0000285 emitGetVirtualRegister(callee, regT2);
barraclough@apple.come367b002008-12-04 05:43:14 +0000286 }
287
barraclough@apple.come367b002008-12-04 05:43:14 +0000288 // Speculatively roll the callframe, assuming argCount will match the arity.
barraclough@apple.com289318a2008-12-22 01:00:07 +0000289 storePtr(callFrameRegister, Address(callFrameRegister, (RegisterFile::CallerFrame + registerOffset) * static_cast<int>(sizeof(Register))));
290 addPtr(Imm32(registerOffset * static_cast<int>(sizeof(Register))), callFrameRegister);
barraclough@apple.combaf4f322009-06-15 20:17:50 +0000291 move(Imm32(argCount), regT1);
barraclough@apple.come367b002008-12-04 05:43:14 +0000292
barraclough@apple.combaf4f322009-06-15 20:17:50 +0000293 m_callStructureStubCompilationInfo[callLinkInfoIndex].callReturnLocation = emitNakedCall(m_globalData->jitStubs.ctiVirtualCallPreLink());
barraclough@apple.come367b002008-12-04 05:43:14 +0000294
barraclough@apple.combaf4f322009-06-15 20:17:50 +0000295 // Put the return value in dst.
296 emitPutVirtualRegister(dst);
297 sampleCodeBlock(m_codeBlock);
barraclough@apple.come367b002008-12-04 05:43:14 +0000298
barraclough@apple.combaf4f322009-06-15 20:17:50 +0000299 // If not, we need an extra case in the if below!
300 ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_call_eval));
barraclough@apple.come367b002008-12-04 05:43:14 +0000301
barraclough@apple.combaf4f322009-06-15 20:17:50 +0000302 // Done! - return back to the hot path.
303 if (opcodeID == op_construct)
304 emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_construct));
305 else
306 emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_call));
barraclough@apple.come367b002008-12-04 05:43:14 +0000307
308 // This handles host functions
barraclough@apple.com289318a2008-12-22 01:00:07 +0000309 callLinkFailNotObject.link(this);
310 callLinkFailNotJSFunction.link(this);
barraclough@apple.combaf4f322009-06-15 20:17:50 +0000311 JITStubCall(this, opcodeID == op_construct ? JITStubs::cti_op_construct_NotJSConstruct : JITStubs::cti_op_call_NotJSFunction).call();
barraclough@apple.come367b002008-12-04 05:43:14 +0000312
barraclough@apple.come367b002008-12-04 05:43:14 +0000313 emitPutVirtualRegister(dst);
barraclough@apple.com24a7abb2009-01-16 23:34:46 +0000314 sampleCodeBlock(m_codeBlock);
barraclough@apple.come367b002008-12-04 05:43:14 +0000315}
316
ggaren@apple.com29496602009-05-12 04:20:29 +0000317/* ------------------------------ END: !ENABLE / ENABLE(JIT_OPTIMIZE_CALL) ------------------------------ */
barraclough@apple.come367b002008-12-04 05:43:14 +0000318
ggaren@apple.com29496602009-05-12 04:20:29 +0000319#endif // !ENABLE(JIT_OPTIMIZE_CALL)
oliver@apple.com65e286e2009-04-08 23:08:28 +0000320
barraclough@apple.come367b002008-12-04 05:43:14 +0000321} // namespace JSC
322
323#endif // ENABLE(JIT)