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