summaryrefslogtreecommitdiffstats
path: root/js/src/jit/arm64/vixl/MozCachingDecoder.h
blob: 5b4cfc17d503dd65e7830489de99d735c4f495f4 (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
#ifndef VIXL_A64_MOZ_CACHING_DECODER_A64_H_
#define VIXL_A64_MOZ_CACHING_DECODER_A64_H_

#include "mozilla/HashTable.h"

#include "jit/arm64/vixl/Decoder-vixl.h"
#include "js/AllocPolicy.h"

#ifdef DEBUG
#define JS_CACHE_SIMULATOR_ARM64 1
#endif

#ifdef JS_CACHE_SIMULATOR_ARM64
namespace vixl {

// This enumeration list the different kind of instructions which can be
// decoded. These kind correspond to the set of visitor defined by the default
// Decoder.
enum class InstDecodedKind : uint8_t {
  NotDecodedYet,
#define DECLARE(E) E,
  VISITOR_LIST(DECLARE)
#undef DECLARE
};

// A SinglePageDecodeCache is used to store the decoded kind of all instructions
// in an executable page of code. Each time an instruction is decoded, its
// decoded kind is recorded in this structure. The previous instruction value is
// also recorded in this structure when using a debug build.
//
// The next time the same offset is visited, the instruction would be decoded
// using the previously recorded decode kind. It is also compared against the
// previously recorded bits of the instruction to check for potential missing
// cache invalidations, in debug builds.
//
// This structure stores the equivalent of a single page of code to have better
// memory locality when using the simulator. As opposed to having a hash-table
// for all instructions. However a hash-table is used by the CachingDecoder to
// map the prefixes of page addresses to these SinglePageDecodeCaches.
class SinglePageDecodeCache {
 public:
  static const uintptr_t PageSize = 1 << 12;
  static const uintptr_t PageMask = PageSize - 1;
  static const uintptr_t InstSize = vixl::kInstructionSize;
  static const uintptr_t InstMask = InstSize - 1;
  static const uintptr_t InstPerPage = PageSize / InstSize;

  SinglePageDecodeCache(const Instruction* inst)
    : pageStart_(PageStart(inst))
  {
    memset(&decodeCache_, int(InstDecodedKind::NotDecodedYet), sizeof(decodeCache_));
  }
  // Compute the start address of the page which contains this instruction.
  static uintptr_t PageStart(const Instruction* inst) {
    return uintptr_t(inst) & ~PageMask;
  }
  // Returns whether the instruction decoded kind is stored in this
  // SinglePageDecodeCache.
  bool contains(const Instruction* inst) {
    return pageStart_ == PageStart(inst);
  }
  void clearDecode(const Instruction* inst) {
    uintptr_t offset = (uintptr_t(inst) & PageMask) / InstSize;
    decodeCache_[offset] = InstDecodedKind::NotDecodedYet;
  }
  InstDecodedKind* decodePtr(const Instruction* inst) {
    uintptr_t offset = (uintptr_t(inst) & PageMask) / InstSize;
    uint32_t instValue = *reinterpret_cast<const uint32_t*>(inst);
    instCache_[offset] = instValue;
    return &decodeCache_[offset];
  }
  InstDecodedKind decode(const Instruction* inst) const {
    uintptr_t offset = (uintptr_t(inst) & PageMask) / InstSize;
    InstDecodedKind val = decodeCache_[offset];
    uint32_t instValue = *reinterpret_cast<const uint32_t*>(inst);
    MOZ_ASSERT_IF(val != InstDecodedKind::NotDecodedYet,
                  instCache_[offset] == instValue);
    return val;
  }

 private:
  // Record the address at which the corresponding code page starts.
  const uintptr_t pageStart_;

  // Cache what instruction got decoded previously, in order to assert if we see
  // any stale instructions after.
  uint32_t instCache_[InstPerPage];

  // Cache the decoding of the instruction such that we can skip the decoding
  // part.
  InstDecodedKind decodeCache_[InstPerPage];
};

// A DecoderVisitor which will record which visitor function should be called
// the next time we want to decode the same instruction.
class CachingDecoderVisitor : public DecoderVisitor {
 public:
  CachingDecoderVisitor() = default;
  virtual ~CachingDecoderVisitor() {}

#define DECLARE(A) virtual void Visit##A(const Instruction* instr) { \
    if (last_) { \
      MOZ_ASSERT(*last_ == InstDecodedKind::NotDecodedYet); \
      *last_ = InstDecodedKind::A; \
      last_ = nullptr; \
    } \
  };

  VISITOR_LIST(DECLARE)
#undef DECLARE

  void setDecodePtr(InstDecodedKind* ptr) {
    last_ = ptr;
  }

 private:
  InstDecodedKind* last_;
};

// The Caching decoder works by extending the default vixl Decoder class. It
// extends it by overloading the Decode function.
//
// The overloaded Decode function checks whether the instruction given as
// argument got decoded before or since it got invalidated. If it was not
// previously decoded, the value of the instruction is recorded as well as the
// kind of instruction. Otherwise, the value of the instruction is checked
// against the previously recorded value and the instruction kind is used to
// skip the decoding visitor and resume the execution of instruction.
//
// The caching decoder stores the equivalent of a page of executable code in a
// hash-table. Each SinglePageDecodeCache stores an array of decoded kind as
// well as the value of the previously decoded instruction.
//
// When testing if an instruction was decoded before, we check if the address of
// the instruction is contained in the last SinglePageDecodeCache. If it is not,
// then the hash-table entry is queried and created if necessary, and the last
// SinglePageDecodeCache is updated. Then, the last SinglePageDecodeCache
// necessary contains the decoded kind of the instruction given as argument.
//
// The caching decoder add an extra function for flushing the cache, which is in
// charge of clearing the decoded kind of instruction in the range of addresses
// given as argument. This is indirectly called by
// CPU::EnsureIAndDCacheCoherency.
class CachingDecoder : public Decoder {
  using ICacheMap = mozilla::HashMap<uintptr_t, SinglePageDecodeCache*>;
 public:
  CachingDecoder()
      : lastPage_(nullptr)
  {
    PrependVisitor(&cachingDecoder_);
  }
  ~CachingDecoder() {
    RemoveVisitor(&cachingDecoder_);
  }

  void Decode(const Instruction* instr);
  void Decode(Instruction* instr) {
    Decode(const_cast<const Instruction*>(instr));
  }

  void FlushICache(void* start, size_t size);

 private:
  // Record the type of the decoded instruction, to avoid decoding it a second
  // time the next time we execute it.
  CachingDecoderVisitor cachingDecoder_;

  // Store the mapping of Instruction pointer to the corresponding
  // SinglePageDecodeCache.
  ICacheMap iCache_;

  // Record the last SinglePageDecodeCache seen, such that we can quickly access
  // it for the next instruction.
  SinglePageDecodeCache* lastPage_;
};

}
#endif // !JS_CACHE_SIMULATOR_ARM64
#endif // !VIXL_A64_MOZ_CACHING_DECODER_A64_H_