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 #include "mozilla/IntegerPrintfMacros.h"
12 #include "mozilla/ScopeExit.h"
13
14 #include <utility>
15
16 #include "jit/BaselineIC.h"
17 #include "jit/BytecodeAnalysis.h"
18 #include "jit/IonScript.h"
19 #include "jit/JitFrames.h"
20 #include "jit/ScriptFromCalleeToken.h"
21 #include "util/Memory.h"
22 #include "vm/BytecodeIterator.h"
23 #include "vm/BytecodeLocation.h"
24 #include "vm/BytecodeUtil.h"
25 #include "vm/FrameIter.h" // js::OnlyJSJitFrameIter
26 #include "vm/JSScript.h"
27 #include "vm/Stack.h"
28 #include "wasm/WasmInstance.h"
29
30 #include "gc/FreeOp-inl.h"
31 #include "jit/JSJitFrameIter-inl.h"
32 #include "vm/BytecodeIterator-inl.h"
33 #include "vm/BytecodeLocation-inl.h"
34 #include "vm/JSScript-inl.h"
35
36 using namespace js;
37 using namespace js::jit;
38
39 using mozilla::CheckedInt;
40
JitScript(JSScript * script,Offset fallbackStubsOffset,Offset endOffset,const char * profileString)41 JitScript::JitScript(JSScript* script, Offset fallbackStubsOffset,
42 Offset endOffset, const char* profileString)
43 : profileString_(profileString),
44 endOffset_(endOffset),
45 icScript_(script->getWarmUpCount(),
46 fallbackStubsOffset - offsetOfICScript(),
47 endOffset - offsetOfICScript(),
48 /*depth=*/0) {
49 // Ensure the baselineScript_ and ionScript_ fields match the BaselineDisabled
50 // and IonDisabled script flags.
51 if (!script->canBaselineCompile()) {
52 setBaselineScriptImpl(script, BaselineDisabledScriptPtr);
53 }
54 if (!script->canIonCompile()) {
55 setIonScriptImpl(script, IonDisabledScriptPtr);
56 }
57 }
58
createJitScript(JSContext * cx)59 bool JSScript::createJitScript(JSContext* cx) {
60 MOZ_ASSERT(!hasJitScript());
61 cx->check(this);
62
63 // Scripts with a JitScript can run in the Baseline Interpreter. Make sure
64 // we don't create a JitScript for scripts we shouldn't Baseline interpret.
65 MOZ_ASSERT_IF(IsBaselineInterpreterEnabled(),
66 CanBaselineInterpretScript(this));
67
68 // Store the profile string in the JitScript if the profiler is enabled.
69 const char* profileString = nullptr;
70 if (cx->runtime()->geckoProfiler().enabled()) {
71 profileString = cx->runtime()->geckoProfiler().profileString(cx, this);
72 if (!profileString) {
73 return false;
74 }
75 }
76
77 static_assert(sizeof(JitScript) % sizeof(uintptr_t) == 0,
78 "Trailing arrays must be aligned properly");
79 static_assert(sizeof(ICEntry) % sizeof(uintptr_t) == 0,
80 "Trailing arrays must be aligned properly");
81
82 static_assert(
83 sizeof(JitScript) == offsetof(JitScript, icScript_) + sizeof(ICScript),
84 "icScript_ must be the last field");
85
86 // Calculate allocation size.
87 CheckedInt<uint32_t> allocSize = sizeof(JitScript);
88 allocSize += CheckedInt<uint32_t>(numICEntries()) * sizeof(ICEntry);
89 allocSize += CheckedInt<uint32_t>(numICEntries()) * sizeof(ICFallbackStub);
90 if (!allocSize.isValid()) {
91 ReportAllocationOverflow(cx);
92 return false;
93 }
94
95 void* raw = cx->pod_malloc<uint8_t>(allocSize.value());
96 MOZ_ASSERT(uintptr_t(raw) % alignof(JitScript) == 0);
97 if (!raw) {
98 return false;
99 }
100
101 size_t fallbackStubsOffset =
102 sizeof(JitScript) + numICEntries() * sizeof(ICEntry);
103
104 UniquePtr<JitScript> jitScript(new (raw) JitScript(
105 this, fallbackStubsOffset, allocSize.value(), profileString));
106
107 // Sanity check the length computation.
108 MOZ_ASSERT(jitScript->numICEntries() == numICEntries());
109
110 jitScript->icScript()->initICEntries(cx, this);
111
112 warmUpData_.initJitScript(jitScript.release());
113 AddCellMemory(this, allocSize.value(), MemoryUse::JitScript);
114
115 // We have a JitScript so we can set the script's jitCodeRaw pointer to the
116 // Baseline Interpreter code.
117 updateJitCodeRaw(cx->runtime());
118
119 return true;
120 }
121
maybeReleaseJitScript(JSFreeOp * fop)122 void JSScript::maybeReleaseJitScript(JSFreeOp* fop) {
123 MOZ_ASSERT(hasJitScript());
124
125 if (zone()->jitZone()->keepJitScripts() || jitScript()->hasBaselineScript() ||
126 jitScript()->active()) {
127 return;
128 }
129
130 releaseJitScript(fop);
131 }
132
releaseJitScript(JSFreeOp * fop)133 void JSScript::releaseJitScript(JSFreeOp* fop) {
134 MOZ_ASSERT(hasJitScript());
135 MOZ_ASSERT(!hasBaselineScript());
136 MOZ_ASSERT(!hasIonScript());
137
138 fop->removeCellMemory(this, jitScript()->allocBytes(), MemoryUse::JitScript);
139
140 JitScript::Destroy(zone(), jitScript());
141 warmUpData_.clearJitScript();
142 updateJitCodeRaw(fop->runtime());
143 }
144
releaseJitScriptOnFinalize(JSFreeOp * fop)145 void JSScript::releaseJitScriptOnFinalize(JSFreeOp* fop) {
146 MOZ_ASSERT(hasJitScript());
147
148 if (hasIonScript()) {
149 IonScript* ion = jitScript()->clearIonScript(fop, this);
150 jit::IonScript::Destroy(fop, ion);
151 }
152
153 if (hasBaselineScript()) {
154 BaselineScript* baseline = jitScript()->clearBaselineScript(fop, this);
155 jit::BaselineScript::Destroy(fop, baseline);
156 }
157
158 releaseJitScript(fop);
159 }
160
trace(JSTracer * trc)161 void JitScript::CachedIonData::trace(JSTracer* trc) {
162 TraceNullableEdge(trc, &templateEnv, "jitscript-iondata-template-env");
163 }
164
trace(JSTracer * trc)165 void JitScript::trace(JSTracer* trc) {
166 icScript_.trace(trc);
167
168 if (hasBaselineScript()) {
169 baselineScript()->trace(trc);
170 }
171
172 if (hasIonScript()) {
173 ionScript()->trace(trc);
174 }
175
176 if (hasCachedIonData()) {
177 cachedIonData().trace(trc);
178 }
179
180 if (hasInliningRoot()) {
181 inliningRoot()->trace(trc);
182 }
183 }
184
trace(JSTracer * trc)185 void ICScript::trace(JSTracer* trc) {
186 // Mark all IC stub codes hanging off the IC stub entries.
187 for (size_t i = 0; i < numICEntries(); i++) {
188 ICEntry& ent = icEntry(i);
189 ent.trace(trc);
190 }
191 }
192
addInlinedChild(JSContext * cx,UniquePtr<ICScript> child,uint32_t pcOffset)193 bool ICScript::addInlinedChild(JSContext* cx, UniquePtr<ICScript> child,
194 uint32_t pcOffset) {
195 MOZ_ASSERT(!hasInlinedChild(pcOffset));
196
197 if (!inlinedChildren_) {
198 inlinedChildren_ = cx->make_unique<Vector<CallSite>>(cx);
199 if (!inlinedChildren_) {
200 return false;
201 }
202 }
203
204 // First reserve space in inlinedChildren_ to ensure that if the ICScript is
205 // added to the inlining root, it can also be added to inlinedChildren_.
206 CallSite callsite(child.get(), pcOffset);
207 if (!inlinedChildren_->reserve(inlinedChildren_->length() + 1)) {
208 return false;
209 }
210 if (!inliningRoot()->addInlinedScript(std::move(child))) {
211 return false;
212 }
213 inlinedChildren_->infallibleAppend(callsite);
214 return true;
215 }
216
findInlinedChild(uint32_t pcOffset)217 ICScript* ICScript::findInlinedChild(uint32_t pcOffset) {
218 for (auto& callsite : *inlinedChildren_) {
219 if (callsite.pcOffset_ == pcOffset) {
220 return callsite.callee_;
221 }
222 }
223 MOZ_CRASH("Inlined child expected at pcOffset");
224 }
225
removeInlinedChild(uint32_t pcOffset)226 void ICScript::removeInlinedChild(uint32_t pcOffset) {
227 MOZ_ASSERT(inliningRoot());
228 inlinedChildren_->eraseIf([pcOffset](const CallSite& callsite) -> bool {
229 return callsite.pcOffset_ == pcOffset;
230 });
231 }
232
hasInlinedChild(uint32_t pcOffset)233 bool ICScript::hasInlinedChild(uint32_t pcOffset) {
234 if (!inlinedChildren_) {
235 return false;
236 }
237 for (auto& callsite : *inlinedChildren_) {
238 if (callsite.pcOffset_ == pcOffset) {
239 return true;
240 }
241 }
242 return false;
243 }
244
resetWarmUpCount(uint32_t count)245 void JitScript::resetWarmUpCount(uint32_t count) {
246 icScript_.resetWarmUpCount(count);
247 if (hasInliningRoot()) {
248 inliningRoot()->resetWarmUpCounts(count);
249 }
250 }
251
ensureProfileString(JSContext * cx,JSScript * script)252 void JitScript::ensureProfileString(JSContext* cx, JSScript* script) {
253 MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled());
254
255 if (profileString_) {
256 return;
257 }
258
259 AutoEnterOOMUnsafeRegion oomUnsafe;
260 profileString_ = cx->runtime()->geckoProfiler().profileString(cx, script);
261 if (!profileString_) {
262 oomUnsafe.crash("Failed to allocate profile string");
263 }
264 }
265
266 /* static */
Destroy(Zone * zone,JitScript * script)267 void JitScript::Destroy(Zone* zone, JitScript* script) {
268 script->prepareForDestruction(zone);
269
270 js_delete(script);
271 }
272
273 struct FallbackStubs {
274 ICScript* const icScript_;
275
FallbackStubsFallbackStubs276 explicit FallbackStubs(ICScript* icScript) : icScript_(icScript) {}
277
numEntriesFallbackStubs278 size_t numEntries() const { return icScript_->numICEntries(); }
operator []FallbackStubs279 ICFallbackStub* operator[](size_t index) const {
280 return icScript_->fallbackStub(index);
281 }
282 };
283
ComputeBinarySearchMid(FallbackStubs stubs,uint32_t pcOffset,size_t * loc)284 static bool ComputeBinarySearchMid(FallbackStubs stubs, uint32_t pcOffset,
285 size_t* loc) {
286 return mozilla::BinarySearchIf(
287 stubs, 0, stubs.numEntries(),
288 [pcOffset](const ICFallbackStub* stub) {
289 if (pcOffset < stub->pcOffset()) {
290 return -1;
291 }
292 if (stub->pcOffset() < pcOffset) {
293 return 1;
294 }
295 return 0;
296 },
297 loc);
298 }
299
icEntryFromPCOffset(uint32_t pcOffset)300 ICEntry& ICScript::icEntryFromPCOffset(uint32_t pcOffset) {
301 size_t mid;
302 MOZ_ALWAYS_TRUE(ComputeBinarySearchMid(FallbackStubs(this), pcOffset, &mid));
303
304 MOZ_ASSERT(mid < numICEntries());
305
306 ICEntry& entry = icEntry(mid);
307 MOZ_ASSERT(fallbackStubForICEntry(&entry)->pcOffset() == pcOffset);
308 return entry;
309 }
310
interpreterICEntryFromPCOffset(uint32_t pcOffset)311 ICEntry* ICScript::interpreterICEntryFromPCOffset(uint32_t pcOffset) {
312 // We have to return the entry to store in BaselineFrame::interpreterICEntry
313 // when resuming in the Baseline Interpreter at pcOffset. The bytecode op at
314 // pcOffset does not necessarily have an ICEntry, so we want to return the
315 // first ICEntry for which the following is true:
316 //
317 // entry.pcOffset() >= pcOffset
318 //
319 // Fortunately, ComputeBinarySearchMid returns exactly this entry.
320
321 size_t mid;
322 ComputeBinarySearchMid(FallbackStubs(this), pcOffset, &mid);
323
324 if (mid < numICEntries()) {
325 ICEntry& entry = icEntry(mid);
326 MOZ_ASSERT(fallbackStubForICEntry(&entry)->pcOffset() >= pcOffset);
327 return &entry;
328 }
329
330 // Resuming at a pc after the last ICEntry. Just return nullptr:
331 // BaselineFrame::interpreterICEntry will never be used in this case.
332 return nullptr;
333 }
334
purgeOptimizedStubs(JSScript * script)335 void JitScript::purgeOptimizedStubs(JSScript* script) {
336 MOZ_ASSERT(script->jitScript() == this);
337
338 Zone* zone = script->zone();
339 if (IsAboutToBeFinalizedUnbarriered(&script)) {
340 // We're sweeping and the script is dead. Don't purge optimized stubs
341 // because (1) accessing CacheIRStubInfo pointers in ICStubs is invalid
342 // because we may have swept them already when we started (incremental)
343 // sweeping and (2) it's unnecessary because this script will be finalized
344 // soon anyway.
345 return;
346 }
347
348 JitSpew(JitSpew_BaselineIC, "Purging optimized stubs");
349
350 icScript()->purgeOptimizedStubs(zone);
351 if (hasInliningRoot()) {
352 inliningRoot()->purgeOptimizedStubs(zone);
353 }
354 #ifdef DEBUG
355 failedICHash_.reset();
356 hasPurgedStubs_ = true;
357 #endif
358 }
359
purgeOptimizedStubs(Zone * zone)360 void ICScript::purgeOptimizedStubs(Zone* zone) {
361 for (size_t i = 0; i < numICEntries(); i++) {
362 ICEntry& entry = icEntry(i);
363 ICStub* lastStub = entry.firstStub();
364 while (!lastStub->isFallback()) {
365 lastStub = lastStub->toCacheIRStub()->next();
366 }
367
368 // Unlink all stubs allocated in the optimized space.
369 ICStub* stub = entry.firstStub();
370 ICCacheIRStub* prev = nullptr;
371
372 while (stub != lastStub) {
373 if (!stub->toCacheIRStub()->allocatedInFallbackSpace()) {
374 lastStub->toFallbackStub()->unlinkStub(zone, &entry, prev,
375 stub->toCacheIRStub());
376 stub = stub->toCacheIRStub()->next();
377 continue;
378 }
379
380 prev = stub->toCacheIRStub();
381 stub = stub->toCacheIRStub()->next();
382 }
383 }
384
385 #ifdef DEBUG
386 // All remaining stubs must be allocated in the fallback space.
387 for (size_t i = 0; i < numICEntries(); i++) {
388 ICEntry& entry = icEntry(i);
389 ICStub* stub = entry.firstStub();
390 while (!stub->isFallback()) {
391 MOZ_ASSERT(stub->toCacheIRStub()->allocatedInFallbackSpace());
392 stub = stub->toCacheIRStub()->next();
393 }
394 }
395 #endif
396 }
397
CachedIonData(EnvironmentObject * templateEnv,IonBytecodeInfo bytecodeInfo)398 JitScript::CachedIonData::CachedIonData(EnvironmentObject* templateEnv,
399 IonBytecodeInfo bytecodeInfo)
400 : templateEnv(templateEnv), bytecodeInfo(bytecodeInfo) {}
401
ensureHasCachedIonData(JSContext * cx,HandleScript script)402 bool JitScript::ensureHasCachedIonData(JSContext* cx, HandleScript script) {
403 MOZ_ASSERT(script->jitScript() == this);
404
405 if (hasCachedIonData()) {
406 return true;
407 }
408
409 Rooted<EnvironmentObject*> templateEnv(cx);
410 if (script->function()) {
411 RootedFunction fun(cx, script->function());
412
413 if (fun->needsNamedLambdaEnvironment()) {
414 templateEnv =
415 NamedLambdaObject::createTemplateObject(cx, fun, gc::TenuredHeap);
416 if (!templateEnv) {
417 return false;
418 }
419 }
420
421 if (fun->needsCallObject()) {
422 templateEnv = CallObject::createTemplateObject(cx, script, templateEnv,
423 gc::TenuredHeap);
424 if (!templateEnv) {
425 return false;
426 }
427 }
428 }
429
430 IonBytecodeInfo bytecodeInfo = AnalyzeBytecodeForIon(cx, script);
431
432 UniquePtr<CachedIonData> data =
433 cx->make_unique<CachedIonData>(templateEnv, bytecodeInfo);
434 if (!data) {
435 return false;
436 }
437
438 cachedIonData_ = std::move(data);
439 return true;
440 }
441
setBaselineScriptImpl(JSScript * script,BaselineScript * baselineScript)442 void JitScript::setBaselineScriptImpl(JSScript* script,
443 BaselineScript* baselineScript) {
444 JSRuntime* rt = script->runtimeFromMainThread();
445 setBaselineScriptImpl(rt->defaultFreeOp(), script, baselineScript);
446 }
447
setBaselineScriptImpl(JSFreeOp * fop,JSScript * script,BaselineScript * baselineScript)448 void JitScript::setBaselineScriptImpl(JSFreeOp* fop, JSScript* script,
449 BaselineScript* baselineScript) {
450 if (hasBaselineScript()) {
451 BaselineScript::preWriteBarrier(script->zone(), baselineScript_);
452 fop->removeCellMemory(script, baselineScript_->allocBytes(),
453 MemoryUse::BaselineScript);
454 baselineScript_ = nullptr;
455 }
456
457 MOZ_ASSERT(ionScript_ == nullptr || ionScript_ == IonDisabledScriptPtr);
458
459 baselineScript_ = baselineScript;
460 if (hasBaselineScript()) {
461 AddCellMemory(script, baselineScript_->allocBytes(),
462 MemoryUse::BaselineScript);
463 }
464
465 script->resetWarmUpResetCounter();
466 script->updateJitCodeRaw(fop->runtime());
467 }
468
setIonScriptImpl(JSScript * script,IonScript * ionScript)469 void JitScript::setIonScriptImpl(JSScript* script, IonScript* ionScript) {
470 JSRuntime* rt = script->runtimeFromMainThread();
471 setIonScriptImpl(rt->defaultFreeOp(), script, ionScript);
472 }
473
setIonScriptImpl(JSFreeOp * fop,JSScript * script,IonScript * ionScript)474 void JitScript::setIonScriptImpl(JSFreeOp* fop, JSScript* script,
475 IonScript* ionScript) {
476 MOZ_ASSERT_IF(ionScript != IonDisabledScriptPtr,
477 !baselineScript()->hasPendingIonCompileTask());
478
479 if (hasIonScript()) {
480 IonScript::preWriteBarrier(script->zone(), ionScript_);
481 fop->removeCellMemory(script, ionScript_->allocBytes(),
482 MemoryUse::IonScript);
483 ionScript_ = nullptr;
484 }
485
486 ionScript_ = ionScript;
487 MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript());
488 if (hasIonScript()) {
489 AddCellMemory(script, ionScript_->allocBytes(), MemoryUse::IonScript);
490 }
491
492 script->updateJitCodeRaw(fop->runtime());
493 }
494
495 #ifdef JS_STRUCTURED_SPEW
HasEnteredCounters(ICEntry & entry)496 static bool HasEnteredCounters(ICEntry& entry) {
497 ICStub* stub = entry.firstStub();
498 if (stub && !stub->isFallback()) {
499 return true;
500 }
501 return false;
502 }
503
JitSpewBaselineICStats(JSScript * script,const char * dumpReason)504 void jit::JitSpewBaselineICStats(JSScript* script, const char* dumpReason) {
505 MOZ_ASSERT(script->hasJitScript());
506 JSContext* cx = TlsContext.get();
507 AutoStructuredSpewer spew(cx, SpewChannel::BaselineICStats, script);
508 if (!spew) {
509 return;
510 }
511
512 JitScript* jitScript = script->jitScript();
513 spew->property("reason", dumpReason);
514 spew->beginListProperty("entries");
515 for (size_t i = 0; i < jitScript->numICEntries(); i++) {
516 ICEntry& entry = jitScript->icEntry(i);
517 ICFallbackStub* fallback = jitScript->fallbackStub(i);
518 if (!HasEnteredCounters(entry)) {
519 continue;
520 }
521
522 uint32_t pcOffset = fallback->pcOffset();
523 jsbytecode* pc = script->offsetToPC(pcOffset);
524
525 unsigned column;
526 unsigned int line = PCToLineNumber(script, pc, &column);
527
528 spew->beginObject();
529 spew->property("op", CodeName(JSOp(*pc)));
530 spew->property("pc", pcOffset);
531 spew->property("line", line);
532 spew->property("column", column);
533
534 spew->beginListProperty("counts");
535 ICStub* stub = entry.firstStub();
536 while (stub && !stub->isFallback()) {
537 uint32_t count = stub->enteredCount();
538 spew->value(count);
539 stub = stub->toCacheIRStub()->next();
540 }
541 spew->endList();
542 spew->property("fallback_count", fallback->enteredCount());
543 spew->endObject();
544 }
545 spew->endList();
546 }
547 #endif
548
MarkActiveJitScripts(JSContext * cx,const JitActivationIterator & activation)549 static void MarkActiveJitScripts(JSContext* cx,
550 const JitActivationIterator& activation) {
551 for (OnlyJSJitFrameIter iter(activation); !iter.done(); ++iter) {
552 const JSJitFrameIter& frame = iter.frame();
553 switch (frame.type()) {
554 case FrameType::BaselineJS:
555 frame.script()->jitScript()->setActive();
556 break;
557 case FrameType::Exit:
558 if (frame.exitFrame()->is<LazyLinkExitFrameLayout>()) {
559 LazyLinkExitFrameLayout* ll =
560 frame.exitFrame()->as<LazyLinkExitFrameLayout>();
561 JSScript* script =
562 ScriptFromCalleeToken(ll->jsFrame()->calleeToken());
563 script->jitScript()->setActive();
564 }
565 break;
566 case FrameType::Bailout:
567 case FrameType::IonJS: {
568 // Keep the JitScript and BaselineScript around, since bailouts from
569 // the ion jitcode need to re-enter into the Baseline code.
570 frame.script()->jitScript()->setActive();
571 for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more();
572 ++inlineIter) {
573 inlineIter.script()->jitScript()->setActive();
574 }
575 break;
576 }
577 default:;
578 }
579 }
580 }
581
MarkActiveJitScripts(Zone * zone)582 void jit::MarkActiveJitScripts(Zone* zone) {
583 if (zone->isAtomsZone()) {
584 return;
585 }
586 JSContext* cx = TlsContext.get();
587 for (JitActivationIterator iter(cx); !iter.done(); ++iter) {
588 if (iter->compartment()->zone() == zone) {
589 MarkActiveJitScripts(cx, iter);
590 }
591 }
592 }
593
getOrCreateInliningRoot(JSContext * cx,JSScript * script)594 InliningRoot* JitScript::getOrCreateInliningRoot(JSContext* cx,
595 JSScript* script) {
596 if (!inliningRoot_) {
597 inliningRoot_ = js::MakeUnique<InliningRoot>(cx, script);
598 if (!inliningRoot_) {
599 ReportOutOfMemory(cx);
600 return nullptr;
601 }
602 icScript_.inliningRoot_ = inliningRoot_.get();
603 }
604 return inliningRoot_.get();
605 }
606
createAllocSite(JSScript * script)607 gc::AllocSite* JitScript::createAllocSite(JSScript* script) {
608 MOZ_ASSERT(script->jitScript() == this);
609
610 Nursery& nursery = script->runtimeFromMainThread()->gc.nursery();
611 if (!nursery.canCreateAllocSite()) {
612 // Don't block attaching an optimized stub, but don't process allocations
613 // for this site.
614 return script->zone()->unknownAllocSite();
615 }
616
617 if (!allocSites_.reserve(allocSites_.length() + 1)) {
618 return nullptr;
619 }
620
621 ICStubSpace* stubSpace = jitScriptStubSpace();
622 auto* site =
623 static_cast<gc::AllocSite*>(stubSpace->alloc(sizeof(gc::AllocSite)));
624 if (!site) {
625 return nullptr;
626 }
627
628 new (site) gc::AllocSite(script->zone(), script);
629
630 allocSites_.infallibleAppend(site);
631
632 nursery.noteAllocSiteCreated();
633
634 return site;
635 }
636
resetAllocSites(bool resetNurserySites,bool resetPretenuredSites)637 bool JitScript::resetAllocSites(bool resetNurserySites,
638 bool resetPretenuredSites) {
639 MOZ_ASSERT(resetNurserySites || resetPretenuredSites);
640
641 bool anyReset = false;
642
643 for (gc::AllocSite* site : allocSites_) {
644 if ((resetNurserySites && site->initialHeap() == gc::DefaultHeap) ||
645 (resetPretenuredSites && site->initialHeap() == gc::TenuredHeap)) {
646 if (site->maybeResetState()) {
647 anyReset = true;
648 }
649 }
650 }
651
652 return anyReset;
653 }
654
jitScriptStubSpace()655 JitScriptICStubSpace* ICScript::jitScriptStubSpace() {
656 if (isInlined()) {
657 return inliningRoot_->jitScriptStubSpace();
658 }
659 return outerJitScript()->jitScriptStubSpace();
660 }
661
outerJitScript()662 JitScript* ICScript::outerJitScript() {
663 MOZ_ASSERT(!isInlined());
664 uint8_t* ptr = reinterpret_cast<uint8_t*>(this);
665 return reinterpret_cast<JitScript*>(ptr - JitScript::offsetOfICScript());
666 }
667
668 #ifdef DEBUG
669 // This hash is used to verify that we do not recompile after a
670 // TranspiledCacheIR invalidation with the exact same ICs.
671 //
672 // It should change iff an ICEntry in this ICScript (or an ICScript
673 // inlined into this ICScript) is modified such that we will make a
674 // different decision in WarpScriptOracle::maybeInlineIC. This means:
675 //
676 // 1. The hash will change if we attach a new stub.
677 // 2. The hash will change if the entered count of any CacheIR stub
678 // other than the first changes from 0.
679 // 3. The hash will change if the entered count of the fallback stub
680 // changes from 0.
681 //
hash()682 HashNumber ICScript::hash() {
683 HashNumber h = 0;
684 for (size_t i = 0; i < numICEntries(); i++) {
685 ICStub* stub = icEntry(i).firstStub();
686
687 // Hash the address of the first stub.
688 h = mozilla::AddToHash(h, stub);
689
690 // Hash whether subsequent stubs have entry count 0.
691 if (!stub->isFallback()) {
692 stub = stub->toCacheIRStub()->next();
693 while (!stub->isFallback()) {
694 h = mozilla::AddToHash(h, stub->enteredCount() == 0);
695 stub = stub->toCacheIRStub()->next();
696 }
697 }
698
699 // Hash whether the fallback has entry count 0.
700 MOZ_ASSERT(stub->isFallback());
701 h = mozilla::AddToHash(h, stub->enteredCount() == 0);
702 }
703
704 if (inlinedChildren_) {
705 for (auto& callsite : *inlinedChildren_) {
706 h = mozilla::AddToHash(h, callsite.callee_->hash());
707 }
708 }
709 return h;
710 }
711 #endif
712