Skip to content

Commit 5ff5ddd

Browse files
committed
[Win64] Insert int3 into trailing empty BBs
Otherwise, the Win64 unwinder considers direct branches to such empty trailing BBs to be a branch out of the function. It treats such a branch as a tail call, which can only be part of an epilogue. If the unwinder misclassifies such a branch as part of the epilogue, it will fail to unwind the stack further. This can lead to bad stack traces, or failure to handle exceptions properly. This is described in https://llvm.org/PR45064#c4, and by the comment at the top of the X86AvoidTrailingCallPass.cpp file. It should be safe to insert int3 for such blocks. An empty trailing BB that reaches this pass is pretty much guaranteed to be unreachable. If a program executed such a block, it would fall off the end of the function. Most of the complexity in this patch comes from threading through the "EHFuncletEntry" boolean on the MIRParser and registering the pass so we can stop and start codegen around it. I used an MIR test because we should teach LLVM to optimize away these branches as a follow-up. Reviewed By: hans Differential Revision: https://reviews.llvm.org/D76531
1 parent ebf83c3 commit 5ff5ddd

File tree

10 files changed

+305
-40
lines changed

10 files changed

+305
-40
lines changed

llvm/lib/CodeGen/MIRParser/MILexer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ static MIToken::TokenKind getIdentifierKind(StringRef Identifier) {
260260
.Case("liveout", MIToken::kw_liveout)
261261
.Case("address-taken", MIToken::kw_address_taken)
262262
.Case("landing-pad", MIToken::kw_landing_pad)
263+
.Case("ehfunclet-entry", MIToken::kw_ehfunclet_entry)
263264
.Case("liveins", MIToken::kw_liveins)
264265
.Case("successors", MIToken::kw_successors)
265266
.Case("floatpred", MIToken::kw_floatpred)

llvm/lib/CodeGen/MIRParser/MILexer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ struct MIToken {
114114
kw_liveout,
115115
kw_address_taken,
116116
kw_landing_pad,
117+
kw_ehfunclet_entry,
117118
kw_liveins,
118119
kw_successors,
119120
kw_floatpred,

llvm/lib/CodeGen/MIRParser/MIParser.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ bool MIParser::parseBasicBlockDefinition(
650650
lex();
651651
bool HasAddressTaken = false;
652652
bool IsLandingPad = false;
653+
bool IsEHFuncletEntry = false;
653654
MachineBasicBlockSection SectionType = MBBS_None;
654655
unsigned Alignment = 0;
655656
BasicBlock *BB = nullptr;
@@ -665,6 +666,10 @@ bool MIParser::parseBasicBlockDefinition(
665666
IsLandingPad = true;
666667
lex();
667668
break;
669+
case MIToken::kw_ehfunclet_entry:
670+
IsEHFuncletEntry = true;
671+
lex();
672+
break;
668673
case MIToken::kw_align:
669674
if (parseAlignment(Alignment))
670675
return true;
@@ -708,6 +713,7 @@ bool MIParser::parseBasicBlockDefinition(
708713
if (HasAddressTaken)
709714
MBB->setHasAddressTaken();
710715
MBB->setIsEHPad(IsLandingPad);
716+
MBB->setIsEHFuncletEntry(IsEHFuncletEntry);
711717
if (SectionType != MBBS_None) {
712718
MBB->setSectionType(SectionType);
713719
MF.setBBSectionsType(BasicBlockSection::List);

llvm/lib/CodeGen/MIRPrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,11 @@ void MIPrinter::print(const MachineBasicBlock &MBB) {
630630
OS << "landing-pad";
631631
HasAttributes = true;
632632
}
633+
if (MBB.isEHFuncletEntry()) {
634+
OS << (HasAttributes ? ", " : " (");
635+
OS << "ehfunclet-entry";
636+
HasAttributes = true;
637+
}
633638
if (MBB.getAlignment() != Align(1)) {
634639
OS << (HasAttributes ? ", " : " (");
635640
OS << "align " << MBB.getAlignment().value();

llvm/lib/Target/X86/X86.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ void initializeFixupLEAPassPass(PassRegistry &);
145145
void initializeFPSPass(PassRegistry &);
146146
void initializeWinEHStatePassPass(PassRegistry &);
147147
void initializeX86AvoidSFBPassPass(PassRegistry &);
148+
void initializeX86AvoidTrailingCallPassPass(PassRegistry &);
148149
void initializeX86CallFrameOptimizationPass(PassRegistry &);
149150
void initializeX86CmovConverterPassPass(PassRegistry &);
150151
void initializeX86CondBrFoldingPassPass(PassRegistry &);

llvm/lib/Target/X86/X86AvoidTrailingCall.cpp

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,29 @@
66
//
77
//===----------------------------------------------------------------------===//
88
//
9-
// The Windows x64 unwinder has trouble unwinding the stack when a return
10-
// address points to the end of the function. This pass maintains the invariant
11-
// that every return address is inside the bounds of its parent function or
12-
// funclet by inserting int3 if the last instruction would otherwise be a call.
9+
// The Windows x64 unwinder decodes the instruction stream during unwinding.
10+
// The unwinder decodes forward from the current PC to detect epilogue code
11+
// patterns.
12+
//
13+
// First, this means that there must be an instruction after every
14+
// call instruction for the unwinder to decode. LLVM must maintain the invariant
15+
// that the last instruction of a function or funclet is not a call, or the
16+
// unwinder may decode into the next function. Similarly, a call may not
17+
// immediately precede an epilogue code pattern. As of this writing, the
18+
// SEH_Epilogue pseudo instruction takes care of that.
19+
//
20+
// Second, all non-tail call jump targets must be within the *half-open*
21+
// interval of the bounds of the function. The unwinder distinguishes between
22+
// internal jump instructions and tail calls in an epilogue sequence by checking
23+
// the jump target against the function bounds from the .pdata section. This
24+
// means that the last regular MBB of an LLVM function must not be empty if
25+
// there are regular jumps targeting it.
26+
//
27+
// This pass upholds these invariants by ensuring that blocks at the end of a
28+
// function or funclet are a) not empty and b) do not end in a CALL instruction.
29+
//
30+
// Unwinder implementation for reference:
31+
// https://github.com/dotnet/coreclr/blob/a9f3fc16483eecfc47fb79c362811d870be02249/src/unwinder/amd64/unwinder_amd64.cpp#L1015
1332
//
1433
//===----------------------------------------------------------------------===//
1534

@@ -18,33 +37,35 @@
1837
#include "X86Subtarget.h"
1938
#include "llvm/CodeGen/MachineInstrBuilder.h"
2039

21-
#define DEBUG_TYPE "x86-avoid-trailing-call"
40+
#define AVOIDCALL_DESC "X86 avoid trailing call pass"
41+
#define AVOIDCALL_NAME "x86-avoid-trailing-call"
42+
43+
#define DEBUG_TYPE AVOIDCALL_NAME
2244

2345
using namespace llvm;
2446

2547
namespace {
26-
2748
class X86AvoidTrailingCallPass : public MachineFunctionPass {
2849
public:
2950
X86AvoidTrailingCallPass() : MachineFunctionPass(ID) {}
3051

3152
bool runOnMachineFunction(MachineFunction &MF) override;
3253

33-
private:
34-
StringRef getPassName() const override {
35-
return "X86 avoid trailing call pass";
36-
}
3754
static char ID;
55+
56+
private:
57+
StringRef getPassName() const override { return AVOIDCALL_DESC; }
3858
};
59+
} // end anonymous namespace
3960

4061
char X86AvoidTrailingCallPass::ID = 0;
4162

42-
} // end anonymous namespace
43-
4463
FunctionPass *llvm::createX86AvoidTrailingCallPass() {
4564
return new X86AvoidTrailingCallPass();
4665
}
4766

67+
INITIALIZE_PASS(X86AvoidTrailingCallPass, AVOIDCALL_NAME, AVOIDCALL_DESC, false, false)
68+
4869
// A real instruction is a non-meta, non-pseudo instruction. Some pseudos
4970
// expand to nothing, and some expand to code. This logic conservatively assumes
5071
// they might expand to nothing.
@@ -62,6 +83,11 @@ bool X86AvoidTrailingCallPass::runOnMachineFunction(MachineFunction &MF) {
6283
const X86InstrInfo &TII = *STI.getInstrInfo();
6384
assert(STI.isTargetWin64() && "pass only runs on Win64");
6485

86+
// We don't need to worry about any of the invariants described above if there
87+
// is no unwind info (CFI).
88+
if (!MF.hasWinCFI())
89+
return false;
90+
6591
// FIXME: Perhaps this pass should also replace SEH_Epilogue by inserting nops
6692
// before epilogues.
6793

@@ -73,33 +99,34 @@ bool X86AvoidTrailingCallPass::runOnMachineFunction(MachineFunction &MF) {
7399
if (NextMBB && !NextMBB->isEHFuncletEntry())
74100
continue;
75101

76-
// Find the last real instruction in this block, or previous blocks if this
77-
// block is empty.
78-
MachineBasicBlock::reverse_iterator LastRealInstr;
79-
for (MachineBasicBlock &RMBB :
80-
make_range(MBB.getReverseIterator(), MF.rend())) {
81-
LastRealInstr = llvm::find_if(reverse(RMBB), isRealInstruction);
82-
if (LastRealInstr != RMBB.rend())
83-
break;
84-
}
85-
86-
// Do nothing if this function or funclet has no instructions.
87-
if (LastRealInstr == MF.begin()->rend())
88-
continue;
102+
// Find the last real instruction in this block.
103+
auto LastRealInstr = llvm::find_if(reverse(MBB), isRealInstruction);
89104

90-
// If this is a call instruction, insert int3 right after it with the same
91-
// DebugLoc. Convert back to a forward iterator and advance the insertion
92-
// position once.
93-
if (isCallInstruction(*LastRealInstr)) {
105+
// If the block is empty or the last real instruction is a call instruction,
106+
// insert an int3. If there is a call instruction, insert the int3 between
107+
// the call and any labels or other meta instructions. If the block is
108+
// empty, insert at block end.
109+
bool IsEmpty = LastRealInstr == MBB.rend();
110+
bool IsCall = !IsEmpty && isCallInstruction(*LastRealInstr);
111+
if (IsEmpty || IsCall) {
94112
LLVM_DEBUG({
95-
dbgs() << "inserting int3 after trailing call instruction:\n";
96-
LastRealInstr->dump();
97-
dbgs() << '\n';
113+
if (IsCall) {
114+
dbgs() << "inserting int3 after trailing call instruction:\n";
115+
LastRealInstr->dump();
116+
dbgs() << '\n';
117+
} else {
118+
dbgs() << "inserting int3 in trailing empty MBB:\n";
119+
MBB.dump();
120+
}
98121
});
99122

100-
MachineBasicBlock::iterator MBBI = std::next(LastRealInstr.getReverse());
101-
BuildMI(*LastRealInstr->getParent(), MBBI, LastRealInstr->getDebugLoc(),
102-
TII.get(X86::INT3));
123+
MachineBasicBlock::iterator MBBI = MBB.end();
124+
DebugLoc DL;
125+
if (IsCall) {
126+
MBBI = std::next(LastRealInstr.getReverse());
127+
DL = LastRealInstr->getDebugLoc();
128+
}
129+
BuildMI(MBB, MBBI, DL, TII.get(X86::INT3));
103130
Changed = true;
104131
}
105132
}

llvm/lib/Target/X86/X86TargetMachine.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeX86Target() {
7979
initializeX86ExecutionDomainFixPass(PR);
8080
initializeX86DomainReassignmentPass(PR);
8181
initializeX86AvoidSFBPassPass(PR);
82+
initializeX86AvoidTrailingCallPassPass(PR);
8283
initializeX86SpeculativeLoadHardeningPassPass(PR);
8384
initializeX86FlagsCopyLoweringPassPass(PR);
8485
initializeX86CondBrFoldingPassPass(PR);

0 commit comments

Comments
 (0)