diff --git a/src/backend/vm.nim b/src/backend/vm.nim index 13f03fb..5dfbb3c 100644 --- a/src/backend/vm.nim +++ b/src/backend/vm.nim @@ -25,7 +25,7 @@ import ../frontend/meta/bytecode import ../util/multibyte -when debugVM or debugMem: +when debugVM or debugMem or debugGC: import std/strformat import std/terminal @@ -97,7 +97,6 @@ proc collect*(self: PeonGC) proc reallocate*(self: PeonGC, p: pointer, oldSize: int, newSize: int): pointer = ## Simple wrapper around realloc/dealloc - self.bytesAllocated.total += newSize - oldSize self.bytesAllocated.current += newSize - oldSize if self.bytesAllocated.current > self.nextGC: self.collect() @@ -110,6 +109,7 @@ proc reallocate*(self: PeonGC, p: pointer, oldSize: int, newSize: int): pointer echo "DEBUG - Memory manager: Deallocating 1 byte of memory" dealloc(p) elif (oldSize > 0 and not p.isNil() and newSize > oldSize) or oldSize == 0: + self.bytesAllocated.total += newSize - oldSize when debugStressGC: self.collect() when debugMem: @@ -127,7 +127,6 @@ proc reallocate*(self: PeonGC, p: pointer, oldSize: int, newSize: int): pointer elif oldSize > 0 and p.isNil(): echo &"DEBUG - Memory manager: Warning, asked to realloc() nil pointer from {oldSize} to {newSize} bytes, ignoring request" except NilAccessDefect: - raise stderr.write("Peon: could not manage memory, segmentation fault\n") quit(139) # For now, there's not much we can do if we can't get the memory we need, so we exit @@ -173,33 +172,32 @@ proc mark(self: ptr HeapObject): bool = return true -proc mark(self: PeonGC): seq[ptr HeapObject] = - ## Marks objects *not* to be +proc markRoots(self: PeonGC): seq[ptr HeapObject] = + ## Marks root objects *not* to be ## collected by the GC and returns - ## them + ## their addresses # Unlike what bob does in his book, # we keep track of objects in a different # way due to how the whole thing is designed. # Specifically, we don't have neat structs for - # all peon objects - # When we allocate() an object, we keep track - # of the box it created along with its type and - # other metadata, as well as the address of said - # box. Then, we can go through the various sources - # of roots in the VM, see if they match any pointers - # we already know about (using a hash set so it's - # really fast), and then we can be sure that anything - # that's in the difference (i.e. mathematical set difference) - # between our full list of pointers and the live ones - # is not a root object, so if it's not indirectly reachable - # through a root itself, it can be freed. I'm not sure if I - # can call this GC strategy precise, since technically there - # is a chance for a regular value to collide with one of the - # pointers we allocated and that would cause a memory leak, - # but with a 64-bit address-space it probably hardly matters, - # so I guess this is a mostly-precise Mark&Sweep collector - var live: HashSet[uint64] = initHashSet[uint64]() + # all peon objects: When we allocate() an object, + # we keep track of the small wrapper it created + # along with its type and other metadata. Then, + # we can go through the various sources of roots + # in the VM, see if they match any pointers we + # already know about (we store them a hash set so + # it's really fast), and then we can be sure that + # anything that's in the difference (i.e. mathematical + # set difference) between our full list of pointers + # and the live ones is not a root object, so if it's not + # indirectly reachable through a root itself, it can be + # freed. I'm not sure if I can call this GC strategy precise, + # since technically there is a chance for a regular value to + # collide with one of the pointers we allocated and that would + # cause a memory leak, but with a 64-bit address-space it probably + # hardly matters, so I guess this is a mostly-precise Mark&Sweep collector + var live = initHashSet[uint64]() for obj in self.vm.calls: if obj in self.pointers: live.incl(obj) @@ -217,8 +215,8 @@ proc mark(self: PeonGC): seq[ptr HeapObject] = if obj.mark(): result.add(obj) when debugMem: - if result.len() > 0: - echo &"DEBUG - GC: Marking object: {result[^1][]}" + if obj.marked: + echo &"DEBUG - GC: Marking object: {obj[]}" proc trace(self: PeonGC, roots: seq[ptr HeapObject]) = @@ -282,7 +280,7 @@ proc collect(self: PeonGC) = let before = self.bytesAllocated.current when debugGC: echo "DEBUG - GC: Starting collection cycle" - self.trace(self.mark()) + self.trace(self.markRoots()) self.sweep() self.nextGC = self.bytesAllocated.current * HeapGrowFactor when debugGC: diff --git a/src/main.nim b/src/main.nim index 38c6b70..02042b6 100644 --- a/src/main.nim +++ b/src/main.nim @@ -14,12 +14,23 @@ ## Peon's main executable +# Our stuff +import frontend/lexer as l +import frontend/parser as p +import frontend/compiler as c +import backend/vm as v +import util/serializer as s +import util/debugger +import util/symbols +import config + # Builtins & external libs import std/strformat import std/strutils import std/terminal import std/parseopt -import std/times +when debugSerializer: + import std/times import std/os # Thanks art <3 @@ -30,15 +41,6 @@ import jale/plugin/editor_history import jale/keycodes import jale/multiline -# Our stuff -import frontend/lexer as l -import frontend/parser as p -import frontend/compiler as c -import backend/vm as v -import util/serializer as s -import util/debugger -import util/symbols -import config # Forward declarations proc getLineEditor: LineEditor