mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-01 17:41:34 +00:00
1323 lines
37 KiB
C++
1323 lines
37 KiB
C++
/* -*- 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 "mozilla/Alignment.h"
|
|
#include "mozilla/MathAlgorithms.h"
|
|
|
|
#include "jit/JitAllocPolicy.h"
|
|
#include "jit/Registers.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;
|
|
|
|
private:
|
|
Code code_;
|
|
|
|
public:
|
|
AnyRegister()
|
|
{ }
|
|
explicit AnyRegister(Register gpr) {
|
|
code_ = gpr.code();
|
|
}
|
|
explicit AnyRegister(FloatRegister fpu) {
|
|
code_ = fpu.code() + Registers::Total;
|
|
}
|
|
static AnyRegister FromCode(uint32_t i) {
|
|
MOZ_ASSERT(i < Total);
|
|
AnyRegister r;
|
|
r.code_ = i;
|
|
return r;
|
|
}
|
|
bool isFloat() const {
|
|
return code_ >= Registers::Total;
|
|
}
|
|
Register gpr() const {
|
|
MOZ_ASSERT(!isFloat());
|
|
return Register::FromCode(code_);
|
|
}
|
|
FloatRegister fpu() const {
|
|
MOZ_ASSERT(isFloat());
|
|
return FloatRegister::FromCode(code_ - Registers::Total);
|
|
}
|
|
bool operator ==(AnyRegister other) const {
|
|
return code_ == other.code_;
|
|
}
|
|
bool operator !=(AnyRegister other) const {
|
|
return code_ != other.code_;
|
|
}
|
|
const char* name() const {
|
|
return isFloat() ? fpu().name() : gpr().name();
|
|
}
|
|
Code code() const {
|
|
return code_;
|
|
}
|
|
bool volatile_() const {
|
|
return isFloat() ? fpu().volatile_() : gpr().volatile_();
|
|
}
|
|
AnyRegister aliased(uint32_t aliasIdx) const {
|
|
AnyRegister ret;
|
|
if (isFloat()) {
|
|
FloatRegister fret;
|
|
fpu().aliased(aliasIdx, &fret);
|
|
ret = AnyRegister(fret);
|
|
} else {
|
|
Register gret;
|
|
gpr().aliased(aliasIdx, &gret);
|
|
ret = AnyRegister(gret);
|
|
}
|
|
MOZ_ASSERT_IF(aliasIdx == 0, ret == *this);
|
|
return ret;
|
|
}
|
|
uint32_t numAliased() const {
|
|
if (isFloat())
|
|
return fpu().numAliased();
|
|
return gpr().numAliased();
|
|
}
|
|
bool aliases(const AnyRegister& other) const {
|
|
if (isFloat() && other.isFloat())
|
|
return fpu().aliases(other.fpu());
|
|
if (!isFloat() && !other.isFloat())
|
|
return gpr().aliases(other.gpr());
|
|
return false;
|
|
}
|
|
// do the two registers hold the same type of data (e.g. both float32, both gpr)
|
|
bool isCompatibleReg (const AnyRegister other) const {
|
|
if (isFloat() && other.isFloat())
|
|
return fpu().equiv(other.fpu());
|
|
if (!isFloat() && !other.isFloat())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
};
|
|
|
|
// 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() {
|
|
MOZ_ASSERT(hasTyped());
|
|
return *data.typed.addr();
|
|
}
|
|
ValueOperand& dataValue() {
|
|
MOZ_ASSERT(hasValue());
|
|
return *data.value.addr();
|
|
}
|
|
|
|
AnyRegister dataTyped() const {
|
|
MOZ_ASSERT(hasTyped());
|
|
return *data.typed.addr();
|
|
}
|
|
const ValueOperand& dataValue() const {
|
|
MOZ_ASSERT(hasValue());
|
|
return *data.value.addr();
|
|
}
|
|
|
|
public:
|
|
|
|
TypedOrValueRegister()
|
|
: type_(MIRType_None)
|
|
{}
|
|
|
|
TypedOrValueRegister(MIRType type, AnyRegister reg)
|
|
: type_(type)
|
|
{
|
|
dataTyped() = reg;
|
|
}
|
|
|
|
MOZ_IMPLICIT 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() const {
|
|
return dataTyped();
|
|
}
|
|
|
|
ValueOperand valueReg() const {
|
|
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() {
|
|
MOZ_ASSERT(constant());
|
|
return *data.constant.addr();
|
|
}
|
|
TypedOrValueRegister& dataReg() {
|
|
MOZ_ASSERT(!constant());
|
|
return *data.reg.addr();
|
|
}
|
|
|
|
public:
|
|
|
|
ConstantOrRegister()
|
|
{}
|
|
|
|
MOZ_IMPLICIT ConstantOrRegister(Value value)
|
|
: constant_(true)
|
|
{
|
|
dataValue() = value;
|
|
}
|
|
|
|
MOZ_IMPLICIT 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) {
|
|
MOZ_ASSERT(!isRegister_);
|
|
constant_ += diff;
|
|
}
|
|
inline Register reg() const {
|
|
MOZ_ASSERT(isRegister_);
|
|
return reg_;
|
|
}
|
|
inline int32_t constant() const {
|
|
MOZ_ASSERT(!isRegister_);
|
|
return constant_;
|
|
}
|
|
inline bool isRegister() const {
|
|
return isRegister_;
|
|
}
|
|
inline bool isConstant() const {
|
|
return !isRegister_;
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
class TypedRegisterSet
|
|
{
|
|
public:
|
|
typedef T RegType;
|
|
typedef typename T::SetType SetType;
|
|
|
|
private:
|
|
SetType bits_;
|
|
|
|
public:
|
|
explicit MOZ_CONSTEXPR TypedRegisterSet(SetType 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 Subtract(const TypedRegisterSet& lhs,
|
|
const TypedRegisterSet& rhs)
|
|
{
|
|
return TypedRegisterSet(lhs.bits_ & ~rhs.bits_);
|
|
}
|
|
static inline TypedRegisterSet VolatileNot(const TypedRegisterSet& in) {
|
|
const SetType 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);
|
|
}
|
|
|
|
bool empty() const {
|
|
return !bits_;
|
|
}
|
|
void clear() {
|
|
bits_ = 0;
|
|
}
|
|
|
|
bool hasRegisterIndex(T reg) const {
|
|
return !!(bits_ & (SetType(1) << reg.code()));
|
|
}
|
|
bool hasAllocatable(T reg) const {
|
|
return !(~bits_ & reg.alignedOrDominatedAliasedSet());
|
|
}
|
|
|
|
void addRegisterIndex(T reg) {
|
|
bits_ |= (SetType(1) << reg.code());
|
|
}
|
|
void addAllocatable(T reg) {
|
|
bits_ |= reg.alignedOrDominatedAliasedSet();
|
|
}
|
|
|
|
|
|
void takeRegisterIndex(T reg) {
|
|
bits_ &= ~(SetType(1) << reg.code());
|
|
}
|
|
void takeAllocatable(T reg) {
|
|
bits_ &= ~reg.alignedOrDominatedAliasedSet();
|
|
}
|
|
|
|
T getAny() const {
|
|
// The choice of first or last here is mostly arbitrary, as they are
|
|
// about the same speed on popular architectures. We choose first, as
|
|
// it has the advantage of using the "lower" registers more often. These
|
|
// registers are sometimes more efficient (e.g. optimized encodings for
|
|
// EAX on x86).
|
|
return getFirst();
|
|
}
|
|
T getFirst() const {
|
|
MOZ_ASSERT(!empty());
|
|
return T::FromCode(T::FirstBit(bits_));
|
|
}
|
|
T getLast() const {
|
|
MOZ_ASSERT(!empty());
|
|
int ireg = T::LastBit(bits_);
|
|
return T::FromCode(ireg);
|
|
}
|
|
|
|
SetType bits() const {
|
|
return bits_;
|
|
}
|
|
uint32_t size() const {
|
|
return T::SetSize(bits_);
|
|
}
|
|
bool operator ==(const TypedRegisterSet<T>& other) const {
|
|
return other.bits_ == bits_;
|
|
}
|
|
TypedRegisterSet<T> reduceSetForPush() const {
|
|
return T::ReduceSetForPush(*this);
|
|
}
|
|
uint32_t getPushSizeInBytes() const {
|
|
return T::GetPushSizeInBytes(*this);
|
|
}
|
|
};
|
|
|
|
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 empty() const {
|
|
return fpu_.empty() && gpr_.empty();
|
|
}
|
|
void clear() {
|
|
fpu_.clear();
|
|
gpr_.clear();
|
|
}
|
|
bool emptyGeneral() const {
|
|
return gpr_.empty();
|
|
}
|
|
bool emptyFloat() const {
|
|
return fpu_.empty();
|
|
}
|
|
MOZ_CONSTEXPR GeneralRegisterSet gprs() const {
|
|
return gpr_;
|
|
}
|
|
GeneralRegisterSet& gprs() {
|
|
return gpr_;
|
|
}
|
|
MOZ_CONSTEXPR FloatRegisterSet fpus() const {
|
|
return fpu_;
|
|
}
|
|
FloatRegisterSet& fpus() {
|
|
return fpu_;
|
|
}
|
|
bool operator ==(const RegisterSet& other) const {
|
|
return other.gpr_ == gpr_ && other.fpu_ == fpu_;
|
|
}
|
|
|
|
};
|
|
|
|
// There are 2 use cases for register sets:
|
|
//
|
|
// 1. To serve as a pool of allocatable register. This is useful for working
|
|
// on the code produced by some stub where free registers are available, or
|
|
// when we can release some registers.
|
|
//
|
|
// 2. To serve as a list of typed registers. This is useful for working with
|
|
// live registers and to manipulate them with the proper instructions. This
|
|
// is used by the register allocator to fill the Safepoints.
|
|
//
|
|
// These 2 uses cases can be used on top of 3 different backend representation
|
|
// of register sets, which are either GeneralRegisterSet, FloatRegisterSet, or
|
|
// RegisterSet (for both). These classes are used to store the bit sets to
|
|
// represent each register.
|
|
//
|
|
// Each use case defines an Accessor class, such as AllocatableSetAccessor or
|
|
// LiveSetAccessor, which is parameterized with the type of the register
|
|
// set. These accessors are in charge of manipulating the register set in a
|
|
// consistent way.
|
|
//
|
|
// The RegSetCommonInterface class is used to wrap the accessors with convenient
|
|
// shortcuts which are based on the accessors.
|
|
//
|
|
// Then, to avoid to many levels of complexity while using these interfaces,
|
|
// shortcut templates are created to make it easy to distinguish between a
|
|
// register set used for allocating registers, or a register set used for making
|
|
// a collection of allocated (live) registers.
|
|
//
|
|
// This separation exists to prevent mixing LiveSet and AllocatableSet
|
|
// manipulations of the same register set, and ensure safety while avoiding
|
|
// false positive.
|
|
|
|
template <typename RegisterSet>
|
|
class AllocatableSet;
|
|
|
|
template <typename RegisterSet>
|
|
class LiveSet;
|
|
|
|
// Base accessors classes have the minimal set of raw methods to manipulate the register set
|
|
// given as parameter in a consistent manner. These methods are:
|
|
//
|
|
// - has: Returns if all the bits needed to take a register are present.
|
|
//
|
|
// - takeUnchecked: Subtracts the bits used to represent the register in the
|
|
// register set.
|
|
//
|
|
// - addUnchecked: Adds the bits used to represent the register in the
|
|
// register set.
|
|
|
|
// The AllocatableSet accessors are used to make a pool of unused
|
|
// registers. Taking or adding registers should consider the aliasing rules of
|
|
// the architecture. For example, on ARM, the following piece of code should
|
|
// work fine, knowing that the double register |d0| is composed of float
|
|
// registers |s0| and |s1|:
|
|
//
|
|
// AllocatableFloatRegisterSet regs;
|
|
// regs.add(s0);
|
|
// regs.add(s1);
|
|
// // d0 is now available.
|
|
// regs.take(d0);
|
|
//
|
|
// These accessors are useful for allocating registers within the functions used
|
|
// to generate stubs, trampolines, and inline caches (BaselineIC, IonCache).
|
|
template <typename Set>
|
|
class AllocatableSetAccessors
|
|
{
|
|
public:
|
|
typedef Set RegSet;
|
|
typedef typename RegSet::RegType RegType;
|
|
typedef typename RegSet::SetType SetType;
|
|
|
|
protected:
|
|
RegSet set_;
|
|
|
|
public:
|
|
AllocatableSetAccessors() : set_() {}
|
|
explicit MOZ_CONSTEXPR AllocatableSetAccessors(SetType set) : set_(set) {}
|
|
explicit MOZ_CONSTEXPR AllocatableSetAccessors(RegSet set) : set_(set) {}
|
|
|
|
bool has(RegType reg) const {
|
|
return set_.hasAllocatable(reg);
|
|
}
|
|
|
|
void addUnchecked(RegType reg) {
|
|
set_.addAllocatable(reg);
|
|
}
|
|
|
|
void takeUnchecked(RegType reg) {
|
|
set_.takeAllocatable(reg);
|
|
}
|
|
};
|
|
|
|
// Specialization of the AllocatableSet accessors for the RegisterSet aggregate.
|
|
template <>
|
|
class AllocatableSetAccessors<RegisterSet>
|
|
{
|
|
public:
|
|
typedef RegisterSet RegSet;
|
|
typedef AnyRegister RegType;
|
|
typedef char SetType;
|
|
|
|
protected:
|
|
RegisterSet set_;
|
|
|
|
public:
|
|
AllocatableSetAccessors() : set_() {}
|
|
explicit MOZ_CONSTEXPR AllocatableSetAccessors(SetType) = delete;
|
|
explicit MOZ_CONSTEXPR AllocatableSetAccessors(RegisterSet set) : set_(set) {}
|
|
|
|
bool has(Register reg) const {
|
|
return set_.gprs().hasAllocatable(reg);
|
|
}
|
|
bool has(FloatRegister reg) const {
|
|
return set_.fpus().hasAllocatable(reg);
|
|
}
|
|
|
|
void addUnchecked(Register reg) {
|
|
set_.gprs().addAllocatable(reg);
|
|
}
|
|
void addUnchecked(FloatRegister reg) {
|
|
set_.fpus().addAllocatable(reg);
|
|
}
|
|
|
|
void takeUnchecked(Register reg) {
|
|
set_.gprs().takeAllocatable(reg);
|
|
}
|
|
void takeUnchecked(FloatRegister reg) {
|
|
set_.fpus().takeAllocatable(reg);
|
|
}
|
|
};
|
|
|
|
|
|
// The LiveSet accessors are used to collect a list of allocated
|
|
// registers. Taking or adding a register should *not* consider the aliases, as
|
|
// we care about interpreting the registers with the correct type. For example,
|
|
// on x64, where one float registers can be interpreted as an Simd128, a Double,
|
|
// or a Float, adding xmm0 as an Simd128, does not make the register available
|
|
// as a Double.
|
|
//
|
|
// LiveFloatRegisterSet regs;
|
|
// regs.add(xmm0.asSimd128());
|
|
// regs.take(xmm0); // Assert!
|
|
//
|
|
// These accessors are useful for recording the result of a register allocator,
|
|
// such as what the Backtracking allocator do on the Safepoints.
|
|
template <typename Set>
|
|
class LiveSetAccessors
|
|
{
|
|
public:
|
|
typedef Set RegSet;
|
|
typedef typename RegSet::RegType RegType;
|
|
typedef typename RegSet::SetType SetType;
|
|
|
|
protected:
|
|
RegSet set_;
|
|
|
|
public:
|
|
LiveSetAccessors() : set_() {}
|
|
explicit MOZ_CONSTEXPR LiveSetAccessors(SetType set) : set_(set) {}
|
|
explicit MOZ_CONSTEXPR LiveSetAccessors(RegSet set) : set_(set) {}
|
|
|
|
bool has(RegType reg) const {
|
|
return set_.hasRegisterIndex(reg);
|
|
}
|
|
|
|
void addUnchecked(RegType reg) {
|
|
set_.addRegisterIndex(reg);
|
|
}
|
|
|
|
void takeUnchecked(RegType reg) {
|
|
set_.takeRegisterIndex(reg);
|
|
}
|
|
};
|
|
|
|
// Specialization of the LiveSet accessors for the RegisterSet aggregate.
|
|
template <>
|
|
class LiveSetAccessors<RegisterSet>
|
|
{
|
|
public:
|
|
typedef RegisterSet RegSet;
|
|
typedef AnyRegister RegType;
|
|
typedef char SetType;
|
|
|
|
protected:
|
|
RegisterSet set_;
|
|
|
|
public:
|
|
LiveSetAccessors() : set_() {}
|
|
explicit MOZ_CONSTEXPR LiveSetAccessors(SetType) = delete;
|
|
explicit MOZ_CONSTEXPR LiveSetAccessors(RegisterSet set) : set_(set) {}
|
|
|
|
bool has(Register reg) const {
|
|
return set_.gprs().hasRegisterIndex(reg);
|
|
}
|
|
bool has(FloatRegister reg) const {
|
|
return set_.fpus().hasRegisterIndex(reg);
|
|
}
|
|
|
|
void addUnchecked(Register reg) {
|
|
set_.gprs().addRegisterIndex(reg);
|
|
}
|
|
void addUnchecked(FloatRegister reg) {
|
|
set_.fpus().addRegisterIndex(reg);
|
|
}
|
|
|
|
void takeUnchecked(Register reg) {
|
|
set_.gprs().takeRegisterIndex(reg);
|
|
}
|
|
void takeUnchecked(FloatRegister reg) {
|
|
set_.fpus().takeRegisterIndex(reg);
|
|
}
|
|
};
|
|
|
|
#define DEFINE_ACCESSOR_CONSTRUCTORS_(REGSET) \
|
|
typedef typename Parent::RegSet RegSet; \
|
|
typedef typename Parent::RegType RegType; \
|
|
typedef typename Parent::SetType SetType; \
|
|
\
|
|
MOZ_CONSTEXPR_TMPL REGSET() : Parent() {} \
|
|
explicit MOZ_CONSTEXPR_TMPL REGSET(SetType set) : Parent(set) {} \
|
|
explicit MOZ_CONSTEXPR_TMPL REGSET(RegSet set) : Parent(set) {}
|
|
|
|
// This class adds checked accessors on top of the unchecked variants defined by
|
|
// AllocatableSet and LiveSet accessors. Also it defines interface which are
|
|
// specialized to the register set implementation, such as |getAny| and
|
|
// |takeAny| variants.
|
|
template <class Accessors, typename Set>
|
|
class SpecializedRegSet : public Accessors
|
|
{
|
|
typedef Accessors Parent;
|
|
|
|
public:
|
|
DEFINE_ACCESSOR_CONSTRUCTORS_(SpecializedRegSet)
|
|
|
|
SetType bits() const {
|
|
return this->Parent::set_.bits();
|
|
}
|
|
|
|
using Parent::has;
|
|
|
|
using Parent::addUnchecked;
|
|
void add(RegType reg) {
|
|
MOZ_ASSERT(!has(reg));
|
|
addUnchecked(reg);
|
|
}
|
|
|
|
using Parent::takeUnchecked;
|
|
void take(RegType reg) {
|
|
MOZ_ASSERT(has(reg));
|
|
takeUnchecked(reg);
|
|
}
|
|
|
|
RegType getAny() const {
|
|
return this->Parent::set_.getAny();
|
|
}
|
|
RegType getFirst() const {
|
|
return this->Parent::set_.getFirst();
|
|
}
|
|
RegType getLast() const {
|
|
return this->Parent::set_.getLast();
|
|
}
|
|
|
|
RegType getAnyExcluding(RegType preclude) {
|
|
if (!has(preclude))
|
|
return getAny();
|
|
|
|
take(preclude);
|
|
RegType result = getAny();
|
|
add(preclude);
|
|
return result;
|
|
}
|
|
|
|
RegType takeAny() {
|
|
RegType reg = getAny();
|
|
take(reg);
|
|
return reg;
|
|
}
|
|
RegType takeFirst() {
|
|
RegType reg = getFirst();
|
|
take(reg);
|
|
return reg;
|
|
}
|
|
RegType takeLast() {
|
|
RegType reg = getLast();
|
|
take(reg);
|
|
return reg;
|
|
}
|
|
|
|
ValueOperand takeAnyValue() {
|
|
#if defined(JS_NUNBOX32)
|
|
return ValueOperand(takeAny(), takeAny());
|
|
#elif defined(JS_PUNBOX64)
|
|
return ValueOperand(takeAny());
|
|
#else
|
|
#error "Bad architecture"
|
|
#endif
|
|
}
|
|
|
|
RegType takeAnyExcluding(RegType preclude) {
|
|
RegType reg = getAnyExcluding(preclude);
|
|
take(reg);
|
|
return reg;
|
|
}
|
|
};
|
|
|
|
// Specialization of the accessors for the RegisterSet aggregate.
|
|
template <class Accessors>
|
|
class SpecializedRegSet<Accessors, RegisterSet> : public Accessors
|
|
{
|
|
typedef Accessors Parent;
|
|
|
|
public:
|
|
DEFINE_ACCESSOR_CONSTRUCTORS_(SpecializedRegSet)
|
|
|
|
GeneralRegisterSet gprs() const {
|
|
return this->Parent::set_.gprs();
|
|
}
|
|
GeneralRegisterSet& gprs() {
|
|
return this->Parent::set_.gprs();
|
|
}
|
|
FloatRegisterSet fpus() const {
|
|
return this->Parent::set_.fpus();
|
|
}
|
|
FloatRegisterSet& fpus() {
|
|
return this->Parent::set_.fpus();
|
|
}
|
|
|
|
bool emptyGeneral() const {
|
|
return this->Parent::set_.emptyGeneral();
|
|
}
|
|
bool emptyFloat() const {
|
|
return this->Parent::set_.emptyFloat();
|
|
}
|
|
|
|
|
|
using Parent::has;
|
|
bool has(AnyRegister reg) const {
|
|
return reg.isFloat() ? has(reg.fpu()) : has(reg.gpr());
|
|
}
|
|
|
|
|
|
using Parent::addUnchecked;
|
|
void addUnchecked(AnyRegister reg) {
|
|
if (reg.isFloat())
|
|
addUnchecked(reg.fpu());
|
|
else
|
|
addUnchecked(reg.gpr());
|
|
}
|
|
|
|
void add(Register reg) {
|
|
MOZ_ASSERT(!has(reg));
|
|
addUnchecked(reg);
|
|
}
|
|
void add(FloatRegister reg) {
|
|
MOZ_ASSERT(!has(reg));
|
|
addUnchecked(reg);
|
|
}
|
|
void add(AnyRegister reg) {
|
|
if (reg.isFloat())
|
|
add(reg.fpu());
|
|
else
|
|
add(reg.gpr());
|
|
}
|
|
|
|
using Parent::takeUnchecked;
|
|
void takeUnchecked(AnyRegister reg) {
|
|
if (reg.isFloat())
|
|
takeUnchecked(reg.fpu());
|
|
else
|
|
takeUnchecked(reg.gpr());
|
|
}
|
|
|
|
void take(Register reg) {
|
|
MOZ_ASSERT(has(reg));
|
|
takeUnchecked(reg);
|
|
}
|
|
void take(FloatRegister reg) {
|
|
MOZ_ASSERT(has(reg));
|
|
takeUnchecked(reg);
|
|
}
|
|
void take(AnyRegister reg) {
|
|
if (reg.isFloat())
|
|
take(reg.fpu());
|
|
else
|
|
take(reg.gpr());
|
|
}
|
|
|
|
Register getAnyGeneral() const {
|
|
return this->Parent::set_.gprs().getAny();
|
|
}
|
|
FloatRegister getAnyFloat() const {
|
|
return this->Parent::set_.fpus().getAny();
|
|
}
|
|
|
|
Register takeAnyGeneral() {
|
|
Register reg = getAnyGeneral();
|
|
take(reg);
|
|
return reg;
|
|
}
|
|
FloatRegister takeAnyFloat() {
|
|
FloatRegister reg = getAnyFloat();
|
|
take(reg);
|
|
return reg;
|
|
}
|
|
ValueOperand takeAnyValue() {
|
|
#if defined(JS_NUNBOX32)
|
|
return ValueOperand(takeAnyGeneral(), takeAnyGeneral());
|
|
#elif defined(JS_PUNBOX64)
|
|
return ValueOperand(takeAnyGeneral());
|
|
#else
|
|
#error "Bad architecture"
|
|
#endif
|
|
}
|
|
};
|
|
|
|
|
|
// Interface which is common to all register set implementations. It overloads
|
|
// |add|, |take| and |takeUnchecked| methods for types such as |ValueOperand|
|
|
// and |TypedOrValueRegister|.
|
|
template <class Accessors, typename Set>
|
|
class CommonRegSet : public SpecializedRegSet<Accessors, Set>
|
|
{
|
|
typedef SpecializedRegSet<Accessors, Set> Parent;
|
|
|
|
public:
|
|
DEFINE_ACCESSOR_CONSTRUCTORS_(CommonRegSet)
|
|
|
|
RegSet set() const {
|
|
return this->Parent::set_;
|
|
}
|
|
RegSet& set() {
|
|
return this->Parent::set_;
|
|
}
|
|
|
|
bool empty() const {
|
|
return this->Parent::set_.empty();
|
|
}
|
|
void clear() {
|
|
this->Parent::set_.clear();
|
|
}
|
|
|
|
using Parent::add;
|
|
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());
|
|
}
|
|
|
|
using Parent::take;
|
|
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 take(TypedOrValueRegister reg) {
|
|
if (reg.hasValue())
|
|
take(reg.valueReg());
|
|
else if (reg.hasTyped())
|
|
take(reg.typedReg());
|
|
}
|
|
|
|
using Parent::takeUnchecked;
|
|
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
|
|
}
|
|
void takeUnchecked(TypedOrValueRegister reg) {
|
|
if (reg.hasValue())
|
|
takeUnchecked(reg.valueReg());
|
|
else if (reg.hasTyped())
|
|
takeUnchecked(reg.typedReg());
|
|
}
|
|
};
|
|
|
|
|
|
// These classes do not provide any additional members, they only use their
|
|
// constructors to forward to the common interface for all register sets. The
|
|
// only benefit of these classes is to provide user friendly names.
|
|
template <typename Set>
|
|
class LiveSet : public CommonRegSet<LiveSetAccessors<Set>, Set>
|
|
{
|
|
typedef CommonRegSet<LiveSetAccessors<Set>, Set> Parent;
|
|
|
|
public:
|
|
DEFINE_ACCESSOR_CONSTRUCTORS_(LiveSet)
|
|
};
|
|
|
|
template <typename Set>
|
|
class AllocatableSet : public CommonRegSet<AllocatableSetAccessors<Set>, Set>
|
|
{
|
|
typedef CommonRegSet<AllocatableSetAccessors<Set>, Set> Parent;
|
|
|
|
public:
|
|
DEFINE_ACCESSOR_CONSTRUCTORS_(AllocatableSet)
|
|
|
|
LiveSet<Set> asLiveSet() const {
|
|
return LiveSet<Set>(this->set());
|
|
}
|
|
};
|
|
|
|
#define DEFINE_ACCESSOR_CONSTRUCTORS_FOR_REGISTERSET_(REGSET) \
|
|
typedef Parent::RegSet RegSet; \
|
|
typedef Parent::RegType RegType; \
|
|
typedef Parent::SetType SetType; \
|
|
\
|
|
MOZ_CONSTEXPR_TMPL REGSET() : Parent() {} \
|
|
explicit MOZ_CONSTEXPR_TMPL REGSET(SetType) = delete; \
|
|
explicit MOZ_CONSTEXPR_TMPL REGSET(RegSet set) : Parent(set) {} \
|
|
MOZ_CONSTEXPR_TMPL REGSET(GeneralRegisterSet gpr, FloatRegisterSet fpu) \
|
|
: Parent(RegisterSet(gpr, fpu)) \
|
|
{} \
|
|
REGSET(REGSET<GeneralRegisterSet> gpr, REGSET<FloatRegisterSet> fpu) \
|
|
: Parent(RegisterSet(gpr.set(), fpu.set())) \
|
|
{}
|
|
|
|
template <>
|
|
class LiveSet<RegisterSet>
|
|
: public CommonRegSet<LiveSetAccessors<RegisterSet>, RegisterSet>
|
|
{
|
|
// Note: We have to provide a qualified name for LiveSetAccessors, as it is
|
|
// interpreted as being the specialized class name inherited from the parent
|
|
// class specialization.
|
|
typedef CommonRegSet<jit::LiveSetAccessors<RegisterSet>, RegisterSet> Parent;
|
|
|
|
public:
|
|
DEFINE_ACCESSOR_CONSTRUCTORS_FOR_REGISTERSET_(LiveSet)
|
|
};
|
|
|
|
template <>
|
|
class AllocatableSet<RegisterSet>
|
|
: public CommonRegSet<AllocatableSetAccessors<RegisterSet>, RegisterSet>
|
|
{
|
|
// Note: We have to provide a qualified name for AllocatableSetAccessors, as
|
|
// it is interpreted as being the specialized class name inherited from the
|
|
// parent class specialization.
|
|
typedef CommonRegSet<jit::AllocatableSetAccessors<RegisterSet>, RegisterSet> Parent;
|
|
|
|
public:
|
|
DEFINE_ACCESSOR_CONSTRUCTORS_FOR_REGISTERSET_(AllocatableSet)
|
|
|
|
LiveSet<RegisterSet> asLiveSet() const {
|
|
return LiveSet<RegisterSet>(this->set());
|
|
}
|
|
};
|
|
|
|
#undef DEFINE_ACCESSOR_CONSTRUCTORS_FOR_REGISTERSET_
|
|
#undef DEFINE_ACCESSOR_CONSTRUCTORS_
|
|
|
|
typedef AllocatableSet<GeneralRegisterSet> AllocatableGeneralRegisterSet;
|
|
typedef AllocatableSet<FloatRegisterSet> AllocatableFloatRegisterSet;
|
|
typedef AllocatableSet<RegisterSet> AllocatableRegisterSet;
|
|
|
|
typedef LiveSet<GeneralRegisterSet> LiveGeneralRegisterSet;
|
|
typedef LiveSet<FloatRegisterSet> LiveFloatRegisterSet;
|
|
typedef LiveSet<RegisterSet> LiveRegisterSet;
|
|
|
|
// iterates in whatever order happens to be convenient.
|
|
// Use TypedRegisterBackwardIterator or TypedRegisterForwardIterator if a
|
|
// specific order is required.
|
|
template <typename T>
|
|
class TypedRegisterIterator
|
|
{
|
|
LiveSet<TypedRegisterSet<T>> regset_;
|
|
|
|
public:
|
|
explicit TypedRegisterIterator(TypedRegisterSet<T> regset) : regset_(regset)
|
|
{ }
|
|
explicit TypedRegisterIterator(LiveSet<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 backwards, that is, rn to r0
|
|
template <typename T>
|
|
class TypedRegisterBackwardIterator
|
|
{
|
|
LiveSet<TypedRegisterSet<T>> regset_;
|
|
|
|
public:
|
|
explicit TypedRegisterBackwardIterator(TypedRegisterSet<T> regset) : regset_(regset)
|
|
{ }
|
|
explicit TypedRegisterBackwardIterator(LiveSet<TypedRegisterSet<T>> regset) : regset_(regset)
|
|
{ }
|
|
TypedRegisterBackwardIterator(const TypedRegisterBackwardIterator& other)
|
|
: regset_(other.regset_)
|
|
{ }
|
|
|
|
bool more() const {
|
|
return !regset_.empty();
|
|
}
|
|
TypedRegisterBackwardIterator<T> operator ++(int) {
|
|
TypedRegisterBackwardIterator<T> old(*this);
|
|
regset_.takeLast();
|
|
return old;
|
|
}
|
|
TypedRegisterBackwardIterator<T>& operator ++() {
|
|
regset_.takeLast();
|
|
return *this;
|
|
}
|
|
T operator*() const {
|
|
return regset_.getLast();
|
|
}
|
|
};
|
|
|
|
// iterates forwards, that is r0 to rn
|
|
template <typename T>
|
|
class TypedRegisterForwardIterator
|
|
{
|
|
LiveSet<TypedRegisterSet<T>> regset_;
|
|
|
|
public:
|
|
explicit TypedRegisterForwardIterator(TypedRegisterSet<T> regset) : regset_(regset)
|
|
{ }
|
|
explicit TypedRegisterForwardIterator(LiveSet<TypedRegisterSet<T>> regset) : regset_(regset)
|
|
{ }
|
|
TypedRegisterForwardIterator(const TypedRegisterForwardIterator& other) : regset_(other.regset_)
|
|
{ }
|
|
|
|
bool more() const {
|
|
return !regset_.empty();
|
|
}
|
|
TypedRegisterForwardIterator<T> operator ++(int) {
|
|
TypedRegisterForwardIterator<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 TypedRegisterBackwardIterator<Register> GeneralRegisterBackwardIterator;
|
|
typedef TypedRegisterBackwardIterator<FloatRegister> FloatRegisterBackwardIterator;
|
|
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)
|
|
{ }
|
|
explicit AnyRegisterIterator(const RegisterSet& set)
|
|
: geniter_(set.gpr_), floatiter_(set.fpu_)
|
|
{ }
|
|
explicit AnyRegisterIterator(const LiveSet<RegisterSet>& set)
|
|
: geniter_(set.gprs()), floatiter_(set.fpus())
|
|
{ }
|
|
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,
|
|
#ifdef JS_CODEGEN_REGISTER_PAIR
|
|
GPR_PAIR,
|
|
#endif
|
|
FPU,
|
|
Stack
|
|
};
|
|
|
|
private:
|
|
Kind kind_;
|
|
union {
|
|
Register::Code gpr_;
|
|
FloatRegister::Code fpu_;
|
|
uint32_t offset_;
|
|
} u;
|
|
|
|
public:
|
|
ABIArg() : kind_(Kind(-1)) { u.offset_ = -1; }
|
|
explicit ABIArg(Register gpr) : kind_(GPR) { u.gpr_ = gpr.code(); }
|
|
explicit ABIArg(Register gprLow, Register gprHigh)
|
|
{
|
|
#if defined(JS_CODEGEN_REGISTER_PAIR)
|
|
kind_ = GPR_PAIR;
|
|
#else
|
|
MOZ_CRASH("Unsupported type of ABI argument.");
|
|
#endif
|
|
u.gpr_ = gprLow.code();
|
|
MOZ_ASSERT(u.gpr_ % 2 == 0);
|
|
MOZ_ASSERT(u.gpr_ + 1 == gprHigh.code());
|
|
}
|
|
explicit ABIArg(FloatRegister fpu) : kind_(FPU) { u.fpu_ = fpu.code(); }
|
|
explicit ABIArg(uint32_t offset) : kind_(Stack) { u.offset_ = offset; }
|
|
|
|
Kind kind() const { return kind_; }
|
|
#ifdef JS_CODEGEN_REGISTER_PAIR
|
|
bool isGeneralRegPair() const { return kind_ == GPR_PAIR; }
|
|
#else
|
|
bool isGeneralRegPair() const { return false; }
|
|
#endif
|
|
|
|
Register gpr() const {
|
|
MOZ_ASSERT(kind() == GPR);
|
|
return Register::FromCode(u.gpr_);
|
|
}
|
|
Register evenGpr() const {
|
|
MOZ_ASSERT(isGeneralRegPair());
|
|
return Register::FromCode(u.gpr_);
|
|
}
|
|
Register oddGpr() const {
|
|
MOZ_ASSERT(isGeneralRegPair());
|
|
return Register::FromCode(u.gpr_ + 1);
|
|
}
|
|
FloatRegister fpu() const { MOZ_ASSERT(kind() == FPU); return FloatRegister::FromCode(u.fpu_); }
|
|
uint32_t offsetFromArgBase() const { MOZ_ASSERT(kind() == Stack); return u.offset_; }
|
|
|
|
bool argInRegister() const { return kind() != Stack; }
|
|
AnyRegister reg() const { return kind_ == GPR ? AnyRegister(gpr()) : AnyRegister(fpu()); }
|
|
};
|
|
|
|
// Get the set of registers which should be saved by a block of code which
|
|
// clobbers all registers besides |unused|, but does not clobber floating point
|
|
// registers.
|
|
inline LiveGeneralRegisterSet
|
|
SavedNonVolatileRegisters(AllocatableGeneralRegisterSet unused)
|
|
{
|
|
LiveGeneralRegisterSet result;
|
|
|
|
for (GeneralRegisterIterator iter(GeneralRegisterSet::NonVolatile()); iter.more(); iter++) {
|
|
Register reg = *iter;
|
|
if (!unused.has(reg))
|
|
result.add(reg);
|
|
}
|
|
|
|
// Some platforms require the link register to be saved, if calls can be made.
|
|
#if defined(JS_CODEGEN_ARM)
|
|
result.add(Register::FromCode(Registers::lr));
|
|
#elif defined(JS_CODEGEN_ARM64)
|
|
result.add(Register::FromCode(Registers::lr));
|
|
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
|
result.add(Register::FromCode(Registers::ra));
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
} // namespace jit
|
|
} // namespace js
|
|
|
|
#endif /* jit_RegisterSets_h */
|