|  | //===----- RISCVMergeBaseOffset.cpp - Optimise address calculations  ------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // 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(); | 
|  | } |