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