>> I’m working on a edu/research compiler that has yet to be equipped with DWARF info. Unlike GCC/Clang/… it doesn’t have spill slots that are “constant” within a procedure, i.e. may procedure code like this:
>>>> …
>>>> Is this supported by the DWARF .cfi_offset directives at all, and if so, how? Generally speaking, what’s the “scope”/“validity period”/“life span” of such directive?
What you're describing is "shrink-wrapping". GCC does supports this
optimization, and I think that LLVM is currently working on it.
DWARF's .debug_frame section certainly supports this, and by extension
the .eh_frame section would also support it.
The .cfi_offset directives are described in the gas manual, and you
might find more help on their use, if you need it, on the binutils
mailing list. In general, the scope of a .cfi_offset directive extends
from its use forward until cancelled by a new CFI directive for that
same register, or until the end of the procedure. So in your example,
you could place them as follows:
_main:
…
je B
A:
movl %edi, -8(%rbp) # EDI spill slot: -8
.cfi_offset 5, -8
…
jmp C
B:
.cfi_restore 5 # This cancels the CFI directive above.
movl %eax, -8(%rbp)
.cfi_offset 0, -8
movl %edi, -16(%rbp) # EDI spill slot: -16
.cfi_offset 5, -16
jmp C
C:
…
But at C, you have a problem. You have two paths that both join at C,
but the register save state is different in each path. Theoretically,
you could have some common code at C that doesn't care, then split
apart again (using the same condition) to restore the registers, but
you can't describe the register save state for that common code, which
means that an exception occurring in that range will not work
correctly.
It's fine to have different regions of code with different register
save states, but it's a bad idea to have two paths join without
compensation code at the end of one or both paths to match the states.
For example, you could restore the spilled registers at the end of
each path (which is probably more typical for a shrink-wrapped
function).
-cary