| //===----- RISCVMergeBaseOffset.cpp - Optimise address calculations ------===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // Merge the offset of address calculation into the offset field | |
| // of instructions in a global address lowering sequence. This pass transforms: | |
| // lui vreg1, %hi(s) | |
| // addi vreg2, vreg1, %lo(s) | |
| // addi vreg3, verg2, Offset | |
| // | |
| // Into: | |
| // lui vreg1, %hi(s+Offset) | |
| // addi vreg2, vreg1, %lo(s+Offset) | |
| // | |
| // The transformation is carried out under certain conditions: | |
| // 1) The offset field in the base of global address lowering sequence is zero. | |
| // 2) The lowered global address has only one use. | |
| // | |
| // The offset field can be in a different form. This pass handles all of them. | |
| //===----------------------------------------------------------------------===// | |
| #include "RISCV.h" | |
| #include "RISCVTargetMachine.h" | |
| #include "llvm/CodeGen/Passes.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/TargetRegistry.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 { | |
| static char ID; | |
| const MachineFunction *MF; | |
| 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, unsigned GSReg, int64_t &Offset); | |
| RISCVMergeBaseOffsetOpt() : MachineFunctionPass(ID) {} | |
| MachineFunctionProperties getRequiredProperties() const override { | |
| return MachineFunctionProperties().set( | |
| MachineFunctionProperties::Property::IsSSA); | |
| } | |
| StringRef getPassName() const override { | |
| return RISCV_MERGE_BASE_OFFSET_NAME; | |
| } | |
| private: | |
| MachineRegisterInfo *MRI; | |
| std::set<MachineInstr *> DeadInstrs; | |
| }; | |
| }; // end anonymous namespace | |
| char RISCVMergeBaseOffsetOpt::ID = 0; | |
| INITIALIZE_PASS(RISCVMergeBaseOffsetOpt, "riscv-merge-base-offset", | |
| RISCV_MERGE_BASE_OFFSET_NAME, false, false) | |
| // Detect the pattern: | |
| // lui vreg1, %hi(s) | |
| // addi vreg2, vreg1, %lo(s) | |
| // | |
| // Pattern only accepted if: | |
| // 1) ADDI has only one use. | |
| // 2) LUI has only one use; which is the ADDI. | |
| // 3) Both ADDI and LUI have GlobalAddress type which indicates that these | |
| // are generated from global address lowering. | |
| // 4) Offset value in the Global Address is 0. | |
| bool RISCVMergeBaseOffsetOpt::detectLuiAddiGlobal(MachineInstr &HiLUI, | |
| MachineInstr *&LoADDI) { | |
| if (HiLUI.getOpcode() != RISCV::LUI || | |
| HiLUI.getOperand(1).getTargetFlags() != RISCVII::MO_HI || | |
| HiLUI.getOperand(1).getType() != MachineOperand::MO_GlobalAddress || | |
| HiLUI.getOperand(1).getOffset() != 0 || | |
| !MRI->hasOneUse(HiLUI.getOperand(0).getReg())) | |
| return false; | |
| unsigned HiLuiDestReg = HiLUI.getOperand(0).getReg(); | |
| LoADDI = MRI->use_begin(HiLuiDestReg)->getParent(); | |
| if (LoADDI->getOpcode() != RISCV::ADDI || | |
| LoADDI->getOperand(2).getTargetFlags() != RISCVII::MO_LO || | |
| LoADDI->getOperand(2).getType() != MachineOperand::MO_GlobalAddress || | |
| LoADDI->getOperand(2).getOffset() != 0 || | |
| !MRI->hasOneUse(LoADDI->getOperand(0).getReg())) | |
| return false; | |
| return true; | |
| } | |
| // Update the offset in HiLUI and LoADDI instructions. | |
| // Delete the tail instruction and update all the uses to use the | |
| // output from LoADDI. | |
| void RISCVMergeBaseOffsetOpt::foldOffset(MachineInstr &HiLUI, | |
| MachineInstr &LoADDI, | |
| MachineInstr &Tail, int64_t Offset) { | |
| // Put the offset back in HiLUI and the LoADDI | |
| HiLUI.getOperand(1).setOffset(Offset); | |
| LoADDI.getOperand(2).setOffset(Offset); | |
| // Delete the tail instruction. | |
| DeadInstrs.insert(&Tail); | |
| MRI->replaceRegWith(Tail.getOperand(0).getReg(), | |
| LoADDI.getOperand(0).getReg()); | |
| LLVM_DEBUG(dbgs() << " Merged offset " << Offset << " into base.\n" | |
| << " " << HiLUI << " " << LoADDI;); | |
| } | |
| // Detect patterns for large offsets that are passed into an ADD instruction. | |
| // | |
| // Base address lowering is of the form: | |
| // HiLUI: lui vreg1, %hi(s) | |
| // LoADDI: addi vreg2, vreg1, %lo(s) | |
| // / \ | |
| // / \ | |
| // / \ | |
| // / The large offset can be of two forms: \ | |
| // 1) Offset that has non zero bits in lower 2) Offset that has non zero | |
| // 12 bits and upper 20 bits bits in upper 20 bits only | |
| // OffseLUI: lui vreg3, 4 | |
| // OffsetTail: addi voff, vreg3, 188 OffsetTail: lui voff, 128 | |
| // \ / | |
| // \ / | |
| // \ / | |
| // \ / | |
| // TailAdd: add vreg4, vreg2, voff | |
| bool RISCVMergeBaseOffsetOpt::matchLargeOffset(MachineInstr &TailAdd, | |
| unsigned GAReg, | |
| int64_t &Offset) { | |
| assert((TailAdd.getOpcode() == RISCV::ADD) && "Expected ADD instruction!"); | |
| unsigned Rs = TailAdd.getOperand(1).getReg(); | |
| unsigned Rt = TailAdd.getOperand(2).getReg(); | |
| unsigned Reg = Rs == GAReg ? Rt : Rs; | |
| // Can't fold if the register has more than one use. | |
| if (!MRI->hasOneUse(Reg)) | |
| return false; | |
| // This can point to an ADDI or a LUI: | |
| MachineInstr &OffsetTail = *MRI->getVRegDef(Reg); | |
| if (OffsetTail.getOpcode() == RISCV::ADDI) { | |
| // The offset value has non zero bits in both %hi and %lo parts. | |
| // Detect an ADDI that feeds from a LUI instruction. | |
| 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; | |
| int64_t OffHi = OffsetLui.getOperand(1).getImm(); | |
| Offset = (OffHi << 12) + OffLo; | |
| LLVM_DEBUG(dbgs() << " Offset Instrs: " << OffsetTail | |
| << " " << OffsetLui); | |
| DeadInstrs.insert(&OffsetTail); | |
| DeadInstrs.insert(&OffsetLui); | |
| return true; | |
| } else if (OffsetTail.getOpcode() == RISCV::LUI) { | |
| // The offset value has all zero bits in the lower 12 bits. Only LUI | |
| // exists. | |
| LLVM_DEBUG(dbgs() << " Offset Instr: " << OffsetTail); | |
| Offset = OffsetTail.getOperand(1).getImm() << 12; | |
| DeadInstrs.insert(&OffsetTail); | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool RISCVMergeBaseOffsetOpt::detectAndFoldOffset(MachineInstr &HiLUI, | |
| MachineInstr &LoADDI) { | |
| unsigned DestReg = LoADDI.getOperand(0).getReg(); | |
| assert(MRI->hasOneUse(DestReg) && "expected one use for LoADDI"); | |
| // LoADDI has only one use. | |
| MachineInstr &Tail = *MRI->use_begin(DestReg)->getParent(); | |
| switch (Tail.getOpcode()) { | |
| default: | |
| LLVM_DEBUG(dbgs() << "Don't know how to get offset from this instr:" | |
| << Tail); | |
| return false; | |
| case RISCV::ADDI: { | |
| // Offset is simply an immediate operand. | |
| int64_t Offset = Tail.getOperand(2).getImm(); | |
| LLVM_DEBUG(dbgs() << " Offset Instr: " << Tail); | |
| foldOffset(HiLUI, LoADDI, Tail, Offset); | |
| return true; | |
| } break; | |
| case RISCV::ADD: { | |
| // The offset is too large to fit in the immediate field of ADDI. | |
| // This can be in two forms: | |
| // 1) LUI hi_Offset followed by: | |
| // ADDI lo_offset | |
| // This happens in case the offset has non zero bits in | |
| // both hi 20 and lo 12 bits. | |
| // 2) LUI (offset20) | |
| // This happens in case the lower 12 bits of the offset are zeros. | |
| int64_t Offset; | |
| if (!matchLargeOffset(Tail, DestReg, Offset)) | |
| return false; | |
| foldOffset(HiLUI, LoADDI, Tail, Offset); | |
| return true; | |
| } break; | |
| case RISCV::LB: | |
| case RISCV::LH: | |
| case RISCV::LW: | |
| case RISCV::LBU: | |
| case RISCV::LHU: | |
| case RISCV::LWU: | |
| case RISCV::LD: | |
| case RISCV::FLW: | |
| case RISCV::FLD: | |
| case RISCV::SB: | |
| case RISCV::SH: | |
| case RISCV::SW: | |
| case RISCV::SD: | |
| case RISCV::FSW: | |
| case RISCV::FSD: { | |
| // Transforms the sequence: Into: | |
| // HiLUI: lui vreg1, %hi(foo) ---> lui vreg1, %hi(foo+8) | |
| // LoADDI: addi vreg2, vreg1, %lo(foo) ---> lw vreg3, lo(foo+8)(vreg1) | |
| // Tail: lw vreg3, 8(vreg2) | |
| if (Tail.getOperand(1).isFI()) | |
| return false; | |
| // Register defined by LoADDI should be used in the base part of the | |
| // load\store instruction. Otherwise, no folding possible. | |
| unsigned BaseAddrReg = Tail.getOperand(1).getReg(); | |
| if (DestReg != BaseAddrReg) | |
| return false; | |
| MachineOperand &TailImmOp = Tail.getOperand(2); | |
| int64_t Offset = TailImmOp.getImm(); | |
| // Update the offsets in global address lowering. | |
| HiLUI.getOperand(1).setOffset(Offset); | |
| // Update the immediate in the Tail instruction to add the offset. | |
| Tail.RemoveOperand(2); | |
| MachineOperand &ImmOp = LoADDI.getOperand(2); | |
| ImmOp.setOffset(Offset); | |
| Tail.addOperand(ImmOp); | |
| // Update the base reg in the Tail instruction to feed from LUI. | |
| // Output of HiLUI is only used in LoADDI, no need to use | |
| // MRI->replaceRegWith(). | |
| Tail.getOperand(1).setReg(HiLUI.getOperand(0).getReg()); | |
| DeadInstrs.insert(&LoADDI); | |
| return true; | |
| } break; | |
| } | |
| return false; | |
| } | |
| bool RISCVMergeBaseOffsetOpt::runOnMachineFunction(MachineFunction &Fn) { | |
| if (skipFunction(Fn.getFunction())) | |
| return 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 with one use: " | |
| << *LoADDI->getOperand(2).getGlobal() << "\n"); | |
| // If the use count is only one, merge the offset | |
| detectAndFoldOffset(HiLUI, *LoADDI); | |
| } | |
| } | |
| // Delete dead instructions. | |
| for (auto *MI : DeadInstrs) | |
| MI->eraseFromParent(); | |
| return true; | |
| } | |
| /// Returns an instance of the Merge Base Offset Optimization pass. | |
| FunctionPass *llvm::createRISCVMergeBaseOffsetOptPass() { | |
| return new RISCVMergeBaseOffsetOpt(); | |
| } |