michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jit_Snapshot_h michael@0: #define jit_Snapshot_h michael@0: michael@0: #include "mozilla/Alignment.h" michael@0: michael@0: #include "jsalloc.h" michael@0: #include "jsbytecode.h" michael@0: michael@0: #include "jit/CompactBuffer.h" michael@0: #include "jit/IonTypes.h" michael@0: #include "jit/Registers.h" michael@0: michael@0: #include "js/HashTable.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: class RValueAllocation; michael@0: michael@0: // A Recover Value Allocation mirror what is known at compiled time as being the michael@0: // MIRType and the LAllocation. This is read out of the snapshot to recover the michael@0: // value which would be there if this frame was an interpreter frame instead of michael@0: // an Ion frame. michael@0: // michael@0: // It is used with the SnapshotIterator to recover a Value from the stack, michael@0: // spilled registers or the list of constant of the compiled script. michael@0: // michael@0: // Unit tests are located in jsapi-tests/testJitRValueAlloc.cpp. michael@0: class RValueAllocation michael@0: { michael@0: public: michael@0: michael@0: // See RValueAllocation encoding in Snapshots.cpp michael@0: enum Mode michael@0: { michael@0: CONSTANT = 0x00, michael@0: CST_UNDEFINED = 0x01, michael@0: CST_NULL = 0x02, michael@0: DOUBLE_REG = 0x03, michael@0: FLOAT32_REG = 0x04, michael@0: FLOAT32_STACK = 0x05, michael@0: #if defined(JS_NUNBOX32) michael@0: UNTYPED_REG_REG = 0x06, michael@0: UNTYPED_REG_STACK = 0x07, michael@0: UNTYPED_STACK_REG = 0x08, michael@0: UNTYPED_STACK_STACK = 0x09, michael@0: #elif defined(JS_PUNBOX64) michael@0: UNTYPED_REG = 0x06, michael@0: UNTYPED_STACK = 0x07, michael@0: #endif michael@0: // The JSValueType is packed in the Mode. michael@0: TYPED_REG_MIN = 0x10, michael@0: TYPED_REG_MAX = 0x17, michael@0: TYPED_REG = TYPED_REG_MIN, michael@0: michael@0: // The JSValueType is packed in the Mode. michael@0: TYPED_STACK_MIN = 0x18, michael@0: TYPED_STACK_MAX = 0x1f, michael@0: TYPED_STACK = TYPED_STACK_MIN, michael@0: michael@0: INVALID = 0x100, michael@0: }; michael@0: michael@0: // See Payload encoding in Snapshots.cpp michael@0: enum PayloadType { michael@0: PAYLOAD_NONE, michael@0: PAYLOAD_INDEX, michael@0: PAYLOAD_STACK_OFFSET, michael@0: PAYLOAD_GPR, michael@0: PAYLOAD_FPU, michael@0: PAYLOAD_PACKED_TAG michael@0: }; michael@0: michael@0: struct Layout { michael@0: PayloadType type1; michael@0: PayloadType type2; michael@0: const char *name; michael@0: }; michael@0: michael@0: private: michael@0: Mode mode_; michael@0: michael@0: // Additional information to recover the content of the allocation. michael@0: union Payload { michael@0: uint32_t index; michael@0: int32_t stackOffset; michael@0: Register gpr; michael@0: FloatRegister fpu; michael@0: JSValueType type; michael@0: }; michael@0: michael@0: Payload arg1_; michael@0: Payload arg2_; michael@0: michael@0: static Payload payloadOfIndex(uint32_t index) { michael@0: Payload p; michael@0: p.index = index; michael@0: return p; michael@0: } michael@0: static Payload payloadOfStackOffset(int32_t offset) { michael@0: Payload p; michael@0: p.stackOffset = offset; michael@0: return p; michael@0: } michael@0: static Payload payloadOfRegister(Register reg) { michael@0: Payload p; michael@0: p.gpr = reg; michael@0: return p; michael@0: } michael@0: static Payload payloadOfFloatRegister(FloatRegister reg) { michael@0: Payload p; michael@0: p.fpu = reg; michael@0: return p; michael@0: } michael@0: static Payload payloadOfValueType(JSValueType type) { michael@0: Payload p; michael@0: p.type = type; michael@0: return p; michael@0: } michael@0: michael@0: static const Layout &layoutFromMode(Mode mode); michael@0: michael@0: static void readPayload(CompactBufferReader &reader, PayloadType t, michael@0: uint8_t *mode, Payload *p); michael@0: static void writePayload(CompactBufferWriter &writer, PayloadType t, michael@0: Payload p); michael@0: static void writePadding(CompactBufferWriter &writer); michael@0: static void dumpPayload(FILE *fp, PayloadType t, Payload p); michael@0: static bool equalPayloads(PayloadType t, Payload lhs, Payload rhs); michael@0: michael@0: RValueAllocation(Mode mode, Payload a1, Payload a2) michael@0: : mode_(mode), michael@0: arg1_(a1), michael@0: arg2_(a2) michael@0: { michael@0: } michael@0: michael@0: RValueAllocation(Mode mode, Payload a1) michael@0: : mode_(mode), michael@0: arg1_(a1) michael@0: { michael@0: } michael@0: michael@0: RValueAllocation(Mode mode) michael@0: : mode_(mode) michael@0: { michael@0: } michael@0: michael@0: public: michael@0: RValueAllocation() michael@0: : mode_(INVALID) michael@0: { } michael@0: michael@0: // DOUBLE_REG michael@0: static RValueAllocation Double(const FloatRegister ®) { michael@0: return RValueAllocation(DOUBLE_REG, payloadOfFloatRegister(reg)); michael@0: } michael@0: michael@0: // FLOAT32_REG or FLOAT32_STACK michael@0: static RValueAllocation Float32(const FloatRegister ®) { michael@0: return RValueAllocation(FLOAT32_REG, payloadOfFloatRegister(reg)); michael@0: } michael@0: static RValueAllocation Float32(int32_t offset) { michael@0: return RValueAllocation(FLOAT32_STACK, payloadOfStackOffset(offset)); michael@0: } michael@0: michael@0: // TYPED_REG or TYPED_STACK michael@0: static RValueAllocation Typed(JSValueType type, const Register ®) { michael@0: JS_ASSERT(type != JSVAL_TYPE_DOUBLE && michael@0: type != JSVAL_TYPE_MAGIC && michael@0: type != JSVAL_TYPE_NULL && michael@0: type != JSVAL_TYPE_UNDEFINED); michael@0: return RValueAllocation(TYPED_REG, payloadOfValueType(type), michael@0: payloadOfRegister(reg)); michael@0: } michael@0: static RValueAllocation Typed(JSValueType type, int32_t offset) { michael@0: JS_ASSERT(type != JSVAL_TYPE_MAGIC && michael@0: type != JSVAL_TYPE_NULL && michael@0: type != JSVAL_TYPE_UNDEFINED); michael@0: return RValueAllocation(TYPED_STACK, payloadOfValueType(type), michael@0: payloadOfStackOffset(offset)); michael@0: } michael@0: michael@0: // UNTYPED michael@0: #if defined(JS_NUNBOX32) michael@0: static RValueAllocation Untyped(const Register &type, const Register &payload) { michael@0: return RValueAllocation(UNTYPED_REG_REG, michael@0: payloadOfRegister(type), michael@0: payloadOfRegister(payload)); michael@0: } michael@0: michael@0: static RValueAllocation Untyped(const Register &type, int32_t payloadStackOffset) { michael@0: return RValueAllocation(UNTYPED_REG_STACK, michael@0: payloadOfRegister(type), michael@0: payloadOfStackOffset(payloadStackOffset)); michael@0: } michael@0: michael@0: static RValueAllocation Untyped(int32_t typeStackOffset, const Register &payload) { michael@0: return RValueAllocation(UNTYPED_STACK_REG, michael@0: payloadOfStackOffset(typeStackOffset), michael@0: payloadOfRegister(payload)); michael@0: } michael@0: michael@0: static RValueAllocation Untyped(int32_t typeStackOffset, int32_t payloadStackOffset) { michael@0: return RValueAllocation(UNTYPED_STACK_STACK, michael@0: payloadOfStackOffset(typeStackOffset), michael@0: payloadOfStackOffset(payloadStackOffset)); michael@0: } michael@0: michael@0: #elif defined(JS_PUNBOX64) michael@0: static RValueAllocation Untyped(const Register ®) { michael@0: return RValueAllocation(UNTYPED_REG, payloadOfRegister(reg)); michael@0: } michael@0: michael@0: static RValueAllocation Untyped(int32_t stackOffset) { michael@0: return RValueAllocation(UNTYPED_STACK, payloadOfStackOffset(stackOffset)); michael@0: } michael@0: #endif michael@0: michael@0: // common constants. michael@0: static RValueAllocation Undefined() { michael@0: return RValueAllocation(CST_UNDEFINED); michael@0: } michael@0: static RValueAllocation Null() { michael@0: return RValueAllocation(CST_NULL); michael@0: } michael@0: michael@0: // CONSTANT's index michael@0: static RValueAllocation ConstantPool(uint32_t index) { michael@0: return RValueAllocation(CONSTANT, payloadOfIndex(index)); michael@0: } michael@0: michael@0: void writeHeader(CompactBufferWriter &writer, JSValueType type, uint32_t regCode) const; michael@0: public: michael@0: static RValueAllocation read(CompactBufferReader &reader); michael@0: void write(CompactBufferWriter &writer) const; michael@0: michael@0: public: michael@0: Mode mode() const { michael@0: return mode_; michael@0: } michael@0: michael@0: uint32_t index() const { michael@0: JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_INDEX); michael@0: return arg1_.index; michael@0: } michael@0: int32_t stackOffset() const { michael@0: JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_STACK_OFFSET); michael@0: return arg1_.stackOffset; michael@0: } michael@0: Register reg() const { michael@0: JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_GPR); michael@0: return arg1_.gpr; michael@0: } michael@0: FloatRegister fpuReg() const { michael@0: JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_FPU); michael@0: return arg1_.fpu; michael@0: } michael@0: JSValueType knownType() const { michael@0: JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_PACKED_TAG); michael@0: return arg1_.type; michael@0: } michael@0: michael@0: int32_t stackOffset2() const { michael@0: JS_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_STACK_OFFSET); michael@0: return arg2_.stackOffset; michael@0: } michael@0: Register reg2() const { michael@0: JS_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_GPR); michael@0: return arg2_.gpr; michael@0: } michael@0: michael@0: public: michael@0: void dump(FILE *fp) const; michael@0: michael@0: public: michael@0: bool operator==(const RValueAllocation &rhs) const { michael@0: if (mode_ != rhs.mode_) michael@0: return false; michael@0: michael@0: const Layout &layout = layoutFromMode(mode()); michael@0: return equalPayloads(layout.type1, arg1_, rhs.arg1_) && michael@0: equalPayloads(layout.type2, arg2_, rhs.arg2_); michael@0: } michael@0: michael@0: HashNumber hash() const; michael@0: michael@0: struct Hasher michael@0: { michael@0: typedef RValueAllocation Key; michael@0: typedef Key Lookup; michael@0: static HashNumber hash(const Lookup &v) { michael@0: return v.hash(); michael@0: } michael@0: static bool match(const Key &k, const Lookup &l) { michael@0: return k == l; michael@0: } michael@0: }; michael@0: }; michael@0: michael@0: class RecoverWriter; michael@0: michael@0: // Collects snapshots in a contiguous buffer, which is copied into IonScript michael@0: // memory after code generation. michael@0: class SnapshotWriter michael@0: { michael@0: CompactBufferWriter writer_; michael@0: CompactBufferWriter allocWriter_; michael@0: michael@0: // Map RValueAllocations to an offset in the allocWriter_ buffer. This is michael@0: // useful as value allocations are repeated frequently. michael@0: typedef RValueAllocation RVA; michael@0: typedef HashMap RValueAllocMap; michael@0: RValueAllocMap allocMap_; michael@0: michael@0: // This is only used to assert sanity. michael@0: uint32_t allocWritten_; michael@0: michael@0: // Used to report size of the snapshot in the spew messages. michael@0: SnapshotOffset lastStart_; michael@0: michael@0: public: michael@0: bool init(); michael@0: michael@0: SnapshotOffset startSnapshot(RecoverOffset recoverOffset, BailoutKind kind); michael@0: #ifdef TRACK_SNAPSHOTS michael@0: void trackSnapshot(uint32_t pcOpcode, uint32_t mirOpcode, uint32_t mirId, michael@0: uint32_t lirOpcode, uint32_t lirId); michael@0: #endif michael@0: bool add(const RValueAllocation &slot); michael@0: michael@0: uint32_t allocWritten() const { michael@0: return allocWritten_; michael@0: } michael@0: void endSnapshot(); michael@0: michael@0: bool oom() const { michael@0: return writer_.oom() || writer_.length() >= MAX_BUFFER_SIZE || michael@0: allocWriter_.oom() || allocWriter_.length() >= MAX_BUFFER_SIZE; michael@0: } michael@0: michael@0: size_t listSize() const { michael@0: return writer_.length(); michael@0: } michael@0: const uint8_t *listBuffer() const { michael@0: return writer_.buffer(); michael@0: } michael@0: michael@0: size_t RVATableSize() const { michael@0: return allocWriter_.length(); michael@0: } michael@0: const uint8_t *RVATableBuffer() const { michael@0: return allocWriter_.buffer(); michael@0: } michael@0: }; michael@0: michael@0: class MResumePoint; michael@0: michael@0: class RecoverWriter michael@0: { michael@0: CompactBufferWriter writer_; michael@0: michael@0: uint32_t nframes_; michael@0: uint32_t framesWritten_; michael@0: michael@0: public: michael@0: SnapshotOffset startRecover(uint32_t frameCount, bool resumeAfter); michael@0: michael@0: bool writeFrame(const MResumePoint *rp); michael@0: michael@0: void endRecover(); michael@0: michael@0: size_t size() const { michael@0: return writer_.length(); michael@0: } michael@0: const uint8_t *buffer() const { michael@0: return writer_.buffer(); michael@0: } michael@0: michael@0: bool oom() const { michael@0: return writer_.oom() || writer_.length() >= MAX_BUFFER_SIZE; michael@0: } michael@0: }; michael@0: michael@0: class RecoverReader; michael@0: michael@0: // A snapshot reader reads the entries out of the compressed snapshot buffer in michael@0: // a script. These entries describe the equivalent interpreter frames at a given michael@0: // position in JIT code. Each entry is an Ion's value allocations, used to michael@0: // recover the corresponding Value from an Ion frame. michael@0: class SnapshotReader michael@0: { michael@0: CompactBufferReader reader_; michael@0: CompactBufferReader allocReader_; michael@0: const uint8_t* allocTable_; michael@0: michael@0: BailoutKind bailoutKind_; michael@0: uint32_t allocRead_; // Number of slots that have been read. michael@0: RecoverOffset recoverOffset_; // Offset of the recover instructions. michael@0: michael@0: #ifdef TRACK_SNAPSHOTS michael@0: private: michael@0: uint32_t pcOpcode_; michael@0: uint32_t mirOpcode_; michael@0: uint32_t mirId_; michael@0: uint32_t lirOpcode_; michael@0: uint32_t lirId_; michael@0: michael@0: public: michael@0: void readTrackSnapshot(); michael@0: void spewBailingFrom() const; michael@0: #endif michael@0: michael@0: private: michael@0: void readSnapshotHeader(); michael@0: uint32_t readAllocationIndex(); michael@0: michael@0: public: michael@0: SnapshotReader(const uint8_t *snapshots, uint32_t offset, michael@0: uint32_t RVATableSize, uint32_t listSize); michael@0: michael@0: RValueAllocation readAllocation(); michael@0: void skipAllocation() { michael@0: readAllocationIndex(); michael@0: } michael@0: michael@0: BailoutKind bailoutKind() const { michael@0: return bailoutKind_; michael@0: } michael@0: RecoverOffset recoverOffset() const { michael@0: return recoverOffset_; michael@0: } michael@0: michael@0: uint32_t numAllocationsRead() const { michael@0: return allocRead_; michael@0: } michael@0: void resetNumAllocationsRead() { michael@0: allocRead_ = 0; michael@0: } michael@0: }; michael@0: michael@0: typedef mozilla::AlignedStorage<4 * sizeof(uint32_t)> RInstructionStorage; michael@0: class RInstruction; michael@0: michael@0: class RecoverReader michael@0: { michael@0: CompactBufferReader reader_; michael@0: michael@0: // Number of encoded instructions. michael@0: uint32_t numInstructions_; michael@0: michael@0: // Number of instruction read. michael@0: uint32_t numInstructionsRead_; michael@0: michael@0: // True if we need to resume after the Resume Point instruction of the michael@0: // innermost frame. michael@0: bool resumeAfter_; michael@0: michael@0: // Space is reserved as part of the RecoverReader to avoid allocations of michael@0: // data which is needed to decode the current instruction. michael@0: RInstructionStorage rawData_; michael@0: michael@0: private: michael@0: void readRecoverHeader(); michael@0: void readInstruction(); michael@0: michael@0: public: michael@0: RecoverReader(SnapshotReader &snapshot, const uint8_t *recovers, uint32_t size); michael@0: michael@0: bool moreInstructions() const { michael@0: return numInstructionsRead_ < numInstructions_; michael@0: } michael@0: void nextInstruction() { michael@0: readInstruction(); michael@0: } michael@0: michael@0: const RInstruction *instruction() const { michael@0: return reinterpret_cast(rawData_.addr()); michael@0: } michael@0: michael@0: bool resumeAfter() const { michael@0: return resumeAfter_; michael@0: } michael@0: }; michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif /* jit_Snapshot_h */