Skip to content

Commit fe7a727

Browse files
committed
[win][x64] Unwind v2 4/4: Use chaining to split unwind info if needed
1 parent ec15cdf commit fe7a727

20 files changed

+585
-151
lines changed

llvm/include/llvm/MC/MCStreamer.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,8 +1024,7 @@ class LLVM_ABI MCStreamer {
10241024
/// for the frame. We cannot use the End marker, as it is not set at the
10251025
/// point of emitting .xdata, in order to indicate that the frame is active.
10261026
virtual void emitWinCFIFuncletOrFuncEnd(SMLoc Loc = SMLoc());
1027-
virtual void emitWinCFIStartChained(SMLoc Loc = SMLoc());
1028-
virtual void emitWinCFIEndChained(SMLoc Loc = SMLoc());
1027+
virtual void emitWinCFISplitChained(SMLoc Loc = SMLoc());
10291028
virtual void emitWinCFIPushReg(MCRegister Register, SMLoc Loc = SMLoc());
10301029
virtual void emitWinCFISetFrame(MCRegister Register, unsigned Offset,
10311030
SMLoc Loc = SMLoc());

llvm/include/llvm/MC/MCWinEH.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ struct FrameInfo {
5959
uint8_t Version = DefaultVersion;
6060

6161
int LastFrameInst = -1;
62-
const FrameInfo *ChainedParent = nullptr;
62+
FrameInfo *ChainedParent = nullptr;
6363
std::vector<Instruction> Instructions;
6464
struct Epilog {
6565
std::vector<Instruction> Instructions;
@@ -90,9 +90,9 @@ struct FrameInfo {
9090
FrameInfo(const MCSymbol *Function, const MCSymbol *BeginFuncEHLabel)
9191
: Begin(BeginFuncEHLabel), Function(Function) {}
9292
FrameInfo(const MCSymbol *Function, const MCSymbol *BeginFuncEHLabel,
93-
const FrameInfo *ChainedParent)
93+
FrameInfo *ChainedParent)
9494
: Begin(BeginFuncEHLabel), Function(Function),
95-
ChainedParent(ChainedParent) {}
95+
Version(ChainedParent->Version), ChainedParent(ChainedParent) {}
9696

9797
bool empty() const {
9898
if (!Instructions.empty())

llvm/lib/MC/MCAsmStreamer.cpp

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -375,8 +375,7 @@ class MCAsmStreamer final : public MCStreamer {
375375
void emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) override;
376376
void emitWinCFIEndProc(SMLoc Loc) override;
377377
void emitWinCFIFuncletOrFuncEnd(SMLoc Loc) override;
378-
void emitWinCFIStartChained(SMLoc Loc) override;
379-
void emitWinCFIEndChained(SMLoc Loc) override;
378+
void emitWinCFISplitChained(SMLoc Loc) override;
380379
void emitWinCFIPushReg(MCRegister Register, SMLoc Loc) override;
381380
void emitWinCFISetFrame(MCRegister Register, unsigned Offset,
382381
SMLoc Loc) override;
@@ -2175,17 +2174,10 @@ void MCAsmStreamer::emitWinCFIFuncletOrFuncEnd(SMLoc Loc) {
21752174
EmitEOL();
21762175
}
21772176

2178-
void MCAsmStreamer::emitWinCFIStartChained(SMLoc Loc) {
2179-
MCStreamer::emitWinCFIStartChained(Loc);
2177+
void MCAsmStreamer::emitWinCFISplitChained(SMLoc Loc) {
2178+
MCStreamer::emitWinCFISplitChained(Loc);
21802179

2181-
OS << "\t.seh_startchained";
2182-
EmitEOL();
2183-
}
2184-
2185-
void MCAsmStreamer::emitWinCFIEndChained(SMLoc Loc) {
2186-
MCStreamer::emitWinCFIEndChained(Loc);
2187-
2188-
OS << "\t.seh_endchained";
2180+
OS << "\t.seh_splitchained";
21892181
EmitEOL();
21902182
}
21912183

llvm/lib/MC/MCParser/COFFAsmParser.cpp

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,8 @@ class COFFAsmParser : public MCAsmParserExtension {
8080
".seh_endproc");
8181
addDirectiveHandler<&COFFAsmParser::parseSEHDirectiveEndFuncletOrFunc>(
8282
".seh_endfunclet");
83-
addDirectiveHandler<&COFFAsmParser::parseSEHDirectiveStartChained>(
84-
".seh_startchained");
85-
addDirectiveHandler<&COFFAsmParser::parseSEHDirectiveEndChained>(
86-
".seh_endchained");
83+
addDirectiveHandler<&COFFAsmParser::parseSEHDirectiveSplitChained>(
84+
".seh_splitchained");
8785
addDirectiveHandler<&COFFAsmParser::parseSEHDirectiveHandler>(
8886
".seh_handler");
8987
addDirectiveHandler<&COFFAsmParser::parseSEHDirectiveHandlerData>(
@@ -143,8 +141,7 @@ class COFFAsmParser : public MCAsmParserExtension {
143141
bool parseSEHDirectiveStartProc(StringRef, SMLoc);
144142
bool parseSEHDirectiveEndProc(StringRef, SMLoc);
145143
bool parseSEHDirectiveEndFuncletOrFunc(StringRef, SMLoc);
146-
bool parseSEHDirectiveStartChained(StringRef, SMLoc);
147-
bool parseSEHDirectiveEndChained(StringRef, SMLoc);
144+
bool parseSEHDirectiveSplitChained(StringRef, SMLoc);
148145
bool parseSEHDirectiveHandler(StringRef, SMLoc);
149146
bool parseSEHDirectiveHandlerData(StringRef, SMLoc);
150147
bool parseSEHDirectiveAllocStack(StringRef, SMLoc);
@@ -685,15 +682,9 @@ bool COFFAsmParser::parseSEHDirectiveEndFuncletOrFunc(StringRef, SMLoc Loc) {
685682
return false;
686683
}
687684

688-
bool COFFAsmParser::parseSEHDirectiveStartChained(StringRef, SMLoc Loc) {
685+
bool COFFAsmParser::parseSEHDirectiveSplitChained(StringRef, SMLoc Loc) {
689686
Lex();
690-
getStreamer().emitWinCFIStartChained(Loc);
691-
return false;
692-
}
693-
694-
bool COFFAsmParser::parseSEHDirectiveEndChained(StringRef, SMLoc Loc) {
695-
Lex();
696-
getStreamer().emitWinCFIEndChained(Loc);
687+
getStreamer().emitWinCFISplitChained(Loc);
697688
return false;
698689
}
699690

llvm/lib/MC/MCStreamer.cpp

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -749,13 +749,14 @@ void MCStreamer::emitWinCFIEndProc(SMLoc Loc) {
749749
WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc);
750750
if (!CurFrame)
751751
return;
752-
if (CurFrame->ChainedParent)
753-
getContext().reportError(Loc, "Not all chained regions terminated!");
754752

755753
MCSymbol *Label = emitCFILabel();
756754
CurFrame->End = Label;
757-
if (!CurFrame->FuncletOrFuncEnd)
758-
CurFrame->FuncletOrFuncEnd = CurFrame->End;
755+
const MCSymbol **FuncletOrFuncEndPtr =
756+
CurFrame->ChainedParent ? &CurFrame->ChainedParent->FuncletOrFuncEnd
757+
: &CurFrame->FuncletOrFuncEnd;
758+
if (!*FuncletOrFuncEndPtr)
759+
*FuncletOrFuncEndPtr = CurFrame->End;
759760

760761
for (size_t I = CurrentProcWinFrameInfoStartIndex, E = WinFrameInfos.size();
761762
I != E; ++I)
@@ -767,48 +768,49 @@ void MCStreamer::emitWinCFIFuncletOrFuncEnd(SMLoc Loc) {
767768
WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc);
768769
if (!CurFrame)
769770
return;
770-
if (CurFrame->ChainedParent)
771-
getContext().reportError(Loc, "Not all chained regions terminated!");
772771

773772
MCSymbol *Label = emitCFILabel();
774-
CurFrame->FuncletOrFuncEnd = Label;
773+
const MCSymbol **FuncletOrFuncEndPtr =
774+
CurFrame->ChainedParent ? &CurFrame->ChainedParent->FuncletOrFuncEnd
775+
: &CurFrame->FuncletOrFuncEnd;
776+
*FuncletOrFuncEndPtr = Label;
775777
}
776778

777-
void MCStreamer::emitWinCFIStartChained(SMLoc Loc) {
779+
void MCStreamer::emitWinCFISplitChained(SMLoc Loc) {
778780
WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc);
779781
if (!CurFrame)
780782
return;
781783

782-
MCSymbol *StartProc = emitCFILabel();
783-
784-
WinFrameInfos.emplace_back(std::make_unique<WinEH::FrameInfo>(
785-
CurFrame->Function, StartProc, CurFrame));
786-
CurrentWinFrameInfo = WinFrameInfos.back().get();
787-
CurrentWinFrameInfo->TextSection = getCurrentSectionOnly();
788-
}
789-
790-
void MCStreamer::emitWinCFIEndChained(SMLoc Loc) {
791-
WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc);
792-
if (!CurFrame)
793-
return;
794-
if (!CurFrame->ChainedParent)
784+
if (!CurFrame->PrologEnd && !CurFrame->ChainedParent)
795785
return getContext().reportError(
796-
Loc, "End of a chained region outside a chained region!");
786+
Loc, "can't split into a new chained region (.seh_splitchained) in the "
787+
"middle of a prolog in " +
788+
CurFrame->Function->getName());
797789

798790
MCSymbol *Label = emitCFILabel();
799791

792+
// Complete the current frame before starting a new, chained one.
800793
CurFrame->End = Label;
801-
CurrentWinFrameInfo = const_cast<WinEH::FrameInfo *>(CurFrame->ChainedParent);
794+
795+
// All chained frames point to the same parent.
796+
WinEH::FrameInfo *ChainedParent =
797+
CurFrame->ChainedParent ? CurFrame->ChainedParent : CurFrame;
798+
799+
WinFrameInfos.emplace_back(std::make_unique<WinEH::FrameInfo>(
800+
CurFrame->Function, Label, ChainedParent));
801+
CurrentWinFrameInfo = WinFrameInfos.back().get();
802+
CurrentWinFrameInfo->TextSection = getCurrentSectionOnly();
802803
}
803804

804805
void MCStreamer::emitWinEHHandler(const MCSymbol *Sym, bool Unwind, bool Except,
805806
SMLoc Loc) {
806807
WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc);
807808
if (!CurFrame)
808809
return;
809-
if (CurFrame->ChainedParent)
810-
return getContext().reportError(
811-
Loc, "Chained unwind areas can't have handlers!");
810+
811+
// Handlers are always associated with the parent frame.
812+
CurFrame = CurFrame->ChainedParent ? CurFrame->ChainedParent : CurFrame;
813+
812814
CurFrame->ExceptionHandler = Sym;
813815
if (!Except && !Unwind)
814816
getContext().reportError(Loc, "Don't know what kind of handler this is!");
@@ -822,8 +824,6 @@ void MCStreamer::emitWinEHHandlerData(SMLoc Loc) {
822824
WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc);
823825
if (!CurFrame)
824826
return;
825-
if (CurFrame->ChainedParent)
826-
getContext().reportError(Loc, "Chained unwind areas can't have handlers!");
827827
}
828828

829829
void MCStreamer::emitCGProfileEntry(const MCSymbolRefExpr *From,
@@ -994,7 +994,8 @@ void MCStreamer::emitWinCFIBeginEpilogue(SMLoc Loc) {
994994
if (!CurFrame)
995995
return;
996996

997-
if (!CurFrame->PrologEnd)
997+
// Chained unwinds aren't guaranteed to have a prolog.
998+
if (!CurFrame->PrologEnd && !CurFrame->ChainedParent)
998999
return getContext().reportError(
9991000
Loc, "starting epilogue (.seh_startepilogue) before prologue has ended "
10001001
"(.seh_endprologue) in " +

llvm/lib/MC/MCWin64EH.cpp

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ class MCSection;
2222

2323
/// MCExpr that represents the epilog unwind code in an unwind table.
2424
class MCUnwindV2EpilogTargetExpr final : public MCTargetExpr {
25-
const MCSymbol *Function;
26-
const MCSymbol *FunctionEnd;
25+
const WinEH::FrameInfo &FrameInfo;
2726
const MCSymbol *UnwindV2Start;
2827
const MCSymbol *EpilogEnd;
2928
uint8_t EpilogSize;
@@ -32,9 +31,11 @@ class MCUnwindV2EpilogTargetExpr final : public MCTargetExpr {
3231
MCUnwindV2EpilogTargetExpr(const WinEH::FrameInfo &FrameInfo,
3332
const WinEH::FrameInfo::Epilog &Epilog,
3433
uint8_t EpilogSize_)
35-
: Function(FrameInfo.Function), FunctionEnd(FrameInfo.FuncletOrFuncEnd),
36-
UnwindV2Start(Epilog.UnwindV2Start), EpilogEnd(Epilog.End),
37-
EpilogSize(EpilogSize_), Loc(Epilog.Loc) {}
34+
: FrameInfo(FrameInfo), UnwindV2Start(Epilog.UnwindV2Start),
35+
EpilogEnd(Epilog.End), EpilogSize(EpilogSize_), Loc(Epilog.Loc) {
36+
assert(UnwindV2Start && "Epilog must have a start");
37+
assert(EpilogEnd && "Epilog must have an end");
38+
}
3839

3940
public:
4041
static MCUnwindV2EpilogTargetExpr *
@@ -250,6 +251,10 @@ static void EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) {
250251
// so, although there are terminators that are large than 1 byte, the
251252
// starting address of the terminator instruction will always be considered
252253
// inside the epilog).
254+
assert(
255+
LastEpilog.UnwindV2Start &&
256+
"If unwind v2 is enabled, epilog must have a unwind v2 start marker");
257+
assert(LastEpilog.End && "Epilog must have an end");
253258
auto MaybeSize = GetOptionalAbsDifference(
254259
OS->getAssembler(), LastEpilog.End, LastEpilog.UnwindV2Start);
255260
if (!MaybeSize) {
@@ -270,9 +275,14 @@ static void EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) {
270275
// If the last epilog is at the end of the function, we can use a special
271276
// encoding for it. Because of our +1 trick for the size, this will only
272277
// work where that final terminator instruction is 1 byte long.
273-
auto LastEpilogToFuncEnd = GetOptionalAbsDifference(
274-
OS->getAssembler(), info->FuncletOrFuncEnd, LastEpilog.UnwindV2Start);
275-
LastEpilogIsAtEnd = (LastEpilogToFuncEnd == EpilogSize);
278+
// NOTE: At the point where the unwind info is emitted, the function may not
279+
// have ended yet (e.g., if there is EH Handler Data), so assume that we
280+
// aren't at the end (since we can't calculate it).
281+
if (info->End) {
282+
auto LastEpilogToFuncEnd = GetOptionalAbsDifference(
283+
OS->getAssembler(), info->End, LastEpilog.UnwindV2Start);
284+
LastEpilogIsAtEnd = (LastEpilogToFuncEnd == EpilogSize);
285+
}
276286

277287
// If we have an odd number of epilog codes, we need to add a padding code.
278288
size_t numEpilogCodes =
@@ -384,28 +394,28 @@ bool MCUnwindV2EpilogTargetExpr::evaluateAsRelocatableImpl(
384394
MCValue &Res, const MCAssembler *Asm) const {
385395
// Calculate the offset to this epilog, and validate it's within the allowed
386396
// range.
387-
auto Offset = GetOptionalAbsDifference(*Asm, FunctionEnd, UnwindV2Start);
397+
auto Offset = GetOptionalAbsDifference(*Asm, FrameInfo.End, UnwindV2Start);
388398
if (!Offset) {
389399
Asm->getContext().reportError(
390400
Loc, "Failed to evaluate epilog offset for Unwind v2 in " +
391-
Function->getName());
401+
FrameInfo.Function->getName());
392402
return false;
393403
}
394404
assert(*Offset > 0);
395405
constexpr uint16_t MaxEpilogOffset = 0x0fff;
396406
if (*Offset > MaxEpilogOffset) {
397407
Asm->getContext().reportError(
398-
Loc,
399-
"Epilog offset is too large for Unwind v2 in " + Function->getName());
408+
Loc, "Epilog offset is too large for Unwind v2 in " +
409+
FrameInfo.Function->getName());
400410
return false;
401411
}
402412

403-
// Sanity check that all epilogs are the same size.
413+
// Validate that all epilogs are the same size.
404414
auto Size = GetOptionalAbsDifference(*Asm, EpilogEnd, UnwindV2Start);
405415
if (Size != (EpilogSize - 1)) {
406416
Asm->getContext().reportError(
407417
Loc, "Size of this epilog does not match size of last epilog in " +
408-
Function->getName());
418+
FrameInfo.Function->getName());
409419
return false;
410420
}
411421

llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFStreamer.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ void X86WinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) {
4444

4545
// We have to emit the unwind info now, because this directive
4646
// actually switches to the .xdata section.
47-
if (WinEH::FrameInfo *CurFrame = getCurrentWinFrameInfo())
47+
if (WinEH::FrameInfo *CurFrame = getCurrentWinFrameInfo()) {
48+
// Handlers are always associated with the parent frame.
49+
CurFrame = CurFrame->ChainedParent ? CurFrame->ChainedParent : CurFrame;
4850
EHStreamer.EmitUnwindInfo(*this, CurFrame, /* HandlerData = */ true);
51+
}
4952
}
5053

5154
void X86WinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) {

llvm/lib/Target/X86/X86InstrCompiler.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,12 @@ let isPseudo = 1, isMeta = 1, SchedRW = [WriteSystem] in {
272272
"#SEH_UnwindV2Start", []>;
273273
}
274274

275+
// Chain instructions:
276+
let isPseudo = 1, isMeta = 1, SchedRW = [WriteSystem] in {
277+
def SEH_SplitChained : I<0, Pseudo, (outs), (ins),
278+
"#SEH_SplitChained", []>;
279+
}
280+
275281
//===----------------------------------------------------------------------===//
276282
// Pseudo instructions used by KCFI.
277283
//===----------------------------------------------------------------------===//

llvm/lib/Target/X86/X86MCInstLower.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,10 @@ void X86AsmPrinter::EmitSEHInstruction(const MachineInstr *MI) {
17951795
OutStreamer->emitWinCFIUnwindVersion(MI->getOperand(0).getImm());
17961796
break;
17971797

1798+
case X86::SEH_SplitChained:
1799+
OutStreamer->emitWinCFISplitChained();
1800+
break;
1801+
17981802
default:
17991803
llvm_unreachable("expected SEH_ instruction");
18001804
}
@@ -2526,6 +2530,7 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) {
25262530
case X86::SEH_EndEpilogue:
25272531
case X86::SEH_UnwindV2Start:
25282532
case X86::SEH_UnwindVersion:
2533+
case X86::SEH_SplitChained:
25292534
EmitSEHInstruction(MI);
25302535
return;
25312536

0 commit comments

Comments
 (0)