Skip to content

Commit 151e8f8

Browse files
committed
[win][x64] Unwind v2 3/n: Add support for emitting unwind v2 information (equivalent to MSVC /d2epilogunwind)
1 parent bbaf743 commit 151e8f8

File tree

24 files changed

+991
-93
lines changed

24 files changed

+991
-93
lines changed

clang/include/clang/Basic/CodeGenOptions.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,9 @@ CODEGENOPT(ImportCallOptimization, 1, 0)
476476
/// (BlocksRuntime) on Windows.
477477
CODEGENOPT(StaticClosure, 1, 0)
478478

479+
/// Enables unwind v2 (epilog) information for x64 Windows.
480+
CODEGENOPT(WinX64EHUnwindV2, 1, 0)
481+
479482
/// FIXME: Make DebugOptions its own top-level .def file.
480483
#include "DebugOptions.def"
481484

clang/include/clang/Driver/Options.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7659,6 +7659,10 @@ def import_call_optimization : Flag<["-"], "import-call-optimization">,
76597659
"by the Windows kernel to enable import call optimization">,
76607660
MarshallingInfoFlag<CodeGenOpts<"ImportCallOptimization">>;
76617661

7662+
def epilog_unwind : Flag<["-"], "winx64-eh-unwindv2">,
7663+
HelpText<"Enable unwind v2 (epilog) information for x64 Windows">,
7664+
MarshallingInfoFlag<CodeGenOpts<"WinX64EHUnwindV2">>;
7665+
76627666
} // let Visibility = [CC1Option]
76637667

76647668
//===----------------------------------------------------------------------===//
@@ -8771,6 +8775,8 @@ def _SLASH_M_Group : OptionGroup<"</M group>">, Group<cl_compile_Group>;
87718775
def _SLASH_volatile_Group : OptionGroup<"</volatile group>">,
87728776
Group<cl_compile_Group>;
87738777

8778+
def _SLASH_d2epilogunwind : CLFlag<"d2epilogunwind">,
8779+
HelpText<"Enable unwind v2 (epilog) information for x64 Windows">;
87748780
def _SLASH_EH : CLJoined<"EH">, HelpText<"Set exception handling model">;
87758781
def _SLASH_EP : CLFlag<"EP">,
87768782
HelpText<"Disable linemarker output and preprocess to stdout">;

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,10 @@ void CodeGenModule::Release() {
13031303
getModule().addModuleFlag(llvm::Module::Warning, "import-call-optimization",
13041304
1);
13051305

1306+
// Enable unwind v2 (epilog).
1307+
if (CodeGenOpts.WinX64EHUnwindV2)
1308+
getModule().addModuleFlag(llvm::Module::Warning, "winx64-eh-unwindv2", 1);
1309+
13061310
// Indicate whether this Module was compiled with -fopenmp
13071311
if (getLangOpts().OpenMP && !getLangOpts().OpenMPSimd)
13081312
getModule().addModuleFlag(llvm::Module::Max, "openmp", LangOpts.OpenMP);

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8529,6 +8529,9 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType,
85298529
if (Args.hasArg(options::OPT__SLASH_kernel))
85308530
CmdArgs.push_back("-fms-kernel");
85318531

8532+
if (Args.hasArg(options::OPT__SLASH_d2epilogunwind))
8533+
CmdArgs.push_back("-winx64-eh-unwindv2");
8534+
85328535
for (const Arg *A : Args.filtered(options::OPT__SLASH_guard)) {
85338536
StringRef GuardArgs = A->getValue();
85348537
// The only valid options are "cf", "cf,nochecks", "cf-", "ehcont" and

clang/test/CodeGen/epilog-unwind.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// RUN: %clang_cc1 -winx64-eh-unwindv2 -emit-llvm %s -o - | FileCheck %s
2+
3+
void f(void) {}
4+
5+
// CHECK: !"winx64-eh-unwindv2", i32 1}

clang/test/Driver/cl-options.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,4 +817,7 @@
817817
// RUN: %clang_cl -vctoolsdir "" /arm64EC /c -target x86_64-pc-windows-msvc -### -- %s 2>&1 | FileCheck --check-prefix=ARM64EC_OVERRIDE %s
818818
// ARM64EC_OVERRIDE: warning: /arm64EC has been overridden by specified target: x86_64-pc-windows-msvc; option ignored
819819

820+
// RUN: %clang_cl /d2epilogunwind /c -### -- %s 2>&1 | FileCheck %s --check-prefix=EPILOGUNWIND
821+
// EPILOGUNWIND: -winx64-eh-unwindv2
822+
820823
void f(void) { }

llvm/include/llvm/MC/MCStreamer.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ class MCStreamer {
259259
bool InEpilogCFI = false;
260260

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

264264
MCFragment *CurFrag = nullptr;
265265

@@ -342,7 +342,9 @@ class MCStreamer {
342342
return WinFrameInfos;
343343
}
344344

345-
MCSymbol *getCurrentEpilog() const { return CurrentEpilog; }
345+
WinEH::FrameInfo::Epilog *getCurrentWinEpilog() const {
346+
return CurrentWinEpilog;
347+
}
346348

347349
bool isInEpilogCFI() const { return InEpilogCFI; }
348350

@@ -1026,6 +1028,8 @@ class MCStreamer {
10261028
virtual void emitWinCFIEndProlog(SMLoc Loc = SMLoc());
10271029
virtual void emitWinCFIBeginEpilogue(SMLoc Loc = SMLoc());
10281030
virtual void emitWinCFIEndEpilogue(SMLoc Loc = SMLoc());
1031+
virtual void emitWinCFIUnwindV2Start(SMLoc Loc = SMLoc());
1032+
virtual void emitWinCFIUnwindVersion(uint8_t Version, SMLoc Loc = SMLoc());
10291033
virtual void emitWinEHHandler(const MCSymbol *Sym, bool Unwind, bool Except,
10301034
SMLoc Loc = SMLoc());
10311035
virtual void emitWinEHHandlerData(SMLoc Loc = SMLoc());

llvm/include/llvm/MC/MCWinEH.h

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#define LLVM_MC_MCWINEH_H
1111

1212
#include "llvm/ADT/MapVector.h"
13+
#include "llvm/ADT/STLExtras.h"
14+
#include "llvm/Support/SMLoc.h"
1315
#include <vector>
1416

1517
namespace llvm {
@@ -42,6 +44,7 @@ struct FrameInfo {
4244
const MCSymbol *FuncletOrFuncEnd = nullptr;
4345
const MCSymbol *ExceptionHandler = nullptr;
4446
const MCSymbol *Function = nullptr;
47+
SMLoc FunctionLoc;
4548
const MCSymbol *PrologEnd = nullptr;
4649
const MCSymbol *Symbol = nullptr;
4750
MCSection *TextSection = nullptr;
@@ -52,16 +55,21 @@ struct FrameInfo {
5255
bool HandlesExceptions = false;
5356
bool EmitAttempted = false;
5457
bool Fragment = false;
58+
constexpr static uint8_t DefaultVersion = 1;
59+
uint8_t Version = DefaultVersion;
5560

5661
int LastFrameInst = -1;
5762
const FrameInfo *ChainedParent = nullptr;
5863
std::vector<Instruction> Instructions;
5964
struct Epilog {
6065
std::vector<Instruction> Instructions;
6166
unsigned Condition;
62-
MCSymbol *End;
67+
const MCSymbol *Start = nullptr;
68+
const MCSymbol *End = nullptr;
69+
const MCSymbol *UnwindV2Start = nullptr;
70+
SMLoc Loc;
6371
};
64-
MapVector<MCSymbol *, Epilog> EpilogMap;
72+
std::vector<Epilog> Epilogs;
6573

6674
// For splitting unwind info of large functions
6775
struct Segment {
@@ -70,7 +78,7 @@ struct FrameInfo {
7078
bool HasProlog;
7179
MCSymbol *Symbol = nullptr;
7280
// Map an Epilog's symbol to its offset within the function.
73-
MapVector<MCSymbol *, int64_t> Epilogs;
81+
MapVector<const MCSymbol *, int64_t> Epilogs;
7482

7583
Segment(int64_t Offset, int64_t Length, bool HasProlog = false)
7684
: Offset(Offset), Length(Length), HasProlog(HasProlog) {}
@@ -89,11 +97,21 @@ struct FrameInfo {
8997
bool empty() const {
9098
if (!Instructions.empty())
9199
return false;
92-
for (const auto &E : EpilogMap)
93-
if (!E.second.Instructions.empty())
100+
for (const auto &E : Epilogs)
101+
if (!E.Instructions.empty())
94102
return false;
95103
return true;
96104
}
105+
106+
auto findEpilog(const MCSymbol *Start) const {
107+
return llvm::find_if(Epilogs,
108+
[Start](const Epilog &E) { return E.Start == Start; });
109+
}
110+
111+
auto findEpilog(const MCSymbol *Start) {
112+
return llvm::find_if(Epilogs,
113+
[Start](Epilog &E) { return E.Start == Start; });
114+
}
97115
};
98116

99117
class UnwindEmitter {

llvm/lib/MC/MCAsmStreamer.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,8 @@ class MCAsmStreamer final : public MCStreamer {
390390
void emitWinCFIEndProlog(SMLoc Loc) override;
391391
void emitWinCFIBeginEpilogue(SMLoc Loc) override;
392392
void emitWinCFIEndEpilogue(SMLoc Loc) override;
393+
void emitWinCFIUnwindV2Start(SMLoc Loc) override;
394+
void emitWinCFIUnwindVersion(uint8_t Version, SMLoc Loc) override;
393395

394396
void emitWinEHHandler(const MCSymbol *Sym, bool Unwind, bool Except,
395397
SMLoc Loc) override;
@@ -2304,6 +2306,20 @@ void MCAsmStreamer::emitWinCFIEndEpilogue(SMLoc Loc) {
23042306
EmitEOL();
23052307
}
23062308

2309+
void MCAsmStreamer::emitWinCFIUnwindV2Start(SMLoc Loc) {
2310+
MCStreamer::emitWinCFIUnwindV2Start(Loc);
2311+
2312+
OS << "\t.seh_unwindv2start";
2313+
EmitEOL();
2314+
}
2315+
2316+
void MCAsmStreamer::emitWinCFIUnwindVersion(uint8_t Version, SMLoc Loc) {
2317+
MCStreamer::emitWinCFIUnwindVersion(Version, Loc);
2318+
2319+
OS << "\t.seh_unwindversion " << (unsigned)Version;
2320+
EmitEOL();
2321+
}
2322+
23072323
void MCAsmStreamer::emitCGProfileEntry(const MCSymbolRefExpr *From,
23082324
const MCSymbolRefExpr *To,
23092325
uint64_t Count) {

llvm/lib/MC/MCParser/COFFAsmParser.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ class COFFAsmParser : public MCAsmParserExtension {
9696
".seh_startepilogue");
9797
addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveEndEpilog>(
9898
".seh_endepilogue");
99+
addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveUnwindV2Start>(
100+
".seh_unwindv2start");
101+
addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveUnwindVersion>(
102+
".seh_unwindversion");
99103
}
100104

101105
bool parseSectionDirectiveText(StringRef, SMLoc) {
@@ -147,6 +151,8 @@ class COFFAsmParser : public MCAsmParserExtension {
147151
bool parseSEHDirectiveEndProlog(StringRef, SMLoc);
148152
bool ParseSEHDirectiveBeginEpilog(StringRef, SMLoc);
149153
bool ParseSEHDirectiveEndEpilog(StringRef, SMLoc);
154+
bool ParseSEHDirectiveUnwindV2Start(StringRef, SMLoc);
155+
bool ParseSEHDirectiveUnwindVersion(StringRef, SMLoc);
150156

151157
bool parseAtUnwindOrAtExcept(bool &unwind, bool &except);
152158
bool parseDirectiveSymbolAttribute(StringRef Directive, SMLoc);
@@ -767,6 +773,28 @@ bool COFFAsmParser::ParseSEHDirectiveEndEpilog(StringRef, SMLoc Loc) {
767773
return false;
768774
}
769775

776+
bool COFFAsmParser::ParseSEHDirectiveUnwindV2Start(StringRef, SMLoc Loc) {
777+
Lex();
778+
getStreamer().emitWinCFIUnwindV2Start(Loc);
779+
return false;
780+
}
781+
782+
bool COFFAsmParser::ParseSEHDirectiveUnwindVersion(StringRef, SMLoc Loc) {
783+
int64_t Version;
784+
if (getParser().parseIntToken(Version, "expected unwind version number"))
785+
return true;
786+
787+
if ((Version < 1) || (Version > UINT8_MAX))
788+
return Error(Loc, "invalid unwind version");
789+
790+
if (getLexer().isNot(AsmToken::EndOfStatement))
791+
return TokError("unexpected token in directive");
792+
793+
Lex();
794+
getStreamer().emitWinCFIUnwindVersion(Version, Loc);
795+
return false;
796+
}
797+
770798
bool COFFAsmParser::parseAtUnwindOrAtExcept(bool &unwind, bool &except) {
771799
StringRef identifier;
772800
if (getLexer().isNot(AsmToken::At) && getLexer().isNot(AsmToken::Percent))

llvm/lib/MC/MCStreamer.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ void MCStreamer::emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) {
743743
std::make_unique<WinEH::FrameInfo>(Symbol, StartProc));
744744
CurrentWinFrameInfo = WinFrameInfos.back().get();
745745
CurrentWinFrameInfo->TextSection = getCurrentSectionOnly();
746+
CurrentWinFrameInfo->FunctionLoc = Loc;
746747
}
747748

748749
void MCStreamer::emitWinCFIEndProc(SMLoc Loc) {
@@ -1001,7 +1002,10 @@ void MCStreamer::emitWinCFIBeginEpilogue(SMLoc Loc) {
10011002
CurFrame->Function->getName());
10021003

10031004
InEpilogCFI = true;
1004-
CurrentEpilog = emitCFILabel();
1005+
CurFrame->Epilogs.push_back(WinEH::FrameInfo::Epilog());
1006+
CurrentWinEpilog = &CurFrame->Epilogs.back();
1007+
CurrentWinEpilog->Start = emitCFILabel();
1008+
CurrentWinEpilog->Loc = Loc;
10051009
}
10061010

10071011
void MCStreamer::emitWinCFIEndEpilogue(SMLoc Loc) {
@@ -1013,10 +1017,50 @@ void MCStreamer::emitWinCFIEndEpilogue(SMLoc Loc) {
10131017
return getContext().reportError(Loc, "Stray .seh_endepilogue in " +
10141018
CurFrame->Function->getName());
10151019

1020+
assert(CurrentWinEpilog);
1021+
if ((CurFrame->Version >= 2) && !CurrentWinEpilog->UnwindV2Start)
1022+
return getContext().reportError(Loc, "Missing .seh_unwindv2start in " +
1023+
CurFrame->Function->getName());
1024+
10161025
InEpilogCFI = false;
10171026
MCSymbol *Label = emitCFILabel();
1018-
CurFrame->EpilogMap[CurrentEpilog].End = Label;
1019-
CurrentEpilog = nullptr;
1027+
CurrentWinEpilog->End = Label;
1028+
CurrentWinEpilog = nullptr;
1029+
}
1030+
1031+
void MCStreamer::emitWinCFIUnwindV2Start(SMLoc Loc) {
1032+
WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc);
1033+
if (!CurFrame)
1034+
return;
1035+
1036+
if (!InEpilogCFI)
1037+
return getContext().reportError(Loc, "Stray .seh_unwindv2start in " +
1038+
CurFrame->Function->getName());
1039+
1040+
assert(CurrentWinEpilog);
1041+
if (CurrentWinEpilog->UnwindV2Start)
1042+
return getContext().reportError(Loc, "Duplicate .seh_unwindv2start in " +
1043+
CurFrame->Function->getName());
1044+
1045+
MCSymbol *Label = emitCFILabel();
1046+
CurrentWinEpilog->UnwindV2Start = Label;
1047+
}
1048+
1049+
void MCStreamer::emitWinCFIUnwindVersion(uint8_t Version, SMLoc Loc) {
1050+
WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc);
1051+
if (!CurFrame)
1052+
return;
1053+
1054+
if (CurFrame->Version != WinEH::FrameInfo::DefaultVersion)
1055+
return getContext().reportError(Loc, "Duplicate .seh_unwindversion in " +
1056+
CurFrame->Function->getName());
1057+
1058+
if (Version != 2)
1059+
return getContext().reportError(
1060+
Loc, "Unsupported version specified in .seh_unwindversion in " +
1061+
CurFrame->Function->getName());
1062+
1063+
CurFrame->Version = Version;
10201064
}
10211065

10221066
void MCStreamer::emitCOFFSafeSEH(MCSymbol const *Symbol) {}

0 commit comments

Comments
 (0)