#include "RISCV.h"
#include "RISCVTargetMachine.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Debug.h"
#include "llvm/Target/TargetOptions.h"
#include <set>
using namespace llvm;
#define DEBUG_TYPE "riscv-merge-base-offset"
#define RISCV_MERGE_BASE_OFFSET_NAME "RISCV Merge Base Offset"
namespace {
struct RISCVMergeBaseOffsetOpt : public MachineFunctionPass {
private:
const RISCVSubtarget *ST = nullptr;
public:
static char ID;
bool runOnMachineFunction(MachineFunction &Fn) override;
bool detectLuiAddiGlobal(MachineInstr &LUI, MachineInstr *&ADDI);
bool detectAndFoldOffset(MachineInstr &HiLUI, MachineInstr &LoADDI);
void foldOffset(MachineInstr &HiLUI, MachineInstr &LoADDI, MachineInstr &Tail,
int64_t Offset);
bool matchLargeOffset(MachineInstr &TailAdd, Register GSReg, int64_t &Offset);
bool matchShiftedOffset(MachineInstr &TailShXAdd, Register GSReg,
int64_t &Offset);
RISCVMergeBaseOffsetOpt() : MachineFunctionPass(ID) {}
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::IsSSA);
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
MachineFunctionPass::getAnalysisUsage(AU);
}
StringRef getPassName() const override {
return RISCV_MERGE_BASE_OFFSET_NAME;
}
private:
MachineRegisterInfo *MRI;
std::set<MachineInstr *> DeadInstrs;
};
}
char RISCVMergeBaseOffsetOpt::ID = 0;
INITIALIZE_PASS(RISCVMergeBaseOffsetOpt, DEBUG_TYPE,
RISCV_MERGE_BASE_OFFSET_NAME, false, false)
bool RISCVMergeBaseOffsetOpt::detectLuiAddiGlobal(MachineInstr &HiLUI,
MachineInstr *&LoADDI) {
if (HiLUI.getOpcode() != RISCV::LUI ||
HiLUI.getOperand(1).getTargetFlags() != RISCVII::MO_HI ||
!HiLUI.getOperand(1).isGlobal() ||
HiLUI.getOperand(1).getOffset() != 0 ||
!MRI->hasOneUse(HiLUI.getOperand(0).getReg()))
return false;
Register HiLuiDestReg = HiLUI.getOperand(0).getReg();
LoADDI = &*MRI->use_instr_begin(HiLuiDestReg);
if (LoADDI->getOpcode() != RISCV::ADDI ||
LoADDI->getOperand(2).getTargetFlags() != RISCVII::MO_LO ||
!LoADDI->getOperand(2).isGlobal() ||
LoADDI->getOperand(2).getOffset() != 0)
return false;
return true;
}
void RISCVMergeBaseOffsetOpt::foldOffset(MachineInstr &HiLUI,
MachineInstr &LoADDI,
MachineInstr &Tail, int64_t Offset) {
assert(isInt<32>(Offset) && "Unexpected offset");
HiLUI.getOperand(1).setOffset(Offset);
LoADDI.getOperand(2).setOffset(Offset);
DeadInstrs.insert(&Tail);
MRI->replaceRegWith(Tail.getOperand(0).getReg(),
LoADDI.getOperand(0).getReg());
LLVM_DEBUG(dbgs() << " Merged offset " << Offset << " into base.\n"
<< " " << HiLUI << " " << LoADDI;);
}
bool RISCVMergeBaseOffsetOpt::matchLargeOffset(MachineInstr &TailAdd,
Register GAReg,
int64_t &Offset) {
assert((TailAdd.getOpcode() == RISCV::ADD) && "Expected ADD instruction!");
Register Rs = TailAdd.getOperand(1).getReg();
Register Rt = TailAdd.getOperand(2).getReg();
Register Reg = Rs == GAReg ? Rt : Rs;
if (!MRI->hasOneUse(Reg))
return false;
MachineInstr &OffsetTail = *MRI->getVRegDef(Reg);
if (OffsetTail.getOpcode() == RISCV::ADDI ||
OffsetTail.getOpcode() == RISCV::ADDIW) {
MachineOperand &AddiImmOp = OffsetTail.getOperand(2);
if (AddiImmOp.getTargetFlags() != RISCVII::MO_None)
return false;
int64_t OffLo = AddiImmOp.getImm();
MachineInstr &OffsetLui =
*MRI->getVRegDef(OffsetTail.getOperand(1).getReg());
MachineOperand &LuiImmOp = OffsetLui.getOperand(1);
if (OffsetLui.getOpcode() != RISCV::LUI ||
LuiImmOp.getTargetFlags() != RISCVII::MO_None ||
!MRI->hasOneUse(OffsetLui.getOperand(0).getReg()))
return false;
Offset = SignExtend64<32>(LuiImmOp.getImm() << 12);
Offset += OffLo;
if (!ST->is64Bit() || OffsetTail.getOpcode() == RISCV::ADDIW)
Offset = SignExtend64<32>(Offset);
if (!isInt<32>(Offset))
return false;
LLVM_DEBUG(dbgs() << " Offset Instrs: " << OffsetTail
<< " " << OffsetLui);
DeadInstrs.insert(&OffsetTail);
DeadInstrs.insert(&OffsetLui);
return true;
} else if (OffsetTail.getOpcode() == RISCV::LUI) {
LLVM_DEBUG(dbgs() << " Offset Instr: " << OffsetTail);
Offset = SignExtend64<32>(OffsetTail.getOperand(1).getImm() << 12);
DeadInstrs.insert(&OffsetTail);
return true;
}
return false;
}
bool RISCVMergeBaseOffsetOpt::matchShiftedOffset(MachineInstr &TailShXAdd,
Register GAReg,
int64_t &Offset) {
assert((TailShXAdd.getOpcode() == RISCV::SH1ADD ||
TailShXAdd.getOpcode() == RISCV::SH2ADD ||
TailShXAdd.getOpcode() == RISCV::SH3ADD) &&
"Expected SHXADD instruction!");
Register Rs1 = TailShXAdd.getOperand(1).getReg();
if (GAReg != TailShXAdd.getOperand(2).getReg())
return false;
if (!MRI->hasOneUse(Rs1))
return false;
MachineInstr &OffsetTail = *MRI->getVRegDef(Rs1);
if (OffsetTail.getOpcode() != RISCV::ADDI)
return false;
if (!OffsetTail.getOperand(1).isReg() ||
OffsetTail.getOperand(1).getReg() != RISCV::X0 ||
!OffsetTail.getOperand(2).isImm())
return false;
Offset = OffsetTail.getOperand(2).getImm();
assert(isInt<12>(Offset) && "Unexpected offset");
unsigned ShAmt;
switch (TailShXAdd.getOpcode()) {
default: llvm_unreachable("Unexpected opcode");
case RISCV::SH1ADD: ShAmt = 1; break;
case RISCV::SH2ADD: ShAmt = 2; break;
case RISCV::SH3ADD: ShAmt = 3; break;
}
Offset = (uint64_t)Offset << ShAmt;
LLVM_DEBUG(dbgs() << " Offset Instr: " << OffsetTail);
DeadInstrs.insert(&OffsetTail);
return true;
}
bool RISCVMergeBaseOffsetOpt::detectAndFoldOffset(MachineInstr &HiLUI,
MachineInstr &LoADDI) {
Register DestReg = LoADDI.getOperand(0).getReg();
if (MRI->hasOneUse(DestReg)) {
MachineInstr &Tail = *MRI->use_instr_begin(DestReg);
switch (Tail.getOpcode()) {
default:
LLVM_DEBUG(dbgs() << "Don't know how to get offset from this instr:"
<< Tail);
break;
case RISCV::ADDI: {
int64_t Offset = Tail.getOperand(2).getImm();
Register TailDestReg = Tail.getOperand(0).getReg();
if (MRI->hasOneUse(TailDestReg)) {
MachineInstr &TailTail = *MRI->use_instr_begin(TailDestReg);
if (TailTail.getOpcode() == RISCV::ADDI) {
Offset += TailTail.getOperand(2).getImm();
LLVM_DEBUG(dbgs() << " Offset Instrs: " << Tail << TailTail);
DeadInstrs.insert(&Tail);
foldOffset(HiLUI, LoADDI, TailTail, Offset);
return true;
}
}
LLVM_DEBUG(dbgs() << " Offset Instr: " << Tail);
foldOffset(HiLUI, LoADDI, Tail, Offset);
return true;
}
case RISCV::ADD: {
int64_t Offset;
if (!matchLargeOffset(Tail, DestReg, Offset))
return false;
foldOffset(HiLUI, LoADDI, Tail, Offset);
return true;
}
case RISCV::SH1ADD:
case RISCV::SH2ADD:
case RISCV::SH3ADD: {
int64_t Offset;
if (!matchShiftedOffset(Tail, DestReg, Offset))
return false;
foldOffset(HiLUI, LoADDI, Tail, Offset);
return true;
}
}
}
Optional<int64_t> CommonOffset;
for (const MachineInstr &UseMI : MRI->use_instructions(DestReg)) {
switch (UseMI.getOpcode()) {
default:
LLVM_DEBUG(dbgs() << "Not a load or store instruction: " << UseMI);
return false;
case RISCV::LB:
case RISCV::LH:
case RISCV::LW:
case RISCV::LBU:
case RISCV::LHU:
case RISCV::LWU:
case RISCV::LD:
case RISCV::FLH:
case RISCV::FLW:
case RISCV::FLD:
case RISCV::SB:
case RISCV::SH:
case RISCV::SW:
case RISCV::SD:
case RISCV::FSH:
case RISCV::FSW:
case RISCV::FSD: {
if (UseMI.getOperand(1).isFI())
return false;
if (DestReg == UseMI.getOperand(0).getReg())
return false;
assert(DestReg == UseMI.getOperand(1).getReg() &&
"Expected base address use");
int64_t Offset = UseMI.getOperand(2).getImm();
if (CommonOffset && Offset != CommonOffset)
return false;
CommonOffset = Offset;
}
}
}
HiLUI.getOperand(1).setOffset(*CommonOffset);
MachineOperand &ImmOp = LoADDI.getOperand(2);
ImmOp.setOffset(*CommonOffset);
for (MachineInstr &UseMI :
llvm::make_early_inc_range(MRI->use_instructions(DestReg))) {
UseMI.removeOperand(2);
UseMI.addOperand(ImmOp);
UseMI.getOperand(1).setReg(HiLUI.getOperand(0).getReg());
}
DeadInstrs.insert(&LoADDI);
return true;
}
bool RISCVMergeBaseOffsetOpt::runOnMachineFunction(MachineFunction &Fn) {
if (skipFunction(Fn.getFunction()))
return false;
ST = &Fn.getSubtarget<RISCVSubtarget>();
bool MadeChange = false;
DeadInstrs.clear();
MRI = &Fn.getRegInfo();
for (MachineBasicBlock &MBB : Fn) {
LLVM_DEBUG(dbgs() << "MBB: " << MBB.getName() << "\n");
for (MachineInstr &HiLUI : MBB) {
MachineInstr *LoADDI = nullptr;
if (!detectLuiAddiGlobal(HiLUI, LoADDI))
continue;
LLVM_DEBUG(dbgs() << " Found lowered global address: "
<< *LoADDI->getOperand(2).getGlobal() << "\n");
MadeChange |= detectAndFoldOffset(HiLUI, *LoADDI);
}
}
for (auto *MI : DeadInstrs)
MI->eraseFromParent();
return MadeChange;
}
FunctionPass *llvm::createRISCVMergeBaseOffsetOptPass() {
return new RISCVMergeBaseOffsetOpt();
}