tor-browser

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

JitScript.cpp (33620B)


      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 "jit/JitScript-inl.h"
      8 
      9 #include "mozilla/BinarySearch.h"
     10 #include "mozilla/CheckedInt.h"
     11 
     12 #include <utility>
     13 
     14 #include "jit/BaselineIC.h"
     15 #include "jit/BaselineJIT.h"
     16 #include "jit/BytecodeAnalysis.h"
     17 #include "jit/CacheIRCompiler.h"
     18 #include "jit/IonScript.h"
     19 #include "jit/JitFrames.h"
     20 #include "jit/JitSpewer.h"
     21 #include "jit/ScriptFromCalleeToken.h"
     22 #include "jit/ShapeList.h"
     23 #include "jit/TrialInlining.h"
     24 #include "jit/WarpSnapshot.h"
     25 #include "js/ColumnNumber.h"  // JS::LimitedColumnNumberOneOrigin
     26 #include "vm/BytecodeUtil.h"
     27 #include "vm/Compartment.h"
     28 #include "vm/FrameIter.h"  // js::OnlyJSJitFrameIter
     29 #include "vm/JitActivation.h"
     30 #include "vm/JSScript.h"
     31 
     32 #include "gc/GCContext-inl.h"
     33 #include "jit/JSJitFrameIter-inl.h"
     34 #include "vm/JSContext-inl.h"
     35 #include "vm/JSScript-inl.h"
     36 
     37 using namespace js;
     38 using namespace js::jit;
     39 
     40 using mozilla::CheckedInt;
     41 
     42 JitScript::JitScript(JSScript* script, Offset fallbackStubsOffset,
     43                     Offset endOffset, const char* profileString)
     44    : profileString_(profileString),
     45      owningScript_(script),
     46      endOffset_(endOffset),
     47      icScript_(script->getWarmUpCount(),
     48                fallbackStubsOffset - offsetOfICScript(),
     49                endOffset - offsetOfICScript(),
     50                /*depth=*/0, script->length()) {
     51  // Ensure the baselineScript_ and ionScript_ fields match the BaselineDisabled
     52  // and IonDisabled script flags.
     53  if (!script->canBaselineCompile()) {
     54    setBaselineScriptImpl(script, BaselineDisabledScriptPtr);
     55  }
     56  if (!script->canIonCompile()) {
     57    setIonScriptImpl(script, IonDisabledScriptPtr);
     58  }
     59 }
     60 
     61 ICScript::~ICScript() {
     62  // The contents of the AllocSite LifoAlloc are removed and freed separately
     63  // after the next minor GC. See prepareForDestruction.
     64  MOZ_ASSERT(allocSitesSpace_.isEmpty());
     65  MOZ_ASSERT(!envAllocSite_);
     66 }
     67 
     68 #ifdef DEBUG
     69 JitScript::~JitScript() {
     70  // BaselineScript and IonScript must have been destroyed at this point.
     71  MOZ_ASSERT(!hasBaselineScript());
     72  MOZ_ASSERT(!hasIonScript());
     73 
     74  MOZ_ASSERT(!isInList());
     75 }
     76 #else
     77 JitScript::~JitScript() = default;
     78 #endif
     79 
     80 bool JSScript::createJitScript(JSContext* cx) {
     81  MOZ_ASSERT(!hasJitScript());
     82  cx->check(this);
     83 
     84  // Scripts with a JitScript can run in the Baseline Interpreter. Make sure
     85  // we don't create a JitScript for scripts we shouldn't Baseline interpret.
     86  MOZ_ASSERT_IF(IsBaselineInterpreterEnabled(),
     87                CanBaselineInterpretScript(this));
     88 
     89  // Store the profile string in the JitScript if the profiler is enabled.
     90  const char* profileString = nullptr;
     91  if (cx->runtime()->geckoProfiler().enabled()) {
     92    profileString = cx->runtime()->geckoProfiler().profileString(cx, this);
     93    if (!profileString) {
     94      return false;
     95    }
     96 
     97    if (!cx->runtime()->geckoProfiler().insertScriptSource(scriptSource())) {
     98      ReportOutOfMemory(cx);
     99      return false;
    100    }
    101  }
    102 
    103  static_assert(sizeof(JitScript) % sizeof(uintptr_t) == 0,
    104                "Trailing arrays must be aligned properly");
    105  static_assert(sizeof(ICEntry) % sizeof(uintptr_t) == 0,
    106                "Trailing arrays must be aligned properly");
    107 
    108  static_assert(
    109      sizeof(JitScript) == offsetof(JitScript, icScript_) + sizeof(ICScript),
    110      "icScript_ must be the last field");
    111 
    112  // Calculate allocation size.
    113  CheckedInt<uint32_t> allocSize = sizeof(JitScript);
    114  allocSize += CheckedInt<uint32_t>(numICEntries()) * sizeof(ICEntry);
    115  allocSize += CheckedInt<uint32_t>(numICEntries()) * sizeof(ICFallbackStub);
    116  if (!allocSize.isValid()) {
    117    ReportAllocationOverflow(cx);
    118    return false;
    119  }
    120 
    121  void* raw = cx->pod_malloc<uint8_t>(allocSize.value());
    122  MOZ_ASSERT(uintptr_t(raw) % alignof(JitScript) == 0);
    123  if (!raw) {
    124    return false;
    125  }
    126 
    127  size_t fallbackStubsOffset =
    128      sizeof(JitScript) + numICEntries() * sizeof(ICEntry);
    129 
    130  UniquePtr<JitScript> jitScript(new (raw) JitScript(
    131      this, fallbackStubsOffset, allocSize.value(), profileString));
    132 
    133  // Sanity check the length computation.
    134  MOZ_ASSERT(jitScript->numICEntries() == numICEntries());
    135 
    136  jitScript->icScript()->initICEntries(cx, this);
    137 
    138  cx->zone()->jitZone()->registerJitScript(jitScript.get());
    139 
    140  warmUpData_.initJitScript(jitScript.release());
    141  AddCellMemory(this, allocSize.value(), MemoryUse::JitScript);
    142 
    143  // We have a JitScript so we can set the script's jitCodeRaw pointer to the
    144  // Baseline Interpreter code.
    145  updateJitCodeRaw(cx->runtime());
    146 
    147  return true;
    148 }
    149 
    150 void JSScript::maybeReleaseJitScript(JS::GCContext* gcx) {
    151  MOZ_ASSERT(hasJitScript());
    152 
    153  if (zone()->jitZone()->keepJitScripts() || jitScript()->hasBaselineScript() ||
    154      jitScript()->icScript()->active()) {
    155    return;
    156  }
    157 
    158  releaseJitScript(gcx);
    159 }
    160 
    161 void JSScript::releaseJitScript(JS::GCContext* gcx) {
    162  MOZ_ASSERT(hasJitScript());
    163  MOZ_ASSERT(!hasBaselineScript());
    164  MOZ_ASSERT(!hasIonScript());
    165 
    166  gcx->removeCellMemory(this, jitScript()->allocBytes(), MemoryUse::JitScript);
    167 
    168  JitScript::Destroy(zone(), jitScript());
    169  warmUpData_.clearJitScript();
    170  updateJitCodeRaw(gcx->runtime());
    171 }
    172 
    173 void JSScript::releaseJitScriptOnFinalize(JS::GCContext* gcx) {
    174  MOZ_ASSERT(hasJitScript());
    175 
    176  if (hasIonScript()) {
    177    IonScript* ion = jitScript()->clearIonScript(gcx, this);
    178    jit::IonScript::Destroy(gcx, ion);
    179  }
    180 
    181  if (hasBaselineScript()) {
    182    BaselineScript* baseline = jitScript()->clearBaselineScript(gcx, this);
    183    jit::BaselineScript::Destroy(gcx, baseline);
    184  }
    185 
    186  releaseJitScript(gcx);
    187 }
    188 
    189 void JitScript::trace(JSTracer* trc) {
    190  TraceEdge(trc, &owningScript_, "JitScript::owningScript_");
    191 
    192  icScript_.trace(trc);
    193 
    194  if (hasBaselineScript()) {
    195    baselineScript()->trace(trc);
    196  }
    197 
    198  if (hasIonScript()) {
    199    ionScript()->trace(trc);
    200  }
    201 
    202  if (templateEnv_.isSome()) {
    203    TraceNullableEdge(trc, templateEnv_.ptr(), "jitscript-template-env");
    204  }
    205 
    206  if (hasInliningRoot()) {
    207    inliningRoot()->trace(trc);
    208  }
    209 }
    210 
    211 void JitScript::traceWeak(JSTracer* trc) {
    212  if (!icScript_.traceWeak(trc)) {
    213    notePurgedStubs();
    214  }
    215 
    216  if (hasInliningRoot()) {
    217    if (!inliningRoot()->traceWeak(trc)) {
    218      notePurgedStubs();
    219    }
    220  }
    221 
    222  if (hasIonScript()) {
    223    ionScript()->traceWeak(trc);
    224  }
    225 }
    226 
    227 void ICScript::trace(JSTracer* trc) {
    228  // Mark all IC stub codes hanging off the IC stub entries.
    229  for (size_t i = 0; i < numICEntries(); i++) {
    230    ICEntry& ent = icEntry(i);
    231    ICFallbackStub* fallback = fallbackStub(i);
    232    ent.trace(trc, fallback);
    233  }
    234 
    235  for (gc::AllocSite* site : allocSites_) {
    236    site->trace(trc);
    237  }
    238 }
    239 
    240 bool ICScript::traceWeak(JSTracer* trc) {
    241  // Mark all IC stub codes hanging off the IC stub entries.
    242  bool allSurvived = true;
    243  for (size_t i = 0; i < numICEntries(); i++) {
    244    ICEntry& ent = icEntry(i);
    245    ICFallbackStub* fallback = fallbackStub(i);
    246    if (!ent.traceWeak(trc, fallback)) {
    247      allSurvived = false;
    248    }
    249  }
    250 
    251  return allSurvived;
    252 }
    253 
    254 bool ICScript::addInlinedChild(JSContext* cx, UniquePtr<ICScript> child,
    255                               uint32_t pcOffset) {
    256  MOZ_ASSERT(!hasInlinedChild(pcOffset));
    257 
    258  if (!inlinedChildren_) {
    259    inlinedChildren_ = cx->make_unique<Vector<CallSite>>(cx);
    260    if (!inlinedChildren_) {
    261      return false;
    262    }
    263  }
    264 
    265  // First reserve space in inlinedChildren_ to ensure that if the ICScript is
    266  // added to the inlining root, it can also be added to inlinedChildren_.
    267  CallSite callsite(child.get(), pcOffset);
    268  if (!inlinedChildren_->reserve(inlinedChildren_->length() + 1)) {
    269    return false;
    270  }
    271  if (!inliningRoot()->addInlinedScript(std::move(child))) {
    272    return false;
    273  }
    274  inlinedChildren_->infallibleAppend(callsite);
    275  return true;
    276 }
    277 
    278 ICScript* ICScript::findInlinedChild(uint32_t pcOffset) {
    279  for (auto& callsite : *inlinedChildren_) {
    280    if (callsite.pcOffset_ == pcOffset) {
    281      return callsite.callee_;
    282    }
    283  }
    284  MOZ_CRASH("Inlined child expected at pcOffset");
    285 }
    286 
    287 void ICScript::removeInlinedChild(uint32_t pcOffset) {
    288  MOZ_ASSERT(inliningRoot());
    289  inlinedChildren_->eraseIf([pcOffset](const CallSite& callsite) -> bool {
    290    return callsite.pcOffset_ == pcOffset;
    291  });
    292 }
    293 
    294 bool ICScript::hasInlinedChild(uint32_t pcOffset) {
    295  if (!inlinedChildren_) {
    296    return false;
    297  }
    298  for (auto& callsite : *inlinedChildren_) {
    299    if (callsite.pcOffset_ == pcOffset) {
    300      return true;
    301    }
    302  }
    303  return false;
    304 }
    305 
    306 void ICScript::purgeInactiveICScripts() {
    307  MOZ_ASSERT(inliningRoot());
    308 
    309  if (!inlinedChildren_) {
    310    return;
    311  }
    312 
    313  inlinedChildren_->eraseIf(
    314      [](const CallSite& callsite) { return !callsite.callee_->active(); });
    315 
    316  if (inlinedChildren_->empty()) {
    317    inlinedChildren_.reset();
    318    return;
    319  }
    320 
    321  // We have an active callee ICScript. This means the current ICScript must be
    322  // active too.
    323  MOZ_ASSERT(active());
    324 }
    325 
    326 void JitScript::resetWarmUpCount(uint32_t count) {
    327  forEachICScript([&](ICScript* script) { script->resetWarmUpCount(count); });
    328 }
    329 
    330 #ifdef DEBUG
    331 bool JitScript::hasActiveICScript() const {
    332  bool hasActive = false;
    333  forEachICScript([&](const ICScript* script) {
    334    if (script->active()) {
    335      hasActive = true;
    336    }
    337  });
    338  return hasActive;
    339 }
    340 #endif
    341 
    342 void JitScript::resetAllActiveFlags() {
    343  forEachICScript([](ICScript* script) { script->resetActive(); });
    344 }
    345 
    346 void JitScript::ensureProfileString(JSContext* cx, JSScript* script) {
    347  MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled());
    348 
    349  if (profileString_) {
    350    return;
    351  }
    352 
    353  AutoEnterOOMUnsafeRegion oomUnsafe;
    354  profileString_ = cx->runtime()->geckoProfiler().profileString(cx, script);
    355  if (!profileString_) {
    356    oomUnsafe.crash("Failed to allocate profile string");
    357  }
    358 }
    359 
    360 void JitScript::ensureProfilerScriptSource(JSContext* cx, JSScript* script) {
    361  MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled());
    362 
    363  AutoEnterOOMUnsafeRegion oomUnsafe;
    364  if (!cx->runtime()->geckoProfiler().insertScriptSource(
    365          script->scriptSource())) {
    366    oomUnsafe.crash("Failed to insert profiled script source");
    367  }
    368 }
    369 
    370 /* static */
    371 void JitScript::Destroy(Zone* zone, JitScript* script) {
    372  script->prepareForDestruction(zone);
    373 
    374  // Remove from JitZone's linked list of JitScripts.
    375  script->remove();
    376 
    377  js_delete(script);
    378 }
    379 
    380 template <typename F>
    381 void JitScript::forEachICScript(const F& f) {
    382  f(&icScript_);
    383  if (hasInliningRoot()) {
    384    inliningRoot()->forEachInlinedScript(f);
    385  }
    386 }
    387 
    388 template <typename F>
    389 void JitScript::forEachICScript(const F& f) const {
    390  f(&icScript_);
    391  if (hasInliningRoot()) {
    392    inliningRoot()->forEachInlinedScript(f);
    393  }
    394 }
    395 
    396 void ICScript::prepareForDestruction(Zone* zone) {
    397  envAllocSite_ = nullptr;  // Points into allocSitesSpace_.
    398 
    399  // Defer freeing AllocSite memory until after the next minor GC, because the
    400  // nursery can point to these alloc sites.
    401  JSRuntime* rt = zone->runtimeFromMainThread();
    402  rt->gc.queueAllLifoBlocksForFreeAfterMinorGC(&allocSitesSpace_);
    403 
    404  // Trigger write barriers.
    405  PreWriteBarrier(zone, this);
    406 }
    407 
    408 void JitScript::prepareForDestruction(Zone* zone) {
    409  forEachICScript(
    410      [&](ICScript* script) { script->prepareForDestruction(zone); });
    411 
    412  // Trigger write barriers.
    413  owningScript_ = nullptr;
    414  baselineScript_.set(zone, nullptr);
    415  ionScript_.set(zone, nullptr);
    416 }
    417 
    418 struct FallbackStubs {
    419  ICScript* const icScript_;
    420 
    421  explicit FallbackStubs(ICScript* icScript) : icScript_(icScript) {}
    422 
    423  size_t numEntries() const { return icScript_->numICEntries(); }
    424  ICFallbackStub* operator[](size_t index) const {
    425    return icScript_->fallbackStub(index);
    426  }
    427 };
    428 
    429 static bool ComputeBinarySearchMid(FallbackStubs stubs, uint32_t pcOffset,
    430                                   size_t* loc) {
    431  return mozilla::BinarySearchIf(
    432      stubs, 0, stubs.numEntries(),
    433      [pcOffset](const ICFallbackStub* stub) {
    434        if (pcOffset < stub->pcOffset()) {
    435          return -1;
    436        }
    437        if (stub->pcOffset() < pcOffset) {
    438          return 1;
    439        }
    440        return 0;
    441      },
    442      loc);
    443 }
    444 
    445 ICEntry& ICScript::icEntryFromPCOffset(uint32_t pcOffset) {
    446  size_t mid;
    447  bool success = ComputeBinarySearchMid(FallbackStubs(this), pcOffset, &mid);
    448 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    449  if (!success) {
    450    MOZ_CRASH_UNSAFE_PRINTF("Missing icEntry for offset %d (max offset: %d)",
    451                            int(pcOffset),
    452                            int(fallbackStub(numICEntries() - 1)->pcOffset()));
    453  }
    454 #endif
    455  MOZ_ALWAYS_TRUE(success);
    456 
    457  MOZ_ASSERT(mid < numICEntries());
    458 
    459  ICEntry& entry = icEntry(mid);
    460  MOZ_ASSERT(fallbackStubForICEntry(&entry)->pcOffset() == pcOffset);
    461  return entry;
    462 }
    463 
    464 ICEntry* ICScript::interpreterICEntryFromPCOffset(uint32_t pcOffset) {
    465  // We have to return the entry to store in BaselineFrame::interpreterICEntry
    466  // when resuming in the Baseline Interpreter at pcOffset. The bytecode op at
    467  // pcOffset does not necessarily have an ICEntry, so we want to return the
    468  // first ICEntry for which the following is true:
    469  //
    470  //    entry.pcOffset() >= pcOffset
    471  //
    472  // Fortunately, ComputeBinarySearchMid returns exactly this entry.
    473 
    474  size_t mid;
    475  ComputeBinarySearchMid(FallbackStubs(this), pcOffset, &mid);
    476 
    477  if (mid < numICEntries()) {
    478    ICEntry& entry = icEntry(mid);
    479    MOZ_ASSERT(fallbackStubForICEntry(&entry)->pcOffset() >= pcOffset);
    480    return &entry;
    481  }
    482 
    483  // Resuming at a pc after the last ICEntry. Just return nullptr:
    484  // BaselineFrame::interpreterICEntry will never be used in this case.
    485  return nullptr;
    486 }
    487 
    488 void JitScript::purgeInactiveICScripts() {
    489  if (!hasInliningRoot()) {
    490    return;
    491  }
    492 
    493  forEachICScript([](ICScript* script) { script->purgeInactiveICScripts(); });
    494 
    495  inliningRoot()->purgeInactiveICScripts();
    496  if (inliningRoot()->numInlinedScripts() == 0) {
    497    inliningRoot_.reset();
    498    icScript()->inliningRoot_ = nullptr;
    499  } else {
    500    // If a callee script is active on the stack, the root script must be active
    501    // too.
    502    MOZ_ASSERT(icScript()->active());
    503  }
    504 }
    505 
    506 void JitScript::purgeStubs(JSScript* script, ICStubSpace& newStubSpace) {
    507  MOZ_ASSERT(script->jitScript() == this);
    508 
    509  Zone* zone = script->zone();
    510  if (IsAboutToBeFinalizedUnbarriered(script)) {
    511    // We're sweeping and the script is dead. Don't purge optimized stubs
    512    // because (1) accessing CacheIRStubInfo pointers in ICStubs is invalid
    513    // because we may have swept them already when we started (incremental)
    514    // sweeping and (2) it's unnecessary because this script will be finalized
    515    // soon anyway.
    516    return;
    517  }
    518 
    519  JitSpew(JitSpew_BaselineIC, "Purging optimized stubs");
    520 
    521  forEachICScript(
    522      [&](ICScript* script) { script->purgeStubs(zone, newStubSpace); });
    523 
    524  notePurgedStubs();
    525 }
    526 
    527 void ICScript::purgeStubs(Zone* zone, ICStubSpace& newStubSpace) {
    528  for (size_t i = 0; i < numICEntries(); i++) {
    529    ICEntry& entry = icEntry(i);
    530    ICFallbackStub* fallback = fallbackStub(i);
    531 
    532    // If this is a trial inlining call site and the callee's ICScript hasn't
    533    // been discarded, clone the IC chain instead of purging stubs. In this case
    534    // both the current ICScript and the callee's inlined ICScript must be
    535    // active on the stack.
    536    //
    537    // We can't purge the IC stubs in this case because it'd confuse trial
    538    // inlining if we try to inline again later and we already have an ICScript
    539    // for this call site.
    540    if (fallback->trialInliningState() == TrialInliningState::Inlined &&
    541        hasInlinedChild(fallback->pcOffset())) {
    542      MOZ_ASSERT(active());
    543 #ifdef DEBUG
    544      // The callee script must be active. Also assert its bytecode size field
    545      // is valid, because this helps catch memory safety issues (bug 1871947).
    546      ICScript* callee = findInlinedChild(fallback->pcOffset());
    547      MOZ_ASSERT(callee->active());
    548      MOZ_ASSERT(callee->bytecodeSize() < inliningRoot()->totalBytecodeSize());
    549 #endif
    550 
    551      JSRuntime* rt = zone->runtimeFromMainThread();
    552      ICCacheIRStub* prev = nullptr;
    553      ICStub* stub = entry.firstStub();
    554      while (stub != fallback) {
    555        ICCacheIRStub* clone = stub->toCacheIRStub()->clone(rt, newStubSpace);
    556        if (prev) {
    557          prev->setNext(clone);
    558        } else {
    559          entry.setFirstStub(clone);
    560        }
    561        MOZ_ASSERT(stub->toCacheIRStub()->next() == clone->next());
    562        prev = clone;
    563        stub = clone->next();
    564      }
    565      continue;
    566    }
    567 
    568    MOZ_ASSERT(!hasInlinedChild(fallback->pcOffset()));
    569 
    570    fallback->discardStubs(zone, &entry);
    571    fallback->state().reset();
    572  }
    573 }
    574 
    575 bool JitScript::ensureHasCachedBaselineJitData(JSContext* cx,
    576                                               HandleScript script) {
    577  if (templateEnv_.isSome()) {
    578    return true;
    579  }
    580 
    581  if (!script->function() ||
    582      !script->function()->needsFunctionEnvironmentObjects()) {
    583    templateEnv_.emplace();
    584    return true;
    585  }
    586 
    587  Rooted<EnvironmentObject*> templateEnv(cx);
    588  Rooted<JSFunction*> fun(cx, script->function());
    589 
    590  if (fun->needsNamedLambdaEnvironment()) {
    591    templateEnv = NamedLambdaObject::createTemplateObject(cx, fun);
    592    if (!templateEnv) {
    593      return false;
    594    }
    595  }
    596 
    597  if (fun->needsCallObject()) {
    598    templateEnv = CallObject::createTemplateObject(cx, script, templateEnv);
    599    if (!templateEnv) {
    600      return false;
    601    }
    602  }
    603 
    604  templateEnv_.emplace(templateEnv);
    605  return true;
    606 }
    607 
    608 bool JitScript::ensureHasCachedIonData(JSContext* cx, HandleScript script) {
    609  MOZ_ASSERT(script->jitScript() == this);
    610 
    611  if (usesEnvironmentChain_.isSome()) {
    612    return true;
    613  }
    614 
    615  if (!ensureHasCachedBaselineJitData(cx, script)) {
    616    return false;
    617  }
    618 
    619  usesEnvironmentChain_.emplace(ScriptUsesEnvironmentChain(script));
    620  return true;
    621 }
    622 
    623 std::pair<CallObject*, NamedLambdaObject*>
    624 JitScript::functionEnvironmentTemplates(JSFunction* fun) const {
    625  EnvironmentObject* templateEnv = templateEnvironment();
    626 
    627  CallObject* callObjectTemplate = nullptr;
    628  if (fun->needsCallObject()) {
    629    callObjectTemplate = &templateEnv->as<CallObject>();
    630  }
    631 
    632  NamedLambdaObject* namedLambdaTemplate = nullptr;
    633  if (fun->needsNamedLambdaEnvironment()) {
    634    if (callObjectTemplate) {
    635      namedLambdaTemplate =
    636          &callObjectTemplate->enclosingEnvironment().as<NamedLambdaObject>();
    637    } else {
    638      namedLambdaTemplate = &templateEnv->as<NamedLambdaObject>();
    639    }
    640  }
    641 
    642  return {callObjectTemplate, namedLambdaTemplate};
    643 }
    644 
    645 void JitScript::setBaselineScriptImpl(JSScript* script,
    646                                      BaselineScript* baselineScript) {
    647  JSRuntime* rt = script->runtimeFromMainThread();
    648  setBaselineScriptImpl(rt->gcContext(), script, baselineScript);
    649 }
    650 
    651 void JitScript::setBaselineScriptImpl(JS::GCContext* gcx, JSScript* script,
    652                                      BaselineScript* baselineScript) {
    653  if (hasBaselineScript()) {
    654    gcx->removeCellMemory(script, baselineScript_->allocBytes(),
    655                          MemoryUse::BaselineScript);
    656    baselineScript_.set(script->zone(), nullptr);
    657  }
    658 
    659  MOZ_ASSERT(ionScript_ == nullptr || ionScript_ == IonDisabledScriptPtr);
    660 
    661  baselineScript_.set(script->zone(), baselineScript);
    662  if (hasBaselineScript()) {
    663    AddCellMemory(script, baselineScript_->allocBytes(),
    664                  MemoryUse::BaselineScript);
    665  }
    666 
    667  script->resetWarmUpResetCounter();
    668  script->updateJitCodeRaw(gcx->runtime());
    669 }
    670 
    671 void JitScript::setIonScriptImpl(JSScript* script, IonScript* ionScript) {
    672  JSRuntime* rt = script->runtimeFromMainThread();
    673  setIonScriptImpl(rt->gcContext(), script, ionScript);
    674 }
    675 
    676 void JitScript::setIonScriptImpl(JS::GCContext* gcx, JSScript* script,
    677                                 IonScript* ionScript) {
    678  MOZ_ASSERT_IF(ionScript != IonDisabledScriptPtr,
    679                !baselineScript()->hasPendingIonCompileTask());
    680 
    681  JS::Zone* zone = script->zone();
    682  if (hasIonScript()) {
    683    gcx->removeCellMemory(script, ionScript_->allocBytes(),
    684                          MemoryUse::IonScript);
    685    ionScript_.set(zone, nullptr);
    686  }
    687 
    688  ionScript_.set(zone, ionScript);
    689  MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript());
    690  if (hasIonScript()) {
    691    AddCellMemory(script, ionScript_->allocBytes(), MemoryUse::IonScript);
    692  }
    693 
    694  script->updateJitCodeRaw(gcx->runtime());
    695 }
    696 
    697 #ifdef JS_STRUCTURED_SPEW
    698 static bool HasEnteredCounters(ICEntry& entry) {
    699  ICStub* stub = entry.firstStub();
    700  if (stub && !stub->isFallback()) {
    701    return true;
    702  }
    703  return false;
    704 }
    705 
    706 void jit::JitSpewBaselineICStats(JSScript* script, const char* dumpReason) {
    707  MOZ_ASSERT(script->hasJitScript());
    708  JSContext* cx = TlsContext.get();
    709  AutoStructuredSpewer spew(cx, SpewChannel::BaselineICStats, script);
    710  if (!spew) {
    711    return;
    712  }
    713 
    714  JitScript* jitScript = script->jitScript();
    715  spew->property("reason", dumpReason);
    716  spew->beginListProperty("entries");
    717  for (size_t i = 0; i < jitScript->numICEntries(); i++) {
    718    ICEntry& entry = jitScript->icEntry(i);
    719    ICFallbackStub* fallback = jitScript->fallbackStub(i);
    720    if (!HasEnteredCounters(entry)) {
    721      continue;
    722    }
    723 
    724    uint32_t pcOffset = fallback->pcOffset();
    725    jsbytecode* pc = script->offsetToPC(pcOffset);
    726 
    727    JS::LimitedColumnNumberOneOrigin column;
    728    unsigned int line = PCToLineNumber(script, pc, &column);
    729 
    730    spew->beginObject();
    731    spew->property("op", CodeName(JSOp(*pc)));
    732    spew->property("pc", pcOffset);
    733    spew->property("line", line);
    734    spew->property("column", column.oneOriginValue());
    735 
    736    spew->beginListProperty("counts");
    737    ICStub* stub = entry.firstStub();
    738    while (stub && !stub->isFallback()) {
    739      uint32_t count = stub->enteredCount();
    740      spew->value(count);
    741      stub = stub->toCacheIRStub()->next();
    742    }
    743    spew->endList();
    744    spew->property("fallback_count", fallback->enteredCount());
    745    spew->endObject();
    746  }
    747  spew->endList();
    748 }
    749 #endif
    750 
    751 using StubHashMap = HashMap<ICCacheIRStub*, ICCacheIRStub*,
    752                            DefaultHasher<ICCacheIRStub*>, SystemAllocPolicy>;
    753 
    754 static void MarkActiveICScriptsAndCopyStubs(
    755    JSContext* cx, const JitActivationIterator& activation,
    756    ICStubSpace& newStubSpace, StubHashMap& alreadyClonedStubs) {
    757  for (OnlyJSJitFrameIter iter(activation); !iter.done(); ++iter) {
    758    const JSJitFrameIter& frame = iter.frame();
    759    switch (frame.type()) {
    760      case FrameType::BaselineJS:
    761        frame.script()->jitScript()->icScript()->setActive();
    762        // If the frame is using a trial-inlining ICScript, we have to preserve
    763        // it too.
    764        if (frame.baselineFrame()->icScript()->isInlined()) {
    765          frame.baselineFrame()->icScript()->setActive();
    766        }
    767        break;
    768      case FrameType::BaselineStub: {
    769        auto* layout = reinterpret_cast<BaselineStubFrameLayout*>(frame.fp());
    770        if (layout->maybeStubPtr() && !layout->maybeStubPtr()->isFallback()) {
    771          ICCacheIRStub* stub = layout->maybeStubPtr()->toCacheIRStub();
    772          auto lookup = alreadyClonedStubs.lookupForAdd(stub);
    773          if (!lookup) {
    774            ICCacheIRStub* newStub = stub->clone(cx->runtime(), newStubSpace);
    775            AutoEnterOOMUnsafeRegion oomUnsafe;
    776            if (!alreadyClonedStubs.add(lookup, stub, newStub)) {
    777              oomUnsafe.crash("MarkActiveICScriptsAndCopyStubs");
    778            }
    779          }
    780          layout->setStubPtr(lookup->value());
    781 
    782          // If this is a trial-inlining call site, also preserve the callee
    783          // ICScript. Inlined constructor calls invoke CreateThisFromIC (which
    784          // can trigger GC) before using the inlined ICScript.
    785          JSJitFrameIter parentFrame(frame);
    786          ++parentFrame;
    787          BaselineFrame* blFrame = parentFrame.baselineFrame();
    788          jsbytecode* pc;
    789          parentFrame.baselineScriptAndPc(nullptr, &pc);
    790          uint32_t pcOffset = blFrame->script()->pcToOffset(pc);
    791          if (blFrame->icScript()->hasInlinedChild(pcOffset)) {
    792            blFrame->icScript()->findInlinedChild(pcOffset)->setActive();
    793          }
    794        }
    795        break;
    796      }
    797      case FrameType::Exit:
    798        if (frame.exitFrame()->is<LazyLinkExitFrameLayout>()) {
    799          LazyLinkExitFrameLayout* ll =
    800              frame.exitFrame()->as<LazyLinkExitFrameLayout>();
    801          JSScript* script =
    802              ScriptFromCalleeToken(ll->jsFrame()->calleeToken());
    803          script->jitScript()->icScript()->setActive();
    804        }
    805        break;
    806      case FrameType::Bailout:
    807      case FrameType::IonJS: {
    808        // Keep the JitScript and BaselineScript around, since bailouts from
    809        // the ion jitcode need to re-enter into the Baseline code.
    810        frame.script()->jitScript()->icScript()->setActive();
    811        for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more();
    812             ++inlineIter) {
    813          inlineIter.script()->jitScript()->icScript()->setActive();
    814        }
    815        // Because we're purging ICScripts, the bailout machinery should use
    816        // the generic ICScript for inlined callees.
    817        frame.ionScript()->notePurgedICScripts();
    818        break;
    819      }
    820      default:;
    821    }
    822  }
    823 }
    824 
    825 void jit::MarkActiveICScriptsAndCopyStubs(Zone* zone,
    826                                          ICStubSpace& newStubSpace) {
    827  if (zone->isAtomsZone()) {
    828    return;
    829  }
    830  StubHashMap alreadyClonedStubs;
    831  JSContext* cx = TlsContext.get();
    832  for (JitActivationIterator iter(cx); !iter.done(); ++iter) {
    833    if (iter->compartment()->zone() == zone) {
    834      MarkActiveICScriptsAndCopyStubs(cx, iter, newStubSpace,
    835                                      alreadyClonedStubs);
    836    }
    837  }
    838 }
    839 
    840 InliningRoot* JitScript::getOrCreateInliningRoot(JSContext* cx,
    841                                                 JSScript* script) {
    842  MOZ_ASSERT(script->jitScript() == this);
    843 
    844  if (!inliningRoot_) {
    845    inliningRoot_ = js::MakeUnique<InliningRoot>(cx, script);
    846    if (!inliningRoot_) {
    847      ReportOutOfMemory(cx);
    848      return nullptr;
    849    }
    850    icScript_.inliningRoot_ = inliningRoot_.get();
    851  }
    852  return inliningRoot_.get();
    853 }
    854 
    855 gc::AllocSite* ICScript::getOrCreateAllocSite(JSScript* outerScript,
    856                                              uint32_t pcOffset) {
    857  // The script must be the outer script.
    858  MOZ_ASSERT(outerScript->jitScript()->icScript() == this ||
    859             (inliningRoot() && inliningRoot()->owningScript() == outerScript));
    860 
    861  // The pcOffset must be valid for this (maybe inlined) script.
    862  MOZ_ASSERT_IF(pcOffset != gc::AllocSite::EnvSitePCOffset,
    863                pcOffset < bytecodeSize());
    864 
    865  for (gc::AllocSite* site : allocSites_) {
    866    if (site->pcOffset() == pcOffset) {
    867      MOZ_ASSERT(site->isNormal());
    868      MOZ_ASSERT(site->script() == outerScript);
    869      MOZ_ASSERT(site->traceKind() == JS::TraceKind::Object);
    870      return site;
    871    }
    872  }
    873 
    874  Nursery& nursery = outerScript->runtimeFromMainThread()->gc.nursery();
    875  if (!nursery.canCreateAllocSite()) {
    876    // Don't block attaching an optimized stub, but don't process allocations
    877    // for this site.
    878    return outerScript->zone()->unknownAllocSite(JS::TraceKind::Object);
    879  }
    880 
    881  if (!allocSites_.reserve(allocSites_.length() + 1)) {
    882    return nullptr;
    883  }
    884 
    885  auto* site = allocSitesSpace_.new_<gc::AllocSite>(
    886      outerScript->zone(), outerScript, pcOffset, JS::TraceKind::Object);
    887  if (!site) {
    888    return nullptr;
    889  }
    890 
    891  allocSites_.infallibleAppend(site);
    892 
    893  nursery.noteAllocSiteCreated();
    894 
    895  return site;
    896 }
    897 
    898 void ICScript::ensureEnvAllocSite(JSScript* outerScript) {
    899  if (envAllocSite_) {
    900    return;
    901  }
    902 
    903  // Use a dummy offset for this site.
    904  uint32_t pcoffset = gc::AllocSite::EnvSitePCOffset;
    905  gc::AllocSite* site = getOrCreateAllocSite(outerScript, pcoffset);
    906  if (!site) {
    907    // Use the unknown site on failure.
    908    site = outerScript->zone()->unknownAllocSite(JS::TraceKind::Object);
    909  }
    910 
    911  envAllocSite_ = site;
    912 }
    913 
    914 bool JitScript::resetAllocSites(bool resetNurserySites,
    915                                bool resetPretenuredSites) {
    916  MOZ_ASSERT(resetNurserySites || resetPretenuredSites);
    917 
    918  bool anyReset = false;
    919 
    920  forEachICScript([&](ICScript* script) {
    921    for (gc::AllocSite* site : script->allocSites_) {
    922      if ((resetNurserySites && site->initialHeap() == gc::Heap::Default) ||
    923          (resetPretenuredSites && site->initialHeap() == gc::Heap::Tenured)) {
    924        if (site->maybeResetState()) {
    925          anyReset = true;
    926        }
    927      }
    928    }
    929  });
    930 
    931  return anyReset;
    932 }
    933 
    934 bool JitScript::hasPretenuredAllocSites() {
    935  bool found = false;
    936  forEachICScript([&](ICScript* script) {
    937    if (!found) {
    938      for (gc::AllocSite* site : script->allocSites_) {
    939        if (site->initialHeap() == gc::Heap::Tenured) {
    940          found = true;
    941        }
    942      }
    943    }
    944  });
    945 
    946  return found;
    947 }
    948 
    949 void JitScript::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
    950                                       size_t* data, size_t* allocSites) const {
    951  *data += mallocSizeOf(this);
    952 
    953  forEachICScript([=, this](const ICScript* script) {
    954    // |data| already includes the outer ICScript because it's part of the
    955    // JitScript.
    956    if (script != &icScript_) {
    957      *data += mallocSizeOf(script);
    958    }
    959 
    960    // |data| already includes the LifoAlloc and Vector, so use
    961    // sizeOfExcludingThis.
    962    *allocSites += script->allocSitesSpace_.sizeOfExcludingThis(mallocSizeOf);
    963    *allocSites += script->allocSites_.sizeOfExcludingThis(mallocSizeOf);
    964  });
    965 }
    966 
    967 JitScript* ICScript::outerJitScript() {
    968  MOZ_ASSERT(!isInlined());
    969  uint8_t* ptr = reinterpret_cast<uint8_t*>(this);
    970  return reinterpret_cast<JitScript*>(ptr - JitScript::offsetOfICScript());
    971 }
    972 
    973 #ifdef DEBUG
    974 // This hash is used to verify that we do not recompile after a
    975 // TranspiledCacheIR invalidation with the exact same ICs.
    976 //
    977 // It should change iff an ICEntry in this ICScript (or an ICScript
    978 // inlined into this ICScript) is modified such that we will make a
    979 // different decision in WarpScriptOracle::maybeInlineIC. This means:
    980 //
    981 // 1. The hash will change if we attach a new stub.
    982 // 2. The hash will change if the entered count of any CacheIR stub
    983 //    other than the first changes from 0.
    984 // 3. The hash will change if the entered count of the fallback stub
    985 //    changes from 0.
    986 // 4. The hash will change if the failure count of the fallback stub
    987 //    changes from 0.
    988 // 5. The hash will change if the set of shapes stored in ShapeListSnapshot
    989 //    is changed by stub folding or GC (the shapes in ShapeListObject are weak
    990 //    pointers).
    991 HashNumber ICScript::hash(JSContext* cx) {
    992  HashNumber h = 0;
    993  for (size_t i = 0; i < numICEntries(); i++) {
    994    ICStub* stub = icEntry(i).firstStub();
    995    ICFallbackStub* fallback = fallbackStub(i);
    996 
    997    // Hash the address of the first stub.
    998    h = mozilla::AddToHash(h, stub);
    999 
   1000    // Hash shapes snapshotted in ShapeListSnapshot for GuardMultipleShapes.
   1001    if (!stub->isFallback() && fallback->mayHaveFoldedStub()) {
   1002      const CacheIRStubInfo* stubInfo = stub->toCacheIRStub()->stubInfo();
   1003      CacheIRReader reader(stubInfo);
   1004      while (reader.more()) {
   1005        CacheOp op = reader.readOp();
   1006        switch (op) {
   1007          case CacheOp::GuardMultipleShapes: {
   1008            auto args = reader.argsForGuardMultipleShapes();
   1009            JSObject* shapes =
   1010                stubInfo->getStubField<StubField::Type::JSObject>(
   1011                    stub->toCacheIRStub(), args.shapesOffset);
   1012            auto* shapesObject = &shapes->as<ShapeListObject>();
   1013            size_t numShapes = shapesObject->length();
   1014            if (ShapeListSnapshot::shouldSnapshot(numShapes)) {
   1015              for (size_t i = 0; i < numShapes; i++) {
   1016                Shape* shape = shapesObject->getUnbarriered(i);
   1017                h = mozilla::AddToHash(h, shape);
   1018              }
   1019              // Also include the GC number to handle the case where we bail
   1020              // out, add an additional shape, remove this new shape during GC,
   1021              // and then recompile with the current set of shapes.
   1022              // See bug 2002447.
   1023              h = mozilla::AddToHash(h, cx->runtime()->gc.majorGCCount());
   1024            }
   1025            break;
   1026          }
   1027          case CacheOp::GuardMultipleShapesToOffset: {
   1028            auto args = reader.argsForGuardMultipleShapesToOffset();
   1029            JSObject* shapes =
   1030                stubInfo->getStubField<StubField::Type::JSObject>(
   1031                    stub->toCacheIRStub(), args.shapesOffset);
   1032            auto* shapesObject = &shapes->as<ShapeListWithOffsetsObject>();
   1033            size_t numShapes = shapesObject->numShapes();
   1034            if (ShapeListSnapshot::shouldSnapshot(numShapes)) {
   1035              for (size_t i = 0; i < numShapes; i++) {
   1036                Shape* shape = shapesObject->getShapeUnbarriered(i);
   1037                h = mozilla::AddToHash(h, shape);
   1038                h = mozilla::AddToHash(h, shapesObject->getOffset(i));
   1039              }
   1040            }
   1041            break;
   1042          }
   1043          default:
   1044            reader.skip(CacheIROpInfos[size_t(op)].argLength);
   1045            break;
   1046        }
   1047      }
   1048    }
   1049 
   1050    // Hash whether subsequent stubs have entry count 0.
   1051    if (!stub->isFallback()) {
   1052      stub = stub->toCacheIRStub()->next();
   1053      while (!stub->isFallback()) {
   1054        h = mozilla::AddToHash(h, stub->enteredCount() == 0);
   1055        stub = stub->toCacheIRStub()->next();
   1056      }
   1057    }
   1058 
   1059    // Hash whether the fallback has entry count 0 and failure count 0.
   1060    MOZ_ASSERT(stub->isFallback());
   1061    h = mozilla::AddToHash(h, stub->enteredCount() == 0);
   1062    h = mozilla::AddToHash(h, stub->toFallbackStub()->state().hasFailures());
   1063  }
   1064 
   1065  return h;
   1066 }
   1067 #endif