#include "MCTargetDesc/PPCPredicates.h"
#include "PPC.h"
#include "PPCInstrBuilder.h"
#include "PPCInstrInfo.h"
#include "PPCMachineFunctionInfo.h"
#include "PPCTargetMachine.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/LiveIntervals.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/PseudoSourceValue.h"
#include "llvm/CodeGen/ScheduleDAG.h"
#include "llvm/CodeGen/SlotIndexes.h"
#include "llvm/InitializePasses.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
static cl::opt<bool> DisableVSXFMAMutate(
"disable-ppc-vsx-fma-mutation",
cl::desc("Disable VSX FMA instruction mutation"), cl::init(true),
cl::Hidden);
#define DEBUG_TYPE "ppc-vsx-fma-mutate"
namespace llvm { namespace PPC {
int getAltVSXFMAOpcode(uint16_t Opcode);
} }
namespace {
struct PPCVSXFMAMutate : public MachineFunctionPass {
static char ID;
PPCVSXFMAMutate() : MachineFunctionPass(ID) {
initializePPCVSXFMAMutatePass(*PassRegistry::getPassRegistry());
}
LiveIntervals *LIS;
const PPCInstrInfo *TII;
protected:
bool processBlock(MachineBasicBlock &MBB) {
bool Changed = false;
MachineRegisterInfo &MRI = MBB.getParent()->getRegInfo();
const TargetRegisterInfo *TRI = &TII->getRegisterInfo();
for (MachineBasicBlock::iterator I = MBB.begin(), IE = MBB.end();
I != IE; ++I) {
MachineInstr &MI = *I;
int AltOpc = PPC::getAltVSXFMAOpcode(MI.getOpcode());
if (AltOpc == -1)
continue;
SlotIndex FMAIdx = LIS->getInstructionIndex(MI);
VNInfo *AddendValNo =
LIS->getInterval(MI.getOperand(1).getReg()).Query(FMAIdx).valueIn();
if (!AddendValNo)
continue;
MachineInstr *AddendMI = LIS->getInstructionFromIndex(AddendValNo->def);
if (!AddendMI || AddendMI->getParent() != MI.getParent())
continue;
if (!AddendMI->isFullCopy())
continue;
Register AddendSrcReg = AddendMI->getOperand(1).getReg();
if (Register::isVirtualRegister(AddendSrcReg)) {
if (MRI.getRegClass(AddendMI->getOperand(0).getReg()) !=
MRI.getRegClass(AddendSrcReg))
continue;
} else {
if (!MRI.getRegClass(AddendMI->getOperand(0).getReg())
->contains(AddendSrcReg))
continue;
}
bool OtherUsers = false, KillsAddendSrc = false;
for (auto J = std::prev(I), JE = MachineBasicBlock::iterator(AddendMI);
J != JE; --J) {
if (J->readsVirtualRegister(AddendMI->getOperand(0).getReg())) {
OtherUsers = true;
break;
}
if (J->modifiesRegister(AddendSrcReg, TRI) ||
J->killsRegister(AddendSrcReg, TRI)) {
KillsAddendSrc = true;
break;
}
}
if (OtherUsers || KillsAddendSrc)
continue;
Register OldFMAReg = MI.getOperand(0).getReg();
unsigned KilledProdOp = 0, OtherProdOp = 0;
Register Reg2 = MI.getOperand(2).getReg();
Register Reg3 = MI.getOperand(3).getReg();
if (LIS->getInterval(Reg2).Query(FMAIdx).isKill()
&& Reg2 != OldFMAReg) {
KilledProdOp = 2;
OtherProdOp = 3;
} else if (LIS->getInterval(Reg3).Query(FMAIdx).isKill()
&& Reg3 != OldFMAReg) {
KilledProdOp = 3;
OtherProdOp = 2;
}
if (!KilledProdOp)
continue;
if (Register::isVirtualRegister(AddendSrcReg) &&
!LIS->getInterval(AddendSrcReg).liveAt(FMAIdx))
continue;
Register KilledProdReg = MI.getOperand(KilledProdOp).getReg();
Register OtherProdReg = MI.getOperand(OtherProdOp).getReg();
unsigned AddSubReg = AddendMI->getOperand(1).getSubReg();
unsigned KilledProdSubReg = MI.getOperand(KilledProdOp).getSubReg();
unsigned OtherProdSubReg = MI.getOperand(OtherProdOp).getSubReg();
bool AddRegKill = AddendMI->getOperand(1).isKill();
bool KilledProdRegKill = MI.getOperand(KilledProdOp).isKill();
bool OtherProdRegKill = MI.getOperand(OtherProdOp).isKill();
bool AddRegUndef = AddendMI->getOperand(1).isUndef();
bool KilledProdRegUndef = MI.getOperand(KilledProdOp).isUndef();
bool OtherProdRegUndef = MI.getOperand(OtherProdOp).isUndef();
if (!MRI.constrainRegClass(KilledProdReg,
MRI.getRegClass(OldFMAReg)))
continue;
assert(OldFMAReg == AddendMI->getOperand(0).getReg() &&
"Addend copy not tied to old FMA output!");
LLVM_DEBUG(dbgs() << "VSX FMA Mutation:\n " << MI);
MI.getOperand(0).setReg(KilledProdReg);
MI.getOperand(1).setReg(KilledProdReg);
MI.getOperand(3).setReg(AddendSrcReg);
MI.getOperand(0).setSubReg(KilledProdSubReg);
MI.getOperand(1).setSubReg(KilledProdSubReg);
MI.getOperand(3).setSubReg(AddSubReg);
MI.getOperand(1).setIsKill(KilledProdRegKill);
MI.getOperand(3).setIsKill(AddRegKill);
MI.getOperand(1).setIsUndef(KilledProdRegUndef);
MI.getOperand(3).setIsUndef(AddRegUndef);
MI.setDesc(TII->get(AltOpc));
if (OtherProdReg == AddendMI->getOperand(0).getReg()) {
MI.getOperand(2).setReg(AddendSrcReg);
MI.getOperand(2).setSubReg(AddSubReg);
MI.getOperand(2).setIsKill(AddRegKill);
MI.getOperand(2).setIsUndef(AddRegUndef);
} else {
MI.getOperand(2).setReg(OtherProdReg);
MI.getOperand(2).setSubReg(OtherProdSubReg);
MI.getOperand(2).setIsKill(OtherProdRegKill);
MI.getOperand(2).setIsUndef(OtherProdRegUndef);
}
LLVM_DEBUG(dbgs() << " -> " << MI);
LiveInterval &FMAInt = LIS->getInterval(OldFMAReg);
VNInfo *FMAValNo = FMAInt.getVNInfoAt(FMAIdx.getRegSlot());
for (auto UI = MRI.reg_nodbg_begin(OldFMAReg), UE = MRI.reg_nodbg_end();
UI != UE;) {
MachineOperand &UseMO = *UI;
MachineInstr *UseMI = UseMO.getParent();
++UI;
if (UseMI == AddendMI)
continue;
UseMO.substVirtReg(KilledProdReg, KilledProdSubReg, *TRI);
}
LiveInterval &NewFMAInt = LIS->getInterval(KilledProdReg);
for (auto &AI : FMAInt) {
if (AI.valno == AddendValNo)
continue;
VNInfo *NewFMAValNo =
NewFMAInt.getNextValue(AI.start, LIS->getVNInfoAllocator());
NewFMAInt.addSegment(
LiveInterval::Segment(AI.start, AI.end, NewFMAValNo));
}
LLVM_DEBUG(dbgs() << " extended: " << NewFMAInt << '\n');
if (!AddendSrcReg.isVirtual())
for (MCRegUnitIterator Units(AddendSrcReg.asMCReg(), TRI);
Units.isValid(); ++Units) {
unsigned Unit = *Units;
LiveRange &AddendSrcRange = LIS->getRegUnit(Unit);
AddendSrcRange.extendInBlock(LIS->getMBBStartIdx(&MBB),
FMAIdx.getRegSlot());
LLVM_DEBUG(dbgs() << " extended: " << AddendSrcRange << '\n');
}
FMAInt.removeValNo(FMAValNo);
LLVM_DEBUG(dbgs() << " trimmed: " << FMAInt << '\n');
LLVM_DEBUG(dbgs() << " removing: " << *AddendMI << '\n');
LIS->RemoveMachineInstrFromMaps(*AddendMI);
AddendMI->eraseFromParent();
Changed = true;
}
return Changed;
}
public:
bool runOnMachineFunction(MachineFunction &MF) override {
if (skipFunction(MF.getFunction()))
return false;
const PPCSubtarget &STI = MF.getSubtarget<PPCSubtarget>();
if (!STI.hasVSX())
return false;
LIS = &getAnalysis<LiveIntervals>();
TII = STI.getInstrInfo();
bool Changed = false;
if (DisableVSXFMAMutate)
return Changed;
for (MachineBasicBlock &B : llvm::make_early_inc_range(MF))
if (processBlock(B))
Changed = true;
return Changed;
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<LiveIntervals>();
AU.addPreserved<LiveIntervals>();
AU.addRequired<SlotIndexes>();
AU.addPreserved<SlotIndexes>();
AU.addRequired<MachineDominatorTree>();
AU.addPreserved<MachineDominatorTree>();
MachineFunctionPass::getAnalysisUsage(AU);
}
};
}
INITIALIZE_PASS_BEGIN(PPCVSXFMAMutate, DEBUG_TYPE,
"PowerPC VSX FMA Mutation", false, false)
INITIALIZE_PASS_DEPENDENCY(LiveIntervals)
INITIALIZE_PASS_DEPENDENCY(SlotIndexes)
INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree)
INITIALIZE_PASS_END(PPCVSXFMAMutate, DEBUG_TYPE,
"PowerPC VSX FMA Mutation", false, false)
char &llvm::PPCVSXFMAMutateID = PPCVSXFMAMutate::ID;
char PPCVSXFMAMutate::ID = 0;
FunctionPass *llvm::createPPCVSXFMAMutatePass() {
return new PPCVSXFMAMutate();
}