Ignore:
Timestamp:
Oct 16, 2009, 5:28:19 PM (16 years ago)
Author:
[email protected]
Message:

Fast for-in enumeration: Cache JSPropertyNameIterator; cache JSStrings
in JSPropertyNameIterator; inline more code.

Patch by Geoffrey Garen <[email protected]> on 2009-10-16
Reviewed by Oliver Hunt.

1.024x as fast on SunSpider (fasta: 1.43x as fast).

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::dump):

  • bytecode/Opcode.h:
  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitGetPropertyNames):
(JSC::BytecodeGenerator::emitNextPropertyName):

  • bytecompiler/BytecodeGenerator.h: Added a few extra operands to

op_get_pnames and op_next_pname so that we can track iteration state
in the register file instead of in the JSPropertyNameIterator. (To be
cacheable, the JSPropertyNameIterator must be stateless.)

  • interpreter/Interpreter.cpp:

(JSC::Interpreter::tryCachePutByID):
(JSC::Interpreter::tryCacheGetByID): Updated for rename to
"normalizePrototypeChain" and removal of "isCacheable".

(JSC::Interpreter::privateExecute): Updated for in-RegisterFile
iteration state tracking.

  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):

  • jit/JIT.h:
  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_get_pnames): Updated for in-RegisterFile
iteration state tracking.

(JSC::JIT::emit_op_next_pname): Inlined code generation for op_next_pname.

  • jit/JITStubs.cpp:

(JSC::JITThunks::tryCachePutByID):
(JSC::JITThunks::tryCacheGetByID): Updated for rename to
"normalizePrototypeChain" and removal of "isCacheable".

(JSC::DEFINE_STUB_FUNCTION):

  • jit/JITStubs.h:

(JSC::): Added has_property and to_object stubs. Removed op_next_pname
stub, since has_property is all we need anymore.

  • parser/Nodes.cpp:

(JSC::ForInNode::emitBytecode): Updated for in-RegisterFile
iteration state tracking.

  • runtime/JSCell.h:
  • runtime/JSObject.cpp:

(JSC::JSObject::getPropertyNames): Don't do caching at this layer
anymore, since we don't create a JSPropertyNameIterator at this layer.

  • runtime/JSPropertyNameIterator.cpp:

(JSC::JSPropertyNameIterator::create): Do do caching at this layer.
(JSC::JSPropertyNameIterator::get): Updated for in-RegisterFile
iteration state tracking.
(JSC::JSPropertyNameIterator::markChildren): Mark our JSStrings.

  • runtime/JSPropertyNameIterator.h:

(JSC::JSPropertyNameIterator::size):
(JSC::JSPropertyNameIterator::setCachedStructure):
(JSC::JSPropertyNameIterator::cachedStructure):
(JSC::JSPropertyNameIterator::setCachedPrototypeChain):
(JSC::JSPropertyNameIterator::cachedPrototypeChain):
(JSC::JSPropertyNameIterator::JSPropertyNameIterator):
(JSC::Structure::setEnumerationCache): Don't store iteration state in
a JSPropertyNameIterator. Do cache a JSPropertyNameIterator in a
Structure.

  • runtime/JSValue.h:

(JSC::asCell):

  • runtime/MarkStack.h: Make those mischievous #include gods happy.
  • runtime/ObjectConstructor.cpp:
  • runtime/Operations.h:

(JSC::normalizePrototypeChain): Renamed countPrototypeChainEntriesAndCheckForProxies
to normalizePrototypeChain, since it changes dictionary prototypes to
non-dictionary objects.

  • runtime/PropertyNameArray.cpp:

(JSC::PropertyNameArray::add):

  • runtime/PropertyNameArray.h:

(JSC::PropertyNameArrayData::PropertyNameArrayData):
(JSC::PropertyNameArray::data):
(JSC::PropertyNameArray::size):
(JSC::PropertyNameArray::begin):
(JSC::PropertyNameArray::end): Simplified some code here to help with
current and future refactoring.

  • runtime/Protect.h:
  • runtime/Structure.cpp:

(JSC::Structure::~Structure):
(JSC::Structure::addPropertyWithoutTransition):
(JSC::Structure::removePropertyWithoutTransition): No need to clear
the enumeration cache with adding / removing properties without
transition. It is an error to add / remove properties without transition
once an object has been observed, and we can ASSERT to catch that.

  • runtime/Structure.h:

(JSC::Structure::enumerationCache): Changed the enumeration cache to
hold a JSPropertyNameIterator.

  • runtime/StructureChain.cpp:
  • runtime/StructureChain.h:

(JSC::StructureChain::head): Removed StructureChain::isCacheable because
it was wrong-headed in two ways: (1) It gave up when a prototype was a
dictionary, but instead we want un-dictionary heavily accessed
prototypes; (2) It folded a test for hasDefaultGetPropertyNames() into
a generic test for "cacheable-ness", but hasDefaultGetPropertyNames()
is only releavant to for-in caching.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/interpreter/Interpreter.cpp

    r49409 r49717  
    939939    }
    940940
    941     StructureChain* protoChain = structure->prototypeChain(callFrame);
    942     if (!protoChain->isCacheable()) {
    943         vPC[0] = getOpcode(op_put_by_id_generic);
    944         return;
    945     }
    946 
    947941    // Structure transition, cache transition info
    948942    if (slot.type() == PutPropertySlot::NewProperty) {
     
    951945            return;
    952946        }
     947
     948        // put_by_id_transition checks the prototype chain for setters.
     949        normalizePrototypeChain(callFrame, baseCell);
     950
    953951        vPC[0] = getOpcode(op_put_by_id_transition);
    954952        vPC[4] = structure->previousID();
    955953        vPC[5] = structure;
    956         vPC[6] = protoChain;
     954        vPC[6] = structure->prototypeChain(callFrame);
    957955        vPC[7] = slot.cachedOffset();
    958956        codeBlock->refStructures(vPC);
     
    10501048    }
    10511049
    1052     size_t count = countPrototypeChainEntriesAndCheckForProxies(callFrame, baseValue, slot);
     1050    size_t count = normalizePrototypeChain(callFrame, baseValue, slot.slotBase());
    10531051    if (!count) {
    10541052        vPC[0] = getOpcode(op_get_by_id_generic);
     
    10561054    }
    10571055
    1058     StructureChain* protoChain = structure->prototypeChain(callFrame);
    1059     if (!protoChain->isCacheable()) {
    1060         vPC[0] = getOpcode(op_get_by_id_generic);
    1061         return;
    1062     }
    1063 
    10641056    vPC[0] = getOpcode(op_get_by_id_chain);
    10651057    vPC[4] = structure;
    1066     vPC[5] = protoChain;
     1058    vPC[5] = structure->prototypeChain(callFrame);
    10671059    vPC[6] = count;
    10681060    vPC[7] = slot.cachedOffset();
     
    35033495    }
    35043496    DEFINE_OPCODE(op_get_pnames) {
    3505         /* get_pnames dst(r) base(r)
     3497        /* get_pnames dst(r) base(r) i(n) size(n) breakTarget(offset)
    35063498
    35073499           Creates a property name list for register base and puts it
    3508            in register dst. This is not a true JavaScript value, just
    3509            a synthetic value used to keep the iteration state in a
    3510            register.
     3500           in register dst, initializing i and size for iteration. If
     3501           base is undefined or null, jumps to breakTarget.
    35113502        */
    35123503        int dst = vPC[1].u.operand;
    35133504        int base = vPC[2].u.operand;
    3514 
    3515         callFrame->r(dst) = JSPropertyNameIterator::create(callFrame, callFrame->r(base).jsValue());
     3505        int i = vPC[3].u.operand;
     3506        int size = vPC[4].u.operand;
     3507        int breakTarget = vPC[5].u.operand;
     3508
     3509        JSValue v = callFrame->r(base).jsValue();
     3510        if (v.isUndefinedOrNull()) {
     3511            vPC += breakTarget;
     3512            NEXT_INSTRUCTION();
     3513        }
     3514
     3515        JSObject* o = v.toObject(callFrame);
     3516        Structure* structure = o->structure();
     3517        JSPropertyNameIterator* jsPropertyNameIterator = structure->enumerationCache();
     3518        if (!jsPropertyNameIterator || jsPropertyNameIterator->cachedPrototypeChain() != structure->prototypeChain(callFrame))
     3519            jsPropertyNameIterator = JSPropertyNameIterator::create(callFrame, o);
     3520
     3521        callFrame->r(dst) = jsPropertyNameIterator;
     3522        callFrame->r(base) = JSValue(o);
     3523        callFrame->r(i) = Register::withInt(0);
     3524        callFrame->r(size) = Register::withInt(jsPropertyNameIterator->size());
    35163525        vPC += OPCODE_LENGTH(op_get_pnames);
    35173526        NEXT_INSTRUCTION();
    35183527    }
    35193528    DEFINE_OPCODE(op_next_pname) {
    3520         /* next_pname dst(r) iter(r) target(offset)
    3521 
    3522            Tries to copies the next name from property name list in
    3523            register iter. If there are names left, then copies one to
    3524            register dst, and jumps to offset target. If there are none
    3525            left, invalidates the iterator and continues to the next
     3529        /* next_pname dst(r) base(r) i(n) size(n) iter(r) target(offset)
     3530
     3531           Copies the next name from the property name list in
     3532           register iter to dst, then jumps to offset target. If there are no
     3533           names left, invalidates the iterator and continues to the next
    35263534           instruction.
    35273535        */
    35283536        int dst = vPC[1].u.operand;
    3529         int iter = vPC[2].u.operand;
    3530         int target = vPC[3].u.operand;
     3537        int base = vPC[2].u.operand;
     3538        int i = vPC[3].u.operand;
     3539        int size = vPC[4].u.operand;
     3540        int iter = vPC[5].u.operand;
     3541        int target = vPC[6].u.operand;
    35313542
    35323543        JSPropertyNameIterator* it = callFrame->r(iter).propertyNameIterator();
    3533         if (JSValue temp = it->next(callFrame)) {
    3534             CHECK_FOR_TIMEOUT();
    3535             callFrame->r(dst) = JSValue(temp);
    3536             vPC += target;
    3537             NEXT_INSTRUCTION();
    3538         }
    3539         it->invalidate();
     3544        while (callFrame->r(i).i() != callFrame->r(size).i()) {
     3545            JSValue key = it->get(callFrame, asObject(callFrame->r(base).jsValue()), callFrame->r(i).i());
     3546            callFrame->r(i) = Register::withInt(callFrame->r(i).i() + 1);
     3547            if (key) {
     3548                CHECK_FOR_TIMEOUT();
     3549                callFrame->r(dst) = key;
     3550                vPC += target;
     3551                NEXT_INSTRUCTION();
     3552            }
     3553        }
    35403554
    35413555        vPC += OPCODE_LENGTH(op_next_pname);
Note: See TracChangeset for help on using the changeset viewer.