tsoding/coroutines

simplifying the yield procedure and allowing a void* userdata

Closed this issue · 1 comments

You don't need to save the rip separately, you can instead put that on the stack. Then once you exchange the stack pointer you can then return from yield. Even for the first time into the entry point.

You can allow the entry point to take a void* userdata by adding rdi to the saved context and in void coroutine_go(void (*f)(void*), void*); you take the second parameter and save that to be restored into rdi for when you jump into the entry point for the first time.

Making these changes in coroutine_go the sequence will then become

    mov rax, [stacks_end]         ;; rax contains the rsp of the new
                                  ;; routine
    sub QWORD [stacks_end], STACK_CAPACITY

    sub rax, 8
    mov QWORD [rax], coroutine_finish
    sub rax, 8
    mov QWORD [rax], rdi          ;;save the function pointer on the stack

    mov [contexts_rsp+rbx*8], rax
    mov QWORD [contexts_rbp+rbx*8], 0
    mov [contexts_rdi+rbx*8], rsi ;; save second argument user_data

    ret

coroutine_yield then becomes:

coroutine_yield:
    mov rbx, [contexts_current]

    mov [contexts_rsp+rbx*8], rsp
    mov [contexts_rbp+rbx*8], rbp
    mov [contexts_rdi+rbx*8], rdi  ;; technically not needed 

;; mov rbx, [contexts_next]

    mov rsp, [contexts_rsp+rbx*8]
    mov rbp, [contexts_rbp+rbx*8]
    mov rdi, [contexts_rdi+rbx*8]  ;; on first time this will be the user_data 
    ret                            ;; on first time this will pop the entrypoint pointer 
                                   ;; and leave coroutine_finish on top as return target

Thank you for the suggestion. Resolved by f6edda4