#include "RISCV.h"
#include "RISCVSubtarget.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/RegisterScavenging.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Debug.h"
using namespace llvm;
#define DEBUG_TYPE "riscv-make-compressible"
#define RISCV_COMPRESS_INSTRS_NAME "RISCV Make Compressible"
namespace {
struct RISCVMakeCompressibleOpt : public MachineFunctionPass {
static char ID;
bool runOnMachineFunction(MachineFunction &Fn) override;
RISCVMakeCompressibleOpt() : MachineFunctionPass(ID) {
initializeRISCVMakeCompressibleOptPass(*PassRegistry::getPassRegistry());
}
StringRef getPassName() const override { return RISCV_COMPRESS_INSTRS_NAME; }
};
}
char RISCVMakeCompressibleOpt::ID = 0;
INITIALIZE_PASS(RISCVMakeCompressibleOpt, "riscv-make-compressible",
RISCV_COMPRESS_INSTRS_NAME, false, false)
static unsigned log2LdstWidth(unsigned Opcode) {
switch (Opcode) {
default:
llvm_unreachable("Unexpected opcode");
case RISCV::LW:
case RISCV::SW:
case RISCV::FLW:
case RISCV::FSW:
return 2;
case RISCV::LD:
case RISCV::SD:
case RISCV::FLD:
case RISCV::FSD:
return 3;
}
}
static uint8_t compressedLDSTOffsetMask(unsigned Opcode) {
return 0x1f << log2LdstWidth(Opcode);
}
static bool compressibleSPOffset(int64_t Offset, unsigned Opcode) {
return log2LdstWidth(Opcode) == 2 ? isShiftedUInt<6, 2>(Offset)
: isShiftedUInt<6, 3>(Offset);
}
static int64_t getBaseAdjustForCompression(int64_t Offset, unsigned Opcode) {
return Offset & ~compressedLDSTOffsetMask(Opcode);
}
static bool isCompressedReg(Register Reg) {
return RISCV::GPRCRegClass.contains(Reg) ||
RISCV::FPR32CRegClass.contains(Reg) ||
RISCV::FPR64CRegClass.contains(Reg);
}
static bool isCompressibleLoad(const MachineInstr &MI) {
const RISCVSubtarget &STI = MI.getMF()->getSubtarget<RISCVSubtarget>();
const unsigned Opcode = MI.getOpcode();
return Opcode == RISCV::LW || (!STI.is64Bit() && Opcode == RISCV::FLW) ||
Opcode == RISCV::LD || Opcode == RISCV::FLD;
}
static bool isCompressibleStore(const MachineInstr &MI) {
const RISCVSubtarget &STI = MI.getMF()->getSubtarget<RISCVSubtarget>();
const unsigned Opcode = MI.getOpcode();
return Opcode == RISCV::SW || (!STI.is64Bit() && Opcode == RISCV::FSW) ||
Opcode == RISCV::SD || Opcode == RISCV::FSD;
}
static RegImmPair getRegImmPairPreventingCompression(const MachineInstr &MI) {
const unsigned Opcode = MI.getOpcode();
if (isCompressibleLoad(MI) || isCompressibleStore(MI)) {
const MachineOperand &MOImm = MI.getOperand(2);
if (!MOImm.isImm())
return RegImmPair(RISCV::NoRegister, 0);
int64_t Offset = MOImm.getImm();
int64_t NewBaseAdjust = getBaseAdjustForCompression(Offset, Opcode);
Register Base = MI.getOperand(1).getReg();
if (RISCV::SPRegClass.contains(Base)) {
if (!compressibleSPOffset(Offset, Opcode) && NewBaseAdjust)
return RegImmPair(Base, NewBaseAdjust);
} else {
Register SrcDest = MI.getOperand(0).getReg();
bool SrcDestCompressed = isCompressedReg(SrcDest);
bool BaseCompressed = isCompressedReg(Base);
if ((!BaseCompressed || NewBaseAdjust) && SrcDestCompressed)
return RegImmPair(Base, NewBaseAdjust);
if (isCompressibleStore(MI)) {
if (!SrcDestCompressed && (BaseCompressed || SrcDest == Base) &&
!NewBaseAdjust)
return RegImmPair(SrcDest, NewBaseAdjust);
}
}
}
return RegImmPair(RISCV::NoRegister, 0);
}
static Register analyzeCompressibleUses(MachineInstr &FirstMI,
RegImmPair RegImm,
SmallVectorImpl<MachineInstr *> &MIs) {
MachineBasicBlock &MBB = *FirstMI.getParent();
const TargetRegisterInfo *TRI =
MBB.getParent()->getSubtarget().getRegisterInfo();
RegScavenger RS;
RS.enterBasicBlock(MBB);
for (MachineBasicBlock::instr_iterator I = FirstMI.getIterator(),
E = MBB.instr_end();
I != E; ++I) {
MachineInstr &MI = *I;
RegImmPair CandidateRegImm = getRegImmPairPreventingCompression(MI);
if (CandidateRegImm.Reg == RegImm.Reg &&
CandidateRegImm.Imm == RegImm.Imm) {
RS.forward(I);
MIs.push_back(&MI);
}
if (MI.modifiesRegister(RegImm.Reg, TRI))
break;
}
if (MIs.size() < 2 || (RegImm.Imm != 0 && MIs.size() < 3))
return RISCV::NoRegister;
const TargetRegisterClass *RCToScavenge;
if (RISCV::GPRRegClass.contains(RegImm.Reg))
RCToScavenge = &RISCV::GPRCRegClass;
else if (RISCV::FPR32RegClass.contains(RegImm.Reg))
RCToScavenge = &RISCV::FPR32CRegClass;
else if (RISCV::FPR64RegClass.contains(RegImm.Reg))
RCToScavenge = &RISCV::FPR64CRegClass;
else
return RISCV::NoRegister;
return RS.scavengeRegisterBackwards(*RCToScavenge, FirstMI.getIterator(),
false, 0,
false);
}
static void updateOperands(MachineInstr &MI, RegImmPair OldRegImm,
Register NewReg) {
unsigned Opcode = MI.getOpcode();
assert((isCompressibleLoad(MI) || isCompressibleStore(MI)) &&
"Unsupported instruction for this optimization.");
int SkipN = 0;
if (isCompressibleStore(MI) && OldRegImm.Imm != 0)
SkipN = 1;
for (MachineOperand &MO : drop_begin(MI.operands(), SkipN))
if (MO.isReg() && MO.getReg() == OldRegImm.Reg) {
if (MO.isDef()) {
assert(isCompressibleLoad(MI));
continue;
}
MO.setReg(NewReg);
}
MachineOperand &MOImm = MI.getOperand(2);
int64_t NewOffset = MOImm.getImm() & compressedLDSTOffsetMask(Opcode);
MOImm.setImm(NewOffset);
}
bool RISCVMakeCompressibleOpt::runOnMachineFunction(MachineFunction &Fn) {
if (skipFunction(Fn.getFunction()) || !Fn.getFunction().hasMinSize())
return false;
const RISCVSubtarget &STI = Fn.getSubtarget<RISCVSubtarget>();
const RISCVInstrInfo &TII = *STI.getInstrInfo();
if (!STI.hasStdExtC())
return false;
for (MachineBasicBlock &MBB : Fn) {
LLVM_DEBUG(dbgs() << "MBB: " << MBB.getName() << "\n");
for (MachineInstr &MI : MBB) {
RegImmPair RegImm = getRegImmPairPreventingCompression(MI);
if (!RegImm.Reg && RegImm.Imm == 0)
continue;
SmallVector<MachineInstr *, 8> MIs;
Register NewReg = analyzeCompressibleUses(MI, RegImm, MIs);
if (!NewReg)
continue;
if (RISCV::GPRRegClass.contains(RegImm.Reg)) {
assert(isInt<12>(RegImm.Imm));
BuildMI(MBB, MI, MI.getDebugLoc(), TII.get(RISCV::ADDI), NewReg)
.addReg(RegImm.Reg)
.addImm(RegImm.Imm);
} else {
assert(RegImm.Imm == 0);
unsigned Opcode = RISCV::FPR32RegClass.contains(RegImm.Reg)
? RISCV::FSGNJ_S
: RISCV::FSGNJ_D;
BuildMI(MBB, MI, MI.getDebugLoc(), TII.get(Opcode), NewReg)
.addReg(RegImm.Reg)
.addReg(RegImm.Reg);
}
for (MachineInstr *UpdateMI : MIs)
updateOperands(*UpdateMI, RegImm, NewReg);
}
}
return true;
}
FunctionPass *llvm::createRISCVMakeCompressibleOptPass() {
return new RISCVMakeCompressibleOpt();
}