Reworked gc support for coroutines. Nim now bootstraps with -d:nimCoroutines

Added gc test to coro.nim
Lots of misc improvements and comments in coro.nim
This commit is contained in:
Rokas Kupstys 2017-02-13 19:42:47 +02:00
commit c3d1b732d6
3 changed files with 231 additions and 164 deletions

View file

@ -68,58 +68,25 @@ proc len(stack: ptr GcStack): int =
s = s.next
when defined(nimCoroutines):
proc stackSize(stackBottom: pointer, pos: pointer=nil): int {.noinline.} =
var sp: pointer
if pos == nil:
var stackTop {.volatile.}: pointer
sp = addr(stackTop)
else:
sp = pos
result = abs(cast[int](sp) - cast[int](stackBottom))
proc GC_addStack*(starts: pointer) {.cdecl, exportc.} =
var sp {.volatile.}: pointer
var stack = cast[ptr GcStack](alloc0(sizeof(GcStack)))
stack.starts = starts
stack.pos = addr sp
if gch.stack == nil:
gch.stack = stack
else:
stack.next = gch.stack
gch.stack.prev = stack
gch.stack = stack
# c_fprintf(stdout, "[GC] added stack 0x%016X\n", starts)
proc GC_removeStack*(starts: pointer) {.cdecl, exportc.} =
var stack = gch.stack
while stack != nil:
if stack.starts == starts:
if stack.prev == nil:
if stack.next != nil:
stack.next.prev = nil
gch.stack = stack.next
else:
stack.prev.next = stack.next
if stack.next != nil:
stack.next.prev = stack.prev
dealloc(stack)
# echo "[GC] removed stack ", starts.repr
break
proc stackSize(stack: ptr GcStack): int {.noinline.} =
if stack.pos != nil:
when defined(stackIncreases):
result = cast[ByteAddress](stack.pos) -% cast[ByteAddress](stack.bottom)
else:
stack = stack.next
result = cast[ByteAddress](stack.bottom) -% cast[ByteAddress](stack.pos)
else:
result = 0
proc GC_setCurrentStack*(starts: pointer) {.cdecl, exportc.} =
var pos {.volatile.}: pointer
pos = addr(pos)
var stack = gch.stack
while stack != nil:
if stack.starts == starts:
stack.pos = pos
stack.maxStackSize = max(stack.maxStackSize, stackSize(stack.starts, pos))
gch.stackActive = stack
return
stack = stack.next
gcAssert(false, "Current stack position does not belong to registered stack")
proc setPosition(stack: ptr GcStack, position: pointer) =
stack.pos = position
stack.maxStackSize = max(stack.maxStackSize, stack.stackSize())
proc setPosition(stack: var GcStack, position: pointer) =
setPosition(addr(stack), position)
proc stackSize(): int {.noinline.} =
for stack in gch.stack.items():
result = result + stack.stackSize()
else:
proc stackSize(): int {.noinline.} =
var stackTop {.volatile.}: pointer
@ -180,18 +147,50 @@ elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
else:
const stackIncreases = false
{.push stack_trace: off.}
when defined(nimCoroutines):
proc GC_addStack(bottom: pointer) {.cdecl, exportc.} =
# c_fprintf(stdout, "GC_addStack: %p;\n", bottom)
var stack = gch.stack.append()
stack.bottom = bottom
stack.setPosition(bottom)
proc GC_removeStack(bottom: pointer) {.cdecl, exportc.} =
# c_fprintf(stdout, "GC_removeStack: %p;\n", bottom)
gch.stack.find(bottom).remove()
proc GC_setActiveStack(bottom: pointer) {.cdecl, exportc.} =
## Sets active stack and updates current stack position.
# c_fprintf(stdout, "GC_setActiveStack: %p;\n", bottom)
var sp {.volatile.}: pointer
gch.activeStack = gch.stack.find(bottom)
gch.activeStack.setPosition(addr(sp))
when not defined(useNimRtl):
{.push stack_trace: off.}
proc setStackBottom(theStackBottom: pointer) =
#c_fprintf(stdout, "stack bottom: %p;\n", theStackBottom)
# the first init must be the one that defines the stack bottom:
when defined(nimCoroutines):
if gch.stack == nil:
# `setStackBottom()` gets called multiple times from main thread.
# Add it only once.
GC_addStack(theStackBottom)
GC_setCurrentStack(theStackBottom)
else:
when defined(nimCoroutines):
proc setStackBottom(theStackBottom: pointer) =
# Initializes main stack of the thread.
if gch.stack.next == nil:
# Main stack was not initialized yet
gch.stack.next = addr(gch.stack)
gch.stack.prev = addr(gch.stack)
gch.stack.bottom = theStackBottom
gch.stack.maxStackSize = 0
gch.activeStack = addr(gch.stack)
else:
var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2
var b = cast[ByteAddress](gch.stack.bottom)
#c_fprintf(stdout, "old: %p new: %p;\n",gch.stackBottom,theStackBottom)
when stackIncreases:
gch.stack.bottom = cast[pointer](min(a, b))
else:
gch.stack.bottom = cast[pointer](max(a, b))
gch.stack.setPosition(theStackBottom)
else:
proc setStackBottom(theStackBottom: pointer) =
#c_fprintf(stdout, "stack bottom: %p;\n", theStackBottom)
# the first init must be the one that defines the stack bottom:
if gch.stackBottom == nil: gch.stackBottom = theStackBottom
else:
var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2
@ -201,7 +200,7 @@ when not defined(useNimRtl):
gch.stackBottom = cast[pointer](min(a, b))
else:
gch.stackBottom = cast[pointer](max(a, b))
{.pop.}
{.pop.}
when defined(sparc): # For SPARC architecture.
when defined(nimCoroutines):
@ -272,12 +271,10 @@ else:
proc isOnStack(p: pointer): bool =
var stackTop {.volatile.}: pointer
stackTop = addr(stackTop)
for stack in items(gch.stack):
var b = cast[ByteAddress](stack.starts)
var a = cast[ByteAddress](stack.starts) - stack.maxStackSize
var x = cast[ByteAddress](p)
if a <=% x and x <=% b:
return true
var b = cast[ByteAddress](gch.activeStack.bottom)
var a = cast[ByteAddress](stackTop)
var x = cast[ByteAddress](p)
result = a <=% x and x <=% b
template forEachStackSlot(gch, gcMark: untyped) {.dirty.} =
# We use a jmp_buf buffer that is in the C stack.
@ -285,35 +282,38 @@ else:
# that 'setjmp' will save registers in the C stack.
type PStackSlice = ptr array[0..7, pointer]
var registers {.noinit.}: C_JmpBuf
discard c_setjmp(registers)
gch.stackActive.pos = addr(registers)
for stack in items(gch.stack):
stack.maxStackSize = max(stack.maxStackSize, stackSize(stack.starts))
var max = cast[ByteAddress](stack.starts)
var sp = cast[ByteAddress](stack.pos)
when defined(amd64):
if stack == gch.stackActive:
# words within the jmp_buf structure may not be properly aligned.
let regEnd = sp +% sizeof(registers)
while sp <% regEnd:
gcMark(gch, cast[PPointer](sp)[])
gcMark(gch, cast[PPointer](sp +% sizeof(pointer) div 2)[])
sp = sp +% sizeof(pointer)
# loop unrolled:
while sp <% max - 8*sizeof(pointer):
gcMark(gch, cast[PStackSlice](sp)[0])
gcMark(gch, cast[PStackSlice](sp)[1])
gcMark(gch, cast[PStackSlice](sp)[2])
gcMark(gch, cast[PStackSlice](sp)[3])
gcMark(gch, cast[PStackSlice](sp)[4])
gcMark(gch, cast[PStackSlice](sp)[5])
gcMark(gch, cast[PStackSlice](sp)[6])
gcMark(gch, cast[PStackSlice](sp)[7])
sp = sp +% sizeof(pointer)*8
# last few entries:
while sp <=% max:
gcMark(gch, cast[PPointer](sp)[])
sp = sp +% sizeof(pointer)
# Update position of stack gc is executing in.
gch.activeStack.setPosition(addr(registers))
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
for stack in gch.stack.items():
var max = cast[ByteAddress](stack.bottom)
var sp = cast[ByteAddress](addr(registers))
when defined(amd64):
if stack == gch.activeStack:
# words within the jmp_buf structure may not be properly aligned.
let regEnd = sp +% sizeof(registers)
while sp <% regEnd:
gcMark(gch, cast[PPointer](sp)[])
gcMark(gch, cast[PPointer](sp +% sizeof(pointer) div 2)[])
sp = sp +% sizeof(pointer)
# Make sure sp is word-aligned
sp = sp and not (sizeof(pointer) - 1)
# loop unrolled:
while sp <% max - 8*sizeof(pointer):
gcMark(gch, cast[PStackSlice](sp)[0])
gcMark(gch, cast[PStackSlice](sp)[1])
gcMark(gch, cast[PStackSlice](sp)[2])
gcMark(gch, cast[PStackSlice](sp)[3])
gcMark(gch, cast[PStackSlice](sp)[4])
gcMark(gch, cast[PStackSlice](sp)[5])
gcMark(gch, cast[PStackSlice](sp)[6])
gcMark(gch, cast[PStackSlice](sp)[7])
sp = sp +% sizeof(pointer)*8
# last few entries:
while sp <=% max:
gcMark(gch, cast[PPointer](sp)[])
sp = sp +% sizeof(pointer)
else:
proc isOnStack(p: pointer): bool =
var stackTop {.volatile.}: pointer