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"
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"
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(),
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
);
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());
71 JitScript::~JitScript() = default;
74 bool JSScript::createJitScript(JSContext
* cx
) {
75 MOZ_ASSERT(!hasJitScript());
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);
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");
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
);
110 void* raw
= cx
->pod_malloc
<uint8_t>(allocSize
.value());
111 MOZ_ASSERT(uintptr_t(raw
) % alignof(JitScript
) == 0);
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());
139 void JSScript::maybeReleaseJitScript(JS::GCContext
* gcx
) {
140 MOZ_ASSERT(hasJitScript());
142 if (zone()->jitZone()->keepJitScripts() || jitScript()->hasBaselineScript() ||
143 jitScript()->active()) {
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
)) {
203 hasPurgedStubs_
= true;
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
);
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
)) {
237 bool ICScript::addInlinedChild(JSContext
* cx
, UniquePtr
<ICScript
> child
,
239 MOZ_ASSERT(!hasInlinedChild(pcOffset
));
241 if (!inlinedChildren_
) {
242 inlinedChildren_
= cx
->make_unique
<Vector
<CallSite
>>(cx
);
243 if (!inlinedChildren_
) {
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)) {
254 if (!inliningRoot()->addInlinedScript(std::move(child
))) {
257 inlinedChildren_
->infallibleAppend(callsite
);
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_
) {
281 for (auto& callsite
: *inlinedChildren_
) {
282 if (callsite
.pcOffset_
== pcOffset
) {
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_
) {
303 AutoEnterOOMUnsafeRegion oomUnsafe
;
304 profileString_
= cx
->runtime()->geckoProfiler().profileString(cx
, script
);
305 if (!profileString_
) {
306 oomUnsafe
.crash("Failed to allocate profile string");
311 void JitScript::Destroy(Zone
* zone
, JitScript
* script
) {
312 script
->prepareForDestruction(zone
);
314 // Remove from JitZone's linked list of JitScripts.
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
,
348 return mozilla::BinarySearchIf(
349 stubs
, 0, stubs
.numEntries(),
350 [pcOffset
](const ICFallbackStub
* stub
) {
351 if (pcOffset
< stub
->pcOffset()) {
354 if (stub
->pcOffset() < pcOffset
) {
362 ICEntry
& ICScript::icEntryFromPCOffset(uint32_t pcOffset
) {
364 bool success
= ComputeBinarySearchMid(FallbackStubs(this), pcOffset
, &mid
);
365 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
367 MOZ_CRASH_UNSAFE_PRINTF("Missing icEntry for offset %d (max offset: %d)",
369 int(fallbackStub(numICEntries() - 1)->pcOffset()));
372 MOZ_ALWAYS_TRUE(success
);
374 MOZ_ASSERT(mid
< numICEntries());
376 ICEntry
& entry
= icEntry(mid
);
377 MOZ_ASSERT(fallbackStubForICEntry(&entry
)->pcOffset() == pcOffset
);
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.
392 ComputeBinarySearchMid(FallbackStubs(this), pcOffset
, &mid
);
394 if (mid
< numICEntries()) {
395 ICEntry
& entry
= icEntry(mid
);
396 MOZ_ASSERT(fallbackStubForICEntry(&entry
)->pcOffset() >= pcOffset
);
400 // Resuming at a pc after the last ICEntry. Just return nullptr:
401 // BaselineFrame::interpreterICEntry will never be used in this case.
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
418 JitSpew(JitSpew_BaselineIC
, "Purging optimized stubs");
420 icScript()->purgeOptimizedStubs(zone
);
421 if (hasInliningRoot()) {
422 inliningRoot()->purgeOptimizedStubs(zone
);
425 failedICHash_
.reset();
426 hasPurgedStubs_
= true;
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();
450 prev
= stub
->toCacheIRStub();
451 stub
= stub
->toCacheIRStub()->next();
454 lastStub
->toFallbackStub()->clearMayHaveFoldedStub();
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();
470 bool JitScript::ensureHasCachedBaselineJitData(JSContext
* cx
,
471 HandleScript script
) {
472 if (templateEnv_
.isSome()) {
476 if (!script
->function() ||
477 !script
->function()->needsFunctionEnvironmentObjects()) {
478 templateEnv_
.emplace();
482 Rooted
<EnvironmentObject
*> templateEnv(cx
);
483 Rooted
<JSFunction
*> fun(cx
, script
->function());
485 if (fun
->needsNamedLambdaEnvironment()) {
486 templateEnv
= NamedLambdaObject::createTemplateObject(cx
, fun
);
492 if (fun
->needsCallObject()) {
493 templateEnv
= CallObject::createTemplateObject(cx
, script
, templateEnv
);
499 templateEnv_
.emplace(templateEnv
);
503 bool JitScript::ensureHasCachedIonData(JSContext
* cx
, HandleScript script
) {
504 MOZ_ASSERT(script
->jitScript() == this);
506 if (usesEnvironmentChain_
.isSome()) {
510 if (!ensureHasCachedBaselineJitData(cx
, script
)) {
514 usesEnvironmentChain_
.emplace(ScriptUsesEnvironmentChain(script
));
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()) {
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
);
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
)) {
597 uint32_t pcOffset
= fallback
->pcOffset();
598 jsbytecode
* pc
= script
->offsetToPC(pcOffset
);
600 JS::LimitedColumnNumberZeroOrigin column
;
601 unsigned int line
= PCToLineNumber(script
, pc
, &column
);
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();
614 stub
= stub
->toCacheIRStub()->next();
617 spew
->property("fallback_count", fallback
->enteredCount());
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();
632 case FrameType::Exit
:
633 if (frame
.exitFrame()->is
<LazyLinkExitFrameLayout
>()) {
634 LazyLinkExitFrameLayout
* ll
=
635 frame
.exitFrame()->as
<LazyLinkExitFrameLayout
>();
637 ScriptFromCalleeToken(ll
->jsFrame()->calleeToken());
638 script
->jitScript()->setActive();
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();
648 inlineIter
.script()->jitScript()->setActive();
657 void jit::MarkActiveJitScripts(Zone
* zone
) {
658 if (zone
->isAtomsZone()) {
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
,
671 if (!inliningRoot_
) {
672 inliningRoot_
= js::MakeUnique
<InliningRoot
>(cx
, script
);
673 if (!inliningRoot_
) {
674 ReportOutOfMemory(cx
);
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
689 return script
->zone()->unknownAllocSite(JS::TraceKind::Object
);
692 if (!allocSites_
.reserve(allocSites_
.length() + 1)) {
696 ICStubSpace
* stubSpace
= jitScriptStubSpace();
698 static_cast<gc::AllocSite
*>(stubSpace
->alloc(sizeof(gc::AllocSite
)));
703 new (site
) gc::AllocSite(script
->zone(), script
, JS::TraceKind::Object
);
705 allocSites_
.infallibleAppend(site
);
707 nursery
.noteAllocSiteCreated();
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()) {
730 JitScriptICStubSpace
* ICScript::jitScriptStubSpace() {
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());
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
756 // 4. The hash will change if the failure count of the fallback stub
758 HashNumber
ICScript::hash() {
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());