Ignore:
Timestamp:
Sep 10, 2015, 12:49:36 PM (10 years ago)
Author:
[email protected]
Message:

There should be one stub hanging off an inline cache that contains code for all of the cases, rather than forming a linked list consisting of one stub per case
https://bugs.webkit.org/show_bug.cgi?id=148717

Reviewed by Michael Saboff.

Source/JavaScriptCore:

This is a major rewrite of the JSC get/put/in inline caches (ICs), motivated by the need to add
fancy new kinds of inline caches for property type inference (https://webkit.org/b/148610).

Previously, our inline caches had some problems that made them difficult to work with. It was
impossible to change any code that was previously generated by the IC except by blowing the
whole IC away, the ICs scaled poorly if there were many cases, and there was a lot of duplicate
and ad hoc code.

Impossible to regenerate a previously generated stub: Say that some access (o.f = v) causes our
IC code to emit some stub; let's call it stub1. Then later we find that we need to emit a
different stub, stub2, where we think that stub2 might subsume stub1. We say that stub2
subsumes stub1 if failing to execute stub2 to completion means that we are guaranteed to fail
to execute stub1 to completion. This could happen in trunk if stub2 has the same base structure
as stub1 but different prototype conditions. It could happen with property type inference if
stub2 has a looser type check on v than stub1 did. Currently, if this happened, we would emit
stub2 and have its slow path jump to stub1. Hence, we would still end up executing the checks
of stub1 before falling through to the slow path. This gets bad when there are many stubs.
Stub1 might be in front of a bunch of other stubs, so when we add stub2, we will end up
executing both stub2's and stub1's checks before falling through to the other stubs. It would
be better if we could remove stub1 from the list at this point. But since stub1 could be linked
to from a different stub that we had already generated, we'd have to have a way of patching
stubs or regenerating them from scratch. This is currenty impossible because we just don't keep
around enough meta-data to mess with a stub after it's generated. After this change, we never
link new stubs onto a linked list of pre-existing stubs; instead each IC will have one stub
hanging off of it and we always regenerate that one stub from scratch. That one stub contains
either a BinarySwitch or a branch cascade to select one of the AccessCases. Each AccessCase is
an object that describes everything we need to regenerate it in the future. This means that
when we add a new case to an IC stub, we can figure out which previous cases this one subsumes.

Poor scalability when there are many cases: Previously, the cases of a polymorphic inline cache
formed a linked list of branches. This meant that the complexity of an inline cache grew
linearly with the number of cases. This change turns this into a BinarySwitch in most cases,
leading to logarithmic scaling.

Duplicate code between get, put, and in: The code for op_get_by_id, op_put_by_id, and op_in
inline caches grew independently and ended up having a lot of duplicate code. We had the worst
kinds of duplicate code. In some cases, the code was copy-pasted. In other cases, we wrote code
that felt like it was new despite the fact that it was logically identical to code that was
already written elsewhere. The main sources of duplication were in selecting a scratch
register, checking all of the ObjectPropertyConditions and the base structure, the pro forma
involved in generating a stub, and the data structures needed to describe all of the access
cases. This change deduplicates all of that code. Now, all of those ICs use the same classes:
the PolymorphicAccess and AccessCase. There is code in those classes that handles all of the
common things, and for the most part the only code that actually specializes for the kind of
access is in some switch statement in AccessCase::generate().

Special-casing of array length and string length: Previously, array.length and string.length
were handled in an ad hoc manner in the get_by_id repatching code. The handling was separate
from the polymorphic get_by_id handling, which meant that we could not handle polymorphic
length accesses if one of the length cases was either array or string length. For example, if
you had "o.length" where the length was either array length or a vanilla length property, then
the get_by_id inline cache would either emit a monomorphic stub for array length, or a
monomorphic stub for the vanilla length property, but never a polymorphic stub (or list) that
could do both. This change addresses this problem by folding array length and string length
into the polymorphic get_by_id code.

This was meant to be a perf-neutral change to enable property type inference, but it ended up
being a 1% Octane speed-up, mainly because of a 14% speed-up in raytrace. This isn't too
surprising, since that test does use inline caches a lot and this change makes inline caches
more scalable.

This also fixes and adds a test for a BinarySwitch bug. BinarySwitch had an optimization for
consecutive integer cases. Using it on typed array structures triggers this bug. It's a hard
bug to trigger any other way because our other switch optimizations will usually use a jump
table in case of consecutive integers.

(JSC::MacroAssemblerCodePtr::dumpWithName):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::printGetByIdCacheStatus):
(JSC::CodeBlock::printPutByIdCacheStatus):
(JSC::CodeBlock::propagateTransitions):
(JSC::CodeBlock::getByValInfoMap):
(JSC::CodeBlock::addStubInfo):
(JSC::CodeBlock::findStubInfo):

  • bytecode/CodeBlock.h:

(JSC::CodeBlock::stubInfoBegin):
(JSC::CodeBlock::stubInfoEnd):

  • bytecode/GetByIdStatus.cpp:

(JSC::GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback):

  • bytecode/PolymorphicAccess.cpp: Copied from Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.cpp.

(JSC::AccessGenerationState::addWatchpoint):
(JSC::AccessGenerationState::restoreScratch):
(JSC::AccessGenerationState::succeed):
(JSC::AccessCase::AccessCase):
(JSC::AccessCase::get):
(JSC::AccessCase::replace):
(JSC::AccessCase::transition):
(JSC::AccessCase::setter):
(JSC::AccessCase::in):
(JSC::AccessCase::getLength):
(JSC::AccessCase::~AccessCase):
(JSC::AccessCase::fromStructureStubInfo):
(JSC::AccessCase::clone):
(JSC::AccessCase::guardedByStructureCheck):
(JSC::AccessCase::alternateBase):
(JSC::AccessCase::canReplace):
(JSC::AccessCase::dump):
(JSC::AccessCase::visitWeak):
(JSC::AccessCase::generateWithGuard):
(JSC::AccessCase::generate):
(JSC::PolymorphicAccess::PolymorphicAccess):
(JSC::PolymorphicAccess::~PolymorphicAccess):
(JSC::PolymorphicAccess::regenerateWithCases):
(JSC::PolymorphicAccess::regenerateWithCase):
(JSC::PolymorphicAccess::visitWeak):
(JSC::PolymorphicAccess::dump):
(JSC::PolymorphicAccess::regenerate):
(WTF::printInternal):
(JSC::GetByIdAccess::GetByIdAccess): Deleted.
(JSC::GetByIdAccess::~GetByIdAccess): Deleted.
(JSC::GetByIdAccess::fromStructureStubInfo): Deleted.
(JSC::GetByIdAccess::visitWeak): Deleted.
(JSC::PolymorphicGetByIdList::PolymorphicGetByIdList): Deleted.
(JSC::PolymorphicGetByIdList::from): Deleted.
(JSC::PolymorphicGetByIdList::~PolymorphicGetByIdList): Deleted.
(JSC::PolymorphicGetByIdList::currentSlowPathTarget): Deleted.
(JSC::PolymorphicGetByIdList::addAccess): Deleted.
(JSC::PolymorphicGetByIdList::isFull): Deleted.
(JSC::PolymorphicGetByIdList::isAlmostFull): Deleted.
(JSC::PolymorphicGetByIdList::didSelfPatching): Deleted.
(JSC::PolymorphicGetByIdList::visitWeak): Deleted.

  • bytecode/PolymorphicAccess.h: Copied from Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.h.

(JSC::AccessCase::isGet):
(JSC::AccessCase::isPut):
(JSC::AccessCase::isIn):
(JSC::AccessCase::type):
(JSC::AccessCase::offset):
(JSC::AccessCase::viaProxy):
(JSC::AccessCase::structure):
(JSC::AccessCase::newStructure):
(JSC::AccessCase::conditionSet):
(JSC::AccessCase::additionalSet):
(JSC::AccessCase::customSlotBase):
(JSC::AccessCase::doesCalls):
(JSC::AccessCase::callLinkInfo):
(JSC::AccessCase::RareData::RareData):
(JSC::PolymorphicAccess::isEmpty):
(JSC::PolymorphicAccess::size):
(JSC::PolymorphicAccess::at):
(JSC::PolymorphicAccess::operator[]):
(JSC::GetByIdAccess::GetByIdAccess): Deleted.
(JSC::GetByIdAccess::isSet): Deleted.
(JSC::GetByIdAccess::operator!): Deleted.
(JSC::GetByIdAccess::type): Deleted.
(JSC::GetByIdAccess::structure): Deleted.
(JSC::GetByIdAccess::conditionSet): Deleted.
(JSC::GetByIdAccess::stubRoutine): Deleted.
(JSC::GetByIdAccess::doesCalls): Deleted.
(JSC::PolymorphicGetByIdList::isEmpty): Deleted.
(JSC::PolymorphicGetByIdList::size): Deleted.
(JSC::PolymorphicGetByIdList::at): Deleted.
(JSC::PolymorphicGetByIdList::operator[]): Deleted.

  • bytecode/PolymorphicAccessStructureList.h: Removed.
  • bytecode/PolymorphicGetByIdList.cpp: Removed.
  • bytecode/PolymorphicGetByIdList.h: Removed.
  • bytecode/PolymorphicPutByIdList.cpp: Removed.
  • bytecode/PolymorphicPutByIdList.h: Removed.
  • bytecode/PutByIdStatus.cpp:

(JSC::PutByIdStatus::computeForStubInfo):

  • bytecode/StructureStubInfo.cpp:

(JSC::StructureStubInfo::deref):
(JSC::StructureStubInfo::addAccessCase):
(JSC::StructureStubInfo::reset):
(JSC::StructureStubInfo::visitWeakReferences):

  • bytecode/StructureStubInfo.h:

(JSC::StructureStubInfo::StructureStubInfo):
(JSC::StructureStubInfo::initGetByIdSelf):
(JSC::StructureStubInfo::initPutByIdReplace):
(JSC::StructureStubInfo::initStub):
(JSC::StructureStubInfo::setSeen):
(JSC::getStructureStubInfoCodeOrigin):
(JSC::isGetByIdAccess): Deleted.
(JSC::isPutByIdAccess): Deleted.
(JSC::isInAccess): Deleted.
(JSC::StructureStubInfo::initGetByIdList): Deleted.
(JSC::StructureStubInfo::initPutByIdTransition): Deleted.
(JSC::StructureStubInfo::initPutByIdList): Deleted.
(JSC::StructureStubInfo::initInList): Deleted.
(JSC::StructureStubInfo::addWatchpoint): Deleted.

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileIn):

  • ftl/FTLCompile.cpp:

(JSC::FTL::mmAllocateDataSection):

  • jit/AccessorCallJITStubRoutine.cpp: Removed.
  • jit/AccessorCallJITStubRoutine.h: Removed.
  • jit/AssemblyHelpers.h:

(JSC::AssemblyHelpers::branchIfEmpty):
(JSC::AssemblyHelpers::branchStructure):
(JSC::AssemblyHelpers::boxBooleanPayload):
(JSC::AssemblyHelpers::boxBoolean):
(JSC::AssemblyHelpers::boxInt32):

  • jit/BinarySwitch.cpp:

(JSC::BinarySwitch::BinarySwitch):
(JSC::BinarySwitch::build):
(JSC::BinarySwitch::Case::dump):
(JSC::BinarySwitch::BranchCode::dump):

  • jit/BinarySwitch.h:

(JSC::BinarySwitch::Case::operator<):
(JSC::BinarySwitch::BranchCode::BranchCode):

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

(JSC::garbageStubInfo):
(JSC::JITInlineCacheGenerator::JITInlineCacheGenerator):
(JSC::JITByIdGenerator::JITByIdGenerator):
(JSC::JITGetByIdGenerator::JITGetByIdGenerator):
(JSC::JITPutByIdGenerator::JITPutByIdGenerator):

  • jit/JITInlineCacheGenerator.h:

(JSC::JITInlineCacheGenerator::JITInlineCacheGenerator):
(JSC::JITInlineCacheGenerator::stubInfo):
(JSC::JITByIdGenerator::JITByIdGenerator):
(JSC::JITByIdGenerator::reportSlowPathCall):

  • jit/JITOperations.cpp:
  • jit/Repatch.cpp:

(JSC::repatchCall):
(JSC::repatchByIdSelfAccess):
(JSC::resetGetByIDCheckAndLoad):
(JSC::resetPutByIDCheckAndLoad):
(JSC::replaceWithJump):
(JSC::tryCacheGetByID):
(JSC::repatchGetByID):
(JSC::appropriateGenericPutByIdFunction):
(JSC::appropriateOptimizingPutByIdFunction):
(JSC::tryCachePutByID):
(JSC::repatchPutByID):
(JSC::tryRepatchIn):
(JSC::repatchIn):
(JSC::resetGetByID):
(JSC::resetPutByID):
(JSC::checkObjectPropertyCondition): Deleted.
(JSC::checkObjectPropertyConditions): Deleted.
(JSC::emitRestoreScratch): Deleted.
(JSC::linkRestoreScratch): Deleted.
(JSC::toString): Deleted.
(JSC::kindFor): Deleted.
(JSC::customFor): Deleted.
(JSC::generateByIdStub): Deleted.
(JSC::patchJumpToGetByIdStub): Deleted.
(JSC::tryBuildGetByIDList): Deleted.
(JSC::buildGetByIDList): Deleted.
(JSC::appropriateListBuildingPutByIdFunction): Deleted.
(JSC::emitPutReplaceStub): Deleted.
(JSC::emitPutTransitionStub): Deleted.
(JSC::tryBuildPutByIdList): Deleted.
(JSC::buildPutByIdList): Deleted.

  • jit/ScratchRegisterAllocator.cpp:

(JSC::ScratchRegisterAllocator::lock):
(JSC::ScratchRegisterAllocator::allocateScratch):

  • jit/ScratchRegisterAllocator.h:

(JSC::ScratchRegisterAllocator::ScratchRegisterAllocator):

  • jsc.cpp:

(GlobalObject::finishCreation):
(functionQuit):
(functionAbort):
(functionFalse1):
(functionFalse2):

  • runtime/Options.h:
  • tests/stress/array-message-passing.js: Added.

(window.addEventListener):
(window.postMessage):
(window._handleEvents):
(testPassed):
(testFailed):
(classCompare):
(bufferCompare):
(viewCompare):
(typedArrayCompare):
(dataViewCompare):
(dataViewCompare2):
(dataViewCompare3):
(createBuffer):
(createTypedArray):
(createTypedArrayOverBuffer):
(new.DataView):
(testList.testList.concat.basicBufferTypes.map):
(doneTest):

Source/WTF:

Beef up dumping a bit.

  • wtf/PrintStream.h:

(WTF::pointerDump):
(WTF::printInternal):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/jsc.cpp

    r189583 r189586  
    481481static EncodedJSValue JSC_HOST_CALL functionTransferArrayBuffer(ExecState*);
    482482static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*);
     483static NO_RETURN_DUE_TO_CRASH EncodedJSValue JSC_HOST_CALL functionAbort(ExecState*);
    483484static EncodedJSValue JSC_HOST_CALL functionFalse1(ExecState*);
    484485static EncodedJSValue JSC_HOST_CALL functionFalse2(ExecState*);
     
    616617        addFunction(vm, "print", functionPrint, 1);
    617618        addFunction(vm, "quit", functionQuit, 0);
     619        addFunction(vm, "abort", functionAbort, 0);
    618620        addFunction(vm, "gc", functionGCAndSweep, 0);
    619621        addFunction(vm, "fullGC", functionFullGC, 0);
     
    13341336}
    13351337
     1338EncodedJSValue JSC_HOST_CALL functionAbort(ExecState*)
     1339{
     1340    CRASH();
     1341}
     1342
    13361343EncodedJSValue JSC_HOST_CALL functionFalse1(ExecState*) { return JSValue::encode(jsBoolean(false)); }
    13371344EncodedJSValue JSC_HOST_CALL functionFalse2(ExecState*) { return JSValue::encode(jsBoolean(false)); }
Note: See TracChangeset for help on using the changeset viewer.