[ARM] Add SEH opcodes in frame lowering

Skip inserting regular CFI instructions if using WinCFI.

This is based a fair amount on the corresponding ARM64 implementation,
but instead of trying to insert the SEH opcodes one by one where
we generate other prolog/epilog instructions, we try to walk over the
whole prolog/epilog range and insert them. This is done because in
many cases, the exact number of instructions inserted is abstracted
away deeper.

For some cases, we manually insert specific SEH opcodes directly where
instructions are generated, where the automatic mapping of instructions
to SEH opcodes doesn't hold up (e.g. for __chkstk stack probes).

Skip Thumb2SizeReduction for SEH prologs/epilogs, and force
tail calls to wide instructions (just like on MachO), to make sure
that the unwind info actually matches the width of the final
instructions, without heuristics about what later passes will do.

Mark SEH instructions as scheduling boundaries, to make sure that they
aren't reordered away from the instruction they describe by
PostRAScheduler.

Mark the SEH instructions with the NoMerge flag, to avoid doing
tail merging of functions that have multiple epilogs that all end
with the same sequence of "b <other>; .seh_nop_w, .seh_endepilogue".

Differential Revision: https://reviews.llvm.org/D125648
This commit is contained in:
Martin Storsjö 2021-11-26 14:28:16 +02:00
parent 5482ae6328
commit d8e67c1ccc
13 changed files with 803 additions and 75 deletions

View File

@ -2274,6 +2274,47 @@ void ARMAsmPrinter::emitInstruction(const MachineInstr *MI) {
EmitToStreamer(*OutStreamer, TmpInstSB);
return;
}
case ARM::SEH_StackAlloc:
ATS.emitARMWinCFIAllocStack(MI->getOperand(0).getImm(),
MI->getOperand(1).getImm());
return;
case ARM::SEH_SaveRegs:
case ARM::SEH_SaveRegs_Ret:
ATS.emitARMWinCFISaveRegMask(MI->getOperand(0).getImm(),
MI->getOperand(1).getImm());
return;
case ARM::SEH_SaveSP:
ATS.emitARMWinCFISaveSP(MI->getOperand(0).getImm());
return;
case ARM::SEH_SaveFRegs:
ATS.emitARMWinCFISaveFRegs(MI->getOperand(0).getImm(),
MI->getOperand(1).getImm());
return;
case ARM::SEH_SaveLR:
ATS.emitARMWinCFISaveLR(MI->getOperand(0).getImm());
return;
case ARM::SEH_Nop:
case ARM::SEH_Nop_Ret:
ATS.emitARMWinCFINop(MI->getOperand(0).getImm());
return;
case ARM::SEH_PrologEnd:
ATS.emitARMWinCFIPrologEnd(/*Fragment=*/false);
return;
case ARM::SEH_EpilogStart:
ATS.emitARMWinCFIEpilogStart(ARMCC::AL);
return;
case ARM::SEH_EpilogEnd:
ATS.emitARMWinCFIEpilogEnd();
return;
}
MCInst TmpInst;

View File

@ -2071,6 +2071,9 @@ bool ARMBaseInstrInfo::isSchedulingBoundary(const MachineInstr &MI,
if (MI.getOpcode() == TargetOpcode::INLINEASM_BR)
return true;
if (isSEHInstruction(MI))
return true;
// Treat the start of the IT block as a scheduling boundary, but schedule
// t2IT along with all instructions following it.
// FIXME: This is a big hammer. But the alternative is to add all potential

View File

@ -757,6 +757,26 @@ static inline bool isValidCoprocessorNumber(unsigned Num,
return true;
}
static inline bool isSEHInstruction(const MachineInstr &MI) {
unsigned Opc = MI.getOpcode();
switch (Opc) {
case ARM::SEH_StackAlloc:
case ARM::SEH_SaveRegs:
case ARM::SEH_SaveRegs_Ret:
case ARM::SEH_SaveSP:
case ARM::SEH_SaveFRegs:
case ARM::SEH_SaveLR:
case ARM::SEH_Nop:
case ARM::SEH_Nop_Ret:
case ARM::SEH_PrologEnd:
case ARM::SEH_EpilogStart:
case ARM::SEH_EpilogEnd:
return true;
default:
return false;
}
}
/// getInstrPredicate - If instruction is predicated, returns its predicate
/// condition, otherwise returns AL. It also returns the condition code
/// register by reference.

View File

@ -214,6 +214,8 @@ public:
unsigned DefSubReg,
const TargetRegisterClass *SrcRC,
unsigned SrcSubReg) const override;
int getSEHRegNum(unsigned i) const { return getEncodingValue(i); }
};
} // end namespace llvm

View File

@ -23,6 +23,7 @@
#include "llvm/CodeGen/LivePhysRegs.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Support/Debug.h"
using namespace llvm;
@ -2107,6 +2108,10 @@ bool ARMExpandPseudo::ExpandMI(MachineBasicBlock &MBB,
case ARM::TCRETURNdi:
case ARM::TCRETURNri: {
MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr();
if (MBBI->getOpcode() == ARM::SEH_EpilogEnd)
MBBI--;
if (MBBI->getOpcode() == ARM::SEH_Nop_Ret)
MBBI--;
assert(MBBI->isReturn() &&
"Can only insert epilog into returning blocks");
unsigned RetOpcode = MBBI->getOpcode();
@ -2116,13 +2121,21 @@ bool ARMExpandPseudo::ExpandMI(MachineBasicBlock &MBB,
// Tail call return: adjust the stack pointer and jump to callee.
MBBI = MBB.getLastNonDebugInstr();
if (MBBI->getOpcode() == ARM::SEH_EpilogEnd)
MBBI--;
if (MBBI->getOpcode() == ARM::SEH_Nop_Ret)
MBBI--;
MachineOperand &JumpTarget = MBBI->getOperand(0);
// Jump to label or value in register.
if (RetOpcode == ARM::TCRETURNdi) {
MachineFunction *MF = MBB.getParent();
bool NeedsWinCFI = MF->getTarget().getMCAsmInfo()->usesWindowsCFI() &&
MF->getFunction().needsUnwindTableEntry();
unsigned TCOpcode =
STI->isThumb()
? (STI->isTargetMachO() ? ARM::tTAILJMPd : ARM::tTAILJMPdND)
? ((STI->isTargetMachO() || NeedsWinCFI) ? ARM::tTAILJMPd
: ARM::tTAILJMPdND)
: ARM::TAILJMPd;
MachineInstrBuilder MIB = BuildMI(MBB, MBBI, dl, TII.get(TCOpcode));
if (JumpTarget.isGlobal())

View File

@ -138,6 +138,7 @@
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/Function.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDwarf.h"
#include "llvm/MC/MCInstrDesc.h"
@ -272,6 +273,187 @@ static int getArgumentStackToRestore(MachineFunction &MF,
return ArgumentPopSize;
}
static bool needsWinCFI(const MachineFunction &MF) {
const Function &F = MF.getFunction();
return MF.getTarget().getMCAsmInfo()->usesWindowsCFI() &&
F.needsUnwindTableEntry();
}
// Given a load or a store instruction, generate an appropriate unwinding SEH
// code on Windows.
static MachineBasicBlock::iterator insertSEH(MachineBasicBlock::iterator MBBI,
const TargetInstrInfo &TII,
unsigned Flags) {
unsigned Opc = MBBI->getOpcode();
MachineBasicBlock *MBB = MBBI->getParent();
MachineFunction &MF = *MBB->getParent();
DebugLoc DL = MBBI->getDebugLoc();
MachineInstrBuilder MIB;
const ARMSubtarget &Subtarget = MF.getSubtarget<ARMSubtarget>();
const ARMBaseRegisterInfo *RegInfo = Subtarget.getRegisterInfo();
Flags |= MachineInstr::NoMerge;
switch (Opc) {
default:
report_fatal_error("No SEH Opcode for instruction " + TII.getName(Opc));
break;
case ARM::t2ADDri: // add.w r11, sp, #xx
case ARM::t2ADDri12: // add.w r11, sp, #xx
case ARM::t2SUBri: // sub.w r4, r11, #xx
case ARM::t2MOVTi16: // movt r4, #xx
case ARM::t2MOVi16: // movw r4, #xx
case ARM::tBL: // bl __chkstk
// These are harmless if used for just setting up a frame pointer,
// but that frame pointer can't be relied upon for unwinding, unless
// set up with SEH_SaveSP.
MIB = BuildMI(MF, DL, TII.get(ARM::SEH_Nop))
.addImm(/*Wide=*/1)
.setMIFlags(Flags);
break;
case ARM::tBLXr: // blx r12 (__chkstk)
MIB = BuildMI(MF, DL, TII.get(ARM::SEH_Nop))
.addImm(/*Wide=*/0)
.setMIFlags(Flags);
break;
case ARM::t2MOVi32imm: // movw+movt
// This pseudo instruction expands into two mov instructions. If the
// second operand is a symbol reference, this will stay as two wide
// instructions, movw+movt. If they're immediates, the first one can
// end up as a narrow mov though.
// As two SEH instructions are appended here, they won't get interleaved
// between the two final movw/movt instructions, but it doesn't make any
// practical difference.
MIB = BuildMI(MF, DL, TII.get(ARM::SEH_Nop))
.addImm(/*Wide=*/1)
.setMIFlags(Flags);
MBB->insertAfter(MBBI, MIB);
MIB = BuildMI(MF, DL, TII.get(ARM::SEH_Nop))
.addImm(/*Wide=*/1)
.setMIFlags(Flags);
break;
case ARM::t2LDMIA_RET:
case ARM::t2LDMIA_UPD:
case ARM::t2STMDB_UPD: {
unsigned Mask = 0;
for (unsigned i = 4, NumOps = MBBI->getNumOperands(); i != NumOps; ++i) {
const MachineOperand &MO = MBBI->getOperand(i);
if (!MO.isReg() || MO.isImplicit())
continue;
unsigned Reg = RegInfo->getSEHRegNum(MO.getReg());
if (Reg == 15)
Reg = 14;
Mask |= 1 << Reg;
}
unsigned SEHOpc =
(Opc == ARM::t2LDMIA_RET) ? ARM::SEH_SaveRegs_Ret : ARM::SEH_SaveRegs;
MIB = BuildMI(MF, DL, TII.get(SEHOpc))
.addImm(Mask)
.addImm(/*Wide=*/1)
.setMIFlags(Flags);
break;
}
case ARM::VSTMDDB_UPD:
case ARM::VLDMDIA_UPD: {
int First = -1, Last = 0;
for (unsigned i = 4, NumOps = MBBI->getNumOperands(); i != NumOps; ++i) {
const MachineOperand &MO = MBBI->getOperand(i);
unsigned Reg = RegInfo->getSEHRegNum(MO.getReg());
if (First == -1)
First = Reg;
Last = Reg;
}
MIB = BuildMI(MF, DL, TII.get(ARM::SEH_SaveFRegs))
.addImm(First)
.addImm(Last)
.setMIFlags(Flags);
break;
}
case ARM::tSUBspi:
case ARM::tADDspi:
MIB = BuildMI(MF, DL, TII.get(ARM::SEH_StackAlloc))
.addImm(MBBI->getOperand(2).getImm() * 4)
.addImm(/*Wide=*/0)
.setMIFlags(Flags);
break;
case ARM::t2SUBspImm:
case ARM::t2SUBspImm12:
case ARM::t2ADDspImm:
case ARM::t2ADDspImm12:
MIB = BuildMI(MF, DL, TII.get(ARM::SEH_StackAlloc))
.addImm(MBBI->getOperand(2).getImm())
.addImm(/*Wide=*/1)
.setMIFlags(Flags);
break;
case ARM::tMOVr:
if (MBBI->getOperand(1).getReg() == ARM::SP &&
(Flags & MachineInstr::FrameSetup)) {
unsigned Reg = RegInfo->getSEHRegNum(MBBI->getOperand(0).getReg());
MIB = BuildMI(MF, DL, TII.get(ARM::SEH_SaveSP))
.addImm(Reg)
.setMIFlags(Flags);
} else if (MBBI->getOperand(0).getReg() == ARM::SP &&
(Flags & MachineInstr::FrameDestroy)) {
unsigned Reg = RegInfo->getSEHRegNum(MBBI->getOperand(1).getReg());
MIB = BuildMI(MF, DL, TII.get(ARM::SEH_SaveSP))
.addImm(Reg)
.setMIFlags(Flags);
} else {
report_fatal_error("No SEH Opcode for MOV");
}
break;
case ARM::tBX_RET:
case ARM::TCRETURNri:
MIB = BuildMI(MF, DL, TII.get(ARM::SEH_Nop_Ret))
.addImm(/*Wide=*/0)
.setMIFlags(Flags);
break;
case ARM::TCRETURNdi:
MIB = BuildMI(MF, DL, TII.get(ARM::SEH_Nop_Ret))
.addImm(/*Wide=*/1)
.setMIFlags(Flags);
break;
}
return MBB->insertAfter(MBBI, MIB);
}
static MachineBasicBlock::iterator
initMBBRange(MachineBasicBlock &MBB, const MachineBasicBlock::iterator &MBBI) {
if (MBBI == MBB.begin())
return MachineBasicBlock::iterator();
return std::prev(MBBI);
}
static void insertSEHRange(MachineBasicBlock &MBB,
MachineBasicBlock::iterator Start,
const MachineBasicBlock::iterator &End,
const ARMBaseInstrInfo &TII, unsigned MIFlags) {
if (Start.isValid())
Start = std::next(Start);
else
Start = MBB.begin();
for (auto MI = Start; MI != End;) {
auto Next = std::next(MI);
// Check if this instruction already has got a SEH opcode added. In that
// case, don't do this generic mapping.
if (Next != End && isSEHInstruction(*Next)) {
MI = std::next(Next);
while (MI != End && isSEHInstruction(*MI))
++MI;
continue;
}
insertSEH(MI, TII, MIFlags);
MI = Next;
}
}
static void emitRegPlusImmediate(
bool isARM, MachineBasicBlock &MBB, MachineBasicBlock::iterator &MBBI,
const DebugLoc &dl, const ARMBaseInstrInfo &TII, unsigned DestReg,
@ -481,6 +663,7 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF,
unsigned NumBytes = MFI.getStackSize();
const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
int FPCXTSaveSize = 0;
bool NeedsWinCFI = needsWinCFI(MF);
// Debug location must be unknown since the first debug location is used
// to determine the end of the prologue.
@ -509,7 +692,14 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF,
MachineInstr::FrameSetup);
DefCFAOffsetCandidates.addInst(std::prev(MBBI), NumBytes, true);
}
DefCFAOffsetCandidates.emitDefCFAOffsets(MBB, dl, TII, HasFP);
if (!NeedsWinCFI)
DefCFAOffsetCandidates.emitDefCFAOffsets(MBB, dl, TII, HasFP);
if (NeedsWinCFI && MBBI != MBB.begin()) {
insertSEHRange(MBB, {}, MBBI, TII, MachineInstr::FrameSetup);
BuildMI(MBB, MBBI, dl, TII.get(ARM::SEH_PrologEnd))
.setMIFlag(MachineInstr::FrameSetup);
MF.setHasWinCFI(true);
}
return;
}
@ -646,15 +836,25 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF,
if (STI.isTargetWindows() && WindowsRequiresStackProbe(MF, NumBytes)) {
uint32_t NumWords = NumBytes >> 2;
if (NumWords < 65536)
if (NumWords < 65536) {
BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MOVi16), ARM::R4)
.addImm(NumWords)
.setMIFlags(MachineInstr::FrameSetup)
.add(predOps(ARMCC::AL));
else
BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MOVi32imm), ARM::R4)
.addImm(NumWords)
.setMIFlags(MachineInstr::FrameSetup);
} else {
// Split into two instructions here, instead of using t2MOVi32imm,
// to allow inserting accurate SEH instructions (including accurate
// instruction size for each of them).
BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MOVi16), ARM::R4)
.addImm(NumWords & 0xffff)
.setMIFlags(MachineInstr::FrameSetup)
.add(predOps(ARMCC::AL));
BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MOVTi16), ARM::R4)
.addReg(ARM::R4)
.addImm(NumWords >> 16)
.setMIFlags(MachineInstr::FrameSetup)
.add(predOps(ARMCC::AL));
}
switch (TM.getCodeModel()) {
case CodeModel::Tiny:
@ -681,12 +881,20 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF,
break;
}
BuildMI(MBB, MBBI, dl, TII.get(ARM::t2SUBrr), ARM::SP)
.addReg(ARM::SP, RegState::Kill)
.addReg(ARM::R4, RegState::Kill)
.setMIFlags(MachineInstr::FrameSetup)
.add(predOps(ARMCC::AL))
.add(condCodeOp());
MachineInstrBuilder Instr, SEH;
Instr = BuildMI(MBB, MBBI, dl, TII.get(ARM::t2SUBrr), ARM::SP)
.addReg(ARM::SP, RegState::Kill)
.addReg(ARM::R4, RegState::Kill)
.setMIFlags(MachineInstr::FrameSetup)
.add(predOps(ARMCC::AL))
.add(condCodeOp());
if (NeedsWinCFI) {
SEH = BuildMI(MF, dl, TII.get(ARM::SEH_StackAlloc))
.addImm(NumBytes)
.addImm(/*Wide=*/1)
.setMIFlags(MachineInstr::FrameSetup);
MBB.insertAfter(Instr, SEH);
}
NumBytes = 0;
}
@ -726,27 +934,38 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF,
dl, TII, FramePtr, ARM::SP,
PushSize + FramePtrOffsetInPush,
MachineInstr::FrameSetup);
if (FramePtrOffsetInPush + PushSize != 0) {
unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfa(
nullptr, MRI->getDwarfRegNum(FramePtr, true),
FPCXTSaveSize + ArgRegsSaveSize - FramePtrOffsetInPush));
BuildMI(MBB, AfterPush, dl, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex)
.setMIFlags(MachineInstr::FrameSetup);
} else {
unsigned CFIIndex =
MF.addFrameInst(MCCFIInstruction::createDefCfaRegister(
nullptr, MRI->getDwarfRegNum(FramePtr, true)));
BuildMI(MBB, AfterPush, dl, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex)
.setMIFlags(MachineInstr::FrameSetup);
if (!NeedsWinCFI) {
if (FramePtrOffsetInPush + PushSize != 0) {
unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfa(
nullptr, MRI->getDwarfRegNum(FramePtr, true),
FPCXTSaveSize + ArgRegsSaveSize - FramePtrOffsetInPush));
BuildMI(MBB, AfterPush, dl, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex)
.setMIFlags(MachineInstr::FrameSetup);
} else {
unsigned CFIIndex =
MF.addFrameInst(MCCFIInstruction::createDefCfaRegister(
nullptr, MRI->getDwarfRegNum(FramePtr, true)));
BuildMI(MBB, AfterPush, dl, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex)
.setMIFlags(MachineInstr::FrameSetup);
}
}
}
// Emit a SEH opcode indicating the prologue end. The rest of the prologue
// instructions below don't need to be replayed to unwind the stack.
if (NeedsWinCFI && MBBI != MBB.begin()) {
insertSEHRange(MBB, {}, MBBI, TII, MachineInstr::FrameSetup);
BuildMI(MBB, MBBI, dl, TII.get(ARM::SEH_PrologEnd))
.setMIFlag(MachineInstr::FrameSetup);
MF.setHasWinCFI(true);
}
// Now that the prologue's actual instructions are finalised, we can insert
// the necessary DWARF cf instructions to describe the situation. Start by
// recording where each register ended up:
if (GPRCS1Size > 0) {
if (GPRCS1Size > 0 && !NeedsWinCFI) {
MachineBasicBlock::iterator Pos = std::next(GPRCS1Push);
int CFIIndex;
for (const auto &Entry : CSI) {
@ -780,7 +999,7 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF,
}
}
if (GPRCS2Size > 0) {
if (GPRCS2Size > 0 && !NeedsWinCFI) {
MachineBasicBlock::iterator Pos = std::next(GPRCS2Push);
for (const auto &Entry : CSI) {
Register Reg = Entry.getReg();
@ -806,7 +1025,7 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF,
}
}
if (DPRCSSize > 0) {
if (DPRCSSize > 0 && !NeedsWinCFI) {
// Since vpush register list cannot have gaps, there may be multiple vpush
// instructions in the prologue.
MachineBasicBlock::iterator Pos = std::next(LastPush);
@ -830,7 +1049,8 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF,
// throughout the process. If we have a frame pointer, it takes over the job
// half-way through, so only the first few .cfi_def_cfa_offset instructions
// actually get emitted.
DefCFAOffsetCandidates.emitDefCFAOffsets(MBB, dl, TII, HasFP);
if (!NeedsWinCFI)
DefCFAOffsetCandidates.emitDefCFAOffsets(MBB, dl, TII, HasFP);
if (STI.isTargetELF() && hasFP(MF))
MFI.setOffsetAdjustment(MFI.getOffsetAdjustment() -
@ -927,7 +1147,14 @@ void ARMFrameLowering::emitEpilogue(MachineFunction &MF,
MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator();
DebugLoc dl = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc();
MachineBasicBlock::iterator RangeStart;
if (!AFI->hasStackFrame()) {
if (MF.hasWinCFI()) {
BuildMI(MBB, MBBI, dl, TII.get(ARM::SEH_EpilogStart))
.setMIFlag(MachineInstr::FrameDestroy);
RangeStart = initMBBRange(MBB, MBBI);
}
if (NumBytes + IncomingArgStackToRestore != 0)
emitSPUpdate(isARM, MBB, MBBI, dl, TII,
NumBytes + IncomingArgStackToRestore,
@ -943,6 +1170,12 @@ void ARMFrameLowering::emitEpilogue(MachineFunction &MF,
++MBBI;
}
if (MF.hasWinCFI()) {
BuildMI(MBB, MBBI, dl, TII.get(ARM::SEH_EpilogStart))
.setMIFlag(MachineInstr::FrameDestroy);
RangeStart = initMBBRange(MBB, MBBI);
}
// Move SP to start of FP callee save spill area.
NumBytes -= (ReservedArgStack +
AFI->getFPCXTSaveAreaSize() +
@ -1029,6 +1262,12 @@ void ARMFrameLowering::emitEpilogue(MachineFunction &MF,
if (AFI->shouldSignReturnAddress() && !AFI->isCmseNSEntryFunction())
BuildMI(MBB, MBBI, DebugLoc(), STI.getInstrInfo()->get(ARM::t2AUT));
}
if (MF.hasWinCFI()) {
insertSEHRange(MBB, RangeStart, MBB.end(), TII, MachineInstr::FrameDestroy);
BuildMI(MBB, MBB.end(), dl, TII.get(ARM::SEH_EpilogEnd))
.setMIFlag(MachineInstr::FrameDestroy);
}
}
/// getFrameIndexReference - Provide a base+offset reference to an FI slot for
@ -2596,17 +2835,19 @@ void ARMFrameLowering::adjustForSegmentedStacks(
// Emit the relevant DWARF information about the change in stack pointer as
// well as where to find both r4 and r5 (the callee-save registers)
CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, 8));
BuildMI(PrevStackMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
nullptr, MRI->getDwarfRegNum(ScratchReg1, true), -4));
BuildMI(PrevStackMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
nullptr, MRI->getDwarfRegNum(ScratchReg0, true), -8));
BuildMI(PrevStackMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
if (!MF.getTarget().getMCAsmInfo()->usesWindowsCFI()) {
CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, 8));
BuildMI(PrevStackMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
nullptr, MRI->getDwarfRegNum(ScratchReg1, true), -4));
BuildMI(PrevStackMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
nullptr, MRI->getDwarfRegNum(ScratchReg0, true), -8));
BuildMI(PrevStackMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
}
// mov SR1, sp
if (Thumb) {
@ -2808,13 +3049,15 @@ void ARMFrameLowering::adjustForSegmentedStacks(
// Emit the DWARF info about the change in stack as well as where to find the
// previous link register
CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, 12));
BuildMI(AllocMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
if (!MF.getTarget().getMCAsmInfo()->usesWindowsCFI()) {
CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, 12));
BuildMI(AllocMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
nullptr, MRI->getDwarfRegNum(ARM::LR, true), -12));
BuildMI(AllocMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
BuildMI(AllocMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
}
// Call __morestack().
if (Thumb) {
@ -2870,9 +3113,11 @@ void ARMFrameLowering::adjustForSegmentedStacks(
}
// Update the CFA offset now that we've popped
CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, 0));
BuildMI(AllocMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
if (!MF.getTarget().getMCAsmInfo()->usesWindowsCFI()) {
CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, 0));
BuildMI(AllocMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
}
// Return from this function.
BuildMI(AllocMBB, DL, TII.get(ST->getReturnOpcode())).add(predOps(ARMCC::AL));
@ -2894,20 +3139,22 @@ void ARMFrameLowering::adjustForSegmentedStacks(
}
// Update the CFA offset now that we've popped
CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, 0));
BuildMI(PostStackMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
if (!MF.getTarget().getMCAsmInfo()->usesWindowsCFI()) {
CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, 0));
BuildMI(PostStackMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
// Tell debuggers that r4 and r5 are now the same as they were in the
// previous function, that they're the "Same Value".
CFIIndex = MF.addFrameInst(MCCFIInstruction::createSameValue(
nullptr, MRI->getDwarfRegNum(ScratchReg0, true)));
BuildMI(PostStackMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
CFIIndex = MF.addFrameInst(MCCFIInstruction::createSameValue(
nullptr, MRI->getDwarfRegNum(ScratchReg1, true)));
BuildMI(PostStackMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
// Tell debuggers that r4 and r5 are now the same as they were in the
// previous function, that they're the "Same Value".
CFIIndex = MF.addFrameInst(MCCFIInstruction::createSameValue(
nullptr, MRI->getDwarfRegNum(ScratchReg0, true)));
BuildMI(PostStackMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
CFIIndex = MF.addFrameInst(MCCFIInstruction::createSameValue(
nullptr, MRI->getDwarfRegNum(ScratchReg1, true)));
BuildMI(PostStackMBB, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
}
// Organizing MBB lists
PostStackMBB->addSuccessor(&PrologueMBB);

View File

@ -6476,3 +6476,24 @@ def CompilerBarrier : PseudoInst<(outs), (ins i32imm:$ordering), NoItinerary,
let AsmString = "@ COMPILER BARRIER";
let hasNoSchedulingInfo = 1;
}
//===----------------------------------------------------------------------===//
// Instructions used for emitting unwind opcodes on Windows.
//===----------------------------------------------------------------------===//
let isPseudo = 1 in {
def SEH_StackAlloc : PseudoInst<(outs), (ins i32imm:$size, i32imm:$wide), NoItinerary, []>, Sched<[]>;
def SEH_SaveRegs : PseudoInst<(outs), (ins i32imm:$mask, i32imm:$wide), NoItinerary, []>, Sched<[]>;
let isTerminator = 1 in
def SEH_SaveRegs_Ret : PseudoInst<(outs), (ins i32imm:$mask, i32imm:$wide), NoItinerary, []>, Sched<[]>;
def SEH_SaveSP : PseudoInst<(outs), (ins i32imm:$reg), NoItinerary, []>, Sched<[]>;
def SEH_SaveFRegs : PseudoInst<(outs), (ins i32imm:$first, i32imm:$last), NoItinerary, []>, Sched<[]>;
let isTerminator = 1 in
def SEH_SaveLR : PseudoInst<(outs), (ins i32imm:$offst), NoItinerary, []>, Sched<[]>;
def SEH_Nop : PseudoInst<(outs), (ins i32imm:$wide), NoItinerary, []>, Sched<[]>;
let isTerminator = 1 in
def SEH_Nop_Ret : PseudoInst<(outs), (ins i32imm:$wide), NoItinerary, []>, Sched<[]>;
def SEH_PrologEnd : PseudoInst<(outs), (ins), NoItinerary, []>, Sched<[]>;
def SEH_EpilogStart : PseudoInst<(outs), (ins), NoItinerary, []>, Sched<[]>;
let isTerminator = 1 in
def SEH_EpilogEnd : PseudoInst<(outs), (ins), NoItinerary, []>, Sched<[]>;
}

View File

@ -3951,6 +3951,7 @@ def t2Bcc : T2I<(outs), (ins brtarget:$target), IIC_Br,
// Tail calls. The MachO version of thumb tail calls uses a t2 branch, so
// it goes here.
// Windows SEH unwinding also needs a strict t2 branch for tail calls.
let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1 in {
// IOS version.
let Uses = [SP] in
@ -3958,7 +3959,7 @@ let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1 in {
(ins thumb_br_target:$dst, pred:$p),
4, IIC_Br, [],
(t2B thumb_br_target:$dst, pred:$p)>,
Requires<[IsThumb2, IsMachO]>, Sched<[WriteBr]>;
Requires<[IsThumb2]>, Sched<[WriteBr]>;
}
// IT block

View File

@ -27,6 +27,7 @@
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/Function.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/Support/CommandLine.h"
@ -205,11 +206,11 @@ namespace {
bool IsSelfLoop);
/// ReduceMI - Attempt to reduce MI, return true on success.
bool ReduceMI(MachineBasicBlock &MBB, MachineInstr *MI,
bool LiveCPSR, bool IsSelfLoop);
bool ReduceMI(MachineBasicBlock &MBB, MachineInstr *MI, bool LiveCPSR,
bool IsSelfLoop, bool SkipPrologueEpilogue);
/// ReduceMBB - Reduce width of instructions in the specified basic block.
bool ReduceMBB(MachineBasicBlock &MBB);
bool ReduceMBB(MachineBasicBlock &MBB, bool SkipPrologueEpilogue);
bool OptimizeSize;
bool MinimizeSize;
@ -1012,11 +1013,15 @@ static bool UpdateCPSRUse(MachineInstr &MI, bool LiveCPSR) {
}
bool Thumb2SizeReduce::ReduceMI(MachineBasicBlock &MBB, MachineInstr *MI,
bool LiveCPSR, bool IsSelfLoop) {
bool LiveCPSR, bool IsSelfLoop,
bool SkipPrologueEpilogue) {
unsigned Opcode = MI->getOpcode();
DenseMap<unsigned, unsigned>::iterator OPI = ReduceOpcodeMap.find(Opcode);
if (OPI == ReduceOpcodeMap.end())
return false;
if (SkipPrologueEpilogue && (MI->getFlag(MachineInstr::FrameSetup) ||
MI->getFlag(MachineInstr::FrameDestroy)))
return false;
const ReduceEntry &Entry = ReduceTable[OPI->second];
// Don't attempt normal reductions on "special" cases for now.
@ -1036,7 +1041,8 @@ bool Thumb2SizeReduce::ReduceMI(MachineBasicBlock &MBB, MachineInstr *MI,
return false;
}
bool Thumb2SizeReduce::ReduceMBB(MachineBasicBlock &MBB) {
bool Thumb2SizeReduce::ReduceMBB(MachineBasicBlock &MBB,
bool SkipPrologueEpilogue) {
bool Modified = false;
// Yes, CPSR could be livein.
@ -1080,7 +1086,7 @@ bool Thumb2SizeReduce::ReduceMBB(MachineBasicBlock &MBB) {
// Does NextMII belong to the same bundle as MI?
bool NextInSameBundle = NextMII != E && NextMII->isBundledWithPred();
if (ReduceMI(MBB, MI, LiveCPSR, IsSelfLoop)) {
if (ReduceMI(MBB, MI, LiveCPSR, IsSelfLoop, SkipPrologueEpilogue)) {
Modified = true;
MachineBasicBlock::instr_iterator I = std::prev(NextMII);
MI = &*I;
@ -1147,8 +1153,10 @@ bool Thumb2SizeReduce::runOnMachineFunction(MachineFunction &MF) {
// predecessors.
ReversePostOrderTraversal<MachineFunction*> RPOT(&MF);
bool Modified = false;
bool NeedsWinCFI = MF.getTarget().getMCAsmInfo()->usesWindowsCFI() &&
MF.getFunction().needsUnwindTableEntry();
for (MachineBasicBlock *MBB : RPOT)
Modified |= ReduceMBB(*MBB);
Modified |= ReduceMBB(*MBB, /*SkipPrologueEpilogue=*/NeedsWinCFI);
return Modified;
}

View File

@ -57,5 +57,5 @@ define arm_aapcs_vfpcc i32 @call_internal() {
}
; CHECK-LABEL: call_internal
; CHECK: b internal
; CHECK: b.w internal

View File

@ -0,0 +1,313 @@
;; Check that this produces the expected assembly output
; RUN: llc -mtriple=thumbv7-windows -o - %s -verify-machineinstrs | FileCheck %s
;; Also try to write an object file, which verifies that the SEH opcodes
;; match the actual prologue/epilogue length.
; RUN: llc -mtriple=thumbv7-windows -filetype=obj -o %t.obj %s -verify-machineinstrs
; CHECK-LABEL: clobberR4Frame:
; CHECK-NEXT: .seh_proc clobberR4Frame
; CHECK-NEXT: @ %bb.0: @ %entry
; CHECK-NEXT: push.w {r4, r7, r11, lr}
; CHECK-NEXT: .seh_save_regs_w {r4, r7, r11, lr}
; CHECK-NEXT: add.w r11, sp, #8
; CHECK-NEXT: .seh_nop_w
; CHECK-NEXT: .seh_endprologue
; CHECK-NEXT: bl other
; CHECK: .seh_startepilogue
; CHECK-NEXT: pop.w {r4, r7, r11, pc}
; CHECK-NEXT: .seh_save_regs_w {r4, r7, r11, lr}
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: .seh_endproc
define arm_aapcs_vfpcc void @clobberR4Frame() uwtable "frame-pointer"="all" {
entry:
call arm_aapcs_vfpcc void @other()
call void asm sideeffect "", "~{r4}"()
ret void
}
; CHECK-LABEL: clobberR4NoFrame:
; CHECK-NEXT: .seh_proc clobberR4NoFrame
; CHECK-NEXT: @ %bb.0: @ %entry
; CHECK-NEXT: push.w {r4, lr}
; CHECK-NEXT: .seh_save_regs_w {r4, lr}
; CHECK-NEXT: .seh_endprologue
; CHECK-NEXT: bl other
; CHECK: .seh_startepilogue
; CHECK-NEXT: pop.w {r4, pc}
; CHECK-NEXT: .seh_save_regs_w {r4, lr}
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: .seh_endproc
define arm_aapcs_vfpcc void @clobberR4NoFrame() uwtable "frame-pointer"="none" {
entry:
call arm_aapcs_vfpcc void @other()
call void asm sideeffect "", "~{r4}"()
ret void
}
; CHECK-LABEL: clobberR4Tail:
; CHECK-NEXT: .seh_proc clobberR4Tail
; CHECK-NEXT: @ %bb.0: @ %entry
; CHECK-NEXT: push.w {r4, lr}
; CHECK-NEXT: .seh_save_regs_w {r4, lr}
; CHECK-NEXT: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK-NEXT: pop.w {r4, lr}
; CHECK-NEXT: .seh_save_regs_w {r4, lr}
; CHECK-NEXT: b.w other
; CHECK-NEXT: .seh_nop_w
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: .seh_endproc
define arm_aapcs_vfpcc void @clobberR4Tail() uwtable "frame-pointer"="none" {
entry:
call void asm sideeffect "", "~{r4}"()
tail call arm_aapcs_vfpcc void @other()
ret void
}
; CHECK-LABEL: clobberD8D10:
; CHECK-NEXT: .seh_proc clobberD8D10
; CHECK-NEXT: @ %bb.0: @ %entry
; CHECK-NEXT: vpush {d8, d9, d10}
; CHECK-NEXT: .seh_save_fregs {d8-d10}
; CHECK-NEXT: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK-NEXT: vpop {d8, d9, d10}
; CHECK-NEXT: .seh_save_fregs {d8-d10}
; CHECK-NEXT: b.w other
; CHECK-NEXT: .seh_nop_w
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: .seh_endproc
define arm_aapcs_vfpcc void @clobberD8D10() uwtable "frame-pointer"="none" {
entry:
call void asm sideeffect "", "~{d8},~{d9},~{d10}"()
tail call arm_aapcs_vfpcc void @other()
ret void
}
declare arm_aapcs_vfpcc void @other()
; CHECK-LABEL: vararg:
; CHECK-NEXT: .seh_proc vararg
; CHECK-NEXT: @ %bb.0: @ %entry
; CHECK-NEXT: sub sp, #12
; CHECK-NEXT: .seh_stackalloc 12
; CHECK-NEXT: push.w {r11, lr}
; CHECK-NEXT: .seh_save_regs_w {r11, lr}
; CHECK-NEXT: sub sp, #4
; CHECK-NEXT: .seh_stackalloc 4
; CHECK-NEXT: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK-NEXT: add sp, #4
; CHECK-NEXT: .seh_stackalloc 4
; CHECK-NEXT: pop.w {r11, lr}
; CHECK-NEXT: .seh_save_regs_w {r11, lr}
; CHECK-NEXT: add sp, #12
; CHECK-NEXT: .seh_stackalloc 12
; CHECK-NEXT: bx lr
; CHECK-NEXT: .seh_nop
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: .seh_endproc
define arm_aapcs_vfpcc void @vararg(i32 noundef %a, ...) uwtable "frame-pointer"="none" {
entry:
%ap = alloca ptr, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %ap)
call void @llvm.va_start(ptr nonnull %ap)
%0 = load ptr, ptr %ap
call arm_aapcs_vfpcc void @useva(ptr noundef %0)
call void @llvm.va_end(ptr nonnull %ap)
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %ap)
ret void
}
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)
declare void @llvm.va_start(ptr)
declare void @llvm.va_end(ptr)
declare arm_aapcs_vfpcc void @useva(ptr noundef)
; CHECK-LABEL: onlystack:
; CHECK-NEXT: .seh_proc onlystack
; CHECK-NEXT: @ %bb.0: @ %entry
; CHECK-NEXT: sub sp, #4
; CHECK-NEXT: .seh_stackalloc 4
; CHECK-NEXT: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK-NEXT: add sp, #4
; CHECK-NEXT: .seh_stackalloc 4
; CHECK-NEXT: bx lr
; CHECK-NEXT: .seh_nop
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: .seh_endproc
define dso_local arm_aapcs_vfpcc void @onlystack() uwtable "frame-pointer"="none" {
entry:
%buf = alloca [4 x i8], align 1
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %buf)
call void asm sideeffect "", "r"(ptr nonnull %buf)
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %buf)
ret void
}
; CHECK-LABEL: func50:
; CHECK-NEXT: .seh_proc func50
; CHECK-NEXT: @ %bb.0: @ %entry
; CHECK-NEXT: push.w {r11, lr}
; CHECK-NEXT: .seh_save_regs_w {r11, lr}
; CHECK-NEXT: sub sp, #56
; CHECK-NEXT: .seh_stackalloc 56
; CHECK-NEXT: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK-NEXT: add sp, #56
; CHECK-NEXT: .seh_stackalloc 56
; CHECK-NEXT: pop.w {r11, pc}
; CHECK-NEXT: .seh_save_regs_w {r11, lr}
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: .seh_endproc
define arm_aapcs_vfpcc void @func50() {
entry:
%buf = alloca [50 x i8], align 1
call void @llvm.lifetime.start.p0(i64 50, ptr nonnull %buf)
call arm_aapcs_vfpcc void @useptr(ptr noundef nonnull %buf)
call void @llvm.lifetime.end.p0(i64 50, ptr nonnull %buf)
ret void
}
; CHECK-LABEL: func4000:
; CHECK-NEXT: .seh_proc func4000
; CHECK-NEXT: @ %bb.0: @ %entry
; CHECK-NEXT: push.w {r11, lr}
; CHECK-NEXT: .seh_save_regs_w {r11, lr}
; CHECK-NEXT: sub.w sp, sp, #4000
; CHECK-NEXT: .seh_stackalloc_w 4000
; CHECK-NEXT: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK-NEXT: add.w sp, sp, #4000
; CHECK-NEXT: .seh_stackalloc_w 4000
; CHECK-NEXT: pop.w {r11, pc}
; CHECK-NEXT: .seh_save_regs_w {r11, lr}
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: .seh_endproc
define arm_aapcs_vfpcc void @func4000() {
entry:
%buf = alloca [4000 x i8], align 1
call void @llvm.lifetime.start.p0(i64 4000, ptr nonnull %buf)
call arm_aapcs_vfpcc void @useptr(ptr noundef nonnull %buf)
call void @llvm.lifetime.end.p0(i64 4000, ptr nonnull %buf)
ret void
}
; CHECK-LABEL: func5000:
; CHECK-NEXT: .seh_proc func5000
; CHECK-NEXT: @ %bb.0: @ %entry
; CHECK-NEXT: push.w {r4, r5, r6, lr}
; CHECK-NEXT: .seh_save_regs_w {r4-r6, lr}
; CHECK-NEXT: movw r4, #1250
; CHECK-NEXT: .seh_nop_w
; CHECK-NEXT: bl __chkstk
; CHECK-NEXT: .seh_nop_w
; CHECK-NEXT: sub.w sp, sp, r4
; CHECK-NEXT: .seh_stackalloc_w 5000
; CHECK-NEXT: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK-NEXT: add.w sp, sp, #4992
; CHECK-NEXT: .seh_stackalloc_w 4992
; CHECK-NEXT: add sp, #8
; CHECK-NEXT: .seh_stackalloc 8
; CHECK-NEXT: pop.w {r4, r5, r6, pc}
; CHECK-NEXT: .seh_save_regs_w {r4-r6, lr}
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: .seh_endproc
define arm_aapcs_vfpcc void @func5000() {
entry:
%buf = alloca [5000 x i8], align 1
call void @llvm.lifetime.start.p0(i64 5000, ptr nonnull %buf)
call arm_aapcs_vfpcc void @useptr(ptr noundef nonnull %buf)
call void @llvm.lifetime.end.p0(i64 5000, ptr nonnull %buf)
ret void
}
; CHECK-LABEL: func262144:
; CHECK-NEXT: .seh_proc func262144
; CHECK-NEXT: @ %bb.0: @ %entry
; CHECK-NEXT: push.w {r4, r5, r6, lr}
; CHECK-NEXT: .seh_save_regs_w {r4-r6, lr}
; CHECK-NEXT: movw r4, #0
; CHECK-NEXT: .seh_nop
; CHECK-NEXT: movt r4, #1
; CHECK-NEXT: .seh_nop_w
; CHECK-NEXT: bl __chkstk
; CHECK-NEXT: .seh_nop_w
; CHECK-NEXT: sub.w sp, sp, r4
; CHECK-NEXT: .seh_stackalloc_w 262144
; CHECK-NEXT: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK-NEXT: add.w sp, sp, #262144
; CHECK-NEXT: .seh_stackalloc_w 262144
; CHECK-NEXT: pop.w {r4, r5, r6, pc}
; CHECK-NEXT: .seh_save_regs_w {r4-r6, lr}
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: .seh_endproc
define arm_aapcs_vfpcc void @func262144() {
entry:
%buf = alloca [262144 x i8], align 1
call void @llvm.lifetime.start.p0(i64 262144, ptr nonnull %buf)
call arm_aapcs_vfpcc void @useptr(ptr noundef nonnull %buf)
call void @llvm.lifetime.end.p0(i64 262144, ptr nonnull %buf)
ret void
}
; CHECK-LABEL: func270000:
; CHECK-NEXT: .seh_proc func270000
; CHECK-NEXT: @ %bb.0: @ %entry
; CHECK-NEXT: push.w {r4, r5, r6, lr}
; CHECK-NEXT: .seh_save_regs_w {r4-r6, lr}
; CHECK-NEXT: movw r4, #1964
; CHECK-NEXT: .seh_nop_w
; CHECK-NEXT: movt r4, #1
; CHECK-NEXT: .seh_nop_w
; CHECK-NEXT: bl __chkstk
; CHECK-NEXT: .seh_nop_w
; CHECK-NEXT: sub.w sp, sp, r4
; CHECK-NEXT: .seh_stackalloc_w 270000
; CHECK-NEXT: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK-NEXT: add.w sp, sp, #268288
; CHECK-NEXT: .seh_stackalloc_w 268288
; CHECK-NEXT: add.w sp, sp, #1712
; CHECK-NEXT: .seh_stackalloc_w 1712
; CHECK-NEXT: pop.w {r4, r5, r6, pc}
; CHECK-NEXT: .seh_save_regs_w {r4-r6, lr}
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: .seh_endproc
define arm_aapcs_vfpcc void @func270000() {
entry:
%buf = alloca [270000 x i8], align 1
call void @llvm.lifetime.start.p0(i64 270000, ptr nonnull %buf)
call arm_aapcs_vfpcc void @useptr(ptr noundef nonnull %buf)
call void @llvm.lifetime.end.p0(i64 270000, ptr nonnull %buf)
ret void
}
declare arm_aapcs_vfpcc void @useptr(ptr noundef)

View File

@ -0,0 +1,59 @@
;; Check that epilogues aren't tail merged.
;; Check that this produces the expected assembly output
; RUN: llc -mtriple=thumbv7-windows -o - %s -verify-machineinstrs | FileCheck %s
;; Also try to write an object file, which verifies that the SEH opcodes
;; match the actual prologue/epilogue length.
; RUN: llc -mtriple=thumbv7-windows -filetype=obj -o %t.obj %s -verify-machineinstrs
; CHECK-LABEL: d:
; CHECK: .seh_proc d
; CHECK: push.w {r11, lr}
; CHECK-NEXT: .seh_save_regs_w {r11, lr}
; CHECK-NEXT: .seh_endprologue
; CHECK: .seh_startepilogue
; CHECK-NEXT: pop.w {r11, lr}
; CHECK-NEXT: .seh_save_regs_w {r11, lr}
; CHECK-NEXT: b.w b
; CHECK-NEXT: .seh_nop_w
; CHECK-NEXT: .seh_endepilogue
; CHECK: .seh_startepilogue
; CHECK-NEXT: pop.w {r11, lr}
; CHECK-NEXT: .seh_save_regs_w {r11, lr}
; CHECK-NEXT: b.w c
; CHECK-NEXT: .seh_nop_w
; CHECK-NEXT: .seh_endepilogue
; CHECK-NEXT: .seh_endproc
@a = global i32 0, align 4
define arm_aapcs_vfpcc void @d() optsize uwtable "frame-pointer"="none" {
entry:
%0 = load i32, ptr @a, align 4
switch i32 %0, label %if.then1 [
i32 10, label %if.then
i32 0, label %if.end2
]
if.then:
tail call arm_aapcs_vfpcc void @b()
br label %return
if.then1:
tail call arm_aapcs_vfpcc void @b()
br label %if.end2
if.end2:
tail call arm_aapcs_vfpcc void @c()
br label %return
return:
ret void
}
declare arm_aapcs_vfpcc void @b(...)
declare arm_aapcs_vfpcc void @c(...)

View File

@ -44,9 +44,9 @@
; OBJ-NEXT: OffsetInParent: 0
; OBJ-NEXT: BasePointerOffset: 12
; OBJ-NEXT: LocalVariableAddrRange {
; OBJ-NEXT: OffsetStart: .text+0x8
; OBJ-NEXT: OffsetStart: .text+0xA
; OBJ-NEXT: ISectStart: 0x0
; OBJ-NEXT: Range: 0x1A
; OBJ-NEXT: Range: 0x1C
; OBJ-NEXT: }
; OBJ-NEXT: }