+2008-09-23 Geoffrey Garen <ggaren@apple.com>
+
+ Reviewed by Darin Adler.
+
+ Changed the layout of the call frame from
+
+ { header, parameters, locals | constants, temporaries }
+
+ to
+
+ { parameters, header | locals, constants, temporaries }
+
+ This simplifies function entry+exit, and enables a number of future
+ optimizations.
+
+ 13.5% speedup on empty call benchmark for bytecode; 23.6% speedup on
+ empty call benchmark for CTI.
+
+ SunSpider says no change. SunSpider --v8 says 1% faster.
+
+ * VM/CTI.cpp:
+
+ Added a bit of abstraction for calculating whether a register is a
+ constant, since this patch changes that calculation:
+ (JSC::CTI::isConstant):
+ (JSC::CTI::getConstant):
+ (JSC::CTI::emitGetArg):
+ (JSC::CTI::emitGetPutArg):
+ (JSC::CTI::getConstantImmediateNumericArg):
+
+ Updated for changes to callframe header location:
+ (JSC::CTI::emitPutToCallFrameHeader):
+ (JSC::CTI::emitGetFromCallFrameHeader):
+ (JSC::CTI::printOpcodeOperandTypes):
+
+ Renamed to spite Oliver:
+ (JSC::CTI::emitInitRegister):
+
+ Added an abstraction for emitting a call through a register, so that
+ calls through registers generate exception info, too:
+ (JSC::CTI::emitCall):
+
+ Updated to match the new callframe header layout, and to support calls
+ through registers, which have no destination address:
+ (JSC::CTI::compileOpCall):
+ (JSC::CTI::privateCompileMainPass):
+ (JSC::CTI::privateCompileSlowCases):
+ (JSC::CTI::privateCompile):
+
+ * VM/CTI.h:
+
+ More of the above:
+ (JSC::CallRecord::CallRecord):
+
+ * VM/CodeBlock.cpp:
+
+ Updated for new register layout:
+ (JSC::registerName):
+ (JSC::CodeBlock::dump):
+
+ * VM/CodeBlock.h:
+
+ Updated CodeBlock to track slightly different information about the
+ register frame, and tweaked the style of an ASSERT_NOT_REACHED.
+ (JSC::CodeBlock::CodeBlock):
+ (JSC::CodeBlock::getStubInfo):
+
+ * VM/CodeGenerator.cpp:
+
+ Added some abstraction around constant register allocation, since this
+ patch changes it, changed codegen to account for the new callframe
+ layout, and added abstraction around register fetching code
+ that used to assume that all local registers lived at negative indices,
+ since vars now live at positive indices:
+ (JSC::CodeGenerator::generate):
+ (JSC::CodeGenerator::addVar):
+ (JSC::CodeGenerator::addGlobalVar):
+ (JSC::CodeGenerator::allocateConstants):
+ (JSC::CodeGenerator::CodeGenerator):
+ (JSC::CodeGenerator::addParameter):
+ (JSC::CodeGenerator::registerFor):
+ (JSC::CodeGenerator::constRegisterFor):
+ (JSC::CodeGenerator::newRegister):
+ (JSC::CodeGenerator::newTemporary):
+ (JSC::CodeGenerator::highestUsedRegister):
+ (JSC::CodeGenerator::addConstant):
+
+ ASSERT that our caller referenced the registers it passed to us.
+ Otherwise, we might overwrite them with parameters:
+ (JSC::CodeGenerator::emitCall):
+ (JSC::CodeGenerator::emitConstruct):
+
+ * VM/CodeGenerator.h:
+
+ Added some abstraction for getting a RegisterID for a given index,
+ since the rules are a little weird:
+ (JSC::CodeGenerator::registerFor):
+
+ * VM/Machine.cpp:
+
+ Utility function to transform a machine return PC to a virtual machine
+ return VPC, for the sake of stack unwinding, since both PCs are stored
+ in the same location now:
+ (JSC::vPCForPC):
+
+ Tweaked to account for new call frame:
+ (JSC::Machine::initializeCallFrame):
+
+ Tweaked to account for registerOffset supplied by caller:
+ (JSC::slideRegisterWindowForCall):
+
+ Tweaked to account for new register layout:
+ (JSC::scopeChainForCall):
+ (JSC::Machine::callEval):
+ (JSC::Machine::dumpRegisters):
+ (JSC::Machine::unwindCallFrame):
+ (JSC::Machine::execute):
+
+ Changed op_call and op_construct to implement the new calling convention:
+ (JSC::Machine::privateExecute):
+
+ Tweaked to account for the new register layout:
+ (JSC::Machine::retrieveArguments):
+ (JSC::Machine::retrieveCaller):
+ (JSC::Machine::retrieveLastCaller):
+ (JSC::Machine::callFrame):
+ (JSC::Machine::getArgumentsData):
+
+ Changed CTI call helpers to implement the new calling convention:
+ (JSC::Machine::cti_op_call_JSFunction):
+ (JSC::Machine::cti_op_call_NotJSFunction):
+ (JSC::Machine::cti_op_ret_activation):
+ (JSC::Machine::cti_op_ret_profiler):
+ (JSC::Machine::cti_op_construct_JSConstruct):
+ (JSC::Machine::cti_op_construct_NotJSConstruct):
+ (JSC::Machine::cti_op_call_eval):
+
+ * VM/Machine.h:
+
+ * VM/Opcode.h:
+
+ Renamed op_initialise_locals to op_init, because this opcode
+ doesn't initialize all locals, and it doesn't initialize only locals.
+ Also, to spite Oliver.
+
+ * VM/RegisterFile.h:
+
+ New call frame enumeration values:
+ (JSC::RegisterFile::):
+
+ Simplified the calculation of whether a RegisterID is a temporary,
+ since we can no longer assume that all positive non-constant registers
+ are temporaries:
+ * VM/RegisterID.h:
+ (JSC::RegisterID::RegisterID):
+ (JSC::RegisterID::setTemporary):
+ (JSC::RegisterID::isTemporary):
+
+ Renamed firstArgumentIndex to firstParameterIndex because the assumption
+ that this variable pertained to the actual arguments supplied by the
+ caller caused me to write some buggy code:
+ * kjs/Arguments.cpp:
+ (JSC::ArgumentsData::ArgumentsData):
+ (JSC::Arguments::Arguments):
+ (JSC::Arguments::fillArgList):
+ (JSC::Arguments::getOwnPropertySlot):
+ (JSC::Arguments::put):
+
+ Updated for new call frame layout:
+ * kjs/DebuggerCallFrame.cpp:
+ (JSC::DebuggerCallFrame::functionName):
+ (JSC::DebuggerCallFrame::type):
+ * kjs/DebuggerCallFrame.h:
+
+ Changed the activation object to account for the fact that a call frame
+ header now sits between parameters and local variables. This change
+ requires all variable objects to do their own marking, since they
+ now use their register storage differently:
+ * kjs/JSActivation.cpp:
+ (JSC::JSActivation::mark):
+ (JSC::JSActivation::copyRegisters):
+ (JSC::JSActivation::createArgumentsObject):
+ * kjs/JSActivation.h:
+
+ Updated global object to use the new interfaces required by the change
+ to JSActivation above:
+ * kjs/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::reset):
+ (JSC::JSGlobalObject::mark):
+ (JSC::JSGlobalObject::copyGlobalsFrom):
+ (JSC::JSGlobalObject::copyGlobalsTo):
+ * kjs/JSGlobalObject.h:
+ (JSC::JSGlobalObject::addStaticGlobals):
+
+ Updated static scope object to use the new interfaces required by the
+ change to JSActivation above:
+ * kjs/JSStaticScopeObject.cpp:
+ (JSC::JSStaticScopeObject::mark):
+ (JSC::JSStaticScopeObject::~JSStaticScopeObject):
+ * kjs/JSStaticScopeObject.h:
+ (JSC::JSStaticScopeObject::JSStaticScopeObject):
+ (JSC::JSStaticScopeObject::d):
+
+ Updated variable object to use the new interfaces required by the
+ change to JSActivation above:
+ * kjs/JSVariableObject.cpp:
+ (JSC::JSVariableObject::copyRegisterArray):
+ (JSC::JSVariableObject::setRegisters):
+ * kjs/JSVariableObject.h:
+
+ Changed the bit twiddling in symbol table not to assume that all indices
+ are negative, since they can be positive now:
+ * kjs/SymbolTable.h:
+ (JSC::SymbolTableEntry::SymbolTableEntry):
+ (JSC::SymbolTableEntry::isNull):
+ (JSC::SymbolTableEntry::getIndex):
+ (JSC::SymbolTableEntry::getAttributes):
+ (JSC::SymbolTableEntry::setAttributes):
+ (JSC::SymbolTableEntry::isReadOnly):
+ (JSC::SymbolTableEntry::pack):
+ (JSC::SymbolTableEntry::isValidIndex):
+
+ Changed call and construct nodes to ref their functions and/or bases,
+ so that emitCall/emitConstruct doesn't overwrite them with parameters.
+ Also, updated for rename to registerFor:
+ * kjs/nodes.cpp:
+ (JSC::ResolveNode::emitCode):
+ (JSC::NewExprNode::emitCode):
+ (JSC::EvalFunctionCallNode::emitCode):
+ (JSC::FunctionCallValueNode::emitCode):
+ (JSC::FunctionCallResolveNode::emitCode):
+ (JSC::FunctionCallBracketNode::emitCode):
+ (JSC::FunctionCallDotNode::emitCode):
+ (JSC::PostfixResolveNode::emitCode):
+ (JSC::DeleteResolveNode::emitCode):
+ (JSC::TypeOfResolveNode::emitCode):
+ (JSC::PrefixResolveNode::emitCode):
+ (JSC::ReadModifyResolveNode::emitCode):
+ (JSC::AssignResolveNode::emitCode):
+ (JSC::ConstDeclNode::emitCodeSingle):
+ (JSC::ForInNode::emitCode):
+
+ Added abstraction for getting exception info out of a call through a
+ register:
+ * masm/X86Assembler.h:
+ (JSC::X86Assembler::emitCall):
+
+ Removed duplicate #if:
+ * wtf/Platform.h:
+
2008-09-23 Kevin McCullough <kmccullough@apple.com>
Reviewed by Darin.
__ZN3JSC15JSWrapperObject4markEv
__ZN3JSC16InternalFunction4infoE
__ZN3JSC16InternalFunctionC2EPNS_9ExecStateEN3WTF10PassRefPtrINS_11StructureIDEEERKNS_10IdentifierE
+__ZN3JSC16JSVariableObject12setRegistersEPNS_8RegisterES2_m
__ZN3JSC16JSVariableObject14deletePropertyEPNS_9ExecStateERKNS_10IdentifierE
__ZN3JSC16JSVariableObject16getPropertyNamesEPNS_9ExecStateERNS_17PropertyNameArrayE
-__ZN3JSC16JSVariableObject16setRegisterArrayEPNS_8RegisterEm
__ZN3JSC16ParserRefCounted3refEv
__ZN3JSC16ParserRefCounted5derefEv
__ZN3JSC17PropertyNameArray3addEPNS_7UString3RepE
#endif
+ALWAYS_INLINE bool CTI::isConstant(int src)
+{
+ return src >= m_codeBlock->numVars && src < m_codeBlock->numVars + m_codeBlock->numConstants;
+}
+
+ALWAYS_INLINE JSValue* CTI::getConstant(ExecState* exec, int src)
+{
+ return m_codeBlock->constantRegisters[src - m_codeBlock->numVars].jsValue(exec);
+}
+
// 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);
+ if (isConstant(src)) {
+ JSValue* js = getConstant(m_exec, src);
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);
+ if (isConstant(src)) {
+ JSValue* js = getConstant(m_exec, src);
m_jit.movl_i32m(reinterpret_cast<unsigned>(js), offset + sizeof(void*), X86::esp);
} else {
m_jit.movl_mr(src * sizeof(Register), X86::edi, scratch);
ALWAYS_INLINE JSValue* CTI::getConstantImmediateNumericArg(unsigned src)
{
- if (src < m_codeBlock->constantRegisters.size()) {
- JSValue* js = m_codeBlock->constantRegisters[src].jsValue(m_exec);
+ if (isConstant(src)) {
+ JSValue* js = getConstant(m_exec, src);
return JSImmediate::isNumber(js) ? js : 0;
}
return 0;
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);
+ m_jit.movl_rm(from, 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);
+ m_jit.movl_mr(entry * sizeof(Register), X86::edi, to);
}
ALWAYS_INLINE void CTI::emitPutResult(unsigned dst, X86Assembler::RegisterID from)
// FIXME: #ifndef NDEBUG, Write the correct m_type to the register.
}
-ALWAYS_INLINE void CTI::emitInitialiseRegister(unsigned dst)
+ALWAYS_INLINE void CTI::emitInitRegister(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.
void CTI::printOpcodeOperandTypes(unsigned src1, unsigned src2)
{
char which1 = '*';
- if (src1 < m_codeBlock->constantRegisters.size()) {
- JSValue* js = m_codeBlock->constantRegisters[src1].jsValue(m_exec);
+ if (isConstant(src1)) {
+ JSValue* js = getConstant(m_exec, src1);
which1 =
JSImmediate::isImmediate(js) ?
(JSImmediate::isNumber(js) ? 'i' :
'k');
}
char which2 = '*';
- if (src2 < m_codeBlock->constantRegisters.size()) {
- JSValue* js = m_codeBlock->constantRegisters[src2].jsValue(m_exec);
+ if (isConstant(src2)) {
+ JSValue* js = getConstant(m_exec, src2);
which2 =
JSImmediate::isImmediate(js) ?
(JSImmediate::isNumber(js) ? 'i' :
#endif
+ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitCall(unsigned opcodeIndex, X86::RegisterID r)
+{
+ X86Assembler::JmpSrc call = m_jit.emitCall(r);
+ m_calls.append(CallRecord(call, opcodeIndex));
+ emitDebugExceptionCheck();
+
+ return call;
+}
+
ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitCall(unsigned opcodeIndex, CTIHelper_j helper)
{
#if ENABLE(SAMPLING_TOOL)
int dst = instruction[i + 1].u.operand;
int firstArg = instruction[i + 4].u.operand;
int argCount = instruction[i + 5].u.operand;
+ int registerOffset = instruction[i + 6].u.operand;
+
+ if (type == OpCallEval)
+ emitGetPutArg(instruction[i + 3].u.operand, 16, X86::ecx);
if (type == OpConstruct) {
- emitPutArgConstant(reinterpret_cast<unsigned>(instruction + i), 16);
- emitPutArgConstant(argCount, 12);
+ emitPutArgConstant(reinterpret_cast<unsigned>(instruction + i), 20);
+ emitPutArgConstant(argCount, 16);
+ emitPutArgConstant(registerOffset, 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?
+ emitPutArgConstant(reinterpret_cast<unsigned>(instruction + i), 12);
+ emitPutArgConstant(argCount, 8);
+ emitPutArgConstant(registerOffset, 4);
+
int thisVal = instruction[i + 3].u.operand;
if (thisVal == missingThisObjectMarker()) {
- emitPutArgConstant(reinterpret_cast<unsigned>(m_exec->globalThisValue()), 4);
- } else
- emitGetPutArg(thisVal, 4, X86::ecx);
+ // FIXME: should this be loaded dynamically off m_exec?
+ m_jit.movl_i32m(reinterpret_cast<unsigned>(m_exec->globalThisValue()), firstArg * sizeof(Register), X86::edi);
+ } else {
+ emitGetArg(thisVal, X86::ecx);
+ emitPutResult(firstArg, X86::ecx);
+ }
}
X86Assembler::JmpSrc wasEval;
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).
+
+ // this sets up the first arg to op_cti_call (func), and explicitly leaves the value in 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);
+ // this sets up the first arg to op_cti_call (func), and explicitly leaves the value in ecx (checked just below).
+ emitGetPutArg(instruction[i + 2].u.operand, 0, X86::ecx);
}
- // 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();
// 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));
+
+ // Initialize the parts of the call frame that have not already been initialized.
+ emitGetCTIParam(CTI_ARGS_r, X86::edi);
+ m_jit.movl_i32m(reinterpret_cast<unsigned>(m_codeBlock), RegisterFile::CallerCodeBlock * sizeof(Register), X86::edi);
+ m_jit.movl_i32m(dst, RegisterFile::ReturnValueRegister * sizeof(Register), X86::edi);
+
// 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);
+ emitCall(i, 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.
+
+ // Restore ExecState::m_scopeChain and CTI_ARGS_scopeChain. NOTE: After
+ // op_ret, %edx holds the caller's scope chain.
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);
+ m_jit.movl_rm(X86::edi, OBJECT_OFFSET(ExecState, m_callFrame), X86::ecx);
// Restore CTI_ARGS_codeBlock.
emitPutCTIParam(m_codeBlock, CTI_ARGS_codeBlock);
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();
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);
+ if (isConstant(src))
+ m_jit.movl_i32r(reinterpret_cast<unsigned>(getConstant(m_exec, src)), X86::edx);
else
emitGetArg(src, X86::edx);
emitPutResult(instruction[i + 1].u.operand, X86::edx);
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 (isConstant(src2)) {
+ JSValue* value = getConstant(m_exec, src2);
if (JSImmediate::isNumber(value)) {
emitGetArg(src1, X86::eax);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
i += 4;
break;
}
- } else if (!(src1 < m_codeBlock->constantRegisters.size())) {
+ } else if (!isConstant(src1)) {
emitGetArg(src1, X86::eax);
emitGetArg(src2, X86::edx);
emitJumpSlowCaseIfNotImmNums(X86::eax, X86::edx, i);
#if ENABLE(SAMPLING_TOOL)
m_jit.movl_i32m(-1, ¤tOpcodeID);
#endif
- m_jit.pushl_m(-((m_codeBlock->numLocals + RegisterFile::CallFrameHeaderSize) - RegisterFile::CTIReturnEIP) * sizeof(Register), X86::edi);
+ m_jit.pushl_m(RegisterFile::ReturnPC * sizeof(Register), X86::edi);
m_jit.ret();
i += 2;
break;
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()) {
+ if (isConstant(src1) || isConstant(src2)) {
unsigned constant = src1;
unsigned nonconstant = src2;
- if (!(src1 < m_codeBlock->constantRegisters.size())) {
+ if (!isConstant(src1)) {
constant = src2;
nonconstant = src1;
}
- JSValue* value = m_codeBlock->constantRegisters[constant].jsValue(m_exec);
+ JSValue* value = getConstant(m_exec, constant);
if (JSImmediate::isNumber(value)) {
emitGetArg(nonconstant, X86::eax);
emitJumpSlowCaseIfNotImmNum(X86::eax, i);
}
case op_call: {
compileOpCall(instruction, i);
- i += 6;
+ i += 7;
break;
}
case op_get_global_var: {
}
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);
+ m_jit.cmpl_i32m(0, RegisterFile::OptionalCalleeActivation * sizeof(Register), X86::edi);
X86Assembler::JmpSrc activation = m_jit.emitUnlinkedJne();
X86Assembler::JmpDst activated = m_jit.label();
// 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);
+ m_jit.movl_mr(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);
+ m_jit.movl_mr(RegisterFile::ReturnPC * sizeof(Register), X86::edi, X86::ecx);
+ m_jit.movl_mr(RegisterFile::CallerRegisters * sizeof(Register), X86::edi, X86::edi);
emitPutCTIParam(X86::edi, CTI_ARGS_r);
m_jit.pushl_r(X86::ecx);
}
case op_construct: {
compileOpCall(instruction, i, OpConstruct);
- i += 6;
+ i += 7;
break;
}
case op_construct_verify: {
}
case op_call_eval: {
compileOpCall(instruction, i, OpCallEval);
- i += 6;
+ i += 7;
break;
}
case op_throw: {
i += 3;
break;
}
- case op_initialise_locals: {
- i++;
+ case op_init: {
+ // Even though CTI doesn't use them, we initialize our constant
+ // registers to zap stale pointers, to avoid unnecessarily prolonging
+ // object lifetime and increasing GC pressure.
+ size_t count = m_codeBlock->numVars + m_codeBlock->constantRegisters.size();
+ for (size_t j = 0; j < count; ++j)
+ emitInitRegister(j);
+
+ i+= 1;
break;
}
case op_get_array_length:
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 (isConstant(src2)) {
+ JSValue* value = getConstant(m_exec, src2);
if (JSImmediate::isNumber(value)) {
X86Assembler::JmpSrc notImm = iter->from;
m_jit.link((++iter)->from, m_jit.label());
}
}
- ASSERT(!(static_cast<unsigned>(instruction[i + 2].u.operand) < m_codeBlock->constantRegisters.size()));
+ ASSERT(!isConstant(instruction[i + 2].u.operand));
X86Assembler::JmpSrc notImm = iter->from;
m_jit.link((++iter)->from, m_jit.label());
// 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);
-
+ emitCall(i, 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.
+
+ // Restore ExecState::m_scopeChain and CTI_ARGS_scopeChain. NOTE: After
+ // op_ret, %edx holds the caller's scope chain.
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);
+ m_jit.movl_rm(X86::edi, 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;
+ i += 7;
break;
}
// 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);
+ emitPutToCallFrameHeader(X86::ecx, RegisterFile::ReturnPC);
// Lazy copy of the scopeChain
X86Assembler::JmpSrc callToUpdateScopeChain;
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);
+ if (iter->to)
+ X86Assembler::link(code, iter->from, iter->to);
m_codeBlock->ctiReturnAddressVPCMap.add(m_jit.getRelocatedAddress(code, iter->from), iter->opcodeIndex);
}
#define ARG_src2 ((JSValue*)((ARGS)[2]))
#define ARG_src3 ((JSValue*)((ARGS)[3]))
#define ARG_src4 ((JSValue*)((ARGS)[4]))
+#define ARG_src5 ((JSValue*)((ARGS)[5]))
#define ARG_id1 ((Identifier*)((ARGS)[1]))
#define ARG_id2 ((Identifier*)((ARGS)[2]))
#define ARG_id3 ((Identifier*)((ARGS)[3]))
#define ARG_int2 ((int)((ARGS)[2]))
#define ARG_int3 ((int)((ARGS)[3]))
#define ARG_int4 ((int)((ARGS)[4]))
+#define ARG_int5 ((int)((ARGS)[5]))
#define ARG_func1 ((FuncDeclNode*)((ARGS)[1]))
#define ARG_funcexp1 ((FuncExprNode*)((ARGS)[1]))
#define ARG_registers1 ((Register*)((ARGS)[1]))
#define ARG_instr3 ((Instruction*)((ARGS)[3]))
#define ARG_instr4 ((Instruction*)((ARGS)[4]))
#define ARG_instr5 ((Instruction*)((ARGS)[5]))
+#define ARG_instr6 ((Instruction*)((ARGS)[6]))
#define CTI_RETURN_ADDRESS ((ARGS)[-1])
, opcodeIndex(i)
{
}
+
+ CallRecord(X86Assembler::JmpSrc f, unsigned i)
+ : from(f)
+ , to(0)
+ , opcodeIndex(i)
+ {
+ }
};
struct JmpTable {
private:
CTI(Machine*, ExecState*, CodeBlock*);
+
+ bool isConstant(int src);
+ JSValue* CTI::getConstant(ExecState*, int src);
+
void privateCompileMainPass();
void privateCompileLinkPass();
void privateCompileSlowCases();
void emitPutArgConstant(unsigned value, unsigned offset);
void emitPutResult(unsigned dst, X86Assembler::RegisterID from = X86::eax);
- void emitInitialiseRegister(unsigned dst);
+ void emitInitRegister(unsigned dst);
void emitPutCTIParam(void* value, unsigned name);
void emitPutCTIParam(X86Assembler::RegisterID from, unsigned name);
void emitDebugExceptionCheck();
+ X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, X86::RegisterID);
X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, CTIHelper_j);
X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, CTIHelper_p);
X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, CTIHelper_b);
static CString registerName(int r)
{
- if (r < 0)
- return (UString("lr") + UString::from(-r)).UTF8String();
-
if (r == missingThisObjectMarker())
return "<null>";
- return (UString("tr") + UString::from(r)).UTF8String();
+ return (UString("r") + UString::from(r)).UTF8String();
}
static CString constantName(ExecState* exec, int k, JSValue* value)
if (exec->machine()->isOpcode(it->u.opcode))
++instructionCount;
- printf("%lu instructions; %lu bytes at %p; %d locals (%d parameters); %d temporaries\n\n",
+ printf("%lu instructions; %lu bytes at %p; %d parameter(s); %d callee register(s)\n\n",
static_cast<unsigned long>(instructionCount),
static_cast<unsigned long>(instructions.size() * sizeof(Instruction)),
- this, numLocals, numParameters, numTemporaries);
+ this, numParameters, numCalleeRegisters);
for (Vector<Instruction>::const_iterator it = begin; it != end; ++it)
dump(exec, begin, it);
printf("\nConstants:\n");
size_t i = 0;
do {
- printf(" tr%u = %s\n", static_cast<unsigned>(i), valueToSourceString(exec, constantRegisters[i].jsValue(exec)).ascii());
+ printf(" r%u = %s\n", static_cast<unsigned>(i), valueToSourceString(exec, constantRegisters[i].jsValue(exec)).ascii());
++i;
} while (i < constantRegisters.size());
}
{
int location = it - begin;
switch (exec->machine()->getOpcodeID(it->u.opcode)) {
- case op_initialise_locals: {
- printf("[%4d] initialise_locals\n", location);
+ case op_init: {
+ printf("[%4d] init\n", location);
break;
}
case op_unexpected_load: {
int r2 = (++it)->u.operand;
int tempCount = (++it)->u.operand;
int argCount = (++it)->u.operand;
- printf("[%4d] call\t\t %s, %s, %s, %d, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), tempCount, argCount);
+ int registerOffset = (++it)->u.operand;
+ printf("[%4d] call\t\t %s, %s, %s, %d, %d, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), tempCount, argCount, registerOffset);
break;
}
case op_call_eval: {
int r2 = (++it)->u.operand;
int tempCount = (++it)->u.operand;
int argCount = (++it)->u.operand;
- printf("[%4d] call_eval\t\t %s, %s, %s, %d, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), tempCount, argCount);
+ int registerOffset = (++it)->u.operand;
+ printf("[%4d] call_eval\t\t %s, %s, %s, %d, %d, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), tempCount, argCount, registerOffset);
break;
}
case op_ret: {
int r2 = (++it)->u.operand;
int tempCount = (++it)->u.operand;
int argCount = (++it)->u.operand;
- printf("[%4d] construct\t %s, %s, %s, %d, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), tempCount, argCount);
+ int registerOffset = (++it)->u.operand;
+ printf("[%4d] construct\t %s, %s, %s, %d, %d, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), tempCount, argCount, registerOffset);
break;
}
case op_construct_verify: {
#if ENABLE(CTI)
, ctiCode(0)
#endif
- , numTemporaries(0)
+ , numCalleeRegisters(0)
+ , numConstants(0)
, numVars(0)
, numParameters(0)
- , numLocals(0)
, needsFullScopeChain(ownerNode_->usesEval() || ownerNode_->needsClosure())
, usesEval(ownerNode_->usesEval())
, codeType(codeType_)
StructureStubInfo& getStubInfo(void* returnAddress)
{
// FIXME: would a binary chop be faster here?
- for (unsigned i = 0; i < structureIDInstructions.size(); ++i) {
+ for (unsigned i = 0; ; ++i) {
if (structureIDInstructions[i].callReturnLocation == returnAddress)
return structureIDInstructions[i];
}
-
- ASSERT_NOT_REACHED();
- // keep the compiler happy.
- static StructureStubInfo duff(0);
- return duff;
}
ScopeNode* ownerNode;
void* ctiCode;
#endif
+ int numCalleeRegisters;
+
+ // NOTE: numConstants holds the number of constant registers allocated
+ // by the code generator, not the number of constant registers used.
+ // (Duplicate constants are uniqued during code generation, and spare
+ // constant registers may be allocated.)
int numConstants;
- int numTemporaries;
int numVars;
int numParameters;
- int numLocals;
int thisRegister;
bool needsFullScopeChain;
bool usesEval;
void CodeGenerator::generate()
{
- m_codeBlock->numLocals = m_codeBlock->numVars + m_codeBlock->numParameters;
m_codeBlock->thisRegister = m_thisRegister.index();
+
if (m_shouldEmitDebugHooks)
m_codeBlock->needsFullScopeChain = true;
bool CodeGenerator::addVar(const Identifier& ident, bool isConstant, RegisterID*& r0)
{
- int index = m_nextVar;
+ int index = m_calleeRegisters.size();
SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0);
pair<SymbolTable::iterator, bool> result = symbolTable().add(ident.ustring().rep(), newEntry);
- if (!result.second)
- index = result.first->second.getIndex();
- else {
- --m_nextVar;
- ++m_codeBlock->numVars;
-
- m_locals.append(index);
+ if (!result.second) {
+ r0 = ®isterFor(result.first->second.getIndex());
+ return false;
}
- r0 = &m_locals[localsIndex(index)];
- return result.second;
+ ++m_codeBlock->numVars;
+ r0 = newRegister();
+ return true;
}
bool CodeGenerator::addGlobalVar(const Identifier& ident, bool isConstant, RegisterID*& r0)
{
- int index = m_nextVar;
+ int index = m_nextGlobal;
SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0);
pair<SymbolTable::iterator, bool> result = symbolTable().add(ident.ustring().rep(), newEntry);
if (!result.second)
index = result.first->second.getIndex();
else {
- --m_nextVar;
- m_locals.append(index + m_globalVarStorageOffset);
+ --m_nextGlobal;
+ m_globals.append(index + m_globalVarStorageOffset);
}
- r0 = &m_locals[localsIndex(index)];
+ r0 = ®isterFor(index);
return result.second;
}
+void CodeGenerator::allocateConstants(size_t count)
+{
+ m_codeBlock->numConstants = count;
+ if (!count)
+ return;
+
+ m_nextConstant = m_calleeRegisters.size();
+
+ for (size_t i = 0; i < count; ++i)
+ newRegister();
+ m_lastConstant = &m_calleeRegisters.last();
+}
+
CodeGenerator::CodeGenerator(ProgramNode* programNode, const Debugger* debugger, const ScopeChain& scopeChain, SymbolTable* symbolTable, CodeBlock* codeBlock, VarStack& varStack, FunctionStack& functionStack)
: m_shouldEmitDebugHooks(!!debugger)
, m_scopeChain(&scopeChain)
, m_dynamicScopeDepth(0)
, m_codeType(GlobalCode)
, m_continueDepth(0)
- , m_nextVar(-1)
+ , m_nextGlobal(-1)
, m_globalData(&scopeChain.globalObject()->globalExec()->globalData())
, m_lastOpcodeID(op_end)
{
+ emitOpcode(op_init);
codeBlock->globalData = m_globalData;
// FIXME: Move code that modifies the global object to Machine::execute.
- m_codeBlock->numConstants = programNode->neededConstants();
- m_codeBlock->numVars = 1; // Allocate space for "this"
+ m_codeBlock->numParameters = 1; // Allocate space for "this"
JSGlobalObject* globalObject = scopeChain.globalObject();
ExecState* exec = globalObject->globalExec();
RegisterFile* registerFile = &exec->globalData().machine->registerFile();
// Shift register indexes in generated code to elide registers allocated by intermediate stack frames.
- m_globalVarStorageOffset = -1 - RegisterFile::CallFrameHeaderSize - registerFile->size();
+ m_globalVarStorageOffset = -RegisterFile::CallFrameHeaderSize - m_codeBlock->numParameters - registerFile->size();
// Add previously defined symbols to bookkeeping.
- m_locals.resize(symbolTable->size());
+ m_globals.resize(symbolTable->size());
SymbolTable::iterator end = symbolTable->end();
for (SymbolTable::iterator it = symbolTable->begin(); it != end; ++it)
- m_locals[localsIndex(it->second.getIndex())].setIndex(it->second.getIndex() + m_globalVarStorageOffset);
+ registerFor(it->second.getIndex()).setIndex(it->second.getIndex() + m_globalVarStorageOffset);
BatchedTransitionOptimizer optimizer(globalObject);
bool canOptimizeNewGlobals = symbolTable->size() + functionStack.size() + varStack.size() < registerFile->maxGlobals();
if (canOptimizeNewGlobals) {
// Shift new symbols so they get stored prior to existing symbols.
- m_nextVar -= symbolTable->size();
+ m_nextGlobal -= symbolTable->size();
for (size_t i = 0; i < functionStack.size(); ++i) {
FuncDeclNode* funcDecl = functionStack[i].get();
emitNewFunction(addGlobalVar(funcDecl->m_ident, false), funcDecl);
}
- for (size_t i = 0; i < varStack.size(); ++i) {
+ Vector<RegisterID*, 32> newVars;
+ for (size_t i = 0; i < varStack.size(); ++i)
if (!globalObject->hasProperty(exec, varStack[i].first))
- emitLoad(addGlobalVar(varStack[i].first, varStack[i].second & DeclarationStacks::IsConstant), jsUndefined());
- }
+ newVars.append(addGlobalVar(varStack[i].first, varStack[i].second & DeclarationStacks::IsConstant));
+
+ allocateConstants(programNode->neededConstants());
+
+ for (size_t i = 0; i < newVars.size(); ++i)
+ emitLoad(newVars[i], jsUndefined());
} else {
for (size_t i = 0; i < functionStack.size(); ++i) {
FuncDeclNode* funcDecl = functionStack[i].get();
attributes |= ReadOnly;
globalObject->putWithAttributes(exec, varStack[i].first, jsUndefined(), attributes);
}
+
+ allocateConstants(programNode->neededConstants());
}
}
, m_dynamicScopeDepth(0)
, m_codeType(FunctionCode)
, m_continueDepth(0)
- , m_nextVar(-1)
, m_globalData(&scopeChain.globalObject()->globalExec()->globalData())
, m_lastOpcodeID(op_end)
{
- emitOpcode(op_initialise_locals);
+ emitOpcode(op_init);
codeBlock->globalData = m_globalData;
- m_codeBlock->numConstants = functionBody->neededConstants();
-
const Node::FunctionStack& functionStack = functionBody->functionStack();
for (size_t i = 0; i < functionStack.size(); ++i) {
FuncDeclNode* funcDecl = functionStack[i].get();
}
Vector<Identifier>& parameters = functionBody->parameters();
- m_nextParameter = m_nextVar - parameters.size(); // parameters are allocated prior to vars
- m_locals.resize(localsIndex(m_nextParameter) + 1); // localsIndex of 0 => m_locals size of 1
+ m_nextParameter = -RegisterFile::CallFrameHeaderSize - parameters.size() - 1;
+ m_parameters.resize(1 + parameters.size()); // reserve space for "this"
// Add "this" as a parameter
m_thisRegister.setIndex(m_nextParameter);
for (size_t i = 0; i < parameters.size(); ++i)
addParameter(parameters[i]);
+
+ allocateConstants(functionBody->neededConstants());
}
CodeGenerator::CodeGenerator(EvalNode* evalNode, const Debugger* debugger, const ScopeChain& scopeChain, SymbolTable* symbolTable, EvalCodeBlock* codeBlock)
, m_globalData(&scopeChain.globalObject()->globalExec()->globalData())
, m_lastOpcodeID(op_end)
{
- emitOpcode(op_initialise_locals);
+ emitOpcode(op_init);
codeBlock->globalData = m_globalData;
+ m_codeBlock->numParameters = 1; // Allocate space for "this"
- m_codeBlock->numConstants = evalNode->neededConstants();
- m_codeBlock->numVars = 1; // Allocate space for "this"
-}
-
-CodeGenerator::~CodeGenerator()
-{
+ allocateConstants(evalNode->neededConstants());
}
RegisterID* CodeGenerator::addParameter(const Identifier& ident)
{
- // Parameters overwrite var declarations, but not function declarations,
- // in the symbol table.
+ // Parameters overwrite var declarations, but not function declarations.
RegisterID* result = 0;
UString::Rep* rep = ident.ustring().rep();
if (!m_functions.contains(rep)) {
symbolTable().set(rep, m_nextParameter);
- m_locals[localsIndex(m_nextParameter)].setIndex(m_nextParameter);
- result = &(m_locals[localsIndex(m_nextParameter)]);
+ RegisterID& parameter = registerFor(m_nextParameter);
+ parameter.setIndex(m_nextParameter);
+ result = ¶meter;
}
// To maintain the calling convention, we have to allocate unique space for
return result;
}
-RegisterID* CodeGenerator::registerForLocal(const Identifier& ident)
+RegisterID* CodeGenerator::registerFor(const Identifier& ident)
{
if (m_codeType == FunctionCode && ident == propertyNames().arguments)
m_codeBlock->needsFullScopeChain = true;
if (entry.isNull())
return 0;
- return &m_locals[localsIndex(entry.getIndex())];
+ return ®isterFor(entry.getIndex());
}
-RegisterID* CodeGenerator::registerForLocalConstInit(const Identifier& ident)
+RegisterID* CodeGenerator::constRegisterFor(const Identifier& ident)
{
if (m_codeType == EvalCode)
return 0;
SymbolTableEntry entry = symbolTable().get(ident.ustring().rep());
ASSERT(!entry.isNull());
- return &m_locals[localsIndex(entry.getIndex())];
+ return ®isterFor(entry.getIndex());
}
bool CodeGenerator::isLocal(const Identifier& ident)
return symbolTable().get(ident.ustring().rep()).isReadOnly();
}
+RegisterID* CodeGenerator::newRegister()
+{
+ m_calleeRegisters.append(m_calleeRegisters.size());
+ m_codeBlock->numCalleeRegisters = max<int>(m_codeBlock->numCalleeRegisters, m_calleeRegisters.size());
+ return &m_calleeRegisters.last();
+}
+
RegisterID* CodeGenerator::newTemporary()
{
// Reclaim free register IDs.
- while (m_temporaries.size() && !m_temporaries.last().refCount())
- m_temporaries.removeLast();
-
- // Allocate new register ID.
- m_temporaries.append(m_temporaries.size() + m_codeBlock->numConstants);
- m_codeBlock->numTemporaries = max<int>(m_codeBlock->numTemporaries, m_temporaries.size());
- return &m_temporaries.last();
+ while (m_calleeRegisters.size() && !m_calleeRegisters.last().refCount())
+ m_calleeRegisters.removeLast();
+
+ RegisterID* result = newRegister();
+ result->setTemporary();
+ return result;
}
RegisterID* CodeGenerator::highestUsedRegister()
{
- while (m_temporaries.size() < static_cast<unsigned>(m_codeBlock->numTemporaries))
- m_temporaries.append(m_temporaries.size());
- return &m_temporaries.last();
+ size_t count = m_codeBlock->numCalleeRegisters;
+ while (m_calleeRegisters.size() < count)
+ newRegister();
+ return &m_calleeRegisters.last();
}
PassRefPtr<LabelID> CodeGenerator::newLabel()
RegisterID* CodeGenerator::addConstant(JSValue* v)
{
- pair<JSValueMap::iterator, bool> result = m_jsValueMap.add(v, m_codeBlock->constantRegisters.size());
+ pair<JSValueMap::iterator, bool> result = m_jsValueMap.add(v, m_nextConstant);
if (result.second) {
- m_constants.append(m_codeBlock->constantRegisters.size());
- m_constants.last().makeConstant();
+ RegisterID& constant = m_calleeRegisters[m_nextConstant];
+
+ ++m_nextConstant;
+
m_codeBlock->constantRegisters.append(v);
- ASSERT(m_codeBlock->constantRegisters.size() <= (unsigned) m_codeBlock->numConstants);
- return &m_constants.last();
+ return &constant;
}
- return &m_constants[result.first->second];
+ return ®isterFor(result.first->second);
}
unsigned CodeGenerator::addUnexpectedConstant(JSValue* v)
RegisterID* CodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, RegisterID* func, RegisterID* base, ArgumentsNode* argumentsNode, unsigned divot, unsigned startOffset, unsigned endOffset)
{
ASSERT(opcodeID == op_call || opcodeID == op_call_eval);
+ ASSERT(func->refCount());
+ ASSERT(!base || base->refCount());
- // Ordinarily, we might ref "func" and "base", to avoid allocating new
- // temporaries in the same registers. In this case, though, we actually
- // want the call frame we allocate to overlap "func" and "base", if they're
- // not otherwise referenced. op_call will read "func" and "base" before
- // writing out the call frame, so this is safe.
-
- // Reserve space for call frame.
- Vector<RefPtr<RegisterID>, RegisterFile::CallFrameHeaderSize> callFrame;
- for (int i = 0; i < RegisterFile::CallFrameHeaderSize; ++i)
- callFrame.append(newTemporary());
-
// Generate code for arguments.
Vector<RefPtr<RegisterID>, 16> argv;
argv.append(newTemporary()); // reserve space for "this"
emitNode(argv.last().get(), n);
}
+ // Reserve space for call frame.
+ Vector<RefPtr<RegisterID>, RegisterFile::CallFrameHeaderSize> callFrame;
+ for (int i = 0; i < RegisterFile::CallFrameHeaderSize; ++i)
+ callFrame.append(newTemporary());
+
emitExpressionInfo(divot, startOffset, endOffset);
emitOpcode(opcodeID);
instructions().append(dst->index());
instructions().append(base ? base->index() : missingThisObjectMarker()); // We encode the "this" value in the instruction stream, to avoid an explicit instruction for copying or loading it.
instructions().append(argv[0]->index()); // argv
instructions().append(argv.size()); // argc
+ instructions().append(argv[0]->index() + argv.size() + RegisterFile::CallFrameHeaderSize); // registerOffset
return dst;
}
{
ASSERT(func->refCount());
- // Reserve space for prototype
+ // Load prototype.
+ emitExpressionInfo(divot, startOffset, endOffset);
RefPtr<RegisterID> funcProto = newTemporary();
-
- // Reserve space for call frame.
- Vector<RefPtr<RegisterID>, RegisterFile::CallFrameHeaderSize> callFrame;
- for (int i = 0; i < RegisterFile::CallFrameHeaderSize; ++i)
- callFrame.append(newTemporary());
+ emitGetById(funcProto.get(), func, globalExec()->propertyNames().prototype);
// Generate code for arguments.
Vector<RefPtr<RegisterID>, 16> argv;
emitNode(argv.last().get(), n);
}
- emitExpressionInfo(divot, startOffset, endOffset);
- emitGetById(funcProto.get(), func, globalExec()->propertyNames().prototype);
+ // Reserve space for call frame.
+ Vector<RefPtr<RegisterID>, RegisterFile::CallFrameHeaderSize> callFrame;
+ for (int i = 0; i < RegisterFile::CallFrameHeaderSize; ++i)
+ callFrame.append(newTemporary());
emitExpressionInfo(divot, startOffset, endOffset);
emitOpcode(op_construct);
instructions().append(funcProto->index());
instructions().append(argv[0]->index()); // argv
instructions().append(argv.size()); // argc
-
+ instructions().append(argv[0]->index() + argv.size() + RegisterFile::CallFrameHeaderSize); // registerOffset
+
emitOpcode(op_construct_verify);
instructions().append(dst->index());
instructions().append(argv[0]->index());
CodeGenerator(FunctionBodyNode*, const Debugger*, const ScopeChain&, SymbolTable*, CodeBlock*);
CodeGenerator(EvalNode*, const Debugger*, const ScopeChain&, SymbolTable*, EvalCodeBlock*);
- ~CodeGenerator();
-
JSGlobalData* globalData() const { return m_globalData; }
const CommonIdentifiers& propertyNames() const { return *m_globalData->propertyNames; }
void generate();
// Returns the register corresponding to a local variable, or 0 if no
- // such register exists. Registers returned by registerForLocal do not
+ // such register exists. Registers returned by registerFor do not
// require explicit reference counting.
- RegisterID* registerForLocal(const Identifier&);
- // Behaves as registerForLocal does, but ignores dynamic scope as
+ RegisterID* registerFor(const Identifier&);
+
+ // Behaves as registerFor does, but ignores dynamic scope as
// dynamic scope should not interfere with const initialisation
- RegisterID* registerForLocalConstInit(const Identifier&);
+ RegisterID* constRegisterFor(const Identifier&);
// Searches the scope chain in an attempt to statically locate the requested
// property. Returns false if for any reason the property cannot be safely
typedef HashMap<UString::Rep*, JSString*, IdentifierRepHash> IdentifierStringMap;
RegisterID* emitCall(OpcodeID, RegisterID*, RegisterID*, RegisterID*, ArgumentsNode*, unsigned divot, unsigned startOffset, unsigned endOffset);
-
- // Maps a register index in the symbol table to a RegisterID index in m_locals.
- int localsIndex(int registerIndex) { return -registerIndex - 1; }
+
+ RegisterID* newRegister();
// Returns the RegisterID corresponding to ident.
RegisterID* addVar(const Identifier& ident, bool isConstant)
bool addGlobalVar(const Identifier&, bool isConstant, RegisterID*&);
RegisterID* addParameter(const Identifier&);
+
+ void allocateConstants(size_t);
+
+ RegisterID& registerFor(int index)
+ {
+ if (index >= 0)
+ return m_calleeRegisters[index];
+
+ if (m_parameters.size()) {
+ ASSERT(!m_globals.size());
+ return m_parameters[index + m_parameters.size() + RegisterFile::CallFrameHeaderSize];
+ }
+
+ return m_globals[-index - 1];
+ }
unsigned addConstant(FuncDeclNode*);
unsigned addConstant(FuncExprNode*);
HashSet<RefPtr<UString::Rep>, IdentifierRepHash> m_functions;
RegisterID m_thisRegister;
- SegmentedVector<RegisterID, 512> m_locals;
- SegmentedVector<RegisterID, 512> m_constants;
- SegmentedVector<RegisterID, 512> m_temporaries;
+ RefPtr<RegisterID> m_lastConstant;
+ SegmentedVector<RegisterID, 512> m_calleeRegisters;
+ SegmentedVector<RegisterID, 512> m_parameters;
+ SegmentedVector<RegisterID, 512> m_globals;
SegmentedVector<LabelID, 512> m_labels;
int m_finallyDepth;
int m_dynamicScopeDepth;
Vector<ControlFlowContext> m_scopeContextStack;
Vector<SwitchInfo> m_switchContextStack;
- int m_nextVar;
+ int m_nextGlobal;
int m_nextParameter;
+ int m_nextConstant;
int m_globalVarStorageOffset;
static void* op_call_indirect;
#endif
+#if ENABLE(CTI)
+
+ALWAYS_INLINE static Instruction* vPCForPC(CodeBlock* codeBlock, void* pc)
+{
+ if (pc >= codeBlock->instructions.begin() && pc < codeBlock->instructions.end())
+ return static_cast<Instruction*>(pc);
+
+ ASSERT(codeBlock->ctiReturnAddressVPCMap.contains(pc));
+ unsigned vPCIndex = codeBlock->ctiReturnAddressVPCMap.get(pc);
+ return codeBlock->instructions.begin() + vPCIndex;
+}
+
+#else // #ENABLE(CTI)
+
+ALWAYS_INLINE static Instruction* vPCForPC(CodeBlock*, void* pc)
+{
+ return static_cast<Instruction*>(pc);
+}
+
+#endif // #ENABLE(CTI)
+
// Returns the depth of the scope chain within a given call frame.
static int depth(CodeBlock* codeBlock, ScopeChain& sc)
{
#endif
}
-ALWAYS_INLINE void Machine::initializeCallFrame(Register* callFrame, CodeBlock* codeBlock, Instruction* vPC, ScopeChainNode* scopeChain, Register* r, int returnValueRegister, int argv, int argc, JSValue* function)
+ALWAYS_INLINE void Machine::initializeCallFrame(Register* callFrame, CodeBlock* codeBlock, Instruction* vPC, ScopeChainNode* scopeChain, Register* r, int returnValueRegister, int argc, JSValue* function)
{
callFrame[RegisterFile::CallerCodeBlock] = codeBlock;
- callFrame[RegisterFile::ReturnVPC] = vPC + 1;
callFrame[RegisterFile::CallerScopeChain] = scopeChain;
callFrame[RegisterFile::CallerRegisters] = r;
+ callFrame[RegisterFile::ReturnPC] = vPC + 1;
callFrame[RegisterFile::ReturnValueRegister] = returnValueRegister;
- callFrame[RegisterFile::ArgumentStartRegister] = argv; // original argument vector (for the sake of the "arguments" object)
callFrame[RegisterFile::ArgumentCount] = argc; // original argument count (for the sake of the "arguments" object)
callFrame[RegisterFile::Callee] = function;
callFrame[RegisterFile::OptionalCalleeActivation] = nullJSValue;
}
-ALWAYS_INLINE Register* slideRegisterWindowForCall(ExecState* exec, CodeBlock* newCodeBlock, RegisterFile* registerFile, Register* registerBase, Register* r, int argv, int argc, JSValue*& exceptionValue)
+ALWAYS_INLINE Register* slideRegisterWindowForCall(ExecState* exec, CodeBlock* newCodeBlock, RegisterFile* registerFile, Register* registerBase, Register* r, size_t registerOffset, int argc, JSValue*& exceptionValue)
{
- size_t registerOffset = argv + newCodeBlock->numLocals;
- size_t size = r - registerBase + registerOffset + newCodeBlock->numConstants + newCodeBlock->numTemporaries;
+ size_t size = r - registerBase + registerOffset + newCodeBlock->numCalleeRegisters;
if (argc == newCodeBlock->numParameters) { // correct number of arguments
if (!registerFile->grow(size)) {
}
r += registerOffset;
} else if (argc < newCodeBlock->numParameters) { // too few arguments -- fill in the blanks
+ size_t omittedArgCount = newCodeBlock->numParameters - argc;
+ registerOffset += omittedArgCount;
+ size += omittedArgCount;
if (!registerFile->grow(size)) {
exceptionValue = createStackOverflowError(exec);
return r;
}
r += registerOffset;
- int omittedArgCount = newCodeBlock->numParameters - argc;
- Register* endOfParams = r - newCodeBlock->numVars;
- for (Register* it = endOfParams - omittedArgCount; it != endOfParams; ++it)
- (*it) = jsUndefined();
- } else { // too many arguments -- copy return info and expected arguments, leaving the extra arguments behind
- int shift = argc + RegisterFile::CallFrameHeaderSize;
- registerOffset += shift;
- size += shift;
+ Register* argv = r - RegisterFile::CallFrameHeaderSize - omittedArgCount;
+ for (size_t i = 0; i < omittedArgCount; ++i)
+ argv[i] = jsUndefined();
+ } else { // too many arguments -- copy expected arguments, leaving the extra arguments behind
+ size_t numParameters = newCodeBlock->numParameters;
+ registerOffset += numParameters;
+ size += numParameters;
if (!registerFile->grow(size)) {
exceptionValue = createStackOverflowError(exec);
}
r += registerOffset;
- Register* it = r - newCodeBlock->numLocals - RegisterFile::CallFrameHeaderSize - shift;
- Register* end = it + RegisterFile::CallFrameHeaderSize + newCodeBlock->numParameters;
- for ( ; it != end; ++it)
- *(it + shift) = *it;
- }
-
- // initialize local variable slots
-#if ENABLE(CTI)
- if (!newCodeBlock->ctiCode)
-#endif
- {
-
+ Register* argv = r - RegisterFile::CallFrameHeaderSize - numParameters - argc;
+ for (size_t i = 0; i < numParameters; ++i)
+ argv[i + argc] = argv[i];
}
return r;
{
if (newCodeBlock->needsFullScopeChain) {
JSActivation* activation = new (exec) JSActivation(exec, functionBodyNode, r);
- r[RegisterFile::OptionalCalleeActivation - RegisterFile::CallFrameHeaderSize - newCodeBlock->numLocals] = activation;
+ r[RegisterFile::OptionalCalleeActivation] = activation;
return callDataScopeChain->copy()->push(activation);
}
JSValue* result = 0;
if (evalNode)
- result = exec->globalData().machine->execute(evalNode.get(), exec, thisObj, r - registerFile->base() + argv + argc, scopeChain, &exceptionValue);
+ result = exec->globalData().machine->execute(evalNode.get(), exec, thisObj, r - registerFile->base() + argv + 1 + RegisterFile::CallFrameHeaderSize, scopeChain, &exceptionValue);
if (*profiler)
(*profiler)->didExecute(exec, scopeChain->globalObject()->evalFunction());
printf("----------------------------------------------------\n");
}
- it = r - codeBlock->numLocals - RegisterFile::CallFrameHeaderSize;
+ it = r - RegisterFile::CallFrameHeaderSize - codeBlock->numParameters;
+ printf("[this] | %10p | %10p \n", it, (*it).v()); ++it;
+ end = it + max(codeBlock->numParameters - 1, 0); // - 1 to skip "this"
+ if (it != end) {
+ do {
+ printf("[param] | %10p | %10p \n", it, (*it).v());
+ ++it;
+ } while (it != end);
+ }
+ printf("----------------------------------------------------\n");
+
printf("[CallerCodeBlock] | %10p | %10p \n", it, (*it).v()); ++it;
- printf("[ReturnVPC] | %10p | %10p \n", it, (*it).v()); ++it;
printf("[CallerScopeChain] | %10p | %10p \n", it, (*it).v()); ++it;
- printf("[CallerRegisterOffset] | %10p | %10p \n", it, (*it).v()); ++it;
+ printf("[CallerRegisters] | %10p | %10p \n", it, (*it).v()); ++it;
+ printf("[ReturnPC] | %10p | %10p \n", it, (*it).v()); ++it;
printf("[ReturnValueRegister] | %10p | %10p \n", it, (*it).v()); ++it;
- printf("[ArgumentStartRegister] | %10p | %10p \n", it, (*it).v()); ++it;
printf("[ArgumentCount] | %10p | %10p \n", it, (*it).v()); ++it;
printf("[Callee] | %10p | %10p \n", it, (*it).v()); ++it;
printf("[OptionalCalleeActivation] | %10p | %10p \n", it, (*it).v()); ++it;
printf("----------------------------------------------------\n");
- printf("[this] | %10p | %10p \n", it, (*it).v()); ++it;
- end = it + max(codeBlock->numParameters - 1, 0); // - 1 to skip "this"
+ int registerCount = 0;
+
+ end = it + codeBlock->numVars;
if (it != end) {
do {
- printf("[param] | %10p | %10p \n", it, (*it).v());
+ printf("[r%2d] | %10p | %10p \n", registerCount, it, (*it).v());
++it;
+ ++registerCount;
} while (it != end);
}
printf("----------------------------------------------------\n");
- if (codeBlock->codeType != GlobalCode) {
- end = it + codeBlock->numVars;
- if (it != end) {
- do {
- printf("[var] | %10p | %10p \n", it, (*it).v());
- ++it;
- } while (it != end);
- printf("----------------------------------------------------\n");
- }
+ end = it + codeBlock->numConstants;
+ if (it != end) {
+ do {
+ printf("[r%2d] | %10p | %10p \n", registerCount, it, (*it).v());
+ ++it;
+ ++registerCount;
+ } while (it != end);
}
+ printf("----------------------------------------------------\n");
- end = it + codeBlock->numTemporaries;
+ end = it + codeBlock->numCalleeRegisters - codeBlock->numConstants - codeBlock->numVars;
if (it != end) {
do {
- printf("[temp] | %10p | %10p \n", it, (*it).v());
+ printf("[r%2d] | %10p | %10p \n", registerCount, it, (*it).v());
++it;
+ ++registerCount;
} while (it != end);
}
+ printf("----------------------------------------------------\n");
}
#endif
NEVER_INLINE bool Machine::unwindCallFrame(ExecState* exec, JSValue* exceptionValue, const Instruction*& vPC, CodeBlock*& codeBlock, ScopeChainNode*& scopeChain, Register*& r)
{
CodeBlock* oldCodeBlock = codeBlock;
- Register* callFrame = r - oldCodeBlock->numLocals - RegisterFile::CallFrameHeaderSize;
if (Debugger* debugger = exec->dynamicGlobalObject()->debugger()) {
DebuggerCallFrame debuggerCallFrame(exec, exec->dynamicGlobalObject(), codeBlock, scopeChain, r, exceptionValue);
- if (callFrame[RegisterFile::Callee].jsValue(exec))
+ if (r[RegisterFile::Callee].jsValue(exec))
debugger->returnEvent(debuggerCallFrame, codeBlock->ownerNode->sourceId(), codeBlock->ownerNode->lastLine());
else
debugger->didExecuteProgram(debuggerCallFrame, codeBlock->ownerNode->sourceId(), codeBlock->ownerNode->lastLine());
}
if (Profiler* profiler = *Profiler::enabledProfilerReference()) {
- if (callFrame[RegisterFile::Callee].jsValue(exec))
- profiler->didExecute(exec, static_cast<JSObject*>(callFrame[RegisterFile::Callee].jsValue(exec)));
+ if (r[RegisterFile::Callee].jsValue(exec))
+ profiler->didExecute(exec, static_cast<JSObject*>(r[RegisterFile::Callee].jsValue(exec)));
else
profiler->didExecute(exec, codeBlock->ownerNode->sourceURL(), codeBlock->ownerNode->lineNo());
}
scopeChain->deref();
// If this call frame created an activation, tear it off.
- if (JSActivation* activation = static_cast<JSActivation*>(callFrame[RegisterFile::OptionalCalleeActivation].jsValue(exec))) {
+ if (JSActivation* activation = static_cast<JSActivation*>(r[RegisterFile::OptionalCalleeActivation].jsValue(exec))) {
ASSERT(activation->isActivationObject());
activation->copyRegisters();
}
- codeBlock = callFrame[RegisterFile::CallerCodeBlock].codeBlock();
+ codeBlock = r[RegisterFile::CallerCodeBlock].codeBlock();
if (!codeBlock)
return false;
- scopeChain = callFrame[RegisterFile::CallerScopeChain].scopeChain();
- r = callFrame[RegisterFile::CallerRegisters].r();
- exec->m_callFrame = r - oldCodeBlock->numLocals - RegisterFile::CallFrameHeaderSize;
- vPC = callFrame[RegisterFile::ReturnVPC].vPC();
+ scopeChain = r[RegisterFile::CallerScopeChain].scopeChain();
+ vPC = vPCForPC(codeBlock, r[RegisterFile::ReturnPC].v());
+ r = r[RegisterFile::CallerRegisters].r();
+ exec->m_callFrame = r;
return true;
}
CodeBlock* codeBlock = &programNode->byteCode(scopeChain);
size_t oldSize = m_registerFile.size();
- size_t newSize = oldSize + RegisterFile::CallFrameHeaderSize + codeBlock->numVars + codeBlock->numConstants + codeBlock->numTemporaries;
+ size_t newSize = oldSize + codeBlock->numParameters + RegisterFile::CallFrameHeaderSize + codeBlock->numCalleeRegisters;
if (!m_registerFile.grow(newSize)) {
*exception = createStackOverflowError(exec);
return 0;
JSGlobalObject* globalObject = exec->dynamicGlobalObject();
globalObject->copyGlobalsTo(m_registerFile);
- Register* callFrame = m_registerFile.base() + oldSize;
-
- // a 0 codeBlock indicates a built-in caller
- initializeCallFrame(callFrame, 0, 0, 0, 0, 0, 0, 0, 0);
-
- Register* r = callFrame + RegisterFile::CallFrameHeaderSize + codeBlock->numVars;
+ Register* r = m_registerFile.base() + oldSize + codeBlock->numParameters + RegisterFile::CallFrameHeaderSize;
r[codeBlock->thisRegister] = thisObj;
-
- for (size_t i = 0; i < codeBlock->constantRegisters.size(); ++i)
- r[i] = codeBlock->constantRegisters[i];
+ initializeCallFrame(r, 0, 0, 0, 0, 0, 0, 0);
if (codeBlock->needsFullScopeChain)
scopeChain = scopeChain->copy();
return 0;
}
- int argv = RegisterFile::CallFrameHeaderSize;
- int argc = args.size() + 1; // implicit "this" parameter
-
size_t oldSize = m_registerFile.size();
- if (!m_registerFile.grow(oldSize + RegisterFile::CallFrameHeaderSize + argc)) {
+ int argc = 1 + args.size(); // implicit "this" parameter
+
+ if (!m_registerFile.grow(oldSize + argc)) {
*exception = createStackOverflowError(exec);
return 0;
}
- Register* callFrame = m_registerFile.base() + oldSize;
-
- // put args in place, including "this"
- Register* dst = callFrame + RegisterFile::CallFrameHeaderSize;
- (*dst) = thisObj;
+ Register* argv = m_registerFile.base() + oldSize;
+ size_t dst = 0;
+ argv[dst] = thisObj;
ArgList::const_iterator end = args.end();
for (ArgList::const_iterator it = args.begin(); it != end; ++it)
- (*++dst) = *it;
-
- // a 0 codeBlock indicates a built-in caller
- initializeCallFrame(callFrame, 0, 0, 0, callFrame, 0, argv, argc, function);
+ argv[++dst] = *it;
CodeBlock* newCodeBlock = &functionBodyNode->byteCode(scopeChain);
- Register* r = slideRegisterWindowForCall(exec, newCodeBlock, &m_registerFile, m_registerFile.base(), callFrame, argv, argc, *exception);
- if (*exception) {
+ Register* r = slideRegisterWindowForCall(exec, newCodeBlock, &m_registerFile, m_registerFile.base(), argv, argc + RegisterFile::CallFrameHeaderSize, argc, *exception);
+ if (UNLIKELY(*exception != 0)) {
m_registerFile.shrink(oldSize);
return 0;
}
+ // a 0 codeBlock indicates a built-in caller
+ initializeCallFrame(r, 0, 0, 0, argv, 0, argc, function);
- ExecState newExec(exec, &m_registerFile, scopeChain, callFrame);
+ ExecState newExec(exec, &m_registerFile, scopeChain, r);
Profiler** profiler = Profiler::enabledProfilerReference();
if (*profiler)
return result;
}
+JSValue* Machine::execute(EvalNode* evalNode, ExecState* exec, JSObject* thisObj, ScopeChainNode* scopeChain, JSValue** exception)
+{
+ return execute(evalNode, exec, thisObj, m_registerFile.size() + evalNode->byteCode(scopeChain).numParameters + RegisterFile::CallFrameHeaderSize, scopeChain, exception);
+}
+
JSValue* Machine::execute(EvalNode* evalNode, ExecState* exec, JSObject* thisObj, int registerOffset, ScopeChainNode* scopeChain, JSValue** exception)
{
if (m_reentryDepth >= MaxReentryDepth) {
}
size_t oldSize = m_registerFile.size();
- size_t newSize = registerOffset + codeBlock->numVars + codeBlock->numConstants + codeBlock->numTemporaries + RegisterFile::CallFrameHeaderSize;
+ size_t newSize = registerOffset + codeBlock->numCalleeRegisters;
if (!m_registerFile.grow(newSize)) {
*exception = createStackOverflowError(exec);
return 0;
}
- Register* callFrame = m_registerFile.base() + registerOffset;
+ Register* r = m_registerFile.base() + registerOffset;
// a 0 codeBlock indicates a built-in caller
- initializeCallFrame(callFrame, 0, 0, 0, 0, 0, 0, 0, 0);
-
- Register* r = callFrame + RegisterFile::CallFrameHeaderSize + codeBlock->numVars;
r[codeBlock->thisRegister] = thisObj;
-
- for (size_t i = 0; i < codeBlock->constantRegisters.size(); ++i)
- r[i] = codeBlock->constantRegisters[i];
+ initializeCallFrame(r, 0, 0, 0, 0, 0, 0, 0);
if (codeBlock->needsFullScopeChain)
scopeChain = scopeChain->copy();
int thisVal = (++vPC)->u.operand;
int firstArg = (++vPC)->u.operand;
int argCount = (++vPC)->u.operand;
+ ++vPC; // registerOffset
JSValue* funcVal = r[func].jsValue(exec);
JSValue* baseVal = r[thisVal].jsValue(exec);
// We didn't find the blessed version of eval, so reset vPC and process
// this instruction as a normal function call, supplying the proper 'this'
// value.
- vPC -= 5;
+ vPC -= 6;
r[thisVal] = baseVal->toThisObject(exec);
#if HAVE(COMPUTED_GOTO)
// fall through to op_call
}
BEGIN_OPCODE(op_call) {
- /* call dst(r) func(r) thisVal(r) firstArg(r) argCount(n)
+ /* call dst(r) func(r) thisVal(r) firstArg(r) argCount(n) registerOffset(n)
Perform a function call. Specifically, call register func
with a "this" value of register thisVal, and put the result
int thisVal = (++vPC)->u.operand;
int firstArg = (++vPC)->u.operand;
int argCount = (++vPC)->u.operand;
+ int registerOffset = (++vPC)->u.operand;
JSValue* v = r[func].jsValue(exec);
CallData callData;
CallType callType = v->getCallData(callData);
- if (*enabledProfilerReference)
- (*enabledProfilerReference)->willExecute(exec, static_cast<JSObject*>(v));
-
- Register* callFrame = r + firstArg - RegisterFile::CallFrameHeaderSize;
- initializeCallFrame(callFrame, codeBlock, vPC, scopeChain, r, dst, firstArg, argCount, v);
- exec->m_callFrame = callFrame;
-
if (callType == CallTypeJS) {
-
ScopeChainNode* callDataScopeChain = callData.js.scopeChain;
FunctionBodyNode* functionBodyNode = callData.js.functionBody;
CodeBlock* newCodeBlock = &functionBodyNode->byteCode(callDataScopeChain);
r[firstArg] = thisVal == missingThisObjectMarker() ? exec->globalThisValue() : r[thisVal].jsValue(exec);
+
+ Register* savedR = r;
- r = slideRegisterWindowForCall(exec, newCodeBlock, registerFile, registerBase, r, firstArg, argCount, exceptionValue);
+ r = slideRegisterWindowForCall(exec, newCodeBlock, registerFile, registerBase, r, registerOffset, argCount, exceptionValue);
if (UNLIKELY(exceptionValue != 0))
goto vm_throw;
+ initializeCallFrame(r, codeBlock, vPC, scopeChain, savedR, dst, argCount, v);
+ exec->m_callFrame = r;
+
+ if (*enabledProfilerReference)
+ (*enabledProfilerReference)->willExecute(exec, static_cast<JSObject*>(v));
+
codeBlock = newCodeBlock;
setScopeChain(exec, scopeChain, scopeChainForCall(exec, functionBodyNode, codeBlock, callDataScopeChain, r));
vPC = codeBlock->instructions.begin();
#if DUMP_OPCODE_STATS
OpcodeStats::resetLastInstruction();
#endif
-
+
NEXT_OPCODE;
}
JSValue* thisValue = thisVal == missingThisObjectMarker() ? exec->globalThisValue() : r[thisVal].jsValue(exec);
ArgList args(r + firstArg + 1, argCount - 1);
+ initializeCallFrame(r + registerOffset, codeBlock, vPC, scopeChain, r, dst, argCount, v);
+ exec->m_callFrame = r + registerOffset;
+
+ if (*enabledProfilerReference)
+ (*enabledProfilerReference)->willExecute(exec, static_cast<JSObject*>(v));
+
MACHINE_SAMPLING_callingHostFunction();
JSValue* returnValue = callData.native.function(exec, static_cast<JSObject*>(v), thisValue, args);
- exec->m_callFrame = r - codeBlock->numLocals - RegisterFile::CallFrameHeaderSize;
+ exec->m_callFrame = r;
VM_CHECK_EXCEPTION();
r[dst] = returnValue;
ASSERT(callType == CallTypeNone);
exceptionValue = createNotAFunctionError(exec, v, vPC, codeBlock);
- exec->m_callFrame = r - codeBlock->numLocals - RegisterFile::CallFrameHeaderSize;
goto vm_throw;
}
BEGIN_OPCODE(op_ret) {
int result = (++vPC)->u.operand;
- Register* callFrame = r - codeBlock->numLocals - RegisterFile::CallFrameHeaderSize;
- if (JSActivation* activation = static_cast<JSActivation*>(callFrame[RegisterFile::OptionalCalleeActivation].jsValue(exec))) {
+ if (JSActivation* activation = static_cast<JSActivation*>(r[RegisterFile::OptionalCalleeActivation].jsValue(exec))) {
ASSERT(!codeBlock->needsFullScopeChain || scopeChain->object == activation);
ASSERT(activation->isActivationObject());
activation->copyRegisters();
}
if (*enabledProfilerReference)
- (*enabledProfilerReference)->didExecute(exec, static_cast<JSObject*>(callFrame[RegisterFile::Callee].jsValue(exec)));
+ (*enabledProfilerReference)->didExecute(exec, static_cast<JSObject*>(r[RegisterFile::Callee].jsValue(exec)));
if (codeBlock->needsFullScopeChain)
scopeChain->deref();
JSValue* returnValue = r[result].jsValue(exec);
- codeBlock = callFrame[RegisterFile::CallerCodeBlock].codeBlock();
+ codeBlock = r[RegisterFile::CallerCodeBlock].codeBlock();
if (!codeBlock)
return returnValue;
- vPC = callFrame[RegisterFile::ReturnVPC].vPC();
- setScopeChain(exec, scopeChain, callFrame[RegisterFile::CallerScopeChain].scopeChain());
- r = callFrame[RegisterFile::CallerRegisters].r();
- exec->m_callFrame = r - codeBlock->numLocals - RegisterFile::CallFrameHeaderSize;
- int dst = callFrame[RegisterFile::ReturnValueRegister].i();
+ vPC = r[RegisterFile::ReturnPC].vPC();
+ setScopeChain(exec, scopeChain, r[RegisterFile::CallerScopeChain].scopeChain());
+ int dst = r[RegisterFile::ReturnValueRegister].i();
+ r = r[RegisterFile::CallerRegisters].r();
+ exec->m_callFrame = r;
r[dst] = returnValue;
NEXT_OPCODE;
}
- BEGIN_OPCODE(op_initialise_locals) {
- for (Register* it = r - codeBlock->numVars + (codeBlock->codeType == EvalCode); it < r; ++it)
- (*it) = jsUndefined();
- for (size_t i = 0; i < codeBlock->constantRegisters.size(); ++i)
- r[i] = codeBlock->constantRegisters[i];
+ BEGIN_OPCODE(op_init) {
+ size_t i = 0;
+
+ for (size_t count = codeBlock->numVars; i < count; ++i)
+ r[i] = jsUndefined();
+
+ for (size_t count = codeBlock->constantRegisters.size(), j = 0; j < count; ++i, ++j)
+ r[i] = codeBlock->constantRegisters[j];
+
++vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_construct) {
- /* construct dst(r) constr(r) constrProto(r) firstArg(r) argCount(n)
+ /* construct dst(r) constr(r) constrProto(r) firstArg(r) argCount(n) registerOffset(n)
Invoke register "constr" as a constructor. For JS
functions, the calling convention is exactly as for the
int constrProto = (++vPC)->u.operand;
int firstArg = (++vPC)->u.operand;
int argCount = (++vPC)->u.operand;
+ int registerOffset = (++vPC)->u.operand;
- JSValue* constrVal = r[constr].jsValue(exec);
+ JSValue* v = r[constr].jsValue(exec);
ConstructData constructData;
- ConstructType constructType = constrVal->getConstructData(constructData);
-
- // Removing this line of code causes a measurable regression on squirrelfish.
- JSObject* constructor = static_cast<JSObject*>(constrVal);
+ ConstructType constructType = v->getConstructData(constructData);
if (constructType == ConstructTypeJS) {
if (*enabledProfilerReference)
- (*enabledProfilerReference)->willExecute(exec, constructor);
+ (*enabledProfilerReference)->willExecute(exec, static_cast<JSObject*>(v));
StructureID* structure;
JSValue* prototype = r[constrProto].jsValue(exec);
r[firstArg] = newObject; // "this" value
- Register* callFrame = r + firstArg - RegisterFile::CallFrameHeaderSize;
- initializeCallFrame(callFrame, codeBlock, vPC, scopeChain, r, dst, firstArg, argCount, constructor);
- exec->m_callFrame = callFrame;
+ Register* savedR = r;
- r = slideRegisterWindowForCall(exec, newCodeBlock, registerFile, registerBase, r, firstArg, argCount, exceptionValue);
- if (exceptionValue)
+ r = slideRegisterWindowForCall(exec, newCodeBlock, registerFile, registerBase, r, registerOffset, argCount, exceptionValue);
+ if (UNLIKELY(exceptionValue != 0))
goto vm_throw;
+ initializeCallFrame(r, codeBlock, vPC, scopeChain, savedR, dst, argCount, v);
+ exec->m_callFrame = r;
+
+ if (*enabledProfilerReference)
+ (*enabledProfilerReference)->didExecute(exec, static_cast<JSObject*>(v));
+
codeBlock = newCodeBlock;
setScopeChain(exec, scopeChain, scopeChainForCall(exec, functionBodyNode, codeBlock, callDataScopeChain, r));
vPC = codeBlock->instructions.begin();
+#if DUMP_OPCODE_STATS
+ OpcodeStats::resetLastInstruction();
+#endif
+
NEXT_OPCODE;
}
if (constructType == ConstructTypeHost) {
- if (*enabledProfilerReference)
- (*enabledProfilerReference)->willExecute(exec, constructor);
-
ArgList args(r + firstArg + 1, argCount - 1);
+ initializeCallFrame(r + registerOffset, codeBlock, vPC, scopeChain, r, dst, argCount, v);
+ exec->m_callFrame = r + registerOffset;
+
+ if (*enabledProfilerReference)
+ (*enabledProfilerReference)->willExecute(exec, static_cast<JSObject*>(v));
+
MACHINE_SAMPLING_callingHostFunction();
- JSValue* returnValue = constructData.native.function(exec, constructor, args);
+ JSValue* returnValue = constructData.native.function(exec, static_cast<JSObject*>(v), args);
+ exec->m_callFrame = r;
VM_CHECK_EXCEPTION();
r[dst] = returnValue;
if (*enabledProfilerReference)
- (*enabledProfilerReference)->didExecute(exec, constructor);
+ (*enabledProfilerReference)->didExecute(exec, static_cast<JSObject*>(v));
++vPC;
NEXT_OPCODE;
ASSERT(constructType == ConstructTypeNone);
- exceptionValue = createNotAConstructorError(exec, constrVal, vPC, codeBlock);
+ exceptionValue = createNotAConstructorError(exec, v, vPC, codeBlock);
goto vm_throw;
}
BEGIN_OPCODE(op_construct_verify) {
JSActivation* activation = static_cast<JSActivation*>(callFrame[RegisterFile::OptionalCalleeActivation].jsValue(exec));
if (!activation) {
- CodeBlock* codeBlock = &function->m_body->generatedByteCode();
- activation = new (exec) JSActivation(exec, function->m_body, callFrame + RegisterFile::CallFrameHeaderSize + codeBlock->numLocals);
+ activation = new (exec) JSActivation(exec, function->m_body, callFrame);
callFrame[RegisterFile::OptionalCalleeActivation] = activation;
}
if (!callerCodeBlock)
return jsNull();
- Register* callerCallFrame = callFrame[RegisterFile::CallerRegisters].r() - callerCodeBlock->numLocals - RegisterFile::CallFrameHeaderSize;
+ Register* callerCallFrame = callFrame[RegisterFile::CallerRegisters].r();
if (JSValue* caller = callerCallFrame[RegisterFile::Callee].jsValue(exec))
return caller;
if (!callerCodeBlock)
return;
- Instruction* vPC = callFrame[RegisterFile::ReturnVPC].vPC();
+ Instruction* vPC = vPCForPC(callerCodeBlock, callFrame[RegisterFile::ReturnPC].v());
lineNumber = callerCodeBlock->lineNumberForVPC(vPC - 1);
sourceId = callerCodeBlock->ownerNode->sourceId();
sourceURL = callerCodeBlock->ownerNode->sourceURL();
continue;
}
- callFrame = callFrame[RegisterFile::CallerRegisters].r() - callerCodeBlock->numLocals - RegisterFile::CallFrameHeaderSize;
+ callFrame = callFrame[RegisterFile::CallerRegisters].r();
}
}
-void Machine::getArgumentsData(Register* callFrame, JSFunction*& function, Register*& argv, int& argc)
+void Machine::getArgumentsData(Register* callFrame, JSFunction*& function, int& firstParameterIndex, Register*& argv, int& argc)
{
function = static_cast<JSFunction*>(callFrame[RegisterFile::Callee].getJSValue());
ASSERT(function->inherits(&JSFunction::info));
+
+ CodeBlock* codeBlock = &function->m_body->generatedByteCode();
+ int numParameters = codeBlock->numParameters;
+ argc = callFrame[RegisterFile::ArgumentCount].i();
- argv = callFrame[RegisterFile::CallerRegisters].r() + callFrame[RegisterFile::ArgumentStartRegister].i() + 1; // + 1 to skip "this"
- argc = callFrame[RegisterFile::ArgumentCount].i() - 1; // - 1 to skip "this"
+ if (argc <= numParameters)
+ argv = callFrame - RegisterFile::CallFrameHeaderSize - numParameters + 1; // + 1 to skip "this"
+ else
+ argv = callFrame - RegisterFile::CallFrameHeaderSize - numParameters - argc + 1; // + 1 to skip "this"
+
+ argc -= 1; // - 1 to skip "this"
+ firstParameterIndex = -RegisterFile::CallFrameHeaderSize - numParameters + 1; // + 1 to skip "this"
}
#if ENABLE(CTI)
RegisterFile* registerFile = ARG_registerFile;
Register* r = ARG_r;
- JSValue* exceptionValue = 0;
Register* registerBase = registerFile->base();
JSValue* funcVal = ARG_src1;
- JSValue* thisValue = ARG_src2;
- int firstArg = ARG_int3;
- int argCount = ARG_int4;
+ int registerOffset = ARG_int2;
+ int argCount = ARG_int3;
- // In the JIT code before entering this function we wil have checked the vptr,
- // and know this is an object of type JSFunction.
#ifndef NDEBUG
CallData callData;
-#endif
ASSERT(funcVal->getCallData(callData) == CallTypeJS);
+#endif
if (*ARG_profilerReference)
(*ARG_profilerReference)->willExecute(exec, static_cast<JSObject*>(funcVal));
FunctionBodyNode* functionBodyNode = static_cast<JSFunction*>(funcVal)->m_body.get();
CodeBlock* newCodeBlock = &functionBodyNode->byteCode(callDataScopeChain);
+
+ Register* savedR = r;
- r[firstArg] = thisValue;
+ JSValue* exceptionValue = 0;
+ r = slideRegisterWindowForCall(exec, newCodeBlock, registerFile, registerBase, r, registerOffset, argCount, exceptionValue);
+ JSVALUE_VM_CHECK_EXCEPTION_ARG(exceptionValue);
- Register* callFrame = r + firstArg - RegisterFile::CallFrameHeaderSize;
- exec->m_callFrame = callFrame;
+ // RegisterFile::CallerCodeBlock is set by caller
+ r[RegisterFile::CallerScopeChain] = ARG_scopeChain;
+ r[RegisterFile::CallerRegisters] = savedR;
+ // RegisterFile::ReturnPC is set by callee
+ // RegisterFile::ReturnValueRegister is set by caller
+ r[RegisterFile::ArgumentCount] = argCount; // original argument count (for the sake of the "arguments" object)
+ r[RegisterFile::Callee] = funcVal;
+ r[RegisterFile::OptionalCalleeActivation] = nullJSValue;
- r = slideRegisterWindowForCall(exec, newCodeBlock, registerFile, registerBase, r, firstArg, argCount, exceptionValue);
- JSVALUE_VM_CHECK_EXCEPTION_ARG(exceptionValue);
-
+ exec->m_callFrame = r;
exec->m_scopeChain = callDataScopeChain;
ARG_setScopeChain(callDataScopeChain);
JSValue* Machine::cti_op_call_NotJSFunction(CTI_ARGS)
{
ExecState* exec = ARG_exec;
- Register* r = ARG_r;
JSValue* funcVal = ARG_src1;
- JSValue* thisValue = ARG_src2;
- int firstArg = ARG_int3;
- int argCount = ARG_int4;
CallData callData;
CallType callType = funcVal->getCallData(callData);
ASSERT(callType != CallTypeJS);
if (callType == CallTypeHost) {
- Register* oldCallFrame = exec->m_callFrame;
- Register* callFrame = r + firstArg - RegisterFile::CallFrameHeaderSize;
- exec->m_callFrame = callFrame;
+ int registerOffset = ARG_int2;
+ int argCount = ARG_int3;
+ Register* r = ARG_r + registerOffset;
+
+ initializeCallFrame(r, ARG_codeBlock, ARG_instr4, ARG_scopeChain, ARG_r, 0, argCount, funcVal);
+ exec->m_callFrame = r;
if (*ARG_profilerReference)
(*ARG_profilerReference)->willExecute(exec, static_cast<JSObject*>(funcVal));
- ArgList argList(r + firstArg + 1, argCount - 1);
+ Register* argv = r - RegisterFile::CallFrameHeaderSize - argCount;
+ ArgList argList(argv + 1, argCount - 1);
CTI_MACHINE_SAMPLING_callingHostFunction();
- JSValue* returnValue = callData.native.function(exec, static_cast<JSObject*>(funcVal), thisValue, argList);
- exec->m_callFrame = oldCallFrame;
+ JSValue* returnValue = callData.native.function(exec, static_cast<JSObject*>(funcVal), argv[0].jsValue(exec), argList);
VM_CHECK_EXCEPTION(JSValue*);
if (*ARG_profilerReference)
(*ARG_profilerReference)->didExecute(exec, static_cast<JSObject*>(funcVal));
+ exec->m_callFrame = ARG_r;
return returnValue;
-
}
ASSERT(callType == CallTypeNone);
- exec->setException(createNotAFunctionError(exec, funcVal, ARG_instr5, ARG_codeBlock));
+ exec->setException(createNotAFunctionError(exec, funcVal, ARG_instr4, ARG_codeBlock));
VM_CHECK_EXCEPTION_AT_END();
return 0;
}
void Machine::cti_op_ret_activation(CTI_ARGS)
{
ExecState* exec = ARG_exec;
- Register* callFrame = ARG_r - ARG_codeBlock->numLocals - RegisterFile::CallFrameHeaderSize;
+ Register* callFrame = ARG_r;
JSActivation* activation = static_cast<JSActivation*>(callFrame[RegisterFile::OptionalCalleeActivation].jsValue(exec));
ASSERT(activation);
{
ExecState* exec = ARG_exec;
- Register* callFrame = ARG_r - ARG_codeBlock->numLocals - RegisterFile::CallFrameHeaderSize;
+ Register* callFrame = ARG_r;
ASSERT(*ARG_profilerReference);
(*ARG_profilerReference)->didExecute(exec, static_cast<JSObject*>(callFrame[RegisterFile::Callee].jsValue(exec)));
}
ExecState* exec = ARG_exec;
RegisterFile* registerFile = ARG_registerFile;
Register* r = ARG_r;
- ScopeChainNode* scopeChain = ARG_scopeChain;
- JSValue* exceptionValue = 0;
Register* registerBase = registerFile->base();
JSValue* constrVal = ARG_src1;
JSValue* constrProtoVal = ARG_src2;
int firstArg = ARG_int3;
- int argCount = ARG_int4;
+ int registerOffset = ARG_int4;
+ int argCount = ARG_int5;
- ConstructData constructData;
#ifndef NDEBUG
- ConstructType constructType =
+ ConstructData constructData;
+ ASSERT(constrVal->getConstructData(constructData) == ConstructTypeJS);
#endif
- constrVal->getConstructData(constructData);
-
- // Removing this line of code causes a measurable regression on sunspider.
- JSObject* constructor = static_cast<JSObject*>(constrVal);
- ASSERT(constructType == ConstructTypeJS);
+ JSFunction* constructor = static_cast<JSFunction*>(constrVal);
if (*ARG_profilerReference)
(*ARG_profilerReference)->willExecute(exec, constructor);
if (constrProtoVal->isObject())
structure = static_cast<JSObject*>(constrProtoVal)->inheritorID();
else
- structure = scopeChain->globalObject()->emptyObjectStructure();
+ structure = ARG_scopeChain->globalObject()->emptyObjectStructure();
JSObject* newObject = new (exec) JSObject(structure);
- ScopeChainNode* callDataScopeChain = constructData.js.scopeChain;
- FunctionBodyNode* functionBodyNode = constructData.js.functionBody;
+ ScopeChainNode* callDataScopeChain = constructor->m_scopeChain.node();
+ FunctionBodyNode* functionBodyNode = constructor->m_body.get();
CodeBlock* newCodeBlock = &functionBodyNode->byteCode(callDataScopeChain);
r[firstArg] = newObject; // "this" value
- Register* callFrame = r + firstArg - RegisterFile::CallFrameHeaderSize;
- exec->m_callFrame = callFrame;
+ Register* savedR = r;
- r = slideRegisterWindowForCall(exec, newCodeBlock, registerFile, registerBase, r, firstArg, argCount, exceptionValue);
+ JSValue* exceptionValue = 0;
+ r = slideRegisterWindowForCall(exec, newCodeBlock, registerFile, registerBase, r, registerOffset, argCount, exceptionValue);
JSVALUE_VM_CHECK_EXCEPTION_ARG(exceptionValue);
+ // RegisterFile::CallerCodeBlock is set by caller
+ r[RegisterFile::CallerScopeChain] = ARG_scopeChain;
+ r[RegisterFile::CallerRegisters] = savedR;
+ // RegisterFile::ReturnPC is set by callee
+ // RegisterFile::ReturnValueRegister is set by caller
+ r[RegisterFile::ArgumentCount] = argCount; // original argument count (for the sake of the "arguments" object)
+ r[RegisterFile::Callee] = constructor;
+ r[RegisterFile::OptionalCalleeActivation] = nullJSValue;
+
+ exec->m_callFrame = r;
exec->m_scopeChain = callDataScopeChain;
ARG_setScopeChain(callDataScopeChain);
JSValue* constrVal = ARG_src1;
int firstArg = ARG_int3;
- int argCount = ARG_int4;
+ int argCount = ARG_int5;
ConstructData constructData;
ConstructType constructType = constrVal->getConstructData(constructData);
- // Removing this line of code causes a measurable regression on squirrelfish.
JSObject* constructor = static_cast<JSObject*>(constrVal);
- ASSERT(constructType != ConstructTypeJS);
-
if (constructType == ConstructTypeHost) {
- Register* oldCallFrame = exec->m_callFrame;
- Register* callFrame = r + firstArg - RegisterFile::CallFrameHeaderSize;
- exec->m_callFrame = callFrame;
-
if (*ARG_profilerReference)
(*ARG_profilerReference)->willExecute(exec, constructor);
CTI_MACHINE_SAMPLING_callingHostFunction();
JSValue* returnValue = constructData.native.function(exec, constructor, argList);
- exec->m_callFrame = oldCallFrame;
VM_CHECK_EXCEPTION(JSValue*);
if (*ARG_profilerReference)
ASSERT(constructType == ConstructTypeNone);
- exec->setException(createNotAConstructorError(exec, constrVal, ARG_instr5, ARG_codeBlock));
+ exec->setException(createNotAConstructorError(exec, constrVal, ARG_instr6, ARG_codeBlock));
VM_CHECK_EXCEPTION_AT_END();
return 0;
}
ScopeChainNode* scopeChain = ARG_scopeChain;
Machine* machine = exec->machine();
- JSValue* exceptionValue = 0;
JSValue* funcVal = ARG_src1;
- JSValue* baseVal = ARG_src2;
- int firstArg = ARG_int3;
- int argCount = ARG_int4;
+ int registerOffset = ARG_int2;
+ int argCount = ARG_int3;
+ JSValue* baseVal = ARG_src5;
if (baseVal == scopeChain->globalObject() && funcVal == scopeChain->globalObject()->evalFunction()) {
JSObject* thisObject = static_cast<JSObject*>(r[codeBlock->thisRegister].jsValue(exec));
- JSValue* result = machine->callEval(exec, codeBlock, thisObject, scopeChain, registerFile, r, firstArg, argCount, exceptionValue);
+ JSValue* exceptionValue = 0;
+ JSValue* result = machine->callEval(exec, codeBlock, thisObject, scopeChain, registerFile, r, registerOffset - RegisterFile::CallFrameHeaderSize - argCount, argCount, exceptionValue);
JSVALUE_VM_CHECK_EXCEPTION_ARG(exceptionValue);
return result;
}
JSValue* execute(ProgramNode*, ExecState*, ScopeChainNode*, JSObject* thisObj, JSValue** exception);
JSValue* execute(FunctionBodyNode*, ExecState*, JSFunction*, JSObject* thisObj, const ArgList& args, ScopeChainNode*, JSValue** exception);
- JSValue* execute(EvalNode* evalNode, ExecState* exec, JSObject* thisObj, ScopeChainNode* scopeChain, JSValue** exception)
- {
- return execute(evalNode, exec, thisObj, m_registerFile.size(), scopeChain, exception);
- }
+ JSValue* execute(EvalNode* evalNode, ExecState* exec, JSObject* thisObj, ScopeChainNode* scopeChain, JSValue** exception);
JSValue* retrieveArguments(ExecState*, JSFunction*) const;
JSValue* retrieveCaller(ExecState*, InternalFunction*) const;
void retrieveLastCaller(ExecState* exec, int& lineNumber, int& sourceId, UString& sourceURL, JSValue*& function) const;
- void getArgumentsData(Register* callFrame, JSFunction*&, Register*& argv, int& argc);
+ void getArgumentsData(Register* callFrame, JSFunction*&, int& firstParameterIndex, Register*& argv, int& argc);
void setTimeoutTime(unsigned timeoutTime) { m_timeoutTime = timeoutTime; }
void startTimeoutCheck()
NEVER_INLINE JSValue* callEval(ExecState* exec, CodeBlock* callingCodeBlock, JSObject* thisObj, ScopeChainNode* scopeChain, RegisterFile*, Register* r, int argv, int argc, JSValue*& exceptionValue);
JSValue* execute(EvalNode*, ExecState*, JSObject* thisObj, int registerOffset, ScopeChainNode*, JSValue** exception);
- ALWAYS_INLINE void initializeCallFrame(Register* callFrame, CodeBlock*, Instruction*, ScopeChainNode*, Register* r, int returnValueRegister, int argv, int argc, JSValue* function);
+ ALWAYS_INLINE static void initializeCallFrame(Register* callFrame, CodeBlock*, Instruction*, ScopeChainNode*, Register* r, int returnValueRegister, int argc, JSValue* function);
ALWAYS_INLINE void setScopeChain(ExecState* exec, ScopeChainNode*&, ScopeChainNode*);
NEVER_INLINE void debug(ExecState*, const CodeBlock*, ScopeChainNode*, Register*, DebugHookID, int firstLine, int lastLine);
#define DUMP_OPCODE_STATS 0
#define FOR_EACH_OPCODE_ID(macro) \
- macro(op_initialise_locals) \
+ macro(op_init) \
macro(op_unexpected_load) \
macro(op_new_object) \
macro(op_new_array) \
class RegisterFile : Noncopyable {
public:
enum CallFrameHeaderEntry {
- CallerCodeBlock = 0,
- ReturnVPC,
- CallerScopeChain,
- CallerRegisters,
- ReturnValueRegister,
- ArgumentStartRegister,
- ArgumentCount,
- Callee,
- OptionalCalleeActivation,
- CTIReturnEIP,
- CallFrameHeaderSize
+ CallFrameHeaderSize = 8,
+
+ CallerCodeBlock = -8,
+ CallerScopeChain = -7,
+ CallerRegisters = -6,
+ ReturnPC = -5,
+ ReturnValueRegister = -4,
+ ArgumentCount = -3,
+ Callee = -2,
+ OptionalCalleeActivation = -1,
};
- enum { ProgramCodeThisRegister = - 1 };
+ enum { ProgramCodeThisRegister = -CallFrameHeaderSize - 1 };
enum { DefaultCapacity = 2 * 1024 * 1024 / sizeof(Register) };
enum { DefaultMaxGlobals = 8 * 1024 };
public:
RegisterID()
: m_refCount(0)
- , m_isConstant(false)
+ , m_isTemporary(false)
#ifndef NDEBUG
, m_didSetIndex(false)
#endif
explicit RegisterID(int index)
: m_refCount(0)
, m_index(index)
- , m_isConstant(false)
+ , m_isTemporary(false)
#ifndef NDEBUG
, m_didSetIndex(true)
#endif
m_index = index;
}
- void makeConstant()
+ void setTemporary()
{
- m_isConstant = true;
+ m_isTemporary = true;
}
int index() const
bool isTemporary()
{
- return m_index >= 0 && !m_isConstant;
+ return m_isTemporary;
}
void ref()
int m_refCount;
int m_index;
- bool m_isConstant;
+ bool m_isTemporary;
#ifndef NDEBUG
bool m_didSetIndex;
#endif
const ClassInfo Arguments::info = { "Arguments", 0, 0, 0 };
struct ArgumentsData : Noncopyable {
- ArgumentsData(JSActivation* activation, unsigned numParameters, unsigned firstArgumentIndex, unsigned numArguments, JSFunction* callee)
+ ArgumentsData(JSActivation* activation, unsigned numParameters, int firstParameterIndex, unsigned numArguments, JSFunction* callee)
: activation(activation)
, numParameters(numParameters)
- , firstArgumentIndex(firstArgumentIndex)
+ , firstParameterIndex(firstParameterIndex)
, numArguments(numArguments)
, extraArguments(0)
, callee(callee)
JSActivation* activation;
unsigned numParameters;
- unsigned firstArgumentIndex;
+ int firstParameterIndex;
unsigned numArguments;
Register* extraArguments;
OwnArrayPtr<bool> deletedArguments;
};
// ECMA 10.1.8
-Arguments::Arguments(ExecState* exec, JSFunction* function, JSActivation* activation, int firstArgumentIndex, Register* argv, int argc)
+Arguments::Arguments(ExecState* exec, JSFunction* function, JSActivation* activation, int firstParameterIndex, Register* argv, int argc)
: JSObject(exec->lexicalGlobalObject()->argumentsStructure())
- , d(new ArgumentsData(activation, function->numParameters(), firstArgumentIndex, argc, function))
+ , d(new ArgumentsData(activation, function->numParameters(), firstParameterIndex, argc, function))
{
ASSERT(activation);
}
if (d->numParameters == d->numArguments) {
- args.initialize(&d->activation->registerAt(d->firstArgumentIndex), d->numArguments);
+ args.initialize(&d->activation->registerAt(d->firstParameterIndex), d->numArguments);
return;
}
unsigned parametersLength = min(d->numParameters, d->numArguments);
unsigned i = 0;
for (; i < parametersLength; ++i)
- args.append(d->activation->uncheckedSymbolTableGetValue(d->firstArgumentIndex + i));
+ args.append(d->activation->uncheckedSymbolTableGetValue(d->firstParameterIndex + i));
for (; i < d->numArguments; ++i)
args.append(d->extraArguments[i - d->numParameters].getJSValue());
return;
unsigned i = 0;
for (; i < parametersLength; ++i) {
if (!d->deletedArguments[i])
- args.append(d->activation->uncheckedSymbolTableGetValue(d->firstArgumentIndex + i));
+ args.append(d->activation->uncheckedSymbolTableGetValue(d->firstParameterIndex + i));
else
args.append(get(exec, i));
}
{
if (i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) {
if (i < d->numParameters)
- d->activation->uncheckedSymbolTableGet(d->firstArgumentIndex + i, slot);
+ d->activation->uncheckedSymbolTableGet(d->firstParameterIndex + i, slot);
else
slot.setValue(d->extraArguments[i - d->numParameters].getJSValue());
return true;
unsigned i = propertyName.toArrayIndex(&isArrayIndex);
if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) {
if (i < d->numParameters)
- d->activation->uncheckedSymbolTableGet(d->firstArgumentIndex + i, slot);
+ d->activation->uncheckedSymbolTableGet(d->firstParameterIndex + i, slot);
else
slot.setValue(d->extraArguments[i - d->numParameters].getJSValue());
return true;
{
if (i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) {
if (i < d->numParameters)
- d->activation->uncheckedSymbolTablePut(d->firstArgumentIndex + i, value);
+ d->activation->uncheckedSymbolTablePut(d->firstParameterIndex + i, value);
else
d->extraArguments[i - d->numParameters] = value;
return;
unsigned i = propertyName.toArrayIndex(&isArrayIndex);
if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) {
if (i < d->numParameters)
- d->activation->uncheckedSymbolTablePut(d->firstArgumentIndex + i, value);
+ d->activation->uncheckedSymbolTablePut(d->firstParameterIndex + i, value);
else
d->extraArguments[i - d->numParameters] = value;
return;
namespace JSC {
-Register* DebuggerCallFrame::callFrame() const
-{
- return m_registers - m_codeBlock->numLocals - RegisterFile::CallFrameHeaderSize;
-}
-
const UString* DebuggerCallFrame::functionName() const
{
if (!m_codeBlock)
return 0;
- JSFunction* function = static_cast<JSFunction*>(callFrame()[RegisterFile::Callee].getJSValue());
+ JSFunction* function = static_cast<JSFunction*>(m_registers[RegisterFile::Callee].getJSValue());
if (!function)
return 0;
return &function->name(m_exec);
DebuggerCallFrame::Type DebuggerCallFrame::type() const
{
- if (callFrame()[RegisterFile::Callee].getJSValue())
+ if (m_registers[RegisterFile::Callee].getJSValue())
return FunctionType;
return ProgramType;
JSValue* exception() const { return m_exception; }
private:
- Register* callFrame() const;
-
ExecState* m_exec;
JSGlobalObject* m_dynamicGlobalObject;
const CodeBlock* m_codeBlock;
delete d();
}
+void JSActivation::mark()
+{
+ Base::mark();
+
+ if (d()->argumentsObject)
+ d()->argumentsObject->mark();
+
+ Register* registerArray = d()->registerArray.get();
+ if (!registerArray)
+ return;
+
+ size_t numParametersMinusThis = d()->functionBody->generatedByteCode().numParameters - 1;
+
+ size_t i = 0;
+ size_t count = numParametersMinusThis;
+ for ( ; i < count; ++i) {
+ Register& r = registerArray[i];
+ if (!r.marked())
+ r.mark();
+ }
+
+ size_t numVars = d()->functionBody->generatedByteCode().numVars;
+
+ // Skip the call frame, which sits between the parameters and vars.
+ i += RegisterFile::CallFrameHeaderSize;
+ count += RegisterFile::CallFrameHeaderSize + numVars;
+
+ for ( ; i < count; ++i) {
+ Register& r = registerArray[i];
+ if (!r.marked())
+ r.mark();
+ }
+}
+
void JSActivation::copyRegisters()
{
- int numLocals = d()->functionBody->generatedByteCode().numLocals;
+ ASSERT(!d()->registerArray);
+ ASSERT(!d()->registerArraySize);
+
+ size_t numParametersMinusThis = d()->functionBody->generatedByteCode().numParameters - 1;
+ size_t numVars = d()->functionBody->generatedByteCode().numVars;
+ size_t numLocals = numVars + numParametersMinusThis;
+
if (!numLocals)
return;
- copyRegisterArray(d()->registers - numLocals, numLocals);
+ int registerOffset = numParametersMinusThis + RegisterFile::CallFrameHeaderSize;
+ size_t registerArraySize = numLocals + RegisterFile::CallFrameHeaderSize;
+
+ Register* registerArray = copyRegisterArray(d()->registers - registerOffset, registerArraySize);
+ setRegisters(registerArray + registerOffset, registerArray, registerArraySize);
}
bool JSActivation::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
return exec->globalThisValue();
}
-void JSActivation::mark()
-{
- Base::mark();
-
- if (d()->argumentsObject)
- d()->argumentsObject->mark();
-}
-
bool JSActivation::isActivationObject() const
{
return true;
Arguments* JSActivation::createArgumentsObject(ExecState* exec)
{
- Register* callFrame = d()->registers - d()->functionBody->generatedByteCode().numLocals - RegisterFile::CallFrameHeaderSize;
-
JSFunction* function;
Register* argv;
int argc;
- exec->machine()->getArgumentsData(callFrame, function, argv, argc);
+ int firstParameterIndex;
+ exec->machine()->getArgumentsData(d()->registers, function, firstParameterIndex, argv, argc);
- int firstArgumentIndex = -d()->functionBody->generatedByteCode().numLocals + 1;
- return new (exec) Arguments(exec, function, this, firstArgumentIndex, argv, argc);
+ return new (exec) Arguments(exec, function, this, firstParameterIndex, argv, argc);
}
} // namespace JSC
JSActivation(ExecState* exec, PassRefPtr<FunctionBodyNode>, Register*);
virtual ~JSActivation();
+ virtual void mark();
+
virtual bool isActivationObject() const;
virtual bool isDynamicScope() const;
virtual JSObject* toThisObject(ExecState*) const;
- virtual void mark();
-
void copyRegisters();
virtual const ClassInfo* classInfo() const { return &info; }
ASSERT(!hasCustomProperties());
symbolTable().clear();
- setRegisterArray(0, 0);
+ setRegisters(0, 0, 0);
ExecState* exec = d()->globalExec.get();
// No need to mark the other structures, because their prototypes are all
// guaranteed to be referenced elsewhere.
+
+ Register* registerArray = d()->registerArray.get();
+ if (!registerArray)
+ return;
+
+ size_t size = d()->registerArraySize;
+ for (size_t i = 0; i < size; ++i) {
+ Register& r = registerArray[i];
+ if (!r.marked())
+ r.mark();
+ }
}
JSGlobalObject* JSGlobalObject::toGlobalObject(ExecState*) const
d()->registers = 0;
return;
}
- copyRegisterArray(registerFile.lastGlobal(), numGlobals);
+
+ Register* registerArray = copyRegisterArray(registerFile.lastGlobal(), numGlobals);
+ setRegisters(registerArray + numGlobals, registerArray, numGlobals);
}
void JSGlobalObject::copyGlobalsTo(RegisterFile& registerFile)
if (d()->registerArray) {
memcpy(registerFile.base() - d()->registerArraySize, d()->registerArray.get(), d()->registerArraySize * sizeof(Register));
- setRegisterArray(0, 0);
+ setRegisters(registerFile.base(), 0, 0);
}
-
- d()->registers = registerFile.base();
}
void* JSGlobalObject::operator new(size_t size, JSGlobalData* globalData)
public:
virtual ~JSGlobalObject();
+ virtual void mark();
+
virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&);
virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&, bool& slotIsWriteable);
virtual void put(ExecState*, const Identifier&, JSValue*, PutPropertySlot&);
ScopeChain& globalScopeChain() { return d()->globalScopeChain; }
- virtual void mark();
-
virtual bool isGlobalObject() const { return true; }
virtual JSGlobalObject* toGlobalObject(ExecState*) const;
inline void JSGlobalObject::addStaticGlobals(GlobalPropertyInfo* globals, int count)
{
- size_t registerArraySize = d()->registerArraySize;
- Register* registerArray = new Register[registerArraySize + count];
+ size_t oldSize = d()->registerArraySize;
+ size_t newSize = oldSize + count;
+ Register* registerArray = new Register[newSize];
if (d()->registerArray)
- memcpy(registerArray + count, d()->registerArray.get(), registerArraySize * sizeof(Register));
- setRegisterArray(registerArray, registerArraySize + count);
+ memcpy(registerArray + count, d()->registerArray.get(), oldSize * sizeof(Register));
+ setRegisters(registerArray + newSize, registerArray, newSize);
- for (int i = 0, index = -static_cast<int>(registerArraySize) - 1; i < count; ++i, --index) {
+ for (int i = 0, index = -static_cast<int>(oldSize) - 1; i < count; ++i, --index) {
GlobalPropertyInfo& global = globals[i];
ASSERT(global.attributes & DontDelete);
SymbolTableEntry newEntry(index, global.attributes);
ASSERT_CLASS_FITS_IN_CELL(JSStaticScopeObject);
+void JSStaticScopeObject::mark()
+{
+ JSVariableObject::mark();
+
+ if (!d()->registerStore.marked())
+ d()->registerStore.mark();
+}
+
JSObject* JSStaticScopeObject::toThisObject(ExecState* exec) const
{
return exec->globalThisValue();
JSStaticScopeObject::~JSStaticScopeObject()
{
- ASSERT(d);
- delete static_cast<JSStaticScopeObjectData*>(d);
+ ASSERT(d());
+ delete d();
}
inline bool JSStaticScopeObject::getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot& slot)
JSStaticScopeObject(ExecState* exec, const Identifier& ident, JSValue* value, unsigned attributes)
: JSVariableObject(exec->globalData().nullProtoStructureID, new JSStaticScopeObjectData())
{
- JSStaticScopeObjectData* data = static_cast<JSStaticScopeObjectData*>(d);
- data->registerStore = value;
+ d()->registerStore = value;
symbolTable().add(ident.ustring().rep(), SymbolTableEntry(-1, attributes));
}
virtual ~JSStaticScopeObject();
+ virtual void mark();
bool isDynamicScope() const;
virtual JSObject* toThisObject(ExecState*) const;
virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&);
virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&, bool& slotIsWriteable);
virtual void put(ExecState*, const Identifier&, JSValue*, PutPropertySlot&);
void putWithAttributes(ExecState*, const Identifier&, JSValue*, unsigned attributes);
+
+ private:
+ JSStaticScopeObjectData* d() { return static_cast<JSStaticScopeObjectData*>(JSVariableObject::d); }
};
}
return JSObject::getPropertyAttributes(exec, propertyName, attributes);
}
-void JSVariableObject::mark()
-{
- JSObject::mark();
-
- if (!d->registerArray)
- return;
-
- Register* end = d->registerArray.get() + d->registerArraySize;
- for (Register* it = d->registerArray.get(); it != end; ++it)
- if (!(*it).marked())
- (*it).mark();
-}
-
bool JSVariableObject::isVariableObject() const
{
return true;
}
-void JSVariableObject::copyRegisterArray(Register* src, size_t count)
+Register* JSVariableObject::copyRegisterArray(Register* src, size_t count)
{
- ASSERT(!d->registerArray);
-
Register* registerArray = new Register[count];
memcpy(registerArray, src, count * sizeof(Register));
- setRegisterArray(registerArray, count);
+ return registerArray;
}
-void JSVariableObject::setRegisterArray(Register* registerArray, size_t count)
+void JSVariableObject::setRegisters(Register* r, Register* registerArray, size_t count)
{
if (registerArray != d->registerArray.get())
d->registerArray.set(registerArray);
d->registerArraySize = count;
- d->registers = registerArray + count;
+ d->registers = r;
}
} // namespace JSC
virtual bool deleteProperty(ExecState*, const Identifier&);
virtual void getPropertyNames(ExecState*, PropertyNameArray&);
- virtual void mark();
virtual bool isVariableObject() const;
virtual bool isDynamicScope() const = 0;
}
SymbolTable* symbolTable; // Maps name -> offset from "r" in register file.
- Register* registers; // Pointers to the register past the end of local storage. (Local storage indexes are negative.)
+ Register* registers; // "r" in the register file.
OwnArrayPtr<Register> registerArray; // Independent copy of registers, used when a variable object copies its registers out of the register file.
size_t registerArraySize;
{
}
- void copyRegisterArray(Register* src, size_t count);
- void setRegisterArray(Register* registerArray, size_t count);
+ Register* copyRegisterArray(Register* src, size_t count);
+ void setRegisters(Register* r, Register* registerArray, size_t count);
bool symbolTableGet(const Identifier&, PropertySlot&);
bool symbolTableGet(const Identifier&, PropertySlot&, bool& slotIsWriteable);
static ALWAYS_INLINE int missingSymbolMarker() { return std::numeric_limits<int>::max(); }
// The bit twiddling in this class assumes that every register index is a
- // reasonably small negative number, and therefore has its high two bits set.
+ // reasonably small positive or negative number, and therefore has its high
+ // four bits all set or all unset.
struct SymbolTableEntry {
SymbolTableEntry()
- : rawValue(0)
+ : m_bits(0)
{
}
-
+
SymbolTableEntry(int index)
{
- ASSERT(index & 0x80000000);
- ASSERT(index & 0x40000000);
-
- rawValue = index & ~0x80000000 & ~0x40000000;
+ ASSERT(isValidIndex(index));
+ pack(index, false, false);
}
-
+
SymbolTableEntry(int index, unsigned attributes)
{
- ASSERT(index & 0x80000000);
- ASSERT(index & 0x40000000);
-
- rawValue = index;
-
- if (!(attributes & ReadOnly))
- rawValue &= ~0x80000000;
-
- if (!(attributes & DontEnum))
- rawValue &= ~0x40000000;
+ ASSERT(isValidIndex(index));
+ pack(index, attributes & ReadOnly, attributes & DontEnum);
}
-
+
bool isNull() const
{
- return !rawValue;
+ return !m_bits;
}
int getIndex() const
{
- ASSERT(!isNull());
- return rawValue | 0x80000000 | 0x40000000;
+ return m_bits >> FlagBits;
}
unsigned getAttributes() const
{
unsigned attributes = 0;
-
- if (rawValue & 0x80000000)
+ if (m_bits & ReadOnlyFlag)
attributes |= ReadOnly;
-
- if (rawValue & 0x40000000)
+ if (m_bits & DontEnumFlag)
attributes |= DontEnum;
-
return attributes;
}
void setAttributes(unsigned attributes)
{
- rawValue = getIndex();
-
- if (!(attributes & ReadOnly))
- rawValue &= ~0x80000000;
-
- if (!(attributes & DontEnum))
- rawValue &= ~0x40000000;
+ pack(getIndex(), attributes & ReadOnly, attributes & DontEnum);
}
bool isReadOnly() const
{
- return rawValue & 0x80000000;
+ return m_bits & ReadOnlyFlag;
+ }
+
+ private:
+ static const unsigned ReadOnlyFlag = 0x1;
+ static const unsigned DontEnumFlag = 0x2;
+ static const unsigned NotNullFlag = 0x4;
+ static const unsigned FlagBits = 3;
+
+ void pack(int index, bool readOnly, bool dontEnum)
+ {
+ m_bits = (index << FlagBits) | NotNullFlag;
+ if (readOnly)
+ m_bits |= ReadOnlyFlag;
+ if (dontEnum)
+ m_bits |= DontEnumFlag;
+ }
+
+ bool isValidIndex(int index)
+ {
+ return ((index << FlagBits) >> FlagBits) == index;
}
- int rawValue;
+ int m_bits;
};
struct SymbolTableIndexHashTraits {
typedef SymbolTableEntry TraitType;
static SymbolTableEntry emptyValue() { return SymbolTableEntry(); }
- static const bool emptyValueIsZero = false;
+ static const bool emptyValueIsZero = true;
static const bool needsDestruction = false;
};
RegisterID* ResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
- if (RegisterID* local = generator.registerForLocal(m_ident)) {
+ if (RegisterID* local = generator.registerFor(m_ident)) {
if (dst == ignoredResult())
return 0;
return generator.moveToDestinationIfNeeded(dst, local);
RegisterID* NewExprNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
- RefPtr<RegisterID> r0 = generator.emitNode(m_expr.get());
- return generator.emitConstruct(generator.finalDestination(dst, r0.get()), r0.get(), m_args.get(), m_divot, m_startOffset, m_endOffset);
+ RefPtr<RegisterID> func = generator.emitNode(m_expr.get());
+ return generator.emitConstruct(generator.finalDestination(dst), func.get(), m_args.get(), m_divot, m_startOffset, m_endOffset);
}
RegisterID* EvalFunctionCallNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
RefPtr<RegisterID> base = generator.tempDestination(dst);
- RegisterID* func = generator.newTemporary();
- generator.emitResolveWithBase(base.get(), func, generator.propertyNames().eval);
- return generator.emitCallEval(generator.finalDestination(dst, base.get()), func, base.get(), m_args.get(), m_divot, m_startOffset, m_endOffset);
+ RefPtr<RegisterID> func = generator.newTemporary();
+ generator.emitResolveWithBase(base.get(), func.get(), generator.propertyNames().eval);
+ return generator.emitCallEval(generator.finalDestination(dst, base.get()), func.get(), base.get(), m_args.get(), m_divot, m_startOffset, m_endOffset);
}
RegisterID* FunctionCallValueNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
- RegisterID* func = generator.emitNode(m_expr.get());
- return generator.emitCall(generator.finalDestination(dst), func, 0, m_args.get(), m_divot, m_startOffset, m_endOffset);
+ RefPtr<RegisterID> func = generator.emitNode(m_expr.get());
+ return generator.emitCall(generator.finalDestination(dst), func.get(), 0, m_args.get(), m_divot, m_startOffset, m_endOffset);
}
RegisterID* FunctionCallResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
- if (RegisterID* local = generator.registerForLocal(m_ident))
- return generator.emitCall(generator.finalDestination(dst), local, 0, m_args.get(), m_divot, m_startOffset, m_endOffset);
+ if (RefPtr<RegisterID> local = generator.registerFor(m_ident))
+ return generator.emitCall(generator.finalDestination(dst), local.get(), 0, m_args.get(), m_divot, m_startOffset, m_endOffset);
int index = 0;
size_t depth = 0;
JSValue* globalObject = 0;
if (generator.findScopedProperty(m_ident, index, depth, false, globalObject) && index != missingSymbolMarker()) {
- RegisterID* func = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject);
- return generator.emitCall(generator.finalDestination(dst), func, 0, m_args.get(), m_divot, m_startOffset, m_endOffset);
+ RefPtr<RegisterID> func = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject);
+ return generator.emitCall(generator.finalDestination(dst), func.get(), 0, m_args.get(), m_divot, m_startOffset, m_endOffset);
}
RefPtr<RegisterID> base = generator.tempDestination(dst);
- RegisterID* func = generator.newTemporary();
+ RefPtr<RegisterID> func = generator.newTemporary();
int identifierStart = m_divot - m_startOffset;
generator.emitExpressionInfo(identifierStart + m_ident.size(), m_ident.size(), 0);
- generator.emitResolveFunction(base.get(), func, m_ident);
- return generator.emitCall(generator.finalDestination(dst, base.get()), func, base.get(), m_args.get(), m_divot, m_startOffset, m_endOffset);
+ generator.emitResolveFunction(base.get(), func.get(), m_ident);
+ return generator.emitCall(generator.finalDestination(dst, base.get()), func.get(), base.get(), m_args.get(), m_divot, m_startOffset, m_endOffset);
}
RegisterID* FunctionCallBracketNode::emitCode(CodeGenerator& generator, RegisterID* dst)
RefPtr<RegisterID> base = generator.emitNode(m_base.get());
RegisterID* property = generator.emitNode(m_subscript.get());
generator.emitExpressionInfo(m_divot - m_subexpressionDivotOffset, m_startOffset - m_subexpressionDivotOffset, m_subexpressionEndOffset);
- RegisterID* function = generator.emitGetByVal(generator.newTemporary(), base.get(), property);
- return generator.emitCall(generator.finalDestination(dst, base.get()), function, base.get(), m_args.get(), m_divot, m_startOffset, m_endOffset);
+ RefPtr<RegisterID> function = generator.emitGetByVal(generator.newTemporary(), base.get(), property);
+ return generator.emitCall(generator.finalDestination(dst, base.get()), function.get(), base.get(), m_args.get(), m_divot, m_startOffset, m_endOffset);
}
RegisterID* FunctionCallDotNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
RefPtr<RegisterID> base = generator.emitNode(m_base.get());
generator.emitExpressionInfo(m_divot - m_subexpressionDivotOffset, m_startOffset - m_subexpressionDivotOffset, m_subexpressionEndOffset);
- RegisterID* function = generator.emitGetById(generator.newTemporary(), base.get(), m_ident);
- return generator.emitCall(generator.finalDestination(dst, base.get()), function, base.get(), m_args.get(), m_divot, m_startOffset, m_endOffset);
+ RefPtr<RegisterID> function = generator.emitGetById(generator.newTemporary(), base.get(), m_ident);
+ return generator.emitCall(generator.finalDestination(dst, base.get()), function.get(), base.get(), m_args.get(), m_divot, m_startOffset, m_endOffset);
}
// ------------------------------ PostfixResolveNode ----------------------------------
RegisterID* PostfixResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
- if (RegisterID* local = generator.registerForLocal(m_ident)) {
+ if (RegisterID* local = generator.registerFor(m_ident)) {
if (generator.isLocalConstant(m_ident)) {
if (dst == ignoredResult())
return 0;
RegisterID* DeleteResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
- if (generator.registerForLocal(m_ident))
+ if (generator.registerFor(m_ident))
return generator.emitUnexpectedLoad(generator.finalDestination(dst), false);
generator.emitExpressionInfo(m_divot, m_startOffset, m_endOffset);
RegisterID* TypeOfResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
- if (RegisterID* local = generator.registerForLocal(m_ident)) {
+ if (RegisterID* local = generator.registerFor(m_ident)) {
if (dst == ignoredResult())
return 0;
return generator.emitTypeOf(generator.finalDestination(dst), local);
RegisterID* PrefixResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
- if (RegisterID* local = generator.registerForLocal(m_ident)) {
+ if (RegisterID* local = generator.registerFor(m_ident)) {
if (generator.isLocalConstant(m_ident)) {
if (dst == ignoredResult())
return 0;
RegisterID* ReadModifyResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
- if (RegisterID* local = generator.registerForLocal(m_ident)) {
+ if (RegisterID* local = generator.registerFor(m_ident)) {
if (generator.isLocalConstant(m_ident)) {
RegisterID* src2 = generator.emitNode(m_right.get());
return emitReadModifyAssignment(generator, generator.finalDestination(dst), local, src2, m_operator);
RegisterID* AssignResolveNode::emitCode(CodeGenerator& generator, RegisterID* dst)
{
- if (RegisterID* local = generator.registerForLocal(m_ident)) {
+ if (RegisterID* local = generator.registerFor(m_ident)) {
if (generator.isLocalConstant(m_ident))
return generator.emitNode(dst, m_right.get());
RegisterID* ConstDeclNode::emitCodeSingle(CodeGenerator& generator)
{
- if (RegisterID* local = generator.registerForLocalConstInit(m_ident)) {
+ if (RegisterID* local = generator.constRegisterFor(m_ident)) {
if (!m_init)
return local;
RegisterID* propertyName;
if (m_lexpr->isResolveNode()) {
const Identifier& ident = static_cast<ResolveNode*>(m_lexpr.get())->identifier();
- propertyName = generator.registerForLocal(ident);
+ propertyName = generator.registerFor(ident);
if (!propertyName) {
propertyName = generator.newTemporary();
RefPtr<RegisterID> protect = propertyName;
emitModRm_opm(GROUP5_OP_JMPN, base, offset);
}
- void call_r(RegisterID dst)
- {
- m_buffer->putByte(OP_GROUP5_Ev);
- emitModRm_opr(GROUP5_OP_CALLN, dst);
- }
-
// Opaque label types
class JmpSrc {
return JmpSrc(m_buffer->getOffset());
}
+ JmpSrc emitCall(RegisterID dst)
+ {
+ m_buffer->putByte(OP_GROUP5_Ev);
+ emitModRm_opr(GROUP5_OP_CALLN, dst);
+ return JmpSrc(m_buffer->getOffset());
+ }
+
JmpDst label()
{
return JmpDst(m_buffer->getOffset());