#ifndef jit_LIR_h
#define jit_LIR_h
#include "mozilla/Array.h"
#include "jit/Bailouts.h"
#include "jit/FixedList.h"
#include "jit/InlineList.h"
#include "jit/JitAllocPolicy.h"
#include "jit/LOpcodes.h"
#include "jit/MIR.h"
#include "jit/MIRGraph.h"
#include "jit/Registers.h"
#include "jit/Safepoints.h"
namespace js {
namespace jit {
class LUse;
class LGeneralReg;
class LFloatReg;
class LStackSlot;
class LArgument;
class LConstantIndex;
class MBasicBlock;
class MIRGenerator;
static const uint32_t VREG_INCREMENT = 1;
static const uint32_t THIS_FRAME_ARGSLOT = 0;
#if defined(JS_NUNBOX32)
# define BOX_PIECES 2
static const uint32_t VREG_TYPE_OFFSET = 0;
static const uint32_t VREG_DATA_OFFSET = 1;
static const uint32_t TYPE_INDEX = 0;
static const uint32_t PAYLOAD_INDEX = 1;
static const uint32_t INT64LOW_INDEX = 0;
static const uint32_t INT64HIGH_INDEX = 1;
#elif defined(JS_PUNBOX64)
# define BOX_PIECES 1
#else
# error "Unknown!"
#endif
static const uint32_t INT64_PIECES = sizeof(int64_t) / sizeof(uintptr_t);
class LAllocation : public TempObject {
uintptr_t bits_;
static const uintptr_t KIND_BITS = 3;
static const uintptr_t KIND_SHIFT = 0;
static const uintptr_t KIND_MASK = (1 << KIND_BITS) - 1;
protected:
static const uintptr_t DATA_BITS = (sizeof(uint32_t) * 8) - KIND_BITS;
static const uintptr_t DATA_SHIFT = KIND_SHIFT + KIND_BITS;
public:
enum Kind {
CONSTANT_VALUE, CONSTANT_INDEX, USE, GPR, FPU, STACK_SLOT, ARGUMENT_SLOT };
static const uintptr_t DATA_MASK = (1 << DATA_BITS) - 1;
protected:
uint32_t data() const { return uint32_t(bits_) >> DATA_SHIFT; }
void setData(uint32_t data) {
MOZ_ASSERT(data <= DATA_MASK);
bits_ &= ~(DATA_MASK << DATA_SHIFT);
bits_ |= (data << DATA_SHIFT);
}
void setKindAndData(Kind kind, uint32_t data) {
MOZ_ASSERT(data <= DATA_MASK);
bits_ = (uint32_t(kind) << KIND_SHIFT) | data << DATA_SHIFT;
}
LAllocation(Kind kind, uint32_t data) { setKindAndData(kind, data); }
explicit LAllocation(Kind kind) { setKindAndData(kind, 0); }
public:
LAllocation() : bits_(0) { MOZ_ASSERT(isBogus()); }
explicit LAllocation(const MConstant* c) {
MOZ_ASSERT(c);
bits_ = uintptr_t(c);
MOZ_ASSERT((bits_ & (KIND_MASK << KIND_SHIFT)) == 0);
bits_ |= CONSTANT_VALUE << KIND_SHIFT;
}
inline explicit LAllocation(AnyRegister reg);
Kind kind() const { return (Kind)((bits_ >> KIND_SHIFT) & KIND_MASK); }
bool isBogus() const { return bits_ == 0; }
bool isUse() const { return kind() == USE; }
bool isConstant() const { return isConstantValue() || isConstantIndex(); }
bool isConstantValue() const { return kind() == CONSTANT_VALUE; }
bool isConstantIndex() const { return kind() == CONSTANT_INDEX; }
bool isGeneralReg() const { return kind() == GPR; }
bool isFloatReg() const { return kind() == FPU; }
bool isStackSlot() const { return kind() == STACK_SLOT; }
bool isArgument() const { return kind() == ARGUMENT_SLOT; }
bool isRegister() const { return isGeneralReg() || isFloatReg(); }
bool isRegister(bool needFloat) const {
return needFloat ? isFloatReg() : isGeneralReg();
}
bool isMemory() const { return isStackSlot() || isArgument(); }
inline uint32_t memorySlot() const;
inline LUse* toUse();
inline const LUse* toUse() const;
inline const LGeneralReg* toGeneralReg() const;
inline const LFloatReg* toFloatReg() const;
inline const LStackSlot* toStackSlot() const;
inline const LArgument* toArgument() const;
inline const LConstantIndex* toConstantIndex() const;
inline AnyRegister toRegister() const;
const MConstant* toConstant() const {
MOZ_ASSERT(isConstantValue());
return reinterpret_cast<const MConstant*>(bits_ &
~(KIND_MASK << KIND_SHIFT));
}
bool operator==(const LAllocation& other) const {
return bits_ == other.bits_;
}
bool operator!=(const LAllocation& other) const {
return bits_ != other.bits_;
}
HashNumber hash() const { return bits_; }
bool aliases(const LAllocation& other) const;
#ifdef JS_JITSPEW
UniqueChars toString() const;
void dump() const;
#endif
};
class LUse : public LAllocation {
static const uint32_t POLICY_BITS = 3;
static const uint32_t POLICY_SHIFT = 0;
static const uint32_t POLICY_MASK = (1 << POLICY_BITS) - 1;
static const uint32_t REG_BITS = 6;
static const uint32_t REG_SHIFT = POLICY_SHIFT + POLICY_BITS;
static const uint32_t REG_MASK = (1 << REG_BITS) - 1;
static const uint32_t USED_AT_START_BITS = 1;
static const uint32_t USED_AT_START_SHIFT = REG_SHIFT + REG_BITS;
static const uint32_t USED_AT_START_MASK = (1 << USED_AT_START_BITS) - 1;
public:
static const uint32_t VREG_BITS =
DATA_BITS - (USED_AT_START_SHIFT + USED_AT_START_BITS);
static const uint32_t VREG_SHIFT = USED_AT_START_SHIFT + USED_AT_START_BITS;
static const uint32_t VREG_MASK = (1 << VREG_BITS) - 1;
enum Policy {
ANY,
REGISTER,
FIXED,
KEEPALIVE,
RECOVERED_INPUT
};
void set(Policy policy, uint32_t reg, bool usedAtStart) {
setKindAndData(USE, (policy << POLICY_SHIFT) | (reg << REG_SHIFT) |
((usedAtStart ? 1 : 0) << USED_AT_START_SHIFT));
}
public:
LUse(uint32_t vreg, Policy policy, bool usedAtStart = false) {
set(policy, 0, usedAtStart);
setVirtualRegister(vreg);
}
explicit LUse(Policy policy, bool usedAtStart = false) {
set(policy, 0, usedAtStart);
}
explicit LUse(Register reg, bool usedAtStart = false) {
set(FIXED, reg.code(), usedAtStart);
}
explicit LUse(FloatRegister reg, bool usedAtStart = false) {
set(FIXED, reg.code(), usedAtStart);
}
LUse(Register reg, uint32_t virtualRegister, bool usedAtStart = false) {
set(FIXED, reg.code(), usedAtStart);
setVirtualRegister(virtualRegister);
}
LUse(FloatRegister reg, uint32_t virtualRegister, bool usedAtStart = false) {
set(FIXED, reg.code(), usedAtStart);
setVirtualRegister(virtualRegister);
}
void setVirtualRegister(uint32_t index) {
MOZ_ASSERT(index < VREG_MASK);
uint32_t old = data() & ~(VREG_MASK << VREG_SHIFT);
setData(old | (index << VREG_SHIFT));
}
Policy policy() const {
Policy policy = (Policy)((data() >> POLICY_SHIFT) & POLICY_MASK);
return policy;
}
uint32_t virtualRegister() const {
uint32_t index = (data() >> VREG_SHIFT) & VREG_MASK;
MOZ_ASSERT(index != 0);
return index;
}
uint32_t registerCode() const {
MOZ_ASSERT(policy() == FIXED);
return (data() >> REG_SHIFT) & REG_MASK;
}
bool isFixedRegister() const { return policy() == FIXED; }
bool usedAtStart() const {
return !!((data() >> USED_AT_START_SHIFT) & USED_AT_START_MASK);
}
};
static const uint32_t MAX_VIRTUAL_REGISTERS = LUse::VREG_MASK;
class LBoxAllocation {
#ifdef JS_NUNBOX32
LAllocation type_;
LAllocation payload_;
#else
LAllocation value_;
#endif
public:
#ifdef JS_NUNBOX32
LBoxAllocation(LAllocation type, LAllocation payload)
: type_(type), payload_(payload) {}
LAllocation type() const { return type_; }
LAllocation payload() const { return payload_; }
#else
explicit LBoxAllocation(LAllocation value) : value_(value) {}
LAllocation value() const { return value_; }
#endif
};
template <class ValT>
class LInt64Value {
#if JS_BITS_PER_WORD == 32
ValT high_;
ValT low_;
#else
ValT value_;
#endif
public:
#if JS_BITS_PER_WORD == 32
LInt64Value(ValT high, ValT low) : high_(high), low_(low) {}
ValT high() const { return high_; }
ValT low() const { return low_; }
#else
explicit LInt64Value(ValT value) : value_(value) {}
ValT value() const { return value_; }
#endif
};
using LInt64Allocation = LInt64Value<LAllocation>;
class LGeneralReg : public LAllocation {
public:
explicit LGeneralReg(Register reg) : LAllocation(GPR, reg.code()) {}
Register reg() const { return Register::FromCode(data()); }
};
class LFloatReg : public LAllocation {
public:
explicit LFloatReg(FloatRegister reg) : LAllocation(FPU, reg.code()) {}
FloatRegister reg() const { return FloatRegister::FromCode(data()); }
};
class LConstantIndex : public LAllocation {
explicit LConstantIndex(uint32_t index)
: LAllocation(CONSTANT_INDEX, index) {}
public:
static LConstantIndex FromIndex(uint32_t index) {
return LConstantIndex(index);
}
uint32_t index() const { return data(); }
};
class LStackSlot : public LAllocation {
public:
explicit LStackSlot(uint32_t slot) : LAllocation(STACK_SLOT, slot) {}
uint32_t slot() const { return data(); }
};
class LArgument : public LAllocation {
public:
explicit LArgument(uint32_t index) : LAllocation(ARGUMENT_SLOT, index) {}
uint32_t index() const { return data(); }
};
inline uint32_t LAllocation::memorySlot() const {
MOZ_ASSERT(isMemory());
return isStackSlot() ? toStackSlot()->slot() : toArgument()->index();
}
class LDefinition {
uint32_t bits_;
LAllocation output_;
static const uint32_t TYPE_BITS = 4;
static const uint32_t TYPE_SHIFT = 0;
static const uint32_t TYPE_MASK = (1 << TYPE_BITS) - 1;
static const uint32_t POLICY_BITS = 2;
static const uint32_t POLICY_SHIFT = TYPE_SHIFT + TYPE_BITS;
static const uint32_t POLICY_MASK = (1 << POLICY_BITS) - 1;
static const uint32_t VREG_BITS =
(sizeof(uint32_t) * 8) - (POLICY_BITS + TYPE_BITS);
static const uint32_t VREG_SHIFT = POLICY_SHIFT + POLICY_BITS;
static const uint32_t VREG_MASK = (1 << VREG_BITS) - 1;
public:
enum Policy {
FIXED,
REGISTER,
MUST_REUSE_INPUT
};
enum Type {
GENERAL, INT32, OBJECT, SLOTS, FLOAT32, DOUBLE, SIMD128INT, SIMD128FLOAT, SINCOS,
#ifdef JS_NUNBOX32
TYPE,
PAYLOAD
#else
BOX #endif
};
void set(uint32_t index, Type type, Policy policy) {
JS_STATIC_ASSERT(MAX_VIRTUAL_REGISTERS <= VREG_MASK);
bits_ =
(index << VREG_SHIFT) | (policy << POLICY_SHIFT) | (type << TYPE_SHIFT);
MOZ_ASSERT_IF(!SupportsSimd, !isSimdType());
}
public:
LDefinition(uint32_t index, Type type, Policy policy = REGISTER) {
set(index, type, policy);
}
explicit LDefinition(Type type, Policy policy = REGISTER) {
set(0, type, policy);
}
LDefinition(Type type, const LAllocation& a) : output_(a) {
set(0, type, FIXED);
}
LDefinition(uint32_t index, Type type, const LAllocation& a) : output_(a) {
set(index, type, FIXED);
}
LDefinition() : bits_(0) { MOZ_ASSERT(isBogusTemp()); }
static LDefinition BogusTemp() { return LDefinition(); }
Policy policy() const {
return (Policy)((bits_ >> POLICY_SHIFT) & POLICY_MASK);
}
Type type() const { return (Type)((bits_ >> TYPE_SHIFT) & TYPE_MASK); }
bool isSimdType() const {
return type() == SIMD128INT || type() == SIMD128FLOAT;
}
bool isCompatibleReg(const AnyRegister& r) const {
if (isFloatReg() && r.isFloat()) {
if (type() == FLOAT32) {
return r.fpu().isSingle();
}
if (type() == DOUBLE) {
return r.fpu().isDouble();
}
if (isSimdType()) {
return r.fpu().isSimd128();
}
MOZ_CRASH("Unexpected MDefinition type");
}
return !isFloatReg() && !r.isFloat();
}
bool isCompatibleDef(const LDefinition& other) const {
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
if (isFloatReg() && other.isFloatReg()) {
return type() == other.type();
}
return !isFloatReg() && !other.isFloatReg();
#else
return isFloatReg() == other.isFloatReg();
#endif
}
bool isFloatReg() const {
return type() == FLOAT32 || type() == DOUBLE || isSimdType();
}
uint32_t virtualRegister() const {
uint32_t index = (bits_ >> VREG_SHIFT) & VREG_MASK;
return index;
}
LAllocation* output() { return &output_; }
const LAllocation* output() const { return &output_; }
bool isFixed() const { return policy() == FIXED; }
bool isBogusTemp() const { return isFixed() && output()->isBogus(); }
void setVirtualRegister(uint32_t index) {
MOZ_ASSERT(index < VREG_MASK);
bits_ &= ~(VREG_MASK << VREG_SHIFT);
bits_ |= index << VREG_SHIFT;
}
void setOutput(const LAllocation& a) {
output_ = a;
if (!a.isUse()) {
bits_ &= ~(POLICY_MASK << POLICY_SHIFT);
bits_ |= FIXED << POLICY_SHIFT;
}
}
void setReusedInput(uint32_t operand) {
output_ = LConstantIndex::FromIndex(operand);
}
uint32_t getReusedInput() const {
MOZ_ASSERT(policy() == LDefinition::MUST_REUSE_INPUT);
return output_.toConstantIndex()->index();
}
static inline Type TypeFrom(MIRType type) {
switch (type) {
case MIRType::Boolean:
case MIRType::Int32:
static_assert(sizeof(bool) <= sizeof(int32_t),
"bool doesn't fit in an int32 slot");
return LDefinition::INT32;
case MIRType::String:
case MIRType::Symbol:
case MIRType::BigInt:
case MIRType::Object:
case MIRType::ObjectOrNull:
case MIRType::RefOrNull:
return LDefinition::OBJECT;
case MIRType::Double:
return LDefinition::DOUBLE;
case MIRType::Float32:
return LDefinition::FLOAT32;
#if defined(JS_PUNBOX64)
case MIRType::Value:
return LDefinition::BOX;
#endif
case MIRType::SinCosDouble:
return LDefinition::SINCOS;
case MIRType::Slots:
case MIRType::Elements:
return LDefinition::SLOTS;
case MIRType::Pointer:
return LDefinition::GENERAL;
#if defined(JS_PUNBOX64)
case MIRType::Int64:
return LDefinition::GENERAL;
#endif
case MIRType::Int8x16:
case MIRType::Int16x8:
case MIRType::Int32x4:
case MIRType::Bool8x16:
case MIRType::Bool16x8:
case MIRType::Bool32x4:
return LDefinition::SIMD128INT;
case MIRType::Float32x4:
return LDefinition::SIMD128FLOAT;
default:
MOZ_CRASH("unexpected type");
}
}
UniqueChars toString() const;
#ifdef JS_JITSPEW
void dump() const;
#endif
};
using LInt64Definition = LInt64Value<LDefinition>;
#define LIROP(op) class L##op;
LIR_OPCODE_LIST(LIROP)
#undef LIROP
class LSnapshot;
class LSafepoint;
class LInstruction;
class LElementVisitor;
constexpr size_t MaxNumLInstructionOperands = 63;
class LNode {
protected:
MDefinition* mir_;
private:
LBlock* block_;
uint32_t id_;
protected:
uint32_t op_ : 10;
uint32_t isCall_ : 1;
uint32_t nonPhiNumOperands_ : 6;
static_assert((1 << 6) - 1 == MaxNumLInstructionOperands,
"packing constraints");
uint32_t nonPhiOperandsOffset_ : 5;
uint32_t numDefs_ : 4;
uint32_t numTemps_ : 4;
public:
enum class Opcode {
#define LIROP(name) name,
LIR_OPCODE_LIST(LIROP)
#undef LIROP
Invalid
};
LNode(Opcode op, uint32_t nonPhiNumOperands, uint32_t numDefs,
uint32_t numTemps)
: mir_(nullptr),
block_(nullptr),
id_(0),
op_(uint32_t(op)),
isCall_(false),
nonPhiNumOperands_(nonPhiNumOperands),
nonPhiOperandsOffset_(0),
numDefs_(numDefs),
numTemps_(numTemps) {
MOZ_ASSERT(op < Opcode::Invalid);
MOZ_ASSERT(op_ == uint32_t(op), "opcode must fit in bitfield");
MOZ_ASSERT(nonPhiNumOperands_ == nonPhiNumOperands,
"nonPhiNumOperands must fit in bitfield");
MOZ_ASSERT(numDefs_ == numDefs, "numDefs must fit in bitfield");
MOZ_ASSERT(numTemps_ == numTemps, "numTemps must fit in bitfield");
}
const char* opName() {
switch (op()) {
#define LIR_NAME_INS(name) \
case Opcode::name: \
return #name;
LIR_OPCODE_LIST(LIR_NAME_INS)
#undef LIR_NAME_INS
default:
MOZ_CRASH("Invalid op");
}
}
private:
const char* extraName() const { return nullptr; }
public:
#ifdef JS_JITSPEW
const char* getExtraName() const;
#endif
Opcode op() const { return Opcode(op_); }
bool isInstruction() const { return op() != Opcode::Phi; }
inline LInstruction* toInstruction();
inline const LInstruction* toInstruction() const;
size_t numDefs() const { return numDefs_; }
bool isCall() const { return isCall_; }
inline bool isCallPreserved(AnyRegister reg) const;
uint32_t id() const { return id_; }
void setId(uint32_t id) {
MOZ_ASSERT(!id_);
MOZ_ASSERT(id);
id_ = id;
}
void setMir(MDefinition* mir) { mir_ = mir; }
MDefinition* mirRaw() const {
return mir_;
}
LBlock* block() const { return block_; }
void setBlock(LBlock* block) { block_ = block; }
inline bool recoversInput() const;
#ifdef JS_JITSPEW
void dump(GenericPrinter& out);
void dump();
static void printName(GenericPrinter& out, Opcode op);
void printName(GenericPrinter& out);
void printOperands(GenericPrinter& out);
#endif
public:
#define LIROP(name) \
bool is##name() const { return op() == Opcode::name; } \
inline L##name* to##name(); \
inline const L##name* to##name() const;
LIR_OPCODE_LIST(LIROP)
#undef LIROP
#define LIR_HEADER(opcode) \
static constexpr LNode::Opcode classOpcode = LNode::Opcode::opcode;
};
class LInstruction : public LNode,
public TempObject,
public InlineListNode<LInstruction> {
LSnapshot* snapshot_;
LSafepoint* safepoint_;
LMoveGroup* inputMoves_;
LMoveGroup* fixReuseMoves_;
LMoveGroup* movesAfter_;
protected:
LInstruction(Opcode opcode, uint32_t numOperands, uint32_t numDefs,
uint32_t numTemps)
: LNode(opcode, numOperands, numDefs, numTemps),
snapshot_(nullptr),
safepoint_(nullptr),
inputMoves_(nullptr),
fixReuseMoves_(nullptr),
movesAfter_(nullptr) {}
void setIsCall() { isCall_ = true; }
public:
inline LDefinition* getDef(size_t index);
void setDef(size_t index, const LDefinition& def) { *getDef(index) = def; }
LAllocation* getOperand(size_t index) const {
MOZ_ASSERT(index < numOperands());
MOZ_ASSERT(nonPhiOperandsOffset_ > 0);
uintptr_t p = reinterpret_cast<uintptr_t>(this + 1) +
nonPhiOperandsOffset_ * sizeof(uintptr_t);
return reinterpret_cast<LAllocation*>(p) + index;
}
void setOperand(size_t index, const LAllocation& a) {
*getOperand(index) = a;
}
void initOperandsOffset(size_t offset) {
MOZ_ASSERT(nonPhiOperandsOffset_ == 0);
MOZ_ASSERT(offset >= sizeof(LInstruction));
MOZ_ASSERT(((offset - sizeof(LInstruction)) % sizeof(uintptr_t)) == 0);
offset = (offset - sizeof(LInstruction)) / sizeof(uintptr_t);
nonPhiOperandsOffset_ = offset;
MOZ_ASSERT(nonPhiOperandsOffset_ == offset, "offset must fit in bitfield");
}
size_t numTemps() const { return numTemps_; }
inline LDefinition* getTemp(size_t index);
LSnapshot* snapshot() const { return snapshot_; }
LSafepoint* safepoint() const { return safepoint_; }
LMoveGroup* inputMoves() const { return inputMoves_; }
void setInputMoves(LMoveGroup* moves) { inputMoves_ = moves; }
LMoveGroup* fixReuseMoves() const { return fixReuseMoves_; }
void setFixReuseMoves(LMoveGroup* moves) { fixReuseMoves_ = moves; }
LMoveGroup* movesAfter() const { return movesAfter_; }
void setMovesAfter(LMoveGroup* moves) { movesAfter_ = moves; }
uint32_t numOperands() const { return nonPhiNumOperands_; }
void assignSnapshot(LSnapshot* snapshot);
void initSafepoint(TempAllocator& alloc);
class InputIterator;
};
LInstruction* LNode::toInstruction() {
MOZ_ASSERT(isInstruction());
return static_cast<LInstruction*>(this);
}
const LInstruction* LNode::toInstruction() const {
MOZ_ASSERT(isInstruction());
return static_cast<const LInstruction*>(this);
}
class LElementVisitor {
LNode* ins_;
protected:
jsbytecode* lastPC_;
jsbytecode* lastNotInlinedPC_;
LNode* instruction() { return ins_; }
void setElement(LNode* ins) {
ins_ = ins;
if (ins->mirRaw()) {
lastPC_ = ins->mirRaw()->trackedPc();
if (ins->mirRaw()->trackedTree()) {
lastNotInlinedPC_ = ins->mirRaw()->profilerLeavePc();
}
}
}
LElementVisitor()
: ins_(nullptr), lastPC_(nullptr), lastNotInlinedPC_(nullptr) {}
};
typedef InlineList<LInstruction>::iterator LInstructionIterator;
typedef InlineList<LInstruction>::reverse_iterator LInstructionReverseIterator;
class MPhi;
class LPhi final : public LNode {
LAllocation* const inputs_;
LDefinition def_;
public:
LIR_HEADER(Phi)
LPhi(MPhi* ins, LAllocation* inputs)
: LNode(classOpcode,
0,
1,
0),
inputs_(inputs) {
setMir(ins);
}
LDefinition* getDef(size_t index) {
MOZ_ASSERT(index == 0);
return &def_;
}
void setDef(size_t index, const LDefinition& def) {
MOZ_ASSERT(index == 0);
def_ = def;
}
size_t numOperands() const { return mir_->toPhi()->numOperands(); }
LAllocation* getOperand(size_t index) {
MOZ_ASSERT(index < numOperands());
return &inputs_[index];
}
void setOperand(size_t index, const LAllocation& a) {
MOZ_ASSERT(index < numOperands());
inputs_[index] = a;
}
size_t numTemps() const = delete;
LDefinition* getTemp(size_t index) = delete;
};
class LMoveGroup;
class LBlock {
MBasicBlock* block_;
FixedList<LPhi> phis_;
InlineList<LInstruction> instructions_;
LMoveGroup* entryMoveGroup_;
LMoveGroup* exitMoveGroup_;
Label label_;
public:
explicit LBlock(MBasicBlock* block);
MOZ_MUST_USE bool init(TempAllocator& alloc);
void add(LInstruction* ins) {
ins->setBlock(this);
instructions_.pushBack(ins);
}
size_t numPhis() const { return phis_.length(); }
LPhi* getPhi(size_t index) { return &phis_[index]; }
const LPhi* getPhi(size_t index) const { return &phis_[index]; }
MBasicBlock* mir() const { return block_; }
LInstructionIterator begin() { return instructions_.begin(); }
LInstructionIterator begin(LInstruction* at) {
return instructions_.begin(at);
}
LInstructionIterator end() { return instructions_.end(); }
LInstructionReverseIterator rbegin() { return instructions_.rbegin(); }
LInstructionReverseIterator rbegin(LInstruction* at) {
return instructions_.rbegin(at);
}
LInstructionReverseIterator rend() { return instructions_.rend(); }
InlineList<LInstruction>& instructions() { return instructions_; }
void insertAfter(LInstruction* at, LInstruction* ins) {
instructions_.insertAfter(at, ins);
}
void insertBefore(LInstruction* at, LInstruction* ins) {
instructions_.insertBefore(at, ins);
}
const LNode* firstElementWithId() const {
return !phis_.empty() ? static_cast<const LNode*>(getPhi(0))
: firstInstructionWithId();
}
uint32_t firstId() const { return firstElementWithId()->id(); }
uint32_t lastId() const { return lastInstructionWithId()->id(); }
const LInstruction* firstInstructionWithId() const;
const LInstruction* lastInstructionWithId() const {
const LInstruction* last = *instructions_.rbegin();
MOZ_ASSERT(last->id());
MOZ_ASSERT(last->numDefs() == 0);
return last;
}
Label* label() {
MOZ_ASSERT(!isTrivial());
return &label_;
}
LMoveGroup* getEntryMoveGroup(TempAllocator& alloc);
LMoveGroup* getExitMoveGroup(TempAllocator& alloc);
bool isTrivial() { return begin()->isGoto() && !mir()->isLoopHeader(); }
#ifdef JS_JITSPEW
void dump(GenericPrinter& out);
void dump();
#endif
};
namespace details {
template <size_t Defs, size_t Temps>
class LInstructionFixedDefsTempsHelper : public LInstruction {
mozilla::Array<LDefinition, Defs + Temps> defsAndTemps_;
protected:
LInstructionFixedDefsTempsHelper(Opcode opcode, uint32_t numOperands)
: LInstruction(opcode, numOperands, Defs, Temps) {}
public:
LDefinition* getDef(size_t index) {
MOZ_ASSERT(index < Defs);
return &defsAndTemps_[index];
}
LDefinition* getTemp(size_t index) {
MOZ_ASSERT(index < Temps);
return &defsAndTemps_[Defs + index];
}
void setDef(size_t index, const LDefinition& def) {
MOZ_ASSERT(index < Defs);
defsAndTemps_[index] = def;
}
void setTemp(size_t index, const LDefinition& a) {
MOZ_ASSERT(index < Temps);
defsAndTemps_[Defs + index] = a;
}
void setInt64Temp(size_t index, const LInt64Definition& a) {
#if JS_BITS_PER_WORD == 32
setTemp(index, a.low());
setTemp(index + 1, a.high());
#else
setTemp(index, a.value());
#endif
}
const LAllocation* input() {
MOZ_ASSERT(numOperands() == 1);
return getOperand(0);
}
const LDefinition* output() {
MOZ_ASSERT(numDefs() == 1);
return getDef(0);
}
static size_t offsetOfDef(size_t index) {
using T = LInstructionFixedDefsTempsHelper<0, 0>;
return offsetof(T, defsAndTemps_) + index * sizeof(LDefinition);
}
static size_t offsetOfTemp(uint32_t numDefs, uint32_t index) {
using T = LInstructionFixedDefsTempsHelper<0, 0>;
return offsetof(T, defsAndTemps_) + (numDefs + index) * sizeof(LDefinition);
}
};
}
inline LDefinition* LInstruction::getDef(size_t index) {
MOZ_ASSERT(index < numDefs());
using T = details::LInstructionFixedDefsTempsHelper<0, 0>;
uint8_t* p = reinterpret_cast<uint8_t*>(this) + T::offsetOfDef(index);
return reinterpret_cast<LDefinition*>(p);
}
inline LDefinition* LInstruction::getTemp(size_t index) {
MOZ_ASSERT(index < numTemps());
using T = details::LInstructionFixedDefsTempsHelper<0, 0>;
uint8_t* p =
reinterpret_cast<uint8_t*>(this) + T::offsetOfTemp(numDefs(), index);
return reinterpret_cast<LDefinition*>(p);
}
template <size_t Defs, size_t Operands, size_t Temps>
class LInstructionHelper
: public details::LInstructionFixedDefsTempsHelper<Defs, Temps> {
mozilla::Array<LAllocation, Operands> operands_;
protected:
explicit LInstructionHelper(LNode::Opcode opcode)
: details::LInstructionFixedDefsTempsHelper<Defs, Temps>(opcode,
Operands) {
static_assert(
Operands == 0 || sizeof(operands_) == Operands * sizeof(LAllocation),
"mozilla::Array should not contain other fields");
if (Operands > 0) {
using T = LInstructionHelper<Defs, Operands, Temps>;
this->initOperandsOffset(offsetof(T, operands_));
}
}
public:
LAllocation* getOperand(size_t index) { return &operands_[index]; }
void setOperand(size_t index, const LAllocation& a) { operands_[index] = a; }
void setBoxOperand(size_t index, const LBoxAllocation& alloc) {
#ifdef JS_NUNBOX32
operands_[index + TYPE_INDEX] = alloc.type();
operands_[index + PAYLOAD_INDEX] = alloc.payload();
#else
operands_[index] = alloc.value();
#endif
}
void setInt64Operand(size_t index, const LInt64Allocation& alloc) {
#if JS_BITS_PER_WORD == 32
operands_[index + INT64LOW_INDEX] = alloc.low();
operands_[index + INT64HIGH_INDEX] = alloc.high();
#else
operands_[index] = alloc.value();
#endif
}
const LInt64Allocation getInt64Operand(size_t offset) {
#if JS_BITS_PER_WORD == 32
return LInt64Allocation(operands_[offset + INT64HIGH_INDEX],
operands_[offset + INT64LOW_INDEX]);
#else
return LInt64Allocation(operands_[offset]);
#endif
}
};
template <size_t Defs, size_t Temps>
class LVariadicInstruction
: public details::LInstructionFixedDefsTempsHelper<Defs, Temps> {
protected:
LVariadicInstruction(LNode::Opcode opcode, size_t numOperands)
: details::LInstructionFixedDefsTempsHelper<Defs, Temps>(opcode,
numOperands) {}
public:
void setBoxOperand(size_t index, const LBoxAllocation& a) {
#ifdef JS_NUNBOX32
this->setOperand(index + TYPE_INDEX, a.type());
this->setOperand(index + PAYLOAD_INDEX, a.payload());
#else
this->setOperand(index, a.value());
#endif
}
};
template <size_t Defs, size_t Operands, size_t Temps>
class LCallInstructionHelper
: public LInstructionHelper<Defs, Operands, Temps> {
protected:
explicit LCallInstructionHelper(LNode::Opcode opcode)
: LInstructionHelper<Defs, Operands, Temps>(opcode) {
this->setIsCall();
}
};
template <size_t Defs, size_t Temps>
class LBinaryCallInstructionHelper
: public LCallInstructionHelper<Defs, 2, Temps> {
protected:
explicit LBinaryCallInstructionHelper(LNode::Opcode opcode)
: LCallInstructionHelper<Defs, 2, Temps>(opcode) {}
public:
const LAllocation* lhs() { return this->getOperand(0); }
const LAllocation* rhs() { return this->getOperand(1); }
};
class LRecoverInfo : public TempObject {
public:
typedef Vector<MNode*, 2, JitAllocPolicy> Instructions;
private:
Instructions instructions_;
RecoverOffset recoverOffset_;
explicit LRecoverInfo(TempAllocator& alloc);
MOZ_MUST_USE bool init(MResumePoint* mir);
template <typename Node>
MOZ_MUST_USE bool appendOperands(Node* ins);
MOZ_MUST_USE bool appendDefinition(MDefinition* def);
MOZ_MUST_USE bool appendResumePoint(MResumePoint* rp);
public:
static LRecoverInfo* New(MIRGenerator* gen, MResumePoint* mir);
MResumePoint* mir() const { return instructions_.back()->toResumePoint(); }
RecoverOffset recoverOffset() const { return recoverOffset_; }
void setRecoverOffset(RecoverOffset offset) {
MOZ_ASSERT(recoverOffset_ == INVALID_RECOVER_OFFSET);
recoverOffset_ = offset;
}
MNode** begin() { return instructions_.begin(); }
MNode** end() { return instructions_.end(); }
size_t numInstructions() const { return instructions_.length(); }
class OperandIter {
private:
MNode** it_;
MNode** end_;
size_t op_;
size_t opEnd_;
MResumePoint* rp_;
MNode* node_;
public:
explicit OperandIter(LRecoverInfo* recoverInfo)
: it_(recoverInfo->begin()),
end_(recoverInfo->end()),
op_(0),
opEnd_(0),
rp_(nullptr),
node_(nullptr) {
settle();
}
void settle() {
opEnd_ = (*it_)->numOperands();
while (opEnd_ == 0) {
++it_;
op_ = 0;
opEnd_ = (*it_)->numOperands();
}
node_ = *it_;
if (node_->isResumePoint()) {
rp_ = node_->toResumePoint();
}
}
MDefinition* operator*() {
if (rp_) { return rp_->getOperand(op_);
}
return node_->getOperand(op_);
}
MDefinition* operator->() {
if (rp_) { return rp_->getOperand(op_);
}
return node_->getOperand(op_);
}
OperandIter& operator++() {
++op_;
if (op_ != opEnd_) {
return *this;
}
op_ = 0;
++it_;
node_ = rp_ = nullptr;
if (!*this) {
settle();
}
return *this;
}
explicit operator bool() const { return it_ == end_; }
#ifdef DEBUG
bool canOptimizeOutIfUnused();
#endif
};
};
class LSnapshot : public TempObject {
private:
uint32_t numSlots_;
LAllocation* slots_;
LRecoverInfo* recoverInfo_;
SnapshotOffset snapshotOffset_;
BailoutId bailoutId_;
BailoutKind bailoutKind_;
LSnapshot(LRecoverInfo* recover, BailoutKind kind);
MOZ_MUST_USE bool init(MIRGenerator* gen);
public:
static LSnapshot* New(MIRGenerator* gen, LRecoverInfo* recover,
BailoutKind kind);
size_t numEntries() const { return numSlots_; }
size_t numSlots() const { return numSlots_ / BOX_PIECES; }
LAllocation* payloadOfSlot(size_t i) {
MOZ_ASSERT(i < numSlots());
size_t entryIndex = (i * BOX_PIECES) + (BOX_PIECES - 1);
return getEntry(entryIndex);
}
#ifdef JS_NUNBOX32
LAllocation* typeOfSlot(size_t i) {
MOZ_ASSERT(i < numSlots());
size_t entryIndex = (i * BOX_PIECES) + (BOX_PIECES - 2);
return getEntry(entryIndex);
}
#endif
LAllocation* getEntry(size_t i) {
MOZ_ASSERT(i < numSlots_);
return &slots_[i];
}
void setEntry(size_t i, const LAllocation& alloc) {
MOZ_ASSERT(i < numSlots_);
slots_[i] = alloc;
}
LRecoverInfo* recoverInfo() const { return recoverInfo_; }
MResumePoint* mir() const { return recoverInfo()->mir(); }
SnapshotOffset snapshotOffset() const { return snapshotOffset_; }
BailoutId bailoutId() const { return bailoutId_; }
void setSnapshotOffset(SnapshotOffset offset) {
MOZ_ASSERT(snapshotOffset_ == INVALID_SNAPSHOT_OFFSET);
snapshotOffset_ = offset;
}
void setBailoutId(BailoutId id) {
MOZ_ASSERT(bailoutId_ == INVALID_BAILOUT_ID);
bailoutId_ = id;
}
BailoutKind bailoutKind() const { return bailoutKind_; }
void rewriteRecoveredInput(LUse input);
};
struct SafepointSlotEntry {
uint32_t stack : 1;
uint32_t slot : 31;
SafepointSlotEntry() : stack(0), slot(0) {}
SafepointSlotEntry(bool stack, uint32_t slot) : stack(stack), slot(slot) {}
explicit SafepointSlotEntry(const LAllocation* a)
: stack(a->isStackSlot()), slot(a->memorySlot()) {}
};
struct SafepointNunboxEntry {
uint32_t typeVreg;
LAllocation type;
LAllocation payload;
SafepointNunboxEntry() : typeVreg(0) {}
SafepointNunboxEntry(uint32_t typeVreg, LAllocation type, LAllocation payload)
: typeVreg(typeVreg), type(type), payload(payload) {}
};
class LSafepoint : public TempObject {
typedef SafepointSlotEntry SlotEntry;
typedef SafepointNunboxEntry NunboxEntry;
public:
typedef Vector<SlotEntry, 0, JitAllocPolicy> SlotList;
typedef Vector<NunboxEntry, 0, JitAllocPolicy> NunboxList;
private:
LiveRegisterSet liveRegs_;
LiveGeneralRegisterSet gcRegs_;
#ifdef CHECK_OSIPOINT_REGISTERS
LiveRegisterSet clobberedRegs_;
#endif
uint32_t safepointOffset_;
uint32_t osiCallPointOffset_;
SlotList gcSlots_;
SlotList valueSlots_;
#ifdef JS_NUNBOX32
NunboxList nunboxParts_;
#elif JS_PUNBOX64
LiveGeneralRegisterSet valueRegs_;
#endif
LiveGeneralRegisterSet slotsOrElementsRegs_;
SlotList slotsOrElementsSlots_;
bool isWasmTrap_;
uint32_t framePushedAtStackMapBase_;
public:
void assertInvariants() {
#ifndef JS_NUNBOX32
MOZ_ASSERT((valueRegs().bits() & ~liveRegs().gprs().bits()) == 0);
#endif
MOZ_ASSERT((gcRegs().bits() & ~liveRegs().gprs().bits()) == 0);
}
explicit LSafepoint(TempAllocator& alloc)
: safepointOffset_(INVALID_SAFEPOINT_OFFSET),
osiCallPointOffset_(0),
gcSlots_(alloc),
valueSlots_(alloc)
#ifdef JS_NUNBOX32
,
nunboxParts_(alloc)
#endif
,
slotsOrElementsSlots_(alloc),
isWasmTrap_(false),
framePushedAtStackMapBase_(0) {
assertInvariants();
}
void addLiveRegister(AnyRegister reg) {
liveRegs_.addUnchecked(reg);
assertInvariants();
}
const LiveRegisterSet& liveRegs() const { return liveRegs_; }
#ifdef CHECK_OSIPOINT_REGISTERS
void addClobberedRegister(AnyRegister reg) {
clobberedRegs_.addUnchecked(reg);
assertInvariants();
}
const LiveRegisterSet& clobberedRegs() const { return clobberedRegs_; }
#endif
void addGcRegister(Register reg) {
gcRegs_.addUnchecked(reg);
assertInvariants();
}
LiveGeneralRegisterSet gcRegs() const { return gcRegs_; }
MOZ_MUST_USE bool addGcSlot(bool stack, uint32_t slot) {
bool result = gcSlots_.append(SlotEntry(stack, slot));
if (result) {
assertInvariants();
}
return result;
}
SlotList& gcSlots() { return gcSlots_; }
SlotList& slotsOrElementsSlots() { return slotsOrElementsSlots_; }
LiveGeneralRegisterSet slotsOrElementsRegs() const {
return slotsOrElementsRegs_;
}
void addSlotsOrElementsRegister(Register reg) {
slotsOrElementsRegs_.addUnchecked(reg);
assertInvariants();
}
MOZ_MUST_USE bool addSlotsOrElementsSlot(bool stack, uint32_t slot) {
bool result = slotsOrElementsSlots_.append(SlotEntry(stack, slot));
if (result) {
assertInvariants();
}
return result;
}
MOZ_MUST_USE bool addSlotsOrElementsPointer(LAllocation alloc) {
if (alloc.isMemory()) {
return addSlotsOrElementsSlot(alloc.isStackSlot(), alloc.memorySlot());
}
MOZ_ASSERT(alloc.isRegister());
addSlotsOrElementsRegister(alloc.toRegister().gpr());
assertInvariants();
return true;
}
bool hasSlotsOrElementsPointer(LAllocation alloc) const {
if (alloc.isRegister()) {
return slotsOrElementsRegs().has(alloc.toRegister().gpr());
}
for (size_t i = 0; i < slotsOrElementsSlots_.length(); i++) {
const SlotEntry& entry = slotsOrElementsSlots_[i];
if (entry.stack == alloc.isStackSlot() &&
entry.slot == alloc.memorySlot()) {
return true;
}
}
return false;
}
MOZ_MUST_USE bool addGcPointer(LAllocation alloc) {
if (alloc.isMemory()) {
return addGcSlot(alloc.isStackSlot(), alloc.memorySlot());
}
if (alloc.isRegister()) {
addGcRegister(alloc.toRegister().gpr());
}
assertInvariants();
return true;
}
bool hasGcPointer(LAllocation alloc) const {
if (alloc.isRegister()) {
return gcRegs().has(alloc.toRegister().gpr());
}
MOZ_ASSERT(alloc.isMemory());
for (size_t i = 0; i < gcSlots_.length(); i++) {
if (gcSlots_[i].stack == alloc.isStackSlot() &&
gcSlots_[i].slot == alloc.memorySlot()) {
return true;
}
}
return false;
}
MOZ_MUST_USE bool addValueSlot(bool stack, uint32_t slot) {
bool result = valueSlots_.append(SlotEntry(stack, slot));
if (result) {
assertInvariants();
}
return result;
}
SlotList& valueSlots() { return valueSlots_; }
bool hasValueSlot(bool stack, uint32_t slot) const {
for (size_t i = 0; i < valueSlots_.length(); i++) {
if (valueSlots_[i].stack == stack && valueSlots_[i].slot == slot) {
return true;
}
}
return false;
}
#ifdef JS_NUNBOX32
MOZ_MUST_USE bool addNunboxParts(uint32_t typeVreg, LAllocation type,
LAllocation payload) {
bool result = nunboxParts_.append(NunboxEntry(typeVreg, type, payload));
if (result) {
assertInvariants();
}
return result;
}
MOZ_MUST_USE bool addNunboxType(uint32_t typeVreg, LAllocation type) {
for (size_t i = 0; i < nunboxParts_.length(); i++) {
if (nunboxParts_[i].type == type) {
return true;
}
if (nunboxParts_[i].type == LUse(typeVreg, LUse::ANY)) {
nunboxParts_[i].type = type;
return true;
}
}
uint32_t payloadVreg = typeVreg + 1;
bool result = nunboxParts_.append(
NunboxEntry(typeVreg, type, LUse(payloadVreg, LUse::ANY)));
if (result) {
assertInvariants();
}
return result;
}
MOZ_MUST_USE bool addNunboxPayload(uint32_t payloadVreg,
LAllocation payload) {
for (size_t i = 0; i < nunboxParts_.length(); i++) {
if (nunboxParts_[i].payload == payload) {
return true;
}
if (nunboxParts_[i].payload == LUse(payloadVreg, LUse::ANY)) {
nunboxParts_[i].payload = payload;
return true;
}
}
uint32_t typeVreg = payloadVreg - 1;
bool result = nunboxParts_.append(
NunboxEntry(typeVreg, LUse(typeVreg, LUse::ANY), payload));
if (result) {
assertInvariants();
}
return result;
}
LAllocation findTypeAllocation(uint32_t typeVreg) {
for (size_t i = 0; i < nunboxParts_.length(); i++) {
if (nunboxParts_[i].typeVreg == typeVreg &&
!nunboxParts_[i].type.isUse()) {
return nunboxParts_[i].type;
}
}
return LUse(typeVreg, LUse::ANY);
}
# ifdef DEBUG
bool hasNunboxPayload(LAllocation payload) const {
if (payload.isMemory() &&
hasValueSlot(payload.isStackSlot(), payload.memorySlot())) {
return true;
}
for (size_t i = 0; i < nunboxParts_.length(); i++) {
if (nunboxParts_[i].payload == payload) {
return true;
}
}
return false;
}
# endif
NunboxList& nunboxParts() { return nunboxParts_; }
#elif JS_PUNBOX64
void addValueRegister(Register reg) {
valueRegs_.add(reg);
assertInvariants();
}
LiveGeneralRegisterSet valueRegs() const { return valueRegs_; }
MOZ_MUST_USE bool addBoxedValue(LAllocation alloc) {
if (alloc.isRegister()) {
Register reg = alloc.toRegister().gpr();
if (!valueRegs().has(reg)) {
addValueRegister(reg);
}
return true;
}
if (hasValueSlot(alloc.isStackSlot(), alloc.memorySlot())) {
return true;
}
return addValueSlot(alloc.isStackSlot(), alloc.memorySlot());
}
bool hasBoxedValue(LAllocation alloc) const {
if (alloc.isRegister()) {
return valueRegs().has(alloc.toRegister().gpr());
}
return hasValueSlot(alloc.isStackSlot(), alloc.memorySlot());
}
#endif
bool encoded() const { return safepointOffset_ != INVALID_SAFEPOINT_OFFSET; }
uint32_t offset() const {
MOZ_ASSERT(encoded());
return safepointOffset_;
}
void setOffset(uint32_t offset) { safepointOffset_ = offset; }
uint32_t osiReturnPointOffset() const {
return osiCallPointOffset_ + Assembler::PatchWrite_NearCallSize();
}
uint32_t osiCallPointOffset() const { return osiCallPointOffset_; }
void setOsiCallPointOffset(uint32_t osiCallPointOffset) {
MOZ_ASSERT(!osiCallPointOffset_);
osiCallPointOffset_ = osiCallPointOffset;
}
bool isWasmTrap() const { return isWasmTrap_; }
void setIsWasmTrap() { isWasmTrap_ = true; }
uint32_t framePushedAtStackMapBase() const {
return framePushedAtStackMapBase_;
}
void setFramePushedAtStackMapBase(uint32_t n) {
MOZ_ASSERT(framePushedAtStackMapBase_ == 0);
framePushedAtStackMapBase_ = n;
}
};
class LInstruction::InputIterator {
private:
LInstruction& ins_;
size_t idx_;
bool snapshot_;
void handleOperandsEnd() {
if (!snapshot_ && idx_ == ins_.numOperands() && ins_.snapshot()) {
idx_ = 0;
snapshot_ = true;
}
}
public:
explicit InputIterator(LInstruction& ins)
: ins_(ins), idx_(0), snapshot_(false) {
handleOperandsEnd();
}
bool more() const {
if (snapshot_) {
return idx_ < ins_.snapshot()->numEntries();
}
if (idx_ < ins_.numOperands()) {
return true;
}
if (ins_.snapshot() && ins_.snapshot()->numEntries()) {
return true;
}
return false;
}
bool isSnapshotInput() const { return snapshot_; }
void next() {
MOZ_ASSERT(more());
idx_++;
handleOperandsEnd();
}
void replace(const LAllocation& alloc) {
if (snapshot_) {
ins_.snapshot()->setEntry(idx_, alloc);
} else {
ins_.setOperand(idx_, alloc);
}
}
LAllocation* operator*() const {
if (snapshot_) {
return ins_.snapshot()->getEntry(idx_);
}
return ins_.getOperand(idx_);
}
LAllocation* operator->() const { return **this; }
};
class LIRGraph {
struct ValueHasher {
typedef Value Lookup;
static HashNumber hash(const Value& v) { return HashNumber(v.asRawBits()); }
static bool match(const Value& lhs, const Value& rhs) { return lhs == rhs; }
};
FixedList<LBlock> blocks_;
mozilla::Vector<Value, 0, JitAllocPolicy> constantPool_;
typedef HashMap<Value, uint32_t, ValueHasher, JitAllocPolicy> ConstantPoolMap;
ConstantPoolMap constantPoolMap_;
Vector<LInstruction*, 0, JitAllocPolicy> safepoints_;
Vector<LInstruction*, 0, JitAllocPolicy> nonCallSafepoints_;
uint32_t numVirtualRegisters_;
uint32_t numInstructions_;
uint32_t localSlotCount_;
uint32_t argumentSlotCount_;
LSnapshot* entrySnapshot_;
MIRGraph& mir_;
public:
explicit LIRGraph(MIRGraph* mir);
MOZ_MUST_USE bool init() {
return blocks_.init(mir_.alloc(), mir_.numBlocks());
}
MIRGraph& mir() const { return mir_; }
size_t numBlocks() const { return blocks_.length(); }
LBlock* getBlock(size_t i) { return &blocks_[i]; }
uint32_t numBlockIds() const { return mir_.numBlockIds(); }
MOZ_MUST_USE bool initBlock(MBasicBlock* mir) {
auto* block = &blocks_[mir->id()];
auto* lir = new (block) LBlock(mir);
return lir->init(mir_.alloc());
}
uint32_t getVirtualRegister() {
numVirtualRegisters_ += VREG_INCREMENT;
return numVirtualRegisters_;
}
uint32_t numVirtualRegisters() const {
return numVirtualRegisters_ + 1;
}
uint32_t getInstructionId() { return numInstructions_++; }
uint32_t numInstructions() const { return numInstructions_; }
void setLocalSlotCount(uint32_t localSlotCount) {
localSlotCount_ = localSlotCount;
}
uint32_t localSlotCount() const { return localSlotCount_; }
uint32_t paddedLocalSlotCount() const {
return AlignBytes(localSlotCount(), JitStackAlignment);
}
size_t paddedLocalSlotsSize() const { return paddedLocalSlotCount(); }
void setArgumentSlotCount(uint32_t argumentSlotCount) {
argumentSlotCount_ = argumentSlotCount;
}
uint32_t argumentSlotCount() const { return argumentSlotCount_; }
size_t argumentsSize() const { return argumentSlotCount() * sizeof(Value); }
uint32_t totalSlotCount() const {
return paddedLocalSlotCount() + argumentsSize();
}
MOZ_MUST_USE bool addConstantToPool(const Value& v, uint32_t* index);
size_t numConstants() const { return constantPool_.length(); }
Value* constantPool() { return &constantPool_[0]; }
void setEntrySnapshot(LSnapshot* snapshot) {
MOZ_ASSERT(!entrySnapshot_);
entrySnapshot_ = snapshot;
}
LSnapshot* entrySnapshot() const {
MOZ_ASSERT(entrySnapshot_);
return entrySnapshot_;
}
bool noteNeedsSafepoint(LInstruction* ins);
size_t numNonCallSafepoints() const { return nonCallSafepoints_.length(); }
LInstruction* getNonCallSafepoint(size_t i) const {
return nonCallSafepoints_[i];
}
size_t numSafepoints() const { return safepoints_.length(); }
LInstruction* getSafepoint(size_t i) const { return safepoints_[i]; }
#ifdef JS_JITSPEW
void dump(GenericPrinter& out);
void dump();
#endif
};
LAllocation::LAllocation(AnyRegister reg) {
if (reg.isFloat()) {
*this = LFloatReg(reg.fpu());
} else {
*this = LGeneralReg(reg.gpr());
}
}
AnyRegister LAllocation::toRegister() const {
MOZ_ASSERT(isRegister());
if (isFloatReg()) {
return AnyRegister(toFloatReg()->reg());
}
return AnyRegister(toGeneralReg()->reg());
}
} }
#include "jit/shared/LIR-shared.h"
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
# if defined(JS_CODEGEN_X86)
# include "jit/x86/LIR-x86.h"
# elif defined(JS_CODEGEN_X64)
# include "jit/x64/LIR-x64.h"
# endif
# include "jit/x86-shared/LIR-x86-shared.h"
#elif defined(JS_CODEGEN_ARM)
# include "jit/arm/LIR-arm.h"
#elif defined(JS_CODEGEN_ARM64)
# include "jit/arm64/LIR-arm64.h"
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
# if defined(JS_CODEGEN_MIPS32)
# include "jit/mips32/LIR-mips32.h"
# elif defined(JS_CODEGEN_MIPS64)
# include "jit/mips64/LIR-mips64.h"
# endif
# include "jit/mips-shared/LIR-mips-shared.h"
#elif defined(JS_CODEGEN_NONE)
# include "jit/none/LIR-none.h"
#else
# error "Unknown architecture!"
#endif
#undef LIR_HEADER
namespace js {
namespace jit {
#define LIROP(name) \
L##name* LNode::to##name() { \
MOZ_ASSERT(is##name()); \
return static_cast<L##name*>(this); \
} \
const L##name* LNode::to##name() const { \
MOZ_ASSERT(is##name()); \
return static_cast<const L##name*>(this); \
}
LIR_OPCODE_LIST(LIROP)
#undef LIROP
#define LALLOC_CAST(type) \
L##type* LAllocation::to##type() { \
MOZ_ASSERT(is##type()); \
return static_cast<L##type*>(this); \
}
#define LALLOC_CONST_CAST(type) \
const L##type* LAllocation::to##type() const { \
MOZ_ASSERT(is##type()); \
return static_cast<const L##type*>(this); \
}
LALLOC_CAST(Use)
LALLOC_CONST_CAST(Use)
LALLOC_CONST_CAST(GeneralReg)
LALLOC_CONST_CAST(FloatReg)
LALLOC_CONST_CAST(StackSlot)
LALLOC_CONST_CAST(Argument)
LALLOC_CONST_CAST(ConstantIndex)
#undef LALLOC_CAST
#ifdef JS_NUNBOX32
static inline signed OffsetToOtherHalfOfNunbox(LDefinition::Type type) {
MOZ_ASSERT(type == LDefinition::TYPE || type == LDefinition::PAYLOAD);
signed offset = (type == LDefinition::TYPE) ? PAYLOAD_INDEX - TYPE_INDEX
: TYPE_INDEX - PAYLOAD_INDEX;
return offset;
}
static inline void AssertTypesFormANunbox(LDefinition::Type type1,
LDefinition::Type type2) {
MOZ_ASSERT((type1 == LDefinition::TYPE && type2 == LDefinition::PAYLOAD) ||
(type2 == LDefinition::TYPE && type1 == LDefinition::PAYLOAD));
}
static inline unsigned OffsetOfNunboxSlot(LDefinition::Type type) {
if (type == LDefinition::PAYLOAD) {
return NUNBOX32_PAYLOAD_OFFSET;
}
return NUNBOX32_TYPE_OFFSET;
}
static inline unsigned BaseOfNunboxSlot(LDefinition::Type type, unsigned slot) {
if (type == LDefinition::PAYLOAD) {
return slot + NUNBOX32_PAYLOAD_OFFSET;
}
return slot + NUNBOX32_TYPE_OFFSET;
}
#endif
} }
#endif