Coroutine rework.

* ucontext backend (default on unix)
* setjmp backend
* fibers backend (default and required on windows)
* Fixed coroutine loop timing issues
* Fixed saving of xmm registers on x64 windows
* Fixed alignment issues
* Updated coroutine sample with cooperative fibonacci calculation.
* Disable glibc security features only when platform jump functions are used
* Removed dependency on fasm.
    * Using fiber api on windows.
    * Other platforms and compilers  will use built in assembler and .S files or API provided by platform libc.
* Replaced stack switching procs with `coroExecWithStack()` which never returns. This makes compiler always generate proper code.
This commit is contained in:
Rokas Kupstys 2017-02-20 17:10:50 +02:00
commit 373e667dbc
11 changed files with 421 additions and 446 deletions

View file

@ -1,62 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Rokas Kupstys
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Architecture-specific optimizations and features.
# arch.nim can be imported by only a subset of the
# architectures supported by Nim.
when defined(windows):
const
ABI* = "ms"
elif defined(unix):
const
ABI* = "unix"
else:
{.error: "Unsupported ABI".}
when defined(amd64):
when defined(unix):
# unix (sysv) ABI
type
JmpBufReg* {.pure.} = enum
BX, BP, R12, R13, R14, R15, SP, IP, TOTAL
elif defined(windows):
# ms ABI
type
JmpBufReg* {.pure.} = enum
BX, BP, R12, R13, R14, R15, SP, IP, SI, DI, TOTAL
type
Reg* {.pure.} = enum
AX, BX, CX, DX, SI, DI, BP, SP, IP, R8, R9, R10, R11, R12, R13, R14, R15, TOTAL
elif defined(i386) or defined(nimdoc):
# identical fastcall calling convention on all x86 OS
type
JmpBufReg* {.pure.} = enum
BX, SI, DI, BP, SP, IP, TOTAL
Reg* {.pure.} = enum
AX, BX, CX, BP, SP, DI, SI, TOTAL
else:
{.error: "Unsupported architecture".}
{.compile: "./" & ABI & "_" & hostCPU & ".asm"}
type
JmpBuf* = array[JmpBufReg.TOTAL, pointer]
Registers* = array[Reg.TOTAL, pointer]
proc getRegisters*(ctx: var Registers) {.importc: "narch_$1", fastcall.}
proc setjmp*(ctx: var JmpBuf): int {.importc: "narch_$1", fastcall.}
proc longjmp*(ctx: JmpBuf, ret=1) {.importc: "narch_$1", fastcall.}
proc coroSwitchStack*(sp: pointer) {.importc: "narch_$1", fastcall.}
proc coroRestoreStack*() {.importc: "narch_$1", fastcall.}

View file

@ -1,79 +0,0 @@
;
;
; Nim's Runtime Library
; (c) Copyright 2015 Rokas Kupstys
;
; See the file "copying.txt", included in this
; distribution, for details about the copyright.
;
section ".text" executable
public narch_getRegisters
public @narch_getRegisters@4
public narch_setjmp
public @narch_setjmp@4
public narch_longjmp
public @narch_longjmp@8
public narch_coroSwitchStack
public @narch_coroSwitchStack@4
public narch_coroRestoreStack
public @narch_coroRestoreStack@0
@narch_getRegisters@4:
narch_getRegisters:
mov [ecx], eax
mov [ecx+4], ebx
mov [ecx+8], ecx
mov [ecx+0Ch], ebp
mov [ecx+10h], esp
mov [ecx+14h], edi
mov [ecx+18h], esi
ret
@narch_setjmp@4:
narch_setjmp:
; Based on code from musl libc Copyright © 2005-2014 Rich Felker, et al.
mov [ecx], ebx
mov [ecx+4], esi
mov [ecx+8], edi
mov [ecx+0Ch], ebp
lea eax, [esp+4]
mov [ecx+10h], eax
mov eax, [esp]
mov [ecx+14h], eax
xor eax, eax
ret
@narch_longjmp@8:
narch_longjmp:
; Based on code from musl libc Copyright © 2005-2014 Rich Felker, et al.
mov eax, edx
test eax, eax
jnz @F
inc eax
@@:
mov ebx, [ecx]
mov esi, [ecx+4]
mov edi, [ecx+8]
mov ebp, [ecx+0Ch]
mov esp, [ecx+10h]
mov edx, [ecx+14h]
jmp edx
@narch_coroSwitchStack@4:
narch_coroSwitchStack:
pop eax ; return address
mov edx, esp ; old esp for saving
mov esp, ecx ; swap stack with one passed to func
push edx ; store old stack pointer on newly switched stack
jmp eax ; return
@narch_coroRestoreStack@0:
narch_coroRestoreStack:
pop eax ; return address
pop esp ; resture old stack pointer
jmp eax ; return

View file

@ -1,90 +0,0 @@
;
;
; Nim's Runtime Library
; (c) Copyright 2015 Rokas Kupstys
;
; See the file "copying.txt", included in this
; distribution, for details about the copyright.
;
format MS64 COFF
section ".text" executable align 16
public narch_getRegisters
public narch_setjmp
public narch_longjmp
public narch_coroSwitchStack
public narch_coroRestoreStack
narch_getRegisters:
mov [rcx], rax
mov [rcx+8], rbx
mov [rcx+10h], rcx
mov [rcx+18h], rdx
mov [rcx+20h], rsi
mov [rcx+28h], rdi
mov [rcx+30h], rbp
mov [rcx+38h], rsp
mov rax, [rsp]
mov [rcx+40h], rax ; rip
mov [rcx+48h], r8
mov [rcx+50h], r9
mov [rcx+58h], r10
mov [rcx+60h], r11
mov [rcx+68h], r12
mov [rcx+70h], r13
mov [rcx+78h], r14
mov [rcx+80h], r15
ret
narch_setjmp:
; Based on code from musl libc Copyright © 2005-2014 Rich Felker, et al.
mov [rcx], rbx ; rcx is jmp_buf, move registers onto it
mov [rcx+8], rbp
mov [rcx+10h], r12
mov [rcx+18h], r13
mov [rcx+20h], r14
mov [rcx+28h], r15
lea rdx, [rsp+8] ; this is our rsp WITHOUT current ret addr
mov [rcx+30h], rdx
mov rdx, [rsp] ; save return addr ptr for new rip
mov [rcx+38h], rdx
mov [rcx+40h], rsi
mov [rcx+48h], rdi
xor rax, rax ; always return 0
ret
narch_longjmp:
; Based on code from musl libc Copyright © 2005-2014 Rich Felker, et al.
mov rax, rdx ; val will be longjmp return
test rax, rax
jnz @F
inc rax ; if val==0, val=1 per longjmp semantics
@@:
mov rbx, [rcx] ; rax is the jmp_buf, restore regs from it
mov rbp, [rcx+8]
mov r12, [rcx+10h]
mov r13, [rcx+18h]
mov r14, [rcx+20h]
mov r15, [rcx+28h]
mov rsp, [rcx+30h] ; this ends up being the stack pointer
mov rdx, [rcx+38h] ; this is the instruction pointer
jmp rdx ; goto saved address without altering rsp
narch_coroSwitchStack:
pop rax ; return address
mov rdx, rsp ; old rsp for saving
mov rsp, rcx ; swap stack with one passed to func
push rdx ; store old stack pointer on newly switched stack
sub rsp, 28h ; stack alignment + shadow space
jmp rax ; return
narch_coroRestoreStack:
pop rax ; return address
add rsp, 28h ; stack alignment + shadow space
pop rsp ; resture old stack pointer
jmp rax ; return

View file

@ -1,12 +0,0 @@
;
;
; Nim's Runtime Library
; (c) Copyright 2015 Rokas Kupstys
;
; See the file "copying.txt", included in this
; distribution, for details about the copyright.
;
format MS COFF
include 'i386.asm'

View file

@ -1,89 +0,0 @@
;
;
; Nim's Runtime Library
; (c) Copyright 2015 Rokas Kupstys
;
; See the file "copying.txt", included in this
; distribution, for details about the copyright.
;
format ELF64
section ".text" executable align 16
public narch_getRegisters
public narch_setjmp
public narch_longjmp
public narch_coroSwitchStack
public narch_coroRestoreStack
narch_getRegisters:
mov [rdi], rax
mov [rdi+8], rbx
mov [rdi+10h], rcx
mov [rdi+18h], rdx
mov [rdi+20h], rsi
mov [rdi+28h], rdi
mov [rdi+30h], rbp
mov [rdi+38h], rsp
mov rax, [rsp]
mov [rdi+40h], rax ; rip
mov [rdi+48h], r8
mov [rdi+50h], r9
mov [rdi+58h], r10
mov [rdi+60h], r11
mov [rdi+68h], r12
mov [rdi+70h], r13
mov [rdi+78h], r14
mov [rdi+80h], r15
ret
narch_setjmp:
; Based on code from musl libc Copyright © 2005-2014 Rich Felker, et al.
mov [rdi], rbx ; rdi is jmp_buf, move registers onto it
mov [rdi+8], rbp
mov [rdi+10h], r12
mov [rdi+18h], r13
mov [rdi+20h], r14
mov [rdi+28h], r15
lea rdx, [rsp+8] ; this is our rsp WITHOUT current ret addr
mov [rdi+30h], rdx
mov rdx, [rsp] ; save return addr ptr for new rip
mov [rdi+38h], rdx
xor rax, rax ; always return 0
ret
narch_longjmp:
; Based on code from musl libc Copyright © 2005-2014 Rich Felker, et al.
mov rax, rsi ; val will be longjmp return
test rax, rax
jnz @F
inc rax ; if val==0, val=1 per longjmp semantics
@@:
mov rbx, [rdi] ; rdi is the jmp_buf, restore regs from it
mov rbp, [rdi+8]
mov r12, [rdi+10h]
mov r13, [rdi+18h]
mov r14, [rdi+20h]
mov r15, [rdi+28h]
mov rsp, [rdi+30h] ; this ends up being the stack pointer
mov rdx, [rdi+38h] ; this is the instruction pointer
jmp rdx ; goto saved address without altering rsp
narch_coroSwitchStack:
pop rsi ; return address
mov rdx, rsp ; old rsp for saving
mov rsp, rdi ; swap stack with one passed to func
push rdx ; store old stack pointer on newly switched stack
sub rsp, 8h ; stack alignment
jmp rsi ; return
narch_coroRestoreStack:
pop rsi ; return address
add rsp, 8h ; stack alignment
pop rsp ; resture old stack pointer
jmp rsi ; return

View file

@ -1,12 +0,0 @@
;
;
; Nim's Runtime Library
; (c) Copyright 2015 Rokas Kupstys
;
; See the file "copying.txt", included in this
; distribution, for details about the copyright.
;
format ELF
include 'i386.asm'

96
lib/arch/x86/amd64.S Normal file
View file

@ -0,0 +1,96 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Rokas Kupstys
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Partially based on code from musl libc Copyright © 2005-2014 Rich Felker, et al.
.globl narch_coroExecWithStack
.globl narch_setjmp
.globl narch_longjmp
.text
# SysV ABI - first argument is rdi.
# MS ABI - first argument is rcx.
#if defined(__MINGW32__) || defined(__MINGW64__)
#define REG_ARG1 rcx
#define REG_ARG2 rdx
#else
#define REG_ARG1 rdi
#define REG_ARG2 rsi
#endif
narch_coroExecWithStack:
mov %REG_ARG2, %rsp # swap stack with one passed to func
sub $0x30, %rsp # shadow space (for ms ABI) 0x20 + 0x10 for possible misalignment
and $-0x10, %rsp # 16-byte stack alignment
call *%REG_ARG1
narch_setjmp:
add $0x10, %REG_ARG1 # 16-byte alignment
and $-0x10, %REG_ARG1
mov %rbx, 0x00(%REG_ARG1) # jmp_buf, move registers onto it
mov %rbp, 0x08(%REG_ARG1)
mov %r12, 0x10(%REG_ARG1)
mov %r13, 0x18(%REG_ARG1)
mov %r14, 0x20(%REG_ARG1)
mov %r15, 0x28(%REG_ARG1)
lea 0x08(%rsp), %rdx # this is our rsp WITHOUT current ret addr
mov %rdx, 0x30(%REG_ARG1)
mov (%rsp), %rdx # save return addr ptr for new rip
mov %rdx, 0x38(%REG_ARG1)
mov %rsi, 0x40(%REG_ARG1)
mov %rdi, 0x48(%REG_ARG1)
#if defined(__MINGW32__) || defined(__MINGW64__)
movaps %xmm6, 0x50(%REG_ARG1)
movaps %xmm7, 0x60(%REG_ARG1)
movaps %xmm8, 0x70(%REG_ARG1)
movaps %xmm9, 0x80(%REG_ARG1)
movaps %xmm10, 0x90(%REG_ARG1)
movaps %xmm11, 0xA0(%REG_ARG1)
movaps %xmm12, 0xB0(%REG_ARG1)
movaps %xmm13, 0xC0(%REG_ARG1)
movaps %xmm14, 0xD0(%REG_ARG1)
movaps %xmm15, 0xE0(%REG_ARG1)
#endif
xor %rax, %rax # always return 0
ret
narch_longjmp:
add $0x10, %REG_ARG1 # 16-byte alignment
and $-0x10, %REG_ARG1 #
mov %REG_ARG2, %rax # val will be longjmp return
test %rax, %rax
jnz narch_longjmp_1
inc %rax # if val==0, val=1 per longjmp semantics
narch_longjmp_1:
mov 0x00(%REG_ARG1), %rbx # jmp_buf, restore regs from it
mov 0x08(%REG_ARG1), %rbp
mov 0x10(%REG_ARG1), %r12
mov 0x18(%REG_ARG1), %r13
mov 0x20(%REG_ARG1), %r14
mov 0x28(%REG_ARG1), %r15
mov 0x30(%REG_ARG1), %rsp # this ends up being the stack pointer
mov 0x38(%REG_ARG1), %rdx # this is the instruction pointer
mov 0x40(%REG_ARG1), %rsi
mov 0x48(%REG_ARG1), %rdi
#if defined(__MINGW32__) || defined(__MINGW64__)
movaps 0x50(%REG_ARG1), %xmm6
movaps 0x60(%REG_ARG1), %xmm7
movaps 0x70(%REG_ARG1), %xmm8
movaps 0x80(%REG_ARG1), %xmm9
movaps 0x90(%REG_ARG1), %xmm10
movaps 0xA0(%REG_ARG1), %xmm11
movaps 0xB0(%REG_ARG1), %xmm12
movaps 0xC0(%REG_ARG1), %xmm13
movaps 0xD0(%REG_ARG1), %xmm14
movaps 0xE0(%REG_ARG1), %xmm15
#endif
jmp *%rdx # goto saved address without altering rsp

64
lib/arch/x86/i386.S Normal file
View file

@ -0,0 +1,64 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Rokas Kupstys
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Partially based on code from musl libc Copyright © 2005-2014 Rich Felker, et al.
.globl narch_coroExecWithStack
.globl narch_setjmp
.globl narch_longjmp
#if defined(__MINGW32__) || defined(__MINGW64__)
.globl @narch_coroExecWithStack@8
.globl @narch_setjmp@4
.globl @narch_longjmp@8
#endif
.text
#if defined(__MINGW32__) || defined(__MINGW64__)
@narch_coroExecWithStack@8:
#endif
narch_coroExecWithStack:
mov %edx, %esp # swap stack with one passed to func
sub $0x10, %esp # 16-byte alignment
and $-0x10, %esp #
sub $4, %esp # Simulate misalignment caused by return addr
jmp *%ecx
#if defined(__MINGW32__) || defined(__MINGW64__)
@narch_setjmp@4:
#endif
narch_setjmp:
mov %ebx, (%ecx)
mov %esi, 0x04(%ecx)
mov %edi, 0x08(%ecx)
mov %ebp, 0x0C(%ecx)
lea 0x04(%esp), %eax
mov %eax, 0x10(%ecx)
mov (%esp), %eax
mov %eax, 0x14(%ecx)
xor %eax, %eax
ret
#if defined(__MINGW32__) || defined(__MINGW64__)
@narch_longjmp@8:
#endif
narch_longjmp:
mov %edx, %eax
test %eax, %eax
jnz narch_longjmp_1
inc %eax
narch_longjmp_1:
mov (%ecx), %ebx
mov 0x04(%ecx), %esi
mov 0x08(%ecx), %edi
mov 0x0C(%ecx), %ebp
mov 0x10(%ecx), %esp
mov 0x14(%ecx), %edx
jmp *%edx