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