diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h index 79c715e3820a6..2a0095c9b7613 100644 --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -1024,8 +1024,7 @@ class LLVM_ABI MCStreamer { /// for the frame. We cannot use the End marker, as it is not set at the /// point of emitting .xdata, in order to indicate that the frame is active. virtual void emitWinCFIFuncletOrFuncEnd(SMLoc Loc = SMLoc()); - virtual void emitWinCFIStartChained(SMLoc Loc = SMLoc()); - virtual void emitWinCFIEndChained(SMLoc Loc = SMLoc()); + virtual void emitWinCFISplitChained(SMLoc Loc = SMLoc()); virtual void emitWinCFIPushReg(MCRegister Register, SMLoc Loc = SMLoc()); virtual void emitWinCFISetFrame(MCRegister Register, unsigned Offset, SMLoc Loc = SMLoc()); diff --git a/llvm/include/llvm/MC/MCWinEH.h b/llvm/include/llvm/MC/MCWinEH.h index 1bbfb9f59bc4c..1118f5b1def97 100644 --- a/llvm/include/llvm/MC/MCWinEH.h +++ b/llvm/include/llvm/MC/MCWinEH.h @@ -59,7 +59,7 @@ struct FrameInfo { uint8_t Version = DefaultVersion; int LastFrameInst = -1; - const FrameInfo *ChainedParent = nullptr; + FrameInfo *ChainedParent = nullptr; std::vector Instructions; struct Epilog { std::vector Instructions; @@ -90,9 +90,9 @@ struct FrameInfo { FrameInfo(const MCSymbol *Function, const MCSymbol *BeginFuncEHLabel) : Begin(BeginFuncEHLabel), Function(Function) {} FrameInfo(const MCSymbol *Function, const MCSymbol *BeginFuncEHLabel, - const FrameInfo *ChainedParent) + FrameInfo *ChainedParent) : Begin(BeginFuncEHLabel), Function(Function), - ChainedParent(ChainedParent) {} + Version(ChainedParent->Version), ChainedParent(ChainedParent) {} bool empty() const { if (!Instructions.empty()) diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index be8c022f39ad1..bee7badbe0bc9 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -375,8 +375,7 @@ class MCAsmStreamer final : public MCStreamer { void emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) override; void emitWinCFIEndProc(SMLoc Loc) override; void emitWinCFIFuncletOrFuncEnd(SMLoc Loc) override; - void emitWinCFIStartChained(SMLoc Loc) override; - void emitWinCFIEndChained(SMLoc Loc) override; + void emitWinCFISplitChained(SMLoc Loc) override; void emitWinCFIPushReg(MCRegister Register, SMLoc Loc) override; void emitWinCFISetFrame(MCRegister Register, unsigned Offset, SMLoc Loc) override; @@ -2175,17 +2174,10 @@ void MCAsmStreamer::emitWinCFIFuncletOrFuncEnd(SMLoc Loc) { EmitEOL(); } -void MCAsmStreamer::emitWinCFIStartChained(SMLoc Loc) { - MCStreamer::emitWinCFIStartChained(Loc); +void MCAsmStreamer::emitWinCFISplitChained(SMLoc Loc) { + MCStreamer::emitWinCFISplitChained(Loc); - OS << "\t.seh_startchained"; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFIEndChained(SMLoc Loc) { - MCStreamer::emitWinCFIEndChained(Loc); - - OS << "\t.seh_endchained"; + OS << "\t.seh_splitchained"; EmitEOL(); } diff --git a/llvm/lib/MC/MCParser/COFFAsmParser.cpp b/llvm/lib/MC/MCParser/COFFAsmParser.cpp index 5dd79946d8779..b795eca73cce5 100644 --- a/llvm/lib/MC/MCParser/COFFAsmParser.cpp +++ b/llvm/lib/MC/MCParser/COFFAsmParser.cpp @@ -80,10 +80,8 @@ class COFFAsmParser : public MCAsmParserExtension { ".seh_endproc"); addDirectiveHandler<&COFFAsmParser::parseSEHDirectiveEndFuncletOrFunc>( ".seh_endfunclet"); - addDirectiveHandler<&COFFAsmParser::parseSEHDirectiveStartChained>( - ".seh_startchained"); - addDirectiveHandler<&COFFAsmParser::parseSEHDirectiveEndChained>( - ".seh_endchained"); + addDirectiveHandler<&COFFAsmParser::parseSEHDirectiveSplitChained>( + ".seh_splitchained"); addDirectiveHandler<&COFFAsmParser::parseSEHDirectiveHandler>( ".seh_handler"); addDirectiveHandler<&COFFAsmParser::parseSEHDirectiveHandlerData>( @@ -143,8 +141,7 @@ class COFFAsmParser : public MCAsmParserExtension { bool parseSEHDirectiveStartProc(StringRef, SMLoc); bool parseSEHDirectiveEndProc(StringRef, SMLoc); bool parseSEHDirectiveEndFuncletOrFunc(StringRef, SMLoc); - bool parseSEHDirectiveStartChained(StringRef, SMLoc); - bool parseSEHDirectiveEndChained(StringRef, SMLoc); + bool parseSEHDirectiveSplitChained(StringRef, SMLoc); bool parseSEHDirectiveHandler(StringRef, SMLoc); bool parseSEHDirectiveHandlerData(StringRef, SMLoc); bool parseSEHDirectiveAllocStack(StringRef, SMLoc); @@ -685,15 +682,9 @@ bool COFFAsmParser::parseSEHDirectiveEndFuncletOrFunc(StringRef, SMLoc Loc) { return false; } -bool COFFAsmParser::parseSEHDirectiveStartChained(StringRef, SMLoc Loc) { +bool COFFAsmParser::parseSEHDirectiveSplitChained(StringRef, SMLoc Loc) { Lex(); - getStreamer().emitWinCFIStartChained(Loc); - return false; -} - -bool COFFAsmParser::parseSEHDirectiveEndChained(StringRef, SMLoc Loc) { - Lex(); - getStreamer().emitWinCFIEndChained(Loc); + getStreamer().emitWinCFISplitChained(Loc); return false; } diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp index bc7398120096e..db22382dbe86b 100644 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -749,13 +749,14 @@ void MCStreamer::emitWinCFIEndProc(SMLoc Loc) { WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc); if (!CurFrame) return; - if (CurFrame->ChainedParent) - getContext().reportError(Loc, "Not all chained regions terminated!"); MCSymbol *Label = emitCFILabel(); CurFrame->End = Label; - if (!CurFrame->FuncletOrFuncEnd) - CurFrame->FuncletOrFuncEnd = CurFrame->End; + const MCSymbol **FuncletOrFuncEndPtr = + CurFrame->ChainedParent ? &CurFrame->ChainedParent->FuncletOrFuncEnd + : &CurFrame->FuncletOrFuncEnd; + if (!*FuncletOrFuncEndPtr) + *FuncletOrFuncEndPtr = CurFrame->End; for (size_t I = CurrentProcWinFrameInfoStartIndex, E = WinFrameInfos.size(); I != E; ++I) @@ -767,38 +768,38 @@ void MCStreamer::emitWinCFIFuncletOrFuncEnd(SMLoc Loc) { WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc); if (!CurFrame) return; - if (CurFrame->ChainedParent) - getContext().reportError(Loc, "Not all chained regions terminated!"); MCSymbol *Label = emitCFILabel(); - CurFrame->FuncletOrFuncEnd = Label; + const MCSymbol **FuncletOrFuncEndPtr = + CurFrame->ChainedParent ? &CurFrame->ChainedParent->FuncletOrFuncEnd + : &CurFrame->FuncletOrFuncEnd; + *FuncletOrFuncEndPtr = Label; } -void MCStreamer::emitWinCFIStartChained(SMLoc Loc) { +void MCStreamer::emitWinCFISplitChained(SMLoc Loc) { WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc); if (!CurFrame) return; - MCSymbol *StartProc = emitCFILabel(); - - WinFrameInfos.emplace_back(std::make_unique( - CurFrame->Function, StartProc, CurFrame)); - CurrentWinFrameInfo = WinFrameInfos.back().get(); - CurrentWinFrameInfo->TextSection = getCurrentSectionOnly(); -} - -void MCStreamer::emitWinCFIEndChained(SMLoc Loc) { - WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc); - if (!CurFrame) - return; - if (!CurFrame->ChainedParent) + if (!CurFrame->PrologEnd && !CurFrame->ChainedParent) return getContext().reportError( - Loc, "End of a chained region outside a chained region!"); + Loc, "can't split into a new chained region (.seh_splitchained) in the " + "middle of a prolog in " + + CurFrame->Function->getName()); MCSymbol *Label = emitCFILabel(); + // Complete the current frame before starting a new, chained one. CurFrame->End = Label; - CurrentWinFrameInfo = const_cast(CurFrame->ChainedParent); + + // All chained frames point to the same parent. + WinEH::FrameInfo *ChainedParent = + CurFrame->ChainedParent ? CurFrame->ChainedParent : CurFrame; + + WinFrameInfos.emplace_back(std::make_unique( + CurFrame->Function, Label, ChainedParent)); + CurrentWinFrameInfo = WinFrameInfos.back().get(); + CurrentWinFrameInfo->TextSection = getCurrentSectionOnly(); } void MCStreamer::emitWinEHHandler(const MCSymbol *Sym, bool Unwind, bool Except, @@ -806,9 +807,10 @@ void MCStreamer::emitWinEHHandler(const MCSymbol *Sym, bool Unwind, bool Except, WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc); if (!CurFrame) return; - if (CurFrame->ChainedParent) - return getContext().reportError( - Loc, "Chained unwind areas can't have handlers!"); + + // Handlers are always associated with the parent frame. + CurFrame = CurFrame->ChainedParent ? CurFrame->ChainedParent : CurFrame; + CurFrame->ExceptionHandler = Sym; if (!Except && !Unwind) getContext().reportError(Loc, "Don't know what kind of handler this is!"); @@ -822,8 +824,6 @@ void MCStreamer::emitWinEHHandlerData(SMLoc Loc) { WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc); if (!CurFrame) return; - if (CurFrame->ChainedParent) - getContext().reportError(Loc, "Chained unwind areas can't have handlers!"); } void MCStreamer::emitCGProfileEntry(const MCSymbolRefExpr *From, @@ -994,7 +994,8 @@ void MCStreamer::emitWinCFIBeginEpilogue(SMLoc Loc) { if (!CurFrame) return; - if (!CurFrame->PrologEnd) + // Chained unwinds aren't guaranteed to have a prolog. + if (!CurFrame->PrologEnd && !CurFrame->ChainedParent) return getContext().reportError( Loc, "starting epilogue (.seh_startepilogue) before prologue has ended " "(.seh_endprologue) in " + diff --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp index 8111ccb8bc69c..75271b08d3252 100644 --- a/llvm/lib/MC/MCWin64EH.cpp +++ b/llvm/lib/MC/MCWin64EH.cpp @@ -22,8 +22,7 @@ class MCSection; /// MCExpr that represents the epilog unwind code in an unwind table. class MCUnwindV2EpilogTargetExpr final : public MCTargetExpr { - const MCSymbol *Function; - const MCSymbol *FunctionEnd; + const WinEH::FrameInfo &FrameInfo; const MCSymbol *UnwindV2Start; const MCSymbol *EpilogEnd; uint8_t EpilogSize; @@ -32,9 +31,11 @@ class MCUnwindV2EpilogTargetExpr final : public MCTargetExpr { MCUnwindV2EpilogTargetExpr(const WinEH::FrameInfo &FrameInfo, const WinEH::FrameInfo::Epilog &Epilog, uint8_t EpilogSize_) - : Function(FrameInfo.Function), FunctionEnd(FrameInfo.FuncletOrFuncEnd), - UnwindV2Start(Epilog.UnwindV2Start), EpilogEnd(Epilog.End), - EpilogSize(EpilogSize_), Loc(Epilog.Loc) {} + : FrameInfo(FrameInfo), UnwindV2Start(Epilog.UnwindV2Start), + EpilogEnd(Epilog.End), EpilogSize(EpilogSize_), Loc(Epilog.Loc) { + assert(UnwindV2Start && "Epilog must have a start"); + assert(EpilogEnd && "Epilog must have an end"); + } public: static MCUnwindV2EpilogTargetExpr * @@ -250,6 +251,10 @@ static void EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) { // so, although there are terminators that are large than 1 byte, the // starting address of the terminator instruction will always be considered // inside the epilog). + assert( + LastEpilog.UnwindV2Start && + "If unwind v2 is enabled, epilog must have a unwind v2 start marker"); + assert(LastEpilog.End && "Epilog must have an end"); auto MaybeSize = GetOptionalAbsDifference( OS->getAssembler(), LastEpilog.End, LastEpilog.UnwindV2Start); if (!MaybeSize) { @@ -270,9 +275,14 @@ static void EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) { // If the last epilog is at the end of the function, we can use a special // encoding for it. Because of our +1 trick for the size, this will only // work where that final terminator instruction is 1 byte long. - auto LastEpilogToFuncEnd = GetOptionalAbsDifference( - OS->getAssembler(), info->FuncletOrFuncEnd, LastEpilog.UnwindV2Start); - LastEpilogIsAtEnd = (LastEpilogToFuncEnd == EpilogSize); + // NOTE: At the point where the unwind info is emitted, the function may not + // have ended yet (e.g., if there is EH Handler Data), so assume that we + // aren't at the end (since we can't calculate it). + if (info->End) { + auto LastEpilogToFuncEnd = GetOptionalAbsDifference( + OS->getAssembler(), info->End, LastEpilog.UnwindV2Start); + LastEpilogIsAtEnd = (LastEpilogToFuncEnd == EpilogSize); + } // If we have an odd number of epilog codes, we need to add a padding code. size_t numEpilogCodes = @@ -384,28 +394,28 @@ bool MCUnwindV2EpilogTargetExpr::evaluateAsRelocatableImpl( MCValue &Res, const MCAssembler *Asm) const { // Calculate the offset to this epilog, and validate it's within the allowed // range. - auto Offset = GetOptionalAbsDifference(*Asm, FunctionEnd, UnwindV2Start); + auto Offset = GetOptionalAbsDifference(*Asm, FrameInfo.End, UnwindV2Start); if (!Offset) { Asm->getContext().reportError( Loc, "Failed to evaluate epilog offset for Unwind v2 in " + - Function->getName()); + FrameInfo.Function->getName()); return false; } assert(*Offset > 0); constexpr uint16_t MaxEpilogOffset = 0x0fff; if (*Offset > MaxEpilogOffset) { Asm->getContext().reportError( - Loc, - "Epilog offset is too large for Unwind v2 in " + Function->getName()); + Loc, "Epilog offset is too large for Unwind v2 in " + + FrameInfo.Function->getName()); return false; } - // Sanity check that all epilogs are the same size. + // Validate that all epilogs are the same size. auto Size = GetOptionalAbsDifference(*Asm, EpilogEnd, UnwindV2Start); if (Size != (EpilogSize - 1)) { Asm->getContext().reportError( Loc, "Size of this epilog does not match size of last epilog in " + - Function->getName()); + FrameInfo.Function->getName()); return false; } diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFStreamer.cpp b/llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFStreamer.cpp index 1ef10928c05d8..e9d1ce3aa593b 100644 --- a/llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFStreamer.cpp +++ b/llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFStreamer.cpp @@ -44,8 +44,11 @@ void X86WinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) { // We have to emit the unwind info now, because this directive // actually switches to the .xdata section. - if (WinEH::FrameInfo *CurFrame = getCurrentWinFrameInfo()) + if (WinEH::FrameInfo *CurFrame = getCurrentWinFrameInfo()) { + // Handlers are always associated with the parent frame. + CurFrame = CurFrame->ChainedParent ? CurFrame->ChainedParent : CurFrame; EHStreamer.EmitUnwindInfo(*this, CurFrame, /* HandlerData = */ true); + } } void X86WinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) { diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td index ec31675731b79..a1266ee3b3974 100644 --- a/llvm/lib/Target/X86/X86InstrCompiler.td +++ b/llvm/lib/Target/X86/X86InstrCompiler.td @@ -272,6 +272,12 @@ let isPseudo = 1, isMeta = 1, SchedRW = [WriteSystem] in { "#SEH_UnwindV2Start", []>; } +// Chain instructions: +let isPseudo = 1, isMeta = 1, SchedRW = [WriteSystem] in { + def SEH_SplitChained : I<0, Pseudo, (outs), (ins), + "#SEH_SplitChained", []>; +} + //===----------------------------------------------------------------------===// // Pseudo instructions used by KCFI. //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/X86/X86MCInstLower.cpp b/llvm/lib/Target/X86/X86MCInstLower.cpp index 481a9be8374ab..46bd8e0332650 100644 --- a/llvm/lib/Target/X86/X86MCInstLower.cpp +++ b/llvm/lib/Target/X86/X86MCInstLower.cpp @@ -1795,6 +1795,10 @@ void X86AsmPrinter::EmitSEHInstruction(const MachineInstr *MI) { OutStreamer->emitWinCFIUnwindVersion(MI->getOperand(0).getImm()); break; + case X86::SEH_SplitChained: + OutStreamer->emitWinCFISplitChained(); + break; + default: llvm_unreachable("expected SEH_ instruction"); } @@ -2526,6 +2530,7 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) { case X86::SEH_EndEpilogue: case X86::SEH_UnwindV2Start: case X86::SEH_UnwindVersion: + case X86::SEH_SplitChained: EmitSEHInstruction(MI); return; diff --git a/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp b/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp index 9bf0abb018c99..45334d5e2ebe7 100644 --- a/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp +++ b/llvm/lib/Target/X86/X86WinEHUnwindV2.cpp @@ -18,6 +18,7 @@ #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/TargetFrameLowering.h" #include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/IR/DiagnosticInfo.h" @@ -32,17 +33,35 @@ STATISTIC(MeetsUnwindV2Criteria, STATISTIC(FailsUnwindV2Criteria, "Number of functions that fail Unwind v2 criteria"); -static cl::opt MaximumUnwindCodes( - "x86-wineh-unwindv2-max-unwind-codes", cl::Hidden, - cl::desc("Maximum number of unwind codes permitted in each unwind info."), - cl::init(UINT8_MAX)); +static cl::opt + UnwindCodeThreshold("x86-wineh-unwindv2-unwind-codes-threshold", cl::Hidden, + cl::desc("Maximum number of unwind codes before " + "splitting into a new unwind info."), + cl::init(UINT8_MAX)); static cl::opt ForceMode("x86-wineh-unwindv2-force-mode", cl::Hidden, cl::desc("Overwrites the Unwind v2 mode for testing purposes.")); +static cl::opt InstructionCountThreshold( + "x86-wineh-unwindv2-instruction-count-threshold", cl::Hidden, + cl::desc("Maximum number of (approximate) instructions before splitting " + "into a new unwind info."), + cl::init(600)); + namespace { +struct EpilogInfo { + MachineInstr *UnwindV2StartLocation; + unsigned ApproximateInstructionPosition; +}; + +struct FrameInfo { + unsigned ApproximatePrologCodeCount; + unsigned ApproximateInstructionCount; + SmallVector EpilogInfos; +}; + class X86WinEHUnwindV2 : public MachineFunctionPass { public: static char ID; @@ -57,9 +76,14 @@ class X86WinEHUnwindV2 : public MachineFunctionPass { private: /// Rejects the current function due to an internal error within LLVM. - static bool rejectCurrentFunctionInternalError(const MachineFunction &MF, - WinX64EHUnwindV2Mode Mode, - StringRef Reason); + static std::nullopt_t rejectCurrentFunctionInternalError( + const MachineFunction &MF, WinX64EHUnwindV2Mode Mode, StringRef Reason); + + // Continues running the analysis on the given function or funclet. + static std::optional + runAnalysisOnFuncOrFunclet(MachineFunction &MF, + MachineFunction::iterator &Iter, + WinX64EHUnwindV2Mode Mode); }; enum class FunctionState { @@ -89,14 +113,11 @@ DebugLoc findDebugLoc(const MachineBasicBlock &MBB) { return DebugLoc::getUnknown(); } -bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { - WinX64EHUnwindV2Mode Mode = - ForceMode.getNumOccurrences() - ? static_cast(ForceMode.getValue()) - : MF.getFunction().getParent()->getWinX64EHUnwindV2Mode(); - - if (Mode == WinX64EHUnwindV2Mode::Disabled) - return false; +std::optional +X86WinEHUnwindV2::runAnalysisOnFuncOrFunclet(MachineFunction &MF, + MachineFunction::iterator &Iter, + WinX64EHUnwindV2Mode Mode) { + const TargetFrameLowering &TFL = *MF.getSubtarget().getFrameLowering(); // Current state of processing the function. We'll assume that all functions // start with a prolog. @@ -108,17 +129,29 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { bool HasSetFrame = false; unsigned ApproximatePrologCodeCount = 0; - // Requested changes. - SmallVector UnwindV2StartLocations; + SmallVector EpilogInfos; + + unsigned ApproximateInstructionCount = 0; + + for (; Iter != MF.end(); ++Iter) { + MachineBasicBlock &MBB = *Iter; + + // If we're already been processing a function, then come across a funclet + // then break since the funclet will get a fresh frame info. + if (MBB.isEHFuncletEntry() && State != FunctionState::InProlog) + break; - for (MachineBasicBlock &MBB : MF) { // Current epilog information. We assume that epilogs cannot cross basic // block boundaries. unsigned PoppedRegCount = 0; bool HasStackDealloc = false; + bool HasSetFrameBack = false; MachineInstr *UnwindV2StartLocation = nullptr; for (MachineInstr &MI : MBB) { + if (!MI.isPseudo() && !MI.isMetaInstruction()) + ApproximateInstructionCount++; + switch (MI.getOpcode()) { // // Prolog handling. @@ -192,7 +225,8 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { // epilog. if (!UnwindV2StartLocation) UnwindV2StartLocation = &MI; - UnwindV2StartLocations.push_back(UnwindV2StartLocation); + EpilogInfos.push_back( + {UnwindV2StartLocation, ApproximateInstructionCount}); State = FunctionState::FinishedEpilog; break; @@ -214,6 +248,7 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { MF, Mode, "Cannot set the frame back after the stack " "allocation has been deallocated"); + HasSetFrameBack = true; } else if (State == FunctionState::FinishedEpilog) return rejectCurrentFunctionInternalError( MF, Mode, "Unexpected mov instruction after the epilog"); @@ -251,6 +286,12 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { // registers, then assume it was used to adjust the stack pointer. HasStackDealloc = true; } else { + // Special case: no explicit stack dealloc is required if SetFrame + // was used and the function has a frame pointer. + if (PoppedRegCount == 0 && HasStackAlloc && !HasStackDealloc && + HasSetFrameBack && TFL.hasFP(MF)) + HasStackDealloc = true; + // After the stack pointer has been adjusted, the epilog must // POP each register in reverse order of the PUSHes in the prolog. PoppedRegCount++; @@ -305,37 +346,66 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { } } - if (UnwindV2StartLocations.empty()) - return false; + return FrameInfo{ApproximatePrologCodeCount, ApproximateInstructionCount, + EpilogInfos}; +} - MachineBasicBlock &FirstMBB = MF.front(); - // Assume +1 for the "header" UOP_Epilog that contains the epilog size, and - // that we won't be able to use the "last epilog at the end of function" - // optimization. - if (ApproximatePrologCodeCount + UnwindV2StartLocations.size() + 1 > - static_cast(MaximumUnwindCodes)) { - if (Mode == WinX64EHUnwindV2Mode::Required) - MF.getFunction().getContext().diagnose(DiagnosticInfoGenericWithLoc( - "Windows x64 Unwind v2 is required, but the function '" + - MF.getName() + - "' has too many unwind codes. Try splitting the function or " - "reducing the number of places where it exits early with a tail " - "call.", - MF.getFunction(), findDebugLoc(FirstMBB))); - - FailsUnwindV2Criteria++; +bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { + WinX64EHUnwindV2Mode Mode = + ForceMode.getNumOccurrences() + ? static_cast(ForceMode.getValue()) + : MF.getFunction().getParent()->getWinX64EHUnwindV2Mode(); + + if (Mode == WinX64EHUnwindV2Mode::Disabled) return false; + + // Requested changes. + SmallVector FrameInfos; + MachineFunction::iterator Iter = MF.begin(); + while (Iter != MF.end()) { + auto FI = runAnalysisOnFuncOrFunclet(MF, Iter, Mode); + if (!FI) + return false; + if (!FI->EpilogInfos.empty()) + FrameInfos.push_back(std::move(*FI)); } + if (FrameInfos.empty()) + return false; + MeetsUnwindV2Criteria++; - // Emit the pseudo instruction that marks the start of each epilog. const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); - for (MachineInstr *MI : UnwindV2StartLocations) { - BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), - TII->get(X86::SEH_UnwindV2Start)); + for (auto &FI : FrameInfos) { + // Walk the list of epilogs backwards and add new SEH pseudo instructions: + // * SEH_UnwindV2Start at the start of each epilog. + // * If the current instruction is too far away from where the last unwind + // info ended OR there are too many unwind codes in the info, then add + // SEH_SplitChained to finish the current info. + unsigned LastUnwindInfoEndPosition = FI.ApproximateInstructionCount; + unsigned UnwindCodeCount = FI.ApproximatePrologCodeCount + 1; + for (auto &Info : llvm::reverse(FI.EpilogInfos)) { + MachineBasicBlock &MBB = *Info.UnwindV2StartLocation->getParent(); + BuildMI(MBB, Info.UnwindV2StartLocation, + Info.UnwindV2StartLocation->getDebugLoc(), + TII->get(X86::SEH_UnwindV2Start)); + + if ((LastUnwindInfoEndPosition - Info.ApproximateInstructionPosition >= + InstructionCountThreshold) || + (UnwindCodeCount >= UnwindCodeThreshold)) { + BuildMI(&MBB, Info.UnwindV2StartLocation->getDebugLoc(), + TII->get(X86::SEH_SplitChained)); + LastUnwindInfoEndPosition = Info.ApproximateInstructionPosition; + // Doesn't reset to 0, as the prolog unwind codes are now in this info. + UnwindCodeCount = FI.ApproximatePrologCodeCount + 1; + } + + UnwindCodeCount++; + } } + // Note that the function is using Unwind v2. + MachineBasicBlock &FirstMBB = MF.front(); BuildMI(FirstMBB, FirstMBB.front(), findDebugLoc(FirstMBB), TII->get(X86::SEH_UnwindVersion)) .addImm(2); @@ -343,7 +413,7 @@ bool X86WinEHUnwindV2::runOnMachineFunction(MachineFunction &MF) { return true; } -bool X86WinEHUnwindV2::rejectCurrentFunctionInternalError( +std::nullopt_t X86WinEHUnwindV2::rejectCurrentFunctionInternalError( const MachineFunction &MF, WinX64EHUnwindV2Mode Mode, StringRef Reason) { if (Mode == WinX64EHUnwindV2Mode::Required) reportFatalInternalError("Windows x64 Unwind v2 is required, but LLVM has " @@ -351,5 +421,5 @@ bool X86WinEHUnwindV2::rejectCurrentFunctionInternalError( MF.getName() + "': " + Reason); FailsUnwindV2Criteria++; - return false; + return std::nullopt; } diff --git a/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_print.txt b/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_print.txt index 6327cffb77e74..19263a6968ef0 100644 --- a/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_print.txt +++ b/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_print.txt @@ -1604,6 +1604,7 @@ Key: SEH_PushReg: [ 0.00 0.00 ] Key: SEH_SaveReg: [ 0.00 0.00 ] Key: SEH_SaveXMM: [ 0.00 0.00 ] Key: SEH_SetFrame: [ 0.00 0.00 ] +Key: SEH_SplitChained: [ 0.00 0.00 ] Key: SEH_StackAlign: [ 0.00 0.00 ] Key: SEH_StackAlloc: [ 0.00 0.00 ] Key: SEH_UnwindV: [ 0.00 0.00 ] diff --git a/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_wo=0.5_print.txt b/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_wo=0.5_print.txt index 4409e6dbd5c72..ce3e3e72ceedc 100644 --- a/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_wo=0.5_print.txt +++ b/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_wo=0.5_print.txt @@ -1604,6 +1604,7 @@ Key: SEH_PushReg: [ 0.00 0.00 ] Key: SEH_SaveReg: [ 0.00 0.00 ] Key: SEH_SaveXMM: [ 0.00 0.00 ] Key: SEH_SetFrame: [ 0.00 0.00 ] +Key: SEH_SplitChained: [ 0.00 0.00 ] Key: SEH_StackAlign: [ 0.00 0.00 ] Key: SEH_StackAlloc: [ 0.00 0.00 ] Key: SEH_UnwindV: [ 0.00 0.00 ] diff --git a/llvm/test/CodeGen/X86/win64-eh-unwindv2-too-many-epilogs.mir b/llvm/test/CodeGen/X86/win64-eh-unwindv2-too-many-epilogs.mir index 70c87ad87f792..ac54ec9f0652d 100644 --- a/llvm/test/CodeGen/X86/win64-eh-unwindv2-too-many-epilogs.mir +++ b/llvm/test/CodeGen/X86/win64-eh-unwindv2-too-many-epilogs.mir @@ -1,32 +1,22 @@ -# Require V2 and restrict the number of unwind codes to 8 -# RUN: not llc -mtriple=x86_64-pc-windows-msvc -o - %s \ -# RUN: -run-pass=x86-wineh-unwindv2 -x86-wineh-unwindv2-max-unwind-codes=8 \ -# RUN: 2>&1 | FileCheck %s -check-prefix=REQUIREV2 - -# Force best-effort and restrict the number of unwind codes to 8 +# Restrict the number of unwind codes to 8 # RUN: llc -mtriple=x86_64-pc-windows-msvc -o - %s \ -# RUN: -run-pass=x86-wineh-unwindv2 -x86-wineh-unwindv2-max-unwind-codes=8 \ -# RUN: -x86-wineh-unwindv2-force-mode=1 | \ -# RUN: FileCheck %s -check-prefix=BESTEFFORT +# RUN: -x86-wineh-unwindv2-unwind-codes-threshold=8 \ +# RUN: -run-pass=x86-wineh-unwindv2 | FileCheck %s \ +# RUN: -check-prefixes=ALLOWLESS,CHECK -# Require V2, but allow the default number of unwind codes (255) +# Allow the default number of unwind codes (255) # RUN: llc -mtriple=x86_64-pc-windows-msvc -o - %s \ -# RUN: -run-pass=x86-wineh-unwindv2 | FileCheck %s -check-prefix=ALLOWMORE - -# Usually 255 unwind codes are permitted, but we passed an arg to llc to limit -# it to 8. -# REQUIREV2: error: example.c:2:1: Windows x64 Unwind v2 is required, but the function 'too_many_epilogs' has too many unwind codes. -# REQUIREV2-SAME: Try splitting the function or reducing the number of places where it exits early with a tail call. +# RUN: -run-pass=x86-wineh-unwindv2 | FileCheck %s \ +# RUN: -check-prefixes=ALLOWMORE,CHECK -# If we force "best effort" mode, then we won't see any errors, but we won't use -# v2. -# BESTEFFORT-NOT: SEH_UnwindVersion -# BESTEFFORT-NOT: SEH_UnwindV2Start +# CHECK-LABEL: too_many_epilogs +# CHECK: SEH_UnwindVersion 2 +# CHECK: SEH_UnwindV2Start -# If we allow more epilogs then too_many_epilogs will compile with v2. -# ALLOWMORE-LABEL: too_many_epilogs -# ALLOWMORE: SEH_UnwindVersion 2 -# ALLOWMORE: SEH_UnwindV2Start +# If we restricted the number of unwind codes, then we expect to use chained +# infos. +# ALLOWLESS: SEH_SplitChained +# ALLOWMORE-NOT: SEH_SplitChained --- | define dso_local void @too_many_epilogs() local_unnamed_addr !dbg !9 { diff --git a/llvm/test/CodeGen/X86/win64-eh-unwindv2-too-many-instr.mir b/llvm/test/CodeGen/X86/win64-eh-unwindv2-too-many-instr.mir new file mode 100644 index 0000000000000..5ffe7b48ba1ff --- /dev/null +++ b/llvm/test/CodeGen/X86/win64-eh-unwindv2-too-many-instr.mir @@ -0,0 +1,89 @@ +# Restrict the number of instructions per info to 4 +# RUN: llc -mtriple=x86_64-pc-windows-msvc -o - %s \ +# RUN: -x86-wineh-unwindv2-instruction-count-threshold=4 \ +# RUN: -run-pass=x86-wineh-unwindv2 | FileCheck %s \ +# RUN: -check-prefixes=ALLOWLESS,CHECK + +# Allow the default number of instructions per info +# RUN: llc -mtriple=x86_64-pc-windows-msvc -o - %s \ +# RUN: -run-pass=x86-wineh-unwindv2 | FileCheck %s \ +# RUN: -check-prefixes=ALLOWMORE,CHECK + +--- | + define dso_local void @too_many_instr() local_unnamed_addr !dbg !9 { + entry: + ret void, !dbg !10 + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2, !3, !4, !5} + + !0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "/app/example.c", directory: "/app") + !2 = !{i32 1, !"winx64-eh-unwindv2", i32 2} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"CodeView", i32 1} + !5 = !{i32 2, !"Debug Info Version", i32 3} + !6 = !DIFile(filename: "example.c", directory: "/app") + !7 = !DISubroutineType(types: !8) + !8 = !{null} + !9 = distinct !DISubprogram(name: "too_many_instr", scope: !6, file: !6, line: 1, type: !7, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0) + !10 = !DILocation(line: 2, column: 1, scope: !9) + !11 = !DILocation(line: 3, column: 1, scope: !9) + !12 = !DILocation(line: 4, column: 1, scope: !9) + !13 = !DILocation(line: 5, column: 1, scope: !9) +... +--- +name: too_many_instr +body: | + bb.0.entry: + frame-setup SEH_EndPrologue + SEH_BeginEpilogue + SEH_EndEpilogue + RET64 debug-location !10 + bb.1: + $rcx = MOV64rr $rax + SEH_BeginEpilogue + SEH_EndEpilogue + RET64 debug-location !11 + bb.2: + $rcx = MOV64rr $rax + SEH_BeginEpilogue + SEH_EndEpilogue + RET64 debug-location !12 + bb.3: + $rcx = MOV64rr $rax + $rcx = MOV64rr $rax + $rcx = MOV64rr $rax + SEH_BeginEpilogue + SEH_EndEpilogue + RET64 debug-location !13 + +... + +# CHECK-LABEL: too_many_instr +# CHECK-LABEL: bb.0.entry: +# CHECK: SEH_UnwindV2Start +# CHECK: RET64 debug-location !10 +# bb.1 + bb.2 have enough instructions that bb.0 has its own info. +# ALLOWLESS-NEXT: SEH_SplitChained +# ALLOWMORE-NOT: SEH_SplitChained + +# CHECK-LABEL: bb.1 +# CHECK: SEH_UnwindV2Start +# CHECK: RET64 debug-location !DILocation(line: 3, column: 1, scope: !6) +# bb.2 doesn't fill the current info, so bb.1 gets added as well. +# CHECK-NOT: SEH_SplitChained + +# CHECK-LABEL: bb.2 +# CHECK: SEH_UnwindV2Start +# CHECK: RET64 debug-location !DILocation(line: 4, column: 1, scope: !6) +# bb.3 has enough instructions by itself that bb.2 needs to split. +# ALLOWLESS-NEXT: SEH_SplitChained +# ALLOWMORE-NOT: SEH_SplitChained + +# CHECK-LABEL: bb.3 +# CHECK: SEH_UnwindV2Start +# CHECK: RET64 debug-location !DILocation(line: 5, column: 1, scope: !6) +# Never split at the end. +# CHECK-NOT: SEH_SplitChained diff --git a/llvm/test/CodeGen/X86/win64-eh-unwindv2.ll b/llvm/test/CodeGen/X86/win64-eh-unwindv2.ll index 0d92d044e1b94..bd36d3ea1f160 100644 --- a/llvm/test/CodeGen/X86/win64-eh-unwindv2.ll +++ b/llvm/test/CodeGen/X86/win64-eh-unwindv2.ll @@ -204,11 +204,111 @@ entry: ; CHECK: int3 ; CHECK-NEXT: .seh_endproc +define dso_local i32 @has_funclet(i32 %x) local_unnamed_addr personality ptr @__C_specific_handler { +entry: + %call = invoke i32 @c(i32 %x) + to label %call.block.1 unwind label %cleanup + +call.block.1: + %call1 = invoke i32 @c(i32 %x) + to label %call.block.2 unwind label %cleanup + +call.block.2: + %add = add nsw i32 %call1, %call + %call2 = invoke i32 @c(i32 %x) + to label %call.block.3 unwind label %cleanup + +call.block.3: + %call3 = invoke i32 @c(i32 %call2) + to label %call.block.4 unwind label %cleanup + +call.block.4: + %add4 = add nsw i32 %add, %call3 + ret i32 %add4 + +cleanup: + %cleanup_token = cleanuppad within none [] + call fastcc void @is_funclet(i32 %x) #18 [ "funclet"(token %cleanup_token) ] + cleanupret from %cleanup_token unwind to caller +} + +define internal fastcc void @is_funclet(i32 %x) local_unnamed_addr { +entry: + %y = alloca i32, i32 %x + ret void +} + +; CHECK-LABEL: has_funclet: +; CHECK: .seh_pushreg %rbp +; CHECK: .seh_pushreg %rsi +; CHECK: .seh_pushreg %rdi +; CHECK: .seh_stackalloc 48 +; CHECK: .seh_setframe %rbp, 48 +; CHECK: .seh_endprologue +; CHECK: .seh_startepilogue +; CHECK: .seh_unwindv2start +; CHECK-NEXT: popq %rdi +; CHECK-NEXT: popq %rsi +; CHECK-NEXT: popq %rbp +; CHECK-NEXT: .seh_endepilogue +; CHECK-NEXT: retq +; CHECK-NEXT: .seh_handlerdata +; CHECK: .text +; CHECK-NEXT: .seh_endproc +; CHECK-LABEL: "?dtor$5@?0?has_funclet@4HA": +; CHECK: .seh_pushreg %rbp +; CHECK: .seh_pushreg %rsi +; CHECK: .seh_pushreg %rdi +; CHECK: .seh_stackalloc 32 +; CHECK: .seh_endprologue +; CHECK: .seh_startepilogue +; CHECK: .seh_unwindv2start +; CHECK-NEXT: popq %rdi +; CHECK-NEXT: popq %rsi +; CHECK-NEXT: popq %rbp +; CHECK-NEXT: .seh_endepilogue +; CHECK-NEXT: retq +; CHECK: .seh_handlerdata +; CHECK-NEXT: .text +; CHECK-NEXT: .seh_endproc +; CHECK-LABEL: is_funclet: +; CHECK: .seh_unwindversion 2 +; CHECK: .seh_pushreg %rbp +; CHECK: .seh_setframe %rbp, 0 +; CHECK: .seh_endprologue +; CHECK: .seh_startepilogue +; CHECK: .seh_unwindv2start +; CHECK: .seh_endepilogue +; CHECK-NEXT: retq +; CHECK-NEXT: .seh_endproc + +define dso_local void @set_frame_and_alloc(i32 %x) local_unnamed_addr { + %y = alloca i32, i32 %x + %z = alloca ptr, align 8 + ret void +} + +; CHECK-LABEL: set_frame_and_alloc: +; CHECK: .seh_unwindversion 2 +; CHECK: .seh_pushreg %rbp +; CHECK: .seh_stackalloc 16 +; CHECK: .seh_setframe %rbp, 16 +; CHECK: .seh_endprologue +; CHECK: .seh_startepilogue +; CHECK-NEXT: movq %rbp, %rsp +; CHECK-NEXT: .seh_unwindv2start +; CHECK-NEXT: popq %rbp +; CHECK-NEXT: .seh_endepilogue +; CHECK-NEXT: retq +; CHECK-NEXT: .seh_endproc + declare i64 @llvm.x86.flags.read.u64() declare void @a() local_unnamed_addr declare i32 @b() local_unnamed_addr declare i32 @c(i32) local_unnamed_addr declare void @d() local_unnamed_addr #1 +declare dso_local i32 @__C_specific_handler(...) + !llvm.module.flags = !{!0} !0 = !{i32 1, !"winx64-eh-unwindv2", i32 2} diff --git a/llvm/test/MC/AsmParser/directive_seh.s b/llvm/test/MC/AsmParser/directive_seh.s index 8eb50ed996dd3..0344e7ea3ad1f 100644 --- a/llvm/test/MC/AsmParser/directive_seh.s +++ b/llvm/test/MC/AsmParser/directive_seh.s @@ -42,13 +42,11 @@ func: # CHECK: .seh_handlerdata .long 0 .text - .seh_startchained .seh_endprologue - .seh_endchained + .seh_splitchained # CHECK: .text -# CHECK: .seh_startchained # CHECK: .seh_endprologue -# CHECK: .seh_endchained +# CHECK: .seh_splitchained .seh_startepilogue # CHECK: .seh_startepilogue lea (%rbx), %rsp diff --git a/llvm/test/MC/AsmParser/seh-directive-errors.s b/llvm/test/MC/AsmParser/seh-directive-errors.s index d9dfe4b4182b9..5ed477b16c7f2 100644 --- a/llvm/test/MC/AsmParser/seh-directive-errors.s +++ b/llvm/test/MC/AsmParser/seh-directive-errors.s @@ -42,6 +42,8 @@ f: # @f .seh_stackalloc 32 .seh_startepilogue # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: starting epilogue (.seh_startepilogue) before prologue has ended (.seh_endprologue) in f + .seh_splitchained + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: can't split into a new chained region (.seh_splitchained) in the middle of a prolog in f .seh_endprologue nop .seh_endepilogue diff --git a/llvm/test/MC/COFF/seh-unwindv2.s b/llvm/test/MC/COFF/seh-unwindv2.s index a746a5ffa5e5b..822b7b954a5f0 100644 --- a/llvm/test/MC/COFF/seh-unwindv2.s +++ b/llvm/test/MC/COFF/seh-unwindv2.s @@ -152,3 +152,212 @@ mismatched_terminators: // CHECK-NEXT: 0x04: ALLOC_SMALL size=40 // CHECK-NEXT: ] // CHECK-NEXT: } + +chained: + .seh_proc chained + .seh_unwindversion 2 + subq $40, %rsp + .seh_stackalloc 40 + .seh_endprologue + callq c + testl %eax, %eax + jle .L_ELSE_3 + movl %eax, %ecx + .seh_startepilogue + addq $40, %rsp + .seh_unwindv2start + .seh_endepilogue + jmp c + .seh_splitchained +.L_ELSE_3: + nop + .seh_startepilogue + addq $40, %rsp + .seh_unwindv2start + .seh_endepilogue + jmp b + .seh_endproc + +// CHECK: RuntimeFunction { +// CHECK-NEXT: StartAddress: chained (0x30) +// CHECK-NEXT: EndAddress: chained [[EndDisp1:\+0x[A-F0-9]+]] (0x34) +// CHECK-NEXT: UnwindInfoAddress: .xdata [[InfoDisp1:\+0x[A-F0-9]+]] (0x38) +// CHECK-NEXT: UnwindInfo { +// CHECK-NEXT: Version: 2 +// CHECK-NEXT: Flags [ (0x0) +// CHECK-NEXT: ] +// CHECK-NEXT: PrologSize: 4 +// CHECK-NEXT: FrameRegister: - +// CHECK-NEXT: FrameOffset: - +// CHECK-NEXT: UnwindCodeCount: 3 +// CHECK-NEXT: UnwindCodes [ +// CHECK-NEXT: 0x01: EPILOG atend=no, length=0x1 +// CHECK-NEXT: 0x05: EPILOG offset=0x5 +// CHECK-NEXT: 0x04: ALLOC_SMALL size=40 +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: StartAddress: chained [[EndDisp1]] (0x3C) +// CHECK-NEXT: EndAddress: chained +0x22 (0x40) +// CHECK-NEXT: UnwindInfoAddress: .xdata +0x40 (0x44) +// CHECK-NEXT: UnwindInfo { +// CHECK-NEXT: Version: 2 +// CHECK-NEXT: Flags [ (0x4) +// CHECK-NEXT: ChainInfo (0x4) +// CHECK-NEXT: ] +// CHECK-NEXT: PrologSize: 0 +// CHECK-NEXT: FrameRegister: - +// CHECK-NEXT: FrameOffset: - +// CHECK-NEXT: UnwindCodeCount: 2 +// CHECK-NEXT: UnwindCodes [ +// CHECK-NEXT: 0x01: EPILOG atend=no, length=0x1 +// CHECK-NEXT: 0x05: EPILOG offset=0x5 +// CHECK-NEXT: ] +// CHECK-NEXT: Chained { +// CHECK-NEXT: StartAddress: chained (0x48) +// CHECK-NEXT: EndAddress: chained [[EndDisp1]] (0x4C) +// CHECK-NEXT: UnwindInfoAddress: .xdata [[InfoDisp1]] (0x50) +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } + +has_ex_handler_data: + .seh_proc has_ex_handler_data + .seh_handler __C_specific_handler, @unwind, @except + .seh_unwindversion 2 + pushq %rbp + .seh_pushreg %rbp + subq $32, %rsp + .seh_stackalloc 32 + leaq 32(%rsp), %rbp + .seh_setframe %rbp, 32 + .seh_endprologue +.has_ex_handler_data_callsite: + callq *__imp_callme(%rip) + nop +.has_ex_handler_data_finish: + .seh_startepilogue + addq $32, %rsp + .seh_unwindv2start + popq %rbp + .seh_endepilogue + retq +.has_ex_handler_data_handler: + jmp .has_ex_handler_data_finish + .seh_handlerdata + .long 1 # Number of call sites + .long .has_ex_handler_data_callsite@IMGREL # LabelStart + .long .has_ex_handler_data_finish@IMGREL # LabelEnd + .long 1 # CatchAll + .long .has_ex_handler_data_handler@IMGREL # ExceptionHandler + .text + .seh_endproc +// CHECK-LABEL: StartAddress: has_ex_handler_data +// CHECK-NEXT: EndAddress: .has_ex_handler_data_handler +0x2 +// CHECK-NEXT: UnwindInfoAddress: .xdata +0x54 +// CHECK-NEXT: UnwindInfo { +// CHECK-NEXT: Version: 2 +// CHECK-NEXT: Flags [ (0x3) +// CHECK-NEXT: ExceptionHandler (0x1) +// CHECK-NEXT: TerminateHandler (0x2) +// CHECK-NEXT: ] +// CHECK-NEXT: PrologSize: 10 +// CHECK-NEXT: FrameRegister: RBP +// CHECK-NEXT: FrameOffset: 0x2 +// CHECK-NEXT: UnwindCodeCount: 5 +// CHECK-NEXT: UnwindCodes [ +// CHECK-NEXT: 0x02: EPILOG atend=no, length=0x2 +// CHECK-NEXT: 0x04: EPILOG offset=0x4 +// CHECK-NEXT: 0x0A: SET_FPREG reg=RBP, offset=0x20 +// CHECK-NEXT: 0x05: ALLOC_SMALL size=32 +// CHECK-NEXT: 0x01: PUSH_NONVOL reg=RBP +// CHECK-NEXT: ] +// CHECK-NEXT: Handler: __C_specific_handler +// CHECK-NEXT: } + +has_ex_handler_data_and_chaining: + .seh_proc chained + .seh_handler __C_specific_handler, @unwind, @except + .seh_unwindversion 2 + subq $40, %rsp + .seh_stackalloc 40 + .seh_endprologue + callq c + testl %eax, %eax + jle .L_ELSE_4 +.has_ex_handler_data_and_chaining_callsite: + callq *__imp_callme(%rip) + nop +.has_ex_handler_data_and_chaining_finish: + movl %eax, %ecx + .seh_startepilogue + addq $40, %rsp + .seh_unwindv2start + .seh_endepilogue + jmp c + .seh_splitchained +.L_ELSE_4: + nop + .seh_startepilogue + addq $40, %rsp + .seh_unwindv2start + .seh_endepilogue + jmp b +.has_ex_handler_data_and_chaining_handler: + jmp .has_ex_handler_data_and_chaining_finish + .seh_handlerdata + .long 1 # Number of call sites + .long .has_ex_handler_data_and_chaining_callsite@IMGREL # LabelStart + .long .has_ex_handler_data_and_chaining_finish@IMGREL # LabelEnd + .long 1 # CatchAll + .long .has_ex_handler_data_and_chaining_handler@IMGREL # ExceptionHandler + .text + .seh_endproc + +// CHECK: RuntimeFunction { +// CHECK-NEXT: StartAddress: has_ex_handler_data_and_chaining (0x54) +// CHECK-NEXT: EndAddress: .has_ex_handler_data_and_chaining_finish [[EndDisp2:\+0x[A-F0-9]+]] (0x58) +// CHECK-NEXT: UnwindInfoAddress: .xdata [[InfoDisp2:\+0x[A-F0-9]+]] (0x5C) +// CHECK-NEXT: UnwindInfo { +// CHECK-NEXT: Version: 2 +// CHECK-NEXT: Flags [ (0x3) +// CHECK-NEXT: ExceptionHandler (0x1) +// CHECK-NEXT: TerminateHandler (0x2) +// CHECK-NEXT: ] +// CHECK-NEXT: PrologSize: 4 +// CHECK-NEXT: FrameRegister: - +// CHECK-NEXT: FrameOffset: - +// CHECK-NEXT: UnwindCodeCount: 3 +// CHECK-NEXT: UnwindCodes [ +// CHECK-NEXT: 0x01: EPILOG atend=no, length=0x1 +// CHECK-NEXT: 0x05: EPILOG offset=0x5 +// CHECK-NEXT: 0x04: ALLOC_SMALL size=40 +// CHECK-NEXT: ] +// CHECK-NEXT: Handler: __C_specific_handler +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: StartAddress: .has_ex_handler_data_and_chaining_finish [[EndDisp2]] (0x60) +// CHECK-NEXT: EndAddress: .has_ex_handler_data_and_chaining_handler +0x2 (0x64) +// CHECK-NEXT: UnwindInfoAddress: .xdata +0xA0 (0x68) +// CHECK-NEXT: UnwindInfo { +// CHECK-NEXT: Version: 2 +// CHECK-NEXT: Flags [ (0x4) +// CHECK-NEXT: ChainInfo (0x4) +// CHECK-NEXT: ] +// CHECK-NEXT: PrologSize: 0 +// CHECK-NEXT: FrameRegister: - +// CHECK-NEXT: FrameOffset: - +// CHECK-NEXT: UnwindCodeCount: 2 +// CHECK-NEXT: UnwindCodes [ +// CHECK-NEXT: 0x01: EPILOG atend=no, length=0x1 +// CHECK-NEXT: 0x07: EPILOG offset=0x7 +// CHECK-NEXT: ] +// CHECK-NEXT: Chained { +// CHECK-NEXT: StartAddress: has_ex_handler_data_and_chaining (0xA8) +// CHECK-NEXT: EndAddress: .has_ex_handler_data_and_chaining_finish [[EndDisp2]] (0xAC) +// CHECK-NEXT: UnwindInfoAddress: .xdata [[InfoDisp2]] (0xB0) +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } diff --git a/llvm/test/MC/COFF/seh.s b/llvm/test/MC/COFF/seh.s index 31e845397f2d0..18ad1d692838c 100644 --- a/llvm/test/MC/COFF/seh.s +++ b/llvm/test/MC/COFF/seh.s @@ -141,9 +141,8 @@ func: .seh_handlerdata .long 0 .text - .seh_startchained .seh_endprologue - .seh_endchained + .seh_splitchained .seh_startepilogue lea (%rbx), %rsp pop %rbx diff --git a/llvm/test/tools/llvm-objdump/COFF/Inputs/win64-unwind.exe.coff-x86_64.asm b/llvm/test/tools/llvm-objdump/COFF/Inputs/win64-unwind.exe.coff-x86_64.asm index c44d59b324036..9c301652f8ea4 100644 --- a/llvm/test/tools/llvm-objdump/COFF/Inputs/win64-unwind.exe.coff-x86_64.asm +++ b/llvm/test/tools/llvm-objdump/COFF/Inputs/win64-unwind.exe.coff-x86_64.asm @@ -19,9 +19,8 @@ func: .seh_handlerdata .long 0 .text - .seh_startchained .seh_endprologue - .seh_endchained + .seh_splitchained lea (%rbx), %rsp pop %rbx addq $24, %rsp