tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

IonScript.h (19341B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef jit_IonScript_h
      8 #define jit_IonScript_h
      9 
     10 #include "mozilla/MemoryReporting.h"  // MallocSizeOf
     11 
     12 #include <stddef.h>  // size_t
     13 #include <stdint.h>  // uint8_t, uint32_t
     14 
     15 #include "jstypes.h"
     16 
     17 #include "gc/Barrier.h"          // HeapPtr{JitCode,Object}
     18 #include "jit/IonTypes.h"        // IonCompilationId
     19 #include "jit/JitCode.h"         // JitCode
     20 #include "jit/JitOptions.h"      // JitOptions
     21 #include "js/TypeDecls.h"        // jsbytecode
     22 #include "util/TrailingArray.h"  // TrailingArray
     23 
     24 namespace js {
     25 namespace jit {
     26 
     27 class SnapshotWriter;
     28 class RecoverWriter;
     29 class SafepointWriter;
     30 class CodegenSafepointIndex;
     31 class SafepointIndex;
     32 class OsiIndex;
     33 class IonIC;
     34 
     35 // An IonScript attaches Ion-generated information to a JSScript. The header
     36 // structure is followed by several arrays of data. These trailing arrays have a
     37 // layout based on offsets (bytes from 'this') stored in the IonScript header.
     38 //
     39 //    <IonScript itself>
     40 //    --
     41 //    PreBarriered<Value>[] constantTable()
     42 //    uint8_t[]             runtimeData()
     43 //    OsiIndex[]            osiIndex()
     44 //    SafepointIndex[]      safepointIndex()
     45 //    uint32_t[]            icIndex()
     46 //    --
     47 //    uint8_t[]             safepoints()
     48 //    uint8_t[]             snapshots()
     49 //    uint8_t[]             snapshotsRVATable()
     50 //    uint8_t[]             recovers()
     51 //
     52 // Note: These are arranged in order of descending alignment requirements to
     53 // avoid the need for padding. The `runtimeData` uses uint64_t alignement due to
     54 // its use of mozilla::AlignedStorage2.
     55 class alignas(8) IonScript final : public TrailingArray<IonScript> {
     56 private:
     57  // Offset (in bytes) from `this` to the start of each trailing array. Each
     58  // array ends where following one begins. There is no implicit padding (except
     59  // possible at very end).
     60  Offset constantTableOffset_ = 0;   // JS::Value aligned
     61  Offset runtimeDataOffset_ = 0;     // uint64_t aligned
     62  Offset nurseryObjectsOffset_ = 0;  // pointer aligned
     63  Offset osiIndexOffset_ = 0;
     64  Offset safepointIndexOffset_ = 0;
     65  Offset icIndexOffset_ = 0;
     66  Offset safepointsOffset_ = 0;
     67  Offset snapshotsOffset_ = 0;
     68  Offset rvaTableOffset_ = 0;
     69  Offset recoversOffset_ = 0;
     70  Offset allocBytes_ = 0;
     71 
     72  // Code pointer containing the actual method.
     73  HeapPtr<JitCode*> method_ = nullptr;
     74 
     75  // Entrypoint for OSR, or nullptr.
     76  jsbytecode* osrPc_ = nullptr;
     77 
     78  // Offset to OSR entrypoint from method_->raw(), or 0.
     79  uint32_t osrEntryOffset_ = 0;
     80 
     81  // Offset of the invalidation epilogue (which pushes this IonScript
     82  // and calls the invalidation thunk).
     83  uint32_t invalidateEpilogueOffset_ = 0;
     84 
     85  // The offset immediately after the IonScript immediate.
     86  // NOTE: technically a constant delta from
     87  // |invalidateEpilogueOffset_|, so we could hard-code this
     88  // per-platform if we want.
     89  uint32_t invalidateEpilogueDataOffset_ = 0;
     90 
     91  // Number of bailouts that have occurred for reasons that could be
     92  // fixed if we invalidated and recompiled.
     93  uint16_t numFixableBailouts_ = 0;
     94 
     95  // Number of bailouts that have occurred for reasons that can't be
     96  // fixed by recompiling: for example, bailing out to catch an exception.
     97  uint16_t numUnfixableBailouts_ = 0;
     98 
     99 public:
    100  enum class LICMState : uint8_t { NeverBailed, Bailed, BailedAndHitFallback };
    101 
    102 private:
    103  // Tracks the state of LICM bailouts.
    104  LICMState licmState_ = LICMState::NeverBailed;
    105 
    106  // Flag set if IonScript was compiled with profiling enabled.
    107  bool hasProfilingInstrumentation_ = false;
    108 
    109  // If true, this IonScript was active on the stack when we discarded JIT code
    110  // and inactive ICScripts. This means we should use the generic ICScripts for
    111  // inlined functions when we bail out.
    112  bool purgedICScripts_ = false;
    113 
    114  // Number of bytes this function reserves on the stack for slots spilled by
    115  // the register allocator.
    116  uint32_t localSlotsSize_ = 0;
    117 
    118  // Number of bytes used passed in as formal arguments or |this|.
    119  uint32_t argumentSlotsSize_ = 0;
    120 
    121  // Frame size is the value that can be added to the StackPointer along
    122  // with the frame prefix to get a valid JitFrameLayout.
    123  uint32_t frameSize_ = 0;
    124 
    125  // Number of references from invalidation records.
    126  uint32_t invalidationCount_ = 0;
    127 
    128  // Identifier of the compilation which produced this code.
    129  IonCompilationId compilationId_;
    130 
    131  // Number of times we tried to enter this script via OSR but failed due to
    132  // a LOOPENTRY pc other than osrPc_.
    133  uint32_t osrPcMismatchCounter_ = 0;
    134 
    135 #ifdef DEBUG
    136  // A hash of the ICScripts used in this compilation.
    137  mozilla::HashNumber icHash_ = 0;
    138 #endif
    139 
    140  // End of fields.
    141 
    142 private:
    143  // Layout helpers
    144  Offset constantTableOffset() const { return constantTableOffset_; }
    145  Offset runtimeDataOffset() const { return runtimeDataOffset_; }
    146  Offset nurseryObjectsOffset() const { return nurseryObjectsOffset_; }
    147  Offset osiIndexOffset() const { return osiIndexOffset_; }
    148  Offset safepointIndexOffset() const { return safepointIndexOffset_; }
    149  Offset icIndexOffset() const { return icIndexOffset_; }
    150  Offset safepointsOffset() const { return safepointsOffset_; }
    151  Offset snapshotsOffset() const { return snapshotsOffset_; }
    152  Offset rvaTableOffset() const { return rvaTableOffset_; }
    153  Offset recoversOffset() const { return recoversOffset_; }
    154  Offset endOffset() const { return allocBytes_; }
    155 
    156  // Hardcode size of incomplete types. These are verified in Ion.cpp.
    157  static constexpr size_t SizeOf_OsiIndex = 2 * sizeof(uint32_t);
    158  static constexpr size_t SizeOf_SafepointIndex = 2 * sizeof(uint32_t);
    159 
    160 public:
    161  //
    162  // Table of constants referenced in snapshots. (JS::Value alignment)
    163  //
    164  HeapPtr<Value>* constants() {
    165    return offsetToPointer<HeapPtr<Value>>(constantTableOffset());
    166  }
    167  size_t numConstants() const {
    168    return numElements<HeapPtr<Value>>(constantTableOffset(),
    169                                       runtimeDataOffset());
    170  }
    171 
    172  //
    173  // IonIC data structures. (uint64_t alignment)
    174  //
    175  uint8_t* runtimeData() {
    176    return offsetToPointer<uint8_t>(runtimeDataOffset());
    177  }
    178  size_t runtimeSize() const {
    179    return numElements<uint8_t>(runtimeDataOffset(), nurseryObjectsOffset());
    180  }
    181 
    182  //
    183  // List of (originally) nursery-allocated objects referenced from JIT code.
    184  // (JSObject* alignment)
    185  //
    186  HeapPtr<JSObject*>* nurseryObjects() {
    187    return offsetToPointer<HeapPtr<JSObject*>>(nurseryObjectsOffset());
    188  }
    189  size_t numNurseryObjects() const {
    190    return numElements<HeapPtr<JSObject*>>(nurseryObjectsOffset(),
    191                                           osiIndexOffset());
    192  }
    193  void* addressOfNurseryObject(uint32_t index) {
    194    MOZ_ASSERT(index < numNurseryObjects());
    195    return &nurseryObjects()[index];
    196  }
    197 
    198  //
    199  // Map OSI-point displacement to snapshot.
    200  //
    201  OsiIndex* osiIndices() { return offsetToPointer<OsiIndex>(osiIndexOffset()); }
    202  const OsiIndex* osiIndices() const {
    203    return offsetToPointer<OsiIndex>(osiIndexOffset());
    204  }
    205  size_t numOsiIndices() const {
    206    return numElements<SizeOf_OsiIndex>(osiIndexOffset(),
    207                                        safepointIndexOffset());
    208  }
    209 
    210  //
    211  // Map code displacement to safepoint / OSI-patch-delta.
    212  //
    213  SafepointIndex* safepointIndices() {
    214    return offsetToPointer<SafepointIndex>(safepointIndexOffset());
    215  }
    216  const SafepointIndex* safepointIndices() const {
    217    return offsetToPointer<SafepointIndex>(safepointIndexOffset());
    218  }
    219  size_t numSafepointIndices() const {
    220    return numElements<SizeOf_SafepointIndex>(safepointIndexOffset(),
    221                                              icIndexOffset());
    222  }
    223 
    224  //
    225  // Offset into `runtimeData` for each (variable-length) IonIC.
    226  //
    227  uint32_t* icIndex() { return offsetToPointer<uint32_t>(icIndexOffset()); }
    228  size_t numICs() const {
    229    return numElements<uint32_t>(icIndexOffset(), safepointsOffset());
    230  }
    231 
    232  //
    233  // Safepoint table as a CompactBuffer.
    234  //
    235  const uint8_t* safepoints() const {
    236    return offsetToPointer<uint8_t>(safepointsOffset());
    237  }
    238  size_t safepointsSize() const {
    239    return numElements<uint8_t>(safepointsOffset(), snapshotsOffset());
    240  }
    241 
    242  //
    243  // Snapshot and RValueAllocation tables as CompactBuffers.
    244  //
    245  const uint8_t* snapshots() const {
    246    return offsetToPointer<uint8_t>(snapshotsOffset());
    247  }
    248  size_t snapshotsListSize() const {
    249    return numElements<uint8_t>(snapshotsOffset(), rvaTableOffset());
    250  }
    251  size_t snapshotsRVATableSize() const {
    252    return numElements<uint8_t>(rvaTableOffset(), recoversOffset());
    253  }
    254 
    255  //
    256  // Recover instruction table as a CompactBuffer.
    257  //
    258  const uint8_t* recovers() const {
    259    return offsetToPointer<uint8_t>(recoversOffset());
    260  }
    261  size_t recoversSize() const {
    262    return numElements<uint8_t>(recoversOffset(), endOffset());
    263  }
    264 
    265 private:
    266  IonScript(IonCompilationId compilationId, uint32_t localSlotsSize,
    267            uint32_t argumentSlotsSize, uint32_t frameSize);
    268 
    269 public:
    270  static IonScript* New(JSContext* cx, IonCompilationId compilationId,
    271                        uint32_t localSlotsSize, uint32_t argumentSlotsSize,
    272                        uint32_t frameSize, size_t snapshotsListSize,
    273                        size_t snapshotsRVATableSize, size_t recoversSize,
    274                        size_t constants, size_t nurseryObjects,
    275                        size_t safepointIndices, size_t osiIndices,
    276                        size_t icEntries, size_t runtimeSize,
    277                        size_t safepointsSize);
    278 
    279  static void Destroy(JS::GCContext* gcx, IonScript* script);
    280 
    281  void trace(JSTracer* trc);
    282  void traceWeak(JSTracer* trc);
    283 
    284  static inline size_t offsetOfInvalidationCount() {
    285    return offsetof(IonScript, invalidationCount_);
    286  }
    287 
    288 public:
    289  JitCode* method() const { return method_; }
    290  void setMethod(JitCode* code) {
    291    MOZ_ASSERT(!invalidated());
    292    method_ = code;
    293  }
    294  void setOsrPc(jsbytecode* osrPc) { osrPc_ = osrPc; }
    295  jsbytecode* osrPc() const { return osrPc_; }
    296  void setOsrEntryOffset(uint32_t offset) {
    297    MOZ_ASSERT(!osrEntryOffset_);
    298    osrEntryOffset_ = offset;
    299  }
    300  uint32_t osrEntryOffset() const { return osrEntryOffset_; }
    301  bool containsCodeAddress(uint8_t* addr) const {
    302    return method()->raw() <= addr &&
    303           addr <= method()->raw() + method()->instructionsSize();
    304  }
    305  bool containsReturnAddress(uint8_t* addr) const {
    306    // This accounts for an off by one error caused by the return address of a
    307    // bailout sitting outside the range of the containing function.
    308    return method()->raw() <= addr &&
    309           addr <= method()->raw() + method()->instructionsSize();
    310  }
    311  void setInvalidationEpilogueOffset(uint32_t offset) {
    312    MOZ_ASSERT(!invalidateEpilogueOffset_);
    313    invalidateEpilogueOffset_ = offset;
    314  }
    315  uint32_t invalidateEpilogueOffset() const {
    316    MOZ_ASSERT(invalidateEpilogueOffset_);
    317    return invalidateEpilogueOffset_;
    318  }
    319  void setInvalidationEpilogueDataOffset(uint32_t offset) {
    320    MOZ_ASSERT(!invalidateEpilogueDataOffset_);
    321    invalidateEpilogueDataOffset_ = offset;
    322  }
    323  uint32_t invalidateEpilogueDataOffset() const {
    324    MOZ_ASSERT(invalidateEpilogueDataOffset_);
    325    return invalidateEpilogueDataOffset_;
    326  }
    327 
    328  uint32_t numFixableBailouts() const { return numFixableBailouts_; }
    329 
    330  void incNumFixableBailouts() { numFixableBailouts_++; }
    331  void resetNumFixableBailouts() { numFixableBailouts_ = 0; }
    332  void incNumUnfixableBailouts() { numUnfixableBailouts_++; }
    333 
    334  bool shouldInvalidate() const {
    335    return numFixableBailouts_ >= JitOptions.frequentBailoutThreshold;
    336  }
    337  bool shouldInvalidateAndDisable() const {
    338    return numUnfixableBailouts_ >= JitOptions.frequentBailoutThreshold * 5;
    339  }
    340 
    341  LICMState licmState() const { return licmState_; }
    342  void setHadLICMBailout() {
    343    if (licmState_ == LICMState::NeverBailed) {
    344      licmState_ = LICMState::Bailed;
    345    }
    346  }
    347  void noteBaselineFallback() {
    348    if (licmState_ == LICMState::Bailed) {
    349      licmState_ = LICMState::BailedAndHitFallback;
    350    }
    351  }
    352 
    353  void setHasProfilingInstrumentation() { hasProfilingInstrumentation_ = true; }
    354  void clearHasProfilingInstrumentation() {
    355    hasProfilingInstrumentation_ = false;
    356  }
    357  bool hasProfilingInstrumentation() const {
    358    return hasProfilingInstrumentation_;
    359  }
    360 
    361  bool purgedICScripts() const { return purgedICScripts_; }
    362  void notePurgedICScripts() { purgedICScripts_ = true; }
    363 
    364  size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
    365    return mallocSizeOf(this);
    366  }
    367  HeapPtr<Value>& getConstant(size_t index) {
    368    MOZ_ASSERT(index < numConstants());
    369    return constants()[index];
    370  }
    371  uint32_t localSlotsSize() const { return localSlotsSize_; }
    372  uint32_t argumentSlotsSize() const { return argumentSlotsSize_; }
    373  uint32_t frameSize() const { return frameSize_; }
    374  const SafepointIndex* getSafepointIndex(uint32_t disp) const;
    375  const SafepointIndex* getSafepointIndex(uint8_t* retAddr) const {
    376    MOZ_ASSERT(containsCodeAddress(retAddr));
    377    return getSafepointIndex(retAddr - method()->raw());
    378  }
    379  const OsiIndex* getOsiIndex(uint32_t disp) const;
    380  const OsiIndex* getOsiIndex(uint8_t* retAddr) const;
    381 
    382  IonIC& getICFromIndex(uint32_t index) {
    383    MOZ_ASSERT(index < numICs());
    384    uint32_t offset = icIndex()[index];
    385    return getIC(offset);
    386  }
    387  inline IonIC& getIC(uint32_t offset) {
    388    MOZ_ASSERT(offset < runtimeSize());
    389    return *reinterpret_cast<IonIC*>(runtimeData() + offset);
    390  }
    391  void purgeICs(Zone* zone);
    392  void copySnapshots(const SnapshotWriter* writer);
    393  void copyRecovers(const RecoverWriter* writer);
    394  void copyConstants(const Value* vp);
    395  void copySafepointIndices(const CodegenSafepointIndex* si);
    396  void copyOsiIndices(const OsiIndex* oi);
    397  void copyRuntimeData(const uint8_t* data);
    398  void copyICEntries(const uint32_t* icEntries);
    399  void copySafepoints(const SafepointWriter* writer);
    400 
    401  bool invalidated() const { return invalidationCount_ != 0; }
    402 
    403  // Invalidate the current compilation.
    404  void invalidate(JSContext* cx, JSScript* script, bool resetUses,
    405                  const char* reason);
    406 
    407  size_t invalidationCount() const { return invalidationCount_; }
    408  void incrementInvalidationCount() { invalidationCount_++; }
    409  void decrementInvalidationCount(JS::GCContext* gcx) {
    410    MOZ_ASSERT(invalidationCount_);
    411    invalidationCount_--;
    412    if (!invalidationCount_) {
    413      Destroy(gcx, this);
    414    }
    415  }
    416  IonCompilationId compilationId() const { return compilationId_; }
    417  uint32_t incrOsrPcMismatchCounter() { return ++osrPcMismatchCounter_; }
    418  void resetOsrPcMismatchCounter() { osrPcMismatchCounter_ = 0; }
    419 
    420  size_t allocBytes() const { return allocBytes_; }
    421 
    422  static void preWriteBarrier(Zone* zone, IonScript* ionScript);
    423 
    424 #ifdef DEBUG
    425  mozilla::HashNumber icHash() const { return icHash_; }
    426  void setICHash(mozilla::HashNumber hash) { icHash_ = hash; }
    427 #endif
    428 };
    429 
    430 // Execution information for a basic block which may persist after the
    431 // accompanying IonScript is destroyed, for use during profiling.
    432 struct IonBlockCounts {
    433 private:
    434  uint32_t id_;
    435 
    436  // Approximate bytecode in the outer (not inlined) script this block
    437  // was generated from.
    438  uint32_t offset_;
    439 
    440  // File and line of the inner script this block was generated from.
    441  char* description_;
    442 
    443  // ids for successors of this block.
    444  uint32_t numSuccessors_;
    445  uint32_t* successors_;
    446 
    447  // Hit count for this block.
    448  uint64_t hitCount_;
    449 
    450  // Text information about the code generated for this block.
    451  char* code_;
    452 
    453 public:
    454  [[nodiscard]] bool init(uint32_t id, uint32_t offset, char* description,
    455                          uint32_t numSuccessors) {
    456    id_ = id;
    457    offset_ = offset;
    458    description_ = description;
    459    numSuccessors_ = numSuccessors;
    460    if (numSuccessors) {
    461      successors_ = js_pod_calloc<uint32_t>(numSuccessors);
    462      if (!successors_) {
    463        return false;
    464      }
    465    }
    466    return true;
    467  }
    468 
    469  void destroy() {
    470    js_free(description_);
    471    js_free(successors_);
    472    js_free(code_);
    473  }
    474 
    475  uint32_t id() const { return id_; }
    476 
    477  uint32_t offset() const { return offset_; }
    478 
    479  const char* description() const { return description_; }
    480 
    481  size_t numSuccessors() const { return numSuccessors_; }
    482 
    483  void setSuccessor(size_t i, uint32_t id) {
    484    MOZ_ASSERT(i < numSuccessors_);
    485    successors_[i] = id;
    486  }
    487 
    488  uint32_t successor(size_t i) const {
    489    MOZ_ASSERT(i < numSuccessors_);
    490    return successors_[i];
    491  }
    492 
    493  uint64_t* addressOfHitCount() { return &hitCount_; }
    494 
    495  uint64_t hitCount() const { return hitCount_; }
    496 
    497  void setCode(const char* code) {
    498    char* ncode = js_pod_malloc<char>(strlen(code) + 1);
    499    if (ncode) {
    500      strcpy(ncode, code);
    501      code_ = ncode;
    502    }
    503  }
    504 
    505  const char* code() const { return code_; }
    506 
    507  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
    508    return mallocSizeOf(description_) + mallocSizeOf(successors_) +
    509           mallocSizeOf(code_);
    510  }
    511 };
    512 
    513 // Execution information for a compiled script which may persist after the
    514 // IonScript is destroyed, for use during profiling.
    515 struct IonScriptCounts {
    516 private:
    517  // Any previous invalidated compilation(s) for the script.
    518  IonScriptCounts* previous_ = nullptr;
    519 
    520  // Information about basic blocks in this script.
    521  size_t numBlocks_ = 0;
    522  IonBlockCounts* blocks_ = nullptr;
    523 
    524 public:
    525  IonScriptCounts() = default;
    526 
    527  ~IonScriptCounts() {
    528    for (size_t i = 0; i < numBlocks_; i++) {
    529      blocks_[i].destroy();
    530    }
    531    js_free(blocks_);
    532    // The list can be long in some corner cases (bug 1140084), so
    533    // unroll the recursion.
    534    IonScriptCounts* victims = previous_;
    535    while (victims) {
    536      IonScriptCounts* victim = victims;
    537      victims = victim->previous_;
    538      victim->previous_ = nullptr;
    539      js_delete(victim);
    540    }
    541  }
    542 
    543  [[nodiscard]] bool init(size_t numBlocks) {
    544    blocks_ = js_pod_calloc<IonBlockCounts>(numBlocks);
    545    if (!blocks_) {
    546      return false;
    547    }
    548 
    549    numBlocks_ = numBlocks;
    550    return true;
    551  }
    552 
    553  size_t numBlocks() const { return numBlocks_; }
    554 
    555  IonBlockCounts& block(size_t i) {
    556    MOZ_ASSERT(i < numBlocks_);
    557    return blocks_[i];
    558  }
    559 
    560  void setPrevious(IonScriptCounts* previous) { previous_ = previous; }
    561 
    562  IonScriptCounts* previous() const { return previous_; }
    563 
    564  size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
    565    size_t size = 0;
    566    auto currCounts = this;
    567    do {
    568      size += currCounts->sizeOfOneIncludingThis(mallocSizeOf);
    569      currCounts = currCounts->previous_;
    570    } while (currCounts);
    571    return size;
    572  }
    573 
    574  size_t sizeOfOneIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
    575    size_t size = mallocSizeOf(this) + mallocSizeOf(blocks_);
    576    for (size_t i = 0; i < numBlocks_; i++) {
    577      blocks_[i].sizeOfExcludingThis(mallocSizeOf);
    578    }
    579    return size;
    580  }
    581 };
    582 
    583 }  // namespace jit
    584 }  // namespace js
    585 
    586 namespace JS {
    587 
    588 template <>
    589 struct DeletePolicy<js::jit::IonScript> {
    590  explicit DeletePolicy(JSRuntime* rt) : rt_(rt) {}
    591  void operator()(const js::jit::IonScript* script);
    592 
    593 private:
    594  JSRuntime* rt_;
    595 };
    596 
    597 }  // namespace JS
    598 
    599 #endif /* jit_IonScript_h */