Repository: dotnet/runtime
File: src/coreclr/nativeaot/Runtime/i386/GcProbe.asm (Assembly)

Code before:
        pop         ecx
        pop         ebp
        ret
endm

;;
;; Sets up a PInvokeTranstionFrame with room for all registers.
;;
;; Register state on entry:
;;  EDX: thread pointer
;;  BITMASK_REG_OR_VALUE: register bitmask, PTTR_SAVE_ALL_PRESERVED at a minimum
;;  EBP: ebp frame setup with correct return address
;;  ESP: points to saved scratch registers
;;
;; Register state on exit:
;;  ESP: pointer to a PInvokeTransitionFrame on the stack
;;  EBX: thread pointer
;;  EAX: trashed
;;  ESI, EDI, EBX, EAX all saved in the frame
;;
;;  ECX is NOT trashed if BITMASK_REG_OR_VALUE is a literal value and not a register
;;
PushProbeFrame macro BITMASK_REG_OR_VALUE
        push        eax                                 ; Save live EAX first
        mov         eax, [ebp - 4]          ; ECX
        xchg        [esp], eax                          ; stack={ECX}, eax=original EAX
        push        eax                     ; EAX
        lea         eax, [ebp + 8]                      ; get caller ESP
        push        eax                     ; ESP
        push        edi                     ; EDI
        push        esi                     ; ESI
        push        ebx                     ; EBX
        push        BITMASK_REG_OR_VALUE    ; register bitmask
ifdef _DEBUG
        mov         eax, BITMASK_REG_OR_VALUE
        and         eax, DEFAULT_PROBE_SAVE_FLAGS
        cmp         eax, DEFAULT_PROBE_SAVE_FLAGS ; make sure we have at least the flags to match what the macro pushes
        je          @F
        call        RhDebugBreak
@@:
endif ;; _DEBUG
        push        edx                     ; Thread *
        mov         eax, [ebp + 0]                      ; find previous EBP value
        push        eax                     ; m_FramePointer
        mov         eax, [ebp + 4]                      ; get return address
        push        eax                     ; m_RIP

        mov         ebx, edx                            ; save Thread pointer for later
endm

;;

Review comment (performance):
I think xchg with mem location on x86 always assumes `.lock` prefix and can be fairly expensive. Can this use something else?

Code after:
        pop         eax
        pop         edx
        pop         ecx
        pop         ebp
        ret
endm

;;
;; Sets up a PInvokeTranstionFrame with room for all registers.
;;
;; Register state on entry:
;;  EDX: thread pointer
;;  BITMASK_REG_OR_VALUE: register bitmask, PTTR_SAVE_ALL_PRESERVED at a minimum
;;  EBP: ebp frame setup with correct return address
;;  ESP: points to saved scratch registers
;;
;; Register state on exit:
;;  ESP: pointer to a PInvokeTransitionFrame on the stack
;;  EBX: thread pointer
;;  EAX: trashed
;;  ESI, EDI, EBX, EAX all saved in the frame
;;
;;  ECX is NOT trashed if BITMASK_REG_OR_VALUE is a literal value and not a register
;;
PushProbeFrame macro BITMASK_REG_OR_VALUE
        push        [ebp - 4]               ; ECX
        push        [ebp - 12]              ; EAX
        lea         eax, [ebp + 8]                      ; get caller ESP
        push        eax                     ; ESP
        push        edi                     ; EDI
        push        esi                     ; ESI
        push        ebx                     ; EBX
        push        BITMASK_REG_OR_VALUE    ; register bitmask
ifdef _DEBUG
        mov         eax, BITMASK_REG_OR_VALUE
        and         eax, DEFAULT_PROBE_SAVE_FLAGS
        cmp         eax, DEFAULT_PROBE_SAVE_FLAGS ; make sure we have at least the flags to match what the macro pushes
        je          @F
        call        RhDebugBreak
@@:
endif ;; _DEBUG
        push        edx                     ; Thread *
        mov         eax, [ebp + 0]                      ; find previous EBP value
        push        eax                     ; m_FramePointer
        mov         eax, [ebp + 4]                      ; get return address
        push        eax                     ; m_RIP

        mov         ebx, edx                            ; save Thread pointer for later
endm

;;