2 * Copyright (C) 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
31 #include "CodeBlock.h"
32 #include "JITInlineMethods.h"
34 #include "JSFunction.h"
35 #include "Interpreter.h"
36 #include "ResultType.h"
37 #include "SamplingTool.h"
47 void JIT::unlinkCall(CallLinkInfo
* callLinkInfo
)
49 // When the JSFunction is deleted the pointer embedded in the instruction stream will no longer be valid
50 // (and, if a new JSFunction happened to be constructed at the same location, we could get a false positive
51 // match). Reset the check so it no longer matches.
52 DataLabelPtr::patch(callLinkInfo
->hotPathBegin
, JSValuePtr::encode(jsImpossibleValue()));
55 void JIT::linkCall(JSFunction
* callee
, CodeBlock
* calleeCodeBlock
, void* ctiCode
, CallLinkInfo
* callLinkInfo
, int callerArgCount
)
57 // Currently we only link calls with the exact number of arguments.
58 if (callerArgCount
== calleeCodeBlock
->m_numParameters
) {
59 ASSERT(!callLinkInfo
->isLinked());
61 calleeCodeBlock
->addCaller(callLinkInfo
);
63 DataLabelPtr::patch(callLinkInfo
->hotPathBegin
, callee
);
64 Jump::patch(callLinkInfo
->hotPathOther
, ctiCode
);
67 // patch the instruction that jumps out to the cold path, so that we only try to link once.
68 void* patchCheck
= reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(callLinkInfo
->hotPathBegin
) + patchOffsetOpCallCompareToJump
);
69 Jump::patch(patchCheck
, callLinkInfo
->coldPathOther
);
72 void JIT::compileOpCallInitializeCallFrame()
74 store32(X86::edx
, Address(callFrameRegister
, RegisterFile::ArgumentCount
* static_cast<int>(sizeof(Register
))));
76 loadPtr(Address(X86::ecx
, FIELD_OFFSET(JSFunction
, m_scopeChain
) + FIELD_OFFSET(ScopeChain
, m_node
)), X86::edx
); // newScopeChain
78 storePtr(ImmPtr(JSValuePtr::encode(noValue())), Address(callFrameRegister
, RegisterFile::OptionalCalleeArguments
* static_cast<int>(sizeof(Register
))));
79 storePtr(X86::ecx
, Address(callFrameRegister
, RegisterFile::Callee
* static_cast<int>(sizeof(Register
))));
80 storePtr(X86::edx
, Address(callFrameRegister
, RegisterFile::ScopeChain
* static_cast<int>(sizeof(Register
))));
83 void JIT::compileOpCallSetupArgs(Instruction
* instruction
)
85 int argCount
= instruction
[3].u
.operand
;
86 int registerOffset
= instruction
[4].u
.operand
;
89 emitPutJITStubArg(X86::ecx
, 1);
90 emitPutJITStubArgConstant(registerOffset
, 2);
91 emitPutJITStubArgConstant(argCount
, 3);
94 void JIT::compileOpCallEvalSetupArgs(Instruction
* instruction
)
96 int argCount
= instruction
[3].u
.operand
;
97 int registerOffset
= instruction
[4].u
.operand
;
100 emitPutJITStubArg(X86::ecx
, 1);
101 emitPutJITStubArgConstant(registerOffset
, 2);
102 emitPutJITStubArgConstant(argCount
, 3);
105 void JIT::compileOpConstructSetupArgs(Instruction
* instruction
)
107 int argCount
= instruction
[3].u
.operand
;
108 int registerOffset
= instruction
[4].u
.operand
;
109 int proto
= instruction
[5].u
.operand
;
110 int thisRegister
= instruction
[6].u
.operand
;
113 emitPutJITStubArg(X86::ecx
, 1);
114 emitPutJITStubArgConstant(registerOffset
, 2);
115 emitPutJITStubArgConstant(argCount
, 3);
116 emitPutJITStubArgFromVirtualRegister(proto
, 4, X86::eax
);
117 emitPutJITStubArgConstant(thisRegister
, 5);
120 #if !ENABLE(JIT_OPTIMIZE_CALL)
122 void JIT::compileOpCall(OpcodeID opcodeID
, Instruction
* instruction
, unsigned)
124 int dst
= instruction
[1].u
.operand
;
125 int callee
= instruction
[2].u
.operand
;
126 int argCount
= instruction
[3].u
.operand
;
127 int registerOffset
= instruction
[4].u
.operand
;
131 if (opcodeID
== op_call_eval
) {
132 emitGetVirtualRegister(callee
, X86::ecx
);
133 compileOpCallEvalSetupArgs(instruction
);
135 emitCTICall(Interpreter::cti_op_call_eval
);
136 wasEval
= jnePtr(X86::eax
, ImmPtr(JSValuePtr::encode(jsImpossibleValue())));
139 emitGetVirtualRegister(callee
, X86::ecx
);
140 // The arguments have been set up on the hot path for op_call_eval
141 if (opcodeID
== op_call
)
142 compileOpCallSetupArgs(instruction
);
143 else if (opcodeID
== op_construct
)
144 compileOpConstructSetupArgs(instruction
);
146 // Check for JSFunctions.
147 emitJumpSlowCaseIfNotJSCell(X86::ecx
);
148 addSlowCase(jnePtr(Address(X86::ecx
), ImmPtr(m_interpreter
->m_jsFunctionVptr
)));
150 // First, in the case of a construct, allocate the new object.
151 if (opcodeID
== op_construct
) {
152 emitCTICall(Interpreter::cti_op_construct_JSConstruct
);
153 emitPutVirtualRegister(registerOffset
- RegisterFile::CallFrameHeaderSize
- argCount
);
154 emitGetVirtualRegister(callee
, X86::ecx
);
157 // Speculatively roll the callframe, assuming argCount will match the arity.
158 storePtr(callFrameRegister
, Address(callFrameRegister
, (RegisterFile::CallerFrame
+ registerOffset
) * static_cast<int>(sizeof(Register
))));
159 addPtr(Imm32(registerOffset
* static_cast<int>(sizeof(Register
))), callFrameRegister
);
160 move(Imm32(argCount
), X86::edx
);
162 emitNakedCall(m_interpreter
->m_ctiVirtualCall
);
164 if (opcodeID
== op_call_eval
)
167 // Put the return value in dst. In the interpreter, op_ret does this.
168 emitPutVirtualRegister(dst
);
170 sampleCodeBlock(m_codeBlock
);
173 void JIT::compileOpCallSlowCase(Instruction
* instruction
, Vector
<SlowCaseEntry
>::iterator
& iter
, unsigned, OpcodeID opcodeID
)
175 int dst
= instruction
[1].u
.operand
;
180 // This handles host functions
181 emitCTICall(((opcodeID
== op_construct
) ? Interpreter::cti_op_construct_NotJSConstruct
: Interpreter::cti_op_call_NotJSFunction
));
182 // Put the return value in dst. In the interpreter, op_ret does this.
183 emitPutVirtualRegister(dst
);
185 sampleCodeBlock(m_codeBlock
);
190 static NO_RETURN
void unreachable()
192 ASSERT_NOT_REACHED();
196 void JIT::compileOpCall(OpcodeID opcodeID
, Instruction
* instruction
, unsigned callLinkInfoIndex
)
198 int dst
= instruction
[1].u
.operand
;
199 int callee
= instruction
[2].u
.operand
;
200 int argCount
= instruction
[3].u
.operand
;
201 int registerOffset
= instruction
[4].u
.operand
;
205 if (opcodeID
== op_call_eval
) {
206 emitGetVirtualRegister(callee
, X86::ecx
);
207 compileOpCallEvalSetupArgs(instruction
);
209 emitCTICall(Interpreter::cti_op_call_eval
);
210 wasEval
= jnePtr(X86::eax
, ImmPtr(JSValuePtr::encode(jsImpossibleValue())));
213 // This plants a check for a cached JSFunction value, so we can plant a fast link to the callee.
214 // This deliberately leaves the callee in ecx, used when setting up the stack frame below
215 emitGetVirtualRegister(callee
, X86::ecx
);
216 DataLabelPtr addressOfLinkedFunctionCheck
;
217 Jump jumpToSlow
= jnePtrWithPatch(X86::ecx
, addressOfLinkedFunctionCheck
, ImmPtr(JSValuePtr::encode(jsImpossibleValue())));
218 addSlowCase(jumpToSlow
);
219 ASSERT(differenceBetween(addressOfLinkedFunctionCheck
, jumpToSlow
) == patchOffsetOpCallCompareToJump
);
220 m_callStructureStubCompilationInfo
[callLinkInfoIndex
].hotPathBegin
= addressOfLinkedFunctionCheck
;
222 // The following is the fast case, only used whan a callee can be linked.
224 // In the case of OpConstruct, call out to a cti_ function to create the new object.
225 if (opcodeID
== op_construct
) {
226 int proto
= instruction
[5].u
.operand
;
227 int thisRegister
= instruction
[6].u
.operand
;
229 emitPutJITStubArg(X86::ecx
, 1);
230 emitPutJITStubArgFromVirtualRegister(proto
, 4, X86::eax
);
231 emitCTICall(Interpreter::cti_op_construct_JSConstruct
);
232 emitPutVirtualRegister(thisRegister
);
233 emitGetVirtualRegister(callee
, X86::ecx
);
236 // Fast version of stack frame initialization, directly relative to edi.
237 // Note that this omits to set up RegisterFile::CodeBlock, which is set in the callee
238 storePtr(ImmPtr(JSValuePtr::encode(noValue())), Address(callFrameRegister
, (registerOffset
+ RegisterFile::OptionalCalleeArguments
) * static_cast<int>(sizeof(Register
))));
239 storePtr(X86::ecx
, Address(callFrameRegister
, (registerOffset
+ RegisterFile::Callee
) * static_cast<int>(sizeof(Register
))));
240 loadPtr(Address(X86::ecx
, FIELD_OFFSET(JSFunction
, m_scopeChain
) + FIELD_OFFSET(ScopeChain
, m_node
)), X86::edx
); // newScopeChain
241 store32(Imm32(argCount
), Address(callFrameRegister
, (registerOffset
+ RegisterFile::ArgumentCount
) * static_cast<int>(sizeof(Register
))));
242 storePtr(callFrameRegister
, Address(callFrameRegister
, (registerOffset
+ RegisterFile::CallerFrame
) * static_cast<int>(sizeof(Register
))));
243 storePtr(X86::edx
, Address(callFrameRegister
, (registerOffset
+ RegisterFile::ScopeChain
) * static_cast<int>(sizeof(Register
))));
244 addPtr(Imm32(registerOffset
* sizeof(Register
)), callFrameRegister
);
246 // Call to the callee
247 m_callStructureStubCompilationInfo
[callLinkInfoIndex
].hotPathOther
= emitNakedCall(reinterpret_cast<void*>(unreachable
));
249 if (opcodeID
== op_call_eval
)
252 // Put the return value in dst. In the interpreter, op_ret does this.
253 emitPutVirtualRegister(dst
);
255 sampleCodeBlock(m_codeBlock
);
258 void JIT::compileOpCallSlowCase(Instruction
* instruction
, Vector
<SlowCaseEntry
>::iterator
& iter
, unsigned callLinkInfoIndex
, OpcodeID opcodeID
)
260 int dst
= instruction
[1].u
.operand
;
261 int callee
= instruction
[2].u
.operand
;
262 int argCount
= instruction
[3].u
.operand
;
263 int registerOffset
= instruction
[4].u
.operand
;
267 // The arguments have been set up on the hot path for op_call_eval
268 if (opcodeID
== op_call
)
269 compileOpCallSetupArgs(instruction
);
270 else if (opcodeID
== op_construct
)
271 compileOpConstructSetupArgs(instruction
);
273 // Fast check for JS function.
274 Jump callLinkFailNotObject
= emitJumpIfNotJSCell(X86::ecx
);
275 Jump callLinkFailNotJSFunction
= jnePtr(Address(X86::ecx
), ImmPtr(m_interpreter
->m_jsFunctionVptr
));
277 // First, in the case of a construct, allocate the new object.
278 if (opcodeID
== op_construct
) {
279 emitCTICall(Interpreter::cti_op_construct_JSConstruct
);
280 emitPutVirtualRegister(registerOffset
- RegisterFile::CallFrameHeaderSize
- argCount
);
281 emitGetVirtualRegister(callee
, X86::ecx
);
284 move(Imm32(argCount
), X86::edx
);
286 // Speculatively roll the callframe, assuming argCount will match the arity.
287 storePtr(callFrameRegister
, Address(callFrameRegister
, (RegisterFile::CallerFrame
+ registerOffset
) * static_cast<int>(sizeof(Register
))));
288 addPtr(Imm32(registerOffset
* static_cast<int>(sizeof(Register
))), callFrameRegister
);
290 m_callStructureStubCompilationInfo
[callLinkInfoIndex
].callReturnLocation
=
291 emitNakedCall(m_interpreter
->m_ctiVirtualCallPreLink
);
293 Jump storeResultForFirstRun
= jump();
295 // FIXME: this label can be removed, since it is a fixed offset from 'callReturnLocation'.
296 // This is the address for the cold path *after* the first run (which tries to link the call).
297 m_callStructureStubCompilationInfo
[callLinkInfoIndex
].coldPathOther
= MacroAssembler::Label(this);
299 // The arguments have been set up on the hot path for op_call_eval
300 if (opcodeID
== op_call
)
301 compileOpCallSetupArgs(instruction
);
302 else if (opcodeID
== op_construct
)
303 compileOpConstructSetupArgs(instruction
);
305 // Check for JSFunctions.
306 Jump isNotObject
= emitJumpIfNotJSCell(X86::ecx
);
307 Jump isJSFunction
= jePtr(Address(X86::ecx
), ImmPtr(m_interpreter
->m_jsFunctionVptr
));
309 // This handles host functions
310 isNotObject
.link(this);
311 callLinkFailNotObject
.link(this);
312 callLinkFailNotJSFunction
.link(this);
313 emitCTICall(((opcodeID
== op_construct
) ? Interpreter::cti_op_construct_NotJSConstruct
: Interpreter::cti_op_call_NotJSFunction
));
314 Jump wasNotJSFunction
= jump();
316 // Next, handle JSFunctions...
317 isJSFunction
.link(this);
319 // First, in the case of a construct, allocate the new object.
320 if (opcodeID
== op_construct
) {
321 emitCTICall(Interpreter::cti_op_construct_JSConstruct
);
322 emitPutVirtualRegister(registerOffset
- RegisterFile::CallFrameHeaderSize
- argCount
);
323 emitGetVirtualRegister(callee
, X86::ecx
);
326 // Speculatively roll the callframe, assuming argCount will match the arity.
327 storePtr(callFrameRegister
, Address(callFrameRegister
, (RegisterFile::CallerFrame
+ registerOffset
) * static_cast<int>(sizeof(Register
))));
328 addPtr(Imm32(registerOffset
* static_cast<int>(sizeof(Register
))), callFrameRegister
);
329 move(Imm32(argCount
), X86::edx
);
331 emitNakedCall(m_interpreter
->m_ctiVirtualCall
);
333 // Put the return value in dst. In the interpreter, op_ret does this.
334 wasNotJSFunction
.link(this);
335 storeResultForFirstRun
.link(this);
336 emitPutVirtualRegister(dst
);
338 sampleCodeBlock(m_codeBlock
);
345 #endif // ENABLE(JIT)