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::LimitedColumnNumberOneOrigin
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(),
47 /*depth=*/0, script
->length()) {
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 ICScript::~ICScript() {
59 // The contents of the AllocSite LifoAlloc are removed and freed separately
60 // after the next minor GC. See prepareForDestruction.
61 MOZ_ASSERT(allocSitesSpace_
.isEmpty());
65 JitScript::~JitScript() {
66 // BaselineScript and IonScript must have been destroyed at this point.
67 MOZ_ASSERT(!hasBaselineScript());
68 MOZ_ASSERT(!hasIonScript());
70 MOZ_ASSERT(!isInList());
73 JitScript::~JitScript() = default;
76 bool JSScript::createJitScript(JSContext
* cx
) {
77 MOZ_ASSERT(!hasJitScript());
80 // Scripts with a JitScript can run in the Baseline Interpreter. Make sure
81 // we don't create a JitScript for scripts we shouldn't Baseline interpret.
82 MOZ_ASSERT_IF(IsBaselineInterpreterEnabled(),
83 CanBaselineInterpretScript(this));
85 // Store the profile string in the JitScript if the profiler is enabled.
86 const char* profileString
= nullptr;
87 if (cx
->runtime()->geckoProfiler().enabled()) {
88 profileString
= cx
->runtime()->geckoProfiler().profileString(cx
, this);
94 static_assert(sizeof(JitScript
) % sizeof(uintptr_t) == 0,
95 "Trailing arrays must be aligned properly");
96 static_assert(sizeof(ICEntry
) % sizeof(uintptr_t) == 0,
97 "Trailing arrays must be aligned properly");
100 sizeof(JitScript
) == offsetof(JitScript
, icScript_
) + sizeof(ICScript
),
101 "icScript_ must be the last field");
103 // Calculate allocation size.
104 CheckedInt
<uint32_t> allocSize
= sizeof(JitScript
);
105 allocSize
+= CheckedInt
<uint32_t>(numICEntries()) * sizeof(ICEntry
);
106 allocSize
+= CheckedInt
<uint32_t>(numICEntries()) * sizeof(ICFallbackStub
);
107 if (!allocSize
.isValid()) {
108 ReportAllocationOverflow(cx
);
112 void* raw
= cx
->pod_malloc
<uint8_t>(allocSize
.value());
113 MOZ_ASSERT(uintptr_t(raw
) % alignof(JitScript
) == 0);
118 size_t fallbackStubsOffset
=
119 sizeof(JitScript
) + numICEntries() * sizeof(ICEntry
);
121 UniquePtr
<JitScript
> jitScript(new (raw
) JitScript(
122 this, fallbackStubsOffset
, allocSize
.value(), profileString
));
124 // Sanity check the length computation.
125 MOZ_ASSERT(jitScript
->numICEntries() == numICEntries());
127 jitScript
->icScript()->initICEntries(cx
, this);
129 cx
->zone()->jitZone()->registerJitScript(jitScript
.get());
131 warmUpData_
.initJitScript(jitScript
.release());
132 AddCellMemory(this, allocSize
.value(), MemoryUse::JitScript
);
134 // We have a JitScript so we can set the script's jitCodeRaw pointer to the
135 // Baseline Interpreter code.
136 updateJitCodeRaw(cx
->runtime());
141 void JSScript::maybeReleaseJitScript(JS::GCContext
* gcx
) {
142 MOZ_ASSERT(hasJitScript());
144 if (zone()->jitZone()->keepJitScripts() || jitScript()->hasBaselineScript() ||
145 jitScript()->icScript()->active()) {
149 releaseJitScript(gcx
);
152 void JSScript::releaseJitScript(JS::GCContext
* gcx
) {
153 MOZ_ASSERT(hasJitScript());
154 MOZ_ASSERT(!hasBaselineScript());
155 MOZ_ASSERT(!hasIonScript());
157 gcx
->removeCellMemory(this, jitScript()->allocBytes(), MemoryUse::JitScript
);
159 JitScript::Destroy(zone(), jitScript());
160 warmUpData_
.clearJitScript();
161 updateJitCodeRaw(gcx
->runtime());
164 void JSScript::releaseJitScriptOnFinalize(JS::GCContext
* gcx
) {
165 MOZ_ASSERT(hasJitScript());
167 if (hasIonScript()) {
168 IonScript
* ion
= jitScript()->clearIonScript(gcx
, this);
169 jit::IonScript::Destroy(gcx
, ion
);
172 if (hasBaselineScript()) {
173 BaselineScript
* baseline
= jitScript()->clearBaselineScript(gcx
, this);
174 jit::BaselineScript::Destroy(gcx
, baseline
);
177 releaseJitScript(gcx
);
180 void JitScript::trace(JSTracer
* trc
) {
181 TraceEdge(trc
, &owningScript_
, "JitScript::owningScript_");
183 icScript_
.trace(trc
);
185 if (hasBaselineScript()) {
186 baselineScript()->trace(trc
);
189 if (hasIonScript()) {
190 ionScript()->trace(trc
);
193 if (templateEnv_
.isSome()) {
194 TraceNullableEdge(trc
, templateEnv_
.ptr(), "jitscript-template-env");
197 if (hasInliningRoot()) {
198 inliningRoot()->trace(trc
);
202 void JitScript::traceWeak(JSTracer
* trc
) {
203 if (!icScript_
.traceWeak(trc
)) {
207 if (hasInliningRoot()) {
208 if (!inliningRoot()->traceWeak(trc
)) {
213 if (hasIonScript()) {
214 ionScript()->traceWeak(trc
);
218 void ICScript::trace(JSTracer
* trc
) {
219 // Mark all IC stub codes hanging off the IC stub entries.
220 for (size_t i
= 0; i
< numICEntries(); i
++) {
221 ICEntry
& ent
= icEntry(i
);
225 for (gc::AllocSite
* site
: allocSites_
) {
230 bool ICScript::traceWeak(JSTracer
* trc
) {
231 // Mark all IC stub codes hanging off the IC stub entries.
232 bool allSurvived
= true;
233 for (size_t i
= 0; i
< numICEntries(); i
++) {
234 ICEntry
& ent
= icEntry(i
);
235 if (!ent
.traceWeak(trc
)) {
243 bool ICScript::addInlinedChild(JSContext
* cx
, UniquePtr
<ICScript
> child
,
245 MOZ_ASSERT(!hasInlinedChild(pcOffset
));
247 if (!inlinedChildren_
) {
248 inlinedChildren_
= cx
->make_unique
<Vector
<CallSite
>>(cx
);
249 if (!inlinedChildren_
) {
254 // First reserve space in inlinedChildren_ to ensure that if the ICScript is
255 // added to the inlining root, it can also be added to inlinedChildren_.
256 CallSite
callsite(child
.get(), pcOffset
);
257 if (!inlinedChildren_
->reserve(inlinedChildren_
->length() + 1)) {
260 if (!inliningRoot()->addInlinedScript(std::move(child
))) {
263 inlinedChildren_
->infallibleAppend(callsite
);
267 ICScript
* ICScript::findInlinedChild(uint32_t pcOffset
) {
268 for (auto& callsite
: *inlinedChildren_
) {
269 if (callsite
.pcOffset_
== pcOffset
) {
270 return callsite
.callee_
;
273 MOZ_CRASH("Inlined child expected at pcOffset");
276 void ICScript::removeInlinedChild(uint32_t pcOffset
) {
277 MOZ_ASSERT(inliningRoot());
278 inlinedChildren_
->eraseIf([pcOffset
](const CallSite
& callsite
) -> bool {
279 return callsite
.pcOffset_
== pcOffset
;
283 bool ICScript::hasInlinedChild(uint32_t pcOffset
) {
284 if (!inlinedChildren_
) {
287 for (auto& callsite
: *inlinedChildren_
) {
288 if (callsite
.pcOffset_
== pcOffset
) {
295 void ICScript::purgeInactiveICScripts() {
296 MOZ_ASSERT(inliningRoot());
298 if (!inlinedChildren_
) {
302 inlinedChildren_
->eraseIf(
303 [](const CallSite
& callsite
) { return !callsite
.callee_
->active(); });
305 if (inlinedChildren_
->empty()) {
306 inlinedChildren_
.reset();
310 // We have an active callee ICScript. This means the current ICScript must be
312 MOZ_ASSERT(active());
315 void JitScript::resetWarmUpCount(uint32_t count
) {
316 forEachICScript([&](ICScript
* script
) { script
->resetWarmUpCount(count
); });
320 bool JitScript::hasActiveICScript() const {
321 bool hasActive
= false;
322 forEachICScript([&](const ICScript
* script
) {
323 if (script
->active()) {
331 void JitScript::resetAllActiveFlags() {
332 forEachICScript([](ICScript
* script
) { script
->resetActive(); });
335 void JitScript::ensureProfileString(JSContext
* cx
, JSScript
* script
) {
336 MOZ_ASSERT(cx
->runtime()->geckoProfiler().enabled());
338 if (profileString_
) {
342 AutoEnterOOMUnsafeRegion oomUnsafe
;
343 profileString_
= cx
->runtime()->geckoProfiler().profileString(cx
, script
);
344 if (!profileString_
) {
345 oomUnsafe
.crash("Failed to allocate profile string");
350 void JitScript::Destroy(Zone
* zone
, JitScript
* script
) {
351 script
->prepareForDestruction(zone
);
353 // Remove from JitZone's linked list of JitScripts.
359 template <typename F
>
360 void JitScript::forEachICScript(const F
& f
) {
362 if (hasInliningRoot()) {
363 inliningRoot()->forEachInlinedScript(f
);
367 template <typename F
>
368 void JitScript::forEachICScript(const F
& f
) const {
370 if (hasInliningRoot()) {
371 inliningRoot()->forEachInlinedScript(f
);
375 void ICScript::prepareForDestruction(Zone
* zone
) {
376 // Defer freeing AllocSite memory until after the next minor GC, because the
377 // nursery can point to these alloc sites.
378 JSRuntime
* rt
= zone
->runtimeFromMainThread();
379 rt
->gc
.queueAllLifoBlocksForFreeAfterMinorGC(&allocSitesSpace_
);
381 // Trigger write barriers.
382 PreWriteBarrier(zone
, this);
385 void JitScript::prepareForDestruction(Zone
* zone
) {
387 [&](ICScript
* script
) { script
->prepareForDestruction(zone
); });
389 // Trigger write barriers.
390 owningScript_
= nullptr;
391 baselineScript_
.set(zone
, nullptr);
392 ionScript_
.set(zone
, nullptr);
395 struct FallbackStubs
{
396 ICScript
* const icScript_
;
398 explicit FallbackStubs(ICScript
* icScript
) : icScript_(icScript
) {}
400 size_t numEntries() const { return icScript_
->numICEntries(); }
401 ICFallbackStub
* operator[](size_t index
) const {
402 return icScript_
->fallbackStub(index
);
406 static bool ComputeBinarySearchMid(FallbackStubs stubs
, uint32_t pcOffset
,
408 return mozilla::BinarySearchIf(
409 stubs
, 0, stubs
.numEntries(),
410 [pcOffset
](const ICFallbackStub
* stub
) {
411 if (pcOffset
< stub
->pcOffset()) {
414 if (stub
->pcOffset() < pcOffset
) {
422 ICEntry
& ICScript::icEntryFromPCOffset(uint32_t pcOffset
) {
424 bool success
= ComputeBinarySearchMid(FallbackStubs(this), pcOffset
, &mid
);
425 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
427 MOZ_CRASH_UNSAFE_PRINTF("Missing icEntry for offset %d (max offset: %d)",
429 int(fallbackStub(numICEntries() - 1)->pcOffset()));
432 MOZ_ALWAYS_TRUE(success
);
434 MOZ_ASSERT(mid
< numICEntries());
436 ICEntry
& entry
= icEntry(mid
);
437 MOZ_ASSERT(fallbackStubForICEntry(&entry
)->pcOffset() == pcOffset
);
441 ICEntry
* ICScript::interpreterICEntryFromPCOffset(uint32_t pcOffset
) {
442 // We have to return the entry to store in BaselineFrame::interpreterICEntry
443 // when resuming in the Baseline Interpreter at pcOffset. The bytecode op at
444 // pcOffset does not necessarily have an ICEntry, so we want to return the
445 // first ICEntry for which the following is true:
447 // entry.pcOffset() >= pcOffset
449 // Fortunately, ComputeBinarySearchMid returns exactly this entry.
452 ComputeBinarySearchMid(FallbackStubs(this), pcOffset
, &mid
);
454 if (mid
< numICEntries()) {
455 ICEntry
& entry
= icEntry(mid
);
456 MOZ_ASSERT(fallbackStubForICEntry(&entry
)->pcOffset() >= pcOffset
);
460 // Resuming at a pc after the last ICEntry. Just return nullptr:
461 // BaselineFrame::interpreterICEntry will never be used in this case.
465 void JitScript::purgeInactiveICScripts() {
466 if (!hasInliningRoot()) {
470 forEachICScript([](ICScript
* script
) { script
->purgeInactiveICScripts(); });
472 inliningRoot()->purgeInactiveICScripts();
473 if (inliningRoot()->numInlinedScripts() == 0) {
474 inliningRoot_
.reset();
475 icScript()->inliningRoot_
= nullptr;
477 // If a callee script is active on the stack, the root script must be active
479 MOZ_ASSERT(icScript()->active());
483 void JitScript::purgeStubs(JSScript
* script
, ICStubSpace
& newStubSpace
) {
484 MOZ_ASSERT(script
->jitScript() == this);
486 Zone
* zone
= script
->zone();
487 if (IsAboutToBeFinalizedUnbarriered(script
)) {
488 // We're sweeping and the script is dead. Don't purge optimized stubs
489 // because (1) accessing CacheIRStubInfo pointers in ICStubs is invalid
490 // because we may have swept them already when we started (incremental)
491 // sweeping and (2) it's unnecessary because this script will be finalized
496 JitSpew(JitSpew_BaselineIC
, "Purging optimized stubs");
499 [&](ICScript
* script
) { script
->purgeStubs(zone
, newStubSpace
); });
504 void ICScript::purgeStubs(Zone
* zone
, ICStubSpace
& newStubSpace
) {
505 for (size_t i
= 0; i
< numICEntries(); i
++) {
506 ICEntry
& entry
= icEntry(i
);
507 ICFallbackStub
* fallback
= fallbackStub(i
);
509 // If this is a trial inlining call site and the callee's ICScript hasn't
510 // been discarded, clone the IC chain instead of purging stubs. In this case
511 // both the current ICScript and the callee's inlined ICScript must be
512 // active on the stack.
514 // We can't purge the IC stubs in this case because it'd confuse trial
515 // inlining if we try to inline again later and we already have an ICScript
516 // for this call site.
517 if (fallback
->trialInliningState() == TrialInliningState::Inlined
&&
518 hasInlinedChild(fallback
->pcOffset())) {
519 MOZ_ASSERT(active());
521 // The callee script must be active. Also assert its bytecode size field
522 // is valid, because this helps catch memory safety issues (bug 1871947).
523 ICScript
* callee
= findInlinedChild(fallback
->pcOffset());
524 MOZ_ASSERT(callee
->active());
525 MOZ_ASSERT(callee
->bytecodeSize() < inliningRoot()->totalBytecodeSize());
528 JSRuntime
* rt
= zone
->runtimeFromMainThread();
529 ICCacheIRStub
* prev
= nullptr;
530 ICStub
* stub
= entry
.firstStub();
531 while (stub
!= fallback
) {
532 ICCacheIRStub
* clone
= stub
->toCacheIRStub()->clone(rt
, newStubSpace
);
534 prev
->setNext(clone
);
536 entry
.setFirstStub(clone
);
538 MOZ_ASSERT(stub
->toCacheIRStub()->next() == clone
->next());
540 stub
= clone
->next();
545 MOZ_ASSERT(!hasInlinedChild(fallback
->pcOffset()));
547 fallback
->discardStubs(zone
, &entry
);
548 fallback
->state().reset();
552 bool JitScript::ensureHasCachedBaselineJitData(JSContext
* cx
,
553 HandleScript script
) {
554 if (templateEnv_
.isSome()) {
558 if (!script
->function() ||
559 !script
->function()->needsFunctionEnvironmentObjects()) {
560 templateEnv_
.emplace();
564 Rooted
<EnvironmentObject
*> templateEnv(cx
);
565 Rooted
<JSFunction
*> fun(cx
, script
->function());
567 if (fun
->needsNamedLambdaEnvironment()) {
568 templateEnv
= NamedLambdaObject::createTemplateObject(cx
, fun
);
574 if (fun
->needsCallObject()) {
575 templateEnv
= CallObject::createTemplateObject(cx
, script
, templateEnv
);
581 templateEnv_
.emplace(templateEnv
);
585 bool JitScript::ensureHasCachedIonData(JSContext
* cx
, HandleScript script
) {
586 MOZ_ASSERT(script
->jitScript() == this);
588 if (usesEnvironmentChain_
.isSome()) {
592 if (!ensureHasCachedBaselineJitData(cx
, script
)) {
596 usesEnvironmentChain_
.emplace(ScriptUsesEnvironmentChain(script
));
600 void JitScript::setBaselineScriptImpl(JSScript
* script
,
601 BaselineScript
* baselineScript
) {
602 JSRuntime
* rt
= script
->runtimeFromMainThread();
603 setBaselineScriptImpl(rt
->gcContext(), script
, baselineScript
);
606 void JitScript::setBaselineScriptImpl(JS::GCContext
* gcx
, JSScript
* script
,
607 BaselineScript
* baselineScript
) {
608 if (hasBaselineScript()) {
609 gcx
->removeCellMemory(script
, baselineScript_
->allocBytes(),
610 MemoryUse::BaselineScript
);
611 baselineScript_
.set(script
->zone(), nullptr);
614 MOZ_ASSERT(ionScript_
== nullptr || ionScript_
== IonDisabledScriptPtr
);
616 baselineScript_
.set(script
->zone(), baselineScript
);
617 if (hasBaselineScript()) {
618 AddCellMemory(script
, baselineScript_
->allocBytes(),
619 MemoryUse::BaselineScript
);
622 script
->resetWarmUpResetCounter();
623 script
->updateJitCodeRaw(gcx
->runtime());
626 void JitScript::setIonScriptImpl(JSScript
* script
, IonScript
* ionScript
) {
627 JSRuntime
* rt
= script
->runtimeFromMainThread();
628 setIonScriptImpl(rt
->gcContext(), script
, ionScript
);
631 void JitScript::setIonScriptImpl(JS::GCContext
* gcx
, JSScript
* script
,
632 IonScript
* ionScript
) {
633 MOZ_ASSERT_IF(ionScript
!= IonDisabledScriptPtr
,
634 !baselineScript()->hasPendingIonCompileTask());
636 JS::Zone
* zone
= script
->zone();
637 if (hasIonScript()) {
638 gcx
->removeCellMemory(script
, ionScript_
->allocBytes(),
639 MemoryUse::IonScript
);
640 ionScript_
.set(zone
, nullptr);
643 ionScript_
.set(zone
, ionScript
);
644 MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript());
645 if (hasIonScript()) {
646 AddCellMemory(script
, ionScript_
->allocBytes(), MemoryUse::IonScript
);
649 script
->updateJitCodeRaw(gcx
->runtime());
652 #ifdef JS_STRUCTURED_SPEW
653 static bool HasEnteredCounters(ICEntry
& entry
) {
654 ICStub
* stub
= entry
.firstStub();
655 if (stub
&& !stub
->isFallback()) {
661 void jit::JitSpewBaselineICStats(JSScript
* script
, const char* dumpReason
) {
662 MOZ_ASSERT(script
->hasJitScript());
663 JSContext
* cx
= TlsContext
.get();
664 AutoStructuredSpewer
spew(cx
, SpewChannel::BaselineICStats
, script
);
669 JitScript
* jitScript
= script
->jitScript();
670 spew
->property("reason", dumpReason
);
671 spew
->beginListProperty("entries");
672 for (size_t i
= 0; i
< jitScript
->numICEntries(); i
++) {
673 ICEntry
& entry
= jitScript
->icEntry(i
);
674 ICFallbackStub
* fallback
= jitScript
->fallbackStub(i
);
675 if (!HasEnteredCounters(entry
)) {
679 uint32_t pcOffset
= fallback
->pcOffset();
680 jsbytecode
* pc
= script
->offsetToPC(pcOffset
);
682 JS::LimitedColumnNumberOneOrigin column
;
683 unsigned int line
= PCToLineNumber(script
, pc
, &column
);
686 spew
->property("op", CodeName(JSOp(*pc
)));
687 spew
->property("pc", pcOffset
);
688 spew
->property("line", line
);
689 spew
->property("column", column
.oneOriginValue());
691 spew
->beginListProperty("counts");
692 ICStub
* stub
= entry
.firstStub();
693 while (stub
&& !stub
->isFallback()) {
694 uint32_t count
= stub
->enteredCount();
696 stub
= stub
->toCacheIRStub()->next();
699 spew
->property("fallback_count", fallback
->enteredCount());
706 static void MarkActiveICScriptsAndCopyStubs(
707 JSContext
* cx
, const JitActivationIterator
& activation
,
708 ICStubSpace
& newStubSpace
) {
709 for (OnlyJSJitFrameIter
iter(activation
); !iter
.done(); ++iter
) {
710 const JSJitFrameIter
& frame
= iter
.frame();
711 switch (frame
.type()) {
712 case FrameType::BaselineJS
:
713 frame
.script()->jitScript()->icScript()->setActive();
714 // If the frame is using a trial-inlining ICScript, we have to preserve
716 if (frame
.baselineFrame()->icScript()->isInlined()) {
717 frame
.baselineFrame()->icScript()->setActive();
720 case FrameType::BaselineStub
: {
721 auto* layout
= reinterpret_cast<BaselineStubFrameLayout
*>(frame
.fp());
722 if (layout
->maybeStubPtr() && !layout
->maybeStubPtr()->isFallback()) {
723 ICCacheIRStub
* stub
= layout
->maybeStubPtr()->toCacheIRStub();
724 ICCacheIRStub
* newStub
= stub
->clone(cx
->runtime(), newStubSpace
);
725 layout
->setStubPtr(newStub
);
727 // If this is a trial-inlining call site, also preserve the callee
728 // ICScript. Inlined constructor calls invoke CreateThisFromIC (which
729 // can trigger GC) before using the inlined ICScript.
730 JSJitFrameIter
parentFrame(frame
);
732 BaselineFrame
* blFrame
= parentFrame
.baselineFrame();
734 parentFrame
.baselineScriptAndPc(nullptr, &pc
);
735 uint32_t pcOffset
= blFrame
->script()->pcToOffset(pc
);
736 if (blFrame
->icScript()->hasInlinedChild(pcOffset
)) {
737 blFrame
->icScript()->findInlinedChild(pcOffset
)->setActive();
742 case FrameType::Exit
:
743 if (frame
.exitFrame()->is
<LazyLinkExitFrameLayout
>()) {
744 LazyLinkExitFrameLayout
* ll
=
745 frame
.exitFrame()->as
<LazyLinkExitFrameLayout
>();
747 ScriptFromCalleeToken(ll
->jsFrame()->calleeToken());
748 script
->jitScript()->icScript()->setActive();
751 case FrameType::Bailout
:
752 case FrameType::IonJS
: {
753 // Keep the JitScript and BaselineScript around, since bailouts from
754 // the ion jitcode need to re-enter into the Baseline code.
755 frame
.script()->jitScript()->icScript()->setActive();
756 for (InlineFrameIterator
inlineIter(cx
, &frame
); inlineIter
.more();
758 inlineIter
.script()->jitScript()->icScript()->setActive();
760 // Because we're purging ICScripts, the bailout machinery should use
761 // the generic ICScript for inlined callees.
762 frame
.ionScript()->notePurgedICScripts();
770 void jit::MarkActiveICScriptsAndCopyStubs(Zone
* zone
,
771 ICStubSpace
& newStubSpace
) {
772 if (zone
->isAtomsZone()) {
775 JSContext
* cx
= TlsContext
.get();
776 for (JitActivationIterator
iter(cx
); !iter
.done(); ++iter
) {
777 if (iter
->compartment()->zone() == zone
) {
778 MarkActiveICScriptsAndCopyStubs(cx
, iter
, newStubSpace
);
783 InliningRoot
* JitScript::getOrCreateInliningRoot(JSContext
* cx
,
785 if (!inliningRoot_
) {
786 inliningRoot_
= js::MakeUnique
<InliningRoot
>(cx
, script
);
787 if (!inliningRoot_
) {
788 ReportOutOfMemory(cx
);
791 icScript_
.inliningRoot_
= inliningRoot_
.get();
793 return inliningRoot_
.get();
796 gc::AllocSite
* ICScript::getOrCreateAllocSite(JSScript
* outerScript
,
798 // The script must be the outer script.
799 MOZ_ASSERT(outerScript
->jitScript()->icScript() == this ||
800 (inliningRoot() && inliningRoot()->owningScript() == outerScript
));
802 // The pcOffset must be for this (maybe inlined) script.
803 MOZ_ASSERT(pcOffset
< bytecodeSize());
805 for (gc::AllocSite
* site
: allocSites_
) {
806 if (site
->pcOffset() == pcOffset
) {
807 MOZ_ASSERT(site
->isNormal());
808 MOZ_ASSERT(site
->script() == outerScript
);
809 MOZ_ASSERT(site
->traceKind() == JS::TraceKind::Object
);
814 Nursery
& nursery
= outerScript
->runtimeFromMainThread()->gc
.nursery();
815 if (!nursery
.canCreateAllocSite()) {
816 // Don't block attaching an optimized stub, but don't process allocations
818 return outerScript
->zone()->unknownAllocSite(JS::TraceKind::Object
);
821 if (!allocSites_
.reserve(allocSites_
.length() + 1)) {
825 auto* site
= allocSitesSpace_
.new_
<gc::AllocSite
>(
826 outerScript
->zone(), outerScript
, pcOffset
, JS::TraceKind::Object
);
831 allocSites_
.infallibleAppend(site
);
833 nursery
.noteAllocSiteCreated();
838 bool JitScript::resetAllocSites(bool resetNurserySites
,
839 bool resetPretenuredSites
) {
840 MOZ_ASSERT(resetNurserySites
|| resetPretenuredSites
);
842 bool anyReset
= false;
844 forEachICScript([&](ICScript
* script
) {
845 for (gc::AllocSite
* site
: script
->allocSites_
) {
846 if ((resetNurserySites
&& site
->initialHeap() == gc::Heap::Default
) ||
847 (resetPretenuredSites
&& site
->initialHeap() == gc::Heap::Tenured
)) {
848 if (site
->maybeResetState()) {
858 void JitScript::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
,
859 size_t* data
, size_t* allocSites
) const {
860 *data
+= mallocSizeOf(this);
862 forEachICScript([=](const ICScript
* script
) {
863 // |data| already includes the outer ICScript because it's part of the
865 if (script
!= &icScript_
) {
866 *data
+= mallocSizeOf(script
);
869 // |data| already includes the LifoAlloc and Vector, so use
870 // sizeOfExcludingThis.
871 *allocSites
+= script
->allocSitesSpace_
.sizeOfExcludingThis(mallocSizeOf
);
872 *allocSites
+= script
->allocSites_
.sizeOfExcludingThis(mallocSizeOf
);
876 JitScript
* ICScript::outerJitScript() {
877 MOZ_ASSERT(!isInlined());
878 uint8_t* ptr
= reinterpret_cast<uint8_t*>(this);
879 return reinterpret_cast<JitScript
*>(ptr
- JitScript::offsetOfICScript());
883 // This hash is used to verify that we do not recompile after a
884 // TranspiledCacheIR invalidation with the exact same ICs.
886 // It should change iff an ICEntry in this ICScript (or an ICScript
887 // inlined into this ICScript) is modified such that we will make a
888 // different decision in WarpScriptOracle::maybeInlineIC. This means:
890 // 1. The hash will change if we attach a new stub.
891 // 2. The hash will change if the entered count of any CacheIR stub
892 // other than the first changes from 0.
893 // 3. The hash will change if the entered count of the fallback stub
895 // 4. The hash will change if the failure count of the fallback stub
897 HashNumber
ICScript::hash() {
899 for (size_t i
= 0; i
< numICEntries(); i
++) {
900 ICStub
* stub
= icEntry(i
).firstStub();
902 // Hash the address of the first stub.
903 h
= mozilla::AddToHash(h
, stub
);
905 // Hash whether subsequent stubs have entry count 0.
906 if (!stub
->isFallback()) {
907 stub
= stub
->toCacheIRStub()->next();
908 while (!stub
->isFallback()) {
909 h
= mozilla::AddToHash(h
, stub
->enteredCount() == 0);
910 stub
= stub
->toCacheIRStub()->next();
914 // Hash whether the fallback has entry count 0 and failure count 0.
915 MOZ_ASSERT(stub
->isFallback());
916 h
= mozilla::AddToHash(h
, stub
->enteredCount() == 0);
917 h
= mozilla::AddToHash(h
, stub
->toFallbackStub()->state().hasFailures());