1 //===----- RISCVMergeBaseOffset.cpp - Optimise address calculations ------===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Merge the offset of address calculation into the offset field 10 // of instructions in a global address lowering sequence. This pass transforms: 11 // lui vreg1, %hi(s) 12 // addi vreg2, vreg1, %lo(s) 13 // addi vreg3, verg2, Offset 14 // 15 // Into: 16 // lui vreg1, %hi(s+Offset) 17 // addi vreg2, vreg1, %lo(s+Offset) 18 // 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. 22 // 23 // The offset field can be in a different form. This pass handles all of them. 24 //===----------------------------------------------------------------------===// 25 26 #include "RISCV.h" 27 #include "RISCVTargetMachine.h" 28 #include "llvm/CodeGen/MachineFunctionPass.h" 29 #include "llvm/CodeGen/Passes.h" 30 #include "llvm/MC/TargetRegistry.h" 31 #include "llvm/Support/Debug.h" 32 #include "llvm/Target/TargetOptions.h" 33 #include <set> 34 using namespace llvm; 35 36 #define DEBUG_TYPE "riscv-merge-base-offset" 37 #define RISCV_MERGE_BASE_OFFSET_NAME "RISCV Merge Base Offset" 38 namespace { 39 40 struct RISCVMergeBaseOffsetOpt : public MachineFunctionPass { 41 private: 42 const RISCVSubtarget *ST = nullptr; 43 44 public: 45 static char ID; 46 bool runOnMachineFunction(MachineFunction &Fn) override; 47 bool detectLuiAddiGlobal(MachineInstr &LUI, MachineInstr *&ADDI); 48 49 bool detectAndFoldOffset(MachineInstr &HiLUI, MachineInstr &LoADDI); 50 void foldOffset(MachineInstr &HiLUI, MachineInstr &LoADDI, MachineInstr &Tail, 51 int64_t Offset); 52 bool matchLargeOffset(MachineInstr &TailAdd, Register GSReg, int64_t &Offset); 53 bool matchShiftedOffset(MachineInstr &TailShXAdd, Register GSReg, 54 int64_t &Offset); 55 56 RISCVMergeBaseOffsetOpt() : MachineFunctionPass(ID) {} 57 58 MachineFunctionProperties getRequiredProperties() const override { 59 return MachineFunctionProperties().set( 60 MachineFunctionProperties::Property::IsSSA); 61 } 62 63 void getAnalysisUsage(AnalysisUsage &AU) const override { 64 AU.setPreservesCFG(); 65 MachineFunctionPass::getAnalysisUsage(AU); 66 } 67 68 StringRef getPassName() const override { 69 return RISCV_MERGE_BASE_OFFSET_NAME; 70 } 71 72 private: 73 MachineRegisterInfo *MRI; 74 std::set<MachineInstr *> DeadInstrs; 75 }; 76 } // end anonymous namespace 77 78 char RISCVMergeBaseOffsetOpt::ID = 0; 79 INITIALIZE_PASS(RISCVMergeBaseOffsetOpt, DEBUG_TYPE, 80 RISCV_MERGE_BASE_OFFSET_NAME, false, false) 81 82 // Detect the pattern: 83 // lui vreg1, %hi(s) 84 // addi vreg2, vreg1, %lo(s) 85 // 86 // Pattern only accepted if: 87 // 1) ADDI has only one use. 88 // 2) LUI has only one use; which is the ADDI. 89 // 3) Both ADDI and LUI have GlobalAddress type which indicates that these 90 // are generated from global address lowering. 91 // 4) Offset value in the Global Address is 0. 92 bool RISCVMergeBaseOffsetOpt::detectLuiAddiGlobal(MachineInstr &HiLUI, 93 MachineInstr *&LoADDI) { 94 if (HiLUI.getOpcode() != RISCV::LUI || 95 HiLUI.getOperand(1).getTargetFlags() != RISCVII::MO_HI || 96 HiLUI.getOperand(1).getType() != MachineOperand::MO_GlobalAddress || 97 HiLUI.getOperand(1).getOffset() != 0 || 98 !MRI->hasOneUse(HiLUI.getOperand(0).getReg())) 99 return false; 100 Register HiLuiDestReg = HiLUI.getOperand(0).getReg(); 101 LoADDI = &*MRI->use_instr_begin(HiLuiDestReg); 102 if (LoADDI->getOpcode() != RISCV::ADDI || 103 LoADDI->getOperand(2).getTargetFlags() != RISCVII::MO_LO || 104 LoADDI->getOperand(2).getType() != MachineOperand::MO_GlobalAddress || 105 LoADDI->getOperand(2).getOffset() != 0 || 106 !MRI->hasOneUse(LoADDI->getOperand(0).getReg())) 107 return false; 108 return true; 109 } 110 111 // Update the offset in HiLUI and LoADDI instructions. 112 // Delete the tail instruction and update all the uses to use the 113 // output from LoADDI. 114 void RISCVMergeBaseOffsetOpt::foldOffset(MachineInstr &HiLUI, 115 MachineInstr &LoADDI, 116 MachineInstr &Tail, int64_t Offset) { 117 assert(isInt<32>(Offset) && "Unexpected offset"); 118 // Put the offset back in HiLUI and the LoADDI 119 HiLUI.getOperand(1).setOffset(Offset); 120 LoADDI.getOperand(2).setOffset(Offset); 121 // Delete the tail instruction. 122 DeadInstrs.insert(&Tail); 123 MRI->replaceRegWith(Tail.getOperand(0).getReg(), 124 LoADDI.getOperand(0).getReg()); 125 LLVM_DEBUG(dbgs() << " Merged offset " << Offset << " into base.\n" 126 << " " << HiLUI << " " << LoADDI;); 127 } 128 129 // Detect patterns for large offsets that are passed into an ADD instruction. 130 // 131 // Base address lowering is of the form: 132 // HiLUI: lui vreg1, %hi(s) 133 // LoADDI: addi vreg2, vreg1, %lo(s) 134 // / \ 135 // / \ 136 // / \ 137 // / The large offset can be of two forms: \ 138 // 1) Offset that has non zero bits in lower 2) Offset that has non zero 139 // 12 bits and upper 20 bits bits in upper 20 bits only 140 // OffseLUI: lui vreg3, 4 141 // OffsetTail: addi voff, vreg3, 188 OffsetTail: lui voff, 128 142 // \ / 143 // \ / 144 // \ / 145 // \ / 146 // TailAdd: add vreg4, vreg2, voff 147 bool RISCVMergeBaseOffsetOpt::matchLargeOffset(MachineInstr &TailAdd, 148 Register GAReg, 149 int64_t &Offset) { 150 assert((TailAdd.getOpcode() == RISCV::ADD) && "Expected ADD instruction!"); 151 Register Rs = TailAdd.getOperand(1).getReg(); 152 Register Rt = TailAdd.getOperand(2).getReg(); 153 Register Reg = Rs == GAReg ? Rt : Rs; 154 155 // Can't fold if the register has more than one use. 156 if (!MRI->hasOneUse(Reg)) 157 return false; 158 // This can point to an ADDI or a LUI: 159 MachineInstr &OffsetTail = *MRI->getVRegDef(Reg); 160 if (OffsetTail.getOpcode() == RISCV::ADDI || 161 OffsetTail.getOpcode() == RISCV::ADDIW) { 162 // The offset value has non zero bits in both %hi and %lo parts. 163 // Detect an ADDI that feeds from a LUI instruction. 164 MachineOperand &AddiImmOp = OffsetTail.getOperand(2); 165 if (AddiImmOp.getTargetFlags() != RISCVII::MO_None) 166 return false; 167 int64_t OffLo = AddiImmOp.getImm(); 168 MachineInstr &OffsetLui = 169 *MRI->getVRegDef(OffsetTail.getOperand(1).getReg()); 170 MachineOperand &LuiImmOp = OffsetLui.getOperand(1); 171 if (OffsetLui.getOpcode() != RISCV::LUI || 172 LuiImmOp.getTargetFlags() != RISCVII::MO_None || 173 !MRI->hasOneUse(OffsetLui.getOperand(0).getReg())) 174 return false; 175 Offset = SignExtend64<32>(LuiImmOp.getImm() << 12); 176 Offset += OffLo; 177 // RV32 ignores the upper 32 bits. ADDIW sign extends the result. 178 if (!ST->is64Bit() || OffsetTail.getOpcode() == RISCV::ADDIW) 179 Offset = SignExtend64<32>(Offset); 180 // We can only fold simm32 offsets. 181 if (!isInt<32>(Offset)) 182 return false; 183 LLVM_DEBUG(dbgs() << " Offset Instrs: " << OffsetTail 184 << " " << OffsetLui); 185 DeadInstrs.insert(&OffsetTail); 186 DeadInstrs.insert(&OffsetLui); 187 return true; 188 } else if (OffsetTail.getOpcode() == RISCV::LUI) { 189 // The offset value has all zero bits in the lower 12 bits. Only LUI 190 // exists. 191 LLVM_DEBUG(dbgs() << " Offset Instr: " << OffsetTail); 192 Offset = SignExtend64<32>(OffsetTail.getOperand(1).getImm() << 12); 193 DeadInstrs.insert(&OffsetTail); 194 return true; 195 } 196 return false; 197 } 198 199 // Detect patterns for offsets that are passed into a SHXADD instruction. 200 // The offset has 1,2, or 3 trailing zeros and fits in simm13, simm14, simm15. 201 // The constant is created with addi voff, x0, C, and shXadd is used to 202 // fill insert the trailing zeros and do the addition. 203 // 204 // HiLUI: lui vreg1, %hi(s) 205 // LoADDI: addi vreg2, vreg1, %lo(s) 206 // OffsetTail: addi voff, x0, C 207 // TailAdd: shXadd vreg4, voff, vreg2 208 bool RISCVMergeBaseOffsetOpt::matchShiftedOffset(MachineInstr &TailShXAdd, 209 Register GAReg, 210 int64_t &Offset) { 211 assert((TailShXAdd.getOpcode() == RISCV::SH1ADD || 212 TailShXAdd.getOpcode() == RISCV::SH2ADD || 213 TailShXAdd.getOpcode() == RISCV::SH3ADD) && 214 "Expected SHXADD instruction!"); 215 216 // The first source is the shifted operand. 217 Register Rs1 = TailShXAdd.getOperand(1).getReg(); 218 219 if (GAReg != TailShXAdd.getOperand(2).getReg()) 220 return false; 221 222 // Can't fold if the register has more than one use. 223 if (!MRI->hasOneUse(Rs1)) 224 return false; 225 // This can point to an ADDI X0, C. 226 MachineInstr &OffsetTail = *MRI->getVRegDef(Rs1); 227 if (OffsetTail.getOpcode() != RISCV::ADDI) 228 return false; 229 if (!OffsetTail.getOperand(1).isReg() || 230 OffsetTail.getOperand(1).getReg() != RISCV::X0 || 231 !OffsetTail.getOperand(2).isImm()) 232 return false; 233 234 Offset = OffsetTail.getOperand(2).getImm(); 235 assert(isInt<12>(Offset) && "Unexpected offset"); 236 237 unsigned ShAmt; 238 switch (TailShXAdd.getOpcode()) { 239 default: llvm_unreachable("Unexpected opcode"); 240 case RISCV::SH1ADD: ShAmt = 1; break; 241 case RISCV::SH2ADD: ShAmt = 2; break; 242 case RISCV::SH3ADD: ShAmt = 3; break; 243 } 244 245 Offset = (uint64_t)Offset << ShAmt; 246 247 LLVM_DEBUG(dbgs() << " Offset Instr: " << OffsetTail); 248 DeadInstrs.insert(&OffsetTail); 249 return true; 250 } 251 252 bool RISCVMergeBaseOffsetOpt::detectAndFoldOffset(MachineInstr &HiLUI, 253 MachineInstr &LoADDI) { 254 Register DestReg = LoADDI.getOperand(0).getReg(); 255 assert(MRI->hasOneUse(DestReg) && "expected one use for LoADDI"); 256 // LoADDI has only one use. 257 MachineInstr &Tail = *MRI->use_instr_begin(DestReg); 258 switch (Tail.getOpcode()) { 259 default: 260 LLVM_DEBUG(dbgs() << "Don't know how to get offset from this instr:" 261 << Tail); 262 return false; 263 case RISCV::ADDI: { 264 // Offset is simply an immediate operand. 265 int64_t Offset = Tail.getOperand(2).getImm(); 266 267 // We might have two ADDIs in a row. 268 Register TailDestReg = Tail.getOperand(0).getReg(); 269 if (MRI->hasOneUse(TailDestReg)) { 270 MachineInstr &TailTail = *MRI->use_instr_begin(TailDestReg); 271 if (TailTail.getOpcode() == RISCV::ADDI) { 272 Offset += TailTail.getOperand(2).getImm(); 273 LLVM_DEBUG(dbgs() << " Offset Instrs: " << Tail << TailTail); 274 DeadInstrs.insert(&Tail); 275 foldOffset(HiLUI, LoADDI, TailTail, Offset); 276 return true; 277 } 278 } 279 280 LLVM_DEBUG(dbgs() << " Offset Instr: " << Tail); 281 foldOffset(HiLUI, LoADDI, Tail, Offset); 282 return true; 283 } 284 case RISCV::ADD: { 285 // The offset is too large to fit in the immediate field of ADDI. 286 // This can be in two forms: 287 // 1) LUI hi_Offset followed by: 288 // ADDI lo_offset 289 // This happens in case the offset has non zero bits in 290 // both hi 20 and lo 12 bits. 291 // 2) LUI (offset20) 292 // This happens in case the lower 12 bits of the offset are zeros. 293 int64_t Offset; 294 if (!matchLargeOffset(Tail, DestReg, Offset)) 295 return false; 296 foldOffset(HiLUI, LoADDI, Tail, Offset); 297 return true; 298 } 299 case RISCV::SH1ADD: 300 case RISCV::SH2ADD: 301 case RISCV::SH3ADD: { 302 // The offset is too large to fit in the immediate field of ADDI. 303 // It may be encoded as (SH2ADD (ADDI X0, C), DestReg) or 304 // (SH3ADD (ADDI X0, C), DestReg). 305 int64_t Offset; 306 if (!matchShiftedOffset(Tail, DestReg, Offset)) 307 return false; 308 foldOffset(HiLUI, LoADDI, Tail, Offset); 309 return true; 310 } 311 case RISCV::LB: 312 case RISCV::LH: 313 case RISCV::LW: 314 case RISCV::LBU: 315 case RISCV::LHU: 316 case RISCV::LWU: 317 case RISCV::LD: 318 case RISCV::FLH: 319 case RISCV::FLW: 320 case RISCV::FLD: 321 case RISCV::SB: 322 case RISCV::SH: 323 case RISCV::SW: 324 case RISCV::SD: 325 case RISCV::FSH: 326 case RISCV::FSW: 327 case RISCV::FSD: { 328 // Transforms the sequence: Into: 329 // HiLUI: lui vreg1, %hi(foo) ---> lui vreg1, %hi(foo+8) 330 // LoADDI: addi vreg2, vreg1, %lo(foo) ---> lw vreg3, lo(foo+8)(vreg1) 331 // Tail: lw vreg3, 8(vreg2) 332 if (Tail.getOperand(1).isFI()) 333 return false; 334 // Register defined by LoADDI should be used in the base part of the 335 // load\store instruction. Otherwise, no folding possible. 336 Register BaseAddrReg = Tail.getOperand(1).getReg(); 337 if (DestReg != BaseAddrReg) 338 return false; 339 MachineOperand &TailImmOp = Tail.getOperand(2); 340 int64_t Offset = TailImmOp.getImm(); 341 // Update the offsets in global address lowering. 342 HiLUI.getOperand(1).setOffset(Offset); 343 // Update the immediate in the Tail instruction to add the offset. 344 Tail.removeOperand(2); 345 MachineOperand &ImmOp = LoADDI.getOperand(2); 346 ImmOp.setOffset(Offset); 347 Tail.addOperand(ImmOp); 348 // Update the base reg in the Tail instruction to feed from LUI. 349 // Output of HiLUI is only used in LoADDI, no need to use 350 // MRI->replaceRegWith(). 351 Tail.getOperand(1).setReg(HiLUI.getOperand(0).getReg()); 352 DeadInstrs.insert(&LoADDI); 353 return true; 354 } 355 } 356 return false; 357 } 358 359 bool RISCVMergeBaseOffsetOpt::runOnMachineFunction(MachineFunction &Fn) { 360 if (skipFunction(Fn.getFunction())) 361 return false; 362 363 ST = &Fn.getSubtarget<RISCVSubtarget>(); 364 365 bool MadeChange = false; 366 DeadInstrs.clear(); 367 MRI = &Fn.getRegInfo(); 368 for (MachineBasicBlock &MBB : Fn) { 369 LLVM_DEBUG(dbgs() << "MBB: " << MBB.getName() << "\n"); 370 for (MachineInstr &HiLUI : MBB) { 371 MachineInstr *LoADDI = nullptr; 372 if (!detectLuiAddiGlobal(HiLUI, LoADDI)) 373 continue; 374 LLVM_DEBUG(dbgs() << " Found lowered global address with one use: " 375 << *LoADDI->getOperand(2).getGlobal() << "\n"); 376 // If the use count is only one, merge the offset 377 MadeChange |= detectAndFoldOffset(HiLUI, *LoADDI); 378 } 379 } 380 // Delete dead instructions. 381 for (auto *MI : DeadInstrs) 382 MI->eraseFromParent(); 383 return MadeChange; 384 } 385 386 /// Returns an instance of the Merge Base Offset Optimization pass. 387 FunctionPass *llvm::createRISCVMergeBaseOffsetOptPass() { 388 return new RISCVMergeBaseOffsetOpt(); 389 } 390