InvalidatingFuse.cpp (4602B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "vm/InvalidatingFuse.h" 8 9 #include "gc/PublicIterators.h" 10 #include "jit/Invalidation.h" 11 #include "jit/JitSpewer.h" 12 #include "vm/JSContext.h" 13 #include "vm/JSScript.h" 14 #include "vm/Logging.h" 15 16 #include "gc/StableCellHasher-inl.h" 17 #include "vm/JSScript-inl.h" 18 19 js::FuseDependentIonScriptSet::FuseDependentIonScriptSet(JSContext* cx, 20 InvalidatingFuse* fuse) 21 : associatedFuse(fuse), ionScripts(cx->runtime()) {} 22 23 bool js::InvalidatingRuntimeFuse::addFuseDependency( 24 JSContext* cx, const jit::IonScriptKey& ionScript) { 25 MOZ_ASSERT(ionScript.script()->zone() == cx->zone()); 26 27 auto* scriptSet = 28 cx->zone()->fuseDependencies.getOrCreateDependentScriptSet(cx, this); 29 if (!scriptSet) { 30 return false; 31 } 32 33 return scriptSet->addScriptForFuse(this, ionScript); 34 } 35 36 // This method will pop the fuse and iterates over its dependencies 37 // iff this fuse is still intact. 38 // This avoids a costly iteration over dependencies in subsequent calls. 39 void js::InvalidatingRuntimeFuse::popFuse(JSContext* cx) { 40 if (!intact()) { 41 return; 42 } 43 44 // Pop the fuse in the base class 45 GuardFuse::popFuse(cx); 46 JS_LOG(fuseInvalidation, Verbose, "Invalidating fuse popping: %s", name()); 47 // do invalidation. 48 for (AllZonesIter z(cx->runtime()); !z.done(); z.next()) { 49 // There's one dependent script set per fuse; just iterate over them all to 50 // find the one we need (see comment on JS::Zone::fuseDependencies for 51 // reasoning). 52 for (auto& fd : z.get()->fuseDependencies) { 53 fd.invalidateForFuse(cx, this); 54 } 55 } 56 } 57 58 void js::FuseDependentIonScriptSet::invalidateForFuse(JSContext* cx, 59 InvalidatingFuse* fuse) { 60 if (associatedFuse != fuse) { 61 return; 62 } 63 ionScripts.get().invalidateAndClear(cx, "fuse"); 64 } 65 66 void js::jit::DependentIonScriptSet::invalidateAndClear(JSContext* cx, 67 const char* reason) { 68 for (const auto& ionScriptKey : ionScripts_) { 69 IonScript* ionScript = ionScriptKey.maybeIonScriptToInvalidate(); 70 if (ionScript) { 71 JSScript* script = ionScriptKey.script(); 72 JitSpew(jit::JitSpew_IonInvalidate, "Invalidating ion script %p for %s", 73 ionScript, reason); 74 JS_LOG(fuseInvalidation, Debug, 75 "Invalidating ion script %s:%d for reason %s", script->filename(), 76 script->lineno(), reason); 77 } 78 } 79 js::jit::Invalidate(cx, ionScripts_); 80 ionScripts_.clearAndFree(); 81 } 82 83 bool js::FuseDependentIonScriptSet::addScriptForFuse( 84 InvalidatingFuse* fuse, const jit::IonScriptKey& ionScript) { 85 MOZ_ASSERT(fuse == associatedFuse); 86 return ionScripts.get().addToSet(ionScript); 87 } 88 89 js::FuseDependentIonScriptSet* 90 js::DependentIonScriptGroup::getOrCreateDependentScriptSet( 91 JSContext* cx, js::InvalidatingFuse* fuse) { 92 for (auto& dss : dependencies) { 93 if (dss.associatedFuse == fuse) { 94 return &dss; 95 } 96 } 97 98 if (!dependencies.emplaceBack(cx, fuse)) { 99 return nullptr; 100 } 101 102 auto& dss = dependencies.back(); 103 MOZ_ASSERT(dss.associatedFuse == fuse); 104 return &dss; 105 } 106 107 bool js::jit::DependentIonScriptSet::addToSet(const IonScriptKey& ionScript) { 108 MOZ_ASSERT(lengthAfterLastCompaction_ <= ionScripts_.length()); 109 110 // If `ionScript` is already in the vector, it must be the last entry. 111 if (!ionScripts_.empty() && ionScripts_.back() == ionScript) { 112 return true; 113 } 114 115 // Assert `ionScript` is not in the vector. Limit this to the last 8 entries 116 // to not slow down debug builds too much. 117 #ifdef DEBUG 118 size_t numToCheck = std::min<size_t>(ionScripts_.length(), 8); 119 for (size_t i = 0; i < numToCheck; i++) { 120 MOZ_ASSERT(ionScripts_[ionScripts_.length() - 1 - i] != ionScript); 121 } 122 #endif 123 124 // Compact the vector if its size doubled since the last compaction. This 125 // isn't required for correctness but it avoids keeping a lot of stale 126 // entries around between GCs. 127 if (ionScripts_.length() / 2 > lengthAfterLastCompaction_) { 128 ionScripts_.eraseIf([](const IonScriptKey& ionScript) { 129 return ionScript.maybeIonScriptToInvalidate() == nullptr; 130 }); 131 lengthAfterLastCompaction_ = ionScripts_.length(); 132 } 133 134 return ionScripts_.append(ionScript); 135 }