tor-browser

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

IonAssemblerBuffer.h (11561B)


      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_shared_IonAssemblerBuffer_h
      8 #define jit_shared_IonAssemblerBuffer_h
      9 
     10 #include "mozilla/Assertions.h"
     11 #include "mozilla/MathAlgorithms.h"
     12 
     13 #include <algorithm>
     14 #include <compare>  // std::strong_ordering
     15 
     16 #include "jit/ProcessExecutableMemory.h"
     17 #include "jit/shared/Assembler-shared.h"
     18 
     19 namespace js {
     20 namespace jit {
     21 
     22 // The offset into a buffer, in bytes.
     23 class BufferOffset {
     24  int offset;
     25 
     26 public:
     27  friend BufferOffset nextOffset();
     28 
     29  constexpr BufferOffset() : offset(INT_MIN) {}
     30 
     31  explicit BufferOffset(int offset_) : offset(offset_) {
     32    MOZ_ASSERT(offset >= 0);
     33  }
     34 
     35  explicit BufferOffset(Label* l) : offset(l->offset()) {
     36    MOZ_ASSERT(offset >= 0);
     37  }
     38 
     39  int getOffset() const { return offset; }
     40  bool assigned() const { return offset != INT_MIN; }
     41 
     42  // A BOffImm is a Branch Offset Immediate. It is an architecture-specific
     43  // structure that holds the immediate for a pc relative branch. diffB takes
     44  // the label for the destination of the branch, and encodes the immediate
     45  // for the branch. This will need to be fixed up later, since A pool may be
     46  // inserted between the branch and its destination.
     47  template <class BOffImm>
     48  BOffImm diffB(BufferOffset other) const {
     49    if (!BOffImm::IsInRange(offset - other.offset)) {
     50      return BOffImm();
     51    }
     52    return BOffImm(offset - other.offset);
     53  }
     54 
     55  template <class BOffImm>
     56  BOffImm diffB(Label* other) const {
     57    MOZ_ASSERT(other->bound());
     58    if (!BOffImm::IsInRange(offset - other->offset())) {
     59      return BOffImm();
     60    }
     61    return BOffImm(offset - other->offset());
     62  }
     63 
     64  constexpr auto operator<=>(const BufferOffset& other) const = default;
     65 };
     66 
     67 template <int SliceSize>
     68 class BufferSlice {
     69 protected:
     70  BufferSlice<SliceSize>* prev_;
     71  BufferSlice<SliceSize>* next_;
     72 
     73  size_t bytelength_;
     74 
     75 public:
     76  mozilla::Array<uint8_t, SliceSize> instructions;
     77 
     78 public:
     79  explicit BufferSlice() : prev_(nullptr), next_(nullptr), bytelength_(0) {}
     80 
     81  size_t length() const { return bytelength_; }
     82  static inline size_t Capacity() { return SliceSize; }
     83 
     84  BufferSlice* getNext() const { return next_; }
     85  BufferSlice* getPrev() const { return prev_; }
     86 
     87  void setNext(BufferSlice<SliceSize>* next) {
     88    MOZ_ASSERT(next_ == nullptr);
     89    MOZ_ASSERT(next->prev_ == nullptr);
     90    next_ = next;
     91    next->prev_ = this;
     92  }
     93 
     94  void putBytes(size_t numBytes, const void* source) {
     95    MOZ_ASSERT(bytelength_ + numBytes <= SliceSize);
     96    if (source) {
     97      memcpy(&instructions[length()], source, numBytes);
     98    }
     99    bytelength_ += numBytes;
    100  }
    101 
    102  MOZ_ALWAYS_INLINE
    103  void putU32Aligned(uint32_t value) {
    104    MOZ_ASSERT(bytelength_ + 4 <= SliceSize);
    105    MOZ_ASSERT((bytelength_ & 3) == 0);
    106    MOZ_ASSERT((uintptr_t(&instructions[0]) & 3) == 0);
    107    *reinterpret_cast<uint32_t*>(&instructions[bytelength_]) = value;
    108    bytelength_ += 4;
    109  }
    110 };
    111 
    112 template <int SliceSize, class Inst>
    113 class AssemblerBuffer {
    114 protected:
    115  using Slice = BufferSlice<SliceSize>;
    116 
    117  // Doubly-linked list of BufferSlices, with the most recent in tail position.
    118  Slice* head;
    119  Slice* tail;
    120 
    121  bool m_oom;
    122 
    123  // How many bytes has been committed to the buffer thus far.
    124  // Does not include tail.
    125  uint32_t bufferSize;
    126 
    127  // How many bytes can be in the buffer.  Normally this is
    128  // MaxCodeBytesPerBuffer, but for pasteup buffers where we handle far jumps
    129  // explicitly it can be larger.
    130  uint32_t maxSize;
    131 
    132  // Finger for speeding up accesses.
    133  Slice* finger;
    134  int finger_offset;
    135 
    136  LifoAlloc lifoAlloc_;
    137 
    138 public:
    139  explicit AssemblerBuffer()
    140      : head(nullptr),
    141        tail(nullptr),
    142        m_oom(false),
    143        bufferSize(0),
    144        maxSize(MaxCodeBytesPerBuffer),
    145        finger(nullptr),
    146        finger_offset(0),
    147        lifoAlloc_(8192, js::BackgroundMallocArena) {}
    148 
    149 public:
    150  bool isAligned(size_t alignment) const {
    151    MOZ_ASSERT(mozilla::IsPowerOfTwo(alignment));
    152    return !(size() & (alignment - 1));
    153  }
    154 
    155  void setUnlimited() { maxSize = MaxCodeBytesPerProcess; }
    156 
    157 private:
    158  Slice* newSlice(LifoAlloc& a) {
    159    if (size() > maxSize - sizeof(Slice)) {
    160      fail_oom();
    161      return nullptr;
    162    }
    163    Slice* tmp = static_cast<Slice*>(a.alloc(sizeof(Slice)));
    164    if (!tmp) {
    165      fail_oom();
    166      return nullptr;
    167    }
    168    return new (tmp) Slice;
    169  }
    170 
    171 public:
    172  bool ensureSpace(size_t size) {
    173    // Space can exist in the most recent Slice.
    174    if (tail && tail->length() + size <= tail->Capacity()) {
    175      // Simulate allocation failure even when we don't need a new slice.
    176      if (js::oom::ShouldFailWithOOM()) {
    177        return fail_oom();
    178      }
    179 
    180      return true;
    181    }
    182 
    183    // Otherwise, a new Slice must be added.
    184    Slice* slice = newSlice(lifoAlloc_);
    185    if (slice == nullptr) {
    186      return fail_oom();
    187    }
    188 
    189    // If this is the first Slice in the buffer, add to head position.
    190    if (!head) {
    191      head = slice;
    192      finger = slice;
    193      finger_offset = 0;
    194    }
    195 
    196    // Finish the last Slice and add the new Slice to the linked list.
    197    if (tail) {
    198      bufferSize += tail->length();
    199      tail->setNext(slice);
    200    }
    201    tail = slice;
    202 
    203    return true;
    204  }
    205 
    206  BufferOffset putByte(uint8_t value) {
    207    return putBytes(sizeof(value), &value);
    208  }
    209 
    210  BufferOffset putShort(uint16_t value) {
    211    return putBytes(sizeof(value), &value);
    212  }
    213 
    214  BufferOffset putInt(uint32_t value) {
    215    return putBytes(sizeof(value), &value);
    216  }
    217 
    218  MOZ_ALWAYS_INLINE
    219  BufferOffset putU32Aligned(uint32_t value) {
    220    if (!ensureSpace(sizeof(value))) {
    221      return BufferOffset();
    222    }
    223 
    224    BufferOffset ret = nextOffset();
    225    tail->putU32Aligned(value);
    226    return ret;
    227  }
    228 
    229  // Add numBytes bytes to this buffer.
    230  // The data must fit in a single slice.
    231  BufferOffset putBytes(size_t numBytes, const void* inst) {
    232    if (!ensureSpace(numBytes)) {
    233      return BufferOffset();
    234    }
    235 
    236    BufferOffset ret = nextOffset();
    237    tail->putBytes(numBytes, inst);
    238    return ret;
    239  }
    240 
    241  // Add a potentially large amount of data to this buffer.
    242  // The data may be distrubuted across multiple slices.
    243  // Return the buffer offset of the first added byte.
    244  BufferOffset putBytesLarge(size_t numBytes, const void* data) {
    245    BufferOffset ret = nextOffset();
    246    while (numBytes > 0) {
    247      if (!ensureSpace(1)) {
    248        return BufferOffset();
    249      }
    250      size_t avail = tail->Capacity() - tail->length();
    251      size_t xfer = numBytes < avail ? numBytes : avail;
    252      MOZ_ASSERT(xfer > 0, "ensureSpace should have allocated a slice");
    253      tail->putBytes(xfer, data);
    254      data = (const uint8_t*)data + xfer;
    255      numBytes -= xfer;
    256    }
    257    return ret;
    258  }
    259 
    260  unsigned int size() const {
    261    if (tail) {
    262      return bufferSize + tail->length();
    263    }
    264    return bufferSize;
    265  }
    266  BufferOffset nextOffset() const { return BufferOffset(size()); }
    267 
    268  bool oom() const { return m_oom; }
    269 
    270  bool fail_oom() {
    271    m_oom = true;
    272 #ifdef DEBUG
    273    JitContext* context = MaybeGetJitContext();
    274    if (context) {
    275      context->setOOM();
    276    }
    277 #endif
    278    return false;
    279  }
    280 
    281 private:
    282  void update_finger(Slice* finger_, int fingerOffset_) {
    283    finger = finger_;
    284    finger_offset = fingerOffset_;
    285  }
    286 
    287  static const unsigned SliceDistanceRequiringFingerUpdate = 3;
    288 
    289  Inst* getInstForwards(BufferOffset off, Slice* start, int startOffset,
    290                        bool updateFinger = false) {
    291    const int offset = off.getOffset();
    292 
    293    int cursor = startOffset;
    294    unsigned slicesSkipped = 0;
    295 
    296    MOZ_ASSERT(offset >= cursor);
    297 
    298    for (Slice* slice = start; slice != nullptr; slice = slice->getNext()) {
    299      const int slicelen = slice->length();
    300 
    301      // Is the offset within the bounds of this slice?
    302      if (offset < cursor + slicelen) {
    303        if (updateFinger ||
    304            slicesSkipped >= SliceDistanceRequiringFingerUpdate) {
    305          update_finger(slice, cursor);
    306        }
    307 
    308        MOZ_ASSERT(offset - cursor < (int)slice->length());
    309        return (Inst*)&slice->instructions[offset - cursor];
    310      }
    311 
    312      cursor += slicelen;
    313      slicesSkipped++;
    314    }
    315 
    316    MOZ_CRASH("Invalid instruction cursor.");
    317  }
    318 
    319  Inst* getInstBackwards(BufferOffset off, Slice* start, int startOffset,
    320                         bool updateFinger = false) {
    321    const int offset = off.getOffset();
    322 
    323    int cursor = startOffset;  // First (lowest) offset in the start Slice.
    324    unsigned slicesSkipped = 0;
    325 
    326    MOZ_ASSERT(offset < int(cursor + start->length()));
    327 
    328    for (Slice* slice = start; slice != nullptr;) {
    329      // Is the offset within the bounds of this slice?
    330      if (offset >= cursor) {
    331        if (updateFinger ||
    332            slicesSkipped >= SliceDistanceRequiringFingerUpdate) {
    333          update_finger(slice, cursor);
    334        }
    335 
    336        MOZ_ASSERT(offset - cursor < (int)slice->length());
    337        return (Inst*)&slice->instructions[offset - cursor];
    338      }
    339 
    340      // Move the cursor to the start of the previous slice.
    341      Slice* prev = slice->getPrev();
    342      cursor -= prev->length();
    343 
    344      slice = prev;
    345      slicesSkipped++;
    346    }
    347 
    348    MOZ_CRASH("Invalid instruction cursor.");
    349  }
    350 
    351 public:
    352  Inst* getInstOrNull(BufferOffset off) {
    353    if (!off.assigned()) {
    354      return nullptr;
    355    }
    356    return getInst(off);
    357  }
    358 
    359  // Get a pointer to the instruction at offset |off| which must be within the
    360  // bounds of the buffer. Use |getInstOrNull()| if |off| may be unassigned.
    361  Inst* getInst(BufferOffset off) {
    362    const int offset = off.getOffset();
    363    // This function is hot, do not make the next line a RELEASE_ASSERT.
    364    MOZ_ASSERT(off.assigned() && offset >= 0 && unsigned(offset) < size());
    365 
    366    // Is the instruction in the last slice?
    367    if (offset >= int(bufferSize)) {
    368      return (Inst*)&tail->instructions[offset - bufferSize];
    369    }
    370 
    371    // How close is this offset to the previous one we looked up?
    372    // If it is sufficiently far from the start and end of the buffer,
    373    // use the finger to start midway through the list.
    374    int finger_dist = abs(offset - finger_offset);
    375    if (finger_dist < std::min(offset, int(bufferSize - offset))) {
    376      if (finger_offset < offset) {
    377        return getInstForwards(off, finger, finger_offset, true);
    378      }
    379      return getInstBackwards(off, finger, finger_offset, true);
    380    }
    381 
    382    // Is the instruction closer to the start or to the end?
    383    if (offset < int(bufferSize - offset)) {
    384      return getInstForwards(off, head, 0);
    385    }
    386 
    387    // The last slice was already checked above, so start at the
    388    // second-to-last.
    389    Slice* prev = tail->getPrev();
    390    return getInstBackwards(off, prev, bufferSize - prev->length());
    391  }
    392 
    393  using ThisClass = AssemblerBuffer<SliceSize, Inst>;
    394 
    395  class AssemblerBufferInstIterator {
    396    BufferOffset bo_;
    397    ThisClass* buffer_;
    398 
    399   public:
    400    explicit AssemblerBufferInstIterator(BufferOffset bo, ThisClass* buffer)
    401        : bo_(bo), buffer_(buffer) {}
    402    void advance(int offset) { bo_ = BufferOffset(bo_.getOffset() + offset); }
    403    Inst* next() {
    404      advance(cur()->size());
    405      return cur();
    406    }
    407    Inst* peek() {
    408      return buffer_->getInst(BufferOffset(bo_.getOffset() + cur()->size()));
    409    }
    410    Inst* cur() const { return buffer_->getInst(bo_); }
    411  };
    412 };
    413 
    414 }  // namespace jit
    415 }  // namespace js
    416 
    417 #endif  // jit_shared_IonAssemblerBuffer_h