[SystemZ] Add instruction-shortening pass

When loading immediates into a GR32, the port prefered LHI, followed by
LLILH or LLILL, followed by IILF.  LHI and IILF are natural 32-bit
operations, but LLILH and LLILL also clear the upper 32 bits of the register.
This was represented as taking a 32-bit subreg of a 64-bit assignment.

Using subregs for something as simple as a move immediate was probably
a bad idea.  Also, I have patches to add support for the high-word facility, 
and we don't want something like LLILH and LLILL to stop the high word of
the same GPR from being used.

This patch therefore uses LHI and IILF to begin with and adds a late
machine-specific pass to use LLILH and LLILL if the other half of the
register is not live.  The high-word patches extend this behavior to
IIHF, LLIHL and LLIHH.

No behavioral change intended.

llvm-svn: 191363
This commit is contained in:
Richard Sandiford 2013-09-25 10:11:07 +00:00
parent 1ccd2f2aee
commit 35ec4e356c
8 changed files with 193 additions and 8 deletions

View File

@ -24,6 +24,7 @@ add_llvm_target(SystemZCodeGen
SystemZMCInstLower.cpp
SystemZRegisterInfo.cpp
SystemZSelectionDAGInfo.cpp
SystemZShortenInst.cpp
SystemZSubtarget.cpp
SystemZTargetMachine.cpp
)

View File

@ -69,6 +69,23 @@ const unsigned SystemZMC::FP128Regs[16] = {
SystemZ::F12Q, SystemZ::F13Q, 0, 0
};
unsigned SystemZMC::getFirstReg(unsigned Reg) {
static unsigned Map[SystemZ::NUM_TARGET_REGS];
static bool Initialized = false;
if (!Initialized) {
for (unsigned I = 0; I < 16; ++I) {
Map[GR32Regs[I]] = I;
Map[GR64Regs[I]] = I;
Map[GR128Regs[I]] = I;
Map[FP32Regs[I]] = I;
Map[FP64Regs[I]] = I;
Map[FP128Regs[I]] = I;
}
}
assert(Reg < SystemZ::NUM_TARGET_REGS);
return Map[Reg];
}
static MCAsmInfo *createSystemZMCAsmInfo(const MCRegisterInfo &MRI,
StringRef TT) {
MCAsmInfo *MAI = new SystemZMCAsmInfo(TT);

View File

@ -47,6 +47,15 @@ namespace SystemZMC {
extern const unsigned FP32Regs[16];
extern const unsigned FP64Regs[16];
extern const unsigned FP128Regs[16];
// Return the 0-based number of the first architectural register that
// contains the given LLVM register. E.g. R1D -> 1.
unsigned getFirstReg(unsigned Reg);
// Return the given register as a GR64.
inline unsigned getRegAsGR64(unsigned Reg) {
return GR64Regs[getFirstReg(Reg)];
}
}
MCCodeEmitter *createSystemZMCCodeEmitter(const MCInstrInfo &MCII,

View File

@ -106,6 +106,7 @@ namespace llvm {
FunctionPass *createSystemZISelDag(SystemZTargetMachine &TM,
CodeGenOpt::Level OptLevel);
FunctionPass *createSystemZElimComparePass(SystemZTargetMachine &TM);
FunctionPass *createSystemZShortenInstPass(SystemZTargetMachine &TM);
FunctionPass *createSystemZLongBranchPass(SystemZTargetMachine &TM);
} // end namespace llvm;
#endif

View File

@ -1212,14 +1212,6 @@ def : Pat<(ctlz GR64:$src),
def : Pat<(i64 (anyext GR32:$src)),
(INSERT_SUBREG (i64 (IMPLICIT_DEF)), GR32:$src, subreg_32bit)>;
// There are no 32-bit equivalents of LLILL and LLILH, so use a full
// 64-bit move followed by a subreg. This preserves the invariant that
// all GR32 operations only modify the low 32 bits.
def : Pat<(i32 imm32ll16:$src),
(EXTRACT_SUBREG (LLILL (LL16 imm:$src)), subreg_32bit)>;
def : Pat<(i32 imm32lh16:$src),
(EXTRACT_SUBREG (LLILH (LH16 imm:$src)), subreg_32bit)>;
// Extend GR32s and GR64s to GR128s.
let usesCustomInserter = 1 in {
def AEXT128_64 : Pseudo<(outs GR128:$dst), (ins GR64:$src), []>;

View File

@ -48,6 +48,10 @@ public:
LLVM_OVERRIDE {
return true;
}
virtual bool trackLivenessAfterRegAlloc(const MachineFunction &MF) const
LLVM_OVERRIDE {
return true;
}
virtual const uint16_t *getCalleeSavedRegs(const MachineFunction *MF = 0)
const LLVM_OVERRIDE;
virtual BitVector getReservedRegs(const MachineFunction &MF)

View File

@ -0,0 +1,159 @@
//===-- SystemZShortenInst.cpp - Instruction-shortening pass --------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass tries to replace instructions with shorter forms. For example,
// IILF can be replaced with LLILL or LLILH if the constant fits and if the
// other 32 bits of the GR64 destination are not live.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "systemz-shorten-inst"
#include "SystemZTargetMachine.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
using namespace llvm;
namespace {
class SystemZShortenInst : public MachineFunctionPass {
public:
static char ID;
SystemZShortenInst(const SystemZTargetMachine &tm);
virtual const char *getPassName() const {
return "SystemZ Instruction Shortening";
}
bool processBlock(MachineBasicBlock *MBB);
bool runOnMachineFunction(MachineFunction &F);
private:
bool shortenIIF(MachineInstr &MI, unsigned *GPRMap, unsigned LiveOther,
unsigned LLIxL, unsigned LLIxH);
const SystemZInstrInfo *TII;
// LowGPRs[I] has bit N set if LLVM register I includes the low
// word of GPR N. HighGPRs is the same for the high word.
unsigned LowGPRs[SystemZ::NUM_TARGET_REGS];
unsigned HighGPRs[SystemZ::NUM_TARGET_REGS];
};
char SystemZShortenInst::ID = 0;
} // end of anonymous namespace
FunctionPass *llvm::createSystemZShortenInstPass(SystemZTargetMachine &TM) {
return new SystemZShortenInst(TM);
}
SystemZShortenInst::SystemZShortenInst(const SystemZTargetMachine &tm)
: MachineFunctionPass(ID), TII(0), LowGPRs(), HighGPRs() {
// Set up LowGPRs and HighGPRs.
for (unsigned I = 0; I < 16; ++I) {
LowGPRs[SystemZMC::GR32Regs[I]] |= 1 << I;
LowGPRs[SystemZMC::GR64Regs[I]] |= 1 << I;
HighGPRs[SystemZMC::GR64Regs[I]] |= 1 << I;
if (unsigned GR128 = SystemZMC::GR128Regs[I]) {
LowGPRs[GR128] |= 3 << I;
HighGPRs[GR128] |= 3 << I;
}
}
}
// MI loads one word of a GPR using an IIxF instruction and LLIxL and LLIxH
// are the halfword immediate loads for the same word. Try to use one of them
// instead of IIxF. If MI loads the high word, GPRMap[X] is the set of high
// words referenced by LLVM register X while LiveOther is the mask of low
// words that are currently live, and vice versa.
bool SystemZShortenInst::shortenIIF(MachineInstr &MI, unsigned *GPRMap,
unsigned LiveOther, unsigned LLIxL,
unsigned LLIxH) {
unsigned Reg = MI.getOperand(0).getReg();
assert(Reg < SystemZ::NUM_TARGET_REGS && "Invalid register number");
unsigned GPRs = GPRMap[Reg];
assert(GPRs != 0 && "Register must be a GPR");
if (GPRs & LiveOther)
return false;
uint64_t Imm = MI.getOperand(1).getImm();
if (SystemZ::isImmLL(Imm)) {
MI.setDesc(TII->get(LLIxL));
MI.getOperand(0).setReg(SystemZMC::getRegAsGR64(Reg));
return true;
}
if (SystemZ::isImmLH(Imm)) {
MI.setDesc(TII->get(LLIxH));
MI.getOperand(0).setReg(SystemZMC::getRegAsGR64(Reg));
MI.getOperand(1).setImm(Imm >> 16);
return true;
}
return false;
}
// Process all instructions in MBB. Return true if something changed.
bool SystemZShortenInst::processBlock(MachineBasicBlock *MBB) {
bool Changed = false;
// Work out which words are live on exit from the block.
unsigned LiveLow = 0;
unsigned LiveHigh = 0;
for (MachineBasicBlock::succ_iterator SI = MBB->succ_begin(),
SE = MBB->succ_end(); SI != SE; ++SI) {
for (MachineBasicBlock::livein_iterator LI = (*SI)->livein_begin(),
LE = (*SI)->livein_end(); LI != LE; ++LI) {
unsigned Reg = *LI;
assert(Reg < SystemZ::NUM_TARGET_REGS && "Invalid register number");
LiveLow |= LowGPRs[Reg];
LiveHigh |= HighGPRs[Reg];
}
}
// Iterate backwards through the block looking for instructions to change.
for (MachineBasicBlock::reverse_iterator MBBI = MBB->rbegin(),
MBBE = MBB->rend(); MBBI != MBBE; ++MBBI) {
MachineInstr &MI = *MBBI;
unsigned Opcode = MI.getOpcode();
if (Opcode == SystemZ::IILF32)
Changed |= shortenIIF(MI, LowGPRs, LiveHigh, SystemZ::LLILL,
SystemZ::LLILH);
unsigned UsedLow = 0;
unsigned UsedHigh = 0;
for (MachineInstr::mop_iterator MOI = MI.operands_begin(),
MOE = MI.operands_end(); MOI != MOE; ++MOI) {
MachineOperand &MO = *MOI;
if (MO.isReg()) {
if (unsigned Reg = MO.getReg()) {
assert(Reg < SystemZ::NUM_TARGET_REGS && "Invalid register number");
if (MO.isDef()) {
LiveLow &= ~LowGPRs[Reg];
LiveHigh &= ~HighGPRs[Reg];
} else if (!MO.isUndef()) {
UsedLow |= LowGPRs[Reg];
UsedHigh |= HighGPRs[Reg];
}
}
}
}
LiveLow |= UsedLow;
LiveHigh |= UsedHigh;
}
return Changed;
}
bool SystemZShortenInst::runOnMachineFunction(MachineFunction &F) {
TII = static_cast<const SystemZInstrInfo *>(F.getTarget().getInstrInfo());
bool Changed = false;
for (MachineFunction::iterator MFI = F.begin(), MFE = F.end();
MFI != MFE; ++MFI)
Changed |= processBlock(MFI);
return Changed;
}

View File

@ -97,6 +97,8 @@ bool SystemZPassConfig::addPreEmitPass() {
// preventing that would be a win or not.
if (getOptLevel() != CodeGenOpt::None)
addPass(createSystemZElimComparePass(getSystemZTargetMachine()));
if (getOptLevel() != CodeGenOpt::None)
addPass(createSystemZShortenInstPass(getSystemZTargetMachine()));
addPass(createSystemZLongBranchPass(getSystemZTargetMachine()));
return true;
}