Coroutine support for i386/amd64 platforms unix/windows OSes markAndSweep/refCounting GCs.
This commit is contained in:
parent
df0e1a515b
commit
43bfda057b
13 changed files with 843 additions and 320 deletions
275
lib/system/gc_common.nim
Normal file
275
lib/system/gc_common.nim
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2015 Rokas Kupstys
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
proc len(stack: ptr GcStack): int =
|
||||
if stack == nil:
|
||||
return 0
|
||||
|
||||
var s = stack
|
||||
result = 1
|
||||
while s.next != nil:
|
||||
inc(result)
|
||||
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(c_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
|
||||
else:
|
||||
stack = stack.next
|
||||
|
||||
proc GC_setCurrentStack*(starts, pos: pointer) {.cdecl, exportc.} =
|
||||
var stack = gch.stack
|
||||
while stack != nil:
|
||||
if stack.starts == starts:
|
||||
stack.pos = pos
|
||||
stack.maxStackSize = max(stack.maxStackSize, stackSize(stack.starts, pos))
|
||||
return
|
||||
stack = stack.next
|
||||
gcAssert(false, "Current stack position does not belong to registered stack")
|
||||
else:
|
||||
proc stackSize(): int {.noinline.} =
|
||||
var stackTop {.volatile.}: pointer
|
||||
result = abs(cast[int](addr(stackTop)) - cast[int](gch.stackBottom))
|
||||
|
||||
iterator items(stack: ptr GcStack): ptr GcStack =
|
||||
var s = stack
|
||||
while not isNil(s):
|
||||
yield s
|
||||
s = s.next
|
||||
|
||||
var
|
||||
localGcInitialized {.rtlThreadVar.}: bool
|
||||
|
||||
proc setupForeignThreadGc*() =
|
||||
## call this if you registered a callback that will be run from a thread not
|
||||
## under your control. This has a cheap thread-local guard, so the GC for
|
||||
## this thread will only be initialized once per thread, no matter how often
|
||||
## it is called.
|
||||
if not localGcInitialized:
|
||||
localGcInitialized = true
|
||||
var stackTop {.volatile.}: pointer
|
||||
setStackBottom(addr(stackTop))
|
||||
initGC()
|
||||
|
||||
# ----------------- stack management --------------------------------------
|
||||
# inspired from Smart Eiffel
|
||||
|
||||
when defined(sparc):
|
||||
const stackIncreases = false
|
||||
elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
|
||||
defined(hp9000s700) or defined(hp9000s800) or defined(hp9000s820):
|
||||
const stackIncreases = true
|
||||
else:
|
||||
const stackIncreases = false
|
||||
|
||||
when not defined(useNimRtl):
|
||||
{.push stack_trace: off.}
|
||||
proc setStackBottom(theStackBottom: pointer) =
|
||||
#c_fprintf(c_stdout, "stack bottom: %p;\n", theStackBottom)
|
||||
# the first init must be the one that defines the stack bottom:
|
||||
when defined(nimCoroutines):
|
||||
GC_addStack(theStackBottom)
|
||||
else:
|
||||
if gch.stackBottom == nil: gch.stackBottom = theStackBottom
|
||||
else:
|
||||
var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2
|
||||
var b = cast[ByteAddress](gch.stackBottom)
|
||||
#c_fprintf(c_stdout, "old: %p new: %p;\n",gch.stackBottom,theStackBottom)
|
||||
when stackIncreases:
|
||||
gch.stackBottom = cast[pointer](min(a, b))
|
||||
else:
|
||||
gch.stackBottom = cast[pointer](max(a, b))
|
||||
{.pop.}
|
||||
|
||||
when defined(sparc): # For SPARC architecture.
|
||||
when defined(nimCoroutines):
|
||||
{.error: "Nim coroutines are not supported on this platform."}
|
||||
|
||||
proc isOnStack(p: pointer): bool =
|
||||
var stackTop {.volatile.}: pointer
|
||||
stackTop = addr(stackTop)
|
||||
var b = cast[TAddress](gch.stackBottom)
|
||||
var a = cast[TAddress](stackTop)
|
||||
var x = cast[TAddress](p)
|
||||
result = a <=% x and x <=% b
|
||||
|
||||
template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
|
||||
when defined(sparcv9):
|
||||
asm """"flushw \n" """
|
||||
else:
|
||||
asm """"ta 0x3 ! ST_FLUSH_WINDOWS\n" """
|
||||
|
||||
var
|
||||
max = gch.stackBottom
|
||||
sp: PPointer
|
||||
stackTop: array[0..1, pointer]
|
||||
sp = addr(stackTop[0])
|
||||
# Addresses decrease as the stack grows.
|
||||
while sp <= max:
|
||||
gcMark(gch, sp[])
|
||||
sp = cast[PPointer](cast[TAddress](sp) +% sizeof(pointer))
|
||||
|
||||
elif defined(ELATE):
|
||||
{.error: "stack marking code is to be written for this architecture".}
|
||||
|
||||
elif stackIncreases:
|
||||
# ---------------------------------------------------------------------------
|
||||
# Generic code for architectures where addresses increase as the stack grows.
|
||||
# ---------------------------------------------------------------------------
|
||||
when defined(nimCoroutines):
|
||||
{.error: "Nim coroutines are not supported on this platform."}
|
||||
proc isOnStack(p: pointer): bool =
|
||||
var stackTop {.volatile.}: pointer
|
||||
stackTop = addr(stackTop)
|
||||
var a = cast[TAddress](gch.stackBottom)
|
||||
var b = cast[TAddress](stackTop)
|
||||
var x = cast[TAddress](p)
|
||||
result = a <=% x and x <=% b
|
||||
|
||||
var
|
||||
jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
|
||||
# a little hack to get the size of a JmpBuf in the generated C code
|
||||
# in a platform independent way
|
||||
|
||||
template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
|
||||
var registers: C_JmpBuf
|
||||
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
|
||||
var max = cast[TAddress](gch.stackBottom)
|
||||
var sp = cast[TAddress](addr(registers)) +% jmpbufSize -% sizeof(pointer)
|
||||
# sp will traverse the JMP_BUF as well (jmp_buf size is added,
|
||||
# otherwise sp would be below the registers structure).
|
||||
while sp >=% max:
|
||||
gcMark(gch, cast[ppointer](sp)[])
|
||||
sp = sp -% sizeof(pointer)
|
||||
|
||||
else:
|
||||
# ---------------------------------------------------------------------------
|
||||
# Generic code for architectures where addresses decrease as the stack grows.
|
||||
# ---------------------------------------------------------------------------
|
||||
when defined(nimCoroutines):
|
||||
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
|
||||
|
||||
template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
|
||||
# We use a jmp_buf buffer that is in the C stack.
|
||||
# Used to traverse the stack and registers assuming
|
||||
# that 'setjmp' will save registers in the C stack.
|
||||
type PStackSlice = ptr array [0..7, pointer]
|
||||
var registers {.noinit.}: Registers
|
||||
getRegisters(registers)
|
||||
for i in registers.low .. registers.high:
|
||||
gcMark(gch, cast[PPointer](registers[i]))
|
||||
|
||||
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)
|
||||
# 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
|
||||
stackTop = addr(stackTop)
|
||||
var b = cast[ByteAddress](gch.stackBottom)
|
||||
var a = cast[ByteAddress](stackTop)
|
||||
var x = cast[ByteAddress](p)
|
||||
result = a <=% x and x <=% b
|
||||
|
||||
template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
|
||||
# We use a jmp_buf buffer that is in the C stack.
|
||||
# Used to traverse the stack and registers assuming
|
||||
# that 'setjmp' will save registers in the C stack.
|
||||
type PStackSlice = ptr array [0..7, pointer]
|
||||
var registers {.noinit.}: C_JmpBuf
|
||||
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
|
||||
var max = cast[ByteAddress](gch.stackBottom)
|
||||
var sp = cast[ByteAddress](addr(registers))
|
||||
when defined(amd64):
|
||||
# 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)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# end of non-portable code
|
||||
# ----------------------------------------------------------------------------
|
||||
Loading…
Add table
Add a link
Reference in a new issue