| //===----------------------- MipsBranchExpansion.cpp ----------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| /// \file |
| /// |
| /// This pass do two things: |
| /// - it expands a branch or jump instruction into a long branch if its offset |
| /// is too large to fit into its immediate field, |
| /// - it inserts nops to prevent forbidden slot hazards. |
| /// |
| /// The reason why this pass combines these two tasks is that one of these two |
| /// tasks can break the result of the previous one. |
| /// |
| /// Example of that is a situation where at first, no branch should be expanded, |
| /// but after adding at least one nop somewhere in the code to prevent a |
| /// forbidden slot hazard, offset of some branches may go out of range. In that |
| /// case it is necessary to check again if there is some branch that needs |
| /// expansion. On the other hand, expanding some branch may cause a control |
| /// transfer instruction to appear in the forbidden slot, which is a hazard that |
| /// should be fixed. This pass alternates between this two tasks untill no |
| /// changes are made. Only then we can be sure that all branches are expanded |
| /// properly, and no hazard situations exist. |
| /// |
| /// Regarding branch expanding: |
| /// |
| /// When branch instruction like beqzc or bnezc has offset that is too large |
| /// to fit into its immediate field, it has to be expanded to another |
| /// instruction or series of instructions. |
| /// |
| /// FIXME: Fix pc-region jump instructions which cross 256MB segment boundaries. |
| /// TODO: Handle out of range bc, b (pseudo) instructions. |
| /// |
| /// Regarding compact branch hazard prevention: |
| /// |
| /// Hazards handled: forbidden slots for MIPSR6. |
| /// |
| /// A forbidden slot hazard occurs when a compact branch instruction is executed |
| /// and the adjacent instruction in memory is a control transfer instruction |
| /// such as a branch or jump, ERET, ERETNC, DERET, WAIT and PAUSE. |
| /// |
| /// For example: |
| /// |
| /// 0x8004 bnec a1,v0,<P+0x18> |
| /// 0x8008 beqc a1,a2,<P+0x54> |
| /// |
| /// In such cases, the processor is required to signal a Reserved Instruction |
| /// exception. |
| /// |
| /// Here, if the instruction at 0x8004 is executed, the processor will raise an |
| /// exception as there is a control transfer instruction at 0x8008. |
| /// |
| /// There are two sources of forbidden slot hazards: |
| /// |
| /// A) A previous pass has created a compact branch directly. |
| /// B) Transforming a delay slot branch into compact branch. This case can be |
| /// difficult to process as lookahead for hazards is insufficient, as |
| /// backwards delay slot fillling can also produce hazards in previously |
| /// processed instuctions. |
| /// |
| /// In future this pass can be extended (or new pass can be created) to handle |
| /// other pipeline hazards, such as various MIPS1 hazards, processor errata that |
| /// require instruction reorganization, etc. |
| /// |
| /// This pass has to run after the delay slot filler as that pass can introduce |
| /// pipeline hazards such as compact branch hazard, hence the existing hazard |
| /// recognizer is not suitable. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "MCTargetDesc/MipsABIInfo.h" |
| #include "MCTargetDesc/MipsBaseInfo.h" |
| #include "MCTargetDesc/MipsMCNaCl.h" |
| #include "MCTargetDesc/MipsMCTargetDesc.h" |
| #include "Mips.h" |
| #include "MipsInstrInfo.h" |
| #include "MipsMachineFunction.h" |
| #include "MipsSubtarget.h" |
| #include "MipsTargetMachine.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/Statistic.h" |
| #include "llvm/ADT/StringRef.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/MachineModuleInfo.h" |
| #include "llvm/CodeGen/MachineOperand.h" |
| #include "llvm/CodeGen/TargetSubtargetInfo.h" |
| #include "llvm/IR/DebugLoc.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/MathExtras.h" |
| #include "llvm/Target/TargetMachine.h" |
| #include <algorithm> |
| #include <cassert> |
| #include <cstdint> |
| #include <iterator> |
| #include <utility> |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "mips-branch-expansion" |
| |
| STATISTIC(NumInsertedNops, "Number of nops inserted"); |
| STATISTIC(LongBranches, "Number of long branches."); |
| |
| static cl::opt<bool> |
| SkipLongBranch("skip-mips-long-branch", cl::init(false), |
| cl::desc("MIPS: Skip branch expansion pass."), cl::Hidden); |
| |
| static cl::opt<bool> |
| ForceLongBranch("force-mips-long-branch", cl::init(false), |
| cl::desc("MIPS: Expand all branches to long format."), |
| cl::Hidden); |
| |
| namespace { |
| |
| using Iter = MachineBasicBlock::iterator; |
| using ReverseIter = MachineBasicBlock::reverse_iterator; |
| |
| struct MBBInfo { |
| uint64_t Size = 0; |
| bool HasLongBranch = false; |
| MachineInstr *Br = nullptr; |
| uint64_t Offset = 0; |
| MBBInfo() = default; |
| }; |
| |
| class MipsBranchExpansion : public MachineFunctionPass { |
| public: |
| static char ID; |
| |
| MipsBranchExpansion() : MachineFunctionPass(ID), ABI(MipsABIInfo::Unknown()) { |
| initializeMipsBranchExpansionPass(*PassRegistry::getPassRegistry()); |
| } |
| |
| StringRef getPassName() const override { |
| return "Mips Branch Expansion Pass"; |
| } |
| |
| bool runOnMachineFunction(MachineFunction &F) override; |
| |
| MachineFunctionProperties getRequiredProperties() const override { |
| return MachineFunctionProperties().set( |
| MachineFunctionProperties::Property::NoVRegs); |
| } |
| |
| private: |
| void splitMBB(MachineBasicBlock *MBB); |
| void initMBBInfo(); |
| int64_t computeOffset(const MachineInstr *Br); |
| uint64_t computeOffsetFromTheBeginning(int MBB); |
| void replaceBranch(MachineBasicBlock &MBB, Iter Br, const DebugLoc &DL, |
| MachineBasicBlock *MBBOpnd); |
| bool buildProperJumpMI(MachineBasicBlock *MBB, |
| MachineBasicBlock::iterator Pos, DebugLoc DL); |
| void expandToLongBranch(MBBInfo &Info); |
| bool handleForbiddenSlot(); |
| bool handlePossibleLongBranch(); |
| |
| const MipsSubtarget *STI; |
| const MipsInstrInfo *TII; |
| |
| MachineFunction *MFp; |
| SmallVector<MBBInfo, 16> MBBInfos; |
| bool IsPIC; |
| MipsABIInfo ABI; |
| bool ForceLongBranchFirstPass = false; |
| }; |
| |
| } // end of anonymous namespace |
| |
| char MipsBranchExpansion::ID = 0; |
| |
| INITIALIZE_PASS(MipsBranchExpansion, DEBUG_TYPE, |
| "Expand out of range branch instructions and fix forbidden" |
| " slot hazards", |
| false, false) |
| |
| /// Returns a pass that clears pipeline hazards. |
| FunctionPass *llvm::createMipsBranchExpansion() { |
| return new MipsBranchExpansion(); |
| } |
| |
| // Find the next real instruction from the current position in current basic |
| // block. |
| static Iter getNextMachineInstrInBB(Iter Position) { |
| Iter I = Position, E = Position->getParent()->end(); |
| I = std::find_if_not(I, E, |
| [](const Iter &Insn) { return Insn->isTransient(); }); |
| |
| return I; |
| } |
| |
| // Find the next real instruction from the current position, looking through |
| // basic block boundaries. |
| static std::pair<Iter, bool> getNextMachineInstr(Iter Position, |
| MachineBasicBlock *Parent) { |
| if (Position == Parent->end()) { |
| do { |
| MachineBasicBlock *Succ = Parent->getNextNode(); |
| if (Succ != nullptr && Parent->isSuccessor(Succ)) { |
| Position = Succ->begin(); |
| Parent = Succ; |
| } else { |
| return std::make_pair(Position, true); |
| } |
| } while (Parent->empty()); |
| } |
| |
| Iter Instr = getNextMachineInstrInBB(Position); |
| if (Instr == Parent->end()) { |
| return getNextMachineInstr(Instr, Parent); |
| } |
| return std::make_pair(Instr, false); |
| } |
| |
| /// Iterate over list of Br's operands and search for a MachineBasicBlock |
| /// operand. |
| static MachineBasicBlock *getTargetMBB(const MachineInstr &Br) { |
| for (unsigned I = 0, E = Br.getDesc().getNumOperands(); I < E; ++I) { |
| const MachineOperand &MO = Br.getOperand(I); |
| |
| if (MO.isMBB()) |
| return MO.getMBB(); |
| } |
| |
| llvm_unreachable("This instruction does not have an MBB operand."); |
| } |
| |
| // Traverse the list of instructions backwards until a non-debug instruction is |
| // found or it reaches E. |
| static ReverseIter getNonDebugInstr(ReverseIter B, const ReverseIter &E) { |
| for (; B != E; ++B) |
| if (!B->isDebugInstr()) |
| return B; |
| |
| return E; |
| } |
| |
| // Split MBB if it has two direct jumps/branches. |
| void MipsBranchExpansion::splitMBB(MachineBasicBlock *MBB) { |
| ReverseIter End = MBB->rend(); |
| ReverseIter LastBr = getNonDebugInstr(MBB->rbegin(), End); |
| |
| // Return if MBB has no branch instructions. |
| if ((LastBr == End) || |
| (!LastBr->isConditionalBranch() && !LastBr->isUnconditionalBranch())) |
| return; |
| |
| ReverseIter FirstBr = getNonDebugInstr(std::next(LastBr), End); |
| |
| // MBB has only one branch instruction if FirstBr is not a branch |
| // instruction. |
| if ((FirstBr == End) || |
| (!FirstBr->isConditionalBranch() && !FirstBr->isUnconditionalBranch())) |
| return; |
| |
| assert(!FirstBr->isIndirectBranch() && "Unexpected indirect branch found."); |
| |
| // Create a new MBB. Move instructions in MBB to the newly created MBB. |
| MachineBasicBlock *NewMBB = |
| MFp->CreateMachineBasicBlock(MBB->getBasicBlock()); |
| |
| // Insert NewMBB and fix control flow. |
| MachineBasicBlock *Tgt = getTargetMBB(*FirstBr); |
| NewMBB->transferSuccessors(MBB); |
| if (Tgt != getTargetMBB(*LastBr)) |
| NewMBB->removeSuccessor(Tgt, true); |
| MBB->addSuccessor(NewMBB); |
| MBB->addSuccessor(Tgt); |
| MFp->insert(std::next(MachineFunction::iterator(MBB)), NewMBB); |
| |
| NewMBB->splice(NewMBB->end(), MBB, LastBr.getReverse(), MBB->end()); |
| } |
| |
| // Fill MBBInfos. |
| void MipsBranchExpansion::initMBBInfo() { |
| // Split the MBBs if they have two branches. Each basic block should have at |
| // most one branch after this loop is executed. |
| for (auto &MBB : *MFp) |
| splitMBB(&MBB); |
| |
| MFp->RenumberBlocks(); |
| MBBInfos.clear(); |
| MBBInfos.resize(MFp->size()); |
| |
| for (unsigned I = 0, E = MBBInfos.size(); I < E; ++I) { |
| MachineBasicBlock *MBB = MFp->getBlockNumbered(I); |
| |
| // Compute size of MBB. |
| for (MachineBasicBlock::instr_iterator MI = MBB->instr_begin(); |
| MI != MBB->instr_end(); ++MI) |
| MBBInfos[I].Size += TII->getInstSizeInBytes(*MI); |
| } |
| } |
| |
| // Compute offset of branch in number of bytes. |
| int64_t MipsBranchExpansion::computeOffset(const MachineInstr *Br) { |
| int64_t Offset = 0; |
| int ThisMBB = Br->getParent()->getNumber(); |
| int TargetMBB = getTargetMBB(*Br)->getNumber(); |
| |
| // Compute offset of a forward branch. |
| if (ThisMBB < TargetMBB) { |
| for (int N = ThisMBB + 1; N < TargetMBB; ++N) |
| Offset += MBBInfos[N].Size; |
| |
| return Offset + 4; |
| } |
| |
| // Compute offset of a backward branch. |
| for (int N = ThisMBB; N >= TargetMBB; --N) |
| Offset += MBBInfos[N].Size; |
| |
| return -Offset + 4; |
| } |
| |
| // Returns the distance in bytes up until MBB |
| uint64_t MipsBranchExpansion::computeOffsetFromTheBeginning(int MBB) { |
| uint64_t Offset = 0; |
| for (int N = 0; N < MBB; ++N) |
| Offset += MBBInfos[N].Size; |
| return Offset; |
| } |
| |
| // Replace Br with a branch which has the opposite condition code and a |
| // MachineBasicBlock operand MBBOpnd. |
| void MipsBranchExpansion::replaceBranch(MachineBasicBlock &MBB, Iter Br, |
| const DebugLoc &DL, |
| MachineBasicBlock *MBBOpnd) { |
| unsigned NewOpc = TII->getOppositeBranchOpc(Br->getOpcode()); |
| const MCInstrDesc &NewDesc = TII->get(NewOpc); |
| |
| MachineInstrBuilder MIB = BuildMI(MBB, Br, DL, NewDesc); |
| |
| for (unsigned I = 0, E = Br->getDesc().getNumOperands(); I < E; ++I) { |
| MachineOperand &MO = Br->getOperand(I); |
| |
| if (!MO.isReg()) { |
| assert(MO.isMBB() && "MBB operand expected."); |
| break; |
| } |
| |
| MIB.addReg(MO.getReg()); |
| } |
| |
| MIB.addMBB(MBBOpnd); |
| |
| if (Br->hasDelaySlot()) { |
| // Bundle the instruction in the delay slot to the newly created branch |
| // and erase the original branch. |
| assert(Br->isBundledWithSucc()); |
| MachineBasicBlock::instr_iterator II = Br.getInstrIterator(); |
| MIBundleBuilder(&*MIB).append((++II)->removeFromBundle()); |
| } |
| Br->eraseFromParent(); |
| } |
| |
| bool MipsBranchExpansion::buildProperJumpMI(MachineBasicBlock *MBB, |
| MachineBasicBlock::iterator Pos, |
| DebugLoc DL) { |
| bool HasR6 = ABI.IsN64() ? STI->hasMips64r6() : STI->hasMips32r6(); |
| bool AddImm = HasR6 && !STI->useIndirectJumpsHazard(); |
| |
| unsigned JR = ABI.IsN64() ? Mips::JR64 : Mips::JR; |
| unsigned JIC = ABI.IsN64() ? Mips::JIC64 : Mips::JIC; |
| unsigned JR_HB = ABI.IsN64() ? Mips::JR_HB64 : Mips::JR_HB; |
| unsigned JR_HB_R6 = ABI.IsN64() ? Mips::JR_HB64_R6 : Mips::JR_HB_R6; |
| |
| unsigned JumpOp; |
| if (STI->useIndirectJumpsHazard()) |
| JumpOp = HasR6 ? JR_HB_R6 : JR_HB; |
| else |
| JumpOp = HasR6 ? JIC : JR; |
| |
| if (JumpOp == Mips::JIC && STI->inMicroMipsMode()) |
| JumpOp = Mips::JIC_MMR6; |
| |
| unsigned ATReg = ABI.IsN64() ? Mips::AT_64 : Mips::AT; |
| MachineInstrBuilder Instr = |
| BuildMI(*MBB, Pos, DL, TII->get(JumpOp)).addReg(ATReg); |
| if (AddImm) |
| Instr.addImm(0); |
| |
| return !AddImm; |
| } |
| |
| // Expand branch instructions to long branches. |
| // TODO: This function has to be fixed for beqz16 and bnez16, because it |
| // currently assumes that all branches have 16-bit offsets, and will produce |
| // wrong code if branches whose allowed offsets are [-128, -126, ..., 126] |
| // are present. |
| void MipsBranchExpansion::expandToLongBranch(MBBInfo &I) { |
| MachineBasicBlock::iterator Pos; |
| MachineBasicBlock *MBB = I.Br->getParent(), *TgtMBB = getTargetMBB(*I.Br); |
| DebugLoc DL = I.Br->getDebugLoc(); |
| const BasicBlock *BB = MBB->getBasicBlock(); |
| MachineFunction::iterator FallThroughMBB = ++MachineFunction::iterator(MBB); |
| MachineBasicBlock *LongBrMBB = MFp->CreateMachineBasicBlock(BB); |
| |
| MFp->insert(FallThroughMBB, LongBrMBB); |
| MBB->replaceSuccessor(TgtMBB, LongBrMBB); |
| |
| if (IsPIC) { |
| MachineBasicBlock *BalTgtMBB = MFp->CreateMachineBasicBlock(BB); |
| MFp->insert(FallThroughMBB, BalTgtMBB); |
| LongBrMBB->addSuccessor(BalTgtMBB); |
| BalTgtMBB->addSuccessor(TgtMBB); |
| |
| // We must select between the MIPS32r6/MIPS64r6 BALC (which is a normal |
| // instruction) and the pre-MIPS32r6/MIPS64r6 definition (which is an |
| // pseudo-instruction wrapping BGEZAL). |
| const unsigned BalOp = |
| STI->hasMips32r6() |
| ? STI->inMicroMipsMode() ? Mips::BALC_MMR6 : Mips::BALC |
| : STI->inMicroMipsMode() ? Mips::BAL_BR_MM : Mips::BAL_BR; |
| |
| if (!ABI.IsN64()) { |
| // Pre R6: |
| // $longbr: |
| // addiu $sp, $sp, -8 |
| // sw $ra, 0($sp) |
| // lui $at, %hi($tgt - $baltgt) |
| // bal $baltgt |
| // addiu $at, $at, %lo($tgt - $baltgt) |
| // $baltgt: |
| // addu $at, $ra, $at |
| // lw $ra, 0($sp) |
| // jr $at |
| // addiu $sp, $sp, 8 |
| // $fallthrough: |
| // |
| |
| // R6: |
| // $longbr: |
| // addiu $sp, $sp, -8 |
| // sw $ra, 0($sp) |
| // lui $at, %hi($tgt - $baltgt) |
| // addiu $at, $at, %lo($tgt - $baltgt) |
| // balc $baltgt |
| // $baltgt: |
| // addu $at, $ra, $at |
| // lw $ra, 0($sp) |
| // addiu $sp, $sp, 8 |
| // jic $at, 0 |
| // $fallthrough: |
| |
| Pos = LongBrMBB->begin(); |
| |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::ADDiu), Mips::SP) |
| .addReg(Mips::SP) |
| .addImm(-8); |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::SW)) |
| .addReg(Mips::RA) |
| .addReg(Mips::SP) |
| .addImm(0); |
| |
| // LUi and ADDiu instructions create 32-bit offset of the target basic |
| // block from the target of BAL(C) instruction. We cannot use immediate |
| // value for this offset because it cannot be determined accurately when |
| // the program has inline assembly statements. We therefore use the |
| // relocation expressions %hi($tgt-$baltgt) and %lo($tgt-$baltgt) which |
| // are resolved during the fixup, so the values will always be correct. |
| // |
| // Since we cannot create %hi($tgt-$baltgt) and %lo($tgt-$baltgt) |
| // expressions at this point (it is possible only at the MC layer), |
| // we replace LUi and ADDiu with pseudo instructions |
| // LONG_BRANCH_LUi and LONG_BRANCH_ADDiu, and add both basic |
| // blocks as operands to these instructions. When lowering these pseudo |
| // instructions to LUi and ADDiu in the MC layer, we will create |
| // %hi($tgt-$baltgt) and %lo($tgt-$baltgt) expressions and add them as |
| // operands to lowered instructions. |
| |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::LONG_BRANCH_LUi), Mips::AT) |
| .addMBB(TgtMBB, MipsII::MO_ABS_HI) |
| .addMBB(BalTgtMBB); |
| |
| MachineInstrBuilder BalInstr = |
| BuildMI(*MFp, DL, TII->get(BalOp)).addMBB(BalTgtMBB); |
| MachineInstrBuilder ADDiuInstr = |
| BuildMI(*MFp, DL, TII->get(Mips::LONG_BRANCH_ADDiu), Mips::AT) |
| .addReg(Mips::AT) |
| .addMBB(TgtMBB, MipsII::MO_ABS_LO) |
| .addMBB(BalTgtMBB); |
| if (STI->hasMips32r6()) { |
| LongBrMBB->insert(Pos, ADDiuInstr); |
| LongBrMBB->insert(Pos, BalInstr); |
| } else { |
| LongBrMBB->insert(Pos, BalInstr); |
| LongBrMBB->insert(Pos, ADDiuInstr); |
| LongBrMBB->rbegin()->bundleWithPred(); |
| } |
| |
| Pos = BalTgtMBB->begin(); |
| |
| BuildMI(*BalTgtMBB, Pos, DL, TII->get(Mips::ADDu), Mips::AT) |
| .addReg(Mips::RA) |
| .addReg(Mips::AT); |
| BuildMI(*BalTgtMBB, Pos, DL, TII->get(Mips::LW), Mips::RA) |
| .addReg(Mips::SP) |
| .addImm(0); |
| if (STI->isTargetNaCl()) |
| // Bundle-align the target of indirect branch JR. |
| TgtMBB->setAlignment(MIPS_NACL_BUNDLE_ALIGN); |
| |
| // In NaCl, modifying the sp is not allowed in branch delay slot. |
| // For MIPS32R6, we can skip using a delay slot branch. |
| bool hasDelaySlot = buildProperJumpMI(BalTgtMBB, Pos, DL); |
| |
| if (STI->isTargetNaCl() || !hasDelaySlot) { |
| BuildMI(*BalTgtMBB, std::prev(Pos), DL, TII->get(Mips::ADDiu), Mips::SP) |
| .addReg(Mips::SP) |
| .addImm(8); |
| } |
| if (hasDelaySlot) { |
| if (STI->isTargetNaCl()) { |
| BuildMI(*BalTgtMBB, Pos, DL, TII->get(Mips::NOP)); |
| } else { |
| BuildMI(*BalTgtMBB, Pos, DL, TII->get(Mips::ADDiu), Mips::SP) |
| .addReg(Mips::SP) |
| .addImm(8); |
| } |
| BalTgtMBB->rbegin()->bundleWithPred(); |
| } |
| } else { |
| // Pre R6: |
| // $longbr: |
| // daddiu $sp, $sp, -16 |
| // sd $ra, 0($sp) |
| // daddiu $at, $zero, %hi($tgt - $baltgt) |
| // dsll $at, $at, 16 |
| // bal $baltgt |
| // daddiu $at, $at, %lo($tgt - $baltgt) |
| // $baltgt: |
| // daddu $at, $ra, $at |
| // ld $ra, 0($sp) |
| // jr64 $at |
| // daddiu $sp, $sp, 16 |
| // $fallthrough: |
| |
| // R6: |
| // $longbr: |
| // daddiu $sp, $sp, -16 |
| // sd $ra, 0($sp) |
| // daddiu $at, $zero, %hi($tgt - $baltgt) |
| // dsll $at, $at, 16 |
| // daddiu $at, $at, %lo($tgt - $baltgt) |
| // balc $baltgt |
| // $baltgt: |
| // daddu $at, $ra, $at |
| // ld $ra, 0($sp) |
| // daddiu $sp, $sp, 16 |
| // jic $at, 0 |
| // $fallthrough: |
| |
| // We assume the branch is within-function, and that offset is within |
| // +/- 2GB. High 32 bits will therefore always be zero. |
| |
| // Note that this will work even if the offset is negative, because |
| // of the +1 modification that's added in that case. For example, if the |
| // offset is -1MB (0xFFFFFFFFFFF00000), the computation for %higher is |
| // |
| // 0xFFFFFFFFFFF00000 + 0x80008000 = 0x000000007FF08000 |
| // |
| // and the bits [47:32] are zero. For %highest |
| // |
| // 0xFFFFFFFFFFF00000 + 0x800080008000 = 0x000080007FF08000 |
| // |
| // and the bits [63:48] are zero. |
| |
| Pos = LongBrMBB->begin(); |
| |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::DADDiu), Mips::SP_64) |
| .addReg(Mips::SP_64) |
| .addImm(-16); |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::SD)) |
| .addReg(Mips::RA_64) |
| .addReg(Mips::SP_64) |
| .addImm(0); |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::LONG_BRANCH_DADDiu), |
| Mips::AT_64) |
| .addReg(Mips::ZERO_64) |
| .addMBB(TgtMBB, MipsII::MO_ABS_HI) |
| .addMBB(BalTgtMBB); |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::DSLL), Mips::AT_64) |
| .addReg(Mips::AT_64) |
| .addImm(16); |
| |
| MachineInstrBuilder BalInstr = |
| BuildMI(*MFp, DL, TII->get(BalOp)).addMBB(BalTgtMBB); |
| MachineInstrBuilder DADDiuInstr = |
| BuildMI(*MFp, DL, TII->get(Mips::LONG_BRANCH_DADDiu), Mips::AT_64) |
| .addReg(Mips::AT_64) |
| .addMBB(TgtMBB, MipsII::MO_ABS_LO) |
| .addMBB(BalTgtMBB); |
| if (STI->hasMips32r6()) { |
| LongBrMBB->insert(Pos, DADDiuInstr); |
| LongBrMBB->insert(Pos, BalInstr); |
| } else { |
| LongBrMBB->insert(Pos, BalInstr); |
| LongBrMBB->insert(Pos, DADDiuInstr); |
| LongBrMBB->rbegin()->bundleWithPred(); |
| } |
| |
| Pos = BalTgtMBB->begin(); |
| |
| BuildMI(*BalTgtMBB, Pos, DL, TII->get(Mips::DADDu), Mips::AT_64) |
| .addReg(Mips::RA_64) |
| .addReg(Mips::AT_64); |
| BuildMI(*BalTgtMBB, Pos, DL, TII->get(Mips::LD), Mips::RA_64) |
| .addReg(Mips::SP_64) |
| .addImm(0); |
| |
| bool hasDelaySlot = buildProperJumpMI(BalTgtMBB, Pos, DL); |
| // If there is no delay slot, Insert stack adjustment before |
| if (!hasDelaySlot) { |
| BuildMI(*BalTgtMBB, std::prev(Pos), DL, TII->get(Mips::DADDiu), |
| Mips::SP_64) |
| .addReg(Mips::SP_64) |
| .addImm(16); |
| } else { |
| BuildMI(*BalTgtMBB, Pos, DL, TII->get(Mips::DADDiu), Mips::SP_64) |
| .addReg(Mips::SP_64) |
| .addImm(16); |
| BalTgtMBB->rbegin()->bundleWithPred(); |
| } |
| } |
| } else { // Not PIC |
| Pos = LongBrMBB->begin(); |
| LongBrMBB->addSuccessor(TgtMBB); |
| |
| // Compute the position of the potentiall jump instruction (basic blocks |
| // before + 4 for the instruction) |
| uint64_t JOffset = computeOffsetFromTheBeginning(MBB->getNumber()) + |
| MBBInfos[MBB->getNumber()].Size + 4; |
| uint64_t TgtMBBOffset = computeOffsetFromTheBeginning(TgtMBB->getNumber()); |
| // If it's a forward jump, then TgtMBBOffset will be shifted by two |
| // instructions |
| if (JOffset < TgtMBBOffset) |
| TgtMBBOffset += 2 * 4; |
| // Compare 4 upper bits to check if it's the same segment |
| bool SameSegmentJump = JOffset >> 28 == TgtMBBOffset >> 28; |
| |
| if (STI->hasMips32r6() && TII->isBranchOffsetInRange(Mips::BC, I.Offset)) { |
| // R6: |
| // $longbr: |
| // bc $tgt |
| // $fallthrough: |
| // |
| BuildMI(*LongBrMBB, Pos, DL, |
| TII->get(STI->inMicroMipsMode() ? Mips::BC_MMR6 : Mips::BC)) |
| .addMBB(TgtMBB); |
| } else if (SameSegmentJump) { |
| // Pre R6: |
| // $longbr: |
| // j $tgt |
| // nop |
| // $fallthrough: |
| // |
| MIBundleBuilder(*LongBrMBB, Pos) |
| .append(BuildMI(*MFp, DL, TII->get(Mips::J)).addMBB(TgtMBB)) |
| .append(BuildMI(*MFp, DL, TII->get(Mips::NOP))); |
| } else { |
| // At this point, offset where we need to branch does not fit into |
| // immediate field of the branch instruction and is not in the same |
| // segment as jump instruction. Therefore we will break it into couple |
| // instructions, where we first load the offset into register, and then we |
| // do branch register. |
| if (ABI.IsN64()) { |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::LONG_BRANCH_LUi2Op_64), |
| Mips::AT_64) |
| .addMBB(TgtMBB, MipsII::MO_HIGHEST); |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::LONG_BRANCH_DADDiu2Op), |
| Mips::AT_64) |
| .addReg(Mips::AT_64) |
| .addMBB(TgtMBB, MipsII::MO_HIGHER); |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::DSLL), Mips::AT_64) |
| .addReg(Mips::AT_64) |
| .addImm(16); |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::LONG_BRANCH_DADDiu2Op), |
| Mips::AT_64) |
| .addReg(Mips::AT_64) |
| .addMBB(TgtMBB, MipsII::MO_ABS_HI); |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::DSLL), Mips::AT_64) |
| .addReg(Mips::AT_64) |
| .addImm(16); |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::LONG_BRANCH_DADDiu2Op), |
| Mips::AT_64) |
| .addReg(Mips::AT_64) |
| .addMBB(TgtMBB, MipsII::MO_ABS_LO); |
| } else { |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::LONG_BRANCH_LUi2Op), |
| Mips::AT) |
| .addMBB(TgtMBB, MipsII::MO_ABS_HI); |
| BuildMI(*LongBrMBB, Pos, DL, TII->get(Mips::LONG_BRANCH_ADDiu2Op), |
| Mips::AT) |
| .addReg(Mips::AT) |
| .addMBB(TgtMBB, MipsII::MO_ABS_LO); |
| } |
| buildProperJumpMI(LongBrMBB, Pos, DL); |
| } |
| } |
| |
| if (I.Br->isUnconditionalBranch()) { |
| // Change branch destination. |
| assert(I.Br->getDesc().getNumOperands() == 1); |
| I.Br->RemoveOperand(0); |
| I.Br->addOperand(MachineOperand::CreateMBB(LongBrMBB)); |
| } else |
| // Change branch destination and reverse condition. |
| replaceBranch(*MBB, I.Br, DL, &*FallThroughMBB); |
| } |
| |
| static void emitGPDisp(MachineFunction &F, const MipsInstrInfo *TII) { |
| MachineBasicBlock &MBB = F.front(); |
| MachineBasicBlock::iterator I = MBB.begin(); |
| DebugLoc DL = MBB.findDebugLoc(MBB.begin()); |
| BuildMI(MBB, I, DL, TII->get(Mips::LUi), Mips::V0) |
| .addExternalSymbol("_gp_disp", MipsII::MO_ABS_HI); |
| BuildMI(MBB, I, DL, TII->get(Mips::ADDiu), Mips::V0) |
| .addReg(Mips::V0) |
| .addExternalSymbol("_gp_disp", MipsII::MO_ABS_LO); |
| MBB.removeLiveIn(Mips::V0); |
| } |
| |
| bool MipsBranchExpansion::handleForbiddenSlot() { |
| // Forbidden slot hazards are only defined for MIPSR6 but not microMIPSR6. |
| if (!STI->hasMips32r6() || STI->inMicroMipsMode()) |
| return false; |
| |
| bool Changed = false; |
| |
| for (MachineFunction::iterator FI = MFp->begin(); FI != MFp->end(); ++FI) { |
| for (Iter I = FI->begin(); I != FI->end(); ++I) { |
| |
| // Forbidden slot hazard handling. Use lookahead over state. |
| if (!TII->HasForbiddenSlot(*I)) |
| continue; |
| |
| Iter Inst; |
| bool LastInstInFunction = |
| std::next(I) == FI->end() && std::next(FI) == MFp->end(); |
| if (!LastInstInFunction) { |
| std::pair<Iter, bool> Res = getNextMachineInstr(std::next(I), &*FI); |
| LastInstInFunction |= Res.second; |
| Inst = Res.first; |
| } |
| |
| if (LastInstInFunction || !TII->SafeInForbiddenSlot(*Inst)) { |
| |
| MachineBasicBlock::instr_iterator Iit = I->getIterator(); |
| if (std::next(Iit) == FI->end() || |
| std::next(Iit)->getOpcode() != Mips::NOP) { |
| Changed = true; |
| MIBundleBuilder(&*I).append( |
| BuildMI(*MFp, I->getDebugLoc(), TII->get(Mips::NOP))); |
| NumInsertedNops++; |
| } |
| } |
| } |
| } |
| |
| return Changed; |
| } |
| |
| bool MipsBranchExpansion::handlePossibleLongBranch() { |
| if (STI->inMips16Mode() || !STI->enableLongBranchPass()) |
| return false; |
| |
| if (SkipLongBranch) |
| return false; |
| |
| bool EverMadeChange = false, MadeChange = true; |
| |
| while (MadeChange) { |
| MadeChange = false; |
| |
| initMBBInfo(); |
| |
| for (unsigned I = 0, E = MBBInfos.size(); I < E; ++I) { |
| MachineBasicBlock *MBB = MFp->getBlockNumbered(I); |
| // Search for MBB's branch instruction. |
| ReverseIter End = MBB->rend(); |
| ReverseIter Br = getNonDebugInstr(MBB->rbegin(), End); |
| |
| if ((Br != End) && Br->isBranch() && !Br->isIndirectBranch() && |
| (Br->isConditionalBranch() || |
| (Br->isUnconditionalBranch() && IsPIC))) { |
| int64_t Offset = computeOffset(&*Br); |
| |
| if (STI->isTargetNaCl()) { |
| // The offset calculation does not include sandboxing instructions |
| // that will be added later in the MC layer. Since at this point we |
| // don't know the exact amount of code that "sandboxing" will add, we |
| // conservatively estimate that code will not grow more than 100%. |
| Offset *= 2; |
| } |
| |
| if (ForceLongBranchFirstPass || |
| !TII->isBranchOffsetInRange(Br->getOpcode(), Offset)) { |
| MBBInfos[I].Offset = Offset; |
| MBBInfos[I].Br = &*Br; |
| } |
| } |
| } // End for |
| |
| ForceLongBranchFirstPass = false; |
| |
| SmallVectorImpl<MBBInfo>::iterator I, E = MBBInfos.end(); |
| |
| for (I = MBBInfos.begin(); I != E; ++I) { |
| // Skip if this MBB doesn't have a branch or the branch has already been |
| // converted to a long branch. |
| if (!I->Br) |
| continue; |
| |
| expandToLongBranch(*I); |
| ++LongBranches; |
| EverMadeChange = MadeChange = true; |
| } |
| |
| MFp->RenumberBlocks(); |
| } |
| |
| return EverMadeChange; |
| } |
| |
| bool MipsBranchExpansion::runOnMachineFunction(MachineFunction &MF) { |
| const TargetMachine &TM = MF.getTarget(); |
| IsPIC = TM.isPositionIndependent(); |
| ABI = static_cast<const MipsTargetMachine &>(TM).getABI(); |
| STI = &static_cast<const MipsSubtarget &>(MF.getSubtarget()); |
| TII = static_cast<const MipsInstrInfo *>(STI->getInstrInfo()); |
| |
| if (IsPIC && ABI.IsO32() && |
| MF.getInfo<MipsFunctionInfo>()->globalBaseRegSet()) |
| emitGPDisp(MF, TII); |
| |
| MFp = &MF; |
| |
| ForceLongBranchFirstPass = ForceLongBranch; |
| // Run these two at least once |
| bool longBranchChanged = handlePossibleLongBranch(); |
| bool forbiddenSlotChanged = handleForbiddenSlot(); |
| |
| bool Changed = longBranchChanged || forbiddenSlotChanged; |
| |
| // Then run them alternatively while there are changes |
| while (forbiddenSlotChanged) { |
| longBranchChanged = handlePossibleLongBranch(); |
| if (!longBranchChanged) |
| break; |
| forbiddenSlotChanged = handleForbiddenSlot(); |
| } |
| |
| return Changed; |
| } |