Changeset 28884 in webkit for trunk/JavaScriptCore/kjs/nodes.cpp


Ignore:
Timestamp:
Dec 20, 2007, 1:32:06 AM (17 years ago)
Author:
[email protected]
Message:

JavaScriptCore:

Reviewed by Oliver Hunt.


Optimized global access to global variables, using a symbol table.


SunSpider reports a 1.5% overall speedup, a 6.2% speedup on 3d-morph,
and a whopping 33.1% speedup on bitops-bitwise-and.

  • API/JSCallbackObjectFunctions.h: Replaced calls to JSObject:: with calls to Base::, since JSObject is not always our base class. This was always a bug, but the bug is even more apparent after some of my changes.

(KJS::::staticFunctionGetter): Replaced use of getDirect with call to
getOwnPropertySlot. Global declarations are no longer stored in the
property map, so a call to getDirect is insufficient for finding
override properties.

  • API/testapi.c:
  • API/testapi.js: Added test for the getDirect change mentioned above.
  • kjs/ExecState.cpp:
  • kjs/ExecState.h: Dialed back the optimization to store a direct pointer to the localStorage buffer. One ExecState can grow the global object's localStorage without another ExecState's knowledge, so ExecState can't store a direct pointer to the localStorage buffer unless/until we invent a way to update all the relevant ExecStates.
  • kjs/JSGlobalObject.cpp: Inserted the symbol table into get and put operations. (KJS::JSGlobalObject::reset): Reset the symbol table and local storage, too. Also, clear the property map here, removing the need for a separate call.
  • kjs/JSVariableObject.cpp:
  • kjs/JSVariableObject.h: Added support for saving localStorage and the symbol table to the back/forward cache, and restoring them.
  • kjs/function.cpp: (KJS::GlobalFuncImp::callAsFunction): Renamed progNode to evalNode because it's an EvalNode, not a ProgramNode.
  • kjs/lookup.h: (KJS::cacheGlobalObject): Replaced put with faster putDirect, since that's how the rest of lookup.h works. putDirect is safe here because cacheGlobalObject is only used for objects whose names are not valid identifiers.
  • kjs/nodes.cpp: The good stuff!

(KJS::EvalNode::processDeclarations): Replaced hasProperty with
the new hasOwnProperty, which is slightly faster.

  • kjs/object.h: Nixed clearProperties because clear() does this job now.
  • kjs/property_map.cpp:
  • kjs/property_map.h: More back/forward cache support.


  • wtf/Vector.h: (WTF::::grow): Added fast non-branching grow function. I used it in an earlier version of this patch, even though it's not used anymore.

JavaScriptGlue:

Build fix.

  • ForwardingHeaders/wtf/VectorTraits.h: Added.

WebCore:

Reviewed by Oliver Hunt.

Build support:

  • ForwardingHeaders/kjs/SymbolTable.h: Added.
  • ForwardingHeaders/wtf/VectorTraits.h: Added.
  • bindings/js/JSDOMWindowCustom.cpp: (WebCore::JSDOMWindow::customGetOwnPropertySlot): Replaced use of getDirectLocation with getOwnPropertySlot. getDirectLocation is no longer valid, since global declarations are not stored in the property map.

(WebCore::JSDOMWindow::customPut): Replaced use of JSObject::put with
JSGlobalObject::put. JSObject::put is no longer valid, since global
declarations are not stored in the property map.

  • bindings/js/kjs_window.cpp: Replaced JSObject:: calls with Base:: calls, since JSObject is not our base class. This was always a bug, but the bug is even more apparent after some of my changes.

(KJS::Window::clear): Removed call to clearProperties because
JSGlobalObject::reset takes care of that now.

  • history/CachedPage.cpp:
  • history/CachedPage.h: Added support for saving a symbol table and localStorage to the page cache, and restoring it.

WebKit/mac:

Reviewed by Oliver Hunt.

Build fix.

  • ForwardingHeaders/kjs/SymbolTable.h: Added.
  • ForwardingHeaders/wtf/VectorTraits.h: Added.

LayoutTests:

Reviewed by Oliver Hunt.


Added some tests to verify some of the changes I made while optimizing
global access to global variables.

  • fast/dom/Window/resources/window-property-clearing-iframe0.html: Added.
  • fast/dom/Window/resources/window-property-clearing-iframe1.html: Added.
  • fast/dom/Window/window-property-clearing-expected.txt: Added.
  • fast/dom/Window/window-property-clearing.html: Added.
  • fast/dom/getter-on-window-object2-expected.txt: Added.
  • fast/dom/getter-on-window-object2.html: Added.

Checked in failing results for these const tests. The symbol table
optimization broke const. (We didn't know this before because our only
tests used global variables.)

  • fast/js/const-expected.txt:
  • fast/js/kde/const-expected.txt:
  • fast/js/resources/for-in-avoid-duplicates.js: Fixed a typo I noticed. Not related to this patch.
  • fast/dom/Window/window-property-shadowing.html: Changed this test to use "this" instead of "window". The fact that "window" worked before, despite an overriding / shadowing var declaration, was a bug.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/kjs/nodes.cpp

    r28855 r28884  
    34893489inline void VarDeclNode::evaluateSingle(ExecState* exec)
    34903490{
     3491    ASSERT(exec->variableObject()->hasOwnProperty(exec, ident) || exec->codeType() == EvalCode); // Guaranteed by processDeclarations.
    34913492    const ScopeChain& chain = exec->scopeChain();
    34923493    JSObject* variableObject = exec->variableObject();
     
    34963497    bool inGlobalScope = ++chain.begin() == chain.end();
    34973498
    3498     if (inGlobalScope && (init || !variableObject->getDirect(ident))) {
    3499         JSValue* val = init ? init->evaluate(exec) : jsUndefined();
    3500         int flags = Internal;
    3501         if (exec->codeType() != EvalCode)
    3502             flags |= DontDelete;
    3503         if (varType == VarDeclNode::Constant)
    3504             flags |= ReadOnly;
    3505         variableObject->putDirect(ident, val, flags);
    3506     } else if (init) {
    3507         JSValue* val = init->evaluate(exec);
    3508         KJS_CHECKEXCEPTIONVOID
     3499    if (init) {
     3500        if (inGlobalScope) {
     3501            JSValue* val = init->evaluate(exec);
     3502            int flags = Internal;
     3503            if (exec->codeType() != EvalCode)
     3504                flags |= DontDelete;
     3505            if (varType == VarDeclNode::Constant)
     3506                flags |= ReadOnly;
     3507            variableObject->put(exec, ident, val, flags);
     3508        } else {
     3509            JSValue* val = init->evaluate(exec);
     3510            KJS_CHECKEXCEPTIONVOID
     3511
     3512            // if the variable object is the top of the scope chain, then that must
     3513            // be where this variable is declared, processVarDecls would have put
     3514            // it there. Don't search the scope chain, to optimize this very common case.
     3515            if (chain.top() != variableObject)
     3516                return handleSlowCase(exec, chain, val);
     3517
     3518            unsigned flags = 0;
     3519            variableObject->getPropertyAttributes(ident, flags);
     3520            if (varType == VarDeclNode::Constant)
     3521                flags |= ReadOnly;
    35093522           
    3510         // if the variable object is the top of the scope chain, then that must
    3511         // be where this variable is declared, processVarDecls would have put
    3512         // it there. Don't search the scope chain, to optimize this very common case.
    3513         if (chain.top() != variableObject)
    3514             return handleSlowCase(exec, chain, val);
    3515 
    3516         unsigned flags = 0;
    3517         variableObject->getPropertyAttributes(ident, flags);
    3518         if (varType == VarDeclNode::Constant)
    3519             flags |= ReadOnly;
    3520        
    3521         ASSERT(variableObject->hasProperty(exec, ident));
    3522         variableObject->put(exec, ident, val, flags);
     3523            variableObject->put(exec, ident, val, flags);
     3524        }
    35233525    }
    35243526}
     
    42754277void FunctionBodyNode::initializeSymbolTable(ExecState* exec)
    42764278{
    4277     size_t i, size;
    4278     size_t count = 0;
    4279 
    4280     // The order of additions here implicitly enforces the mutual exclusion described in ECMA 10.1.3.
    4281     for (i = 0, size = m_varStack.size(); i < size; ++i) {
    4282         if (m_varStack[i]->ident != exec->propertyNames().arguments)
    4283             m_symbolTable.set(m_varStack[i]->ident.ustring().rep(), count);
    4284         count++;
    4285     }
    4286 
    4287     for (i = 0, size = m_parameters.size(); i < size; ++i)
    4288         m_symbolTable.set(m_parameters[i].ustring().rep(), count++);
    4289 
    4290     for (i = 0, size = m_functionStack.size(); i < size; ++i)
    4291         m_symbolTable.set(m_functionStack[i]->ident.ustring().rep(), count++);
    4292 }
    4293 
    4294 void FunctionBodyNode::optimizeVariableAccess()
     4279    SymbolTable& symbolTable = exec->variableObject()->symbolTable();
     4280    ASSERT(!symbolTable.size());
     4281
     4282    size_t localStorageIndex = 0;
     4283
     4284    for (size_t i = 0, size = m_parameters.size(); i < size; ++i, ++localStorageIndex) {
     4285        UString::Rep* rep = m_parameters[i].ustring().rep();
     4286        symbolTable.set(rep, localStorageIndex);
     4287    }
     4288
     4289    for (size_t i = 0, size = m_functionStack.size(); i < size; ++i, ++localStorageIndex) {
     4290        UString::Rep* rep = m_functionStack[i]->ident.ustring().rep();
     4291        symbolTable.set(rep, localStorageIndex);
     4292    }
     4293
     4294    for (size_t i = 0, size = m_varStack.size(); i < size; ++i, ++localStorageIndex) {
     4295        Identifier& ident = m_varStack[i]->ident;
     4296        if (ident == exec->propertyNames().arguments)
     4297            continue;
     4298        symbolTable.add(ident.ustring().rep(), localStorageIndex);
     4299    }
     4300}
     4301
     4302void ProgramNode::initializeSymbolTable(ExecState* exec)
     4303{
     4304    // If a previous script defined a symbol with the same name as one of our
     4305    // symbols, to avoid breaking previously optimized nodes, we need to reuse
     4306    // the symbol's existing storage index. So, we can't be as efficient as
     4307    // FunctionBodyNode::initializeSymbolTable, which knows that no bindings
     4308    // have yet been made.
     4309   
     4310    JSVariableObject* variableObject = exec->variableObject();
     4311    SymbolTable& symbolTable = variableObject->symbolTable();
     4312
     4313    size_t localStorageIndex = symbolTable.size();
     4314    size_t size;
     4315   
     4316    size = m_functionStack.size();
     4317    m_functionIndexes.resize(size);
     4318    for (size_t i = 0; i < size; ++i) {
     4319        UString::Rep* rep = m_functionStack[i]->ident.ustring().rep();
     4320        pair<SymbolTable::iterator, bool> result = symbolTable.add(rep, localStorageIndex);
     4321        m_functionIndexes[i] = result.first->second;
     4322        if (result.second)
     4323            ++localStorageIndex;
     4324    }
     4325
     4326    size = m_varStack.size();
     4327    m_varIndexes.resize(size);
     4328    for (size_t i = 0; i < size; ++i) {
     4329        const Identifier& ident = m_varStack[i]->ident;
     4330        if (variableObject->getDirect(ident)) {
     4331            m_varIndexes[i] = missingSymbolMarker(); // Signal not to initialize this declaration.
     4332            continue;
     4333        }
     4334
     4335        UString::Rep* rep = ident.ustring().rep();
     4336        pair<SymbolTable::iterator, bool> result = symbolTable.add(rep, localStorageIndex);
     4337        if (!result.second) {
     4338            m_varIndexes[i] = missingSymbolMarker(); // Signal not to initialize this declaration.
     4339            continue;
     4340        }
     4341        m_varIndexes[i] = result.first->second;
     4342        ++localStorageIndex;
     4343    }
     4344}
     4345
     4346void ScopeNode::optimizeVariableAccess(ExecState* exec)
    42954347{
    42964348    DeclarationStacks::NodeStack nodeStack;
     
    42994351        return;
    43004352   
     4353    SymbolTable& symbolTable = exec->variableObject()->symbolTable();
    43014354    while (true) {
    4302         node->optimizeVariableAccess(m_symbolTable, nodeStack);
     4355        node->optimizeVariableAccess(symbolTable, nodeStack);
    43034356       
    43044357        size_t size = nodeStack.size();
     
    43154368    if (!m_initialized) {
    43164369        initializeSymbolTable(exec);
    4317         optimizeVariableAccess();
     4370        optimizeVariableAccess(exec);
    43184371       
    43194372        m_initialized = true;
     
    43214374
    43224375    LocalStorage& localStorage = exec->variableObject()->localStorage();
     4376   
     4377    // We can't just resize localStorage here because that would temporarily
     4378    // leave uninitialized entries, which would crash GC during the mark phase.
    43234379    localStorage.reserveCapacity(m_varStack.size() + m_parameters.size() + m_functionStack.size());
    43244380   
    43254381    int minAttributes = Internal | DontDelete;
    43264382   
    4327     size_t i, size;
    4328 
    4329     // NOTE: Must match the order of addition in initializeSymbolTable().
    4330 
    4331     for (i = 0, size = m_varStack.size(); i < size; ++i) {
     4383    // In order for our localStorage indexes to be correct, we must match the
     4384    // order of addition in initializeSymbolTable().
     4385
     4386    const List& args = *exec->arguments();
     4387    for (size_t i = 0, size = m_parameters.size(); i < size; ++i)
     4388        localStorage.uncheckedAppend(LocalStorageEntry(args[i], DontDelete));
     4389
     4390    for (size_t i = 0, size = m_functionStack.size(); i < size; ++i) {
     4391        FuncDeclNode* node = m_functionStack[i];
     4392        localStorage.uncheckedAppend(LocalStorageEntry(node->makeFunction(exec), minAttributes));
     4393    }
     4394
     4395    for (size_t i = 0, size = m_varStack.size(); i < size; ++i) {
    43324396        VarDeclNode* node = m_varStack[i];
    43334397        int attributes = minAttributes;
    43344398        if (node->varType == VarDeclNode::Constant)
    43354399            attributes |= ReadOnly;
    4336         localStorage.append(LocalStorageEntry(jsUndefined(), attributes));
    4337     }
    4338 
    4339     const List& args = *exec->arguments();
    4340     for (i = 0, size = m_parameters.size(); i < size; ++i)
    4341         localStorage.append(LocalStorageEntry(args[i], DontDelete));
    4342 
    4343     for (i = 0, size = m_functionStack.size(); i < size; ++i) {
     4400        localStorage.uncheckedAppend(LocalStorageEntry(jsUndefined(), attributes));
     4401    }
     4402}
     4403
     4404static void gccIsCrazy() KJS_FAST_CALL;
     4405static void gccIsCrazy()
     4406{
     4407}
     4408
     4409void ProgramNode::processDeclarations(ExecState* exec)
     4410{
     4411    // If you remove this call, some SunSpider tests, including
     4412    // bitops-nsieve-bits.js, will regress substantially on Mac, due to a ~40%
     4413    // increase in L2 cache misses. FIXME: WTF?
     4414    gccIsCrazy();
     4415   
     4416    initializeSymbolTable(exec);
     4417    optimizeVariableAccess(exec);
     4418
     4419    LocalStorage& localStorage = exec->variableObject()->localStorage();
     4420
     4421    // We can't just resize localStorage here because that would temporarily
     4422    // leave uninitialized entries, which would crash GC during the mark phase.
     4423    localStorage.reserveCapacity(localStorage.size() + m_varStack.size() + m_functionStack.size());
     4424
     4425    int minAttributes = Internal | DontDelete;
     4426
     4427    // In order for our localStorage indexes to be correct, we must match the
     4428    // order of addition in initializeSymbolTable().
     4429
     4430    for (size_t i = 0, size = m_functionStack.size(); i < size; ++i) {
    43444431        FuncDeclNode* node = m_functionStack[i];
    4345         localStorage.append(LocalStorageEntry(node->makeFunction(exec), minAttributes));
    4346     }
    4347 
    4348     exec->updateLocalStorage();
    4349 }
    4350 
    4351 void ProgramNode::processDeclarations(ExecState* exec)
    4352 {
    4353     size_t i, size;
    4354 
    4355     JSVariableObject* variableObject = exec->variableObject();
    4356    
    4357     int minAttributes = Internal | DontDelete;
    4358 
    4359     for (i = 0, size = m_varStack.size(); i < size; ++i) {
     4432        LocalStorageEntry entry = LocalStorageEntry(node->makeFunction(exec), minAttributes);
     4433        size_t index = m_functionIndexes[i];
     4434
     4435        if (index == localStorage.size())
     4436            localStorage.uncheckedAppend(entry);
     4437        else {
     4438            ASSERT(index < localStorage.size());
     4439            localStorage[index] = entry;
     4440        }
     4441    }
     4442
     4443    for (size_t i = 0, size = m_varStack.size(); i < size; ++i) {
     4444        size_t index = m_varIndexes[i];
     4445        if (index == missingSymbolMarker())
     4446            continue;
     4447
    43604448        VarDeclNode* node = m_varStack[i];
    4361         if (variableObject->hasProperty(exec, node->ident))
    4362             continue;
    43634449        int attributes = minAttributes;
    43644450        if (node->varType == VarDeclNode::Constant)
    43654451            attributes |= ReadOnly;
    4366         variableObject->put(exec, node->ident, jsUndefined(), attributes);
    4367     }
    4368 
    4369     for (i = 0, size = m_functionStack.size(); i < size; ++i) {
    4370         FuncDeclNode* node = m_functionStack[i];
    4371         variableObject->put(exec, node->ident, node->makeFunction(exec), minAttributes);
     4452        LocalStorageEntry entry = LocalStorageEntry(jsUndefined(), attributes);
     4453           
     4454        ASSERT(index == localStorage.size());
     4455        localStorage.uncheckedAppend(entry);
    43724456    }
    43734457}
     
    43754459void EvalNode::processDeclarations(ExecState* exec)
    43764460{
     4461    // We could optimize access to pre-existing symbols here, but SunSpider
     4462    // reports that to be a net loss.
     4463
    43774464    size_t i, size;
    43784465
     
    43834470    for (i = 0, size = m_varStack.size(); i < size; ++i) {
    43844471        VarDeclNode* node = m_varStack[i];
    4385         if (variableObject->hasProperty(exec, node->ident))
     4472        if (variableObject->hasOwnProperty(exec, node->ident))
    43864473            continue;
    43874474        int attributes = minAttributes;
Note: See TracChangeset for help on using the changeset viewer.