tor-browser

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

MozCachingDecoder.h (6591B)


      1 #ifndef VIXL_A64_MOZ_CACHING_DECODER_A64_H_
      2 #define VIXL_A64_MOZ_CACHING_DECODER_A64_H_
      3 
      4 #include "mozilla/HashTable.h"
      5 
      6 #include "jit/arm64/vixl/Decoder-vixl.h"
      7 #include "js/AllocPolicy.h"
      8 
      9 #ifdef DEBUG
     10 #define JS_CACHE_SIMULATOR_ARM64 1
     11 #endif
     12 
     13 #ifdef JS_CACHE_SIMULATOR_ARM64
     14 namespace vixl {
     15 
     16 // This enumeration list the different kind of instructions which can be
     17 // decoded. These kind correspond to the set of visitor defined by the default
     18 // Decoder.
     19 enum class InstDecodedKind : uint8_t {
     20  NotDecodedYet,
     21 #define DECLARE(E) E,
     22  VISITOR_LIST(DECLARE)
     23 #undef DECLARE
     24 };
     25 
     26 // A SinglePageDecodeCache is used to store the decoded kind of all instructions
     27 // in an executable page of code. Each time an instruction is decoded, its
     28 // decoded kind is recorded in this structure. The previous instruction value is
     29 // also recorded in this structure when using a debug build.
     30 //
     31 // The next time the same offset is visited, the instruction would be decoded
     32 // using the previously recorded decode kind. It is also compared against the
     33 // previously recorded bits of the instruction to check for potential missing
     34 // cache invalidations, in debug builds.
     35 //
     36 // This structure stores the equivalent of a single page of code to have better
     37 // memory locality when using the simulator. As opposed to having a hash-table
     38 // for all instructions. However a hash-table is used by the CachingDecoder to
     39 // map the prefixes of page addresses to these SinglePageDecodeCaches.
     40 class SinglePageDecodeCache {
     41 public:
     42  static const uintptr_t PageSize = 1 << 12;
     43  static const uintptr_t PageMask = PageSize - 1;
     44  static const uintptr_t InstSize = vixl::kInstructionSize;
     45  static const uintptr_t InstMask = InstSize - 1;
     46  static const uintptr_t InstPerPage = PageSize / InstSize;
     47 
     48  SinglePageDecodeCache(const Instruction* inst)
     49    : pageStart_(PageStart(inst))
     50  {
     51    memset(&decodeCache_, int(InstDecodedKind::NotDecodedYet), sizeof(decodeCache_));
     52  }
     53  // Compute the start address of the page which contains this instruction.
     54  static uintptr_t PageStart(const Instruction* inst) {
     55    return uintptr_t(inst) & ~PageMask;
     56  }
     57  // Returns whether the instruction decoded kind is stored in this
     58  // SinglePageDecodeCache.
     59  bool contains(const Instruction* inst) {
     60    return pageStart_ == PageStart(inst);
     61  }
     62  void clearDecode(const Instruction* inst) {
     63    uintptr_t offset = (uintptr_t(inst) & PageMask) / InstSize;
     64    decodeCache_[offset] = InstDecodedKind::NotDecodedYet;
     65  }
     66  InstDecodedKind* decodePtr(const Instruction* inst) {
     67    uintptr_t offset = (uintptr_t(inst) & PageMask) / InstSize;
     68    uint32_t instValue = *reinterpret_cast<const uint32_t*>(inst);
     69    instCache_[offset] = instValue;
     70    return &decodeCache_[offset];
     71  }
     72  InstDecodedKind decode(const Instruction* inst) const {
     73    uintptr_t offset = (uintptr_t(inst) & PageMask) / InstSize;
     74    InstDecodedKind val = decodeCache_[offset];
     75    uint32_t instValue = *reinterpret_cast<const uint32_t*>(inst);
     76    MOZ_ASSERT_IF(val != InstDecodedKind::NotDecodedYet,
     77                  instCache_[offset] == instValue);
     78    return val;
     79  }
     80 
     81 private:
     82  // Record the address at which the corresponding code page starts.
     83  const uintptr_t pageStart_;
     84 
     85  // Cache what instruction got decoded previously, in order to assert if we see
     86  // any stale instructions after.
     87  uint32_t instCache_[InstPerPage];
     88 
     89  // Cache the decoding of the instruction such that we can skip the decoding
     90  // part.
     91  InstDecodedKind decodeCache_[InstPerPage];
     92 };
     93 
     94 // A DecoderVisitor which will record which visitor function should be called
     95 // the next time we want to decode the same instruction.
     96 class CachingDecoderVisitor : public DecoderVisitor {
     97 public:
     98  CachingDecoderVisitor() = default;
     99  virtual ~CachingDecoderVisitor() {}
    100 
    101 #define DECLARE(A) virtual void Visit##A(const Instruction* instr) { \
    102    if (last_) { \
    103      MOZ_ASSERT(*last_ == InstDecodedKind::NotDecodedYet); \
    104      *last_ = InstDecodedKind::A; \
    105      last_ = nullptr; \
    106    } \
    107  };
    108 
    109  VISITOR_LIST(DECLARE)
    110 #undef DECLARE
    111 
    112  void setDecodePtr(InstDecodedKind* ptr) {
    113    last_ = ptr;
    114  }
    115 
    116 private:
    117  InstDecodedKind* last_;
    118 };
    119 
    120 // The Caching decoder works by extending the default vixl Decoder class. It
    121 // extends it by overloading the Decode function.
    122 //
    123 // The overloaded Decode function checks whether the instruction given as
    124 // argument got decoded before or since it got invalidated. If it was not
    125 // previously decoded, the value of the instruction is recorded as well as the
    126 // kind of instruction. Otherwise, the value of the instruction is checked
    127 // against the previously recorded value and the instruction kind is used to
    128 // skip the decoding visitor and resume the execution of instruction.
    129 //
    130 // The caching decoder stores the equivalent of a page of executable code in a
    131 // hash-table. Each SinglePageDecodeCache stores an array of decoded kind as
    132 // well as the value of the previously decoded instruction.
    133 //
    134 // When testing if an instruction was decoded before, we check if the address of
    135 // the instruction is contained in the last SinglePageDecodeCache. If it is not,
    136 // then the hash-table entry is queried and created if necessary, and the last
    137 // SinglePageDecodeCache is updated. Then, the last SinglePageDecodeCache
    138 // necessary contains the decoded kind of the instruction given as argument.
    139 //
    140 // The caching decoder add an extra function for flushing the cache, which is in
    141 // charge of clearing the decoded kind of instruction in the range of addresses
    142 // given as argument. This is indirectly called by
    143 // CPU::EnsureIAndDCacheCoherency.
    144 class CachingDecoder : public Decoder {
    145  using ICacheMap = mozilla::HashMap<uintptr_t, SinglePageDecodeCache*>;
    146 public:
    147  CachingDecoder()
    148      : lastPage_(nullptr)
    149  {
    150    PrependVisitor(&cachingDecoder_);
    151  }
    152  ~CachingDecoder() {
    153    RemoveVisitor(&cachingDecoder_);
    154  }
    155 
    156  void Decode(const Instruction* instr);
    157  void Decode(Instruction* instr) {
    158    Decode(const_cast<const Instruction*>(instr));
    159  }
    160 
    161  void FlushICache(void* start, size_t size);
    162 
    163 private:
    164  // Record the type of the decoded instruction, to avoid decoding it a second
    165  // time the next time we execute it.
    166  CachingDecoderVisitor cachingDecoder_;
    167 
    168  // Store the mapping of Instruction pointer to the corresponding
    169  // SinglePageDecodeCache.
    170  ICacheMap iCache_;
    171 
    172  // Record the last SinglePageDecodeCache seen, such that we can quickly access
    173  // it for the next instruction.
    174  SinglePageDecodeCache* lastPage_;
    175 };
    176 
    177 }
    178 #endif // !JS_CACHE_SIMULATOR_ARM64
    179 #endif // !VIXL_A64_MOZ_CACHING_DECODER_A64_H_