diff options
Diffstat (limited to 'js/src/jit/Snapshots.h')
-rw-r--r-- | js/src/jit/Snapshots.h | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/js/src/jit/Snapshots.h b/js/src/jit/Snapshots.h new file mode 100644 index 000000000..7aac3ccf6 --- /dev/null +++ b/js/src/jit/Snapshots.h @@ -0,0 +1,579 @@ +/* -*- 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: + MOZ_MUST_USE 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 + MOZ_MUST_USE 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; + } +}; + +class RInstructionStorage +{ + static const size_t Size = 4 * sizeof(uint32_t); + mozilla::AlignedStorage<Size> mem; + + public: + const void* addr() const { return mem.addr(); } + void* addr() { return mem.addr(); } + + RInstructionStorage() = default; + + RInstructionStorage(const RInstructionStorage& other) { + memcpy(addr(), other.addr(), Size); + } + void operator=(const RInstructionStorage& other) { + memcpy(addr(), other.addr(), Size); + } +}; + +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 */ |