Skip to content

Commit c3e6555

Browse files
amharctmsri
authored andcommitted
Call Frame Information (CFI) Handling for Basic Block Sections
This patch handles CFI with basic block sections, which unlike DebugInfo does not support ranges. The DWARF standard explicitly requires emitting separate CFI Frame Descriptor Entries for each contiguous fragment of a function. Thus, the CFI information for all callee-saved registers (possibly including the frame pointer, if necessary) have to be emitted along with redefining the Call Frame Address (CFA), viz. where the current frame starts. CFI directives are emitted in FDE’s in the object file with a low_pc, high_pc specification. So, a single FDE must point to a contiguous code region unlike debug info which has the support for ranges. This is what complicates CFI for basic block sections. Now, what happens when we start placing individual basic blocks in unique sections: * Basic block sections allow the linker to randomly reorder basic blocks in the address space such that a given basic block can become non-contiguous with the original function. * The different basic block sections can no longer share the cfi_startproc and cfi_endproc directives. So, each basic block section should emit this independently. * Each (cfi_startproc, cfi_endproc) directive will result in a new FDE that caters to that basic block section. * Now, this basic block section needs to duplicate the information from the entry block to compute the CFA as it is an independent entity. It cannot refer to the FDE of the original function and hence must duplicate all the stuff that is needed to compute the CFA on its own. * We are working on a de-duplication patch that can share common information in FDEs in a CIE (Common Information Entry) and we will present this as a follow up patch. This can significantly reduce the duplication overhead and is particularly useful when several basic block sections are created. * The CFI directives are emitted similarly for registers that are pushed onto the stack, like callee saved registers in the prologue. There are cfi directives that emit how to retrieve the value of the register at that point when the push happened. This has to be duplicated too in a basic block that is floated as a separate section. Differential Revision: https://reviews.llvm.org/D79978
1 parent 16f777f commit c3e6555

File tree

10 files changed

+241
-27
lines changed

10 files changed

+241
-27
lines changed

llvm/include/llvm/CodeGen/TargetFrameLowering.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,17 @@ class TargetFrameLowering {
202202
virtual void emitEpilogue(MachineFunction &MF,
203203
MachineBasicBlock &MBB) const = 0;
204204

205+
/// With basic block sections, emit callee saved frame moves for basic blocks
206+
/// that are in a different section.
207+
virtual void
208+
emitCalleeSavedFrameMoves(MachineBasicBlock &MBB,
209+
MachineBasicBlock::iterator MBBI) const {}
210+
211+
virtual void emitCalleeSavedFrameMoves(MachineBasicBlock &MBB,
212+
MachineBasicBlock::iterator MBBI,
213+
const DebugLoc &DL,
214+
bool IsPrologue) const {}
215+
205216
/// Replace a StackProbe stub (if any) with the actual probe code inline
206217
virtual void inlineStackProbe(MachineFunction &MF,
207218
MachineBasicBlock &PrologueMBB) const {}

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3067,18 +3067,30 @@ void AsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) {
30673067
if (isVerbose() && MBB.hasLabelMustBeEmitted()) {
30683068
OutStreamer->AddComment("Label of block must be emitted");
30693069
}
3070+
auto *BBSymbol = MBB.getSymbol();
30703071
// Switch to a new section if this basic block must begin a section.
30713072
if (MBB.isBeginSection()) {
30723073
OutStreamer->SwitchSection(
30733074
getObjFileLowering().getSectionForMachineBasicBlock(MF->getFunction(),
30743075
MBB, TM));
3075-
CurrentSectionBeginSym = MBB.getSymbol();
3076+
CurrentSectionBeginSym = BBSymbol;
30763077
}
3077-
OutStreamer->emitLabel(MBB.getSymbol());
3078+
OutStreamer->emitLabel(BBSymbol);
3079+
// With BB sections, each basic block must handle CFI information on its own
3080+
// if it begins a section.
3081+
if (MBB.isBeginSection())
3082+
for (const HandlerInfo &HI : Handlers)
3083+
HI.Handler->beginBasicBlock(MBB);
30783084
}
30793085
}
30803086

3081-
void AsmPrinter::emitBasicBlockEnd(const MachineBasicBlock &MBB) {}
3087+
void AsmPrinter::emitBasicBlockEnd(const MachineBasicBlock &MBB) {
3088+
// Check if CFI information needs to be updated for this MBB with basic block
3089+
// sections.
3090+
if (MBB.isEndSection())
3091+
for (const HandlerInfo &HI : Handlers)
3092+
HI.Handler->endBasicBlock(MBB);
3093+
}
30823094

30833095
void AsmPrinter::emitVisibility(MCSymbol *Sym, unsigned Visibility,
30843096
bool IsDefinition) const {

llvm/lib/CodeGen/AsmPrinter/DwarfCFIException.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ void DwarfCFIExceptionBase::markFunctionEnd() {
4747
}
4848

4949
void DwarfCFIExceptionBase::endFragment() {
50-
if (shouldEmitCFI)
50+
if (shouldEmitCFI && !Asm->MF->hasBBSections())
5151
Asm->OutStreamer->emitCFIEndProc();
5252
}
5353

@@ -172,3 +172,12 @@ void DwarfCFIException::endFunction(const MachineFunction *MF) {
172172

173173
emitExceptionTable();
174174
}
175+
176+
void DwarfCFIException::beginBasicBlock(const MachineBasicBlock &MBB) {
177+
beginFragment(&MBB, getExceptionSym);
178+
}
179+
180+
void DwarfCFIException::endBasicBlock(const MachineBasicBlock &MBB) {
181+
if (shouldEmitCFI)
182+
Asm->OutStreamer->emitCFIEndProc();
183+
}

llvm/lib/CodeGen/AsmPrinter/DwarfException.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ class LLVM_LIBRARY_VISIBILITY DwarfCFIException : public DwarfCFIExceptionBase {
6666

6767
void beginFragment(const MachineBasicBlock *MBB,
6868
ExceptionSymbolProvider ESP) override;
69+
70+
void beginBasicBlock(const MachineBasicBlock &MBB) override;
71+
void endBasicBlock(const MachineBasicBlock &MBB) override;
6972
};
7073

7174
class LLVM_LIBRARY_VISIBILITY ARMException : public DwarfCFIExceptionBase {

llvm/lib/CodeGen/CFIInstrInserter.cpp

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -303,28 +303,31 @@ bool CFIInstrInserter::insertCFIInstrs(MachineFunction &MF) {
303303
auto MBBI = MBBInfo.MBB->begin();
304304
DebugLoc DL = MBBInfo.MBB->findDebugLoc(MBBI);
305305

306-
if (PrevMBBInfo->OutgoingCFAOffset != MBBInfo.IncomingCFAOffset) {
306+
// If the current MBB will be placed in a unique section, a full DefCfa
307+
// must be emitted.
308+
const bool ForceFullCFA = MBB.isBeginSection();
309+
310+
if ((PrevMBBInfo->OutgoingCFAOffset != MBBInfo.IncomingCFAOffset &&
311+
PrevMBBInfo->OutgoingCFARegister != MBBInfo.IncomingCFARegister) ||
312+
ForceFullCFA) {
307313
// If both outgoing offset and register of a previous block don't match
308-
// incoming offset and register of this block, add a def_cfa instruction
309-
// with the correct offset and register for this block.
310-
if (PrevMBBInfo->OutgoingCFARegister != MBBInfo.IncomingCFARegister) {
311-
unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfa(
312-
nullptr, MBBInfo.IncomingCFARegister, getCorrectCFAOffset(&MBB)));
313-
BuildMI(*MBBInfo.MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
314-
.addCFIIndex(CFIIndex);
315-
// If outgoing offset of a previous block doesn't match incoming offset
316-
// of this block, add a def_cfa_offset instruction with the correct
317-
// offset for this block.
318-
} else {
319-
unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(
320-
nullptr, getCorrectCFAOffset(&MBB)));
321-
BuildMI(*MBBInfo.MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
322-
.addCFIIndex(CFIIndex);
323-
}
314+
// incoming offset and register of this block, or if this block begins a
315+
// section, add a def_cfa instruction with the correct offset and
316+
// register for this block.
317+
unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfa(
318+
nullptr, MBBInfo.IncomingCFARegister, getCorrectCFAOffset(&MBB)));
319+
BuildMI(*MBBInfo.MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
320+
.addCFIIndex(CFIIndex);
321+
InsertedCFIInstr = true;
322+
} else if (PrevMBBInfo->OutgoingCFAOffset != MBBInfo.IncomingCFAOffset) {
323+
// If outgoing offset of a previous block doesn't match incoming offset
324+
// of this block, add a def_cfa_offset instruction with the correct
325+
// offset for this block.
326+
unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(
327+
nullptr, getCorrectCFAOffset(&MBB)));
328+
BuildMI(*MBBInfo.MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
329+
.addCFIIndex(CFIIndex);
324330
InsertedCFIInstr = true;
325-
// If outgoing register of a previous block doesn't match incoming
326-
// register of this block, add a def_cfa_register instruction with the
327-
// correct register for this block.
328331
} else if (PrevMBBInfo->OutgoingCFARegister !=
329332
MBBInfo.IncomingCFARegister) {
330333
unsigned CFIIndex =
@@ -335,6 +338,14 @@ bool CFIInstrInserter::insertCFIInstrs(MachineFunction &MF) {
335338
InsertedCFIInstr = true;
336339
}
337340

341+
if (ForceFullCFA) {
342+
MF.getSubtarget().getFrameLowering()->emitCalleeSavedFrameMoves(
343+
*MBBInfo.MBB, MBBI);
344+
InsertedCFIInstr = true;
345+
PrevMBBInfo = &MBBInfo;
346+
continue;
347+
}
348+
338349
BitVector SetDifference = PrevMBBInfo->OutgoingCSRSaved;
339350
SetDifference.reset(MBBInfo.IncomingCSRSaved);
340351
for (int Reg : SetDifference.set_bits()) {

llvm/lib/Target/AArch64/AArch64FrameLowering.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ class AArch64FrameLowering : public TargetFrameLowering {
2424
: TargetFrameLowering(StackGrowsDown, Align(16), 0, Align(16),
2525
true /*StackRealignable*/) {}
2626

27-
void emitCalleeSavedFrameMoves(MachineBasicBlock &MBB,
28-
MachineBasicBlock::iterator MBBI) const;
27+
void
28+
emitCalleeSavedFrameMoves(MachineBasicBlock &MBB,
29+
MachineBasicBlock::iterator MBBI) const override;
2930

3031
MachineBasicBlock::iterator
3132
eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,

llvm/lib/Target/X86/X86FrameLowering.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,29 @@ void X86FrameLowering::BuildCFI(MachineBasicBlock &MBB,
479479
.addCFIIndex(CFIIndex);
480480
}
481481

482+
/// Emits Dwarf Info specifying offsets of callee saved registers and
483+
/// frame pointer. This is called only when basic block sections are enabled.
484+
void X86FrameLowering::emitCalleeSavedFrameMoves(
485+
MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) const {
486+
MachineFunction &MF = *MBB.getParent();
487+
if (!hasFP(MF)) {
488+
emitCalleeSavedFrameMoves(MBB, MBBI, DebugLoc{}, true);
489+
return;
490+
}
491+
const MachineModuleInfo &MMI = MF.getMMI();
492+
const MCRegisterInfo *MRI = MMI.getContext().getRegisterInfo();
493+
const unsigned FramePtr = TRI->getFrameRegister(MF);
494+
const unsigned MachineFramePtr =
495+
STI.isTarget64BitILP32() ? unsigned(getX86SubSuperRegister(FramePtr, 64))
496+
: FramePtr;
497+
unsigned DwarfReg = MRI->getDwarfRegNum(MachineFramePtr, true);
498+
// Offset = space for return address + size of the frame pointer itself.
499+
unsigned Offset = (Is64Bit ? 8 : 4) + (Uses64BitFramePtr ? 8 : 4);
500+
BuildCFI(MBB, MBBI, DebugLoc{},
501+
MCCFIInstruction::createOffset(nullptr, DwarfReg, -Offset));
502+
emitCalleeSavedFrameMoves(MBB, MBBI, DebugLoc{}, true);
503+
}
504+
482505
void X86FrameLowering::emitCalleeSavedFrameMoves(
483506
MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
484507
const DebugLoc &DL, bool IsPrologue) const {

llvm/lib/Target/X86/X86FrameLowering.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,14 @@ class X86FrameLowering : public TargetFrameLowering {
5858
void inlineStackProbe(MachineFunction &MF,
5959
MachineBasicBlock &PrologMBB) const override;
6060

61+
void
62+
emitCalleeSavedFrameMoves(MachineBasicBlock &MBB,
63+
MachineBasicBlock::iterator MBBI) const override;
64+
6165
void emitCalleeSavedFrameMoves(MachineBasicBlock &MBB,
6266
MachineBasicBlock::iterator MBBI,
63-
const DebugLoc &DL, bool IsPrologue) const;
67+
const DebugLoc &DL,
68+
bool IsPrologue) const override;
6469

6570
/// emitProlog/emitEpilog - These methods insert prolog and epilog code into
6671
/// the function.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
; RUN: llc -O0 %s --basicblock-sections=all -mtriple=x86_64 -filetype=asm --frame-pointer=all -o - | FileCheck --check-prefix=SECTIONS_CFI %s
2+
; RUN: llc -O0 %s --basicblock-sections=all -mtriple=x86_64 -filetype=asm --frame-pointer=none -o - | FileCheck --check-prefix=SECTIONS_NOFP_CFI %s
3+
; RUN: llc -O0 %s --basicblock-sections=all -mtriple=x86_64 -filetype=obj --frame-pointer=all -o - | llvm-dwarfdump --eh-frame - | FileCheck --check-prefix=EH_FRAME %s
4+
5+
;; void f1();
6+
;; void f3(bool b) {
7+
;; if (b)
8+
;; f1();
9+
;; }
10+
11+
12+
; SECTIONS_CFI: _Z2f3b:
13+
; SECTIONS_CFI: .cfi_startproc
14+
; SECTIONS_CFI: .cfi_def_cfa_offset 16
15+
; SECTIONS_CFI: .cfi_offset %rbp, -16
16+
; SECTIONS_CFI: .cfi_def_cfa_register %rbp
17+
; SECTIONS_CFI: .cfi_endproc
18+
19+
; SECTIONS_CFI: _Z2f3b.1:
20+
; SECTIONS_CFI-NEXT: .cfi_startproc
21+
; SECTIONS_CFI-NEXT: .cfi_def_cfa %rbp, 16
22+
; SECTIONS_CFI-NEXT: .cfi_offset %rbp, -16
23+
; SECTIONS_CFI: .cfi_endproc
24+
25+
; SECTIONS_CFI: _Z2f3b.2:
26+
; SECTIONS_CFI-NEXT: .cfi_startproc
27+
; SECTIONS_CFI-NEXT: .cfi_def_cfa %rbp, 16
28+
; SECTIONS_CFI-NEXT: .cfi_offset %rbp, -16
29+
; SECTIONS_CFI: .cfi_def_cfa
30+
; SECTIONS_CFI: .cfi_endproc
31+
32+
33+
; SECTIONS_NOFP_CFI: _Z2f3b:
34+
; SECTIONS_NOFP_CFI: .cfi_startproc
35+
; SECTIONS_NOFP_CFI: .cfi_def_cfa_offset 16
36+
; SECTIONS_NOFP_CFI: .cfi_endproc
37+
38+
; SECTIONS_NOFP_CFI: _Z2f3b.1:
39+
; SECTIONS_NOFP_CFI-NEXT: .cfi_startproc
40+
; SECTIONS_NOFP_CFI-NEXT: .cfi_def_cfa %rsp, 16
41+
; SECTIONS_NOFP_CFI: .cfi_endproc
42+
43+
; SECTIONS_NOFP_CFI: _Z2f3b.2:
44+
; SECTIONS_NOFP_CFI-NEXT: .cfi_startproc
45+
; SECTIONS_NOFP_CFI-NEXT: .cfi_def_cfa %rsp, 16
46+
; SECTIONS_NOFP_CFI: .cfi_endproc
47+
48+
49+
;; There must be 1 CIE and 3 FDEs.
50+
51+
; EH_FRAME: CIE
52+
; EH_FRAME: DW_CFA_def_cfa
53+
; EH_FRAME: DW_CFA_offset
54+
55+
; EH_FRAME: FDE cie=
56+
; EH_FRAME: DW_CFA_def_cfa_offset
57+
; EH_FRAME: DW_CFA_offset
58+
; EH_FRAME: DW_CFA_def_cfa_register
59+
60+
; EH_FRAME: FDE cie=
61+
; EH_FRAME: DW_CFA_def_cfa
62+
; EH_FRAME: DW_CFA_offset
63+
64+
; EH_FRAME: FDE cie=
65+
; EH_FRAME: DW_CFA_def_cfa
66+
; EH_FRAME: DW_CFA_offset
67+
68+
; Function Attrs: noinline optnone uwtable
69+
define dso_local void @_Z2f3b(i1 zeroext %b) {
70+
entry:
71+
%b.addr = alloca i8, align 1
72+
%frombool = zext i1 %b to i8
73+
store i8 %frombool, i8* %b.addr, align 1
74+
%0 = load i8, i8* %b.addr, align 1
75+
%tobool = trunc i8 %0 to i1
76+
br i1 %tobool, label %if.then, label %if.end
77+
78+
if.then: ; preds = %entry
79+
call void @_Z2f1v()
80+
br label %if.end
81+
82+
if.end: ; preds = %if.then, %entry
83+
ret void
84+
}
85+
86+
declare dso_local void @_Z2f1v()
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
;; This test checks if CFI instructions for all callee saved registers are emitted
2+
;; correctly with basic block sections.
3+
; RUN: llc %s -mtriple=x86_64 -filetype=asm --basicblock-sections=all --frame-pointer=all -o - | FileCheck --check-prefix=SECTIONS_CFI %s
4+
5+
; SECTIONS_CFI: _Z3foob:
6+
; SECTIONS_CFI: .cfi_offset %rbp, -16
7+
; SECTIONS_CFI: .cfi_offset [[RA:%r.+]], -56
8+
; SECTIONS_CFI-NEXT: .cfi_offset [[RB:%r.+]], -48
9+
; SECTIONS_CFI-NEXT: .cfi_offset [[RC:%r.+]], -40
10+
; SECTIONS_CFI-NEXT: .cfi_offset [[RD:%r.+]], -32
11+
; SECTIONS_CFI-NEXT: .cfi_offset [[RE:%r.+]], -24
12+
13+
; SECTIONS_CFI: _Z3foob.1:
14+
; SECTIONS_CFI: .cfi_offset %rbp, -16
15+
; SECTIONS_CFI: .cfi_offset [[RA]], -56
16+
; SECTIONS_CFI-NEXT: .cfi_offset [[RB]], -48
17+
; SECTIONS_CFI-NEXT: .cfi_offset [[RC]], -40
18+
; SECTIONS_CFI-NEXT: .cfi_offset [[RD]], -32
19+
; SECTIONS_CFI-NEXT: .cfi_offset [[RE]], -24
20+
21+
; SECTIONS_CFI: _Z3foob.2:
22+
; SECTIONS_CFI: .cfi_offset %rbp, -16
23+
; SECTIONS_CFI: .cfi_offset [[RA]], -56
24+
; SECTIONS_CFI-NEXT: .cfi_offset [[RB]], -48
25+
; SECTIONS_CFI-NEXT: .cfi_offset [[RC]], -40
26+
; SECTIONS_CFI-NEXT: .cfi_offset [[RD]], -32
27+
; SECTIONS_CFI-NEXT: .cfi_offset [[RE]], -24
28+
29+
30+
;; void foo(bool b) {
31+
;; if (b) // adds a basic block
32+
;; // clobber all callee-save registers to force them to be callee-saved and to
33+
;; // be described by cfi_offset directives.
34+
;; asm("nop" ::: "r12", "r13", "r14", "r15", "rbx");
35+
;; }
36+
37+
define dso_local void @_Z3foob(i1 zeroext %b) {
38+
entry:
39+
%b.addr = alloca i8, align 1
40+
%frombool = zext i1 %b to i8
41+
store i8 %frombool, i8* %b.addr, align 1
42+
%0 = load i8, i8* %b.addr, align 1
43+
%tobool = trunc i8 %0 to i1
44+
br i1 %tobool, label %if.then, label %if.end
45+
46+
if.then: ; preds = %entry
47+
call void asm sideeffect "nop", "~{r12},~{r13},~{r14},~{r15},~{rbx},~{dirflag},~{fpsr},~{flags}"() #1, !srcloc !2
48+
br label %if.end
49+
50+
if.end: ; preds = %if.then, %entry
51+
ret void
52+
}
53+
!2 = !{i32 38}

0 commit comments

Comments
 (0)