/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2013-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include "JIT.h"
#include "CodeBlock.h"
-#include "JITInlineMethods.h"
-#include "JITStubCall.h"
+#include "JITInlines.h"
#include "JSArray.h"
#include "JSFunction.h"
#include "Interpreter.h"
+#include "JSCInlines.h"
+#include "LinkBuffer.h"
+#include "RepatchBuffer.h"
#include "ResultType.h"
#include "SamplingTool.h"
+#include "SetupVarargsFrame.h"
+#include "StackAlignment.h"
+#include "ThunkGenerators.h"
+#include <wtf/StringPrintStream.h>
-#ifndef NDEBUG
-#include <stdio.h>
-#endif
-
-using namespace std;
namespace JSC {
-void JIT::compileOpCallInitializeCallFrame()
-{
- // regT0 holds callee, regT1 holds argCount
- loadPtr(Address(regT0, OBJECT_OFFSETOF(JSFunction, m_scopeChain)), regT3); // scopeChain
- emitPutIntToCallFrameHeader(regT1, RegisterFile::ArgumentCount);
- emitPutCellToCallFrameHeader(regT0, RegisterFile::Callee);
- emitPutCellToCallFrameHeader(regT3, RegisterFile::ScopeChain);
-}
-
-void JIT::emit_op_call_put_result(Instruction* instruction)
+void JIT::emitPutCallResult(Instruction* instruction)
{
int dst = instruction[1].u.operand;
+ emitValueProfilingSite();
emitPutVirtualRegister(dst);
}
-void JIT::compileOpCallVarargs(Instruction* instruction)
-{
- int callee = instruction[1].u.operand;
- int argCountRegister = instruction[2].u.operand;
- int registerOffset = instruction[3].u.operand;
-
- emitGetVirtualRegister(argCountRegister, regT1);
- emitFastArithImmToInt(regT1);
- emitGetVirtualRegister(callee, regT0);
- addPtr(Imm32(registerOffset), regT1, regT2);
-
- // Check for JSFunctions.
- emitJumpSlowCaseIfNotJSCell(regT0);
- addSlowCase(branchPtr(NotEqual, Address(regT0), TrustedImmPtr(m_globalData->jsFunctionVPtr)));
-
- // Speculatively roll the callframe, assuming argCount will match the arity.
- mul32(TrustedImm32(sizeof(Register)), regT2, regT2);
- intptr_t offset = (intptr_t)sizeof(Register) * (intptr_t)RegisterFile::CallerFrame;
- addPtr(Imm32((int32_t)offset), regT2, regT3);
- addPtr(callFrameRegister, regT3);
- storePtr(callFrameRegister, regT3);
- addPtr(regT2, callFrameRegister);
- emitNakedCall(m_globalData->jitStubs->ctiVirtualCall());
-
- sampleCodeBlock(m_codeBlock);
-}
-
-void JIT::compileOpCallVarargsSlowCase(Instruction*, Vector<SlowCaseEntry>::iterator& iter)
+void JIT::compileSetupVarargsFrame(Instruction* instruction, CallLinkInfo* info)
{
- linkSlowCase(iter);
- linkSlowCase(iter);
-
- JITStubCall stubCall(this, cti_op_call_NotJSFunction);
- stubCall.addArgument(regT0);
- stubCall.addArgument(regT2);
- stubCall.addArgument(regT1);
- stubCall.call();
-
- sampleCodeBlock(m_codeBlock);
-}
+ int thisValue = instruction[3].u.operand;
+ int arguments = instruction[4].u.operand;
+ int firstFreeRegister = instruction[5].u.operand;
+ int firstVarArgOffset = instruction[6].u.operand;
+
+ emitGetVirtualRegister(arguments, regT1);
+ callOperation(operationSizeFrameForVarargs, regT1, -firstFreeRegister, firstVarArgOffset);
+ move(TrustedImm32(-firstFreeRegister), regT1);
+ emitSetVarargsFrame(*this, returnValueGPR, false, regT1, regT1);
+ addPtr(TrustedImm32(-(sizeof(CallerFrameAndPC) + WTF::roundUpToMultipleOf(stackAlignmentBytes(), 5 * sizeof(void*)))), regT1, stackPointerRegister);
+ emitGetVirtualRegister(arguments, regT2);
+ callOperation(operationSetupVarargsFrame, regT1, regT2, firstVarArgOffset, regT0);
+ move(returnValueGPR, regT1);
+
+ // Profile the argument count.
+ load32(Address(regT1, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset), regT2);
+ load8(info->addressOfMaxNumArguments(), regT0);
+ Jump notBiggest = branch32(Above, regT0, regT2);
+ Jump notSaturated = branch32(BelowOrEqual, regT2, TrustedImm32(255));
+ move(TrustedImm32(255), regT2);
+ notSaturated.link(this);
+ store8(regT2, info->addressOfMaxNumArguments());
+ notBiggest.link(this);
-#if !ENABLE(JIT_OPTIMIZE_CALL)
+ // Initialize 'this'.
+ emitGetVirtualRegister(thisValue, regT0);
+ store64(regT0, Address(regT1, CallFrame::thisArgumentOffset() * static_cast<int>(sizeof(Register))));
-/* ------------------------------ BEGIN: !ENABLE(JIT_OPTIMIZE_CALL) ------------------------------ */
+ addPtr(TrustedImm32(sizeof(CallerFrameAndPC)), regT1, stackPointerRegister);
+}
-void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned)
+void JIT::compileCallEval(Instruction* instruction)
{
- int callee = instruction[1].u.operand;
- int argCount = instruction[2].u.operand;
- int registerOffset = instruction[3].u.operand;
-
- // Handle eval
- Jump wasEval;
- if (opcodeID == op_call_eval) {
- JITStubCall stubCall(this, cti_op_call_eval);
- stubCall.addArgument(callee, regT0);
- stubCall.addArgument(JIT::Imm32(registerOffset));
- stubCall.addArgument(JIT::Imm32(argCount));
- stubCall.call();
- wasEval = branchPtr(NotEqual, regT0, TrustedImmPtr(JSValue::encode(JSValue())));
- }
-
- emitGetVirtualRegister(callee, regT0);
+ addPtr(TrustedImm32(-static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC))), stackPointerRegister, regT1);
+ storePtr(callFrameRegister, Address(regT1, CallFrame::callerFrameOffset()));
- // Check for JSFunctions.
- emitJumpSlowCaseIfNotJSCell(regT0);
- addSlowCase(branchPtr(NotEqual, Address(regT0), TrustedImmPtr(m_globalData->jsFunctionVPtr)));
+ addPtr(TrustedImm32(stackPointerOffsetFor(m_codeBlock) * sizeof(Register)), callFrameRegister, stackPointerRegister);
+ checkStackPointerAlignment();
- // Speculatively roll the callframe, assuming argCount will match the arity.
- storePtr(callFrameRegister, Address(callFrameRegister, (RegisterFile::CallerFrame + registerOffset) * static_cast<int>(sizeof(Register))));
- addPtr(Imm32(registerOffset * static_cast<int>(sizeof(Register))), callFrameRegister);
- move(Imm32(argCount), regT1);
+ callOperation(operationCallEval, regT1);
- emitNakedCall(opcodeID == op_construct ? m_globalData->jitStubs->ctiVirtualConstruct() : m_globalData->jitStubs->ctiVirtualCall());
-
- if (opcodeID == op_call_eval)
- wasEval.link(this);
+ addSlowCase(branch64(Equal, regT0, TrustedImm64(JSValue::encode(JSValue()))));
sampleCodeBlock(m_codeBlock);
+
+ emitPutCallResult(instruction);
}
-void JIT::compileOpCallSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter, unsigned, OpcodeID opcodeID)
+void JIT::compileCallEvalSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter)
{
- int argCount = instruction[2].u.operand;
- int registerOffset = instruction[3].u.operand;
-
- linkSlowCase(iter);
linkSlowCase(iter);
+ int registerOffset = -instruction[4].u.operand;
- JITStubCall stubCall(this, opcodeID == op_construct ? cti_op_construct_NotJSConstruct : cti_op_call_NotJSFunction);
- stubCall.addArgument(regT0);
- stubCall.addArgument(JIT::Imm32(registerOffset));
- stubCall.addArgument(JIT::Imm32(argCount));
- stubCall.call();
+ addPtr(TrustedImm32(registerOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), callFrameRegister, stackPointerRegister);
+
+ load64(Address(stackPointerRegister, sizeof(Register) * JSStack::Callee - sizeof(CallerFrameAndPC)), regT0);
+ move(TrustedImmPtr(&CallLinkInfo::dummy()), regT2);
+ emitNakedCall(m_vm->getCTIStub(virtualCallThunkGenerator).code());
+ addPtr(TrustedImm32(stackPointerOffsetFor(m_codeBlock) * sizeof(Register)), callFrameRegister, stackPointerRegister);
+ checkStackPointerAlignment();
sampleCodeBlock(m_codeBlock);
+
+ emitPutCallResult(instruction);
}
-#else // !ENABLE(JIT_OPTIMIZE_CALL)
-
-/* ------------------------------ BEGIN: ENABLE(JIT_OPTIMIZE_CALL) ------------------------------ */
-
void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned callLinkInfoIndex)
{
- int callee = instruction[1].u.operand;
- int argCount = instruction[2].u.operand;
- int registerOffset = instruction[3].u.operand;
+ CallLinkInfo* info = m_codeBlock->addCallLinkInfo();
+
+ int callee = instruction[2].u.operand;
+
+ /* Caller always:
+ - Updates callFrameRegister to callee callFrame.
+ - Initializes ArgumentCount; CallerFrame; Callee.
+
+ For a JS call:
+ - Callee initializes ReturnPC; CodeBlock.
+ - Callee restores callFrameRegister before return.
+
+ For a non-JS call:
+ - Caller initializes ReturnPC; CodeBlock.
+ - Caller restores callFrameRegister after return.
+ */
+ COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct), call_and_construct_opcodes_must_be_same_length);
+ COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_call_varargs), call_and_call_varargs_opcodes_must_be_same_length);
+ COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct_varargs), call_and_construct_varargs_opcodes_must_be_same_length);
+ if (opcodeID == op_call_varargs || opcodeID == op_construct_varargs)
+ compileSetupVarargsFrame(instruction, info);
+ else {
+ int argCount = instruction[3].u.operand;
+ int registerOffset = -instruction[4].u.operand;
+
+ if (opcodeID == op_call && shouldEmitProfiling()) {
+ emitGetVirtualRegister(registerOffset + CallFrame::argumentOffsetIncludingThis(0), regT0);
+ Jump done = emitJumpIfNotJSCell(regT0);
+ load32(Address(regT0, JSCell::structureIDOffset()), regT0);
+ store32(regT0, instruction[OPCODE_LENGTH(op_call) - 2].u.arrayProfile->addressOfLastSeenStructureID());
+ done.link(this);
+ }
+
+ addPtr(TrustedImm32(registerOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), callFrameRegister, stackPointerRegister);
+ store32(TrustedImm32(argCount), Address(stackPointerRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC)));
+ } // SP holds newCallFrame + sizeof(CallerFrameAndPC), with ArgumentCount initialized.
+
+ uint32_t bytecodeOffset = instruction - m_codeBlock->instructions().begin();
+ uint32_t locationBits = CallFrame::Location::encodeAsBytecodeOffset(bytecodeOffset);
+ store32(TrustedImm32(locationBits), Address(callFrameRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + TagOffset));
+ emitGetVirtualRegister(callee, regT0); // regT0 holds callee.
- // Handle eval
- Jump wasEval;
+ store64(regT0, Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) - sizeof(CallerFrameAndPC)));
+
if (opcodeID == op_call_eval) {
- JITStubCall stubCall(this, cti_op_call_eval);
- stubCall.addArgument(callee, regT0);
- stubCall.addArgument(JIT::Imm32(registerOffset));
- stubCall.addArgument(JIT::Imm32(argCount));
- stubCall.call();
- wasEval = branchPtr(NotEqual, regT0, TrustedImmPtr(JSValue::encode(JSValue())));
+ compileCallEval(instruction);
+ return;
}
- // This plants a check for a cached JSFunction value, so we can plant a fast link to the callee.
- // This deliberately leaves the callee in ecx, used when setting up the stack frame below
- emitGetVirtualRegister(callee, regT0);
DataLabelPtr addressOfLinkedFunctionCheck;
+ Jump slowCase = branchPtrWithPatch(NotEqual, regT0, addressOfLinkedFunctionCheck, TrustedImmPtr(0));
+ addSlowCase(slowCase);
- BEGIN_UNINTERRUPTED_SEQUENCE(sequenceOpCall);
-
- Jump jumpToSlow = branchPtrWithPatch(NotEqual, regT0, addressOfLinkedFunctionCheck, TrustedImmPtr(JSValue::encode(JSValue())));
-
- END_UNINTERRUPTED_SEQUENCE(sequenceOpCall);
-
- addSlowCase(jumpToSlow);
- ASSERT_JIT_OFFSET(differenceBetween(addressOfLinkedFunctionCheck, jumpToSlow), patchOffsetOpCallCompareToJump);
- m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck;
- m_callStructureStubCompilationInfo[callLinkInfoIndex].isCall = opcodeID != op_construct;
+ ASSERT(m_callCompilationInfo.size() == callLinkInfoIndex);
+ info->setUpCall(CallLinkInfo::callTypeFor(opcodeID), CodeOrigin(m_bytecodeOffset), regT0);
+ m_callCompilationInfo.append(CallCompilationInfo());
+ m_callCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck;
+ m_callCompilationInfo[callLinkInfoIndex].callLinkInfo = info;
- // The following is the fast case, only used whan a callee can be linked.
+ m_callCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedCall();
- // Fast version of stack frame initialization, directly relative to edi.
- // Note that this omits to set up RegisterFile::CodeBlock, which is set in the callee
-
- loadPtr(Address(regT0, OBJECT_OFFSETOF(JSFunction, m_scopeChain)), regT1); // newScopeChain
-
- store32(TrustedImm32(Int32Tag), intTagFor(registerOffset + RegisterFile::ArgumentCount));
- store32(Imm32(argCount), intPayloadFor(registerOffset + RegisterFile::ArgumentCount));
- storePtr(callFrameRegister, Address(callFrameRegister, (registerOffset + RegisterFile::CallerFrame) * static_cast<int>(sizeof(Register))));
- storePtr(regT0, Address(callFrameRegister, (registerOffset + RegisterFile::Callee) * static_cast<int>(sizeof(Register))));
- storePtr(regT1, Address(callFrameRegister, (registerOffset + RegisterFile::ScopeChain) * static_cast<int>(sizeof(Register))));
- addPtr(Imm32(registerOffset * sizeof(Register)), callFrameRegister);
-
- // Call to the callee
- m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedCall();
-
- if (opcodeID == op_call_eval)
- wasEval.link(this);
+ addPtr(TrustedImm32(stackPointerOffsetFor(m_codeBlock) * sizeof(Register)), callFrameRegister, stackPointerRegister);
+ checkStackPointerAlignment();
sampleCodeBlock(m_codeBlock);
+
+ emitPutCallResult(instruction);
}
-void JIT::compileOpCallSlowCase(Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter, unsigned callLinkInfoIndex, OpcodeID opcodeID)
+void JIT::compileOpCallSlowCase(OpcodeID opcodeID, Instruction* instruction, Vector<SlowCaseEntry>::iterator& iter, unsigned callLinkInfoIndex)
{
- int argCount = instruction[2].u.operand;
- int registerOffset = instruction[3].u.operand;
+ if (opcodeID == op_call_eval) {
+ compileCallEvalSlowCase(instruction, iter);
+ return;
+ }
linkSlowCase(iter);
- // Fast check for JS function.
- Jump callLinkFailNotObject = emitJumpIfNotJSCell(regT0);
- Jump callLinkFailNotJSFunction = branchPtr(NotEqual, Address(regT0), TrustedImmPtr(m_globalData->jsFunctionVPtr));
+ ThunkGenerator generator = linkThunkGeneratorFor(
+ (opcodeID == op_construct || opcodeID == op_construct_varargs) ? CodeForConstruct : CodeForCall,
+ RegisterPreservationNotRequired);
+
+ move(TrustedImmPtr(m_callCompilationInfo[callLinkInfoIndex].callLinkInfo), regT2);
+ m_callCompilationInfo[callLinkInfoIndex].callReturnLocation = emitNakedCall(m_vm->getCTIStub(generator).code());
- // Speculatively roll the callframe, assuming argCount will match the arity.
- storePtr(callFrameRegister, Address(callFrameRegister, (RegisterFile::CallerFrame + registerOffset) * static_cast<int>(sizeof(Register))));
- addPtr(Imm32(registerOffset * static_cast<int>(sizeof(Register))), callFrameRegister);
- move(Imm32(argCount), regT1);
+ addPtr(TrustedImm32(stackPointerOffsetFor(m_codeBlock) * sizeof(Register)), callFrameRegister, stackPointerRegister);
+ checkStackPointerAlignment();
- m_callStructureStubCompilationInfo[callLinkInfoIndex].callReturnLocation = emitNakedCall(opcodeID == op_construct ? m_globalData->jitStubs->ctiVirtualConstructLink() : m_globalData->jitStubs->ctiVirtualCallLink());
+ sampleCodeBlock(m_codeBlock);
+
+ emitPutCallResult(instruction);
+}
- // Done! - return back to the hot path.
- ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_call_eval));
- ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct));
- emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_call));
+void JIT::emit_op_call(Instruction* currentInstruction)
+{
+ compileOpCall(op_call, currentInstruction, m_callLinkInfoIndex++);
+}
- // This handles host functions
- callLinkFailNotObject.link(this);
- callLinkFailNotJSFunction.link(this);
+void JIT::emit_op_call_eval(Instruction* currentInstruction)
+{
+ compileOpCall(op_call_eval, currentInstruction, m_callLinkInfoIndex);
+}
- JITStubCall stubCall(this, opcodeID == op_construct ? cti_op_construct_NotJSConstruct : cti_op_call_NotJSFunction);
- stubCall.addArgument(regT0);
- stubCall.addArgument(JIT::Imm32(registerOffset));
- stubCall.addArgument(JIT::Imm32(argCount));
- stubCall.call();
+void JIT::emit_op_call_varargs(Instruction* currentInstruction)
+{
+ compileOpCall(op_call_varargs, currentInstruction, m_callLinkInfoIndex++);
+}
+
+void JIT::emit_op_construct_varargs(Instruction* currentInstruction)
+{
+ compileOpCall(op_construct_varargs, currentInstruction, m_callLinkInfoIndex++);
+}
- sampleCodeBlock(m_codeBlock);
+void JIT::emit_op_construct(Instruction* currentInstruction)
+{
+ compileOpCall(op_construct, currentInstruction, m_callLinkInfoIndex++);
}
-/* ------------------------------ END: !ENABLE / ENABLE(JIT_OPTIMIZE_CALL) ------------------------------ */
+void JIT::emitSlow_op_call(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ compileOpCallSlowCase(op_call, currentInstruction, iter, m_callLinkInfoIndex++);
+}
-#endif // !ENABLE(JIT_OPTIMIZE_CALL)
+void JIT::emitSlow_op_call_eval(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ compileOpCallSlowCase(op_call_eval, currentInstruction, iter, m_callLinkInfoIndex);
+}
+
+void JIT::emitSlow_op_call_varargs(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ compileOpCallSlowCase(op_call_varargs, currentInstruction, iter, m_callLinkInfoIndex++);
+}
+
+void JIT::emitSlow_op_construct_varargs(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ compileOpCallSlowCase(op_construct_varargs, currentInstruction, iter, m_callLinkInfoIndex++);
+}
+
+void JIT::emitSlow_op_construct(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ compileOpCallSlowCase(op_construct, currentInstruction, iter, m_callLinkInfoIndex++);
+}
} // namespace JSC