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