/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "vm/InvalidatingFuse.h" #include "gc/PublicIterators.h" #include "jit/Invalidation.h" #include "jit/JitSpewer.h" #include "vm/JSContext.h" #include "vm/JSScript.h" #include "vm/Logging.h" #include "gc/StableCellHasher-inl.h" #include "vm/JSScript-inl.h" js::FuseDependentIonScriptSet::FuseDependentIonScriptSet(JSContext* cx, InvalidatingFuse* fuse) : associatedFuse(fuse), ionScripts(cx->runtime()) {} bool js::InvalidatingRuntimeFuse::addFuseDependency( JSContext* cx, const jit::IonScriptKey& ionScript) { MOZ_ASSERT(ionScript.script()->zone() == cx->zone()); auto* scriptSet = cx->zone()->fuseDependencies.getOrCreateDependentScriptSet(cx, this); if (!scriptSet) { return false; } return scriptSet->addScriptForFuse(this, ionScript); } // This method will pop the fuse and iterates over its dependencies // iff this fuse is still intact. // This avoids a costly iteration over dependencies in subsequent calls. void js::InvalidatingRuntimeFuse::popFuse(JSContext* cx) { if (!intact()) { return; } // Pop the fuse in the base class GuardFuse::popFuse(cx); JS_LOG(fuseInvalidation, Verbose, "Invalidating fuse popping: %s", name()); // do invalidation. for (AllZonesIter z(cx->runtime()); !z.done(); z.next()) { // There's one dependent script set per fuse; just iterate over them all to // find the one we need (see comment on JS::Zone::fuseDependencies for // reasoning). for (auto& fd : z.get()->fuseDependencies) { fd.invalidateForFuse(cx, this); } } } void js::FuseDependentIonScriptSet::invalidateForFuse(JSContext* cx, InvalidatingFuse* fuse) { if (associatedFuse != fuse) { return; } ionScripts.get().invalidateAndClear(cx, "fuse"); } void js::jit::DependentIonScriptSet::invalidateAndClear(JSContext* cx, const char* reason) { for (const auto& ionScriptKey : ionScripts_) { IonScript* ionScript = ionScriptKey.maybeIonScriptToInvalidate(); if (ionScript) { JSScript* script = ionScriptKey.script(); JitSpew(jit::JitSpew_IonInvalidate, "Invalidating ion script %p for %s", ionScript, reason); JS_LOG(fuseInvalidation, Debug, "Invalidating ion script %s:%d for reason %s", script->filename(), script->lineno(), reason); } } js::jit::Invalidate(cx, ionScripts_); ionScripts_.clearAndFree(); } bool js::FuseDependentIonScriptSet::addScriptForFuse( InvalidatingFuse* fuse, const jit::IonScriptKey& ionScript) { MOZ_ASSERT(fuse == associatedFuse); return ionScripts.get().addToSet(ionScript); } js::FuseDependentIonScriptSet* js::DependentIonScriptGroup::getOrCreateDependentScriptSet( JSContext* cx, js::InvalidatingFuse* fuse) { for (auto& dss : dependencies) { if (dss.associatedFuse == fuse) { return &dss; } } if (!dependencies.emplaceBack(cx, fuse)) { return nullptr; } auto& dss = dependencies.back(); MOZ_ASSERT(dss.associatedFuse == fuse); return &dss; } bool js::jit::DependentIonScriptSet::addToSet(const IonScriptKey& ionScript) { MOZ_ASSERT(lengthAfterLastCompaction_ <= ionScripts_.length()); // If `ionScript` is already in the vector, it must be the last entry. if (!ionScripts_.empty() && ionScripts_.back() == ionScript) { return true; } // Assert `ionScript` is not in the vector. Limit this to the last 8 entries // to not slow down debug builds too much. #ifdef DEBUG size_t numToCheck = std::min(ionScripts_.length(), 8); for (size_t i = 0; i < numToCheck; i++) { MOZ_ASSERT(ionScripts_[ionScripts_.length() - 1 - i] != ionScript); } #endif // Compact the vector if its size doubled since the last compaction. This // isn't required for correctness but it avoids keeping a lot of stale // entries around between GCs. if (ionScripts_.length() / 2 > lengthAfterLastCompaction_) { ionScripts_.eraseIf([](const IonScriptKey& ionScript) { return ionScript.maybeIonScriptToInvalidate() == nullptr; }); lengthAfterLastCompaction_ = ionScripts_.length(); } return ionScripts_.append(ionScript); }