PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, UniquedStringImpl* uid) { UNUSED_PARAM(profiledBlock); UNUSED_PARAM(bytecodeIndex); UNUSED_PARAM(uid); VM& vm = *profiledBlock->vm(); Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex; StructureID structureID = instruction[4].u.structureID; if (!structureID) return PutByIdStatus(NoInformation); Structure* structure = vm.heap.structureIDTable().get(structureID); StructureID newStructureID = instruction[6].u.structureID; if (!newStructureID) { PropertyOffset offset = structure->getConcurrently(uid); if (!isValidOffset(offset)) return PutByIdStatus(NoInformation); return PutByIdVariant::replace(structure, offset, structure->inferredTypeDescriptorFor(uid)); } Structure* newStructure = vm.heap.structureIDTable().get(newStructureID); ASSERT(structure->transitionWatchpointSetHasBeenInvalidated()); PropertyOffset offset = newStructure->getConcurrently(uid); if (!isValidOffset(offset)) return PutByIdStatus(NoInformation); ObjectPropertyConditionSet conditionSet; if (!(instruction[8].u.putByIdFlags & PutByIdIsDirect)) { conditionSet = generateConditionsForPropertySetterMissConcurrently( *profiledBlock->vm(), profiledBlock->globalObject(), structure, uid); if (!conditionSet.isValid()) return PutByIdStatus(NoInformation); } return PutByIdVariant::transition( structure, newStructure, conditionSet, offset, newStructure->inferredTypeDescriptorFor(uid)); }
PutByIdStatus PutByIdStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, UniquedStringImpl* uid, bool isDirect) { if (parseIndex(*uid)) return PutByIdStatus(TakesSlowPath); if (set.isEmpty()) return PutByIdStatus(); 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); unsigned attributes; PropertyOffset offset = structure->getConcurrently(uid, attributes); if (isValidOffset(offset)) { if (attributes & CustomAccessor) return PutByIdStatus(MakesCalls); 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); } PutByIdVariant variant = PutByIdVariant::replace(structure, offset, structure->inferredTypeDescriptorFor(uid)); if (!result.appendVariant(variant)) return PutByIdStatus(TakesSlowPath); continue; } // 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); // 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); ObjectPropertyConditionSet conditionSet; if (!isDirect) { conditionSet = generateConditionsForPropertySetterMissConcurrently( globalObject->vm(), globalObject, structure, uid); if (!conditionSet.isValid()) 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, conditionSet, offset, transition->inferredTypeDescriptorFor(uid))); if (!didAppend) return PutByIdStatus(TakesSlowPath); } return result; }
PutByIdStatus PutByIdStatus::computeForStubInfo( const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, UniquedStringImpl* uid, CallLinkStatus::ExitSiteData callExitSiteData) { if (!stubInfo || !stubInfo->everConsidered) return PutByIdStatus(); if (stubInfo->tookSlowPath) return PutByIdStatus(TakesSlowPath); switch (stubInfo->cacheType) { case CacheType::Unset: // This means that we attempted to cache but failed for some reason. return PutByIdStatus(TakesSlowPath); case CacheType::PutByIdReplace: { PropertyOffset offset = stubInfo->u.byIdSelf.baseObjectStructure->getConcurrently(uid); if (isValidOffset(offset)) { return PutByIdVariant::replace( stubInfo->u.byIdSelf.baseObjectStructure.get(), offset, InferredType::Top); } return PutByIdStatus(TakesSlowPath); } case CacheType::Stub: { PolymorphicAccess* list = stubInfo->u.stub; PutByIdStatus result; result.m_state = Simple; State slowPathState = TakesSlowPath; for (unsigned i = 0; i < list->size(); ++i) { const AccessCase& access = list->at(i); if (access.doesCalls()) slowPathState = MakesCalls; } for (unsigned i = 0; i < list->size(); ++i) { const AccessCase& access = list->at(i); if (access.viaProxy()) return PutByIdStatus(slowPathState); PutByIdVariant variant; switch (access.type()) { case AccessCase::Replace: { Structure* structure = access.structure(); PropertyOffset offset = structure->getConcurrently(uid); if (!isValidOffset(offset)) return PutByIdStatus(slowPathState); variant = PutByIdVariant::replace( structure, offset, structure->inferredTypeDescriptorFor(uid)); break; } case AccessCase::Transition: { PropertyOffset offset = access.newStructure()->getConcurrently(uid); if (!isValidOffset(offset)) return PutByIdStatus(slowPathState); ObjectPropertyConditionSet conditionSet = access.conditionSet(); if (!conditionSet.structuresEnsureValidity()) return PutByIdStatus(slowPathState); variant = PutByIdVariant::transition( access.structure(), access.newStructure(), conditionSet, offset, access.newStructure()->inferredTypeDescriptorFor(uid)); break; } case AccessCase::Setter: { Structure* structure = access.structure(); ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor( structure, access.conditionSet(), uid); switch (complexGetStatus.kind()) { case ComplexGetStatus::ShouldSkip: continue; case ComplexGetStatus::TakesSlowPath: return PutByIdStatus(slowPathState); case ComplexGetStatus::Inlineable: { CallLinkInfo* callLinkInfo = access.callLinkInfo(); ASSERT(callLinkInfo); std::unique_ptr<CallLinkStatus> callLinkStatus = std::make_unique<CallLinkStatus>( CallLinkStatus::computeFor( locker, profiledBlock, *callLinkInfo, callExitSiteData)); variant = PutByIdVariant::setter( structure, complexGetStatus.offset(), complexGetStatus.conditionSet(), WTFMove(callLinkStatus)); } } break; } case AccessCase::CustomSetter: return PutByIdStatus(MakesCalls); default: return PutByIdStatus(slowPathState); } if (!result.appendVariant(variant)) return PutByIdStatus(slowPathState); } return result; } default: return PutByIdStatus(TakesSlowPath); } }