blob: f6f860fd1a24b4ab867a3596185f63940e441808 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef jit_RegisterSets_h
#define jit_RegisterSets_h
#include "Registers.h"
#include "jit/IonAllocPolicy.h"
namespace js {
namespace jit {
struct AnyRegister {
typedef uint32_t Code;
static const uint32_t Total = Registers::Total + FloatRegisters::Total;
static const uint32_t Invalid = UINT_MAX;
union {
Registers::Code gpr_;
FloatRegisters::Code fpu_;
};
bool isFloat_;
AnyRegister()
{ }
explicit AnyRegister(Register gpr) {
gpr_ = gpr.code();
isFloat_ = false;
}
explicit AnyRegister(FloatRegister fpu) {
fpu_ = fpu.code();
isFloat_ = true;
}
static AnyRegister FromCode(uint32_t i) {
JS_ASSERT(i < Total);
AnyRegister r;
if (i < Registers::Total) {
r.gpr_ = Register::Code(i);
r.isFloat_ = false;
} else {
r.fpu_ = FloatRegister::Code(i - Registers::Total);
r.isFloat_ = true;
}
return r;
}
bool isFloat() const {
return isFloat_;
}
Register gpr() const {
JS_ASSERT(!isFloat());
return Register::FromCode(gpr_);
}
FloatRegister fpu() const {
JS_ASSERT(isFloat());
return FloatRegister::FromCode(fpu_);
}
bool operator ==(const AnyRegister &other) const {
return isFloat()
? (other.isFloat() && fpu_ == other.fpu_)
: (!other.isFloat() && gpr_ == other.gpr_);
}
bool operator !=(const AnyRegister &other) const {
return isFloat()
? (!other.isFloat() || fpu_ != other.fpu_)
: (other.isFloat() || gpr_ != other.gpr_);
}
const char *name() const {
return isFloat()
? FloatRegister::FromCode(fpu_).name()
: Register::FromCode(gpr_).name();
}
const Code code() const {
return isFloat()
? fpu_ + Registers::Total
: gpr_;
}
bool volatile_() const {
return isFloat() ? fpu().volatile_() : gpr().volatile_();
}
};
// Registers to hold a boxed value. Uses one register on 64 bit
// platforms, two registers on 32 bit platforms.
class ValueOperand
{
#if defined(JS_NUNBOX32)
Register type_;
Register payload_;
public:
MOZ_CONSTEXPR ValueOperand(Register type, Register payload)
: type_(type), payload_(payload)
{ }
Register typeReg() const {
return type_;
}
Register payloadReg() const {
return payload_;
}
Register scratchReg() const {
return payloadReg();
}
bool operator==(const ValueOperand &o) const {
return type_ == o.type_ && payload_ == o.payload_;
}
bool operator!=(const ValueOperand &o) const {
return !(*this == o);
}
#elif defined(JS_PUNBOX64)
Register value_;
public:
explicit MOZ_CONSTEXPR ValueOperand(Register value)
: value_(value)
{ }
Register valueReg() const {
return value_;
}
Register scratchReg() const {
return valueReg();
}
bool operator==(const ValueOperand &o) const {
return value_ == o.value_;
}
bool operator!=(const ValueOperand &o) const {
return !(*this == o);
}
#endif
ValueOperand() {}
};
// Registers to hold either either a typed or untyped value.
class TypedOrValueRegister
{
// Type of value being stored.
MIRType type_;
// Space to hold either an AnyRegister or a ValueOperand.
union U {
mozilla::AlignedStorage2<AnyRegister> typed;
mozilla::AlignedStorage2<ValueOperand> value;
} data;
AnyRegister &dataTyped() {
JS_ASSERT(hasTyped());
return *data.typed.addr();
}
ValueOperand &dataValue() {
JS_ASSERT(hasValue());
return *data.value.addr();
}
public:
TypedOrValueRegister()
: type_(MIRType_None)
{}
TypedOrValueRegister(MIRType type, AnyRegister reg)
: type_(type)
{
dataTyped() = reg;
}
TypedOrValueRegister(ValueOperand value)
: type_(MIRType_Value)
{
dataValue() = value;
}
MIRType type() const {
return type_;
}
bool hasTyped() const {
return type() != MIRType_None && type() != MIRType_Value;
}
bool hasValue() const {
return type() == MIRType_Value;
}
AnyRegister typedReg() {
return dataTyped();
}
ValueOperand valueReg() {
return dataValue();
}
AnyRegister scratchReg() {
if (hasValue())
return AnyRegister(valueReg().scratchReg());
return typedReg();
}
};
// A constant value, or registers to hold a typed/untyped value.
class ConstantOrRegister
{
// Whether a constant value is being stored.
bool constant_;
// Space to hold either a Value or a TypedOrValueRegister.
union U {
mozilla::AlignedStorage2<Value> constant;
mozilla::AlignedStorage2<TypedOrValueRegister> reg;
} data;
Value &dataValue() {
JS_ASSERT(constant());
return *data.constant.addr();
}
TypedOrValueRegister &dataReg() {
JS_ASSERT(!constant());
return *data.reg.addr();
}
public:
ConstantOrRegister()
{}
ConstantOrRegister(Value value)
: constant_(true)
{
dataValue() = value;
}
ConstantOrRegister(TypedOrValueRegister reg)
: constant_(false)
{
dataReg() = reg;
}
bool constant() {
return constant_;
}
Value value() {
return dataValue();
}
TypedOrValueRegister reg() {
return dataReg();
}
};
struct Int32Key {
bool isRegister_;
union {
Register reg_;
int32_t constant_;
};
explicit Int32Key(Register reg)
: isRegister_(true), reg_(reg)
{ }
explicit Int32Key(int32_t index)
: isRegister_(false), constant_(index)
{ }
inline void bumpConstant(int diff) {
JS_ASSERT(!isRegister_);
constant_ += diff;
}
inline Register reg() const {
JS_ASSERT(isRegister_);
return reg_;
}
inline int32_t constant() const {
JS_ASSERT(!isRegister_);
return constant_;
}
inline bool isRegister() const {
return isRegister_;
}
inline bool isConstant() const {
return !isRegister_;
}
};
template <typename T>
class TypedRegisterSet
{
uint32_t bits_;
public:
explicit MOZ_CONSTEXPR TypedRegisterSet(uint32_t bits)
: bits_(bits)
{ }
MOZ_CONSTEXPR TypedRegisterSet() : bits_(0)
{ }
MOZ_CONSTEXPR TypedRegisterSet(const TypedRegisterSet<T> &set) : bits_(set.bits_)
{ }
static inline TypedRegisterSet All() {
return TypedRegisterSet(T::Codes::AllocatableMask);
}
static inline TypedRegisterSet Intersect(const TypedRegisterSet &lhs,
const TypedRegisterSet &rhs) {
return TypedRegisterSet(lhs.bits_ & rhs.bits_);
}
static inline TypedRegisterSet Union(const TypedRegisterSet &lhs,
const TypedRegisterSet &rhs) {
return TypedRegisterSet(lhs.bits_ | rhs.bits_);
}
static inline TypedRegisterSet Not(const TypedRegisterSet &in) {
return TypedRegisterSet(~in.bits_ & T::Codes::AllocatableMask);
}
static inline TypedRegisterSet VolatileNot(const TypedRegisterSet &in) {
const uint32_t allocatableVolatile =
T::Codes::AllocatableMask & T::Codes::VolatileMask;
return TypedRegisterSet(~in.bits_ & allocatableVolatile);
}
static inline TypedRegisterSet Volatile() {
return TypedRegisterSet(T::Codes::AllocatableMask & T::Codes::VolatileMask);
}
static inline TypedRegisterSet NonVolatile() {
return TypedRegisterSet(T::Codes::AllocatableMask & T::Codes::NonVolatileMask);
}
void intersect(TypedRegisterSet other) {
bits_ &= ~other.bits_;
}
bool has(T reg) const {
return !!(bits_ & (1 << reg.code()));
}
bool hasNextRegister(T reg) const {
if (reg.code() == sizeof(bits_)*8)
return false;
return !!(bits_ & (1 << (reg.code()+1)));
}
void addUnchecked(T reg) {
bits_ |= (1 << reg.code());
}
void add(T reg) {
JS_ASSERT(!has(reg));
addUnchecked(reg);
}
void add(ValueOperand value) {
#if defined(JS_NUNBOX32)
add(value.payloadReg());
add(value.typeReg());
#elif defined(JS_PUNBOX64)
add(value.valueReg());
#else
#error "Bad architecture"
#endif
}
// Determemine if some register are still allocated. This function should
// be used with the set of allocatable registers used for the initialization
// of the current set.
bool someAllocated(const TypedRegisterSet &allocatable) const {
return allocatable.bits_ & ~bits_;
}
bool empty() const {
return !bits_;
}
void take(T reg) {
JS_ASSERT(has(reg));
bits_ &= ~(1 << reg.code());
}
void takeUnchecked(T reg) {
bits_ &= ~(1 << reg.code());
}
void take(ValueOperand value) {
#if defined(JS_NUNBOX32)
take(value.payloadReg());
take(value.typeReg());
#elif defined(JS_PUNBOX64)
take(value.valueReg());
#else
#error "Bad architecture"
#endif
}
void takeUnchecked(ValueOperand value) {
#if defined(JS_NUNBOX32)
takeUnchecked(value.payloadReg());
takeUnchecked(value.typeReg());
#elif defined(JS_PUNBOX64)
takeUnchecked(value.valueReg());
#else
#error "Bad architecture"
#endif
}
T getAny() const {
JS_ASSERT(!empty());
int ireg;
JS_FLOOR_LOG2(ireg, bits_);
return T::FromCode(ireg);
}
T getFirst() const {
JS_ASSERT(!empty());
int ireg = js_bitscan_ctz32(bits_);
return T::FromCode(ireg);
}
T takeAny() {
JS_ASSERT(!empty());
T reg = getAny();
take(reg);
return reg;
}
T takeAnyExcluding(T preclude) {
if (!has(preclude))
return takeAny();
take(preclude);
T result = takeAny();
add(preclude);
return result;
}
ValueOperand takeAnyValue() {
#if defined(JS_NUNBOX32)
T type = takeAny();
T payload = takeAny();
return ValueOperand(type, payload);
#elif defined(JS_PUNBOX64)
T reg = takeAny();
return ValueOperand(reg);
#else
#error "Bad architecture"
#endif
}
T takeFirst() {
JS_ASSERT(!empty());
T reg = getFirst();
take(reg);
return reg;
}
void clear() {
bits_ = 0;
}
uint32_t bits() const {
return bits_;
}
uint32_t size() const {
#if defined(JS_CPU_MIPS)
return __builtin_popcount(bits_);
#else
uint32_t sum2 = (bits_ & 0x55555555) + ((bits_ & 0xaaaaaaaa) >> 1);
uint32_t sum4 = (sum2 & 0x33333333) + ((sum2 & 0xcccccccc) >> 2);
uint32_t sum8 = (sum4 & 0x0f0f0f0f) + ((sum4 & 0xf0f0f0f0) >> 4);
uint32_t sum16 = (sum8 & 0x00ff00ff) + ((sum8 & 0xff00ff00) >> 8);
return sum16;
#endif
}
bool operator ==(const TypedRegisterSet<T> &other) const {
return other.bits_ == bits_;
}
};
typedef TypedRegisterSet<Register> GeneralRegisterSet;
typedef TypedRegisterSet<FloatRegister> FloatRegisterSet;
class AnyRegisterIterator;
class RegisterSet {
GeneralRegisterSet gpr_;
FloatRegisterSet fpu_;
friend class AnyRegisterIterator;
public:
RegisterSet()
{ }
MOZ_CONSTEXPR RegisterSet(const GeneralRegisterSet &gpr, const FloatRegisterSet &fpu)
: gpr_(gpr),
fpu_(fpu)
{ }
static inline RegisterSet All() {
return RegisterSet(GeneralRegisterSet::All(), FloatRegisterSet::All());
}
static inline RegisterSet Intersect(const RegisterSet &lhs, const RegisterSet &rhs) {
return RegisterSet(GeneralRegisterSet::Intersect(lhs.gpr_, rhs.gpr_),
FloatRegisterSet::Intersect(lhs.fpu_, rhs.fpu_));
}
static inline RegisterSet Union(const RegisterSet &lhs, const RegisterSet &rhs) {
return RegisterSet(GeneralRegisterSet::Union(lhs.gpr_, rhs.gpr_),
FloatRegisterSet::Union(lhs.fpu_, rhs.fpu_));
}
static inline RegisterSet Not(const RegisterSet &in) {
return RegisterSet(GeneralRegisterSet::Not(in.gpr_),
FloatRegisterSet::Not(in.fpu_));
}
static inline RegisterSet VolatileNot(const RegisterSet &in) {
return RegisterSet(GeneralRegisterSet::VolatileNot(in.gpr_),
FloatRegisterSet::VolatileNot(in.fpu_));
}
static inline RegisterSet Volatile() {
return RegisterSet(GeneralRegisterSet::Volatile(), FloatRegisterSet::Volatile());
}
bool has(Register reg) const {
return gpr_.has(reg);
}
bool has(FloatRegister reg) const {
return fpu_.has(reg);
}
bool has(AnyRegister reg) const {
return reg.isFloat() ? has(reg.fpu()) : has(reg.gpr());
}
void add(Register reg) {
gpr_.add(reg);
}
void add(FloatRegister reg) {
fpu_.add(reg);
}
void add(const AnyRegister &any) {
if (any.isFloat())
add(any.fpu());
else
add(any.gpr());
}
void add(ValueOperand value) {
#if defined(JS_NUNBOX32)
add(value.payloadReg());
add(value.typeReg());
#elif defined(JS_PUNBOX64)
add(value.valueReg());
#else
#error "Bad architecture"
#endif
}
void add(TypedOrValueRegister reg) {
if (reg.hasValue())
add(reg.valueReg());
else if (reg.hasTyped())
add(reg.typedReg());
}
void addUnchecked(Register reg) {
gpr_.addUnchecked(reg);
}
void addUnchecked(FloatRegister reg) {
fpu_.addUnchecked(reg);
}
void addUnchecked(const AnyRegister &any) {
if (any.isFloat())
addUnchecked(any.fpu());
else
addUnchecked(any.gpr());
}
bool empty(bool floats) const {
return floats ? fpu_.empty() : gpr_.empty();
}
FloatRegister takeFloat() {
return fpu_.takeAny();
}
Register takeGeneral() {
return gpr_.takeAny();
}
ValueOperand takeValueOperand() {
#if defined(JS_NUNBOX32)
return ValueOperand(takeGeneral(), takeGeneral());
#elif defined(JS_PUNBOX64)
return ValueOperand(takeGeneral());
#else
#error "Bad architecture"
#endif
}
void take(const AnyRegister &reg) {
if (reg.isFloat())
fpu_.take(reg.fpu());
else
gpr_.take(reg.gpr());
}
AnyRegister takeAny(bool isFloat) {
if (isFloat)
return AnyRegister(takeFloat());
return AnyRegister(takeGeneral());
}
void clear() {
gpr_.clear();
fpu_.clear();
}
MOZ_CONSTEXPR GeneralRegisterSet gprs() const {
return gpr_;
}
MOZ_CONSTEXPR FloatRegisterSet fpus() const {
return fpu_;
}
bool operator ==(const RegisterSet &other) const {
return other.gpr_ == gpr_ && other.fpu_ == fpu_;
}
void maybeTake(Register reg) {
if (gpr_.has(reg))
gpr_.take(reg);
}
void maybeTake(FloatRegister reg) {
if (fpu_.has(reg))
fpu_.take(reg);
}
void maybeTake(AnyRegister reg) {
if (has(reg))
take(reg);
}
void maybeTake(ValueOperand value) {
#if defined(JS_NUNBOX32)
if (gpr_.has(value.typeReg()))
gpr_.take(value.typeReg());
if (gpr_.has(value.payloadReg()))
gpr_.take(value.payloadReg());
#elif defined(JS_PUNBOX64)
if (gpr_.has(value.valueReg()))
gpr_.take(value.valueReg());
#else
#error "Bad architecture"
#endif
}
void maybeTake(TypedOrValueRegister reg) {
if (reg.hasValue())
maybeTake(reg.valueReg());
else if (reg.hasTyped())
maybeTake(reg.typedReg());
}
};
// iterates backwards, that is, rn to r0
template <typename T>
class TypedRegisterIterator
{
TypedRegisterSet<T> regset_;
public:
TypedRegisterIterator(TypedRegisterSet<T> regset) : regset_(regset)
{ }
TypedRegisterIterator(const TypedRegisterIterator &other) : regset_(other.regset_)
{ }
bool more() const {
return !regset_.empty();
}
TypedRegisterIterator<T> operator ++(int) {
TypedRegisterIterator<T> old(*this);
regset_.takeAny();
return old;
}
TypedRegisterIterator<T>& operator ++() {
regset_.takeAny();
return *this;
}
T operator *() const {
return regset_.getAny();
}
};
// iterates forwards, that is r0 to rn
template <typename T>
class TypedRegisterForwardIterator
{
TypedRegisterSet<T> regset_;
public:
TypedRegisterForwardIterator(TypedRegisterSet<T> regset) : regset_(regset)
{ }
TypedRegisterForwardIterator(const TypedRegisterForwardIterator &other) : regset_(other.regset_)
{ }
bool more() const {
return !regset_.empty();
}
TypedRegisterForwardIterator<T> operator ++(int) {
TypedRegisterIterator<T> old(*this);
regset_.takeFirst();
return old;
}
TypedRegisterForwardIterator<T>& operator ++() {
regset_.takeFirst();
return *this;
}
T operator *() const {
return regset_.getFirst();
}
};
typedef TypedRegisterIterator<Register> GeneralRegisterIterator;
typedef TypedRegisterIterator<FloatRegister> FloatRegisterIterator;
typedef TypedRegisterForwardIterator<Register> GeneralRegisterForwardIterator;
typedef TypedRegisterForwardIterator<FloatRegister> FloatRegisterForwardIterator;
class AnyRegisterIterator
{
GeneralRegisterIterator geniter_;
FloatRegisterIterator floatiter_;
public:
AnyRegisterIterator()
: geniter_(GeneralRegisterSet::All()), floatiter_(FloatRegisterSet::All())
{ }
AnyRegisterIterator(GeneralRegisterSet genset, FloatRegisterSet floatset)
: geniter_(genset), floatiter_(floatset)
{ }
AnyRegisterIterator(const RegisterSet &set)
: geniter_(set.gpr_), floatiter_(set.fpu_)
{ }
AnyRegisterIterator(const AnyRegisterIterator &other)
: geniter_(other.geniter_), floatiter_(other.floatiter_)
{ }
bool more() const {
return geniter_.more() || floatiter_.more();
}
AnyRegisterIterator operator ++(int) {
AnyRegisterIterator old(*this);
if (geniter_.more())
geniter_++;
else
floatiter_++;
return old;
}
AnyRegister operator *() const {
if (geniter_.more())
return AnyRegister(*geniter_);
return AnyRegister(*floatiter_);
}
};
class ABIArg
{
public:
enum Kind { GPR, FPU, Stack };
private:
Kind kind_;
union {
Registers::Code gpr_;
FloatRegisters::Code fpu_;
uint32_t offset_;
} u;
public:
ABIArg() : kind_(Kind(-1)) { u.offset_ = -1; }
ABIArg(Register gpr) : kind_(GPR) { u.gpr_ = gpr.code(); }
ABIArg(FloatRegister fpu) : kind_(FPU) { u.fpu_ = fpu.code(); }
ABIArg(uint32_t offset) : kind_(Stack) { u.offset_ = offset; }
Kind kind() const { return kind_; }
Register gpr() const { JS_ASSERT(kind() == GPR); return Register::FromCode(u.gpr_); }
FloatRegister fpu() const { JS_ASSERT(kind() == FPU); return FloatRegister::FromCode(u.fpu_); }
uint32_t offsetFromArgBase() const { JS_ASSERT(kind() == Stack); return u.offset_; }
bool argInRegister() const { return kind() != Stack; }
AnyRegister reg() const { return kind_ == GPR ? AnyRegister(gpr()) : AnyRegister(fpu()); }
};
class AsmJSHeapAccess
{
uint32_t offset_;
uint8_t opLength_;
#if defined(JS_CPU_X86)
uint8_t cmpDelta_;
#endif
uint8_t isFloat32Load_;
jit::AnyRegister::Code loadedReg_ : 8;
JS_STATIC_ASSERT(jit::AnyRegister::Total < UINT8_MAX);
public:
#if defined(JS_CPU_X86)
AsmJSHeapAccess(uint32_t cmp, uint32_t offset, uint32_t after, ArrayBufferView::ViewType vt,
AnyRegister loadedReg)
: offset_(offset),
opLength_(after - offset),
cmpDelta_(offset - cmp),
isFloat32Load_(vt == ArrayBufferView::TYPE_FLOAT32),
loadedReg_(loadedReg.code())
{}
AsmJSHeapAccess(uint32_t cmp, uint32_t offset, uint8_t after)
: offset_(offset),
opLength_(after - offset),
cmpDelta_(offset - cmp),
isFloat32Load_(false),
loadedReg_(UINT8_MAX)
{}
#else
AsmJSHeapAccess(uint32_t offset, uint32_t after, ArrayBufferView::ViewType vt,
AnyRegister loadedReg)
: offset_(offset),
opLength_(after - offset),
isFloat32Load_(vt == ArrayBufferView::TYPE_FLOAT32),
loadedReg_(loadedReg.code())
{}
AsmJSHeapAccess(uint32_t offset, uint8_t after)
: offset_(offset),
opLength_(after - offset),
isFloat32Load_(false),
loadedReg_(UINT8_MAX)
{}
#endif
#if defined(JS_CPU_MIPS)
explicit AsmJSHeapAccess(uint32_t offset)
: offset_(offset)
{}
#endif
uint32_t offset() const { return offset_; }
unsigned opLength() const { return opLength_; }
bool isLoad() const { return loadedReg_ != UINT8_MAX; }
bool isFloat32Load() const { return isFloat32Load_; }
jit::AnyRegister loadedReg() const { return jit::AnyRegister::FromCode(loadedReg_); }
#if defined(JS_CPU_X86)
void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); }
void *patchOffsetAt(uint8_t *code) const { return code + (offset_ + opLength_); }
#endif
void updateOffset(uint32_t offset) { offset_ = offset; }
};
typedef Vector<AsmJSHeapAccess, 0, IonAllocPolicy> AsmJSHeapAccessVector;
#ifdef JS_CPU_ARM
struct AsmJSBoundsCheck
{
unsigned offset_;
AsmJSBoundsCheck(unsigned offset)
: offset_(offset)
{}
void setOffset(uint32_t offset) { offset_ = offset; }
unsigned offset() {return offset_;}
};
typedef Vector<AsmJSBoundsCheck, 0, IonAllocPolicy> AsmJSBoundsCheckVector;
#endif
} // namespace jit
} // namespace js
#endif /* jit_RegisterSets_h */