Ignore:
Timestamp:
Sep 23, 2008, 5:27:18 PM (17 years ago)
Author:
[email protected]
Message:

2008-09-23 Geoffrey Garen <[email protected]>

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:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/VM/CodeGenerator.cpp

    r36751 r36821  
    130130void CodeGenerator::generate()
    131131{
    132     m_codeBlock->numLocals = m_codeBlock->numVars + m_codeBlock->numParameters;
    133132    m_codeBlock->thisRegister = m_thisRegister.index();
     133
    134134    if (m_shouldEmitDebugHooks)
    135135        m_codeBlock->needsFullScopeChain = true;
     
    153153bool CodeGenerator::addVar(const Identifier& ident, bool isConstant, RegisterID*& r0)
    154154{
    155     int index = m_nextVar;
     155    int index = m_calleeRegisters.size();
     156    SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0);
     157    pair<SymbolTable::iterator, bool> result = symbolTable().add(ident.ustring().rep(), newEntry);
     158
     159    if (!result.second) {
     160        r0 = &registerFor(result.first->second.getIndex());
     161        return false;
     162    }
     163
     164    ++m_codeBlock->numVars;
     165    r0 = newRegister();
     166    return true;
     167}
     168
     169bool CodeGenerator::addGlobalVar(const Identifier& ident, bool isConstant, RegisterID*& r0)
     170{
     171    int index = m_nextGlobal;
    156172    SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0);
    157173    pair<SymbolTable::iterator, bool> result = symbolTable().add(ident.ustring().rep(), newEntry);
     
    160176        index = result.first->second.getIndex();
    161177    else {
    162         --m_nextVar;
    163         ++m_codeBlock->numVars;
    164 
    165         m_locals.append(index);
    166     }
    167 
    168     r0 = &m_locals[localsIndex(index)];
     178        --m_nextGlobal;
     179        m_globals.append(index + m_globalVarStorageOffset);
     180    }
     181
     182    r0 = &registerFor(index);
    169183    return result.second;
    170184}
    171185
    172 bool CodeGenerator::addGlobalVar(const Identifier& ident, bool isConstant, RegisterID*& r0)
    173 {
    174     int index = m_nextVar;
    175     SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0);
    176     pair<SymbolTable::iterator, bool> result = symbolTable().add(ident.ustring().rep(), newEntry);
    177 
    178     if (!result.second)
    179         index = result.first->second.getIndex();
    180     else {
    181         --m_nextVar;
    182         m_locals.append(index + m_globalVarStorageOffset);
    183     }
    184 
    185     r0 = &m_locals[localsIndex(index)];
    186     return result.second;
     186void CodeGenerator::allocateConstants(size_t count)
     187{
     188    m_codeBlock->numConstants = count;
     189    if (!count)
     190        return;
     191   
     192    m_nextConstant = m_calleeRegisters.size();
     193
     194    for (size_t i = 0; i < count; ++i)
     195        newRegister();
     196    m_lastConstant = &m_calleeRegisters.last();
    187197}
    188198
     
    198208    , m_codeType(GlobalCode)
    199209    , m_continueDepth(0)
    200     , m_nextVar(-1)
     210    , m_nextGlobal(-1)
    201211    , m_globalData(&scopeChain.globalObject()->globalExec()->globalData())
    202212    , m_lastOpcodeID(op_end)
    203213{
     214    emitOpcode(op_init);
    204215    codeBlock->globalData = m_globalData;
    205216
    206217    // FIXME: Move code that modifies the global object to Machine::execute.
    207218   
    208     m_codeBlock->numConstants = programNode->neededConstants();
    209     m_codeBlock->numVars = 1; // Allocate space for "this"
     219    m_codeBlock->numParameters = 1; // Allocate space for "this"
    210220
    211221    JSGlobalObject* globalObject = scopeChain.globalObject();
     
    214224   
    215225    // Shift register indexes in generated code to elide registers allocated by intermediate stack frames.
    216     m_globalVarStorageOffset = -1 - RegisterFile::CallFrameHeaderSize - registerFile->size();
     226    m_globalVarStorageOffset = -RegisterFile::CallFrameHeaderSize - m_codeBlock->numParameters - registerFile->size();
    217227
    218228    // Add previously defined symbols to bookkeeping.
    219     m_locals.resize(symbolTable->size());
     229    m_globals.resize(symbolTable->size());
    220230    SymbolTable::iterator end = symbolTable->end();
    221231    for (SymbolTable::iterator it = symbolTable->begin(); it != end; ++it)
    222         m_locals[localsIndex(it->second.getIndex())].setIndex(it->second.getIndex() + m_globalVarStorageOffset);
     232        registerFor(it->second.getIndex()).setIndex(it->second.getIndex() + m_globalVarStorageOffset);
    223233       
    224234    BatchedTransitionOptimizer optimizer(globalObject);
     
    227237    if (canOptimizeNewGlobals) {
    228238        // Shift new symbols so they get stored prior to existing symbols.
    229         m_nextVar -= symbolTable->size();
     239        m_nextGlobal -= symbolTable->size();
    230240
    231241        for (size_t i = 0; i < functionStack.size(); ++i) {
     
    235245        }
    236246
    237         for (size_t i = 0; i < varStack.size(); ++i) {
     247        Vector<RegisterID*, 32> newVars;
     248        for (size_t i = 0; i < varStack.size(); ++i)
    238249            if (!globalObject->hasProperty(exec, varStack[i].first))
    239                 emitLoad(addGlobalVar(varStack[i].first, varStack[i].second & DeclarationStacks::IsConstant), jsUndefined());
    240         }
     250                newVars.append(addGlobalVar(varStack[i].first, varStack[i].second & DeclarationStacks::IsConstant));
     251
     252        allocateConstants(programNode->neededConstants());
     253
     254        for (size_t i = 0; i < newVars.size(); ++i)
     255            emitLoad(newVars[i], jsUndefined());
    241256    } else {
    242257        for (size_t i = 0; i < functionStack.size(); ++i) {
     
    252267            globalObject->putWithAttributes(exec, varStack[i].first, jsUndefined(), attributes);
    253268        }
     269
     270        allocateConstants(programNode->neededConstants());
    254271    }
    255272}
     
    265282    , m_codeType(FunctionCode)
    266283    , m_continueDepth(0)
    267     , m_nextVar(-1)
    268284    , m_globalData(&scopeChain.globalObject()->globalExec()->globalData())
    269285    , m_lastOpcodeID(op_end)
    270286{
    271     emitOpcode(op_initialise_locals);
     287    emitOpcode(op_init);
    272288    codeBlock->globalData = m_globalData;
    273 
    274     m_codeBlock->numConstants = functionBody->neededConstants();
    275289
    276290    const Node::FunctionStack& functionStack = functionBody->functionStack();
     
    292306
    293307    Vector<Identifier>& parameters = functionBody->parameters();
    294     m_nextParameter = m_nextVar - parameters.size(); // parameters are allocated prior to vars
    295     m_locals.resize(localsIndex(m_nextParameter) + 1); // localsIndex of 0 => m_locals size of 1
     308    m_nextParameter = -RegisterFile::CallFrameHeaderSize - parameters.size() - 1;
     309    m_parameters.resize(1 + parameters.size()); // reserve space for "this"
    296310
    297311    // Add "this" as a parameter
     
    302316    for (size_t i = 0; i < parameters.size(); ++i)
    303317        addParameter(parameters[i]);
     318
     319    allocateConstants(functionBody->neededConstants());
    304320}
    305321
     
    318334    , m_lastOpcodeID(op_end)
    319335{
    320     emitOpcode(op_initialise_locals);
     336    emitOpcode(op_init);
    321337    codeBlock->globalData = m_globalData;
    322 
    323     m_codeBlock->numConstants = evalNode->neededConstants();
    324     m_codeBlock->numVars = 1; // Allocate space for "this"
    325 }
    326 
    327 CodeGenerator::~CodeGenerator()
    328 {
     338    m_codeBlock->numParameters = 1; // Allocate space for "this"
     339
     340    allocateConstants(evalNode->neededConstants());
    329341}
    330342
    331343RegisterID* CodeGenerator::addParameter(const Identifier& ident)
    332344{
    333     // Parameters overwrite var declarations, but not function declarations,
    334     // in the symbol table.
     345    // Parameters overwrite var declarations, but not function declarations.
    335346    RegisterID* result = 0;
    336347    UString::Rep* rep = ident.ustring().rep();
    337348    if (!m_functions.contains(rep)) {
    338349        symbolTable().set(rep, m_nextParameter);
    339         m_locals[localsIndex(m_nextParameter)].setIndex(m_nextParameter);
    340         result = &(m_locals[localsIndex(m_nextParameter)]);
     350        RegisterID& parameter = registerFor(m_nextParameter);
     351        parameter.setIndex(m_nextParameter);
     352        result = &parameter;
    341353    }
    342354
     
    348360}
    349361
    350 RegisterID* CodeGenerator::registerForLocal(const Identifier& ident)
     362RegisterID* CodeGenerator::registerFor(const Identifier& ident)
    351363{
    352364    if (m_codeType == FunctionCode && ident == propertyNames().arguments)
     
    363375        return 0;
    364376
    365     return &m_locals[localsIndex(entry.getIndex())];
    366 }
    367 
    368 RegisterID* CodeGenerator::registerForLocalConstInit(const Identifier& ident)
     377    return &registerFor(entry.getIndex());
     378}
     379
     380RegisterID* CodeGenerator::constRegisterFor(const Identifier& ident)
    369381{
    370382    if (m_codeType == EvalCode)
     
    374386    ASSERT(!entry.isNull());
    375387
    376     return &m_locals[localsIndex(entry.getIndex())];
     388    return &registerFor(entry.getIndex());
    377389}
    378390
     
    390402}
    391403
     404RegisterID* CodeGenerator::newRegister()
     405{
     406    m_calleeRegisters.append(m_calleeRegisters.size());
     407    m_codeBlock->numCalleeRegisters = max<int>(m_codeBlock->numCalleeRegisters, m_calleeRegisters.size());
     408    return &m_calleeRegisters.last();
     409}
     410
    392411RegisterID* CodeGenerator::newTemporary()
    393412{
    394413    // Reclaim free register IDs.
    395     while (m_temporaries.size() && !m_temporaries.last().refCount())
    396         m_temporaries.removeLast();
    397 
    398     // Allocate new register ID.
    399     m_temporaries.append(m_temporaries.size() + m_codeBlock->numConstants);
    400     m_codeBlock->numTemporaries = max<int>(m_codeBlock->numTemporaries, m_temporaries.size());
    401     return &m_temporaries.last();
     414    while (m_calleeRegisters.size() && !m_calleeRegisters.last().refCount())
     415        m_calleeRegisters.removeLast();
     416       
     417    RegisterID* result = newRegister();
     418    result->setTemporary();
     419    return result;
    402420}
    403421
    404422RegisterID* CodeGenerator::highestUsedRegister()
    405423{
    406     while (m_temporaries.size() < static_cast<unsigned>(m_codeBlock->numTemporaries))
    407         m_temporaries.append(m_temporaries.size());
    408     return &m_temporaries.last();
     424    size_t count = m_codeBlock->numCalleeRegisters;
     425    while (m_calleeRegisters.size() < count)
     426        newRegister();
     427    return &m_calleeRegisters.last();
    409428}
    410429
     
    580599RegisterID* CodeGenerator::addConstant(JSValue* v)
    581600{
    582     pair<JSValueMap::iterator, bool> result = m_jsValueMap.add(v, m_codeBlock->constantRegisters.size());
     601    pair<JSValueMap::iterator, bool> result = m_jsValueMap.add(v, m_nextConstant);
    583602    if (result.second) {
    584         m_constants.append(m_codeBlock->constantRegisters.size());
    585         m_constants.last().makeConstant();
     603        RegisterID& constant = m_calleeRegisters[m_nextConstant];
     604       
     605        ++m_nextConstant;
     606
    586607        m_codeBlock->constantRegisters.append(v);
    587         ASSERT(m_codeBlock->constantRegisters.size() <= (unsigned) m_codeBlock->numConstants);
    588         return &m_constants.last();
    589     }
    590 
    591     return &m_constants[result.first->second];
     608        return &constant;
     609    }
     610
     611    return &registerFor(result.first->second);
    592612}
    593613
     
    10901110{
    10911111    ASSERT(opcodeID == op_call || opcodeID == op_call_eval);
     1112    ASSERT(func->refCount());
     1113    ASSERT(!base || base->refCount());
    10921114   
    1093     // Ordinarily, we might ref "func" and "base", to avoid allocating new
    1094     // temporaries in the same registers. In this case, though, we actually
    1095     // want the call frame we allocate to overlap "func" and "base", if they're
    1096     // not otherwise referenced. op_call will read "func" and "base" before
    1097     // writing out the call frame, so this is safe.
    1098 
    1099     // Reserve space for call frame.
    1100     Vector<RefPtr<RegisterID>, RegisterFile::CallFrameHeaderSize> callFrame;
    1101     for (int i = 0; i < RegisterFile::CallFrameHeaderSize; ++i)
    1102         callFrame.append(newTemporary());
    1103 
    11041115    // Generate code for arguments.
    11051116    Vector<RefPtr<RegisterID>, 16> argv;
     
    11101121    }
    11111122
     1123    // Reserve space for call frame.
     1124    Vector<RefPtr<RegisterID>, RegisterFile::CallFrameHeaderSize> callFrame;
     1125    for (int i = 0; i < RegisterFile::CallFrameHeaderSize; ++i)
     1126        callFrame.append(newTemporary());
     1127
    11121128    emitExpressionInfo(divot, startOffset, endOffset);
    11131129    emitOpcode(opcodeID);
     
    11171133    instructions().append(argv[0]->index()); // argv
    11181134    instructions().append(argv.size()); // argc
     1135    instructions().append(argv[0]->index() + argv.size() + RegisterFile::CallFrameHeaderSize); // registerOffset
    11191136    return dst;
    11201137}
     
    11311148    ASSERT(func->refCount());
    11321149
    1133     // Reserve space for prototype
     1150    // Load prototype.
     1151    emitExpressionInfo(divot, startOffset, endOffset);
    11341152    RefPtr<RegisterID> funcProto = newTemporary();
    1135 
    1136     // Reserve space for call frame.
    1137     Vector<RefPtr<RegisterID>, RegisterFile::CallFrameHeaderSize> callFrame;
    1138     for (int i = 0; i < RegisterFile::CallFrameHeaderSize; ++i)
    1139         callFrame.append(newTemporary());
     1153    emitGetById(funcProto.get(), func, globalExec()->propertyNames().prototype);
    11401154
    11411155    // Generate code for arguments.
     
    11471161    }
    11481162
    1149     emitExpressionInfo(divot, startOffset, endOffset);
    1150     emitGetById(funcProto.get(), func, globalExec()->propertyNames().prototype);
     1163    // Reserve space for call frame.
     1164    Vector<RefPtr<RegisterID>, RegisterFile::CallFrameHeaderSize> callFrame;
     1165    for (int i = 0; i < RegisterFile::CallFrameHeaderSize; ++i)
     1166        callFrame.append(newTemporary());
    11511167
    11521168    emitExpressionInfo(divot, startOffset, endOffset);
     
    11571173    instructions().append(argv[0]->index()); // argv
    11581174    instructions().append(argv.size()); // argc
    1159    
     1175    instructions().append(argv[0]->index() + argv.size() + RegisterFile::CallFrameHeaderSize); // registerOffset
     1176
    11601177    emitOpcode(op_construct_verify);
    11611178    instructions().append(dst->index());
Note: See TracChangeset for help on using the changeset viewer.