Bug 1321521 - RegisterSets: Add a register type to getAny and add the equivalent hasAny function. r=lth

This commit is contained in:
Nicolas B. Pierron 2017-01-16 12:47:34 +00:00
Родитель 25012b4646
Коммит 9e0785daca
12 изменённых файлов: 459 добавлений и 68 удалений

Просмотреть файл

@ -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) {