summaryrefslogtreecommitdiffstats
path: root/js/src/jit/JitcodeMap.h
blob: b4ed8ae7ff7c1a1c0b435beb04b42aed357f394e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jit_JitcodeMap_h
#define jit_JitcodeMap_h

#include "mozilla/Assertions.h"  // MOZ_ASSERT, MOZ_ASSERT_IF, MOZ_CRASH

#include <stddef.h>  // size_t
#include <stdint.h>  // uint8_t, uint32_t, uint64_t

#include "ds/AvlTree.h"         // AvlTree
#include "jit/CompactBuffer.h"  // CompactBufferReader, CompactBufferWriter
#include "jit/shared/Assembler-shared.h"  // CodeOffset
#include "js/AllocPolicy.h"               // SystemAllocPolicy
#include "js/TypeDecls.h"                 // jsbytecode
#include "js/Vector.h"                    // Vector
#include "vm/BytecodeLocation.h"          // BytecodeLocation

class JSScript;
class JSTracer;
struct JSRuntime;

namespace JS {
class Zone;
}  // namespace JS

namespace js {

class GCMarker;

namespace jit {

class InlineScriptTree;

/*
 * The jitcode map implements tables to allow mapping from addresses in jitcode
 * to the list of (JSScript*, jsbytecode*) pairs that are implicitly active in
 * the frame at that point in the native code.
 *
 * To represent this information efficiently, a multi-level table is used.
 *
 * At the top level, a global AVL-tree of JitcodeGlobalEntry describing the
 * mapping for each individual JitCode generated by compiles.  The entries are
 * ordered by their nativeStartAddr.
 *
 * Every entry in the table is of fixed size, but there are different entry
 * types, distinguished by the kind field.
 */

class JitcodeGlobalTable;
class JitcodeIonTable;
class JitcodeRegionEntry;

struct NativeToBytecode {
  CodeOffset nativeOffset;
  InlineScriptTree* tree;
  jsbytecode* pc;
};

// Describes range [start, end) of JIT-generated code.
class JitCodeRange {
 protected:
  void* const nativeStartAddr_;
  void* const nativeEndAddr_;

 public:
  JitCodeRange(void* start, void* end)
      : nativeStartAddr_(start), nativeEndAddr_(end) {
    MOZ_ASSERT(start < end);
  }

  // Comparator used by the AvlTree.
  static int compare(const JitCodeRange* r1, const JitCodeRange* r2) {
    // JitCodeRange includes 'start' but excludes 'end'.
    if (r1->nativeEndAddr_ <= r2->nativeStartAddr_) {
      return -1;
    }
    if (r1->nativeStartAddr_ >= r2->nativeEndAddr_) {
      return 1;
    }
    return 0;
  }

  void* nativeStartAddr() const { return nativeStartAddr_; }
  void* nativeEndAddr() const { return nativeEndAddr_; }

  bool containsPointer(void* ptr) const {
    return nativeStartAddr() <= ptr && ptr < nativeEndAddr();
  }
};

typedef Vector<BytecodeLocation, 0, SystemAllocPolicy> BytecodeLocationVector;

class IonEntry;
class IonICEntry;
class BaselineEntry;
class BaselineInterpreterEntry;
class DummyEntry;

// Base class for all entries.
class JitcodeGlobalEntry : public JitCodeRange {
 protected:
  JitCode* jitcode_;
  // If this entry is referenced from the profiler buffer, this is the
  // position where the most recent sample that references it starts.
  // Otherwise set to kNoSampleInBuffer.
  static const uint64_t kNoSampleInBuffer = UINT64_MAX;
  uint64_t samplePositionInBuffer_ = kNoSampleInBuffer;

 public:
  enum class Kind : uint8_t {
    Ion,
    IonIC,
    Baseline,
    BaselineInterpreter,
    Dummy
  };

 protected:
  Kind kind_;

  JitcodeGlobalEntry(Kind kind, JitCode* code, void* nativeStartAddr,
                     void* nativeEndAddr)
      : JitCodeRange(nativeStartAddr, nativeEndAddr),
        jitcode_(code),
        kind_(kind) {
    MOZ_ASSERT(code);
    MOZ_ASSERT(nativeStartAddr);
    MOZ_ASSERT(nativeEndAddr);
  }

  // Protected destructor to ensure this is called through DestroyPolicy.
  ~JitcodeGlobalEntry() = default;

  JitcodeGlobalEntry(const JitcodeGlobalEntry& other) = delete;
  void operator=(const JitcodeGlobalEntry& other) = delete;

 public:
  struct DestroyPolicy {
    void operator()(JitcodeGlobalEntry* entry);
  };

  void setSamplePositionInBuffer(uint64_t bufferWritePos) {
    samplePositionInBuffer_ = bufferWritePos;
  }
  void setAsExpired() { samplePositionInBuffer_ = kNoSampleInBuffer; }
  bool isSampled(uint64_t bufferRangeStart) {
    if (samplePositionInBuffer_ == kNoSampleInBuffer) {
      return false;
    }
    return bufferRangeStart <= samplePositionInBuffer_;
  }

  Kind kind() const { return kind_; }
  bool isIon() const { return kind() == Kind::Ion; }
  bool isIonIC() const { return kind() == Kind::IonIC; }
  bool isBaseline() const { return kind() == Kind::Baseline; }
  bool isBaselineInterpreter() const {
    return kind() == Kind::BaselineInterpreter;
  }
  bool isDummy() const { return kind() == Kind::Dummy; }

  inline const IonEntry& asIon() const;
  inline const IonICEntry& asIonIC() const;
  inline const BaselineEntry& asBaseline() const;
  inline const BaselineInterpreterEntry& asBaselineInterpreter() const;
  inline const DummyEntry& asDummy() const;

  inline IonEntry& asIon();
  inline IonICEntry& asIonIC();
  inline BaselineEntry& asBaseline();
  inline BaselineInterpreterEntry& asBaselineInterpreter();
  inline DummyEntry& asDummy();

  JitCode* jitcode() const { return jitcode_; }
  JitCode** jitcodePtr() { return &jitcode_; }
  Zone* zone() const { return jitcode()->zone(); }

  bool traceJitcode(JSTracer* trc);
  bool isJitcodeMarkedFromAnyThread(JSRuntime* rt);

  bool trace(JSTracer* trc);
  void traceWeak(JSTracer* trc);
  uint64_t lookupRealmID(JSRuntime* rt, void* ptr) const;
  void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const;

  // Read the inline call stack at a given point in the native code and append
  // into the given vector.  Innermost (script,pc) pair will be appended first,
  // and outermost appended last.
  //
  // Returns false on memory failure.
  [[nodiscard]] bool callStackAtAddr(JSRuntime* rt, void* ptr,
                                     BytecodeLocationVector& results,
                                     uint32_t* depth) const;
  uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
                           uint32_t maxResults) const;
};

using UniqueJitcodeGlobalEntry =
    UniquePtr<JitcodeGlobalEntry, JitcodeGlobalEntry::DestroyPolicy>;

template <typename T, typename... Args>
inline UniqueJitcodeGlobalEntry MakeJitcodeGlobalEntry(JSContext* cx,
                                                       Args&&... args) {
  UniqueJitcodeGlobalEntry res(js_new<T>(std::forward<Args>(args)...));
  if (!res) {
    ReportOutOfMemory(cx);
  }
  return res;
}

class IonEntry : public JitcodeGlobalEntry {
 public:
  struct ScriptNamePair {
    JSScript* script;
    UniqueChars str;
    ScriptNamePair(JSScript* script, UniqueChars str)
        : script(script), str(std::move(str)) {}
  };
  using ScriptList = Vector<ScriptNamePair, 2, SystemAllocPolicy>;

 private:
  ScriptList scriptList_;

  // regionTable_ points to the start of the region table within the
  // packed map for compile represented by this entry.  Since the
  // region table occurs at the tail of the memory region, this pointer
  // points somewhere inside the region memory space, and not to the start
  // of the memory space.
  const JitcodeIonTable* regionTable_;

 public:
  IonEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr,
           ScriptList&& scriptList, JitcodeIonTable* regionTable)
      : JitcodeGlobalEntry(Kind::Ion, code, nativeStartAddr, nativeEndAddr),
        scriptList_(std::move(scriptList)),
        regionTable_(regionTable) {
    MOZ_ASSERT(regionTable);
  }

  ~IonEntry();

  ScriptList& scriptList() { return scriptList_; }

  size_t numScripts() const { return scriptList_.length(); }

  JSScript* getScript(unsigned idx) const {
    MOZ_ASSERT(idx < numScripts());
    return scriptList_[idx].script;
  }

  const char* getStr(unsigned idx) const {
    MOZ_ASSERT(idx < numScripts());
    return scriptList_[idx].str.get();
  }

  const JitcodeIonTable* regionTable() const { return regionTable_; }

  void* canonicalNativeAddrFor(void* ptr) const;

  [[nodiscard]] bool callStackAtAddr(void* ptr, BytecodeLocationVector& results,
                                     uint32_t* depth) const;

  uint32_t callStackAtAddr(void* ptr, const char** results,
                           uint32_t maxResults) const;

  uint64_t lookupRealmID(void* ptr) const;

  bool trace(JSTracer* trc);
  void traceWeak(JSTracer* trc);
};

class IonICEntry : public JitcodeGlobalEntry {
  // Address for this IC in the IonScript code. Most operations on IonICEntry
  // use this to forward to the IonEntry.
  void* rejoinAddr_;

 public:
  IonICEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr,
             void* rejoinAddr)
      : JitcodeGlobalEntry(Kind::IonIC, code, nativeStartAddr, nativeEndAddr),
        rejoinAddr_(rejoinAddr) {
    MOZ_ASSERT(rejoinAddr_);
  }

  void* rejoinAddr() const { return rejoinAddr_; }

  void* canonicalNativeAddrFor(void* ptr) const;

  [[nodiscard]] bool callStackAtAddr(JSRuntime* rt, void* ptr,
                                     BytecodeLocationVector& results,
                                     uint32_t* depth) const;

  uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
                           uint32_t maxResults) const;

  uint64_t lookupRealmID(JSRuntime* rt, void* ptr) const;

  bool trace(JSTracer* trc);
  void traceWeak(JSTracer* trc);
};

class BaselineEntry : public JitcodeGlobalEntry {
  JSScript* script_;
  UniqueChars str_;

 public:
  BaselineEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr,
                JSScript* script, UniqueChars str)
      : JitcodeGlobalEntry(Kind::Baseline, code, nativeStartAddr,
                           nativeEndAddr),
        script_(script),
        str_(std::move(str)) {
    MOZ_ASSERT(script_);
    MOZ_ASSERT(str_);
  }

  JSScript* script() const { return script_; }

  const char* str() const { return str_.get(); }

  void* canonicalNativeAddrFor(void* ptr) const;

  [[nodiscard]] bool callStackAtAddr(void* ptr, BytecodeLocationVector& results,
                                     uint32_t* depth) const;

  uint32_t callStackAtAddr(void* ptr, const char** results,
                           uint32_t maxResults) const;

  uint64_t lookupRealmID() const;

  bool trace(JSTracer* trc);
  void traceWeak(JSTracer* trc);
};

class BaselineInterpreterEntry : public JitcodeGlobalEntry {
 public:
  BaselineInterpreterEntry(JitCode* code, void* nativeStartAddr,
                           void* nativeEndAddr)
      : JitcodeGlobalEntry(Kind::BaselineInterpreter, code, nativeStartAddr,
                           nativeEndAddr) {}

  void* canonicalNativeAddrFor(void* ptr) const;

  [[nodiscard]] bool callStackAtAddr(void* ptr, BytecodeLocationVector& results,
                                     uint32_t* depth) const;

  uint32_t callStackAtAddr(void* ptr, const char** results,
                           uint32_t maxResults) const;

  uint64_t lookupRealmID() const;
};

// Dummy entries are created for jitcode generated when profiling is not
// turned on, so that they have representation in the global table if they are
// on the stack when profiling is enabled.
class DummyEntry : public JitcodeGlobalEntry {
 public:
  DummyEntry(JitCode* code, void* nativeStartAddr, void* nativeEndAddr)
      : JitcodeGlobalEntry(Kind::Dummy, code, nativeStartAddr, nativeEndAddr) {}

  void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const {
    return nullptr;
  }

  [[nodiscard]] bool callStackAtAddr(JSRuntime* rt, void* ptr,
                                     BytecodeLocationVector& results,
                                     uint32_t* depth) const {
    return true;
  }

  uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
                           uint32_t maxResults) const {
    return 0;
  }

  uint64_t lookupRealmID() const { return 0; }
};

inline const IonEntry& JitcodeGlobalEntry::asIon() const {
  MOZ_ASSERT(isIon());
  return *static_cast<const IonEntry*>(this);
}

inline const IonICEntry& JitcodeGlobalEntry::asIonIC() const {
  MOZ_ASSERT(isIonIC());
  return *static_cast<const IonICEntry*>(this);
}

inline const BaselineEntry& JitcodeGlobalEntry::asBaseline() const {
  MOZ_ASSERT(isBaseline());
  return *static_cast<const BaselineEntry*>(this);
}

inline const BaselineInterpreterEntry&
JitcodeGlobalEntry::asBaselineInterpreter() const {
  MOZ_ASSERT(isBaselineInterpreter());
  return *static_cast<const BaselineInterpreterEntry*>(this);
}

inline const DummyEntry& JitcodeGlobalEntry::asDummy() const {
  MOZ_ASSERT(isDummy());
  return *static_cast<const DummyEntry*>(this);
}

inline IonEntry& JitcodeGlobalEntry::asIon() {
  MOZ_ASSERT(isIon());
  return *static_cast<IonEntry*>(this);
}

inline IonICEntry& JitcodeGlobalEntry::asIonIC() {
  MOZ_ASSERT(isIonIC());
  return *static_cast<IonICEntry*>(this);
}

inline BaselineEntry& JitcodeGlobalEntry::asBaseline() {
  MOZ_ASSERT(isBaseline());
  return *static_cast<BaselineEntry*>(this);
}

inline BaselineInterpreterEntry& JitcodeGlobalEntry::asBaselineInterpreter() {
  MOZ_ASSERT(isBaselineInterpreter());
  return *static_cast<BaselineInterpreterEntry*>(this);
}

inline DummyEntry& JitcodeGlobalEntry::asDummy() {
  MOZ_ASSERT(isDummy());
  return *static_cast<DummyEntry*>(this);
}

// Global table of JitcodeGlobalEntry entries.
class JitcodeGlobalTable {
 private:
  // Vector containing (and owning) all entries. This is unsorted and used for
  // iterating over all entries, because the AvlTree currently doesn't support
  // modifications while iterating.
  using EntryVector = Vector<UniqueJitcodeGlobalEntry, 0, SystemAllocPolicy>;
  EntryVector entries_;

  // AVL tree containing all entries in the Vector above. This is used to
  // efficiently look up the entry corresponding to a native code address.
  using EntryTree = AvlTree<JitCodeRange*, JitCodeRange>;
  static const size_t LIFO_CHUNK_SIZE = 16 * 1024;
  LifoAlloc alloc_;
  EntryTree tree_;

 public:
  JitcodeGlobalTable() : alloc_(LIFO_CHUNK_SIZE), tree_(&alloc_) {}

  bool empty() const {
    MOZ_ASSERT(entries_.empty() == tree_.empty());
    return entries_.empty();
  }

  JitcodeGlobalEntry* lookup(void* ptr) { return lookupInternal(ptr); }

  const JitcodeGlobalEntry* lookupForSampler(void* ptr, JSRuntime* rt,
                                             uint64_t samplePosInBuffer);

  [[nodiscard]] bool addEntry(UniqueJitcodeGlobalEntry entry);

  void setAllEntriesAsExpired();
  [[nodiscard]] bool markIteratively(GCMarker* marker);
  void traceWeak(JSRuntime* rt, JSTracer* trc);

 private:
  JitcodeGlobalEntry* lookupInternal(void* ptr);
};

// clang-format off
/*
 * Container class for main jitcode table.
 * The Region table's memory is structured as follows:
 *
 *      +------------------------------------------------+   |
 *      |  Region 1 Run                                  |   |
 *      |------------------------------------------------|   |
 *      |  Region 2 Run                                  |   |
 *      |                                                |   |
 *      |                                                |   |
 *      |------------------------------------------------|   |
 *      |  Region 3 Run                                  |   |
 *      |                                                |   |
 *      |------------------------------------------------|   |-- Payload
 *      |                                                |   |
 *      |               ...                              |   |
 *      |                                                |   |
 *      |------------------------------------------------|   |
 *      |  Region M Run                                  |   |
 *      |                                                |   |
 *      +================================================+ <- RegionTable pointer points here
 *      | uint23_t numRegions = M                        |   |
 *      +------------------------------------------------+   |
 *      | Region 1                                       |   |
 *      |   uint32_t entryOffset = size(Payload)         |   |
 *      +------------------------------------------------+   |
 *      |                                                |   |-- Table
 *      |   ...                                          |   |
 *      |                                                |   |
 *      +------------------------------------------------+   |
 *      | Region M                                       |   |
 *      |   uint32_t entryOffset                         |   |
 *      +------------------------------------------------+   |
 *
 * The region table is composed of two sections: a tail section that contains a table of
 * fixed-size entries containing offsets into the the head section, and a head section that
 * holds a sequence of variable-sized runs.  The table in the tail section serves to
 * locate the variable-length encoded structures in the head section.
 *
 * The entryOffsets in the table indicate the bytes offset to subtract from the regionTable
 * pointer to arrive at the encoded region in the payload.
 *
 *
 * Variable-length entries in payload
 * ----------------------------------
 * The entryOffsets in the region table's fixed-sized entries refer to a location within the
 * variable-length payload section.  This location contains a compactly encoded "run" of
 * mappings.
 *
 * Each run starts by describing the offset within the native code it starts at, and the
 * sequence of (JSScript*, jsbytecode*) pairs active at that site.  Following that, there
 * are a number of variable-length entries encoding (nativeOffsetDelta, bytecodeOffsetDelta)
 * pairs for the run.
 *
 *      VarUint32 nativeOffset;
 *          - The offset from nativeStartAddr in the global table entry at which
 *            the jitcode for this region starts.
 *
 *      Uint8_t scriptDepth;
 *          - The depth of inlined scripts for this region.
 *
 *      List<VarUint32> inlineScriptPcStack;
 *          - We encode (2 * scriptDepth) VarUint32s here.  Each pair of uint32s are taken
 *            as an index into the scriptList in the global table entry, and a pcOffset
 *            respectively.
 *
 *      List<NativeAndBytecodeDelta> deltaRun;
 *          - The rest of the entry is a deltaRun that stores a series of variable-length
 *            encoded NativeAndBytecodeDelta datums.
 */
// clang-format on
class JitcodeRegionEntry {
 private:
  static const unsigned MAX_RUN_LENGTH = 100;

 public:
  static void WriteHead(CompactBufferWriter& writer, uint32_t nativeOffset,
                        uint8_t scriptDepth);
  static void ReadHead(CompactBufferReader& reader, uint32_t* nativeOffset,
                       uint8_t* scriptDepth);

  static void WriteScriptPc(CompactBufferWriter& writer, uint32_t scriptIdx,
                            uint32_t pcOffset);
  static void ReadScriptPc(CompactBufferReader& reader, uint32_t* scriptIdx,
                           uint32_t* pcOffset);

  static void WriteDelta(CompactBufferWriter& writer, uint32_t nativeDelta,
                         int32_t pcDelta);
  static void ReadDelta(CompactBufferReader& reader, uint32_t* nativeDelta,
                        int32_t* pcDelta);

  // Given a pointer into an array of NativeToBytecode (and a pointer to the end
  // of the array), compute the number of entries that would be consume by
  // outputting a run starting at this one.
  static uint32_t ExpectedRunLength(const NativeToBytecode* entry,
                                    const NativeToBytecode* end);

  // Write a run, starting at the given NativeToBytecode entry, into the given
  // buffer writer.
  [[nodiscard]] static bool WriteRun(CompactBufferWriter& writer,
                                     const IonEntry::ScriptList& scriptList,
                                     uint32_t runLength,
                                     const NativeToBytecode* entry);

  // Delta Run entry formats are encoded little-endian:
  //
  //  byte 0
  //  NNNN-BBB0
  //      Single byte format.  nativeDelta in [0, 15], pcDelta in [0, 7]
  //
  static const uint32_t ENC1_MASK = 0x1;
  static const uint32_t ENC1_MASK_VAL = 0x0;

  static const uint32_t ENC1_NATIVE_DELTA_MAX = 0xf;
  static const unsigned ENC1_NATIVE_DELTA_SHIFT = 4;

  static const uint32_t ENC1_PC_DELTA_MASK = 0x0e;
  static const int32_t ENC1_PC_DELTA_MAX = 0x7;
  static const unsigned ENC1_PC_DELTA_SHIFT = 1;

  //  byte 1    byte 0
  //  NNNN-NNNN BBBB-BB01
  //      Two-byte format.  nativeDelta in [0, 255], pcDelta in [0, 63]
  //
  static const uint32_t ENC2_MASK = 0x3;
  static const uint32_t ENC2_MASK_VAL = 0x1;

  static const uint32_t ENC2_NATIVE_DELTA_MAX = 0xff;
  static const unsigned ENC2_NATIVE_DELTA_SHIFT = 8;

  static const uint32_t ENC2_PC_DELTA_MASK = 0x00fc;
  static const int32_t ENC2_PC_DELTA_MAX = 0x3f;
  static const unsigned ENC2_PC_DELTA_SHIFT = 2;

  //  byte 2    byte 1    byte 0
  //  NNNN-NNNN NNNB-BBBB BBBB-B011
  //      Three-byte format.  nativeDelta in [0, 2047], pcDelta in [-512, 511]
  //
  static const uint32_t ENC3_MASK = 0x7;
  static const uint32_t ENC3_MASK_VAL = 0x3;

  static const uint32_t ENC3_NATIVE_DELTA_MAX = 0x7ff;
  static const unsigned ENC3_NATIVE_DELTA_SHIFT = 13;

  static const uint32_t ENC3_PC_DELTA_MASK = 0x001ff8;
  static const int32_t ENC3_PC_DELTA_MAX = 0x1ff;
  static const int32_t ENC3_PC_DELTA_MIN = -ENC3_PC_DELTA_MAX - 1;
  static const unsigned ENC3_PC_DELTA_SHIFT = 3;

  //  byte 3    byte 2    byte 1    byte 0
  //  NNNN-NNNN NNNN-NNNN BBBB-BBBB BBBB-B111
  //      Three-byte format.  nativeDelta in [0, 65535],
  //                          pcDelta in [-4096, 4095]
  static const uint32_t ENC4_MASK = 0x7;
  static const uint32_t ENC4_MASK_VAL = 0x7;

  static const uint32_t ENC4_NATIVE_DELTA_MAX = 0xffff;
  static const unsigned ENC4_NATIVE_DELTA_SHIFT = 16;

  static const uint32_t ENC4_PC_DELTA_MASK = 0x0000fff8;
  static const int32_t ENC4_PC_DELTA_MAX = 0xfff;
  static const int32_t ENC4_PC_DELTA_MIN = -ENC4_PC_DELTA_MAX - 1;
  static const unsigned ENC4_PC_DELTA_SHIFT = 3;

  static bool IsDeltaEncodeable(uint32_t nativeDelta, int32_t pcDelta) {
    return (nativeDelta <= ENC4_NATIVE_DELTA_MAX) &&
           (pcDelta >= ENC4_PC_DELTA_MIN) && (pcDelta <= ENC4_PC_DELTA_MAX);
  }

 private:
  const uint8_t* data_;
  const uint8_t* end_;

  // Unpacked state from jitcode entry.
  uint32_t nativeOffset_;
  uint8_t scriptDepth_;
  const uint8_t* scriptPcStack_;
  const uint8_t* deltaRun_;

  void unpack();

 public:
  JitcodeRegionEntry(const uint8_t* data, const uint8_t* end)
      : data_(data),
        end_(end),
        nativeOffset_(0),
        scriptDepth_(0),
        scriptPcStack_(nullptr),
        deltaRun_(nullptr) {
    MOZ_ASSERT(data_ < end_);
    unpack();
    MOZ_ASSERT(scriptPcStack_ < end_);
    MOZ_ASSERT(deltaRun_ <= end_);
  }

  uint32_t nativeOffset() const { return nativeOffset_; }
  uint32_t scriptDepth() const { return scriptDepth_; }

  class ScriptPcIterator {
   private:
    const uint8_t* start_;
    const uint8_t* end_;
#ifdef DEBUG
    uint32_t count_;
#endif
    uint32_t idx_;
    const uint8_t* cur_;

   public:
    ScriptPcIterator(const uint8_t* start, const uint8_t* end, uint32_t count)
        : start_(start),
          end_(end),
#ifdef DEBUG
          count_(count),
#endif
          idx_(0),
          cur_(start_) {
    }

    bool hasMore() const {
      MOZ_ASSERT((idx_ == count_) == (cur_ == end_));
      MOZ_ASSERT((idx_ < count_) == (cur_ < end_));
      return cur_ < end_;
    }

    void readNext(uint32_t* scriptIdxOut, uint32_t* pcOffsetOut) {
      MOZ_ASSERT(scriptIdxOut);
      MOZ_ASSERT(pcOffsetOut);
      MOZ_ASSERT(hasMore());

      CompactBufferReader reader(cur_, end_);
      ReadScriptPc(reader, scriptIdxOut, pcOffsetOut);

      cur_ = reader.currentPosition();
      MOZ_ASSERT(cur_ <= end_);

      idx_++;
      MOZ_ASSERT_IF(idx_ == count_, cur_ == end_);
    }

    void reset() {
      idx_ = 0;
      cur_ = start_;
    }
  };

  ScriptPcIterator scriptPcIterator() const {
    // End of script+pc sequence is the start of the delta run.
    return ScriptPcIterator(scriptPcStack_, deltaRun_, scriptDepth_);
  }

  class DeltaIterator {
   private:
    const uint8_t* start_;
    const uint8_t* end_;
    const uint8_t* cur_;

   public:
    DeltaIterator(const uint8_t* start, const uint8_t* end)
        : start_(start), end_(end), cur_(start) {}

    bool hasMore() const {
      MOZ_ASSERT(cur_ <= end_);
      return cur_ < end_;
    }

    void readNext(uint32_t* nativeDeltaOut, int32_t* pcDeltaOut) {
      MOZ_ASSERT(nativeDeltaOut != nullptr);
      MOZ_ASSERT(pcDeltaOut != nullptr);

      MOZ_ASSERT(hasMore());

      CompactBufferReader reader(cur_, end_);
      ReadDelta(reader, nativeDeltaOut, pcDeltaOut);

      cur_ = reader.currentPosition();
      MOZ_ASSERT(cur_ <= end_);
    }

    void reset() { cur_ = start_; }
  };
  DeltaIterator deltaIterator() const { return DeltaIterator(deltaRun_, end_); }

  uint32_t findPcOffset(uint32_t queryNativeOffset,
                        uint32_t startPcOffset) const;
};

class JitcodeIonTable {
 private:
  /* Variable length payload section "below" here. */
  uint32_t numRegions_;
  uint32_t regionOffsets_[1];

  const uint8_t* payloadEnd() const {
    return reinterpret_cast<const uint8_t*>(this);
  }

 public:
  JitcodeIonTable() = delete;

  uint32_t numRegions() const { return numRegions_; }

  uint32_t regionOffset(uint32_t regionIndex) const {
    MOZ_ASSERT(regionIndex < numRegions());
    return regionOffsets_[regionIndex];
  }

  JitcodeRegionEntry regionEntry(uint32_t regionIndex) const {
    const uint8_t* regionStart = payloadEnd() - regionOffset(regionIndex);
    const uint8_t* regionEnd = payloadEnd();
    if (regionIndex < numRegions_ - 1) {
      regionEnd -= regionOffset(regionIndex + 1);
    }
    return JitcodeRegionEntry(regionStart, regionEnd);
  }

  uint32_t findRegionEntry(uint32_t offset) const;

  const uint8_t* payloadStart() const {
    // The beginning of the payload the beginning of the first region are the
    // same.
    return payloadEnd() - regionOffset(0);
  }

  [[nodiscard]] static bool WriteIonTable(
      CompactBufferWriter& writer, const IonEntry::ScriptList& scriptList,
      const NativeToBytecode* start, const NativeToBytecode* end,
      uint32_t* tableOffsetOut, uint32_t* numRegionsOut);
};

}  // namespace jit
}  // namespace js

#endif /* jit_JitcodeMap_h */