Commit a933b704 authored by bbudge's avatar bbudge Committed by Commit bot

[Turbofan] Add the concept of aliasing to RegisterConfiguration.

- Adds the concept of FP register aliasing to RegisterConfiguration.
- Changes RegisterAllocator to distinguish between FP representations
when allocating.
- Changes LinearScanAllocator to detect interference when FP register
aliasing is combining, as on ARM.
- Changes ARM code generation to allow all registers s0 - s31 to be
accessed.
- Adds unit tests for RegisterConfiguration, mostly to test aliasing
calculations.

LOG=N
BUG=v8:4124

Review-Url: https://codereview.chromium.org/2086653003
Cr-Commit-Position: refs/heads/master@{#37251}
parent f0a03f0b
...@@ -36,7 +36,9 @@ class ArmOperandConverter final : public InstructionOperandConverter { ...@@ -36,7 +36,9 @@ class ArmOperandConverter final : public InstructionOperandConverter {
} }
SwVfpRegister ToFloat32Register(InstructionOperand* op) { SwVfpRegister ToFloat32Register(InstructionOperand* op) {
return ToFloat64Register(op).low(); DCHECK(LocationOperand::cast(op)->representation() ==
MachineRepresentation::kFloat32);
return LocationOperand::cast(op)->GetFloatRegister();
} }
LowDwVfpRegister OutputFloat64Register(size_t index = 0) { LowDwVfpRegister OutputFloat64Register(size_t index = 0) {
......
...@@ -61,8 +61,16 @@ FlagsCondition CommuteFlagsCondition(FlagsCondition condition) { ...@@ -61,8 +61,16 @@ FlagsCondition CommuteFlagsCondition(FlagsCondition condition) {
bool InstructionOperand::InterferesWith(const InstructionOperand& that) const { bool InstructionOperand::InterferesWith(const InstructionOperand& that) const {
if (!IsFPRegister() || !that.IsFPRegister()) return EqualsCanonicalized(that); if (!IsFPRegister() || !that.IsFPRegister()) return EqualsCanonicalized(that);
return LocationOperand::cast(this)->register_code() ==
LocationOperand::cast(that).register_code(); const LocationOperand& loc1 = *LocationOperand::cast(this);
const LocationOperand& loc2 = LocationOperand::cast(that);
const RegisterConfiguration* config =
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN);
if (config->fp_aliasing_kind() != RegisterConfiguration::COMBINE)
return loc1.register_code() == loc2.register_code();
return config->AreAliases(loc1.representation(), loc1.register_code(),
loc2.representation(), loc2.register_code());
} }
void InstructionOperand::Print(const RegisterConfiguration* config) const { void InstructionOperand::Print(const RegisterConfiguration* config) const {
......
...@@ -28,20 +28,34 @@ typedef ZoneMap<MoveKey, unsigned, MoveKeyCompare> MoveMap; ...@@ -28,20 +28,34 @@ typedef ZoneMap<MoveKey, unsigned, MoveKeyCompare> MoveMap;
typedef ZoneSet<InstructionOperand, CompareOperandModuloType> OperandSet; typedef ZoneSet<InstructionOperand, CompareOperandModuloType> OperandSet;
bool Blocks(const OperandSet& set, const InstructionOperand& operand) { bool Blocks(const OperandSet& set, const InstructionOperand& operand) {
if (!operand.IsFPRegister()) return set.find(operand) != set.end(); if (set.find(operand) != set.end()) return true;
// Only FP registers alias.
if (!operand.IsFPRegister()) return false;
const LocationOperand& loc = LocationOperand::cast(operand); const LocationOperand& loc = LocationOperand::cast(operand);
if (loc.representation() == MachineRepresentation::kFloat64) { MachineRepresentation rep = loc.representation();
return set.find(operand) != set.end() || MachineRepresentation other_fp_rep = rep == MachineRepresentation::kFloat64
set.find(LocationOperand(loc.kind(), loc.location_kind(), ? MachineRepresentation::kFloat32
MachineRepresentation::kFloat32, : MachineRepresentation::kFloat64;
loc.register_code())) != set.end(); const RegisterConfiguration* config =
} RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN);
DCHECK_EQ(MachineRepresentation::kFloat32, loc.representation()); if (config->fp_aliasing_kind() != RegisterConfiguration::COMBINE) {
return set.find(operand) != set.end() || // Overlap aliasing case.
set.find(LocationOperand(loc.kind(), loc.location_kind(), return set.find(LocationOperand(loc.kind(), loc.location_kind(),
MachineRepresentation::kFloat64, other_fp_rep, loc.register_code())) !=
loc.register_code())) != set.end(); set.end();
}
// Combine aliasing case.
int alias_base_index = -1;
int aliases = config->GetAliases(rep, loc.register_code(), other_fp_rep,
&alias_base_index);
while (aliases--) {
int aliased_reg = alias_base_index + aliases;
if (set.find(LocationOperand(loc.kind(), loc.location_kind(), other_fp_rep,
aliased_reg)) != set.end())
return true;
}
return false;
} }
int FindFirstNonEmptySlot(const Instruction* instr) { int FindFirstNonEmptySlot(const Instruction* instr) {
......
...@@ -89,7 +89,7 @@ class PendingAssessment final : public Assessment { ...@@ -89,7 +89,7 @@ class PendingAssessment final : public Assessment {
DISALLOW_COPY_AND_ASSIGN(PendingAssessment); DISALLOW_COPY_AND_ASSIGN(PendingAssessment);
}; };
// FinalAssessmens are associated to operands that we know to be a certain // FinalAssessments are associated to operands that we know to be a certain
// virtual register. // virtual register.
class FinalAssessment final : public Assessment { class FinalAssessment final : public Assessment {
public: public:
......
This diff is collapsed.
...@@ -678,8 +678,7 @@ class SpillRange final : public ZoneObject { ...@@ -678,8 +678,7 @@ class SpillRange final : public ZoneObject {
SpillRange(TopLevelLiveRange* range, Zone* zone); SpillRange(TopLevelLiveRange* range, Zone* zone);
UseInterval* interval() const { return use_interval_; } UseInterval* interval() const { return use_interval_; }
// Currently, only 4 or 8 byte slots are supported.
int ByteWidth() const;
bool IsEmpty() const { return live_ranges_.empty(); } bool IsEmpty() const { return live_ranges_.empty(); }
bool TryMerge(SpillRange* other); bool TryMerge(SpillRange* other);
bool HasSlot() const { return assigned_slot_ != kUnassignedSlot; } bool HasSlot() const { return assigned_slot_ != kUnassignedSlot; }
...@@ -768,6 +767,12 @@ class RegisterAllocationData final : public ZoneObject { ...@@ -768,6 +767,12 @@ class RegisterAllocationData final : public ZoneObject {
ZoneVector<TopLevelLiveRange*>& fixed_live_ranges() { ZoneVector<TopLevelLiveRange*>& fixed_live_ranges() {
return fixed_live_ranges_; return fixed_live_ranges_;
} }
ZoneVector<TopLevelLiveRange*>& fixed_float_live_ranges() {
return fixed_float_live_ranges_;
}
const ZoneVector<TopLevelLiveRange*>& fixed_float_live_ranges() const {
return fixed_float_live_ranges_;
}
ZoneVector<TopLevelLiveRange*>& fixed_double_live_ranges() { ZoneVector<TopLevelLiveRange*>& fixed_double_live_ranges() {
return fixed_double_live_ranges_; return fixed_double_live_ranges_;
} }
...@@ -779,7 +784,7 @@ class RegisterAllocationData final : public ZoneObject { ...@@ -779,7 +784,7 @@ class RegisterAllocationData final : public ZoneObject {
ZoneVector<SpillRange*>& spill_ranges() { return spill_ranges_; } ZoneVector<SpillRange*>& spill_ranges() { return spill_ranges_; }
DelayedReferences& delayed_references() { return delayed_references_; } DelayedReferences& delayed_references() { return delayed_references_; }
InstructionSequence* code() const { return code_; } InstructionSequence* code() const { return code_; }
// This zone is for datastructures only needed during register allocation // This zone is for data structures only needed during register allocation
// phases. // phases.
Zone* allocation_zone() const { return allocation_zone_; } Zone* allocation_zone() const { return allocation_zone_; }
// This zone is for InstructionOperands and moves that live beyond register // This zone is for InstructionOperands and moves that live beyond register
...@@ -810,7 +815,7 @@ class RegisterAllocationData final : public ZoneObject { ...@@ -810,7 +815,7 @@ class RegisterAllocationData final : public ZoneObject {
bool ExistsUseWithoutDefinition(); bool ExistsUseWithoutDefinition();
bool RangesDefinedInDeferredStayInDeferred(); bool RangesDefinedInDeferredStayInDeferred();
void MarkAllocated(RegisterKind kind, int index); void MarkAllocated(MachineRepresentation rep, int index);
PhiMapValue* InitializePhiMap(const InstructionBlock* block, PhiMapValue* InitializePhiMap(const InstructionBlock* block,
PhiInstruction* phi); PhiInstruction* phi);
...@@ -835,6 +840,7 @@ class RegisterAllocationData final : public ZoneObject { ...@@ -835,6 +840,7 @@ class RegisterAllocationData final : public ZoneObject {
ZoneVector<BitVector*> live_out_sets_; ZoneVector<BitVector*> live_out_sets_;
ZoneVector<TopLevelLiveRange*> live_ranges_; ZoneVector<TopLevelLiveRange*> live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_live_ranges_; ZoneVector<TopLevelLiveRange*> fixed_live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_float_live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_double_live_ranges_; ZoneVector<TopLevelLiveRange*> fixed_double_live_ranges_;
ZoneVector<SpillRange*> spill_ranges_; ZoneVector<SpillRange*> spill_ranges_;
DelayedReferences delayed_references_; DelayedReferences delayed_references_;
...@@ -911,9 +917,9 @@ class LiveRangeBuilder final : public ZoneObject { ...@@ -911,9 +917,9 @@ class LiveRangeBuilder final : public ZoneObject {
void ProcessLoopHeader(const InstructionBlock* block, BitVector* live); void ProcessLoopHeader(const InstructionBlock* block, BitVector* live);
static int FixedLiveRangeID(int index) { return -index - 1; } static int FixedLiveRangeID(int index) { return -index - 1; }
int FixedDoubleLiveRangeID(int index); int FixedFPLiveRangeID(int index, MachineRepresentation rep);
TopLevelLiveRange* FixedLiveRangeFor(int index); TopLevelLiveRange* FixedLiveRangeFor(int index);
TopLevelLiveRange* FixedDoubleLiveRangeFor(int index); TopLevelLiveRange* FixedFPLiveRangeFor(int index, MachineRepresentation rep);
void MapPhiHint(InstructionOperand* operand, UsePosition* use_pos); void MapPhiHint(InstructionOperand* operand, UsePosition* use_pos);
void ResolvePhiHint(InstructionOperand* operand, UsePosition* use_pos); void ResolvePhiHint(InstructionOperand* operand, UsePosition* use_pos);
...@@ -947,7 +953,7 @@ class LiveRangeBuilder final : public ZoneObject { ...@@ -947,7 +953,7 @@ class LiveRangeBuilder final : public ZoneObject {
class RegisterAllocator : public ZoneObject { class RegisterAllocator : public ZoneObject {
public: public:
explicit RegisterAllocator(RegisterAllocationData* data, RegisterKind kind); RegisterAllocator(RegisterAllocationData* data, RegisterKind kind);
protected: protected:
RegisterAllocationData* data() const { return data_; } RegisterAllocationData* data() const { return data_; }
...@@ -955,9 +961,14 @@ class RegisterAllocator : public ZoneObject { ...@@ -955,9 +961,14 @@ class RegisterAllocator : public ZoneObject {
RegisterKind mode() const { return mode_; } RegisterKind mode() const { return mode_; }
int num_registers() const { return num_registers_; } int num_registers() const { return num_registers_; }
int num_allocatable_registers() const { return num_allocatable_registers_; } int num_allocatable_registers() const { return num_allocatable_registers_; }
int allocatable_register_code(int allocatable_index) const { const int* allocatable_register_codes() const {
return allocatable_register_codes_[allocatable_index]; return allocatable_register_codes_;
} }
// Returns true if registers do not combine to form larger registers, i.e.
// no complex aliasing detection is required. This is always true for the
// general register pass, and true for the FP register pass except for arm
// and mips archs.
bool no_combining() const { return no_combining_; }
// TODO(mtrofin): explain why splitting in gap START is always OK. // TODO(mtrofin): explain why splitting in gap START is always OK.
LifetimePosition GetSplitPositionForInstruction(const LiveRange* range, LifetimePosition GetSplitPositionForInstruction(const LiveRange* range,
...@@ -1009,6 +1020,9 @@ class RegisterAllocator : public ZoneObject { ...@@ -1009,6 +1020,9 @@ class RegisterAllocator : public ZoneObject {
int num_allocatable_registers_; int num_allocatable_registers_;
const int* allocatable_register_codes_; const int* allocatable_register_codes_;
private:
bool no_combining_;
DISALLOW_COPY_AND_ASSIGN(RegisterAllocator); DISALLOW_COPY_AND_ASSIGN(RegisterAllocator);
}; };
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/assembler.h" #include "src/assembler.h"
#include "src/macro-assembler.h" #include "src/macro-assembler.h"
#include "src/register-configuration.h"
#include "src/wasm/wasm-module.h" #include "src/wasm/wasm-module.h"
...@@ -178,7 +179,18 @@ struct Allocator { ...@@ -178,7 +179,18 @@ struct Allocator {
if (IsFloatingPoint(type)) { if (IsFloatingPoint(type)) {
// Allocate a floating point register/stack location. // Allocate a floating point register/stack location.
if (fp_offset < fp_count) { if (fp_offset < fp_count) {
return regloc(fp_regs[fp_offset++]); DoubleRegister reg = fp_regs[fp_offset++];
#if V8_TARGET_ARCH_ARM
// Allocate floats using a double register, but modify the code to
// reflect how ARM FP registers alias.
// TODO(bbudge) Modify wasm linkage to allow use of all float regs.
if (type == kAstF32) {
int float_reg_code = reg.code() * 2;
DCHECK(float_reg_code < RegisterConfiguration::kMaxFPRegisters);
return regloc(DoubleRegister::from_code(float_reg_code));
}
#endif
return regloc(reg);
} else { } else {
int offset = -1 - stack_offset; int offset = -1 - stack_offset;
stack_offset += Words(type); stack_offset += Words(type);
......
...@@ -57,16 +57,14 @@ class ArchDefaultRegisterConfiguration : public RegisterConfiguration { ...@@ -57,16 +57,14 @@ class ArchDefaultRegisterConfiguration : public RegisterConfiguration {
Register::kNumRegisters, DoubleRegister::kMaxNumRegisters, Register::kNumRegisters, DoubleRegister::kMaxNumRegisters,
#if V8_TARGET_ARCH_IA32 #if V8_TARGET_ARCH_IA32
kMaxAllocatableGeneralRegisterCount, kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount, kMaxAllocatableDoubleRegisterCount, AliasingKind::OVERLAP,
kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_X87 #elif V8_TARGET_ARCH_X87
kMaxAllocatableGeneralRegisterCount, kMaxAllocatableGeneralRegisterCount,
compiler == TURBOFAN ? 1 : kMaxAllocatableDoubleRegisterCount, compiler == TURBOFAN ? 1 : kMaxAllocatableDoubleRegisterCount,
compiler == TURBOFAN ? 1 : kMaxAllocatableDoubleRegisterCount, AliasingKind::OVERLAP,
#elif V8_TARGET_ARCH_X64 #elif V8_TARGET_ARCH_X64
kMaxAllocatableGeneralRegisterCount, kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount, kMaxAllocatableDoubleRegisterCount, AliasingKind::OVERLAP,
kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_ARM #elif V8_TARGET_ARCH_ARM
FLAG_enable_embedded_constant_pool FLAG_enable_embedded_constant_pool
? (kMaxAllocatableGeneralRegisterCount - 1) ? (kMaxAllocatableGeneralRegisterCount - 1)
...@@ -74,27 +72,22 @@ class ArchDefaultRegisterConfiguration : public RegisterConfiguration { ...@@ -74,27 +72,22 @@ class ArchDefaultRegisterConfiguration : public RegisterConfiguration {
CpuFeatures::IsSupported(VFP32DREGS) CpuFeatures::IsSupported(VFP32DREGS)
? kMaxAllocatableDoubleRegisterCount ? kMaxAllocatableDoubleRegisterCount
: (ALLOCATABLE_NO_VFP32_DOUBLE_REGISTERS(REGISTER_COUNT) 0), : (ALLOCATABLE_NO_VFP32_DOUBLE_REGISTERS(REGISTER_COUNT) 0),
ALLOCATABLE_NO_VFP32_DOUBLE_REGISTERS(REGISTER_COUNT) 0, AliasingKind::COMBINE,
#elif V8_TARGET_ARCH_ARM64 #elif V8_TARGET_ARCH_ARM64
kMaxAllocatableGeneralRegisterCount, kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount, kMaxAllocatableDoubleRegisterCount, AliasingKind::OVERLAP,
kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_MIPS #elif V8_TARGET_ARCH_MIPS
kMaxAllocatableGeneralRegisterCount, kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount, kMaxAllocatableDoubleRegisterCount, AliasingKind::OVERLAP,
kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_MIPS64 #elif V8_TARGET_ARCH_MIPS64
kMaxAllocatableGeneralRegisterCount, kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount, kMaxAllocatableDoubleRegisterCount, AliasingKind::OVERLAP,
kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_PPC #elif V8_TARGET_ARCH_PPC
kMaxAllocatableGeneralRegisterCount, kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount, kMaxAllocatableDoubleRegisterCount, AliasingKind::OVERLAP,
kMaxAllocatableDoubleRegisterCount,
#elif V8_TARGET_ARCH_S390 #elif V8_TARGET_ARCH_S390
kMaxAllocatableGeneralRegisterCount, kMaxAllocatableGeneralRegisterCount,
kMaxAllocatableDoubleRegisterCount, kMaxAllocatableDoubleRegisterCount, AliasingKind::OVERLAP,
kMaxAllocatableDoubleRegisterCount,
#else #else
#error Unsupported target architecture. #error Unsupported target architecture.
#endif #endif
...@@ -135,17 +128,18 @@ const RegisterConfiguration* RegisterConfiguration::ArchDefault( ...@@ -135,17 +128,18 @@ const RegisterConfiguration* RegisterConfiguration::ArchDefault(
RegisterConfiguration::RegisterConfiguration( RegisterConfiguration::RegisterConfiguration(
int num_general_registers, int num_double_registers, int num_general_registers, int num_double_registers,
int num_allocatable_general_registers, int num_allocatable_double_registers, int num_allocatable_general_registers, int num_allocatable_double_registers,
int num_allocatable_aliased_double_registers, AliasingKind fp_aliasing_kind, const int* allocatable_general_codes,
const int* allocatable_general_codes, const int* allocatable_double_codes, const int* allocatable_double_codes,
const char* const* general_register_names, const char* const* general_register_names,
const char* const* float_register_names, const char* const* float_register_names,
const char* const* double_register_names) const char* const* double_register_names)
: num_general_registers_(num_general_registers), : num_general_registers_(num_general_registers),
num_float_registers_(0),
num_double_registers_(num_double_registers), num_double_registers_(num_double_registers),
num_allocatable_general_registers_(num_allocatable_general_registers), num_allocatable_general_registers_(num_allocatable_general_registers),
num_allocatable_double_registers_(num_allocatable_double_registers), num_allocatable_double_registers_(num_allocatable_double_registers),
num_allocatable_aliased_double_registers_( num_allocatable_float_registers_(0),
num_allocatable_aliased_double_registers), fp_aliasing_kind_(fp_aliasing_kind),
allocatable_general_codes_mask_(0), allocatable_general_codes_mask_(0),
allocatable_double_codes_mask_(0), allocatable_double_codes_mask_(0),
allocatable_general_codes_(allocatable_general_codes), allocatable_general_codes_(allocatable_general_codes),
...@@ -161,6 +155,79 @@ RegisterConfiguration::RegisterConfiguration( ...@@ -161,6 +155,79 @@ RegisterConfiguration::RegisterConfiguration(
for (int i = 0; i < num_allocatable_double_registers_; ++i) { for (int i = 0; i < num_allocatable_double_registers_; ++i) {
allocatable_double_codes_mask_ |= (1 << allocatable_double_codes_[i]); allocatable_double_codes_mask_ |= (1 << allocatable_double_codes_[i]);
} }
if (fp_aliasing_kind_ == COMBINE) {
num_float_registers_ = num_double_registers_ * 2 <= kMaxFPRegisters
? num_double_registers_ * 2
: kMaxFPRegisters;
num_allocatable_float_registers_ = 0;
for (int i = 0; i < num_allocatable_double_registers_; i++) {
int base_code = allocatable_double_codes_[i] * 2;
if (base_code >= kMaxFPRegisters) continue;
allocatable_float_codes_[num_allocatable_float_registers_++] = base_code;
allocatable_float_codes_[num_allocatable_float_registers_++] =
base_code + 1;
}
} else {
DCHECK(fp_aliasing_kind_ == OVERLAP);
num_float_registers_ = num_double_registers_;
num_allocatable_float_registers_ = num_allocatable_double_registers_;
for (int i = 0; i < num_allocatable_float_registers_; ++i) {
allocatable_float_codes_[i] = allocatable_double_codes_[i];
}
}
}
int RegisterConfiguration::GetAliases(MachineRepresentation rep, int index,
MachineRepresentation other_rep,
int* alias_base_index) const {
DCHECK(fp_aliasing_kind_ == COMBINE);
DCHECK(rep == MachineRepresentation::kFloat32 ||
rep == MachineRepresentation::kFloat64);
DCHECK(other_rep == MachineRepresentation::kFloat32 ||
other_rep == MachineRepresentation::kFloat64);
if (rep == other_rep) {
*alias_base_index = index;
return 1;
}
if (rep == MachineRepresentation::kFloat32) {
DCHECK(other_rep == MachineRepresentation::kFloat64);
DCHECK(index < num_allocatable_float_registers_);
*alias_base_index = index / 2;
return 1;
}
DCHECK(rep == MachineRepresentation::kFloat64);
DCHECK(other_rep == MachineRepresentation::kFloat32);
if (index * 2 >= kMaxFPRegisters) {
// Alias indices are out of float register range.
return 0;
}
*alias_base_index = index * 2;
return 2;
}
bool RegisterConfiguration::AreAliases(MachineRepresentation rep, int index,
MachineRepresentation other_rep,
int other_index) const {
DCHECK(fp_aliasing_kind_ == COMBINE);
DCHECK(rep == MachineRepresentation::kFloat32 ||
rep == MachineRepresentation::kFloat64);
DCHECK(other_rep == MachineRepresentation::kFloat32 ||
other_rep == MachineRepresentation::kFloat64);
if (rep == other_rep) {
return index == other_index;
}
if (rep == MachineRepresentation::kFloat32) {
DCHECK(other_rep == MachineRepresentation::kFloat64);
return index / 2 == other_index;
}
DCHECK(rep == MachineRepresentation::kFloat64);
DCHECK(other_rep == MachineRepresentation::kFloat32);
if (index * 2 >= kMaxFPRegisters) {
// Alias indices are out of float register range.
return false;
}
return index == other_index / 2;
} }
#undef REGISTER_COUNT #undef REGISTER_COUNT
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define V8_COMPILER_REGISTER_CONFIGURATION_H_ #define V8_COMPILER_REGISTER_CONFIGURATION_H_
#include "src/base/macros.h" #include "src/base/macros.h"
#include "src/machine-type.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -21,6 +22,13 @@ class RegisterConfiguration { ...@@ -21,6 +22,13 @@ class RegisterConfiguration {
// until x87 TF supports all of the registers that Crankshaft does. // until x87 TF supports all of the registers that Crankshaft does.
enum CompilerSelector { CRANKSHAFT, TURBOFAN }; enum CompilerSelector { CRANKSHAFT, TURBOFAN };
enum AliasingKind {
// Registers alias a single register of every other size (e.g. Intel).
OVERLAP,
// Registers alias two registers of the next smaller size (e.g. ARM).
COMBINE
};
// Architecture independent maxes. // Architecture independent maxes.
static const int kMaxGeneralRegisters = 32; static const int kMaxGeneralRegisters = 32;
static const int kMaxFPRegisters = 32; static const int kMaxFPRegisters = 32;
...@@ -30,7 +38,7 @@ class RegisterConfiguration { ...@@ -30,7 +38,7 @@ class RegisterConfiguration {
RegisterConfiguration(int num_general_registers, int num_double_registers, RegisterConfiguration(int num_general_registers, int num_double_registers,
int num_allocatable_general_registers, int num_allocatable_general_registers,
int num_allocatable_double_registers, int num_allocatable_double_registers,
int num_allocatable_aliased_double_registers, AliasingKind fp_aliasing_kind,
const int* allocatable_general_codes, const int* allocatable_general_codes,
const int* allocatable_double_codes, const int* allocatable_double_codes,
char const* const* general_names, char const* const* general_names,
...@@ -38,6 +46,7 @@ class RegisterConfiguration { ...@@ -38,6 +46,7 @@ class RegisterConfiguration {
char const* const* double_names); char const* const* double_names);
int num_general_registers() const { return num_general_registers_; } int num_general_registers() const { return num_general_registers_; }
int num_float_registers() const { return num_float_registers_; }
int num_double_registers() const { return num_double_registers_; } int num_double_registers() const { return num_double_registers_; }
int num_allocatable_general_registers() const { int num_allocatable_general_registers() const {
return num_allocatable_general_registers_; return num_allocatable_general_registers_;
...@@ -45,12 +54,10 @@ class RegisterConfiguration { ...@@ -45,12 +54,10 @@ class RegisterConfiguration {
int num_allocatable_double_registers() const { int num_allocatable_double_registers() const {
return num_allocatable_double_registers_; return num_allocatable_double_registers_;
} }
// TODO(turbofan): This is a temporary work-around required because our int num_allocatable_float_registers() const {
// register allocator does not yet support the aliasing of single/double return num_allocatable_float_registers_;
// registers on ARM.
int num_allocatable_aliased_double_registers() const {
return num_allocatable_aliased_double_registers_;
} }
AliasingKind fp_aliasing_kind() const { return fp_aliasing_kind_; }
int32_t allocatable_general_codes_mask() const { int32_t allocatable_general_codes_mask() const {
return allocatable_general_codes_mask_; return allocatable_general_codes_mask_;
} }
...@@ -63,6 +70,9 @@ class RegisterConfiguration { ...@@ -63,6 +70,9 @@ class RegisterConfiguration {
int GetAllocatableDoubleCode(int index) const { int GetAllocatableDoubleCode(int index) const {
return allocatable_double_codes_[index]; return allocatable_double_codes_[index];
} }
int GetAllocatableFloatCode(int index) const {
return allocatable_float_codes_[index];
}
const char* GetGeneralRegisterName(int code) const { const char* GetGeneralRegisterName(int code) const {
return general_register_names_[code]; return general_register_names_[code];
} }
...@@ -78,17 +88,35 @@ class RegisterConfiguration { ...@@ -78,17 +88,35 @@ class RegisterConfiguration {
const int* allocatable_double_codes() const { const int* allocatable_double_codes() const {
return allocatable_double_codes_; return allocatable_double_codes_;
} }
const int* allocatable_float_codes() const {
return allocatable_float_codes_;
}
// Aliasing calculations for floating point registers, when fp_aliasing_kind()
// is COMBINE. Currently only implemented for kFloat32, or kFloat64 reps.
// Returns the number of aliases, and if > 0, alias_base_index is set to the
// index of the first alias.
int GetAliases(MachineRepresentation rep, int index,
MachineRepresentation other_rep, int* alias_base_index) const;
// Returns a value indicating whether two registers alias each other, when
// fp_aliasing_kind() is COMBINE. Currently only implemented for kFloat32, or
// kFloat64 reps.
bool AreAliases(MachineRepresentation rep, int index,
MachineRepresentation other_rep, int other_index) const;
private: private:
const int num_general_registers_; const int num_general_registers_;
int num_float_registers_;
const int num_double_registers_; const int num_double_registers_;
int num_allocatable_general_registers_; int num_allocatable_general_registers_;
int num_allocatable_double_registers_; int num_allocatable_double_registers_;
int num_allocatable_aliased_double_registers_; int num_allocatable_float_registers_;
AliasingKind fp_aliasing_kind_;
int32_t allocatable_general_codes_mask_; int32_t allocatable_general_codes_mask_;
int32_t allocatable_double_codes_mask_; int32_t allocatable_double_codes_mask_;
const int* allocatable_general_codes_; const int* allocatable_general_codes_;
const int* allocatable_double_codes_; const int* allocatable_double_codes_;
int allocatable_float_codes_[kMaxFPRegisters];
char const* const* general_register_names_; char const* const* general_register_names_;
char const* const* float_register_names_; char const* const* float_register_names_;
char const* const* double_register_names_; char const* const* double_register_names_;
......
...@@ -91,10 +91,19 @@ class Float32RegisterPairs : public Pairs { ...@@ -91,10 +91,19 @@ class Float32RegisterPairs : public Pairs {
Float32RegisterPairs() Float32RegisterPairs()
: Pairs( : Pairs(
100, 100,
#if V8_TARGET_ARCH_ARM
// TODO(bbudge) Modify wasm linkage to allow use of all float regs.
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN)
->num_allocatable_aliased_double_registers(), ->num_allocatable_double_registers() /
2 -
2,
#else
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN)
->allocatable_double_codes()) {} ->num_allocatable_double_registers(),
#endif
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN)
->allocatable_double_codes()) {
}
}; };
...@@ -105,7 +114,7 @@ class Float64RegisterPairs : public Pairs { ...@@ -105,7 +114,7 @@ class Float64RegisterPairs : public Pairs {
: Pairs( : Pairs(
100, 100,
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN)
->num_allocatable_aliased_double_registers(), ->num_allocatable_double_registers(),
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN)
->allocatable_double_codes()) {} ->allocatable_double_codes()) {}
}; };
...@@ -136,7 +145,12 @@ struct Allocator { ...@@ -136,7 +145,12 @@ struct Allocator {
if (IsFloatingPoint(type.representation())) { if (IsFloatingPoint(type.representation())) {
// Allocate a floating point register/stack location. // Allocate a floating point register/stack location.
if (fp_offset < fp_count) { if (fp_offset < fp_count) {
return LinkageLocation::ForRegister(fp_regs[fp_offset++]); int code = fp_regs[fp_offset++];
#if V8_TARGET_ARCH_ARM
// TODO(bbudge) Modify wasm linkage to allow use of all float regs.
if (type.representation() == MachineRepresentation::kFloat32) code *= 2;
#endif
return LinkageLocation::ForRegister(code);
} else { } else {
int offset = -1 - stack_offset; int offset = -1 - stack_offset;
stack_offset += StackWords(type); stack_offset += StackWords(type);
......
...@@ -67,8 +67,8 @@ RegisterConfiguration* InstructionSequenceTest::config() { ...@@ -67,8 +67,8 @@ RegisterConfiguration* InstructionSequenceTest::config() {
if (config_.is_empty()) { if (config_.is_empty()) {
config_.Reset(new RegisterConfiguration( config_.Reset(new RegisterConfiguration(
num_general_registers_, num_double_registers_, num_general_registers_, num_general_registers_, num_double_registers_, num_general_registers_,
num_double_registers_, num_double_registers_, allocatable_codes, num_double_registers_, RegisterConfiguration::OVERLAP,
allocatable_double_codes, general_register_names_, allocatable_codes, allocatable_double_codes, general_register_names_,
double_register_names_, // float register names double_register_names_, // float register names
double_register_names_)); double_register_names_));
} }
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/register-configuration.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
const MachineRepresentation kFloat32 = MachineRepresentation::kFloat32;
const MachineRepresentation kFloat64 = MachineRepresentation::kFloat64;
class RegisterConfigurationUnitTest : public ::testing::Test {
public:
RegisterConfigurationUnitTest() {}
virtual ~RegisterConfigurationUnitTest() {}
private:
};
TEST_F(RegisterConfigurationUnitTest, BasicProperties) {
const int kNumGeneralRegs = 3;
const int kNumDoubleRegs = 4;
const int kNumAllocatableGeneralRegs = 2;
const int kNumAllocatableDoubleRegs = 2;
int general_codes[kNumAllocatableGeneralRegs] = {1, 2};
int double_codes[kNumAllocatableDoubleRegs] = {2, 3};
RegisterConfiguration test(
kNumGeneralRegs, kNumDoubleRegs, kNumAllocatableGeneralRegs,
kNumAllocatableDoubleRegs, RegisterConfiguration::OVERLAP, general_codes,
double_codes, nullptr, nullptr, nullptr);
EXPECT_EQ(test.num_general_registers(), kNumGeneralRegs);
EXPECT_EQ(test.num_double_registers(), kNumDoubleRegs);
EXPECT_EQ(test.num_allocatable_general_registers(),
kNumAllocatableGeneralRegs);
EXPECT_EQ(test.num_allocatable_double_registers(), kNumAllocatableDoubleRegs);
EXPECT_EQ(test.num_allocatable_float_registers(), kNumAllocatableDoubleRegs);
EXPECT_EQ(test.allocatable_general_codes_mask(),
(1 << general_codes[0]) | (1 << general_codes[1]));
EXPECT_EQ(test.GetAllocatableGeneralCode(0), general_codes[0]);
EXPECT_EQ(test.GetAllocatableGeneralCode(1), general_codes[1]);
EXPECT_EQ(test.allocatable_double_codes_mask(),
(1 << double_codes[0]) | (1 << double_codes[1]));
EXPECT_EQ(test.GetAllocatableDoubleCode(0), double_codes[0]);
EXPECT_EQ(test.GetAllocatableDoubleCode(1), double_codes[1]);
}
TEST_F(RegisterConfigurationUnitTest, Aliasing) {
const int kNumGeneralRegs = 3;
const int kNumDoubleRegs = 4;
const int kNumAllocatableGeneralRegs = 2;
const int kNumAllocatableDoubleRegs = 3;
int general_codes[] = {1, 2};
int double_codes[] = {2, 3, 16}; // reg 16 should not alias registers 32, 33.
RegisterConfiguration test(
kNumGeneralRegs, kNumDoubleRegs, kNumAllocatableGeneralRegs,
kNumAllocatableDoubleRegs, RegisterConfiguration::COMBINE, general_codes,
double_codes, nullptr, nullptr, nullptr);
// There are 3 allocatable double regs, but only 2 can alias float regs.
EXPECT_EQ(test.num_allocatable_float_registers(), 4);
// Test that float registers combine in pairs to form double registers.
EXPECT_EQ(test.GetAllocatableFloatCode(0), double_codes[0] * 2);
EXPECT_EQ(test.GetAllocatableFloatCode(1), double_codes[0] * 2 + 1);
EXPECT_EQ(test.GetAllocatableFloatCode(2), double_codes[1] * 2);
EXPECT_EQ(test.GetAllocatableFloatCode(3), double_codes[1] * 2 + 1);
// Registers alias themselves.
EXPECT_TRUE(test.AreAliases(kFloat32, 0, kFloat32, 0));
EXPECT_TRUE(test.AreAliases(kFloat64, 0, kFloat64, 0));
// Registers don't alias other registers of the same size.
EXPECT_FALSE(test.AreAliases(kFloat32, 1, kFloat32, 0));
EXPECT_FALSE(test.AreAliases(kFloat64, 1, kFloat64, 0));
// Float registers combine in pairs and alias double registers.
EXPECT_TRUE(test.AreAliases(kFloat32, 0, kFloat64, 0));
EXPECT_TRUE(test.AreAliases(kFloat32, 1, kFloat64, 0));
EXPECT_TRUE(test.AreAliases(kFloat64, 0, kFloat32, 0));
EXPECT_TRUE(test.AreAliases(kFloat64, 0, kFloat32, 1));
EXPECT_FALSE(test.AreAliases(kFloat32, 0, kFloat64, 1));
EXPECT_FALSE(test.AreAliases(kFloat32, 1, kFloat64, 1));
EXPECT_TRUE(test.AreAliases(kFloat64, 0, kFloat32, 1));
EXPECT_TRUE(test.AreAliases(kFloat64, 1, kFloat32, 2));
EXPECT_TRUE(test.AreAliases(kFloat64, 1, kFloat32, 3));
EXPECT_TRUE(test.AreAliases(kFloat64, 2, kFloat32, 4));
EXPECT_TRUE(test.AreAliases(kFloat64, 2, kFloat32, 5));
int alias_base_index = -1;
EXPECT_EQ(test.GetAliases(kFloat32, 0, kFloat32, &alias_base_index), 1);
EXPECT_EQ(alias_base_index, 0);
EXPECT_EQ(test.GetAliases(kFloat64, 1, kFloat64, &alias_base_index), 1);
EXPECT_EQ(alias_base_index, 1);
EXPECT_EQ(test.GetAliases(kFloat32, 0, kFloat64, &alias_base_index), 1);
EXPECT_EQ(alias_base_index, 0);
EXPECT_EQ(test.GetAliases(kFloat32, 1, kFloat64, &alias_base_index), 1);
EXPECT_EQ(test.GetAliases(kFloat32, 2, kFloat64, &alias_base_index), 1);
EXPECT_EQ(alias_base_index, 1);
EXPECT_EQ(test.GetAliases(kFloat32, 3, kFloat64, &alias_base_index), 1);
EXPECT_EQ(alias_base_index, 1);
EXPECT_EQ(test.GetAliases(kFloat64, 0, kFloat32, &alias_base_index), 2);
EXPECT_EQ(alias_base_index, 0);
EXPECT_EQ(test.GetAliases(kFloat64, 1, kFloat32, &alias_base_index), 2);
EXPECT_EQ(alias_base_index, 2);
// Non-allocatable codes still alias.
EXPECT_EQ(test.GetAliases(kFloat64, 2, kFloat32, &alias_base_index), 2);
EXPECT_EQ(alias_base_index, 4);
// High numbered double registers don't alias nonexistent single registers.
EXPECT_EQ(
test.GetAliases(kFloat64, RegisterConfiguration::kMaxFPRegisters / 2,
kFloat32, &alias_base_index),
0);
EXPECT_EQ(
test.GetAliases(kFloat64, RegisterConfiguration::kMaxFPRegisters / 2 + 1,
kFloat32, &alias_base_index),
0);
EXPECT_EQ(test.GetAliases(kFloat64, RegisterConfiguration::kMaxFPRegisters,
kFloat32, &alias_base_index),
0);
}
} // namespace internal
} // namespace v8
...@@ -120,6 +120,7 @@ ...@@ -120,6 +120,7 @@
'heap/scavenge-job-unittest.cc', 'heap/scavenge-job-unittest.cc',
'heap/slot-set-unittest.cc', 'heap/slot-set-unittest.cc',
'locked-queue-unittest.cc', 'locked-queue-unittest.cc',
'register-configuration-unittest.cc',
'run-all-unittests.cc', 'run-all-unittests.cc',
'test-utils.h', 'test-utils.h',
'test-utils.cc', 'test-utils.cc',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment