//===- ARCInstrInfo.cpp - ARC Instruction Information -----------*- C++ -*-===// // // 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 the ARC implementation of the TargetInstrInfo class. // //===----------------------------------------------------------------------===// #include "ARCInstrInfo.h" #include "ARC.h" #include "ARCMachineFunctionInfo.h" #include "ARCSubtarget.h" #include "MCTargetDesc/ARCInfo.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineMemOperand.h" #include "llvm/Support/Debug.h" #include "llvm/Support/TargetRegistry.h" using namespace llvm; #define GET_INSTRINFO_CTOR_DTOR #include "ARCGenInstrInfo.inc" #define DEBUG_TYPE "arc-inst-info" enum AddrIncType { NoAddInc = 0, PreInc = 1, PostInc = 2, Scaled = 3 }; enum TSFlagsConstants { TSF_AddrModeOff = 0, TSF_AddModeMask = 3 }; // Pin the vtable to this file. void ARCInstrInfo::anchor() {} ARCInstrInfo::ARCInstrInfo() : ARCGenInstrInfo(ARC::ADJCALLSTACKDOWN, ARC::ADJCALLSTACKUP), RI() {} static bool isZeroImm(const MachineOperand &Op) { return Op.isImm() && Op.getImm() == 0; } static bool isLoad(int Opcode) { return Opcode == ARC::LD_rs9 || Opcode == ARC::LDH_rs9 || Opcode == ARC::LDB_rs9; } static bool isStore(int Opcode) { return Opcode == ARC::ST_rs9 || Opcode == ARC::STH_rs9 || Opcode == ARC::STB_rs9; } /// If the specified machine instruction is a direct /// load from a stack slot, return the virtual or physical register number of /// the destination along with the FrameIndex of the loaded stack slot. If /// not, return 0. This predicate must return 0 if the instruction has /// any side effects other than loading from the stack slot. unsigned ARCInstrInfo::isLoadFromStackSlot(const MachineInstr &MI, int &FrameIndex) const { int Opcode = MI.getOpcode(); if (isLoad(Opcode)) { if ((MI.getOperand(1).isFI()) && // is a stack slot (MI.getOperand(2).isImm()) && // the imm is zero (isZeroImm(MI.getOperand(2)))) { FrameIndex = MI.getOperand(1).getIndex(); return MI.getOperand(0).getReg(); } } return 0; } /// If the specified machine instruction is a direct /// store to a stack slot, return the virtual or physical register number of /// the source reg along with the FrameIndex of the loaded stack slot. If /// not, return 0. This predicate must return 0 if the instruction has /// any side effects other than storing to the stack slot. unsigned ARCInstrInfo::isStoreToStackSlot(const MachineInstr &MI, int &FrameIndex) const { int Opcode = MI.getOpcode(); if (isStore(Opcode)) { if ((MI.getOperand(1).isFI()) && // is a stack slot (MI.getOperand(2).isImm()) && // the imm is zero (isZeroImm(MI.getOperand(2)))) { FrameIndex = MI.getOperand(1).getIndex(); return MI.getOperand(0).getReg(); } } return 0; } /// Return the inverse of passed condition, i.e. turning COND_E to COND_NE. static ARCCC::CondCode GetOppositeBranchCondition(ARCCC::CondCode CC) { switch (CC) { default: llvm_unreachable("Illegal condition code!"); case ARCCC::EQ: return ARCCC::NE; case ARCCC::NE: return ARCCC::EQ; case ARCCC::LO: return ARCCC::HS; case ARCCC::HS: return ARCCC::LO; case ARCCC::GT: return ARCCC::LE; case ARCCC::GE: return ARCCC::LT; case ARCCC::VS: return ARCCC::VC; case ARCCC::VC: return ARCCC::VS; case ARCCC::LT: return ARCCC::GE; case ARCCC::LE: return ARCCC::GT; case ARCCC::HI: return ARCCC::LS; case ARCCC::LS: return ARCCC::HI; case ARCCC::NZ: return ARCCC::Z; case ARCCC::Z: return ARCCC::NZ; } } static bool isUncondBranchOpcode(int Opc) { return Opc == ARC::BR; } static bool isCondBranchOpcode(int Opc) { return Opc == ARC::BRcc_rr_p || Opc == ARC::BRcc_ru6_p; } static bool isJumpOpcode(int Opc) { return Opc == ARC::J; } /// Analyze the branching code at the end of MBB, returning /// true if it cannot be understood (e.g. it's a switch dispatch or isn't /// implemented for a target). Upon success, this returns false and returns /// with the following information in various cases: /// /// 1. If this block ends with no branches (it just falls through to its succ) /// just return false, leaving TBB/FBB null. /// 2. If this block ends with only an unconditional branch, it sets TBB to be /// the destination block. /// 3. If this block ends with a conditional branch and it falls through to a /// successor block, it sets TBB to be the branch destination block and a /// list of operands that evaluate the condition. These operands can be /// passed to other TargetInstrInfo methods to create new branches. /// 4. If this block ends with a conditional branch followed by an /// unconditional branch, it returns the 'true' destination in TBB, the /// 'false' destination in FBB, and a list of operands that evaluate the /// condition. These operands can be passed to other TargetInstrInfo /// methods to create new branches. /// /// Note that RemoveBranch and insertBranch must be implemented to support /// cases where this method returns success. /// /// If AllowModify is true, then this routine is allowed to modify the basic /// block (e.g. delete instructions after the unconditional branch). bool ARCInstrInfo::analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, MachineBasicBlock *&FBB, SmallVectorImpl &Cond, bool AllowModify) const { TBB = FBB = nullptr; MachineBasicBlock::iterator I = MBB.end(); if (I == MBB.begin()) return false; --I; while (isPredicated(*I) || I->isTerminator() || I->isDebugValue()) { // Flag to be raised on unanalyzeable instructions. This is useful in cases // where we want to clean up on the end of the basic block before we bail // out. bool CantAnalyze = false; // Skip over DEBUG values and predicated nonterminators. while (I->isDebugInstr() || !I->isTerminator()) { if (I == MBB.begin()) return false; --I; } if (isJumpOpcode(I->getOpcode())) { // Indirect branches and jump tables can't be analyzed, but we still want // to clean up any instructions at the tail of the basic block. CantAnalyze = true; } else if (isUncondBranchOpcode(I->getOpcode())) { TBB = I->getOperand(0).getMBB(); } else if (isCondBranchOpcode(I->getOpcode())) { // Bail out if we encounter multiple conditional branches. if (!Cond.empty()) return true; assert(!FBB && "FBB should have been null."); FBB = TBB; TBB = I->getOperand(0).getMBB(); Cond.push_back(I->getOperand(1)); Cond.push_back(I->getOperand(2)); Cond.push_back(I->getOperand(3)); } else if (I->isReturn()) { // Returns can't be analyzed, but we should run cleanup. CantAnalyze = !isPredicated(*I); } else { // We encountered other unrecognized terminator. Bail out immediately. return true; } // Cleanup code - to be run for unpredicated unconditional branches and // returns. if (!isPredicated(*I) && (isUncondBranchOpcode(I->getOpcode()) || isJumpOpcode(I->getOpcode()) || I->isReturn())) { // Forget any previous condition branch information - it no longer // applies. Cond.clear(); FBB = nullptr; // If we can modify the function, delete everything below this // unconditional branch. if (AllowModify) { MachineBasicBlock::iterator DI = std::next(I); while (DI != MBB.end()) { MachineInstr &InstToDelete = *DI; ++DI; InstToDelete.eraseFromParent(); } } } if (CantAnalyze) return true; if (I == MBB.begin()) return false; --I; } // We made it past the terminators without bailing out - we must have // analyzed this branch successfully. return false; } unsigned ARCInstrInfo::removeBranch(MachineBasicBlock &MBB, int *BytesRemoved) const { assert(!BytesRemoved && "Code size not handled"); MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr(); if (I == MBB.end()) return 0; if (!isUncondBranchOpcode(I->getOpcode()) && !isCondBranchOpcode(I->getOpcode())) return 0; // Remove the branch. I->eraseFromParent(); I = MBB.end(); if (I == MBB.begin()) return 1; --I; if (!isCondBranchOpcode(I->getOpcode())) return 1; // Remove the branch. I->eraseFromParent(); return 2; } void ARCInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &dl, MCRegister DestReg, MCRegister SrcReg, bool KillSrc) const { assert(ARC::GPR32RegClass.contains(SrcReg) && "Only GPR32 src copy supported."); assert(ARC::GPR32RegClass.contains(DestReg) && "Only GPR32 dest copy supported."); BuildMI(MBB, I, dl, get(ARC::MOV_rr), DestReg) .addReg(SrcReg, getKillRegState(KillSrc)); } void ARCInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, Register SrcReg, bool isKill, int FrameIndex, const TargetRegisterClass *RC, const TargetRegisterInfo *TRI) const { DebugLoc dl = MBB.findDebugLoc(I); MachineFunction &MF = *MBB.getParent(); MachineFrameInfo &MFI = MF.getFrameInfo(); MachineMemOperand *MMO = MF.getMachineMemOperand( MachinePointerInfo::getFixedStack(MF, FrameIndex), MachineMemOperand::MOStore, MFI.getObjectSize(FrameIndex), MFI.getObjectAlign(FrameIndex)); assert(MMO && "Couldn't get MachineMemOperand for store to stack."); assert(TRI->getSpillSize(*RC) == 4 && "Only support 4-byte stores to stack now."); assert(ARC::GPR32RegClass.hasSubClassEq(RC) && "Only support GPR32 stores to stack now."); LLVM_DEBUG(dbgs() << "Created store reg=" << printReg(SrcReg, TRI) << " to FrameIndex=" << FrameIndex << "\n"); BuildMI(MBB, I, dl, get(ARC::ST_rs9)) .addReg(SrcReg, getKillRegState(isKill)) .addFrameIndex(FrameIndex) .addImm(0) .addMemOperand(MMO); } void ARCInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, Register DestReg, int FrameIndex, const TargetRegisterClass *RC, const TargetRegisterInfo *TRI) const { DebugLoc dl = MBB.findDebugLoc(I); MachineFunction &MF = *MBB.getParent(); MachineFrameInfo &MFI = MF.getFrameInfo(); MachineMemOperand *MMO = MF.getMachineMemOperand( MachinePointerInfo::getFixedStack(MF, FrameIndex), MachineMemOperand::MOLoad, MFI.getObjectSize(FrameIndex), MFI.getObjectAlign(FrameIndex)); assert(MMO && "Couldn't get MachineMemOperand for store to stack."); assert(TRI->getSpillSize(*RC) == 4 && "Only support 4-byte loads from stack now."); assert(ARC::GPR32RegClass.hasSubClassEq(RC) && "Only support GPR32 stores to stack now."); LLVM_DEBUG(dbgs() << "Created load reg=" << printReg(DestReg, TRI) << " from FrameIndex=" << FrameIndex << "\n"); BuildMI(MBB, I, dl, get(ARC::LD_rs9)) .addReg(DestReg, RegState::Define) .addFrameIndex(FrameIndex) .addImm(0) .addMemOperand(MMO); } /// Return the inverse opcode of the specified Branch instruction. bool ARCInstrInfo::reverseBranchCondition( SmallVectorImpl &Cond) const { assert((Cond.size() == 3) && "Invalid ARC branch condition!"); Cond[2].setImm(GetOppositeBranchCondition((ARCCC::CondCode)Cond[2].getImm())); return false; } MachineBasicBlock::iterator ARCInstrInfo::loadImmediate(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, unsigned Reg, uint64_t Value) const { DebugLoc dl = MBB.findDebugLoc(MI); if (isInt<12>(Value)) { return BuildMI(MBB, MI, dl, get(ARC::MOV_rs12), Reg) .addImm(Value) .getInstr(); } llvm_unreachable("Need Arc long immediate instructions."); } unsigned ARCInstrInfo::insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, ArrayRef Cond, const DebugLoc &dl, int *BytesAdded) const { assert(!BytesAdded && "Code size not handled."); // Shouldn't be a fall through. assert(TBB && "insertBranch must not be told to insert a fallthrough"); assert((Cond.size() == 3 || Cond.size() == 0) && "ARC branch conditions have two components!"); if (Cond.empty()) { BuildMI(&MBB, dl, get(ARC::BR)).addMBB(TBB); return 1; } int BccOpc = Cond[1].isImm() ? ARC::BRcc_ru6_p : ARC::BRcc_rr_p; MachineInstrBuilder MIB = BuildMI(&MBB, dl, get(BccOpc)); MIB.addMBB(TBB); for (unsigned i = 0; i < 3; i++) { MIB.add(Cond[i]); } // One-way conditional branch. if (!FBB) { return 1; } // Two-way conditional branch. BuildMI(&MBB, dl, get(ARC::BR)).addMBB(FBB); return 2; } unsigned ARCInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { if (MI.isInlineAsm()) { const MachineFunction *MF = MI.getParent()->getParent(); const char *AsmStr = MI.getOperand(0).getSymbolName(); return getInlineAsmLength(AsmStr, *MF->getTarget().getMCAsmInfo()); } return MI.getDesc().getSize(); } bool ARCInstrInfo::isPostIncrement(const MachineInstr &MI) const { const MCInstrDesc &MID = MI.getDesc(); const uint64_t F = MID.TSFlags; return ((F >> TSF_AddrModeOff) & TSF_AddModeMask) == PostInc; } bool ARCInstrInfo::isPreIncrement(const MachineInstr &MI) const { const MCInstrDesc &MID = MI.getDesc(); const uint64_t F = MID.TSFlags; return ((F >> TSF_AddrModeOff) & TSF_AddModeMask) == PreInc; } bool ARCInstrInfo::getBaseAndOffsetPosition(const MachineInstr &MI, unsigned &BasePos, unsigned &OffsetPos) const { if (!MI.mayLoad() && !MI.mayStore()) return false; BasePos = 1; OffsetPos = 2; if (isPostIncrement(MI) || isPreIncrement(MI)) { BasePos++; OffsetPos++; } if (!MI.getOperand(BasePos).isReg() || !MI.getOperand(OffsetPos).isImm()) return false; return true; }