1 //===- AArch64MIPeepholeOpt.cpp - AArch64 MI peephole optimization pass ---===// 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 // This pass performs below peephole optimizations on MIR level. 10 // 11 // 1. MOVi32imm + ANDWrr ==> ANDWri + ANDWri 12 // MOVi64imm + ANDXrr ==> ANDXri + ANDXri 13 // 14 // 2. MOVi32imm + ADDWrr ==> ADDWRi + ADDWRi 15 // MOVi64imm + ADDXrr ==> ANDXri + ANDXri 16 // 17 // 3. MOVi32imm + SUBWrr ==> SUBWRi + SUBWRi 18 // MOVi64imm + SUBXrr ==> SUBXri + SUBXri 19 // 20 // The mov pseudo instruction could be expanded to multiple mov instructions 21 // later. In this case, we could try to split the constant operand of mov 22 // instruction into two immediates which can be directly encoded into 23 // *Wri/*Xri instructions. It makes two AND/ADD/SUB instructions instead of 24 // multiple `mov` + `and/add/sub` instructions. 25 // 26 // 4. Remove redundant ORRWrs which is generated by zero-extend. 27 // 28 // %3:gpr32 = ORRWrs $wzr, %2, 0 29 // %4:gpr64 = SUBREG_TO_REG 0, %3, %subreg.sub_32 30 // 31 // If AArch64's 32-bit form of instruction defines the source operand of 32 // ORRWrs, we can remove the ORRWrs because the upper 32 bits of the source 33 // operand are set to zero. 34 // 35 //===----------------------------------------------------------------------===// 36 37 #include "AArch64ExpandImm.h" 38 #include "AArch64InstrInfo.h" 39 #include "MCTargetDesc/AArch64AddressingModes.h" 40 #include "llvm/ADT/SetVector.h" 41 #include "llvm/CodeGen/MachineDominators.h" 42 #include "llvm/CodeGen/MachineLoopInfo.h" 43 44 using namespace llvm; 45 46 #define DEBUG_TYPE "aarch64-mi-peephole-opt" 47 48 namespace { 49 50 struct AArch64MIPeepholeOpt : public MachineFunctionPass { 51 static char ID; 52 53 AArch64MIPeepholeOpt() : MachineFunctionPass(ID) { 54 initializeAArch64MIPeepholeOptPass(*PassRegistry::getPassRegistry()); 55 } 56 57 const AArch64InstrInfo *TII; 58 MachineLoopInfo *MLI; 59 MachineRegisterInfo *MRI; 60 61 bool checkMovImmInstr(MachineInstr &MI, MachineInstr *&MovMI, 62 MachineInstr *&SubregToRegMI); 63 64 template <typename T> 65 bool visitADDSUB(MachineInstr &MI, 66 SmallSetVector<MachineInstr *, 8> &ToBeRemoved, bool IsAdd); 67 template <typename T> 68 bool visitAND(MachineInstr &MI, 69 SmallSetVector<MachineInstr *, 8> &ToBeRemoved); 70 bool visitORR(MachineInstr &MI, 71 SmallSetVector<MachineInstr *, 8> &ToBeRemoved); 72 bool runOnMachineFunction(MachineFunction &MF) override; 73 74 StringRef getPassName() const override { 75 return "AArch64 MI Peephole Optimization pass"; 76 } 77 78 void getAnalysisUsage(AnalysisUsage &AU) const override { 79 AU.setPreservesCFG(); 80 AU.addRequired<MachineLoopInfo>(); 81 MachineFunctionPass::getAnalysisUsage(AU); 82 } 83 }; 84 85 char AArch64MIPeepholeOpt::ID = 0; 86 87 } // end anonymous namespace 88 89 INITIALIZE_PASS(AArch64MIPeepholeOpt, "aarch64-mi-peephole-opt", 90 "AArch64 MI Peephole Optimization", false, false) 91 92 template <typename T> 93 static bool splitBitmaskImm(T Imm, unsigned RegSize, T &Imm1Enc, T &Imm2Enc) { 94 T UImm = static_cast<T>(Imm); 95 if (AArch64_AM::isLogicalImmediate(UImm, RegSize)) 96 return false; 97 98 // If this immediate can be handled by one instruction, do not split it. 99 SmallVector<AArch64_IMM::ImmInsnModel, 4> Insn; 100 AArch64_IMM::expandMOVImm(UImm, RegSize, Insn); 101 if (Insn.size() == 1) 102 return false; 103 104 // The bitmask immediate consists of consecutive ones. Let's say there is 105 // constant 0b00000000001000000000010000000000 which does not consist of 106 // consecutive ones. We can split it in to two bitmask immediate like 107 // 0b00000000001111111111110000000000 and 0b11111111111000000000011111111111. 108 // If we do AND with these two bitmask immediate, we can see original one. 109 unsigned LowestBitSet = countTrailingZeros(UImm); 110 unsigned HighestBitSet = Log2_64(UImm); 111 112 // Create a mask which is filled with one from the position of lowest bit set 113 // to the position of highest bit set. 114 T NewImm1 = (static_cast<T>(2) << HighestBitSet) - 115 (static_cast<T>(1) << LowestBitSet); 116 // Create a mask which is filled with one outside the position of lowest bit 117 // set and the position of highest bit set. 118 T NewImm2 = UImm | ~NewImm1; 119 120 // If the split value is not valid bitmask immediate, do not split this 121 // constant. 122 if (!AArch64_AM::isLogicalImmediate(NewImm2, RegSize)) 123 return false; 124 125 Imm1Enc = AArch64_AM::encodeLogicalImmediate(NewImm1, RegSize); 126 Imm2Enc = AArch64_AM::encodeLogicalImmediate(NewImm2, RegSize); 127 return true; 128 } 129 130 template <typename T> 131 bool AArch64MIPeepholeOpt::visitAND( 132 MachineInstr &MI, SmallSetVector<MachineInstr *, 8> &ToBeRemoved) { 133 // Try below transformation. 134 // 135 // MOVi32imm + ANDWrr ==> ANDWri + ANDWri 136 // MOVi64imm + ANDXrr ==> ANDXri + ANDXri 137 // 138 // The mov pseudo instruction could be expanded to multiple mov instructions 139 // later. Let's try to split the constant operand of mov instruction into two 140 // bitmask immediates. It makes only two AND instructions intead of multiple 141 // mov + and instructions. 142 143 unsigned RegSize = sizeof(T) * 8; 144 assert((RegSize == 32 || RegSize == 64) && 145 "Invalid RegSize for AND bitmask peephole optimization"); 146 147 // Perform several essential checks against current MI. 148 MachineInstr *MovMI = nullptr, *SubregToRegMI = nullptr; 149 if (!checkMovImmInstr(MI, MovMI, SubregToRegMI)) 150 return false; 151 152 // Split the bitmask immediate into two. 153 T UImm = static_cast<T>(MovMI->getOperand(1).getImm()); 154 // For the 32 bit form of instruction, the upper 32 bits of the destination 155 // register are set to zero. If there is SUBREG_TO_REG, set the upper 32 bits 156 // of UImm to zero. 157 if (SubregToRegMI) 158 UImm &= 0xFFFFFFFF; 159 T Imm1Enc; 160 T Imm2Enc; 161 if (!splitBitmaskImm(UImm, RegSize, Imm1Enc, Imm2Enc)) 162 return false; 163 164 // Create new AND MIs. 165 DebugLoc DL = MI.getDebugLoc(); 166 MachineBasicBlock *MBB = MI.getParent(); 167 const TargetRegisterClass *ANDImmRC = 168 (RegSize == 32) ? &AArch64::GPR32spRegClass : &AArch64::GPR64spRegClass; 169 Register DstReg = MI.getOperand(0).getReg(); 170 Register SrcReg = MI.getOperand(1).getReg(); 171 Register NewTmpReg = MRI->createVirtualRegister(ANDImmRC); 172 Register NewDstReg = MRI->createVirtualRegister(ANDImmRC); 173 unsigned Opcode = (RegSize == 32) ? AArch64::ANDWri : AArch64::ANDXri; 174 175 MRI->constrainRegClass(NewTmpReg, MRI->getRegClass(SrcReg)); 176 BuildMI(*MBB, MI, DL, TII->get(Opcode), NewTmpReg) 177 .addReg(SrcReg) 178 .addImm(Imm1Enc); 179 180 MRI->constrainRegClass(NewDstReg, MRI->getRegClass(DstReg)); 181 BuildMI(*MBB, MI, DL, TII->get(Opcode), NewDstReg) 182 .addReg(NewTmpReg) 183 .addImm(Imm2Enc); 184 185 MRI->replaceRegWith(DstReg, NewDstReg); 186 // replaceRegWith changes MI's definition register. Keep it for SSA form until 187 // deleting MI. 188 MI.getOperand(0).setReg(DstReg); 189 190 ToBeRemoved.insert(&MI); 191 if (SubregToRegMI) 192 ToBeRemoved.insert(SubregToRegMI); 193 ToBeRemoved.insert(MovMI); 194 195 return true; 196 } 197 198 bool AArch64MIPeepholeOpt::visitORR( 199 MachineInstr &MI, SmallSetVector<MachineInstr *, 8> &ToBeRemoved) { 200 // Check this ORR comes from below zero-extend pattern. 201 // 202 // def : Pat<(i64 (zext GPR32:$src)), 203 // (SUBREG_TO_REG (i32 0), (ORRWrs WZR, GPR32:$src, 0), sub_32)>; 204 if (MI.getOperand(3).getImm() != 0) 205 return false; 206 207 if (MI.getOperand(1).getReg() != AArch64::WZR) 208 return false; 209 210 MachineInstr *SrcMI = MRI->getUniqueVRegDef(MI.getOperand(2).getReg()); 211 if (!SrcMI) 212 return false; 213 214 // From https://developer.arm.com/documentation/dui0801/b/BABBGCAC 215 // 216 // When you use the 32-bit form of an instruction, the upper 32 bits of the 217 // source registers are ignored and the upper 32 bits of the destination 218 // register are set to zero. 219 // 220 // If AArch64's 32-bit form of instruction defines the source operand of 221 // zero-extend, we do not need the zero-extend. Let's check the MI's opcode is 222 // real AArch64 instruction and if it is not, do not process the opcode 223 // conservatively. 224 if (SrcMI->getOpcode() <= TargetOpcode::GENERIC_OP_END) 225 return false; 226 227 Register DefReg = MI.getOperand(0).getReg(); 228 Register SrcReg = MI.getOperand(2).getReg(); 229 MRI->replaceRegWith(DefReg, SrcReg); 230 MRI->clearKillFlags(SrcReg); 231 // replaceRegWith changes MI's definition register. Keep it for SSA form until 232 // deleting MI. 233 MI.getOperand(0).setReg(DefReg); 234 ToBeRemoved.insert(&MI); 235 236 LLVM_DEBUG(dbgs() << "Removed: " << MI << "\n"); 237 238 return true; 239 } 240 241 template <typename T> 242 static bool splitAddSubImm(T Imm, unsigned RegSize, T &Imm0, T &Imm1) { 243 // The immediate must be in the form of ((imm0 << 12) + imm1), in which both 244 // imm0 and imm1 are non-zero 12-bit unsigned int. 245 if ((Imm & 0xfff000) == 0 || (Imm & 0xfff) == 0 || 246 (Imm & ~static_cast<T>(0xffffff)) != 0) 247 return false; 248 249 // The immediate can not be composed via a single instruction. 250 SmallVector<AArch64_IMM::ImmInsnModel, 4> Insn; 251 AArch64_IMM::expandMOVImm(Imm, RegSize, Insn); 252 if (Insn.size() == 1) 253 return false; 254 255 // Split Imm into (Imm0 << 12) + Imm1; 256 Imm0 = (Imm >> 12) & 0xfff; 257 Imm1 = Imm & 0xfff; 258 return true; 259 } 260 261 template <typename T> 262 bool AArch64MIPeepholeOpt::visitADDSUB( 263 MachineInstr &MI, SmallSetVector<MachineInstr *, 8> &ToBeRemoved, 264 bool IsAdd) { 265 // Try below transformation. 266 // 267 // MOVi32imm + ADDWrr ==> ADDWri + ADDWri 268 // MOVi64imm + ADDXrr ==> ADDXri + ADDXri 269 // 270 // MOVi32imm + SUBWrr ==> SUBWri + SUBWri 271 // MOVi64imm + SUBXrr ==> SUBXri + SUBXri 272 // 273 // The mov pseudo instruction could be expanded to multiple mov instructions 274 // later. Let's try to split the constant operand of mov instruction into two 275 // legal add/sub immediates. It makes only two ADD/SUB instructions intead of 276 // multiple `mov` + `and/sub` instructions. 277 278 unsigned RegSize = sizeof(T) * 8; 279 assert((RegSize == 32 || RegSize == 64) && 280 "Invalid RegSize for legal add/sub immediate peephole optimization"); 281 282 // Perform several essential checks against current MI. 283 MachineInstr *MovMI, *SubregToRegMI; 284 if (!checkMovImmInstr(MI, MovMI, SubregToRegMI)) 285 return false; 286 287 // Split the immediate to Imm0 and Imm1, and calculate the Opcode. 288 T Imm = static_cast<T>(MovMI->getOperand(1).getImm()), Imm0, Imm1; 289 unsigned Opcode; 290 if (splitAddSubImm(Imm, RegSize, Imm0, Imm1)) { 291 if (IsAdd) 292 Opcode = RegSize == 32 ? AArch64::ADDWri : AArch64::ADDXri; 293 else 294 Opcode = RegSize == 32 ? AArch64::SUBWri : AArch64::SUBXri; 295 } else if (splitAddSubImm(-Imm, RegSize, Imm0, Imm1)) { 296 if (IsAdd) 297 Opcode = RegSize == 32 ? AArch64::SUBWri : AArch64::SUBXri; 298 else 299 Opcode = RegSize == 32 ? AArch64::ADDWri : AArch64::ADDXri; 300 } else { 301 return false; 302 } 303 304 // Create new ADD/SUB MIs. 305 DebugLoc DL = MI.getDebugLoc(); 306 MachineBasicBlock *MBB = MI.getParent(); 307 const TargetRegisterClass *RC = 308 (RegSize == 32) ? &AArch64::GPR32spRegClass : &AArch64::GPR64spRegClass; 309 Register DstReg = MI.getOperand(0).getReg(); 310 Register SrcReg = MI.getOperand(1).getReg(); 311 Register NewTmpReg = MRI->createVirtualRegister(RC); 312 Register NewDstReg = MRI->createVirtualRegister(RC); 313 314 MRI->constrainRegClass(SrcReg, RC); 315 BuildMI(*MBB, MI, DL, TII->get(Opcode), NewTmpReg) 316 .addReg(SrcReg) 317 .addImm(Imm0) 318 .addImm(12); 319 320 MRI->constrainRegClass(NewDstReg, MRI->getRegClass(DstReg)); 321 BuildMI(*MBB, MI, DL, TII->get(Opcode), NewDstReg) 322 .addReg(NewTmpReg) 323 .addImm(Imm1) 324 .addImm(0); 325 326 MRI->replaceRegWith(DstReg, NewDstReg); 327 // replaceRegWith changes MI's definition register. Keep it for SSA form until 328 // deleting MI. 329 MI.getOperand(0).setReg(DstReg); 330 331 // Record the MIs need to be removed. 332 ToBeRemoved.insert(&MI); 333 if (SubregToRegMI) 334 ToBeRemoved.insert(SubregToRegMI); 335 ToBeRemoved.insert(MovMI); 336 337 return true; 338 } 339 340 // Checks if the corresponding MOV immediate instruction is applicable for 341 // this peephole optimization. 342 bool AArch64MIPeepholeOpt::checkMovImmInstr(MachineInstr &MI, 343 MachineInstr *&MovMI, 344 MachineInstr *&SubregToRegMI) { 345 // Check whether current MBB is in loop and the AND is loop invariant. 346 MachineBasicBlock *MBB = MI.getParent(); 347 MachineLoop *L = MLI->getLoopFor(MBB); 348 if (L && !L->isLoopInvariant(MI)) 349 return false; 350 351 // Check whether current MI's operand is MOV with immediate. 352 MovMI = MRI->getUniqueVRegDef(MI.getOperand(2).getReg()); 353 if (!MovMI) 354 return false; 355 356 // If it is SUBREG_TO_REG, check its operand. 357 SubregToRegMI = nullptr; 358 if (MovMI->getOpcode() == TargetOpcode::SUBREG_TO_REG) { 359 SubregToRegMI = MovMI; 360 MovMI = MRI->getUniqueVRegDef(MovMI->getOperand(2).getReg()); 361 if (!MovMI) 362 return false; 363 } 364 365 if (MovMI->getOpcode() != AArch64::MOVi32imm && 366 MovMI->getOpcode() != AArch64::MOVi64imm) 367 return false; 368 369 // If the MOV has multiple uses, do not split the immediate because it causes 370 // more instructions. 371 if (!MRI->hasOneUse(MovMI->getOperand(0).getReg())) 372 return false; 373 if (SubregToRegMI && !MRI->hasOneUse(SubregToRegMI->getOperand(0).getReg())) 374 return false; 375 376 // It is OK to perform this peephole optimization. 377 return true; 378 } 379 380 bool AArch64MIPeepholeOpt::runOnMachineFunction(MachineFunction &MF) { 381 if (skipFunction(MF.getFunction())) 382 return false; 383 384 TII = static_cast<const AArch64InstrInfo *>(MF.getSubtarget().getInstrInfo()); 385 MLI = &getAnalysis<MachineLoopInfo>(); 386 MRI = &MF.getRegInfo(); 387 388 assert (MRI->isSSA() && "Expected to be run on SSA form!"); 389 390 bool Changed = false; 391 SmallSetVector<MachineInstr *, 8> ToBeRemoved; 392 393 for (MachineBasicBlock &MBB : MF) { 394 for (MachineInstr &MI : MBB) { 395 switch (MI.getOpcode()) { 396 default: 397 break; 398 case AArch64::ANDWrr: 399 Changed = visitAND<uint32_t>(MI, ToBeRemoved); 400 break; 401 case AArch64::ANDXrr: 402 Changed = visitAND<uint64_t>(MI, ToBeRemoved); 403 break; 404 case AArch64::ORRWrs: 405 Changed = visitORR(MI, ToBeRemoved); 406 break; 407 case AArch64::ADDWrr: 408 Changed = visitADDSUB<uint32_t>(MI, ToBeRemoved, true); 409 break; 410 case AArch64::SUBWrr: 411 Changed = visitADDSUB<uint32_t>(MI, ToBeRemoved, false); 412 break; 413 case AArch64::ADDXrr: 414 Changed = visitADDSUB<uint64_t>(MI, ToBeRemoved, true); 415 break; 416 case AArch64::SUBXrr: 417 Changed = visitADDSUB<uint64_t>(MI, ToBeRemoved, false); 418 break; 419 } 420 } 421 } 422 423 for (MachineInstr *MI : ToBeRemoved) 424 MI->eraseFromParent(); 425 426 return Changed; 427 } 428 429 FunctionPass *llvm::createAArch64MIPeepholeOptPass() { 430 return new AArch64MIPeepholeOpt(); 431 } 432