| //===- AArch64InstrInfo.cpp - AArch64 Instruction Information -------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file contains the AArch64 implementation of the TargetInstrInfo class. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "AArch64.h" |
| #include "AArch64InstrInfo.h" |
| #include "AArch64MachineFunctionInfo.h" |
| #include "AArch64TargetMachine.h" |
| #include "MCTargetDesc/AArch64MCTargetDesc.h" |
| #include "Utils/AArch64BaseInfo.h" |
| #include "llvm/CodeGen/MachineConstantPool.h" |
| #include "llvm/CodeGen/MachineDominators.h" |
| #include "llvm/CodeGen/MachineFrameInfo.h" |
| #include "llvm/CodeGen/MachineFunctionPass.h" |
| #include "llvm/CodeGen/MachineInstrBuilder.h" |
| #include "llvm/CodeGen/MachineRegisterInfo.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/TargetRegistry.h" |
| |
| #include <algorithm> |
| |
| #define GET_INSTRINFO_CTOR |
| #include "AArch64GenInstrInfo.inc" |
| |
| using namespace llvm; |
| |
| AArch64InstrInfo::AArch64InstrInfo(const AArch64Subtarget &STI) |
| : AArch64GenInstrInfo(AArch64::ADJCALLSTACKDOWN, AArch64::ADJCALLSTACKUP), |
| RI(*this, STI), Subtarget(STI) {} |
| |
| void AArch64InstrInfo::copyPhysReg(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator I, DebugLoc DL, |
| unsigned DestReg, unsigned SrcReg, |
| bool KillSrc) const { |
| unsigned Opc = 0; |
| unsigned ZeroReg = 0; |
| if (DestReg == AArch64::XSP || SrcReg == AArch64::XSP) { |
| // E.g. ADD xDst, xsp, #0 (, lsl #0) |
| BuildMI(MBB, I, DL, get(AArch64::ADDxxi_lsl0_s), DestReg) |
| .addReg(SrcReg) |
| .addImm(0); |
| return; |
| } else if (DestReg == AArch64::WSP || SrcReg == AArch64::WSP) { |
| // E.g. ADD wDST, wsp, #0 (, lsl #0) |
| BuildMI(MBB, I, DL, get(AArch64::ADDwwi_lsl0_s), DestReg) |
| .addReg(SrcReg) |
| .addImm(0); |
| return; |
| } else if (DestReg == AArch64::NZCV) { |
| assert(AArch64::GPR64RegClass.contains(SrcReg)); |
| // E.g. MSR NZCV, xDST |
| BuildMI(MBB, I, DL, get(AArch64::MSRix)) |
| .addImm(A64SysReg::NZCV) |
| .addReg(SrcReg); |
| } else if (SrcReg == AArch64::NZCV) { |
| assert(AArch64::GPR64RegClass.contains(DestReg)); |
| // E.g. MRS xDST, NZCV |
| BuildMI(MBB, I, DL, get(AArch64::MRSxi), DestReg) |
| .addImm(A64SysReg::NZCV); |
| } else if (AArch64::GPR64RegClass.contains(DestReg)) { |
| assert(AArch64::GPR64RegClass.contains(SrcReg)); |
| Opc = AArch64::ORRxxx_lsl; |
| ZeroReg = AArch64::XZR; |
| } else if (AArch64::GPR32RegClass.contains(DestReg)) { |
| assert(AArch64::GPR32RegClass.contains(SrcReg)); |
| Opc = AArch64::ORRwww_lsl; |
| ZeroReg = AArch64::WZR; |
| } else if (AArch64::FPR32RegClass.contains(DestReg)) { |
| assert(AArch64::FPR32RegClass.contains(SrcReg)); |
| BuildMI(MBB, I, DL, get(AArch64::FMOVss), DestReg) |
| .addReg(SrcReg); |
| return; |
| } else if (AArch64::FPR64RegClass.contains(DestReg)) { |
| assert(AArch64::FPR64RegClass.contains(SrcReg)); |
| BuildMI(MBB, I, DL, get(AArch64::FMOVdd), DestReg) |
| .addReg(SrcReg); |
| return; |
| } else if (AArch64::FPR128RegClass.contains(DestReg)) { |
| assert(AArch64::FPR128RegClass.contains(SrcReg)); |
| |
| // FIXME: there's no good way to do this, at least without NEON: |
| // + There's no single move instruction for q-registers |
| // + We can't create a spill slot and use normal STR/LDR because stack |
| // allocation has already happened |
| // + We can't go via X-registers with FMOV because register allocation has |
| // already happened. |
| // This may not be efficient, but at least it works. |
| BuildMI(MBB, I, DL, get(AArch64::LSFP128_PreInd_STR), AArch64::XSP) |
| .addReg(SrcReg) |
| .addReg(AArch64::XSP) |
| .addImm(0x1ff & -16); |
| |
| BuildMI(MBB, I, DL, get(AArch64::LSFP128_PostInd_LDR), DestReg) |
| .addReg(AArch64::XSP, RegState::Define) |
| .addReg(AArch64::XSP) |
| .addImm(16); |
| return; |
| } else { |
| llvm_unreachable("Unknown register class in copyPhysReg"); |
| } |
| |
| // E.g. ORR xDst, xzr, xSrc, lsl #0 |
| BuildMI(MBB, I, DL, get(Opc), DestReg) |
| .addReg(ZeroReg) |
| .addReg(SrcReg) |
| .addImm(0); |
| } |
| |
| MachineInstr * |
| AArch64InstrInfo::emitFrameIndexDebugValue(MachineFunction &MF, int FrameIx, |
| uint64_t Offset, const MDNode *MDPtr, |
| DebugLoc DL) const { |
| MachineInstrBuilder MIB = BuildMI(MF, DL, get(AArch64::DBG_VALUE)) |
| .addFrameIndex(FrameIx).addImm(0) |
| .addImm(Offset) |
| .addMetadata(MDPtr); |
| return &*MIB; |
| } |
| |
| /// Does the Opcode represent a conditional branch that we can remove and re-add |
| /// at the end of a basic block? |
| static bool isCondBranch(unsigned Opc) { |
| return Opc == AArch64::Bcc || Opc == AArch64::CBZw || Opc == AArch64::CBZx || |
| Opc == AArch64::CBNZw || Opc == AArch64::CBNZx || |
| Opc == AArch64::TBZwii || Opc == AArch64::TBZxii || |
| Opc == AArch64::TBNZwii || Opc == AArch64::TBNZxii; |
| } |
| |
| /// Takes apart a given conditional branch MachineInstr (see isCondBranch), |
| /// setting TBB to the destination basic block and populating the Cond vector |
| /// with data necessary to recreate the conditional branch at a later |
| /// date. First element will be the opcode, and subsequent ones define the |
| /// conditions being branched on in an instruction-specific manner. |
| static void classifyCondBranch(MachineInstr *I, MachineBasicBlock *&TBB, |
| SmallVectorImpl<MachineOperand> &Cond) { |
| switch(I->getOpcode()) { |
| case AArch64::Bcc: |
| case AArch64::CBZw: |
| case AArch64::CBZx: |
| case AArch64::CBNZw: |
| case AArch64::CBNZx: |
| // These instructions just have one predicate operand in position 0 (either |
| // a condition code or a register being compared). |
| Cond.push_back(MachineOperand::CreateImm(I->getOpcode())); |
| Cond.push_back(I->getOperand(0)); |
| TBB = I->getOperand(1).getMBB(); |
| return; |
| case AArch64::TBZwii: |
| case AArch64::TBZxii: |
| case AArch64::TBNZwii: |
| case AArch64::TBNZxii: |
| // These have two predicate operands: a register and a bit position. |
| Cond.push_back(MachineOperand::CreateImm(I->getOpcode())); |
| Cond.push_back(I->getOperand(0)); |
| Cond.push_back(I->getOperand(1)); |
| TBB = I->getOperand(2).getMBB(); |
| return; |
| default: |
| llvm_unreachable("Unknown conditional branch to classify"); |
| } |
| } |
| |
| |
| bool |
| AArch64InstrInfo::AnalyzeBranch(MachineBasicBlock &MBB,MachineBasicBlock *&TBB, |
| MachineBasicBlock *&FBB, |
| SmallVectorImpl<MachineOperand> &Cond, |
| bool AllowModify) const { |
| // If the block has no terminators, it just falls into the block after it. |
| MachineBasicBlock::iterator I = MBB.end(); |
| if (I == MBB.begin()) |
| return false; |
| --I; |
| while (I->isDebugValue()) { |
| if (I == MBB.begin()) |
| return false; |
| --I; |
| } |
| if (!isUnpredicatedTerminator(I)) |
| return false; |
| |
| // Get the last instruction in the block. |
| MachineInstr *LastInst = I; |
| |
| // If there is only one terminator instruction, process it. |
| unsigned LastOpc = LastInst->getOpcode(); |
| if (I == MBB.begin() || !isUnpredicatedTerminator(--I)) { |
| if (LastOpc == AArch64::Bimm) { |
| TBB = LastInst->getOperand(0).getMBB(); |
| return false; |
| } |
| if (isCondBranch(LastOpc)) { |
| classifyCondBranch(LastInst, TBB, Cond); |
| return false; |
| } |
| return true; // Can't handle indirect branch. |
| } |
| |
| // Get the instruction before it if it is a terminator. |
| MachineInstr *SecondLastInst = I; |
| unsigned SecondLastOpc = SecondLastInst->getOpcode(); |
| |
| // If AllowModify is true and the block ends with two or more unconditional |
| // branches, delete all but the first unconditional branch. |
| if (AllowModify && LastOpc == AArch64::Bimm) { |
| while (SecondLastOpc == AArch64::Bimm) { |
| LastInst->eraseFromParent(); |
| LastInst = SecondLastInst; |
| LastOpc = LastInst->getOpcode(); |
| if (I == MBB.begin() || !isUnpredicatedTerminator(--I)) { |
| // Return now the only terminator is an unconditional branch. |
| TBB = LastInst->getOperand(0).getMBB(); |
| return false; |
| } else { |
| SecondLastInst = I; |
| SecondLastOpc = SecondLastInst->getOpcode(); |
| } |
| } |
| } |
| |
| // If there are three terminators, we don't know what sort of block this is. |
| if (SecondLastInst && I != MBB.begin() && isUnpredicatedTerminator(--I)) |
| return true; |
| |
| // If the block ends with a B and a Bcc, handle it. |
| if (LastOpc == AArch64::Bimm) { |
| if (SecondLastOpc == AArch64::Bcc) { |
| TBB = SecondLastInst->getOperand(1).getMBB(); |
| Cond.push_back(MachineOperand::CreateImm(AArch64::Bcc)); |
| Cond.push_back(SecondLastInst->getOperand(0)); |
| FBB = LastInst->getOperand(0).getMBB(); |
| return false; |
| } else if (isCondBranch(SecondLastOpc)) { |
| classifyCondBranch(SecondLastInst, TBB, Cond); |
| FBB = LastInst->getOperand(0).getMBB(); |
| return false; |
| } |
| } |
| |
| // If the block ends with two unconditional branches, handle it. The second |
| // one is not executed, so remove it. |
| if (SecondLastOpc == AArch64::Bimm && LastOpc == AArch64::Bimm) { |
| TBB = SecondLastInst->getOperand(0).getMBB(); |
| I = LastInst; |
| if (AllowModify) |
| I->eraseFromParent(); |
| return false; |
| } |
| |
| // Otherwise, can't handle this. |
| return true; |
| } |
| |
| bool AArch64InstrInfo::ReverseBranchCondition( |
| SmallVectorImpl<MachineOperand> &Cond) const { |
| switch (Cond[0].getImm()) { |
| case AArch64::Bcc: { |
| A64CC::CondCodes CC = static_cast<A64CC::CondCodes>(Cond[1].getImm()); |
| CC = A64InvertCondCode(CC); |
| Cond[1].setImm(CC); |
| return false; |
| } |
| case AArch64::CBZw: |
| Cond[0].setImm(AArch64::CBNZw); |
| return false; |
| case AArch64::CBZx: |
| Cond[0].setImm(AArch64::CBNZx); |
| return false; |
| case AArch64::CBNZw: |
| Cond[0].setImm(AArch64::CBZw); |
| return false; |
| case AArch64::CBNZx: |
| Cond[0].setImm(AArch64::CBZx); |
| return false; |
| case AArch64::TBZwii: |
| Cond[0].setImm(AArch64::TBNZwii); |
| return false; |
| case AArch64::TBZxii: |
| Cond[0].setImm(AArch64::TBNZxii); |
| return false; |
| case AArch64::TBNZwii: |
| Cond[0].setImm(AArch64::TBZwii); |
| return false; |
| case AArch64::TBNZxii: |
| Cond[0].setImm(AArch64::TBZxii); |
| return false; |
| default: |
| llvm_unreachable("Unknown branch type"); |
| } |
| } |
| |
| |
| unsigned |
| AArch64InstrInfo::InsertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, |
| MachineBasicBlock *FBB, |
| const SmallVectorImpl<MachineOperand> &Cond, |
| DebugLoc DL) const { |
| if (FBB == 0 && Cond.empty()) { |
| BuildMI(&MBB, DL, get(AArch64::Bimm)).addMBB(TBB); |
| return 1; |
| } else if (FBB == 0) { |
| MachineInstrBuilder MIB = BuildMI(&MBB, DL, get(Cond[0].getImm())); |
| for (int i = 1, e = Cond.size(); i != e; ++i) |
| MIB.addOperand(Cond[i]); |
| MIB.addMBB(TBB); |
| return 1; |
| } |
| |
| MachineInstrBuilder MIB = BuildMI(&MBB, DL, get(Cond[0].getImm())); |
| for (int i = 1, e = Cond.size(); i != e; ++i) |
| MIB.addOperand(Cond[i]); |
| MIB.addMBB(TBB); |
| |
| BuildMI(&MBB, DL, get(AArch64::Bimm)).addMBB(FBB); |
| return 2; |
| } |
| |
| unsigned AArch64InstrInfo::RemoveBranch(MachineBasicBlock &MBB) const { |
| MachineBasicBlock::iterator I = MBB.end(); |
| if (I == MBB.begin()) return 0; |
| --I; |
| while (I->isDebugValue()) { |
| if (I == MBB.begin()) |
| return 0; |
| --I; |
| } |
| if (I->getOpcode() != AArch64::Bimm && !isCondBranch(I->getOpcode())) |
| return 0; |
| |
| // Remove the branch. |
| I->eraseFromParent(); |
| |
| I = MBB.end(); |
| |
| if (I == MBB.begin()) return 1; |
| --I; |
| if (!isCondBranch(I->getOpcode())) |
| return 1; |
| |
| // Remove the branch. |
| I->eraseFromParent(); |
| return 2; |
| } |
| |
| bool |
| AArch64InstrInfo::expandPostRAPseudo(MachineBasicBlock::iterator MBBI) const { |
| MachineInstr &MI = *MBBI; |
| MachineBasicBlock &MBB = *MI.getParent(); |
| |
| unsigned Opcode = MI.getOpcode(); |
| switch (Opcode) { |
| case AArch64::TLSDESC_BLRx: { |
| MachineInstr *NewMI = |
| BuildMI(MBB, MBBI, MI.getDebugLoc(), get(AArch64::TLSDESCCALL)) |
| .addOperand(MI.getOperand(1)); |
| MI.setDesc(get(AArch64::BLRx)); |
| |
| llvm::finalizeBundle(MBB, NewMI, *++MBBI); |
| return true; |
| } |
| default: |
| return false; |
| } |
| |
| return false; |
| } |
| |
| void |
| AArch64InstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator MBBI, |
| unsigned SrcReg, bool isKill, |
| int FrameIdx, |
| const TargetRegisterClass *RC, |
| const TargetRegisterInfo *TRI) const { |
| DebugLoc DL = MBB.findDebugLoc(MBBI); |
| MachineFunction &MF = *MBB.getParent(); |
| MachineFrameInfo &MFI = *MF.getFrameInfo(); |
| unsigned Align = MFI.getObjectAlignment(FrameIdx); |
| |
| MachineMemOperand *MMO |
| = MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(FrameIdx), |
| MachineMemOperand::MOStore, |
| MFI.getObjectSize(FrameIdx), |
| Align); |
| |
| unsigned StoreOp = 0; |
| if (RC->hasType(MVT::i64) || RC->hasType(MVT::i32)) { |
| switch(RC->getSize()) { |
| case 4: StoreOp = AArch64::LS32_STR; break; |
| case 8: StoreOp = AArch64::LS64_STR; break; |
| default: |
| llvm_unreachable("Unknown size for regclass"); |
| } |
| } else { |
| assert((RC->hasType(MVT::f32) || RC->hasType(MVT::f64) || |
| RC->hasType(MVT::f128)) |
| && "Expected integer or floating type for store"); |
| switch (RC->getSize()) { |
| case 4: StoreOp = AArch64::LSFP32_STR; break; |
| case 8: StoreOp = AArch64::LSFP64_STR; break; |
| case 16: StoreOp = AArch64::LSFP128_STR; break; |
| default: |
| llvm_unreachable("Unknown size for regclass"); |
| } |
| } |
| |
| MachineInstrBuilder NewMI = BuildMI(MBB, MBBI, DL, get(StoreOp)); |
| NewMI.addReg(SrcReg, getKillRegState(isKill)) |
| .addFrameIndex(FrameIdx) |
| .addImm(0) |
| .addMemOperand(MMO); |
| |
| } |
| |
| void |
| AArch64InstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator MBBI, |
| unsigned DestReg, int FrameIdx, |
| const TargetRegisterClass *RC, |
| const TargetRegisterInfo *TRI) const { |
| DebugLoc DL = MBB.findDebugLoc(MBBI); |
| MachineFunction &MF = *MBB.getParent(); |
| MachineFrameInfo &MFI = *MF.getFrameInfo(); |
| unsigned Align = MFI.getObjectAlignment(FrameIdx); |
| |
| MachineMemOperand *MMO |
| = MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(FrameIdx), |
| MachineMemOperand::MOLoad, |
| MFI.getObjectSize(FrameIdx), |
| Align); |
| |
| unsigned LoadOp = 0; |
| if (RC->hasType(MVT::i64) || RC->hasType(MVT::i32)) { |
| switch(RC->getSize()) { |
| case 4: LoadOp = AArch64::LS32_LDR; break; |
| case 8: LoadOp = AArch64::LS64_LDR; break; |
| default: |
| llvm_unreachable("Unknown size for regclass"); |
| } |
| } else { |
| assert((RC->hasType(MVT::f32) || RC->hasType(MVT::f64) |
| || RC->hasType(MVT::f128)) |
| && "Expected integer or floating type for store"); |
| switch (RC->getSize()) { |
| case 4: LoadOp = AArch64::LSFP32_LDR; break; |
| case 8: LoadOp = AArch64::LSFP64_LDR; break; |
| case 16: LoadOp = AArch64::LSFP128_LDR; break; |
| default: |
| llvm_unreachable("Unknown size for regclass"); |
| } |
| } |
| |
| MachineInstrBuilder NewMI = BuildMI(MBB, MBBI, DL, get(LoadOp), DestReg); |
| NewMI.addFrameIndex(FrameIdx) |
| .addImm(0) |
| .addMemOperand(MMO); |
| } |
| |
| unsigned AArch64InstrInfo::estimateRSStackLimit(MachineFunction &MF) const { |
| unsigned Limit = (1 << 16) - 1; |
| for (MachineFunction::iterator BB = MF.begin(),E = MF.end(); BB != E; ++BB) { |
| for (MachineBasicBlock::iterator I = BB->begin(), E = BB->end(); |
| I != E; ++I) { |
| for (unsigned i = 0, e = I->getNumOperands(); i != e; ++i) { |
| if (!I->getOperand(i).isFI()) continue; |
| |
| // When using ADDxxi_lsl0_s to get the address of a stack object, 0xfff |
| // is the largest offset guaranteed to fit in the immediate offset. |
| if (I->getOpcode() == AArch64::ADDxxi_lsl0_s) { |
| Limit = std::min(Limit, 0xfffu); |
| break; |
| } |
| |
| int AccessScale, MinOffset, MaxOffset; |
| getAddressConstraints(*I, AccessScale, MinOffset, MaxOffset); |
| Limit = std::min(Limit, static_cast<unsigned>(MaxOffset)); |
| |
| break; // At most one FI per instruction |
| } |
| } |
| } |
| |
| return Limit; |
| } |
| void AArch64InstrInfo::getAddressConstraints(const MachineInstr &MI, |
| int &AccessScale, int &MinOffset, |
| int &MaxOffset) const { |
| switch (MI.getOpcode()) { |
| default: llvm_unreachable("Unkown load/store kind"); |
| case TargetOpcode::DBG_VALUE: |
| AccessScale = 1; |
| MinOffset = INT_MIN; |
| MaxOffset = INT_MAX; |
| return; |
| case AArch64::LS8_LDR: case AArch64::LS8_STR: |
| case AArch64::LSFP8_LDR: case AArch64::LSFP8_STR: |
| case AArch64::LDRSBw: |
| case AArch64::LDRSBx: |
| AccessScale = 1; |
| MinOffset = 0; |
| MaxOffset = 0xfff; |
| return; |
| case AArch64::LS16_LDR: case AArch64::LS16_STR: |
| case AArch64::LSFP16_LDR: case AArch64::LSFP16_STR: |
| case AArch64::LDRSHw: |
| case AArch64::LDRSHx: |
| AccessScale = 2; |
| MinOffset = 0; |
| MaxOffset = 0xfff * AccessScale; |
| return; |
| case AArch64::LS32_LDR: case AArch64::LS32_STR: |
| case AArch64::LSFP32_LDR: case AArch64::LSFP32_STR: |
| case AArch64::LDRSWx: |
| case AArch64::LDPSWx: |
| AccessScale = 4; |
| MinOffset = 0; |
| MaxOffset = 0xfff * AccessScale; |
| return; |
| case AArch64::LS64_LDR: case AArch64::LS64_STR: |
| case AArch64::LSFP64_LDR: case AArch64::LSFP64_STR: |
| case AArch64::PRFM: |
| AccessScale = 8; |
| MinOffset = 0; |
| MaxOffset = 0xfff * AccessScale; |
| return; |
| case AArch64::LSFP128_LDR: case AArch64::LSFP128_STR: |
| AccessScale = 16; |
| MinOffset = 0; |
| MaxOffset = 0xfff * AccessScale; |
| return; |
| case AArch64::LSPair32_LDR: case AArch64::LSPair32_STR: |
| case AArch64::LSFPPair32_LDR: case AArch64::LSFPPair32_STR: |
| AccessScale = 4; |
| MinOffset = -0x40 * AccessScale; |
| MaxOffset = 0x3f * AccessScale; |
| return; |
| case AArch64::LSPair64_LDR: case AArch64::LSPair64_STR: |
| case AArch64::LSFPPair64_LDR: case AArch64::LSFPPair64_STR: |
| AccessScale = 8; |
| MinOffset = -0x40 * AccessScale; |
| MaxOffset = 0x3f * AccessScale; |
| return; |
| case AArch64::LSFPPair128_LDR: case AArch64::LSFPPair128_STR: |
| AccessScale = 16; |
| MinOffset = -0x40 * AccessScale; |
| MaxOffset = 0x3f * AccessScale; |
| return; |
| } |
| } |
| |
| unsigned AArch64InstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { |
| const MCInstrDesc &MCID = MI.getDesc(); |
| const MachineBasicBlock &MBB = *MI.getParent(); |
| const MachineFunction &MF = *MBB.getParent(); |
| const MCAsmInfo &MAI = *MF.getTarget().getMCAsmInfo(); |
| |
| if (MCID.getSize()) |
| return MCID.getSize(); |
| |
| if (MI.getOpcode() == AArch64::INLINEASM) |
| return getInlineAsmLength(MI.getOperand(0).getSymbolName(), MAI); |
| |
| if (MI.isLabel()) |
| return 0; |
| |
| switch (MI.getOpcode()) { |
| case TargetOpcode::BUNDLE: |
| return getInstBundleLength(MI); |
| case TargetOpcode::IMPLICIT_DEF: |
| case TargetOpcode::KILL: |
| case TargetOpcode::PROLOG_LABEL: |
| case TargetOpcode::EH_LABEL: |
| case TargetOpcode::DBG_VALUE: |
| return 0; |
| case AArch64::TLSDESCCALL: |
| return 0; |
| default: |
| llvm_unreachable("Unknown instruction class"); |
| } |
| } |
| |
| unsigned AArch64InstrInfo::getInstBundleLength(const MachineInstr &MI) const { |
| unsigned Size = 0; |
| MachineBasicBlock::const_instr_iterator I = MI; |
| MachineBasicBlock::const_instr_iterator E = MI.getParent()->instr_end(); |
| while (++I != E && I->isInsideBundle()) { |
| assert(!I->isBundle() && "No nested bundle!"); |
| Size += getInstSizeInBytes(*I); |
| } |
| return Size; |
| } |
| |
| bool llvm::rewriteA64FrameIndex(MachineInstr &MI, unsigned FrameRegIdx, |
| unsigned FrameReg, int &Offset, |
| const AArch64InstrInfo &TII) { |
| MachineBasicBlock &MBB = *MI.getParent(); |
| MachineFunction &MF = *MBB.getParent(); |
| MachineFrameInfo &MFI = *MF.getFrameInfo(); |
| |
| MFI.getObjectOffset(FrameRegIdx); |
| llvm_unreachable("Unimplemented rewriteFrameIndex"); |
| } |
| |
| void llvm::emitRegUpdate(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator MBBI, |
| DebugLoc dl, const TargetInstrInfo &TII, |
| unsigned DstReg, unsigned SrcReg, unsigned ScratchReg, |
| int64_t NumBytes, MachineInstr::MIFlag MIFlags) { |
| if (NumBytes == 0 && DstReg == SrcReg) |
| return; |
| else if (abs(NumBytes) & ~0xffffff) { |
| // Generically, we have to materialize the offset into a temporary register |
| // and subtract it. There are a couple of ways this could be done, for now |
| // we'll use a movz/movk or movn/movk sequence. |
| uint64_t Bits = static_cast<uint64_t>(abs(NumBytes)); |
| BuildMI(MBB, MBBI, dl, TII.get(AArch64::MOVZxii), ScratchReg) |
| .addImm(0xffff & Bits).addImm(0) |
| .setMIFlags(MIFlags); |
| |
| Bits >>= 16; |
| if (Bits & 0xffff) { |
| BuildMI(MBB, MBBI, dl, TII.get(AArch64::MOVKxii), ScratchReg) |
| .addReg(ScratchReg) |
| .addImm(0xffff & Bits).addImm(1) |
| .setMIFlags(MIFlags); |
| } |
| |
| Bits >>= 16; |
| if (Bits & 0xffff) { |
| BuildMI(MBB, MBBI, dl, TII.get(AArch64::MOVKxii), ScratchReg) |
| .addReg(ScratchReg) |
| .addImm(0xffff & Bits).addImm(2) |
| .setMIFlags(MIFlags); |
| } |
| |
| Bits >>= 16; |
| if (Bits & 0xffff) { |
| BuildMI(MBB, MBBI, dl, TII.get(AArch64::MOVKxii), ScratchReg) |
| .addReg(ScratchReg) |
| .addImm(0xffff & Bits).addImm(3) |
| .setMIFlags(MIFlags); |
| } |
| |
| // ADD DST, SRC, xTMP (, lsl #0) |
| unsigned AddOp = NumBytes > 0 ? AArch64::ADDxxx_uxtx : AArch64::SUBxxx_uxtx; |
| BuildMI(MBB, MBBI, dl, TII.get(AddOp), DstReg) |
| .addReg(SrcReg, RegState::Kill) |
| .addReg(ScratchReg, RegState::Kill) |
| .addImm(0) |
| .setMIFlag(MIFlags); |
| return; |
| } |
| |
| // Now we know that the adjustment can be done in at most two add/sub |
| // (immediate) instructions, which is always more efficient than a |
| // literal-pool load, or even a hypothetical movz/movk/add sequence |
| |
| // Decide whether we're doing addition or subtraction |
| unsigned LowOp, HighOp; |
| if (NumBytes >= 0) { |
| LowOp = AArch64::ADDxxi_lsl0_s; |
| HighOp = AArch64::ADDxxi_lsl12_s; |
| } else { |
| LowOp = AArch64::SUBxxi_lsl0_s; |
| HighOp = AArch64::SUBxxi_lsl12_s; |
| NumBytes = abs(NumBytes); |
| } |
| |
| // If we're here, at the very least a move needs to be produced, which just |
| // happens to be materializable by an ADD. |
| if ((NumBytes & 0xfff) || NumBytes == 0) { |
| BuildMI(MBB, MBBI, dl, TII.get(LowOp), DstReg) |
| .addReg(SrcReg, RegState::Kill) |
| .addImm(NumBytes & 0xfff) |
| .setMIFlag(MIFlags); |
| |
| // Next update should use the register we've just defined. |
| SrcReg = DstReg; |
| } |
| |
| if (NumBytes & 0xfff000) { |
| BuildMI(MBB, MBBI, dl, TII.get(HighOp), DstReg) |
| .addReg(SrcReg, RegState::Kill) |
| .addImm(NumBytes >> 12) |
| .setMIFlag(MIFlags); |
| } |
| } |
| |
| void llvm::emitSPUpdate(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, |
| DebugLoc dl, const TargetInstrInfo &TII, |
| unsigned ScratchReg, int64_t NumBytes, |
| MachineInstr::MIFlag MIFlags) { |
| emitRegUpdate(MBB, MI, dl, TII, AArch64::XSP, AArch64::XSP, AArch64::X16, |
| NumBytes, MIFlags); |
| } |
| |
| |
| namespace { |
| struct LDTLSCleanup : public MachineFunctionPass { |
| static char ID; |
| LDTLSCleanup() : MachineFunctionPass(ID) {} |
| |
| virtual bool runOnMachineFunction(MachineFunction &MF) { |
| AArch64MachineFunctionInfo* MFI |
| = MF.getInfo<AArch64MachineFunctionInfo>(); |
| if (MFI->getNumLocalDynamicTLSAccesses() < 2) { |
| // No point folding accesses if there isn't at least two. |
| return false; |
| } |
| |
| MachineDominatorTree *DT = &getAnalysis<MachineDominatorTree>(); |
| return VisitNode(DT->getRootNode(), 0); |
| } |
| |
| // Visit the dominator subtree rooted at Node in pre-order. |
| // If TLSBaseAddrReg is non-null, then use that to replace any |
| // TLS_base_addr instructions. Otherwise, create the register |
| // when the first such instruction is seen, and then use it |
| // as we encounter more instructions. |
| bool VisitNode(MachineDomTreeNode *Node, unsigned TLSBaseAddrReg) { |
| MachineBasicBlock *BB = Node->getBlock(); |
| bool Changed = false; |
| |
| // Traverse the current block. |
| for (MachineBasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; |
| ++I) { |
| switch (I->getOpcode()) { |
| case AArch64::TLSDESC_BLRx: |
| // Make sure it's a local dynamic access. |
| if (!I->getOperand(1).isSymbol() || |
| strcmp(I->getOperand(1).getSymbolName(), "_TLS_MODULE_BASE_")) |
| break; |
| |
| if (TLSBaseAddrReg) |
| I = ReplaceTLSBaseAddrCall(I, TLSBaseAddrReg); |
| else |
| I = SetRegister(I, &TLSBaseAddrReg); |
| Changed = true; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| // Visit the children of this block in the dominator tree. |
| for (MachineDomTreeNode::iterator I = Node->begin(), E = Node->end(); |
| I != E; ++I) { |
| Changed |= VisitNode(*I, TLSBaseAddrReg); |
| } |
| |
| return Changed; |
| } |
| |
| // Replace the TLS_base_addr instruction I with a copy from |
| // TLSBaseAddrReg, returning the new instruction. |
| MachineInstr *ReplaceTLSBaseAddrCall(MachineInstr *I, |
| unsigned TLSBaseAddrReg) { |
| MachineFunction *MF = I->getParent()->getParent(); |
| const AArch64TargetMachine *TM = |
| static_cast<const AArch64TargetMachine *>(&MF->getTarget()); |
| const AArch64InstrInfo *TII = TM->getInstrInfo(); |
| |
| // Insert a Copy from TLSBaseAddrReg to x0, which is where the rest of the |
| // code sequence assumes the address will be. |
| MachineInstr *Copy = BuildMI(*I->getParent(), I, I->getDebugLoc(), |
| TII->get(TargetOpcode::COPY), |
| AArch64::X0) |
| .addReg(TLSBaseAddrReg); |
| |
| // Erase the TLS_base_addr instruction. |
| I->eraseFromParent(); |
| |
| return Copy; |
| } |
| |
| // Create a virtal register in *TLSBaseAddrReg, and populate it by |
| // inserting a copy instruction after I. Returns the new instruction. |
| MachineInstr *SetRegister(MachineInstr *I, unsigned *TLSBaseAddrReg) { |
| MachineFunction *MF = I->getParent()->getParent(); |
| const AArch64TargetMachine *TM = |
| static_cast<const AArch64TargetMachine *>(&MF->getTarget()); |
| const AArch64InstrInfo *TII = TM->getInstrInfo(); |
| |
| // Create a virtual register for the TLS base address. |
| MachineRegisterInfo &RegInfo = MF->getRegInfo(); |
| *TLSBaseAddrReg = RegInfo.createVirtualRegister(&AArch64::GPR64RegClass); |
| |
| // Insert a copy from X0 to TLSBaseAddrReg for later. |
| MachineInstr *Next = I->getNextNode(); |
| MachineInstr *Copy = BuildMI(*I->getParent(), Next, I->getDebugLoc(), |
| TII->get(TargetOpcode::COPY), |
| *TLSBaseAddrReg) |
| .addReg(AArch64::X0); |
| |
| return Copy; |
| } |
| |
| virtual const char *getPassName() const { |
| return "Local Dynamic TLS Access Clean-up"; |
| } |
| |
| virtual void getAnalysisUsage(AnalysisUsage &AU) const { |
| AU.setPreservesCFG(); |
| AU.addRequired<MachineDominatorTree>(); |
| MachineFunctionPass::getAnalysisUsage(AU); |
| } |
| }; |
| } |
| |
| char LDTLSCleanup::ID = 0; |
| FunctionPass* |
| llvm::createAArch64CleanupLocalDynamicTLSPass() { return new LDTLSCleanup(); } |