https://reviews.llvm.org/D42848 handled CFA related cfi directives but didn't handle callee-save-registers (csr) related cfi directives like .cfi_offset and .cfi_restore. This patch adds that part.
Basically it reuses the framework created in D42848. For each basicblock, the patch tracks which csr set have been saved at its CFG predecessors's exits, and compare the csr set with the set at its previous basicblock's exit (The previous block is the block laid before the current block). If the saved csr set at its previous basicblock's exit is larger, .cfi_restore will be inserted.
It fixes the cfi information for the simple case below. The case is shrinkwrap optimized by llvm:
--------------- 1.cc ---------------- void goo(); long foo(bool cond, long *a) { if (__builtin_expect(cond, 1)) { goo(); return a[0] + a[1] + a[2]; } return 0; } ------------------------------------ clang_without_patch -O2 -fno-omit-frame-pointer -S 1.cc .cfi_startproc # %bb.0: # %entry testb %dil, %dil je .LBB0_1 # %bb.2: # %if.then pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset %rbp, -16 movq %rsp, %rbp .cfi_def_cfa_register %rbp pushq %rbx pushq %rax .cfi_offset %rbx, -24 movq %rsi, %rbx callq _Z3goov movq 8(%rbx), %rax addq (%rbx), %rax addq 16(%rbx), %rax addq $8, %rsp popq %rbx popq %rbp .cfi_def_cfa %rsp, 8 retq .LBB0_1: xorl %eax, %eax retq .Lfunc_end0: .size _Z3foobPl, .Lfunc_end0-_Z3foobPl .cfi_endproc clang_with_patch -O2 -fno-omit-frame-pointer -S 1.cc .cfi_startproc # %bb.0: # %entry testb %dil, %dil je .LBB0_1 # %bb.2: # %if.then pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset %rbp, -16 movq %rsp, %rbp .cfi_def_cfa_register %rbp pushq %rbx pushq %rax .cfi_offset %rbx, -24 movq %rsi, %rbx callq _Z3goov movq 8(%rbx), %rax addq (%rbx), %rax addq 16(%rbx), %rax addq $8, %rsp popq %rbx popq %rbp .cfi_def_cfa %rsp, 8 retq .LBB0_1: .cfi_restore %rbx <==== .cfi_restore inserted. .cfi_restore %rbp <==== .cfi_restore inserted. xorl %eax, %eax <==== without .cfi_restore above, here libunwind will erroneously think the values of %rbx and %rbp in last stack frame are saved in current stack frame. retq .Lfunc_end0: .size _Z3foobPl, .Lfunc_end0-_Z3foobPl .cfi_endproc
You can use BitVectors as sets of registers, with the supported bitwise operations to provide similar set operations as std::set.