Ignore:
Timestamp:
Oct 3, 2017, 6:53:18 PM (8 years ago)
Author:
[email protected]
Message:

Implement polymorphic prototypes
https://bugs.webkit.org/show_bug.cgi?id=176391

Reviewed by Filip Pizlo.

JSTests:

  • microbenchmarks/poly-proto-access.js: Added.

(assert):
(foo.C):
(foo.C.prototype.get bar):
(foo):
(bar):

  • microbenchmarks/poly-proto-put-transition-speed.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(performSet):

  • microbenchmarks/poly-proto-setter-speed.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo.C.prototype.set p):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(performSet):

  • stress/constructor-with-return.js:

(i.tests.forEach.Constructor):
(i.tests.forEach):
(tests.forEach.Constructor): Deleted.
(tests.forEach): Deleted.

  • stress/dom-jit-with-poly-proto.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(validate):

  • stress/poly-proto-custom-value-and-accessor.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(items.forEach):
(set get for):

  • stress/poly-proto-intrinsic-getter-correctness.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(foo):

  • stress/poly-proto-miss.js: Added.

(makePolyProtoInstanceWithNullPrototype.foo.C):
(makePolyProtoInstanceWithNullPrototype.foo):
(makePolyProtoInstanceWithNullPrototype):
(assert):
(validate):

  • stress/poly-proto-op-in-caching.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(validate):
(validate2):

  • stress/poly-proto-put-transition.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(performSet):
(i.obj.proto.set p):

  • stress/poly-proto-set-prototype.js: Added.

(assert):
(let.alternateProto.get x):
(let.alternateProto2.get y):
(let.alternateProto2.get x):
(foo.C):
(foo):
(validate):

  • stress/poly-proto-setter.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo.C.prototype.set p):
(makePolyProtoObject.foo.C.prototype.get p):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(performSet):

  • stress/poly-proto-using-inheritance.js: Added.

(assert):
(foo.C):
(foo.C.prototype.get baz):
(foo):
(bar.C):
(bar):
(validate):

  • stress/primitive-poly-proto.js: Added.

(makePolyProtoInstance.foo.C):
(makePolyProtoInstance.foo):
(makePolyProtoInstance):
(assert):
(validate):

  • stress/prototype-is-not-js-object.js: Added.

(foo.bar):
(foo):
(assert):
(validate):

  • stress/try-get-by-id-poly-proto.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(tryGetByIdText):
(x.proto.get bar):
(validate):

  • typeProfiler/overflow.js:

Source/JavaScriptCore:

This patch changes JSC's object model with respect to where the prototype
of an object is stored. Previously, it was always stored as
a constant value inside Structure. So an object's structure used to
always tell you what its prototype is. Anytime an object changed
its prototype, it would do a structure transition. This enables
a large class of optimizations: just by doing a structure check,
we know what the prototype is.

However, this design falls down when you have many objects that
have the same shape, but only differ in what their prototype value
is. This arises in many JS programs. A simple, and probably common, example
is when the program has a constructor inside of a function:
`
function foo() {

class C {

constructor() { this.field1 = 42; ...; this.fieldN = 42; }
method1() { doStuffWith(this.field); }
method2() { doStuffWith(this.field); }

}
let c = new C;
do things with c;
}

repeatedly call foo() here.
`

Before this patch, in the above program, each time new C created an
object, it would create an object with a different structure. The
reason for this is that each time foo is called, there is a new
instance of C.prototype. However, each new C that was created
with have identical shape sans its prototype value. This would
cause all ICs that used c to quickly give up on any form of caching
because they would see too many structures and give up and permanently
divert control flow to the slow path.

This patch fixes this issue by expanding the notion of where the prototype
of an object is stored. There are now two notions of where the prototype
is stored. A Structure can now be in two modes:

  1. Mono proto mode. This is the same mode as we used to have. It means

the structure itself has a constant prototype value.

  1. Poly proto mode. This means the structure knows nothing about the

prototype value itself. Objects with this structure store their prototype
in normal object field storage. The structure will tell you the offset of
this prototype inside the object's storage. As of today, we only reserve
inline slots for the prototype field because poly proto only occurs
for JSFinalObject. However, this will be expanded to support out of line
offsets in a future patch when we extend poly proto to work when we inherit
from builtin types like Map and Array.

In this initial patch, we do poly proto style inline caching whenever
we see an object that is poly proto or if an object in its prototype lookup
chain is poly proto. Poly proto ICs work by verifying the lookup chain
at runtime. This essentially boils down to performing structure checks
up the prototype chain. In a future patch, we're going to extend object
property condition set to work with objects that don't have poly proto bases.

Initially, accesses that have poly proto access chains will always turn
into GetById/PutById in the DFG. In a future patch, I'm going to teach
the DFG how to inline certain accesses that have poly proto in the access
chain.

One of most interesting parts about this patch is how we decide when to go
poly proto. This patch uses a profiling based approach. An IC will inform
a watchpoint that it sees an opportunity when two Structure's are structurally
the same, sans the base object's prototype. This means that two structures
have equivalent shapes all the way up the prototype chain. To support fast
structural comparison, we compute a hash for a structure based on the properties
it has. We compute this hash as we add properties to the structure. This
computation is nearly free since we always add UniquedStringImpl*'s which
already have their hashes computed. To compare structural equivalence, we
just compare hash values all the way up the prototype chain. This means we
can get hash conflicts between two structures, but it's extremely rare. First,
it'll be rare for two structures to have the same hash. Secondly, we only
consider structures originating from the same executable.

How we set up this poly proto watchpoint is crucial to its design. When we create_this
an object originating from some executable, that executable will create a Box<InlineWatchpointSet>.
Each structure that originates from this executable will get a copy of that
Box<InlineWatchpointSet>. As that structure transitions to new structures,
they too will get a copy of that Box<InilneWatchpointSet>. Therefore, when
invalidating an arbitrary structure's poly proto watchpoint, we will know
the next time we create_this from that executable that it had been
invalidated, and that we should create an object with a poly proto
structure. We also use the pointer value of this Box<InlineWatchpointSet>
to determine if two structures originated from the same executable. This
pruning will severely limit the chances of getting a hash conflict in practice.

This patch is neutral on my MBP on traditional JS benchmarks like Octane/Kraken/Sunspider.
It may be a 1-2% ARES-6 progression.

This patch is between neutral and a 9x progression on the various tests
I added. Most of the microbenchmarks are progressed by at least 50%.

  • JavaScriptCore.xcodeproj/project.pbxproj:
  • Sources.txt:
  • builtins/BuiltinNames.cpp:
  • builtins/BuiltinNames.h:

(JSC::BuiltinNames::BuiltinNames):
(JSC::BuiltinNames::underscoreProtoPrivateName const):

  • bytecode/AccessCase.cpp:

(JSC::AccessCase::AccessCase):
(JSC::AccessCase::create):
(JSC::AccessCase::commit):
(JSC::AccessCase::guardedByStructureCheck const):
(JSC::AccessCase::canReplace const):
(JSC::AccessCase::dump const):
(JSC::AccessCase::visitWeak const):
(JSC::AccessCase::propagateTransitions const):
(JSC::AccessCase::generateWithGuard):
(JSC::AccessCase::generateImpl):

  • bytecode/AccessCase.h:

(JSC::AccessCase::usesPolyProto const):
(JSC::AccessCase::AccessCase):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::finishCreation):

  • bytecode/GetByIdStatus.cpp:

(JSC::GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback):

  • bytecode/GetterSetterAccessCase.cpp:

(JSC::GetterSetterAccessCase::GetterSetterAccessCase):
(JSC::GetterSetterAccessCase::create):

  • bytecode/GetterSetterAccessCase.h:
  • bytecode/InternalFunctionAllocationProfile.h:

(JSC::InternalFunctionAllocationProfile::createAllocationStructureFromBase):

  • bytecode/IntrinsicGetterAccessCase.cpp:

(JSC::IntrinsicGetterAccessCase::IntrinsicGetterAccessCase):

  • bytecode/IntrinsicGetterAccessCase.h:
  • bytecode/ModuleNamespaceAccessCase.cpp:

(JSC::ModuleNamespaceAccessCase::ModuleNamespaceAccessCase):

  • bytecode/ObjectAllocationProfile.cpp: Added.

(JSC::ObjectAllocationProfile::initializeProfile):
(JSC::ObjectAllocationProfile::possibleDefaultPropertyCount):

  • bytecode/ObjectAllocationProfile.h:

(JSC::ObjectAllocationProfile::clear):
(JSC::ObjectAllocationProfile::initialize): Deleted.
(JSC::ObjectAllocationProfile::possibleDefaultPropertyCount): Deleted.

  • bytecode/ObjectPropertyConditionSet.cpp:
  • bytecode/PolyProtoAccessChain.cpp: Added.

(JSC::PolyProtoAccessChain::create):
(JSC::PolyProtoAccessChain::needImpurePropertyWatchpoint const):
(JSC::PolyProtoAccessChain::operator== const):
(JSC::PolyProtoAccessChain::dump const):

  • bytecode/PolyProtoAccessChain.h: Added.

(JSC::PolyProtoAccessChain::clone):
(JSC::PolyProtoAccessChain:: const):
(JSC::PolyProtoAccessChain::operator!= const):
(JSC::PolyProtoAccessChain::forEach const):

  • bytecode/PolymorphicAccess.cpp:

(JSC::PolymorphicAccess::addCases):
(JSC::PolymorphicAccess::regenerate):
(WTF::printInternal):

  • bytecode/PolymorphicAccess.h:

(JSC::AccessGenerationResult::shouldResetStub const):
(JSC::AccessGenerationState::AccessGenerationState):

  • bytecode/PropertyCondition.cpp:

(JSC::PropertyCondition::isStillValidAssumingImpurePropertyWatchpoint const):

  • bytecode/ProxyableAccessCase.cpp:

(JSC::ProxyableAccessCase::ProxyableAccessCase):
(JSC::ProxyableAccessCase::create):

  • bytecode/ProxyableAccessCase.h:
  • bytecode/PutByIdStatus.cpp:

(JSC::PutByIdStatus::computeForStubInfo):

  • bytecode/StructureStubInfo.cpp:

(JSC::StructureStubInfo::addAccessCase):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::load):
(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGGraph.cpp:

(JSC::DFG::Graph::canDoFastSpread):

  • dfg/DFGOperations.cpp:
  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileInstanceOfForObject):
(JSC::DFG::SpeculativeJIT::compileInstanceOf):

  • dfg/DFGSpeculativeJIT.h:
  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileInstanceOf):

  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_instanceof):

  • jit/JITOpcodes32_64.cpp:

(JSC::JIT::emit_op_instanceof):

  • jit/Repatch.cpp:

(JSC::tryCacheGetByID):
(JSC::tryCachePutByID):
(JSC::tryRepatchIn):

  • jsc.cpp:

(WTF::DOMJITGetterBaseJSObject::DOMJITGetterBaseJSObject):
(WTF::DOMJITGetterBaseJSObject::createStructure):
(WTF::DOMJITGetterBaseJSObject::create):
(WTF::DOMJITGetterBaseJSObject::DOMJITAttribute::DOMJITAttribute):
(WTF::DOMJITGetterBaseJSObject::DOMJITAttribute::slowCall):
(WTF::DOMJITGetterBaseJSObject::DOMJITAttribute::callDOMGetter):
(WTF::DOMJITGetterBaseJSObject::customGetter):
(WTF::DOMJITGetterBaseJSObject::finishCreation):
(GlobalObject::finishCreation):
(functionCreateDOMJITGetterBaseJSObject):

  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::LLINT_SLOW_PATH_DECL):

  • runtime/ArrayPrototype.cpp:

(JSC::holesMustForwardToPrototype):
(JSC::fastJoin):
(JSC::arrayProtoFuncReverse):
(JSC::moveElements):

  • runtime/ClonedArguments.cpp:

(JSC::ClonedArguments::createEmpty):
(JSC::ClonedArguments::createWithInlineFrame):
(JSC::ClonedArguments::createWithMachineFrame):
(JSC::ClonedArguments::createByCopyingFrom):

  • runtime/CommonSlowPaths.cpp:

(JSC::SLOW_PATH_DECL):

  • runtime/FunctionExecutable.cpp:

(JSC::FunctionExecutable::visitChildren):

  • runtime/FunctionExecutable.h:
  • runtime/FunctionRareData.cpp:

(JSC::FunctionRareData::initializeObjectAllocationProfile):

  • runtime/FunctionRareData.h:
  • runtime/InternalFunction.cpp:

(JSC::InternalFunction::createSubclassStructureSlow):

  • runtime/JSArray.cpp:

(JSC::JSArray::fastSlice):
(JSC::JSArray::shiftCountWithArrayStorage):
(JSC::JSArray::shiftCountWithAnyIndexingType):
(JSC::JSArray::isIteratorProtocolFastAndNonObservable):

  • runtime/JSArrayInlines.h:

(JSC::JSArray::canFastCopy):

  • runtime/JSCJSValue.cpp:

(JSC::JSValue::dumpInContextAssumingStructure const):

  • runtime/JSFunction.cpp:

(JSC::JSFunction::prototypeForConstruction):
(JSC::JSFunction::allocateAndInitializeRareData):
(JSC::JSFunction::initializeRareData):
(JSC::JSFunction::getOwnPropertySlot):

  • runtime/JSFunction.h:
  • runtime/JSMap.cpp:

(JSC::JSMap::isIteratorProtocolFastAndNonObservable):
(JSC::JSMap::canCloneFastAndNonObservable):

  • runtime/JSObject.cpp:

(JSC::JSObject::putInlineSlow):
(JSC::JSObject::createInitialIndexedStorage):
(JSC::JSObject::createArrayStorage):
(JSC::JSObject::convertUndecidedToArrayStorage):
(JSC::JSObject::convertInt32ToArrayStorage):
(JSC::JSObject::convertDoubleToArrayStorage):
(JSC::JSObject::convertContiguousToArrayStorage):
(JSC::JSObject::ensureInt32Slow):
(JSC::JSObject::ensureDoubleSlow):
(JSC::JSObject::ensureContiguousSlow):
(JSC::JSObject::ensureArrayStorageSlow):
(JSC::JSObject::setPrototypeDirect):
(JSC::JSObject::ordinaryToPrimitive const):
(JSC::JSObject::putByIndexBeyondVectorLength):
(JSC::JSObject::putDirectIndexSlowOrBeyondVectorLength):
(JSC::JSObject::getEnumerableLength):
(JSC::JSObject::anyObjectInChainMayInterceptIndexedAccesses const):
(JSC::JSObject::prototypeChainMayInterceptStoreTo):
(JSC::JSObject::needsSlowPutIndexing const):
(JSC::JSObject::suggestedArrayStorageTransition const):

  • runtime/JSObject.h:

(JSC::JSObject::finishCreation):
(JSC::JSObject::getPrototypeDirect const):
(JSC::JSObject::getPropertySlot):

  • runtime/JSObjectInlines.h:

(JSC::JSObject::getPropertySlot):
(JSC::JSObject::getNonIndexPropertySlot):
(JSC::JSObject::putInlineForJSObject):

  • runtime/JSPropertyNameEnumerator.h:

(JSC::propertyNameEnumerator):

  • runtime/JSSet.cpp:

(JSC::JSSet::isIteratorProtocolFastAndNonObservable):
(JSC::JSSet::canCloneFastAndNonObservable):

  • runtime/LazyClassStructure.h:

(JSC::LazyClassStructure::prototypeConcurrently const): Deleted.

  • runtime/Operations.cpp:

(JSC::normalizePrototypeChain):

  • runtime/Operations.h:
  • runtime/Options.h:
  • runtime/PrototypeMap.cpp:

(JSC::PrototypeMap::createEmptyStructure):
(JSC::PrototypeMap::emptyStructureForPrototypeFromBaseStructure):
(JSC::PrototypeMap::emptyObjectStructureForPrototype):
(JSC::PrototypeMap::clearEmptyObjectStructureForPrototype):

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

(JSC::Structure::Structure):
(JSC::Structure::create):
(JSC::Structure::holesMustForwardToPrototype const):
(JSC::Structure::changePrototypeTransition):
(JSC::Structure::isCheapDuringGC):
(JSC::Structure::toStructureShape):
(JSC::Structure::dump const):
(JSC::Structure::canCachePropertyNameEnumerator const):
(JSC::Structure::anyObjectInChainMayInterceptIndexedAccesses const): Deleted.
(JSC::Structure::needsSlowPutIndexing const): Deleted.
(JSC::Structure::suggestedArrayStorageTransition const): Deleted.
(JSC::Structure::prototypeForLookup const): Deleted.
(JSC::Structure::prototypeChainMayInterceptStoreTo): Deleted.
(JSC::Structure::canUseForAllocationsOf): Deleted.

  • runtime/Structure.h:
  • runtime/StructureChain.h:
  • runtime/StructureInlines.h:

(JSC::Structure::create):
(JSC::Structure::storedPrototypeObject const):
(JSC::Structure::storedPrototypeStructure const):
(JSC::Structure::storedPrototype const):
(JSC::prototypeForLookupPrimitiveImpl):
(JSC::Structure::prototypeForLookup const):
(JSC::Structure::prototypeChain const):
(JSC::Structure::isValid const):
(JSC::Structure::add):
(JSC::Structure::setPropertyTable):
(JSC::Structure::shouldConvertToPolyProto):

  • runtime/StructureRareData.h:
  • runtime/TypeProfilerLog.cpp:

(JSC::TypeProfilerLog::processLogEntries):

  • runtime/TypeSet.cpp:

(JSC::TypeSet::addTypeInformation):

  • runtime/TypeSet.h:
  • runtime/WriteBarrier.h:

(JSC::WriteBarrierBase<Unknown>::isInt32 const):

Source/WTF:

  • wtf/Box.h:

(WTF::Box::operator bool const):
(WTF::Box::operator bool): Deleted.
Make Box movable. Also ensure its operator bool doesn't do an atomic increment.

  • wtf/RefPtr.h:

(WTF::RefPtr::operator bool const):
Add explicit operator bool() for RefPtr.

Tools:

  • Scripts/run-jsc-stress-tests:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/bytecode/AccessCase.cpp

    r221954 r222827  
    5656}
    5757
    58 AccessCase::AccessCase(VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet)
     58AccessCase::AccessCase(VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet, std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain)
    5959    : m_type(type)
    6060    , m_offset(offset)
     61    , m_polyProtoAccessChain(WTFMove(prototypeAccessChain))
    6162{
    6263    m_structure.setMayBeNull(vm, owner, structure);
     
    6465}
    6566
    66 std::unique_ptr<AccessCase> AccessCase::create(VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet)
     67std::unique_ptr<AccessCase> AccessCase::create(VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet, std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain)
    6768{
    6869    switch (type) {
    6970    case InHit:
    7071    case InMiss:
     72        break;
    7173    case ArrayLength:
    7274    case StringLength:
     
    7577    case ModuleNamespaceLoad:
    7678    case Replace:
     79        RELEASE_ASSERT(!prototypeAccessChain);
    7780        break;
    7881    default:
    79         ASSERT_NOT_REACHED();
     82        RELEASE_ASSERT_NOT_REACHED();
    8083    };
    8184
    82     return std::unique_ptr<AccessCase>(new AccessCase(vm, owner, type, offset, structure, conditionSet));
     85    return std::unique_ptr<AccessCase>(new AccessCase(vm, owner, type, offset, structure, conditionSet, WTFMove(prototypeAccessChain)));
    8386}
    8487
    8588std::unique_ptr<AccessCase> AccessCase::create(
    8689    VM& vm, JSCell* owner, PropertyOffset offset, Structure* oldStructure, Structure* newStructure,
    87     const ObjectPropertyConditionSet& conditionSet)
     90    const ObjectPropertyConditionSet& conditionSet, std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain)
    8891{
    8992    RELEASE_ASSERT(oldStructure == newStructure->previousID());
     
    97100    }
    98101
    99     return std::unique_ptr<AccessCase>(new AccessCase(vm, owner, Transition, offset, newStructure, conditionSet));
     102    return std::unique_ptr<AccessCase>(new AccessCase(vm, owner, Transition, offset, newStructure, conditionSet, WTFMove(prototypeAccessChain)));
    100103}
    101104
     
    136139
    137140    if ((structure() && structure()->needImpurePropertyWatchpoint())
    138         || m_conditionSet.needImpurePropertyWatchpoint())
     141        || m_conditionSet.needImpurePropertyWatchpoint()
     142        || (m_polyProtoAccessChain && m_polyProtoAccessChain->needImpurePropertyWatchpoint()))
    139143        result.append(vm.ensureWatchpointSetForImpureProperty(ident));
    140144
     
    150154{
    151155    if (viaProxy())
     156        return false;
     157
     158    if (m_polyProtoAccessChain)
    152159        return false;
    153160
     
    211218    }
    212219    default:
     220        if (other.type() != type())
     221            return false;
     222
     223        if (m_polyProtoAccessChain) {
     224            if (!other.m_polyProtoAccessChain)
     225                return false;
     226            // This is the only check we need since PolyProtoAccessChain contains the base structure.
     227            // If we ever change it to contain only the prototype chain, we'll also need to change
     228            // this to check the base structure.
     229            return structure() == other.structure()
     230                && *m_polyProtoAccessChain == *other.m_polyProtoAccessChain;
     231        }
     232
    213233        if (!guardedByStructureCheck() || !other.guardedByStructureCheck())
    214234            return false;
     
    220240void AccessCase::dump(PrintStream& out) const
    221241{
    222     out.print(m_type, ":(");
     242    out.print("\n", m_type, ":(");
    223243
    224244    CommaPrinter comma;
    225245
    226246    out.print(comma, m_state);
    227 
    228     if (m_type == Transition)
    229         out.print(comma, "structure = ", pointerDump(structure()), " -> ", pointerDump(newStructure()));
    230     else if (m_structure)
    231         out.print(comma, "structure = ", pointerDump(m_structure.get()));
    232247
    233248    if (isValidOffset(m_offset))
     
    236251        out.print(comma, "conditions = ", m_conditionSet);
    237252
     253    if (m_polyProtoAccessChain) {
     254        out.print(comma, "prototype access chain = ");
     255        m_polyProtoAccessChain->dump(structure(), out);
     256    } else {
     257        if (m_type == Transition)
     258            out.print(comma, "structure = ", pointerDump(structure()), " -> ", pointerDump(newStructure()));
     259        else if (m_structure)
     260            out.print(comma, "structure = ", pointerDump(m_structure.get()));
     261    }
     262
    238263    dumpImpl(out, comma);
    239264    out.print(")");
     
    244269    if (m_structure && !Heap::isMarked(m_structure.get()))
    245270        return false;
     271    if (m_polyProtoAccessChain) {
     272        for (Structure* structure : m_polyProtoAccessChain->chain()) {
     273            if (!Heap::isMarked(structure))
     274                return false;
     275        }
     276    }
    246277    if (!m_conditionSet.areStillLive())
    247278        return false;
     
    274305        result &= m_structure->markIfCheap(visitor);
    275306
     307    if (m_polyProtoAccessChain) {
     308        for (Structure* structure : m_polyProtoAccessChain->chain())
     309            result &= structure->markIfCheap(visitor);
     310    }
     311
    276312    switch (m_type) {
    277313    case Transition:
     
    373409
    374410    default: {
    375         if (viaProxy()) {
    376             fallThrough.append(
    377                 jit.branch8(
    378                     CCallHelpers::NotEqual,
    379                     CCallHelpers::Address(baseGPR, JSCell::typeInfoTypeOffset()),
    380                     CCallHelpers::TrustedImm32(PureForwardingProxyType)));
    381 
    382             jit.loadPtr(CCallHelpers::Address(baseGPR, JSProxy::targetOffset()), scratchGPR);
    383 
    384             fallThrough.append(
    385                 jit.branchStructure(
    386                     CCallHelpers::NotEqual,
    387                     CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()),
    388                     structure()));
     411        if (m_polyProtoAccessChain) {
     412            GPRReg baseForAccessGPR = state.scratchGPR;
     413            jit.move(state.baseGPR, baseForAccessGPR);
     414            m_polyProtoAccessChain->forEach(structure(), [&] (Structure* structure, bool atEnd) {
     415                fallThrough.append(
     416                    jit.branchStructure(
     417                        CCallHelpers::NotEqual,
     418                        CCallHelpers::Address(baseForAccessGPR, JSCell::structureIDOffset()),
     419                        structure));
     420                if (atEnd) {
     421                    if ((m_type == Miss || m_type == InMiss || m_type == Transition) && structure->hasPolyProto()) {
     422                        // For a Miss/InMiss/Transition, we must ensure we're at the end when the last item is poly proto.
     423                        // Transitions must do this because they need to verify there isn't a setter in the chain.
     424                        // Miss/InMiss need to do this to ensure there isn't a new item at the end of the chain that
     425                        // has the property.
     426                        PropertyOffset polyProtoOffset = structure->polyProtoOffset();
     427                        RELEASE_ASSERT(isInlineOffset(polyProtoOffset));
     428#if USE(JSVALUE64)
     429                        jit.load64(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(polyProtoOffset)), baseForAccessGPR);
     430                        fallThrough.append(jit.branch64(CCallHelpers::NotEqual, baseForAccessGPR, CCallHelpers::TrustedImm64(ValueNull)));
     431#else
     432                        jit.load32(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(polyProtoOffset) + PayloadOffset), baseForAccessGPR);
     433                        fallThrough.append(jit.branchTestPtr(CCallHelpers::NonZero, baseForAccessGPR));
     434#endif
     435                    }
     436                } else {
     437                    if (structure->hasMonoProto()) {
     438                        JSValue prototype = structure->prototypeForLookup(state.m_globalObject);
     439                        RELEASE_ASSERT(prototype.isObject());
     440                        jit.move(CCallHelpers::TrustedImmPtr(asObject(prototype)), baseForAccessGPR);
     441                    } else {
     442                        RELEASE_ASSERT(structure->isObject()); // Primitives must have a stored prototype. We use prototypeForLookup for them.
     443                        PropertyOffset polyProtoOffset = structure->polyProtoOffset();
     444                        RELEASE_ASSERT(isInlineOffset(polyProtoOffset));
     445#if USE(JSVALUE64)
     446                        jit.load64(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(polyProtoOffset)), baseForAccessGPR);
     447                        fallThrough.append(jit.branch64(CCallHelpers::Equal, baseForAccessGPR, CCallHelpers::TrustedImm64(ValueNull)));
     448#else
     449                        jit.load32(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(polyProtoOffset) + PayloadOffset), baseForAccessGPR);
     450                        fallThrough.append(jit.branchTestPtr(CCallHelpers::Zero, baseForAccessGPR));
     451#endif
     452                    }
     453                }
     454            });
    389455        } else {
    390             fallThrough.append(
    391                 jit.branchStructure(
    392                     CCallHelpers::NotEqual,
    393                     CCallHelpers::Address(baseGPR, JSCell::structureIDOffset()),
    394                     structure()));
     456            if (viaProxy()) {
     457                fallThrough.append(
     458                    jit.branch8(
     459                        CCallHelpers::NotEqual,
     460                        CCallHelpers::Address(baseGPR, JSCell::typeInfoTypeOffset()),
     461                        CCallHelpers::TrustedImm32(PureForwardingProxyType)));
     462
     463                jit.loadPtr(CCallHelpers::Address(baseGPR, JSProxy::targetOffset()), scratchGPR);
     464
     465                fallThrough.append(
     466                    jit.branchStructure(
     467                        CCallHelpers::NotEqual,
     468                        CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()),
     469                        structure()));
     470            } else {
     471                fallThrough.append(
     472                    jit.branchStructure(
     473                        CCallHelpers::NotEqual,
     474                        CCallHelpers::Address(baseGPR, JSCell::structureIDOffset()),
     475                        structure()));
     476            }
    395477        }
    396478        break;
     
    429511
    430512    for (const ObjectPropertyCondition& condition : m_conditionSet) {
     513        RELEASE_ASSERT(!m_polyProtoAccessChain);
     514
    431515        Structure* structure = condition.object()->structure();
    432516
     
    505589
    506590        GPRReg baseForAccessGPR;
    507         if (!m_conditionSet.isEmpty()) {
    508             jit.move(
    509                 CCallHelpers::TrustedImmPtr(alternateBase()),
    510                 scratchGPR);
     591        if (m_polyProtoAccessChain) {
     592            // This isn't pretty, but we know we got here via generateWithGuard,
     593            // and it left the baseForAccess inside scratchGPR. We could re-derive the base,
     594            // but it'd require emitting the same code to load the base twice.
    511595            baseForAccessGPR = scratchGPR;
    512         } else
    513             baseForAccessGPR = baseForGetGPR;
     596        } else {
     597            if (!m_conditionSet.isEmpty()) {
     598                jit.move(
     599                    CCallHelpers::TrustedImmPtr(alternateBase()), scratchGPR);
     600                baseForAccessGPR = scratchGPR;
     601            } else
     602                baseForAccessGPR = baseForGetGPR;
     603        }
    514604
    515605        GPRReg loadedValueGPR = InvalidGPRReg;
     
    10171107    case IntrinsicGetter: {
    10181108        RELEASE_ASSERT(isValidOffset(offset()));
    1019        
     1109
    10201110        // We need to ensure the getter value does not move from under us. Note that GetterSetters
    10211111        // are immutable so we just need to watch the property not any value inside it.
Note: See TracChangeset for help on using the changeset viewer.