зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1321521
- RegisterSets: Add a register type to getAny and add the equivalent hasAny function. r=lth
This commit is contained in:
Родитель
25012b4646
Коммит
9e0785daca
|
@ -419,7 +419,7 @@ BacktrackingAllocator::init()
|
|||
registers[reg.code()].allocatable = true;
|
||||
}
|
||||
while (!remainingRegisters.emptyFloat()) {
|
||||
AnyRegister reg = AnyRegister(remainingRegisters.takeAnyFloat());
|
||||
AnyRegister reg = AnyRegister(remainingRegisters.takeAnyFloat<RegTypeName::Any>());
|
||||
registers[reg.code()].allocatable = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -4686,7 +4686,7 @@ CodeGenerator::generateArgumentsChecks(bool bailout)
|
|||
MResumePoint* rp = mir.entryResumePoint();
|
||||
|
||||
// No registers are allocated yet, so it's safe to grab anything.
|
||||
Register temp = GeneralRegisterSet(EntryTempMask).getAny();
|
||||
Register temp = AllocatableGeneralRegisterSet(EntryTempMask).getAny();
|
||||
|
||||
const CompileInfo& info = gen->info();
|
||||
|
||||
|
|
|
@ -392,22 +392,22 @@ class TypedRegisterSet
|
|||
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();
|
||||
static constexpr RegTypeName DefaultType = RegType::DefaultType;
|
||||
|
||||
template <RegTypeName Name>
|
||||
SetType allLive() const {
|
||||
return T::template LiveAsIndexableSet<Name>(bits_);
|
||||
}
|
||||
T getFirst() const {
|
||||
MOZ_ASSERT(!empty());
|
||||
return T::FromCode(T::FirstBit(bits_));
|
||||
template <RegTypeName Name>
|
||||
SetType allAllocatable() const {
|
||||
return T::template AllocatableAsIndexableSet<Name>(bits_);
|
||||
}
|
||||
T getLast() const {
|
||||
MOZ_ASSERT(!empty());
|
||||
int ireg = T::LastBit(bits_);
|
||||
return T::FromCode(ireg);
|
||||
|
||||
static RegType FirstRegister(SetType set) {
|
||||
return RegType::FromCode(RegType::FirstBit(set));
|
||||
}
|
||||
static RegType LastRegister(SetType set) {
|
||||
return RegType::FromCode(RegType::LastBit(set));
|
||||
}
|
||||
|
||||
SetType bits() const {
|
||||
|
@ -481,6 +481,9 @@ class RegisterSet {
|
|||
bool emptyFloat() const {
|
||||
return fpu_.empty();
|
||||
}
|
||||
|
||||
static constexpr RegTypeName DefaultType = RegTypeName::GPR;
|
||||
|
||||
constexpr GeneralRegisterSet gprs() const {
|
||||
return gpr_;
|
||||
}
|
||||
|
@ -540,6 +543,9 @@ 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:
|
||||
//
|
||||
// - all<Type>: Returns a bit-set of all the register of a specific type
|
||||
// which are present.
|
||||
//
|
||||
// - has: Returns if all the bits needed to take a register are present.
|
||||
//
|
||||
// - takeUnchecked: Subtracts the bits used to represent the register in the
|
||||
|
@ -573,6 +579,11 @@ class AllocatableSetAccessors
|
|||
protected:
|
||||
RegSet set_;
|
||||
|
||||
template <RegTypeName Name>
|
||||
SetType all() const {
|
||||
return set_.template allAllocatable<Name>();
|
||||
}
|
||||
|
||||
public:
|
||||
AllocatableSetAccessors() : set_() {}
|
||||
explicit constexpr AllocatableSetAccessors(SetType set) : set_(set) {}
|
||||
|
@ -582,6 +593,11 @@ class AllocatableSetAccessors
|
|||
return set_.hasAllocatable(reg);
|
||||
}
|
||||
|
||||
template <RegTypeName Name>
|
||||
bool hasAny(RegType reg) const {
|
||||
return all<Name>() != 0;
|
||||
}
|
||||
|
||||
void addUnchecked(RegType reg) {
|
||||
set_.addAllocatable(reg);
|
||||
}
|
||||
|
@ -603,6 +619,15 @@ class AllocatableSetAccessors<RegisterSet>
|
|||
protected:
|
||||
RegisterSet set_;
|
||||
|
||||
template <RegTypeName Name>
|
||||
GeneralRegisterSet::SetType allGpr() const {
|
||||
return set_.gprs().allAllocatable<Name>();
|
||||
}
|
||||
template <RegTypeName Name>
|
||||
FloatRegisterSet::SetType allFpu() const {
|
||||
return set_.fpus().allAllocatable<Name>();
|
||||
}
|
||||
|
||||
public:
|
||||
AllocatableSetAccessors() : set_() {}
|
||||
explicit constexpr AllocatableSetAccessors(SetType) = delete;
|
||||
|
@ -655,6 +680,11 @@ class LiveSetAccessors
|
|||
protected:
|
||||
RegSet set_;
|
||||
|
||||
template <RegTypeName Name>
|
||||
SetType all() const {
|
||||
return set_.template allLive<Name>();
|
||||
}
|
||||
|
||||
public:
|
||||
LiveSetAccessors() : set_() {}
|
||||
explicit constexpr LiveSetAccessors(SetType set) : set_(set) {}
|
||||
|
@ -685,6 +715,15 @@ class LiveSetAccessors<RegisterSet>
|
|||
protected:
|
||||
RegisterSet set_;
|
||||
|
||||
template <RegTypeName Name>
|
||||
GeneralRegisterSet::SetType allGpr() const {
|
||||
return set_.gprs().allLive<Name>();
|
||||
}
|
||||
template <RegTypeName Name>
|
||||
FloatRegisterSet::SetType allFpu() const {
|
||||
return set_.fpus().allLive<Name>();
|
||||
}
|
||||
|
||||
public:
|
||||
LiveSetAccessors() : set_() {}
|
||||
explicit constexpr LiveSetAccessors(SetType) = delete;
|
||||
|
@ -751,47 +790,68 @@ class SpecializedRegSet : public Accessors
|
|||
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();
|
||||
template <RegTypeName Name>
|
||||
bool hasAny() const {
|
||||
return Parent::template all<Name>() != 0;
|
||||
}
|
||||
|
||||
template <RegTypeName Name = RegSet::DefaultType>
|
||||
RegType getFirst() const {
|
||||
SetType set = Parent::template all<Name>();
|
||||
MOZ_ASSERT(set);
|
||||
return RegSet::FirstRegister(set);
|
||||
}
|
||||
template <RegTypeName Name = RegSet::DefaultType>
|
||||
RegType getLast() const {
|
||||
SetType set = Parent::template all<Name>();
|
||||
MOZ_ASSERT(set);
|
||||
return RegSet::LastRegister(set);
|
||||
}
|
||||
template <RegTypeName Name = RegSet::DefaultType>
|
||||
RegType 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<Name>();
|
||||
}
|
||||
|
||||
template <RegTypeName Name = RegSet::DefaultType>
|
||||
RegType getAnyExcluding(RegType preclude) {
|
||||
if (!has(preclude))
|
||||
return getAny();
|
||||
return getAny<Name>();
|
||||
|
||||
take(preclude);
|
||||
RegType result = getAny();
|
||||
RegType result = getAny<Name>();
|
||||
add(preclude);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <RegTypeName Name = RegSet::DefaultType>
|
||||
RegType takeAny() {
|
||||
RegType reg = getAny();
|
||||
RegType reg = getAny<Name>();
|
||||
take(reg);
|
||||
return reg;
|
||||
}
|
||||
template <RegTypeName Name = RegSet::DefaultType>
|
||||
RegType takeFirst() {
|
||||
RegType reg = getFirst();
|
||||
RegType reg = getFirst<Name>();
|
||||
take(reg);
|
||||
return reg;
|
||||
}
|
||||
template <RegTypeName Name = RegSet::DefaultType>
|
||||
RegType takeLast() {
|
||||
RegType reg = getLast();
|
||||
RegType reg = getLast<Name>();
|
||||
take(reg);
|
||||
return reg;
|
||||
}
|
||||
|
||||
ValueOperand takeAnyValue() {
|
||||
#if defined(JS_NUNBOX32)
|
||||
return ValueOperand(takeAny(), takeAny());
|
||||
return ValueOperand(takeAny<RegTypeName::GPR>(), takeAny<RegTypeName::GPR>());
|
||||
#elif defined(JS_PUNBOX64)
|
||||
return ValueOperand(takeAny());
|
||||
return ValueOperand(takeAny<RegTypeName::GPR>());
|
||||
#else
|
||||
#error "Bad architecture"
|
||||
#endif
|
||||
|
@ -805,8 +865,9 @@ class SpecializedRegSet : public Accessors
|
|||
#endif
|
||||
}
|
||||
|
||||
template <RegTypeName Name = RegSet::DefaultType>
|
||||
RegType takeAnyExcluding(RegType preclude) {
|
||||
RegType reg = getAnyExcluding(preclude);
|
||||
RegType reg = getAnyExcluding<Name>(preclude);
|
||||
take(reg);
|
||||
return reg;
|
||||
}
|
||||
|
@ -841,12 +902,17 @@ class SpecializedRegSet<Accessors, RegisterSet> : public Accessors
|
|||
return this->Parent::set_.emptyFloat();
|
||||
}
|
||||
|
||||
|
||||
using Parent::has;
|
||||
bool has(AnyRegister reg) const {
|
||||
return reg.isFloat() ? has(reg.fpu()) : has(reg.gpr());
|
||||
}
|
||||
|
||||
template <RegTypeName Name>
|
||||
bool hasAny() const {
|
||||
if (Name == RegTypeName::GPR)
|
||||
return Parent::template allGpr<RegTypeName::GPR>() != 0;
|
||||
return Parent::template allFpu<Name>() != 0;
|
||||
}
|
||||
|
||||
using Parent::addUnchecked;
|
||||
void addUnchecked(AnyRegister reg) {
|
||||
|
@ -895,10 +961,15 @@ class SpecializedRegSet<Accessors, RegisterSet> : public Accessors
|
|||
}
|
||||
|
||||
Register getAnyGeneral() const {
|
||||
return this->Parent::set_.gprs().getAny();
|
||||
GeneralRegisterSet::SetType set = Parent::template allGpr<RegTypeName::GPR>();
|
||||
MOZ_ASSERT(set);
|
||||
return GeneralRegisterSet::FirstRegister(set);
|
||||
}
|
||||
template <RegTypeName Name = RegTypeName::Float64>
|
||||
FloatRegister getAnyFloat() const {
|
||||
return this->Parent::set_.fpus().getAny();
|
||||
FloatRegisterSet::SetType set = Parent::template allFpu<Name>();
|
||||
MOZ_ASSERT(set);
|
||||
return FloatRegisterSet::FirstRegister(set);
|
||||
}
|
||||
|
||||
Register takeAnyGeneral() {
|
||||
|
@ -906,8 +977,9 @@ class SpecializedRegSet<Accessors, RegisterSet> : public Accessors
|
|||
take(reg);
|
||||
return reg;
|
||||
}
|
||||
template <RegTypeName Name = RegTypeName::Float64>
|
||||
FloatRegister takeAnyFloat() {
|
||||
FloatRegister reg = getAnyFloat();
|
||||
FloatRegister reg = getAnyFloat<Name>();
|
||||
take(reg);
|
||||
return reg;
|
||||
}
|
||||
|
@ -1105,11 +1177,11 @@ class TypedRegisterIterator
|
|||
return !regset_.empty();
|
||||
}
|
||||
TypedRegisterIterator<T>& operator ++() {
|
||||
regset_.takeAny();
|
||||
regset_.template takeAny<RegTypeName::Any>();
|
||||
return *this;
|
||||
}
|
||||
T operator*() const {
|
||||
return regset_.getAny();
|
||||
return regset_.template getAny<RegTypeName::Any>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1132,11 +1204,11 @@ class TypedRegisterBackwardIterator
|
|||
return !regset_.empty();
|
||||
}
|
||||
TypedRegisterBackwardIterator<T>& operator ++() {
|
||||
regset_.takeLast();
|
||||
regset_.template takeLast<RegTypeName::Any>();
|
||||
return *this;
|
||||
}
|
||||
T operator*() const {
|
||||
return regset_.getLast();
|
||||
return regset_.template getLast<RegTypeName::Any>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1158,11 +1230,11 @@ class TypedRegisterForwardIterator
|
|||
return !regset_.empty();
|
||||
}
|
||||
TypedRegisterForwardIterator<T>& operator ++() {
|
||||
regset_.takeFirst();
|
||||
regset_.template takeFirst<RegTypeName::Any>();
|
||||
return *this;
|
||||
}
|
||||
T operator*() const {
|
||||
return regset_.getFirst();
|
||||
return regset_.template getFirst<RegTypeName::Any>();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -88,6 +88,19 @@ struct Register {
|
|||
return SetType(1) << code();
|
||||
}
|
||||
|
||||
static constexpr RegTypeName DefaultType = RegTypeName::GPR;
|
||||
|
||||
template <RegTypeName = DefaultType>
|
||||
static SetType LiveAsIndexableSet(SetType s) {
|
||||
return SetType(0);
|
||||
}
|
||||
|
||||
template <RegTypeName Name = DefaultType>
|
||||
static SetType AllocatableAsIndexableSet(SetType s) {
|
||||
static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable");
|
||||
return SetType(0);
|
||||
}
|
||||
|
||||
static uint32_t SetSize(SetType x) {
|
||||
return Codes::SetSize(x);
|
||||
}
|
||||
|
@ -99,6 +112,24 @@ struct Register {
|
|||
}
|
||||
};
|
||||
|
||||
template <> inline Register::SetType
|
||||
Register::LiveAsIndexableSet<RegTypeName::GPR>(SetType set)
|
||||
{
|
||||
return set;
|
||||
}
|
||||
|
||||
template <> inline Register::SetType
|
||||
Register::LiveAsIndexableSet<RegTypeName::Any>(SetType set)
|
||||
{
|
||||
return set;
|
||||
}
|
||||
|
||||
template <> inline Register::SetType
|
||||
Register::AllocatableAsIndexableSet<RegTypeName::GPR>(SetType set)
|
||||
{
|
||||
return set;
|
||||
}
|
||||
|
||||
#if defined(JS_NUNBOX32)
|
||||
static const uint32_t INT64LOW_OFFSET = 0 * sizeof(int32_t);
|
||||
static const uint32_t INT64HIGH_OFFSET = 1 * sizeof(int32_t);
|
||||
|
|
|
@ -81,7 +81,8 @@ StupidAllocator::init()
|
|||
registers[registerCount++].reg = AnyRegister(remainingRegisters.takeAnyGeneral());
|
||||
|
||||
while (!remainingRegisters.emptyFloat())
|
||||
registers[registerCount++].reg = AnyRegister(remainingRegisters.takeAnyFloat());
|
||||
registers[registerCount++].reg =
|
||||
AnyRegister(remainingRegisters.takeAnyFloat<RegTypeName::Any>());
|
||||
|
||||
MOZ_ASSERT(registerCount <= MAX_REGISTERS);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jit/shared/Architecture-shared.h"
|
||||
|
||||
#include "js/Utility.h"
|
||||
|
||||
// GCC versions 4.6 and above define __ARM_PCS_VFP to denote a hard-float
|
||||
|
@ -285,6 +287,37 @@ class FloatRegisters
|
|||
static const uint32_t TotalPhys = 32;
|
||||
static uint32_t ActualTotalPhys();
|
||||
|
||||
// ARM float registers overlap in a way that for 1 double registers, in the
|
||||
// range d0-d15, we have 2 singles register in the range s0-s31. d16-d31
|
||||
// have no single register aliases. The aliasing rule state that d{n}
|
||||
// aliases s{2n} and s{2n+1}, for n in [0 .. 15].
|
||||
//
|
||||
// The register set is used to represent either allocatable register or live
|
||||
// registers. The register maps d0-d15 and s0-s31 to a single bit each. The
|
||||
// registers d16-d31 are not used at the moment.
|
||||
//
|
||||
// uuuu uuuu uuuu uuuu dddd dddd dddd dddd ssss ssss ssss ssss ssss ssss ssss ssss
|
||||
// ^ ^ ^ ^
|
||||
// '-- d15 d0 --' '-- s31 s0 --'
|
||||
//
|
||||
// LiveSet are handled by adding the bit of each register without
|
||||
// considering the aliases.
|
||||
//
|
||||
// AllocatableSet are handled by adding and removing the bit of each
|
||||
// aligned-or-dominated-aliased registers.
|
||||
//
|
||||
// ...0...00... : s{2n}, s{2n+1} and d{n} are not available
|
||||
// ...1...01... : s{2n} is available (*)
|
||||
// ...0...10... : s{2n+1} is available
|
||||
// ...1...11... : s{2n}, s{2n+1} and d{n} are available
|
||||
//
|
||||
// (*) Note that d{n} bit is set, but is not available because s{2n+1} bit
|
||||
// is not set, which is required as d{n} dominates s{2n+1}. The d{n} bit is
|
||||
// set, because s{2n} is aligned.
|
||||
//
|
||||
// | d{n} |
|
||||
// | s{2n+1} | s{2n} |
|
||||
//
|
||||
typedef uint64_t SetType;
|
||||
static const SetType AllSingleMask = (1ull << TotalSingle) - 1;
|
||||
static const SetType AllDoubleMask = ((1ull << TotalDouble) - 1) << TotalSingle;
|
||||
|
@ -357,12 +390,9 @@ class VFPRegister
|
|||
|
||||
protected:
|
||||
RegType kind : 2;
|
||||
// ARM doesn't have more than 32 registers. Don't take more bits than we'll
|
||||
// need. Presently, we don't have plans to address the upper and lower
|
||||
// halves of the double registers seprately, so 5 bits should suffice. If we
|
||||
// do decide to address them seprately (vmov, I'm looking at you), we will
|
||||
// likely specify it as a separate field.
|
||||
public:
|
||||
// ARM doesn't have more than 32 registers of each type, so 5 bits should
|
||||
// suffice.
|
||||
uint32_t code_ : 5;
|
||||
protected:
|
||||
bool _isInvalid : 1;
|
||||
|
@ -370,7 +400,7 @@ class VFPRegister
|
|||
|
||||
public:
|
||||
constexpr VFPRegister(uint32_t r, RegType k)
|
||||
: kind(k), code_ (Code(r)), _isInvalid(false), _isMissing(false)
|
||||
: kind(k), code_(Code(r)), _isInvalid(false), _isMissing(false)
|
||||
{ }
|
||||
constexpr VFPRegister()
|
||||
: kind(Double), code_(Code(0)), _isInvalid(true), _isMissing(false)
|
||||
|
@ -533,7 +563,7 @@ class VFPRegister
|
|||
// s1.alignedOrDominatedAliasedSet() == s1.
|
||||
// d0.alignedOrDominatedAliasedSet() == s0 | s1 | d0.
|
||||
//
|
||||
// This way the Allocator register set does not have to do any arithmetics
|
||||
// This way the Allocatable register set does not have to do any arithmetics
|
||||
// to know if a register is available or not, as we have the following
|
||||
// relations:
|
||||
//
|
||||
|
@ -553,6 +583,19 @@ class VFPRegister
|
|||
return (SetType(0b11) << (code_ * 2)) | (SetType(1) << (32 + code_));
|
||||
}
|
||||
|
||||
static constexpr RegTypeName DefaultType = RegTypeName::Float64;
|
||||
|
||||
template <RegTypeName = DefaultType>
|
||||
static SetType LiveAsIndexableSet(SetType s) {
|
||||
return SetType(0);
|
||||
}
|
||||
|
||||
template <RegTypeName Name = DefaultType>
|
||||
static SetType AllocatableAsIndexableSet(SetType s) {
|
||||
static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable");
|
||||
return SetType(0);
|
||||
}
|
||||
|
||||
static uint32_t SetSize(SetType x) {
|
||||
static_assert(sizeof(SetType) == 8, "SetType must be 64 bits");
|
||||
return mozilla::CountPopulation32(x);
|
||||
|
@ -572,6 +615,79 @@ class VFPRegister
|
|||
|
||||
};
|
||||
|
||||
template <> inline VFPRegister::SetType
|
||||
VFPRegister::LiveAsIndexableSet<RegTypeName::Float32>(SetType set)
|
||||
{
|
||||
return set & FloatRegisters::AllSingleMask;
|
||||
}
|
||||
|
||||
template <> inline VFPRegister::SetType
|
||||
VFPRegister::LiveAsIndexableSet<RegTypeName::Float64>(SetType set)
|
||||
{
|
||||
return set & FloatRegisters::AllDoubleMask;
|
||||
}
|
||||
|
||||
template <> inline VFPRegister::SetType
|
||||
VFPRegister::LiveAsIndexableSet<RegTypeName::Any>(SetType set)
|
||||
{
|
||||
return set;
|
||||
}
|
||||
|
||||
template <> inline VFPRegister::SetType
|
||||
VFPRegister::AllocatableAsIndexableSet<RegTypeName::Float32>(SetType set)
|
||||
{
|
||||
// Single registers are not dominating any smaller registers, thus masking
|
||||
// is enough to convert an allocatable set into a set of register list all
|
||||
// single register available.
|
||||
return set & FloatRegisters::AllSingleMask;
|
||||
}
|
||||
|
||||
template <> inline VFPRegister::SetType
|
||||
VFPRegister::AllocatableAsIndexableSet<RegTypeName::Float64>(SetType set)
|
||||
{
|
||||
// An allocatable float register set is represented as follow:
|
||||
//
|
||||
// uuuu uuuu uuuu uuuu dddd dddd dddd dddd ssss ssss ssss ssss ssss ssss ssss ssss
|
||||
// ^ ^ ^ ^
|
||||
// '-- d15 d0 --' '-- s31 s0 --'
|
||||
//
|
||||
// ...0...00... : s{2n}, s{2n+1} and d{n} are not available
|
||||
// ...1...01... : s{2n} is available
|
||||
// ...0...10... : s{2n+1} is available
|
||||
// ...1...11... : s{2n}, s{2n+1} and d{n} are available
|
||||
//
|
||||
// The goal of this function is to return the set of double registers which
|
||||
// are available as an indexable bit set. This implies that iff a double bit
|
||||
// is set in the returned set, then the register is available.
|
||||
//
|
||||
// To do so, this functions converts the 32 bits set of single registers
|
||||
// into a 16 bits set of equivalent double registers. Then, we mask out
|
||||
// double registers which do not have all the single register that compose
|
||||
// them. As d{n} bit is set when s{2n} is available, we only need to take
|
||||
// s{2n+1} into account.
|
||||
|
||||
// Convert s7s6s5s4 s3s2s1s0 into s7s5s3s1, for all s0-s31.
|
||||
SetType s2d = AllocatableAsIndexableSet<RegTypeName::Float32>(set);
|
||||
static_assert(FloatRegisters::TotalSingle == 32, "Wrong mask");
|
||||
s2d = (0xaaaaaaaa & s2d) >> 1; // Filter s{2n+1} registers.
|
||||
// Group adjacent bits as follow:
|
||||
// 0.0.s3.s1 == ((0.s3.0.s1) >> 1 | (0.s3.0.s1)) & 0b0011;
|
||||
s2d = ((s2d >> 1) | s2d) & 0x33333333; // 0a0b --> 00ab
|
||||
s2d = ((s2d >> 2) | s2d) & 0x0f0f0f0f; // 00ab00cd --> 0000abcd
|
||||
s2d = ((s2d >> 4) | s2d) & 0x00ff00ff;
|
||||
s2d = ((s2d >> 8) | s2d) & 0x0000ffff;
|
||||
// Move the s7s5s3s1 to the aliased double positions.
|
||||
s2d = s2d << FloatRegisters::TotalSingle;
|
||||
|
||||
// Note: We currently do not use any representation for d16-d31.
|
||||
static_assert(FloatRegisters::TotalDouble == 16,
|
||||
"d16-d31 do not have a single register mapping");
|
||||
|
||||
// Filter out any double register which are not allocatable due to
|
||||
// non-aligned dominated single registers.
|
||||
return set & s2d;
|
||||
}
|
||||
|
||||
// The only floating point register set that we work with are the VFP Registers.
|
||||
typedef VFPRegister FloatRegister;
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#include "jit/shared/Architecture-shared.h"
|
||||
|
||||
#include "js/Utility.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -269,8 +271,8 @@ class FloatRegisters
|
|||
(1 << FloatRegisters::d30)) * SpreadCoefficient;
|
||||
|
||||
static const SetType VolatileMask = AllMask & ~NonVolatileMask;
|
||||
static const SetType AllDoubleMask = AllMask;
|
||||
static const SetType AllSingleMask = AllMask;
|
||||
static const SetType AllDoubleMask = AllPhysMask << TotalPhys;
|
||||
static const SetType AllSingleMask = AllPhysMask;
|
||||
|
||||
static const SetType WrapperMask = VolatileMask;
|
||||
|
||||
|
@ -404,7 +406,7 @@ struct FloatRegister
|
|||
return numAliased();
|
||||
}
|
||||
void alignedAliased(uint32_t aliasIdx, FloatRegister* ret) {
|
||||
MOZ_ASSERT(aliasIdx == 0);
|
||||
MOZ_ASSERT(aliasIdx < numAliased());
|
||||
aliased(aliasIdx, ret);
|
||||
}
|
||||
SetType alignedOrDominatedAliasedSet() const {
|
||||
|
@ -430,6 +432,19 @@ struct FloatRegister
|
|||
return 63 - mozilla::CountLeadingZeroes64(x);
|
||||
}
|
||||
|
||||
static constexpr RegTypeName DefaultType = RegTypeName::Float64;
|
||||
|
||||
template <RegTypeName Name = DefaultType>
|
||||
static SetType LiveAsIndexableSet(SetType s) {
|
||||
return SetType(0);
|
||||
}
|
||||
|
||||
template <RegTypeName Name = DefaultType>
|
||||
static SetType AllocatableAsIndexableSet(SetType s) {
|
||||
static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable");
|
||||
return LiveAsIndexableSet<Name>(s);
|
||||
}
|
||||
|
||||
static TypedRegisterSet<FloatRegister> ReduceSetForPush(const TypedRegisterSet<FloatRegister>& s);
|
||||
static uint32_t GetSizeInBytes(const TypedRegisterSet<FloatRegister>& s);
|
||||
static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s);
|
||||
|
@ -440,6 +455,24 @@ struct FloatRegister
|
|||
FloatRegisters::Kind k_ : 1;
|
||||
};
|
||||
|
||||
template <> inline FloatRegister::SetType
|
||||
FloatRegister::LiveAsIndexableSet<RegTypeName::Float32>(SetType set)
|
||||
{
|
||||
return set & FloatRegisters::AllSingleMask;
|
||||
}
|
||||
|
||||
template <> inline FloatRegister::SetType
|
||||
FloatRegister::LiveAsIndexableSet<RegTypeName::Float64>(SetType set)
|
||||
{
|
||||
return set & FloatRegisters::AllDoubleMask;
|
||||
}
|
||||
|
||||
template <> inline FloatRegister::SetType
|
||||
FloatRegister::LiveAsIndexableSet<RegTypeName::Any>(SetType set)
|
||||
{
|
||||
return set;
|
||||
}
|
||||
|
||||
// ARM/D32 has double registers that cannot be treated as float32.
|
||||
// Luckily, ARMv8 doesn't have the same misfortune.
|
||||
inline bool
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jit/shared/Architecture-shared.h"
|
||||
|
||||
#include "js/Utility.h"
|
||||
|
||||
// gcc appears to use _mips_hard_float to denote
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/* -*- 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_shared_Architecture_shared_h
|
||||
#define jit_shared_Architecture_shared_h
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
enum class RegTypeName {
|
||||
GPR,
|
||||
Float32,
|
||||
Float64,
|
||||
Vector128,
|
||||
Any
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_shared_Architecture_shared_h */
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#include "jit/shared/Architecture-shared.h"
|
||||
|
||||
#include "jit/x86-shared/Constants-x86-shared.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -262,6 +264,7 @@ class FloatRegisters {
|
|||
static const SetType AllMask = AllPhysMask * Spread;
|
||||
static const SetType AllDoubleMask = AllPhysMask * SpreadDouble;
|
||||
static const SetType AllSingleMask = AllPhysMask * SpreadSingle;
|
||||
static const SetType AllVector128Mask = AllPhysMask * SpreadSimd128;
|
||||
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
static const SetType NonAllocatableMask =
|
||||
|
@ -436,11 +439,48 @@ struct FloatRegister {
|
|||
return Codes::Spread << reg_;
|
||||
}
|
||||
|
||||
static constexpr RegTypeName DefaultType = RegTypeName::Float64;
|
||||
|
||||
template <RegTypeName = DefaultType>
|
||||
static SetType LiveAsIndexableSet(SetType s) {
|
||||
return SetType(0);
|
||||
}
|
||||
|
||||
template <RegTypeName Name = DefaultType>
|
||||
static SetType AllocatableAsIndexableSet(SetType s) {
|
||||
static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable");
|
||||
return LiveAsIndexableSet<Name>(s);
|
||||
}
|
||||
|
||||
static TypedRegisterSet<FloatRegister> ReduceSetForPush(const TypedRegisterSet<FloatRegister>& s);
|
||||
static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s);
|
||||
uint32_t getRegisterDumpOffsetInBytes();
|
||||
};
|
||||
|
||||
template <> inline FloatRegister::SetType
|
||||
FloatRegister::LiveAsIndexableSet<RegTypeName::Float32>(SetType set)
|
||||
{
|
||||
return set & FloatRegisters::AllSingleMask;
|
||||
}
|
||||
|
||||
template <> inline FloatRegister::SetType
|
||||
FloatRegister::LiveAsIndexableSet<RegTypeName::Float64>(SetType set)
|
||||
{
|
||||
return set & FloatRegisters::AllDoubleMask;
|
||||
}
|
||||
|
||||
template <> inline FloatRegister::SetType
|
||||
FloatRegister::LiveAsIndexableSet<RegTypeName::Vector128>(SetType set)
|
||||
{
|
||||
return set & FloatRegisters::AllVector128Mask;
|
||||
}
|
||||
|
||||
template <> inline FloatRegister::SetType
|
||||
FloatRegister::LiveAsIndexableSet<RegTypeName::Any>(SetType set)
|
||||
{
|
||||
return set;
|
||||
}
|
||||
|
||||
// Arm/D32 has double registers that can NOT be treated as float32
|
||||
// and this requires some dances in lowering.
|
||||
inline bool
|
||||
|
|
|
@ -35,6 +35,12 @@ CoPrime(size_t a, size_t b)
|
|||
} \
|
||||
}
|
||||
|
||||
#define BEGIN_All_WALK(RegTotal) \
|
||||
static const size_t Total = RegTotal; \
|
||||
size_t walk = 1; \
|
||||
size_t start = 0; \
|
||||
size_t index = start;
|
||||
|
||||
#define FOR_ALL_REGISTERS(Register, reg) \
|
||||
do { \
|
||||
Register reg = Register::FromCode(index);
|
||||
|
@ -136,3 +142,70 @@ BEGIN_TEST(testJitRegisterSet_FPU)
|
|||
return true;
|
||||
}
|
||||
END_TEST(testJitRegisterSet_FPU)
|
||||
|
||||
void pullAllFpus(AllocatableFloatRegisterSet& set, uint32_t& max_bits, uint32_t bits) {
|
||||
FloatRegisterSet allocSet(set.bits());
|
||||
FloatRegisterSet available_f32(allocSet.allAllocatable<RegTypeName::Float32>());
|
||||
FloatRegisterSet available_f64(allocSet.allAllocatable<RegTypeName::Float64>());
|
||||
FloatRegisterSet available_v128(allocSet.allAllocatable<RegTypeName::Vector128>());
|
||||
for (FloatRegisterIterator it(available_f32); it.more(); ++it) {
|
||||
FloatRegister tmp = *it;
|
||||
set.take(tmp);
|
||||
pullAllFpus(set, max_bits, bits + 32);
|
||||
set.add(tmp);
|
||||
}
|
||||
for (FloatRegisterIterator it(available_f64); it.more(); ++it) {
|
||||
FloatRegister tmp = *it;
|
||||
set.take(tmp);
|
||||
pullAllFpus(set, max_bits, bits + 64);
|
||||
set.add(tmp);
|
||||
}
|
||||
for (FloatRegisterIterator it(available_v128); it.more(); ++it) {
|
||||
FloatRegister tmp = *it;
|
||||
set.take(tmp);
|
||||
pullAllFpus(set, max_bits, bits + 128);
|
||||
set.add(tmp);
|
||||
}
|
||||
if (bits >= max_bits)
|
||||
max_bits = bits;
|
||||
}
|
||||
|
||||
BEGIN_TEST(testJitRegisterSet_FPU_Aliases)
|
||||
{
|
||||
BEGIN_All_WALK(FloatRegisters::Total);
|
||||
FOR_ALL_REGISTERS(FloatRegister, reg) {
|
||||
AllocatableFloatRegisterSet pool;
|
||||
pool.add(reg);
|
||||
|
||||
uint32_t alias_bits = 0;
|
||||
for (uint32_t i = 0; i < reg.numAlignedAliased(); i++) {
|
||||
FloatRegister alias;
|
||||
reg.alignedAliased(i, &alias);
|
||||
|
||||
if (alias.isSingle()) {
|
||||
if (alias_bits <= 32)
|
||||
alias_bits = 32;
|
||||
} else if (alias.isDouble()) {
|
||||
if (alias_bits <= 64)
|
||||
alias_bits = 64;
|
||||
} else if (alias.isSimd128()) {
|
||||
if (alias_bits <= 128)
|
||||
alias_bits = 128;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t max_bits = 0;
|
||||
pullAllFpus(pool, max_bits, 0);
|
||||
|
||||
// By adding one register, we expect that we should not be able to pull
|
||||
// more than any of its aligned aliases. This rule should hold for both
|
||||
// x64 and ARM.
|
||||
CHECK(max_bits <= alias_bits);
|
||||
|
||||
// We added one register, we expect to be able to pull it back.
|
||||
CHECK(max_bits > 0);
|
||||
} END_FOR_ALL_REGISTERS;
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testJitRegisterSet_FPU_Aliases)
|
||||
|
|
|
@ -206,6 +206,18 @@ static const Register ScratchRegARM = CallTempReg2;
|
|||
# define FLOAT_TO_I64_CALLOUT
|
||||
#endif
|
||||
|
||||
template<MIRType t>
|
||||
struct RegTypeOf {
|
||||
static_assert(t == MIRType::Float32 || t == MIRType::Double, "Float mask type");
|
||||
};
|
||||
|
||||
template<> struct RegTypeOf<MIRType::Float32> {
|
||||
static constexpr RegTypeName value = RegTypeName::Float32;
|
||||
};
|
||||
template<> struct RegTypeOf<MIRType::Double> {
|
||||
static constexpr RegTypeName value = RegTypeName::Float64;
|
||||
};
|
||||
|
||||
class BaseCompiler
|
||||
{
|
||||
// We define our own ScratchRegister abstractions, deferring to
|
||||
|
@ -790,17 +802,9 @@ class BaseCompiler
|
|||
// allocated then d0 is not allocatable; if s0 and s1 are freed
|
||||
// individually then d0 becomes allocatable.
|
||||
|
||||
template<MIRType t>
|
||||
FloatRegisters::SetType maskFromTypeFPU() {
|
||||
static_assert(t == MIRType::Float32 || t == MIRType::Double, "Float mask type");
|
||||
if (t == MIRType::Float32)
|
||||
return FloatRegisters::AllSingleMask;
|
||||
return FloatRegisters::AllDoubleMask;
|
||||
}
|
||||
|
||||
template<MIRType t>
|
||||
bool hasFPU() {
|
||||
return !!(availFPU_.bits() & maskFromTypeFPU<t>());
|
||||
return availFPU_.hasAny<RegTypeOf<t>::value>();
|
||||
}
|
||||
|
||||
bool isAvailable(FloatRegister r) {
|
||||
|
@ -814,12 +818,7 @@ class BaseCompiler
|
|||
|
||||
template<MIRType t>
|
||||
FloatRegister allocFPU() {
|
||||
MOZ_ASSERT(hasFPU<t>());
|
||||
FloatRegister r =
|
||||
FloatRegisterSet::Intersect(FloatRegisterSet(availFPU_.bits()),
|
||||
FloatRegisterSet(maskFromTypeFPU<t>())).getAny();
|
||||
availFPU_.take(r);
|
||||
return r;
|
||||
return availFPU_.takeAny<RegTypeOf<t>::value>();
|
||||
}
|
||||
|
||||
void freeFPU(FloatRegister r) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче