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