| //===- AArch64ExpandPseudoInsts.cpp - Expand pseudo instructions ----------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file contains a pass that expands pseudo instructions into target |
| // instructions to allow proper scheduling and other late optimizations. This |
| // pass should be run after register allocation but before the post-regalloc |
| // scheduling pass. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "AArch64ExpandImm.h" |
| #include "AArch64InstrInfo.h" |
| #include "AArch64MachineFunctionInfo.h" |
| #include "AArch64Subtarget.h" |
| #include "MCTargetDesc/AArch64AddressingModes.h" |
| #include "Utils/AArch64BaseInfo.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/Triple.h" |
| #include "llvm/CodeGen/LivePhysRegs.h" |
| #include "llvm/CodeGen/MachineBasicBlock.h" |
| #include "llvm/CodeGen/MachineFunction.h" |
| #include "llvm/CodeGen/MachineFunctionPass.h" |
| #include "llvm/CodeGen/MachineInstr.h" |
| #include "llvm/CodeGen/MachineInstrBuilder.h" |
| #include "llvm/CodeGen/MachineOperand.h" |
| #include "llvm/CodeGen/TargetSubtargetInfo.h" |
| #include "llvm/IR/DebugLoc.h" |
| #include "llvm/MC/MCInstrDesc.h" |
| #include "llvm/Pass.h" |
| #include "llvm/Support/CodeGen.h" |
| #include "llvm/Support/MathExtras.h" |
| #include "llvm/Target/TargetMachine.h" |
| #include <cassert> |
| #include <cstdint> |
| #include <iterator> |
| #include <limits> |
| #include <utility> |
| |
| using namespace llvm; |
| |
| #define AARCH64_EXPAND_PSEUDO_NAME "AArch64 pseudo instruction expansion pass" |
| |
| namespace { |
| |
| class AArch64ExpandPseudo : public MachineFunctionPass { |
| public: |
| const AArch64InstrInfo *TII; |
| |
| static char ID; |
| |
| AArch64ExpandPseudo() : MachineFunctionPass(ID) { |
| initializeAArch64ExpandPseudoPass(*PassRegistry::getPassRegistry()); |
| } |
| |
| bool runOnMachineFunction(MachineFunction &Fn) override; |
| |
| StringRef getPassName() const override { return AARCH64_EXPAND_PSEUDO_NAME; } |
| |
| private: |
| bool expandMBB(MachineBasicBlock &MBB); |
| bool expandMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, |
| MachineBasicBlock::iterator &NextMBBI); |
| bool expandMOVImm(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, |
| unsigned BitSize); |
| |
| bool expandCMP_SWAP(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, |
| unsigned LdarOp, unsigned StlrOp, unsigned CmpOp, |
| unsigned ExtendImm, unsigned ZeroReg, |
| MachineBasicBlock::iterator &NextMBBI); |
| bool expandCMP_SWAP_128(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator MBBI, |
| MachineBasicBlock::iterator &NextMBBI); |
| bool expandSetTagLoop(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator MBBI, |
| MachineBasicBlock::iterator &NextMBBI); |
| }; |
| |
| } // end anonymous namespace |
| |
| char AArch64ExpandPseudo::ID = 0; |
| |
| INITIALIZE_PASS(AArch64ExpandPseudo, "aarch64-expand-pseudo", |
| AARCH64_EXPAND_PSEUDO_NAME, false, false) |
| |
| /// Transfer implicit operands on the pseudo instruction to the |
| /// instructions created from the expansion. |
| static void transferImpOps(MachineInstr &OldMI, MachineInstrBuilder &UseMI, |
| MachineInstrBuilder &DefMI) { |
| const MCInstrDesc &Desc = OldMI.getDesc(); |
| for (unsigned i = Desc.getNumOperands(), e = OldMI.getNumOperands(); i != e; |
| ++i) { |
| const MachineOperand &MO = OldMI.getOperand(i); |
| assert(MO.isReg() && MO.getReg()); |
| if (MO.isUse()) |
| UseMI.add(MO); |
| else |
| DefMI.add(MO); |
| } |
| } |
| |
| /// Expand a MOVi32imm or MOVi64imm pseudo instruction to one or more |
| /// real move-immediate instructions to synthesize the immediate. |
| bool AArch64ExpandPseudo::expandMOVImm(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator MBBI, |
| unsigned BitSize) { |
| MachineInstr &MI = *MBBI; |
| Register DstReg = MI.getOperand(0).getReg(); |
| uint64_t RenamableState = |
| MI.getOperand(0).isRenamable() ? RegState::Renamable : 0; |
| uint64_t Imm = MI.getOperand(1).getImm(); |
| |
| if (DstReg == AArch64::XZR || DstReg == AArch64::WZR) { |
| // Useless def, and we don't want to risk creating an invalid ORR (which |
| // would really write to sp). |
| MI.eraseFromParent(); |
| return true; |
| } |
| |
| SmallVector<AArch64_IMM::ImmInsnModel, 4> Insn; |
| AArch64_IMM::expandMOVImm(Imm, BitSize, Insn); |
| assert(Insn.size() != 0); |
| |
| SmallVector<MachineInstrBuilder, 4> MIBS; |
| for (auto I = Insn.begin(), E = Insn.end(); I != E; ++I) { |
| bool LastItem = std::next(I) == E; |
| switch (I->Opcode) |
| { |
| default: llvm_unreachable("unhandled!"); break; |
| |
| case AArch64::ORRWri: |
| case AArch64::ORRXri: |
| MIBS.push_back(BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(I->Opcode)) |
| .add(MI.getOperand(0)) |
| .addReg(BitSize == 32 ? AArch64::WZR : AArch64::XZR) |
| .addImm(I->Op2)); |
| break; |
| case AArch64::MOVNWi: |
| case AArch64::MOVNXi: |
| case AArch64::MOVZWi: |
| case AArch64::MOVZXi: { |
| bool DstIsDead = MI.getOperand(0).isDead(); |
| MIBS.push_back(BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(I->Opcode)) |
| .addReg(DstReg, RegState::Define | |
| getDeadRegState(DstIsDead && LastItem) | |
| RenamableState) |
| .addImm(I->Op1) |
| .addImm(I->Op2)); |
| } break; |
| case AArch64::MOVKWi: |
| case AArch64::MOVKXi: { |
| Register DstReg = MI.getOperand(0).getReg(); |
| bool DstIsDead = MI.getOperand(0).isDead(); |
| MIBS.push_back(BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(I->Opcode)) |
| .addReg(DstReg, |
| RegState::Define | |
| getDeadRegState(DstIsDead && LastItem) | |
| RenamableState) |
| .addReg(DstReg) |
| .addImm(I->Op1) |
| .addImm(I->Op2)); |
| } break; |
| } |
| } |
| transferImpOps(MI, MIBS.front(), MIBS.back()); |
| MI.eraseFromParent(); |
| return true; |
| } |
| |
| bool AArch64ExpandPseudo::expandCMP_SWAP( |
| MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, unsigned LdarOp, |
| unsigned StlrOp, unsigned CmpOp, unsigned ExtendImm, unsigned ZeroReg, |
| MachineBasicBlock::iterator &NextMBBI) { |
| MachineInstr &MI = *MBBI; |
| DebugLoc DL = MI.getDebugLoc(); |
| const MachineOperand &Dest = MI.getOperand(0); |
| Register StatusReg = MI.getOperand(1).getReg(); |
| bool StatusDead = MI.getOperand(1).isDead(); |
| // Duplicating undef operands into 2 instructions does not guarantee the same |
| // value on both; However undef should be replaced by xzr anyway. |
| assert(!MI.getOperand(2).isUndef() && "cannot handle undef"); |
| Register AddrReg = MI.getOperand(2).getReg(); |
| Register DesiredReg = MI.getOperand(3).getReg(); |
| Register NewReg = MI.getOperand(4).getReg(); |
| |
| MachineFunction *MF = MBB.getParent(); |
| auto LoadCmpBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); |
| auto StoreBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); |
| auto DoneBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); |
| |
| MF->insert(++MBB.getIterator(), LoadCmpBB); |
| MF->insert(++LoadCmpBB->getIterator(), StoreBB); |
| MF->insert(++StoreBB->getIterator(), DoneBB); |
| |
| // .Lloadcmp: |
| // mov wStatus, 0 |
| // ldaxr xDest, [xAddr] |
| // cmp xDest, xDesired |
| // b.ne .Ldone |
| if (!StatusDead) |
| BuildMI(LoadCmpBB, DL, TII->get(AArch64::MOVZWi), StatusReg) |
| .addImm(0).addImm(0); |
| BuildMI(LoadCmpBB, DL, TII->get(LdarOp), Dest.getReg()) |
| .addReg(AddrReg); |
| BuildMI(LoadCmpBB, DL, TII->get(CmpOp), ZeroReg) |
| .addReg(Dest.getReg(), getKillRegState(Dest.isDead())) |
| .addReg(DesiredReg) |
| .addImm(ExtendImm); |
| BuildMI(LoadCmpBB, DL, TII->get(AArch64::Bcc)) |
| .addImm(AArch64CC::NE) |
| .addMBB(DoneBB) |
| .addReg(AArch64::NZCV, RegState::Implicit | RegState::Kill); |
| LoadCmpBB->addSuccessor(DoneBB); |
| LoadCmpBB->addSuccessor(StoreBB); |
| |
| // .Lstore: |
| // stlxr wStatus, xNew, [xAddr] |
| // cbnz wStatus, .Lloadcmp |
| BuildMI(StoreBB, DL, TII->get(StlrOp), StatusReg) |
| .addReg(NewReg) |
| .addReg(AddrReg); |
| BuildMI(StoreBB, DL, TII->get(AArch64::CBNZW)) |
| .addReg(StatusReg, getKillRegState(StatusDead)) |
| .addMBB(LoadCmpBB); |
| StoreBB->addSuccessor(LoadCmpBB); |
| StoreBB->addSuccessor(DoneBB); |
| |
| DoneBB->splice(DoneBB->end(), &MBB, MI, MBB.end()); |
| DoneBB->transferSuccessors(&MBB); |
| |
| MBB.addSuccessor(LoadCmpBB); |
| |
| NextMBBI = MBB.end(); |
| MI.eraseFromParent(); |
| |
| // Recompute livein lists. |
| LivePhysRegs LiveRegs; |
| computeAndAddLiveIns(LiveRegs, *DoneBB); |
| computeAndAddLiveIns(LiveRegs, *StoreBB); |
| computeAndAddLiveIns(LiveRegs, *LoadCmpBB); |
| // Do an extra pass around the loop to get loop carried registers right. |
| StoreBB->clearLiveIns(); |
| computeAndAddLiveIns(LiveRegs, *StoreBB); |
| LoadCmpBB->clearLiveIns(); |
| computeAndAddLiveIns(LiveRegs, *LoadCmpBB); |
| |
| return true; |
| } |
| |
| bool AArch64ExpandPseudo::expandCMP_SWAP_128( |
| MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, |
| MachineBasicBlock::iterator &NextMBBI) { |
| MachineInstr &MI = *MBBI; |
| DebugLoc DL = MI.getDebugLoc(); |
| MachineOperand &DestLo = MI.getOperand(0); |
| MachineOperand &DestHi = MI.getOperand(1); |
| Register StatusReg = MI.getOperand(2).getReg(); |
| bool StatusDead = MI.getOperand(2).isDead(); |
| // Duplicating undef operands into 2 instructions does not guarantee the same |
| // value on both; However undef should be replaced by xzr anyway. |
| assert(!MI.getOperand(3).isUndef() && "cannot handle undef"); |
| Register AddrReg = MI.getOperand(3).getReg(); |
| Register DesiredLoReg = MI.getOperand(4).getReg(); |
| Register DesiredHiReg = MI.getOperand(5).getReg(); |
| Register NewLoReg = MI.getOperand(6).getReg(); |
| Register NewHiReg = MI.getOperand(7).getReg(); |
| |
| MachineFunction *MF = MBB.getParent(); |
| auto LoadCmpBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); |
| auto StoreBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); |
| auto DoneBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); |
| |
| MF->insert(++MBB.getIterator(), LoadCmpBB); |
| MF->insert(++LoadCmpBB->getIterator(), StoreBB); |
| MF->insert(++StoreBB->getIterator(), DoneBB); |
| |
| // .Lloadcmp: |
| // ldaxp xDestLo, xDestHi, [xAddr] |
| // cmp xDestLo, xDesiredLo |
| // sbcs xDestHi, xDesiredHi |
| // b.ne .Ldone |
| BuildMI(LoadCmpBB, DL, TII->get(AArch64::LDAXPX)) |
| .addReg(DestLo.getReg(), RegState::Define) |
| .addReg(DestHi.getReg(), RegState::Define) |
| .addReg(AddrReg); |
| BuildMI(LoadCmpBB, DL, TII->get(AArch64::SUBSXrs), AArch64::XZR) |
| .addReg(DestLo.getReg(), getKillRegState(DestLo.isDead())) |
| .addReg(DesiredLoReg) |
| .addImm(0); |
| BuildMI(LoadCmpBB, DL, TII->get(AArch64::CSINCWr), StatusReg) |
| .addUse(AArch64::WZR) |
| .addUse(AArch64::WZR) |
| .addImm(AArch64CC::EQ); |
| BuildMI(LoadCmpBB, DL, TII->get(AArch64::SUBSXrs), AArch64::XZR) |
| .addReg(DestHi.getReg(), getKillRegState(DestHi.isDead())) |
| .addReg(DesiredHiReg) |
| .addImm(0); |
| BuildMI(LoadCmpBB, DL, TII->get(AArch64::CSINCWr), StatusReg) |
| .addUse(StatusReg, RegState::Kill) |
| .addUse(StatusReg, RegState::Kill) |
| .addImm(AArch64CC::EQ); |
| BuildMI(LoadCmpBB, DL, TII->get(AArch64::CBNZW)) |
| .addUse(StatusReg, getKillRegState(StatusDead)) |
| .addMBB(DoneBB); |
| LoadCmpBB->addSuccessor(DoneBB); |
| LoadCmpBB->addSuccessor(StoreBB); |
| |
| // .Lstore: |
| // stlxp wStatus, xNewLo, xNewHi, [xAddr] |
| // cbnz wStatus, .Lloadcmp |
| BuildMI(StoreBB, DL, TII->get(AArch64::STLXPX), StatusReg) |
| .addReg(NewLoReg) |
| .addReg(NewHiReg) |
| .addReg(AddrReg); |
| BuildMI(StoreBB, DL, TII->get(AArch64::CBNZW)) |
| .addReg(StatusReg, getKillRegState(StatusDead)) |
| .addMBB(LoadCmpBB); |
| StoreBB->addSuccessor(LoadCmpBB); |
| StoreBB->addSuccessor(DoneBB); |
| |
| DoneBB->splice(DoneBB->end(), &MBB, MI, MBB.end()); |
| DoneBB->transferSuccessors(&MBB); |
| |
| MBB.addSuccessor(LoadCmpBB); |
| |
| NextMBBI = MBB.end(); |
| MI.eraseFromParent(); |
| |
| // Recompute liveness bottom up. |
| LivePhysRegs LiveRegs; |
| computeAndAddLiveIns(LiveRegs, *DoneBB); |
| computeAndAddLiveIns(LiveRegs, *StoreBB); |
| computeAndAddLiveIns(LiveRegs, *LoadCmpBB); |
| // Do an extra pass in the loop to get the loop carried dependencies right. |
| StoreBB->clearLiveIns(); |
| computeAndAddLiveIns(LiveRegs, *StoreBB); |
| LoadCmpBB->clearLiveIns(); |
| computeAndAddLiveIns(LiveRegs, *LoadCmpBB); |
| |
| return true; |
| } |
| |
| bool AArch64ExpandPseudo::expandSetTagLoop( |
| MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, |
| MachineBasicBlock::iterator &NextMBBI) { |
| MachineInstr &MI = *MBBI; |
| DebugLoc DL = MI.getDebugLoc(); |
| Register SizeReg = MI.getOperand(2).getReg(); |
| Register AddressReg = MI.getOperand(3).getReg(); |
| |
| MachineFunction *MF = MBB.getParent(); |
| |
| bool ZeroData = MI.getOpcode() == AArch64::STZGloop; |
| const unsigned OpCode = |
| ZeroData ? AArch64::STZ2GPostIndex : AArch64::ST2GPostIndex; |
| |
| auto LoopBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); |
| auto DoneBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); |
| |
| MF->insert(++MBB.getIterator(), LoopBB); |
| MF->insert(++LoopBB->getIterator(), DoneBB); |
| |
| BuildMI(LoopBB, DL, TII->get(OpCode)) |
| .addDef(AddressReg) |
| .addReg(AddressReg) |
| .addReg(AddressReg) |
| .addImm(2) |
| .cloneMemRefs(MI) |
| .setMIFlags(MI.getFlags()); |
| BuildMI(LoopBB, DL, TII->get(AArch64::SUBXri)) |
| .addDef(SizeReg) |
| .addReg(SizeReg) |
| .addImm(16 * 2) |
| .addImm(0); |
| BuildMI(LoopBB, DL, TII->get(AArch64::CBNZX)).addUse(SizeReg).addMBB(LoopBB); |
| |
| LoopBB->addSuccessor(LoopBB); |
| LoopBB->addSuccessor(DoneBB); |
| |
| DoneBB->splice(DoneBB->end(), &MBB, MI, MBB.end()); |
| DoneBB->transferSuccessors(&MBB); |
| |
| MBB.addSuccessor(LoopBB); |
| |
| NextMBBI = MBB.end(); |
| MI.eraseFromParent(); |
| // Recompute liveness bottom up. |
| LivePhysRegs LiveRegs; |
| computeAndAddLiveIns(LiveRegs, *DoneBB); |
| computeAndAddLiveIns(LiveRegs, *LoopBB); |
| // Do an extra pass in the loop to get the loop carried dependencies right. |
| // FIXME: is this necessary? |
| LoopBB->clearLiveIns(); |
| computeAndAddLiveIns(LiveRegs, *LoopBB); |
| DoneBB->clearLiveIns(); |
| computeAndAddLiveIns(LiveRegs, *DoneBB); |
| |
| return true; |
| } |
| |
| /// If MBBI references a pseudo instruction that should be expanded here, |
| /// do the expansion and return true. Otherwise return false. |
| bool AArch64ExpandPseudo::expandMI(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator MBBI, |
| MachineBasicBlock::iterator &NextMBBI) { |
| MachineInstr &MI = *MBBI; |
| unsigned Opcode = MI.getOpcode(); |
| switch (Opcode) { |
| default: |
| break; |
| |
| case AArch64::ADDWrr: |
| case AArch64::SUBWrr: |
| case AArch64::ADDXrr: |
| case AArch64::SUBXrr: |
| case AArch64::ADDSWrr: |
| case AArch64::SUBSWrr: |
| case AArch64::ADDSXrr: |
| case AArch64::SUBSXrr: |
| case AArch64::ANDWrr: |
| case AArch64::ANDXrr: |
| case AArch64::BICWrr: |
| case AArch64::BICXrr: |
| case AArch64::ANDSWrr: |
| case AArch64::ANDSXrr: |
| case AArch64::BICSWrr: |
| case AArch64::BICSXrr: |
| case AArch64::EONWrr: |
| case AArch64::EONXrr: |
| case AArch64::EORWrr: |
| case AArch64::EORXrr: |
| case AArch64::ORNWrr: |
| case AArch64::ORNXrr: |
| case AArch64::ORRWrr: |
| case AArch64::ORRXrr: { |
| unsigned Opcode; |
| switch (MI.getOpcode()) { |
| default: |
| return false; |
| case AArch64::ADDWrr: Opcode = AArch64::ADDWrs; break; |
| case AArch64::SUBWrr: Opcode = AArch64::SUBWrs; break; |
| case AArch64::ADDXrr: Opcode = AArch64::ADDXrs; break; |
| case AArch64::SUBXrr: Opcode = AArch64::SUBXrs; break; |
| case AArch64::ADDSWrr: Opcode = AArch64::ADDSWrs; break; |
| case AArch64::SUBSWrr: Opcode = AArch64::SUBSWrs; break; |
| case AArch64::ADDSXrr: Opcode = AArch64::ADDSXrs; break; |
| case AArch64::SUBSXrr: Opcode = AArch64::SUBSXrs; break; |
| case AArch64::ANDWrr: Opcode = AArch64::ANDWrs; break; |
| case AArch64::ANDXrr: Opcode = AArch64::ANDXrs; break; |
| case AArch64::BICWrr: Opcode = AArch64::BICWrs; break; |
| case AArch64::BICXrr: Opcode = AArch64::BICXrs; break; |
| case AArch64::ANDSWrr: Opcode = AArch64::ANDSWrs; break; |
| case AArch64::ANDSXrr: Opcode = AArch64::ANDSXrs; break; |
| case AArch64::BICSWrr: Opcode = AArch64::BICSWrs; break; |
| case AArch64::BICSXrr: Opcode = AArch64::BICSXrs; break; |
| case AArch64::EONWrr: Opcode = AArch64::EONWrs; break; |
| case AArch64::EONXrr: Opcode = AArch64::EONXrs; break; |
| case AArch64::EORWrr: Opcode = AArch64::EORWrs; break; |
| case AArch64::EORXrr: Opcode = AArch64::EORXrs; break; |
| case AArch64::ORNWrr: Opcode = AArch64::ORNWrs; break; |
| case AArch64::ORNXrr: Opcode = AArch64::ORNXrs; break; |
| case AArch64::ORRWrr: Opcode = AArch64::ORRWrs; break; |
| case AArch64::ORRXrr: Opcode = AArch64::ORRXrs; break; |
| } |
| MachineInstrBuilder MIB1 = |
| BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(Opcode), |
| MI.getOperand(0).getReg()) |
| .add(MI.getOperand(1)) |
| .add(MI.getOperand(2)) |
| .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, 0)); |
| transferImpOps(MI, MIB1, MIB1); |
| MI.eraseFromParent(); |
| return true; |
| } |
| |
| case AArch64::LOADgot: { |
| MachineFunction *MF = MBB.getParent(); |
| Register DstReg = MI.getOperand(0).getReg(); |
| const MachineOperand &MO1 = MI.getOperand(1); |
| unsigned Flags = MO1.getTargetFlags(); |
| |
| if (MF->getTarget().getCodeModel() == CodeModel::Tiny) { |
| // Tiny codemodel expand to LDR |
| MachineInstrBuilder MIB = BuildMI(MBB, MBBI, MI.getDebugLoc(), |
| TII->get(AArch64::LDRXl), DstReg); |
| |
| if (MO1.isGlobal()) { |
| MIB.addGlobalAddress(MO1.getGlobal(), 0, Flags); |
| } else if (MO1.isSymbol()) { |
| MIB.addExternalSymbol(MO1.getSymbolName(), Flags); |
| } else { |
| assert(MO1.isCPI() && |
| "Only expect globals, externalsymbols, or constant pools"); |
| MIB.addConstantPoolIndex(MO1.getIndex(), MO1.getOffset(), Flags); |
| } |
| } else { |
| // Small codemodel expand into ADRP + LDR. |
| MachineFunction &MF = *MI.getParent()->getParent(); |
| DebugLoc DL = MI.getDebugLoc(); |
| MachineInstrBuilder MIB1 = |
| BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::ADRP), DstReg); |
| |
| MachineInstrBuilder MIB2; |
| if (MF.getSubtarget<AArch64Subtarget>().isTargetILP32()) { |
| auto TRI = MBB.getParent()->getSubtarget().getRegisterInfo(); |
| unsigned Reg32 = TRI->getSubReg(DstReg, AArch64::sub_32); |
| unsigned DstFlags = MI.getOperand(0).getTargetFlags(); |
| MIB2 = BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::LDRWui)) |
| .addDef(Reg32) |
| .addReg(DstReg, RegState::Kill) |
| .addReg(DstReg, DstFlags | RegState::Implicit); |
| } else { |
| unsigned DstReg = MI.getOperand(0).getReg(); |
| MIB2 = BuildMI(MBB, MBBI, DL, TII->get(AArch64::LDRXui)) |
| .add(MI.getOperand(0)) |
| .addUse(DstReg, RegState::Kill); |
| } |
| |
| if (MO1.isGlobal()) { |
| MIB1.addGlobalAddress(MO1.getGlobal(), 0, Flags | AArch64II::MO_PAGE); |
| MIB2.addGlobalAddress(MO1.getGlobal(), 0, |
| Flags | AArch64II::MO_PAGEOFF | AArch64II::MO_NC); |
| } else if (MO1.isSymbol()) { |
| MIB1.addExternalSymbol(MO1.getSymbolName(), Flags | AArch64II::MO_PAGE); |
| MIB2.addExternalSymbol(MO1.getSymbolName(), Flags | |
| AArch64II::MO_PAGEOFF | |
| AArch64II::MO_NC); |
| } else { |
| assert(MO1.isCPI() && |
| "Only expect globals, externalsymbols, or constant pools"); |
| MIB1.addConstantPoolIndex(MO1.getIndex(), MO1.getOffset(), |
| Flags | AArch64II::MO_PAGE); |
| MIB2.addConstantPoolIndex(MO1.getIndex(), MO1.getOffset(), |
| Flags | AArch64II::MO_PAGEOFF | |
| AArch64II::MO_NC); |
| } |
| |
| transferImpOps(MI, MIB1, MIB2); |
| } |
| MI.eraseFromParent(); |
| return true; |
| } |
| |
| case AArch64::MOVaddr: |
| case AArch64::MOVaddrJT: |
| case AArch64::MOVaddrCP: |
| case AArch64::MOVaddrBA: |
| case AArch64::MOVaddrTLS: |
| case AArch64::MOVaddrEXT: { |
| // Expand into ADRP + ADD. |
| Register DstReg = MI.getOperand(0).getReg(); |
| MachineInstrBuilder MIB1 = |
| BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::ADRP), DstReg) |
| .add(MI.getOperand(1)); |
| |
| if (MI.getOperand(1).getTargetFlags() & AArch64II::MO_TAGGED) { |
| // MO_TAGGED on the page indicates a tagged address. Set the tag now. |
| // We do so by creating a MOVK that sets bits 48-63 of the register to |
| // (global address + 0x100000000 - PC) >> 48. This assumes that we're in |
| // the small code model so we can assume a binary size of <= 4GB, which |
| // makes the untagged PC relative offset positive. The binary must also be |
| // loaded into address range [0, 2^48). Both of these properties need to |
| // be ensured at runtime when using tagged addresses. |
| auto Tag = MI.getOperand(1); |
| Tag.setTargetFlags(AArch64II::MO_PREL | AArch64II::MO_G3); |
| Tag.setOffset(0x100000000); |
| BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::MOVKXi), DstReg) |
| .addReg(DstReg) |
| .add(Tag) |
| .addImm(48); |
| } |
| |
| MachineInstrBuilder MIB2 = |
| BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::ADDXri)) |
| .add(MI.getOperand(0)) |
| .addReg(DstReg) |
| .add(MI.getOperand(2)) |
| .addImm(0); |
| |
| transferImpOps(MI, MIB1, MIB2); |
| MI.eraseFromParent(); |
| return true; |
| } |
| case AArch64::ADDlowTLS: |
| // Produce a plain ADD |
| BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::ADDXri)) |
| .add(MI.getOperand(0)) |
| .add(MI.getOperand(1)) |
| .add(MI.getOperand(2)) |
| .addImm(0); |
| MI.eraseFromParent(); |
| return true; |
| |
| case AArch64::MOVbaseTLS: { |
| Register DstReg = MI.getOperand(0).getReg(); |
| auto SysReg = AArch64SysReg::TPIDR_EL0; |
| MachineFunction *MF = MBB.getParent(); |
| if (MF->getTarget().getTargetTriple().isOSFuchsia() && |
| MF->getTarget().getCodeModel() == CodeModel::Kernel) |
| SysReg = AArch64SysReg::TPIDR_EL1; |
| else if (MF->getSubtarget<AArch64Subtarget>().useEL3ForTP()) |
| SysReg = AArch64SysReg::TPIDR_EL3; |
| else if (MF->getSubtarget<AArch64Subtarget>().useEL2ForTP()) |
| SysReg = AArch64SysReg::TPIDR_EL2; |
| else if (MF->getSubtarget<AArch64Subtarget>().useEL1ForTP()) |
| SysReg = AArch64SysReg::TPIDR_EL1; |
| BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::MRS), DstReg) |
| .addImm(SysReg); |
| MI.eraseFromParent(); |
| return true; |
| } |
| |
| case AArch64::MOVi32imm: |
| return expandMOVImm(MBB, MBBI, 32); |
| case AArch64::MOVi64imm: |
| return expandMOVImm(MBB, MBBI, 64); |
| case AArch64::RET_ReallyLR: { |
| // Hiding the LR use with RET_ReallyLR may lead to extra kills in the |
| // function and missing live-ins. We are fine in practice because callee |
| // saved register handling ensures the register value is restored before |
| // RET, but we need the undef flag here to appease the MachineVerifier |
| // liveness checks. |
| MachineInstrBuilder MIB = |
| BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::RET)) |
| .addReg(AArch64::LR, RegState::Undef); |
| transferImpOps(MI, MIB, MIB); |
| MI.eraseFromParent(); |
| return true; |
| } |
| case AArch64::CMP_SWAP_8: |
| return expandCMP_SWAP(MBB, MBBI, AArch64::LDAXRB, AArch64::STLXRB, |
| AArch64::SUBSWrx, |
| AArch64_AM::getArithExtendImm(AArch64_AM::UXTB, 0), |
| AArch64::WZR, NextMBBI); |
| case AArch64::CMP_SWAP_16: |
| return expandCMP_SWAP(MBB, MBBI, AArch64::LDAXRH, AArch64::STLXRH, |
| AArch64::SUBSWrx, |
| AArch64_AM::getArithExtendImm(AArch64_AM::UXTH, 0), |
| AArch64::WZR, NextMBBI); |
| case AArch64::CMP_SWAP_32: |
| return expandCMP_SWAP(MBB, MBBI, AArch64::LDAXRW, AArch64::STLXRW, |
| AArch64::SUBSWrs, |
| AArch64_AM::getShifterImm(AArch64_AM::LSL, 0), |
| AArch64::WZR, NextMBBI); |
| case AArch64::CMP_SWAP_64: |
| return expandCMP_SWAP(MBB, MBBI, |
| AArch64::LDAXRX, AArch64::STLXRX, AArch64::SUBSXrs, |
| AArch64_AM::getShifterImm(AArch64_AM::LSL, 0), |
| AArch64::XZR, NextMBBI); |
| case AArch64::CMP_SWAP_128: |
| return expandCMP_SWAP_128(MBB, MBBI, NextMBBI); |
| |
| case AArch64::AESMCrrTied: |
| case AArch64::AESIMCrrTied: { |
| MachineInstrBuilder MIB = |
| BuildMI(MBB, MBBI, MI.getDebugLoc(), |
| TII->get(Opcode == AArch64::AESMCrrTied ? AArch64::AESMCrr : |
| AArch64::AESIMCrr)) |
| .add(MI.getOperand(0)) |
| .add(MI.getOperand(1)); |
| transferImpOps(MI, MIB, MIB); |
| MI.eraseFromParent(); |
| return true; |
| } |
| case AArch64::IRGstack: { |
| MachineFunction &MF = *MBB.getParent(); |
| const AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>(); |
| const AArch64FrameLowering *TFI = |
| MF.getSubtarget<AArch64Subtarget>().getFrameLowering(); |
| |
| // IRG does not allow immediate offset. getTaggedBasePointerOffset should |
| // almost always point to SP-after-prologue; if not, emit a longer |
| // instruction sequence. |
| int BaseOffset = -AFI->getTaggedBasePointerOffset(); |
| unsigned FrameReg; |
| StackOffset FrameRegOffset = TFI->resolveFrameOffsetReference( |
| MF, BaseOffset, false /*isFixed*/, false /*isSVE*/, FrameReg, |
| /*PreferFP=*/false, |
| /*ForSimm=*/true); |
| Register SrcReg = FrameReg; |
| if (FrameRegOffset) { |
| // Use output register as temporary. |
| SrcReg = MI.getOperand(0).getReg(); |
| emitFrameOffset(MBB, &MI, MI.getDebugLoc(), SrcReg, FrameReg, |
| FrameRegOffset, TII); |
| } |
| BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::IRG)) |
| .add(MI.getOperand(0)) |
| .addUse(SrcReg) |
| .add(MI.getOperand(2)); |
| MI.eraseFromParent(); |
| return true; |
| } |
| case AArch64::TAGPstack: { |
| int64_t Offset = MI.getOperand(2).getImm(); |
| BuildMI(MBB, MBBI, MI.getDebugLoc(), |
| TII->get(Offset >= 0 ? AArch64::ADDG : AArch64::SUBG)) |
| .add(MI.getOperand(0)) |
| .add(MI.getOperand(1)) |
| .addImm(std::abs(Offset)) |
| .add(MI.getOperand(4)); |
| MI.eraseFromParent(); |
| return true; |
| } |
| case AArch64::STGloop: |
| case AArch64::STZGloop: |
| return expandSetTagLoop(MBB, MBBI, NextMBBI); |
| } |
| return false; |
| } |
| |
| /// Iterate over the instructions in basic block MBB and expand any |
| /// pseudo instructions. Return true if anything was modified. |
| bool AArch64ExpandPseudo::expandMBB(MachineBasicBlock &MBB) { |
| bool Modified = false; |
| |
| MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end(); |
| while (MBBI != E) { |
| MachineBasicBlock::iterator NMBBI = std::next(MBBI); |
| Modified |= expandMI(MBB, MBBI, NMBBI); |
| MBBI = NMBBI; |
| } |
| |
| return Modified; |
| } |
| |
| bool AArch64ExpandPseudo::runOnMachineFunction(MachineFunction &MF) { |
| TII = static_cast<const AArch64InstrInfo *>(MF.getSubtarget().getInstrInfo()); |
| |
| bool Modified = false; |
| for (auto &MBB : MF) |
| Modified |= expandMBB(MBB); |
| return Modified; |
| } |
| |
| /// Returns an instance of the pseudo instruction expansion pass. |
| FunctionPass *llvm::createAArch64ExpandPseudoPass() { |
| return new AArch64ExpandPseudo(); |
| } |