In addition to printing the individual fields, synthesize and print the corresponding prolog for the unwind info (in reverse order, to match how it's printed for non-packed unwind info).
Maybe add a comment noting the source of this algorithm.
I guess the steps are in reverse order from the documentation?
I don't understand the if (RF.RegI() + RF.RegF() > 0) check; if the function homes registers, the homing region have to be contiguous.
That said, the compiler could stick any sequence of four instructions here, really; maybe we should just print them as nops.
Yes, that's right; will mention that in a comment.
Normally, if RegI or RegF are nonzero (that was just an obscure way of writing if (RegI() > 0 || RegF() > 0)), either of them will have done the predecrement, e.g. like in the testcase for func5, in forward order:
str x19, [sp, #-80]! stp x0, x1, [sp, #8] stp x2, x3, [sp, #24] stp x4, x5, [sp, #40] stp x6, x7, [sp, #56]
However, if both RegI and RegF are zero (and CR != 1, I think I haven't covered that case), neither of the instructions that normally would have done the predecrement by decrementing the stack by SavSZ (which includes the homing area) have existed, and the only sensible way of writing that prologue would be
stp x0, x1, [sp, #-64]! stp x2, x3, [sp, #16] stp x4, x5, [sp, #32] stp x6, x7, [sp, #48] sub sp, sp, #LocSZ
(which is func6 in the testcase). For that case, one would have SavSZ = 64 and LocSZ = FrameSize - 64, and the final stack decrement doesn't include the 64 bytes that are expected to have been decremented as SavSZ. So for that case, the equivalent unwinding opcodes would be "alloc_s 64, nop, nop, nop", instead of four nops.
Technically yes, the compiler could stick any sequence of instructions there as it just is nops, unwidning-wise, but I think printing this makes it clearer, as to where exactly the homing region is relative to the stack pointer.
Added a comment about the reference for synthesizing the prologue, fixed the homing area stack adjustment check for RegI=0, RegF=0, CR=1, added a testcase for that case and expanded on the comment for that bit in the code.
If strictly following the steps for the equivalent prologue in the docs for the packed format, the "sub sp, sp, #0" corresponds to step 5d/5e. But I guess it's a matter of taste whether to print it or not (and I don't feel strongly about it).
As this is the last instruction in the prologue/first in the epilogue, it shouldn't really matter whether it's included or not, even when unwinding. If it's considered included in the synthesized prologue, the prologue becomes one instruction longer. If unwinding from that particular location, it could consider the unwind part starting from an unfinished prologue and skip the "sub sp, sp, #0" synthesized instruction, which has no effect. Or if it's not included in the synthesized prologue, the unwind is considered originating in the body of the function, and executing the full prologue, with the same effect.
So it's just a matter of taste and/or trying to stick closely to the spec where possible.
I'd prefer to match what MSVC would actually generate where possible.