diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp --- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp +++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp @@ -2274,6 +2274,47 @@ 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_SetFP: + ATS.emitARMWinCFISetFP(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: + ATS.emitARMWinCFINop(MI->getOperand(0).getImm()); + return; + + case ARM::SEH_PrologEnd: + ATS.emitARMWinCFIPrologEnd(); + return; + + case ARM::SEH_EpilogStart: + ATS.emitARMWinCFIEpilogStart(); + return; + + case ARM::SEH_EpilogEnd: + ATS.emitARMWinCFIEpilogEnd(MI->getOperand(0).getImm(), + MI->getOperand(1).getImm()); + return; } MCInst TmpInst; diff --git a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.h b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.h --- a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.h +++ b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.h @@ -214,6 +214,8 @@ unsigned DefSubReg, const TargetRegisterClass *SrcRC, unsigned SrcSubReg) const override; + + int getSEHRegNum(unsigned i) const { return getEncodingValue(i); } }; } // end namespace llvm diff --git a/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp b/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp --- a/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp +++ b/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp @@ -2107,6 +2107,8 @@ case ARM::TCRETURNdi: case ARM::TCRETURNri: { MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); + if (MBBI->getOpcode() == ARM::SEH_EpilogEnd) + MBBI--; assert(MBBI->isReturn() && "Can only insert epilog into returning blocks"); unsigned RetOpcode = MBBI->getOpcode(); @@ -2116,6 +2118,8 @@ // Tail call return: adjust the stack pointer and jump to callee. MBBI = MBB.getLastNonDebugInstr(); + if (MBBI->getOpcode() == ARM::SEH_EpilogEnd) + MBBI--; MachineOperand &JumpTarget = MBBI->getOperand(0); // Jump to label or value in register. diff --git a/llvm/lib/Target/ARM/ARMFrameLowering.cpp b/llvm/lib/Target/ARM/ARMFrameLowering.cpp --- a/llvm/lib/Target/ARM/ARMFrameLowering.cpp +++ b/llvm/lib/Target/ARM/ARMFrameLowering.cpp @@ -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,202 @@ 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(); + const ARMBaseRegisterInfo *RegInfo = Subtarget.getRegisterInfo(); + + switch (Opc) { + default: + errs() << "No SEH Opcode for instruction " << TII.getName(Opc) << "\n"; + std::abort(); + 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 + // These arm 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_SetFP. + 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; + bool Wide = false; + 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; + if (Reg >= 8 && Reg <= 13) + Wide = true; + else if (Opc == ARM::t2LDMIA_UPD && Reg == 14) + Wide = true; + 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 : 0) + .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_SetFP)) + .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_SetFP)) + .addImm(Reg) + .setMIFlags(Flags); + } else { + MIB = BuildMI(MF, DL, TII.get(ARM::SEH_Nop)) + .addImm(/*Wide=*/0) + .setMIFlags(Flags); + } + break; + + case ARM::t2BFC: + MIB = BuildMI(MF, DL, TII.get(ARM::SEH_Nop)) + .addImm(/*Wide=*/1) + .setMIFlags(Flags); + break; + + case ARM::tBX_RET: + case ARM::TCRETURNri: + MIB = BuildMI(MF, DL, TII.get(ARM::SEH_EpilogEnd)) + .addImm(/*Nop=*/1) + .addImm(/*Wide=*/0) + .setMIFlags(Flags); + break; + + case ARM::TCRETURNdi: + MIB = BuildMI(MF, DL, TII.get(ARM::SEH_EpilogEnd)) + .addImm(/*Nop=*/1) + .addImm(/*Wide=*/1) + .setMIFlags(Flags); + break; + } + auto I = MBB->insertAfter(MBBI, MIB); + switch (Opc) { + case ARM::t2LDMIA_RET: + MIB = BuildMI(MF, DL, TII.get(ARM::SEH_EpilogEnd)) + .addImm(/*Nop=*/0) + .addImm(/*Wide=*/0) + .setMIFlags(Flags); + I = MBB->insertAfter(I, MIB); + break; + } + return I; +} + +static bool isSEH(MachineBasicBlock::iterator &MBBI) { + unsigned Opc = MBBI->getOpcode(); + switch (Opc) { + case ARM::SEH_StackAlloc: + case ARM::SEH_SaveRegs: + case ARM::SEH_SetFP: + case ARM::SEH_SaveFRegs: + case ARM::SEH_SaveLR: + case ARM::SEH_Nop: + case ARM::SEH_PrologEnd: + case ARM::SEH_EpilogStart: + case ARM::SEH_EpilogEnd: + return true; + default: + return false; + } +} + +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 && isSEH(Next)) { + MI = std::next(Next); + while (MI != End && isSEH(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, @@ -482,6 +679,7 @@ unsigned NumBytes = MFI.getStackSize(); const std::vector &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. @@ -510,7 +708,8 @@ MachineInstr::FrameSetup); DefCFAOffsetCandidates.addInst(std::prev(MBBI), NumBytes, true); } - DefCFAOffsetCandidates.emitDefCFAOffsets(MBB, dl, TII, HasFP); + if (!NeedsWinCFI) + DefCFAOffsetCandidates.emitDefCFAOffsets(MBB, dl, TII, HasFP); return; } @@ -647,15 +846,36 @@ if (STI.isTargetWindows() && WindowsRequiresStackProbe(MF, NumBytes)) { uint32_t NumWords = NumBytes >> 2; - 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); + MachineInstrBuilder Instr, SEH; + if (NumWords < 65536) { + Instr = BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MOVi16), ARM::R4) + .addImm(NumWords) + .setMIFlags(MachineInstr::FrameSetup) + .add(predOps(ARMCC::AL)); + } else { + Instr = BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MOVi16), ARM::R4) + .addImm(NumWords & 0xffff) + .setMIFlags(MachineInstr::FrameSetup) + .add(predOps(ARMCC::AL)); + if (NeedsWinCFI) { + bool Wide = (NumWords & 0xffff) >= 256; + SEH = BuildMI(MF, dl, TII.get(ARM::SEH_Nop)) + .addImm(Wide ? 1 : 0) + .setMIFlags(MachineInstr::FrameSetup); + MBB.insertAfter(Instr, SEH); + } + Instr = BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MOVTi16), ARM::R4) + .addReg(ARM::R4) + .addImm(NumWords >> 16) + .setMIFlags(MachineInstr::FrameSetup) + .add(predOps(ARMCC::AL)); + } + if (NeedsWinCFI) { + SEH = BuildMI(MF, dl, TII.get(ARM::SEH_Nop)) + .addImm(/*Wide=*/1) + .setMIFlags(MachineInstr::FrameSetup); + MBB.insertAfter(Instr, SEH); + } switch (TM.getCodeModel()) { case CodeModel::Tiny: @@ -663,31 +883,63 @@ case CodeModel::Small: case CodeModel::Medium: case CodeModel::Kernel: - BuildMI(MBB, MBBI, dl, TII.get(ARM::tBL)) - .add(predOps(ARMCC::AL)) - .addExternalSymbol("__chkstk") - .addReg(ARM::R4, RegState::Implicit) - .setMIFlags(MachineInstr::FrameSetup); + Instr = BuildMI(MBB, MBBI, dl, TII.get(ARM::tBL)) + .add(predOps(ARMCC::AL)) + .addExternalSymbol("__chkstk") + .addReg(ARM::R4, RegState::Implicit) + .setMIFlags(MachineInstr::FrameSetup); + if (NeedsWinCFI) { + SEH = BuildMI(MF, dl, TII.get(ARM::SEH_Nop)) + .addImm(/*Wide=*/1) + .setMIFlags(MachineInstr::FrameSetup); + MBB.insertAfter(Instr, SEH); + } break; case CodeModel::Large: - BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MOVi32imm), ARM::R12) - .addExternalSymbol("__chkstk") - .setMIFlags(MachineInstr::FrameSetup); + Instr = BuildMI(MBB, MBBI, dl, TII.get(ARM::t2MOVi32imm), ARM::R12) + .addExternalSymbol("__chkstk") + .setMIFlags(MachineInstr::FrameSetup); + if (NeedsWinCFI) { + // t2MOVi32imm above expands into two instructions; append two + // SEH_Nop after the pseudo instruction above. They won't get + // interleaved between the final movw/movt instructions, but it + // doesn't make any practical difference as long as the prolog/epilog + // start/end are in the right places. + for (int I = 0; I < 2; I++) { + SEH = BuildMI(MF, dl, TII.get(ARM::SEH_Nop)) + .addImm(/*Wide=*/1) + .setMIFlags(MachineInstr::FrameSetup); + MBB.insertAfter(Instr, SEH); + } + } - BuildMI(MBB, MBBI, dl, TII.get(ARM::tBLXr)) - .add(predOps(ARMCC::AL)) - .addReg(ARM::R12, RegState::Kill) - .addReg(ARM::R4, RegState::Implicit) - .setMIFlags(MachineInstr::FrameSetup); + Instr = BuildMI(MBB, MBBI, dl, TII.get(ARM::tBLXr)) + .add(predOps(ARMCC::AL)) + .addReg(ARM::R12, RegState::Kill) + .addReg(ARM::R4, RegState::Implicit) + .setMIFlags(MachineInstr::FrameSetup); + if (NeedsWinCFI) { + SEH = BuildMI(MF, dl, TII.get(ARM::SEH_Nop)) + .addImm(/*Wide=*/0) + .setMIFlags(MachineInstr::FrameSetup); + MBB.insertAfter(Instr, SEH); + } 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()); + 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; } @@ -727,27 +979,29 @@ 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); + } } } // 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) { @@ -792,7 +1046,7 @@ case ARM::R10: case ARM::R11: case ARM::R12: - if (STI.splitFramePushPop(MF)) { + if (STI.splitFramePushPop(MF) && !NeedsWinCFI) { unsigned DwarfReg = MRI->getDwarfRegNum( Reg == ARM::R12 ? ARM::RA_AUTH_CODE : Reg, true); unsigned Offset = MFI.getObjectOffset(FI); @@ -815,7 +1069,8 @@ Register Reg = Entry.getReg(); int FI = Entry.getFrameIdx(); if ((Reg >= ARM::D0 && Reg <= ARM::D31) && - (Reg < ARM::D8 || Reg >= ARM::D8 + AFI->getNumAlignedDPRCS2Regs())) { + (Reg < ARM::D8 || Reg >= ARM::D8 + AFI->getNumAlignedDPRCS2Regs()) && + !NeedsWinCFI) { unsigned DwarfReg = MRI->getDwarfRegNum(Reg, true); unsigned Offset = MFI.getObjectOffset(FI); unsigned CFIIndex = MF.addFrameInst( @@ -831,7 +1086,8 @@ // 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() - @@ -861,9 +1117,19 @@ // -- out lower bits in r4 // mov sp, r4 // FIXME: It will be better just to find spare register here. - BuildMI(MBB, MBBI, dl, TII.get(ARM::tMOVr), ARM::R4) - .addReg(ARM::SP, RegState::Kill) - .add(predOps(ARMCC::AL)); + auto Instr = BuildMI(MBB, MBBI, dl, TII.get(ARM::tMOVr), ARM::R4) + .addReg(ARM::SP, RegState::Kill) + .add(predOps(ARMCC::AL)); + if (NeedsWinCFI) { + // This "mov r4, sp" would be mapped to ".seh_set_fp r4", but r4 doesn't + // contain a stable frame pointer, thus manually insert a .seh_nop + // here instead. + auto SEH = BuildMI(MF, dl, TII.get(ARM::SEH_Nop)) + .addImm(/*Wide=*/0) + .setMIFlags(MachineInstr::FrameSetup); + MBB.insertAfter(Instr, SEH); + } + emitAligningInstructions(MF, AFI, TII, MBB, MBBI, dl, ARM::R4, MaxAlign, false); BuildMI(MBB, MBBI, dl, TII.get(ARM::tMOVr), ARM::SP) @@ -896,6 +1162,15 @@ // checks for hasVarSizedObjects. if (MFI.hasVarSizedObjects()) AFI->setShouldRestoreSPFromFP(true); + + // The very last FrameSetup instruction indicates the end of prologue. Emit a + // SEH opcode indicating the prologue end. + 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); + } } void ARMFrameLowering::emitEpilogue(MachineFunction &MF, @@ -928,7 +1203,14 @@ 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, @@ -944,6 +1226,12 @@ ++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() + @@ -1030,6 +1318,9 @@ 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); } /// getFrameIndexReference - Provide a base+offset reference to an FI slot for @@ -2597,17 +2888,19 @@ // 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) { @@ -2809,13 +3102,15 @@ // 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) { @@ -2871,9 +3166,11 @@ } // 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)); @@ -2895,20 +3192,22 @@ } // 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); - - // 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); + 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); + } // Organizing MBB lists PostStackMBB->addSuccessor(&PrologueMBB); diff --git a/llvm/lib/Target/ARM/ARMInstrInfo.td b/llvm/lib/Target/ARM/ARMInstrInfo.td --- a/llvm/lib/Target/ARM/ARMInstrInfo.td +++ b/llvm/lib/Target/ARM/ARMInstrInfo.td @@ -6476,3 +6476,22 @@ 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_SetFP : 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<[]>; + 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 i32imm:$nop, i32imm:$wide), NoItinerary, []>, Sched<[]>; +} diff --git a/llvm/lib/Target/ARM/ARMSubtarget.cpp b/llvm/lib/Target/ARM/ARMSubtarget.cpp --- a/llvm/lib/Target/ARM/ARMSubtarget.cpp +++ b/llvm/lib/Target/ARM/ARMSubtarget.cpp @@ -407,6 +407,9 @@ return false; if (disablePostRAScheduler()) return false; + // PostRAScheduler shuffles the SEH opcodes wrt the rest of the prologue. + if (TM.getMCAsmInfo() && TM.getMCAsmInfo()->usesWindowsCFI()) + return false; // Thumb1 cores will generally not benefit from post-ra scheduling return !isThumb1Only(); } diff --git a/llvm/test/CodeGen/ARM/Windows/wineh-opcodes.ll b/llvm/test/CodeGen/ARM/Windows/wineh-opcodes.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/ARM/Windows/wineh-opcodes.ll @@ -0,0 +1,287 @@ +;; Check that this produces the expected assembly output +; RUN: llc -mtriple=thumbv7-windows -o - %s | 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 + +; 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 {r4, lr} +; CHECK-NEXT: .seh_save_regs {r4, lr} +; CHECK-NEXT: .seh_endprologue +; CHECK-NEXT: bl other + +; CHECK: .seh_startepilogue +; CHECK-NEXT: pop {r4, pc} +; CHECK-NEXT: .seh_save_regs {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 {r4, lr} +; CHECK-NEXT: .seh_save_regs {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 other +; CHECK-NEXT: .seh_endepilogue_nop_w +; 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 other +; CHECK-NEXT: .seh_endepilogue_nop_w +; 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_endepilogue_nop +; 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: 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 {r4, r5, r6, lr} +; CHECK-NEXT: .seh_save_regs {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 {r4, r5, r6, pc} +; CHECK-NEXT: .seh_save_regs {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 {r4, r5, r6, lr} +; CHECK-NEXT: .seh_save_regs {r4-r6, lr} +; CHECK-NEXT: movs 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 {r4, r5, r6, pc} +; CHECK-NEXT: .seh_save_regs {r4-r6, lr} +; CHECK-NEXT: .seh_endepilogue +; CHECK-NEXT: .seh_endfunclet +; 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 {r4, r5, r6, lr} +; CHECK-NEXT: .seh_save_regs {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 {r4, r5, r6, pc} +; CHECK-NEXT: .seh_save_regs {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)