TCounter.Create({rdi_class: PVMT or TCounter; rsi_flag: SizeInt}):
# allocate space for local variables
# [rsp]: SizeInt = rsi_flag
# [rsp+8]: PVMT or TCounter = VMT or instance
# [rsp+16]: SizeInt = destructor flag
# [rsp+24]: TExceptAddr = constructor try..finally frame (24 bytes)
# [rsp+24]: pjmp_buf = buf
# [rsp+32]: PExceptAddr = next
# [rsp+40]: LongInt = frametype
# [rsp+48]: jmp_buf = constructor try..finally target (64 bytes on Linux)
# ... registers
# [rsp+112]: SizeInt = constructor setjmp result
# [rsp+120]: TExceptAddr = destructor try..finally frame (24 bytes)
# [rsp+120]: pjmp_buf = buf
# [rsp+128]: PExceptAddr = next
# [rsp+136]: LongInt = frametype
# [rsp+144]: jmp_buf = destructor try..finally target (64 bytes on Linux)
# ... registers
# [rsp+208]: SizeInt = destructor setjmp result
lea rsp, [rsp-216]
# store arguments
mov [rsp+8], rdi # TCounter VMT or instance
mov [rsp], rsi # flag
# check if allocation needed (flag = 1)
cmp rsi, 1
jne .no_alloc
# allocate new instance
mov rax, [rsp+8] # VMT of TCounter
mov rdx, [rsp+8]
mov rdi, rax # TCounter as self
call [rdx+104] # TCounter.NewInstance()
mov [rsp+8], rax # store instance
.no_alloc:
# run user-defined constructor code if instance not nil
cmp [rsp+8], 0
je .return
# set up try..finally
lea rdx, [rsp+24] # new address
lea rsi, [rsp+48] # jump buffer
mov edi, 1 # exception frame type
call fpc_pushexceptaddr
# set longjmp target
mov rdi, rax # jump buffer
call fpc_setjmp
# get longjmp result linked to jump buffer
movsxd rdx, eax
mov [rsp+112], rdx
# proceed only after original setjmp invocation
test eax, eax
jne .finally
# prevent TCounter.BeforeDestruction from running
mov [rsp+16], -1
# user-defined constructor code
mov rax, [rsp+8]
mov [rax+8], DEADBEEFh
# allow TCounter.BeforeDestruction to run
# reached only if user-defined constructor code did not fail
mov [rsp+16], 1
# check again if instance is not nil
cmp [rsp+8], 0
je .finally
# check if this is the first constructor invocation
cmp [rsp], 0
je .finally
# run post-construction handler
mov rdi, [rsp+8] # instance as self
mov rax, [rsp+8]
mov rax, [rax] # VMT of instance
call [rax+136] # TCounter.AfterConstruction()
.finally:
# clean up try..finally
call fpc_popaddrstack
# run destructor if an exception was raised
mov rax, [rsp+112]
test rax, rax
je .return
# set up try..except
lea rdx, [rsp+120] # new address
lea rsi, [rsp+144] # jump buffer
mov edi, 1 # exception frame type
call fpc_pushexceptaddr
# set longjmp target
mov rdi, rax # jump buffer
call fpc_setjmp
# get longjmp result linked to jump buffer
movsxd rdx, eax
mov [rsp+208], rdx
# proceed only after original setjmp invocation
test eax, eax
jne .finally_destructor
# check if this is the first constructor invocation
cmp [rsp], 0
je .no_dealloc
# run destructor
mov rsi, [rsp+16] # destructor flag
mov rdi, [rsp+8] # instance as self
mov rax, [rsp+8]
mov rax, [rax] # VMT of instance
call [rax+96] # TCounter.Destroy()
.no_dealloc:
# clean up try..finally and re-raise original constructor exception
call fpc_popaddrstack
call fpc_reraise
.finally_destructor:
# clean up try..finally
call fpc_popaddrstack
# check if destructor itself raised an exception
mov rax, [rsp+208]
test rax, rax
je .no_nested_exception
# re-raise nested destructor exception
call fpc_raise_nested
.no_nested_exception:
# clean up potentially re-raised FPC exception objects
call fpc_doneexception
.return:
# deallocate locals and return instance
mov rax, [rsp+8] # instance
lea rsp, [rsp+216]
ret