1 //===----- RISCVMergeBaseOffset.cpp - Optimise address calculations ------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Merge the offset of address calculation into the offset field
10 // of instructions in a global address lowering sequence. This pass transforms:
12 // addi vreg2, vreg1, %lo(s)
13 // addi vreg3, verg2, Offset
16 // lui vreg1, %hi(s+Offset)
17 // addi vreg2, vreg1, %lo(s+Offset)
19 // The transformation is carried out under certain conditions:
20 // 1) The offset field in the base of global address lowering sequence is zero.
21 // 2) The lowered global address has only one use.
23 // The offset field can be in a different form. This pass handles all of them.
24 //===----------------------------------------------------------------------===//
27 #include "RISCVTargetMachine.h"
28 #include "llvm/CodeGen/Passes.h"
29 #include "llvm/MC/TargetRegistry.h"
30 #include "llvm/Support/Debug.h"
31 #include "llvm/Target/TargetOptions.h"
35 #define DEBUG_TYPE "riscv-merge-base-offset"
36 #define RISCV_MERGE_BASE_OFFSET_NAME "RISCV Merge Base Offset"
39 struct RISCVMergeBaseOffsetOpt : public MachineFunctionPass {
41 bool runOnMachineFunction(MachineFunction &Fn) override;
42 bool detectLuiAddiGlobal(MachineInstr &LUI, MachineInstr *&ADDI);
44 bool detectAndFoldOffset(MachineInstr &HiLUI, MachineInstr &LoADDI);
45 void foldOffset(MachineInstr &HiLUI, MachineInstr &LoADDI, MachineInstr &Tail,
47 bool matchLargeOffset(MachineInstr &TailAdd, Register GSReg, int64_t &Offset);
48 RISCVMergeBaseOffsetOpt() : MachineFunctionPass(ID) {}
50 MachineFunctionProperties getRequiredProperties() const override {
51 return MachineFunctionProperties().set(
52 MachineFunctionProperties::Property::IsSSA);
55 void getAnalysisUsage(AnalysisUsage &AU) const override {
57 MachineFunctionPass::getAnalysisUsage(AU);
60 StringRef getPassName() const override {
61 return RISCV_MERGE_BASE_OFFSET_NAME;
65 MachineRegisterInfo *MRI;
66 std::set<MachineInstr *> DeadInstrs;
68 } // end anonymous namespace
70 char RISCVMergeBaseOffsetOpt::ID = 0;
71 INITIALIZE_PASS(RISCVMergeBaseOffsetOpt, DEBUG_TYPE,
72 RISCV_MERGE_BASE_OFFSET_NAME, false, false)
74 // Detect the pattern:
76 // addi vreg2, vreg1, %lo(s)
78 // Pattern only accepted if:
79 // 1) ADDI has only one use.
80 // 2) LUI has only one use; which is the ADDI.
81 // 3) Both ADDI and LUI have GlobalAddress type which indicates that these
82 // are generated from global address lowering.
83 // 4) Offset value in the Global Address is 0.
84 bool RISCVMergeBaseOffsetOpt::detectLuiAddiGlobal(MachineInstr &HiLUI,
85 MachineInstr *&LoADDI) {
86 if (HiLUI.getOpcode() != RISCV::LUI ||
87 HiLUI.getOperand(1).getTargetFlags() != RISCVII::MO_HI ||
88 HiLUI.getOperand(1).getType() != MachineOperand::MO_GlobalAddress ||
89 HiLUI.getOperand(1).getOffset() != 0 ||
90 !MRI->hasOneUse(HiLUI.getOperand(0).getReg()))
92 Register HiLuiDestReg = HiLUI.getOperand(0).getReg();
93 LoADDI = MRI->use_begin(HiLuiDestReg)->getParent();
94 if (LoADDI->getOpcode() != RISCV::ADDI ||
95 LoADDI->getOperand(2).getTargetFlags() != RISCVII::MO_LO ||
96 LoADDI->getOperand(2).getType() != MachineOperand::MO_GlobalAddress ||
97 LoADDI->getOperand(2).getOffset() != 0 ||
98 !MRI->hasOneUse(LoADDI->getOperand(0).getReg()))
103 // Update the offset in HiLUI and LoADDI instructions.
104 // Delete the tail instruction and update all the uses to use the
105 // output from LoADDI.
106 void RISCVMergeBaseOffsetOpt::foldOffset(MachineInstr &HiLUI,
107 MachineInstr &LoADDI,
108 MachineInstr &Tail, int64_t Offset) {
109 // Put the offset back in HiLUI and the LoADDI
110 HiLUI.getOperand(1).setOffset(Offset);
111 LoADDI.getOperand(2).setOffset(Offset);
112 // Delete the tail instruction.
113 DeadInstrs.insert(&Tail);
114 MRI->replaceRegWith(Tail.getOperand(0).getReg(),
115 LoADDI.getOperand(0).getReg());
116 LLVM_DEBUG(dbgs() << " Merged offset " << Offset << " into base.\n"
117 << " " << HiLUI << " " << LoADDI;);
120 // Detect patterns for large offsets that are passed into an ADD instruction.
122 // Base address lowering is of the form:
123 // HiLUI: lui vreg1, %hi(s)
124 // LoADDI: addi vreg2, vreg1, %lo(s)
128 // / The large offset can be of two forms: \
129 // 1) Offset that has non zero bits in lower 2) Offset that has non zero
130 // 12 bits and upper 20 bits bits in upper 20 bits only
131 // OffseLUI: lui vreg3, 4
132 // OffsetTail: addi voff, vreg3, 188 OffsetTail: lui voff, 128
137 // TailAdd: add vreg4, vreg2, voff
138 bool RISCVMergeBaseOffsetOpt::matchLargeOffset(MachineInstr &TailAdd,
141 assert((TailAdd.getOpcode() == RISCV::ADD) && "Expected ADD instruction!");
142 Register Rs = TailAdd.getOperand(1).getReg();
143 Register Rt = TailAdd.getOperand(2).getReg();
144 Register Reg = Rs == GAReg ? Rt : Rs;
146 // Can't fold if the register has more than one use.
147 if (!MRI->hasOneUse(Reg))
149 // This can point to an ADDI or a LUI:
150 MachineInstr &OffsetTail = *MRI->getVRegDef(Reg);
151 if (OffsetTail.getOpcode() == RISCV::ADDI) {
152 // The offset value has non zero bits in both %hi and %lo parts.
153 // Detect an ADDI that feeds from a LUI instruction.
154 MachineOperand &AddiImmOp = OffsetTail.getOperand(2);
155 if (AddiImmOp.getTargetFlags() != RISCVII::MO_None)
157 int64_t OffLo = AddiImmOp.getImm();
158 MachineInstr &OffsetLui =
159 *MRI->getVRegDef(OffsetTail.getOperand(1).getReg());
160 MachineOperand &LuiImmOp = OffsetLui.getOperand(1);
161 if (OffsetLui.getOpcode() != RISCV::LUI ||
162 LuiImmOp.getTargetFlags() != RISCVII::MO_None ||
163 !MRI->hasOneUse(OffsetLui.getOperand(0).getReg()))
165 int64_t OffHi = OffsetLui.getOperand(1).getImm();
166 Offset = (OffHi << 12) + OffLo;
167 LLVM_DEBUG(dbgs() << " Offset Instrs: " << OffsetTail
168 << " " << OffsetLui);
169 DeadInstrs.insert(&OffsetTail);
170 DeadInstrs.insert(&OffsetLui);
172 } else if (OffsetTail.getOpcode() == RISCV::LUI) {
173 // The offset value has all zero bits in the lower 12 bits. Only LUI
175 LLVM_DEBUG(dbgs() << " Offset Instr: " << OffsetTail);
176 Offset = OffsetTail.getOperand(1).getImm() << 12;
177 DeadInstrs.insert(&OffsetTail);
183 bool RISCVMergeBaseOffsetOpt::detectAndFoldOffset(MachineInstr &HiLUI,
184 MachineInstr &LoADDI) {
185 Register DestReg = LoADDI.getOperand(0).getReg();
186 assert(MRI->hasOneUse(DestReg) && "expected one use for LoADDI");
187 // LoADDI has only one use.
188 MachineInstr &Tail = *MRI->use_begin(DestReg)->getParent();
189 switch (Tail.getOpcode()) {
191 LLVM_DEBUG(dbgs() << "Don't know how to get offset from this instr:"
195 // Offset is simply an immediate operand.
196 int64_t Offset = Tail.getOperand(2).getImm();
197 LLVM_DEBUG(dbgs() << " Offset Instr: " << Tail);
198 foldOffset(HiLUI, LoADDI, Tail, Offset);
202 // The offset is too large to fit in the immediate field of ADDI.
203 // This can be in two forms:
204 // 1) LUI hi_Offset followed by:
206 // This happens in case the offset has non zero bits in
207 // both hi 20 and lo 12 bits.
209 // This happens in case the lower 12 bits of the offset are zeros.
211 if (!matchLargeOffset(Tail, DestReg, Offset))
213 foldOffset(HiLUI, LoADDI, Tail, Offset);
233 // Transforms the sequence: Into:
234 // HiLUI: lui vreg1, %hi(foo) ---> lui vreg1, %hi(foo+8)
235 // LoADDI: addi vreg2, vreg1, %lo(foo) ---> lw vreg3, lo(foo+8)(vreg1)
236 // Tail: lw vreg3, 8(vreg2)
237 if (Tail.getOperand(1).isFI())
239 // Register defined by LoADDI should be used in the base part of the
240 // load\store instruction. Otherwise, no folding possible.
241 Register BaseAddrReg = Tail.getOperand(1).getReg();
242 if (DestReg != BaseAddrReg)
244 MachineOperand &TailImmOp = Tail.getOperand(2);
245 int64_t Offset = TailImmOp.getImm();
246 // Update the offsets in global address lowering.
247 HiLUI.getOperand(1).setOffset(Offset);
248 // Update the immediate in the Tail instruction to add the offset.
249 Tail.RemoveOperand(2);
250 MachineOperand &ImmOp = LoADDI.getOperand(2);
251 ImmOp.setOffset(Offset);
252 Tail.addOperand(ImmOp);
253 // Update the base reg in the Tail instruction to feed from LUI.
254 // Output of HiLUI is only used in LoADDI, no need to use
255 // MRI->replaceRegWith().
256 Tail.getOperand(1).setReg(HiLUI.getOperand(0).getReg());
257 DeadInstrs.insert(&LoADDI);
264 bool RISCVMergeBaseOffsetOpt::runOnMachineFunction(MachineFunction &Fn) {
265 if (skipFunction(Fn.getFunction()))
268 bool MadeChange = false;
270 MRI = &Fn.getRegInfo();
271 for (MachineBasicBlock &MBB : Fn) {
272 LLVM_DEBUG(dbgs() << "MBB: " << MBB.getName() << "\n");
273 for (MachineInstr &HiLUI : MBB) {
274 MachineInstr *LoADDI = nullptr;
275 if (!detectLuiAddiGlobal(HiLUI, LoADDI))
277 LLVM_DEBUG(dbgs() << " Found lowered global address with one use: "
278 << *LoADDI->getOperand(2).getGlobal() << "\n");
279 // If the use count is only one, merge the offset
280 MadeChange |= detectAndFoldOffset(HiLUI, *LoADDI);
283 // Delete dead instructions.
284 for (auto *MI : DeadInstrs)
285 MI->eraseFromParent();
289 /// Returns an instance of the Merge Base Offset Optimization pass.
290 FunctionPass *llvm::createRISCVMergeBaseOffsetOptPass() {
291 return new RISCVMergeBaseOffsetOpt();