| 1 | //=- LoongArchInstrInfo.cpp - LoongArch Instruction Information -*- C++ -*-===// |
| 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 file contains the LoongArch implementation of the TargetInstrInfo class. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "LoongArchInstrInfo.h" |
| 14 | #include "LoongArch.h" |
| 15 | #include "LoongArchMachineFunctionInfo.h" |
| 16 | #include "LoongArchRegisterInfo.h" |
| 17 | #include "MCTargetDesc/LoongArchMCTargetDesc.h" |
| 18 | #include "MCTargetDesc/LoongArchMatInt.h" |
| 19 | #include "llvm/CodeGen/RegisterScavenging.h" |
| 20 | #include "llvm/CodeGen/StackMaps.h" |
| 21 | #include "llvm/MC/MCContext.h" |
| 22 | #include "llvm/MC/MCInstBuilder.h" |
| 23 | #include "llvm/Support/CommandLine.h" |
| 24 | |
| 25 | using namespace llvm; |
| 26 | |
| 27 | static cl::opt<bool> DisableRelocSched( |
| 28 | "loongarch-disable-reloc-sched" , |
| 29 | cl::desc("Disable scheduling of instructions with target flags" ), |
| 30 | cl::init(Val: false), cl::Hidden); |
| 31 | |
| 32 | #define GET_INSTRINFO_CTOR_DTOR |
| 33 | #include "LoongArchGenInstrInfo.inc" |
| 34 | |
| 35 | LoongArchInstrInfo::LoongArchInstrInfo(const LoongArchSubtarget &STI) |
| 36 | : LoongArchGenInstrInfo(STI, RegInfo, LoongArch::ADJCALLSTACKDOWN, |
| 37 | LoongArch::ADJCALLSTACKUP), |
| 38 | RegInfo(STI.getHwMode()), STI(STI) {} |
| 39 | |
| 40 | MCInst LoongArchInstrInfo::getNop() const { |
| 41 | return MCInstBuilder(LoongArch::ANDI) |
| 42 | .addReg(Reg: LoongArch::R0) |
| 43 | .addReg(Reg: LoongArch::R0) |
| 44 | .addImm(Val: 0); |
| 45 | } |
| 46 | |
| 47 | void LoongArchInstrInfo::copyPhysReg(MachineBasicBlock &MBB, |
| 48 | MachineBasicBlock::iterator MBBI, |
| 49 | const DebugLoc &DL, Register DstReg, |
| 50 | Register SrcReg, bool KillSrc, |
| 51 | bool RenamableDest, |
| 52 | bool RenamableSrc) const { |
| 53 | if (LoongArch::GPRRegClass.contains(Reg1: DstReg, Reg2: SrcReg)) { |
| 54 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, MCID: get(Opcode: LoongArch::OR), DestReg: DstReg) |
| 55 | .addReg(RegNo: SrcReg, Flags: getKillRegState(B: KillSrc)) |
| 56 | .addReg(RegNo: LoongArch::R0); |
| 57 | return; |
| 58 | } |
| 59 | |
| 60 | // VR->VR copies. |
| 61 | if (LoongArch::LSX128RegClass.contains(Reg1: DstReg, Reg2: SrcReg)) { |
| 62 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, MCID: get(Opcode: LoongArch::VORI_B), DestReg: DstReg) |
| 63 | .addReg(RegNo: SrcReg, Flags: getKillRegState(B: KillSrc)) |
| 64 | .addImm(Val: 0); |
| 65 | return; |
| 66 | } |
| 67 | |
| 68 | // XR->XR copies. |
| 69 | if (LoongArch::LASX256RegClass.contains(Reg1: DstReg, Reg2: SrcReg)) { |
| 70 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, MCID: get(Opcode: LoongArch::XVORI_B), DestReg: DstReg) |
| 71 | .addReg(RegNo: SrcReg, Flags: getKillRegState(B: KillSrc)) |
| 72 | .addImm(Val: 0); |
| 73 | return; |
| 74 | } |
| 75 | |
| 76 | // GPR->CFR copy. |
| 77 | if (LoongArch::CFRRegClass.contains(Reg: DstReg) && |
| 78 | LoongArch::GPRRegClass.contains(Reg: SrcReg)) { |
| 79 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, MCID: get(Opcode: LoongArch::MOVGR2CF), DestReg: DstReg) |
| 80 | .addReg(RegNo: SrcReg, Flags: getKillRegState(B: KillSrc)); |
| 81 | return; |
| 82 | } |
| 83 | // CFR->GPR copy. |
| 84 | if (LoongArch::GPRRegClass.contains(Reg: DstReg) && |
| 85 | LoongArch::CFRRegClass.contains(Reg: SrcReg)) { |
| 86 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, MCID: get(Opcode: LoongArch::MOVCF2GR), DestReg: DstReg) |
| 87 | .addReg(RegNo: SrcReg, Flags: getKillRegState(B: KillSrc)); |
| 88 | return; |
| 89 | } |
| 90 | // CFR->CFR copy. |
| 91 | if (LoongArch::CFRRegClass.contains(Reg1: DstReg, Reg2: SrcReg)) { |
| 92 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, MCID: get(Opcode: LoongArch::PseudoCopyCFR), DestReg: DstReg) |
| 93 | .addReg(RegNo: SrcReg, Flags: getKillRegState(B: KillSrc)); |
| 94 | return; |
| 95 | } |
| 96 | |
| 97 | // FPR->FPR copies. |
| 98 | unsigned Opc; |
| 99 | if (LoongArch::FPR32RegClass.contains(Reg1: DstReg, Reg2: SrcReg)) { |
| 100 | Opc = LoongArch::FMOV_S; |
| 101 | } else if (LoongArch::FPR64RegClass.contains(Reg1: DstReg, Reg2: SrcReg)) { |
| 102 | Opc = LoongArch::FMOV_D; |
| 103 | } else if (LoongArch::GPRRegClass.contains(Reg: DstReg) && |
| 104 | LoongArch::FPR32RegClass.contains(Reg: SrcReg)) { |
| 105 | // FPR32 -> GPR copies |
| 106 | Opc = LoongArch::MOVFR2GR_S; |
| 107 | } else if (LoongArch::GPRRegClass.contains(Reg: DstReg) && |
| 108 | LoongArch::FPR64RegClass.contains(Reg: SrcReg)) { |
| 109 | // FPR64 -> GPR copies |
| 110 | Opc = LoongArch::MOVFR2GR_D; |
| 111 | } else { |
| 112 | // TODO: support other copies. |
| 113 | llvm_unreachable("Impossible reg-to-reg copy" ); |
| 114 | } |
| 115 | |
| 116 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, MCID: get(Opcode: Opc), DestReg: DstReg) |
| 117 | .addReg(RegNo: SrcReg, Flags: getKillRegState(B: KillSrc)); |
| 118 | } |
| 119 | |
| 120 | void LoongArchInstrInfo::storeRegToStackSlot( |
| 121 | MachineBasicBlock &MBB, MachineBasicBlock::iterator I, Register SrcReg, |
| 122 | bool IsKill, int FI, const TargetRegisterClass *RC, |
| 123 | |
| 124 | Register VReg, MachineInstr::MIFlag Flags) const { |
| 125 | MachineFunction *MF = MBB.getParent(); |
| 126 | MachineFrameInfo &MFI = MF->getFrameInfo(); |
| 127 | |
| 128 | unsigned Opcode; |
| 129 | if (LoongArch::GPRRegClass.hasSubClassEq(RC)) |
| 130 | Opcode = TRI.getRegSizeInBits(RC: LoongArch::GPRRegClass) == 32 |
| 131 | ? LoongArch::ST_W |
| 132 | : LoongArch::ST_D; |
| 133 | else if (LoongArch::FPR32RegClass.hasSubClassEq(RC)) |
| 134 | Opcode = LoongArch::FST_S; |
| 135 | else if (LoongArch::FPR64RegClass.hasSubClassEq(RC)) |
| 136 | Opcode = LoongArch::FST_D; |
| 137 | else if (LoongArch::LSX128RegClass.hasSubClassEq(RC)) |
| 138 | Opcode = LoongArch::VST; |
| 139 | else if (LoongArch::LASX256RegClass.hasSubClassEq(RC)) |
| 140 | Opcode = LoongArch::XVST; |
| 141 | else if (LoongArch::CFRRegClass.hasSubClassEq(RC)) |
| 142 | Opcode = LoongArch::PseudoST_CFR; |
| 143 | else |
| 144 | llvm_unreachable("Can't store this register to stack slot" ); |
| 145 | |
| 146 | MachineMemOperand *MMO = MF->getMachineMemOperand( |
| 147 | PtrInfo: MachinePointerInfo::getFixedStack(MF&: *MF, FI), F: MachineMemOperand::MOStore, |
| 148 | Size: MFI.getObjectSize(ObjectIdx: FI), BaseAlignment: MFI.getObjectAlign(ObjectIdx: FI)); |
| 149 | |
| 150 | BuildMI(BB&: MBB, I, MIMD: DebugLoc(), MCID: get(Opcode)) |
| 151 | .addReg(RegNo: SrcReg, Flags: getKillRegState(B: IsKill)) |
| 152 | .addFrameIndex(Idx: FI) |
| 153 | .addImm(Val: 0) |
| 154 | .addMemOperand(MMO); |
| 155 | } |
| 156 | |
| 157 | void LoongArchInstrInfo::loadRegFromStackSlot( |
| 158 | MachineBasicBlock &MBB, MachineBasicBlock::iterator I, Register DstReg, |
| 159 | int FI, const TargetRegisterClass *RC, Register VReg, unsigned SubReg, |
| 160 | MachineInstr::MIFlag Flags) const { |
| 161 | MachineFunction *MF = MBB.getParent(); |
| 162 | MachineFrameInfo &MFI = MF->getFrameInfo(); |
| 163 | DebugLoc DL; |
| 164 | if (I != MBB.end()) |
| 165 | DL = I->getDebugLoc(); |
| 166 | |
| 167 | unsigned Opcode; |
| 168 | if (LoongArch::GPRRegClass.hasSubClassEq(RC)) |
| 169 | Opcode = RegInfo.getRegSizeInBits(RC: LoongArch::GPRRegClass) == 32 |
| 170 | ? LoongArch::LD_W |
| 171 | : LoongArch::LD_D; |
| 172 | else if (LoongArch::FPR32RegClass.hasSubClassEq(RC)) |
| 173 | Opcode = LoongArch::FLD_S; |
| 174 | else if (LoongArch::FPR64RegClass.hasSubClassEq(RC)) |
| 175 | Opcode = LoongArch::FLD_D; |
| 176 | else if (LoongArch::LSX128RegClass.hasSubClassEq(RC)) |
| 177 | Opcode = LoongArch::VLD; |
| 178 | else if (LoongArch::LASX256RegClass.hasSubClassEq(RC)) |
| 179 | Opcode = LoongArch::XVLD; |
| 180 | else if (LoongArch::CFRRegClass.hasSubClassEq(RC)) |
| 181 | Opcode = LoongArch::PseudoLD_CFR; |
| 182 | else |
| 183 | llvm_unreachable("Can't load this register from stack slot" ); |
| 184 | |
| 185 | MachineMemOperand *MMO = MF->getMachineMemOperand( |
| 186 | PtrInfo: MachinePointerInfo::getFixedStack(MF&: *MF, FI), F: MachineMemOperand::MOLoad, |
| 187 | Size: MFI.getObjectSize(ObjectIdx: FI), BaseAlignment: MFI.getObjectAlign(ObjectIdx: FI)); |
| 188 | |
| 189 | BuildMI(BB&: MBB, I, MIMD: DL, MCID: get(Opcode), DestReg: DstReg) |
| 190 | .addFrameIndex(Idx: FI) |
| 191 | .addImm(Val: 0) |
| 192 | .addMemOperand(MMO); |
| 193 | } |
| 194 | |
| 195 | void LoongArchInstrInfo::movImm(MachineBasicBlock &MBB, |
| 196 | MachineBasicBlock::iterator MBBI, |
| 197 | const DebugLoc &DL, Register DstReg, |
| 198 | uint64_t Val, MachineInstr::MIFlag Flag) const { |
| 199 | Register SrcReg = LoongArch::R0; |
| 200 | |
| 201 | if (!STI.is64Bit() && !isInt<32>(x: Val)) |
| 202 | report_fatal_error(reason: "Should only materialize 32-bit constants for LA32" ); |
| 203 | |
| 204 | auto Seq = LoongArchMatInt::generateInstSeq(Val); |
| 205 | assert(!Seq.empty()); |
| 206 | |
| 207 | for (auto &Inst : Seq) { |
| 208 | switch (Inst.Opc) { |
| 209 | case LoongArch::LU12I_W: |
| 210 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, MCID: get(Opcode: Inst.Opc), DestReg: DstReg) |
| 211 | .addImm(Val: Inst.Imm) |
| 212 | .setMIFlag(Flag); |
| 213 | break; |
| 214 | case LoongArch::ADDI_W: |
| 215 | case LoongArch::ORI: |
| 216 | case LoongArch::LU32I_D: // "rj" is needed due to InstrInfo pattern |
| 217 | case LoongArch::LU52I_D: |
| 218 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, MCID: get(Opcode: Inst.Opc), DestReg: DstReg) |
| 219 | .addReg(RegNo: SrcReg, Flags: RegState::Kill) |
| 220 | .addImm(Val: Inst.Imm) |
| 221 | .setMIFlag(Flag); |
| 222 | break; |
| 223 | case LoongArch::BSTRINS_D: |
| 224 | BuildMI(BB&: MBB, I: MBBI, MIMD: DL, MCID: get(Opcode: Inst.Opc), DestReg: DstReg) |
| 225 | .addReg(RegNo: SrcReg, Flags: RegState::Kill) |
| 226 | .addReg(RegNo: SrcReg, Flags: RegState::Kill) |
| 227 | .addImm(Val: Inst.Imm >> 32) |
| 228 | .addImm(Val: Inst.Imm & 0xFF) |
| 229 | .setMIFlag(Flag); |
| 230 | break; |
| 231 | default: |
| 232 | assert(false && "Unknown insn emitted by LoongArchMatInt" ); |
| 233 | } |
| 234 | |
| 235 | // Only the first instruction has $zero as its source. |
| 236 | SrcReg = DstReg; |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | unsigned LoongArchInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { |
| 241 | unsigned Opcode = MI.getOpcode(); |
| 242 | |
| 243 | if (Opcode == TargetOpcode::INLINEASM || |
| 244 | Opcode == TargetOpcode::INLINEASM_BR) { |
| 245 | const MachineFunction *MF = MI.getParent()->getParent(); |
| 246 | const MCAsmInfo *MAI = MF->getTarget().getMCAsmInfo(); |
| 247 | return getInlineAsmLength(Str: MI.getOperand(i: 0).getSymbolName(), MAI: *MAI); |
| 248 | } |
| 249 | |
| 250 | unsigned NumBytes = 0; |
| 251 | const MCInstrDesc &Desc = MI.getDesc(); |
| 252 | |
| 253 | // Size should be preferably set in |
| 254 | // llvm/lib/Target/LoongArch/LoongArch*InstrInfo.td (default case). |
| 255 | // Specific cases handle instructions of variable sizes. |
| 256 | switch (Desc.getOpcode()) { |
| 257 | default: |
| 258 | return Desc.getSize(); |
| 259 | case TargetOpcode::STATEPOINT: |
| 260 | NumBytes = StatepointOpers(&MI).getNumPatchBytes(); |
| 261 | assert(NumBytes % 4 == 0 && "Invalid number of NOP bytes requested!" ); |
| 262 | // No patch bytes means a normal call inst (i.e. `bl`) is emitted. |
| 263 | if (NumBytes == 0) |
| 264 | NumBytes = 4; |
| 265 | break; |
| 266 | } |
| 267 | return NumBytes; |
| 268 | } |
| 269 | |
| 270 | bool LoongArchInstrInfo::isAsCheapAsAMove(const MachineInstr &MI) const { |
| 271 | const unsigned Opcode = MI.getOpcode(); |
| 272 | switch (Opcode) { |
| 273 | default: |
| 274 | break; |
| 275 | case LoongArch::ADDI_D: |
| 276 | case LoongArch::ORI: |
| 277 | case LoongArch::XORI: |
| 278 | return (MI.getOperand(i: 1).isReg() && |
| 279 | MI.getOperand(i: 1).getReg() == LoongArch::R0) || |
| 280 | (MI.getOperand(i: 2).isImm() && MI.getOperand(i: 2).getImm() == 0); |
| 281 | } |
| 282 | return MI.isAsCheapAsAMove(); |
| 283 | } |
| 284 | |
| 285 | MachineBasicBlock * |
| 286 | LoongArchInstrInfo::getBranchDestBlock(const MachineInstr &MI) const { |
| 287 | assert(MI.getDesc().isBranch() && "Unexpected opcode!" ); |
| 288 | // The branch target is always the last operand. |
| 289 | return MI.getOperand(i: MI.getNumExplicitOperands() - 1).getMBB(); |
| 290 | } |
| 291 | |
| 292 | static void parseCondBranch(MachineInstr &LastInst, MachineBasicBlock *&Target, |
| 293 | SmallVectorImpl<MachineOperand> &Cond) { |
| 294 | // Block ends with fall-through condbranch. |
| 295 | assert(LastInst.getDesc().isConditionalBranch() && |
| 296 | "Unknown conditional branch" ); |
| 297 | int NumOp = LastInst.getNumExplicitOperands(); |
| 298 | Target = LastInst.getOperand(i: NumOp - 1).getMBB(); |
| 299 | |
| 300 | Cond.push_back(Elt: MachineOperand::CreateImm(Val: LastInst.getOpcode())); |
| 301 | for (int i = 0; i < NumOp - 1; i++) |
| 302 | Cond.push_back(Elt: LastInst.getOperand(i)); |
| 303 | } |
| 304 | |
| 305 | bool LoongArchInstrInfo::analyzeBranch(MachineBasicBlock &MBB, |
| 306 | MachineBasicBlock *&TBB, |
| 307 | MachineBasicBlock *&FBB, |
| 308 | SmallVectorImpl<MachineOperand> &Cond, |
| 309 | bool AllowModify) const { |
| 310 | TBB = FBB = nullptr; |
| 311 | Cond.clear(); |
| 312 | |
| 313 | // If the block has no terminators, it just falls into the block after it. |
| 314 | MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr(); |
| 315 | if (I == MBB.end() || !isUnpredicatedTerminator(MI: *I)) |
| 316 | return false; |
| 317 | |
| 318 | // Count the number of terminators and find the first unconditional or |
| 319 | // indirect branch. |
| 320 | MachineBasicBlock::iterator FirstUncondOrIndirectBr = MBB.end(); |
| 321 | int NumTerminators = 0; |
| 322 | for (auto J = I.getReverse(); J != MBB.rend() && isUnpredicatedTerminator(MI: *J); |
| 323 | J++) { |
| 324 | NumTerminators++; |
| 325 | if (J->getDesc().isUnconditionalBranch() || |
| 326 | J->getDesc().isIndirectBranch()) { |
| 327 | FirstUncondOrIndirectBr = J.getReverse(); |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | // If AllowModify is true, we can erase any terminators after |
| 332 | // FirstUncondOrIndirectBR. |
| 333 | if (AllowModify && FirstUncondOrIndirectBr != MBB.end()) { |
| 334 | while (std::next(x: FirstUncondOrIndirectBr) != MBB.end()) { |
| 335 | std::next(x: FirstUncondOrIndirectBr)->eraseFromParent(); |
| 336 | NumTerminators--; |
| 337 | } |
| 338 | I = FirstUncondOrIndirectBr; |
| 339 | } |
| 340 | |
| 341 | // Handle a single unconditional branch. |
| 342 | if (NumTerminators == 1 && I->getDesc().isUnconditionalBranch()) { |
| 343 | TBB = getBranchDestBlock(MI: *I); |
| 344 | return false; |
| 345 | } |
| 346 | |
| 347 | // Handle a single conditional branch. |
| 348 | if (NumTerminators == 1 && I->getDesc().isConditionalBranch()) { |
| 349 | parseCondBranch(LastInst&: *I, Target&: TBB, Cond); |
| 350 | return false; |
| 351 | } |
| 352 | |
| 353 | // Handle a conditional branch followed by an unconditional branch. |
| 354 | if (NumTerminators == 2 && std::prev(x: I)->getDesc().isConditionalBranch() && |
| 355 | I->getDesc().isUnconditionalBranch()) { |
| 356 | parseCondBranch(LastInst&: *std::prev(x: I), Target&: TBB, Cond); |
| 357 | FBB = getBranchDestBlock(MI: *I); |
| 358 | return false; |
| 359 | } |
| 360 | |
| 361 | // Otherwise, we can't handle this. |
| 362 | return true; |
| 363 | } |
| 364 | |
| 365 | bool LoongArchInstrInfo::isBranchOffsetInRange(unsigned BranchOp, |
| 366 | int64_t BrOffset) const { |
| 367 | switch (BranchOp) { |
| 368 | default: |
| 369 | llvm_unreachable("Unknown branch instruction!" ); |
| 370 | case LoongArch::BEQ: |
| 371 | case LoongArch::BNE: |
| 372 | case LoongArch::BLT: |
| 373 | case LoongArch::BGE: |
| 374 | case LoongArch::BLTU: |
| 375 | case LoongArch::BGEU: |
| 376 | return isInt<18>(x: BrOffset); |
| 377 | case LoongArch::BEQZ: |
| 378 | case LoongArch::BNEZ: |
| 379 | case LoongArch::BCEQZ: |
| 380 | case LoongArch::BCNEZ: |
| 381 | return isInt<23>(x: BrOffset); |
| 382 | case LoongArch::B: |
| 383 | case LoongArch::PseudoBR: |
| 384 | return isInt<28>(x: BrOffset); |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | bool LoongArchInstrInfo::isSafeToMove(const MachineInstr &MI, |
| 389 | const MachineBasicBlock *MBB, |
| 390 | const MachineFunction &MF) const { |
| 391 | if (DisableRelocSched) { |
| 392 | for (const MachineOperand &MO : MI.operands()) |
| 393 | if (MO.getTargetFlags()) |
| 394 | return false; |
| 395 | } |
| 396 | |
| 397 | auto MII = MI.getIterator(); |
| 398 | auto MIE = MBB->end(); |
| 399 | |
| 400 | // According to psABI v2.30: |
| 401 | // |
| 402 | // https://github.com/loongson/la-abi-specs/releases/tag/v2.30 |
| 403 | // |
| 404 | // The following instruction patterns are prohibited from being reordered: |
| 405 | // |
| 406 | // * pcalau12i $a0, %pc_hi20(s) |
| 407 | // addi.d $a1, $zero, %pc_lo12(s) |
| 408 | // lu32i.d $a1, %pc64_lo20(s) |
| 409 | // lu52i.d $a1, $a1, %pc64_hi12(s) |
| 410 | // |
| 411 | // * pcalau12i $a0, %got_pc_hi20(s) | %ld_pc_hi20(s) | %gd_pc_hi20(s) |
| 412 | // addi.d $a1, $zero, %got_pc_lo12(s) |
| 413 | // lu32i.d $a1, %got64_pc_lo20(s) |
| 414 | // lu52i.d $a1, $a1, %got64_pc_hi12(s) |
| 415 | // |
| 416 | // * pcalau12i $a0, %ie_pc_hi20(s) |
| 417 | // addi.d $a1, $zero, %ie_pc_lo12(s) |
| 418 | // lu32i.d $a1, %ie64_pc_lo20(s) |
| 419 | // lu52i.d $a1, $a1, %ie64_pc_hi12(s) |
| 420 | // |
| 421 | // * pcalau12i $a0, %desc_pc_hi20(s) |
| 422 | // addi.d $a1, $zero, %desc_pc_lo12(s) |
| 423 | // lu32i.d $a1, %desc64_pc_lo20(s) |
| 424 | // lu52i.d $a1, $a1, %desc64_pc_hi12(s) |
| 425 | // |
| 426 | // For simplicity, only pcalau12i and lu52i.d are marked as scheduling |
| 427 | // boundaries, and the instructions between them are guaranteed to be |
| 428 | // ordered according to data dependencies. |
| 429 | switch (MI.getOpcode()) { |
| 430 | case LoongArch::PCALAU12I: { |
| 431 | auto AddI = std::next(x: MII); |
| 432 | if (AddI == MIE || AddI->getOpcode() != LoongArch::ADDI_D) |
| 433 | break; |
| 434 | auto Lu32I = std::next(x: AddI); |
| 435 | if (Lu32I == MIE || Lu32I->getOpcode() != LoongArch::LU32I_D) |
| 436 | break; |
| 437 | auto MO0 = MI.getOperand(i: 1).getTargetFlags(); |
| 438 | auto MO1 = AddI->getOperand(i: 2).getTargetFlags(); |
| 439 | auto MO2 = Lu32I->getOperand(i: 2).getTargetFlags(); |
| 440 | if (MO0 == LoongArchII::MO_PCREL_HI && MO1 == LoongArchII::MO_PCREL_LO && |
| 441 | MO2 == LoongArchII::MO_PCREL64_LO) |
| 442 | return false; |
| 443 | if ((MO0 == LoongArchII::MO_GOT_PC_HI || MO0 == LoongArchII::MO_LD_PC_HI || |
| 444 | MO0 == LoongArchII::MO_GD_PC_HI) && |
| 445 | MO1 == LoongArchII::MO_GOT_PC_LO && MO2 == LoongArchII::MO_GOT_PC64_LO) |
| 446 | return false; |
| 447 | if (MO0 == LoongArchII::MO_IE_PC_HI && MO1 == LoongArchII::MO_IE_PC_LO && |
| 448 | MO2 == LoongArchII::MO_IE_PC64_LO) |
| 449 | return false; |
| 450 | if (MO0 == LoongArchII::MO_DESC_PC_HI && |
| 451 | MO1 == LoongArchII::MO_DESC_PC_LO && |
| 452 | MO2 == LoongArchII::MO_DESC64_PC_LO) |
| 453 | return false; |
| 454 | break; |
| 455 | } |
| 456 | case LoongArch::LU52I_D: { |
| 457 | auto MO = MI.getOperand(i: 2).getTargetFlags(); |
| 458 | if (MO == LoongArchII::MO_PCREL64_HI || MO == LoongArchII::MO_GOT_PC64_HI || |
| 459 | MO == LoongArchII::MO_IE_PC64_HI || MO == LoongArchII::MO_DESC64_PC_HI) |
| 460 | return false; |
| 461 | break; |
| 462 | } |
| 463 | default: |
| 464 | break; |
| 465 | } |
| 466 | |
| 467 | const auto &STI = MF.getSubtarget<LoongArchSubtarget>(); |
| 468 | if (STI.hasFeature(Feature: LoongArch::FeatureRelax)) { |
| 469 | // When linker relaxation enabled, the following instruction patterns are |
| 470 | // prohibited from being reordered: |
| 471 | // |
| 472 | // * pcalau12i $a0, %pc_hi20(s) |
| 473 | // addi.w/d $a0, $a0, %pc_lo12(s) |
| 474 | // |
| 475 | // * pcalau12i $a0, %got_pc_hi20(s) |
| 476 | // ld.w/d $a0, $a0, %got_pc_lo12(s) |
| 477 | // |
| 478 | // * pcalau12i $a0, %ld_pc_hi20(s) | %gd_pc_hi20(s) |
| 479 | // addi.w/d $a0, $a0, %got_pc_lo12(s) |
| 480 | // |
| 481 | // * pcalau12i $a0, %desc_pc_hi20(s) |
| 482 | // addi.w/d $a0, $a0, %desc_pc_lo12(s) |
| 483 | // ld.w/d $ra, $a0, %desc_ld(s) |
| 484 | // jirl $ra, $ra, %desc_call(s) |
| 485 | unsigned AddiOp = STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W; |
| 486 | unsigned LdOp = STI.is64Bit() ? LoongArch::LD_D : LoongArch::LD_W; |
| 487 | switch (MI.getOpcode()) { |
| 488 | case LoongArch::PCALAU12I: { |
| 489 | auto MO0 = LoongArchII::getDirectFlags(MO: MI.getOperand(i: 1)); |
| 490 | auto SecondOp = std::next(x: MII); |
| 491 | if (MO0 == LoongArchII::MO_DESC_PC_HI) { |
| 492 | if (SecondOp == MIE || SecondOp->getOpcode() != AddiOp) |
| 493 | break; |
| 494 | auto Ld = std::next(x: SecondOp); |
| 495 | if (Ld == MIE || Ld->getOpcode() != LdOp) |
| 496 | break; |
| 497 | auto MO1 = LoongArchII::getDirectFlags(MO: SecondOp->getOperand(i: 2)); |
| 498 | auto MO2 = LoongArchII::getDirectFlags(MO: Ld->getOperand(i: 2)); |
| 499 | if (MO1 == LoongArchII::MO_DESC_PC_LO && MO2 == LoongArchII::MO_DESC_LD) |
| 500 | return false; |
| 501 | break; |
| 502 | } |
| 503 | if (SecondOp == MIE || |
| 504 | (SecondOp->getOpcode() != AddiOp && SecondOp->getOpcode() != LdOp)) |
| 505 | break; |
| 506 | auto MO1 = LoongArchII::getDirectFlags(MO: SecondOp->getOperand(i: 2)); |
| 507 | if (MO0 == LoongArchII::MO_PCREL_HI && SecondOp->getOpcode() == AddiOp && |
| 508 | MO1 == LoongArchII::MO_PCREL_LO) |
| 509 | return false; |
| 510 | if (MO0 == LoongArchII::MO_GOT_PC_HI && SecondOp->getOpcode() == LdOp && |
| 511 | MO1 == LoongArchII::MO_GOT_PC_LO) |
| 512 | return false; |
| 513 | if ((MO0 == LoongArchII::MO_LD_PC_HI || |
| 514 | MO0 == LoongArchII::MO_GD_PC_HI) && |
| 515 | SecondOp->getOpcode() == AddiOp && MO1 == LoongArchII::MO_GOT_PC_LO) |
| 516 | return false; |
| 517 | break; |
| 518 | } |
| 519 | case LoongArch::ADDI_W: |
| 520 | case LoongArch::ADDI_D: { |
| 521 | auto MO = LoongArchII::getDirectFlags(MO: MI.getOperand(i: 2)); |
| 522 | if (MO == LoongArchII::MO_PCREL_LO || MO == LoongArchII::MO_GOT_PC_LO) |
| 523 | return false; |
| 524 | break; |
| 525 | } |
| 526 | case LoongArch::LD_W: |
| 527 | case LoongArch::LD_D: { |
| 528 | auto MO = LoongArchII::getDirectFlags(MO: MI.getOperand(i: 2)); |
| 529 | if (MO == LoongArchII::MO_GOT_PC_LO) |
| 530 | return false; |
| 531 | break; |
| 532 | } |
| 533 | case LoongArch::PseudoDESC_CALL: { |
| 534 | auto MO = LoongArchII::getDirectFlags(MO: MI.getOperand(i: 2)); |
| 535 | if (MO == LoongArchII::MO_DESC_CALL) |
| 536 | return false; |
| 537 | break; |
| 538 | } |
| 539 | default: |
| 540 | break; |
| 541 | } |
| 542 | } |
| 543 | |
| 544 | return true; |
| 545 | } |
| 546 | |
| 547 | bool LoongArchInstrInfo::isSchedulingBoundary(const MachineInstr &MI, |
| 548 | const MachineBasicBlock *MBB, |
| 549 | const MachineFunction &MF) const { |
| 550 | if (TargetInstrInfo::isSchedulingBoundary(MI, MBB, MF)) |
| 551 | return true; |
| 552 | |
| 553 | if (!isSafeToMove(MI, MBB, MF)) |
| 554 | return true; |
| 555 | |
| 556 | return false; |
| 557 | } |
| 558 | |
| 559 | unsigned LoongArchInstrInfo::removeBranch(MachineBasicBlock &MBB, |
| 560 | int *BytesRemoved) const { |
| 561 | if (BytesRemoved) |
| 562 | *BytesRemoved = 0; |
| 563 | MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr(); |
| 564 | if (I == MBB.end()) |
| 565 | return 0; |
| 566 | |
| 567 | if (!I->getDesc().isBranch()) |
| 568 | return 0; |
| 569 | |
| 570 | // Remove the branch. |
| 571 | if (BytesRemoved) |
| 572 | *BytesRemoved += getInstSizeInBytes(MI: *I); |
| 573 | I->eraseFromParent(); |
| 574 | |
| 575 | I = MBB.end(); |
| 576 | |
| 577 | if (I == MBB.begin()) |
| 578 | return 1; |
| 579 | --I; |
| 580 | if (!I->getDesc().isConditionalBranch()) |
| 581 | return 1; |
| 582 | |
| 583 | // Remove the branch. |
| 584 | if (BytesRemoved) |
| 585 | *BytesRemoved += getInstSizeInBytes(MI: *I); |
| 586 | I->eraseFromParent(); |
| 587 | return 2; |
| 588 | } |
| 589 | |
| 590 | // Inserts a branch into the end of the specific MachineBasicBlock, returning |
| 591 | // the number of instructions inserted. |
| 592 | unsigned LoongArchInstrInfo::insertBranch( |
| 593 | MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, |
| 594 | ArrayRef<MachineOperand> Cond, const DebugLoc &DL, int *BytesAdded) const { |
| 595 | if (BytesAdded) |
| 596 | *BytesAdded = 0; |
| 597 | |
| 598 | // Shouldn't be a fall through. |
| 599 | assert(TBB && "insertBranch must not be told to insert a fallthrough" ); |
| 600 | assert(Cond.size() <= 3 && Cond.size() != 1 && |
| 601 | "LoongArch branch conditions have at most two components!" ); |
| 602 | |
| 603 | // Unconditional branch. |
| 604 | if (Cond.empty()) { |
| 605 | MachineInstr &MI = *BuildMI(BB: &MBB, MIMD: DL, MCID: get(Opcode: LoongArch::PseudoBR)).addMBB(MBB: TBB); |
| 606 | if (BytesAdded) |
| 607 | *BytesAdded += getInstSizeInBytes(MI); |
| 608 | return 1; |
| 609 | } |
| 610 | |
| 611 | // Either a one or two-way conditional branch. |
| 612 | MachineInstrBuilder MIB = BuildMI(BB: &MBB, MIMD: DL, MCID: get(Opcode: Cond[0].getImm())); |
| 613 | for (unsigned i = 1; i < Cond.size(); ++i) |
| 614 | MIB.add(MO: Cond[i]); |
| 615 | MIB.addMBB(MBB: TBB); |
| 616 | if (BytesAdded) |
| 617 | *BytesAdded += getInstSizeInBytes(MI: *MIB); |
| 618 | |
| 619 | // One-way conditional branch. |
| 620 | if (!FBB) |
| 621 | return 1; |
| 622 | |
| 623 | // Two-way conditional branch. |
| 624 | MachineInstr &MI = *BuildMI(BB: &MBB, MIMD: DL, MCID: get(Opcode: LoongArch::PseudoBR)).addMBB(MBB: FBB); |
| 625 | if (BytesAdded) |
| 626 | *BytesAdded += getInstSizeInBytes(MI); |
| 627 | return 2; |
| 628 | } |
| 629 | |
| 630 | void LoongArchInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB, |
| 631 | MachineBasicBlock &DestBB, |
| 632 | MachineBasicBlock &RestoreBB, |
| 633 | const DebugLoc &DL, |
| 634 | int64_t BrOffset, |
| 635 | RegScavenger *RS) const { |
| 636 | assert(RS && "RegScavenger required for long branching" ); |
| 637 | assert(MBB.empty() && |
| 638 | "new block should be inserted for expanding unconditional branch" ); |
| 639 | assert(MBB.pred_size() == 1); |
| 640 | |
| 641 | MachineFunction *MF = MBB.getParent(); |
| 642 | MachineRegisterInfo &MRI = MF->getRegInfo(); |
| 643 | const TargetRegisterInfo *TRI = MF->getSubtarget().getRegisterInfo(); |
| 644 | LoongArchMachineFunctionInfo *LAFI = |
| 645 | MF->getInfo<LoongArchMachineFunctionInfo>(); |
| 646 | bool Has32S = STI.hasFeature(Feature: LoongArch::Feature32S); |
| 647 | |
| 648 | if (!isInt<32>(x: BrOffset)) |
| 649 | report_fatal_error( |
| 650 | reason: "Branch offsets outside of the signed 32-bit range not supported" ); |
| 651 | |
| 652 | Register ScratchReg = MRI.createVirtualRegister(RegClass: &LoongArch::GPRRegClass); |
| 653 | MachineInstr *PCAI = nullptr; |
| 654 | MachineInstr *ADDI = nullptr; |
| 655 | auto II = MBB.end(); |
| 656 | unsigned ADDIOp = STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W; |
| 657 | |
| 658 | if (Has32S) { |
| 659 | PCAI = BuildMI(BB&: MBB, I: II, MIMD: DL, MCID: get(Opcode: LoongArch::PCALAU12I), DestReg: ScratchReg) |
| 660 | .addMBB(MBB: &DestBB, TargetFlags: LoongArchII::MO_PCREL_HI); |
| 661 | ADDI = BuildMI(BB&: MBB, I: II, MIMD: DL, MCID: get(Opcode: ADDIOp), DestReg: ScratchReg) |
| 662 | .addReg(RegNo: ScratchReg) |
| 663 | .addMBB(MBB: &DestBB, TargetFlags: LoongArchII::MO_PCREL_LO); |
| 664 | } else { |
| 665 | MCSymbol *PCAddSymbol = MF->getContext().createNamedTempSymbol(Name: "pcadd_hi" ); |
| 666 | PCAI = BuildMI(BB&: MBB, I: II, MIMD: DL, MCID: get(Opcode: LoongArch::PCADDU12I), DestReg: ScratchReg) |
| 667 | .addMBB(MBB: &DestBB, TargetFlags: LoongArchII::MO_PCADD_HI); |
| 668 | PCAI->setPreInstrSymbol(MF&: *MF, Symbol: PCAddSymbol); |
| 669 | ADDI = BuildMI(BB&: MBB, I: II, MIMD: DL, MCID: get(Opcode: ADDIOp), DestReg: ScratchReg) |
| 670 | .addReg(RegNo: ScratchReg) |
| 671 | .addSym(Sym: PCAddSymbol, TargetFlags: LoongArchII::MO_PCADD_LO); |
| 672 | } |
| 673 | BuildMI(BB&: MBB, I: II, MIMD: DL, MCID: get(Opcode: LoongArch::PseudoBRIND)) |
| 674 | .addReg(RegNo: ScratchReg, Flags: RegState::Kill) |
| 675 | .addImm(Val: 0); |
| 676 | |
| 677 | RS->enterBasicBlockEnd(MBB); |
| 678 | Register Scav = RS->scavengeRegisterBackwards( |
| 679 | RC: LoongArch::GPRRegClass, To: PCAI->getIterator(), /*RestoreAfter=*/false, |
| 680 | /*SPAdj=*/0, /*AllowSpill=*/false); |
| 681 | if (Scav != LoongArch::NoRegister) |
| 682 | RS->setRegUsed(Reg: Scav); |
| 683 | else { |
| 684 | // When there is no scavenged register, it needs to specify a register. |
| 685 | // Specify t8 register because it won't be used too often. |
| 686 | Scav = LoongArch::R20; |
| 687 | int FrameIndex = LAFI->getBranchRelaxationSpillFrameIndex(); |
| 688 | if (FrameIndex == -1) |
| 689 | report_fatal_error(reason: "The function size is incorrectly estimated." ); |
| 690 | storeRegToStackSlot(MBB, I: PCAI, SrcReg: Scav, /*IsKill=*/true, FI: FrameIndex, |
| 691 | RC: &LoongArch::GPRRegClass, VReg: Register()); |
| 692 | TRI->eliminateFrameIndex(MI: std::prev(x: PCAI->getIterator()), |
| 693 | /*SpAdj=*/SPAdj: 0, /*FIOperandNum=*/1); |
| 694 | PCAI->getOperand(i: 1).setMBB(&RestoreBB); |
| 695 | if (Has32S) |
| 696 | ADDI->getOperand(i: 2).setMBB(&RestoreBB); |
| 697 | loadRegFromStackSlot(MBB&: RestoreBB, I: RestoreBB.end(), DstReg: Scav, FI: FrameIndex, |
| 698 | RC: &LoongArch::GPRRegClass, VReg: Register()); |
| 699 | TRI->eliminateFrameIndex(MI: RestoreBB.back(), |
| 700 | /*SpAdj=*/SPAdj: 0, /*FIOperandNum=*/1); |
| 701 | } |
| 702 | MRI.replaceRegWith(FromReg: ScratchReg, ToReg: Scav); |
| 703 | MRI.clearVirtRegs(); |
| 704 | } |
| 705 | |
| 706 | static unsigned getOppositeBranchOpc(unsigned Opc) { |
| 707 | switch (Opc) { |
| 708 | default: |
| 709 | llvm_unreachable("Unrecognized conditional branch" ); |
| 710 | case LoongArch::BEQ: |
| 711 | return LoongArch::BNE; |
| 712 | case LoongArch::BNE: |
| 713 | return LoongArch::BEQ; |
| 714 | case LoongArch::BEQZ: |
| 715 | return LoongArch::BNEZ; |
| 716 | case LoongArch::BNEZ: |
| 717 | return LoongArch::BEQZ; |
| 718 | case LoongArch::BCEQZ: |
| 719 | return LoongArch::BCNEZ; |
| 720 | case LoongArch::BCNEZ: |
| 721 | return LoongArch::BCEQZ; |
| 722 | case LoongArch::BLT: |
| 723 | return LoongArch::BGE; |
| 724 | case LoongArch::BGE: |
| 725 | return LoongArch::BLT; |
| 726 | case LoongArch::BLTU: |
| 727 | return LoongArch::BGEU; |
| 728 | case LoongArch::BGEU: |
| 729 | return LoongArch::BLTU; |
| 730 | } |
| 731 | } |
| 732 | |
| 733 | bool LoongArchInstrInfo::reverseBranchCondition( |
| 734 | SmallVectorImpl<MachineOperand> &Cond) const { |
| 735 | assert((Cond.size() && Cond.size() <= 3) && "Invalid branch condition!" ); |
| 736 | Cond[0].setImm(getOppositeBranchOpc(Opc: Cond[0].getImm())); |
| 737 | return false; |
| 738 | } |
| 739 | |
| 740 | std::pair<unsigned, unsigned> |
| 741 | LoongArchInstrInfo::decomposeMachineOperandsTargetFlags(unsigned TF) const { |
| 742 | const unsigned Mask = LoongArchII::MO_DIRECT_FLAG_MASK; |
| 743 | return std::make_pair(x: TF & Mask, y: TF & ~Mask); |
| 744 | } |
| 745 | |
| 746 | ArrayRef<std::pair<unsigned, const char *>> |
| 747 | LoongArchInstrInfo::getSerializableDirectMachineOperandTargetFlags() const { |
| 748 | using namespace LoongArchII; |
| 749 | // TODO: Add more target flags. |
| 750 | static const std::pair<unsigned, const char *> TargetFlags[] = { |
| 751 | {MO_CALL, "loongarch-call" }, |
| 752 | {MO_CALL_PLT, "loongarch-call-plt" }, |
| 753 | {MO_PCREL_HI, "loongarch-pcrel-hi" }, |
| 754 | {MO_PCREL_LO, "loongarch-pcrel-lo" }, |
| 755 | {MO_PCREL64_LO, "loongarch-pcrel64-lo" }, |
| 756 | {MO_PCREL64_HI, "loongarch-pcrel64-hi" }, |
| 757 | {MO_GOT_PC_HI, "loongarch-got-pc-hi" }, |
| 758 | {MO_GOT_PC_LO, "loongarch-got-pc-lo" }, |
| 759 | {MO_GOT_PC64_LO, "loongarch-got-pc64-lo" }, |
| 760 | {MO_GOT_PC64_HI, "loongarch-got-pc64-hi" }, |
| 761 | {MO_LE_HI, "loongarch-le-hi" }, |
| 762 | {MO_LE_LO, "loongarch-le-lo" }, |
| 763 | {MO_LE64_LO, "loongarch-le64-lo" }, |
| 764 | {MO_LE64_HI, "loongarch-le64-hi" }, |
| 765 | {MO_IE_PC_HI, "loongarch-ie-pc-hi" }, |
| 766 | {MO_IE_PC_LO, "loongarch-ie-pc-lo" }, |
| 767 | {MO_IE_PC64_LO, "loongarch-ie-pc64-lo" }, |
| 768 | {MO_IE_PC64_HI, "loongarch-ie-pc64-hi" }, |
| 769 | {MO_LD_PC_HI, "loongarch-ld-pc-hi" }, |
| 770 | {MO_GD_PC_HI, "loongarch-gd-pc-hi" }, |
| 771 | {MO_CALL30, "loongarch-call30" }, |
| 772 | {MO_CALL36, "loongarch-call36" }, |
| 773 | {MO_DESC_PC_HI, "loongarch-desc-pc-hi" }, |
| 774 | {MO_DESC_PC_LO, "loongarch-desc-pc-lo" }, |
| 775 | {MO_DESC64_PC_LO, "loongarch-desc64-pc-lo" }, |
| 776 | {MO_DESC64_PC_HI, "loongarch-desc64-pc-hi" }, |
| 777 | {MO_DESC_LD, "loongarch-desc-ld" }, |
| 778 | {MO_DESC_CALL, "loongarch-desc-call" }, |
| 779 | {MO_LE_HI_R, "loongarch-le-hi-r" }, |
| 780 | {MO_LE_ADD_R, "loongarch-le-add-r" }, |
| 781 | {MO_LE_LO_R, "loongarch-le-lo-r" }, |
| 782 | {MO_PCADD_HI, "loongarch-pcadd-hi" }, |
| 783 | {MO_PCADD_LO, "loongarch-pcadd-lo" }, |
| 784 | {MO_GOT_PCADD_HI, "loongarch-got-pcadd-hi" }, |
| 785 | {MO_GOT_PCADD_LO, "loongarch-got-pcadd-lo" }, |
| 786 | {MO_IE_PCADD_HI, "loongarch-ie-pcadd-hi" }, |
| 787 | {MO_IE_PCADD_LO, "loongarch-ie-pcadd-lo" }, |
| 788 | {MO_LD_PCADD_HI, "loongarch-ld-pcadd-hi" }, |
| 789 | {MO_LD_PCADD_LO, "loongarch-ld-pcadd-lo" }, |
| 790 | {MO_GD_PCADD_HI, "loongarch-gd-pcadd-hi" }, |
| 791 | {MO_GD_PCADD_LO, "loongarch-gd-pcadd-lo" }, |
| 792 | {MO_DESC_PCADD_HI, "loongarch-pcadd-desc-hi" }, |
| 793 | {MO_DESC_PCADD_LO, "loongarch-pcadd-desc-lo" }}; |
| 794 | return ArrayRef(TargetFlags); |
| 795 | } |
| 796 | |
| 797 | ArrayRef<std::pair<unsigned, const char *>> |
| 798 | LoongArchInstrInfo::getSerializableBitmaskMachineOperandTargetFlags() const { |
| 799 | using namespace LoongArchII; |
| 800 | static const std::pair<unsigned, const char *> TargetFlags[] = { |
| 801 | {MO_RELAX, "loongarch-relax" }}; |
| 802 | return ArrayRef(TargetFlags); |
| 803 | } |
| 804 | |
| 805 | bool LoongArchInstrInfo::canFoldIntoAddrMode(const MachineInstr &MemI, |
| 806 | Register Reg, |
| 807 | const MachineInstr &AddrI, |
| 808 | ExtAddrMode &AM) const { |
| 809 | enum MemIOffsetType { |
| 810 | Imm14Shift2, |
| 811 | Imm12, |
| 812 | Imm11Shift1, |
| 813 | Imm10Shift2, |
| 814 | Imm9Shift3, |
| 815 | Imm8, |
| 816 | Imm8Shift1, |
| 817 | Imm8Shift2, |
| 818 | Imm8Shift3 |
| 819 | }; |
| 820 | |
| 821 | MemIOffsetType OT; |
| 822 | switch (MemI.getOpcode()) { |
| 823 | default: |
| 824 | return false; |
| 825 | case LoongArch::LDPTR_W: |
| 826 | case LoongArch::LDPTR_D: |
| 827 | case LoongArch::STPTR_W: |
| 828 | case LoongArch::STPTR_D: |
| 829 | OT = Imm14Shift2; |
| 830 | break; |
| 831 | case LoongArch::LD_B: |
| 832 | case LoongArch::LD_H: |
| 833 | case LoongArch::LD_W: |
| 834 | case LoongArch::LD_D: |
| 835 | case LoongArch::LD_BU: |
| 836 | case LoongArch::LD_HU: |
| 837 | case LoongArch::LD_WU: |
| 838 | case LoongArch::ST_B: |
| 839 | case LoongArch::ST_H: |
| 840 | case LoongArch::ST_W: |
| 841 | case LoongArch::ST_D: |
| 842 | case LoongArch::FLD_S: |
| 843 | case LoongArch::FLD_D: |
| 844 | case LoongArch::FST_S: |
| 845 | case LoongArch::FST_D: |
| 846 | case LoongArch::VLD: |
| 847 | case LoongArch::VST: |
| 848 | case LoongArch::XVLD: |
| 849 | case LoongArch::XVST: |
| 850 | case LoongArch::VLDREPL_B: |
| 851 | case LoongArch::XVLDREPL_B: |
| 852 | OT = Imm12; |
| 853 | break; |
| 854 | case LoongArch::VLDREPL_H: |
| 855 | case LoongArch::XVLDREPL_H: |
| 856 | OT = Imm11Shift1; |
| 857 | break; |
| 858 | case LoongArch::VLDREPL_W: |
| 859 | case LoongArch::XVLDREPL_W: |
| 860 | OT = Imm10Shift2; |
| 861 | break; |
| 862 | case LoongArch::VLDREPL_D: |
| 863 | case LoongArch::XVLDREPL_D: |
| 864 | OT = Imm9Shift3; |
| 865 | break; |
| 866 | case LoongArch::VSTELM_B: |
| 867 | case LoongArch::XVSTELM_B: |
| 868 | OT = Imm8; |
| 869 | break; |
| 870 | case LoongArch::VSTELM_H: |
| 871 | case LoongArch::XVSTELM_H: |
| 872 | OT = Imm8Shift1; |
| 873 | break; |
| 874 | case LoongArch::VSTELM_W: |
| 875 | case LoongArch::XVSTELM_W: |
| 876 | OT = Imm8Shift2; |
| 877 | break; |
| 878 | case LoongArch::VSTELM_D: |
| 879 | case LoongArch::XVSTELM_D: |
| 880 | OT = Imm8Shift3; |
| 881 | break; |
| 882 | } |
| 883 | |
| 884 | if (MemI.getOperand(i: 0).getReg() == Reg) |
| 885 | return false; |
| 886 | |
| 887 | if ((AddrI.getOpcode() != LoongArch::ADDI_W && |
| 888 | AddrI.getOpcode() != LoongArch::ADDI_D) || |
| 889 | !AddrI.getOperand(i: 1).isReg() || !AddrI.getOperand(i: 2).isImm()) |
| 890 | return false; |
| 891 | |
| 892 | int64_t OldOffset = MemI.getOperand(i: 2).getImm(); |
| 893 | int64_t Disp = AddrI.getOperand(i: 2).getImm(); |
| 894 | int64_t NewOffset = OldOffset + Disp; |
| 895 | if (!STI.is64Bit()) |
| 896 | NewOffset = SignExtend64<32>(x: NewOffset); |
| 897 | |
| 898 | if (!(OT == Imm14Shift2 && isShiftedInt<14, 2>(x: NewOffset) && STI.hasUAL()) && |
| 899 | !(OT == Imm12 && isInt<12>(x: NewOffset)) && |
| 900 | !(OT == Imm11Shift1 && isShiftedInt<11, 1>(x: NewOffset)) && |
| 901 | !(OT == Imm10Shift2 && isShiftedInt<10, 2>(x: NewOffset)) && |
| 902 | !(OT == Imm9Shift3 && isShiftedInt<9, 3>(x: NewOffset)) && |
| 903 | !(OT == Imm8 && isInt<8>(x: NewOffset)) && |
| 904 | !(OT == Imm8Shift1 && isShiftedInt<8, 1>(x: NewOffset)) && |
| 905 | !(OT == Imm8Shift2 && isShiftedInt<8, 2>(x: NewOffset)) && |
| 906 | !(OT == Imm8Shift3 && isShiftedInt<8, 3>(x: NewOffset))) |
| 907 | return false; |
| 908 | |
| 909 | AM.BaseReg = AddrI.getOperand(i: 1).getReg(); |
| 910 | AM.ScaledReg = 0; |
| 911 | AM.Scale = 0; |
| 912 | AM.Displacement = NewOffset; |
| 913 | AM.Form = ExtAddrMode::Formula::Basic; |
| 914 | return true; |
| 915 | } |
| 916 | |
| 917 | MachineInstr * |
| 918 | LoongArchInstrInfo::emitLdStWithAddr(MachineInstr &MemI, |
| 919 | const ExtAddrMode &AM) const { |
| 920 | const DebugLoc &DL = MemI.getDebugLoc(); |
| 921 | MachineBasicBlock &MBB = *MemI.getParent(); |
| 922 | |
| 923 | assert(AM.ScaledReg == 0 && AM.Scale == 0 && |
| 924 | "Addressing mode not supported for folding" ); |
| 925 | |
| 926 | unsigned MemIOp = MemI.getOpcode(); |
| 927 | switch (MemIOp) { |
| 928 | default: |
| 929 | return BuildMI(BB&: MBB, I&: MemI, MIMD: DL, MCID: get(Opcode: MemIOp)) |
| 930 | .addReg(RegNo: MemI.getOperand(i: 0).getReg(), Flags: getDefRegState(B: MemI.mayLoad())) |
| 931 | .addReg(RegNo: AM.BaseReg) |
| 932 | .addImm(Val: AM.Displacement) |
| 933 | .setMemRefs(MemI.memoperands()) |
| 934 | .setMIFlags(MemI.getFlags()); |
| 935 | case LoongArch::VSTELM_B: |
| 936 | case LoongArch::VSTELM_H: |
| 937 | case LoongArch::VSTELM_W: |
| 938 | case LoongArch::VSTELM_D: |
| 939 | case LoongArch::XVSTELM_B: |
| 940 | case LoongArch::XVSTELM_H: |
| 941 | case LoongArch::XVSTELM_W: |
| 942 | case LoongArch::XVSTELM_D: |
| 943 | return BuildMI(BB&: MBB, I&: MemI, MIMD: DL, MCID: get(Opcode: MemIOp)) |
| 944 | .addReg(RegNo: MemI.getOperand(i: 0).getReg()) |
| 945 | .addReg(RegNo: AM.BaseReg) |
| 946 | .addImm(Val: AM.Displacement) |
| 947 | .addImm(Val: MemI.getOperand(i: 3).getImm()) |
| 948 | .setMemRefs(MemI.memoperands()) |
| 949 | .setMIFlags(MemI.getFlags()); |
| 950 | } |
| 951 | } |
| 952 | |
| 953 | // Returns true if this is the sext.w pattern, addi.w rd, rs, 0. |
| 954 | bool LoongArch::isSEXT_W(const MachineInstr &MI) { |
| 955 | return MI.getOpcode() == LoongArch::ADDI_W && MI.getOperand(i: 1).isReg() && |
| 956 | MI.getOperand(i: 2).isImm() && MI.getOperand(i: 2).getImm() == 0; |
| 957 | } |
| 958 | |