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_