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_LIR_h michael@0: #define jit_LIR_h michael@0: michael@0: // This file declares the core data structures for LIR: storage allocations for michael@0: // inputs and outputs, as well as the interface instructions must conform to. michael@0: michael@0: #include "mozilla/Array.h" michael@0: michael@0: #include "jit/Bailouts.h" michael@0: #include "jit/InlineList.h" michael@0: #include "jit/IonAllocPolicy.h" michael@0: #include "jit/LOpcodes.h" michael@0: #include "jit/MIR.h" michael@0: #include "jit/MIRGraph.h" michael@0: #include "jit/Registers.h" michael@0: #include "jit/Safepoints.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: class LUse; michael@0: class LGeneralReg; michael@0: class LFloatReg; michael@0: class LStackSlot; michael@0: class LArgument; michael@0: class LConstantIndex; michael@0: class MBasicBlock; michael@0: class MTableSwitch; michael@0: class MIRGenerator; michael@0: class MSnapshot; michael@0: michael@0: static const uint32_t VREG_INCREMENT = 1; michael@0: michael@0: static const uint32_t THIS_FRAME_ARGSLOT = 0; michael@0: michael@0: #if defined(JS_NUNBOX32) michael@0: # define BOX_PIECES 2 michael@0: static const uint32_t VREG_TYPE_OFFSET = 0; michael@0: static const uint32_t VREG_DATA_OFFSET = 1; michael@0: static const uint32_t TYPE_INDEX = 0; michael@0: static const uint32_t PAYLOAD_INDEX = 1; michael@0: #elif defined(JS_PUNBOX64) michael@0: # define BOX_PIECES 1 michael@0: #else michael@0: # error "Unknown!" michael@0: #endif michael@0: michael@0: // Represents storage for an operand. For constants, the pointer is tagged michael@0: // with a single bit, and the untagged pointer is a pointer to a Value. michael@0: class LAllocation : public TempObject michael@0: { michael@0: uintptr_t bits_; michael@0: michael@0: static const uintptr_t TAG_BIT = 1; michael@0: static const uintptr_t TAG_SHIFT = 0; michael@0: static const uintptr_t TAG_MASK = 1 << TAG_SHIFT; michael@0: static const uintptr_t KIND_BITS = 3; michael@0: static const uintptr_t KIND_SHIFT = TAG_SHIFT + TAG_BIT; michael@0: static const uintptr_t KIND_MASK = (1 << KIND_BITS) - 1; michael@0: michael@0: protected: michael@0: static const uintptr_t DATA_BITS = (sizeof(uint32_t) * 8) - KIND_BITS - TAG_BIT; michael@0: static const uintptr_t DATA_SHIFT = KIND_SHIFT + KIND_BITS; michael@0: static const uintptr_t DATA_MASK = (1 << DATA_BITS) - 1; michael@0: michael@0: public: michael@0: enum Kind { michael@0: USE, // Use of a virtual register, with physical allocation policy. michael@0: CONSTANT_VALUE, // Constant js::Value. michael@0: CONSTANT_INDEX, // Constant arbitrary index. michael@0: GPR, // General purpose register. michael@0: FPU, // Floating-point register. michael@0: STACK_SLOT, // Stack slot. michael@0: ARGUMENT_SLOT // Argument slot. michael@0: }; michael@0: michael@0: protected: michael@0: bool isTagged() const { michael@0: return !!(bits_ & TAG_MASK); michael@0: } michael@0: michael@0: int32_t data() const { michael@0: return int32_t(bits_) >> DATA_SHIFT; michael@0: } michael@0: void setData(int32_t data) { michael@0: JS_ASSERT(int32_t(data) <= int32_t(DATA_MASK)); michael@0: bits_ &= ~(DATA_MASK << DATA_SHIFT); michael@0: bits_ |= (data << DATA_SHIFT); michael@0: } michael@0: void setKindAndData(Kind kind, uint32_t data) { michael@0: JS_ASSERT(int32_t(data) <= int32_t(DATA_MASK)); michael@0: bits_ = (uint32_t(kind) << KIND_SHIFT) | data << DATA_SHIFT; michael@0: } michael@0: michael@0: LAllocation(Kind kind, uint32_t data) { michael@0: setKindAndData(kind, data); michael@0: } michael@0: explicit LAllocation(Kind kind) { michael@0: setKindAndData(kind, 0); michael@0: } michael@0: michael@0: public: michael@0: LAllocation() : bits_(0) michael@0: { } michael@0: michael@0: static LAllocation *New(TempAllocator &alloc) { michael@0: return new(alloc) LAllocation(); michael@0: } michael@0: template michael@0: static LAllocation *New(TempAllocator &alloc, const T &other) { michael@0: return new(alloc) LAllocation(other); michael@0: } michael@0: michael@0: // The value pointer must be rooted in MIR and have its low bit cleared. michael@0: explicit LAllocation(const Value *vp) { michael@0: bits_ = uintptr_t(vp); michael@0: JS_ASSERT(!isTagged()); michael@0: bits_ |= TAG_MASK; michael@0: } michael@0: inline explicit LAllocation(const AnyRegister ®); michael@0: michael@0: Kind kind() const { michael@0: if (isTagged()) michael@0: return CONSTANT_VALUE; michael@0: return (Kind)((bits_ >> KIND_SHIFT) & KIND_MASK); michael@0: } michael@0: michael@0: bool isUse() const { michael@0: return kind() == USE; michael@0: } michael@0: bool isConstant() const { michael@0: return isConstantValue() || isConstantIndex(); michael@0: } michael@0: bool isConstantValue() const { michael@0: return kind() == CONSTANT_VALUE; michael@0: } michael@0: bool isConstantIndex() const { michael@0: return kind() == CONSTANT_INDEX; michael@0: } michael@0: bool isValue() const { michael@0: return kind() == CONSTANT_VALUE; michael@0: } michael@0: bool isGeneralReg() const { michael@0: return kind() == GPR; michael@0: } michael@0: bool isFloatReg() const { michael@0: return kind() == FPU; michael@0: } michael@0: bool isStackSlot() const { michael@0: return kind() == STACK_SLOT; michael@0: } michael@0: bool isArgument() const { michael@0: return kind() == ARGUMENT_SLOT; michael@0: } michael@0: bool isRegister() const { michael@0: return isGeneralReg() || isFloatReg(); michael@0: } michael@0: bool isRegister(bool needFloat) const { michael@0: return needFloat ? isFloatReg() : isGeneralReg(); michael@0: } michael@0: bool isMemory() const { michael@0: return isStackSlot() || isArgument(); michael@0: } michael@0: inline LUse *toUse(); michael@0: inline const LUse *toUse() const; michael@0: inline const LGeneralReg *toGeneralReg() const; michael@0: inline const LFloatReg *toFloatReg() const; michael@0: inline const LStackSlot *toStackSlot() const; michael@0: inline const LArgument *toArgument() const; michael@0: inline const LConstantIndex *toConstantIndex() const; michael@0: inline AnyRegister toRegister() const; michael@0: michael@0: const Value *toConstant() const { michael@0: JS_ASSERT(isConstantValue()); michael@0: return reinterpret_cast(bits_ & ~TAG_MASK); michael@0: } michael@0: michael@0: bool operator ==(const LAllocation &other) const { michael@0: return bits_ == other.bits_; michael@0: } michael@0: michael@0: bool operator !=(const LAllocation &other) const { michael@0: return bits_ != other.bits_; michael@0: } michael@0: michael@0: HashNumber hash() const { michael@0: return bits_; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: const char *toString() const; michael@0: #else michael@0: const char *toString() const { return "???"; } michael@0: #endif michael@0: michael@0: void dump() const; michael@0: }; michael@0: michael@0: class LUse : public LAllocation michael@0: { michael@0: static const uint32_t POLICY_BITS = 3; michael@0: static const uint32_t POLICY_SHIFT = 0; michael@0: static const uint32_t POLICY_MASK = (1 << POLICY_BITS) - 1; michael@0: static const uint32_t REG_BITS = 5; michael@0: static const uint32_t REG_SHIFT = POLICY_SHIFT + POLICY_BITS; michael@0: static const uint32_t REG_MASK = (1 << REG_BITS) - 1; michael@0: michael@0: // Whether the physical register for this operand may be reused for a def. michael@0: static const uint32_t USED_AT_START_BITS = 1; michael@0: static const uint32_t USED_AT_START_SHIFT = REG_SHIFT + REG_BITS; michael@0: static const uint32_t USED_AT_START_MASK = (1 << USED_AT_START_BITS) - 1; michael@0: michael@0: public: michael@0: // Virtual registers get the remaining 20 bits. michael@0: static const uint32_t VREG_BITS = DATA_BITS - (USED_AT_START_SHIFT + USED_AT_START_BITS); michael@0: static const uint32_t VREG_SHIFT = USED_AT_START_SHIFT + USED_AT_START_BITS; michael@0: static const uint32_t VREG_MASK = (1 << VREG_BITS) - 1; michael@0: michael@0: enum Policy { michael@0: // Input should be in a read-only register or stack slot. michael@0: ANY, michael@0: michael@0: // Input must be in a read-only register. michael@0: REGISTER, michael@0: michael@0: // Input must be in a specific, read-only register. michael@0: FIXED, michael@0: michael@0: // Keep the used virtual register alive, and use whatever allocation is michael@0: // available. This is similar to ANY but hints to the register allocator michael@0: // that it is never useful to optimize this site. michael@0: KEEPALIVE, michael@0: michael@0: // For snapshot inputs, indicates that the associated instruction will michael@0: // write this input to its output register before bailing out. michael@0: // The register allocator may thus allocate that output register, and michael@0: // does not need to keep the virtual register alive (alternatively, michael@0: // this may be treated as KEEPALIVE). michael@0: RECOVERED_INPUT michael@0: }; michael@0: michael@0: void set(Policy policy, uint32_t reg, bool usedAtStart) { michael@0: setKindAndData(USE, (policy << POLICY_SHIFT) | michael@0: (reg << REG_SHIFT) | michael@0: ((usedAtStart ? 1 : 0) << USED_AT_START_SHIFT)); michael@0: } michael@0: michael@0: public: michael@0: LUse(uint32_t vreg, Policy policy, bool usedAtStart = false) { michael@0: set(policy, 0, usedAtStart); michael@0: setVirtualRegister(vreg); michael@0: } michael@0: LUse(Policy policy, bool usedAtStart = false) { michael@0: set(policy, 0, usedAtStart); michael@0: } michael@0: LUse(Register reg, bool usedAtStart = false) { michael@0: set(FIXED, reg.code(), usedAtStart); michael@0: } michael@0: LUse(FloatRegister reg, bool usedAtStart = false) { michael@0: set(FIXED, reg.code(), usedAtStart); michael@0: } michael@0: LUse(Register reg, uint32_t virtualRegister) { michael@0: set(FIXED, reg.code(), false); michael@0: setVirtualRegister(virtualRegister); michael@0: } michael@0: LUse(FloatRegister reg, uint32_t virtualRegister) { michael@0: set(FIXED, reg.code(), false); michael@0: setVirtualRegister(virtualRegister); michael@0: } michael@0: michael@0: void setVirtualRegister(uint32_t index) { michael@0: JS_ASSERT(index < VREG_MASK); michael@0: michael@0: uint32_t old = data() & ~(VREG_MASK << VREG_SHIFT); michael@0: setData(old | (index << VREG_SHIFT)); michael@0: } michael@0: michael@0: Policy policy() const { michael@0: Policy policy = (Policy)((data() >> POLICY_SHIFT) & POLICY_MASK); michael@0: return policy; michael@0: } michael@0: uint32_t virtualRegister() const { michael@0: uint32_t index = (data() >> VREG_SHIFT) & VREG_MASK; michael@0: return index; michael@0: } michael@0: uint32_t registerCode() const { michael@0: JS_ASSERT(policy() == FIXED); michael@0: return (data() >> REG_SHIFT) & REG_MASK; michael@0: } michael@0: bool isFixedRegister() const { michael@0: return policy() == FIXED; michael@0: } michael@0: bool usedAtStart() const { michael@0: return !!((data() >> USED_AT_START_SHIFT) & USED_AT_START_MASK); michael@0: } michael@0: }; michael@0: michael@0: static const uint32_t MAX_VIRTUAL_REGISTERS = LUse::VREG_MASK; michael@0: michael@0: class LGeneralReg : public LAllocation michael@0: { michael@0: public: michael@0: explicit LGeneralReg(Register reg) michael@0: : LAllocation(GPR, reg.code()) michael@0: { } michael@0: michael@0: Register reg() const { michael@0: return Register::FromCode(data()); michael@0: } michael@0: }; michael@0: michael@0: class LFloatReg : public LAllocation michael@0: { michael@0: public: michael@0: explicit LFloatReg(FloatRegister reg) michael@0: : LAllocation(FPU, reg.code()) michael@0: { } michael@0: michael@0: FloatRegister reg() const { michael@0: return FloatRegister::FromCode(data()); michael@0: } michael@0: }; michael@0: michael@0: // Arbitrary constant index. michael@0: class LConstantIndex : public LAllocation michael@0: { michael@0: explicit LConstantIndex(uint32_t index) michael@0: : LAllocation(CONSTANT_INDEX, index) michael@0: { } michael@0: michael@0: public: michael@0: // Used as a placeholder for inputs that can be ignored. michael@0: static LConstantIndex Bogus() { michael@0: return LConstantIndex(0); michael@0: } michael@0: michael@0: static LConstantIndex FromIndex(uint32_t index) { michael@0: return LConstantIndex(index); michael@0: } michael@0: michael@0: uint32_t index() const { michael@0: return data(); michael@0: } michael@0: }; michael@0: michael@0: // Stack slots are indices into the stack. The indices are byte indices. michael@0: class LStackSlot : public LAllocation michael@0: { michael@0: public: michael@0: explicit LStackSlot(uint32_t slot) michael@0: : LAllocation(STACK_SLOT, slot) michael@0: { } michael@0: michael@0: uint32_t slot() const { michael@0: return data(); michael@0: } michael@0: }; michael@0: michael@0: // Arguments are reverse indices into the stack. The indices are byte indices. michael@0: class LArgument : public LAllocation michael@0: { michael@0: public: michael@0: explicit LArgument(int32_t index) michael@0: : LAllocation(ARGUMENT_SLOT, index) michael@0: { } michael@0: michael@0: int32_t index() const { michael@0: return data(); michael@0: } michael@0: }; michael@0: michael@0: // Represents storage for a definition. michael@0: class LDefinition michael@0: { michael@0: // Bits containing policy, type, and virtual register. michael@0: uint32_t bits_; michael@0: michael@0: // Before register allocation, this optionally contains a fixed policy. michael@0: // Register allocation assigns this field to a physical policy if none is michael@0: // preset. michael@0: // michael@0: // Right now, pre-allocated outputs are limited to the following: michael@0: // * Physical argument stack slots. michael@0: // * Physical registers. michael@0: LAllocation output_; michael@0: michael@0: static const uint32_t TYPE_BITS = 3; michael@0: static const uint32_t TYPE_SHIFT = 0; michael@0: static const uint32_t TYPE_MASK = (1 << TYPE_BITS) - 1; michael@0: static const uint32_t POLICY_BITS = 2; michael@0: static const uint32_t POLICY_SHIFT = TYPE_SHIFT + TYPE_BITS; michael@0: static const uint32_t POLICY_MASK = (1 << POLICY_BITS) - 1; michael@0: michael@0: static const uint32_t VREG_BITS = (sizeof(uint32_t) * 8) - (POLICY_BITS + TYPE_BITS); michael@0: static const uint32_t VREG_SHIFT = POLICY_SHIFT + POLICY_BITS; michael@0: static const uint32_t VREG_MASK = (1 << VREG_BITS) - 1; michael@0: michael@0: public: michael@0: // Note that definitions, by default, are always allocated a register, michael@0: // unless the policy specifies that an input can be re-used and that input michael@0: // is a stack slot. michael@0: enum Policy { michael@0: // A random register of an appropriate class will be assigned. michael@0: DEFAULT, michael@0: michael@0: // The policy is predetermined by the LAllocation attached to this michael@0: // definition. The allocation may be: michael@0: // * A register, which may not appear as any fixed temporary. michael@0: // * A stack slot or argument. michael@0: // michael@0: // Register allocation will not modify a preset allocation. michael@0: PRESET, michael@0: michael@0: // One definition per instruction must re-use the first input michael@0: // allocation, which (for now) must be a register. michael@0: MUST_REUSE_INPUT, michael@0: michael@0: // This definition's virtual register is the same as another; this is michael@0: // for instructions which consume a register and silently define it as michael@0: // the same register. It is not legal to use this if doing so would michael@0: // change the type of the virtual register. michael@0: PASSTHROUGH michael@0: }; michael@0: michael@0: enum Type { michael@0: GENERAL, // Generic, integer or pointer-width data (GPR). michael@0: INT32, // int32 data (GPR). michael@0: OBJECT, // Pointer that may be collected as garbage (GPR). michael@0: SLOTS, // Slots/elements pointer that may be moved by minor GCs (GPR). michael@0: FLOAT32, // 32-bit floating-point value (FPU). michael@0: DOUBLE, // 64-bit floating-point value (FPU). michael@0: #ifdef JS_NUNBOX32 michael@0: // A type virtual register must be followed by a payload virtual michael@0: // register, as both will be tracked as a single gcthing. michael@0: TYPE, michael@0: PAYLOAD michael@0: #else michael@0: BOX // Joined box, for punbox systems. (GPR, gcthing) michael@0: #endif michael@0: }; michael@0: michael@0: void set(uint32_t index, Type type, Policy policy) { michael@0: JS_STATIC_ASSERT(MAX_VIRTUAL_REGISTERS <= VREG_MASK); michael@0: bits_ = (index << VREG_SHIFT) | (policy << POLICY_SHIFT) | (type << TYPE_SHIFT); michael@0: } michael@0: michael@0: public: michael@0: LDefinition(uint32_t index, Type type, Policy policy = DEFAULT) { michael@0: set(index, type, policy); michael@0: } michael@0: michael@0: LDefinition(Type type, Policy policy = DEFAULT) { michael@0: set(0, type, policy); michael@0: } michael@0: michael@0: LDefinition(Type type, const LAllocation &a) michael@0: : output_(a) michael@0: { michael@0: set(0, type, PRESET); michael@0: } michael@0: michael@0: LDefinition(uint32_t index, Type type, const LAllocation &a) michael@0: : output_(a) michael@0: { michael@0: set(index, type, PRESET); michael@0: } michael@0: michael@0: LDefinition() : bits_(0) michael@0: { } michael@0: michael@0: static LDefinition BogusTemp() { michael@0: return LDefinition(GENERAL, LConstantIndex::Bogus()); michael@0: } michael@0: michael@0: Policy policy() const { michael@0: return (Policy)((bits_ >> POLICY_SHIFT) & POLICY_MASK); michael@0: } michael@0: Type type() const { michael@0: return (Type)((bits_ >> TYPE_SHIFT) & TYPE_MASK); michael@0: } michael@0: bool isFloatReg() const { michael@0: return type() == FLOAT32 || type() == DOUBLE; michael@0: } michael@0: uint32_t virtualRegister() const { michael@0: return (bits_ >> VREG_SHIFT) & VREG_MASK; michael@0: } michael@0: LAllocation *output() { michael@0: return &output_; michael@0: } michael@0: const LAllocation *output() const { michael@0: return &output_; michael@0: } michael@0: bool isPreset() const { michael@0: return policy() == PRESET; michael@0: } michael@0: bool isBogusTemp() const { michael@0: return isPreset() && output()->isConstantIndex(); michael@0: } michael@0: void setVirtualRegister(uint32_t index) { michael@0: JS_ASSERT(index < VREG_MASK); michael@0: bits_ &= ~(VREG_MASK << VREG_SHIFT); michael@0: bits_ |= index << VREG_SHIFT; michael@0: } michael@0: void setOutput(const LAllocation &a) { michael@0: output_ = a; michael@0: if (!a.isUse()) { michael@0: bits_ &= ~(POLICY_MASK << POLICY_SHIFT); michael@0: bits_ |= PRESET << POLICY_SHIFT; michael@0: } michael@0: } michael@0: void setReusedInput(uint32_t operand) { michael@0: output_ = LConstantIndex::FromIndex(operand); michael@0: } michael@0: uint32_t getReusedInput() const { michael@0: JS_ASSERT(policy() == LDefinition::MUST_REUSE_INPUT); michael@0: return output_.toConstantIndex()->index(); michael@0: } michael@0: michael@0: static inline Type TypeFrom(MIRType type) { michael@0: switch (type) { michael@0: case MIRType_Boolean: michael@0: case MIRType_Int32: michael@0: // The stack slot allocator doesn't currently support allocating michael@0: // 1-byte slots, so for now we lower MIRType_Boolean into INT32. michael@0: static_assert(sizeof(bool) <= sizeof(int32_t), "bool doesn't fit in an int32 slot"); michael@0: return LDefinition::INT32; michael@0: case MIRType_String: michael@0: case MIRType_Object: michael@0: return LDefinition::OBJECT; michael@0: case MIRType_Double: michael@0: return LDefinition::DOUBLE; michael@0: case MIRType_Float32: michael@0: return LDefinition::FLOAT32; michael@0: #if defined(JS_PUNBOX64) michael@0: case MIRType_Value: michael@0: return LDefinition::BOX; michael@0: #endif michael@0: case MIRType_Slots: michael@0: case MIRType_Elements: michael@0: return LDefinition::SLOTS; michael@0: case MIRType_Pointer: michael@0: return LDefinition::GENERAL; michael@0: case MIRType_ForkJoinContext: michael@0: return LDefinition::GENERAL; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected type"); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: // Forward declarations of LIR types. michael@0: #define LIROP(op) class L##op; michael@0: LIR_OPCODE_LIST(LIROP) michael@0: #undef LIROP michael@0: michael@0: class LSnapshot; michael@0: class LSafepoint; michael@0: class LInstructionVisitor; michael@0: michael@0: class LInstruction michael@0: : public TempObject, michael@0: public InlineListNode michael@0: { michael@0: uint32_t id_; michael@0: michael@0: // This snapshot could be set after a ResumePoint. It is used to restart michael@0: // from the resume point pc. michael@0: LSnapshot *snapshot_; michael@0: michael@0: // Structure capturing the set of stack slots and registers which are known michael@0: // to hold either gcthings or Values. michael@0: LSafepoint *safepoint_; michael@0: michael@0: protected: michael@0: MDefinition *mir_; michael@0: michael@0: LInstruction() michael@0: : id_(0), michael@0: snapshot_(nullptr), michael@0: safepoint_(nullptr), michael@0: mir_(nullptr) michael@0: { } michael@0: michael@0: public: michael@0: class InputIterator; michael@0: enum Opcode { michael@0: # define LIROP(name) LOp_##name, michael@0: LIR_OPCODE_LIST(LIROP) michael@0: # undef LIROP michael@0: LOp_Invalid michael@0: }; michael@0: michael@0: const char *opName() { michael@0: switch (op()) { michael@0: # define LIR_NAME_INS(name) \ michael@0: case LOp_##name: return #name; michael@0: LIR_OPCODE_LIST(LIR_NAME_INS) michael@0: # undef LIR_NAME_INS michael@0: default: michael@0: return "Invalid"; michael@0: } michael@0: } michael@0: michael@0: // Hook for opcodes to add extra high level detail about what code will be michael@0: // emitted for the op. michael@0: virtual const char *extraName() const { michael@0: return nullptr; michael@0: } michael@0: michael@0: public: michael@0: virtual Opcode op() const = 0; michael@0: michael@0: // Returns the number of outputs of this instruction. If an output is michael@0: // unallocated, it is an LDefinition, defining a virtual register. michael@0: virtual size_t numDefs() const = 0; michael@0: virtual LDefinition *getDef(size_t index) = 0; michael@0: virtual void setDef(size_t index, const LDefinition &def) = 0; michael@0: michael@0: // Returns information about operands. michael@0: virtual size_t numOperands() const = 0; michael@0: virtual LAllocation *getOperand(size_t index) = 0; michael@0: virtual void setOperand(size_t index, const LAllocation &a) = 0; michael@0: michael@0: // Returns information about temporary registers needed. Each temporary michael@0: // register is an LUse with a TEMPORARY policy, or a fixed register. michael@0: virtual size_t numTemps() const = 0; michael@0: virtual LDefinition *getTemp(size_t index) = 0; michael@0: virtual void setTemp(size_t index, const LDefinition &a) = 0; michael@0: michael@0: // Returns the number of successors of this instruction, if it is a control michael@0: // transfer instruction, or zero otherwise. michael@0: virtual size_t numSuccessors() const = 0; michael@0: virtual MBasicBlock *getSuccessor(size_t i) const = 0; michael@0: virtual void setSuccessor(size_t i, MBasicBlock *successor) = 0; michael@0: michael@0: virtual bool isCall() const { michael@0: return false; michael@0: } michael@0: uint32_t id() const { michael@0: return id_; michael@0: } michael@0: void setId(uint32_t id) { michael@0: JS_ASSERT(!id_); michael@0: JS_ASSERT(id); michael@0: id_ = id; michael@0: } michael@0: LSnapshot *snapshot() const { michael@0: return snapshot_; michael@0: } michael@0: LSafepoint *safepoint() const { michael@0: return safepoint_; michael@0: } michael@0: void setMir(MDefinition *mir) { michael@0: mir_ = mir; michael@0: } michael@0: MDefinition *mirRaw() const { michael@0: /* Untyped MIR for this op. Prefer mir() methods in subclasses. */ michael@0: return mir_; michael@0: } michael@0: void assignSnapshot(LSnapshot *snapshot); michael@0: void initSafepoint(TempAllocator &alloc); michael@0: michael@0: // For an instruction which has a MUST_REUSE_INPUT output, whether that michael@0: // output register will be restored to its original value when bailing out. michael@0: virtual bool recoversInput() const { michael@0: return false; michael@0: } michael@0: michael@0: virtual void dump(FILE *fp); michael@0: void dump(); michael@0: static void printName(FILE *fp, Opcode op); michael@0: virtual void printName(FILE *fp); michael@0: virtual void printOperands(FILE *fp); michael@0: virtual void printInfo(FILE *fp) { } michael@0: michael@0: public: michael@0: // Opcode testing and casts. michael@0: # define LIROP(name) \ michael@0: bool is##name() const { \ michael@0: return op() == LOp_##name; \ michael@0: } \ michael@0: inline L##name *to##name(); michael@0: LIR_OPCODE_LIST(LIROP) michael@0: # undef LIROP michael@0: michael@0: virtual bool accept(LInstructionVisitor *visitor) = 0; michael@0: }; michael@0: michael@0: class LInstructionVisitor michael@0: { michael@0: LInstruction *ins_; michael@0: michael@0: protected: michael@0: jsbytecode *lastPC_; michael@0: michael@0: LInstruction *instruction() { michael@0: return ins_; michael@0: } michael@0: michael@0: public: michael@0: void setInstruction(LInstruction *ins) { michael@0: ins_ = ins; michael@0: if (ins->mirRaw()) michael@0: lastPC_ = ins->mirRaw()->trackedPc(); michael@0: } michael@0: michael@0: LInstructionVisitor() michael@0: : ins_(nullptr), michael@0: lastPC_(nullptr) michael@0: {} michael@0: michael@0: public: michael@0: #define VISIT_INS(op) virtual bool visit##op(L##op *) { MOZ_ASSUME_UNREACHABLE("NYI: " #op); } michael@0: LIR_OPCODE_LIST(VISIT_INS) michael@0: #undef VISIT_INS michael@0: }; michael@0: michael@0: typedef InlineList::iterator LInstructionIterator; michael@0: typedef InlineList::reverse_iterator LInstructionReverseIterator; michael@0: michael@0: class LPhi; michael@0: class LMoveGroup; michael@0: class LBlock : public TempObject michael@0: { michael@0: MBasicBlock *block_; michael@0: Vector phis_; michael@0: InlineList instructions_; michael@0: LMoveGroup *entryMoveGroup_; michael@0: LMoveGroup *exitMoveGroup_; michael@0: Label label_; michael@0: michael@0: LBlock(TempAllocator &alloc, MBasicBlock *block) michael@0: : block_(block), michael@0: phis_(alloc), michael@0: entryMoveGroup_(nullptr), michael@0: exitMoveGroup_(nullptr) michael@0: { } michael@0: michael@0: public: michael@0: static LBlock *New(TempAllocator &alloc, MBasicBlock *from) { michael@0: return new(alloc) LBlock(alloc, from); michael@0: } michael@0: void add(LInstruction *ins) { michael@0: instructions_.pushBack(ins); michael@0: } michael@0: bool addPhi(LPhi *phi) { michael@0: return phis_.append(phi); michael@0: } michael@0: size_t numPhis() const { michael@0: return phis_.length(); michael@0: } michael@0: LPhi *getPhi(size_t index) const { michael@0: return phis_[index]; michael@0: } michael@0: void removePhi(size_t index) { michael@0: phis_.erase(&phis_[index]); michael@0: } michael@0: void clearPhis() { michael@0: phis_.clear(); michael@0: } michael@0: MBasicBlock *mir() const { michael@0: return block_; michael@0: } michael@0: LInstructionIterator begin() { michael@0: return instructions_.begin(); michael@0: } michael@0: LInstructionIterator begin(LInstruction *at) { michael@0: return instructions_.begin(at); michael@0: } michael@0: LInstructionIterator end() { michael@0: return instructions_.end(); michael@0: } michael@0: LInstructionReverseIterator rbegin() { michael@0: return instructions_.rbegin(); michael@0: } michael@0: LInstructionReverseIterator rbegin(LInstruction *at) { michael@0: return instructions_.rbegin(at); michael@0: } michael@0: LInstructionReverseIterator rend() { michael@0: return instructions_.rend(); michael@0: } michael@0: InlineList &instructions() { michael@0: return instructions_; michael@0: } michael@0: void insertAfter(LInstruction *at, LInstruction *ins) { michael@0: instructions_.insertAfter(at, ins); michael@0: } michael@0: void insertBefore(LInstruction *at, LInstruction *ins) { michael@0: JS_ASSERT(!at->isLabel()); michael@0: instructions_.insertBefore(at, ins); michael@0: } michael@0: uint32_t firstId(); michael@0: uint32_t lastId(); michael@0: Label *label() { michael@0: return &label_; michael@0: } michael@0: LMoveGroup *getEntryMoveGroup(TempAllocator &alloc); michael@0: LMoveGroup *getExitMoveGroup(TempAllocator &alloc); michael@0: }; michael@0: michael@0: template michael@0: class LInstructionHelper : public LInstruction michael@0: { michael@0: mozilla::Array defs_; michael@0: mozilla::Array operands_; michael@0: mozilla::Array temps_; michael@0: michael@0: public: michael@0: size_t numDefs() const MOZ_FINAL MOZ_OVERRIDE { michael@0: return Defs; michael@0: } michael@0: LDefinition *getDef(size_t index) MOZ_FINAL MOZ_OVERRIDE { michael@0: return &defs_[index]; michael@0: } michael@0: size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE { michael@0: return Operands; michael@0: } michael@0: LAllocation *getOperand(size_t index) MOZ_FINAL MOZ_OVERRIDE { michael@0: return &operands_[index]; michael@0: } michael@0: size_t numTemps() const MOZ_FINAL MOZ_OVERRIDE { michael@0: return Temps; michael@0: } michael@0: LDefinition *getTemp(size_t index) MOZ_FINAL MOZ_OVERRIDE { michael@0: return &temps_[index]; michael@0: } michael@0: michael@0: void setDef(size_t index, const LDefinition &def) MOZ_FINAL MOZ_OVERRIDE { michael@0: defs_[index] = def; michael@0: } michael@0: void setOperand(size_t index, const LAllocation &a) MOZ_FINAL MOZ_OVERRIDE { michael@0: operands_[index] = a; michael@0: } michael@0: void setTemp(size_t index, const LDefinition &a) MOZ_FINAL MOZ_OVERRIDE { michael@0: temps_[index] = a; michael@0: } michael@0: michael@0: size_t numSuccessors() const { michael@0: return 0; michael@0: } michael@0: MBasicBlock *getSuccessor(size_t i) const { michael@0: JS_ASSERT(false); michael@0: return nullptr; michael@0: } michael@0: void setSuccessor(size_t i, MBasicBlock *successor) { michael@0: JS_ASSERT(false); michael@0: } michael@0: michael@0: // Default accessors, assuming a single input and output, respectively. michael@0: const LAllocation *input() { michael@0: JS_ASSERT(numOperands() == 1); michael@0: return getOperand(0); michael@0: } michael@0: const LDefinition *output() { michael@0: JS_ASSERT(numDefs() == 1); michael@0: return getDef(0); michael@0: } michael@0: michael@0: virtual void printInfo(FILE *fp) { michael@0: printOperands(fp); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: class LCallInstructionHelper : public LInstructionHelper michael@0: { michael@0: public: michael@0: virtual bool isCall() const { michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class LRecoverInfo : public TempObject michael@0: { michael@0: public: michael@0: typedef Vector Instructions; michael@0: michael@0: private: michael@0: // List of instructions needed to recover the stack frames. michael@0: // Outer frames are stored before inner frames. michael@0: Instructions instructions_; michael@0: michael@0: // Cached offset where this resume point is encoded. michael@0: RecoverOffset recoverOffset_; michael@0: michael@0: LRecoverInfo(TempAllocator &alloc); michael@0: bool init(MResumePoint *mir); michael@0: michael@0: public: michael@0: static LRecoverInfo *New(MIRGenerator *gen, MResumePoint *mir); michael@0: michael@0: // Resume point of the inner most function. michael@0: MResumePoint *mir() const { michael@0: return instructions_.back(); michael@0: } michael@0: RecoverOffset recoverOffset() const { michael@0: return recoverOffset_; michael@0: } michael@0: void setRecoverOffset(RecoverOffset offset) { michael@0: JS_ASSERT(recoverOffset_ == INVALID_RECOVER_OFFSET); michael@0: recoverOffset_ = offset; michael@0: } michael@0: michael@0: MResumePoint **begin() { michael@0: return instructions_.begin(); michael@0: } michael@0: MResumePoint **end() { michael@0: return instructions_.end(); michael@0: } michael@0: }; michael@0: michael@0: // An LSnapshot is the reflection of an MResumePoint in LIR. Unlike MResumePoints, michael@0: // they cannot be shared, as they are filled in by the register allocator in michael@0: // order to capture the precise low-level stack state in between an michael@0: // instruction's input and output. During code generation, LSnapshots are michael@0: // compressed and saved in the compiled script. michael@0: class LSnapshot : public TempObject michael@0: { michael@0: private: michael@0: uint32_t numSlots_; michael@0: LAllocation *slots_; michael@0: LRecoverInfo *recoverInfo_; michael@0: SnapshotOffset snapshotOffset_; michael@0: BailoutId bailoutId_; michael@0: BailoutKind bailoutKind_; michael@0: michael@0: LSnapshot(LRecoverInfo *recover, BailoutKind kind); michael@0: bool init(MIRGenerator *gen); michael@0: michael@0: public: michael@0: static LSnapshot *New(MIRGenerator *gen, LRecoverInfo *recover, BailoutKind kind); michael@0: michael@0: size_t numEntries() const { michael@0: return numSlots_; michael@0: } michael@0: size_t numSlots() const { michael@0: return numSlots_ / BOX_PIECES; michael@0: } michael@0: LAllocation *payloadOfSlot(size_t i) { michael@0: JS_ASSERT(i < numSlots()); michael@0: size_t entryIndex = (i * BOX_PIECES) + (BOX_PIECES - 1); michael@0: return getEntry(entryIndex); michael@0: } michael@0: #ifdef JS_NUNBOX32 michael@0: LAllocation *typeOfSlot(size_t i) { michael@0: JS_ASSERT(i < numSlots()); michael@0: size_t entryIndex = (i * BOX_PIECES) + (BOX_PIECES - 2); michael@0: return getEntry(entryIndex); michael@0: } michael@0: #endif michael@0: LAllocation *getEntry(size_t i) { michael@0: JS_ASSERT(i < numSlots_); michael@0: return &slots_[i]; michael@0: } michael@0: void setEntry(size_t i, const LAllocation &alloc) { michael@0: JS_ASSERT(i < numSlots_); michael@0: slots_[i] = alloc; michael@0: } michael@0: LRecoverInfo *recoverInfo() const { michael@0: return recoverInfo_; michael@0: } michael@0: MResumePoint *mir() const { michael@0: return recoverInfo()->mir(); michael@0: } michael@0: SnapshotOffset snapshotOffset() const { michael@0: return snapshotOffset_; michael@0: } michael@0: BailoutId bailoutId() const { michael@0: return bailoutId_; michael@0: } michael@0: void setSnapshotOffset(SnapshotOffset offset) { michael@0: JS_ASSERT(snapshotOffset_ == INVALID_SNAPSHOT_OFFSET); michael@0: snapshotOffset_ = offset; michael@0: } michael@0: void setBailoutId(BailoutId id) { michael@0: JS_ASSERT(bailoutId_ == INVALID_BAILOUT_ID); michael@0: bailoutId_ = id; michael@0: } michael@0: BailoutKind bailoutKind() const { michael@0: return bailoutKind_; michael@0: } michael@0: void setBailoutKind(BailoutKind kind) { michael@0: bailoutKind_ = kind; michael@0: } michael@0: void rewriteRecoveredInput(LUse input); michael@0: }; michael@0: michael@0: struct SafepointNunboxEntry { michael@0: LAllocation type; michael@0: LAllocation payload; michael@0: michael@0: SafepointNunboxEntry() { } michael@0: SafepointNunboxEntry(LAllocation type, LAllocation payload) michael@0: : type(type), payload(payload) michael@0: { } michael@0: }; michael@0: michael@0: class LSafepoint : public TempObject michael@0: { michael@0: typedef SafepointNunboxEntry NunboxEntry; michael@0: michael@0: public: michael@0: typedef Vector SlotList; michael@0: typedef Vector NunboxList; michael@0: michael@0: private: michael@0: // The information in a safepoint describes the registers and gc related michael@0: // values that are live at the start of the associated instruction. michael@0: michael@0: // The set of registers which are live at an OOL call made within the michael@0: // instruction. This includes any registers for inputs which are not michael@0: // use-at-start, any registers for temps, and any registers live after the michael@0: // call except outputs of the instruction. michael@0: // michael@0: // For call instructions, the live regs are empty. Call instructions may michael@0: // have register inputs or temporaries, which will *not* be in the live michael@0: // registers: if passed to the call, the values passed will be marked via michael@0: // MarkJitExitFrame, and no registers can be live after the instruction michael@0: // except its outputs. michael@0: RegisterSet liveRegs_; michael@0: michael@0: // The subset of liveRegs which contains gcthing pointers. michael@0: GeneralRegisterSet gcRegs_; michael@0: michael@0: #ifdef CHECK_OSIPOINT_REGISTERS michael@0: // Clobbered regs of the current instruction. This set is never written to michael@0: // the safepoint; it's only used by assertions during compilation. michael@0: RegisterSet clobberedRegs_; michael@0: #endif michael@0: michael@0: // Offset to a position in the safepoint stream, or michael@0: // INVALID_SAFEPOINT_OFFSET. michael@0: uint32_t safepointOffset_; michael@0: michael@0: // Assembler buffer displacement to OSI point's call location. michael@0: uint32_t osiCallPointOffset_; michael@0: michael@0: // List of stack slots which have gcthing pointers. michael@0: SlotList gcSlots_; michael@0: michael@0: // List of stack slots which have Values. michael@0: SlotList valueSlots_; michael@0: michael@0: #ifdef JS_NUNBOX32 michael@0: // List of registers (in liveRegs) and stack slots which contain pieces of Values. michael@0: NunboxList nunboxParts_; michael@0: michael@0: // Number of nunboxParts which are not completely filled in. michael@0: uint32_t partialNunboxes_; michael@0: #elif JS_PUNBOX64 michael@0: // The subset of liveRegs which have Values. michael@0: GeneralRegisterSet valueRegs_; michael@0: #endif michael@0: michael@0: // The subset of liveRegs which contains pointers to slots/elements. michael@0: GeneralRegisterSet slotsOrElementsRegs_; michael@0: michael@0: // List of stack slots which have slots/elements pointers. michael@0: SlotList slotsOrElementsSlots_; michael@0: michael@0: public: michael@0: void assertInvariants() { michael@0: // Every register in valueRegs and gcRegs should also be in liveRegs. michael@0: #ifndef JS_NUNBOX32 michael@0: JS_ASSERT((valueRegs().bits() & ~liveRegs().gprs().bits()) == 0); michael@0: #endif michael@0: JS_ASSERT((gcRegs().bits() & ~liveRegs().gprs().bits()) == 0); michael@0: } michael@0: michael@0: LSafepoint(TempAllocator &alloc) michael@0: : safepointOffset_(INVALID_SAFEPOINT_OFFSET) michael@0: , osiCallPointOffset_(0) michael@0: , gcSlots_(alloc) michael@0: , valueSlots_(alloc) michael@0: #ifdef JS_NUNBOX32 michael@0: , nunboxParts_(alloc) michael@0: , partialNunboxes_(0) michael@0: #endif michael@0: , slotsOrElementsSlots_(alloc) michael@0: { michael@0: assertInvariants(); michael@0: } michael@0: void addLiveRegister(AnyRegister reg) { michael@0: liveRegs_.addUnchecked(reg); michael@0: assertInvariants(); michael@0: } michael@0: const RegisterSet &liveRegs() const { michael@0: return liveRegs_; michael@0: } michael@0: #ifdef CHECK_OSIPOINT_REGISTERS michael@0: void addClobberedRegister(AnyRegister reg) { michael@0: clobberedRegs_.addUnchecked(reg); michael@0: assertInvariants(); michael@0: } michael@0: const RegisterSet &clobberedRegs() const { michael@0: return clobberedRegs_; michael@0: } michael@0: #endif michael@0: void addGcRegister(Register reg) { michael@0: gcRegs_.addUnchecked(reg); michael@0: assertInvariants(); michael@0: } michael@0: GeneralRegisterSet gcRegs() const { michael@0: return gcRegs_; michael@0: } michael@0: bool addGcSlot(uint32_t slot) { michael@0: bool result = gcSlots_.append(slot); michael@0: if (result) michael@0: assertInvariants(); michael@0: return result; michael@0: } michael@0: SlotList &gcSlots() { michael@0: return gcSlots_; michael@0: } michael@0: michael@0: SlotList &slotsOrElementsSlots() { michael@0: return slotsOrElementsSlots_; michael@0: } michael@0: GeneralRegisterSet slotsOrElementsRegs() const { michael@0: return slotsOrElementsRegs_; michael@0: } michael@0: void addSlotsOrElementsRegister(Register reg) { michael@0: slotsOrElementsRegs_.addUnchecked(reg); michael@0: assertInvariants(); michael@0: } michael@0: bool addSlotsOrElementsSlot(uint32_t slot) { michael@0: bool result = slotsOrElementsSlots_.append(slot); michael@0: if (result) michael@0: assertInvariants(); michael@0: return result; michael@0: } michael@0: bool addSlotsOrElementsPointer(LAllocation alloc) { michael@0: if (alloc.isStackSlot()) michael@0: return addSlotsOrElementsSlot(alloc.toStackSlot()->slot()); michael@0: JS_ASSERT(alloc.isRegister()); michael@0: addSlotsOrElementsRegister(alloc.toRegister().gpr()); michael@0: assertInvariants(); michael@0: return true; michael@0: } michael@0: bool hasSlotsOrElementsPointer(LAllocation alloc) const { michael@0: if (alloc.isRegister()) michael@0: return slotsOrElementsRegs().has(alloc.toRegister().gpr()); michael@0: if (alloc.isStackSlot()) { michael@0: for (size_t i = 0; i < slotsOrElementsSlots_.length(); i++) { michael@0: if (slotsOrElementsSlots_[i] == alloc.toStackSlot()->slot()) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool addGcPointer(LAllocation alloc) { michael@0: if (alloc.isStackSlot()) michael@0: return addGcSlot(alloc.toStackSlot()->slot()); michael@0: if (alloc.isRegister()) michael@0: addGcRegister(alloc.toRegister().gpr()); michael@0: assertInvariants(); michael@0: return true; michael@0: } michael@0: michael@0: bool hasGcPointer(LAllocation alloc) const { michael@0: if (alloc.isRegister()) michael@0: return gcRegs().has(alloc.toRegister().gpr()); michael@0: if (alloc.isStackSlot()) { michael@0: for (size_t i = 0; i < gcSlots_.length(); i++) { michael@0: if (gcSlots_[i] == alloc.toStackSlot()->slot()) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: JS_ASSERT(alloc.isArgument()); michael@0: return true; michael@0: } michael@0: michael@0: bool addValueSlot(uint32_t slot) { michael@0: bool result = valueSlots_.append(slot); michael@0: if (result) michael@0: assertInvariants(); michael@0: return result; michael@0: } michael@0: SlotList &valueSlots() { michael@0: return valueSlots_; michael@0: } michael@0: michael@0: bool hasValueSlot(uint32_t slot) const { michael@0: for (size_t i = 0; i < valueSlots_.length(); i++) { michael@0: if (valueSlots_[i] == slot) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: #ifdef JS_NUNBOX32 michael@0: michael@0: bool addNunboxParts(LAllocation type, LAllocation payload) { michael@0: bool result = nunboxParts_.append(NunboxEntry(type, payload)); michael@0: if (result) michael@0: assertInvariants(); michael@0: return result; michael@0: } michael@0: michael@0: bool addNunboxType(uint32_t typeVreg, LAllocation type) { michael@0: for (size_t i = 0; i < nunboxParts_.length(); i++) { michael@0: if (nunboxParts_[i].type == type) michael@0: return true; michael@0: if (nunboxParts_[i].type == LUse(typeVreg, LUse::ANY)) { michael@0: nunboxParts_[i].type = type; michael@0: partialNunboxes_--; michael@0: return true; michael@0: } michael@0: } michael@0: partialNunboxes_++; michael@0: michael@0: // vregs for nunbox pairs are adjacent, with the type coming first. michael@0: uint32_t payloadVreg = typeVreg + 1; michael@0: bool result = nunboxParts_.append(NunboxEntry(type, LUse(payloadVreg, LUse::ANY))); michael@0: if (result) michael@0: assertInvariants(); michael@0: return result; michael@0: } michael@0: michael@0: bool hasNunboxType(LAllocation type) const { michael@0: if (type.isArgument()) michael@0: return true; michael@0: if (type.isStackSlot() && hasValueSlot(type.toStackSlot()->slot() + 1)) michael@0: return true; michael@0: for (size_t i = 0; i < nunboxParts_.length(); i++) { michael@0: if (nunboxParts_[i].type == type) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool addNunboxPayload(uint32_t payloadVreg, LAllocation payload) { michael@0: for (size_t i = 0; i < nunboxParts_.length(); i++) { michael@0: if (nunboxParts_[i].payload == payload) michael@0: return true; michael@0: if (nunboxParts_[i].payload == LUse(payloadVreg, LUse::ANY)) { michael@0: partialNunboxes_--; michael@0: nunboxParts_[i].payload = payload; michael@0: return true; michael@0: } michael@0: } michael@0: partialNunboxes_++; michael@0: michael@0: // vregs for nunbox pairs are adjacent, with the type coming first. michael@0: uint32_t typeVreg = payloadVreg - 1; michael@0: bool result = nunboxParts_.append(NunboxEntry(LUse(typeVreg, LUse::ANY), payload)); michael@0: if (result) michael@0: assertInvariants(); michael@0: return result; michael@0: } michael@0: michael@0: bool hasNunboxPayload(LAllocation payload) const { michael@0: if (payload.isArgument()) michael@0: return true; michael@0: if (payload.isStackSlot() && hasValueSlot(payload.toStackSlot()->slot())) michael@0: return true; michael@0: for (size_t i = 0; i < nunboxParts_.length(); i++) { michael@0: if (nunboxParts_[i].payload == payload) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: NunboxList &nunboxParts() { michael@0: return nunboxParts_; michael@0: } michael@0: michael@0: uint32_t partialNunboxes() { michael@0: return partialNunboxes_; michael@0: } michael@0: michael@0: #elif JS_PUNBOX64 michael@0: michael@0: void addValueRegister(Register reg) { michael@0: valueRegs_.add(reg); michael@0: assertInvariants(); michael@0: } michael@0: GeneralRegisterSet valueRegs() const { michael@0: return valueRegs_; michael@0: } michael@0: michael@0: bool addBoxedValue(LAllocation alloc) { michael@0: if (alloc.isRegister()) { michael@0: Register reg = alloc.toRegister().gpr(); michael@0: if (!valueRegs().has(reg)) michael@0: addValueRegister(reg); michael@0: return true; michael@0: } michael@0: if (alloc.isStackSlot()) { michael@0: uint32_t slot = alloc.toStackSlot()->slot(); michael@0: for (size_t i = 0; i < valueSlots().length(); i++) { michael@0: if (valueSlots()[i] == slot) michael@0: return true; michael@0: } michael@0: return addValueSlot(slot); michael@0: } michael@0: JS_ASSERT(alloc.isArgument()); michael@0: return true; michael@0: } michael@0: michael@0: bool hasBoxedValue(LAllocation alloc) const { michael@0: if (alloc.isRegister()) michael@0: return valueRegs().has(alloc.toRegister().gpr()); michael@0: if (alloc.isStackSlot()) michael@0: return hasValueSlot(alloc.toStackSlot()->slot()); michael@0: JS_ASSERT(alloc.isArgument()); michael@0: return true; michael@0: } michael@0: michael@0: #endif // JS_PUNBOX64 michael@0: michael@0: bool encoded() const { michael@0: return safepointOffset_ != INVALID_SAFEPOINT_OFFSET; michael@0: } michael@0: uint32_t offset() const { michael@0: JS_ASSERT(encoded()); michael@0: return safepointOffset_; michael@0: } michael@0: void setOffset(uint32_t offset) { michael@0: safepointOffset_ = offset; michael@0: } michael@0: uint32_t osiReturnPointOffset() const { michael@0: // In general, pointer arithmetic on code is bad, but in this case, michael@0: // getting the return address from a call instruction, stepping over pools michael@0: // would be wrong. michael@0: return osiCallPointOffset_ + Assembler::patchWrite_NearCallSize(); michael@0: } michael@0: uint32_t osiCallPointOffset() const { michael@0: return osiCallPointOffset_; michael@0: } michael@0: void setOsiCallPointOffset(uint32_t osiCallPointOffset) { michael@0: JS_ASSERT(!osiCallPointOffset_); michael@0: osiCallPointOffset_ = osiCallPointOffset; michael@0: } michael@0: void fixupOffset(MacroAssembler *masm) { michael@0: osiCallPointOffset_ = masm->actualOffset(osiCallPointOffset_); michael@0: } michael@0: }; michael@0: michael@0: class LInstruction::InputIterator michael@0: { michael@0: private: michael@0: LInstruction &ins_; michael@0: size_t idx_; michael@0: bool snapshot_; michael@0: michael@0: void handleOperandsEnd() { michael@0: // Iterate on the snapshot when iteration over all operands is done. michael@0: if (!snapshot_ && idx_ == ins_.numOperands() && ins_.snapshot()) { michael@0: idx_ = 0; michael@0: snapshot_ = true; michael@0: } michael@0: } michael@0: michael@0: public: michael@0: InputIterator(LInstruction &ins) : michael@0: ins_(ins), michael@0: idx_(0), michael@0: snapshot_(false) michael@0: { michael@0: handleOperandsEnd(); michael@0: } michael@0: michael@0: bool more() const { michael@0: if (snapshot_) michael@0: return idx_ < ins_.snapshot()->numEntries(); michael@0: if (idx_ < ins_.numOperands()) michael@0: return true; michael@0: if (ins_.snapshot() && ins_.snapshot()->numEntries()) michael@0: return true; michael@0: return false; michael@0: } michael@0: michael@0: bool isSnapshotInput() const { michael@0: return snapshot_; michael@0: } michael@0: michael@0: void next() { michael@0: JS_ASSERT(more()); michael@0: idx_++; michael@0: handleOperandsEnd(); michael@0: } michael@0: michael@0: void replace(const LAllocation &alloc) { michael@0: if (snapshot_) michael@0: ins_.snapshot()->setEntry(idx_, alloc); michael@0: else michael@0: ins_.setOperand(idx_, alloc); michael@0: } michael@0: michael@0: LAllocation *operator *() const { michael@0: if (snapshot_) michael@0: return ins_.snapshot()->getEntry(idx_); michael@0: return ins_.getOperand(idx_); michael@0: } michael@0: michael@0: LAllocation *operator ->() const { michael@0: return **this; michael@0: } michael@0: }; michael@0: michael@0: class LIRGraph michael@0: { michael@0: struct ValueHasher michael@0: { michael@0: typedef Value Lookup; michael@0: static HashNumber hash(const Value &v) { michael@0: return HashNumber(v.asRawBits()); michael@0: } michael@0: static bool match(const Value &lhs, const Value &rhs) { michael@0: return lhs == rhs; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: bool canOptimizeOutIfUnused(); michael@0: #endif michael@0: }; michael@0: michael@0: michael@0: Vector blocks_; michael@0: Vector constantPool_; michael@0: typedef HashMap ConstantPoolMap; michael@0: ConstantPoolMap constantPoolMap_; michael@0: Vector safepoints_; michael@0: Vector nonCallSafepoints_; michael@0: uint32_t numVirtualRegisters_; michael@0: uint32_t numInstructions_; michael@0: michael@0: // Number of stack slots needed for local spills. michael@0: uint32_t localSlotCount_; michael@0: // Number of stack slots needed for argument construction for calls. michael@0: uint32_t argumentSlotCount_; michael@0: michael@0: // Snapshot taken before any LIR has been lowered. michael@0: LSnapshot *entrySnapshot_; michael@0: michael@0: // LBlock containing LOsrEntry, or nullptr. michael@0: LBlock *osrBlock_; michael@0: michael@0: MIRGraph &mir_; michael@0: michael@0: public: michael@0: LIRGraph(MIRGraph *mir); michael@0: michael@0: bool init() { michael@0: return constantPoolMap_.init(); michael@0: } michael@0: MIRGraph &mir() const { michael@0: return mir_; michael@0: } michael@0: size_t numBlocks() const { michael@0: return blocks_.length(); michael@0: } michael@0: LBlock *getBlock(size_t i) const { michael@0: return blocks_[i]; michael@0: } michael@0: uint32_t numBlockIds() const { michael@0: return mir_.numBlockIds(); michael@0: } michael@0: bool addBlock(LBlock *block) { michael@0: return blocks_.append(block); michael@0: } michael@0: uint32_t getVirtualRegister() { michael@0: numVirtualRegisters_ += VREG_INCREMENT; michael@0: return numVirtualRegisters_; michael@0: } michael@0: uint32_t numVirtualRegisters() const { michael@0: // Virtual registers are 1-based, not 0-based, so add one as a michael@0: // convenience for 0-based arrays. michael@0: return numVirtualRegisters_ + 1; michael@0: } michael@0: uint32_t getInstructionId() { michael@0: return numInstructions_++; michael@0: } michael@0: uint32_t numInstructions() const { michael@0: return numInstructions_; michael@0: } michael@0: void setLocalSlotCount(uint32_t localSlotCount) { michael@0: localSlotCount_ = localSlotCount; michael@0: } michael@0: uint32_t localSlotCount() const { michael@0: return localSlotCount_; michael@0: } michael@0: // Return the localSlotCount() value rounded up so that it satisfies the michael@0: // platform stack alignment requirement, and so that it's a multiple of michael@0: // the number of slots per Value. michael@0: uint32_t paddedLocalSlotCount() const { michael@0: // Round to StackAlignment, but also round to at least sizeof(Value) in michael@0: // case that's greater, because StackOffsetOfPassedArg rounds argument michael@0: // slots to 8-byte boundaries. michael@0: size_t Alignment = Max(sizeof(StackAlignment), sizeof(Value)); michael@0: return AlignBytes(localSlotCount(), Alignment); michael@0: } michael@0: size_t paddedLocalSlotsSize() const { michael@0: return paddedLocalSlotCount(); michael@0: } michael@0: void setArgumentSlotCount(uint32_t argumentSlotCount) { michael@0: argumentSlotCount_ = argumentSlotCount; michael@0: } michael@0: uint32_t argumentSlotCount() const { michael@0: return argumentSlotCount_; michael@0: } michael@0: size_t argumentsSize() const { michael@0: return argumentSlotCount() * sizeof(Value); michael@0: } michael@0: uint32_t totalSlotCount() const { michael@0: return paddedLocalSlotCount() + argumentsSize(); michael@0: } michael@0: bool addConstantToPool(const Value &v, uint32_t *index); michael@0: size_t numConstants() const { michael@0: return constantPool_.length(); michael@0: } michael@0: Value *constantPool() { michael@0: return &constantPool_[0]; michael@0: } michael@0: void setEntrySnapshot(LSnapshot *snapshot) { michael@0: JS_ASSERT(!entrySnapshot_); michael@0: JS_ASSERT(snapshot->bailoutKind() == Bailout_Normal); michael@0: snapshot->setBailoutKind(Bailout_ArgumentCheck); michael@0: entrySnapshot_ = snapshot; michael@0: } michael@0: LSnapshot *entrySnapshot() const { michael@0: JS_ASSERT(entrySnapshot_); michael@0: return entrySnapshot_; michael@0: } michael@0: void setOsrBlock(LBlock *block) { michael@0: JS_ASSERT(!osrBlock_); michael@0: osrBlock_ = block; michael@0: } michael@0: LBlock *osrBlock() const { michael@0: return osrBlock_; michael@0: } michael@0: bool noteNeedsSafepoint(LInstruction *ins); michael@0: size_t numNonCallSafepoints() const { michael@0: return nonCallSafepoints_.length(); michael@0: } michael@0: LInstruction *getNonCallSafepoint(size_t i) const { michael@0: return nonCallSafepoints_[i]; michael@0: } michael@0: size_t numSafepoints() const { michael@0: return safepoints_.length(); michael@0: } michael@0: LInstruction *getSafepoint(size_t i) const { michael@0: return safepoints_[i]; michael@0: } michael@0: void removeBlock(size_t i); michael@0: }; michael@0: michael@0: LAllocation::LAllocation(const AnyRegister ®) michael@0: { michael@0: if (reg.isFloat()) michael@0: *this = LFloatReg(reg.fpu()); michael@0: else michael@0: *this = LGeneralReg(reg.gpr()); michael@0: } michael@0: michael@0: AnyRegister michael@0: LAllocation::toRegister() const michael@0: { michael@0: JS_ASSERT(isRegister()); michael@0: if (isFloatReg()) michael@0: return AnyRegister(toFloatReg()->reg()); michael@0: return AnyRegister(toGeneralReg()->reg()); michael@0: } michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #define LIR_HEADER(opcode) \ michael@0: Opcode op() const { \ michael@0: return LInstruction::LOp_##opcode; \ michael@0: } \ michael@0: bool accept(LInstructionVisitor *visitor) { \ michael@0: visitor->setInstruction(this); \ michael@0: return visitor->visit##opcode(this); \ michael@0: } michael@0: michael@0: #include "jit/LIR-Common.h" michael@0: #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) michael@0: # if defined(JS_CODEGEN_X86) michael@0: # include "jit/x86/LIR-x86.h" michael@0: # elif defined(JS_CODEGEN_X64) michael@0: # include "jit/x64/LIR-x64.h" michael@0: # endif michael@0: # include "jit/shared/LIR-x86-shared.h" michael@0: #elif defined(JS_CODEGEN_ARM) michael@0: # include "jit/arm/LIR-arm.h" michael@0: #elif defined(JS_CODEGEN_MIPS) michael@0: # include "jit/mips/LIR-mips.h" michael@0: #else michael@0: # error "Unknown architecture!" michael@0: #endif michael@0: michael@0: #undef LIR_HEADER michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: #define LIROP(name) \ michael@0: L##name *LInstruction::to##name() \ michael@0: { \ michael@0: JS_ASSERT(is##name()); \ michael@0: return static_cast(this); \ michael@0: } michael@0: LIR_OPCODE_LIST(LIROP) michael@0: #undef LIROP michael@0: michael@0: #define LALLOC_CAST(type) \ michael@0: L##type *LAllocation::to##type() { \ michael@0: JS_ASSERT(is##type()); \ michael@0: return static_cast(this); \ michael@0: } michael@0: #define LALLOC_CONST_CAST(type) \ michael@0: const L##type *LAllocation::to##type() const { \ michael@0: JS_ASSERT(is##type()); \ michael@0: return static_cast(this); \ michael@0: } michael@0: michael@0: LALLOC_CAST(Use) michael@0: LALLOC_CONST_CAST(Use) michael@0: LALLOC_CONST_CAST(GeneralReg) michael@0: LALLOC_CONST_CAST(FloatReg) michael@0: LALLOC_CONST_CAST(StackSlot) michael@0: LALLOC_CONST_CAST(Argument) michael@0: LALLOC_CONST_CAST(ConstantIndex) michael@0: michael@0: #undef LALLOC_CAST michael@0: michael@0: #ifdef JS_NUNBOX32 michael@0: static inline signed michael@0: OffsetToOtherHalfOfNunbox(LDefinition::Type type) michael@0: { michael@0: JS_ASSERT(type == LDefinition::TYPE || type == LDefinition::PAYLOAD); michael@0: signed offset = (type == LDefinition::TYPE) michael@0: ? PAYLOAD_INDEX - TYPE_INDEX michael@0: : TYPE_INDEX - PAYLOAD_INDEX; michael@0: return offset; michael@0: } michael@0: michael@0: static inline void michael@0: AssertTypesFormANunbox(LDefinition::Type type1, LDefinition::Type type2) michael@0: { michael@0: JS_ASSERT((type1 == LDefinition::TYPE && type2 == LDefinition::PAYLOAD) || michael@0: (type2 == LDefinition::TYPE && type1 == LDefinition::PAYLOAD)); michael@0: } michael@0: michael@0: static inline unsigned michael@0: OffsetOfNunboxSlot(LDefinition::Type type) michael@0: { michael@0: if (type == LDefinition::PAYLOAD) michael@0: return NUNBOX32_PAYLOAD_OFFSET; michael@0: return NUNBOX32_TYPE_OFFSET; michael@0: } michael@0: michael@0: // Note that stack indexes for LStackSlot are modelled backwards, so a michael@0: // double-sized slot starting at 2 has its next word at 1, *not* 3. michael@0: static inline unsigned michael@0: BaseOfNunboxSlot(LDefinition::Type type, unsigned slot) michael@0: { michael@0: if (type == LDefinition::PAYLOAD) michael@0: return slot + NUNBOX32_PAYLOAD_OFFSET; michael@0: return slot + NUNBOX32_TYPE_OFFSET; michael@0: } michael@0: #endif michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #endif /* jit_LIR_h */