source: webkit/trunk/JavaScriptCore/jit/JITCall.cpp@ 41036

Last change on this file since 41036 was 40963, checked in by [email protected], 16 years ago

2009-02-12 Gavin Barraclough <[email protected]>

Reviewed by Sam Weinig.

Remove (/reduce) use of hard-wired register names from the JIT.
Currently there is no abstraction of registers used in the JIT,
which has a number of negative consequences. Hard-wiring x86
register names makes the JIT less portable to other platforms,
and prevents us from performing dynamic register allocation to
attempt to maintain more temporary values in machine registers.
(The latter will be more important on x86-64, where we have more
registers to make use of).

Also, remove MacroAssembler::mod32. This was not providing a
useful abstraction, and was not in keeping with the rest of the
MacroAssembler interface, in having specific register requirements.

  • assembler/MacroAssemblerX86Common.h:
  • jit/JIT.cpp: (JSC::JIT::compileOpStrictEq): (JSC::JIT::emitSlowScriptCheck): (JSC::JIT::privateCompileMainPass): (JSC::JIT::privateCompileSlowCases): (JSC::JIT::privateCompile): (JSC::JIT::privateCompileCTIMachineTrampolines):
  • jit/JIT.h:
  • jit/JITArithmetic.cpp: (JSC::JIT::compileFastArith_op_lshift): (JSC::JIT::compileFastArithSlow_op_lshift): (JSC::JIT::compileFastArith_op_rshift): (JSC::JIT::compileFastArithSlow_op_rshift): (JSC::JIT::compileFastArith_op_bitand): (JSC::JIT::compileFastArithSlow_op_bitand): (JSC::JIT::compileFastArith_op_mod): (JSC::JIT::compileFastArithSlow_op_mod): (JSC::JIT::compileFastArith_op_post_inc): (JSC::JIT::compileFastArithSlow_op_post_inc): (JSC::JIT::compileFastArith_op_post_dec): (JSC::JIT::compileFastArithSlow_op_post_dec): (JSC::JIT::compileFastArith_op_pre_inc): (JSC::JIT::compileFastArithSlow_op_pre_inc): (JSC::JIT::compileFastArith_op_pre_dec): (JSC::JIT::compileFastArithSlow_op_pre_dec): (JSC::JIT::compileFastArith_op_add): (JSC::JIT::compileFastArith_op_mul): (JSC::JIT::compileFastArith_op_sub): (JSC::JIT::compileBinaryArithOp):
  • jit/JITCall.cpp: (JSC::JIT::compileOpCallInitializeCallFrame): (JSC::JIT::compileOpCallSetupArgs): (JSC::JIT::compileOpCallEvalSetupArgs): (JSC::JIT::compileOpConstructSetupArgs): (JSC::JIT::compileOpCall): (JSC::JIT::compileOpCallSlowCase):
  • jit/JITInlineMethods.h: (JSC::JIT::emitGetVirtualRegister): (JSC::JIT::emitPutVirtualRegister): (JSC::JIT::emitNakedCall): (JSC::JIT::restoreArgumentReference): (JSC::JIT::restoreArgumentReferenceForTrampoline):
  • jit/JITPropertyAccess.cpp: (JSC::JIT::compileGetByIdHotPath): (JSC::JIT::compilePutByIdHotPath): (JSC::JIT::compileGetByIdSlowCase): (JSC::JIT::compilePutByIdSlowCase): (JSC::JIT::privateCompilePutByIdTransition): (JSC::JIT::privateCompilePatchGetArrayLength): (JSC::JIT::privateCompileGetByIdSelf): (JSC::JIT::privateCompileGetByIdProto): (JSC::JIT::privateCompileGetByIdSelfList): (JSC::JIT::privateCompileGetByIdProtoList): (JSC::JIT::privateCompileGetByIdChainList): (JSC::JIT::privateCompileGetByIdChain): (JSC::JIT::privateCompilePutByIdReplace):
File size: 13.9 KB
Line 
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#include "JIT.h"
28
29#if ENABLE(JIT)
30
31#include "CodeBlock.h"
32#include "JITInlineMethods.h"
33#include "JSArray.h"
34#include "JSFunction.h"
35#include "Interpreter.h"
36#include "ResultType.h"
37#include "SamplingTool.h"
38
39#ifndef NDEBUG
40#include <stdio.h>
41#endif
42
43using namespace std;
44
45namespace JSC {
46
47void JIT::unlinkCall(CallLinkInfo* callLinkInfo)
48{
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 callLinkInfo->hotPathBegin.repatch(JSValuePtr::encode(jsImpossibleValue()));
53}
54
55void JIT::linkCall(JSFunction* callee, CodeBlock* calleeCodeBlock, JITCode ctiCode, CallLinkInfo* callLinkInfo, int callerArgCount)
56{
57 // Currently we only link calls with the exact number of arguments.
58 if (callerArgCount == calleeCodeBlock->m_numParameters) {
59 ASSERT(!callLinkInfo->isLinked());
60
61 calleeCodeBlock->addCaller(callLinkInfo);
62
63 callLinkInfo->hotPathBegin.repatch(callee);
64 callLinkInfo->hotPathOther.relink(ctiCode.addressForCall());
65 }
66
67 // patch the instruction that jumps out to the cold path, so that we only try to link once.
68 callLinkInfo->hotPathBegin.jumpAtOffset(patchOffsetOpCallCompareToJump).relink(callLinkInfo->coldPathOther);
69}
70
71void JIT::compileOpCallInitializeCallFrame()
72{
73 store32(regT1, Address(callFrameRegister, RegisterFile::ArgumentCount * static_cast<int>(sizeof(Register))));
74
75 loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_scopeChain) + FIELD_OFFSET(ScopeChain, m_node)), regT1); // newScopeChain
76
77 storePtr(ImmPtr(JSValuePtr::encode(noValue())), Address(callFrameRegister, RegisterFile::OptionalCalleeArguments * static_cast<int>(sizeof(Register))));
78 storePtr(regT2, Address(callFrameRegister, RegisterFile::Callee * static_cast<int>(sizeof(Register))));
79 storePtr(regT1, Address(callFrameRegister, RegisterFile::ScopeChain * static_cast<int>(sizeof(Register))));
80}
81
82void JIT::compileOpCallSetupArgs(Instruction* instruction)
83{
84 int argCount = instruction[3].u.operand;
85 int registerOffset = instruction[4].u.operand;
86
87 // ecx holds func
88 emitPutJITStubArg(regT2, 1);
89 emitPutJITStubArgConstant(registerOffset, 2);
90 emitPutJITStubArgConstant(argCount, 3);
91}
92
93void JIT::compileOpCallEvalSetupArgs(Instruction* instruction)
94{
95 int argCount = instruction[3].u.operand;
96 int registerOffset = instruction[4].u.operand;
97
98 // ecx holds func
99 emitPutJITStubArg(regT2, 1);
100 emitPutJITStubArgConstant(registerOffset, 2);
101 emitPutJITStubArgConstant(argCount, 3);
102}
103
104void JIT::compileOpConstructSetupArgs(Instruction* instruction)
105{
106 int argCount = instruction[3].u.operand;
107 int registerOffset = instruction[4].u.operand;
108 int proto = instruction[5].u.operand;
109 int thisRegister = instruction[6].u.operand;
110
111 // ecx holds func
112 emitPutJITStubArg(regT2, 1);
113 emitPutJITStubArgConstant(registerOffset, 2);
114 emitPutJITStubArgConstant(argCount, 3);
115 emitPutJITStubArgFromVirtualRegister(proto, 4, regT0);
116 emitPutJITStubArgConstant(thisRegister, 5);
117}
118
119#if !ENABLE(JIT_OPTIMIZE_CALL)
120
121void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned)
122{
123 int dst = instruction[1].u.operand;
124 int callee = instruction[2].u.operand;
125 int argCount = instruction[3].u.operand;
126 int registerOffset = instruction[4].u.operand;
127
128 // Handle eval
129 Jump wasEval;
130 if (opcodeID == op_call_eval) {
131 emitGetVirtualRegister(callee, regT2);
132 compileOpCallEvalSetupArgs(instruction);
133
134 emitCTICall(Interpreter::cti_op_call_eval);
135 wasEval = branchPtr(NotEqual, regT0, ImmPtr(JSValuePtr::encode(jsImpossibleValue())));
136 }
137
138 emitGetVirtualRegister(callee, regT2);
139 // The arguments have been set up on the hot path for op_call_eval
140 if (opcodeID == op_call)
141 compileOpCallSetupArgs(instruction);
142 else if (opcodeID == op_construct)
143 compileOpConstructSetupArgs(instruction);
144
145 // Check for JSFunctions.
146 emitJumpSlowCaseIfNotJSCell(regT2);
147 addSlowCase(branchPtr(NotEqual, Address(regT2), ImmPtr(m_interpreter->m_jsFunctionVptr)));
148
149 // First, in the case of a construct, allocate the new object.
150 if (opcodeID == op_construct) {
151 emitCTICall(Interpreter::cti_op_construct_JSConstruct);
152 emitPutVirtualRegister(registerOffset - RegisterFile::CallFrameHeaderSize - argCount);
153 emitGetVirtualRegister(callee, regT2);
154 }
155
156 // Speculatively roll the callframe, assuming argCount will match the arity.
157 storePtr(callFrameRegister, Address(callFrameRegister, (RegisterFile::CallerFrame + registerOffset) * static_cast<int>(sizeof(Register))));
158 addPtr(Imm32(registerOffset * static_cast<int>(sizeof(Register))), callFrameRegister);
159 move(Imm32(argCount), regT1);
160
161 emitNakedCall(m_interpreter->m_ctiVirtualCall);
162
163 if (opcodeID == op_call_eval)
164 wasEval.link(this);
165
166 // Put the return value in dst. In the interpreter, op_ret does this.
167 emitPutVirtualRegister(dst);
168
169 sampleCodeBlock(m_codeBlock);
170}
171
172void JIT::compileOpCallSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter, unsigned, OpcodeID opcodeID)
173{
174 int dst = instruction[1].u.operand;
175
176 linkSlowCase(iter);
177 linkSlowCase(iter);
178
179 // This handles host functions
180 emitCTICall(((opcodeID == op_construct) ? Interpreter::cti_op_construct_NotJSConstruct : Interpreter::cti_op_call_NotJSFunction));
181 // Put the return value in dst. In the interpreter, op_ret does this.
182 emitPutVirtualRegister(dst);
183
184 sampleCodeBlock(m_codeBlock);
185}
186
187#else
188
189static NO_RETURN void unreachable()
190{
191 ASSERT_NOT_REACHED();
192 exit(1);
193}
194
195void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned callLinkInfoIndex)
196{
197 int dst = instruction[1].u.operand;
198 int callee = instruction[2].u.operand;
199 int argCount = instruction[3].u.operand;
200 int registerOffset = instruction[4].u.operand;
201
202 // Handle eval
203 Jump wasEval;
204 if (opcodeID == op_call_eval) {
205 emitGetVirtualRegister(callee, regT2);
206 compileOpCallEvalSetupArgs(instruction);
207
208 emitCTICall(Interpreter::cti_op_call_eval);
209 wasEval = branchPtr(NotEqual, regT0, ImmPtr(JSValuePtr::encode(jsImpossibleValue())));
210 }
211
212 // This plants a check for a cached JSFunction value, so we can plant a fast link to the callee.
213 // This deliberately leaves the callee in ecx, used when setting up the stack frame below
214 emitGetVirtualRegister(callee, regT2);
215 DataLabelPtr addressOfLinkedFunctionCheck;
216 Jump jumpToSlow = branchPtrWithPatch(NotEqual, regT2, addressOfLinkedFunctionCheck, ImmPtr(JSValuePtr::encode(jsImpossibleValue())));
217 addSlowCase(jumpToSlow);
218 ASSERT(differenceBetween(addressOfLinkedFunctionCheck, jumpToSlow) == patchOffsetOpCallCompareToJump);
219 m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck;
220
221 // The following is the fast case, only used whan a callee can be linked.
222
223 // In the case of OpConstruct, call out to a cti_ function to create the new object.
224 if (opcodeID == op_construct) {
225 int proto = instruction[5].u.operand;
226 int thisRegister = instruction[6].u.operand;
227
228 emitPutJITStubArg(regT2, 1);
229 emitPutJITStubArgFromVirtualRegister(proto, 4, regT0);
230 emitCTICall(Interpreter::cti_op_construct_JSConstruct);
231 emitPutVirtualRegister(thisRegister);
232 emitGetVirtualRegister(callee, regT2);
233 }
234
235 // Fast version of stack frame initialization, directly relative to edi.
236 // Note that this omits to set up RegisterFile::CodeBlock, which is set in the callee
237 storePtr(ImmPtr(JSValuePtr::encode(noValue())), Address(callFrameRegister, (registerOffset + RegisterFile::OptionalCalleeArguments) * static_cast<int>(sizeof(Register))));
238 storePtr(regT2, Address(callFrameRegister, (registerOffset + RegisterFile::Callee) * static_cast<int>(sizeof(Register))));
239 loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_scopeChain) + FIELD_OFFSET(ScopeChain, m_node)), regT1); // newScopeChain
240 store32(Imm32(argCount), Address(callFrameRegister, (registerOffset + RegisterFile::ArgumentCount) * static_cast<int>(sizeof(Register))));
241 storePtr(callFrameRegister, Address(callFrameRegister, (registerOffset + RegisterFile::CallerFrame) * static_cast<int>(sizeof(Register))));
242 storePtr(regT1, Address(callFrameRegister, (registerOffset + RegisterFile::ScopeChain) * static_cast<int>(sizeof(Register))));
243 addPtr(Imm32(registerOffset * sizeof(Register)), callFrameRegister);
244
245 // Call to the callee
246 m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedCall(reinterpret_cast<void*>(unreachable));
247
248 if (opcodeID == op_call_eval)
249 wasEval.link(this);
250
251 // Put the return value in dst. In the interpreter, op_ret does this.
252 emitPutVirtualRegister(dst);
253
254 sampleCodeBlock(m_codeBlock);
255}
256
257void JIT::compileOpCallSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter, unsigned callLinkInfoIndex, OpcodeID opcodeID)
258{
259 int dst = instruction[1].u.operand;
260 int callee = instruction[2].u.operand;
261 int argCount = instruction[3].u.operand;
262 int registerOffset = instruction[4].u.operand;
263
264 linkSlowCase(iter);
265
266 // The arguments have been set up on the hot path for op_call_eval
267 if (opcodeID == op_call)
268 compileOpCallSetupArgs(instruction);
269 else if (opcodeID == op_construct)
270 compileOpConstructSetupArgs(instruction);
271
272 // Fast check for JS function.
273 Jump callLinkFailNotObject = emitJumpIfNotJSCell(regT2);
274 Jump callLinkFailNotJSFunction = branchPtr(NotEqual, Address(regT2), ImmPtr(m_interpreter->m_jsFunctionVptr));
275
276 // First, in the case of a construct, allocate the new object.
277 if (opcodeID == op_construct) {
278 emitCTICall(Interpreter::cti_op_construct_JSConstruct);
279 emitPutVirtualRegister(registerOffset - RegisterFile::CallFrameHeaderSize - argCount);
280 emitGetVirtualRegister(callee, regT2);
281 }
282
283 move(Imm32(argCount), regT1);
284
285 // Speculatively roll the callframe, assuming argCount will match the arity.
286 storePtr(callFrameRegister, Address(callFrameRegister, (RegisterFile::CallerFrame + registerOffset) * static_cast<int>(sizeof(Register))));
287 addPtr(Imm32(registerOffset * static_cast<int>(sizeof(Register))), callFrameRegister);
288
289 m_callStructureStubCompilationInfo[callLinkInfoIndex].callReturnLocation =
290 emitNakedCall(m_interpreter->m_ctiVirtualCallPreLink);
291
292 Jump storeResultForFirstRun = jump();
293
294// FIXME: this label can be removed, since it is a fixed offset from 'callReturnLocation'.
295 // This is the address for the cold path *after* the first run (which tries to link the call).
296 m_callStructureStubCompilationInfo[callLinkInfoIndex].coldPathOther = MacroAssembler::Label(this);
297
298 // The arguments have been set up on the hot path for op_call_eval
299 if (opcodeID == op_call)
300 compileOpCallSetupArgs(instruction);
301 else if (opcodeID == op_construct)
302 compileOpConstructSetupArgs(instruction);
303
304 // Check for JSFunctions.
305 Jump isNotObject = emitJumpIfNotJSCell(regT2);
306 Jump isJSFunction = branchPtr(Equal, Address(regT2), ImmPtr(m_interpreter->m_jsFunctionVptr));
307
308 // This handles host functions
309 isNotObject.link(this);
310 callLinkFailNotObject.link(this);
311 callLinkFailNotJSFunction.link(this);
312 emitCTICall(((opcodeID == op_construct) ? Interpreter::cti_op_construct_NotJSConstruct : Interpreter::cti_op_call_NotJSFunction));
313 Jump wasNotJSFunction = jump();
314
315 // Next, handle JSFunctions...
316 isJSFunction.link(this);
317
318 // First, in the case of a construct, allocate the new object.
319 if (opcodeID == op_construct) {
320 emitCTICall(Interpreter::cti_op_construct_JSConstruct);
321 emitPutVirtualRegister(registerOffset - RegisterFile::CallFrameHeaderSize - argCount);
322 emitGetVirtualRegister(callee, regT2);
323 }
324
325 // Speculatively roll the callframe, assuming argCount will match the arity.
326 storePtr(callFrameRegister, Address(callFrameRegister, (RegisterFile::CallerFrame + registerOffset) * static_cast<int>(sizeof(Register))));
327 addPtr(Imm32(registerOffset * static_cast<int>(sizeof(Register))), callFrameRegister);
328 move(Imm32(argCount), regT1);
329
330 emitNakedCall(m_interpreter->m_ctiVirtualCall);
331
332 // Put the return value in dst. In the interpreter, op_ret does this.
333 wasNotJSFunction.link(this);
334 storeResultForFirstRun.link(this);
335 emitPutVirtualRegister(dst);
336
337 sampleCodeBlock(m_codeBlock);
338}
339
340#endif
341
342} // namespace JSC
343
344#endif // ENABLE(JIT)
Note: See TracBrowser for help on using the repository browser.