summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/system/gc.nim114
-rw-r--r--lib/system/gc2.nim66
-rw-r--r--lib/system/gc_common.nim279
-rw-r--r--lib/system/gc_ms.nim30
4 files changed, 214 insertions, 275 deletions
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index 515aa9851..551dae459 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -64,21 +64,23 @@ type
     maxPause: int64          # max measured GC pause in nanoseconds
 
   GcStack {.final, pure.} = object
-    prev: ptr GcStack
-    next: ptr GcStack
+    when defined(nimCoroutines):
+      prev: ptr GcStack
+      next: ptr GcStack
+      maxStackSize: int      # Used to track statistics because we can not use
+                             # GcStat.maxStackSize when multiple stacks exist.
     bottom: pointer
+
+    when withRealTime or defined(nimCoroutines):
+      pos: pointer           # Used with `withRealTime` only for code clarity, see GC_Step().
     when withRealTime:
       bottomSaved: pointer
-    pos: pointer
-    maxStackSize: int
 
   GcHeap {.final, pure.} = object # this contains the zero count and
-                                   # non-zero count table
+                                  # non-zero count table
+    stack: GcStack
     when defined(nimCoroutines):
-      stack: GcStack
-      activeStack: ptr GcStack
-    else:
-      stackBottom: pointer
+      activeStack: ptr GcStack    # current executing coroutine stack.
     cycleThreshold: int
     when useCellIds:
       idGenerator: int
@@ -122,53 +124,6 @@ template gcAssert(cond: bool, msg: string) =
       #echo x[]
       quit 1
 
-when defined(nimCoroutines):
-  iterator items(first: var GcStack): ptr GcStack =
-    var item = addr(first)
-    while true:
-      yield item
-      item = item.next
-      if item == addr(first):
-        break
-
-  proc append(first: var GcStack, stack: ptr GcStack) =
-    ## Append stack to the ring of stacks.
-    first.prev.next = stack
-    stack.prev = first.prev
-    first.prev = stack
-    stack.next = addr(first)
-
-  proc append(first: var GcStack): ptr GcStack =
-    ## Allocate new GcStack object, append it to the ring of stacks and return it.
-    result = cast[ptr GcStack](alloc0(sizeof(GcStack)))
-    first.append(result)
-
-  proc remove(first: var GcStack, stack: ptr GcStack) =
-    ## Remove stack from ring of stacks.
-    gcAssert(addr(first) != stack, "Main application stack can not be removed")
-    if addr(first) == stack or stack == nil:
-      return
-    stack.prev.next = stack.next
-    stack.next.prev = stack.prev
-    dealloc(stack)
-
-  proc remove(stack: ptr GcStack) =
-    gch.stack.remove(stack)
-
-  proc find(first: var GcStack, bottom: pointer): ptr GcStack =
-    ## Find stack struct based on bottom pointer. If `bottom` is nil then main
-    ## thread stack is is returned.
-    if bottom == nil:
-      return addr(gch.stack)
-
-    for stack in first.items():
-      if stack.bottom == bottom:
-        return stack
-
-  proc len(stack: var GcStack): int =
-    for _ in stack.items():
-      result = result + 1
-
 proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
   if (c.refcount and ZctFlag) == 0:
     c.refcount = c.refcount or ZctFlag
@@ -931,40 +886,25 @@ when withRealTime:
       collectCTBody(gch)
     release(gch)
 
-  when defined(nimCoroutines):
-    proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
-      if stackSize >= 0:
-        var stackTop {.volatile.}: pointer
-        gch.activeStack.pos = addr(stackTop)
-
-        for stack in gch.stack.items():
-          stack.bottomSaved = stack.bottom
-          when stackIncreases:
-            stack.bottom = cast[pointer](
-              cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize)
-          else:
-            stack.bottom = cast[pointer](
-              cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize)
-
-      GC_step(gch, us, strongAdvice)
-
-      if stackSize >= 0:
-        for stack in gch.stack.items():
-          stack.bottom = stack.bottomSaved
-  else:
-    proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
+  proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
+    if stackSize >= 0:
       var stackTop {.volatile.}: pointer
-      let prevStackBottom = gch.stackBottom
-      if stackSize >= 0:
-        stackTop = addr(stackTop)
+      gch.getActiveStack().pos = addr(stackTop)
+
+      for stack in gch.stack.items():
+        stack.bottomSaved = stack.bottom
         when stackIncreases:
-          gch.stackBottom = cast[pointer](
-            cast[ByteAddress](stackTop) - sizeof(pointer) * 6 - stackSize)
+          stack.bottom = cast[pointer](
+            cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize)
         else:
-          gch.stackBottom = cast[pointer](
-            cast[ByteAddress](stackTop) + sizeof(pointer) * 6 + stackSize)
-      GC_step(gch, us, strongAdvice)
-      gch.stackBottom = prevStackBottom
+          stack.bottom = cast[pointer](
+            cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize)
+
+    GC_step(gch, us, strongAdvice)
+
+    if stackSize >= 0:
+      for stack in gch.stack.items():
+        stack.bottom = stack.bottomSaved
 
 when not defined(useNimRtl):
   proc GC_disable() =
diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim
index ce2bfc2ae..083c06fe3 100644
--- a/lib/system/gc2.nim
+++ b/lib/system/gc2.nim
@@ -15,9 +15,6 @@
 
 # XXX Ensure by smart color masking that the object is not in the ZCT.
 
-when defined(nimCoroutines):
-  import arch
-
 {.push profiler:off.}
 
 const
@@ -72,19 +69,26 @@ type
     maxStackCells: int       # max stack cells in ``decStack``
     cycleTableSize: int      # max entries in cycle table
     maxPause: int64          # max measured GC pause in nanoseconds
-
-  GcStack = object
-    prev: ptr GcStack
-    next: ptr GcStack
-    starts: pointer
-    pos: pointer
-    maxStackSize: int
+  
+  GcStack {.final, pure.} = object
+    when nimCoroutines:
+      prev: ptr GcStack
+      next: ptr GcStack
+      maxStackSize: int      # Used to track statistics because we can not use
+                             # GcStat.maxStackSize when multiple stacks exist.
+    bottom: pointer
+
+    when withRealTime or nimCoroutines:
+      pos: pointer           # Used with `withRealTime` only for code clarity, see GC_Step().
+    when withRealTime:
+      bottomSaved: pointer
 
   GcHeap = object # this contains the zero count and
                   # non-zero count table
     black, red: int # either 0 or 1.
-    stack: ptr GcStack
-    stackBottom: pointer
+    stack: GcStack
+    when nimCoroutines:
+      activeStack: ptr GcStack    # current executing coroutine stack.
     phase: Phase
     cycleThreshold: int
     when useCellIds:
@@ -913,7 +917,7 @@ proc collectCTBody(gch: var GcHeap) =
     let t0 = getticks()
   sysAssert(allocInv(gch.region), "collectCT: begin")
 
-  when not defined(nimCoroutines):
+  when not nimCoroutines:
     gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
   sysAssert(gch.decStack.len == 0, "collectCT")
   prepareForInteriorPointerChecking(gch.region)
@@ -938,16 +942,16 @@ proc collectCTBody(gch: var GcHeap) =
       if gch.maxPause > 0 and duration > gch.maxPause:
         c_fprintf(stdout, "[GC] missed deadline: %ld\n", duration)
 
-when defined(nimCoroutines):
+when nimCoroutines:
   proc currentStackSizes(): int =
     for stack in items(gch.stack):
-      result = result + stackSize(stack.starts, stack.pos)
+      result = result + stack.stackSize()
 
 proc collectCT(gch: var GcHeap) =
   # stackMarkCosts prevents some pathological behaviour: Stack marking
   # becomes more expensive with large stacks and large stacks mean that
   # cells with RC=0 are more likely to be kept alive by the stack.
-  when defined(nimCoroutines):
+  when nimCoroutines:
     let stackMarkCosts = max(currentStackSizes() div (16*sizeof(int)), ZctThreshold)
   else:
     let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
@@ -971,18 +975,24 @@ when withRealTime:
       collectCTBody(gch)
 
   proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
-    var stackTop {.volatile.}: pointer
-    let prevStackBottom = gch.stackBottom
     if stackSize >= 0:
-      stackTop = addr(stackTop)
-      when stackIncreases:
-        gch.stackBottom = cast[pointer](
-          cast[ByteAddress](stackTop) - sizeof(pointer) * 6 - stackSize)
-      else:
-        gch.stackBottom = cast[pointer](
-          cast[ByteAddress](stackTop) + sizeof(pointer) * 6 + stackSize)
+      var stackTop {.volatile.}: pointer
+      gch.getActiveStack().pos = addr(stackTop)
+
+      for stack in gch.stack.items():
+        stack.bottomSaved = stack.bottom
+        when stackIncreases:
+          stack.bottom = cast[pointer](
+            cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize)
+        else:
+          stack.bottom = cast[pointer](
+            cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize)
+
     GC_step(gch, us, strongAdvice)
-    gch.stackBottom = prevStackBottom
+
+    if stackSize >= 0:
+      for stack in gch.stack.items():
+        stack.bottom = stack.bottomSaved
 
 when not defined(useNimRtl):
   proc GC_disable() =
@@ -1024,10 +1034,10 @@ when not defined(useNimRtl):
              "[GC] zct capacity: " & $gch.zct.cap & "\n" &
              "[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
              "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000)
-    when defined(nimCoroutines):
+    when nimCoroutines:
       result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
       for stack in items(gch.stack):
-        result = result & "[GC]   stack " & stack.starts.repr & "[GC]     max stack size " & $stack.maxStackSize & "\n"
+        result = result & "[GC]   stack " & stack.bottom.repr & "[GC]     max stack size " & $stack.maxStackSize & "\n"
     else:
       result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
     GC_enable()
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index c5714eef6..70fd3412f 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -57,26 +57,77 @@ proc isNotForeign*(x: ForeignCell): bool =
   ## No deep copy has to be performed then.
   x.owner == addr(gch)
 
-proc len(stack: ptr GcStack): int =
-  if stack == nil:
-    return 0
+when nimCoroutines:
+  iterator items(first: var GcStack): ptr GcStack =
+    var item = addr(first)
+    while true:
+      yield item
+      item = item.next
+      if item == addr(first):
+        break
+
+  proc append(first: var GcStack, stack: ptr GcStack) =
+    ## Append stack to the ring of stacks.
+    first.prev.next = stack
+    stack.prev = first.prev
+    first.prev = stack
+    stack.next = addr(first)
+
+  proc append(first: var GcStack): ptr GcStack =
+    ## Allocate new GcStack object, append it to the ring of stacks and return it.
+    result = cast[ptr GcStack](alloc0(sizeof(GcStack)))
+    first.append(result)
+
+  proc remove(first: var GcStack, stack: ptr GcStack) =
+    ## Remove stack from ring of stacks.
+    gcAssert(addr(first) != stack, "Main application stack can not be removed")
+    if addr(first) == stack or stack == nil:
+      return
+    stack.prev.next = stack.next
+    stack.next.prev = stack.prev
+    dealloc(stack)
+
+  proc remove(stack: ptr GcStack) =
+    gch.stack.remove(stack)
+
+  proc find(first: var GcStack, bottom: pointer): ptr GcStack =
+    ## Find stack struct based on bottom pointer. If `bottom` is nil then main
+    ## thread stack is is returned.
+    if bottom == nil:
+      return addr(gch.stack)
+
+    for stack in first.items():
+      if stack.bottom == bottom:
+        return stack
+
+  proc len(stack: var GcStack): int =
+    for _ in stack.items():
+      result = result + 1
+else:
+  # This iterator gets optimized out in forEachStackSlot().
+  iterator items(first: var GcStack): ptr GcStack = yield addr(first)
+  proc len(stack: var GcStack): int = 1
 
-  var s = stack
-  result = 1
-  while s.next != nil:
-    inc(result)
-    s = s.next
+proc stackSize(stack: ptr GcStack): int {.noinline.} =
+  when defined(nimCoroutines):
+    var pos = stack.pos
+  else:
+    var pos {.volatile.}: pointer
+    pos = addr(pos)
 
-when defined(nimCoroutines):
-  proc stackSize(stack: ptr GcStack): int {.noinline.} =
-    if stack.pos != nil:
-      when defined(stackIncreases):
-        result = cast[ByteAddress](stack.pos) -% cast[ByteAddress](stack.bottom)
-      else:
-        result = cast[ByteAddress](stack.bottom) -% cast[ByteAddress](stack.pos)
+  if pos != nil:
+    when defined(stackIncreases):
+      result = cast[ByteAddress](pos) -% cast[ByteAddress](stack.bottom)
     else:
-      result = 0
+      result = cast[ByteAddress](stack.bottom) -% cast[ByteAddress](pos)
+  else:
+    result = 0
+
+proc stackSize(): int {.noinline.} =
+  for stack in gch.stack.items():
+    result = result + stack.stackSize()
 
+when defined(nimCoroutines):
   proc setPosition(stack: ptr GcStack, position: pointer) =
     stack.pos = position
     stack.maxStackSize = max(stack.maxStackSize, stack.stackSize())
@@ -84,19 +135,18 @@ when defined(nimCoroutines):
   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
-    result = abs(cast[int](addr(stackTop)) - cast[int](gch.stackBottom))
+  proc getActiveStack(gch: var GcHeap): ptr GcStack =
+    return gch.activeStack
 
-iterator items(stack: ptr GcStack): ptr GcStack =
-  var s = stack
-  while not isNil(s):
-    yield s
-    s = s.next
+  proc isActiveStack(stack: ptr GcStack): bool =
+    return gch.activeStack == stack
+else:
+  # Stack positions do not need to be tracked if coroutines are not used.
+  proc setPosition(stack: ptr GcStack, position: pointer) = discard
+  proc setPosition(stack: var GcStack, position: pointer) = discard
+  # There is just one stack - main stack of the thread. It is active always.
+  proc getActiveStack(gch: var GcHeap): ptr GcStack = addr(gch.stack)
+  proc isActiveStack(stack: ptr GcStack): bool = true
 
 when declared(threadType):
   proc setupForeignThreadGc*() {.gcsafe.} =
@@ -167,9 +217,9 @@ when defined(nimCoroutines):
     gch.activeStack.setPosition(addr(sp))
 
 when not defined(useNimRtl):
-  when defined(nimCoroutines):
-    proc setStackBottom(theStackBottom: pointer) =
-      # Initializes main stack of the thread.
+  proc setStackBottom(theStackBottom: pointer) =
+    # Initializes main stack of the thread.
+    when defined(nimCoroutines):
       if gch.stack.next == nil:
         # Main stack was not initialized yet
         gch.stack.next = addr(gch.stack)
@@ -177,43 +227,39 @@ when not defined(useNimRtl):
         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) =
+    if gch.stack.bottom == nil:
+      # This branch will not be called when -d:nimCoroutines - it is fine,
+      # because same thing is done just above.
       #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
+      gch.stack.bottom = theStackBottom
+    elif theStackBottom != gch.stack.bottom:
+      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.stack.bottom,theStackBottom)
+      when stackIncreases:
+        gch.stack.bottom = cast[pointer](min(a, b))
       else:
-        var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2
-        var b = cast[ByteAddress](gch.stackBottom)
-        #c_fprintf(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))
+        gch.stack.bottom = cast[pointer](max(a, b))
+
+    gch.stack.setPosition(theStackBottom)
 {.pop.}
 
+proc isOnStack(p: pointer): bool =
+  var stackTop {.volatile.}: pointer
+  stackTop = addr(stackTop)
+  var a = cast[ByteAddress](gch.getActiveStack().bottom)
+  var b = cast[ByteAddress](stackTop)
+  when not stackIncreases:
+    swap(a, b)
+  var x = cast[ByteAddress](p)
+  result = a <=% x and x <=% b
+
 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[ByteAddress](gch.stackBottom)
-    var a = cast[ByteAddress](stackTop)
-    var x = cast[ByteAddress](p)
-    result = a <=% x and x <=% b
-
   template forEachStackSlot(gch, gcMark: untyped) {.dirty.} =
     when defined(sparcv9):
       asm  """"flushw \n" """
@@ -221,7 +267,7 @@ when defined(sparc): # For SPARC architecture.
       asm  """"ta      0x3   ! ST_FLUSH_WINDOWS\n" """
 
     var
-      max = gch.stackBottom
+      max = gch.stack.bottom
       sp: PPointer
       stackTop: array[0..1, pointer]
     sp = addr(stackTop[0])
@@ -237,16 +283,6 @@ 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[ByteAddress](gch.stackBottom)
-    var b = cast[ByteAddress](stackTop)
-    var x = cast[ByteAddress](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
@@ -254,91 +290,42 @@ elif stackIncreases:
 
   template forEachStackSlot(gch, gcMark: untyped) {.dirty.} =
     var registers {.noinit.}: C_JmpBuf
+    # sp will traverse the JMP_BUF as well (jmp_buf size is added,
+    # otherwise sp would be below the registers structure).
+    var regAddr = addr(registers) +% jmpbufSize
+
     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)) +% 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)
+      for stack in gch.stack.items():
+        var max = cast[ByteAddress](gch.stack.bottom)
+        var sp = cast[ByteAddress](addr(registers)) -% sizeof(pointer)
+        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)
-      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.
-      # 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
-      # 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
-      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: untyped) {.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)
+  template forEachStackSlot(gch, gcMark: untyped) {.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
+    # Update position of stack gc is executing in.
+    gch.getActiveStack().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):
-          # 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)
+          if stack.isActiveStack():
+            # 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:
diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim
index f927575dd..5896af88e 100644
--- a/lib/system/gc_ms.nim
+++ b/lib/system/gc_ms.nim
@@ -10,9 +10,6 @@
 # A simple mark&sweep garbage collector for Nim. Define the
 # symbol ``gcUseBitvectors`` to generate a variant of this GC.
 
-when defined(nimCoroutines):
-  import arch
-
 {.push profiler:off.}
 
 const
@@ -51,17 +48,22 @@ type
     maxStackSize: int        # max stack size
     freedObjects: int        # max entries in cycle table
 
-  GcStack {.final.} = object
-    prev: ptr GcStack
-    next: ptr GcStack
-    starts: pointer
-    pos: pointer
-    maxStackSize: int
+  GcStack {.final, pure.} = object
+    when nimCoroutines:
+      prev: ptr GcStack
+      next: ptr GcStack
+      maxStackSize: int      # Used to track statistics because we can not use
+                             # GcStat.maxStackSize when multiple stacks exist.
+    bottom: pointer
+
+    when nimCoroutines:
+      pos: pointer
 
   GcHeap = object            # this contains the zero count and
                              # non-zero count table
-    stack: ptr GcStack
-    stackBottom: pointer
+    stack: GcStack
+    when nimCoroutines:
+      activeStack: ptr GcStack    # current executing coroutine stack.
     cycleThreshold: int
     when useCellIds:
       idGenerator: int
@@ -423,7 +425,7 @@ proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
   forEachStackSlot(gch, gcMark)
 
 proc collectCTBody(gch: var GcHeap) =
-  when not defined(nimCoroutines):
+  when not nimCoroutines:
     gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
   prepareForInteriorPointerChecking(gch.region)
   markStackAndRegisters(gch)
@@ -479,10 +481,10 @@ when not defined(useNimRtl):
              "[GC] collections: " & $gch.stat.collections & "\n" &
              "[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
              "[GC] freed objects: " & $gch.stat.freedObjects & "\n"
-    when defined(nimCoroutines):
+    when nimCoroutines:
       result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
       for stack in items(gch.stack):
-        result = result & "[GC]   stack " & stack.starts.repr & "[GC]     max stack size " & $stack.maxStackSize & "\n"
+        result = result & "[GC]   stack " & stack.bottom.repr & "[GC]     max stack size " & $stack.maxStackSize & "\n"
     else:
       result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
     GC_enable()