diff --git a/llvm/include/llvm/CodeGen/MachineOutliner.h b/llvm/include/llvm/CodeGen/MachineOutliner.h --- a/llvm/include/llvm/CodeGen/MachineOutliner.h +++ b/llvm/include/llvm/CodeGen/MachineOutliner.h @@ -76,9 +76,16 @@ /// instructions in this \p Candidate. /// /// This is optionally used by the target to determine which registers have - /// been used across the sequence. + /// been used before the sequence. LiveRegUnits UsedInSequence; + /// Contains the accumulated register liveness information for the + /// instructions before this \p Candidate. + /// + /// This is optionally used by the target to determine which registers have + /// been used across the sequence. + LiveRegUnits BeforeSequence; + /// Target-specific flags for this Candidate's MBB. unsigned Flags = 0x0; @@ -104,8 +111,8 @@ /// Returns the call overhead of this candidate if it is in the list. unsigned getCallOverhead() const { return CallOverhead; } - MachineBasicBlock::iterator &front() { return FirstInst; } - MachineBasicBlock::iterator &back() { return LastInst; } + const MachineBasicBlock::iterator &front() const { return FirstInst; } + const MachineBasicBlock::iterator &back() const { return LastInst; } MachineFunction *getMF() const { return MBB->getParent(); } MachineBasicBlock *getMBB() const { return MBB; } @@ -156,6 +163,13 @@ UsedInSequence.init(TRI); std::for_each(front(), std::next(back()), [this](MachineInstr &MI) { UsedInSequence.accumulate(MI); }); + + // Walk from the start of the machine basic block up until the start of the + // outlining candidate. + BeforeSequence.init(TRI); + BeforeSequence.addLiveIns(*MBB); + std::for_each(MBB->begin(), front(), + [this](MachineInstr &MI) { BeforeSequence.accumulate(MI); }); } }; diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.h b/llvm/lib/Target/ARM/ARMBaseInstrInfo.h --- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.h +++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.h @@ -370,12 +370,35 @@ MI->getOpcode() == ARM::t2WhileLoopStartLR; } +protected: + /// Returns true if Target is able to save the LR to the stack, given a + /// MachineOutliner::Candidate as context. While this is always possible in + /// Subtargets that are ARM32 and Thumb2, Thumb1 has additional requirements + /// to be able to save the LR to the stack. The Thumb1InstrInfo will override + /// this function and provide more constraints. + virtual bool + canSaveLRToStack(const outliner::Candidate &C) const { return true; } + + /// Combine the saving and restoring of LR into one operation, as well as + /// emitting the CFI instructions. Thumb1 does this operation differently, so + /// this function will be overridden in Thumb1InstrInfo. + + /// This function will take two iterators that indicate Front and Back of a + /// region, and emit a save to the front as well as a restore to the + /// back. Both iterators will be advanced forward, and the return value will + /// be an iterator pointing to the back of the save of the LR. + virtual MachineBasicBlock::iterator + saveAndRestoreLR(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &Front, + MachineBasicBlock::iterator &Back, + const outliner::Candidate &C) const; + private: /// Returns an unused general-purpose register which can be used for /// constructing an outlined call if one exists. Returns 0 otherwise. unsigned findRegisterToSaveLRTo(const outliner::Candidate &C) const; - // Adds an instruction which saves the link register on top of the stack into + /// Adds an instruction which saves the link register on top of the stack into /// the MachineBasicBlock \p MBB at position \p It. void saveLROnStack(MachineBasicBlock &MBB, MachineBasicBlock::iterator It) const; diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp --- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp @@ -5571,13 +5571,13 @@ /// BX LR I2 /// BX LR /// -/// +-------------------------+--------+-----+ -/// | | Thumb2 | ARM | -/// +-------------------------+--------+-----+ -/// | Call overhead in Bytes | 4 | 4 | -/// | Frame overhead in Bytes | 0 | 0 | -/// | Stack fixup required | No | No | -/// +-------------------------+--------+-----+ +/// +-------------------------+--------+--------+-----+ +/// | | Thumb1 | Thumb2 | ARM | +/// +-------------------------+--------+--------+-----+ +/// | Call overhead in Bytes | 4 | 4 | 4 | +/// | Frame overhead in Bytes | 0 | 0 | 0 | +/// | Stack fixup required | No | No | No | +/// +-------------------------+--------+--------+-----+ /// /// \p MachineOutlinerThunk implies that the function is being created from /// a sequence of instructions ending in a call. The outlined function is @@ -5591,13 +5591,13 @@ /// BL f I2 /// B f /// -/// +-------------------------+--------+-----+ -/// | | Thumb2 | ARM | -/// +-------------------------+--------+-----+ -/// | Call overhead in Bytes | 4 | 4 | -/// | Frame overhead in Bytes | 0 | 0 | -/// | Stack fixup required | No | No | -/// +-------------------------+--------+-----+ +/// +-------------------------+--------+--------+-----+ +/// | | Thumb1 | Thumb2 | ARM | +/// +-------------------------+--------+--------+-----+ +/// | Call overhead in Bytes | 4 | 4 | 4 | +/// | Frame overhead in Bytes | 0 | 0 | 0 | +/// | Stack fixup required | No | No | No | +/// +-------------------------+--------+--------+-----+ /// /// \p MachineOutlinerNoLRSave implies that the function should be called using /// a BL instruction, but doesn't require LR to be saved and restored. This @@ -5611,13 +5611,13 @@ /// I3 /// BX LR /// -/// +-------------------------+--------+-----+ -/// | | Thumb2 | ARM | -/// +-------------------------+--------+-----+ -/// | Call overhead in Bytes | 4 | 4 | -/// | Frame overhead in Bytes | 4 | 4 | -/// | Stack fixup required | No | No | -/// +-------------------------+--------+-----+ +/// +-------------------------+--------+--------+-----+ +/// | | Thumb1 | Thumb2 | ARM | +/// +-------------------------+--------+--------+-----+ +/// | Call overhead in Bytes | 4 | 4 | 4 | +/// | Frame overhead in Bytes | 4 | 4 | 4 | +/// | Stack fixup required | No | No | No | +/// +-------------------------+--------+--------+-----+ /// /// \p MachineOutlinerRegSave implies that the function should be called with a /// save and restore of LR to an available register. This allows us to avoid @@ -5632,13 +5632,13 @@ /// I3 /// BX LR /// -/// +-------------------------+--------+-----+ -/// | | Thumb2 | ARM | -/// +-------------------------+--------+-----+ -/// | Call overhead in Bytes | 8 | 12 | -/// | Frame overhead in Bytes | 2 | 4 | -/// | Stack fixup required | No | No | -/// +-------------------------+--------+-----+ +/// +-------------------------+--------+--------+-----+ +/// | | Thumb1 | Thumb2 | ARM | +/// +-------------------------+--------+--------+-----+ +/// | Call overhead in Bytes | 8 | 8 | 12 | +/// | Frame overhead in Bytes | 2 | 2 | 4 | +/// | Stack fixup required | No | No | No | +/// +-------------------------+--------+--------+-----+ /// /// \p MachineOutlinerDefault implies that the function should be called with /// a save and restore of LR to the stack. @@ -5651,13 +5651,13 @@ /// I3 /// BX LR /// -/// +-------------------------+--------+-----+ -/// | | Thumb2 | ARM | -/// +-------------------------+--------+-----+ -/// | Call overhead in Bytes | 8 | 12 | -/// | Frame overhead in Bytes | 2 | 4 | -/// | Stack fixup required | Yes | Yes | -/// +-------------------------+--------+-----+ +/// +-------------------------+--------+--------+-----+ +/// | | Thumb1 | Thumb2 | ARM | +/// +-------------------------+--------+--------+-----+ +/// | Call overhead in Bytes | 14 | 8 | 12 | +/// | Frame overhead in Bytes | 2 | 2 | 4 | +/// | Stack fixup required | Yes | Yes | Yes | +/// +-------------------------+--------+--------+-----+ enum MachineOutlinerClass { MachineOutlinerTailCall, @@ -5686,6 +5686,18 @@ const int FrameDefault; const int SaveRestoreLROnStack; + // This enum encodes the various costs of performing certain aspects of + // outlining. The cost of an outline is f + n * c + r, where: + // + // - f is the cost of the frame construction. + // - n is the number of repeated sequences, and c is the cost of a call. + // (This is a simplification, as each repeated sequence can have a different + // call cost) + // - r is the cost of an extra save and restore of the LR in the frame. This + // is optional, if the frame doesn't require saving and restoring the LR. + // + // For more details, see ARMBaseInstrInfo::getOutliningCandidateInfo and + // llvm::outliner::Candidate. OutlinerCosts(const ARMSubtarget &target) : CallTailCall(target.isThumb() ? 4 : 4), FrameTailCall(target.isThumb() ? 0 : 0), @@ -5695,9 +5707,9 @@ FrameNoLRSave(target.isThumb() ? 4 : 4), CallRegSave(target.isThumb() ? 8 : 12), FrameRegSave(target.isThumb() ? 2 : 4), - CallDefault(target.isThumb() ? 8 : 12), + CallDefault(target.isThumb() ? target.isThumb1Only() ? 14 : 8 : 12), FrameDefault(target.isThumb() ? 2 : 4), - SaveRestoreLROnStack(target.isThumb() ? 8 : 8) {} + SaveRestoreLROnStack(target.isThumb() ? target.isThumb1Only() ? 10 : 8 : 8) {} }; unsigned @@ -5891,16 +5903,28 @@ FrameID = MachineOutlinerNoLRSave; } else SetCandidateCallInfo(MachineOutlinerDefault, Costs.CallDefault); + + // Thumb1 requires an extra register in order to restore the LR from the + // stack for MachineOutlinerDefault. This register needs to be liveout of + // the repeated sequence. It can be live, but must be unused in the + // repeated sequence. If we cannot find such a register, then all + // MachineOutlinerDefault candidates cannot be outlined. + if (Subtarget.isThumb1Only() && !canSaveLRToStack(FirstCand)) { + llvm::erase_if(RepeatedSequenceLocs, [](outliner::Candidate &C) { + return C.CallConstructionID == MachineOutlinerDefault; + }); + } } // Does every candidate's MBB contain a call? If so, then we might have a // call in the range. if (FlagsSetInAll & MachineOutlinerMBBFlags::HasCalls) { + bool SaveLRRequired = false; // check if the range contains a call. These require a save + restore of // the link register. if (std::any_of(FirstCand.front(), FirstCand.back(), [](const MachineInstr &MI) { return MI.isCall(); })) - NumBytesToCreateFrame += Costs.SaveRestoreLROnStack; + SaveLRRequired = true; // Handle the last instruction separately. If it is tail call, then the // last instruction is a call, we don't want to save + restore in this @@ -5909,7 +5933,15 @@ // consider this as well. else if (FrameID != MachineOutlinerThunk && FrameID != MachineOutlinerTailCall && FirstCand.back()->isCall()) + SaveLRRequired = true; + + if (SaveLRRequired) { + if (Subtarget.isThumb1Only() && !canSaveLRToStack(FirstCand)) { + return outliner::OutlinedFunction(); + } + NumBytesToCreateFrame += Costs.SaveRestoreLROnStack; + } } return outliner::OutlinedFunction(RepeatedSequenceLocs, SequenceSize, @@ -6037,10 +6069,6 @@ if (F.hasSection()) return false; - // FIXME: Thumb1 outlining is not handled - if (MF.getInfo()->isThumb1OnlyFunction()) - return false; - // It's safe to outline from MF. return true; } @@ -6162,7 +6190,7 @@ if (MI.isCall()) { // Get the function associated with the call. Look at each operand and find - // the one that represents the calle and get its name. + // the one that represents the callee and get its name. const Function *Callee = nullptr; for (const MachineOperand &MOP : MI.operands()) { if (MOP.isGlobal()) { @@ -6359,6 +6387,32 @@ .setMIFlags(MachineInstr::FrameDestroy); } +MachineBasicBlock::iterator ARMBaseInstrInfo::saveAndRestoreLR( + MachineBasicBlock &MBB, MachineBasicBlock::iterator &Front, + MachineBasicBlock::iterator &Back, const outliner::Candidate &C) const { + const ARMFunctionInfo &AFI = *MBB.getParent()->getInfo(); + + // Insert a save before the outlined region + saveLROnStack(MBB, Front); + if (!AFI.isLRSpilled()) + emitCFIForLRSaveOnStack(MBB, Front); + + // Create a copy of the Front iterator where it is now. Advance it backwards, + // so it does not progress, in the case where Front == Back. + MachineBasicBlock::iterator AfterLRSave = Front; + AfterLRSave--; + + // Insert a restore before the terminator for the function. Restore LR. + restoreLRFromStack(MBB, Back); + if (!AFI.isLRSpilled()) + emitCFIForLRRestoreFromStack(MBB, Back); + + // Move this iterator forward one, to where Front used to be, in case Front + // has also changed. + AfterLRSave++; + return AfterLRSave; +} + void ARMBaseInstrInfo::buildOutlinedFrame( MachineBasicBlock &MBB, MachineFunction &MF, const outliner::OutlinedFunction &OF) const { @@ -6398,19 +6452,15 @@ if (!MBB.isLiveIn(ARM::LR)) MBB.addLiveIn(ARM::LR); - // Insert a save before the outlined region - saveLROnStack(MBB, It); - emitCFIForLRSaveOnStack(MBB, It); - // Fix up the instructions in the range, since we're going to modify the // stack. assert(OF.FrameConstructionID != MachineOutlinerDefault && "Can only fix up stack references once"); fixupPostOutline(MBB); - // Insert a restore before the terminator for the function. Restore LR. - restoreLRFromStack(MBB, Et); - emitCFIForLRRestoreFromStack(MBB, Et); + // Insert a save of the LR before the outlined region, and restore it + // before the terminator for the function. + saveAndRestoreLR(MBB, It, Et, OF.Candidates[0]); } // If this is a tail call outlined function, then there's already a return. @@ -6486,17 +6536,20 @@ It--; return CallPt; } - // We have the default case. Save and restore from SP. + + // Save and restore the LR from the stack if (!MBB.isLiveIn(ARM::LR)) MBB.addLiveIn(ARM::LR); - saveLROnStack(MBB, It); - if (!AFI.isLRSpilled()) - emitCFIForLRSaveOnStack(MBB, It); - CallPt = MBB.insert(It, CallMIB); - restoreLRFromStack(MBB, It); - if (!AFI.isLRSpilled()) - emitCFIForLRRestoreFromStack(MBB, It); + + MachineBasicBlock::iterator AfterLRSave = saveAndRestoreLR(MBB, It, It, C); + + // Insert the call right after the save, but before the restore + CallPt = MBB.insert(AfterLRSave, CallMIB); + + // Move the pointer backwards, so the correct instructions get deleted from + // the basic block. It--; + return CallPt; } @@ -6528,4 +6581,3 @@ return (MF.getSubtarget().hardenSlsBlr()) ? ARM::BLX_pred_noip : ARM::BLX_pred; } - diff --git a/llvm/lib/Target/ARM/Thumb1InstrInfo.h b/llvm/lib/Target/ARM/Thumb1InstrInfo.h --- a/llvm/lib/Target/ARM/Thumb1InstrInfo.h +++ b/llvm/lib/Target/ARM/Thumb1InstrInfo.h @@ -53,6 +53,52 @@ const TargetRegisterInfo *TRI) const override; bool canCopyGluedNodeDuringSchedule(SDNode *N) const override; + +protected: + MachineBasicBlock::iterator + saveAndRestoreLR(MachineBasicBlock &MBB, MachineBasicBlock::iterator &Front, + MachineBasicBlock::iterator &Back, + const outliner::Candidate &C) const override; + + /// For Thumb1, we can only save to the stack if there's an unused register + /// in the repeated sequence for outlining candidate \p C. + bool canSaveLRToStack(const outliner::Candidate &C) const override { + return findUnusedLowRegister(C, nullptr).id() != 0u; + } + +private: + /// Returns an unused general-purpose register which can be used for + /// saving the LR to the stack, if one exists. Returns 0 otherwise. + Register findUnusedLowRegister(const outliner::Candidate &C, + bool *RegIsLive) const; + Register findUnusedLowRegister(MachineFunction *MF, + LiveRegUnits UsedInSequence, + LiveRegUnits BeforeSequence, + bool *RegIsLive) const; + + /// Push the LR and an auxiliary register onto the stack, making sure the LR + /// is at the top of the stack. + void saveLRAndRegOnStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &It, Register Reg) const; + + /// Emit CFI instructions for the above operation, to detail where on the + /// stack the LR and the auxiliary register are stored. + void emitCFIForLRAndRegSaveOnStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator It, + Register Reg) const; + + // Pop the LR off the stack, and move it into the appropriate place. Then, + // restore the value of the auxiliary register. + void restoreLRAndRegFromStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &It, + Register Reg) const; + + // Emit CFI instructions for freeing up stack space and restoring the LR and + // the auxiliary register. + void emitCFIForLRAndRegRestoreFromStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator It, + Register Reg) const; + private: void expandLoadStackGuard(MachineBasicBlock::iterator MI) const override; }; diff --git a/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp b/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp --- a/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp +++ b/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp @@ -12,9 +12,11 @@ #include "Thumb1InstrInfo.h" #include "ARMSubtarget.h" +#include "ARMMachineFunctionInfo.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstBuilder.h" @@ -153,3 +155,227 @@ return false; } + +void Thumb1InstrInfo::saveLRAndRegOnStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &It, + Register Reg) const { + // Free up an unused register + BuildMI(MBB, It, DebugLoc(), get(ARM::tPUSH)) + .add(predOps(ARMCC::AL)) + .addReg(Reg); + + // Ensure that the LR is pushed on top of the previously free register, by + // pushing it in a separate instruction. + BuildMI(MBB, It, DebugLoc(), get(ARM::tPUSH)) + .add(predOps(ARMCC::AL)) + .addReg(ARM::LR); +} + +void Thumb1InstrInfo::emitCFIForLRAndRegSaveOnStack( + MachineBasicBlock &MBB, MachineBasicBlock::iterator It, + Register Reg) const { + MachineFunction &MF = *MBB.getParent(); + const MCRegisterInfo *MRI = getSubtarget().getRegisterInfo(); + int BytesPerReg = 4; + + // Add a CFI saying the stack was moved down. + int64_t StackPosEntry = + MF.addFrameInst( + MCCFIInstruction::cfiDefCfaOffset(nullptr, BytesPerReg * 2)); + BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) + .addCFIIndex(StackPosEntry) + .setMIFlags(MachineInstr::FrameSetup); + + // Add a CFI saying that the LR that we want to find is now higher than + // before. + unsigned DwarfLR = MRI->getDwarfRegNum(ARM::LR, true); + int64_t LRPosEntry = MF.addFrameInst( + MCCFIInstruction::createOffset(nullptr, DwarfLR, BytesPerReg * -2)); + BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) + .addCFIIndex(LRPosEntry) + .setMIFlags(MachineInstr::FrameSetup); + + // If we saved an additional register, also note that the value of that + // register has been saved to the stack as well. + if (Reg != ARM::LR) { + unsigned SavedReg = MRI->getDwarfRegNum(Reg, true); + int64_t SavedRegPosEntry = MF.addFrameInst(MCCFIInstruction::createOffset( + nullptr, SavedReg, BytesPerReg * -1)); + BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) + .addCFIIndex(SavedRegPosEntry) + .setMIFlags(MachineInstr::FrameSetup); + } +} + +void Thumb1InstrInfo::restoreLRAndRegFromStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &It, + Register Reg) const { + // The LR should be on the top of the stack, so we pop it off into the freed + // register. + BuildMI(MBB, It, DebugLoc(), get(ARM::tPOP)) + .add(predOps(ARMCC::AL)) + .addReg(Reg, RegState::Define); + + // Move the LR into the appropriate location. + copyPhysReg(MBB, It, DebugLoc(), ARM::LR, Reg, true); + + // Pop the original value of the freed register. + BuildMI(MBB, It, DebugLoc(), get(ARM::tPOP)) + .add(predOps(ARMCC::AL)) + .addReg(Reg, RegState::Define); +} + +void Thumb1InstrInfo::emitCFIForLRAndRegRestoreFromStack( + MachineBasicBlock &MBB, MachineBasicBlock::iterator It, + Register Reg) const { + // Now stack has moved back up... + MachineFunction &MF = *MBB.getParent(); + const MCRegisterInfo *MRI = getSubtarget().getRegisterInfo(); + + int64_t StackPosEntry = + MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, 0)); + BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) + .addCFIIndex(StackPosEntry) + .setMIFlags(MachineInstr::FrameDestroy); + + // ... and we have restored LR. + unsigned DwarfLR = MRI->getDwarfRegNum(ARM::LR, true); + int64_t LRPosEntry = + MF.addFrameInst(MCCFIInstruction::createRestore(nullptr, DwarfLR)); + BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) + .addCFIIndex(LRPosEntry) + .setMIFlags(MachineInstr::FrameDestroy); + + // ... and we have restored the freed register, if we didn't push the LR + // twice. + if (Reg != ARM::LR) { + unsigned SavedReg = MRI->getDwarfRegNum(Reg, true); + int64_t SavedRegPosEntry = + MF.addFrameInst(MCCFIInstruction::createRestore(nullptr, SavedReg)); + BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) + .addCFIIndex(SavedRegPosEntry) + .setMIFlags(MachineInstr::FrameDestroy); + } +} + +Register +Thumb1InstrInfo::findUnusedLowRegister(MachineFunction *MF, + LiveRegUnits UsedInSequence, + LiveRegUnits BeforeSequence, + bool *RegIsLive) const { + const ARMBaseRegisterInfo *ARI = static_cast( + MF->getSubtarget().getRegisterInfo()); + + BitVector regsReserved = ARI->getReservedRegs(*MF); + + // Check if there is an available register across the sequence that we can + // use. + Register DeadReg = 0u; + + for (Register Reg : ARM::rGPRRegClass) { + if (!(Reg < regsReserved.size() && regsReserved.test(Reg)) && + Reg != ARM::LR && // LR is not reserved, but don't use it. + Reg != ARM::R12 && // R12 is not guaranteed to be preserved. + isARMLowRegister(Reg) && // Pop's destination register must be Lo + UsedInSequence.available(Reg)) { + DeadReg = Reg; + + // We would prefer the register be def'd, so there's less problems + // regarding an undefined register being pushed onto the stack. + if (!BeforeSequence.available(Reg)) { + if (RegIsLive) *RegIsLive = true; + return Reg; + } + } + } + + // If no def'd registers are found, notify the caller and return any of the + // undefined registers found. + if (RegIsLive) *RegIsLive = false; + return DeadReg; +} + +Register Thumb1InstrInfo::findUnusedLowRegister(const outliner::Candidate &C, + bool *RegIsLive) const { + assert(C.LRUWasSet && + "Outlining Candidate LRU information wasn't initialized."); + return findUnusedLowRegister(C.getMF(), + C.UsedInSequence, C.BeforeSequence, + RegIsLive); +} + + +/// Thumb1 will need to save and restore the LR differently than Thumb2 and +/// ARM32. The additional requirements are extracted from an outlining +/// candidate, as well as a region of instructions specified by \p Front and \p +/// Back. Requirements for saving and restoring the LR are that the region of +/// instructions must have an unused register. If there exists such a register, +/// say R4, then to save and restore the LR, we do the following: + +/// PUSH R4 ;; Free up an auxilary register +/// PUSH LR ;; LR must go on the top of the stack + +/// ...... ;; Original sequence of instructions + +/// POP R4 ;; Pop LR off the stack into R4 +/// ;; (Cannot be directly popped into LR) +/// MOV LR, R4 ;; Move LR into the correct register +/// POP R4 ;; Pop the original value of R4 into R4 + +/// The above sequence requires R4 to be live before the sequence. If not, LLVM +/// will complain that an undef'd physical register is being used. In situations +/// where all unused registers are also undefined, just push the LR twice, to +/// conform to the ARM calling convention of 8-byte stack alignment. +MachineBasicBlock::iterator +Thumb1InstrInfo::saveAndRestoreLR(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &Front, + MachineBasicBlock::iterator &Back, + const outliner::Candidate &C) const { + const ARMFunctionInfo &AFI = *MBB.getParent()->getInfo(); + + // Find a register that has no uses in the repeated sequence + bool LiveReg = false; + Register FreeReg = 0u; + + // If we're inserting a call into an outlining candidate, a lot of liveness + // information has already been computed. Just pass the candidate. + if(C.front() == Front) + FreeReg = findUnusedLowRegister(C, &LiveReg); + else { + // Saving the LR in an outlining frame needs a recomputed BeforeSequence + // LRU. The UsedInSequence can be reused. + LiveRegUnits BeforeSequence; + BeforeSequence.init(*getSubtarget().getRegisterInfo()); + BeforeSequence.addLiveIns(MBB); + std::for_each(MBB.begin(), Front, + [&BeforeSequence](MachineInstr &MI) { BeforeSequence.accumulate(MI); }); + + FreeReg = findUnusedLowRegister(MBB.getParent(), + C.UsedInSequence, BeforeSequence, + &LiveReg); + } + + // This register might not be def'd. If it is not def'd, then we will push + // the LR twice. + Register RegToSave = LiveReg ? FreeReg : ARM::LR; + assert(FreeReg != 0 && "No callee-saved register available?"); + + // Save the LR and an additional register. + saveLRAndRegOnStack(MBB, Front, RegToSave); + if (!AFI.isLRSpilled()) + emitCFIForLRAndRegSaveOnStack(MBB, Front, RegToSave); + + // Keep a copy of the front, and progress it backwards, in case Front == + // Back. + MachineBasicBlock::iterator AfterLRSave = Front; + AfterLRSave--; + + // Restore the LR and the additional register. + restoreLRAndRegFromStack(MBB, Back, FreeReg); + if (!AFI.isLRSpilled()) + emitCFIForLRAndRegRestoreFromStack(MBB, Back, RegToSave); + + // Move this forward, back to where Front used to be, or still is. + AfterLRSave++; + return AfterLRSave; +} diff --git a/llvm/test/CodeGen/ARM/machine-outliner-default.mir b/llvm/test/CodeGen/ARM/machine-outliner-default.mir --- a/llvm/test/CodeGen/ARM/machine-outliner-default.mir +++ b/llvm/test/CodeGen/ARM/machine-outliner-default.mir @@ -4,11 +4,14 @@ --- | define void @outline_default_arm() #0 { ret void } - define void @outline_default_thumb() #1 { ret void } + define void @outline_default_thumb2() #1 { ret void } + define void @outline_default_thumb1() #2 { ret void } + define void @outline_default_no_low_registers_thumb1() #2 { ret void } declare void @bar() attributes #0 = { minsize optsize } attributes #1 = { minsize optsize "target-features"="+armv7-a,+thumb-mode" } + attributes #2 = { minsize optsize "target-features"="+armv6-m,+thumb-mode" } ... --- @@ -66,10 +69,10 @@ ... --- -name: outline_default_thumb +name: outline_default_thumb2 tracksRegLiveness: true body: | - ; CHECK-LABEL: name: outline_default_thumb + ; CHECK-LABEL: name: outline_default_thumb2 ; CHECK: bb.0: ; CHECK: liveins: $lr ; CHECK: early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg @@ -111,6 +114,150 @@ liveins: $lr, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 $r2 = tMOVr $lr, 14, $noreg tBX_RET 14, $noreg +... +--- + +name: outline_default_thumb1 +tracksRegLiveness: true +body: | + ; CHECK-LABEL: name: outline_default_thumb1 + ; CHECK: bb.0: + ; CHECK: liveins: $lr + ; CHECK: tPUSH 14 /* CC::al */, $noreg, $lr, implicit-def $sp, implicit $sp + ; CHECK: tPUSH 14 /* CC::al */, $noreg, $lr, implicit-def $sp, implicit $sp + ; CHECK: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_2 + ; CHECK: tPOP 14 /* CC::al */, $noreg, def $r7, implicit-def $sp, implicit $sp + ; CHECK: $lr = tMOVr killed $r7, 14 /* CC::al */, $noreg + ; CHECK: tPOP 14 /* CC::al */, $noreg, def $r7, implicit-def $sp, implicit $sp + ; CHECK: bb.1: + ; CHECK: liveins: $lr, $r0, $r1, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 + ; CHECK: tPUSH 14 /* CC::al */, $noreg, $r5, implicit-def $sp, implicit $sp + ; CHECK: tPUSH 14 /* CC::al */, $noreg, $lr, implicit-def $sp, implicit $sp + ; CHECK: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_2 + ; CHECK: tPOP 14 /* CC::al */, $noreg, def $r5, implicit-def $sp, implicit $sp + ; CHECK: $lr = tMOVr killed $r5, 14 /* CC::al */, $noreg + ; CHECK: tPOP 14 /* CC::al */, $noreg, def $r5, implicit-def $sp, implicit $sp + ; CHECK: bb.2: + ; CHECK: liveins: $lr, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 + ; CHECK: tPUSH 14 /* CC::al */, $noreg, $r5, implicit-def $sp, implicit $sp + ; CHECK: tPUSH 14 /* CC::al */, $noreg, $lr, implicit-def $sp, implicit $sp + ; CHECK: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_2 + ; CHECK: tPOP 14 /* CC::al */, $noreg, def $r5, implicit-def $sp, implicit $sp + ; CHECK: $lr = tMOVr killed $r5, 14 /* CC::al */, $noreg + ; CHECK: tPOP 14 /* CC::al */, $noreg, def $r5, implicit-def $sp, implicit $sp + ; CHECK: bb.3: + ; CHECK: liveins: $lr, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 + ; CHECK: $r2 = tMOVr $lr, 14 /* CC::al */, $noreg + ; CHECK: tBX_RET 14 /* CC::al */, $noreg + bb.0: + liveins: $lr + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r0, $cpsr = tMOVi8 2, 14, $noreg + $r1, $cpsr = tMOVi8 3, 14, $noreg + $r1, $cpsr = tMOVi8 4, 14, $noreg + $r2, $cpsr = tMOVi8 5, 14, $noreg + $r2, $cpsr = tMOVi8 6, 14, $noreg + $r3, $cpsr = tMOVi8 7, 14, $noreg + $r3, $cpsr = tMOVi8 8, 14, $noreg + $r4, $cpsr = tMOVi8 9, 14, $noreg + $r4, $cpsr = tMOVi8 10, 14, $noreg + $r0, $cpsr = tMOVi8 11, 14, $noreg + $r0, $cpsr = tMOVi8 12, 14, $noreg + bb.1: + liveins: $lr, $r0, $r1, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r0, $cpsr = tMOVi8 2, 14, $noreg + $r1, $cpsr = tMOVi8 3, 14, $noreg + $r1, $cpsr = tMOVi8 4, 14, $noreg + $r2, $cpsr = tMOVi8 5, 14, $noreg + $r2, $cpsr = tMOVi8 6, 14, $noreg + $r3, $cpsr = tMOVi8 7, 14, $noreg + $r3, $cpsr = tMOVi8 8, 14, $noreg + $r4, $cpsr = tMOVi8 9, 14, $noreg + $r4, $cpsr = tMOVi8 10, 14, $noreg + $r0, $cpsr = tMOVi8 11, 14, $noreg + $r0, $cpsr = tMOVi8 12, 14, $noreg + bb.2: + liveins: $lr, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r0, $cpsr = tMOVi8 2, 14, $noreg + $r1, $cpsr = tMOVi8 3, 14, $noreg + $r1, $cpsr = tMOVi8 4, 14, $noreg + $r2, $cpsr = tMOVi8 5, 14, $noreg + $r2, $cpsr = tMOVi8 6, 14, $noreg + $r3, $cpsr = tMOVi8 7, 14, $noreg + $r3, $cpsr = tMOVi8 8, 14, $noreg + $r4, $cpsr = tMOVi8 9, 14, $noreg + $r4, $cpsr = tMOVi8 10, 14, $noreg + $r0, $cpsr = tMOVi8 11, 14, $noreg + $r0, $cpsr = tMOVi8 12, 14, $noreg + bb.3: + liveins: $lr, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 + $r2 = tMOVr $lr, 14, $noreg + tBX_RET 14, $noreg +... +--- + +name: outline_default_no_low_registers_thumb1 +tracksRegLiveness: true +body: | + ; CHECK-LABEL: name: outline_default_no_low_registers_thumb1 + ; CHECK: bb.0: + ; CHECK: liveins: $lr + ; CHECK: $r0, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r1, $cpsr = tMOVi8 2, 14 /* CC::al */, $noreg + ; CHECK: $r2, $cpsr = tMOVi8 3, 14 /* CC::al */, $noreg + ; CHECK: $r3, $cpsr = tMOVi8 4, 14 /* CC::al */, $noreg + ; CHECK: $r4, $cpsr = tMOVi8 5, 14 /* CC::al */, $noreg + ; CHECK: $r5, $cpsr = tMOVi8 6, 14 /* CC::al */, $noreg + ; CHECK: $r6, $cpsr = tMOVi8 7, 14 /* CC::al */, $noreg + ; CHECK: $r7, $cpsr = tMOVi8 8, 14 /* CC::al */, $noreg + ; CHECK: $r0, $cpsr = tMOVi8 9, 14 /* CC::al */, $noreg + ; CHECK: $r1, $cpsr = tMOVi8 10, 14 /* CC::al */, $noreg + ; CHECK: bb.1: + ; CHECK: liveins: $lr, $r8, $r9, $r10, $r11 + ; CHECK: $r0, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r1, $cpsr = tMOVi8 2, 14 /* CC::al */, $noreg + ; CHECK: $r2, $cpsr = tMOVi8 3, 14 /* CC::al */, $noreg + ; CHECK: $r3, $cpsr = tMOVi8 4, 14 /* CC::al */, $noreg + ; CHECK: $r4, $cpsr = tMOVi8 5, 14 /* CC::al */, $noreg + ; CHECK: $r5, $cpsr = tMOVi8 6, 14 /* CC::al */, $noreg + ; CHECK: $r6, $cpsr = tMOVi8 7, 14 /* CC::al */, $noreg + ; CHECK: $r7, $cpsr = tMOVi8 8, 14 /* CC::al */, $noreg + ; CHECK: $r0, $cpsr = tMOVi8 9, 14 /* CC::al */, $noreg + ; CHECK: $r1, $cpsr = tMOVi8 10, 14 /* CC::al */, $noreg + ; CHECK: bb.2: + ; CHECK: liveins: $lr, $r8, $r9, $r10, $r11 + ; CHECK: $r2 = tMOVr $lr, 14 /* CC::al */, $noreg + ; CHECK: tBX_RET 14 /* CC::al */, $noreg + bb.0: + liveins: $lr + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r1, $cpsr = tMOVi8 2, 14, $noreg + $r2, $cpsr = tMOVi8 3, 14, $noreg + $r3, $cpsr = tMOVi8 4, 14, $noreg + $r4, $cpsr = tMOVi8 5, 14, $noreg + $r5, $cpsr = tMOVi8 6, 14, $noreg + $r6, $cpsr = tMOVi8 7, 14, $noreg + $r7, $cpsr = tMOVi8 8, 14, $noreg + $r0, $cpsr = tMOVi8 9, 14, $noreg + $r1, $cpsr = tMOVi8 10, 14, $noreg + bb.1: + liveins: $lr, $r8, $r9, $r10, $r11 + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r1, $cpsr = tMOVi8 2, 14, $noreg + $r2, $cpsr = tMOVi8 3, 14, $noreg + $r3, $cpsr = tMOVi8 4, 14, $noreg + $r4, $cpsr = tMOVi8 5, 14, $noreg + $r5, $cpsr = tMOVi8 6, 14, $noreg + $r6, $cpsr = tMOVi8 7, 14, $noreg + $r7, $cpsr = tMOVi8 8, 14, $noreg + $r0, $cpsr = tMOVi8 9, 14, $noreg + $r1, $cpsr = tMOVi8 10, 14, $noreg + bb.2: + liveins: $lr, $r8, $r9, $r10, $r11 + $r2 = tMOVr $lr, 14, $noreg + tBX_RET 14, $noreg ; CHECK-LABEL: name: OUTLINED_FUNCTION_0 ; CHECK: bb.0: @@ -132,5 +279,17 @@ ; CHECK: $r3 = t2MOVi 1, 14 /* CC::al */, $noreg, $noreg ; CHECK: tBX_RET 14 /* CC::al */, $noreg - - + ; CHECK-LABEL: name: OUTLINED_FUNCTION_2 + ; CHECK: bb.0: + ; CHECK: liveins: $lr, $r11, $r5, $r6, $r7, $r8, $r9, $r10 + ; CHECK: $r0, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r0, $cpsr = tMOVi8 2, 14 /* CC::al */, $noreg + ; CHECK: $r1, $cpsr = tMOVi8 3, 14 /* CC::al */, $noreg + ; CHECK: $r1, $cpsr = tMOVi8 4, 14 /* CC::al */, $noreg + ; CHECK: $r2, $cpsr = tMOVi8 5, 14 /* CC::al */, $noreg + ; CHECK: $r2, $cpsr = tMOVi8 6, 14 /* CC::al */, $noreg + ; CHECK: $r3, $cpsr = tMOVi8 7, 14 /* CC::al */, $noreg + ; CHECK: $r3, $cpsr = tMOVi8 8, 14 /* CC::al */, $noreg + ; CHECK: $r4, $cpsr = tMOVi8 9, 14 /* CC::al */, $noreg + ; CHECK: $r4, $cpsr = tMOVi8 10, 14 /* CC::al */, $noreg + ; CHECK: tBX_RET 14 /* CC::al */, $noreg diff --git a/llvm/test/CodeGen/ARM/machine-outliner-lr-regsave.mir b/llvm/test/CodeGen/ARM/machine-outliner-lr-regsave.mir --- a/llvm/test/CodeGen/ARM/machine-outliner-lr-regsave.mir +++ b/llvm/test/CodeGen/ARM/machine-outliner-lr-regsave.mir @@ -4,11 +4,12 @@ --- | define void @outline_save_reg_arm() #0 { ret void } - define void @outline_save_reg_thumb() #1 { ret void } - declare void @z() + define void @outline_save_reg_thumb2() #1 { ret void } + define void @outline_save_reg_thumb1() #2 { ret void } attributes #0 = { minsize optsize } attributes #1 = { minsize optsize "target-features"="+armv7-a,+thumb-mode" } + attributes #2 = { minsize optsize "target-features"="+armv6-m,+thumb-mode" } ... --- @@ -79,10 +80,10 @@ ... --- -name: outline_save_reg_thumb +name: outline_save_reg_thumb2 tracksRegLiveness: true body: | - ; CHECK-LABEL: name: outline_save_reg_thumb + ; CHECK-LABEL: name: outline_save_reg_thumb2 ; CHECK: bb.0: ; CHECK: liveins: $lr ; CHECK: $r6 = tMOVr killed $lr, 14 /* CC::al */, $noreg @@ -143,6 +144,80 @@ liveins: $lr $r2 = tMOVr $lr, 14, $noreg tBX_RET 14, $noreg +... +--- + +name: outline_save_reg_thumb1 +tracksRegLiveness: true +body: | + ; CHECK-LABEL: name: outline_save_reg_thumb1 + ; CHECK: bb.0: + ; CHECK: liveins: $lr + ; CHECK: $r7 = tMOVr killed $lr, 14 /* CC::al */, $noreg + ; CHECK: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_2 + ; CHECK: $lr = tMOVr killed $r7, 14 /* CC::al */, $noreg + ; CHECK: bb.1: + ; CHECK: liveins: $lr + ; CHECK: $r7 = tMOVr killed $lr, 14 /* CC::al */, $noreg + ; CHECK: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_2 + ; CHECK: $lr = tMOVr killed $r7, 14 /* CC::al */, $noreg + ; CHECK: bb.2: + ; CHECK: liveins: $lr + ; CHECK: tPUSH 14 /* CC::al */, $noreg, $lr, implicit-def $sp, implicit $sp + ; CHECK: tPUSH 14 /* CC::al */, $noreg, $lr, implicit-def $sp, implicit $sp + ; CHECK: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_2 + ; CHECK: tPOP 14 /* CC::al */, $noreg, def $r7, implicit-def $sp, implicit $sp + ; CHECK: $lr = tMOVr killed $r7, 14 /* CC::al */, $noreg + ; CHECK: tPOP 14 /* CC::al */, $noreg, def $r7, implicit-def $sp, implicit $sp + ; CHECK: bb.3: + ; CHECK: liveins: $lr, $r0, $r6, $r7 + ; CHECK: $r7 = tMOVr killed $lr, 14 /* CC::al */, $noreg + ; CHECK: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_2 + ; CHECK: $lr = tMOVr killed $r7, 14 /* CC::al */, $noreg + ; CHECK: bb.4: + ; CHECK: liveins: $lr + ; CHECK: $r2 = tMOVr $lr, 14 /* CC::al */, $noreg + ; CHECK: tBX_RET 14 /* CC::al */, $noreg + bb.0: + liveins: $lr + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r1, $cpsr = tMOVi8 1, 14, $noreg + $r2, $cpsr = tMOVi8 1, 14, $noreg + $r3, $cpsr = tMOVi8 1, 14, $noreg + $r4, $cpsr = tMOVi8 1, 14, $noreg + $r5, $cpsr = tMOVi8 1, 14, $noreg + $r6, $cpsr = tMOVi8 1, 14, $noreg + bb.1: + liveins: $lr + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r1, $cpsr = tMOVi8 1, 14, $noreg + $r2, $cpsr = tMOVi8 1, 14, $noreg + $r3, $cpsr = tMOVi8 1, 14, $noreg + $r4, $cpsr = tMOVi8 1, 14, $noreg + $r5, $cpsr = tMOVi8 1, 14, $noreg + $r6, $cpsr = tMOVi8 1, 14, $noreg + bb.2: + liveins: $lr + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r1, $cpsr = tMOVi8 1, 14, $noreg + $r2, $cpsr = tMOVi8 1, 14, $noreg + $r3, $cpsr = tMOVi8 1, 14, $noreg + $r4, $cpsr = tMOVi8 1, 14, $noreg + $r5, $cpsr = tMOVi8 1, 14, $noreg + $r6, $cpsr = tMOVi8 1, 14, $noreg + bb.3: + liveins: $lr, $r0, $r6, $r7, $r8, $r9, $r10, $r11 + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r1, $cpsr = tMOVi8 1, 14, $noreg + $r2, $cpsr = tMOVi8 1, 14, $noreg + $r3, $cpsr = tMOVi8 1, 14, $noreg + $r4, $cpsr = tMOVi8 1, 14, $noreg + $r5, $cpsr = tMOVi8 1, 14, $noreg + $r6, $cpsr = tMOVi8 1, 14, $noreg + bb.4: + liveins: $lr + $r2 = tMOVr $lr, 14, $noreg + tBX_RET 14, $noreg ; CHECK-LABEL: name: OUTLINED_FUNCTION_0 ; CHECK: bb.0: @@ -166,3 +241,14 @@ ; CHECK: $r5 = MOVi 1, 14 /* CC::al */, $noreg, $noreg ; CHECK: MOVPCLR 14 /* CC::al */, $noreg + ; CHECK-LABEL: name: OUTLINED_FUNCTION_2 + ; CHECK: bb.0: + ; CHECK: liveins: $lr + ; CHECK: $r0, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r1, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r2, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r3, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r4, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r5, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r6, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: tBX_RET 14 /* CC::al */, $noreg diff --git a/llvm/test/CodeGen/ARM/machine-outliner-no-lr-save.mir b/llvm/test/CodeGen/ARM/machine-outliner-no-lr-save.mir --- a/llvm/test/CodeGen/ARM/machine-outliner-no-lr-save.mir +++ b/llvm/test/CodeGen/ARM/machine-outliner-no-lr-save.mir @@ -4,12 +4,14 @@ --- | define void @outline_no_save_ok_arm() #0 { ret void } - define void @outline_no_save_ok_thumb() #1 { ret void } + define void @outline_no_save_ok_thumb2() #1 { ret void } + define void @outline_no_save_ok_thumb1() #2 { ret void } declare void @foo() attributes #0 = { minsize optsize } attributes #1 = { minsize optsize "target-features"="+armv7-a,+thumb-mode" } + attributes #2 = { minsize optsize "target-features"="+armv6-m,+thumb-mode" } ... --- @@ -40,10 +42,10 @@ ... --- -name: outline_no_save_ok_thumb +name: outline_no_save_ok_thumb2 tracksRegLiveness: true body: | - ; CHECK-LABEL: name: outline_no_save_ok_thumb + ; CHECK-LABEL: name: outline_no_save_ok_thumb2 ; CHECK: bb.0: ; CHECK: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_0 ; CHECK: bb.1: @@ -65,6 +67,36 @@ bb.2: tBX_RET 14, $noreg +... +--- + +name: outline_no_save_ok_thumb1 +tracksRegLiveness: true +body: | + ; CHECK-LABEL: name: outline_no_save_ok_thumb1 + ; CHECK: bb.0: + ; CHECK: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_2 + ; CHECK: bb.1: + ; CHECK: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_2 + ; CHECK: bb.2: + ; CHECK: tBX_RET 14 /* CC::al */, $noreg + bb.0: + $r2, $cpsr = tMOVi8 1, 14, $noreg + $r2, $cpsr = tMOVi8 1, 14, $noreg + $r2, $cpsr = tMOVi8 1, 14, $noreg + $r2, $cpsr = tMOVi8 1, 14, $noreg + $r2, $cpsr = tMOVi8 1, 14, $noreg + tSTRspi $r2, $sp, 0, 14, $noreg + bb.1: + $r2, $cpsr = tMOVi8 1, 14, $noreg + $r2, $cpsr = tMOVi8 1, 14, $noreg + $r2, $cpsr = tMOVi8 1, 14, $noreg + $r2, $cpsr = tMOVi8 1, 14, $noreg + $r2, $cpsr = tMOVi8 1, 14, $noreg + tSTRspi $r2, $sp, 0, 14, $noreg + bb.2: + tBX_RET 14, $noreg + ; CHECK-LABEL: name: OUTLINED_FUNCTION_0 ; CHECK: bb.0: ; CHECK: $r2 = t2MOVi 1, 14 /* CC::al */, $noreg, $noreg @@ -82,3 +114,13 @@ ; CHECK: $r2 = MOVi 1, 14 /* CC::al */, $noreg, $noreg ; CHECK: $r3 = LDRi12 $sp, 8, 14 /* CC::al */, $noreg ; CHECK: MOVPCLR 14 /* CC::al */, $noreg + + ; CHECK-LABEL: name: OUTLINED_FUNCTION_2 + ; CHECK: bb.0: + ; CHECK: $r2, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r2, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r2, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r2, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: $r2, $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg + ; CHECK: tSTRspi $r2, $sp, 0, 14 /* CC::al */, $noreg + ; CHECK: tBX_RET 14 /* CC::al */, $noreg diff --git a/llvm/test/CodeGen/ARM/machine-outliner-tail.ll b/llvm/test/CodeGen/ARM/machine-outliner-tail.ll --- a/llvm/test/CodeGen/ARM/machine-outliner-tail.ll +++ b/llvm/test/CodeGen/ARM/machine-outliner-tail.ll @@ -1,14 +1,14 @@ ; RUN: llc -enable-machine-outliner -verify-machineinstrs -mtriple=arm-- \ ; RUN: --stop-after=machine-outliner < %s | FileCheck %s --check-prefix=ARM ; RUN: llc -enable-machine-outliner -verify-machineinstrs -mtriple=thumbv7-- \ -; RUN: --stop-after=machine-outliner < %s | FileCheck %s --check-prefix=THUMB +; RUN: --stop-after=machine-outliner < %s | FileCheck %s --check-prefix=THUMB2 +; RUN: llc -verify-machineinstrs -mtriple=thumbv8m.main \ +; RUN: --stop-after=machine-outliner < %s | FileCheck %s --check-prefix=THUMB2 ; RUN: llc -enable-machine-outliner -verify-machineinstrs \ ; RUN: -mtriple=thumbv7-apple-darwin --stop-after=machine-outliner < %s \ ; RUN: | FileCheck %s --check-prefix=MACHO ; RUN: llc -enable-machine-outliner -verify-machineinstrs -mtriple=thumbv5-- \ ; RUN: --stop-after=machine-outliner < %s | FileCheck %s --check-prefix=THUMB1 -; RUN: llc -verify-machineinstrs -mtriple=thumbv8m.main \ -; RUN: --stop-after=machine-outliner < %s | FileCheck %s --check-prefix=THUMB ; ARM-LABEL: name: OUTLINED_FUNCTION_0 ; ARM: $r0 = MOVi 1, 14 /* CC::al */, $noreg, $noreg @@ -17,12 +17,19 @@ ; ARM-NEXT: $r3 = MOVi 4, 14 /* CC::al */, $noreg, $noreg ; ARM-NEXT: TAILJMPd @z -; THUMB-LABEL: name: OUTLINED_FUNCTION_0 -; THUMB: $r0, dead $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg -; THUMB-NEXT: $r1, dead $cpsr = tMOVi8 2, 14 /* CC::al */, $noreg -; THUMB-NEXT: $r2, dead $cpsr = tMOVi8 3, 14 /* CC::al */, $noreg -; THUMB-NEXT: $r3, dead $cpsr = tMOVi8 4, 14 /* CC::al */, $noreg -; THUMB-NEXT: tTAILJMPdND @z, 14 /* CC::al */, $noreg +; THUMB2-LABEL: name: OUTLINED_FUNCTION_0 +; THUMB2: $r0, dead $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg +; THUMB2-NEXT: $r1, dead $cpsr = tMOVi8 2, 14 /* CC::al */, $noreg +; THUMB2-NEXT: $r2, dead $cpsr = tMOVi8 3, 14 /* CC::al */, $noreg +; THUMB2-NEXT: $r3, dead $cpsr = tMOVi8 4, 14 /* CC::al */, $noreg +; THUMB2-NEXT: tTAILJMPdND @z, 14 /* CC::al */, $noreg + +; THUMB1-LABEL: name: OUTLINED_FUNCTION_0 +; THUMB1: $r0, dead $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg +; THUMB1-NEXT: $r1, dead $cpsr = tMOVi8 2, 14 /* CC::al */, $noreg +; THUMB1-NEXT: $r2, dead $cpsr = tMOVi8 3, 14 /* CC::al */, $noreg +; THUMB1-NEXT: $r3, dead $cpsr = tMOVi8 4, 14 /* CC::al */, $noreg +; THUMB1-NEXT: tTAILJMPdND @z, 14 /* CC::al */, $noreg ; MACHO-LABEL: name: OUTLINED_FUNCTION_0 ; MACHO: $r0, dead $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg @@ -31,8 +38,6 @@ ; MACHO-NEXT: $r3, dead $cpsr = tMOVi8 4, 14 /* CC::al */, $noreg ; MACHO-NEXT: tTAILJMPd @z, 14 /* CC::al */, $noreg -; THUMB1-NOT: OUTLINED_FUNCTION_0 - define void @a() #0 { entry: tail call void @z(i32 1, i32 2, i32 3, i32 4) diff --git a/llvm/test/CodeGen/ARM/machine-outliner-thumb-cfi-call.mir b/llvm/test/CodeGen/ARM/machine-outliner-thumb-cfi-call.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/ARM/machine-outliner-thumb-cfi-call.mir @@ -0,0 +1,144 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -mtriple=thumbv6-- -run-pass=machine-outliner -verify-machineinstrs \ +# RUN: %s -o - | FileCheck %s --check-prefixes=THUMB1,ALL +# RUN: llc -mtriple=thumbv7-- -run-pass=machine-outliner -verify-machineinstrs \ +# RUN: %s -o - | FileCheck %s --check-prefixes=THUMB2,ALL + +--- | + define void @call_save_lr_to_stack() #0 { ret void } + define void @call_save_lr_to_register() #0 { ret void } + + attributes #0 = { minsize optsize } +... +--- + +name: call_save_lr_to_stack +tracksRegLiveness: true +body: | + bb.0: + liveins: $lr + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r0, $cpsr = tMOVi8 2, 14, $noreg + $r1, $cpsr = tMOVi8 3, 14, $noreg + $r1, $cpsr = tMOVi8 4, 14, $noreg + $r2, $cpsr = tMOVi8 5, 14, $noreg + $r2, $cpsr = tMOVi8 6, 14, $noreg + $r3, $cpsr = tMOVi8 7, 14, $noreg + $r3, $cpsr = tMOVi8 8, 14, $noreg + $r4, $cpsr = tMOVi8 9, 14, $noreg + $r4, $cpsr = tMOVi8 10, 14, $noreg + $r5, $cpsr = tMOVi8 11, 14, $noreg + $r5, $cpsr = tMOVi8 12, 14, $noreg + $r6, $cpsr = tMOVi8 13, 14, $noreg + $r6, $cpsr = tMOVi8 14, 14, $noreg + $r0, $cpsr = tMOVi8 15, 14, $noreg + $r0, $cpsr = tMOVi8 16, 14, $noreg + bb.1: + liveins: $lr, $r0, $r1, $r2, $r3, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r0, $cpsr = tMOVi8 2, 14, $noreg + $r1, $cpsr = tMOVi8 3, 14, $noreg + $r1, $cpsr = tMOVi8 4, 14, $noreg + $r2, $cpsr = tMOVi8 5, 14, $noreg + $r2, $cpsr = tMOVi8 6, 14, $noreg + $r3, $cpsr = tMOVi8 7, 14, $noreg + $r3, $cpsr = tMOVi8 8, 14, $noreg + $r4, $cpsr = tMOVi8 9, 14, $noreg + $r4, $cpsr = tMOVi8 10, 14, $noreg + $r5, $cpsr = tMOVi8 11, 14, $noreg + $r5, $cpsr = tMOVi8 12, 14, $noreg + $r6, $cpsr = tMOVi8 13, 14, $noreg + $r6, $cpsr = tMOVi8 14, 14, $noreg + $r0, $cpsr = tMOVi8 15, 14, $noreg + $r0, $cpsr = tMOVi8 16, 14, $noreg + bb.3: + liveins: $lr, $r0, $r1, $r2, $r3, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 + tBX_RET 14, $noreg + + + ; THUMB1-LABEL: name: call_save_lr_to_stack + ; THUMB1: bb.0: + ; THUMB1: tPUSH 14 /* CC::al */, $noreg, $lr, implicit-def $sp, implicit $sp + ; THUMB1-NEXT: tPUSH 14 /* CC::al */, $noreg, $lr, implicit-def $sp, implicit $sp + ; THUMB1-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8 + ; THUMB1-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8 + ; THUMB1: tPOP 14 /* CC::al */, $noreg, def $r7, implicit-def $sp, implicit $sp + ; THUMB1-NEXT: $lr = tMOVr killed $r7, 14 /* CC::al */, $noreg + ; THUMB1-NEXT: tPOP 14 /* CC::al */, $noreg, def $r7, implicit-def $sp, implicit $sp + ; THUMB1-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0 + ; THUMB1-NEXT: frame-destroy CFI_INSTRUCTION restore $lr + ; THUMB1: bb.1: + ; THUMB1: tPUSH 14 /* CC::al */, $noreg, $r7, implicit-def $sp, implicit $sp + ; THUMB1-NEXT: tPUSH 14 /* CC::al */, $noreg, $lr, implicit-def $sp, implicit $sp + ; THUMB1-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8 + ; THUMB1-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8 + ; THUMB1-NEXT: frame-setup CFI_INSTRUCTION offset $r7, -4 + ; THUMB1: tPOP 14 /* CC::al */, $noreg, def $r7, implicit-def $sp, implicit $sp + ; THUMB1-NEXT: $lr = tMOVr killed $r7, 14 /* CC::al */, $noreg + ; THUMB1-NEXT: tPOP 14 /* CC::al */, $noreg, def $r7, implicit-def $sp, implicit $sp + ; THUMB1-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0 + ; THUMB1-NEXT: frame-destroy CFI_INSTRUCTION restore $lr + ; THUMB1-NEXT: frame-destroy CFI_INSTRUCTION restore $r7 + + ; THUMB2-LABEL: name: call_save_lr_to_stack + ; THUMB2: bb.0: + ; THUMB2: early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg + ; THUMB2-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8 + ; THUMB2-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8 + ; THUMB2: $lr, $sp = t2LDR_POST $sp, 8, 14 /* CC::al */, $noreg + ; THUMB2-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0 + ; THUMB2-NEXT: frame-destroy CFI_INSTRUCTION restore $lr + ; THUMB2: bb.1: + ; THUMB2: early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg + ; THUMB2-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8 + ; THUMB2-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8 + ; THUMB2: $lr, $sp = t2LDR_POST $sp, 8, 14 /* CC::al */, $noreg + ; THUMB2-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0 + ; THUMB2-NEXT: frame-destroy CFI_INSTRUCTION restore $lr + +... +--- + +name: call_save_lr_to_register +tracksRegLiveness: true +body: | + bb.0: + liveins: $lr + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r0, $cpsr = tMOVi8 2, 14, $noreg + $r1, $cpsr = tMOVi8 3, 14, $noreg + $r1, $cpsr = tMOVi8 4, 14, $noreg + $r2, $cpsr = tMOVi8 5, 14, $noreg + $r2, $cpsr = tMOVi8 6, 14, $noreg + $r3, $cpsr = tMOVi8 7, 14, $noreg + $r3, $cpsr = tMOVi8 8, 14, $noreg + $r4, $cpsr = tMOVi8 9, 14, $noreg + $r4, $cpsr = tMOVi8 10, 14, $noreg + bb.1: + liveins: $lr, $r8, $r9, $r10, $r11 + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r0, $cpsr = tMOVi8 2, 14, $noreg + $r1, $cpsr = tMOVi8 3, 14, $noreg + $r1, $cpsr = tMOVi8 4, 14, $noreg + $r2, $cpsr = tMOVi8 5, 14, $noreg + $r2, $cpsr = tMOVi8 6, 14, $noreg + $r3, $cpsr = tMOVi8 7, 14, $noreg + $r3, $cpsr = tMOVi8 8, 14, $noreg + $r4, $cpsr = tMOVi8 9, 14, $noreg + $r4, $cpsr = tMOVi8 10, 14, $noreg + bb.3: + liveins: $lr, $r8, $r9, $r10, $r11 + tBX_RET 14, $noreg + + + ; ALL-LABEL: name: call_save_lr_to_register + ; ALL: bb.0: + ; ALL: $r5 = tMOVr killed $lr, 14 /* CC::al */, $noreg + ; ALL-NEXT: frame-setup CFI_INSTRUCTION register $lr, $r5 + ; ALL: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_ + ; ALL-NEXT: $lr = tMOVr killed $r5, 14 /* CC::al */, $noreg + ; ALL: bb.1: + ; ALL: $r5 = tMOVr killed $lr, 14 /* CC::al */, $noreg + ; ALL-NEXT: frame-setup CFI_INSTRUCTION register $lr, $r5 + ; ALL: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_ + ; ALL-NEXT: $lr = tMOVr killed $r5, 14 /* CC::al */, $noreg diff --git a/llvm/test/CodeGen/ARM/machine-outliner-thumb-cfi-frame.mir b/llvm/test/CodeGen/ARM/machine-outliner-thumb-cfi-frame.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/ARM/machine-outliner-thumb-cfi-frame.mir @@ -0,0 +1,73 @@ +# RUN: llc -mtriple=thumbv6-- -run-pass=prologepilog -run-pass=machine-outliner \ +# RUN: -verify-machineinstrs %s -o - | FileCheck %s --check-prefix=THUMB1 +# RUN: llc -mtriple=thumbv7-- -run-pass=prologepilog -run-pass=machine-outliner \ +# RUN: -verify-machineinstrs %s -o - | FileCheck %s --check-prefix=THUMB2 + +--- | + define void @frame_save_lr_to_stack() #0 { ret void } + define void @bar() #0 { ret void } + + attributes #0 = { minsize optsize } +... +--- + +name: bar +tracksRegLiveness: true +body: | + bb.0: + BX_RET 14, $noreg +... +--- + +name: frame_save_lr_to_stack +tracksRegLiveness: true +body: | + bb.0: + tBL 14, $noreg, @bar, implicit-def dead $lr, implicit $sp + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r1, $cpsr = tMOVi8 2, 14, $noreg + $r2, $cpsr = tMOVi8 3, 14, $noreg + $r3, $cpsr = tMOVi8 4, 14, $noreg + tBL 14, $noreg, @bar, implicit-def dead $lr, implicit $sp + $r0, $cpsr = tMOVi8 5, 14, $noreg + $r1, $cpsr = tMOVi8 6, 14, $noreg + $r2, $cpsr = tMOVi8 7, 14, $noreg + $r3, $cpsr = tMOVi8 8, 14, $noreg + bb.1: + tBL 14, $noreg, @bar, implicit-def dead $lr, implicit $sp + $r0, $cpsr = tMOVi8 1, 14, $noreg + $r1, $cpsr = tMOVi8 2, 14, $noreg + $r2, $cpsr = tMOVi8 3, 14, $noreg + $r3, $cpsr = tMOVi8 4, 14, $noreg + tBL 14, $noreg, @bar, implicit-def dead $lr, implicit $sp + $r0, $cpsr = tMOVi8 5, 14, $noreg + $r1, $cpsr = tMOVi8 6, 14, $noreg + $r2, $cpsr = tMOVi8 7, 14, $noreg + $r3, $cpsr = tMOVi8 8, 14, $noreg + bb.3: + tBX_RET 14, $noreg + + ; THUMB1-LABEL: OUTLINED_FUNCTION_0 + ; THUMB1: bb.0: + ; THUMB1: tPUSH 14 /* CC::al */, $noreg, $r4, implicit-def $sp, implicit $sp + ; THUMB1-NEXT: tPUSH 14 /* CC::al */, $noreg, $lr, implicit-def $sp, implicit $sp + ; THUMB1-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8 + ; THUMB1-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8 + ; THUMB1-NEXT: frame-setup CFI_INSTRUCTION offset $r4, -4 + ; THUMB1: tPOP 14 /* CC::al */, $noreg, def $r4, implicit-def $sp, implicit $sp + ; THUMB1-NEXT: $lr = tMOVr killed $r4, 14 /* CC::al */, $noreg + ; THUMB1-NEXT: tPOP 14 /* CC::al */, $noreg, def $r4, implicit-def $sp, implicit $sp + ; THUMB1-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0 + ; THUMB1-NEXT: frame-destroy CFI_INSTRUCTION restore $lr + ; THUMB1-NEXT: frame-destroy CFI_INSTRUCTION restore $r4 + ; THUMB1-NEXT: tBX_RET 14 /* CC::al */, $noreg + + ; THUMB2-LABEL: OUTLINED_FUNCTION_0 + ; THUMB2: bb.0: + ; THUMB2: early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg + ; THUMB2-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8 + ; THUMB2-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -8 + ; THUMB2: $lr, $sp = t2LDR_POST $sp, 8, 14 /* CC::al */, $noreg + ; THUMB2-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0 + ; THUMB2-NEXT: frame-destroy CFI_INSTRUCTION restore $lr + ; THUMB2-NEXT: tBX_RET 14 /* CC::al */, $noreg diff --git a/llvm/test/CodeGen/ARM/machine-outliner-thunk.ll b/llvm/test/CodeGen/ARM/machine-outliner-thunk.ll --- a/llvm/test/CodeGen/ARM/machine-outliner-thunk.ll +++ b/llvm/test/CodeGen/ARM/machine-outliner-thunk.ll @@ -1,14 +1,14 @@ ; RUN: llc -enable-machine-outliner -verify-machineinstrs -mtriple=armv7-- \ ; RUN: -stop-after=machine-outliner < %s | FileCheck %s --check-prefix=ARM ; RUN: llc -enable-machine-outliner -verify-machineinstrs -mtriple=thumbv7-- \ -; RUN: -stop-after=machine-outliner < %s | FileCheck %s --check-prefix=THUMB +; RUN: -stop-after=machine-outliner < %s | FileCheck %s --check-prefix=THUMB2 ; RUN: llc -enable-machine-outliner -verify-machineinstrs \ ; RUN: -mtriple=thumbv7-apple-darwin -stop-after=machine-outliner < %s \ ; RUN: | FileCheck %s --check-prefix=MACHO ; RUN: llc -enable-machine-outliner -verify-machineinstrs -mtriple=thumbv5-- \ ; RUN: --stop-after=machine-outliner < %s | FileCheck %s --check-prefix=THUMB1 ; RUN: llc -verify-machineinstrs -mtriple=thumbv8m.main \ -; RUN: --stop-after=machine-outliner < %s | FileCheck %s --check-prefix=THUMB +; RUN: --stop-after=machine-outliner < %s | FileCheck %s --check-prefix=THUMB2 declare i32 @thunk_called_fn(i32, i32, i32, i32) @@ -24,16 +24,27 @@ ; ARM-NEXT: renamable $r0 = ADDri killed renamable $r0, 8, 14 /* CC::al */, $noreg, $noreg ; ARM-NEXT: $sp = frame-destroy LDMIA_RET $sp, 14 /* CC::al */, $noreg, def $r11, def $pc, implicit killed $r0 -; THUMB-LABEL: name: a -; THUMB: bb.0.entry: -; THUMB-NEXT: liveins: $r7, $lr -; THUMB: frame-setup tPUSH 14 /* CC::al */, $noreg, killed $r7, killed $lr -; THUMB-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8 -; THUMB-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -4 -; THUMB-NEXT: frame-setup CFI_INSTRUCTION offset $r7, -8 -; THUMB-NEXT: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_0{{.*}} -; THUMB-NEXT: renamable $r0, dead $cpsr = tADDi8 killed renamable $r0, 8, 14 /* CC::al */, $noreg -; THUMB-NEXT: tPOP_RET 14 /* CC::al */, $noreg, def $r7, def $pc +; THUMB2-LABEL: name: a +; THUMB2: bb.0.entry: +; THUMB2-NEXT: liveins: $r7, $lr +; THUMB2: frame-setup tPUSH 14 /* CC::al */, $noreg, killed $r7, killed $lr +; THUMB2-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8 +; THUMB2-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -4 +; THUMB2-NEXT: frame-setup CFI_INSTRUCTION offset $r7, -8 +; THUMB2-NEXT: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_0{{.*}} +; THUMB2-NEXT: renamable $r0, dead $cpsr = tADDi8 killed renamable $r0, 8, 14 /* CC::al */, $noreg +; THUMB2-NEXT: tPOP_RET 14 /* CC::al */, $noreg, def $r7, def $pc + +; THUMB1-LABEL: name: a +; THUMB1: bb.0.entry: +; THUMB1-NEXT: liveins: $r7, $lr +; THUMB1: frame-setup tPUSH 14 /* CC::al */, $noreg, killed $r7, killed $lr +; THUMB1-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8 +; THUMB1-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -4 +; THUMB1-NEXT: frame-setup CFI_INSTRUCTION offset $r7, -8 +; THUMB1-NEXT: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_0{{.*}} +; THUMB1-NEXT: renamable $r0, dead $cpsr = tADDi8 killed renamable $r0, 8, 14 /* CC::al */, $noreg +; THUMB1-NEXT: tPOP_RET 14 /* CC::al */, $noreg, def $r7, def $pc ; MACHO-LABEL: name: a ; MACHO: bb.0.entry: @@ -46,8 +57,6 @@ ; MACHO-NEXT: $lr, $sp = frame-destroy t2LDR_POST $sp, 4, 14 /* CC::al */, $noreg ; MACHO-NEXT: tBX_RET 14 /* CC::al */, $noreg, implicit killed $r0 -; THUMB1-NOT: OUTLINED_FUNCTION_0 - entry: %call = tail call i32 @thunk_called_fn(i32 1, i32 2, i32 3, i32 4) %cx = add i32 %call, 8 @@ -66,16 +75,27 @@ ; ARM-NEXT: renamable $r0 = ADDri killed renamable $r0, 88, 14 /* CC::al */, $noreg, $noreg ; ARM-NEXT: $sp = frame-destroy LDMIA_RET $sp, 14 /* CC::al */, $noreg, def $r11, def $pc, implicit killed $r0 -; THUMB-LABEL: name: b -; THUMB: bb.0.entry: -; THUMB-NEXT: liveins: $r7, $lr -; THUMB: frame-setup tPUSH 14 /* CC::al */, $noreg, killed $r7, killed $lr -; THUMB-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8 -; THUMB-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -4 -; THUMB-NEXT: frame-setup CFI_INSTRUCTION offset $r7, -8 -; THUMB-NEXT: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_0{{.*}} -; THUMB-NEXT: renamable $r0, dead $cpsr = tADDi8 killed renamable $r0, 88, 14 /* CC::al */, $noreg -; THUMB-NEXT: tPOP_RET 14 /* CC::al */, $noreg, def $r7, def $pc +; THUMB2-LABEL: name: b +; THUMB2: bb.0.entry: +; THUMB2-NEXT: liveins: $r7, $lr +; THUMB2: frame-setup tPUSH 14 /* CC::al */, $noreg, killed $r7, killed $lr +; THUMB2-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8 +; THUMB2-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -4 +; THUMB2-NEXT: frame-setup CFI_INSTRUCTION offset $r7, -8 +; THUMB2-NEXT: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_0{{.*}} +; THUMB2-NEXT: renamable $r0, dead $cpsr = tADDi8 killed renamable $r0, 88, 14 /* CC::al */, $noreg +; THUMB2-NEXT: tPOP_RET 14 /* CC::al */, $noreg, def $r7, def $pc + +; THUMB1-LABEL: name: b +; THUMB1: bb.0.entry: +; THUMB1-NEXT: liveins: $r7, $lr +; THUMB1: frame-setup tPUSH 14 /* CC::al */, $noreg, killed $r7, killed $lr +; THUMB1-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 8 +; THUMB1-NEXT: frame-setup CFI_INSTRUCTION offset $lr, -4 +; THUMB1-NEXT: frame-setup CFI_INSTRUCTION offset $r7, -8 +; THUMB1-NEXT: tBL 14 /* CC::al */, $noreg, @OUTLINED_FUNCTION_0{{.*}} +; THUMB1-NEXT: renamable $r0, dead $cpsr = tADDi8 killed renamable $r0, 88, 14 /* CC::al */, $noreg +; THUMB1-NEXT: tPOP_RET 14 /* CC::al */, $noreg, def $r7, def $pc ; MACHO-LABEL: name: b ; MACHO: bb.0.entry: @@ -102,14 +122,23 @@ ; ARM-NEXT: $r3 = MOVi 4, 14 /* CC::al */, $noreg, $noreg ; ARM-NEXT: TAILJMPd @thunk_called_fn, implicit $sp -; THUMB-LABEL: name: OUTLINED_FUNCTION_0 -; THUMB: bb.0: -; THUMB-NEXT: liveins: $r11, $r10, $r9, $r8, $r6, $r5, $r4, $d15, $d14, $d13, $d12, $d11, $d10, $d9, $d8 -; THUMB: $r0, dead $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg -; THUMB-NEXT: $r1, dead $cpsr = tMOVi8 2, 14 /* CC::al */, $noreg -; THUMB-NEXT: $r2, dead $cpsr = tMOVi8 3, 14 /* CC::al */, $noreg -; THUMB-NEXT: $r3, dead $cpsr = tMOVi8 4, 14 /* CC::al */, $noreg -; THUMB-NEXT: tTAILJMPdND @thunk_called_fn, 14 /* CC::al */, $noreg, implicit $sp +; THUMB2-LABEL: name: OUTLINED_FUNCTION_0 +; THUMB2: bb.0: +; THUMB2-NEXT: liveins: $r11, $r10, $r9, $r8, $r6, $r5, $r4, $d15, $d14, $d13, $d12, $d11, $d10, $d9, $d8 +; THUMB2: $r0, dead $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg +; THUMB2-NEXT: $r1, dead $cpsr = tMOVi8 2, 14 /* CC::al */, $noreg +; THUMB2-NEXT: $r2, dead $cpsr = tMOVi8 3, 14 /* CC::al */, $noreg +; THUMB2-NEXT: $r3, dead $cpsr = tMOVi8 4, 14 /* CC::al */, $noreg +; THUMB2-NEXT: tTAILJMPdND @thunk_called_fn, 14 /* CC::al */, $noreg, implicit $sp + +; THUMB1-LABEL: name: OUTLINED_FUNCTION_0 +; THUMB1: bb.0: +; THUMB1-NEXT: liveins: $r6, $r5, $r4, $r11, $r10, $r9, $r8, $d15, $d14, $d13, $d12, $d11, $d10, $d9, $d8 +; THUMB1: $r0, dead $cpsr = tMOVi8 1, 14 /* CC::al */, $noreg +; THUMB1-NEXT: $r1, dead $cpsr = tMOVi8 2, 14 /* CC::al */, $noreg +; THUMB1-NEXT: $r2, dead $cpsr = tMOVi8 3, 14 /* CC::al */, $noreg +; THUMB1-NEXT: $r3, dead $cpsr = tMOVi8 4, 14 /* CC::al */, $noreg +; THUMB1-NEXT: tTAILJMPdND @thunk_called_fn, 14 /* CC::al */, $noreg, implicit $sp ; MACHO-LABEL: name: OUTLINED_FUNCTION_0 ; MACHO: bb.0: