Coverage Report

Created: 2018-09-25 14:53

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