In the example below epilog_repro using displacement free [rsp] (o_phrase) warns:
[WARN] flowchart(140001000): EH3[140001000]: get_epilog_split_eas(14000101E): Bad instruction for SAVE_NONVOL
While epilog_control with an explicitly encoded [rsp+disp8=0] (o_displ) does not.
; ml64 /nologo /c /Foeh3_repro.obj eh3_repro.asm
; lld-link /nodefaultlib /entry:epilog_repro /subsystem:console /export:epilog_repro /export:epilog_control /out:eh3_repro.exe eh3_repro.obj vcruntime.lib
option casemap:none
option prologue:none
option epilogue:none
extern __CxxFrameHandler3:proc
.code
public epilog_repro
public epilog_control
epilog_repro proc
push r13
sub rsp, 20h
vmovaps xmmword ptr [rsp], xmm6
epilog_repro_body::
test cl, 1
jz short result_path_0
result_path_1:
mov r13, rcx
jmp short epilog_restore
result_path_0:
lea r13, [rcx]
jmp short epilog_restore
epilog_restore:
mov rax, r13
nop
movaps xmm6, xmmword ptr [rsp] ; 0F 28 34 24, source is o_phrase
add rsp, 20h
pop r13
ret
epilog_repro_end::
epilog_repro endp
ALIGN 16
epilog_control proc
push r13
sub rsp, 20h
vmovaps xmmword ptr [rsp], xmm6
epilog_control_body::
test cl, 1
jz short control_path_0
control_path_1:
mov r13, rcx
jmp short control_restore
control_path_0:
lea r13, [rcx]
jmp short control_restore
control_restore:
mov rax, r13
db 0Fh, 28h, 74h, 24h, 00h ; movaps xmm6, [rsp+disp8=0], source is o_displ
add rsp, 20h
pop r13
ret
epilog_control_end::
epilog_control endp
epilog_repro_cleanup proc
ret
epilog_repro_cleanup endp
_xdata SEGMENT READONLY ALIAS(".xdata")
ALIGN 4
epilog_repro_unwind label byte
; Version=1, Flags=EHANDLER|UHANDLER, Prolog=0Bh, 4 unwind code slots
db 19h, 0Bh, 4, 0
db 0Bh, 68h ; UWOP_SAVE_XMM128 xmm6, [rsp]
dw 0
db 06h, 32h ; UWOP_ALLOC_SMALL 20h
db 02h, 0D0h ; UWOP_PUSH_NONVOL r13
ALIGN 4
dd imagerel __CxxFrameHandler3
dd imagerel epilog_repro_funcinfo
; One cleanup, no catches
epilog_repro_funcinfo label dword
dd 19930522h
dd 1
dd imagerel epilog_repro_unwind_map
dd 0
dd 0
dd 2
dd imagerel epilog_repro_ip_to_state
dd 0
dd 0
dd 1
epilog_repro_unwind_map label dword
dd -1
dd imagerel epilog_repro_cleanup
epilog_repro_ip_to_state label dword
dd imagerel epilog_repro
dd -1
dd imagerel epilog_repro_body
dd 0
ALIGN 4
epilog_control_unwind label byte
; Identical prolog description, only a different FuncInfo RVA
db 19h, 0Bh, 4, 0
db 0Bh, 68h ; UWOP_SAVE_XMM128 xmm6, [rsp]
dw 0
db 06h, 32h ; UWOP_ALLOC_SMALL 20h
db 02h, 0D0h ; UWOP_PUSH_NONVOL r13
ALIGN 4
dd imagerel __CxxFrameHandler3
dd imagerel epilog_control_funcinfo
epilog_control_funcinfo label dword
dd 19930522h
dd 1
dd imagerel epilog_repro_unwind_map
dd 0
dd 0
dd 2
dd imagerel epilog_control_ip_to_state
dd 0
dd 0
dd 1
epilog_control_ip_to_state label dword
dd imagerel epilog_control
dd -1
dd imagerel epilog_control_body
dd 0
_xdata ENDS
_pdata SEGMENT READONLY ALIAS(".pdata")
ALIGN 4
dd imagerel epilog_repro
dd imagerel epilog_repro_end
dd imagerel epilog_repro_unwind
dd imagerel epilog_control
dd imagerel epilog_control_end
dd imagerel epilog_control_unwind
_pdata ENDS
end
Both restore the same register from the same stack address. The functions have matching unwind operations and EH3 metadata, the return value move precedes the restore in both.
Discovered this in a production Rust binary.