tor-browser

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

Snapshots.h (18689B)


      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_Snapshot_h
      8 #define jit_Snapshot_h
      9 
     10 #include "mozilla/Attributes.h"
     11 
     12 #include "jit/CompactBuffer.h"
     13 #include "jit/IonTypes.h"
     14 #include "jit/Registers.h"
     15 #include "js/AllocPolicy.h"
     16 #include "js/HashTable.h"
     17 #include "js/TypeDecls.h"
     18 
     19 namespace js {
     20 class JS_PUBLIC_API GenericPrinter;
     21 
     22 namespace jit {
     23 
     24 class RValueAllocation;
     25 
     26 // A Recover Value Allocation mirror what is known at compiled time as being the
     27 // MIRType and the LAllocation.  This is read out of the snapshot to recover the
     28 // value which would be there if this frame was an interpreter frame instead of
     29 // an Ion frame.
     30 //
     31 // It is used with the SnapshotIterator to recover a Value from the stack,
     32 // spilled registers or the list of constant of the compiled script.
     33 //
     34 // Unit tests are located in jsapi-tests/testJitRValueAlloc.cpp.
     35 class RValueAllocation {
     36 public:
     37  // See RValueAllocation encoding in Snapshots.cpp
     38  enum Mode {
     39    CONSTANT = 0x00,
     40    CST_UNDEFINED = 0x01,
     41    CST_NULL = 0x02,
     42    DOUBLE_REG = 0x03,
     43    FLOAT32_REG = 0x04,
     44    FLOAT32_STACK = 0x05,
     45 #if defined(JS_NUNBOX32)
     46    UNTYPED_REG_REG = 0x06,
     47    UNTYPED_REG_STACK = 0x07,
     48    UNTYPED_STACK_REG = 0x08,
     49    UNTYPED_STACK_STACK = 0x09,
     50 #elif defined(JS_PUNBOX64)
     51    UNTYPED_REG = 0x06,
     52    UNTYPED_STACK = 0x07,
     53 #endif
     54 
     55    // Recover instructions.
     56    RECOVER_INSTRUCTION = 0x0a,
     57    RI_WITH_DEFAULT_CST = 0x0b,
     58 
     59    // IntPtr value.
     60    INTPTR_CST = 0x0c,
     61    INTPTR_REG = 0x0d,
     62    INTPTR_STACK = 0x0e,
     63    INTPTR_INT32_STACK = 0x0f,
     64 
     65    // The JSValueType is packed in the Mode.
     66    TYPED_REG_MIN = 0x10,
     67    TYPED_REG_MAX = 0x1f,
     68    TYPED_REG = TYPED_REG_MIN,
     69 
     70    // The JSValueType is packed in the Mode.
     71    TYPED_STACK_MIN = 0x20,
     72    TYPED_STACK_MAX = 0x2f,
     73    TYPED_STACK = TYPED_STACK_MIN,
     74 
     75    // Int64 value.
     76    INT64_CST = 0x30,
     77 #if defined(JS_NUNBOX32)
     78    INT64_REG_REG = 0x31,
     79    INT64_REG_STACK = 0x32,
     80    INT64_STACK_REG = 0x33,
     81    INT64_STACK_STACK = 0x34,
     82 #elif defined(JS_PUNBOX64)
     83    INT64_REG = 0x31,
     84    INT64_STACK = 0x32,
     85 #endif
     86 
     87    // This mask can be used with any other valid mode. When this flag is
     88    // set on the mode, this inform the snapshot iterator that even if the
     89    // allocation is readable, the content of if might be incomplete unless
     90    // all side-effects are executed.
     91    RECOVER_SIDE_EFFECT_MASK = 0x80,
     92 
     93    // This mask represents the set of bits which can be used to encode a
     94    // value in a snapshot. The mode is used to determine how to interpret
     95    // the union of values and how to pack the value in memory.
     96    MODE_BITS_MASK = 0x17f,
     97 
     98    INVALID = 0x100,
     99  };
    100 
    101  enum { PACKED_TAG_MASK = 0x0f };
    102 
    103  // See Payload encoding in Snapshots.cpp
    104  enum PayloadType {
    105    PAYLOAD_NONE,
    106    PAYLOAD_INDEX,
    107    PAYLOAD_STACK_OFFSET,
    108    PAYLOAD_GPR,
    109    PAYLOAD_FPU,
    110    PAYLOAD_PACKED_TAG
    111  };
    112 
    113  struct Layout {
    114    PayloadType type1;
    115    PayloadType type2;
    116    const char* name;
    117  };
    118 
    119 private:
    120  Mode mode_;
    121 
    122  // Additional information to recover the content of the allocation.
    123  struct FloatRegisterBits {
    124    uint32_t data;
    125    bool operator==(const FloatRegisterBits& other) const {
    126      return data == other.data;
    127    }
    128    uint32_t code() const { return data; }
    129    const char* name() const {
    130      FloatRegister tmp = FloatRegister::FromCode(data);
    131      return tmp.name();
    132    }
    133  };
    134 
    135  union Payload {
    136    uint32_t index;
    137    int32_t stackOffset;
    138    Register gpr;
    139    FloatRegisterBits fpu;
    140    JSValueType type;
    141 
    142    Payload() : index(0) {
    143      static_assert(sizeof(index) == sizeof(Payload),
    144                    "All Payload bits are initialized.");
    145    }
    146  };
    147 
    148  Payload arg1_;
    149  Payload arg2_;
    150 
    151  static Payload payloadOfIndex(uint32_t index) {
    152    Payload p;
    153    p.index = index;
    154    return p;
    155  }
    156  static Payload payloadOfStackOffset(int32_t offset) {
    157    Payload p;
    158    p.stackOffset = offset;
    159    return p;
    160  }
    161  static Payload payloadOfRegister(Register reg) {
    162    Payload p;
    163    p.gpr = reg;
    164    return p;
    165  }
    166  static Payload payloadOfFloatRegister(FloatRegister reg) {
    167    Payload p;
    168    FloatRegisterBits b;
    169    b.data = reg.code();
    170    p.fpu = b;
    171    return p;
    172  }
    173  static Payload payloadOfValueType(JSValueType type) {
    174    Payload p;
    175    p.type = type;
    176    return p;
    177  }
    178 
    179  static const Layout& layoutFromMode(Mode mode);
    180 
    181  static void readPayload(CompactBufferReader& reader, PayloadType t,
    182                          uint8_t* mode, Payload* p);
    183  static void writePayload(CompactBufferWriter& writer, PayloadType t,
    184                           Payload p);
    185  static void writePadding(CompactBufferWriter& writer);
    186 #ifdef JS_JITSPEW
    187  static void dumpPayload(GenericPrinter& out, PayloadType t, Payload p);
    188 #endif
    189  static bool equalPayloads(PayloadType t, Payload lhs, Payload rhs);
    190 
    191  RValueAllocation(Mode mode, Payload a1, Payload a2)
    192      : mode_(mode), arg1_(a1), arg2_(a2) {}
    193 
    194  RValueAllocation(Mode mode, Payload a1) : mode_(mode), arg1_(a1) {
    195    arg2_.index = 0;
    196  }
    197 
    198  explicit RValueAllocation(Mode mode) : mode_(mode) {
    199    arg1_.index = 0;
    200    arg2_.index = 0;
    201  }
    202 
    203 public:
    204  RValueAllocation() : mode_(INVALID) {
    205    arg1_.index = 0;
    206    arg2_.index = 0;
    207  }
    208 
    209  // DOUBLE_REG
    210  static RValueAllocation Double(FloatRegister reg) {
    211    return RValueAllocation(DOUBLE_REG, payloadOfFloatRegister(reg));
    212  }
    213 
    214  // FLOAT32_REG or FLOAT32_STACK
    215  static RValueAllocation Float32(FloatRegister reg) {
    216    return RValueAllocation(FLOAT32_REG, payloadOfFloatRegister(reg));
    217  }
    218  static RValueAllocation Float32(int32_t offset) {
    219    return RValueAllocation(FLOAT32_STACK, payloadOfStackOffset(offset));
    220  }
    221 
    222  // TYPED_REG or TYPED_STACK
    223  static RValueAllocation Typed(JSValueType type, Register reg) {
    224    MOZ_ASSERT(type != JSVAL_TYPE_DOUBLE && type != JSVAL_TYPE_MAGIC &&
    225               type != JSVAL_TYPE_NULL && type != JSVAL_TYPE_UNDEFINED);
    226    return RValueAllocation(TYPED_REG, payloadOfValueType(type),
    227                            payloadOfRegister(reg));
    228  }
    229  static RValueAllocation Typed(JSValueType type, int32_t offset) {
    230    MOZ_ASSERT(type != JSVAL_TYPE_MAGIC && type != JSVAL_TYPE_NULL &&
    231               type != JSVAL_TYPE_UNDEFINED);
    232    return RValueAllocation(TYPED_STACK, payloadOfValueType(type),
    233                            payloadOfStackOffset(offset));
    234  }
    235 
    236  // UNTYPED
    237 #if defined(JS_NUNBOX32)
    238  static RValueAllocation Untyped(Register type, Register payload) {
    239    return RValueAllocation(UNTYPED_REG_REG, payloadOfRegister(type),
    240                            payloadOfRegister(payload));
    241  }
    242 
    243  static RValueAllocation Untyped(Register type, int32_t payloadStackOffset) {
    244    return RValueAllocation(UNTYPED_REG_STACK, payloadOfRegister(type),
    245                            payloadOfStackOffset(payloadStackOffset));
    246  }
    247 
    248  static RValueAllocation Untyped(int32_t typeStackOffset, Register payload) {
    249    return RValueAllocation(UNTYPED_STACK_REG,
    250                            payloadOfStackOffset(typeStackOffset),
    251                            payloadOfRegister(payload));
    252  }
    253 
    254  static RValueAllocation Untyped(int32_t typeStackOffset,
    255                                  int32_t payloadStackOffset) {
    256    return RValueAllocation(UNTYPED_STACK_STACK,
    257                            payloadOfStackOffset(typeStackOffset),
    258                            payloadOfStackOffset(payloadStackOffset));
    259  }
    260 
    261 #elif defined(JS_PUNBOX64)
    262  static RValueAllocation Untyped(Register reg) {
    263    return RValueAllocation(UNTYPED_REG, payloadOfRegister(reg));
    264  }
    265 
    266  static RValueAllocation Untyped(int32_t stackOffset) {
    267    return RValueAllocation(UNTYPED_STACK, payloadOfStackOffset(stackOffset));
    268  }
    269 #endif
    270 
    271  // common constants.
    272  static RValueAllocation Undefined() {
    273    return RValueAllocation(CST_UNDEFINED);
    274  }
    275  static RValueAllocation Null() { return RValueAllocation(CST_NULL); }
    276 
    277  // CONSTANT's index
    278  static RValueAllocation ConstantPool(uint32_t index) {
    279    return RValueAllocation(CONSTANT, payloadOfIndex(index));
    280  }
    281 
    282  // Recover instruction's index
    283  static RValueAllocation RecoverInstruction(uint32_t index) {
    284    return RValueAllocation(RECOVER_INSTRUCTION, payloadOfIndex(index));
    285  }
    286  static RValueAllocation RecoverInstruction(uint32_t riIndex,
    287                                             uint32_t cstIndex) {
    288    return RValueAllocation(RI_WITH_DEFAULT_CST, payloadOfIndex(riIndex),
    289                            payloadOfIndex(cstIndex));
    290  }
    291 
    292  // IntPtr
    293 #if !defined(JS_64BIT)
    294  static RValueAllocation IntPtrConstant(uint32_t index) {
    295    return RValueAllocation(INTPTR_CST, payloadOfIndex(index));
    296  }
    297 #else
    298  static RValueAllocation IntPtrConstant(uint32_t lowIndex,
    299                                         uint32_t highIndex) {
    300    return RValueAllocation(INTPTR_CST, payloadOfIndex(lowIndex),
    301                            payloadOfIndex(highIndex));
    302  }
    303 #endif
    304  static RValueAllocation IntPtr(Register reg) {
    305    return RValueAllocation(INTPTR_REG, payloadOfRegister(reg));
    306  }
    307  static RValueAllocation IntPtr(int32_t offset) {
    308    return RValueAllocation(INTPTR_STACK, payloadOfStackOffset(offset));
    309  }
    310  static RValueAllocation IntPtrInt32(int32_t offset) {
    311    return RValueAllocation(INTPTR_INT32_STACK, payloadOfStackOffset(offset));
    312  }
    313 
    314  // Int64
    315  static RValueAllocation Int64Constant(uint32_t lowIndex, uint32_t highIndex) {
    316    return RValueAllocation(INT64_CST, payloadOfIndex(lowIndex),
    317                            payloadOfIndex(highIndex));
    318  }
    319 #if defined(JS_NUNBOX32)
    320  static RValueAllocation Int64(Register low, Register high) {
    321    return RValueAllocation(INT64_REG_REG, payloadOfRegister(low),
    322                            payloadOfRegister(high));
    323  }
    324 
    325  static RValueAllocation Int64(Register low, int32_t highStackOffset) {
    326    return RValueAllocation(INT64_REG_STACK, payloadOfRegister(low),
    327                            payloadOfStackOffset(highStackOffset));
    328  }
    329 
    330  static RValueAllocation Int64(int32_t lowStackOffset, Register high) {
    331    return RValueAllocation(INT64_STACK_REG,
    332                            payloadOfStackOffset(lowStackOffset),
    333                            payloadOfRegister(high));
    334  }
    335 
    336  static RValueAllocation Int64(int32_t lowStackOffset,
    337                                int32_t highStackOffset) {
    338    return RValueAllocation(INT64_STACK_STACK,
    339                            payloadOfStackOffset(lowStackOffset),
    340                            payloadOfStackOffset(highStackOffset));
    341  }
    342 
    343 #elif defined(JS_PUNBOX64)
    344  static RValueAllocation Int64(Register reg) {
    345    return RValueAllocation(INT64_REG, payloadOfRegister(reg));
    346  }
    347 
    348  static RValueAllocation Int64(int32_t stackOffset) {
    349    return RValueAllocation(INT64_STACK, payloadOfStackOffset(stackOffset));
    350  }
    351 #endif
    352 
    353  void setNeedSideEffect() {
    354    MOZ_ASSERT(!needSideEffect() && mode_ != INVALID);
    355    mode_ = Mode(mode_ | RECOVER_SIDE_EFFECT_MASK);
    356  }
    357 
    358  void writeHeader(CompactBufferWriter& writer, JSValueType type,
    359                   uint32_t regCode) const;
    360 
    361 public:
    362  static RValueAllocation read(CompactBufferReader& reader);
    363  void write(CompactBufferWriter& writer) const;
    364 
    365 public:
    366  bool valid() const { return mode_ != INVALID; }
    367  Mode mode() const { return Mode(mode_ & MODE_BITS_MASK); }
    368  bool needSideEffect() const { return mode_ & RECOVER_SIDE_EFFECT_MASK; }
    369 
    370  uint32_t index() const {
    371    MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_INDEX);
    372    return arg1_.index;
    373  }
    374  int32_t stackOffset() const {
    375    MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_STACK_OFFSET);
    376    return arg1_.stackOffset;
    377  }
    378  Register reg() const {
    379    MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_GPR);
    380    return arg1_.gpr;
    381  }
    382  FloatRegister fpuReg() const {
    383    MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_FPU);
    384    FloatRegisterBits b = arg1_.fpu;
    385    return FloatRegister::FromCode(b.data);
    386  }
    387  JSValueType knownType() const {
    388    MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_PACKED_TAG);
    389    return arg1_.type;
    390  }
    391 
    392  uint32_t index2() const {
    393    MOZ_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_INDEX);
    394    return arg2_.index;
    395  }
    396  int32_t stackOffset2() const {
    397    MOZ_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_STACK_OFFSET);
    398    return arg2_.stackOffset;
    399  }
    400  Register reg2() const {
    401    MOZ_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_GPR);
    402    return arg2_.gpr;
    403  }
    404 
    405 public:
    406 #ifdef JS_JITSPEW
    407  void dump(GenericPrinter& out) const;
    408 #endif
    409 
    410  bool operator==(const RValueAllocation& rhs) const {
    411    // Note, this equality compares the verbatim content of the payload,
    412    // which is made possible because we ensure that the payload content is
    413    // fully initialized during the creation.
    414    static_assert(sizeof(int32_t) == sizeof(Payload),
    415                  "All Payload bits are compared.");
    416    return mode_ == rhs.mode_ && arg1_.index == rhs.arg1_.index &&
    417           arg2_.index == rhs.arg2_.index;
    418  }
    419 
    420  HashNumber hash() const;
    421 
    422  struct Hasher {
    423    using Key = RValueAllocation;
    424    using Lookup = Key;
    425    static HashNumber hash(const Lookup& v) { return v.hash(); }
    426    static bool match(const Key& k, const Lookup& l) { return k == l; }
    427  };
    428 };
    429 
    430 class RecoverWriter;
    431 
    432 // Collects snapshots in a contiguous buffer, which is copied into IonScript
    433 // memory after code generation.
    434 class SnapshotWriter {
    435  CompactBufferWriter writer_;
    436  CompactBufferWriter allocWriter_;
    437 
    438  // Map RValueAllocations to an offset in the allocWriter_ buffer.  This is
    439  // useful as value allocations are repeated frequently.
    440  using RVA = RValueAllocation;
    441  using RValueAllocMap = HashMap<RVA, uint32_t, RVA::Hasher, SystemAllocPolicy>;
    442  RValueAllocMap allocMap_;
    443 
    444  // This is only used to assert sanity.
    445  uint32_t allocWritten_;
    446 
    447  // Used to report size of the snapshot in the spew messages.
    448  SnapshotOffset lastStart_;
    449 
    450 public:
    451  SnapshotWriter();
    452 
    453  SnapshotOffset startSnapshot(RecoverOffset recoverOffset, BailoutKind kind);
    454 #ifdef TRACK_SNAPSHOTS
    455  void trackSnapshot(uint32_t pcOpcode, uint32_t mirOpcode, uint32_t mirId,
    456                     uint32_t lirOpcode, uint32_t lirId);
    457 #endif
    458  [[nodiscard]] bool add(const RValueAllocation& slot);
    459 
    460  uint32_t allocWritten() const { return allocWritten_; }
    461  void endSnapshot();
    462 
    463  bool oom() const {
    464    return writer_.oom() || writer_.length() >= MAX_BUFFER_SIZE ||
    465           allocWriter_.oom() || allocWriter_.length() >= MAX_BUFFER_SIZE;
    466  }
    467 
    468  size_t listSize() const { return writer_.length(); }
    469  const uint8_t* listBuffer() const { return writer_.buffer(); }
    470 
    471  size_t RVATableSize() const { return allocWriter_.length(); }
    472  const uint8_t* RVATableBuffer() const { return allocWriter_.buffer(); }
    473 };
    474 
    475 class MNode;
    476 
    477 class RecoverWriter {
    478  CompactBufferWriter writer_;
    479 
    480  uint32_t instructionCount_;
    481  uint32_t instructionsWritten_;
    482 
    483 public:
    484  SnapshotOffset startRecover(uint32_t instructionCount);
    485 
    486  void writeInstruction(const MNode* rp);
    487 
    488  void endRecover();
    489 
    490  size_t size() const { return writer_.length(); }
    491  const uint8_t* buffer() const { return writer_.buffer(); }
    492 
    493  bool oom() const {
    494    return writer_.oom() || writer_.length() >= MAX_BUFFER_SIZE;
    495  }
    496 };
    497 
    498 class RecoverReader;
    499 
    500 // A snapshot reader reads the entries out of the compressed snapshot buffer in
    501 // a script. These entries describe the equivalent interpreter frames at a given
    502 // position in JIT code. Each entry is an Ion's value allocations, used to
    503 // recover the corresponding Value from an Ion frame.
    504 class SnapshotReader {
    505  CompactBufferReader reader_;
    506  CompactBufferReader allocReader_;
    507  const uint8_t* allocTable_;
    508 
    509  BailoutKind bailoutKind_;
    510  uint32_t allocRead_;           // Number of slots that have been read.
    511  RecoverOffset recoverOffset_;  // Offset of the recover instructions.
    512 
    513 #ifdef TRACK_SNAPSHOTS
    514 private:
    515  uint32_t pcOpcode_;
    516  uint32_t mirOpcode_;
    517  uint32_t mirId_;
    518  uint32_t lirOpcode_;
    519  uint32_t lirId_;
    520 
    521 public:
    522  void readTrackSnapshot();
    523  void spewBailingFrom() const;
    524 #endif
    525 
    526 private:
    527  void readSnapshotHeader();
    528  uint32_t readAllocationIndex();
    529 
    530 public:
    531  SnapshotReader(const uint8_t* snapshots, uint32_t offset,
    532                 uint32_t RVATableSize, uint32_t listSize);
    533 
    534  RValueAllocation readAllocation();
    535  void skipAllocation() { readAllocationIndex(); }
    536 
    537  BailoutKind bailoutKind() const { return bailoutKind_; }
    538  RecoverOffset recoverOffset() const { return recoverOffset_; }
    539 
    540  uint32_t numAllocationsRead() const { return allocRead_; }
    541  void resetNumAllocationsRead() { allocRead_ = 0; }
    542 };
    543 
    544 class MOZ_NON_PARAM RInstructionStorage {
    545  static constexpr size_t Size = 4 * sizeof(uint32_t);
    546 
    547  // This presumes all RInstructionStorage are safely void*-alignable.
    548  // RInstruction::readRecoverData asserts that no RInstruction subclass
    549  // has stricter alignment requirements than RInstructionStorage.
    550  static constexpr size_t Alignment = alignof(void*);
    551 
    552  alignas(Alignment) unsigned char mem[Size];
    553 
    554 public:
    555  const void* addr() const { return mem; }
    556  void* addr() { return mem; }
    557 
    558  RInstructionStorage() = default;
    559 
    560  // Making a copy of raw bytes holding a RInstruction instance would be a
    561  // strict aliasing violation: see bug 1269319 for an instance of bytewise
    562  // copying having caused crashes.
    563  RInstructionStorage(const RInstructionStorage&) = delete;
    564  RInstructionStorage& operator=(const RInstructionStorage& other) = delete;
    565 };
    566 
    567 class RInstruction;
    568 
    569 class RecoverReader {
    570  CompactBufferReader reader_;
    571 
    572  // Number of encoded instructions.
    573  uint32_t numInstructions_;
    574 
    575  // Number of instruction read.
    576  uint32_t numInstructionsRead_;
    577 
    578  // Space is reserved as part of the RecoverReader to avoid allocations of
    579  // data which is needed to decode the current instruction.
    580  RInstructionStorage rawData_;
    581 
    582 private:
    583  void readRecoverHeader();
    584  void readInstruction();
    585 
    586 public:
    587  RecoverReader(SnapshotReader& snapshot, const uint8_t* recovers,
    588                uint32_t size);
    589  explicit RecoverReader(const RecoverReader& rr);
    590  RecoverReader& operator=(const RecoverReader& rr);
    591 
    592  uint32_t numInstructions() const { return numInstructions_; }
    593  uint32_t numInstructionsRead() const { return numInstructionsRead_; }
    594 
    595  bool moreInstructions() const {
    596    return numInstructionsRead_ < numInstructions_;
    597  }
    598  void nextInstruction() { readInstruction(); }
    599 
    600  const RInstruction* instruction() const {
    601    return reinterpret_cast<const RInstruction*>(rawData_.addr());
    602  }
    603 };
    604 
    605 }  // namespace jit
    606 }  // namespace js
    607 
    608 #endif /* jit_Snapshot_h */