Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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 : #ifndef jit_JitcodeMap_h
8 : #define jit_JitcodeMap_h
9 :
10 : #include "jit/CompactBuffer.h"
11 : #include "jit/CompileInfo.h"
12 : #include "jit/ExecutableAllocator.h"
13 : #include "jit/OptimizationTracking.h"
14 : #include "jit/shared/CodeGenerator-shared.h"
15 :
16 : namespace js {
17 : namespace jit {
18 :
19 : /*
20 : * The Ion jitcode map implements tables to allow mapping from addresses in ion jitcode
21 : * to the list of (JSScript*, jsbytecode*) pairs that are implicitly active in the frame at
22 : * that point in the native code.
23 : *
24 : * To represent this information efficiently, a multi-level table is used.
25 : *
26 : * At the top level, a global splay-tree of JitcodeGlobalEntry describings the mapping for
27 : * each individual IonCode script generated by compiles. The entries are ordered by their
28 : * nativeStartAddr.
29 : *
30 : * Every entry in the table is of fixed size, but there are different entry types,
31 : * distinguished by the kind field.
32 : */
33 :
34 : class JitcodeGlobalTable;
35 : class JitcodeIonTable;
36 : class JitcodeRegionEntry;
37 :
38 : class JitcodeGlobalEntry;
39 :
40 : class JitcodeSkiplistTower
41 : {
42 : public:
43 : static const unsigned MAX_HEIGHT = 32;
44 :
45 : private:
46 : uint8_t height_;
47 : bool isFree_;
48 : JitcodeGlobalEntry* ptrs_[1];
49 :
50 : public:
51 632 : explicit JitcodeSkiplistTower(unsigned height)
52 632 : : height_(height),
53 632 : isFree_(false)
54 : {
55 632 : MOZ_ASSERT(height >= 1 && height <= MAX_HEIGHT);
56 632 : clearPtrs();
57 632 : }
58 :
59 20375 : unsigned height() const {
60 20375 : return height_;
61 : }
62 :
63 : JitcodeGlobalEntry** ptrs(unsigned level) {
64 : return ptrs_;
65 : }
66 :
67 17139 : JitcodeGlobalEntry* next(unsigned level) const {
68 17139 : MOZ_ASSERT(!isFree_);
69 17139 : MOZ_ASSERT(level < height());
70 17139 : return ptrs_[level];
71 : }
72 2469 : void setNext(unsigned level, JitcodeGlobalEntry* entry) {
73 2469 : MOZ_ASSERT(!isFree_);
74 2469 : MOZ_ASSERT(level < height());
75 2469 : ptrs_[level] = entry;
76 2469 : }
77 :
78 : //
79 : // When stored in a free-list, towers use 'ptrs_[0]' to store a
80 : // pointer to the next tower. In this context only, 'ptrs_[0]'
81 : // may refer to a |JitcodeSkiplistTower*| instead of a
82 : // |JitcodeGlobalEntry*|.
83 : //
84 :
85 0 : void addToFreeList(JitcodeSkiplistTower** freeList) {
86 0 : JitcodeSkiplistTower* nextFreeTower = *freeList;
87 0 : MOZ_ASSERT_IF(nextFreeTower, nextFreeTower->isFree_ &&
88 : nextFreeTower->height() == height_);
89 0 : ptrs_[0] = (JitcodeGlobalEntry*) nextFreeTower;
90 0 : isFree_ = true;
91 0 : *freeList = this;
92 0 : }
93 :
94 632 : static JitcodeSkiplistTower* PopFromFreeList(JitcodeSkiplistTower** freeList) {
95 632 : if (!*freeList)
96 632 : return nullptr;
97 :
98 0 : JitcodeSkiplistTower* tower = *freeList;
99 0 : MOZ_ASSERT(tower->isFree_);
100 0 : JitcodeSkiplistTower* nextFreeTower = (JitcodeSkiplistTower*) tower->ptrs_[0];
101 0 : tower->clearPtrs();
102 0 : tower->isFree_ = false;
103 0 : *freeList = nextFreeTower;
104 0 : return tower;
105 : }
106 :
107 632 : static size_t CalculateSize(unsigned height) {
108 632 : MOZ_ASSERT(height >= 1);
109 : return sizeof(JitcodeSkiplistTower) +
110 632 : (sizeof(JitcodeGlobalEntry*) * (height - 1));
111 : }
112 :
113 : private:
114 632 : void clearPtrs() {
115 1883 : for (unsigned i = 0; i < height_; i++)
116 1251 : ptrs_[0] = nullptr;
117 632 : }
118 : };
119 :
120 : class JitcodeGlobalEntry
121 : {
122 : friend class JitcodeGlobalTable;
123 :
124 : public:
125 : enum Kind {
126 : INVALID = 0,
127 : Ion,
128 : Baseline,
129 : IonCache,
130 : Dummy,
131 : Query,
132 : LIMIT
133 : };
134 : JS_STATIC_ASSERT(LIMIT <= 8);
135 :
136 : struct BytecodeLocation {
137 : JSScript* script;
138 : jsbytecode* pc;
139 0 : BytecodeLocation(JSScript* script, jsbytecode* pc) : script(script), pc(pc) {}
140 : };
141 : typedef Vector<BytecodeLocation, 0, SystemAllocPolicy> BytecodeLocationVector;
142 : typedef Vector<const char*, 0, SystemAllocPolicy> ProfileStringVector;
143 :
144 : struct BaseEntry
145 : {
146 : JitCode* jitcode_;
147 : void* nativeStartAddr_;
148 : void* nativeEndAddr_;
149 : uint32_t gen_;
150 : Kind kind_ : 7;
151 :
152 1264 : void init() {
153 1264 : jitcode_ = nullptr;
154 1264 : nativeStartAddr_ = nullptr;
155 1264 : nativeEndAddr_ = nullptr;
156 1264 : gen_ = UINT32_MAX;
157 1264 : kind_ = INVALID;
158 1264 : }
159 :
160 632 : void init(Kind kind, JitCode* code,
161 : void* nativeStartAddr, void* nativeEndAddr)
162 : {
163 632 : MOZ_ASSERT_IF(kind != Query, code);
164 632 : MOZ_ASSERT(nativeStartAddr);
165 632 : MOZ_ASSERT(nativeEndAddr);
166 632 : MOZ_ASSERT(kind > INVALID && kind < LIMIT);
167 632 : jitcode_ = code;
168 632 : nativeStartAddr_ = nativeStartAddr;
169 632 : nativeEndAddr_ = nativeEndAddr;
170 632 : gen_ = UINT32_MAX;
171 632 : kind_ = kind;
172 632 : }
173 :
174 : uint32_t generation() const {
175 : return gen_;
176 : }
177 0 : void setGeneration(uint32_t gen) {
178 0 : gen_ = gen;
179 0 : }
180 0 : bool isSampled(uint32_t currentGen, uint32_t lapCount) {
181 0 : if (gen_ == UINT32_MAX || currentGen == UINT32_MAX)
182 0 : return false;
183 0 : MOZ_ASSERT(currentGen >= gen_);
184 0 : return (currentGen - gen_) <= lapCount;
185 : }
186 :
187 62336 : Kind kind() const {
188 62336 : return kind_;
189 : }
190 0 : JitCode* jitcode() const {
191 0 : return jitcode_;
192 : }
193 84263 : void* nativeStartAddr() const {
194 84263 : return nativeStartAddr_;
195 : }
196 59413 : void* nativeEndAddr() const {
197 59413 : return nativeEndAddr_;
198 : }
199 :
200 36258 : bool startsBelowPointer(void* ptr) const {
201 36258 : return ((uint8_t*)nativeStartAddr()) <= ((uint8_t*) ptr);
202 : }
203 35241 : bool endsAbovePointer(void* ptr) const {
204 35241 : return ((uint8_t*)nativeEndAddr()) > ((uint8_t*) ptr);
205 : }
206 24172 : bool containsPointer(void* ptr) const {
207 24172 : return startsBelowPointer(ptr) && endsAbovePointer(ptr);
208 : }
209 :
210 : template <class ShouldTraceProvider> bool traceJitcode(JSTracer* trc);
211 : bool isJitcodeMarkedFromAnyThread(JSRuntime* rt);
212 : bool isJitcodeAboutToBeFinalized();
213 : };
214 :
215 : struct IonEntry : public BaseEntry
216 : {
217 : // regionTable_ points to the start of the region table within the
218 : // packed map for compile represented by this entry. Since the
219 : // region table occurs at the tail of the memory region, this pointer
220 : // points somewhere inside the region memory space, and not to the start
221 : // of the memory space.
222 : JitcodeIonTable* regionTable_;
223 :
224 : // optsRegionTable_ points to the table within the compact
225 : // optimizations map indexing all regions that have tracked
226 : // optimization attempts. optsTypesTable_ is the tracked typed info
227 : // associated with the attempts vectors; it is the same length as the
228 : // attempts table. optsAttemptsTable_ is the table indexing those
229 : // attempts vectors.
230 : //
231 : // All pointers point into the same block of memory; the beginning of
232 : // the block is optRegionTable_->payloadStart().
233 : const IonTrackedOptimizationsRegionTable* optsRegionTable_;
234 : const IonTrackedOptimizationsTypesTable* optsTypesTable_;
235 : const IonTrackedOptimizationsAttemptsTable* optsAttemptsTable_;
236 :
237 : // The types table above records type sets, which have been gathered
238 : // into one vector here.
239 : IonTrackedTypeVector* optsAllTypes_;
240 :
241 : // Linked list pointers to allow traversing through all entries that
242 : // could possibly contain nursery pointers. Note that the contained
243 : // pointers can be mutated into nursery pointers at any time.
244 : IonEntry* prevNursery_;
245 : IonEntry* nextNursery_;
246 :
247 : struct ScriptNamePair {
248 : JSScript* script;
249 : char* str;
250 : };
251 :
252 : struct SizedScriptList {
253 : uint32_t size;
254 : ScriptNamePair pairs[1];
255 0 : SizedScriptList(uint32_t sz, JSScript** scrs, char** strs) : size(sz) {
256 0 : for (uint32_t i = 0; i < size; i++) {
257 0 : pairs[i].script = scrs[i];
258 0 : pairs[i].str = strs[i];
259 : }
260 0 : }
261 :
262 0 : static uint32_t AllocSizeFor(uint32_t nscripts) {
263 0 : return sizeof(SizedScriptList) + ((nscripts - 1) * sizeof(ScriptNamePair));
264 : }
265 : };
266 :
267 : SizedScriptList* scriptList_;
268 :
269 0 : void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr,
270 : SizedScriptList* scriptList, JitcodeIonTable* regionTable)
271 : {
272 0 : MOZ_ASSERT(scriptList);
273 0 : MOZ_ASSERT(regionTable);
274 0 : BaseEntry::init(Ion, code, nativeStartAddr, nativeEndAddr);
275 0 : regionTable_ = regionTable;
276 0 : scriptList_ = scriptList;
277 0 : optsRegionTable_ = nullptr;
278 0 : optsTypesTable_ = nullptr;
279 0 : optsAllTypes_ = nullptr;
280 0 : optsAttemptsTable_ = nullptr;
281 0 : prevNursery_ = nextNursery_ = nullptr;
282 0 : }
283 :
284 0 : void initTrackedOptimizations(const IonTrackedOptimizationsRegionTable* regionTable,
285 : const IonTrackedOptimizationsTypesTable* typesTable,
286 : const IonTrackedOptimizationsAttemptsTable* attemptsTable,
287 : IonTrackedTypeVector* allTypes)
288 : {
289 0 : optsRegionTable_ = regionTable;
290 0 : optsTypesTable_ = typesTable;
291 0 : optsAttemptsTable_ = attemptsTable;
292 0 : optsAllTypes_ = allTypes;
293 0 : }
294 :
295 0 : SizedScriptList* sizedScriptList() const {
296 0 : return scriptList_;
297 : }
298 :
299 0 : unsigned numScripts() const {
300 0 : return scriptList_->size;
301 : }
302 :
303 0 : JSScript* getScript(unsigned idx) const {
304 0 : MOZ_ASSERT(idx < numScripts());
305 0 : return sizedScriptList()->pairs[idx].script;
306 : }
307 :
308 0 : const char* getStr(unsigned idx) const {
309 0 : MOZ_ASSERT(idx < numScripts());
310 0 : return sizedScriptList()->pairs[idx].str;
311 : }
312 :
313 : void destroy();
314 :
315 0 : JitcodeIonTable* regionTable() const {
316 0 : return regionTable_;
317 : }
318 :
319 : int scriptIndex(JSScript* script) const {
320 : unsigned count = numScripts();
321 : for (unsigned i = 0; i < count; i++) {
322 : if (getScript(i) == script)
323 : return i;
324 : }
325 : return -1;
326 : }
327 :
328 : void* canonicalNativeAddrFor(JSRuntime*rt, void* ptr) const;
329 :
330 : MOZ_MUST_USE bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results,
331 : uint32_t* depth) const;
332 :
333 : uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
334 : uint32_t maxResults) const;
335 :
336 : void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr,
337 : JSScript** script, jsbytecode** pc) const;
338 :
339 0 : bool hasTrackedOptimizations() const {
340 0 : return !!optsRegionTable_;
341 : }
342 :
343 : const IonTrackedOptimizationsRegionTable* trackedOptimizationsRegionTable() const {
344 : MOZ_ASSERT(hasTrackedOptimizations());
345 : return optsRegionTable_;
346 : }
347 :
348 : uint8_t numOptimizationAttempts() const {
349 : MOZ_ASSERT(hasTrackedOptimizations());
350 : return optsAttemptsTable_->numEntries();
351 : }
352 :
353 0 : IonTrackedOptimizationsAttempts trackedOptimizationAttempts(uint8_t index) {
354 0 : MOZ_ASSERT(hasTrackedOptimizations());
355 0 : return optsAttemptsTable_->entry(index);
356 : }
357 :
358 0 : IonTrackedOptimizationsTypeInfo trackedOptimizationTypeInfo(uint8_t index) {
359 0 : MOZ_ASSERT(hasTrackedOptimizations());
360 0 : return optsTypesTable_->entry(index);
361 : }
362 :
363 0 : const IonTrackedTypeVector* allTrackedTypes() {
364 0 : MOZ_ASSERT(hasTrackedOptimizations());
365 0 : return optsAllTypes_;
366 : }
367 :
368 : mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr(
369 : JSRuntime *rt,
370 : void* ptr,
371 : uint32_t* entryOffsetOut);
372 :
373 : void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index,
374 : JS::ForEachTrackedOptimizationAttemptOp& op);
375 : void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index,
376 : IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op);
377 :
378 : template <class ShouldTraceProvider> bool trace(JSTracer* trc);
379 : void sweepChildren();
380 : bool isMarkedFromAnyThread(JSRuntime* rt);
381 : };
382 :
383 : struct BaselineEntry : public BaseEntry
384 : {
385 : JSScript* script_;
386 : const char* str_;
387 :
388 : // Last location that caused Ion to abort compilation and the reason
389 : // therein, if any. Only actionable aborts are tracked. Internal
390 : // errors like OOMs are not.
391 : jsbytecode* ionAbortPc_;
392 : const char* ionAbortMessage_;
393 :
394 627 : void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr,
395 : JSScript* script, const char* str)
396 : {
397 627 : MOZ_ASSERT(script != nullptr);
398 627 : BaseEntry::init(Baseline, code, nativeStartAddr, nativeEndAddr);
399 627 : script_ = script;
400 627 : str_ = str;
401 627 : }
402 :
403 0 : JSScript* script() const {
404 0 : return script_;
405 : }
406 :
407 0 : const char* str() const {
408 0 : return str_;
409 : }
410 :
411 0 : void trackIonAbort(jsbytecode* pc, const char* message) {
412 0 : MOZ_ASSERT(script_->containsPC(pc));
413 0 : MOZ_ASSERT(message);
414 0 : ionAbortPc_ = pc;
415 0 : ionAbortMessage_ = message;
416 0 : }
417 :
418 : bool hadIonAbort() const {
419 : MOZ_ASSERT(!ionAbortPc_ || ionAbortMessage_);
420 : return ionAbortPc_ != nullptr;
421 : }
422 :
423 : void destroy();
424 :
425 : void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const;
426 :
427 : MOZ_MUST_USE bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results,
428 : uint32_t* depth) const;
429 :
430 : uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
431 : uint32_t maxResults) const;
432 :
433 : void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr,
434 : JSScript** script, jsbytecode** pc) const;
435 :
436 : template <class ShouldTraceProvider> bool trace(JSTracer* trc);
437 : void sweepChildren();
438 : bool isMarkedFromAnyThread(JSRuntime* rt);
439 : };
440 :
441 : struct IonCacheEntry : public BaseEntry
442 : {
443 : void* rejoinAddr_;
444 : JS::TrackedOutcome trackedOutcome_;
445 :
446 : void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr,
447 : void* rejoinAddr, JS::TrackedOutcome trackedOutcome)
448 : {
449 : MOZ_ASSERT(rejoinAddr != nullptr);
450 : BaseEntry::init(IonCache, code, nativeStartAddr, nativeEndAddr);
451 : rejoinAddr_ = rejoinAddr;
452 : trackedOutcome_ = trackedOutcome;
453 : }
454 :
455 0 : void* rejoinAddr() const {
456 0 : return rejoinAddr_;
457 : }
458 : JS::TrackedOutcome trackedOutcome() const {
459 : return trackedOutcome_;
460 : }
461 :
462 0 : void destroy() {}
463 :
464 : void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const;
465 :
466 : MOZ_MUST_USE bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results,
467 : uint32_t* depth) const;
468 :
469 : uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
470 : uint32_t maxResults) const;
471 :
472 : void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr,
473 : JSScript** script, jsbytecode** pc) const;
474 :
475 0 : bool hasTrackedOptimizations() const { return true; }
476 : mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr(
477 : JSRuntime *rt,
478 : void* ptr,
479 : uint32_t* entryOffsetOut);
480 : void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index,
481 : JS::ForEachTrackedOptimizationAttemptOp& op);
482 : void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index,
483 : IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op);
484 :
485 : template <class ShouldTraceProvider> bool trace(JSTracer* trc);
486 : void sweepChildren(JSRuntime* rt);
487 : bool isMarkedFromAnyThread(JSRuntime* rt);
488 : };
489 :
490 : // Dummy entries are created for jitcode generated when profiling is not turned on,
491 : // so that they have representation in the global table if they are on the
492 : // stack when profiling is enabled.
493 : struct DummyEntry : public BaseEntry
494 : {
495 5 : void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr) {
496 5 : BaseEntry::init(Dummy, code, nativeStartAddr, nativeEndAddr);
497 5 : }
498 :
499 0 : void destroy() {}
500 :
501 0 : void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const {
502 0 : return nullptr;
503 : }
504 :
505 0 : MOZ_MUST_USE bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results,
506 : uint32_t* depth) const
507 : {
508 0 : return true;
509 : }
510 :
511 0 : uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
512 : uint32_t maxResults) const
513 : {
514 0 : return 0;
515 : }
516 :
517 0 : void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr,
518 : JSScript** script, jsbytecode** pc) const
519 : {
520 0 : *script = nullptr;
521 0 : *pc = nullptr;
522 0 : }
523 : };
524 :
525 : // QueryEntry is never stored in the table, just used for queries
526 : // where an instance of JitcodeGlobalEntry is required to do tree
527 : // lookups.
528 : struct QueryEntry : public BaseEntry
529 : {
530 0 : void init(void* addr) {
531 0 : BaseEntry::init(Query, nullptr, addr, addr);
532 0 : }
533 : uint8_t* addr() const {
534 : return reinterpret_cast<uint8_t*>(nativeStartAddr());
535 : }
536 0 : void destroy() {}
537 : };
538 :
539 : private:
540 : JitcodeSkiplistTower* tower_;
541 :
542 : union {
543 : // Shadowing BaseEntry instance to allow access to base fields
544 : // and type extraction.
545 : BaseEntry base_;
546 :
547 : // The most common entry type: describing jitcode generated by
548 : // Ion main-line code.
549 : IonEntry ion_;
550 :
551 : // Baseline jitcode.
552 : BaselineEntry baseline_;
553 :
554 : // IonCache stubs.
555 : IonCacheEntry ionCache_;
556 :
557 : // Dummy entries.
558 : DummyEntry dummy_;
559 :
560 : // When doing queries on the SplayTree for particular addresses,
561 : // the query addresses are representd using a QueryEntry.
562 : QueryEntry query_;
563 : };
564 :
565 : public:
566 1264 : JitcodeGlobalEntry()
567 1264 : : tower_(nullptr)
568 : {
569 1264 : base_.init();
570 1264 : }
571 :
572 0 : explicit JitcodeGlobalEntry(const IonEntry& ion)
573 0 : : JitcodeGlobalEntry()
574 : {
575 0 : ion_ = ion;
576 0 : }
577 :
578 627 : explicit JitcodeGlobalEntry(const BaselineEntry& baseline)
579 627 : : JitcodeGlobalEntry()
580 : {
581 627 : baseline_ = baseline;
582 627 : }
583 :
584 : explicit JitcodeGlobalEntry(const IonCacheEntry& ionCache)
585 : : JitcodeGlobalEntry()
586 : {
587 : ionCache_ = ionCache;
588 : }
589 :
590 5 : explicit JitcodeGlobalEntry(const DummyEntry& dummy)
591 5 : : JitcodeGlobalEntry()
592 : {
593 5 : dummy_ = dummy;
594 5 : }
595 :
596 0 : explicit JitcodeGlobalEntry(const QueryEntry& query)
597 0 : : JitcodeGlobalEntry()
598 : {
599 0 : query_ = query;
600 0 : }
601 :
602 0 : static JitcodeGlobalEntry MakeQuery(void* ptr) {
603 : QueryEntry query;
604 0 : query.init(ptr);
605 0 : return JitcodeGlobalEntry(query);
606 : }
607 :
608 0 : void destroy() {
609 0 : switch (kind()) {
610 : case Ion:
611 0 : ionEntry().destroy();
612 0 : break;
613 : case Baseline:
614 0 : baselineEntry().destroy();
615 0 : break;
616 : case IonCache:
617 0 : ionCacheEntry().destroy();
618 0 : break;
619 : case Dummy:
620 0 : dummyEntry().destroy();
621 0 : break;
622 : case Query:
623 0 : queryEntry().destroy();
624 0 : break;
625 : default:
626 0 : MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
627 : }
628 0 : }
629 :
630 : JitCode* jitcode() const {
631 : return baseEntry().jitcode();
632 : }
633 48005 : void* nativeStartAddr() const {
634 48005 : return base_.nativeStartAddr();
635 : }
636 24172 : void* nativeEndAddr() const {
637 24172 : return base_.nativeEndAddr();
638 : }
639 :
640 : uint32_t generation() const {
641 : return baseEntry().generation();
642 : }
643 0 : void setGeneration(uint32_t gen) {
644 0 : baseEntry().setGeneration(gen);
645 0 : }
646 0 : void setAsExpired() {
647 0 : baseEntry().setGeneration(UINT32_MAX);
648 0 : }
649 0 : bool isSampled(uint32_t currentGen, uint32_t lapCount) {
650 0 : return baseEntry().isSampled(currentGen, lapCount);
651 : }
652 :
653 12086 : bool startsBelowPointer(void* ptr) const {
654 12086 : return base_.startsBelowPointer(ptr);
655 : }
656 11747 : bool endsAbovePointer(void* ptr) const {
657 11747 : return base_.endsAbovePointer(ptr);
658 : }
659 24172 : bool containsPointer(void* ptr) const {
660 24172 : return base_.containsPointer(ptr);
661 : }
662 :
663 12086 : bool overlapsWith(const JitcodeGlobalEntry& entry) const {
664 : // Catch full containment of |entry| within |this|, and partial overlaps.
665 12086 : if (containsPointer(entry.nativeStartAddr()) || containsPointer(entry.nativeEndAddr()))
666 0 : return true;
667 :
668 : // Catch full containment of |this| within |entry|.
669 12086 : if (startsBelowPointer(entry.nativeEndAddr()) && endsAbovePointer(entry.nativeStartAddr()))
670 0 : return true;
671 :
672 12086 : return false;
673 : }
674 :
675 62336 : Kind kind() const {
676 62336 : return base_.kind();
677 : }
678 :
679 0 : bool isValid() const {
680 0 : return (kind() > INVALID) && (kind() < LIMIT);
681 : }
682 1264 : bool isIon() const {
683 1264 : return kind() == Ion;
684 : }
685 632 : bool isBaseline() const {
686 632 : return kind() == Baseline;
687 : }
688 5 : bool isIonCache() const {
689 5 : return kind() == IonCache;
690 : }
691 5 : bool isDummy() const {
692 5 : return kind() == Dummy;
693 : }
694 60430 : bool isQuery() const {
695 60430 : return kind() == Query;
696 : }
697 :
698 0 : BaseEntry& baseEntry() {
699 0 : MOZ_ASSERT(isValid());
700 0 : return base_;
701 : }
702 0 : IonEntry& ionEntry() {
703 0 : MOZ_ASSERT(isIon());
704 0 : return ion_;
705 : }
706 0 : BaselineEntry& baselineEntry() {
707 0 : MOZ_ASSERT(isBaseline());
708 0 : return baseline_;
709 : }
710 0 : IonCacheEntry& ionCacheEntry() {
711 0 : MOZ_ASSERT(isIonCache());
712 0 : return ionCache_;
713 : }
714 0 : DummyEntry& dummyEntry() {
715 0 : MOZ_ASSERT(isDummy());
716 0 : return dummy_;
717 : }
718 0 : QueryEntry& queryEntry() {
719 0 : MOZ_ASSERT(isQuery());
720 0 : return query_;
721 : }
722 :
723 : const BaseEntry& baseEntry() const {
724 : MOZ_ASSERT(isValid());
725 : return base_;
726 : }
727 0 : const IonEntry& ionEntry() const {
728 0 : MOZ_ASSERT(isIon());
729 0 : return ion_;
730 : }
731 0 : const BaselineEntry& baselineEntry() const {
732 0 : MOZ_ASSERT(isBaseline());
733 0 : return baseline_;
734 : }
735 0 : const IonCacheEntry& ionCacheEntry() const {
736 0 : MOZ_ASSERT(isIonCache());
737 0 : return ionCache_;
738 : }
739 0 : const DummyEntry& dummyEntry() const {
740 0 : MOZ_ASSERT(isDummy());
741 0 : return dummy_;
742 : }
743 : const QueryEntry& queryEntry() const {
744 : MOZ_ASSERT(isQuery());
745 : return query_;
746 : }
747 :
748 0 : void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const {
749 0 : switch (kind()) {
750 : case Ion:
751 0 : return ionEntry().canonicalNativeAddrFor(rt, ptr);
752 : case Baseline:
753 0 : return baselineEntry().canonicalNativeAddrFor(rt, ptr);
754 : case IonCache:
755 0 : return ionCacheEntry().canonicalNativeAddrFor(rt, ptr);
756 : case Dummy:
757 0 : return dummyEntry().canonicalNativeAddrFor(rt, ptr);
758 : default:
759 0 : MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
760 : }
761 : return nullptr;
762 : }
763 :
764 : // Read the inline call stack at a given point in the native code and append into
765 : // the given vector. Innermost (script,pc) pair will be appended first, and
766 : // outermost appended last.
767 : //
768 : // Returns false on memory failure.
769 0 : MOZ_MUST_USE bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results,
770 : uint32_t* depth) const
771 : {
772 0 : switch (kind()) {
773 : case Ion:
774 0 : return ionEntry().callStackAtAddr(rt, ptr, results, depth);
775 : case Baseline:
776 0 : return baselineEntry().callStackAtAddr(rt, ptr, results, depth);
777 : case IonCache:
778 0 : return ionCacheEntry().callStackAtAddr(rt, ptr, results, depth);
779 : case Dummy:
780 0 : return dummyEntry().callStackAtAddr(rt, ptr, results, depth);
781 : default:
782 0 : MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
783 : }
784 : return false;
785 : }
786 :
787 0 : uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
788 : uint32_t maxResults) const
789 : {
790 0 : switch (kind()) {
791 : case Ion:
792 0 : return ionEntry().callStackAtAddr(rt, ptr, results, maxResults);
793 : case Baseline:
794 0 : return baselineEntry().callStackAtAddr(rt, ptr, results, maxResults);
795 : case IonCache:
796 0 : return ionCacheEntry().callStackAtAddr(rt, ptr, results, maxResults);
797 : case Dummy:
798 0 : return dummyEntry().callStackAtAddr(rt, ptr, results, maxResults);
799 : default:
800 0 : MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
801 : }
802 : return false;
803 : }
804 :
805 0 : void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr,
806 : JSScript** script, jsbytecode** pc) const
807 : {
808 0 : switch (kind()) {
809 : case Ion:
810 0 : return ionEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc);
811 : case Baseline:
812 0 : return baselineEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc);
813 : case IonCache:
814 0 : return ionCacheEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc);
815 : case Dummy:
816 0 : return dummyEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc);
817 : default:
818 0 : MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
819 : }
820 : }
821 :
822 : // Figure out the number of the (JSScript*, jsbytecode*) pairs that are active
823 : // at this location.
824 : uint32_t lookupInlineCallDepth(void* ptr);
825 :
826 : // Compare two global entries.
827 : static int compare(const JitcodeGlobalEntry& ent1, const JitcodeGlobalEntry& ent2);
828 12086 : int compareTo(const JitcodeGlobalEntry& other) {
829 12086 : return compare(*this, other);
830 : }
831 :
832 : // Compute a profiling string for a given script.
833 : static char* createScriptString(JSContext* cx, JSScript* script, size_t* length=nullptr);
834 :
835 0 : bool hasTrackedOptimizations() const {
836 0 : switch (kind()) {
837 : case Ion:
838 0 : return ionEntry().hasTrackedOptimizations();
839 : case IonCache:
840 0 : return ionCacheEntry().hasTrackedOptimizations();
841 : case Baseline:
842 : case Dummy:
843 0 : break;
844 : default:
845 0 : MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
846 : }
847 0 : return false;
848 : }
849 :
850 632 : bool canHoldNurseryPointers() const {
851 632 : return isIon() && ionEntry().hasTrackedOptimizations();
852 : }
853 :
854 0 : mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr(
855 : JSRuntime *rt,
856 : void* addr,
857 : uint32_t* entryOffsetOut)
858 : {
859 0 : switch (kind()) {
860 : case Ion:
861 0 : return ionEntry().trackedOptimizationIndexAtAddr(rt, addr, entryOffsetOut);
862 : case IonCache:
863 0 : return ionCacheEntry().trackedOptimizationIndexAtAddr(rt, addr, entryOffsetOut);
864 : case Baseline:
865 : case Dummy:
866 0 : break;
867 : default:
868 0 : MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
869 : }
870 0 : return mozilla::Nothing();
871 : }
872 :
873 0 : void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index,
874 : JS::ForEachTrackedOptimizationAttemptOp& op)
875 : {
876 0 : switch (kind()) {
877 : case Ion:
878 0 : ionEntry().forEachOptimizationAttempt(rt, index, op);
879 0 : return;
880 : case IonCache:
881 0 : ionCacheEntry().forEachOptimizationAttempt(rt, index, op);
882 0 : return;
883 : case Baseline:
884 : case Dummy:
885 0 : break;
886 : default:
887 0 : MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
888 : }
889 : }
890 :
891 0 : void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index,
892 : IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op)
893 : {
894 0 : switch (kind()) {
895 : case Ion:
896 0 : ionEntry().forEachOptimizationTypeInfo(rt, index, op);
897 0 : return;
898 : case IonCache:
899 0 : ionCacheEntry().forEachOptimizationTypeInfo(rt, index, op);
900 0 : return;
901 : case Baseline:
902 : case Dummy:
903 0 : break;
904 : default:
905 0 : MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
906 : }
907 : }
908 :
909 : IonTrackedOptimizationsAttempts trackedOptimizationAttempts(uint8_t index) {
910 : return ionEntry().trackedOptimizationAttempts(index);
911 : }
912 :
913 : IonTrackedOptimizationsTypeInfo trackedOptimizationTypeInfo(uint8_t index) {
914 : return ionEntry().trackedOptimizationTypeInfo(index);
915 : }
916 :
917 : const IonTrackedTypeVector* allTrackedTypes() {
918 : return ionEntry().allTrackedTypes();
919 : }
920 :
921 0 : Zone* zone() {
922 0 : return baseEntry().jitcode()->zone();
923 : }
924 :
925 : template <class ShouldTraceProvider>
926 0 : bool trace(JSTracer* trc) {
927 0 : bool tracedAny = baseEntry().traceJitcode<ShouldTraceProvider>(trc);
928 0 : switch (kind()) {
929 : case Ion:
930 0 : tracedAny |= ionEntry().trace<ShouldTraceProvider>(trc);
931 0 : break;
932 : case Baseline:
933 0 : tracedAny |= baselineEntry().trace<ShouldTraceProvider>(trc);
934 0 : break;
935 : case IonCache:
936 0 : tracedAny |= ionCacheEntry().trace<ShouldTraceProvider>(trc);
937 0 : break;
938 : case Dummy:
939 0 : break;
940 : default:
941 0 : MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
942 : }
943 0 : return tracedAny;
944 : }
945 :
946 0 : void sweepChildren(JSRuntime* rt) {
947 0 : switch (kind()) {
948 : case Ion:
949 0 : ionEntry().sweepChildren();
950 0 : break;
951 : case Baseline:
952 0 : baselineEntry().sweepChildren();
953 0 : break;
954 : case IonCache:
955 0 : ionCacheEntry().sweepChildren(rt);
956 0 : break;
957 : case Dummy:
958 0 : break;
959 : default:
960 0 : MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
961 : }
962 0 : }
963 :
964 0 : bool isMarkedFromAnyThread(JSRuntime* rt) {
965 0 : if (!baseEntry().isJitcodeMarkedFromAnyThread(rt))
966 0 : return false;
967 0 : switch (kind()) {
968 : case Ion:
969 0 : return ionEntry().isMarkedFromAnyThread(rt);
970 : case Baseline:
971 0 : return baselineEntry().isMarkedFromAnyThread(rt);
972 : case IonCache:
973 0 : return ionCacheEntry().isMarkedFromAnyThread(rt);
974 : case Dummy:
975 0 : break;
976 : default:
977 0 : MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
978 : }
979 0 : return true;
980 : }
981 :
982 : //
983 : // When stored in a free-list, entries use 'tower_' to store a
984 : // pointer to the next entry. In this context only, 'tower_'
985 : // may refer to a |JitcodeGlobalEntry*| instead of a
986 : // |JitcodeSkiplistTower*|.
987 : //
988 :
989 0 : void addToFreeList(JitcodeGlobalEntry** freeList) {
990 0 : MOZ_ASSERT(!isValid());
991 :
992 0 : JitcodeGlobalEntry* nextFreeEntry = *freeList;
993 0 : MOZ_ASSERT_IF(nextFreeEntry, !nextFreeEntry->isValid());
994 :
995 0 : tower_ = (JitcodeSkiplistTower*) nextFreeEntry;
996 0 : *freeList = this;
997 0 : }
998 :
999 632 : static JitcodeGlobalEntry* PopFromFreeList(JitcodeGlobalEntry** freeList) {
1000 632 : if (!*freeList)
1001 632 : return nullptr;
1002 :
1003 0 : JitcodeGlobalEntry* entry = *freeList;
1004 0 : MOZ_ASSERT(!entry->isValid());
1005 0 : JitcodeGlobalEntry* nextFreeEntry = (JitcodeGlobalEntry*) entry->tower_;
1006 0 : entry->tower_ = nullptr;
1007 0 : *freeList = nextFreeEntry;
1008 0 : return entry;
1009 : }
1010 : };
1011 :
1012 : /*
1013 : * Global table of JitcodeGlobalEntry values sorted by native address range.
1014 : */
1015 : class JitcodeGlobalTable
1016 : {
1017 : private:
1018 : static const size_t LIFO_CHUNK_SIZE = 16 * 1024;
1019 :
1020 : LifoAlloc alloc_;
1021 : JitcodeGlobalEntry* freeEntries_;
1022 : uint32_t rand_;
1023 : uint32_t skiplistSize_;
1024 : JitcodeGlobalEntry::IonEntry* nurseryEntries_;
1025 :
1026 : JitcodeGlobalEntry* startTower_[JitcodeSkiplistTower::MAX_HEIGHT];
1027 : JitcodeSkiplistTower* freeTowers_[JitcodeSkiplistTower::MAX_HEIGHT];
1028 :
1029 : public:
1030 4 : JitcodeGlobalTable()
1031 4 : : alloc_(LIFO_CHUNK_SIZE), freeEntries_(nullptr), rand_(0), skiplistSize_(0),
1032 4 : nurseryEntries_(nullptr)
1033 : {
1034 132 : for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++)
1035 128 : startTower_[i] = nullptr;
1036 132 : for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++)
1037 128 : freeTowers_[i] = nullptr;
1038 4 : }
1039 0 : ~JitcodeGlobalTable() {}
1040 :
1041 0 : bool empty() const {
1042 0 : return skiplistSize_ == 0;
1043 : }
1044 :
1045 0 : const JitcodeGlobalEntry* lookup(void* ptr) {
1046 0 : return lookupInternal(ptr);
1047 : }
1048 :
1049 0 : JitcodeGlobalEntry& lookupInfallible(void* ptr) {
1050 0 : JitcodeGlobalEntry* entry = lookupInternal(ptr);
1051 0 : MOZ_ASSERT(entry);
1052 0 : return *entry;
1053 : }
1054 :
1055 : const JitcodeGlobalEntry& lookupForSamplerInfallible(void* ptr, JSRuntime* rt,
1056 : uint32_t sampleBufferGen);
1057 :
1058 0 : MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry::IonEntry& entry, JSRuntime* rt) {
1059 0 : return addEntry(JitcodeGlobalEntry(entry), rt);
1060 : }
1061 627 : MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry::BaselineEntry& entry, JSRuntime* rt) {
1062 627 : return addEntry(JitcodeGlobalEntry(entry), rt);
1063 : }
1064 : MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry::IonCacheEntry& entry, JSRuntime* rt) {
1065 : return addEntry(JitcodeGlobalEntry(entry), rt);
1066 : }
1067 5 : MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry::DummyEntry& entry, JSRuntime* rt) {
1068 5 : return addEntry(JitcodeGlobalEntry(entry), rt);
1069 : }
1070 :
1071 : void removeEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt);
1072 : void releaseEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt);
1073 :
1074 : void setAllEntriesAsExpired(JSRuntime* rt);
1075 : void traceForMinorGC(JSTracer* trc);
1076 : MOZ_MUST_USE bool markIteratively(GCMarker* marker);
1077 : void sweep(JSRuntime* rt);
1078 :
1079 : private:
1080 : MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry& entry, JSRuntime* rt);
1081 :
1082 : JitcodeGlobalEntry* lookupInternal(void* ptr);
1083 :
1084 : // Initialize towerOut such that towerOut[i] (for i in [0, MAX_HEIGHT-1])
1085 : // is a JitcodeGlobalEntry that is sorted to be <query, whose successor at
1086 : // level i is either null, or sorted to be >= query.
1087 : //
1088 : // If entry with the given properties does not exist for level i, then
1089 : // towerOut[i] is initialized to nullptr.
1090 : void searchInternal(const JitcodeGlobalEntry& query, JitcodeGlobalEntry** towerOut);
1091 :
1092 : JitcodeGlobalEntry* searchAtHeight(unsigned level, JitcodeGlobalEntry* start,
1093 : const JitcodeGlobalEntry& query);
1094 :
1095 : // Calculate next random tower height.
1096 : unsigned generateTowerHeight();
1097 :
1098 : JitcodeSkiplistTower* allocateTower(unsigned height);
1099 : JitcodeGlobalEntry* allocateEntry();
1100 :
1101 : #ifdef DEBUG
1102 : void verifySkiplist();
1103 : #else
1104 : void verifySkiplist() {}
1105 : #endif
1106 :
1107 0 : void addToNurseryList(JitcodeGlobalEntry::IonEntry* entry) {
1108 0 : MOZ_ASSERT(entry->prevNursery_ == nullptr);
1109 0 : MOZ_ASSERT(entry->nextNursery_ == nullptr);
1110 :
1111 0 : entry->nextNursery_ = nurseryEntries_;
1112 0 : if (nurseryEntries_)
1113 0 : nurseryEntries_->prevNursery_ = entry;
1114 0 : nurseryEntries_ = entry;
1115 0 : }
1116 :
1117 0 : void removeFromNurseryList(JitcodeGlobalEntry::IonEntry* entry) {
1118 : // Splice out of list to be scanned on a minor GC.
1119 0 : if (entry->prevNursery_)
1120 0 : entry->prevNursery_->nextNursery_ = entry->nextNursery_;
1121 0 : if (entry->nextNursery_)
1122 0 : entry->nextNursery_->prevNursery_ = entry->prevNursery_;
1123 :
1124 0 : if (nurseryEntries_ == entry)
1125 0 : nurseryEntries_ = entry->nextNursery_;
1126 :
1127 0 : entry->prevNursery_ = entry->nextNursery_ = nullptr;
1128 0 : }
1129 :
1130 : public:
1131 : class Range
1132 : {
1133 : protected:
1134 : JitcodeGlobalTable& table_;
1135 : JitcodeGlobalEntry* cur_;
1136 :
1137 : public:
1138 0 : explicit Range(JitcodeGlobalTable& table)
1139 0 : : table_(table),
1140 0 : cur_(table.startTower_[0])
1141 0 : { }
1142 :
1143 0 : JitcodeGlobalEntry* front() const {
1144 0 : MOZ_ASSERT(!empty());
1145 0 : return cur_;
1146 : }
1147 :
1148 0 : bool empty() const {
1149 0 : return !cur_;
1150 : }
1151 :
1152 0 : void popFront() {
1153 0 : MOZ_ASSERT(!empty());
1154 0 : cur_ = cur_->tower_->next(0);
1155 0 : }
1156 : };
1157 :
1158 : // An enumerator class that can remove entries as it enumerates. If this
1159 : // functionality is not needed, use Range instead.
1160 : class Enum : public Range
1161 : {
1162 : JSRuntime* rt_;
1163 : JitcodeGlobalEntry* next_;
1164 : JitcodeGlobalEntry* prevTower_[JitcodeSkiplistTower::MAX_HEIGHT];
1165 :
1166 : public:
1167 : Enum(JitcodeGlobalTable& table, JSRuntime* rt);
1168 :
1169 : void popFront();
1170 : void removeFront();
1171 : };
1172 : };
1173 :
1174 :
1175 : /*
1176 : * Container class for main jitcode table.
1177 : * The Region table's memory is structured as follows:
1178 : *
1179 : * +------------------------------------------------+ |
1180 : * | Region 1 Run | |
1181 : * |------------------------------------------------| |
1182 : * | Region 2 Run | |
1183 : * | | |
1184 : * | | |
1185 : * |------------------------------------------------| |
1186 : * | Region 3 Run | |
1187 : * | | |
1188 : * |------------------------------------------------| |-- Payload
1189 : * | | |
1190 : * | ... | |
1191 : * | | |
1192 : * |------------------------------------------------| |
1193 : * | Region M Run | |
1194 : * | | |
1195 : * +================================================+ <- RegionTable pointer points here
1196 : * | uint23_t numRegions = M | |
1197 : * +------------------------------------------------+ |
1198 : * | Region 1 | |
1199 : * | uint32_t entryOffset = size(Payload) | |
1200 : * +------------------------------------------------+ |
1201 : * | | |-- Table
1202 : * | ... | |
1203 : * | | |
1204 : * +------------------------------------------------+ |
1205 : * | Region M | |
1206 : * | uint32_t entryOffset | |
1207 : * +------------------------------------------------+ |
1208 : *
1209 : * The region table is composed of two sections: a tail section that contains a table of
1210 : * fixed-size entries containing offsets into the the head section, and a head section that
1211 : * holds a sequence of variable-sized runs. The table in the tail section serves to
1212 : * locate the variable-length encoded structures in the head section.
1213 : *
1214 : * The entryOffsets in the table indicate the bytes offset to subtract from the regionTable
1215 : * pointer to arrive at the encoded region in the payload.
1216 : *
1217 : *
1218 : * Variable-length entries in payload
1219 : * ----------------------------------
1220 : * The entryOffsets in the region table's fixed-sized entries refer to a location within the
1221 : * variable-length payload section. This location contains a compactly encoded "run" of
1222 : * mappings.
1223 : *
1224 : * Each run starts by describing the offset within the native code it starts at, and the
1225 : * sequence of (JSScript*, jsbytecode*) pairs active at that site. Following that, there
1226 : * are a number of variable-length entries encoding (nativeOffsetDelta, bytecodeOffsetDelta)
1227 : * pairs for the run.
1228 : *
1229 : * VarUint32 nativeOffset;
1230 : * - The offset from nativeStartAddr in the global table entry at which
1231 : * the jitcode for this region starts.
1232 : *
1233 : * Uint8_t scriptDepth;
1234 : * - The depth of inlined scripts for this region.
1235 : *
1236 : * List<VarUint32> inlineScriptPcStack;
1237 : * - We encode (2 * scriptDepth) VarUint32s here. Each pair of uint32s are taken
1238 : * as an index into the scriptList in the global table entry, and a pcOffset
1239 : * respectively.
1240 : *
1241 : * List<NativeAndBytecodeDelta> deltaRun;
1242 : * - The rest of the entry is a deltaRun that stores a series of variable-length
1243 : * encoded NativeAndBytecodeDelta datums.
1244 : */
1245 : class JitcodeRegionEntry
1246 : {
1247 : private:
1248 : static const unsigned MAX_RUN_LENGTH = 100;
1249 :
1250 : public:
1251 : static void WriteHead(CompactBufferWriter& writer,
1252 : uint32_t nativeOffset, uint8_t scriptDepth);
1253 : static void ReadHead(CompactBufferReader& reader,
1254 : uint32_t* nativeOffset, uint8_t* scriptDepth);
1255 :
1256 : static void WriteScriptPc(CompactBufferWriter& writer, uint32_t scriptIdx, uint32_t pcOffset);
1257 : static void ReadScriptPc(CompactBufferReader& reader, uint32_t* scriptIdx, uint32_t* pcOffset);
1258 :
1259 : static void WriteDelta(CompactBufferWriter& writer, uint32_t nativeDelta, int32_t pcDelta);
1260 : static void ReadDelta(CompactBufferReader& reader, uint32_t* nativeDelta, int32_t* pcDelta);
1261 :
1262 : // Given a pointer into an array of NativeToBytecode (and a pointer to the end of the array),
1263 : // compute the number of entries that would be consume by outputting a run starting
1264 : // at this one.
1265 : static uint32_t ExpectedRunLength(const CodeGeneratorShared::NativeToBytecode* entry,
1266 : const CodeGeneratorShared::NativeToBytecode* end);
1267 :
1268 : // Write a run, starting at the given NativeToBytecode entry, into the given buffer writer.
1269 : static MOZ_MUST_USE bool WriteRun(CompactBufferWriter& writer, JSScript** scriptList,
1270 : uint32_t scriptListSize, uint32_t runLength,
1271 : const CodeGeneratorShared::NativeToBytecode* entry);
1272 :
1273 : // Delta Run entry formats are encoded little-endian:
1274 : //
1275 : // byte 0
1276 : // NNNN-BBB0
1277 : // Single byte format. nativeDelta in [0, 15], pcDelta in [0, 7]
1278 : //
1279 : static const uint32_t ENC1_MASK = 0x1;
1280 : static const uint32_t ENC1_MASK_VAL = 0x0;
1281 :
1282 : static const uint32_t ENC1_NATIVE_DELTA_MAX = 0xf;
1283 : static const unsigned ENC1_NATIVE_DELTA_SHIFT = 4;
1284 :
1285 : static const uint32_t ENC1_PC_DELTA_MASK = 0x0e;
1286 : static const int32_t ENC1_PC_DELTA_MAX = 0x7;
1287 : static const unsigned ENC1_PC_DELTA_SHIFT = 1;
1288 :
1289 : // byte 1 byte 0
1290 : // NNNN-NNNN BBBB-BB01
1291 : // Two-byte format. nativeDelta in [0, 255], pcDelta in [0, 63]
1292 : //
1293 : static const uint32_t ENC2_MASK = 0x3;
1294 : static const uint32_t ENC2_MASK_VAL = 0x1;
1295 :
1296 : static const uint32_t ENC2_NATIVE_DELTA_MAX = 0xff;
1297 : static const unsigned ENC2_NATIVE_DELTA_SHIFT = 8;
1298 :
1299 : static const uint32_t ENC2_PC_DELTA_MASK = 0x00fc;
1300 : static const int32_t ENC2_PC_DELTA_MAX = 0x3f;
1301 : static const unsigned ENC2_PC_DELTA_SHIFT = 2;
1302 :
1303 : // byte 2 byte 1 byte 0
1304 : // NNNN-NNNN NNNB-BBBB BBBB-B011
1305 : // Three-byte format. nativeDelta in [0, 2047], pcDelta in [-512, 511]
1306 : //
1307 : static const uint32_t ENC3_MASK = 0x7;
1308 : static const uint32_t ENC3_MASK_VAL = 0x3;
1309 :
1310 : static const uint32_t ENC3_NATIVE_DELTA_MAX = 0x7ff;
1311 : static const unsigned ENC3_NATIVE_DELTA_SHIFT = 13;
1312 :
1313 : static const uint32_t ENC3_PC_DELTA_MASK = 0x001ff8;
1314 : static const int32_t ENC3_PC_DELTA_MAX = 0x1ff;
1315 : static const int32_t ENC3_PC_DELTA_MIN = -ENC3_PC_DELTA_MAX - 1;
1316 : static const unsigned ENC3_PC_DELTA_SHIFT = 3;
1317 :
1318 : // byte 3 byte 2 byte 1 byte 0
1319 : // NNNN-NNNN NNNN-NNNN BBBB-BBBB BBBB-B111
1320 : // Three-byte format. nativeDelta in [0, 65535], pcDelta in [-4096, 4095]
1321 : static const uint32_t ENC4_MASK = 0x7;
1322 : static const uint32_t ENC4_MASK_VAL = 0x7;
1323 :
1324 : static const uint32_t ENC4_NATIVE_DELTA_MAX = 0xffff;
1325 : static const unsigned ENC4_NATIVE_DELTA_SHIFT = 16;
1326 :
1327 : static const uint32_t ENC4_PC_DELTA_MASK = 0x0000fff8;
1328 : static const int32_t ENC4_PC_DELTA_MAX = 0xfff;
1329 : static const int32_t ENC4_PC_DELTA_MIN = -ENC4_PC_DELTA_MAX - 1;
1330 : static const unsigned ENC4_PC_DELTA_SHIFT = 3;
1331 :
1332 0 : static bool IsDeltaEncodeable(uint32_t nativeDelta, int32_t pcDelta) {
1333 0 : return (nativeDelta <= ENC4_NATIVE_DELTA_MAX) &&
1334 0 : (pcDelta >= ENC4_PC_DELTA_MIN) && (pcDelta <= ENC4_PC_DELTA_MAX);
1335 : }
1336 :
1337 : private:
1338 : const uint8_t* data_;
1339 : const uint8_t* end_;
1340 :
1341 : // Unpacked state from jitcode entry.
1342 : uint32_t nativeOffset_;
1343 : uint8_t scriptDepth_;
1344 : const uint8_t* scriptPcStack_;
1345 : const uint8_t* deltaRun_;
1346 :
1347 : void unpack();
1348 :
1349 : public:
1350 0 : JitcodeRegionEntry(const uint8_t* data, const uint8_t* end)
1351 0 : : data_(data), end_(end),
1352 : nativeOffset_(0), scriptDepth_(0),
1353 0 : scriptPcStack_(nullptr), deltaRun_(nullptr)
1354 : {
1355 0 : MOZ_ASSERT(data_ < end_);
1356 0 : unpack();
1357 0 : MOZ_ASSERT(scriptPcStack_ < end_);
1358 0 : MOZ_ASSERT(deltaRun_ <= end_);
1359 0 : }
1360 :
1361 0 : uint32_t nativeOffset() const {
1362 0 : return nativeOffset_;
1363 : }
1364 0 : uint32_t scriptDepth() const {
1365 0 : return scriptDepth_;
1366 : }
1367 :
1368 : class ScriptPcIterator
1369 : {
1370 : private:
1371 : uint32_t count_;
1372 : const uint8_t* start_;
1373 : const uint8_t* end_;
1374 :
1375 : uint32_t idx_;
1376 : const uint8_t* cur_;
1377 :
1378 : public:
1379 0 : ScriptPcIterator(uint32_t count, const uint8_t* start, const uint8_t* end)
1380 0 : : count_(count), start_(start), end_(end), idx_(0), cur_(start_)
1381 0 : {}
1382 :
1383 0 : bool hasMore() const
1384 : {
1385 0 : MOZ_ASSERT((idx_ == count_) == (cur_ == end_));
1386 0 : MOZ_ASSERT((idx_ < count_) == (cur_ < end_));
1387 0 : return cur_ < end_;
1388 : }
1389 :
1390 0 : void readNext(uint32_t* scriptIdxOut, uint32_t* pcOffsetOut)
1391 : {
1392 0 : MOZ_ASSERT(scriptIdxOut);
1393 0 : MOZ_ASSERT(pcOffsetOut);
1394 0 : MOZ_ASSERT(hasMore());
1395 :
1396 0 : CompactBufferReader reader(cur_, end_);
1397 0 : ReadScriptPc(reader, scriptIdxOut, pcOffsetOut);
1398 :
1399 0 : cur_ = reader.currentPosition();
1400 0 : MOZ_ASSERT(cur_ <= end_);
1401 :
1402 0 : idx_++;
1403 0 : MOZ_ASSERT_IF(idx_ == count_, cur_ == end_);
1404 0 : }
1405 :
1406 0 : void reset() {
1407 0 : idx_ = 0;
1408 0 : cur_ = start_;
1409 0 : }
1410 : };
1411 :
1412 0 : ScriptPcIterator scriptPcIterator() const {
1413 : // End of script+pc sequence is the start of the delta run.
1414 0 : return ScriptPcIterator(scriptDepth_, scriptPcStack_, deltaRun_);
1415 : }
1416 :
1417 : class DeltaIterator {
1418 : private:
1419 : const uint8_t* start_;
1420 : const uint8_t* end_;
1421 : const uint8_t* cur_;
1422 :
1423 : public:
1424 0 : DeltaIterator(const uint8_t* start, const uint8_t* end)
1425 0 : : start_(start), end_(end), cur_(start)
1426 0 : {}
1427 :
1428 0 : bool hasMore() const
1429 : {
1430 0 : MOZ_ASSERT(cur_ <= end_);
1431 0 : return cur_ < end_;
1432 : }
1433 :
1434 0 : void readNext(uint32_t* nativeDeltaOut, int32_t* pcDeltaOut)
1435 : {
1436 0 : MOZ_ASSERT(nativeDeltaOut != nullptr);
1437 0 : MOZ_ASSERT(pcDeltaOut != nullptr);
1438 :
1439 0 : MOZ_ASSERT(hasMore());
1440 :
1441 0 : CompactBufferReader reader(cur_, end_);
1442 0 : ReadDelta(reader, nativeDeltaOut, pcDeltaOut);
1443 :
1444 0 : cur_ = reader.currentPosition();
1445 0 : MOZ_ASSERT(cur_ <= end_);
1446 0 : }
1447 :
1448 : void reset() {
1449 : cur_ = start_;
1450 : }
1451 : };
1452 0 : DeltaIterator deltaIterator() const {
1453 0 : return DeltaIterator(deltaRun_, end_);
1454 : }
1455 :
1456 : uint32_t findPcOffset(uint32_t queryNativeOffset, uint32_t startPcOffset) const;
1457 : };
1458 :
1459 : class JitcodeIonTable
1460 : {
1461 : private:
1462 : /* Variable length payload section "below" here. */
1463 : uint32_t numRegions_;
1464 : uint32_t regionOffsets_[1];
1465 :
1466 0 : const uint8_t* payloadEnd() const {
1467 0 : return reinterpret_cast<const uint8_t*>(this);
1468 : }
1469 :
1470 : public:
1471 : explicit JitcodeIonTable(uint32_t numRegions)
1472 : : numRegions_(numRegions)
1473 : {
1474 : for (uint32_t i = 0; i < numRegions; i++)
1475 : regionOffsets_[i] = 0;
1476 : }
1477 :
1478 : MOZ_MUST_USE bool makeIonEntry(JSContext* cx, JitCode* code, uint32_t numScripts,
1479 : JSScript** scripts, JitcodeGlobalEntry::IonEntry& out);
1480 :
1481 0 : uint32_t numRegions() const {
1482 0 : return numRegions_;
1483 : }
1484 :
1485 0 : uint32_t regionOffset(uint32_t regionIndex) const {
1486 0 : MOZ_ASSERT(regionIndex < numRegions());
1487 0 : return regionOffsets_[regionIndex];
1488 : }
1489 :
1490 0 : JitcodeRegionEntry regionEntry(uint32_t regionIndex) const {
1491 0 : const uint8_t* regionStart = payloadEnd() - regionOffset(regionIndex);
1492 0 : const uint8_t* regionEnd = payloadEnd();
1493 0 : if (regionIndex < numRegions_ - 1)
1494 0 : regionEnd -= regionOffset(regionIndex + 1);
1495 0 : return JitcodeRegionEntry(regionStart, regionEnd);
1496 : }
1497 :
1498 : bool regionContainsOffset(uint32_t regionIndex, uint32_t nativeOffset) {
1499 : MOZ_ASSERT(regionIndex < numRegions());
1500 :
1501 : JitcodeRegionEntry ent = regionEntry(regionIndex);
1502 : if (nativeOffset < ent.nativeOffset())
1503 : return false;
1504 :
1505 : if (regionIndex == numRegions_ - 1)
1506 : return true;
1507 :
1508 : return nativeOffset < regionEntry(regionIndex + 1).nativeOffset();
1509 : }
1510 :
1511 : uint32_t findRegionEntry(uint32_t offset) const;
1512 :
1513 0 : const uint8_t* payloadStart() const {
1514 : // The beginning of the payload the beginning of the first region are the same.
1515 0 : return payloadEnd() - regionOffset(0);
1516 : }
1517 :
1518 : static MOZ_MUST_USE bool WriteIonTable(CompactBufferWriter& writer,
1519 : JSScript** scriptList, uint32_t scriptListSize,
1520 : const CodeGeneratorShared::NativeToBytecode* start,
1521 : const CodeGeneratorShared::NativeToBytecode* end,
1522 : uint32_t* tableOffsetOut, uint32_t* numRegionsOut);
1523 : };
1524 :
1525 :
1526 : } // namespace jit
1527 : } // namespace js
1528 :
1529 : #endif /* jit_JitcodeMap_h */
|