blob: 06bd6115a1d9eda7ec89d50fd7d413b9aa25239c [file] [log] [blame]
/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "CTI.h"
#if ENABLE(CTI)
#include "CodeBlock.h"
#include "JSArray.h"
#include "Machine.h"
#include "wrec/WREC.h"
using namespace std;
namespace JSC {
#if COMPILER(GCC) && PLATFORM(X86)
asm(
".globl _ctiTrampoline" "\n"
"_ctiTrampoline:" "\n"
"pushl %esi" "\n"
"pushl %edi" "\n"
"subl $0x24, %esp" "\n"
"movl $512, %esi" "\n"
"call *0x30(%esp)" "\n" //Ox30 = 0x0C * 4, 0x0C = CTI_ARGS_code
"addl $0x24, %esp" "\n"
"popl %edi" "\n"
"popl %esi" "\n"
"ret" "\n"
);
asm(
".globl _ctiVMThrowTrampoline" "\n"
"_ctiVMThrowTrampoline:" "\n"
#ifndef NDEBUG
"movl 0x34(%esp), %ecx" "\n" //Ox34 = 0x0D * 4, 0x0D = CTI_ARGS_exec
"cmpl $0, 8(%ecx)" "\n"
"jne 1f" "\n"
"int3" "\n"
"1:" "\n"
#endif
"call __ZN3JSC7Machine12cti_vm_throwEPv" "\n"
"addl $0x24, %esp" "\n"
"popl %edi" "\n"
"popl %esi" "\n"
"ret" "\n"
);
#elif COMPILER(MSVC)
extern "C"
{
__declspec(naked) JSValue* ctiTrampoline(void* code, ExecState* exec, RegisterFile* registerFile, Register* r, ScopeChainNode* scopeChain, CodeBlock* codeBlock, JSValue** exception, Profiler**)
{
__asm {
push esi;
push edi;
sub esp, 0x24;
mov esi, 512;
mov [esp], esp;
call [esp + 0x30];
add esp, 0x24;
pop edi;
pop esi;
ret;
}
}
__declspec(naked) void ctiVMThrowTrampoline()
{
__asm {
mov [esp], esp;
call JSC::Machine::cti_vm_throw;
add esp, 0x24;
pop edi;
pop esi;
ret;
}
}
}
#endif
// get arg puts an arg from the SF register array into a h/w register
ALWAYS_INLINE void CTI::emitGetArg(unsigned src, X86Assembler::RegisterID dst)
{
// TODO: we want to reuse values that are already in registers if we can - add a register allocator!
if (src < m_codeBlock->constantRegisters.size()) {
JSValue* js = m_codeBlock->constantRegisters[src].jsValue(m_exec);
m_jit.movl_i32r(reinterpret_cast<unsigned>(js), dst);
} else
m_jit.movl_mr(src * sizeof(Register), X86::edi, dst);
}
// get arg puts an arg from the SF register array onto the stack, as an arg to a context threaded function.
ALWAYS_INLINE void CTI::emitGetPutArg(unsigned src, unsigned offset, X86Assembler::RegisterID scratch)
{
if (src < m_codeBlock->constantRegisters.size()) {
JSValue* js = m_codeBlock->constantRegisters[src].jsValue(m_exec);
m_jit.movl_i32m(reinterpret_cast<unsigned>(js), offset + sizeof(void*), X86::esp);
} else {
m_jit.movl_mr(src * sizeof(Register), X86::edi, scratch);
m_jit.movl_rm(scratch, offset + sizeof(void*), X86::esp);
}
}
// puts an arg onto the stack, as an arg to a context threaded function.
ALWAYS_INLINE void CTI::emitPutArg(X86Assembler::RegisterID src, unsigned offset)
{
m_jit.movl_rm(src, offset + sizeof(void*), X86::esp);
}
ALWAYS_INLINE void CTI::emitPutArgConstant(unsigned value, unsigned offset)
{
m_jit.movl_i32m(value, offset + sizeof(void*), X86::esp);
}
ALWAYS_INLINE JSValue* CTI::getConstantImmediateNumericArg(unsigned src)
{
if (src < m_codeBlock->constantRegisters.size()) {
JSValue* js = m_codeBlock->constantRegisters[src].jsValue(m_exec);
return JSImmediate::isNumber(js) ? js : 0;
}
return 0;
}
ALWAYS_INLINE void CTI::emitPutCTIParam(void* value, unsigned name)
{
m_jit.movl_i32m(reinterpret_cast<intptr_t>(value), name * sizeof(void*), X86::esp);
}
ALWAYS_INLINE void CTI::emitPutCTIParam(X86Assembler::RegisterID from, unsigned name)
{
m_jit.movl_rm(from, name * sizeof(void*), X86::esp);
}
ALWAYS_INLINE void CTI::emitGetCTIParam(unsigned name, X86Assembler::RegisterID to)
{
m_jit.movl_mr(name * sizeof(void*), X86::esp, to);
}
ALWAYS_INLINE void CTI::emitPutToCallFrameHeader(X86Assembler::RegisterID from, RegisterFile::CallFrameHeaderEntry entry)
{
m_jit.movl_rm(from, -((m_codeBlock->numLocals + RegisterFile::CallFrameHeaderSize) - entry) * sizeof(Register), X86::edi);
}
ALWAYS_INLINE void CTI::emitGetFromCallFrameHeader(RegisterFile::CallFrameHeaderEntry entry, X86Assembler::RegisterID to)
{
m_jit.movl_mr(-((m_codeBlock->numLocals + RegisterFile::CallFrameHeaderSize) - entry) * sizeof(Register), X86::edi, to);
}
ALWAYS_INLINE void CTI::emitPutResult(unsigned dst, X86Assembler::RegisterID from)
{
m_jit.movl_rm(from, dst * sizeof(Register), X86::edi);
// FIXME: #ifndef NDEBUG, Write the correct m_type to the register.
}
ALWAYS_INLINE void CTI::emitInitialiseRegister(unsigned dst)
{
m_jit.movl_i32m(reinterpret_cast<unsigned>(jsUndefined()), dst * sizeof(Register), X86::edi);
// FIXME: #ifndef NDEBUG, Write the correct m_type to the register.
}
#if ENABLE(SAMPLING_TOOL)
unsigned inCalledCode = 0;
#endif
void ctiSetReturnAddress(void** where, void* what)
{
*where = what;
}
void ctiRepatchCallByReturnAddress(void* where, void* what)
{
(static_cast<void**>(where))[-1] = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(what) - reinterpret_cast<uintptr_t>(where));
}
#ifdef NDEBUG
ALWAYS_INLINE void CTI::emitDebugExceptionCheck()
{
}
#else
ALWAYS_INLINE void CTI::emitDebugExceptionCheck()
{
emitGetCTIParam(CTI_ARGS_exec, X86::ecx);
m_jit.cmpl_i32m(0, OBJECT_OFFSET(ExecState, m_exception), X86::ecx);
X86Assembler::JmpSrc noException = m_jit.emitUnlinkedJe();
m_jit.emitInt3();
m_jit.link(noException, m_jit.label());
}
void CTI::printOpcodeOperandTypes(unsigned src1, unsigned src2)
{
char which1 = '*';
if (src1 < m_codeBlock->constantRegisters.size()) {
JSValue* js = m_codeBlock->constantRegisters[src1].jsValue(m_exec);
which1 =
JSImmediate::isImmediate(js) ?
(JSImmediate::isNumber(js) ? 'i' :
JSImmediate::isBoolean(js) ? 'b' :
js->isUndefined() ? 'u' :
js->isNull() ? 'n' : '?')
:
(js->isString() ? 's' :
js->isObject() ? 'o' :
'k');
}
char which2 = '*';
if (src2 < m_codeBlock->constantRegisters.size()) {
JSValue* js = m_codeBlock->constantRegisters[src2].jsValue(m_exec);
which2 =
JSImmediate::isImmediate(js) ?
(JSImmediate::isNumber(js) ? 'i' :
JSImmediate::isBoolean(js) ? 'b' :
js->isUndefined() ? 'u' :
js->isNull() ? 'n' : '?')
:
(js->isString() ? 's' :
js->isObject() ? 'o' :
'k');
}
if ((which1 != '*') | (which2 != '*'))
fprintf(stderr, "Types %c %c\n", which1, which2);
}
#endif
ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitCall(unsigned opcodeIndex, CTIHelper_j helper)
{
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(1, &inCalledCode);
#endif
X86Assembler::JmpSrc call = m_jit.emitCall();
m_calls.append(CallRecord(call, helper, opcodeIndex));
emitDebugExceptionCheck();
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(0, &inCalledCode);
#endif
return call;
}
ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitCall(unsigned opcodeIndex, CTIHelper_p helper)
{
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(1, &inCalledCode);
#endif
X86Assembler::JmpSrc call = m_jit.emitCall();
m_calls.append(CallRecord(call, helper, opcodeIndex));
emitDebugExceptionCheck();
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(0, &inCalledCode);
#endif
return call;
}
ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitCall(unsigned opcodeIndex, CTIHelper_b helper)
{
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(1, &inCalledCode);
#endif
X86Assembler::JmpSrc call = m_jit.emitCall();
m_calls.append(CallRecord(call, helper, opcodeIndex));
emitDebugExceptionCheck();
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(0, &inCalledCode);
#endif
return call;
}
ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitCall(unsigned opcodeIndex, CTIHelper_v helper)
{
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(1, &inCalledCode);
#endif
X86Assembler::JmpSrc call = m_jit.emitCall();
m_calls.append(CallRecord(call, helper, opcodeIndex));
emitDebugExceptionCheck();
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(0, &inCalledCode);
#endif
return call;
}
ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitCall(unsigned opcodeIndex, CTIHelper_s helper)
{
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(1, &inCalledCode);
#endif
X86Assembler::JmpSrc call = m_jit.emitCall();
m_calls.append(CallRecord(call, helper, opcodeIndex));
emitDebugExceptionCheck();
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(0, &inCalledCode);
#endif
return call;
}
ALWAYS_INLINE void CTI::emitJumpSlowCaseIfNotJSCell(X86Assembler::RegisterID reg, unsigned opcodeIndex)
{
m_jit.testl_i32r(JSImmediate::TagMask, reg);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), opcodeIndex));
}
ALWAYS_INLINE void CTI::emitJumpSlowCaseIfNotImmNum(X86Assembler::RegisterID reg, unsigned opcodeIndex)
{
m_jit.testl_i32r(JSImmediate::TagBitTypeInteger, reg);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJe(), opcodeIndex));
}
ALWAYS_INLINE void CTI::emitJumpSlowCaseIfNotImmNums(X86Assembler::RegisterID reg1, X86Assembler::RegisterID reg2, unsigned opcodeIndex)
{
m_jit.movl_rr(reg1, X86::ecx);
m_jit.andl_rr(reg2, X86::ecx);
emitJumpSlowCaseIfNotImmNum(X86::ecx, opcodeIndex);
}
ALWAYS_INLINE unsigned CTI::getDeTaggedConstantImmediate(JSValue* imm)
{
ASSERT(JSImmediate::isNumber(imm));
return reinterpret_cast<unsigned>(imm) & ~JSImmediate::TagBitTypeInteger;
}
ALWAYS_INLINE void CTI::emitFastArithDeTagImmediate(X86Assembler::RegisterID reg)
{
// op_mod relies on this being a sub - setting zf if result is 0.
m_jit.subl_i8r(JSImmediate::TagBitTypeInteger, reg);
}
ALWAYS_INLINE void CTI::emitFastArithReTagImmediate(X86Assembler::RegisterID reg)
{
m_jit.addl_i8r(JSImmediate::TagBitTypeInteger, reg);
}
ALWAYS_INLINE void CTI::emitFastArithPotentiallyReTagImmediate(X86Assembler::RegisterID reg)
{
m_jit.orl_i32r(JSImmediate::TagBitTypeInteger, reg);
}
ALWAYS_INLINE void CTI::emitFastArithImmToInt(X86Assembler::RegisterID reg)
{
m_jit.sarl_i8r(1, reg);
}
ALWAYS_INLINE void CTI::emitFastArithIntToImmOrSlowCase(X86Assembler::RegisterID reg, unsigned opcodeIndex)
{
m_jit.addl_rr(reg, reg);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), opcodeIndex));
emitFastArithReTagImmediate(reg);
}
ALWAYS_INLINE void CTI::emitFastArithIntToImmNoCheck(X86Assembler::RegisterID reg)
{
m_jit.addl_rr(reg, reg);
emitFastArithReTagImmediate(reg);
}
ALWAYS_INLINE void CTI::emitTagAsBoolImmediate(X86Assembler::RegisterID reg)
{
m_jit.shl_i8r(JSImmediate::ExtendedPayloadShift, reg);
m_jit.orl_i32r(JSImmediate::FullTagTypeBool, reg);
}
CTI::CTI(Machine* machine, ExecState* exec, CodeBlock* codeBlock)
: m_jit(machine->jitCodeBuffer())
, m_machine(machine)
, m_exec(exec)
, m_codeBlock(codeBlock)
, m_labels(codeBlock ? codeBlock->instructions.size() : 0)
, m_structureStubCompilationInfo(codeBlock ? codeBlock->structureIDInstructions.size() : 0)
{
}
#define CTI_COMPILE_BINARY_OP(name) \
case name: { \
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx); \
emitGetPutArg(instruction[i + 3].u.operand, 4, X86::ecx); \
emitCall(i, Machine::cti_##name); \
emitPutResult(instruction[i + 1].u.operand); \
i += 4; \
break; \
}
#define CTI_COMPILE_UNARY_OP(name) \
case name: { \
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx); \
emitCall(i, Machine::cti_##name); \
emitPutResult(instruction[i + 1].u.operand); \
i += 3; \
break; \
}
#if ENABLE(SAMPLING_TOOL)
OpcodeID currentOpcodeID = static_cast<OpcodeID>(-1);
#endif
void CTI::compileOpCall(Instruction* instruction, unsigned i, CompileOpCallType type)
{
int dst = instruction[i + 1].u.operand;
int firstArg = instruction[i + 4].u.operand;
int argCount = instruction[i + 5].u.operand;
if (type == OpConstruct) {
emitPutArgConstant(reinterpret_cast<unsigned>(instruction + i), 16);
emitPutArgConstant(argCount, 12);
emitPutArgConstant(firstArg, 8);
emitGetPutArg(instruction[i + 3].u.operand, 4, X86::ecx);
} else {
emitPutArgConstant(reinterpret_cast<unsigned>(instruction + i), 16);
emitPutArgConstant(argCount, 12);
emitPutArgConstant(firstArg, 8);
// FIXME: should this be loaded dynamically off m_exec?
int thisVal = instruction[i + 3].u.operand;
if (thisVal == missingThisObjectMarker()) {
emitPutArgConstant(reinterpret_cast<unsigned>(m_exec->globalThisValue()), 4);
} else
emitGetPutArg(thisVal, 4, X86::ecx);
}
X86Assembler::JmpSrc wasEval;
if (type == OpCallEval) {
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
emitCall(i, Machine::cti_op_call_eval);
m_jit.emitRestoreArgumentReference();
emitGetCTIParam(CTI_ARGS_r, X86::edi); // edi := r
m_jit.cmpl_i32r(reinterpret_cast<unsigned>(JSImmediate::impossibleValue()), X86::eax);
wasEval = m_jit.emitUnlinkedJne();
// this reloads the first arg into ecx (checked just below).
emitGetArg(instruction[i + 2].u.operand, X86::ecx);
} else {
// this sets up the first arg, and explicitly leaves the value in ecx (checked just below).
emitGetArg(instruction[i + 2].u.operand, X86::ecx);
emitPutArg(X86::ecx, 0);
}
// initializeCallFrame!
m_jit.movl_i32m(reinterpret_cast<unsigned>(m_codeBlock), (firstArg - RegisterFile::CallFrameHeaderSize + RegisterFile::CallerCodeBlock) * sizeof(Register), X86::edi);
m_jit.movl_i32m(reinterpret_cast<unsigned>(instruction + i), (firstArg - RegisterFile::CallFrameHeaderSize + RegisterFile::ReturnVPC) * sizeof(Register), X86::edi);
emitGetCTIParam(CTI_ARGS_scopeChain, X86::edx);
m_jit.movl_rm(X86::edx, (firstArg - RegisterFile::CallFrameHeaderSize + RegisterFile::CallerScopeChain) * sizeof(Register), X86::edi);
m_jit.movl_rm(X86::edi, (firstArg - RegisterFile::CallFrameHeaderSize + RegisterFile::CallerRegisters) * sizeof(Register), X86::edi);
m_jit.movl_i32m(dst, (firstArg - RegisterFile::CallFrameHeaderSize + RegisterFile::ReturnValueRegister) * sizeof(Register), X86::edi);
m_jit.movl_i32m(firstArg, (firstArg - RegisterFile::CallFrameHeaderSize + RegisterFile::ArgumentStartRegister) * sizeof(Register), X86::edi);
m_jit.movl_i32m(argCount, (firstArg - RegisterFile::CallFrameHeaderSize + RegisterFile::ArgumentCount) * sizeof(Register), X86::edi);
m_jit.movl_rm(X86::ecx, (firstArg - RegisterFile::CallFrameHeaderSize + RegisterFile::Callee) * sizeof(Register), X86::edi);
m_jit.movl_i32m(0, (firstArg - RegisterFile::CallFrameHeaderSize + RegisterFile::OptionalCalleeActivation) * sizeof(Register), X86::edi);
// CTIReturnEIP (set in callee)
// Fast check for JS function.
m_jit.testl_i32r(JSImmediate::TagMask, X86::ecx);
X86Assembler::JmpSrc isNotObject = m_jit.emitUnlinkedJne();
m_jit.cmpl_i32m(reinterpret_cast<unsigned>(m_machine->m_jsFunctionVptr), X86::ecx);
X86Assembler::JmpSrc isJSFunction = m_jit.emitUnlinkedJe();
m_jit.link(isNotObject, m_jit.label());
// This handles host functions
emitCall(i, ((type == OpConstruct) ? Machine::cti_op_construct_NotJSConstruct : Machine::cti_op_call_NotJSFunction));
emitGetCTIParam(CTI_ARGS_r, X86::edi); // edi := r
X86Assembler::JmpSrc wasNotJSFunction = m_jit.emitUnlinkedJmp();
m_jit.link(isJSFunction, m_jit.label());
// This handles JSFunctions
emitCall(i, ((type == OpConstruct) ? Machine::cti_op_construct_JSConstruct : Machine::cti_op_call_JSFunction));
// Check the ctiCode has been generated - if not, this is handled in a slow case.
m_jit.testl_rr(X86::eax, X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJe(), i));
m_jit.call_r(X86::eax);
// In the interpreter the following actions are performed by op_ret:
// Store the scope chain - returned by op_ret in %edx (see below) - to ExecState::m_scopeChain and CTI_ARGS_scopeChain on the stack.
emitGetCTIParam(CTI_ARGS_exec, X86::ecx);
emitPutCTIParam(X86::edx, CTI_ARGS_scopeChain);
m_jit.movl_rm(X86::edx, OBJECT_OFFSET(ExecState, m_scopeChain), X86::ecx);
// Restore ExecState::m_callFrame.
m_jit.leal_mr(-(m_codeBlock->numLocals + RegisterFile::CallFrameHeaderSize) * sizeof(Register), X86::edi, X86::edx);
m_jit.movl_rm(X86::edx, OBJECT_OFFSET(ExecState, m_callFrame), X86::ecx);
// Restore CTI_ARGS_codeBlock.
emitPutCTIParam(m_codeBlock, CTI_ARGS_codeBlock);
X86Assembler::JmpDst end = m_jit.label();
m_jit.link(wasNotJSFunction, end);
if (type == OpCallEval)
m_jit.link(wasEval, end);
emitPutResult(dst);
}
void CTI::emitSlowScriptCheck(unsigned opcodeIndex)
{
m_jit.subl_i8r(1, X86::esi);
X86Assembler::JmpSrc skipTimeout = m_jit.emitUnlinkedJne();
emitCall(opcodeIndex, Machine::cti_timeout_check);
emitGetCTIParam(CTI_ARGS_exec, X86::ecx);
m_jit.movl_mr(OBJECT_OFFSET(ExecState, m_globalData), X86::ecx, X86::ecx);
m_jit.movl_mr(OBJECT_OFFSET(JSGlobalData, machine), X86::ecx, X86::ecx);
m_jit.movl_mr(OBJECT_OFFSET(Machine, m_ticksUntilNextTimeoutCheck), X86::ecx, X86::esi);
m_jit.link(skipTimeout, m_jit.label());
}
void CTI::privateCompileMainPass()
{
if (m_codeBlock->codeType == FunctionCode) {
for (int i = -m_codeBlock->numVars; i < 0; i++)
emitInitialiseRegister(i);
}
for (size_t i = 0; i < m_codeBlock->constantRegisters.size(); ++i)
emitInitialiseRegister(i);
Instruction* instruction = m_codeBlock->instructions.begin();
unsigned instructionCount = m_codeBlock->instructions.size();
unsigned structureIDInstructionIndex = 0;
for (unsigned i = 0; i < instructionCount; ) {
m_labels[i] = m_jit.label();
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(m_machine->getOpcodeID(instruction[i].u.opcode), &currentOpcodeID);
#endif
ASSERT_WITH_MESSAGE(m_machine->isOpcode(instruction[i].u.opcode), "privateCompileMainPass gone bad @ %d", i);
m_jit.emitRestoreArgumentReference();
switch (m_machine->getOpcodeID(instruction[i].u.opcode)) {
case op_mov: {
unsigned src = instruction[i + 2].u.operand;
if (src < m_codeBlock->constantRegisters.size())
m_jit.movl_i32r(reinterpret_cast<unsigned>(m_codeBlock->constantRegisters[src].jsValue(m_exec)), X86::edx);
else
emitGetArg(src, X86::edx);
emitPutResult(instruction[i + 1].u.operand, X86::edx);
i += 3;
break;
}
case op_add: {
unsigned dst = instruction[i + 1].u.operand;
unsigned src1 = instruction[i + 2].u.operand;
unsigned src2 = instruction[i + 3].u.operand;
if (src2 < m_codeBlock->constantRegisters.size()) {
JSValue* value = m_codeBlock->constantRegisters[src2].jsValue(m_exec);
if (JSImmediate::isNumber(value)) {
emitGetArg(src1, X86::eax);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
m_jit.addl_i32r(getDeTaggedConstantImmediate(value), X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
emitPutResult(dst);
i += 4;
break;
}
} else if (!(src1 < m_codeBlock->constantRegisters.size())) {
emitGetArg(src1, X86::eax);
emitGetArg(src2, X86::edx);
emitJumpSlowCaseIfNotImmNums(X86::eax, X86::edx, i);
emitFastArithDeTagImmediate(X86::eax);
m_jit.addl_rr(X86::edx, X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
emitPutResult(dst);
i += 4;
break;
}
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
emitGetPutArg(instruction[i + 3].u.operand, 4, X86::ecx);
emitCall(i, Machine::cti_op_add);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_end: {
if (m_codeBlock->needsFullScopeChain)
emitCall(i, Machine::cti_op_end);
emitGetArg(instruction[i + 1].u.operand, X86::eax);
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(-1, &currentOpcodeID);
#endif
m_jit.pushl_m(-((m_codeBlock->numLocals + RegisterFile::CallFrameHeaderSize) - RegisterFile::CTIReturnEIP) * sizeof(Register), X86::edi);
m_jit.ret();
i += 2;
break;
}
case op_jmp: {
unsigned target = instruction[i + 1].u.operand;
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJmp(), i + 1 + target));
i += 2;
break;
}
case op_pre_inc: {
int srcDst = instruction[i + 1].u.operand;
emitGetArg(srcDst, X86::eax);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
m_jit.addl_i8r(getDeTaggedConstantImmediate(JSImmediate::oneImmediate()), X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
emitPutResult(srcDst, X86::eax);
i += 2;
break;
}
case op_loop: {
emitSlowScriptCheck(i);
unsigned target = instruction[i + 1].u.operand;
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJmp(), i + 1 + target));
i += 2;
break;
}
case op_loop_if_less: {
emitSlowScriptCheck(i);
unsigned target = instruction[i + 3].u.operand;
JSValue* src2imm = getConstantImmediateNumericArg(instruction[i + 2].u.operand);
if (src2imm) {
emitGetArg(instruction[i + 1].u.operand, X86::edx);
emitJumpSlowCaseIfNotImmNum(X86::edx, i);
m_jit.cmpl_i32r(reinterpret_cast<unsigned>(src2imm), X86::edx);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJl(), i + 3 + target));
} else {
emitGetArg(instruction[i + 1].u.operand, X86::eax);
emitGetArg(instruction[i + 2].u.operand, X86::edx);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
emitJumpSlowCaseIfNotImmNum(X86::edx, i);
m_jit.cmpl_rr(X86::edx, X86::eax);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJl(), i + 3 + target));
}
i += 4;
break;
}
case op_loop_if_lesseq: {
emitSlowScriptCheck(i);
unsigned target = instruction[i + 3].u.operand;
JSValue* src2imm = getConstantImmediateNumericArg(instruction[i + 2].u.operand);
if (src2imm) {
emitGetArg(instruction[i + 1].u.operand, X86::edx);
emitJumpSlowCaseIfNotImmNum(X86::edx, i);
m_jit.cmpl_i32r(reinterpret_cast<unsigned>(src2imm), X86::edx);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJle(), i + 3 + target));
} else {
emitGetArg(instruction[i + 1].u.operand, X86::eax);
emitGetArg(instruction[i + 2].u.operand, X86::edx);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
emitJumpSlowCaseIfNotImmNum(X86::edx, i);
m_jit.cmpl_rr(X86::edx, X86::eax);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJle(), i + 3 + target));
}
i += 4;
break;
}
case op_new_object: {
emitCall(i, Machine::cti_op_new_object);
emitPutResult(instruction[i + 1].u.operand);
i += 2;
break;
}
case op_put_by_id: {
// In order to be able to repatch both the StructureID, and the object offset, we store one pointer,
// to just after the arguments have been loaded into registers 'hotPathBegin', and we generate code
// such that the StructureID & offset are always at the same distance from this.
emitGetArg(instruction[i + 1].u.operand, X86::eax);
emitGetArg(instruction[i + 3].u.operand, X86::edx);
ASSERT(m_codeBlock->structureIDInstructions[structureIDInstructionIndex].opcodeIndex == i);
X86Assembler::JmpDst hotPathBegin = m_jit.label();
m_structureStubCompilationInfo[structureIDInstructionIndex].hotPathBegin = hotPathBegin;
++structureIDInstructionIndex;
// Jump to a slow case if either the base object is an immediate, or if the StructureID does not match.
emitJumpSlowCaseIfNotJSCell(X86::eax, i);
// It is important that the following instruction plants a 32bit immediate, in order that it can be patched over.
m_jit.cmpl_i32m(repatchGetByIdDefaultStructureID, OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
ASSERT(X86Assembler::getDifferenceBetweenLabels(hotPathBegin, m_jit.label()) == repatchOffsetPutByIdStructureID);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
// Plant a load from a bogus ofset in the object's property map; we will patch this later, if it is to be used.
m_jit.movl_mr(OBJECT_OFFSET(JSObject, m_propertyStorage), X86::eax, X86::eax);
m_jit.movl_rm(X86::edx, repatchGetByIdDefaultOffset, X86::eax);
ASSERT(X86Assembler::getDifferenceBetweenLabels(hotPathBegin, m_jit.label()) == repatchOffsetPutByIdPropertyMapOffset);
i += 8;
break;
}
case op_get_by_id: {
// As for put_by_id, get_by_id requires the offset of the StructureID and the offset of the access to be repatched.
// Additionally, for get_by_id we need repatch the offset of the branch to the slow case (we repatch this to jump
// to array-length / prototype access tranpolines, and finally we also the the property-map access offset as a label
// to jump back to if one of these trampolies finds a match.
emitGetArg(instruction[i + 2].u.operand, X86::eax);
ASSERT(m_codeBlock->structureIDInstructions[structureIDInstructionIndex].opcodeIndex == i);
X86Assembler::JmpDst hotPathBegin = m_jit.label();
m_structureStubCompilationInfo[structureIDInstructionIndex].hotPathBegin = hotPathBegin;
++structureIDInstructionIndex;
emitJumpSlowCaseIfNotJSCell(X86::eax, i);
m_jit.cmpl_i32m(repatchGetByIdDefaultStructureID, OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
ASSERT(X86Assembler::getDifferenceBetweenLabels(hotPathBegin, m_jit.label()) == repatchOffsetGetByIdStructureID);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
ASSERT(X86Assembler::getDifferenceBetweenLabels(hotPathBegin, m_jit.label()) == repatchOffsetGetByIdBranchToSlowCase);
m_jit.movl_mr(OBJECT_OFFSET(JSObject, m_propertyStorage), X86::eax, X86::eax);
m_jit.movl_mr(repatchGetByIdDefaultOffset, X86::eax, X86::ecx);
ASSERT(X86Assembler::getDifferenceBetweenLabels(hotPathBegin, m_jit.label()) == repatchOffsetGetByIdPropertyMapOffset);
emitPutResult(instruction[i + 1].u.operand, X86::ecx);
i += 8;
break;
}
case op_instanceof: {
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
emitGetPutArg(instruction[i + 3].u.operand, 4, X86::ecx);
emitGetPutArg(instruction[i + 4].u.operand, 8, X86::ecx);
emitCall(i, Machine::cti_op_instanceof);
emitPutResult(instruction[i + 1].u.operand);
i += 5;
break;
}
case op_del_by_id: {
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
Identifier* ident = &(m_codeBlock->identifiers[instruction[i + 3].u.operand]);
emitPutArgConstant(reinterpret_cast<unsigned>(ident), 4);
emitCall(i, Machine::cti_op_del_by_id);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_mul: {
unsigned dst = instruction[i + 1].u.operand;
unsigned src1 = instruction[i + 2].u.operand;
unsigned src2 = instruction[i + 3].u.operand;
if (src1 < m_codeBlock->constantRegisters.size() || src2 < m_codeBlock->constantRegisters.size()) {
unsigned constant = src1;
unsigned nonconstant = src2;
if (!(src1 < m_codeBlock->constantRegisters.size())) {
constant = src2;
nonconstant = src1;
}
JSValue* value = m_codeBlock->constantRegisters[constant].jsValue(m_exec);
if (JSImmediate::isNumber(value)) {
emitGetArg(nonconstant, X86::eax);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
emitFastArithImmToInt(X86::eax);
m_jit.imull_i32r( X86::eax, getDeTaggedConstantImmediate(value), X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
emitFastArithPotentiallyReTagImmediate(X86::eax);
emitPutResult(dst);
i += 4;
break;
}
}
emitGetArg(src1, X86::eax);
emitGetArg(src2, X86::edx);
emitJumpSlowCaseIfNotImmNums(X86::eax, X86::edx, i);
emitFastArithDeTagImmediate(X86::eax);
emitFastArithImmToInt(X86::edx);
m_jit.imull_rr(X86::edx, X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
emitFastArithPotentiallyReTagImmediate(X86::eax);
emitPutResult(dst);
i += 4;
break;
}
case op_new_func: {
FuncDeclNode* func = (m_codeBlock->functions[instruction[i + 2].u.operand]).get();
emitPutArgConstant(reinterpret_cast<unsigned>(func), 0);
emitCall(i, Machine::cti_op_new_func);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_call: {
compileOpCall(instruction, i);
i += 6;
break;
}
case op_get_global_var: {
JSVariableObject* globalObject = static_cast<JSVariableObject*>(instruction[i + 2].u.jsCell);
m_jit.movl_i32r(reinterpret_cast<unsigned>(globalObject), X86::eax);
emitGetVariableObjectRegister(X86::eax, instruction[i + 3].u.operand, X86::eax);
emitPutResult(instruction[i + 1].u.operand, X86::eax);
i += 4;
break;
}
case op_put_global_var: {
JSVariableObject* globalObject = static_cast<JSVariableObject*>(instruction[i + 1].u.jsCell);
m_jit.movl_i32r(reinterpret_cast<unsigned>(globalObject), X86::eax);
emitGetArg(instruction[i + 3].u.operand, X86::edx);
emitPutVariableObjectRegister(X86::edx, X86::eax, instruction[i + 2].u.operand);
i += 4;
break;
}
case op_get_scoped_var: {
int skip = instruction[i + 3].u.operand + m_codeBlock->needsFullScopeChain;
emitGetCTIParam(CTI_ARGS_scopeChain, X86::eax);
while (skip--)
m_jit.movl_mr(OBJECT_OFFSET(ScopeChainNode, next), X86::eax, X86::eax);
m_jit.movl_mr(OBJECT_OFFSET(ScopeChainNode, object), X86::eax, X86::eax);
emitGetVariableObjectRegister(X86::eax, instruction[i + 2].u.operand, X86::eax);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_put_scoped_var: {
int skip = instruction[i + 2].u.operand + m_codeBlock->needsFullScopeChain;
emitGetCTIParam(CTI_ARGS_scopeChain, X86::edx);
emitGetArg(instruction[i + 3].u.operand, X86::eax);
while (skip--)
m_jit.movl_mr(OBJECT_OFFSET(ScopeChainNode, next), X86::edx, X86::edx);
m_jit.movl_mr(OBJECT_OFFSET(ScopeChainNode, object), X86::edx, X86::edx);
emitPutVariableObjectRegister(X86::eax, X86::edx, instruction[i + 1].u.operand);
i += 4;
break;
}
case op_ret: {
// Check for an activation - if there is one, jump to the hook below.
m_jit.cmpl_i32m(0, -(m_codeBlock->numLocals + RegisterFile::CallFrameHeaderSize - RegisterFile::OptionalCalleeActivation) * sizeof(Register), X86::edi);
X86Assembler::JmpSrc activation = m_jit.emitUnlinkedJne();
X86Assembler::JmpDst activated = m_jit.label();
// Check for a profiler - if there is one, jump to the hook below.
emitGetCTIParam(CTI_ARGS_profilerReference, X86::eax);
m_jit.cmpl_i32m(0, X86::eax);
X86Assembler::JmpSrc profile = m_jit.emitUnlinkedJne();
X86Assembler::JmpDst profiled = m_jit.label();
// We could JIT generate the deref, only calling out to C when the refcount hits zero.
if (m_codeBlock->needsFullScopeChain)
emitCall(i, Machine::cti_op_ret_scopeChain);
// Return the result in %eax, and the caller scope chain in %edx (this is read from the callee call frame,
// but is only assigned to ExecState::m_scopeChain if returning to a JSFunction).
emitGetArg(instruction[i + 1].u.operand, X86::eax);
m_jit.movl_mr(-(m_codeBlock->numLocals + RegisterFile::CallFrameHeaderSize - RegisterFile::CallerScopeChain) * sizeof(Register), X86::edi, X86::edx);
// Restore the machine return addess from the callframe, roll the callframe back to the caller callframe,
// and preserve a copy of r on the stack at CTI_ARGS_r.
m_jit.movl_mr(-(m_codeBlock->numLocals + RegisterFile::CallFrameHeaderSize - RegisterFile::CTIReturnEIP) * sizeof(Register), X86::edi, X86::ecx);
m_jit.movl_mr(-(m_codeBlock->numLocals + RegisterFile::CallFrameHeaderSize - RegisterFile::CallerRegisters) * sizeof(Register), X86::edi, X86::edi);
emitPutCTIParam(X86::edi, CTI_ARGS_r);
m_jit.pushl_r(X86::ecx);
m_jit.ret();
// Activation hook
m_jit.link(activation, m_jit.label());
emitCall(i, Machine::cti_op_ret_activation);
m_jit.link(m_jit.emitUnlinkedJmp(), activated);
// Profiling hook
m_jit.link(profile, m_jit.label());
emitCall(i, Machine::cti_op_ret_profiler);
m_jit.link(m_jit.emitUnlinkedJmp(), profiled);
i += 2;
break;
}
case op_new_array: {
m_jit.leal_mr(sizeof(Register) * instruction[i + 2].u.operand, X86::edi, X86::edx);
emitPutArg(X86::edx, 0);
emitPutArgConstant(instruction[i + 3].u.operand, 4);
emitCall(i, Machine::cti_op_new_array);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_resolve: {
Identifier* ident = &(m_codeBlock->identifiers[instruction[i + 2].u.operand]);
emitPutArgConstant(reinterpret_cast<unsigned>(ident), 0);
emitCall(i, Machine::cti_op_resolve);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_construct: {
compileOpCall(instruction, i, OpConstruct);
i += 6;
break;
}
case op_construct_verify: {
emitGetArg(instruction[i + 1].u.operand, X86::eax);
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
X86Assembler::JmpSrc isImmediate = m_jit.emitUnlinkedJne();
m_jit.movl_mr(OBJECT_OFFSET(JSCell, m_structureID), X86::eax, X86::ecx);
m_jit.cmpl_i32m(ObjectType, OBJECT_OFFSET(StructureID, m_type), X86::ecx);
X86Assembler::JmpSrc isObject = m_jit.emitUnlinkedJe();
m_jit.link(isImmediate, m_jit.label());
emitGetArg(instruction[i + 2].u.operand, X86::ecx);
emitPutResult(instruction[i + 1].u.operand, X86::ecx);
m_jit.link(isObject, m_jit.label());
i += 3;
break;
}
case op_get_by_val: {
emitGetArg(instruction[i + 2].u.operand, X86::eax);
emitGetArg(instruction[i + 3].u.operand, X86::edx);
emitJumpSlowCaseIfNotImmNum(X86::edx, i);
emitFastArithImmToInt(X86::edx);
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
m_jit.cmpl_i32m(reinterpret_cast<unsigned>(m_machine->m_jsArrayVptr), X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
// This is an array; get the m_storage pointer into ecx, then check if the index is below the fast cutoff
m_jit.movl_mr(OBJECT_OFFSET(JSArray, m_storage), X86::eax, X86::ecx);
m_jit.cmpl_rm(X86::edx, OBJECT_OFFSET(JSArray, m_fastAccessCutoff), X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJbe(), i));
// Get the value from the vector
m_jit.movl_mr(OBJECT_OFFSET(ArrayStorage, m_vector[0]), X86::ecx, X86::edx, sizeof(JSValue*), X86::eax);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_resolve_func: {
Identifier* ident = &(m_codeBlock->identifiers[instruction[i + 3].u.operand]);
emitPutArgConstant(reinterpret_cast<unsigned>(ident), 0);
emitCall(i, Machine::cti_op_resolve_func);
emitPutResult(instruction[i + 1].u.operand);
emitGetCTIParam(CTI_ARGS_2ndResult, X86::eax);
emitPutResult(instruction[i + 2].u.operand);
i += 4;
break;
}
case op_sub: {
emitGetArg(instruction[i + 2].u.operand, X86::eax);
emitGetArg(instruction[i + 3].u.operand, X86::edx);
emitJumpSlowCaseIfNotImmNums(X86::eax, X86::edx, i);
m_jit.subl_rr(X86::edx, X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
emitFastArithReTagImmediate(X86::eax);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_put_by_val: {
emitGetArg(instruction[i + 1].u.operand, X86::eax);
emitGetArg(instruction[i + 2].u.operand, X86::edx);
emitJumpSlowCaseIfNotImmNum(X86::edx, i);
emitFastArithImmToInt(X86::edx);
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
m_jit.cmpl_i32m(reinterpret_cast<unsigned>(m_machine->m_jsArrayVptr), X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
// This is an array; get the m_storage pointer into ecx, then check if the index is below the fast cutoff
m_jit.movl_mr(OBJECT_OFFSET(JSArray, m_storage), X86::eax, X86::ecx);
m_jit.cmpl_rm(X86::edx, OBJECT_OFFSET(JSArray, m_fastAccessCutoff), X86::eax);
X86Assembler::JmpSrc inFastVector = m_jit.emitUnlinkedJa();
// No; oh well, check if the access if within the vector - if so, we may still be okay.
m_jit.cmpl_rm(X86::edx, OBJECT_OFFSET(ArrayStorage, m_vectorLength), X86::ecx);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJbe(), i));
// This is a write to the slow part of the vector; first, we have to check if this would be the first write to this location.
// FIXME: should be able to handle initial write to array; increment the the number of items in the array, and potentially update fast access cutoff.
m_jit.cmpl_i8m(0, OBJECT_OFFSET(ArrayStorage, m_vector[0]), X86::ecx, X86::edx, sizeof(JSValue*));
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJe(), i));
// All good - put the value into the array.
m_jit.link(inFastVector, m_jit.label());
emitGetArg(instruction[i + 3].u.operand, X86::eax);
m_jit.movl_rm(X86::eax, OBJECT_OFFSET(ArrayStorage, m_vector[0]), X86::ecx, X86::edx, sizeof(JSValue*));
i += 4;
break;
}
CTI_COMPILE_BINARY_OP(op_lesseq)
case op_loop_if_true: {
emitSlowScriptCheck(i);
unsigned target = instruction[i + 2].u.operand;
emitGetArg(instruction[i + 1].u.operand, X86::eax);
m_jit.cmpl_i32r(reinterpret_cast<uint32_t>(JSImmediate::zeroImmediate()), X86::eax);
X86Assembler::JmpSrc isZero = m_jit.emitUnlinkedJe();
m_jit.testl_i32r(JSImmediate::TagBitTypeInteger, X86::eax);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJne(), i + 2 + target));
m_jit.cmpl_i32r(reinterpret_cast<uint32_t>(JSImmediate::trueImmediate()), X86::eax);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJe(), i + 2 + target));
m_jit.cmpl_i32r(reinterpret_cast<uint32_t>(JSImmediate::falseImmediate()), X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
m_jit.link(isZero, m_jit.label());
i += 3;
break;
};
case op_resolve_base: {
Identifier* ident = &(m_codeBlock->identifiers[instruction[i + 2].u.operand]);
emitPutArgConstant(reinterpret_cast<unsigned>(ident), 0);
emitCall(i, Machine::cti_op_resolve_base);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_negate: {
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
emitCall(i, Machine::cti_op_negate);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_resolve_skip: {
Identifier* ident = &(m_codeBlock->identifiers[instruction[i + 2].u.operand]);
emitPutArgConstant(reinterpret_cast<unsigned>(ident), 0);
emitPutArgConstant(instruction[i + 3].u.operand + m_codeBlock->needsFullScopeChain, 4);
emitCall(i, Machine::cti_op_resolve_skip);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_resolve_global: {
// Fast case
unsigned globalObject = reinterpret_cast<unsigned>(instruction[i + 2].u.jsCell);
Identifier* ident = &(m_codeBlock->identifiers[instruction[i + 3].u.operand]);
void* structureIDAddr = reinterpret_cast<void*>(instruction + i + 4);
void* offsetAddr = reinterpret_cast<void*>(instruction + i + 5);
// Check StructureID of global object
m_jit.movl_i32r(globalObject, X86::eax);
m_jit.movl_mr(structureIDAddr, X86::edx);
m_jit.cmpl_rm(X86::edx, OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
X86Assembler::JmpSrc slowCase = m_jit.emitUnlinkedJne(); // StructureIDs don't match
m_slowCases.append(SlowCaseEntry(slowCase, i));
// Load cached property
m_jit.movl_mr(OBJECT_OFFSET(JSGlobalObject, m_propertyStorage), X86::eax, X86::eax);
m_jit.movl_mr(offsetAddr, X86::edx);
m_jit.movl_mr(0, X86::eax, X86::edx, sizeof(JSValue*), X86::eax);
emitPutResult(instruction[i + 1].u.operand);
X86Assembler::JmpSrc end = m_jit.emitUnlinkedJmp();
// Slow case
m_jit.link(slowCase, m_jit.label());
emitPutArgConstant(globalObject, 0);
emitPutArgConstant(reinterpret_cast<unsigned>(ident), 4);
emitPutArgConstant(reinterpret_cast<unsigned>(instruction + i), 8);
emitCall(i, Machine::cti_op_resolve_global);
emitPutResult(instruction[i + 1].u.operand);
m_jit.link(end, m_jit.label());
i += 6;
++structureIDInstructionIndex;
break;
}
CTI_COMPILE_BINARY_OP(op_div)
case op_pre_dec: {
int srcDst = instruction[i + 1].u.operand;
emitGetArg(srcDst, X86::eax);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
m_jit.subl_i8r(getDeTaggedConstantImmediate(JSImmediate::oneImmediate()), X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
emitPutResult(srcDst, X86::eax);
i += 2;
break;
}
case op_jnless: {
unsigned target = instruction[i + 3].u.operand;
JSValue* src2imm = getConstantImmediateNumericArg(instruction[i + 2].u.operand);
if (src2imm) {
emitGetArg(instruction[i + 1].u.operand, X86::edx);
emitJumpSlowCaseIfNotImmNum(X86::edx, i);
m_jit.cmpl_i32r(reinterpret_cast<unsigned>(src2imm), X86::edx);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJge(), i + 3 + target));
} else {
emitGetArg(instruction[i + 1].u.operand, X86::eax);
emitGetArg(instruction[i + 2].u.operand, X86::edx);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
emitJumpSlowCaseIfNotImmNum(X86::edx, i);
m_jit.cmpl_rr(X86::edx, X86::eax);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJge(), i + 3 + target));
}
i += 4;
break;
}
case op_not: {
emitGetArg(instruction[i + 2].u.operand, X86::eax);
m_jit.xorl_i8r(JSImmediate::FullTagTypeBool, X86::eax);
m_jit.testl_i32r(JSImmediate::FullTagTypeMask, X86::eax); // i8?
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
m_jit.xorl_i8r((JSImmediate::FullTagTypeBool | JSImmediate::ExtendedPayloadBitBoolValue), X86::eax);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_jfalse: {
unsigned target = instruction[i + 2].u.operand;
emitGetArg(instruction[i + 1].u.operand, X86::eax);
m_jit.cmpl_i32r(reinterpret_cast<uint32_t>(JSImmediate::zeroImmediate()), X86::eax);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJe(), i + 2 + target));
m_jit.testl_i32r(JSImmediate::TagBitTypeInteger, X86::eax);
X86Assembler::JmpSrc isNonZero = m_jit.emitUnlinkedJne();
m_jit.cmpl_i32r(reinterpret_cast<uint32_t>(JSImmediate::falseImmediate()), X86::eax);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJe(), i + 2 + target));
m_jit.cmpl_i32r(reinterpret_cast<uint32_t>(JSImmediate::trueImmediate()), X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
m_jit.link(isNonZero, m_jit.label());
i += 3;
break;
};
case op_post_inc: {
int srcDst = instruction[i + 2].u.operand;
emitGetArg(srcDst, X86::eax);
m_jit.movl_rr(X86::eax, X86::edx);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
m_jit.addl_i8r(getDeTaggedConstantImmediate(JSImmediate::oneImmediate()), X86::edx);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
emitPutResult(srcDst, X86::edx);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_unexpected_load: {
JSValue* v = m_codeBlock->unexpectedConstants[instruction[i + 2].u.operand];
m_jit.movl_i32r(reinterpret_cast<unsigned>(v), X86::eax);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_jsr: {
int retAddrDst = instruction[i + 1].u.operand;
int target = instruction[i + 2].u.operand;
m_jit.movl_i32m(0, sizeof(Register) * retAddrDst, X86::edi);
X86Assembler::JmpDst addrPosition = m_jit.label();
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJmp(), i + 2 + target));
X86Assembler::JmpDst sretTarget = m_jit.label();
m_jsrSites.append(JSRInfo(addrPosition, sretTarget));
i += 3;
break;
}
case op_sret: {
m_jit.jmp_m(sizeof(Register) * instruction[i + 1].u.operand, X86::edi);
i += 2;
break;
}
case op_eq: {
emitGetArg(instruction[i + 2].u.operand, X86::eax);
emitGetArg(instruction[i + 3].u.operand, X86::edx);
emitJumpSlowCaseIfNotImmNums(X86::eax, X86::edx, i);
m_jit.cmpl_rr(X86::edx, X86::eax);
m_jit.sete_r(X86::eax);
m_jit.movzbl_rr(X86::eax, X86::eax);
emitTagAsBoolImmediate(X86::eax);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_lshift: {
emitGetArg(instruction[i + 2].u.operand, X86::eax);
emitGetArg(instruction[i + 3].u.operand, X86::ecx);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
emitJumpSlowCaseIfNotImmNum(X86::ecx, i);
emitFastArithImmToInt(X86::eax);
emitFastArithImmToInt(X86::ecx);
m_jit.shll_CLr(X86::eax);
emitFastArithIntToImmOrSlowCase(X86::eax, i);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_bitand: {
unsigned src1 = instruction[i + 2].u.operand;
unsigned src2 = instruction[i + 3].u.operand;
unsigned dst = instruction[i + 1].u.operand;
if (JSValue* value = getConstantImmediateNumericArg(src1)) {
emitGetArg(src2, X86::eax);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
m_jit.andl_i32r(reinterpret_cast<unsigned>(value), X86::eax); // FIXME: make it more obvious this is relying on the format of JSImmediate
emitPutResult(dst);
} else if (JSValue* value = getConstantImmediateNumericArg(src2)) {
emitGetArg(src1, X86::eax);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
m_jit.andl_i32r(reinterpret_cast<unsigned>(value), X86::eax);
emitPutResult(dst);
} else {
emitGetArg(src1, X86::eax);
emitGetArg(src2, X86::edx);
m_jit.andl_rr(X86::edx, X86::eax);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
emitPutResult(dst);
}
i += 4;
break;
}
case op_rshift: {
emitGetArg(instruction[i + 2].u.operand, X86::eax);
emitGetArg(instruction[i + 3].u.operand, X86::ecx);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
emitJumpSlowCaseIfNotImmNum(X86::ecx, i);
emitFastArithImmToInt(X86::ecx);
m_jit.sarl_CLr(X86::eax);
emitFastArithPotentiallyReTagImmediate(X86::eax);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_bitnot: {
emitGetArg(instruction[i + 2].u.operand, X86::eax);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
m_jit.xorl_i8r(~JSImmediate::TagBitTypeInteger, X86::eax);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_resolve_with_base: {
Identifier* ident = &(m_codeBlock->identifiers[instruction[i + 3].u.operand]);
emitPutArgConstant(reinterpret_cast<unsigned>(ident), 0);
emitCall(i, Machine::cti_op_resolve_with_base);
emitPutResult(instruction[i + 1].u.operand);
emitGetCTIParam(CTI_ARGS_2ndResult, X86::eax);
emitPutResult(instruction[i + 2].u.operand);
i += 4;
break;
}
case op_new_func_exp: {
FuncExprNode* func = (m_codeBlock->functionExpressions[instruction[i + 2].u.operand]).get();
emitPutArgConstant(reinterpret_cast<unsigned>(func), 0);
emitCall(i, Machine::cti_op_new_func_exp);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_mod: {
emitGetArg(instruction[i + 2].u.operand, X86::eax);
emitGetArg(instruction[i + 3].u.operand, X86::ecx);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
emitJumpSlowCaseIfNotImmNum(X86::ecx, i);
emitFastArithDeTagImmediate(X86::eax);
emitFastArithDeTagImmediate(X86::ecx);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJe(), i)); // This is checking if the last detag resulted in a value 0.
m_jit.cdq();
m_jit.idivl_r(X86::ecx);
emitFastArithReTagImmediate(X86::edx);
m_jit.movl_rr(X86::edx, X86::eax);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_jtrue: {
unsigned target = instruction[i + 2].u.operand;
emitGetArg(instruction[i + 1].u.operand, X86::eax);
m_jit.cmpl_i32r(reinterpret_cast<uint32_t>(JSImmediate::zeroImmediate()), X86::eax);
X86Assembler::JmpSrc isZero = m_jit.emitUnlinkedJe();
m_jit.testl_i32r(JSImmediate::TagBitTypeInteger, X86::eax);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJne(), i + 2 + target));
m_jit.cmpl_i32r(reinterpret_cast<uint32_t>(JSImmediate::trueImmediate()), X86::eax);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJe(), i + 2 + target));
m_jit.cmpl_i32r(reinterpret_cast<uint32_t>(JSImmediate::falseImmediate()), X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
m_jit.link(isZero, m_jit.label());
i += 3;
break;
}
CTI_COMPILE_BINARY_OP(op_less)
CTI_COMPILE_BINARY_OP(op_neq)
case op_post_dec: {
int srcDst = instruction[i + 2].u.operand;
emitGetArg(srcDst, X86::eax);
m_jit.movl_rr(X86::eax, X86::edx);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
m_jit.subl_i8r(getDeTaggedConstantImmediate(JSImmediate::oneImmediate()), X86::edx);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJo(), i));
emitPutResult(srcDst, X86::edx);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
CTI_COMPILE_BINARY_OP(op_urshift)
case op_bitxor: {
emitGetArg(instruction[i + 2].u.operand, X86::eax);
emitGetArg(instruction[i + 3].u.operand, X86::edx);
emitJumpSlowCaseIfNotImmNums(X86::eax, X86::edx, i);
m_jit.xorl_rr(X86::edx, X86::eax);
emitFastArithReTagImmediate(X86::eax);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_new_regexp: {
RegExp* regExp = m_codeBlock->regexps[instruction[i + 2].u.operand].get();
emitPutArgConstant(reinterpret_cast<unsigned>(regExp), 0);
emitCall(i, Machine::cti_op_new_regexp);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_bitor: {
emitGetArg(instruction[i + 2].u.operand, X86::eax);
emitGetArg(instruction[i + 3].u.operand, X86::edx);
emitJumpSlowCaseIfNotImmNums(X86::eax, X86::edx, i);
m_jit.orl_rr(X86::edx, X86::eax);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_call_eval: {
compileOpCall(instruction, i, OpCallEval);
i += 6;
break;
}
case op_throw: {
emitGetPutArg(instruction[i + 1].u.operand, 0, X86::ecx);
emitCall(i, Machine::cti_op_throw);
m_jit.addl_i8r(0x24, X86::esp);
m_jit.popl_r(X86::edi);
m_jit.popl_r(X86::esi);
m_jit.ret();
i += 2;
break;
}
case op_get_pnames: {
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
emitCall(i, Machine::cti_op_get_pnames);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_next_pname: {
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
unsigned target = instruction[i + 3].u.operand;
emitCall(i, Machine::cti_op_next_pname);
m_jit.testl_rr(X86::eax, X86::eax);
X86Assembler::JmpSrc endOfIter = m_jit.emitUnlinkedJe();
emitPutResult(instruction[i + 1].u.operand);
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJmp(), i + 3 + target));
m_jit.link(endOfIter, m_jit.label());
i += 4;
break;
}
case op_push_scope: {
emitGetPutArg(instruction[i + 1].u.operand, 0, X86::ecx);
emitCall(i, Machine::cti_op_push_scope);
i += 2;
break;
}
case op_pop_scope: {
emitCall(i, Machine::cti_op_pop_scope);
i += 1;
break;
}
CTI_COMPILE_UNARY_OP(op_typeof)
CTI_COMPILE_UNARY_OP(op_is_undefined)
CTI_COMPILE_UNARY_OP(op_is_boolean)
CTI_COMPILE_UNARY_OP(op_is_number)
CTI_COMPILE_UNARY_OP(op_is_string)
CTI_COMPILE_UNARY_OP(op_is_object)
CTI_COMPILE_UNARY_OP(op_is_function)
CTI_COMPILE_BINARY_OP(op_nstricteq)
case op_stricteq: {
unsigned dst = instruction[i + 1].u.operand;
unsigned src1 = instruction[i + 2].u.operand;
unsigned src2 = instruction[i + 3].u.operand;
emitGetArg(src1, X86::eax);
emitGetArg(src2, X86::edx);
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
X86Assembler::JmpSrc firstNotImmediate = m_jit.emitUnlinkedJe();
m_jit.testl_i32r(JSImmediate::TagMask, X86::edx);
X86Assembler::JmpSrc secondNotImmediate = m_jit.emitUnlinkedJe();
m_jit.cmpl_rr(X86::edx, X86::eax);
m_jit.sete_r(X86::eax);
m_jit.movzbl_rr(X86::eax, X86::eax);
emitTagAsBoolImmediate(X86::eax);
X86Assembler::JmpSrc bothWereImmediates = m_jit.emitUnlinkedJmp();
m_jit.link(firstNotImmediate, m_jit.label());
// check that edx is immediate but not the zero immediate
m_jit.testl_i32r(JSImmediate::TagMask, X86::edx);
m_jit.setz_r(X86::ecx);
m_jit.movzbl_rr(X86::ecx, X86::ecx); // ecx is now 1 if edx was nonimmediate
m_jit.cmpl_i32r(reinterpret_cast<uint32_t>(JSImmediate::zeroImmediate()), X86::edx);
m_jit.sete_r(X86::edx);
m_jit.movzbl_rr(X86::edx, X86::edx); // edx is now 1 if edx was the 0 immediate
m_jit.orl_rr(X86::ecx, X86::edx);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJnz(), i));
m_jit.movl_i32r(reinterpret_cast<uint32_t>(jsBoolean(false)), X86::eax);
X86Assembler::JmpSrc firstWasNotImmediate = m_jit.emitUnlinkedJmp();
m_jit.link(secondNotImmediate, m_jit.label());
// check that eax is not the zero immediate (we know it must be immediate)
m_jit.cmpl_i32r(reinterpret_cast<uint32_t>(JSImmediate::zeroImmediate()), X86::eax);
m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJe(), i));
m_jit.movl_i32r(reinterpret_cast<uint32_t>(jsBoolean(false)), X86::eax);
m_jit.link(bothWereImmediates, m_jit.label());
m_jit.link(firstWasNotImmediate, m_jit.label());
emitPutResult(dst);
i += 4;
break;
}
case op_to_jsnumber: {
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
emitCall(i, Machine::cti_op_to_jsnumber);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_in: {
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
emitGetPutArg(instruction[i + 3].u.operand, 4, X86::ecx);
emitCall(i, Machine::cti_op_in);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_push_new_scope: {
Identifier* ident = &(m_codeBlock->identifiers[instruction[i + 2].u.operand]);
emitPutArgConstant(reinterpret_cast<unsigned>(ident), 0);
emitGetPutArg(instruction[i + 3].u.operand, 4, X86::ecx);
emitCall(i, Machine::cti_op_push_new_scope);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_catch: {
emitGetCTIParam(CTI_ARGS_r, X86::edi); // edi := r
emitGetCTIParam(CTI_ARGS_exec, X86::ecx);
m_jit.movl_mr(OBJECT_OFFSET(ExecState, m_exception), X86::ecx, X86::eax);
m_jit.movl_i32m(0, OBJECT_OFFSET(ExecState, m_exception), X86::ecx);
emitPutResult(instruction[i + 1].u.operand);
i += 2;
break;
}
case op_jmp_scopes: {
unsigned count = instruction[i + 1].u.operand;
emitPutArgConstant(count, 0);
emitCall(i, Machine::cti_op_jmp_scopes);
unsigned target = instruction[i + 2].u.operand;
m_jmpTable.append(JmpTable(m_jit.emitUnlinkedJmp(), i + 2 + target));
i += 3;
break;
}
case op_put_by_index: {
emitGetPutArg(instruction[i + 1].u.operand, 0, X86::ecx);
emitPutArgConstant(instruction[i + 2].u.operand, 4);
emitGetPutArg(instruction[i + 3].u.operand, 8, X86::ecx);
emitCall(i, Machine::cti_op_put_by_index);
i += 4;
break;
}
case op_switch_imm: {
unsigned tableIndex = instruction[i + 1].u.operand;
unsigned defaultOffset = instruction[i + 2].u.operand;
unsigned scrutinee = instruction[i + 3].u.operand;
// create jump table for switch destinations, track this switch statement.
SimpleJumpTable* jumpTable = &m_codeBlock->immediateSwitchJumpTables[tableIndex];
m_switches.append(SwitchRecord(jumpTable, i, defaultOffset, SwitchRecord::Immediate));
jumpTable->ctiOffsets.grow(jumpTable->branchOffsets.size());
emitGetPutArg(scrutinee, 0, X86::ecx);
emitPutArgConstant(tableIndex, 4);
emitCall(i, Machine::cti_op_switch_imm);
m_jit.jmp_r(X86::eax);
i += 4;
break;
}
case op_switch_char: {
unsigned tableIndex = instruction[i + 1].u.operand;
unsigned defaultOffset = instruction[i + 2].u.operand;
unsigned scrutinee = instruction[i + 3].u.operand;
// create jump table for switch destinations, track this switch statement.
SimpleJumpTable* jumpTable = &m_codeBlock->characterSwitchJumpTables[tableIndex];
m_switches.append(SwitchRecord(jumpTable, i, defaultOffset, SwitchRecord::Character));
jumpTable->ctiOffsets.grow(jumpTable->branchOffsets.size());
emitGetPutArg(scrutinee, 0, X86::ecx);
emitPutArgConstant(tableIndex, 4);
emitCall(i, Machine::cti_op_switch_char);
m_jit.jmp_r(X86::eax);
i += 4;
break;
}
case op_switch_string: {
unsigned tableIndex = instruction[i + 1].u.operand;
unsigned defaultOffset = instruction[i + 2].u.operand;
unsigned scrutinee = instruction[i + 3].u.operand;
// create jump table for switch destinations, track this switch statement.
StringJumpTable* jumpTable = &m_codeBlock->stringSwitchJumpTables[tableIndex];
m_switches.append(SwitchRecord(jumpTable, i, defaultOffset));
emitGetPutArg(scrutinee, 0, X86::ecx);
emitPutArgConstant(tableIndex, 4);
emitCall(i, Machine::cti_op_switch_string);
m_jit.jmp_r(X86::eax);
i += 4;
break;
}
case op_del_by_val: {
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
emitGetPutArg(instruction[i + 3].u.operand, 4, X86::ecx);
emitCall(i, Machine::cti_op_del_by_val);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_put_getter: {
emitGetPutArg(instruction[i + 1].u.operand, 0, X86::ecx);
Identifier* ident = &(m_codeBlock->identifiers[instruction[i + 2].u.operand]);
emitPutArgConstant(reinterpret_cast<unsigned>(ident), 4);
emitGetPutArg(instruction[i + 3].u.operand, 8, X86::ecx);
emitCall(i, Machine::cti_op_put_getter);
i += 4;
break;
}
case op_put_setter: {
emitGetPutArg(instruction[i + 1].u.operand, 0, X86::ecx);
Identifier* ident = &(m_codeBlock->identifiers[instruction[i + 2].u.operand]);
emitPutArgConstant(reinterpret_cast<unsigned>(ident), 4);
emitGetPutArg(instruction[i + 3].u.operand, 8, X86::ecx);
emitCall(i, Machine::cti_op_put_setter);
i += 4;
break;
}
case op_new_error: {
JSValue* message = m_codeBlock->unexpectedConstants[instruction[i + 3].u.operand];
emitPutArgConstant(instruction[i + 2].u.operand, 0);
emitPutArgConstant(reinterpret_cast<unsigned>(message), 4);
emitPutArgConstant(m_codeBlock->lineNumberForVPC(&instruction[i]), 8);
emitCall(i, Machine::cti_op_new_error);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_debug: {
emitPutArgConstant(instruction[i + 1].u.operand, 0);
emitPutArgConstant(instruction[i + 2].u.operand, 4);
emitPutArgConstant(instruction[i + 3].u.operand, 8);
emitCall(i, Machine::cti_op_debug);
i += 4;
break;
}
case op_eq_null: {
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
emitCall(i, Machine::cti_op_eq_null);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_neq_null: {
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
emitCall(i, Machine::cti_op_neq_null);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_initialise_locals: {
i++;
break;
}
case op_get_array_length:
case op_get_by_id_chain:
case op_get_by_id_generic:
case op_get_by_id_proto:
case op_get_by_id_self:
case op_get_string_length:
case op_put_by_id_generic:
case op_put_by_id_replace:
case op_put_by_id_transition:
ASSERT_NOT_REACHED();
}
}
ASSERT(structureIDInstructionIndex == m_codeBlock->structureIDInstructions.size());
}
void CTI::privateCompileLinkPass()
{
unsigned jmpTableCount = m_jmpTable.size();
for (unsigned i = 0; i < jmpTableCount; ++i)
m_jit.link(m_jmpTable[i].from, m_labels[m_jmpTable[i].to]);
m_jmpTable.clear();
}
#define CTI_COMPILE_BINARY_OP_SLOW_CASE(name) \
case name: { \
m_jit.link(iter->from, m_jit.label()); \
emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx); \
emitGetPutArg(instruction[i + 3].u.operand, 4, X86::ecx); \
emitCall(i, Machine::cti_##name); \
emitPutResult(instruction[i + 1].u.operand); \
i += 4; \
break; \
}
void CTI::privateCompileSlowCases()
{
unsigned structureIDInstructionIndex = 0;
Instruction* instruction = m_codeBlock->instructions.begin();
for (Vector<SlowCaseEntry>::iterator iter = m_slowCases.begin(); iter != m_slowCases.end(); ++iter) {
unsigned i = iter->to;
m_jit.emitRestoreArgumentReference();
switch (m_machine->getOpcodeID(instruction[i].u.opcode)) {
case op_add: {
unsigned dst = instruction[i + 1].u.operand;
unsigned src2 = instruction[i + 3].u.operand;
if (src2 < m_codeBlock->constantRegisters.size()) {
JSValue* value = m_codeBlock->constantRegisters[src2].jsValue(m_exec);
if (JSImmediate::isNumber(value)) {
X86Assembler::JmpSrc notImm = iter->from;
m_jit.link((++iter)->from, m_jit.label());
m_jit.subl_i32r(getDeTaggedConstantImmediate(value), X86::eax);
m_jit.link(notImm, m_jit.label());
emitPutArg(X86::eax, 0);
emitGetPutArg(src2, 4, X86::ecx);
emitCall(i, Machine::cti_op_add);
emitPutResult(dst);
i += 4;
break;
}
}
ASSERT(!(static_cast<unsigned>(instruction[i + 2].u.operand) < m_codeBlock->constantRegisters.size()));
X86Assembler::JmpSrc notImm = iter->from;
m_jit.link((++iter)->from, m_jit.label());
m_jit.subl_rr(X86::edx, X86::eax);
emitFastArithReTagImmediate(X86::eax);
m_jit.link(notImm, m_jit.label());
emitPutArg(X86::eax, 0);
emitPutArg(X86::edx, 4);
emitCall(i, Machine::cti_op_add);
emitPutResult(dst);
i += 4;
break;
}
case op_get_by_val: {
// The slow case that handles accesses to arrays (below) may jump back up to here.
X86Assembler::JmpDst beginGetByValSlow = m_jit.label();
X86Assembler::JmpSrc notImm = iter->from;
m_jit.link((++iter)->from, m_jit.label());
m_jit.link((++iter)->from, m_jit.label());
emitFastArithIntToImmNoCheck(X86::edx);
m_jit.link(notImm, m_jit.label());
emitPutArg(X86::eax, 0);
emitPutArg(X86::edx, 4);
emitCall(i, Machine::cti_op_get_by_val);
emitPutResult(instruction[i + 1].u.operand);
m_jit.link(m_jit.emitUnlinkedJmp(), m_labels[i + 4]);
// This is slow case that handles accesses to arrays above the fast cut-off.
// First, check if this is an access to the vector
m_jit.link((++iter)->from, m_jit.label());
m_jit.cmpl_rm(X86::edx, OBJECT_OFFSET(ArrayStorage, m_vectorLength), X86::ecx);
m_jit.link(m_jit.emitUnlinkedJbe(), beginGetByValSlow);
// okay, missed the fast region, but it is still in the vector. Get the value.
m_jit.movl_mr(OBJECT_OFFSET(ArrayStorage, m_vector[0]), X86::ecx, X86::edx, sizeof(JSValue*), X86::ecx);
// Check whether the value loaded is zero; if so we need to return undefined.
m_jit.testl_rr(X86::ecx, X86::ecx);
m_jit.link(m_jit.emitUnlinkedJe(), beginGetByValSlow);
emitPutResult(instruction[i + 1].u.operand, X86::ecx);
i += 4;
break;
}
case op_sub: {
X86Assembler::JmpSrc notImm = iter->from;
m_jit.link((++iter)->from, m_jit.label());
m_jit.addl_rr(X86::edx, X86::eax);
m_jit.link(notImm, m_jit.label());
emitPutArg(X86::eax, 0);
emitPutArg(X86::edx, 4);
emitCall(i, Machine::cti_op_sub);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_rshift: {
m_jit.link(iter->from, m_jit.label());
m_jit.link((++iter)->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitPutArg(X86::ecx, 4);
emitCall(i, Machine::cti_op_rshift);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_lshift: {
X86Assembler::JmpSrc notImm1 = iter->from;
X86Assembler::JmpSrc notImm2 = (++iter)->from;
m_jit.link((++iter)->from, m_jit.label());
emitGetArg(instruction[i + 2].u.operand, X86::eax);
emitGetArg(instruction[i + 3].u.operand, X86::ecx);
m_jit.link(notImm1, m_jit.label());
m_jit.link(notImm2, m_jit.label());
emitPutArg(X86::eax, 0);
emitPutArg(X86::ecx, 4);
emitCall(i, Machine::cti_op_lshift);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_loop_if_less: {
emitSlowScriptCheck(i);
unsigned target = instruction[i + 3].u.operand;
JSValue* src2imm = getConstantImmediateNumericArg(instruction[i + 2].u.operand);
if (src2imm) {
m_jit.link(iter->from, m_jit.label());
emitPutArg(X86::edx, 0);
emitGetPutArg(instruction[i + 2].u.operand, 4, X86::ecx);
emitCall(i, Machine::cti_op_loop_if_less);
m_jit.testl_rr(X86::eax, X86::eax);
m_jit.link(m_jit.emitUnlinkedJne(), m_labels[i + 3 + target]);
} else {
m_jit.link(iter->from, m_jit.label());
m_jit.link((++iter)->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitPutArg(X86::edx, 4);
emitCall(i, Machine::cti_op_loop_if_less);
m_jit.testl_rr(X86::eax, X86::eax);
m_jit.link(m_jit.emitUnlinkedJne(), m_labels[i + 3 + target]);
}
i += 4;
break;
}
case op_put_by_id: {
m_jit.link(iter->from, m_jit.label());
m_jit.link((++iter)->from, m_jit.label());
Identifier* ident = &(m_codeBlock->identifiers[instruction[i + 2].u.operand]);
emitPutArgConstant(reinterpret_cast<unsigned>(ident), 4);
emitPutArg(X86::eax, 0);
emitPutArg(X86::edx, 8);
X86Assembler::JmpSrc call = emitCall(i, Machine::cti_op_put_by_id);
// Track the location of the call; this will be used to recover repatch information.
ASSERT(m_codeBlock->structureIDInstructions[structureIDInstructionIndex].opcodeIndex == i);
m_structureStubCompilationInfo[structureIDInstructionIndex].callReturnLocation = call;
++structureIDInstructionIndex;
i += 8;
break;
}
case op_get_by_id: {
// As for the hot path of get_by_id, above, we ensure that we can use an architecture specific offset
// so that we only need track one pointer into the slow case code - we track a pointer to the location
// of the call (which we can use to look up the repatch information), but should a array-length or
// prototype access tramopile fail we want to bail out back to here. To do so we can subtract back
// the distance from the call to the head of the slow case.
m_jit.link(iter->from, m_jit.label());
m_jit.link((++iter)->from, m_jit.label());
#ifndef NDEBUG
X86Assembler::JmpDst coldPathBegin = m_jit.label();
#endif
emitPutArg(X86::eax, 0);
Identifier* ident = &(m_codeBlock->identifiers[instruction[i + 3].u.operand]);
emitPutArgConstant(reinterpret_cast<unsigned>(ident), 4);
X86Assembler::JmpSrc call = emitCall(i, Machine::cti_op_get_by_id);
ASSERT(X86Assembler::getDifferenceBetweenLabels(coldPathBegin, call) == repatchOffsetGetByIdSlowCaseCall);
emitPutResult(instruction[i + 1].u.operand);
// Track the location of the call; this will be used to recover repatch information.
ASSERT(m_codeBlock->structureIDInstructions[structureIDInstructionIndex].opcodeIndex == i);
m_structureStubCompilationInfo[structureIDInstructionIndex].callReturnLocation = call;
++structureIDInstructionIndex;
i += 8;
break;
}
case op_resolve_global: {
++structureIDInstructionIndex;
i += 6;
break;
}
case op_loop_if_lesseq: {
emitSlowScriptCheck(i);
unsigned target = instruction[i + 3].u.operand;
JSValue* src2imm = getConstantImmediateNumericArg(instruction[i + 2].u.operand);
if (src2imm) {
m_jit.link(iter->from, m_jit.label());
emitPutArg(X86::edx, 0);
emitGetPutArg(instruction[i + 2].u.operand, 4, X86::ecx);
emitCall(i, Machine::cti_op_loop_if_lesseq);
m_jit.testl_rr(X86::eax, X86::eax);
m_jit.link(m_jit.emitUnlinkedJne(), m_labels[i + 3 + target]);
} else {
m_jit.link(iter->from, m_jit.label());
m_jit.link((++iter)->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitPutArg(X86::edx, 4);
emitCall(i, Machine::cti_op_loop_if_lesseq);
m_jit.testl_rr(X86::eax, X86::eax);
m_jit.link(m_jit.emitUnlinkedJne(), m_labels[i + 3 + target]);
}
i += 4;
break;
}
case op_pre_inc: {
unsigned srcDst = instruction[i + 1].u.operand;
X86Assembler::JmpSrc notImm = iter->from;
m_jit.link((++iter)->from, m_jit.label());
m_jit.subl_i8r(getDeTaggedConstantImmediate(JSImmediate::oneImmediate()), X86::eax);
m_jit.link(notImm, m_jit.label());
emitPutArg(X86::eax, 0);
emitCall(i, Machine::cti_op_pre_inc);
emitPutResult(srcDst);
i += 2;
break;
}
case op_put_by_val: {
// Normal slow cases - either is not an immediate imm, or is an array.
X86Assembler::JmpSrc notImm = iter->from;
m_jit.link((++iter)->from, m_jit.label());
m_jit.link((++iter)->from, m_jit.label());
emitFastArithIntToImmNoCheck(X86::edx);
m_jit.link(notImm, m_jit.label());
emitGetArg(instruction[i + 3].u.operand, X86::ecx);
emitPutArg(X86::eax, 0);
emitPutArg(X86::edx, 4);
emitPutArg(X86::ecx, 8);
emitCall(i, Machine::cti_op_put_by_val);
m_jit.link(m_jit.emitUnlinkedJmp(), m_labels[i + 4]);
// slow cases for immediate int accesses to arrays
m_jit.link((++iter)->from, m_jit.label());
m_jit.link((++iter)->from, m_jit.label());
emitGetArg(instruction[i + 3].u.operand, X86::ecx);
emitPutArg(X86::eax, 0);
emitPutArg(X86::edx, 4);
emitPutArg(X86::ecx, 8);
emitCall(i, Machine::cti_op_put_by_val_array);
i += 4;
break;
}
case op_loop_if_true: {
emitSlowScriptCheck(i);
m_jit.link(iter->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitCall(i, Machine::cti_op_jtrue);
m_jit.testl_rr(X86::eax, X86::eax);
unsigned target = instruction[i + 2].u.operand;
m_jit.link(m_jit.emitUnlinkedJne(), m_labels[i + 2 + target]);
i += 3;
break;
}
case op_pre_dec: {
unsigned srcDst = instruction[i + 1].u.operand;
X86Assembler::JmpSrc notImm = iter->from;
m_jit.link((++iter)->from, m_jit.label());
m_jit.addl_i8r(getDeTaggedConstantImmediate(JSImmediate::oneImmediate()), X86::eax);
m_jit.link(notImm, m_jit.label());
emitPutArg(X86::eax, 0);
emitCall(i, Machine::cti_op_pre_dec);
emitPutResult(srcDst);
i += 2;
break;
}
case op_jnless: {
unsigned target = instruction[i + 3].u.operand;
JSValue* src2imm = getConstantImmediateNumericArg(instruction[i + 2].u.operand);
if (src2imm) {
m_jit.link(iter->from, m_jit.label());
emitPutArg(X86::edx, 0);
emitGetPutArg(instruction[i + 2].u.operand, 4, X86::ecx);
emitCall(i, Machine::cti_op_jless);
m_jit.testl_rr(X86::eax, X86::eax);
m_jit.link(m_jit.emitUnlinkedJe(), m_labels[i + 3 + target]);
} else {
m_jit.link(iter->from, m_jit.label());
m_jit.link((++iter)->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitPutArg(X86::edx, 4);
emitCall(i, Machine::cti_op_jless);
m_jit.testl_rr(X86::eax, X86::eax);
m_jit.link(m_jit.emitUnlinkedJe(), m_labels[i + 3 + target]);
}
i += 4;
break;
}
case op_not: {
m_jit.link(iter->from, m_jit.label());
m_jit.xorl_i8r(JSImmediate::FullTagTypeBool, X86::eax);
emitPutArg(X86::eax, 0);
emitCall(i, Machine::cti_op_not);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_jfalse: {
m_jit.link(iter->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitCall(i, Machine::cti_op_jtrue);
m_jit.testl_rr(X86::eax, X86::eax);
unsigned target = instruction[i + 2].u.operand;
m_jit.link(m_jit.emitUnlinkedJe(), m_labels[i + 2 + target]); // inverted!
i += 3;
break;
}
case op_post_inc: {
unsigned srcDst = instruction[i + 2].u.operand;
m_jit.link(iter->from, m_jit.label());
m_jit.link((++iter)->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitCall(i, Machine::cti_op_post_inc);
emitPutResult(instruction[i + 1].u.operand);
emitGetCTIParam(CTI_ARGS_2ndResult, X86::eax);
emitPutResult(srcDst);
i += 3;
break;
}
case op_bitnot: {
m_jit.link(iter->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitCall(i, Machine::cti_op_bitnot);
emitPutResult(instruction[i + 1].u.operand);
i += 3;
break;
}
case op_bitand: {
unsigned src1 = instruction[i + 2].u.operand;
unsigned src2 = instruction[i + 3].u.operand;
unsigned dst = instruction[i + 1].u.operand;
if (getConstantImmediateNumericArg(src1)) {
m_jit.link(iter->from, m_jit.label());
emitGetPutArg(src1, 0, X86::ecx);
emitPutArg(X86::eax, 4);
emitCall(i, Machine::cti_op_bitand);
emitPutResult(dst);
} else if (getConstantImmediateNumericArg(src2)) {
m_jit.link(iter->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitGetPutArg(src2, 4, X86::ecx);
emitCall(i, Machine::cti_op_bitand);
emitPutResult(dst);
} else {
m_jit.link(iter->from, m_jit.label());
emitGetPutArg(src1, 0, X86::ecx);
emitPutArg(X86::edx, 4);
emitCall(i, Machine::cti_op_bitand);
emitPutResult(dst);
}
i += 4;
break;
}
case op_jtrue: {
m_jit.link(iter->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitCall(i, Machine::cti_op_jtrue);
m_jit.testl_rr(X86::eax, X86::eax);
unsigned target = instruction[i + 2].u.operand;
m_jit.link(m_jit.emitUnlinkedJne(), m_labels[i + 2 + target]);
i += 3;
break;
}
case op_post_dec: {
unsigned srcDst = instruction[i + 2].u.operand;
m_jit.link(iter->from, m_jit.label());
m_jit.link((++iter)->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitCall(i, Machine::cti_op_post_dec);
emitPutResult(instruction[i + 1].u.operand);
emitGetCTIParam(CTI_ARGS_2ndResult, X86::eax);
emitPutResult(srcDst);
i += 3;
break;
}
case op_bitxor: {
m_jit.link(iter->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitPutArg(X86::edx, 4);
emitCall(i, Machine::cti_op_bitxor);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_bitor: {
m_jit.link(iter->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitPutArg(X86::edx, 4);
emitCall(i, Machine::cti_op_bitor);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
case op_eq:
m_jit.link(iter->from, m_jit.label());
emitPutArg(X86::eax, 0);
emitPutArg(X86::edx, 4);
emitCall(i, Machine::cti_op_eq);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
CTI_COMPILE_BINARY_OP_SLOW_CASE(op_stricteq);
case op_mod: {
X86Assembler::JmpSrc notImm1 = iter->from;
X86Assembler::JmpSrc notImm2 = (++iter)->from;
m_jit.link((++iter)->from, m_jit.label());
emitFastArithReTagImmediate(X86::eax);
emitFastArithReTagImmediate(X86::ecx);
m_jit.link(notImm1, m_jit.label());
m_jit.link(notImm2, m_jit.label());
emitPutArg(X86::eax, 0);
emitPutArg(X86::ecx, 4);
emitCall(i, Machine::cti_op_mod);
emitPutResult(instruction[i + 1].u.operand);
i += 4;
break;
}
CTI_COMPILE_BINARY_OP_SLOW_CASE(op_mul);
case op_call:
case op_call_eval:
case op_construct: {
m_jit.link(iter->from, m_jit.label());
m_jit.emitRestoreArgumentReference();
// We jump to this slow case if the ctiCode for the codeBlock has not yet been generated; compile it now.
emitCall(i, Machine::cti_vm_compile);
m_jit.call_r(X86::eax);
// Instead of checking for 0 we could initialize the CodeBlock::ctiCode to point to a trampoline that would trigger the translation.
// In the interpreter the following actions are performed by op_ret:
// Store the scope chain - returned by op_ret in %edx (see below) - to ExecState::m_scopeChain and CTI_ARGS_scopeChain on the stack.
emitGetCTIParam(CTI_ARGS_exec, X86::ecx);
emitPutCTIParam(X86::edx, CTI_ARGS_scopeChain);
m_jit.movl_rm(X86::edx, OBJECT_OFFSET(ExecState, m_scopeChain), X86::ecx);
// Restore ExecState::m_callFrame.
m_jit.leal_mr(-(m_codeBlock->numLocals + RegisterFile::CallFrameHeaderSize) * sizeof(Register), X86::edi, X86::edx);
m_jit.movl_rm(X86::edx, OBJECT_OFFSET(ExecState, m_callFrame), X86::ecx);
// Restore CTI_ARGS_codeBlock.
emitPutCTIParam(m_codeBlock, CTI_ARGS_codeBlock);
emitPutResult(instruction[i + 1].u.operand);
i += 6;
break;
}
default:
ASSERT_NOT_REACHED();
break;
}
m_jit.link(m_jit.emitUnlinkedJmp(), m_labels[i]);
}
ASSERT(structureIDInstructionIndex == m_codeBlock->structureIDInstructions.size());
}
void CTI::privateCompile()
{
// Could use a popl_m, but would need to offset the following instruction if so.
m_jit.popl_r(X86::ecx);
emitGetCTIParam(CTI_ARGS_r, X86::edi); // edi := r
emitPutToCallFrameHeader(X86::ecx, RegisterFile::CTIReturnEIP);
// Lazy copy of the scopeChain
X86Assembler::JmpSrc callToUpdateScopeChain;
if ((m_codeBlock->codeType == FunctionCode) && m_codeBlock->needsFullScopeChain) {
m_jit.emitRestoreArgumentReference();
callToUpdateScopeChain = m_jit.emitCall();
}
privateCompileMainPass();
privateCompileLinkPass();
privateCompileSlowCases();
ASSERT(m_jmpTable.isEmpty());
void* code = m_jit.copy();
ASSERT(code);
// Translate vPC offsets into addresses in JIT generated code, for switch tables.
for (unsigned i = 0; i < m_switches.size(); ++i) {
SwitchRecord record = m_switches[i];
unsigned opcodeIndex = record.m_opcodeIndex;
if (record.m_type != SwitchRecord::String) {
ASSERT(record.m_type == SwitchRecord::Immediate || record.m_type == SwitchRecord::Character);
ASSERT(record.m_jumpTable.m_simpleJumpTable->branchOffsets.size() == record.m_jumpTable.m_simpleJumpTable->ctiOffsets.size());
record.m_jumpTable.m_simpleJumpTable->ctiDefault = m_jit.getRelocatedAddress(code, m_labels[opcodeIndex + 3 + record.m_defaultOffset]);
for (unsigned j = 0; j < record.m_jumpTable.m_simpleJumpTable->branchOffsets.size(); ++j) {
unsigned offset = record.m_jumpTable.m_simpleJumpTable->branchOffsets[j];
record.m_jumpTable.m_simpleJumpTable->ctiOffsets[j] = offset ? m_jit.getRelocatedAddress(code, m_labels[opcodeIndex + 3 + offset]) : record.m_jumpTable.m_simpleJumpTable->ctiDefault;
}
} else {
ASSERT(record.m_type == SwitchRecord::String);
record.m_jumpTable.m_stringJumpTable->ctiDefault = m_jit.getRelocatedAddress(code, m_labels[opcodeIndex + 3 + record.m_defaultOffset]);
StringJumpTable::StringOffsetTable::iterator end = record.m_jumpTable.m_stringJumpTable->offsetTable.end();
for (StringJumpTable::StringOffsetTable::iterator it = record.m_jumpTable.m_stringJumpTable->offsetTable.begin(); it != end; ++it) {
unsigned offset = it->second.branchOffset;
it->second.ctiOffset = offset ? m_jit.getRelocatedAddress(code, m_labels[opcodeIndex + 3 + offset]) : record.m_jumpTable.m_stringJumpTable->ctiDefault;
}
}
}
for (Vector<HandlerInfo>::iterator iter = m_codeBlock->exceptionHandlers.begin(); iter != m_codeBlock->exceptionHandlers.end(); ++iter)
iter->nativeCode = m_jit.getRelocatedAddress(code, m_labels[iter->target]);
// FIXME: There doesn't seem to be a way to hint to a hashmap that it should make a certain capacity available;
// could be faster if we could do something like this:
// m_codeBlock->ctiReturnAddressVPCMap.grow(m_calls.size());
for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
X86Assembler::link(code, iter->from, iter->to);
m_codeBlock->ctiReturnAddressVPCMap.add(m_jit.getRelocatedAddress(code, iter->from), iter->opcodeIndex);
}
if ((m_codeBlock->codeType == FunctionCode) && m_codeBlock->needsFullScopeChain)
X86Assembler::link(code, callToUpdateScopeChain, (void*)Machine::cti_vm_updateScopeChain);
// Link absolute addresses for jsr
for (Vector<JSRInfo>::iterator iter = m_jsrSites.begin(); iter != m_jsrSites.end(); ++iter)
X86Assembler::linkAbsoluteAddress(code, iter->addrPosition, iter->target);
for (unsigned i = 0; i < m_codeBlock->structureIDInstructions.size(); ++i) {
StructureStubInfo& info = m_codeBlock->structureIDInstructions[i];
info.callReturnLocation = X86Assembler::getRelocatedAddress(code, m_structureStubCompilationInfo[i].callReturnLocation);
info.hotPathBegin = X86Assembler::getRelocatedAddress(code, m_structureStubCompilationInfo[i].hotPathBegin);
}
m_codeBlock->ctiCode = code;
}
void CTI::privateCompileGetByIdSelf(StructureID* structureID, size_t cachedOffset, void* returnAddress)
{
// Check eax is an object of the right StructureID.
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
X86Assembler::JmpSrc failureCases1 = m_jit.emitUnlinkedJne();
m_jit.cmpl_i32m(reinterpret_cast<uint32_t>(structureID), OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
X86Assembler::JmpSrc failureCases2 = m_jit.emitUnlinkedJne();
// Checks out okay! - getDirectOffset
m_jit.movl_mr(OBJECT_OFFSET(JSObject, m_propertyStorage), X86::eax, X86::eax);
m_jit.movl_mr(cachedOffset * sizeof(JSValue*), X86::eax, X86::eax);
m_jit.ret();
void* code = m_jit.copy();
ASSERT(code);
X86Assembler::link(code, failureCases1, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
X86Assembler::link(code, failureCases2, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
m_codeBlock->getStubInfo(returnAddress).stubRoutine = code;
ctiRepatchCallByReturnAddress(returnAddress, code);
}
void CTI::privateCompileGetByIdProto(StructureID* structureID, StructureID* prototypeStructureID, size_t cachedOffset, void* returnAddress)
{
#if USE(CTI_REPATCH_PIC)
StructureStubInfo& info = m_codeBlock->getStubInfo(returnAddress);
// We don't want to repatch more than once - in future go to cti_op_put_by_id_generic.
ctiRepatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
// The prototype object definitely exists (if this stub exists the CodeBlock is referencing a StructureID that is
// referencing the prototype object - let's speculatively load it's table nice and early!)
JSObject* protoObject = static_cast<JSObject*>(structureID->prototypeForLookup(m_exec));
PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage;
m_jit.movl_mr(static_cast<void*>(protoPropertyStorage), X86::edx);
// check eax is an object of the right StructureID.
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
X86Assembler::JmpSrc failureCases1 = m_jit.emitUnlinkedJne();
m_jit.cmpl_i32m(reinterpret_cast<uint32_t>(structureID), OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
X86Assembler::JmpSrc failureCases2 = m_jit.emitUnlinkedJne();
// Check the prototype object's StructureID had not changed.
StructureID** protoStructureIDAddress = &(protoObject->m_structureID);
m_jit.cmpl_i32m(reinterpret_cast<uint32_t>(prototypeStructureID), static_cast<void*>(protoStructureIDAddress));
X86Assembler::JmpSrc failureCases3 = m_jit.emitUnlinkedJne();
// Checks out okay! - getDirectOffset
m_jit.movl_mr(cachedOffset * sizeof(JSValue*), X86::edx, X86::ecx);
X86Assembler::JmpSrc success = m_jit.emitUnlinkedJmp();
void* code = m_jit.copy();
ASSERT(code);
// Use the repatch information to link the failure cases back to the original slow case routine.
void* slowCaseBegin = reinterpret_cast<char*>(info.callReturnLocation) - repatchOffsetGetByIdSlowCaseCall;
X86Assembler::link(code, failureCases1, slowCaseBegin);
X86Assembler::link(code, failureCases2, slowCaseBegin);
X86Assembler::link(code, failureCases3, slowCaseBegin);
// On success return back to the hot patch code, at a point it will perform the store to dest for us.
intptr_t successDest = (intptr_t)(info.hotPathBegin) + repatchOffsetGetByIdPropertyMapOffset;
X86Assembler::link(code, success, reinterpret_cast<void*>(successDest));
// Track the stub we have created so that it will be deleted later.
m_codeBlock->getStubInfo(returnAddress).stubRoutine = code;
// Finally repatch the jump to sow case back in the hot path to jump here instead.
// FIXME: should revert this repatching, on failure.
intptr_t jmpLocation = reinterpret_cast<intptr_t>(info.hotPathBegin) + repatchOffsetGetByIdBranchToSlowCase;
X86Assembler::repatchBranchOffset(jmpLocation, code);
#else
// The prototype object definitely exists (if this stub exists the CodeBlock is referencing a StructureID that is
// referencing the prototype object - let's speculatively load it's table nice and early!)
JSObject* protoObject = static_cast<JSObject*>(structureID->prototypeForLookup(m_exec));
PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage;
m_jit.movl_mr(static_cast<void*>(protoPropertyStorage), X86::edx);
// check eax is an object of the right StructureID.
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
X86Assembler::JmpSrc failureCases1 = m_jit.emitUnlinkedJne();
m_jit.cmpl_i32m(reinterpret_cast<uint32_t>(structureID), OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
X86Assembler::JmpSrc failureCases2 = m_jit.emitUnlinkedJne();
// Check the prototype object's StructureID had not changed.
StructureID** protoStructureIDAddress = &(protoObject->m_structureID);
m_jit.cmpl_i32m(reinterpret_cast<uint32_t>(prototypeStructureID), static_cast<void*>(protoStructureIDAddress));
X86Assembler::JmpSrc failureCases3 = m_jit.emitUnlinkedJne();
// Checks out okay! - getDirectOffset
m_jit.movl_mr(cachedOffset * sizeof(JSValue*), X86::edx, X86::eax);
m_jit.ret();
void* code = m_jit.copy();
ASSERT(code);
X86Assembler::link(code, failureCases1, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
X86Assembler::link(code, failureCases2, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
X86Assembler::link(code, failureCases3, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
m_codeBlock->getStubInfo(returnAddress).stubRoutine = code;
ctiRepatchCallByReturnAddress(returnAddress, code);
#endif
}
void CTI::privateCompileGetByIdChain(StructureID* structureID, StructureIDChain* chain, size_t count, size_t cachedOffset, void* returnAddress)
{
ASSERT(count);
Vector<X86Assembler::JmpSrc> bucketsOfFail;
// Check eax is an object of the right StructureID.
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
bucketsOfFail.append(m_jit.emitUnlinkedJne());
m_jit.cmpl_i32m(reinterpret_cast<uint32_t>(structureID), OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
bucketsOfFail.append(m_jit.emitUnlinkedJne());
StructureID* currStructureID = structureID;
RefPtr<StructureID>* chainEntries = chain->head();
JSObject* protoObject = 0;
for (unsigned i = 0; i<count; ++i) {
protoObject = static_cast<JSObject*>(currStructureID->prototypeForLookup(m_exec));
currStructureID = chainEntries[i].get();
// Check the prototype object's StructureID had not changed.
StructureID** protoStructureIDAddress = &(protoObject->m_structureID);
m_jit.cmpl_i32m(reinterpret_cast<uint32_t>(currStructureID), static_cast<void*>(protoStructureIDAddress));
bucketsOfFail.append(m_jit.emitUnlinkedJne());
}
ASSERT(protoObject);
PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage;
m_jit.movl_mr(static_cast<void*>(protoPropertyStorage), X86::edx);
m_jit.movl_mr(cachedOffset * sizeof(JSValue*), X86::edx, X86::eax);
m_jit.ret();
bucketsOfFail.append(m_jit.emitUnlinkedJmp());
void* code = m_jit.copy();
ASSERT(code);
for (unsigned i = 0; i < bucketsOfFail.size(); ++i)
X86Assembler::link(code, bucketsOfFail[i], reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
m_codeBlock->getStubInfo(returnAddress).stubRoutine = code;
ctiRepatchCallByReturnAddress(returnAddress, code);
}
void CTI::privateCompilePutByIdReplace(StructureID* structureID, size_t cachedOffset, void* returnAddress)
{
// check eax is an object of the right StructureID.
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
X86Assembler::JmpSrc failureCases1 = m_jit.emitUnlinkedJne();
m_jit.cmpl_i32m(reinterpret_cast<uint32_t>(structureID), OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
X86Assembler::JmpSrc failureCases2 = m_jit.emitUnlinkedJne();
// checks out okay! - putDirectOffset
m_jit.movl_mr(OBJECT_OFFSET(JSObject, m_propertyStorage), X86::eax, X86::eax);
m_jit.movl_rm(X86::edx, cachedOffset * sizeof(JSValue*), X86::eax);
m_jit.ret();
void* code = m_jit.copy();
ASSERT(code);
X86Assembler::link(code, failureCases1, reinterpret_cast<void*>(Machine::cti_op_put_by_id_fail));
X86Assembler::link(code, failureCases2, reinterpret_cast<void*>(Machine::cti_op_put_by_id_fail));
m_codeBlock->getStubInfo(returnAddress).stubRoutine = code;
ctiRepatchCallByReturnAddress(returnAddress, code);
}
extern "C" {
static JSValue* SFX_CALL transitionObject(StructureID* newStructureID, size_t cachedOffset, JSObject* baseObject, JSValue* value)
{
StructureID* oldStructureID = newStructureID->previousID();
baseObject->transitionTo(newStructureID);
if (oldStructureID->propertyMap().storageSize() == JSObject::inlineStorageCapacity)
baseObject->allocatePropertyStorage(oldStructureID->propertyMap().storageSize(), oldStructureID->propertyMap().size());
baseObject->putDirectOffset(cachedOffset, value);
return baseObject;
}
}
static inline bool transitionWillNeedStorageRealloc(StructureID* oldStructureID, StructureID* newStructureID)
{
if (oldStructureID->propertyMap().storageSize() == JSObject::inlineStorageCapacity)
return true;
if (oldStructureID->propertyMap().storageSize() < JSObject::inlineStorageCapacity)
return false;
if (oldStructureID->propertyMap().size() != newStructureID->propertyMap().size())
return true;
return false;
}
void CTI::privateCompilePutByIdTransition(StructureID* oldStructureID, StructureID* newStructureID, size_t cachedOffset, StructureIDChain* sIDC, void* returnAddress)
{
Vector<X86Assembler::JmpSrc, 16> failureCases;
// check eax is an object of the right StructureID.
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
failureCases.append(m_jit.emitUnlinkedJne());
m_jit.cmpl_i32m(reinterpret_cast<uint32_t>(oldStructureID), OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
failureCases.append(m_jit.emitUnlinkedJne());
Vector<X86Assembler::JmpSrc> successCases;
// ecx = baseObject
m_jit.movl_mr(OBJECT_OFFSET(JSCell, m_structureID), X86::eax, X86::ecx);
// proto(ecx) = baseObject->structureID()->prototype()
m_jit.cmpl_i32m(ObjectType, OBJECT_OFFSET(StructureID, m_type), X86::ecx);
failureCases.append(m_jit.emitUnlinkedJne());
m_jit.movl_mr(OBJECT_OFFSET(StructureID, m_prototype), X86::ecx, X86::ecx);
// ecx = baseObject->m_structureID
for (RefPtr<StructureID>* it = sIDC->head(); *it; ++it) {
// null check the prototype
m_jit.cmpl_i32r(reinterpret_cast<intptr_t> (jsNull()), X86::ecx);
successCases.append(m_jit.emitUnlinkedJe());
// Check the structure id
m_jit.cmpl_i32m(reinterpret_cast<uint32_t>(it->get()), OBJECT_OFFSET(JSCell, m_structureID), X86::ecx);
failureCases.append(m_jit.emitUnlinkedJne());
m_jit.movl_mr(OBJECT_OFFSET(JSCell, m_structureID), X86::ecx, X86::ecx);
m_jit.cmpl_i32m(ObjectType, OBJECT_OFFSET(StructureID, m_type), X86::ecx);
failureCases.append(m_jit.emitUnlinkedJne());
m_jit.movl_mr(OBJECT_OFFSET(StructureID, m_prototype), X86::ecx, X86::ecx);
}
failureCases.append(m_jit.emitUnlinkedJne());
for (unsigned i = 0; i < successCases.size(); ++i)
m_jit.link(successCases[i], m_jit.label());
X86Assembler::JmpSrc callTarget;
// Fast case, don't need to do any heavy lifting, so don't bother making a call.
if (!transitionWillNeedStorageRealloc(oldStructureID, newStructureID)) {
// Assumes m_refCount can be decremented easily, refcount decrement is safe as
// codeblock should ensure oldStructureID->m_refCount > 0
m_jit.subl_i8m(1, reinterpret_cast<void*>(oldStructureID));
m_jit.addl_i8m(1, reinterpret_cast<void*>(newStructureID));
m_jit.movl_i32m(reinterpret_cast<uint32_t>(newStructureID), OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
// write the value
m_jit.movl_mr(OBJECT_OFFSET(JSObject, m_propertyStorage), X86::eax, X86::eax);
m_jit.movl_rm(X86::edx, cachedOffset * sizeof(JSValue*), X86::eax);
} else {
// Slow case transition -- we're going to need to quite a bit of work,
// so just make a call
m_jit.pushl_r(X86::edx);
m_jit.pushl_r(X86::eax);
m_jit.movl_i32r(cachedOffset, X86::eax);
m_jit.pushl_r(X86::eax);
m_jit.movl_i32r(reinterpret_cast<uint32_t>(newStructureID), X86::eax);
m_jit.pushl_r(X86::eax);
callTarget = m_jit.emitCall();
m_jit.addl_i32r(4 * sizeof(void*), X86::esp);
}
m_jit.ret();
void* code = m_jit.copy();
ASSERT(code);
for (unsigned i = 0; i < failureCases.size(); ++i)
X86Assembler::link(code, failureCases[i], reinterpret_cast<void*>(Machine::cti_op_put_by_id_fail));
if (transitionWillNeedStorageRealloc(oldStructureID, newStructureID))
X86Assembler::link(code, callTarget, reinterpret_cast<void*>(transitionObject));
m_codeBlock->getStubInfo(returnAddress).stubRoutine = code;
ctiRepatchCallByReturnAddress(returnAddress, code);
}
void* CTI::privateCompileArrayLengthTrampoline()
{
// Check eax is an array
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
X86Assembler::JmpSrc failureCases1 = m_jit.emitUnlinkedJne();
m_jit.cmpl_i32m(reinterpret_cast<unsigned>(m_machine->m_jsArrayVptr), X86::eax);
X86Assembler::JmpSrc failureCases2 = m_jit.emitUnlinkedJne();
// Checks out okay! - get the length from the storage
m_jit.movl_mr(OBJECT_OFFSET(JSArray, m_storage), X86::eax, X86::eax);
m_jit.movl_mr(OBJECT_OFFSET(ArrayStorage, m_length), X86::eax, X86::eax);
m_jit.addl_rr(X86::eax, X86::eax);
X86Assembler::JmpSrc failureCases3 = m_jit.emitUnlinkedJo();
m_jit.addl_i8r(1, X86::eax);
m_jit.ret();
void* code = m_jit.copy();
ASSERT(code);
X86Assembler::link(code, failureCases1, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
X86Assembler::link(code, failureCases2, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
X86Assembler::link(code, failureCases3, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
return code;
}
void* CTI::privateCompileStringLengthTrampoline()
{
// Check eax is a string
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
X86Assembler::JmpSrc failureCases1 = m_jit.emitUnlinkedJne();
m_jit.cmpl_i32m(reinterpret_cast<unsigned>(m_machine->m_jsStringVptr), X86::eax);
X86Assembler::JmpSrc failureCases2 = m_jit.emitUnlinkedJne();
// Checks out okay! - get the length from the Ustring.
m_jit.movl_mr(OBJECT_OFFSET(JSString, m_value) + OBJECT_OFFSET(UString, m_rep), X86::eax, X86::eax);
m_jit.movl_mr(OBJECT_OFFSET(UString::Rep, len), X86::eax, X86::eax);
m_jit.addl_rr(X86::eax, X86::eax);
X86Assembler::JmpSrc failureCases3 = m_jit.emitUnlinkedJo();
m_jit.addl_i8r(1, X86::eax);
m_jit.ret();
void* code = m_jit.copy();
ASSERT(code);
X86Assembler::link(code, failureCases1, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
X86Assembler::link(code, failureCases2, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
X86Assembler::link(code, failureCases3, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
return code;
}
void CTI::patchGetByIdSelf(CodeBlock* codeBlock, StructureID* structureID, size_t cachedOffset, void* returnAddress)
{
StructureStubInfo& info = codeBlock->getStubInfo(returnAddress);
// We don't want to repatch more than once - in future go to cti_op_get_by_id_generic.
// Should probably go to Machine::cti_op_get_by_id_fail, but that doesn't do anything interesting right now.
ctiRepatchCallByReturnAddress(returnAddress, (void*)(Machine::cti_op_get_by_id_generic));
// Repatch the offset into the propoerty map to load from, then repatch the StructureID to look for.
X86Assembler::repatchDisplacement(reinterpret_cast<intptr_t>(info.hotPathBegin) + repatchOffsetGetByIdPropertyMapOffset, cachedOffset * sizeof(JSValue*));
X86Assembler::repatchImmediate(reinterpret_cast<intptr_t>(info.hotPathBegin) + repatchOffsetGetByIdStructureID, reinterpret_cast<uint32_t>(structureID));
}
void CTI::patchPutByIdReplace(CodeBlock* codeBlock, StructureID* structureID, size_t cachedOffset, void* returnAddress)
{
StructureStubInfo& info = codeBlock->getStubInfo(returnAddress);
// We don't want to repatch more than once - in future go to cti_op_put_by_id_generic.
// Should probably go to Machine::cti_op_put_by_id_fail, but that doesn't do anything interesting right now.
ctiRepatchCallByReturnAddress(returnAddress, (void*)(Machine::cti_op_put_by_id_generic));
// Repatch the offset into the propoerty map to load from, then repatch the StructureID to look for.
X86Assembler::repatchDisplacement(reinterpret_cast<intptr_t>(info.hotPathBegin) + repatchOffsetPutByIdPropertyMapOffset, cachedOffset * sizeof(JSValue*));
X86Assembler::repatchImmediate(reinterpret_cast<intptr_t>(info.hotPathBegin) + repatchOffsetPutByIdStructureID, reinterpret_cast<uint32_t>(structureID));
}
void CTI::privateCompilePatchGetArrayLength(void* returnAddress)
{
StructureStubInfo& info = m_codeBlock->getStubInfo(returnAddress);
// We don't want to repatch more than once - in future go to cti_op_put_by_id_generic.
ctiRepatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(Machine::cti_op_get_by_id_fail));
// Check eax is an array
m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
X86Assembler::JmpSrc failureCases1 = m_jit.emitUnlinkedJne();
m_jit.cmpl_i32m(reinterpret_cast<unsigned>(m_machine->m_jsArrayVptr), X86::eax);
X86Assembler::JmpSrc failureCases2 = m_jit.emitUnlinkedJne();
// Checks out okay! - get the length from the storage
m_jit.movl_mr(OBJECT_OFFSET(JSArray, m_storage), X86::eax, X86::ecx);
m_jit.movl_mr(OBJECT_OFFSET(ArrayStorage, m_length), X86::ecx, X86::ecx);
m_jit.addl_rr(X86::ecx, X86::ecx);
X86Assembler::JmpSrc failureCases3 = m_jit.emitUnlinkedJo();
m_jit.addl_i8r(1, X86::ecx);
X86Assembler::JmpSrc success = m_jit.emitUnlinkedJmp();
void* code = m_jit.copy();
ASSERT(code);
// Use the repatch information to link the failure cases back to the original slow case routine.
void* slowCaseBegin = reinterpret_cast<char*>(info.callReturnLocation) - repatchOffsetGetByIdSlowCaseCall;
X86Assembler::link(code, failureCases1, slowCaseBegin);
X86Assembler::link(code, failureCases2, slowCaseBegin);
X86Assembler::link(code, failureCases3, slowCaseBegin);
// On success return back to the hot patch code, at a point it will perform the store to dest for us.
intptr_t successDest = (intptr_t)(info.hotPathBegin) + repatchOffsetGetByIdPropertyMapOffset;
X86Assembler::link(code, success, reinterpret_cast<void*>(successDest));
// Track the stub we have created so that it will be deleted later.
m_codeBlock->getStubInfo(returnAddress).stubRoutine = code;
// Finally repatch the jump to sow case back in the hot path to jump here instead.
// FIXME: should revert this repatching, on failure.
intptr_t jmpLocation = reinterpret_cast<intptr_t>(info.hotPathBegin) + repatchOffsetGetByIdBranchToSlowCase;
X86Assembler::repatchBranchOffset(jmpLocation, code);
}
void CTI::emitGetVariableObjectRegister(X86Assembler::RegisterID variableObject, int index, X86Assembler::RegisterID dst)
{
m_jit.movl_mr(JSVariableObject::offsetOf_d(), variableObject, dst);
m_jit.movl_mr(JSVariableObject::offsetOf_Data_registers(), dst, dst);
m_jit.movl_mr(index * sizeof(Register), dst, dst);
}
void CTI::emitPutVariableObjectRegister(X86Assembler::RegisterID src, X86Assembler::RegisterID variableObject, int index)
{
m_jit.movl_mr(JSVariableObject::offsetOf_d(), variableObject, variableObject);
m_jit.movl_mr(JSVariableObject::offsetOf_Data_registers(), variableObject, variableObject);
m_jit.movl_rm(src, index * sizeof(Register), variableObject);
}
#if ENABLE(WREC)
void* CTI::compileRegExp(ExecState* exec, const UString& pattern, unsigned* numSubpatterns_ptr, const char** error_ptr, bool ignoreCase, bool multiline)
{
// TODO: better error messages
if (pattern.size() > MaxPatternSize) {
*error_ptr = "regular expression too large";
return 0;
}
X86Assembler jit(exec->machine()->jitCodeBuffer());
WRECParser parser(pattern, ignoreCase, multiline, jit);
jit.emitConvertToFastCall();
// (0) Setup:
// Preserve regs & initialize outputRegister.
jit.pushl_r(WRECGenerator::outputRegister);
jit.pushl_r(WRECGenerator::currentValueRegister);
// push pos onto the stack, both to preserve and as a parameter available to parseDisjunction
jit.pushl_r(WRECGenerator::currentPositionRegister);
// load output pointer
jit.movl_mr(16
#if COMPILER(MSVC)
+ 3 * sizeof(void*)
#endif
, X86::esp, WRECGenerator::outputRegister);
// restart point on match fail.
WRECGenerator::JmpDst nextLabel = jit.label();
// (1) Parse Disjunction:
// Parsing the disjunction should fully consume the pattern.
JmpSrcVector failures;
parser.parseDisjunction(failures);
if (parser.isEndOfPattern()) {
parser.m_err = WRECParser::Error_malformedPattern;
}
if (parser.m_err) {
// TODO: better error messages
*error_ptr = "TODO: better error messages";
return 0;
}
// (2) Success:
// Set return value & pop registers from the stack.
jit.testl_rr(WRECGenerator::outputRegister, WRECGenerator::outputRegister);
WRECGenerator::JmpSrc noOutput = jit.emitUnlinkedJe();
jit.movl_rm(WRECGenerator::currentPositionRegister, 4, WRECGenerator::outputRegister);
jit.popl_r(X86::eax);
jit.movl_rm(X86::eax, WRECGenerator::outputRegister);
jit.popl_r(WRECGenerator::currentValueRegister);
jit.popl_r(WRECGenerator::outputRegister);
jit.ret();
jit.link(noOutput, jit.label());
jit.popl_r(X86::eax);
jit.movl_rm(X86::eax, WRECGenerator::outputRegister);
jit.popl_r(WRECGenerator::currentValueRegister);
jit.popl_r(WRECGenerator::outputRegister);
jit.ret();
// (3) Failure:
// All fails link to here. Progress the start point & if it is within scope, loop.
// Otherwise, return fail value.
WRECGenerator::JmpDst here = jit.label();
for (unsigned i = 0; i < failures.size(); ++i)
jit.link(failures[i], here);
failures.clear();
jit.movl_mr(X86::esp, WRECGenerator::currentPositionRegister);
jit.addl_i8r(1, WRECGenerator::currentPositionRegister);
jit.movl_rm(WRECGenerator::currentPositionRegister, X86::esp);
jit.cmpl_rr(WRECGenerator::lengthRegister, WRECGenerator::currentPositionRegister);
jit.link(jit.emitUnlinkedJle(), nextLabel);
jit.addl_i8r(4, X86::esp);
jit.movl_i32r(-1, X86::eax);
jit.popl_r(WRECGenerator::currentValueRegister);
jit.popl_r(WRECGenerator::outputRegister);
jit.ret();
*numSubpatterns_ptr = parser.m_numSubpatterns;
void* code = jit.copy();
ASSERT(code);
return code;
}
#endif // ENABLE(WREC)
} // namespace JSC
#endif // ENABLE(CTI)