GC constraint solving should be parallel
[WebKit-https.git] / Source / JavaScriptCore / bytecode / AccessCase.cpp
index 2807410..651a8c9 100644 (file)
 
 namespace JSC {
 
+namespace AccessCaseInternal {
 static const bool verbose = false;
+}
 
-AccessCase::AccessCase(VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet)
+AccessCase::AccessCase(VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet, std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain)
     : m_type(type)
     , m_offset(offset)
+    , m_polyProtoAccessChain(WTFMove(prototypeAccessChain))
 {
     m_structure.setMayBeNull(vm, owner, structure);
     m_conditionSet = conditionSet;
 }
 
-std::unique_ptr<AccessCase> AccessCase::create(VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet)
+std::unique_ptr<AccessCase> AccessCase::create(VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet, std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain)
 {
     switch (type) {
     case InHit:
     case InMiss:
+        break;
     case ArrayLength:
     case StringLength:
     case DirectArgumentsLength:
     case ScopedArgumentsLength:
     case ModuleNamespaceLoad:
     case Replace:
+        RELEASE_ASSERT(!prototypeAccessChain);
         break;
     default:
-        ASSERT_NOT_REACHED();
+        RELEASE_ASSERT_NOT_REACHED();
     };
 
-    return std::unique_ptr<AccessCase>(new AccessCase(vm, owner, type, offset, structure, conditionSet));
+    return std::unique_ptr<AccessCase>(new AccessCase(vm, owner, type, offset, structure, conditionSet, WTFMove(prototypeAccessChain)));
 }
 
 std::unique_ptr<AccessCase> AccessCase::create(
     VM& vm, JSCell* owner, PropertyOffset offset, Structure* oldStructure, Structure* newStructure,
-    const ObjectPropertyConditionSet& conditionSet)
+    const ObjectPropertyConditionSet& conditionSet, std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain)
 {
     RELEASE_ASSERT(oldStructure == newStructure->previousID());
 
@@ -94,7 +99,7 @@ std::unique_ptr<AccessCase> AccessCase::create(
         return nullptr;
     }
 
-    return std::unique_ptr<AccessCase>(new AccessCase(vm, owner, Transition, offset, newStructure, conditionSet));
+    return std::unique_ptr<AccessCase>(new AccessCase(vm, owner, Transition, offset, newStructure, conditionSet, WTFMove(prototypeAccessChain)));
 }
 
 AccessCase::~AccessCase()
@@ -131,14 +136,24 @@ Vector<WatchpointSet*, 2> AccessCase::commit(VM& vm, const Identifier& ident)
     RELEASE_ASSERT(m_state == Primordial || m_state == Committed);
 
     Vector<WatchpointSet*, 2> result;
+    Structure* structure = this->structure();
 
-    if ((structure() && structure()->needImpurePropertyWatchpoint())
-        || m_conditionSet.needImpurePropertyWatchpoint())
+    if ((structure && structure->needImpurePropertyWatchpoint())
+        || m_conditionSet.needImpurePropertyWatchpoint()
+        || (m_polyProtoAccessChain && m_polyProtoAccessChain->needImpurePropertyWatchpoint()))
         result.append(vm.ensureWatchpointSetForImpureProperty(ident));
 
     if (additionalSet())
         result.append(additionalSet());
 
+    if (structure
+        && structure->hasRareData()
+        && structure->rareData()->hasSharedPolyProtoWatchpoint()
+        && structure->rareData()->sharedPolyProtoWatchpoint()->isStillValid()) {
+        WatchpointSet* set = structure->rareData()->sharedPolyProtoWatchpoint()->inflate();
+        result.append(set);
+    }
+
     m_state = Committed;
 
     return result;
@@ -149,6 +164,9 @@ bool AccessCase::guardedByStructureCheck() const
     if (viaProxy())
         return false;
 
+    if (m_polyProtoAccessChain)
+        return false;
+
     switch (m_type) {
     case ArrayLength:
     case StringLength:
@@ -208,6 +226,19 @@ bool AccessCase::canReplace(const AccessCase& other) const
         return thisCase.moduleNamespaceObject() == otherCase.moduleNamespaceObject();
     }
     default:
+        if (other.type() != type())
+            return false;
+
+        if (m_polyProtoAccessChain) {
+            if (!other.m_polyProtoAccessChain)
+                return false;
+            // This is the only check we need since PolyProtoAccessChain contains the base structure.
+            // If we ever change it to contain only the prototype chain, we'll also need to change
+            // this to check the base structure.
+            return structure() == other.structure()
+                && *m_polyProtoAccessChain == *other.m_polyProtoAccessChain;
+        }
+
         if (!guardedByStructureCheck() || !other.guardedByStructureCheck())
             return false;
 
@@ -217,22 +248,27 @@ bool AccessCase::canReplace(const AccessCase& other) const
 
 void AccessCase::dump(PrintStream& out) const
 {
-    out.print(m_type, ":(");
+    out.print("\n", m_type, ":(");
 
     CommaPrinter comma;
 
     out.print(comma, m_state);
 
-    if (m_type == Transition)
-        out.print(comma, "structure = ", pointerDump(structure()), " -> ", pointerDump(newStructure()));
-    else if (m_structure)
-        out.print(comma, "structure = ", pointerDump(m_structure.get()));
-
     if (isValidOffset(m_offset))
         out.print(comma, "offset = ", m_offset);
     if (!m_conditionSet.isEmpty())
         out.print(comma, "conditions = ", m_conditionSet);
 
+    if (m_polyProtoAccessChain) {
+        out.print(comma, "prototype access chain = ");
+        m_polyProtoAccessChain->dump(structure(), out);
+    } else {
+        if (m_type == Transition)
+            out.print(comma, "structure = ", pointerDump(structure()), " -> ", pointerDump(newStructure()));
+        else if (m_structure)
+            out.print(comma, "structure = ", pointerDump(m_structure.get()));
+    }
+
     dumpImpl(out, comma);
     out.print(")");
 }
@@ -241,6 +277,12 @@ bool AccessCase::visitWeak(VM& vm) const
 {
     if (m_structure && !Heap::isMarked(m_structure.get()))
         return false;
+    if (m_polyProtoAccessChain) {
+        for (Structure* structure : m_polyProtoAccessChain->chain()) {
+            if (!Heap::isMarked(structure))
+                return false;
+        }
+    }
     if (!m_conditionSet.areStillLive())
         return false;
     if (isAccessor()) {
@@ -271,9 +313,14 @@ bool AccessCase::propagateTransitions(SlotVisitor& visitor) const
     if (m_structure)
         result &= m_structure->markIfCheap(visitor);
 
+    if (m_polyProtoAccessChain) {
+        for (Structure* structure : m_polyProtoAccessChain->chain())
+            result &= structure->markIfCheap(visitor);
+    }
+
     switch (m_type) {
     case Transition:
-        if (Heap::isMarkedConcurrently(m_structure->previousID()))
+        if (Heap::isMarked(m_structure->previousID()))
             visitor.appendUnbarriered(m_structure.get());
         else
             result = false;
@@ -370,26 +417,68 @@ void AccessCase::generateWithGuard(
     }
 
     default: {
-        if (viaProxy()) {
-            fallThrough.append(
-                jit.branch8(
-                    CCallHelpers::NotEqual,
-                    CCallHelpers::Address(baseGPR, JSCell::typeInfoTypeOffset()),
-                    CCallHelpers::TrustedImm32(PureForwardingProxyType)));
-
-            jit.loadPtr(CCallHelpers::Address(baseGPR, JSProxy::targetOffset()), scratchGPR);
-
-            fallThrough.append(
-                jit.branchStructure(
-                    CCallHelpers::NotEqual,
-                    CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()),
-                    structure()));
+        if (m_polyProtoAccessChain) {
+            GPRReg baseForAccessGPR = state.scratchGPR;
+            jit.move(state.baseGPR, baseForAccessGPR);
+            m_polyProtoAccessChain->forEach(structure(), [&] (Structure* structure, bool atEnd) {
+                fallThrough.append(
+                    jit.branchStructure(
+                        CCallHelpers::NotEqual,
+                        CCallHelpers::Address(baseForAccessGPR, JSCell::structureIDOffset()),
+                        structure));
+                if (atEnd) {
+                    if ((m_type == Miss || m_type == InMiss || m_type == Transition) && structure->hasPolyProto()) {
+                        // For a Miss/InMiss/Transition, we must ensure we're at the end when the last item is poly proto.
+                        // Transitions must do this because they need to verify there isn't a setter in the chain.
+                        // Miss/InMiss need to do this to ensure there isn't a new item at the end of the chain that
+                        // has the property.
+#if USE(JSVALUE64)
+                        jit.load64(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(knownPolyProtoOffset)), baseForAccessGPR);
+                        fallThrough.append(jit.branch64(CCallHelpers::NotEqual, baseForAccessGPR, CCallHelpers::TrustedImm64(ValueNull)));
+#else
+                        jit.load32(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(knownPolyProtoOffset) + PayloadOffset), baseForAccessGPR);
+                        fallThrough.append(jit.branchTestPtr(CCallHelpers::NonZero, baseForAccessGPR));
+#endif
+                    }
+                } else {
+                    if (structure->hasMonoProto()) {
+                        JSValue prototype = structure->prototypeForLookup(state.m_globalObject);
+                        RELEASE_ASSERT(prototype.isObject());
+                        jit.move(CCallHelpers::TrustedImmPtr(asObject(prototype)), baseForAccessGPR);
+                    } else {
+                        RELEASE_ASSERT(structure->isObject()); // Primitives must have a stored prototype. We use prototypeForLookup for them.
+#if USE(JSVALUE64)
+                        jit.load64(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(knownPolyProtoOffset)), baseForAccessGPR);
+                        fallThrough.append(jit.branch64(CCallHelpers::Equal, baseForAccessGPR, CCallHelpers::TrustedImm64(ValueNull)));
+#else
+                        jit.load32(MacroAssembler::Address(baseForAccessGPR, offsetRelativeToBase(knownPolyProtoOffset) + PayloadOffset), baseForAccessGPR);
+                        fallThrough.append(jit.branchTestPtr(CCallHelpers::Zero, baseForAccessGPR));
+#endif
+                    }
+                }
+            });
         } else {
-            fallThrough.append(
-                jit.branchStructure(
-                    CCallHelpers::NotEqual,
-                    CCallHelpers::Address(baseGPR, JSCell::structureIDOffset()),
-                    structure()));
+            if (viaProxy()) {
+                fallThrough.append(
+                    jit.branch8(
+                        CCallHelpers::NotEqual,
+                        CCallHelpers::Address(baseGPR, JSCell::typeInfoTypeOffset()),
+                        CCallHelpers::TrustedImm32(PureForwardingProxyType)));
+
+                jit.loadPtr(CCallHelpers::Address(baseGPR, JSProxy::targetOffset()), scratchGPR);
+
+                fallThrough.append(
+                    jit.branchStructure(
+                        CCallHelpers::NotEqual,
+                        CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()),
+                        structure()));
+            } else {
+                fallThrough.append(
+                    jit.branchStructure(
+                        CCallHelpers::NotEqual,
+                        CCallHelpers::Address(baseGPR, JSCell::structureIDOffset()),
+                        structure()));
+            }
         }
         break;
     } };
@@ -408,7 +497,7 @@ void AccessCase::generate(AccessGenerationState& state)
 void AccessCase::generateImpl(AccessGenerationState& state)
 {
     SuperSamplerScope superSamplerScope(false);
-    if (verbose)
+    if (AccessCaseInternal::verbose)
         dataLog("\n\nGenerating code for: ", *this, "\n");
 
     ASSERT(m_state == Generated); // We rely on the callers setting this for us.
@@ -426,6 +515,8 @@ void AccessCase::generateImpl(AccessGenerationState& state)
     ASSERT(m_conditionSet.structuresEnsureValidityAssumingImpurePropertyWatchpoint());
 
     for (const ObjectPropertyCondition& condition : m_conditionSet) {
+        RELEASE_ASSERT(!m_polyProtoAccessChain);
+
         Structure* structure = condition.object()->structure();
 
         if (condition.isWatchableAssumingImpurePropertyWatchpoint()) {
@@ -502,13 +593,19 @@ void AccessCase::generateImpl(AccessGenerationState& state)
             baseForGetGPR = baseGPR;
 
         GPRReg baseForAccessGPR;
-        if (!m_conditionSet.isEmpty()) {
-            jit.move(
-                CCallHelpers::TrustedImmPtr(alternateBase()),
-                scratchGPR);
+        if (m_polyProtoAccessChain) {
+            // This isn't pretty, but we know we got here via generateWithGuard,
+            // and it left the baseForAccess inside scratchGPR. We could re-derive the base,
+            // but it'd require emitting the same code to load the base twice.
             baseForAccessGPR = scratchGPR;
-        } else
-            baseForAccessGPR = baseForGetGPR;
+        } else {
+            if (!m_conditionSet.isEmpty()) {
+                jit.move(
+                    CCallHelpers::TrustedImmPtr(alternateBase()), scratchGPR);
+                baseForAccessGPR = scratchGPR;
+            } else
+                baseForAccessGPR = baseForGetGPR;
+        }
 
         GPRReg loadedValueGPR = InvalidGPRReg;
         if (m_type != CustomValueGetter && m_type != CustomAccessorGetter && m_type != CustomValueSetter && m_type != CustomAccessorSetter) {
@@ -527,8 +624,6 @@ void AccessCase::generateImpl(AccessGenerationState& state)
                 jit.loadPtr(
                     CCallHelpers::Address(baseForAccessGPR, JSObject::butterflyOffset()),
                     loadedValueGPR);
-                // FIXME: Do caging!
-                // https://bugs.webkit.org/show_bug.cgi?id=175295
                 storageGPR = loadedValueGPR;
             }
 
@@ -791,11 +886,11 @@ void AccessCase::generateImpl(AccessGenerationState& state)
 
     case Replace: {
         if (InferredType* type = structure()->inferredTypeFor(ident.impl())) {
-            if (verbose)
+            if (AccessCaseInternal::verbose)
                 dataLog("Have type: ", type->descriptor(), "\n");
             state.failAndRepatch.append(
                 jit.branchIfNotType(valueRegs, scratchGPR, type->descriptor()));
-        } else if (verbose)
+        } else if (AccessCaseInternal::verbose)
             dataLog("Don't have type.\n");
 
         if (isInlineOffset(m_offset)) {
@@ -821,11 +916,11 @@ void AccessCase::generateImpl(AccessGenerationState& state)
         RELEASE_ASSERT(GPRInfo::numberOfRegisters >= 6 || !structure()->outOfLineCapacity() || structure()->outOfLineCapacity() == newStructure()->outOfLineCapacity());
 
         if (InferredType* type = newStructure()->inferredTypeFor(ident.impl())) {
-            if (verbose)
+            if (AccessCaseInternal::verbose)
                 dataLog("Have type: ", type->descriptor(), "\n");
             state.failAndRepatch.append(
                 jit.branchIfNotType(valueRegs, scratchGPR, type->descriptor()));
-        } else if (verbose)
+        } else if (AccessCaseInternal::verbose)
             dataLog("Don't have type.\n");
 
         // NOTE: This logic is duplicated in AccessCase::doesCalls(). It's important that doesCalls() knows
@@ -860,7 +955,7 @@ void AccessCase::generateImpl(AccessGenerationState& state)
             size_t newSize = newStructure()->outOfLineCapacity() * sizeof(JSValue);
 
             if (allocatingInline) {
-                MarkedAllocator* allocator = vm.jsValueGigacageAuxiliarySpace.allocatorFor(newSize);
+                MarkedAllocator* allocator = vm.jsValueGigacageAuxiliarySpace.allocatorFor(newSize, AllocatorForMode::AllocatorIfExists);
 
                 if (!allocator) {
                     // Yuck, this case would suck!
@@ -879,8 +974,6 @@ void AccessCase::generateImpl(AccessGenerationState& state)
                     // already had out-of-line property storage).
 
                     jit.loadPtr(CCallHelpers::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR3);
-                    // FIXME: Do caging!
-                    // https://bugs.webkit.org/show_bug.cgi?id=175295
 
                     // We have scratchGPR = new storage, scratchGPR3 = old storage,
                     // scratchGPR2 = available
@@ -947,7 +1040,9 @@ void AccessCase::generateImpl(AccessGenerationState& state)
                 state.emitExplicitExceptionHandler();
                 
                 noException.link(&jit);
-                state.restoreLiveRegistersFromStackForCall(spillState);
+                RegisterSet resultRegisterToExclude;
+                resultRegisterToExclude.set(scratchGPR);
+                state.restoreLiveRegistersFromStackForCall(spillState, resultRegisterToExclude);
             }
         }
         
@@ -959,11 +1054,8 @@ void AccessCase::generateImpl(AccessGenerationState& state)
                     JSObject::offsetOfInlineStorage() +
                     offsetInInlineStorage(m_offset) * sizeof(JSValue)));
         } else {
-            if (!allocating) {
+            if (!allocating)
                 jit.loadPtr(CCallHelpers::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR);
-                // FIXME: Do caging!
-                // https://bugs.webkit.org/show_bug.cgi?id=175295
-            }
             jit.storeValue(
                 valueRegs,
                 CCallHelpers::Address(scratchGPR, offsetInButterfly(m_offset) * sizeof(JSValue)));
@@ -999,8 +1091,6 @@ void AccessCase::generateImpl(AccessGenerationState& state)
         
     case ArrayLength: {
         jit.loadPtr(CCallHelpers::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR);
-        // FIXME: Do caging!
-        // https://bugs.webkit.org/show_bug.cgi?id=175295
         jit.load32(CCallHelpers::Address(scratchGPR, ArrayStorage::lengthOffset()), scratchGPR);
         state.failAndIgnore.append(
             jit.branch32(CCallHelpers::LessThan, scratchGPR, CCallHelpers::TrustedImm32(0)));
@@ -1018,7 +1108,7 @@ void AccessCase::generateImpl(AccessGenerationState& state)
         
     case IntrinsicGetter: {
         RELEASE_ASSERT(isValidOffset(offset()));
-        
+
         // We need to ensure the getter value does not move from under us. Note that GetterSetters
         // are immutable so we just need to watch the property not any value inside it.
         Structure* currStructure;