• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
43  using namespace std;
44  
45  namespace JSC {
46  
unlinkCall(CallLinkInfo * callLinkInfo)47  void 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      DataLabelPtr::patch(callLinkInfo->hotPathBegin, JSValuePtr::encode(jsImpossibleValue()));
53  }
54  
linkCall(JSFunction * callee,CodeBlock * calleeCodeBlock,void * ctiCode,CallLinkInfo * callLinkInfo,int callerArgCount)55  void JIT::linkCall(JSFunction* callee, CodeBlock* calleeCodeBlock, void* 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          DataLabelPtr::patch(callLinkInfo->hotPathBegin, callee);
64          Jump::patch(callLinkInfo->hotPathOther, ctiCode);
65      }
66  
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);
70  }
71  
compileOpCallInitializeCallFrame()72  void JIT::compileOpCallInitializeCallFrame()
73  {
74      store32(X86::edx, Address(callFrameRegister, RegisterFile::ArgumentCount * static_cast<int>(sizeof(Register))));
75  
76      loadPtr(Address(X86::ecx, FIELD_OFFSET(JSFunction, m_scopeChain) + FIELD_OFFSET(ScopeChain, m_node)), X86::edx); // newScopeChain
77  
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))));
81  }
82  
compileOpCallSetupArgs(Instruction * instruction)83  void JIT::compileOpCallSetupArgs(Instruction* instruction)
84  {
85      int argCount = instruction[3].u.operand;
86      int registerOffset = instruction[4].u.operand;
87  
88      // ecx holds func
89      emitPutJITStubArg(X86::ecx, 1);
90      emitPutJITStubArgConstant(registerOffset, 2);
91      emitPutJITStubArgConstant(argCount, 3);
92  }
93  
compileOpCallEvalSetupArgs(Instruction * instruction)94  void JIT::compileOpCallEvalSetupArgs(Instruction* instruction)
95  {
96      int argCount = instruction[3].u.operand;
97      int registerOffset = instruction[4].u.operand;
98  
99      // ecx holds func
100      emitPutJITStubArg(X86::ecx, 1);
101      emitPutJITStubArgConstant(registerOffset, 2);
102      emitPutJITStubArgConstant(argCount, 3);
103  }
104  
compileOpConstructSetupArgs(Instruction * instruction)105  void JIT::compileOpConstructSetupArgs(Instruction* instruction)
106  {
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;
111  
112      // ecx holds func
113      emitPutJITStubArg(X86::ecx, 1);
114      emitPutJITStubArgConstant(registerOffset, 2);
115      emitPutJITStubArgConstant(argCount, 3);
116      emitPutJITStubArgFromVirtualRegister(proto, 4, X86::eax);
117      emitPutJITStubArgConstant(thisRegister, 5);
118  }
119  
120  #if !ENABLE(JIT_OPTIMIZE_CALL)
121  
compileOpCall(OpcodeID opcodeID,Instruction * instruction,unsigned)122  void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned)
123  {
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;
128  
129      // Handle eval
130      Jump wasEval;
131      if (opcodeID == op_call_eval) {
132          emitGetVirtualRegister(callee, X86::ecx);
133          compileOpCallEvalSetupArgs(instruction);
134  
135          emitCTICall(Interpreter::cti_op_call_eval);
136          wasEval = jnePtr(X86::eax, ImmPtr(JSValuePtr::encode(jsImpossibleValue())));
137      }
138  
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);
145  
146      // Check for JSFunctions.
147      emitJumpSlowCaseIfNotJSCell(X86::ecx);
148      addSlowCase(jnePtr(Address(X86::ecx), ImmPtr(m_interpreter->m_jsFunctionVptr)));
149  
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);
155      }
156  
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);
161  
162      emitNakedCall(m_interpreter->m_ctiVirtualCall);
163  
164      if (opcodeID == op_call_eval)
165          wasEval.link(this);
166  
167      // Put the return value in dst. In the interpreter, op_ret does this.
168      emitPutVirtualRegister(dst);
169  
170      sampleCodeBlock(m_codeBlock);
171  }
172  
compileOpCallSlowCase(Instruction * instruction,Vector<SlowCaseEntry>::iterator & iter,unsigned,OpcodeID opcodeID)173  void JIT::compileOpCallSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter, unsigned, OpcodeID opcodeID)
174  {
175      int dst = instruction[1].u.operand;
176  
177      linkSlowCase(iter);
178      linkSlowCase(iter);
179  
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);
184  
185      sampleCodeBlock(m_codeBlock);
186  }
187  
188  #else
189  
unreachable()190  static NO_RETURN void unreachable()
191  {
192      ASSERT_NOT_REACHED();
193      exit(1);
194  }
195  
compileOpCall(OpcodeID opcodeID,Instruction * instruction,unsigned callLinkInfoIndex)196  void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned callLinkInfoIndex)
197  {
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;
202  
203      // Handle eval
204      Jump wasEval;
205      if (opcodeID == op_call_eval) {
206          emitGetVirtualRegister(callee, X86::ecx);
207          compileOpCallEvalSetupArgs(instruction);
208  
209          emitCTICall(Interpreter::cti_op_call_eval);
210          wasEval = jnePtr(X86::eax, ImmPtr(JSValuePtr::encode(jsImpossibleValue())));
211      }
212  
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;
221  
222      // The following is the fast case, only used whan a callee can be linked.
223  
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;
228  
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);
234      }
235  
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);
245  
246      // Call to the callee
247      m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedCall(reinterpret_cast<void*>(unreachable));
248  
249      if (opcodeID == op_call_eval)
250          wasEval.link(this);
251  
252      // Put the return value in dst. In the interpreter, op_ret does this.
253      emitPutVirtualRegister(dst);
254  
255      sampleCodeBlock(m_codeBlock);
256  }
257  
compileOpCallSlowCase(Instruction * instruction,Vector<SlowCaseEntry>::iterator & iter,unsigned callLinkInfoIndex,OpcodeID opcodeID)258  void JIT::compileOpCallSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter, unsigned callLinkInfoIndex, OpcodeID opcodeID)
259  {
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;
264  
265      linkSlowCase(iter);
266  
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);
272  
273      // Fast check for JS function.
274      Jump callLinkFailNotObject = emitJumpIfNotJSCell(X86::ecx);
275      Jump callLinkFailNotJSFunction = jnePtr(Address(X86::ecx), ImmPtr(m_interpreter->m_jsFunctionVptr));
276  
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);
282      }
283  
284      move(Imm32(argCount), X86::edx);
285  
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);
289  
290      m_callStructureStubCompilationInfo[callLinkInfoIndex].callReturnLocation =
291          emitNakedCall(m_interpreter->m_ctiVirtualCallPreLink);
292  
293      Jump storeResultForFirstRun = jump();
294  
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);
298  
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);
304  
305      // Check for JSFunctions.
306      Jump isNotObject = emitJumpIfNotJSCell(X86::ecx);
307      Jump isJSFunction = jePtr(Address(X86::ecx), ImmPtr(m_interpreter->m_jsFunctionVptr));
308  
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();
315  
316      // Next, handle JSFunctions...
317      isJSFunction.link(this);
318  
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);
324      }
325  
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);
330  
331      emitNakedCall(m_interpreter->m_ctiVirtualCall);
332  
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);
337  
338      sampleCodeBlock(m_codeBlock);
339  }
340  
341  #endif
342  
343  } // namespace JSC
344  
345  #endif // ENABLE(JIT)
346