| //===-- RISCVInstrInfo.cpp - RISCV 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 RISCV implementation of the TargetInstrInfo class. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "RISCVInstrInfo.h" |
| #include "RISCV.h" |
| #include "RISCVSubtarget.h" |
| #include "RISCVTargetMachine.h" |
| #include "Utils/RISCVMatInt.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/CodeGen/MachineFunctionPass.h" |
| #include "llvm/CodeGen/MachineInstrBuilder.h" |
| #include "llvm/CodeGen/MachineRegisterInfo.h" |
| #include "llvm/CodeGen/RegisterScavenging.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/TargetRegistry.h" |
| |
| using namespace llvm; |
| |
| #define GEN_CHECK_COMPRESS_INSTR |
| #include "RISCVGenCompressInstEmitter.inc" |
| |
| #define GET_INSTRINFO_CTOR_DTOR |
| #include "RISCVGenInstrInfo.inc" |
| |
| RISCVInstrInfo::RISCVInstrInfo(RISCVSubtarget &STI) |
| : RISCVGenInstrInfo(RISCV::ADJCALLSTACKDOWN, RISCV::ADJCALLSTACKUP), |
| STI(STI) {} |
| |
| unsigned RISCVInstrInfo::isLoadFromStackSlot(const MachineInstr &MI, |
| int &FrameIndex) const { |
| switch (MI.getOpcode()) { |
| default: |
| return 0; |
| case RISCV::LB: |
| case RISCV::LBU: |
| case RISCV::LH: |
| case RISCV::LHU: |
| case RISCV::LW: |
| case RISCV::FLW: |
| case RISCV::LWU: |
| case RISCV::LD: |
| case RISCV::FLD: |
| break; |
| } |
| |
| if (MI.getOperand(1).isFI() && MI.getOperand(2).isImm() && |
| MI.getOperand(2).getImm() == 0) { |
| FrameIndex = MI.getOperand(1).getIndex(); |
| return MI.getOperand(0).getReg(); |
| } |
| |
| return 0; |
| } |
| |
| unsigned RISCVInstrInfo::isStoreToStackSlot(const MachineInstr &MI, |
| int &FrameIndex) const { |
| switch (MI.getOpcode()) { |
| default: |
| return 0; |
| case RISCV::SB: |
| case RISCV::SH: |
| case RISCV::SW: |
| case RISCV::FSW: |
| case RISCV::SD: |
| case RISCV::FSD: |
| break; |
| } |
| |
| if (MI.getOperand(0).isFI() && MI.getOperand(1).isImm() && |
| MI.getOperand(1).getImm() == 0) { |
| FrameIndex = MI.getOperand(0).getIndex(); |
| return MI.getOperand(2).getReg(); |
| } |
| |
| return 0; |
| } |
| |
| void RISCVInstrInfo::copyPhysReg(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator MBBI, |
| const DebugLoc &DL, MCRegister DstReg, |
| MCRegister SrcReg, bool KillSrc) const { |
| if (RISCV::GPRRegClass.contains(DstReg, SrcReg)) { |
| BuildMI(MBB, MBBI, DL, get(RISCV::ADDI), DstReg) |
| .addReg(SrcReg, getKillRegState(KillSrc)) |
| .addImm(0); |
| return; |
| } |
| |
| // FPR->FPR copies |
| unsigned Opc; |
| if (RISCV::FPR32RegClass.contains(DstReg, SrcReg)) |
| Opc = RISCV::FSGNJ_S; |
| else if (RISCV::FPR64RegClass.contains(DstReg, SrcReg)) |
| Opc = RISCV::FSGNJ_D; |
| else |
| llvm_unreachable("Impossible reg-to-reg copy"); |
| |
| BuildMI(MBB, MBBI, DL, get(Opc), DstReg) |
| .addReg(SrcReg, getKillRegState(KillSrc)) |
| .addReg(SrcReg, getKillRegState(KillSrc)); |
| } |
| |
| void RISCVInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator I, |
| unsigned SrcReg, bool IsKill, int FI, |
| const TargetRegisterClass *RC, |
| const TargetRegisterInfo *TRI) const { |
| DebugLoc DL; |
| if (I != MBB.end()) |
| DL = I->getDebugLoc(); |
| |
| unsigned Opcode; |
| |
| if (RISCV::GPRRegClass.hasSubClassEq(RC)) |
| Opcode = TRI->getRegSizeInBits(RISCV::GPRRegClass) == 32 ? |
| RISCV::SW : RISCV::SD; |
| else if (RISCV::FPR32RegClass.hasSubClassEq(RC)) |
| Opcode = RISCV::FSW; |
| else if (RISCV::FPR64RegClass.hasSubClassEq(RC)) |
| Opcode = RISCV::FSD; |
| else |
| llvm_unreachable("Can't store this register to stack slot"); |
| |
| BuildMI(MBB, I, DL, get(Opcode)) |
| .addReg(SrcReg, getKillRegState(IsKill)) |
| .addFrameIndex(FI) |
| .addImm(0); |
| } |
| |
| void RISCVInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator I, |
| unsigned DstReg, int FI, |
| const TargetRegisterClass *RC, |
| const TargetRegisterInfo *TRI) const { |
| DebugLoc DL; |
| if (I != MBB.end()) |
| DL = I->getDebugLoc(); |
| |
| unsigned Opcode; |
| |
| if (RISCV::GPRRegClass.hasSubClassEq(RC)) |
| Opcode = TRI->getRegSizeInBits(RISCV::GPRRegClass) == 32 ? |
| RISCV::LW : RISCV::LD; |
| else if (RISCV::FPR32RegClass.hasSubClassEq(RC)) |
| Opcode = RISCV::FLW; |
| else if (RISCV::FPR64RegClass.hasSubClassEq(RC)) |
| Opcode = RISCV::FLD; |
| else |
| llvm_unreachable("Can't load this register from stack slot"); |
| |
| BuildMI(MBB, I, DL, get(Opcode), DstReg).addFrameIndex(FI).addImm(0); |
| } |
| |
| void RISCVInstrInfo::movImm(MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator MBBI, |
| const DebugLoc &DL, Register DstReg, uint64_t Val, |
| MachineInstr::MIFlag Flag) const { |
| MachineFunction *MF = MBB.getParent(); |
| MachineRegisterInfo &MRI = MF->getRegInfo(); |
| bool IsRV64 = MF->getSubtarget<RISCVSubtarget>().is64Bit(); |
| Register SrcReg = RISCV::X0; |
| Register Result = MRI.createVirtualRegister(&RISCV::GPRRegClass); |
| unsigned Num = 0; |
| |
| if (!IsRV64 && !isInt<32>(Val)) |
| report_fatal_error("Should only materialize 32-bit constants for RV32"); |
| |
| RISCVMatInt::InstSeq Seq; |
| RISCVMatInt::generateInstSeq(Val, IsRV64, Seq); |
| assert(Seq.size() > 0); |
| |
| for (RISCVMatInt::Inst &Inst : Seq) { |
| // Write the final result to DstReg if it's the last instruction in the Seq. |
| // Otherwise, write the result to the temp register. |
| if (++Num == Seq.size()) |
| Result = DstReg; |
| |
| if (Inst.Opc == RISCV::LUI) { |
| BuildMI(MBB, MBBI, DL, get(RISCV::LUI), Result) |
| .addImm(Inst.Imm) |
| .setMIFlag(Flag); |
| } else { |
| BuildMI(MBB, MBBI, DL, get(Inst.Opc), Result) |
| .addReg(SrcReg, RegState::Kill) |
| .addImm(Inst.Imm) |
| .setMIFlag(Flag); |
| } |
| // Only the first instruction has X0 as its source. |
| SrcReg = Result; |
| } |
| } |
| |
| // The contents of values added to Cond are not examined outside of |
| // RISCVInstrInfo, giving us flexibility in what to push to it. For RISCV, we |
| // push BranchOpcode, Reg1, Reg2. |
| static void parseCondBranch(MachineInstr &LastInst, MachineBasicBlock *&Target, |
| SmallVectorImpl<MachineOperand> &Cond) { |
| // Block ends with fall-through condbranch. |
| assert(LastInst.getDesc().isConditionalBranch() && |
| "Unknown conditional branch"); |
| Target = LastInst.getOperand(2).getMBB(); |
| Cond.push_back(MachineOperand::CreateImm(LastInst.getOpcode())); |
| Cond.push_back(LastInst.getOperand(0)); |
| Cond.push_back(LastInst.getOperand(1)); |
| } |
| |
| static unsigned getOppositeBranchOpcode(int Opc) { |
| switch (Opc) { |
| default: |
| llvm_unreachable("Unrecognized conditional branch"); |
| case RISCV::BEQ: |
| return RISCV::BNE; |
| case RISCV::BNE: |
| return RISCV::BEQ; |
| case RISCV::BLT: |
| return RISCV::BGE; |
| case RISCV::BGE: |
| return RISCV::BLT; |
| case RISCV::BLTU: |
| return RISCV::BGEU; |
| case RISCV::BGEU: |
| return RISCV::BLTU; |
| } |
| } |
| |
| bool RISCVInstrInfo::analyzeBranch(MachineBasicBlock &MBB, |
| MachineBasicBlock *&TBB, |
| MachineBasicBlock *&FBB, |
| SmallVectorImpl<MachineOperand> &Cond, |
| bool AllowModify) const { |
| TBB = FBB = nullptr; |
| Cond.clear(); |
| |
| // If the block has no terminators, it just falls into the block after it. |
| MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr(); |
| if (I == MBB.end() || !isUnpredicatedTerminator(*I)) |
| return false; |
| |
| // Count the number of terminators and find the first unconditional or |
| // indirect branch. |
| MachineBasicBlock::iterator FirstUncondOrIndirectBr = MBB.end(); |
| int NumTerminators = 0; |
| for (auto J = I.getReverse(); J != MBB.rend() && isUnpredicatedTerminator(*J); |
| J++) { |
| NumTerminators++; |
| if (J->getDesc().isUnconditionalBranch() || |
| J->getDesc().isIndirectBranch()) { |
| FirstUncondOrIndirectBr = J.getReverse(); |
| } |
| } |
| |
| // If AllowModify is true, we can erase any terminators after |
| // FirstUncondOrIndirectBR. |
| if (AllowModify && FirstUncondOrIndirectBr != MBB.end()) { |
| while (std::next(FirstUncondOrIndirectBr) != MBB.end()) { |
| std::next(FirstUncondOrIndirectBr)->eraseFromParent(); |
| NumTerminators--; |
| } |
| I = FirstUncondOrIndirectBr; |
| } |
| |
| // We can't handle blocks that end in an indirect branch. |
| if (I->getDesc().isIndirectBranch()) |
| return true; |
| |
| // We can't handle blocks with more than 2 terminators. |
| if (NumTerminators > 2) |
| return true; |
| |
| // Handle a single unconditional branch. |
| if (NumTerminators == 1 && I->getDesc().isUnconditionalBranch()) { |
| TBB = I->getOperand(0).getMBB(); |
| return false; |
| } |
| |
| // Handle a single conditional branch. |
| if (NumTerminators == 1 && I->getDesc().isConditionalBranch()) { |
| parseCondBranch(*I, TBB, Cond); |
| return false; |
| } |
| |
| // Handle a conditional branch followed by an unconditional branch. |
| if (NumTerminators == 2 && std::prev(I)->getDesc().isConditionalBranch() && |
| I->getDesc().isUnconditionalBranch()) { |
| parseCondBranch(*std::prev(I), TBB, Cond); |
| FBB = I->getOperand(0).getMBB(); |
| return false; |
| } |
| |
| // Otherwise, we can't handle this. |
| return true; |
| } |
| |
| unsigned RISCVInstrInfo::removeBranch(MachineBasicBlock &MBB, |
| int *BytesRemoved) const { |
| if (BytesRemoved) |
| *BytesRemoved = 0; |
| MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr(); |
| if (I == MBB.end()) |
| return 0; |
| |
| if (!I->getDesc().isUnconditionalBranch() && |
| !I->getDesc().isConditionalBranch()) |
| return 0; |
| |
| // Remove the branch. |
| if (BytesRemoved) |
| *BytesRemoved += getInstSizeInBytes(*I); |
| I->eraseFromParent(); |
| |
| I = MBB.end(); |
| |
| if (I == MBB.begin()) |
| return 1; |
| --I; |
| if (!I->getDesc().isConditionalBranch()) |
| return 1; |
| |
| // Remove the branch. |
| if (BytesRemoved) |
| *BytesRemoved += getInstSizeInBytes(*I); |
| I->eraseFromParent(); |
| return 2; |
| } |
| |
| // Inserts a branch into the end of the specific MachineBasicBlock, returning |
| // the number of instructions inserted. |
| unsigned RISCVInstrInfo::insertBranch( |
| MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, |
| ArrayRef<MachineOperand> Cond, const DebugLoc &DL, int *BytesAdded) const { |
| if (BytesAdded) |
| *BytesAdded = 0; |
| |
| // Shouldn't be a fall through. |
| assert(TBB && "InsertBranch must not be told to insert a fallthrough"); |
| assert((Cond.size() == 3 || Cond.size() == 0) && |
| "RISCV branch conditions have two components!"); |
| |
| // Unconditional branch. |
| if (Cond.empty()) { |
| MachineInstr &MI = *BuildMI(&MBB, DL, get(RISCV::PseudoBR)).addMBB(TBB); |
| if (BytesAdded) |
| *BytesAdded += getInstSizeInBytes(MI); |
| return 1; |
| } |
| |
| // Either a one or two-way conditional branch. |
| unsigned Opc = Cond[0].getImm(); |
| MachineInstr &CondMI = |
| *BuildMI(&MBB, DL, get(Opc)).add(Cond[1]).add(Cond[2]).addMBB(TBB); |
| if (BytesAdded) |
| *BytesAdded += getInstSizeInBytes(CondMI); |
| |
| // One-way conditional branch. |
| if (!FBB) |
| return 1; |
| |
| // Two-way conditional branch. |
| MachineInstr &MI = *BuildMI(&MBB, DL, get(RISCV::PseudoBR)).addMBB(FBB); |
| if (BytesAdded) |
| *BytesAdded += getInstSizeInBytes(MI); |
| return 2; |
| } |
| |
| unsigned RISCVInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB, |
| MachineBasicBlock &DestBB, |
| const DebugLoc &DL, |
| int64_t BrOffset, |
| RegScavenger *RS) const { |
| assert(RS && "RegScavenger required for long branching"); |
| assert(MBB.empty() && |
| "new block should be inserted for expanding unconditional branch"); |
| assert(MBB.pred_size() == 1); |
| |
| MachineFunction *MF = MBB.getParent(); |
| MachineRegisterInfo &MRI = MF->getRegInfo(); |
| const auto &TM = static_cast<const RISCVTargetMachine &>(MF->getTarget()); |
| |
| if (TM.isPositionIndependent()) |
| report_fatal_error("Unable to insert indirect branch"); |
| |
| if (!isInt<32>(BrOffset)) |
| report_fatal_error( |
| "Branch offsets outside of the signed 32-bit range not supported"); |
| |
| // FIXME: A virtual register must be used initially, as the register |
| // scavenger won't work with empty blocks (SIInstrInfo::insertIndirectBranch |
| // uses the same workaround). |
| Register ScratchReg = MRI.createVirtualRegister(&RISCV::GPRRegClass); |
| auto II = MBB.end(); |
| |
| MachineInstr &LuiMI = *BuildMI(MBB, II, DL, get(RISCV::LUI), ScratchReg) |
| .addMBB(&DestBB, RISCVII::MO_HI); |
| BuildMI(MBB, II, DL, get(RISCV::PseudoBRIND)) |
| .addReg(ScratchReg, RegState::Kill) |
| .addMBB(&DestBB, RISCVII::MO_LO); |
| |
| RS->enterBasicBlockEnd(MBB); |
| unsigned Scav = RS->scavengeRegisterBackwards(RISCV::GPRRegClass, |
| LuiMI.getIterator(), false, 0); |
| MRI.replaceRegWith(ScratchReg, Scav); |
| MRI.clearVirtRegs(); |
| RS->setRegUsed(Scav); |
| return 8; |
| } |
| |
| bool RISCVInstrInfo::reverseBranchCondition( |
| SmallVectorImpl<MachineOperand> &Cond) const { |
| assert((Cond.size() == 3) && "Invalid branch condition!"); |
| Cond[0].setImm(getOppositeBranchOpcode(Cond[0].getImm())); |
| return false; |
| } |
| |
| MachineBasicBlock * |
| RISCVInstrInfo::getBranchDestBlock(const MachineInstr &MI) const { |
| assert(MI.getDesc().isBranch() && "Unexpected opcode!"); |
| // The branch target is always the last operand. |
| int NumOp = MI.getNumExplicitOperands(); |
| return MI.getOperand(NumOp - 1).getMBB(); |
| } |
| |
| bool RISCVInstrInfo::isBranchOffsetInRange(unsigned BranchOp, |
| int64_t BrOffset) const { |
| // Ideally we could determine the supported branch offset from the |
| // RISCVII::FormMask, but this can't be used for Pseudo instructions like |
| // PseudoBR. |
| switch (BranchOp) { |
| default: |
| llvm_unreachable("Unexpected opcode!"); |
| case RISCV::BEQ: |
| case RISCV::BNE: |
| case RISCV::BLT: |
| case RISCV::BGE: |
| case RISCV::BLTU: |
| case RISCV::BGEU: |
| return isIntN(13, BrOffset); |
| case RISCV::JAL: |
| case RISCV::PseudoBR: |
| return isIntN(21, BrOffset); |
| } |
| } |
| |
| unsigned RISCVInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { |
| unsigned Opcode = MI.getOpcode(); |
| |
| switch (Opcode) { |
| default: { |
| if (MI.getParent() && MI.getParent()->getParent()) { |
| const auto MF = MI.getMF(); |
| const auto &TM = static_cast<const RISCVTargetMachine &>(MF->getTarget()); |
| const MCRegisterInfo &MRI = *TM.getMCRegisterInfo(); |
| const MCSubtargetInfo &STI = *TM.getMCSubtargetInfo(); |
| const RISCVSubtarget &ST = MF->getSubtarget<RISCVSubtarget>(); |
| if (isCompressibleInst(MI, &ST, MRI, STI)) |
| return 2; |
| } |
| return get(Opcode).getSize(); |
| } |
| case TargetOpcode::EH_LABEL: |
| case TargetOpcode::IMPLICIT_DEF: |
| case TargetOpcode::KILL: |
| case TargetOpcode::DBG_VALUE: |
| return 0; |
| case RISCV::PseudoCALLReg: |
| case RISCV::PseudoCALL: |
| case RISCV::PseudoTAIL: |
| case RISCV::PseudoLLA: |
| case RISCV::PseudoLA: |
| case RISCV::PseudoLA_TLS_IE: |
| case RISCV::PseudoLA_TLS_GD: |
| return 8; |
| case TargetOpcode::INLINEASM: |
| case TargetOpcode::INLINEASM_BR: { |
| const MachineFunction &MF = *MI.getParent()->getParent(); |
| const auto &TM = static_cast<const RISCVTargetMachine &>(MF.getTarget()); |
| return getInlineAsmLength(MI.getOperand(0).getSymbolName(), |
| *TM.getMCAsmInfo()); |
| } |
| } |
| } |
| |
| bool RISCVInstrInfo::isAsCheapAsAMove(const MachineInstr &MI) const { |
| const unsigned Opcode = MI.getOpcode(); |
| switch(Opcode) { |
| default: |
| break; |
| case RISCV::ADDI: |
| case RISCV::ORI: |
| case RISCV::XORI: |
| return (MI.getOperand(1).isReg() && MI.getOperand(1).getReg() == RISCV::X0); |
| } |
| return MI.isAsCheapAsAMove(); |
| } |
| |
| bool RISCVInstrInfo::verifyInstruction(const MachineInstr &MI, |
| StringRef &ErrInfo) const { |
| const MCInstrInfo *MCII = STI.getInstrInfo(); |
| MCInstrDesc const &Desc = MCII->get(MI.getOpcode()); |
| |
| for (auto &OI : enumerate(Desc.operands())) { |
| unsigned OpType = OI.value().OperandType; |
| if (OpType >= RISCVOp::OPERAND_FIRST_RISCV_IMM && |
| OpType <= RISCVOp::OPERAND_LAST_RISCV_IMM) { |
| const MachineOperand &MO = MI.getOperand(OI.index()); |
| if (MO.isImm()) { |
| int64_t Imm = MO.getImm(); |
| bool Ok; |
| switch (OpType) { |
| default: |
| llvm_unreachable("Unexpected operand type"); |
| case RISCVOp::OPERAND_UIMM4: |
| Ok = isUInt<4>(Imm); |
| break; |
| case RISCVOp::OPERAND_UIMM5: |
| Ok = isUInt<5>(Imm); |
| break; |
| case RISCVOp::OPERAND_UIMM12: |
| Ok = isUInt<12>(Imm); |
| break; |
| case RISCVOp::OPERAND_SIMM12: |
| Ok = isInt<12>(Imm); |
| break; |
| case RISCVOp::OPERAND_SIMM13_LSB0: |
| Ok = isShiftedInt<12, 1>(Imm); |
| break; |
| case RISCVOp::OPERAND_UIMM20: |
| Ok = isUInt<20>(Imm); |
| break; |
| case RISCVOp::OPERAND_SIMM21_LSB0: |
| Ok = isShiftedInt<20, 1>(Imm); |
| break; |
| case RISCVOp::OPERAND_UIMMLOG2XLEN: |
| if (STI.getTargetTriple().isArch64Bit()) |
| Ok = isUInt<6>(Imm); |
| else |
| Ok = isUInt<5>(Imm); |
| break; |
| } |
| if (!Ok) { |
| ErrInfo = "Invalid immediate"; |
| return false; |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| // Return true if get the base operand, byte offset of an instruction and the |
| // memory width. Width is the size of memory that is being loaded/stored. |
| bool RISCVInstrInfo::getMemOperandWithOffsetWidth( |
| const MachineInstr &LdSt, const MachineOperand *&BaseReg, int64_t &Offset, |
| unsigned &Width, const TargetRegisterInfo *TRI) const { |
| if (!LdSt.mayLoadOrStore()) |
| return false; |
| |
| // Here we assume the standard RISC-V ISA, which uses a base+offset |
| // addressing mode. You'll need to relax these conditions to support custom |
| // load/stores instructions. |
| if (LdSt.getNumExplicitOperands() != 3) |
| return false; |
| if (!LdSt.getOperand(1).isReg() || !LdSt.getOperand(2).isImm()) |
| return false; |
| |
| if (!LdSt.hasOneMemOperand()) |
| return false; |
| |
| Width = (*LdSt.memoperands_begin())->getSize(); |
| BaseReg = &LdSt.getOperand(1); |
| Offset = LdSt.getOperand(2).getImm(); |
| return true; |
| } |
| |
| bool RISCVInstrInfo::areMemAccessesTriviallyDisjoint( |
| const MachineInstr &MIa, const MachineInstr &MIb) const { |
| assert(MIa.mayLoadOrStore() && "MIa must be a load or store."); |
| assert(MIb.mayLoadOrStore() && "MIb must be a load or store."); |
| |
| if (MIa.hasUnmodeledSideEffects() || MIb.hasUnmodeledSideEffects() || |
| MIa.hasOrderedMemoryRef() || MIb.hasOrderedMemoryRef()) |
| return false; |
| |
| // Retrieve the base register, offset from the base register and width. Width |
| // is the size of memory that is being loaded/stored (e.g. 1, 2, 4). If |
| // base registers are identical, and the offset of a lower memory access + |
| // the width doesn't overlap the offset of a higher memory access, |
| // then the memory accesses are different. |
| const TargetRegisterInfo *TRI = STI.getRegisterInfo(); |
| const MachineOperand *BaseOpA = nullptr, *BaseOpB = nullptr; |
| int64_t OffsetA = 0, OffsetB = 0; |
| unsigned int WidthA = 0, WidthB = 0; |
| if (getMemOperandWithOffsetWidth(MIa, BaseOpA, OffsetA, WidthA, TRI) && |
| getMemOperandWithOffsetWidth(MIb, BaseOpB, OffsetB, WidthB, TRI)) { |
| if (BaseOpA->isIdenticalTo(*BaseOpB)) { |
| int LowOffset = std::min(OffsetA, OffsetB); |
| int HighOffset = std::max(OffsetA, OffsetB); |
| int LowWidth = (LowOffset == OffsetA) ? WidthA : WidthB; |
| if (LowOffset + LowWidth <= HighOffset) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| std::pair<unsigned, unsigned> |
| RISCVInstrInfo::decomposeMachineOperandsTargetFlags(unsigned TF) const { |
| const unsigned Mask = RISCVII::MO_DIRECT_FLAG_MASK; |
| return std::make_pair(TF & Mask, TF & ~Mask); |
| } |
| |
| ArrayRef<std::pair<unsigned, const char *>> |
| RISCVInstrInfo::getSerializableDirectMachineOperandTargetFlags() const { |
| using namespace RISCVII; |
| static const std::pair<unsigned, const char *> TargetFlags[] = { |
| {MO_CALL, "riscv-call"}, |
| {MO_PLT, "riscv-plt"}, |
| {MO_LO, "riscv-lo"}, |
| {MO_HI, "riscv-hi"}, |
| {MO_PCREL_LO, "riscv-pcrel-lo"}, |
| {MO_PCREL_HI, "riscv-pcrel-hi"}, |
| {MO_GOT_HI, "riscv-got-hi"}, |
| {MO_TPREL_LO, "riscv-tprel-lo"}, |
| {MO_TPREL_HI, "riscv-tprel-hi"}, |
| {MO_TPREL_ADD, "riscv-tprel-add"}, |
| {MO_TLS_GOT_HI, "riscv-tls-got-hi"}, |
| {MO_TLS_GD_HI, "riscv-tls-gd-hi"}}; |
| return makeArrayRef(TargetFlags); |
| } |
| bool RISCVInstrInfo::isFunctionSafeToOutlineFrom( |
| MachineFunction &MF, bool OutlineFromLinkOnceODRs) const { |
| const Function &F = MF.getFunction(); |
| |
| // Can F be deduplicated by the linker? If it can, don't outline from it. |
| if (!OutlineFromLinkOnceODRs && F.hasLinkOnceODRLinkage()) |
| return false; |
| |
| // Don't outline from functions with section markings; the program could |
| // expect that all the code is in the named section. |
| if (F.hasSection()) |
| return false; |
| |
| // It's safe to outline from MF. |
| return true; |
| } |
| |
| bool RISCVInstrInfo::isMBBSafeToOutlineFrom(MachineBasicBlock &MBB, |
| unsigned &Flags) const { |
| // More accurate safety checking is done in getOutliningCandidateInfo. |
| return true; |
| } |
| |
| // Enum values indicating how an outlined call should be constructed. |
| enum MachineOutlinerConstructionID { |
| MachineOutlinerDefault |
| }; |
| |
| outliner::OutlinedFunction RISCVInstrInfo::getOutliningCandidateInfo( |
| std::vector<outliner::Candidate> &RepeatedSequenceLocs) const { |
| |
| // First we need to filter out candidates where the X5 register (IE t0) can't |
| // be used to setup the function call. |
| auto CannotInsertCall = [](outliner::Candidate &C) { |
| const TargetRegisterInfo *TRI = C.getMF()->getSubtarget().getRegisterInfo(); |
| |
| C.initLRU(*TRI); |
| LiveRegUnits LRU = C.LRU; |
| return !LRU.available(RISCV::X5); |
| }; |
| |
| RepeatedSequenceLocs.erase(std::remove_if(RepeatedSequenceLocs.begin(), |
| RepeatedSequenceLocs.end(), |
| CannotInsertCall), |
| RepeatedSequenceLocs.end()); |
| |
| // If the sequence doesn't have enough candidates left, then we're done. |
| if (RepeatedSequenceLocs.size() < 2) |
| return outliner::OutlinedFunction(); |
| |
| unsigned SequenceSize = 0; |
| |
| auto I = RepeatedSequenceLocs[0].front(); |
| auto E = std::next(RepeatedSequenceLocs[0].back()); |
| for (; I != E; ++I) |
| SequenceSize += getInstSizeInBytes(*I); |
| |
| // call t0, function = 8 bytes. |
| unsigned CallOverhead = 8; |
| for (auto &C : RepeatedSequenceLocs) |
| C.setCallInfo(MachineOutlinerDefault, CallOverhead); |
| |
| // jr t0 = 4 bytes, 2 bytes if compressed instructions are enabled. |
| unsigned FrameOverhead = 4; |
| if (RepeatedSequenceLocs[0].getMF()->getSubtarget() |
| .getFeatureBits()[RISCV::FeatureStdExtC]) |
| FrameOverhead = 2; |
| |
| return outliner::OutlinedFunction(RepeatedSequenceLocs, SequenceSize, |
| FrameOverhead, MachineOutlinerDefault); |
| } |
| |
| outliner::InstrType |
| RISCVInstrInfo::getOutliningType(MachineBasicBlock::iterator &MBBI, |
| unsigned Flags) const { |
| MachineInstr &MI = *MBBI; |
| MachineBasicBlock *MBB = MI.getParent(); |
| const TargetRegisterInfo *TRI = |
| MBB->getParent()->getSubtarget().getRegisterInfo(); |
| |
| // Positions generally can't safely be outlined. |
| if (MI.isPosition()) { |
| // We can manually strip out CFI instructions later. |
| if (MI.isCFIInstruction()) |
| return outliner::InstrType::Invisible; |
| |
| return outliner::InstrType::Illegal; |
| } |
| |
| // Don't trust the user to write safe inline assembly. |
| if (MI.isInlineAsm()) |
| return outliner::InstrType::Illegal; |
| |
| // We can't outline branches to other basic blocks. |
| if (MI.isTerminator() && !MBB->succ_empty()) |
| return outliner::InstrType::Illegal; |
| |
| // We need support for tail calls to outlined functions before return |
| // statements can be allowed. |
| if (MI.isReturn()) |
| return outliner::InstrType::Illegal; |
| |
| // Don't allow modifying the X5 register which we use for return addresses for |
| // these outlined functions. |
| if (MI.modifiesRegister(RISCV::X5, TRI) || |
| MI.getDesc().hasImplicitDefOfPhysReg(RISCV::X5)) |
| return outliner::InstrType::Illegal; |
| |
| // Make sure the operands don't reference something unsafe. |
| for (const auto &MO : MI.operands()) |
| if (MO.isMBB() || MO.isBlockAddress() || MO.isCPI()) |
| return outliner::InstrType::Illegal; |
| |
| // Don't allow instructions which won't be materialized to impact outlining |
| // analysis. |
| if (MI.isMetaInstruction()) |
| return outliner::InstrType::Invisible; |
| |
| return outliner::InstrType::Legal; |
| } |
| |
| void RISCVInstrInfo::buildOutlinedFrame( |
| MachineBasicBlock &MBB, MachineFunction &MF, |
| const outliner::OutlinedFunction &OF) const { |
| |
| // Strip out any CFI instructions |
| bool Changed = true; |
| while (Changed) { |
| Changed = false; |
| auto I = MBB.begin(); |
| auto E = MBB.end(); |
| for (; I != E; ++I) { |
| if (I->isCFIInstruction()) { |
| I->removeFromParent(); |
| Changed = true; |
| break; |
| } |
| } |
| } |
| |
| // Add in a return instruction to the end of the outlined frame. |
| MBB.insert(MBB.end(), BuildMI(MF, DebugLoc(), get(RISCV::JALR)) |
| .addReg(RISCV::X0, RegState::Define) |
| .addReg(RISCV::X5) |
| .addImm(0)); |
| } |
| |
| MachineBasicBlock::iterator RISCVInstrInfo::insertOutlinedCall( |
| Module &M, MachineBasicBlock &MBB, MachineBasicBlock::iterator &It, |
| MachineFunction &MF, const outliner::Candidate &C) const { |
| |
| // Add in a call instruction to the outlined function at the given location. |
| It = MBB.insert(It, |
| BuildMI(MF, DebugLoc(), get(RISCV::PseudoCALLReg), RISCV::X5) |
| .addGlobalAddress(M.getNamedValue(MF.getName()), 0, |
| RISCVII::MO_CALL)); |
| return It; |
| } |