[PATCH] D79978: Call Frame Information (CFI) Handling for Basic Block Sections

Sriraman Tallam via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 10 16:48:55 PDT 2020


tmsriram added a comment.

> Honestly I am not a CFI expert but I have read enough bits of LLVM libunwind and am not completely CFI illiterate (I have fixed a very subtle negative cfiDefCfa bug). The description of the patch is still puzzling me.
>  I think it lacks a summary about what the patch intends to do.

Here is an attempt and trying to help understand what needs to be addressed to get CFI right with basic block sections:

One of the main goals of CFI directives is to allow the unwinder to retrieve the Canonical Frame Address (CFA) from any PC.

CFA - Canonical Frame Address, is the address of the stack pointer just before a call instruction is executed.

FDE - Frame Descriptor Entries are unique to each section and holds the CFI information in eh_frame section

CIE - Common Information Entries that hold information that is common to multiple FDEs in eh_frame section.

Let’s first cover some basics of CFI which will help us understand what needs to change to support  basic block sections. The CFI directives for a simple program without any basic block sections:

  void f1(); 
  void f3(bool b) {
    if (b) f1();
  }

With the frame pointer using -fno-omit-frame-pointer:

  	.type	_Z2f3b, at function
  _Z2f3b:                                 # @_Z2f3b
  	.cfi_startproc
  # %bb.0:                                # %entry
  	pushq	%rbp
  	.cfi_def_cfa_offset 16
  	.cfi_offset %rbp, -16
  	movq	%rsp, %rbp
  	.cfi_def_cfa_register %rbp
              …
  	popq	%rbp
  	.cfi_def_cfa %rsp, 8
  	retq
  .Lfunc_end0:
  	.size	_Z2f3b, .Lfunc_end0-_Z2f3b



- All CFI directives need to be placed between a cfi_startproc and cfi_endproc directive
- A section/function is the smallest granularity for CFI directives. All CFI directives within a section can share the same cfi_startproc and cfi_endproc.  However, each function must have a unique startproc and endproc directive too even without function sections.
- Looking through the directives in sequence, “cfi_def_cfa_offset 16” tells us that initially the CFA (Canonical Frame Address) is 16 bytes from where the stack pointer is currently because we pushed the return address and %rbp onto the stack. “cfi_offset %rbp, -16” says the rbp register’s old value is now available 16 bytes from the CFA, because of the push.
- “.cfi_def_cfa_register %rbp”  basically says that the CFA can now be computed off %rbp as we just moved the stack pointer to it, the original offset of 16 from the value of rbp is used.  This is efficient as we can change the %rsp now as much as we want and we don’t have to worry about generating directives to update CFA.
- So, just before returning from the function we pop %rbp and correctly update that CFI needs to be computed using the %rsp.
- 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.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D79978/new/

https://reviews.llvm.org/D79978





More information about the llvm-commits mailing list