tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Pretenuring.cpp (21128B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sw=2 et tw=80:
      3 *
      4 * This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      6 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #include "gc/Pretenuring.h"
      9 
     10 #include "mozilla/Sprintf.h"
     11 
     12 #include "gc/GCInternals.h"
     13 #include "gc/PublicIterators.h"
     14 #include "jit/BaselineJIT.h"
     15 #include "jit/Invalidation.h"
     16 #include "js/Prefs.h"
     17 
     18 #include "gc/Marking-inl.h"
     19 #include "gc/PrivateIterators-inl.h"
     20 #include "vm/JSScript-inl.h"
     21 
     22 using namespace js;
     23 using namespace js::gc;
     24 
     25 // The maximum number of alloc sites to create between each minor
     26 // collection. Stop tracking allocation after this limit is reached. This
     27 // prevents unbounded time traversing the list during minor GC.
     28 static constexpr size_t MaxAllocSitesPerMinorGC = 600;
     29 
     30 // The maximum number of times to invalidate JIT code for a site. After this we
     31 // leave the site's state as Unknown and don't pretenure allocations.
     32 // Note we use 4 bits to store the invalidation count.
     33 static constexpr size_t MaxInvalidationCount = 5;
     34 
     35 // The minimum number of allocated cells needed to determine the survival rate
     36 // of cells in newly created arenas.
     37 static constexpr size_t MinCellsRequiredForSurvivalRate = 100;
     38 
     39 // The young survival rate below which a major collection is determined to have
     40 // a low young survival rate.
     41 static constexpr double LowYoungSurvivalThreshold = 0.05;
     42 
     43 // The number of consecutive major collections with a low young survival rate
     44 // that must occur before recovery is attempted.
     45 static constexpr size_t LowYoungSurvivalCountBeforeRecovery = 2;
     46 
     47 // The proportion of the nursery that must be promoted above which a minor
     48 // collection may be determined to have a high nursery survival rate.
     49 static constexpr double HighNurserySurvivalPromotionThreshold = 0.6;
     50 
     51 // The number of nursery allocations made by optimized JIT code that must be
     52 // promoted above which a minor collection may be determined to have a high
     53 // nursery survival rate.
     54 static constexpr size_t HighNurserySurvivalOptimizedAllocThreshold = 10000;
     55 
     56 // The number of consecutive minor collections with a high nursery survival rate
     57 // that must occur before recovery is attempted.
     58 static constexpr size_t HighNurserySurvivalCountBeforeRecovery = 2;
     59 
     60 AllocSite* const AllocSite::EndSentinel = reinterpret_cast<AllocSite*>(1);
     61 JSScript* const AllocSite::WasmScript =
     62    reinterpret_cast<JSScript*>(AllocSite::STATE_MASK + 1);
     63 
     64 /* static */
     65 void AllocSite::staticAsserts() {
     66  static_assert(jit::BaselineMaxScriptLength <= MaxValidPCOffset);
     67 }
     68 
     69 bool PretenuringNursery::canCreateAllocSite() {
     70  MOZ_ASSERT(allocSitesCreated <= MaxAllocSitesPerMinorGC);
     71  return JS::Prefs::site_based_pretenuring() &&
     72         allocSitesCreated < MaxAllocSitesPerMinorGC;
     73 }
     74 
     75 size_t PretenuringNursery::doPretenuring(GCRuntime* gc, JS::GCReason reason,
     76                                         bool validPromotionRate,
     77                                         double promotionRate,
     78                                         const AllocSiteFilter& reportFilter) {
     79  size_t sitesActive = 0;
     80  size_t sitesPretenured = 0;
     81  size_t sitesInvalidated = 0;
     82  size_t zonesWithHighNurserySurvival = 0;
     83 
     84  // Zero allocation counts.
     85  totalAllocCount_ = 0;
     86  for (ZonesIter zone(gc, SkipAtoms); !zone.done(); zone.next()) {
     87    for (auto& count : zone->pretenuring.nurseryPromotedCounts) {
     88      count = 0;
     89    }
     90  }
     91 
     92  // Check whether previously optimized code has changed its behaviour and
     93  // needs to be recompiled so that it can pretenure its allocations.
     94  if (validPromotionRate) {
     95    for (ZonesIter zone(gc, SkipAtoms); !zone.done(); zone.next()) {
     96      bool highNurserySurvivalRate =
     97          promotionRate > HighNurserySurvivalPromotionThreshold &&
     98          zone->optimizedAllocSite()->nurseryPromotedCount >=
     99              HighNurserySurvivalOptimizedAllocThreshold;
    100      zone->pretenuring.noteHighNurserySurvivalRate(highNurserySurvivalRate);
    101      if (highNurserySurvivalRate) {
    102        zonesWithHighNurserySurvival++;
    103      }
    104    }
    105  }
    106 
    107  if (reportFilter.enabled) {
    108    AllocSite::printInfoHeader(gc, reason, promotionRate);
    109  }
    110 
    111  AllocSite* site = allocatedSites;
    112  allocatedSites = AllocSite::EndSentinel;
    113  while (site != AllocSite::EndSentinel) {
    114    AllocSite* next = site->nextNurseryAllocated;
    115    site->nextNurseryAllocated = nullptr;
    116 
    117    if (site->isNormal()) {
    118      sitesActive++;
    119      updateTotalAllocCounts(site);
    120      auto result =
    121          site->processSite(gc, NormalSiteAttentionThreshold, reportFilter);
    122      if (result == AllocSite::WasPretenured ||
    123          result == AllocSite::WasPretenuredAndInvalidated) {
    124        sitesPretenured++;
    125        if (site->hasScript()) {
    126          site->script()->realm()->numAllocSitesPretenured++;
    127        }
    128      }
    129      if (result == AllocSite::WasPretenuredAndInvalidated) {
    130        sitesInvalidated++;
    131      }
    132    } else if (site->isMissing()) {
    133      sitesActive++;
    134      updateTotalAllocCounts(site);
    135      site->processMissingSite(reportFilter);
    136    }
    137 
    138    site = next;
    139  }
    140 
    141  // Catch-all sites don't end up on the list if they are only used from
    142  // optimized JIT code, so process them here.
    143 
    144  for (ZonesIter zone(gc, SkipAtoms); !zone.done(); zone.next()) {
    145    for (auto& site : zone->pretenuring.unknownAllocSites) {
    146      updateTotalAllocCounts(&site);
    147      if (site.traceKind() == JS::TraceKind::Object) {
    148        site.processCatchAllSite(reportFilter);
    149      } else {
    150        site.processSite(gc, UnknownSiteAttentionThreshold, reportFilter);
    151      }
    152      // Result checked in Nursery::doPretenuring.
    153    }
    154    updateTotalAllocCounts(zone->optimizedAllocSite());
    155    zone->optimizedAllocSite()->processCatchAllSite(reportFilter);
    156 
    157    // The data from the promoted alloc sites is never used so clear them here.
    158    for (AllocSite& site : zone->pretenuring.promotedAllocSites) {
    159      site.resetNurseryAllocations();
    160    }
    161  }
    162 
    163  if (reportFilter.enabled) {
    164    AllocSite::printInfoFooter(allocSitesCreated, sitesActive, sitesPretenured,
    165                               sitesInvalidated);
    166    if (zonesWithHighNurserySurvival) {
    167      fprintf(stderr, "  %zu zones with high nursery survival rate\n",
    168              zonesWithHighNurserySurvival);
    169    }
    170  }
    171 
    172  allocSitesCreated = 0;
    173 
    174  return sitesPretenured;
    175 }
    176 
    177 AllocSite::SiteResult AllocSite::processSite(
    178    GCRuntime* gc, size_t attentionThreshold,
    179    const AllocSiteFilter& reportFilter) {
    180  MOZ_ASSERT(isNormal() || isUnknown());
    181  MOZ_ASSERT(nurseryAllocCount >= nurseryPromotedCount);
    182 
    183  SiteResult result = NoChange;
    184 
    185  bool hasPromotionRate = false;
    186  double promotionRate = 0.0;
    187  bool wasInvalidated = false;
    188 
    189  if (nurseryAllocCount > attentionThreshold) {
    190    promotionRate = double(nurseryPromotedCount) / double(nurseryAllocCount);
    191    hasPromotionRate = true;
    192 
    193    AllocSite::State prevState = state();
    194    updateStateOnMinorGC(promotionRate);
    195    AllocSite::State newState = state();
    196 
    197    if (prevState == AllocSite::State::Unknown &&
    198        newState == AllocSite::State::LongLived) {
    199      result = WasPretenured;
    200 
    201      // We can optimize JIT code before we realise that a site should be
    202      // pretenured. Make sure we invalidate any existing optimized code.
    203      if (isNormal() && hasScript()) {
    204        wasInvalidated = invalidateScript(gc);
    205        if (wasInvalidated) {
    206          result = WasPretenuredAndInvalidated;
    207        }
    208      }
    209    }
    210  }
    211 
    212  if (reportFilter.matches(*this)) {
    213    printInfo(hasPromotionRate, promotionRate, wasInvalidated);
    214  }
    215 
    216  resetNurseryAllocations();
    217 
    218  return result;
    219 }
    220 
    221 void AllocSite::processMissingSite(const AllocSiteFilter& reportFilter) {
    222  MOZ_ASSERT(isMissing());
    223  MOZ_ASSERT(nurseryAllocCount >= nurseryPromotedCount);
    224 
    225  // Forward counts from missing sites to the relevant unknown site.
    226  AllocSite* unknownSite = zone()->unknownAllocSite(traceKind());
    227  unknownSite->nurseryAllocCount += nurseryAllocCount;
    228  unknownSite->nurseryPromotedCount += nurseryPromotedCount;
    229 
    230  // Update state but only so we can report it.
    231  bool hasPromotionRate = false;
    232  double promotionRate = 0.0;
    233  if (nurseryAllocCount > NormalSiteAttentionThreshold) {
    234    promotionRate = double(nurseryPromotedCount) / double(nurseryAllocCount);
    235    hasPromotionRate = true;
    236    updateStateOnMinorGC(promotionRate);
    237  }
    238 
    239  if (reportFilter.matches(*this)) {
    240    printInfo(hasPromotionRate, promotionRate, false);
    241  }
    242 
    243  resetNurseryAllocations();
    244 }
    245 
    246 void AllocSite::processCatchAllSite(const AllocSiteFilter& reportFilter) {
    247  MOZ_ASSERT(isUnknown() || isOptimized());
    248 
    249  if (!hasNurseryAllocations()) {
    250    return;
    251  }
    252 
    253  if (reportFilter.matches(*this)) {
    254    printInfo(false, 0.0, false);
    255  }
    256 
    257  resetNurseryAllocations();
    258 }
    259 
    260 void PretenuringNursery::updateTotalAllocCounts(AllocSite* site) {
    261  JS::TraceKind kind = site->traceKind();
    262  totalAllocCount_ += site->nurseryAllocCount;
    263  PretenuringZone& zone = site->zone()->pretenuring;
    264  size_t i = size_t(kind);
    265  MOZ_ASSERT(i < std::size(zone.nurseryPromotedCounts));
    266  zone.nurseryPromotedCounts[i] += site->nurseryPromotedCount;
    267 }
    268 
    269 bool AllocSite::invalidateScript(GCRuntime* gc) {
    270  CancelOffThreadIonCompile(script());
    271 
    272  if (!script()->hasIonScript()) {
    273    return false;
    274  }
    275 
    276  if (invalidationLimitReached()) {
    277    MOZ_ASSERT(state() == State::Unknown);
    278    return false;
    279  }
    280 
    281  invalidationCount++;
    282  if (invalidationLimitReached()) {
    283    setState(State::Unknown);
    284  }
    285 
    286  JSContext* cx = gc->rt->mainContextFromOwnThread();
    287  jit::Invalidate(cx, script(),
    288                  /* resetUses = */ false,
    289                  /* cancelOffThread = */ true);
    290  return true;
    291 }
    292 
    293 bool AllocSite::invalidationLimitReached() const {
    294  MOZ_ASSERT(invalidationCount <= MaxInvalidationCount);
    295  return invalidationCount == MaxInvalidationCount;
    296 }
    297 
    298 void PretenuringNursery::maybeStopPretenuring(GCRuntime* gc) {
    299  for (GCZonesIter zone(gc); !zone.done(); zone.next()) {
    300    double rate;
    301    if (zone->pretenuring.calculateYoungTenuredSurvivalRate(&rate)) {
    302      bool lowYoungSurvivalRate = rate < LowYoungSurvivalThreshold;
    303      zone->pretenuring.noteLowYoungTenuredSurvivalRate(lowYoungSurvivalRate);
    304    }
    305  }
    306 }
    307 
    308 void AllocSite::updateStateOnMinorGC(double promotionRate) {
    309  // The state changes based on whether the promotion rate is deemed high
    310  // (greater that 90%):
    311  //
    312  //                      high                          high
    313  //               ------------------>           ------------------>
    314  //   ShortLived                       Unknown                        LongLived
    315  //               <------------------           <------------------
    316  //                      !high                         !high
    317  //
    318  // The nursery is used to allocate if the site's state is Unknown or
    319  // ShortLived. There are no direct transition between ShortLived and LongLived
    320  // to avoid pretenuring sites that we've recently observed being short-lived.
    321 
    322  if (invalidationLimitReached()) {
    323    MOZ_ASSERT(state() == State::Unknown);
    324    return;
    325  }
    326 
    327  bool highPromotionRate = promotionRate >= 0.9;
    328 
    329  switch (state()) {
    330    case State::Unknown:
    331      if (highPromotionRate) {
    332        setState(State::LongLived);
    333      } else {
    334        setState(State::ShortLived);
    335      }
    336      break;
    337 
    338    case State::ShortLived: {
    339      if (highPromotionRate) {
    340        setState(State::Unknown);
    341      }
    342      break;
    343    }
    344 
    345    case State::LongLived: {
    346      if (!highPromotionRate) {
    347        setState(State::Unknown);
    348      }
    349      break;
    350    }
    351  }
    352 }
    353 
    354 bool AllocSite::maybeResetState() {
    355  if (invalidationLimitReached()) {
    356    MOZ_ASSERT(state() == State::Unknown);
    357    return false;
    358  }
    359 
    360  invalidationCount++;
    361  setState(State::Unknown);
    362  return true;
    363 }
    364 
    365 void AllocSite::trace(JSTracer* trc) {
    366  if (hasScript()) {
    367    JSScript* s = script();
    368    TraceManuallyBarrieredEdge(trc, &s, "AllocSite script");
    369    if (s != script()) {
    370      setScript(s);
    371    }
    372  }
    373 }
    374 
    375 bool AllocSite::traceWeak(JSTracer* trc) {
    376  if (hasScript()) {
    377    JSScript* s = script();
    378    if (!TraceManuallyBarrieredWeakEdge(trc, &s, "AllocSite script")) {
    379      return false;
    380    }
    381    if (s != script()) {
    382      setScript(s);
    383    }
    384  }
    385 
    386  return true;
    387 }
    388 
    389 bool AllocSite::needsSweep(JSTracer* trc) const {
    390  if (hasScript()) {
    391    JSScript* s = script();
    392    return IsAboutToBeFinalizedUnbarriered(s);
    393  }
    394 
    395  return false;
    396 }
    397 
    398 bool PretenuringZone::calculateYoungTenuredSurvivalRate(double* rateOut) {
    399  MOZ_ASSERT(allocCountInNewlyCreatedArenas >=
    400             survivorCountInNewlyCreatedArenas);
    401  if (allocCountInNewlyCreatedArenas < MinCellsRequiredForSurvivalRate) {
    402    return false;
    403  }
    404 
    405  *rateOut = double(survivorCountInNewlyCreatedArenas) /
    406             double(allocCountInNewlyCreatedArenas);
    407  return true;
    408 }
    409 
    410 void PretenuringZone::noteLowYoungTenuredSurvivalRate(
    411    bool lowYoungSurvivalRate) {
    412  if (lowYoungSurvivalRate) {
    413    lowYoungTenuredSurvivalCount++;
    414  } else {
    415    lowYoungTenuredSurvivalCount = 0;
    416  }
    417 }
    418 
    419 void PretenuringZone::noteHighNurserySurvivalRate(
    420    bool highNurserySurvivalRate) {
    421  if (highNurserySurvivalRate) {
    422    highNurserySurvivalCount++;
    423  } else {
    424    highNurserySurvivalCount = 0;
    425  }
    426 }
    427 
    428 bool PretenuringZone::shouldResetNurseryAllocSites() {
    429  bool shouldReset =
    430      highNurserySurvivalCount >= HighNurserySurvivalCountBeforeRecovery;
    431  if (shouldReset) {
    432    highNurserySurvivalCount = 0;
    433  }
    434  return shouldReset;
    435 }
    436 
    437 bool PretenuringZone::shouldResetPretenuredAllocSites() {
    438  bool shouldReset =
    439      lowYoungTenuredSurvivalCount >= LowYoungSurvivalCountBeforeRecovery;
    440  if (shouldReset) {
    441    lowYoungTenuredSurvivalCount = 0;
    442  }
    443  return shouldReset;
    444 }
    445 
    446 static const char* AllocSiteKindName(AllocSite::Kind kind) {
    447  switch (kind) {
    448    case AllocSite::Kind::Normal:
    449      return "normal";
    450    case AllocSite::Kind::Unknown:
    451      return "unknown";
    452    case AllocSite::Kind::Optimized:
    453      return "optimized";
    454    case AllocSite::Kind::Missing:
    455      return "missing";
    456    default:
    457      MOZ_CRASH("Bad AllocSite kind");
    458  }
    459 }
    460 
    461 /* static */
    462 void AllocSite::printInfoHeader(GCRuntime* gc, JS::GCReason reason,
    463                                double promotionRate) {
    464  fprintf(stderr,
    465          "Pretenuring info after minor GC %zu for %s reason with promotion "
    466          "rate %4.1f%%:\n",
    467          size_t(gc->minorGCCount()), ExplainGCReason(reason),
    468          promotionRate * 100.0);
    469  fprintf(stderr, "  %-16s %-16s %-20s %-12s %-9s %-9s %-8s %-8s %-6s %-10s\n",
    470          "Site", "Zone", "Location", "BytecodeOp", "SiteKind", "TraceKind",
    471          "NAllocs", "Promotes", "PRate", "State");
    472 }
    473 
    474 static const char* FindBaseName(const char* filename) {
    475 #ifdef XP_WIN
    476  constexpr char PathSeparator = '\\';
    477 #else
    478  constexpr char PathSeparator = '/';
    479 #endif
    480 
    481  const char* lastSep = strrchr(filename, PathSeparator);
    482  if (!lastSep) {
    483    return filename;
    484  }
    485 
    486  return lastSep + 1;
    487 }
    488 
    489 void AllocSite::printInfo(bool hasPromotionRate, double promotionRate,
    490                          bool wasInvalidated) const {
    491  // Zone.
    492  fprintf(stderr, "  %16p %16p", this, zone());
    493 
    494  // Location and bytecode op (not present for catch-all sites).
    495  char location[21] = {'\0'};
    496  char opName[13] = {'\0'};
    497  if (hasScript()) {
    498    uint32_t line = PCToLineNumber(script(), script()->offsetToPC(pcOffset()));
    499    const char* scriptName = FindBaseName(script()->filename());
    500    SprintfLiteral(location, "%s:%u", scriptName, line);
    501    BytecodeLocation location = script()->offsetToLocation(pcOffset());
    502    SprintfLiteral(opName, "%s", CodeName(location.getOp()));
    503  }
    504  fprintf(stderr, " %-20s %-12s", location, opName);
    505 
    506  // Which kind of site this is.
    507  fprintf(stderr, " %-9s", AllocSiteKindName(kind()));
    508 
    509  // Trace kind, except for optimized sites.
    510  const char* traceKindName = "";
    511  if (!isOptimized()) {
    512    traceKindName = JS::GCTraceKindToAscii(traceKind());
    513  }
    514  fprintf(stderr, " %-9s", traceKindName);
    515 
    516  // Nursery allocation count, missing for optimized sites.
    517  char buffer[16] = {'\0'};
    518  if (!isOptimized()) {
    519    SprintfLiteral(buffer, "%8" PRIu32, nurseryAllocCount);
    520  }
    521  fprintf(stderr, " %8s", buffer);
    522 
    523  // Nursery promotion count.
    524  fprintf(stderr, " %8" PRIu32, nurseryPromotedCount);
    525 
    526  // Promotion rate, if there were enough allocations.
    527  buffer[0] = '\0';
    528  if (hasPromotionRate) {
    529    SprintfLiteral(buffer, "%5.1f%%", std::min(1.0, promotionRate) * 100);
    530  }
    531  fprintf(stderr, " %6s", buffer);
    532 
    533  // Current state where applicable.
    534  const char* state = "";
    535  if (!isOptimized()) {
    536    state = stateName();
    537  }
    538  fprintf(stderr, " %-10s", state);
    539 
    540  // Whether the associated script was invalidated.
    541  if (wasInvalidated) {
    542    fprintf(stderr, " invalidated");
    543  }
    544 
    545  fprintf(stderr, "\n");
    546 }
    547 
    548 /* static */
    549 void AllocSite::printInfoFooter(size_t sitesCreated, size_t sitesActive,
    550                                size_t sitesPretenured,
    551                                size_t sitesInvalidated) {
    552  fprintf(stderr,
    553          "  %zu alloc sites created, %zu active, %zu pretenured, %zu "
    554          "invalidated\n",
    555          sitesCreated, sitesActive, sitesPretenured, sitesInvalidated);
    556 }
    557 
    558 const char* AllocSite::stateName() const {
    559  switch (state()) {
    560    case State::ShortLived:
    561      return "ShortLived";
    562    case State::Unknown:
    563      return "Unknown";
    564    case State::LongLived:
    565      return "LongLived";
    566  }
    567 
    568  MOZ_CRASH("Unknown state");
    569 }
    570 
    571 static bool StringIsPrefix(const CharRange& prefix, const char* whole) {
    572  MOZ_ASSERT(prefix.length() != 0);
    573  return strncmp(prefix.begin().get(), whole, prefix.length()) == 0;
    574 }
    575 
    576 /* static */
    577 bool AllocSiteFilter::readFromString(const char* string,
    578                                     AllocSiteFilter* filter) {
    579  *filter = AllocSiteFilter();
    580 
    581  CharRangeVector parts;
    582  if (!SplitStringBy(string, ',', &parts)) {
    583    MOZ_CRASH("OOM parsing AllocSiteFilter");
    584  }
    585 
    586  for (const auto& part : parts) {
    587    if (StringIsPrefix(part, "normal")) {
    588      filter->siteKindMask |= 1 << size_t(AllocSite::Kind::Normal);
    589    } else if (StringIsPrefix(part, "unknown")) {
    590      filter->siteKindMask |= 1 << size_t(AllocSite::Kind::Unknown);
    591    } else if (StringIsPrefix(part, "optimized")) {
    592      filter->siteKindMask |= 1 << size_t(AllocSite::Kind::Optimized);
    593    } else if (StringIsPrefix(part, "missing")) {
    594      filter->siteKindMask |= 1 << size_t(AllocSite::Kind::Missing);
    595    } else if (StringIsPrefix(part, "object")) {
    596      filter->traceKindMask |= 1 << size_t(JS::TraceKind::Object);
    597    } else if (StringIsPrefix(part, "string")) {
    598      filter->traceKindMask |= 1 << size_t(JS::TraceKind::String);
    599    } else if (StringIsPrefix(part, "bigint")) {
    600      filter->traceKindMask |= 1 << size_t(JS::TraceKind::BigInt);
    601    } else if (StringIsPrefix(part, "longlived")) {
    602      filter->stateMask |= 1 << size_t(AllocSite::State::LongLived);
    603    } else if (StringIsPrefix(part, "shortlived")) {
    604      filter->stateMask |= 1 << size_t(AllocSite::State::ShortLived);
    605    } else {
    606      char* end;
    607      filter->allocThreshold = strtol(part.begin().get(), &end, 10);
    608      if (end < part.end().get()) {
    609        return false;
    610      }
    611    }
    612  }
    613 
    614  filter->enabled = true;
    615 
    616  return true;
    617 }
    618 
    619 template <typename Enum>
    620 static bool MaskFilterMatches(uint8_t mask, Enum value) {
    621  static_assert(std::is_enum_v<Enum>);
    622 
    623  if (mask == 0) {
    624    return true;  // Match if filter not specified.
    625  }
    626 
    627  MOZ_ASSERT(size_t(value) < 8);
    628  uint8_t bit = 1 << size_t(value);
    629  return (mask & bit) != 0;
    630 }
    631 
    632 bool AllocSiteFilter::matches(const AllocSite& site) const {
    633  // The state is not relevant for other kinds so skip filter.
    634  bool matchState = site.isNormal() || site.isMissing();
    635 
    636  return enabled &&
    637         (allocThreshold == 0 || site.allocCount() >= allocThreshold) &&
    638         MaskFilterMatches(siteKindMask, site.kind()) &&
    639         MaskFilterMatches(traceKindMask, site.traceKind()) &&
    640         (!matchState || MaskFilterMatches(stateMask, site.state()));
    641 }
    642 
    643 #ifdef JS_GC_ZEAL
    644 
    645 AllocSite* js::gc::GetOrCreateMissingAllocSite(JSContext* cx, JSScript* script,
    646                                               uint32_t pcOffset,
    647                                               JS::TraceKind traceKind) {
    648  // Doesn't increment allocSitesCreated so as not to disturb pretenuring.
    649 
    650  Zone* zone = cx->zone();
    651  auto& missingSites = zone->missingSites;
    652  if (!missingSites) {
    653    missingSites = MakeUnique<MissingAllocSites>(zone);
    654    if (!missingSites) {
    655      return nullptr;
    656    }
    657  }
    658 
    659  auto scriptPtr = missingSites->scriptMap.lookupForAdd(script);
    660  if (!scriptPtr && !missingSites->scriptMap.add(
    661                        scriptPtr, script, MissingAllocSites::SiteMap())) {
    662    return nullptr;
    663  }
    664  auto& siteMap = scriptPtr->value();
    665 
    666  auto sitePtr = siteMap.lookupForAdd(pcOffset);
    667  if (!sitePtr) {
    668    UniquePtr<AllocSite> site = MakeUnique<AllocSite>(
    669        zone, script, pcOffset, traceKind, AllocSite::Kind::Missing);
    670    if (!site || !siteMap.add(sitePtr, pcOffset, std::move(site))) {
    671      return nullptr;
    672    }
    673  }
    674 
    675  return sitePtr->value().get();
    676 }
    677 
    678 #endif  // JS_GC_ZEAL