EH3 split epilog does not match valid stack restores

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.

Thank you for the detailed report! I confirm the problem, we will fix it.