1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 11:02:59 +02:00

[Hexagon] Use general purpose registers to spill pred/mod registers into

Patch by Tobias Edler Von Koch.

llvm-svn: 258527
This commit is contained in:
Krzysztof Parzyszek 2016-01-22 19:15:58 +00:00
parent 8d0283f1a9
commit 7ec3ade80f
5 changed files with 347 additions and 73 deletions

View File

@ -19,6 +19,7 @@
#include "HexagonTargetMachine.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SCCIterator.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
@ -1072,80 +1073,65 @@ static bool needToReserveScavengingSpillSlots(MachineFunction &MF,
}
/// Replaces the predicate spill code pseudo instructions by valid instructions.
bool HexagonFrameLowering::replacePredRegPseudoSpillCode(MachineFunction &MF)
const {
auto &HST = static_cast<const HexagonSubtarget&>(MF.getSubtarget());
auto &HII = *HST.getInstrInfo();
MachineRegisterInfo &MRI = MF.getRegInfo();
bool HasReplacedPseudoInst = false;
// Replace predicate spill pseudo instructions by real code.
// Loop over all of the basic blocks.
for (MachineFunction::iterator MBBb = MF.begin(), MBBe = MF.end();
MBBb != MBBe; ++MBBb) {
MachineBasicBlock *MBB = &*MBBb;
// Traverse the basic block.
MachineBasicBlock::iterator NextII;
for (MachineBasicBlock::iterator MII = MBB->begin(); MII != MBB->end();
MII = NextII) {
MachineInstr *MI = MII;
NextII = std::next(MII);
int Opc = MI->getOpcode();
if (Opc == Hexagon::STriw_pred) {
HasReplacedPseudoInst = true;
// STriw_pred FI, 0, SrcReg;
unsigned VirtReg = MRI.createVirtualRegister(&Hexagon::IntRegsRegClass);
unsigned SrcReg = MI->getOperand(2).getReg();
bool IsOrigSrcRegKilled = MI->getOperand(2).isKill();
/// Find a GPR register that's available in the range from It to any use of
/// the 'spill' in FI.
static bool findAvailableReg(int FI, MachineBasicBlock::iterator It,
MachineBasicBlock::iterator End,
unsigned *AvailReg,
unsigned *NumUses,
const TargetRegisterInfo *TRI,
RegScavenger *Scavenger) {
assert(Scavenger->getCurrentPosition() == It &&
"Unexpected scavenger position!");
assert(MI->getOperand(0).isFI() && "Expect a frame index");
assert(Hexagon::PredRegsRegClass.contains(SrcReg) &&
"Not a predicate register");
BitVector Avail(Scavenger->getRegsAvailable(&Hexagon::IntRegsRegClass));
if (Avail.none())
return false;
// Insert transfer to general purpose register.
// VirtReg = C2_tfrpr SrcPredReg
BuildMI(*MBB, MII, MI->getDebugLoc(), HII.get(Hexagon::C2_tfrpr),
VirtReg).addReg(SrcReg, getKillRegState(IsOrigSrcRegKilled));
BitVector AvailByLastLoad(Avail.size());
// Change instruction to S2_storeri_io.
// S2_storeri_io FI, 0, VirtReg
MI->setDesc(HII.get(Hexagon::S2_storeri_io));
MI->getOperand(2).setReg(VirtReg);
MI->getOperand(2).setIsKill();
while (++It != End) {
MachineInstr *MI = It;
if (MI->isDebugValue())
continue;
} else if (Opc == Hexagon::LDriw_pred) {
// DstReg = LDriw_pred FI, 0
MachineOperand &M0 = MI->getOperand(0);
if (M0.isDead()) {
MBB->erase(MII);
continue;
}
// Remove all registers modified by this inst from Avail
int Reg = Avail.find_first();
while (Reg != -1) {
if (MI->modifiesRegister(Reg, TRI))
Avail[Reg] = false;
else
assert(!MI->readsRegister(Reg, TRI) && "Inst reads undefined register");
unsigned VirtReg = MRI.createVirtualRegister(&Hexagon::IntRegsRegClass);
unsigned DestReg = MI->getOperand(0).getReg();
assert(MI->getOperand(1).isFI() && "Expect a frame index");
assert(Hexagon::PredRegsRegClass.contains(DestReg) &&
"Not a predicate register");
// Change instruction to L2_loadri_io.
// VirtReg = L2_loadri_io FI, 0
MI->setDesc(HII.get(Hexagon::L2_loadri_io));
MI->getOperand(0).setReg(VirtReg);
// Insert transfer to general purpose register.
// DestReg = C2_tfrrp VirtReg
const MCInstrDesc &D = HII.get(Hexagon::C2_tfrrp);
BuildMI(*MBB, std::next(MII), MI->getDebugLoc(), D, DestReg)
.addReg(VirtReg, getKillRegState(true));
HasReplacedPseudoInst = true;
}
Reg = Avail.find_next(Reg);
}
int Opc = MI->getOpcode();
// Stop if we find a store that overwrites the current spill in FI
if ((Opc == Hexagon::STriw_pred || Opc == Hexagon::STriw_mod) &&
MI->getOperand(0).getIndex() == FI)
break;
if ((Opc == Hexagon::LDriw_pred || Opc == Hexagon::LDriw_mod) &&
MI->getOperand(1).getIndex() == FI && !MI->getOperand(0).isDead()) {
AvailByLastLoad = Avail;
++(*NumUses);
}
// Give up early if there are no registers available
if (AvailByLastLoad.none() && Avail.none())
break;
}
return HasReplacedPseudoInst;
if (AvailByLastLoad.none())
return false;
*AvailReg = (unsigned) AvailByLastLoad.find_first();
return true;
}
void HexagonFrameLowering::determineCalleeSaves(MachineFunction &MF,
BitVector &SavedRegs,
RegScavenger *RS) const {
@ -1167,13 +1153,13 @@ void HexagonFrameLowering::determineCalleeSaves(MachineFunction &MF,
const TargetRegisterClass &RC = Hexagon::IntRegsRegClass;
// Replace predicate register pseudo spill code.
bool HasReplacedPseudoInst = replacePredRegPseudoSpillCode(MF);
bool HasReplacedPseudoInst = replacePseudoRegTransferCode(MF);
// We need to reserve a a spill slot if scavenging could potentially require
// spilling a scavenged register.
if (HasReplacedPseudoInst && needToReserveScavengingSpillSlots(MF, HRI)) {
MachineFrameInfo *MFI = MF.getFrameInfo();
for (int i=0; i < NumberScavengerSlots; i++)
for (int i = 0; i < NumberScavengerSlots; i++)
RS->addScavengingFrameIndex(
MFI->CreateSpillStackObject(RC.getSize(), RC.getAlignment()));
}
@ -1327,6 +1313,231 @@ bool HexagonFrameLowering::assignCalleeSavedSpillSlots(MachineFunction &MF,
}
/// Expands pseudo instructions that copy/spill/restore registers that cannot
/// have these operations done directly. For spills/restores, it will attempt
/// to spill into a general-purpose register, instead of spilling to memory.
bool HexagonFrameLowering::replacePseudoRegTransferCode(MachineFunction &MF)
const {
auto &HST = static_cast<const HexagonSubtarget&>(MF.getSubtarget());
auto &HII = *HST.getInstrInfo();
auto &HRI = *HST.getRegisterInfo();
MachineRegisterInfo &MRI = MF.getRegInfo();
bool HasReplacedPseudoInst = false;
// We use the register scavenger purely for tracking of available registers
// here, but do the 'scavenging' on our own.
RegScavenger Scavenger;
// Map from PredReg spill FIs to GPRs and remaining number of uses
DenseMap<int,std::pair<unsigned, unsigned>> FItoRegUses;
// PredReg FIs that cannot be 'spilled' into GPRs because they are live
// across BB boundaries.
SmallSet<int, 8> AlwaysSpill;
// Pred Reg FIs that have been spilled in a block due to shortage of GPRs
SmallSet<int, 8> LocallySpilled;
// Do an SCC traversal of the MachineFunction. This is to make sure we detect
// cases where a PredReg *must* be spilled to memory because it is live across
// BasicBlock boundaries: we see the reload before the spill and can mark the
// PredReg's FI in AlwaysSpill.
for (auto It = scc_begin(&MF); !It.isAtEnd(); ++It) {
const std::vector<MachineBasicBlock *> &Scc = *It;
for (MachineBasicBlock *MBB : Scc) {
if (MBB->empty())
continue;
Scavenger.enterBasicBlock(MBB);
Scavenger.forward();
LocallySpilled.clear();
// Traverse the basic block.
MachineBasicBlock::iterator NextII;
for (auto MII = MBB->begin(); MII != MBB->end(); MII = NextII) {
MachineInstr *MI = MII;
NextII = std::next(MII);
assert(Scavenger.getCurrentPosition() == MII &&
"Unexpected scavenger position");
unsigned Opc = MI->getOpcode();
DebugLoc DL = MI->getDebugLoc();
if (Opc == TargetOpcode::COPY) {
unsigned DestReg = MI->getOperand(0).getReg();
unsigned SrcReg = MI->getOperand(1).getReg();
MachineInstr *EraseMI = nullptr;
if (Hexagon::ModRegsRegClass.contains(DestReg) &&
Hexagon::ModRegsRegClass.contains(SrcReg)) {
unsigned T = MRI.createVirtualRegister(&Hexagon::IntRegsRegClass);
BuildMI(*MBB, MII, DL, HII.get(TargetOpcode::COPY), T)
.addOperand(MI->getOperand(1));
BuildMI(*MBB, MII, DL, HII.get(TargetOpcode::COPY), DestReg)
.addReg(T, RegState::Kill);
EraseMI = &*MII;
HasReplacedPseudoInst = true;
}
if (NextII != MBB->end())
Scavenger.forward(); // Move to next instruction
if (EraseMI)
MBB->erase(EraseMI);
} else if (Opc == Hexagon::STriw_pred || Opc == Hexagon::STriw_mod) {
// STriw_pred FI, 0, SrcReg
unsigned SrcReg = MI->getOperand(2).getReg();
bool IsOrigSrcRegKilled = MI->getOperand(2).isKill();
assert(MI->getOperand(0).isFI() && "Expect a frame index");
assert((Hexagon::PredRegsRegClass.contains(SrcReg) ||
Hexagon::ModRegsRegClass.contains(SrcReg)) &&
"Not a predicate or modifier register");
int FI = MI->getOperand(0).getIndex();
assert(!FItoRegUses.count(FI) &&
"Still expecting a load of this spilled predicate register!");
// Check whether we have an available GPR here and all the way to the
// reload(s) of this spill
unsigned AvailReg, NumUses = 0;
if (!AlwaysSpill.count(FI) && findAvailableReg(FI, MII, MBB->end(),
&AvailReg, &NumUses, &HRI, &Scavenger)) {
// Found a register we can move this into instead of spilling
if (Opc == Hexagon::STriw_pred)
BuildMI(*MBB, MII, MI->getDebugLoc(), HII.get(Hexagon::C2_tfrpr),
AvailReg).addReg(SrcReg, getKillRegState(IsOrigSrcRegKilled));
else
BuildMI(*MBB, MII, MI->getDebugLoc(), HII.get(Hexagon::A2_tfrcrr),
AvailReg).addReg(SrcReg, getKillRegState(IsOrigSrcRegKilled));
// Mark the register as used in the function (important for callee
// saved registers).
BitVector UsedPhysRegsMask = MRI.getUsedPhysRegsMask();
UsedPhysRegsMask.set(AvailReg);
MRI.setUsedPhysRegMask(UsedPhysRegsMask);
Scavenger.setRegUsed(AvailReg);
if (NextII != MBB->end())
Scavenger.forward();
FItoRegUses[FI] = std::make_pair(AvailReg, NumUses);
LocallySpilled.erase(FI);
MBB->erase(MII);
} else {
// No register available. Insert actual spill.
// VirtReg = C2_tfrpr SrcPredReg
unsigned VirtReg =
MRI.createVirtualRegister(&Hexagon::IntRegsRegClass);
if (Opc == Hexagon::STriw_pred)
BuildMI(*MBB, MII, MI->getDebugLoc(), HII.get(Hexagon::C2_tfrpr),
VirtReg).addReg(SrcReg, getKillRegState(IsOrigSrcRegKilled));
else
BuildMI(*MBB, MII, MI->getDebugLoc(), HII.get(Hexagon::A2_tfrcrr),
VirtReg).addReg(SrcReg, getKillRegState(IsOrigSrcRegKilled));
// Change instruction to S2_storeri_io.
// S2_storeri_io FI, 0, VirtReg
MI->setDesc(HII.get(Hexagon::S2_storeri_io));
MI->getOperand(2).setReg(VirtReg);
MI->getOperand(2).setIsKill();
HasReplacedPseudoInst = true;
if (NextII != MBB->end())
Scavenger.forward();
if (!AlwaysSpill.count(FI))
LocallySpilled.insert(FI);
}
} else if (Opc == Hexagon::LDriw_pred || Opc == Hexagon::LDriw_mod) {
// DstReg = LDriw_pred FI, 0
unsigned DestReg = MI->getOperand(0).getReg();
assert(MI->getOperand(1).isFI() && "Expect a frame index");
assert((Hexagon::PredRegsRegClass.contains(DestReg) ||
Hexagon::ModRegsRegClass.contains(DestReg)) &&
"Not a predicate or modifier register");
int FI = MI->getOperand(1).getIndex();
MachineOperand &M0 = MI->getOperand(0);
if (M0.isDead()) {
if (NextII != MBB->end())
Scavenger.forward();
MBB->erase(MII);
continue;
}
if (FItoRegUses.count(FI)) {
// Reload from GPR
std::pair<unsigned,unsigned> &SpillInfo = FItoRegUses[FI];
--SpillInfo.second;
bool IsKill = SpillInfo.second == 0;
if (Opc == Hexagon::LDriw_pred)
BuildMI(*MBB, std::next(MII), MI->getDebugLoc(),
HII.get(Hexagon::C2_tfrrp), DestReg).addReg(SpillInfo.first,
getKillRegState(IsKill));
else
BuildMI(*MBB, std::next(MII), MI->getDebugLoc(),
HII.get(Hexagon::A2_tfrrcr), DestReg).addReg(SpillInfo.first,
getKillRegState(IsKill));
if (IsKill)
FItoRegUses.erase(FI);
Scavenger.forward(); // Process the newly inserted instruction
if (NextII != MBB->end())
Scavenger.forward(); // Move to next instruction
MBB->erase(MII);
} else {
// Reload from memory
// If this wasn't spilled previously in this block, the PredReg in
// this FI is live across blocks. Make sure it never ends up in a
// register.
if (!LocallySpilled.count(FI))
AlwaysSpill.insert(FI);
unsigned VirtReg =
MRI.createVirtualRegister(&Hexagon::IntRegsRegClass);
// Change instruction to L2_loadri_io.
// VirtReg = L2_loadri_io FI, 0
MI->setDesc(HII.get(Hexagon::L2_loadri_io));
MI->getOperand(0).setReg(VirtReg);
// Insert transfer to general purpose register.
// DestReg = C2_tfrrp VirtReg
if (Opc == Hexagon::LDriw_pred)
BuildMI(*MBB, std::next(MII), MI->getDebugLoc(),
HII.get(Hexagon::C2_tfrrp), DestReg).addReg(VirtReg,
getKillRegState(true));
else
BuildMI(*MBB, std::next(MII), MI->getDebugLoc(),
HII.get(Hexagon::A2_tfrrcr), DestReg).addReg(VirtReg,
getKillRegState(true));
Scavenger.forward(); // Process newly inserted instruction
if (NextII != MBB->end())
Scavenger.forward(); // Move to next instruction
HasReplacedPseudoInst = true;
}
} else if (NextII != MBB->end())
Scavenger.forward();
}
assert(FItoRegUses.empty() && "PredRegs in GPRs outlast this block!");
}
}
return HasReplacedPseudoInst;
}
void HexagonFrameLowering::expandAlloca(MachineInstr *AI,
const HexagonInstrInfo &HII, unsigned SP, unsigned CF) const {
MachineBasicBlock &MB = *AI->getParent();

View File

@ -93,7 +93,7 @@ private:
MachineBasicBlock::iterator At) const;
void adjustForCalleeSavedRegsSpillCall(MachineFunction &MF) const;
bool replacePredRegPseudoSpillCode(MachineFunction &MF) const;
bool replacePseudoRegTransferCode(MachineFunction &MF) const;
bool replaceVecPredRegPseudoSpillCode(MachineFunction &MF) const;
void findShrunkPrologEpilog(MachineFunction &MF, MachineBasicBlock *&PrologB,

View File

@ -728,8 +728,12 @@ void HexagonInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
.addReg(SrcReg, getKillRegState(isKill)).addMemOperand(MMO);
} else if (Hexagon::PredRegsRegClass.hasSubClassEq(RC)) {
BuildMI(MBB, I, DL, get(Hexagon::STriw_pred))
.addFrameIndex(FI).addImm(0)
.addReg(SrcReg, getKillRegState(isKill)).addMemOperand(MMO);
.addFrameIndex(FI).addImm(0)
.addReg(SrcReg, getKillRegState(isKill)).addMemOperand(MMO);
} else if (Hexagon::ModRegsRegClass.hasSubClassEq(RC)) {
BuildMI(MBB, I, DL, get(Hexagon::STriw_mod))
.addFrameIndex(FI).addImm(0)
.addReg(SrcReg, getKillRegState(isKill)).addMemOperand(MMO);
} else {
llvm_unreachable("Unimplemented");
}
@ -747,15 +751,18 @@ void HexagonInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
MachineMemOperand *MMO = MF.getMachineMemOperand(
MachinePointerInfo::getFixedStack(MF, FI), MachineMemOperand::MOLoad,
MFI.getObjectSize(FI), Align);
if (RC == &Hexagon::IntRegsRegClass) {
if (Hexagon::IntRegsRegClass.hasSubClassEq(RC)) {
BuildMI(MBB, I, DL, get(Hexagon::L2_loadri_io), DestReg)
.addFrameIndex(FI).addImm(0).addMemOperand(MMO);
} else if (RC == &Hexagon::DoubleRegsRegClass) {
} else if (Hexagon::DoubleRegsRegClass.hasSubClassEq(RC)) {
BuildMI(MBB, I, DL, get(Hexagon::L2_loadrd_io), DestReg)
.addFrameIndex(FI).addImm(0).addMemOperand(MMO);
} else if (RC == &Hexagon::PredRegsRegClass) {
} else if (Hexagon::PredRegsRegClass.hasSubClassEq(RC)) {
BuildMI(MBB, I, DL, get(Hexagon::LDriw_pred), DestReg)
.addFrameIndex(FI).addImm(0).addMemOperand(MMO);
.addFrameIndex(FI).addImm(0).addMemOperand(MMO);
} else if (Hexagon::ModRegsRegClass.hasSubClassEq(RC)) {
BuildMI(MBB, I, DL, get(Hexagon::LDriw_mod), DestReg)
.addFrameIndex(FI).addImm(0).addMemOperand(MMO);
} else {
llvm_unreachable("Can't store this register to stack slot");
}
@ -2461,6 +2468,8 @@ bool HexagonInstrInfo::isValidOffset(unsigned Opcode, int Offset,
// any size. Later pass knows how to handle it.
case Hexagon::STriw_pred:
case Hexagon::LDriw_pred:
case Hexagon::STriw_mod:
case Hexagon::LDriw_mod:
return true;
case Hexagon::TFR_FI:

View File

@ -2010,6 +2010,12 @@ let isExtendable = 1, opExtendable = 2, isExtentSigned = 1, opExtentBits = 13,
def LDriw_pred : LDInst<(outs PredRegs:$dst),
(ins IntRegs:$addr, s11_2Ext:$off),
".error \"should not emit\"", []>;
// Load modifier.
let isExtendable = 1, opExtendable = 2, isExtentSigned = 1, opExtentBits = 13,
isCodeGenOnly = 1, isPseudo = 1, hasSideEffects = 0 in
def LDriw_mod : LDInst<(outs ModRegs:$dst),
(ins IntRegs:$addr, s11_2Ext:$off),
".error \"should not emit\"", []>;
let Defs = [R29, R30, R31], Uses = [R30], hasSideEffects = 0 in
def L2_deallocframe : LDInst<(outs), (ins),
@ -3651,6 +3657,12 @@ let isExtendable = 1, opExtendable = 1, isExtentSigned = 1, opExtentBits = 13,
def STriw_pred : STInst<(outs),
(ins IntRegs:$addr, s11_2Ext:$off, PredRegs:$src1),
".error \"should not emit\"", []>;
// Store modifier.
let isExtendable = 1, opExtendable = 1, isExtentSigned = 1, opExtentBits = 13,
isCodeGenOnly = 1, isPseudo = 1, hasSideEffects = 0 in
def STriw_mod : STInst<(outs),
(ins IntRegs:$addr, s11_2Ext:$off, ModRegs:$src1),
".error \"should not emit\"", []>;
// S2_allocframe: Allocate stack frame.
let Defs = [R29, R30], Uses = [R29, R31, R30],

View File

@ -0,0 +1,42 @@
; RUN: llc -march=hexagon -O2 < %s | FileCheck %s
;
; This checks that predicate registers are moved to GPRs instead of spilling
; where possible.
; CHECK: p0 =
; CHECK-NOT: memw(r29
define i32 @f(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) {
entry:
%cmp = icmp eq i32 %a, 1
%cmp1 = icmp eq i32 %b, 2
%or.cond = and i1 %cmp, %cmp1
%cmp3 = icmp eq i32 %c, 3
%or.cond30 = and i1 %or.cond, %cmp3
%cmp5 = icmp eq i32 %d, 4
%or.cond31 = and i1 %or.cond30, %cmp5
%cmp7 = icmp eq i32 %e, 5
%or.cond32 = and i1 %or.cond31, %cmp7
%ret.0 = zext i1 %or.cond32 to i32
%cmp8 = icmp eq i32 %a, 3
%cmp10 = icmp eq i32 %b, 4
%or.cond33 = and i1 %cmp8, %cmp10
%cmp12 = icmp eq i32 %c, 5
%or.cond34 = and i1 %or.cond33, %cmp12
%cmp14 = icmp eq i32 %d, 6
%or.cond35 = and i1 %or.cond34, %cmp14
%cmp16 = icmp eq i32 %e, 7
%or.cond36 = and i1 %or.cond35, %cmp16
%ret.1 = select i1 %or.cond36, i32 2, i32 %ret.0
%cmp21 = icmp eq i32 %b, 8
%or.cond37 = and i1 %cmp, %cmp21
%cmp23 = icmp eq i32 %c, 2
%or.cond38 = and i1 %or.cond37, %cmp23
%cmp25 = icmp eq i32 %d, 1
%or.cond39 = and i1 %or.cond38, %cmp25
%cmp27 = icmp eq i32 %e, 3
%or.cond40 = and i1 %or.cond39, %cmp27
%ret.2 = select i1 %or.cond40, i32 3, i32 %ret.1
ret i32 %ret.2
}