X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/81345200c95645a1b0d2635520f96ad55dfde63f..refs/heads/master:/bytecode/PutByIdStatus.cpp diff --git a/bytecode/PutByIdStatus.cpp b/bytecode/PutByIdStatus.cpp index 5743750..9e219d2 100644 --- a/bytecode/PutByIdStatus.cpp +++ b/bytecode/PutByIdStatus.cpp @@ -26,7 +26,9 @@ #include "config.h" #include "PutByIdStatus.h" +#include "AccessorCallJITStubRoutine.h" #include "CodeBlock.h" +#include "ComplexGetStatus.h" #include "LLIntData.h" #include "LowLevelInterpreter.h" #include "JSCInlines.h" @@ -40,7 +42,11 @@ namespace JSC { bool PutByIdStatus::appendVariant(const PutByIdVariant& variant) { for (unsigned i = 0; i < m_variants.size(); ++i) { - if (m_variants[i].oldStructure() == variant.oldStructure()) + if (m_variants[i].attemptToMerge(variant)) + return true; + } + for (unsigned i = 0; i < m_variants.size(); ++i) { + if (m_variants[i].oldStructure().overlaps(variant.oldStructure())) return false; } m_variants.append(variant); @@ -48,17 +54,15 @@ bool PutByIdStatus::appendVariant(const PutByIdVariant& variant) } #if ENABLE(DFG_JIT) -bool PutByIdStatus::hasExitSite(const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, unsigned bytecodeIndex, ExitingJITType exitType) +bool PutByIdStatus::hasExitSite(const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, unsigned bytecodeIndex) { - return profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCache, exitType)) - || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCacheWatchpoint, exitType)) - || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadWeakConstantCache, exitType)) - || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadWeakConstantCacheWatchpoint, exitType)); + return profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadCache)) + || profiledBlock->hasExitSite(locker, DFG::FrequentExitSite(bytecodeIndex, BadConstantCache)); } #endif -PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, StringImpl* uid) +PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, UniquedStringImpl* uid) { UNUSED_PARAM(profiledBlock); UNUSED_PARAM(bytecodeIndex); @@ -71,7 +75,7 @@ PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned if (instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id) || instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id_out_of_line)) { - PropertyOffset offset = structure->getConcurrently(*profiledBlock->vm(), uid); + PropertyOffset offset = structure->getConcurrently(uid); if (!isValidOffset(offset)) return PutByIdStatus(NoInformation); @@ -90,17 +94,18 @@ PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned ASSERT(newStructure); ASSERT(chain); - PropertyOffset offset = newStructure->getConcurrently(*profiledBlock->vm(), uid); + PropertyOffset offset = newStructure->getConcurrently(uid); if (!isValidOffset(offset)) return PutByIdStatus(NoInformation); - return PutByIdVariant::transition( - structure, newStructure, - chain ? adoptRef(new IntendedStructureChain(profiledBlock, structure, chain)) : 0, - offset); + RefPtr intendedChain; + if (chain) + intendedChain = adoptRef(new IntendedStructureChain(profiledBlock, structure, chain)); + + return PutByIdVariant::transition(structure, newStructure, intendedChain.get(), offset); } -PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, StringImpl* uid) +PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid) { ConcurrentJITLocker locker(profiledBlock->m_lock); @@ -108,12 +113,13 @@ PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& m UNUSED_PARAM(bytecodeIndex); UNUSED_PARAM(uid); #if ENABLE(DFG_JIT) - if (profiledBlock->likelyToTakeSlowCase(bytecodeIndex) - || hasExitSite(locker, profiledBlock, bytecodeIndex)) + if (hasExitSite(locker, profiledBlock, bytecodeIndex)) return PutByIdStatus(TakesSlowPath); StructureStubInfo* stubInfo = map.get(CodeOrigin(bytecodeIndex)); - PutByIdStatus result = computeForStubInfo(locker, profiledBlock, stubInfo, uid); + PutByIdStatus result = computeForStubInfo( + locker, profiledBlock, stubInfo, uid, + CallLinkStatus::computeExitSiteData(locker, profiledBlock, bytecodeIndex)); if (!result) return computeFromLLInt(profiledBlock, bytecodeIndex, uid); @@ -125,14 +131,19 @@ PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& m } #if ENABLE(JIT) -PutByIdStatus PutByIdStatus::computeForStubInfo(const ConcurrentJITLocker&, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, StringImpl* uid) +PutByIdStatus PutByIdStatus::computeForStubInfo( + const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, + UniquedStringImpl* uid, CallLinkStatus::ExitSiteData callExitSiteData) { - if (!stubInfo || !stubInfo->seen) + if (!stubInfo) return PutByIdStatus(); - if (stubInfo->resetByGC) + if (stubInfo->tookSlowPath) return PutByIdStatus(TakesSlowPath); - + + if (!stubInfo->seen) + return PutByIdStatus(); + switch (stubInfo->accessType) { case access_unset: // If the JIT saw it but didn't optimize it, then assume that this takes slow path. @@ -140,8 +151,7 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(const ConcurrentJITLocker&, Code case access_put_by_id_replace: { PropertyOffset offset = - stubInfo->u.putByIdReplace.baseObjectStructure->getConcurrently( - *profiledBlock->vm(), uid); + stubInfo->u.putByIdReplace.baseObjectStructure->getConcurrently(uid); if (isValidOffset(offset)) { return PutByIdVariant::replace( stubInfo->u.putByIdReplace.baseObjectStructure.get(), offset); @@ -153,16 +163,18 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(const ConcurrentJITLocker&, Code case access_put_by_id_transition_direct: { ASSERT(stubInfo->u.putByIdTransition.previousStructure->transitionWatchpointSetHasBeenInvalidated()); PropertyOffset offset = - stubInfo->u.putByIdTransition.structure->getConcurrently( - *profiledBlock->vm(), uid); + stubInfo->u.putByIdTransition.structure->getConcurrently(uid); if (isValidOffset(offset)) { + RefPtr chain; + if (stubInfo->u.putByIdTransition.chain) { + chain = adoptRef(new IntendedStructureChain( + profiledBlock, stubInfo->u.putByIdTransition.previousStructure.get(), + stubInfo->u.putByIdTransition.chain.get())); + } return PutByIdVariant::transition( stubInfo->u.putByIdTransition.previousStructure.get(), stubInfo->u.putByIdTransition.structure.get(), - stubInfo->u.putByIdTransition.chain ? adoptRef(new IntendedStructureChain( - profiledBlock, stubInfo->u.putByIdTransition.previousStructure.get(), - stubInfo->u.putByIdTransition.chain.get())) : 0, - offset); + chain.get(), offset); } return PutByIdStatus(TakesSlowPath); } @@ -173,41 +185,89 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(const ConcurrentJITLocker&, Code PutByIdStatus result; result.m_state = Simple; + State slowPathState = TakesSlowPath; + for (unsigned i = 0; i < list->size(); ++i) { + const PutByIdAccess& access = list->at(i); + + switch (access.type()) { + case PutByIdAccess::Setter: + case PutByIdAccess::CustomSetter: + slowPathState = MakesCalls; + break; + default: + break; + } + } + for (unsigned i = 0; i < list->size(); ++i) { const PutByIdAccess& access = list->at(i); + PutByIdVariant variant; + switch (access.type()) { case PutByIdAccess::Replace: { Structure* structure = access.structure(); - PropertyOffset offset = structure->getConcurrently(*profiledBlock->vm(), uid); + PropertyOffset offset = structure->getConcurrently(uid); if (!isValidOffset(offset)) - return PutByIdStatus(TakesSlowPath); - if (!result.appendVariant(PutByIdVariant::replace(structure, offset))) - return PutByIdStatus(TakesSlowPath); + return PutByIdStatus(slowPathState); + variant = PutByIdVariant::replace(structure, offset); break; } case PutByIdAccess::Transition: { PropertyOffset offset = - access.newStructure()->getConcurrently(*profiledBlock->vm(), uid); + access.newStructure()->getConcurrently(uid); if (!isValidOffset(offset)) - return PutByIdStatus(TakesSlowPath); - bool ok = result.appendVariant(PutByIdVariant::transition( - access.oldStructure(), access.newStructure(), - access.chain() ? adoptRef(new IntendedStructureChain( - profiledBlock, access.oldStructure(), access.chain())) : 0, - offset)); - if (!ok) - return PutByIdStatus(TakesSlowPath); + return PutByIdStatus(slowPathState); + RefPtr chain; + if (access.chain()) { + chain = adoptRef(new IntendedStructureChain( + profiledBlock, access.oldStructure(), access.chain())); + if (!chain->isStillValid()) + continue; + } + variant = PutByIdVariant::transition( + access.oldStructure(), access.newStructure(), chain.get(), offset); break; } - case PutByIdAccess::Setter: + + case PutByIdAccess::Setter: { + Structure* structure = access.structure(); + + ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor( + profiledBlock, structure, access.chain(), access.chainCount(), uid); + + switch (complexGetStatus.kind()) { + case ComplexGetStatus::ShouldSkip: + continue; + + case ComplexGetStatus::TakesSlowPath: + return PutByIdStatus(slowPathState); + + case ComplexGetStatus::Inlineable: { + AccessorCallJITStubRoutine* stub = static_cast( + access.stubRoutine()); + std::unique_ptr callLinkStatus = + std::make_unique( + CallLinkStatus::computeFor( + locker, profiledBlock, *stub->m_callLinkInfo, callExitSiteData)); + + variant = PutByIdVariant::setter( + structure, complexGetStatus.offset(), complexGetStatus.chain(), + WTF::move(callLinkStatus)); + } } + break; + } + case PutByIdAccess::CustomSetter: return PutByIdStatus(MakesCalls); default: - return PutByIdStatus(TakesSlowPath); + return PutByIdStatus(slowPathState); } + + if (!result.appendVariant(variant)) + return PutByIdStatus(slowPathState); } return result; @@ -219,20 +279,24 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(const ConcurrentJITLocker&, Code } #endif -PutByIdStatus PutByIdStatus::computeFor(CodeBlock* baselineBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap, StubInfoMap& dfgMap, CodeOrigin codeOrigin, StringImpl* uid) +PutByIdStatus PutByIdStatus::computeFor(CodeBlock* baselineBlock, CodeBlock* dfgBlock, StubInfoMap& baselineMap, StubInfoMap& dfgMap, CodeOrigin codeOrigin, UniquedStringImpl* uid) { #if ENABLE(DFG_JIT) if (dfgBlock) { + CallLinkStatus::ExitSiteData exitSiteData; { ConcurrentJITLocker locker(baselineBlock->m_lock); - if (hasExitSite(locker, baselineBlock, codeOrigin.bytecodeIndex, ExitFromFTL)) + if (hasExitSite(locker, baselineBlock, codeOrigin.bytecodeIndex)) return PutByIdStatus(TakesSlowPath); + exitSiteData = CallLinkStatus::computeExitSiteData( + locker, baselineBlock, codeOrigin.bytecodeIndex); } PutByIdStatus result; { ConcurrentJITLocker locker(dfgBlock->m_lock); - result = computeForStubInfo(locker, dfgBlock, dfgMap.get(codeOrigin), uid); + result = computeForStubInfo( + locker, dfgBlock, dfgMap.get(codeOrigin), uid, exitSiteData); } // We use TakesSlowPath in some cases where the stub was unset. That's weird and @@ -249,88 +313,109 @@ PutByIdStatus PutByIdStatus::computeFor(CodeBlock* baselineBlock, CodeBlock* dfg return computeFor(baselineBlock, baselineMap, codeOrigin.bytecodeIndex, uid); } -PutByIdStatus PutByIdStatus::computeFor(VM& vm, JSGlobalObject* globalObject, Structure* structure, StringImpl* uid, bool isDirect) +PutByIdStatus PutByIdStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, UniquedStringImpl* uid, bool isDirect) { - if (toUInt32FromStringImpl(uid) != PropertyName::NotAnIndex) + if (parseIndex(*uid)) return PutByIdStatus(TakesSlowPath); - if (!structure) - return PutByIdStatus(TakesSlowPath); + if (set.isEmpty()) + return PutByIdStatus(); - if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType) - return PutByIdStatus(TakesSlowPath); + PutByIdStatus result; + result.m_state = Simple; + for (unsigned i = 0; i < set.size(); ++i) { + Structure* structure = set[i]; + + if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType) + return PutByIdStatus(TakesSlowPath); - if (!structure->propertyAccessesAreCacheable()) - return PutByIdStatus(TakesSlowPath); + if (!structure->propertyAccessesAreCacheable()) + return PutByIdStatus(TakesSlowPath); - unsigned attributes; - JSCell* specificValue; - PropertyOffset offset = structure->getConcurrently(vm, uid, attributes, specificValue); - if (isValidOffset(offset)) { - if (attributes & CustomAccessor) - return PutByIdStatus(MakesCalls); + unsigned attributes; + PropertyOffset offset = structure->getConcurrently(uid, attributes); + if (isValidOffset(offset)) { + if (attributes & CustomAccessor) + return PutByIdStatus(MakesCalls); - if (attributes & (Accessor | ReadOnly)) - return PutByIdStatus(TakesSlowPath); - if (specificValue) { - // We need the PutById slow path to verify that we're storing the right value into - // the specialized slot. - return PutByIdStatus(TakesSlowPath); + if (attributes & (Accessor | ReadOnly)) + return PutByIdStatus(TakesSlowPath); + + WatchpointSet* replaceSet = structure->propertyReplacementWatchpointSet(offset); + if (!replaceSet || replaceSet->isStillValid()) { + // When this executes, it'll create, and fire, this replacement watchpoint set. + // That means that this has probably never executed or that something fishy is + // going on. Also, we cannot create or fire the watchpoint set from the concurrent + // JIT thread, so even if we wanted to do this, we'd need to have a lazy thingy. + // So, better leave this alone and take slow path. + return PutByIdStatus(TakesSlowPath); + } + + if (!result.appendVariant(PutByIdVariant::replace(structure, offset))) + return PutByIdStatus(TakesSlowPath); + continue; } - return PutByIdVariant::replace(structure, offset); - } - // Our hypothesis is that we're doing a transition. Before we prove that this is really - // true, we want to do some sanity checks. + // Our hypothesis is that we're doing a transition. Before we prove that this is really + // true, we want to do some sanity checks. - // Don't cache put transitions on dictionaries. - if (structure->isDictionary()) - return PutByIdStatus(TakesSlowPath); + // Don't cache put transitions on dictionaries. + if (structure->isDictionary()) + return PutByIdStatus(TakesSlowPath); - // If the structure corresponds to something that isn't an object, then give up, since - // we don't want to be adding properties to strings. - if (structure->typeInfo().type() == StringType) - return PutByIdStatus(TakesSlowPath); + // If the structure corresponds to something that isn't an object, then give up, since + // we don't want to be adding properties to strings. + if (!structure->typeInfo().isObject()) + return PutByIdStatus(TakesSlowPath); - RefPtr chain; - if (!isDirect) { - chain = adoptRef(new IntendedStructureChain(globalObject, structure)); + RefPtr chain; + if (!isDirect) { + chain = adoptRef(new IntendedStructureChain(globalObject, structure)); - // If the prototype chain has setters or read-only properties, then give up. - if (chain->mayInterceptStoreTo(vm, uid)) - return PutByIdStatus(TakesSlowPath); + // If the prototype chain has setters or read-only properties, then give up. + if (chain->mayInterceptStoreTo(uid)) + return PutByIdStatus(TakesSlowPath); - // If the prototype chain hasn't been normalized (i.e. there are proxies or dictionaries) - // then give up. The dictionary case would only happen if this structure has not been - // used in an optimized put_by_id transition. And really the only reason why we would - // bail here is that I don't really feel like having the optimizing JIT go and flatten - // dictionaries if we have evidence to suggest that those objects were never used as - // prototypes in a cacheable prototype access - i.e. there's a good chance that some of - // the other checks below will fail. - if (!chain->isNormalized()) + // If the prototype chain hasn't been normalized (i.e. there are proxies or dictionaries) + // then give up. The dictionary case would only happen if this structure has not been + // used in an optimized put_by_id transition. And really the only reason why we would + // bail here is that I don't really feel like having the optimizing JIT go and flatten + // dictionaries if we have evidence to suggest that those objects were never used as + // prototypes in a cacheable prototype access - i.e. there's a good chance that some of + // the other checks below will fail. + if (structure->isProxy() || !chain->isNormalized()) + return PutByIdStatus(TakesSlowPath); + } + + // We only optimize if there is already a structure that the transition is cached to. + Structure* transition = Structure::addPropertyTransitionToExistingStructureConcurrently(structure, uid, 0, offset); + if (!transition) + return PutByIdStatus(TakesSlowPath); + ASSERT(isValidOffset(offset)); + + bool didAppend = result.appendVariant( + PutByIdVariant::transition(structure, transition, chain.get(), offset)); + if (!didAppend) return PutByIdStatus(TakesSlowPath); } - // We only optimize if there is already a structure that the transition is cached to. - // Among other things, this allows us to guard against a transition with a specific - // value. - // - // - If we're storing a value that could be specific: this would only be a problem if - // the existing transition did have a specific value already, since if it didn't, - // then we would behave "as if" we were not storing a specific value. If it did - // have a specific value, then we'll know - the fact that we pass 0 for - // specificValue will tell us. - // - // - If we're not storing a value that could be specific: again, this would only be a - // problem if the existing transition did have a specific value, which we check for - // by passing 0 for the specificValue. - Structure* transition = Structure::addPropertyTransitionToExistingStructureConcurrently(structure, uid, 0, 0, offset); - if (!transition) - return PutByIdStatus(TakesSlowPath); // This occurs in bizarre cases only. See above. - ASSERT(!transition->transitionDidInvolveSpecificValue()); - ASSERT(isValidOffset(offset)); + return result; +} + +bool PutByIdStatus::makesCalls() const +{ + if (m_state == MakesCalls) + return true; + + if (m_state != Simple) + return false; + + for (unsigned i = m_variants.size(); i--;) { + if (m_variants[i].makesCalls()) + return true; + } - return PutByIdVariant::transition(structure, transition, chain.release(), offset); + return false; } void PutByIdStatus::dump(PrintStream& out) const