Skip to content

[win][x64] Unwind v2 3/n: Add support for emitting unwind v2 information (equivalent to MSVC /d2epilogunwind) #129142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,9 @@ CODEGENOPT(StaticClosure, 1, 0)
/// Assume that UAVs/SRVs may alias
CODEGENOPT(ResMayAlias, 1, 0)

/// Enables unwind v2 (epilog) information for x64 Windows.
CODEGENOPT(WinX64EHUnwindV2, 1, 0)

/// FIXME: Make DebugOptions its own top-level .def file.
#include "DebugOptions.def"

Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2167,6 +2167,11 @@ defm assume_nothrow_exception_dtor: BoolFOption<"assume-nothrow-exception-dtor",
LangOpts<"AssumeNothrowExceptionDtor">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Assume that exception objects' destructors are non-throwing">,
NegFlag<SetFalse>>;
defm winx64_eh_unwindv2 : BoolFOption<"winx64-eh-unwindv2",
CodeGenOpts<"WinX64EHUnwindV2">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
NegFlag<SetFalse, [], [ClangOption], "Disable">,
BothFlags<[], [ClangOption], " unwind v2 (epilog) information for x64 Windows">>;
def fexcess_precision_EQ : Joined<["-"], "fexcess-precision=">, Group<f_Group>,
Visibility<[ClangOption, CLOption]>,
HelpText<"Allows control over excess precision on targets where native "
Expand Down Expand Up @@ -8935,6 +8940,8 @@ def _SLASH_M_Group : OptionGroup<"</M group>">, Group<cl_compile_Group>;
def _SLASH_volatile_Group : OptionGroup<"</volatile group>">,
Group<cl_compile_Group>;

def _SLASH_d2epilogunwind : CLFlag<"d2epilogunwind">,
HelpText<"Enable unwind v2 (epilog) information for x64 Windows">;
def _SLASH_EH : CLJoined<"EH">, HelpText<"Set exception handling model">;
def _SLASH_EP : CLFlag<"EP">,
HelpText<"Disable linemarker output and preprocess to stdout">;
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1307,6 +1307,10 @@ void CodeGenModule::Release() {
getModule().addModuleFlag(llvm::Module::Warning, "import-call-optimization",
1);

// Enable unwind v2 (epilog).
if (CodeGenOpts.WinX64EHUnwindV2)
getModule().addModuleFlag(llvm::Module::Warning, "winx64-eh-unwindv2", 1);

// Indicate whether this Module was compiled with -fopenmp
if (getLangOpts().OpenMP && !getLangOpts().OpenMPSimd)
getModule().addModuleFlag(llvm::Module::Max, "openmp", LangOpts.OpenMP);
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7503,6 +7503,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
}
}

// Unwind v2 (epilog) information for x64 Windows.
Args.addOptInFlag(CmdArgs, options::OPT_fwinx64_eh_unwindv2,
options::OPT_fno_winx64_eh_unwindv2);

// C++ "sane" operator new.
Args.addOptOutFlag(CmdArgs, options::OPT_fassume_sane_operator_new,
options::OPT_fno_assume_sane_operator_new);
Expand Down Expand Up @@ -8548,6 +8552,10 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType,
if (Args.hasArg(options::OPT__SLASH_kernel))
CmdArgs.push_back("-fms-kernel");

// Unwind v2 (epilog) information for x64 Windows.
if (Args.hasArg(options::OPT__SLASH_d2epilogunwind))
CmdArgs.push_back("-fwinx64-eh-unwindv2");

for (const Arg *A : Args.filtered(options::OPT__SLASH_guard)) {
StringRef GuardArgs = A->getValue();
// The only valid options are "cf", "cf,nochecks", "cf-", "ehcont" and
Expand Down
9 changes: 9 additions & 0 deletions clang/test/CodeGen/epilog-unwind.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s -check-prefix=DISABLED
// RUN: %clang_cc1 -fwinx64-eh-unwindv2 -emit-llvm %s -o - | FileCheck %s -check-prefix=ENABLED
// RUN: %clang -fwinx64-eh-unwindv2 -S -emit-llvm %s -o - | FileCheck %s -check-prefix=ENABLED
// RUN: %clang -fno-winx64-eh-unwindv2 -S -emit-llvm %s -o - | FileCheck %s -check-prefix=DISABLED

void f(void) {}

// ENABLED: !"winx64-eh-unwindv2", i32 1}
// DISABLED-NOT: "winx64-eh-unwindv2"
3 changes: 3 additions & 0 deletions clang/test/Driver/cl-options.c
Original file line number Diff line number Diff line change
Expand Up @@ -820,4 +820,7 @@
// RUN: %clang_cl -vctoolsdir "" /arm64EC /c -target x86_64-pc-windows-msvc -### -- %s 2>&1 | FileCheck --check-prefix=ARM64EC_OVERRIDE %s
// ARM64EC_OVERRIDE: warning: /arm64EC has been overridden by specified target: x86_64-pc-windows-msvc; option ignored

// RUN: %clang_cl /d2epilogunwind /c -### -- %s 2>&1 | FileCheck %s --check-prefix=EPILOGUNWIND
// EPILOGUNWIND: -fwinx64-eh-unwindv2

void f(void) { }
13 changes: 7 additions & 6 deletions llvm/include/llvm/MC/MCStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,8 @@ class MCStreamer {
bool AllowAutoPadding = false;

protected:
// True if we are processing SEH directives in an epilogue.
bool InEpilogCFI = false;

// Symbol of the current epilog for which we are processing SEH directives.
MCSymbol *CurrentEpilog = nullptr;
WinEH::FrameInfo::Epilog *CurrentWinEpilog = nullptr;

MCFragment *CurFrag = nullptr;

Expand Down Expand Up @@ -342,9 +339,11 @@ class MCStreamer {
return WinFrameInfos;
}

MCSymbol *getCurrentEpilog() const { return CurrentEpilog; }
WinEH::FrameInfo::Epilog *getCurrentWinEpilog() const {
return CurrentWinEpilog;
}

bool isInEpilogCFI() const { return InEpilogCFI; }
bool isInEpilogCFI() const { return CurrentWinEpilog; }

void generateCompactUnwindEncodings(MCAsmBackend *MAB);

Expand Down Expand Up @@ -1026,6 +1025,8 @@ class MCStreamer {
virtual void emitWinCFIEndProlog(SMLoc Loc = SMLoc());
virtual void emitWinCFIBeginEpilogue(SMLoc Loc = SMLoc());
virtual void emitWinCFIEndEpilogue(SMLoc Loc = SMLoc());
virtual void emitWinCFIUnwindV2Start(SMLoc Loc = SMLoc());
virtual void emitWinCFIUnwindVersion(uint8_t Version, SMLoc Loc = SMLoc());
virtual void emitWinEHHandler(const MCSymbol *Sym, bool Unwind, bool Except,
SMLoc Loc = SMLoc());
virtual void emitWinEHHandlerData(SMLoc Loc = SMLoc());
Expand Down
9 changes: 8 additions & 1 deletion llvm/include/llvm/MC/MCWinEH.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLVM_MC_MCWINEH_H

#include "llvm/ADT/MapVector.h"
#include "llvm/Support/SMLoc.h"
#include <vector>

namespace llvm {
Expand Down Expand Up @@ -42,6 +43,7 @@ struct FrameInfo {
const MCSymbol *FuncletOrFuncEnd = nullptr;
const MCSymbol *ExceptionHandler = nullptr;
const MCSymbol *Function = nullptr;
SMLoc FunctionLoc;
const MCSymbol *PrologEnd = nullptr;
const MCSymbol *Symbol = nullptr;
MCSection *TextSection = nullptr;
Expand All @@ -52,14 +54,19 @@ struct FrameInfo {
bool HandlesExceptions = false;
bool EmitAttempted = false;
bool Fragment = false;
constexpr static uint8_t DefaultVersion = 1;
uint8_t Version = DefaultVersion;

int LastFrameInst = -1;
const FrameInfo *ChainedParent = nullptr;
std::vector<Instruction> Instructions;
struct Epilog {
std::vector<Instruction> Instructions;
unsigned Condition;
MCSymbol *End;
const MCSymbol *Start = nullptr;
const MCSymbol *End = nullptr;
const MCSymbol *UnwindV2Start = nullptr;
SMLoc Loc;
};
MapVector<MCSymbol *, Epilog> EpilogMap;

Expand Down
16 changes: 16 additions & 0 deletions llvm/lib/MC/MCAsmStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@ class MCAsmStreamer final : public MCStreamer {
void emitWinCFIEndProlog(SMLoc Loc) override;
void emitWinCFIBeginEpilogue(SMLoc Loc) override;
void emitWinCFIEndEpilogue(SMLoc Loc) override;
void emitWinCFIUnwindV2Start(SMLoc Loc) override;
void emitWinCFIUnwindVersion(uint8_t Version, SMLoc Loc) override;

void emitWinEHHandler(const MCSymbol *Sym, bool Unwind, bool Except,
SMLoc Loc) override;
Expand Down Expand Up @@ -2305,6 +2307,20 @@ void MCAsmStreamer::emitWinCFIEndEpilogue(SMLoc Loc) {
EmitEOL();
}

void MCAsmStreamer::emitWinCFIUnwindV2Start(SMLoc Loc) {
MCStreamer::emitWinCFIUnwindV2Start(Loc);

OS << "\t.seh_unwindv2start";
EmitEOL();
}

void MCAsmStreamer::emitWinCFIUnwindVersion(uint8_t Version, SMLoc Loc) {
MCStreamer::emitWinCFIUnwindVersion(Version, Loc);

OS << "\t.seh_unwindversion " << (unsigned)Version;
EmitEOL();
}

void MCAsmStreamer::emitCGProfileEntry(const MCSymbolRefExpr *From,
const MCSymbolRefExpr *To,
uint64_t Count) {
Expand Down
28 changes: 28 additions & 0 deletions llvm/lib/MC/MCParser/COFFAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ class COFFAsmParser : public MCAsmParserExtension {
".seh_startepilogue");
addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveEndEpilog>(
".seh_endepilogue");
addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveUnwindV2Start>(
".seh_unwindv2start");
addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveUnwindVersion>(
".seh_unwindversion");
}

bool parseSectionDirectiveText(StringRef, SMLoc) {
Expand Down Expand Up @@ -147,6 +151,8 @@ class COFFAsmParser : public MCAsmParserExtension {
bool parseSEHDirectiveEndProlog(StringRef, SMLoc);
bool ParseSEHDirectiveBeginEpilog(StringRef, SMLoc);
bool ParseSEHDirectiveEndEpilog(StringRef, SMLoc);
bool ParseSEHDirectiveUnwindV2Start(StringRef, SMLoc);
bool ParseSEHDirectiveUnwindVersion(StringRef, SMLoc);

bool parseAtUnwindOrAtExcept(bool &unwind, bool &except);
bool parseDirectiveSymbolAttribute(StringRef Directive, SMLoc);
Expand Down Expand Up @@ -774,6 +780,28 @@ bool COFFAsmParser::ParseSEHDirectiveEndEpilog(StringRef, SMLoc Loc) {
return false;
}

bool COFFAsmParser::ParseSEHDirectiveUnwindV2Start(StringRef, SMLoc Loc) {
Lex();
getStreamer().emitWinCFIUnwindV2Start(Loc);
return false;
}

bool COFFAsmParser::ParseSEHDirectiveUnwindVersion(StringRef, SMLoc Loc) {
int64_t Version;
if (getParser().parseIntToken(Version, "expected unwind version number"))
return true;

if ((Version < 1) || (Version > UINT8_MAX))
return Error(Loc, "invalid unwind version");

if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in directive");

Lex();
getStreamer().emitWinCFIUnwindVersion(Version, Loc);
return false;
}

bool COFFAsmParser::parseAtUnwindOrAtExcept(bool &unwind, bool &except) {
StringRef identifier;
if (getLexer().isNot(AsmToken::At) && getLexer().isNot(AsmToken::Percent))
Expand Down
53 changes: 47 additions & 6 deletions llvm/lib/MC/MCStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,7 @@ void MCStreamer::emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) {
std::make_unique<WinEH::FrameInfo>(Symbol, StartProc));
CurrentWinFrameInfo = WinFrameInfos.back().get();
CurrentWinFrameInfo->TextSection = getCurrentSectionOnly();
CurrentWinFrameInfo->FunctionLoc = Loc;
}

void MCStreamer::emitWinCFIEndProc(SMLoc Loc) {
Expand Down Expand Up @@ -1000,23 +1001,63 @@ void MCStreamer::emitWinCFIBeginEpilogue(SMLoc Loc) {
"(.seh_endprologue) in " +
CurFrame->Function->getName());

InEpilogCFI = true;
CurrentEpilog = emitCFILabel();
MCSymbol *Label = emitCFILabel();
CurrentWinEpilog =
&CurFrame->EpilogMap.insert_or_assign(Label, WinEH::FrameInfo::Epilog())
.first->second;
CurrentWinEpilog->Start = Label;
CurrentWinEpilog->Loc = Loc;
}

void MCStreamer::emitWinCFIEndEpilogue(SMLoc Loc) {
WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc);
if (!CurFrame)
return;

if (!InEpilogCFI)
if (!CurrentWinEpilog)
return getContext().reportError(Loc, "Stray .seh_endepilogue in " +
CurFrame->Function->getName());

InEpilogCFI = false;
if ((CurFrame->Version >= 2) && !CurrentWinEpilog->UnwindV2Start)
return getContext().reportError(Loc, "Missing .seh_unwindv2start in " +
CurFrame->Function->getName());

CurrentWinEpilog->End = emitCFILabel();
CurrentWinEpilog = nullptr;
}

void MCStreamer::emitWinCFIUnwindV2Start(SMLoc Loc) {
WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc);
if (!CurFrame)
return;

if (!CurrentWinEpilog)
return getContext().reportError(Loc, "Stray .seh_unwindv2start in " +
CurFrame->Function->getName());

if (CurrentWinEpilog->UnwindV2Start)
return getContext().reportError(Loc, "Duplicate .seh_unwindv2start in " +
CurFrame->Function->getName());

MCSymbol *Label = emitCFILabel();
CurFrame->EpilogMap[CurrentEpilog].End = Label;
CurrentEpilog = nullptr;
CurrentWinEpilog->UnwindV2Start = Label;
}

void MCStreamer::emitWinCFIUnwindVersion(uint8_t Version, SMLoc Loc) {
WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc);
if (!CurFrame)
return;

if (CurFrame->Version != WinEH::FrameInfo::DefaultVersion)
return getContext().reportError(Loc, "Duplicate .seh_unwindversion in " +
CurFrame->Function->getName());

if (Version != 2)
return getContext().reportError(
Loc, "Unsupported version specified in .seh_unwindversion in " +
CurFrame->Function->getName());

CurFrame->Version = Version;
}

void MCStreamer::emitCOFFSafeSEH(MCSymbol const *Symbol) {}
Expand Down
Loading
Loading