Skip to content

Commit 33374c4

Browse files
[CFIFixup] Allow function prologues to span more than one basic block (#68984)
The CFIFixup pass assumes a function prologue is contained in a single basic block. This assumption is broken with upcoming support for stack probing (`-fstack-clash-protection`) in AArch64 - the emitted probing sequence in a prologue may contain loops, i.e. more than one basic block. The generated CFG is not arbitrary though: * CFI instructions are outside of any loops * for any two CFI instructions of the function prologue one dominates and is post-dominated by the other Thus, for the prologue CFI instructions, if one is executed then all are executed, there is a total order of executions, and the last instruction in that order can be considered the end of the prologoue for the purpose of inserting the initial `.cfi_remember_state` directive. That last instruction is found by finding the first block in the post-order traversal which contains prologue CFI instructions.
1 parent d4cec1c commit 33374c4

File tree

2 files changed

+45
-29
lines changed

2 files changed

+45
-29
lines changed

llvm/lib/CodeGen/CFIFixup.cpp

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,27 @@
1010
// This pass inserts the necessary instructions to adjust for the inconsistency
1111
// of the call-frame information caused by final machine basic block layout.
1212
// The pass relies in constraints LLVM imposes on the placement of
13-
// save/restore points (cf. ShrinkWrap):
14-
// * there is a single basic block, containing the function prologue
15-
// * possibly multiple epilogue blocks, where each epilogue block is
16-
// complete and self-contained, i.e. CSR restore instructions (and the
17-
// corresponding CFI instructions are not split across two or more blocks.
18-
// * prologue and epilogue blocks are outside of any loops
19-
// Thus, during execution, at the beginning and at the end of each basic block
20-
// the function can be in one of two states:
13+
// save/restore points (cf. ShrinkWrap) and has certain preconditions about
14+
// placement of CFI instructions:
15+
// * For any two CFI instructions of the function prologue one dominates
16+
// and is post-dominated by the other.
17+
// * The function possibly contains multiple epilogue blocks, where each
18+
// epilogue block is complete and self-contained, i.e. CSR restore
19+
// instructions (and the corresponding CFI instructions)
20+
// are not split across two or more blocks.
21+
// * CFI instructions are not contained in any loops.
22+
23+
// Thus, during execution, at the beginning and at the end of each basic block,
24+
// following the prologue, the function can be in one of two states:
2125
// - "has a call frame", if the function has executed the prologue, and
2226
// has not executed any epilogue
2327
// - "does not have a call frame", if the function has not executed the
2428
// prologue, or has executed an epilogue
2529
// which can be computed by a single RPO traversal.
2630

31+
// The location of the prologue is determined by finding the first block in the
32+
// reverse traversal which contains CFI instructions.
33+
2734
// In order to accommodate backends which do not generate unwind info in
2835
// epilogues we compute an additional property "strong no call frame on entry",
2936
// which is set for the entry point of the function and for every block
@@ -85,17 +92,30 @@ static bool isPrologueCFIInstruction(const MachineInstr &MI) {
8592
MI.getFlag(MachineInstr::FrameSetup);
8693
}
8794

88-
static bool containsPrologue(const MachineBasicBlock &MBB) {
89-
return llvm::any_of(MBB.instrs(), isPrologueCFIInstruction);
90-
}
91-
9295
static bool containsEpilogue(const MachineBasicBlock &MBB) {
9396
return llvm::any_of(llvm::reverse(MBB), [](const auto &MI) {
9497
return MI.getOpcode() == TargetOpcode::CFI_INSTRUCTION &&
9598
MI.getFlag(MachineInstr::FrameDestroy);
9699
});
97100
}
98101

102+
static MachineBasicBlock *
103+
findPrologueEnd(MachineFunction &MF, MachineBasicBlock::iterator &PrologueEnd) {
104+
// Even though we should theoretically traverse the blocks in post-order, we
105+
// can't encode correctly cases where prologue blocks are not laid out in
106+
// topological order. Then, assuming topological order, we can just traverse
107+
// the function in reverse.
108+
for (MachineBasicBlock &MBB : reverse(MF)) {
109+
for (MachineInstr &MI : reverse(MBB.instrs())) {
110+
if (!isPrologueCFIInstruction(MI))
111+
continue;
112+
PrologueEnd = std::next(MI.getIterator());
113+
return &MBB;
114+
}
115+
}
116+
return nullptr;
117+
}
118+
99119
bool CFIFixup::runOnMachineFunction(MachineFunction &MF) {
100120
const TargetFrameLowering &TFL = *MF.getSubtarget().getFrameLowering();
101121
if (!TFL.enableCFIFixup(MF))
@@ -105,6 +125,13 @@ bool CFIFixup::runOnMachineFunction(MachineFunction &MF) {
105125
if (NumBlocks < 2)
106126
return false;
107127

128+
// Find the prologue and the point where we can issue the first
129+
// `.cfi_remember_state`.
130+
MachineBasicBlock::iterator PrologueEnd;
131+
MachineBasicBlock *PrologueBlock = findPrologueEnd(MF, PrologueEnd);
132+
if (PrologueBlock == nullptr)
133+
return false;
134+
108135
struct BlockFlags {
109136
bool Reachable : 1;
110137
bool StrongNoFrameOnEntry : 1;
@@ -116,21 +143,15 @@ bool CFIFixup::runOnMachineFunction(MachineFunction &MF) {
116143
BlockInfo[0].StrongNoFrameOnEntry = true;
117144

118145
// Compute the presence/absence of frame at each basic block.
119-
MachineBasicBlock *PrologueBlock = nullptr;
120146
ReversePostOrderTraversal<MachineBasicBlock *> RPOT(&*MF.begin());
121147
for (MachineBasicBlock *MBB : RPOT) {
122148
BlockFlags &Info = BlockInfo[MBB->getNumber()];
123149

124150
// Set to true if the current block contains the prologue or the epilogue,
125151
// respectively.
126-
bool HasPrologue = false;
152+
bool HasPrologue = MBB == PrologueBlock;
127153
bool HasEpilogue = false;
128154

129-
if (!PrologueBlock && !Info.HasFrameOnEntry && containsPrologue(*MBB)) {
130-
PrologueBlock = MBB;
131-
HasPrologue = true;
132-
}
133-
134155
if (Info.HasFrameOnEntry || HasPrologue)
135156
HasEpilogue = containsEpilogue(*MBB);
136157

@@ -149,9 +170,6 @@ bool CFIFixup::runOnMachineFunction(MachineFunction &MF) {
149170
}
150171
}
151172

152-
if (!PrologueBlock)
153-
return false;
154-
155173
// Walk the blocks of the function in "physical" order.
156174
// Every block inherits the frame state (as recorded in the unwind tables)
157175
// of the previous block. If the intended frame state is different, insert
@@ -162,10 +180,7 @@ bool CFIFixup::runOnMachineFunction(MachineFunction &MF) {
162180
// insert a `.cfi_remember_state`, in the case that the current block needs a
163181
// `.cfi_restore_state`.
164182
MachineBasicBlock *InsertMBB = PrologueBlock;
165-
MachineBasicBlock::iterator InsertPt = PrologueBlock->begin();
166-
for (MachineInstr &MI : *PrologueBlock)
167-
if (isPrologueCFIInstruction(MI))
168-
InsertPt = std::next(MI.getIterator());
183+
MachineBasicBlock::iterator InsertPt = PrologueEnd;
169184

170185
assert(InsertPt != PrologueBlock->begin() &&
171186
"Inconsistent notion of \"prologue block\"");

llvm/test/CodeGen/AArch64/cfi-fixup-multi-block-prologue.mir

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 3
12
# RUN: llc -run-pass=cfi-fixup %s -o - | FileCheck %s
23
--- |
34
source_filename = "cfi-fixup.ll"
@@ -111,9 +112,8 @@ body: |
111112
; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $w29, -16
112113
; CHECK-NEXT: $x9 = frame-setup SUBXri $sp, 7, 12
113114
; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa $w9, 28688
114-
; CHECK-NEXT: CFI_INSTRUCTION remember_state
115-
116-
; CHECK: bb.1.entry:
115+
; CHECK-NEXT: {{ $}}
116+
; CHECK-NEXT: bb.1.entry:
117117
; CHECK-NEXT: successors: %bb.2(0x40000000), %bb.1(0x40000000)
118118
; CHECK-NEXT: liveins: $x9
119119
; CHECK-NEXT: {{ $}}
@@ -129,6 +129,7 @@ body: |
129129
; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_register $wsp
130130
; CHECK-NEXT: $sp = frame-setup SUBXri $sp, 1328, 0
131131
; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 30016
132+
; CHECK-NEXT: CFI_INSTRUCTION remember_state
132133
; CHECK-NEXT: frame-setup STRXui $xzr, $sp, 0
133134
; CHECK-NEXT: CBZW renamable $w0, %bb.6
134135
; CHECK-NEXT: {{ $}}

0 commit comments

Comments
 (0)