Prefer cheap registers for busy live ranges.

On the x86-64 and thumb2 targets, some registers are more expensive to encode
than others in the same register class.

Add a CostPerUse field to the TableGen register description, and make it
available from TRI->getCostPerUse. This represents the cost of a REX prefix or a
32-bit instruction encoding required by choosing a high register.

Teach the greedy register allocator to prefer cheap registers for busy live
ranges (as indicated by spill weight).

llvm-svn: 129864
This commit is contained in:
Jakob Stoklund Olesen 2011-04-20 18:19:48 +00:00
parent d828f316af
commit 0e34c1dfac
9 changed files with 81 additions and 16 deletions

View File

@ -78,6 +78,13 @@ class Register<string n> {
// -1 indicates that the gcc number is undefined and -2 that register number
// is invalid for this mode/flavour.
list<int> DwarfNumbers = [];
// CostPerUse - Additional cost of instructions using this register compared
// to other registers in its class. The register allocator will try to
// minimize the number of instructions using a register with a CostPerUse.
// This is used by the x86-64 and ARM Thumb targets where some registers
// require larger instruction encodings.
int CostPerUse = 0;
}
// RegisterWithSubRegs - This can be used to define instances of Register which

View File

@ -46,6 +46,7 @@ struct TargetRegisterDesc {
const unsigned *Overlaps; // Overlapping registers, described above
const unsigned *SubRegs; // Sub-register set, described above
const unsigned *SuperRegs; // Super-register set, described above
unsigned CostPerUse; // Extra cost of instructions using register.
};
class TargetRegisterClass {
@ -426,6 +427,12 @@ public:
return get(RegNo).Name;
}
/// getCostPerUse - Return the additional cost of using this register instead
/// of other registers in its class.
unsigned getCostPerUse(unsigned RegNo) const {
return get(RegNo).CostPerUse;
}
/// getNumRegs - Return the number of registers this target has (useful for
/// sizing arrays holding per register information)
unsigned getNumRegs() const {

View File

@ -278,6 +278,7 @@ void RegAllocBase::assign(LiveInterval &VirtReg, unsigned PhysReg) {
<< " to " << PrintReg(PhysReg, TRI) << '\n');
assert(!VRM->hasPhys(VirtReg.reg) && "Duplicate VirtReg assignment");
VRM->assignVirt2Phys(VirtReg.reg, PhysReg);
MRI->setPhysRegUsed(PhysReg);
PhysReg2LiveUnion[PhysReg].unify(VirtReg);
++NumAssigned;
}

View File

@ -187,8 +187,10 @@ private:
unsigned nextSplitPoint(unsigned);
bool canEvictInterference(LiveInterval&, unsigned, float&);
unsigned tryAssign(LiveInterval&, AllocationOrder&,
SmallVectorImpl<LiveInterval*>&);
unsigned tryEvict(LiveInterval&, AllocationOrder&,
SmallVectorImpl<LiveInterval*>&);
SmallVectorImpl<LiveInterval*>&, unsigned = ~0u);
unsigned tryRegionSplit(LiveInterval&, AllocationOrder&,
SmallVectorImpl<LiveInterval*>&);
unsigned tryLocalSplit(LiveInterval&, AllocationOrder&,
@ -334,6 +336,37 @@ LiveInterval *RAGreedy::dequeue() {
return LI;
}
//===----------------------------------------------------------------------===//
// Direct Assignment
//===----------------------------------------------------------------------===//
/// tryAssign - Try to assign VirtReg to an available register.
unsigned RAGreedy::tryAssign(LiveInterval &VirtReg,
AllocationOrder &Order,
SmallVectorImpl<LiveInterval*> &NewVRegs) {
Order.rewind();
unsigned PhysReg;
while ((PhysReg = Order.next()))
if (!checkPhysRegInterference(VirtReg, PhysReg))
break;
if (!PhysReg || Order.isHint(PhysReg))
return PhysReg;
// PhysReg is available. Try to evict interference from a cheaper alternative.
unsigned Cost = TRI->getCostPerUse(PhysReg);
// Most registers have 0 additional cost.
if (!Cost)
return PhysReg;
DEBUG(dbgs() << PrintReg(PhysReg, TRI) << " is available at cost " << Cost
<< '\n');
unsigned CheapReg = tryEvict(VirtReg, Order, NewVRegs, Cost);
return CheapReg ? CheapReg : PhysReg;
}
//===----------------------------------------------------------------------===//
// Interference eviction
//===----------------------------------------------------------------------===//
@ -371,7 +404,8 @@ bool RAGreedy::canEvictInterference(LiveInterval &VirtReg, unsigned PhysReg,
/// @return Physreg to assign VirtReg, or 0.
unsigned RAGreedy::tryEvict(LiveInterval &VirtReg,
AllocationOrder &Order,
SmallVectorImpl<LiveInterval*> &NewVRegs){
SmallVectorImpl<LiveInterval*> &NewVRegs,
unsigned CostPerUseLimit) {
NamedRegionTimer T("Evict", TimerGroupName, TimePassesIsEnabled);
// Keep track of the lightest single interference seen so far.
@ -380,6 +414,12 @@ unsigned RAGreedy::tryEvict(LiveInterval &VirtReg,
Order.rewind();
while (unsigned PhysReg = Order.next()) {
if (TRI->getCostPerUse(PhysReg) >= CostPerUseLimit)
continue;
// The first use of a register in a function has cost 1.
if (CostPerUseLimit == 1 && !MRI->isPhysRegUsed(PhysReg))
continue;
float Weight = BestWeight;
if (!canEvictInterference(VirtReg, PhysReg, Weight))
continue;
@ -1230,10 +1270,8 @@ unsigned RAGreedy::selectOrSplit(LiveInterval &VirtReg,
SmallVectorImpl<LiveInterval*> &NewVRegs) {
// First try assigning a free register.
AllocationOrder Order(VirtReg.reg, *VRM, ReservedRegs);
while (unsigned PhysReg = Order.next()) {
if (!checkPhysRegInterference(VirtReg, PhysReg))
return PhysReg;
}
if (unsigned PhysReg = tryAssign(VirtReg, Order, NewVRegs))
return PhysReg;
if (unsigned PhysReg = tryEvict(VirtReg, Order, NewVRegs))
return PhysReg;

View File

@ -70,6 +70,8 @@ def R4 : ARMReg< 4, "r4">, DwarfRegNum<[4]>;
def R5 : ARMReg< 5, "r5">, DwarfRegNum<[5]>;
def R6 : ARMReg< 6, "r6">, DwarfRegNum<[6]>;
def R7 : ARMReg< 7, "r7">, DwarfRegNum<[7]>;
// These require 32-bit instructions.
let CostPerUse = 1 in {
def R8 : ARMReg< 8, "r8">, DwarfRegNum<[8]>;
def R9 : ARMReg< 9, "r9">, DwarfRegNum<[9]>;
def R10 : ARMReg<10, "r10">, DwarfRegNum<[10]>;
@ -78,6 +80,7 @@ def R12 : ARMReg<12, "r12">, DwarfRegNum<[12]>;
def SP : ARMReg<13, "sp">, DwarfRegNum<[13]>;
def LR : ARMReg<14, "lr">, DwarfRegNum<[14]>;
def PC : ARMReg<15, "pc">, DwarfRegNum<[15]>;
}
// Float registers
def S0 : ARMFReg< 0, "s0">; def S1 : ARMFReg< 1, "s1">;

View File

@ -46,7 +46,8 @@ let Namespace = "X86" in {
def CL : Register<"cl">, DwarfRegNum<[2, 1, 1]>;
def BL : Register<"bl">, DwarfRegNum<[3, 3, 3]>;
// X86-64 only
// X86-64 only, requires REX.
let CostPerUse = 1 in {
def SIL : Register<"sil">, DwarfRegNum<[4, 6, 6]>;
def DIL : Register<"dil">, DwarfRegNum<[5, 7, 7]>;
def BPL : Register<"bpl">, DwarfRegNum<[6, 4, 5]>;
@ -59,6 +60,7 @@ let Namespace = "X86" in {
def R13B : Register<"r13b">, DwarfRegNum<[13, -2, -2]>;
def R14B : Register<"r14b">, DwarfRegNum<[14, -2, -2]>;
def R15B : Register<"r15b">, DwarfRegNum<[15, -2, -2]>;
}
// High registers. On x86-64, these cannot be used in any instruction
// with a REX prefix.
@ -82,8 +84,8 @@ let Namespace = "X86" in {
}
def IP : Register<"ip">, DwarfRegNum<[16]>;
// X86-64 only
let SubRegIndices = [sub_8bit] in {
// X86-64 only, requires REX.
let SubRegIndices = [sub_8bit], CostPerUse = 1 in {
def R8W : RegisterWithSubRegs<"r8w", [R8B]>, DwarfRegNum<[8, -2, -2]>;
def R9W : RegisterWithSubRegs<"r9w", [R9B]>, DwarfRegNum<[9, -2, -2]>;
def R10W : RegisterWithSubRegs<"r10w", [R10B]>, DwarfRegNum<[10, -2, -2]>;
@ -105,7 +107,8 @@ let Namespace = "X86" in {
def ESP : RegisterWithSubRegs<"esp", [SP]>, DwarfRegNum<[7, 5, 4]>;
def EIP : RegisterWithSubRegs<"eip", [IP]>, DwarfRegNum<[16, 8, 8]>;
// X86-64 only
// X86-64 only, requires REX
let CostPerUse = 1 in {
def R8D : RegisterWithSubRegs<"r8d", [R8W]>, DwarfRegNum<[8, -2, -2]>;
def R9D : RegisterWithSubRegs<"r9d", [R9W]>, DwarfRegNum<[9, -2, -2]>;
def R10D : RegisterWithSubRegs<"r10d", [R10W]>, DwarfRegNum<[10, -2, -2]>;
@ -114,7 +117,7 @@ let Namespace = "X86" in {
def R13D : RegisterWithSubRegs<"r13d", [R13W]>, DwarfRegNum<[13, -2, -2]>;
def R14D : RegisterWithSubRegs<"r14d", [R14W]>, DwarfRegNum<[14, -2, -2]>;
def R15D : RegisterWithSubRegs<"r15d", [R15W]>, DwarfRegNum<[15, -2, -2]>;
}
}}
// 64-bit registers, X86-64 only
let SubRegIndices = [sub_32bit] in {
@ -127,6 +130,8 @@ let Namespace = "X86" in {
def RBP : RegisterWithSubRegs<"rbp", [EBP]>, DwarfRegNum<[6, -2, -2]>;
def RSP : RegisterWithSubRegs<"rsp", [ESP]>, DwarfRegNum<[7, -2, -2]>;
// These also require REX.
let CostPerUse = 1 in {
def R8 : RegisterWithSubRegs<"r8", [R8D]>, DwarfRegNum<[8, -2, -2]>;
def R9 : RegisterWithSubRegs<"r9", [R9D]>, DwarfRegNum<[9, -2, -2]>;
def R10 : RegisterWithSubRegs<"r10", [R10D]>, DwarfRegNum<[10, -2, -2]>;
@ -136,7 +141,7 @@ let Namespace = "X86" in {
def R14 : RegisterWithSubRegs<"r14", [R14D]>, DwarfRegNum<[14, -2, -2]>;
def R15 : RegisterWithSubRegs<"r15", [R15D]>, DwarfRegNum<[15, -2, -2]>;
def RIP : RegisterWithSubRegs<"rip", [EIP]>, DwarfRegNum<[16, -2, -2]>;
}
}}
// MMX Registers. These are actually aliased to ST0 .. ST7
def MM0 : Register<"mm0">, DwarfRegNum<[41, 29, 29]>;
@ -170,6 +175,7 @@ let Namespace = "X86" in {
def XMM7: Register<"xmm7">, DwarfRegNum<[24, 28, 28]>;
// X86-64 only
let CostPerUse = 1 in {
def XMM8: Register<"xmm8">, DwarfRegNum<[25, -2, -2]>;
def XMM9: Register<"xmm9">, DwarfRegNum<[26, -2, -2]>;
def XMM10: Register<"xmm10">, DwarfRegNum<[27, -2, -2]>;
@ -178,7 +184,7 @@ let Namespace = "X86" in {
def XMM13: Register<"xmm13">, DwarfRegNum<[30, -2, -2]>;
def XMM14: Register<"xmm14">, DwarfRegNum<[31, -2, -2]>;
def XMM15: Register<"xmm15">, DwarfRegNum<[32, -2, -2]>;
}
}}
// YMM Registers, used by AVX instructions
let SubRegIndices = [sub_xmm] in {

View File

@ -31,6 +31,7 @@ namespace llvm {
const std::string &getName() const;
unsigned DeclaredSpillSize, DeclaredSpillAlignment;
unsigned EnumValue;
unsigned CostPerUse;
CodeGenRegister(Record *R);
};

View File

@ -172,6 +172,7 @@ void CodeGenTarget::ReadRegisters() const {
CodeGenRegister::CodeGenRegister(Record *R) : TheDef(R) {
DeclaredSpillSize = R->getValueAsInt("SpillSize");
DeclaredSpillAlignment = R->getValueAsInt("SpillAlignment");
CostPerUse = R->getValueAsInt("CostPerUse");
}
const std::string &CodeGenRegister::getName() const {

View File

@ -841,7 +841,7 @@ void RegisterInfoEmitter::run(raw_ostream &OS) {
}
OS<<"\n const TargetRegisterDesc RegisterDescriptors[] = { // Descriptors\n";
OS << " { \"NOREG\",\t0,\t0,\t0 },\n";
OS << " { \"NOREG\",\t0,\t0,\t0,\t0 },\n";
// Now that register alias and sub-registers sets have been emitted, emit the
// register descriptors now.
@ -854,9 +854,10 @@ void RegisterInfoEmitter::run(raw_ostream &OS) {
else
OS << "Empty_SubRegsSet,\t";
if (!RegisterSuperRegs[Reg.TheDef].empty())
OS << Reg.getName() << "_SuperRegsSet },\n";
OS << Reg.getName() << "_SuperRegsSet,\t";
else
OS << "Empty_SuperRegsSet },\n";
OS << "Empty_SuperRegsSet,\t";
OS << Reg.CostPerUse << " },\n";
}
OS << " };\n"; // End of register descriptors...