Ignore:
Timestamp:
Jul 5, 2012, 3:55:51 PM (13 years ago)
Author:
[email protected]
Message:

Inline property storage should not be wasted when it is exhausted
https://bugs.webkit.org/show_bug.cgi?id=90347

Reviewed by Gavin Barraclough.

Previously, if we switched an object from using inline storage to out-of-line
storage, we would abandon the inline storage. This would have two main implications:
(i) all accesses to the object, even for properties that were previously in inline
storage, must now take an extra indirection; and (ii) we waste a non-trivial amount
of space since we must allocate additional out-of-line storage to hold properties
that would have fit in the inline storage. There's also the copying cost when
switching to out-of-line storage - we must copy all inline properties into ouf-of-line
storage.

This patch changes the way that object property storage works so that we can use both
inline and out-of-line storage concurrently. This is accomplished by introducing a
new notion of property offset. This PropertyOffset is a 32-bit signed integer and it
behaves as follows:

offset == -1: invalid offset, indicating a property that does not exist.

0 <= offset <= inlineStorageCapacity: offset into inline storage.

inlineStorageCapacity < offset: offset into out-of-line storage.

Because non-final objects don't have inline storage, the only valid PropertyOffsets
for those objects' properties are -1 or > inlineStorageCapacity.

This now means that the decision to use inline or out-of-line storage for an access is
made based on the offset, rather than the structure. It also means that any access
where the offset is a variable must have an extra branch, unless the type of the
object is also known (if it's known to be a non-final object then we can just assert
that the offset is >= inlineStorageCapacity).

This looks like a big Kraken speed-up and a slight V8 speed-up.

(ARMv7Assembler):
(JSC::ARMv7Assembler::ldrWide8BitImmediate):
(JSC::ARMv7Assembler::replaceWithLoad):
(JSC::ARMv7Assembler::replaceWithAddressComputation):

  • assembler/AbstractMacroAssembler.h:

(AbstractMacroAssembler):
(ConvertibleLoadLabel):
(JSC::AbstractMacroAssembler::ConvertibleLoadLabel::ConvertibleLoadLabel):
(JSC::AbstractMacroAssembler::ConvertibleLoadLabel::isSet):
(JSC::AbstractMacroAssembler::labelIgnoringWatchpoints):
(JSC::AbstractMacroAssembler::replaceWithLoad):
(JSC::AbstractMacroAssembler::replaceWithAddressComputation):

  • assembler/CodeLocation.h:

(JSC):
(CodeLocationCommon):
(CodeLocationConvertibleLoad):
(JSC::CodeLocationConvertibleLoad::CodeLocationConvertibleLoad):
(JSC::CodeLocationCommon::convertibleLoadAtOffset):

  • assembler/LinkBuffer.cpp:

(JSC::LinkBuffer::finalizeCodeWithDisassembly):

  • assembler/LinkBuffer.h:

(LinkBuffer):
(JSC::LinkBuffer::locationOf):

  • assembler/MacroAssemblerARMv7.h:

(MacroAssemblerARMv7):
(JSC::MacroAssemblerARMv7::convertibleLoadPtr):

  • assembler/MacroAssemblerX86.h:

(JSC::MacroAssemblerX86::convertibleLoadPtr):
(MacroAssemblerX86):

  • assembler/MacroAssemblerX86_64.h:

(JSC::MacroAssemblerX86_64::convertibleLoadPtr):
(MacroAssemblerX86_64):

  • assembler/RepatchBuffer.h:

(RepatchBuffer):
(JSC::RepatchBuffer::replaceWithLoad):
(JSC::RepatchBuffer::replaceWithAddressComputation):
(JSC::RepatchBuffer::setLoadInstructionIsActive):

  • assembler/X86Assembler.h:

(JSC::X86Assembler::replaceWithLoad):
(X86Assembler):
(JSC::X86Assembler::replaceWithAddressComputation):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::printGetByIdOp):
(JSC::CodeBlock::dump):
(JSC::CodeBlock::finalizeUnconditionally):

  • bytecode/GetByIdStatus.cpp:

(JSC::GetByIdStatus::computeFromLLInt):
(JSC::GetByIdStatus::computeForChain):
(JSC::GetByIdStatus::computeFor):

  • bytecode/GetByIdStatus.h:

(JSC::GetByIdStatus::GetByIdStatus):
(JSC::GetByIdStatus::offset):
(GetByIdStatus):

  • bytecode/Opcode.h:

(JSC):
(JSC::padOpcodeName):

  • bytecode/PutByIdStatus.cpp:

(JSC::PutByIdStatus::computeFromLLInt):
(JSC::PutByIdStatus::computeFor):

  • bytecode/PutByIdStatus.h:

(JSC::PutByIdStatus::PutByIdStatus):
(JSC::PutByIdStatus::offset):
(PutByIdStatus):

  • bytecode/ResolveGlobalStatus.cpp:

(JSC):
(JSC::computeForStructure):

  • bytecode/ResolveGlobalStatus.h:

(JSC::ResolveGlobalStatus::ResolveGlobalStatus):
(JSC::ResolveGlobalStatus::offset):
(ResolveGlobalStatus):

  • bytecode/StructureSet.h:

(StructureSet):

  • bytecode/StructureStubInfo.h:
  • dfg/DFGByteCodeParser.cpp:

(ByteCodeParser):
(JSC::DFG::ByteCodeParser::handleGetByOffset):
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGCapabilities.h:

(JSC::DFG::canCompileOpcode):

  • dfg/DFGJITCompiler.cpp:

(JSC::DFG::JITCompiler::link):

  • dfg/DFGJITCompiler.h:

(JSC::DFG::PropertyAccessRecord::PropertyAccessRecord):
(PropertyAccessRecord):

  • dfg/DFGRepatch.cpp:

(JSC::DFG::dfgRepatchByIdSelfAccess):
(JSC::DFG::generateProtoChainAccessStub):
(JSC::DFG::tryCacheGetByID):
(JSC::DFG::tryBuildGetByIDList):
(JSC::DFG::tryBuildGetByIDProtoList):
(JSC::DFG::emitPutReplaceStub):
(JSC::DFG::emitPutTransitionStub):
(JSC::DFG::tryCachePutByID):
(JSC::DFG::tryBuildPutByIdList):

  • dfg/DFGSpeculativeJIT.h:

(JSC::DFG::SpeculativeJIT::emitAllocateBasicJSObject):

  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::cachedGetById):
(JSC::DFG::SpeculativeJIT::cachedPutById):
(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::cachedGetById):
(JSC::DFG::SpeculativeJIT::cachedPutById):
(JSC::DFG::SpeculativeJIT::compile):

  • heap/MarkStack.cpp:

(JSC::visitChildren):

  • interpreter/Interpreter.cpp:

(JSC::Interpreter::tryCacheGetByID):
(JSC::Interpreter::privateExecute):

  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
(JSC::PropertyStubCompilationInfo::copyToStubInfo):

  • jit/JIT.h:

(JSC::PropertyStubCompilationInfo::PropertyStubCompilationInfo):
(JSC::JIT::compileGetByIdProto):
(JSC::JIT::compileGetByIdSelfList):
(JSC::JIT::compileGetByIdProtoList):
(JSC::JIT::compileGetByIdChainList):
(JSC::JIT::compileGetByIdChain):
(JSC::JIT::compilePutByIdTransition):
(JIT):

  • jit/JITInlineMethods.h:

(JSC::JIT::emitAllocateBasicJSObject):

  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_resolve_global):

  • jit/JITOpcodes32_64.cpp:

(JSC::JIT::emit_op_resolve_global):

  • jit/JITPropertyAccess.cpp:

(JSC::JIT::compileGetDirectOffset):
(JSC::JIT::emit_op_method_check):
(JSC::JIT::compileGetByIdHotPath):
(JSC::JIT::emit_op_put_by_id):
(JSC::JIT::compilePutDirectOffset):
(JSC::JIT::privateCompilePutByIdTransition):
(JSC::JIT::patchGetByIdSelf):
(JSC::JIT::patchPutByIdReplace):
(JSC::JIT::privateCompileGetByIdProto):
(JSC::JIT::privateCompileGetByIdSelfList):
(JSC::JIT::privateCompileGetByIdProtoList):
(JSC::JIT::privateCompileGetByIdChainList):
(JSC::JIT::privateCompileGetByIdChain):

  • jit/JITPropertyAccess32_64.cpp:

(JSC::JIT::emit_op_method_check):
(JSC::JIT::compileGetByIdHotPath):
(JSC::JIT::emit_op_put_by_id):
(JSC::JIT::compilePutDirectOffset):
(JSC::JIT::compileGetDirectOffset):
(JSC::JIT::privateCompilePutByIdTransition):
(JSC::JIT::patchGetByIdSelf):
(JSC::JIT::patchPutByIdReplace):
(JSC::JIT::privateCompileGetByIdProto):
(JSC::JIT::privateCompileGetByIdSelfList):
(JSC::JIT::privateCompileGetByIdProtoList):
(JSC::JIT::privateCompileGetByIdChainList):
(JSC::JIT::privateCompileGetByIdChain):
(JSC::JIT::emit_op_get_by_pname):

  • jit/JITStubs.cpp:

(JSC::JITThunks::tryCacheGetByID):
(JSC::DEFINE_STUB_FUNCTION):

  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::LLINT_SLOW_PATH_DECL):

  • llint/LowLevelInterpreter.asm:
  • llint/LowLevelInterpreter32_64.asm:
  • llint/LowLevelInterpreter64.asm:
  • offlineasm/x86.rb:
  • runtime/JSGlobalObject.h:

(JSGlobalObject):
(JSC::JSGlobalObject::functionNameOffset):

  • runtime/JSObject.cpp:

(JSC::JSObject::visitChildren):
(JSC):
(JSC::JSFinalObject::visitChildren):
(JSC::JSObject::put):
(JSC::JSObject::deleteProperty):
(JSC::JSObject::getPropertySpecificValue):
(JSC::JSObject::removeDirect):
(JSC::JSObject::growOutOfLineStorage):
(JSC::JSObject::getOwnPropertyDescriptor):

  • runtime/JSObject.h:

(JSObject):
(JSC::JSObject::getDirect):
(JSC::JSObject::getDirectLocation):
(JSC::JSObject::hasInlineStorage):
(JSC::JSObject::inlineStorageUnsafe):
(JSC::JSObject::inlineStorage):
(JSC::JSObject::outOfLineStorage):
(JSC::JSObject::locationForOffset):
(JSC::JSObject::offsetForLocation):
(JSC::JSObject::getDirectOffset):
(JSC::JSObject::putDirectOffset):
(JSC::JSObject::putUndefinedAtDirectOffset):
(JSC::JSObject::addressOfOutOfLineStorage):
(JSC::JSObject::finishCreation):
(JSC::JSNonFinalObject::JSNonFinalObject):
(JSC::JSNonFinalObject::finishCreation):
(JSFinalObject):
(JSC::JSFinalObject::finishCreation):
(JSC::JSFinalObject::JSFinalObject):
(JSC::JSObject::offsetOfOutOfLineStorage):
(JSC::JSObject::setOutOfLineStorage):
(JSC::JSObject::JSObject):
(JSC):
(JSC::JSCell::fastGetOwnProperty):
(JSC::JSObject::putDirectInternal):
(JSC::JSObject::setStructureAndReallocateStorageIfNecessary):
(JSC::JSObject::putDirectWithoutTransition):
(JSC::offsetRelativeToPatchedStorage):
(JSC::indexRelativeToBase):
(JSC::offsetRelativeToBase):

  • runtime/JSPropertyNameIterator.cpp:

(JSC::JSPropertyNameIterator::create):

  • runtime/JSPropertyNameIterator.h:

(JSPropertyNameIterator):
(JSC::JSPropertyNameIterator::getOffset):
(JSC::JSPropertyNameIterator::finishCreation):

  • runtime/JSValue.cpp:

(JSC::JSValue::putToPrimitive):

  • runtime/Operations.h:

(JSC::normalizePrototypeChain):

  • runtime/Options.cpp:

(JSC):
(JSC::Options::initialize):

  • runtime/PropertyMapHashTable.h:

(PropertyMapEntry):
(JSC::PropertyMapEntry::PropertyMapEntry):
(PropertyTable):
(JSC::PropertyTable::PropertyTable):
(JSC::PropertyTable::getDeletedOffset):
(JSC::PropertyTable::addDeletedOffset):
(JSC::PropertyTable::nextOffset):
(JSC):
(JSC::PropertyTable::sizeInMemory):

  • runtime/PropertyOffset.h: Added.

(JSC):
(JSC::checkOffset):
(JSC::validateOffset):
(JSC::isValidOffset):
(JSC::isInlineOffset):
(JSC::isOutOfLineOffset):
(JSC::offsetInInlineStorage):
(JSC::offsetInOutOfLineStorage):
(JSC::offsetInRespectiveStorage):
(JSC::numberOfOutOfLineSlotsForLastOffset):
(JSC::numberOfSlotsForLastOffset):
(JSC::nextPropertyOffsetFor):
(JSC::firstPropertyOffsetFor):

  • runtime/PropertySlot.h:

(JSC::PropertySlot::cachedOffset):
(JSC::PropertySlot::setValue):
(JSC::PropertySlot::setCacheableGetterSlot):
(JSC::PropertySlot::clearOffset):

  • runtime/PutPropertySlot.h:

(JSC::PutPropertySlot::setExistingProperty):
(JSC::PutPropertySlot::setNewProperty):
(JSC::PutPropertySlot::cachedOffset):
(PutPropertySlot):

  • runtime/Structure.cpp:

(JSC::Structure::Structure):
(JSC::Structure::materializePropertyMap):
(JSC::nextOutOfLineStorageCapacity):
(JSC::Structure::growOutOfLineCapacity):
(JSC::Structure::suggestedNewOutOfLineStorageCapacity):
(JSC::Structure::addPropertyTransitionToExistingStructure):
(JSC::Structure::addPropertyTransition):
(JSC::Structure::removePropertyTransition):
(JSC::Structure::flattenDictionaryStructure):
(JSC::Structure::addPropertyWithoutTransition):
(JSC::Structure::removePropertyWithoutTransition):
(JSC::Structure::copyPropertyTableForPinning):
(JSC::Structure::get):
(JSC::Structure::putSpecificValue):
(JSC::Structure::remove):

  • runtime/Structure.h:

(Structure):
(JSC::Structure::putWillGrowOutOfLineStorage):
(JSC::Structure::previousID):
(JSC::Structure::outOfLineCapacity):
(JSC::Structure::outOfLineSizeForKnownFinalObject):
(JSC::Structure::outOfLineSizeForKnownNonFinalObject):
(JSC::Structure::outOfLineSize):
(JSC::Structure::hasInlineStorage):
(JSC::Structure::inlineCapacity):
(JSC::Structure::inlineSizeForKnownFinalObject):
(JSC::Structure::inlineSize):
(JSC::Structure::totalStorageSize):
(JSC::Structure::totalStorageCapacity):
(JSC::Structure::firstValidOffset):
(JSC::Structure::lastValidOffset):
(JSC::Structure::isValidOffset):
(JSC::Structure::isEmpty):
(JSC::Structure::transitionCount):
(JSC::Structure::get):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r121798 r121925  
    100100    void handleGetByOffset(
    101101        int destinationOperand, SpeculatedType, NodeIndex base, unsigned identifierNumber,
    102         bool useInlineStorage, size_t offset);
     102        PropertyOffset);
    103103    void handleGetById(
    104104        int destinationOperand, SpeculatedType, NodeIndex base, unsigned identifierNumber,
     
    16311631void ByteCodeParser::handleGetByOffset(
    16321632    int destinationOperand, SpeculatedType prediction, NodeIndex base, unsigned identifierNumber,
    1633     bool useInlineStorage, size_t offset)
     1633    PropertyOffset offset)
    16341634{
    16351635    NodeIndex propertyStorage;
    1636     size_t offsetOffset;
    1637     if (useInlineStorage) {
     1636    if (isInlineOffset(offset))
    16381637        propertyStorage = base;
    1639         ASSERT(!(sizeof(JSObject) % sizeof(EncodedJSValue)));
    1640         offsetOffset = sizeof(JSObject) / sizeof(EncodedJSValue);
    1641     } else {
     1638    else
    16421639        propertyStorage = addToGraph(GetPropertyStorage, base);
    1643         offsetOffset = 0;
    1644     }
    16451640    set(destinationOperand,
    16461641        addToGraph(
     
    16491644       
    16501645    StorageAccessData storageAccessData;
    1651     storageAccessData.offset = offset + offsetOffset;
     1646    storageAccessData.offset = indexRelativeToBase(offset);
    16521647    storageAccessData.identifierNumber = identifierNumber;
    16531648    m_graph.m_storageAccessData.append(storageAccessData);
     
    16781673    addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(getByIdStatus.structureSet())), base);
    16791674   
    1680     bool useInlineStorage;
    16811675    if (!getByIdStatus.chain().isEmpty()) {
    16821676        Structure* currentStructure = getByIdStatus.structureSet().singletonStructure();
     
    16871681            base = addStructureTransitionCheck(currentObject, currentStructure);
    16881682        }
    1689         useInlineStorage = currentStructure->isUsingInlineStorage();
    1690     } else
    1691         useInlineStorage = getByIdStatus.structureSet().allAreUsingInlinePropertyStorage();
     1683    }
    16921684   
    16931685    // Unless we want bugs like https://bugs.webkit.org/show_bug.cgi?id=88783, we need to
     
    17081700   
    17091701    handleGetByOffset(
    1710         destinationOperand, prediction, base, identifierNumber, useInlineStorage,
    1711         getByIdStatus.offset());
     1702        destinationOperand, prediction, base, identifierNumber, getByIdStatus.offset());
    17121703}
    17131704
     
    21732164            SpeculatedType prediction = getPrediction();
    21742165           
    2175             ASSERT(interpreter->getOpcodeID(getInstruction->u.opcode) == op_get_by_id);
     2166            ASSERT(interpreter->getOpcodeID(getInstruction->u.opcode) == op_get_by_id
     2167                   || interpreter->getOpcodeID(getInstruction->u.opcode) == op_get_by_id_out_of_line);
    21762168           
    21772169            NodeIndex base = get(getInstruction[2].u.operand);
     
    22262218            NEXT_OPCODE(op_put_scoped_var);
    22272219        }
    2228         case op_get_by_id: {
     2220        case op_get_by_id:
     2221        case op_get_by_id_out_of_line: {
    22292222            SpeculatedType prediction = getPredictionWithoutOSRExit();
    22302223           
     
    22422235        }
    22432236        case op_put_by_id:
     2237        case op_put_by_id_out_of_line:
    22442238        case op_put_by_id_transition_direct:
    2245         case op_put_by_id_transition_normal: {
     2239        case op_put_by_id_transition_normal:
     2240        case op_put_by_id_transition_direct_out_of_line:
     2241        case op_put_by_id_transition_normal_out_of_line: {
    22462242            NodeIndex value = get(currentInstruction[3].u.operand);
    22472243            NodeIndex base = get(currentInstruction[1].u.operand);
     
    22602256            if (!hasExitSite && putByIdStatus.isSimpleReplace()) {
    22612257                addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(putByIdStatus.oldStructure())), base);
    2262                 size_t offsetOffset;
    22632258                NodeIndex propertyStorage;
    2264                 if (putByIdStatus.oldStructure()->isUsingInlineStorage()) {
     2259                if (isInlineOffset(putByIdStatus.offset()))
    22652260                    propertyStorage = base;
    2266                     ASSERT(!(sizeof(JSObject) % sizeof(EncodedJSValue)));
    2267                     offsetOffset = sizeof(JSObject) / sizeof(EncodedJSValue);
    2268                 } else {
     2261                else
    22692262                    propertyStorage = addToGraph(GetPropertyStorage, base);
    2270                     offsetOffset = 0;
    2271                 }
    22722263                addToGraph(PutByOffset, OpInfo(m_graph.m_storageAccessData.size()), propertyStorage, base, value);
    22732264               
    22742265                StorageAccessData storageAccessData;
    2275                 storageAccessData.offset = putByIdStatus.offset() + offsetOffset;
     2266                storageAccessData.offset = indexRelativeToBase(putByIdStatus.offset());
    22762267                storageAccessData.identifierNumber = identifierNumber;
    22772268                m_graph.m_storageAccessData.append(storageAccessData);
    22782269            } else if (!hasExitSite
    22792270                       && putByIdStatus.isSimpleTransition()
    2280                        && putByIdStatus.oldStructure()->propertyStorageCapacity() == putByIdStatus.newStructure()->propertyStorageCapacity()
     2271                       && putByIdStatus.oldStructure()->outOfLineCapacity() == putByIdStatus.newStructure()->outOfLineCapacity()
    22812272                       && structureChainIsStillValid(
    22822273                           direct,
     
    23092300                    base);
    23102301               
    2311                 size_t offsetOffset;
    23122302                NodeIndex propertyStorage;
    2313                 if (putByIdStatus.newStructure()->isUsingInlineStorage()) {
     2303                if (isInlineOffset(putByIdStatus.offset()))
    23142304                    propertyStorage = base;
    2315                     ASSERT(!(sizeof(JSObject) % sizeof(EncodedJSValue)));
    2316                     offsetOffset = sizeof(JSObject) / sizeof(EncodedJSValue);
    2317                 } else {
     2305                else
    23182306                    propertyStorage = addToGraph(GetPropertyStorage, base);
    2319                     offsetOffset = 0;
    2320                 }
    23212307                addToGraph(
    23222308                    PutByOffset,
     
    23272313               
    23282314                StorageAccessData storageAccessData;
    2329                 storageAccessData.offset = putByIdStatus.offset() + offsetOffset;
     2315                storageAccessData.offset = indexRelativeToBase(putByIdStatus.offset());
    23302316                storageAccessData.identifierNumber = identifierNumber;
    23312317                m_graph.m_storageAccessData.append(storageAccessData);
     
    27392725                    handleGetByOffset(
    27402726                        currentInstruction[1].u.operand, prediction, globalObject,
    2741                         identifierNumber, status.structure()->isUsingInlineStorage(),
    2742                         status.offset());
     2727                        identifierNumber, status.offset());
    27432728                }
    27442729               
Note: See TracChangeset for help on using the changeset viewer.