Backed out changeset 2cbc53cb5d01 (bug 1851911) for causing bustages on JitScript...
[gecko.git] / js / src / jit / JitScript.cpp
blob9e19b2304044966d54f5630faf10deea7cc7daf0
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/. */
7 #include "jit/JitScript-inl.h"
9 #include "mozilla/BinarySearch.h"
10 #include "mozilla/CheckedInt.h"
12 #include <utility>
14 #include "jit/BaselineIC.h"
15 #include "jit/BaselineJIT.h"
16 #include "jit/BytecodeAnalysis.h"
17 #include "jit/IonScript.h"
18 #include "jit/JitFrames.h"
19 #include "jit/JitSpewer.h"
20 #include "jit/ScriptFromCalleeToken.h"
21 #include "jit/TrialInlining.h"
22 #include "js/ColumnNumber.h" // JS::LimitedColumnNumberZeroOrigin
23 #include "vm/BytecodeUtil.h"
24 #include "vm/Compartment.h"
25 #include "vm/FrameIter.h" // js::OnlyJSJitFrameIter
26 #include "vm/JitActivation.h"
27 #include "vm/JSScript.h"
29 #include "gc/GCContext-inl.h"
30 #include "jit/JSJitFrameIter-inl.h"
31 #include "vm/JSContext-inl.h"
32 #include "vm/JSScript-inl.h"
34 using namespace js;
35 using namespace js::jit;
37 using mozilla::CheckedInt;
39 JitScript::JitScript(JSScript* script, Offset fallbackStubsOffset,
40 Offset endOffset, const char* profileString)
41 : profileString_(profileString),
42 owningScript_(script),
43 endOffset_(endOffset),
44 icScript_(script->getWarmUpCount(),
45 fallbackStubsOffset - offsetOfICScript(),
46 endOffset - offsetOfICScript(),
47 /*depth=*/0) {
48 // Ensure the baselineScript_ and ionScript_ fields match the BaselineDisabled
49 // and IonDisabled script flags.
50 if (!script->canBaselineCompile()) {
51 setBaselineScriptImpl(script, BaselineDisabledScriptPtr);
53 if (!script->canIonCompile()) {
54 setIonScriptImpl(script, IonDisabledScriptPtr);
58 #ifdef DEBUG
59 JitScript::~JitScript() {
60 // The contents of the stub space are removed and freed separately after the
61 // next minor GC. See prepareForDestruction.
62 MOZ_ASSERT(jitScriptStubSpace_.isEmpty());
64 // BaselineScript and IonScript must have been destroyed at this point.
65 MOZ_ASSERT(!hasBaselineScript());
66 MOZ_ASSERT(!hasIonScript());
68 MOZ_ASSERT(!isInList());
70 #else
71 JitScript::~JitScript() = default;
72 #endif
74 bool JSScript::createJitScript(JSContext* cx) {
75 MOZ_ASSERT(!hasJitScript());
76 cx->check(this);
78 // Scripts with a JitScript can run in the Baseline Interpreter. Make sure
79 // we don't create a JitScript for scripts we shouldn't Baseline interpret.
80 MOZ_ASSERT_IF(IsBaselineInterpreterEnabled(),
81 CanBaselineInterpretScript(this));
83 // Store the profile string in the JitScript if the profiler is enabled.
84 const char* profileString = nullptr;
85 if (cx->runtime()->geckoProfiler().enabled()) {
86 profileString = cx->runtime()->geckoProfiler().profileString(cx, this);
87 if (!profileString) {
88 return false;
92 static_assert(sizeof(JitScript) % sizeof(uintptr_t) == 0,
93 "Trailing arrays must be aligned properly");
94 static_assert(sizeof(ICEntry) % sizeof(uintptr_t) == 0,
95 "Trailing arrays must be aligned properly");
97 static_assert(
98 sizeof(JitScript) == offsetof(JitScript, icScript_) + sizeof(ICScript),
99 "icScript_ must be the last field");
101 // Calculate allocation size.
102 CheckedInt<uint32_t> allocSize = sizeof(JitScript);
103 allocSize += CheckedInt<uint32_t>(numICEntries()) * sizeof(ICEntry);
104 allocSize += CheckedInt<uint32_t>(numICEntries()) * sizeof(ICFallbackStub);
105 if (!allocSize.isValid()) {
106 ReportAllocationOverflow(cx);
107 return false;
110 void* raw = cx->pod_malloc<uint8_t>(allocSize.value());
111 MOZ_ASSERT(uintptr_t(raw) % alignof(JitScript) == 0);
112 if (!raw) {
113 return false;
116 size_t fallbackStubsOffset =
117 sizeof(JitScript) + numICEntries() * sizeof(ICEntry);
119 UniquePtr<JitScript> jitScript(new (raw) JitScript(
120 this, fallbackStubsOffset, allocSize.value(), profileString));
122 // Sanity check the length computation.
123 MOZ_ASSERT(jitScript->numICEntries() == numICEntries());
125 jitScript->icScript()->initICEntries(cx, this);
127 cx->zone()->jitZone()->registerJitScript(jitScript.get());
129 warmUpData_.initJitScript(jitScript.release());
130 AddCellMemory(this, allocSize.value(), MemoryUse::JitScript);
132 // We have a JitScript so we can set the script's jitCodeRaw pointer to the
133 // Baseline Interpreter code.
134 updateJitCodeRaw(cx->runtime());
136 return true;
139 void JSScript::maybeReleaseJitScript(JS::GCContext* gcx) {
140 MOZ_ASSERT(hasJitScript());
142 if (zone()->jitZone()->keepJitScripts() || jitScript()->hasBaselineScript() ||
143 jitScript()->active()) {
144 return;
147 releaseJitScript(gcx);
150 void JSScript::releaseJitScript(JS::GCContext* gcx) {
151 MOZ_ASSERT(hasJitScript());
152 MOZ_ASSERT(!hasBaselineScript());
153 MOZ_ASSERT(!hasIonScript());
155 gcx->removeCellMemory(this, jitScript()->allocBytes(), MemoryUse::JitScript);
157 JitScript::Destroy(zone(), jitScript());
158 warmUpData_.clearJitScript();
159 updateJitCodeRaw(gcx->runtime());
162 void JSScript::releaseJitScriptOnFinalize(JS::GCContext* gcx) {
163 MOZ_ASSERT(hasJitScript());
165 if (hasIonScript()) {
166 IonScript* ion = jitScript()->clearIonScript(gcx, this);
167 jit::IonScript::Destroy(gcx, ion);
170 if (hasBaselineScript()) {
171 BaselineScript* baseline = jitScript()->clearBaselineScript(gcx, this);
172 jit::BaselineScript::Destroy(gcx, baseline);
175 releaseJitScript(gcx);
178 void JitScript::trace(JSTracer* trc) {
179 TraceEdge(trc, &owningScript_, "JitScript::owningScript_");
181 icScript_.trace(trc);
183 if (hasBaselineScript()) {
184 baselineScript()->trace(trc);
187 if (hasIonScript()) {
188 ionScript()->trace(trc);
191 if (templateEnv_.isSome()) {
192 TraceNullableEdge(trc, templateEnv_.ptr(), "jitscript-template-env");
195 if (hasInliningRoot()) {
196 inliningRoot()->trace(trc);
200 void JitScript::traceWeak(JSTracer* trc) {
201 if (!icScript_.traceWeak(trc)) {
202 #ifdef DEBUG
203 hasPurgedStubs_ = true;
204 #endif
207 if (hasInliningRoot()) {
208 inliningRoot()->traceWeak(trc);
211 if (hasIonScript()) {
212 ionScript()->traceWeak(trc);
216 void ICScript::trace(JSTracer* trc) {
217 // Mark all IC stub codes hanging off the IC stub entries.
218 for (size_t i = 0; i < numICEntries(); i++) {
219 ICEntry& ent = icEntry(i);
220 ent.trace(trc);
224 bool ICScript::traceWeak(JSTracer* trc) {
225 // Mark all IC stub codes hanging off the IC stub entries.
226 bool allSurvived = true;
227 for (size_t i = 0; i < numICEntries(); i++) {
228 ICEntry& ent = icEntry(i);
229 if (!ent.traceWeak(trc)) {
230 allSurvived = false;
234 return allSurvived;
237 bool ICScript::addInlinedChild(JSContext* cx, UniquePtr<ICScript> child,
238 uint32_t pcOffset) {
239 MOZ_ASSERT(!hasInlinedChild(pcOffset));
241 if (!inlinedChildren_) {
242 inlinedChildren_ = cx->make_unique<Vector<CallSite>>(cx);
243 if (!inlinedChildren_) {
244 return false;
248 // First reserve space in inlinedChildren_ to ensure that if the ICScript is
249 // added to the inlining root, it can also be added to inlinedChildren_.
250 CallSite callsite(child.get(), pcOffset);
251 if (!inlinedChildren_->reserve(inlinedChildren_->length() + 1)) {
252 return false;
254 if (!inliningRoot()->addInlinedScript(std::move(child))) {
255 return false;
257 inlinedChildren_->infallibleAppend(callsite);
258 return true;
261 ICScript* ICScript::findInlinedChild(uint32_t pcOffset) {
262 for (auto& callsite : *inlinedChildren_) {
263 if (callsite.pcOffset_ == pcOffset) {
264 return callsite.callee_;
267 MOZ_CRASH("Inlined child expected at pcOffset");
270 void ICScript::removeInlinedChild(uint32_t pcOffset) {
271 MOZ_ASSERT(inliningRoot());
272 inlinedChildren_->eraseIf([pcOffset](const CallSite& callsite) -> bool {
273 return callsite.pcOffset_ == pcOffset;
277 bool ICScript::hasInlinedChild(uint32_t pcOffset) {
278 if (!inlinedChildren_) {
279 return false;
281 for (auto& callsite : *inlinedChildren_) {
282 if (callsite.pcOffset_ == pcOffset) {
283 return true;
286 return false;
289 void JitScript::resetWarmUpCount(uint32_t count) {
290 icScript_.resetWarmUpCount(count);
291 if (hasInliningRoot()) {
292 inliningRoot()->resetWarmUpCounts(count);
296 void JitScript::ensureProfileString(JSContext* cx, JSScript* script) {
297 MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled());
299 if (profileString_) {
300 return;
303 AutoEnterOOMUnsafeRegion oomUnsafe;
304 profileString_ = cx->runtime()->geckoProfiler().profileString(cx, script);
305 if (!profileString_) {
306 oomUnsafe.crash("Failed to allocate profile string");
310 /* static */
311 void JitScript::Destroy(Zone* zone, JitScript* script) {
312 script->prepareForDestruction(zone);
314 // Remove from JitZone's linked list of JitScripts.
315 script->remove();
317 js_delete(script);
320 void JitScript::prepareForDestruction(Zone* zone) {
321 // When the script contains pointers to nursery things, the store buffer can
322 // contain entries that point into the fallback stub space. Since we can
323 // destroy scripts outside the context of a GC, this situation could result
324 // in us trying to mark invalid store buffer entries.
326 // Defer freeing any allocated blocks until after the next minor GC.
327 jitScriptStubSpace_.freeAllAfterMinorGC(zone);
329 // Trigger write barriers.
330 owningScript_ = nullptr;
331 baselineScript_.set(zone, nullptr);
332 ionScript_.set(zone, nullptr);
335 struct FallbackStubs {
336 ICScript* const icScript_;
338 explicit FallbackStubs(ICScript* icScript) : icScript_(icScript) {}
340 size_t numEntries() const { return icScript_->numICEntries(); }
341 ICFallbackStub* operator[](size_t index) const {
342 return icScript_->fallbackStub(index);
346 static bool ComputeBinarySearchMid(FallbackStubs stubs, uint32_t pcOffset,
347 size_t* loc) {
348 return mozilla::BinarySearchIf(
349 stubs, 0, stubs.numEntries(),
350 [pcOffset](const ICFallbackStub* stub) {
351 if (pcOffset < stub->pcOffset()) {
352 return -1;
354 if (stub->pcOffset() < pcOffset) {
355 return 1;
357 return 0;
359 loc);
362 ICEntry& ICScript::icEntryFromPCOffset(uint32_t pcOffset) {
363 size_t mid;
364 bool success = ComputeBinarySearchMid(FallbackStubs(this), pcOffset, &mid);
365 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
366 if (!success) {
367 MOZ_CRASH_UNSAFE_PRINTF("Missing icEntry for offset %d (max offset: %d)",
368 int(pcOffset),
369 int(fallbackStub(numICEntries() - 1)->pcOffset()));
371 #endif
372 MOZ_ALWAYS_TRUE(success);
374 MOZ_ASSERT(mid < numICEntries());
376 ICEntry& entry = icEntry(mid);
377 MOZ_ASSERT(fallbackStubForICEntry(&entry)->pcOffset() == pcOffset);
378 return entry;
381 ICEntry* ICScript::interpreterICEntryFromPCOffset(uint32_t pcOffset) {
382 // We have to return the entry to store in BaselineFrame::interpreterICEntry
383 // when resuming in the Baseline Interpreter at pcOffset. The bytecode op at
384 // pcOffset does not necessarily have an ICEntry, so we want to return the
385 // first ICEntry for which the following is true:
387 // entry.pcOffset() >= pcOffset
389 // Fortunately, ComputeBinarySearchMid returns exactly this entry.
391 size_t mid;
392 ComputeBinarySearchMid(FallbackStubs(this), pcOffset, &mid);
394 if (mid < numICEntries()) {
395 ICEntry& entry = icEntry(mid);
396 MOZ_ASSERT(fallbackStubForICEntry(&entry)->pcOffset() >= pcOffset);
397 return &entry;
400 // Resuming at a pc after the last ICEntry. Just return nullptr:
401 // BaselineFrame::interpreterICEntry will never be used in this case.
402 return nullptr;
405 void JitScript::purgeOptimizedStubs(JSScript* script) {
406 MOZ_ASSERT(script->jitScript() == this);
408 Zone* zone = script->zone();
409 if (IsAboutToBeFinalizedUnbarriered(script)) {
410 // We're sweeping and the script is dead. Don't purge optimized stubs
411 // because (1) accessing CacheIRStubInfo pointers in ICStubs is invalid
412 // because we may have swept them already when we started (incremental)
413 // sweeping and (2) it's unnecessary because this script will be finalized
414 // soon anyway.
415 return;
418 JitSpew(JitSpew_BaselineIC, "Purging optimized stubs");
420 icScript()->purgeOptimizedStubs(zone);
421 if (hasInliningRoot()) {
422 inliningRoot()->purgeOptimizedStubs(zone);
424 #ifdef DEBUG
425 failedICHash_.reset();
426 hasPurgedStubs_ = true;
427 #endif
430 void ICScript::purgeOptimizedStubs(Zone* zone) {
431 for (size_t i = 0; i < numICEntries(); i++) {
432 ICEntry& entry = icEntry(i);
433 ICStub* lastStub = entry.firstStub();
434 while (!lastStub->isFallback()) {
435 lastStub = lastStub->toCacheIRStub()->next();
438 // Unlink all stubs allocated in the optimized space.
439 ICStub* stub = entry.firstStub();
440 ICCacheIRStub* prev = nullptr;
442 while (stub != lastStub) {
443 if (!stub->toCacheIRStub()->allocatedInFallbackSpace()) {
444 lastStub->toFallbackStub()->unlinkStub(zone, &entry, prev,
445 stub->toCacheIRStub());
446 stub = stub->toCacheIRStub()->next();
447 continue;
450 prev = stub->toCacheIRStub();
451 stub = stub->toCacheIRStub()->next();
454 lastStub->toFallbackStub()->clearMayHaveFoldedStub();
457 #ifdef DEBUG
458 // All remaining stubs must be allocated in the fallback space.
459 for (size_t i = 0; i < numICEntries(); i++) {
460 ICEntry& entry = icEntry(i);
461 ICStub* stub = entry.firstStub();
462 while (!stub->isFallback()) {
463 MOZ_ASSERT(stub->toCacheIRStub()->allocatedInFallbackSpace());
464 stub = stub->toCacheIRStub()->next();
467 #endif
470 bool JitScript::ensureHasCachedBaselineJitData(JSContext* cx,
471 HandleScript script) {
472 if (templateEnv_.isSome()) {
473 return true;
476 if (!script->function() ||
477 !script->function()->needsFunctionEnvironmentObjects()) {
478 templateEnv_.emplace();
479 return true;
482 Rooted<EnvironmentObject*> templateEnv(cx);
483 Rooted<JSFunction*> fun(cx, script->function());
485 if (fun->needsNamedLambdaEnvironment()) {
486 templateEnv = NamedLambdaObject::createTemplateObject(cx, fun);
487 if (!templateEnv) {
488 return false;
492 if (fun->needsCallObject()) {
493 templateEnv = CallObject::createTemplateObject(cx, script, templateEnv);
494 if (!templateEnv) {
495 return false;
499 templateEnv_.emplace(templateEnv);
500 return true;
503 bool JitScript::ensureHasCachedIonData(JSContext* cx, HandleScript script) {
504 MOZ_ASSERT(script->jitScript() == this);
506 if (usesEnvironmentChain_.isSome()) {
507 return true;
510 if (!ensureHasCachedBaselineJitData(cx, script)) {
511 return false;
514 usesEnvironmentChain_.emplace(ScriptUsesEnvironmentChain(script));
515 return true;
518 void JitScript::setBaselineScriptImpl(JSScript* script,
519 BaselineScript* baselineScript) {
520 JSRuntime* rt = script->runtimeFromMainThread();
521 setBaselineScriptImpl(rt->gcContext(), script, baselineScript);
524 void JitScript::setBaselineScriptImpl(JS::GCContext* gcx, JSScript* script,
525 BaselineScript* baselineScript) {
526 if (hasBaselineScript()) {
527 gcx->removeCellMemory(script, baselineScript_->allocBytes(),
528 MemoryUse::BaselineScript);
529 baselineScript_.set(script->zone(), nullptr);
532 MOZ_ASSERT(ionScript_ == nullptr || ionScript_ == IonDisabledScriptPtr);
534 baselineScript_.set(script->zone(), baselineScript);
535 if (hasBaselineScript()) {
536 AddCellMemory(script, baselineScript_->allocBytes(),
537 MemoryUse::BaselineScript);
540 script->resetWarmUpResetCounter();
541 script->updateJitCodeRaw(gcx->runtime());
544 void JitScript::setIonScriptImpl(JSScript* script, IonScript* ionScript) {
545 JSRuntime* rt = script->runtimeFromMainThread();
546 setIonScriptImpl(rt->gcContext(), script, ionScript);
549 void JitScript::setIonScriptImpl(JS::GCContext* gcx, JSScript* script,
550 IonScript* ionScript) {
551 MOZ_ASSERT_IF(ionScript != IonDisabledScriptPtr,
552 !baselineScript()->hasPendingIonCompileTask());
554 JS::Zone* zone = script->zone();
555 if (hasIonScript()) {
556 gcx->removeCellMemory(script, ionScript_->allocBytes(),
557 MemoryUse::IonScript);
558 ionScript_.set(zone, nullptr);
561 ionScript_.set(zone, ionScript);
562 MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript());
563 if (hasIonScript()) {
564 AddCellMemory(script, ionScript_->allocBytes(), MemoryUse::IonScript);
567 script->updateJitCodeRaw(gcx->runtime());
570 #ifdef JS_STRUCTURED_SPEW
571 static bool HasEnteredCounters(ICEntry& entry) {
572 ICStub* stub = entry.firstStub();
573 if (stub && !stub->isFallback()) {
574 return true;
576 return false;
579 void jit::JitSpewBaselineICStats(JSScript* script, const char* dumpReason) {
580 MOZ_ASSERT(script->hasJitScript());
581 JSContext* cx = TlsContext.get();
582 AutoStructuredSpewer spew(cx, SpewChannel::BaselineICStats, script);
583 if (!spew) {
584 return;
587 JitScript* jitScript = script->jitScript();
588 spew->property("reason", dumpReason);
589 spew->beginListProperty("entries");
590 for (size_t i = 0; i < jitScript->numICEntries(); i++) {
591 ICEntry& entry = jitScript->icEntry(i);
592 ICFallbackStub* fallback = jitScript->fallbackStub(i);
593 if (!HasEnteredCounters(entry)) {
594 continue;
597 uint32_t pcOffset = fallback->pcOffset();
598 jsbytecode* pc = script->offsetToPC(pcOffset);
600 JS::LimitedColumnNumberZeroOrigin column;
601 unsigned int line = PCToLineNumber(script, pc, &column);
603 spew->beginObject();
604 spew->property("op", CodeName(JSOp(*pc)));
605 spew->property("pc", pcOffset);
606 spew->property("line", line);
607 spew->property("column", column.zeroOriginValue());
609 spew->beginListProperty("counts");
610 ICStub* stub = entry.firstStub();
611 while (stub && !stub->isFallback()) {
612 uint32_t count = stub->enteredCount();
613 spew->value(count);
614 stub = stub->toCacheIRStub()->next();
616 spew->endList();
617 spew->property("fallback_count", fallback->enteredCount());
618 spew->endObject();
620 spew->endList();
622 #endif
624 static void MarkActiveJitScripts(JSContext* cx,
625 const JitActivationIterator& activation) {
626 for (OnlyJSJitFrameIter iter(activation); !iter.done(); ++iter) {
627 const JSJitFrameIter& frame = iter.frame();
628 switch (frame.type()) {
629 case FrameType::BaselineJS:
630 frame.script()->jitScript()->setActive();
631 break;
632 case FrameType::Exit:
633 if (frame.exitFrame()->is<LazyLinkExitFrameLayout>()) {
634 LazyLinkExitFrameLayout* ll =
635 frame.exitFrame()->as<LazyLinkExitFrameLayout>();
636 JSScript* script =
637 ScriptFromCalleeToken(ll->jsFrame()->calleeToken());
638 script->jitScript()->setActive();
640 break;
641 case FrameType::Bailout:
642 case FrameType::IonJS: {
643 // Keep the JitScript and BaselineScript around, since bailouts from
644 // the ion jitcode need to re-enter into the Baseline code.
645 frame.script()->jitScript()->setActive();
646 for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more();
647 ++inlineIter) {
648 inlineIter.script()->jitScript()->setActive();
650 break;
652 default:;
657 void jit::MarkActiveJitScripts(Zone* zone) {
658 if (zone->isAtomsZone()) {
659 return;
661 JSContext* cx = TlsContext.get();
662 for (JitActivationIterator iter(cx); !iter.done(); ++iter) {
663 if (iter->compartment()->zone() == zone) {
664 MarkActiveJitScripts(cx, iter);
669 InliningRoot* JitScript::getOrCreateInliningRoot(JSContext* cx,
670 JSScript* script) {
671 if (!inliningRoot_) {
672 inliningRoot_ = js::MakeUnique<InliningRoot>(cx, script);
673 if (!inliningRoot_) {
674 ReportOutOfMemory(cx);
675 return nullptr;
677 icScript_.inliningRoot_ = inliningRoot_.get();
679 return inliningRoot_.get();
682 gc::AllocSite* JitScript::createAllocSite(JSScript* script) {
683 MOZ_ASSERT(script->jitScript() == this);
685 Nursery& nursery = script->runtimeFromMainThread()->gc.nursery();
686 if (!nursery.canCreateAllocSite()) {
687 // Don't block attaching an optimized stub, but don't process allocations
688 // for this site.
689 return script->zone()->unknownAllocSite(JS::TraceKind::Object);
692 if (!allocSites_.reserve(allocSites_.length() + 1)) {
693 return nullptr;
696 ICStubSpace* stubSpace = jitScriptStubSpace();
697 auto* site =
698 static_cast<gc::AllocSite*>(stubSpace->alloc(sizeof(gc::AllocSite)));
699 if (!site) {
700 return nullptr;
703 new (site) gc::AllocSite(script->zone(), script, JS::TraceKind::Object);
705 allocSites_.infallibleAppend(site);
707 nursery.noteAllocSiteCreated();
709 return site;
712 bool JitScript::resetAllocSites(bool resetNurserySites,
713 bool resetPretenuredSites) {
714 MOZ_ASSERT(resetNurserySites || resetPretenuredSites);
716 bool anyReset = false;
718 for (gc::AllocSite* site : allocSites_) {
719 if ((resetNurserySites && site->initialHeap() == gc::Heap::Default) ||
720 (resetPretenuredSites && site->initialHeap() == gc::Heap::Tenured)) {
721 if (site->maybeResetState()) {
722 anyReset = true;
727 return anyReset;
730 JitScriptICStubSpace* ICScript::jitScriptStubSpace() {
731 if (isInlined()) {
732 return inliningRoot_->jitScriptStubSpace();
734 return outerJitScript()->jitScriptStubSpace();
737 JitScript* ICScript::outerJitScript() {
738 MOZ_ASSERT(!isInlined());
739 uint8_t* ptr = reinterpret_cast<uint8_t*>(this);
740 return reinterpret_cast<JitScript*>(ptr - JitScript::offsetOfICScript());
743 #ifdef DEBUG
744 // This hash is used to verify that we do not recompile after a
745 // TranspiledCacheIR invalidation with the exact same ICs.
747 // It should change iff an ICEntry in this ICScript (or an ICScript
748 // inlined into this ICScript) is modified such that we will make a
749 // different decision in WarpScriptOracle::maybeInlineIC. This means:
751 // 1. The hash will change if we attach a new stub.
752 // 2. The hash will change if the entered count of any CacheIR stub
753 // other than the first changes from 0.
754 // 3. The hash will change if the entered count of the fallback stub
755 // changes from 0.
756 // 4. The hash will change if the failure count of the fallback stub
757 // changes from 0.
758 HashNumber ICScript::hash() {
759 HashNumber h = 0;
760 for (size_t i = 0; i < numICEntries(); i++) {
761 ICStub* stub = icEntry(i).firstStub();
763 // Hash the address of the first stub.
764 h = mozilla::AddToHash(h, stub);
766 // Hash whether subsequent stubs have entry count 0.
767 if (!stub->isFallback()) {
768 stub = stub->toCacheIRStub()->next();
769 while (!stub->isFallback()) {
770 h = mozilla::AddToHash(h, stub->enteredCount() == 0);
771 stub = stub->toCacheIRStub()->next();
775 // Hash whether the fallback has entry count 0 and failure count 0.
776 MOZ_ASSERT(stub->isFallback());
777 h = mozilla::AddToHash(h, stub->enteredCount() == 0);
778 h = mozilla::AddToHash(h, stub->toFallbackStub()->state().hasFailures());
781 return h;
783 #endif