BROKEN gc
This commit is contained in:
parent
5963a25f37
commit
0f9440dfb0
|
@ -16,7 +16,7 @@ requires "nim >= 1.6.2"
|
||||||
|
|
||||||
task test, "run tests":
|
task test, "run tests":
|
||||||
exec "nim c --gc:arc -d:release --skipProjCfg --skipParentCfg --out:bin/nds src/nds.nim"
|
exec "nim c --gc:arc -d:release --skipProjCfg --skipParentCfg --out:bin/nds src/nds.nim"
|
||||||
exec "nim c --gc:arc -d:debug --skipProjCfg --skipParentCfg -r tests/test.nim"
|
exec "nim c --gc:arc -d:release --skipProjCfg --skipParentCfg -r tests/test.nim"
|
||||||
exec "rm tests/test"
|
exec "rm tests/test"
|
||||||
|
|
||||||
task debug, "build nds for debugging":
|
task debug, "build nds for debugging":
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import ../scanner
|
import ../scanner
|
||||||
import ../chunk
|
import ../chunk
|
||||||
import ../config
|
import ../config
|
||||||
|
import ../types/ndobject
|
||||||
|
import ../memory
|
||||||
|
|
||||||
import types
|
import types
|
||||||
import utils
|
import utils
|
||||||
|
@ -13,6 +15,8 @@ import functions
|
||||||
import statement
|
import statement
|
||||||
|
|
||||||
proc compile*(comp: Compiler) =
|
proc compile*(comp: Compiler) =
|
||||||
|
let enableGCOld = enableGC
|
||||||
|
enableGC = false
|
||||||
comp.scanner = newScanner(comp.source)
|
comp.scanner = newScanner(comp.source)
|
||||||
comp.writeChunk(0, opNil)
|
comp.writeChunk(0, opNil)
|
||||||
# the starting stackIndex is 0, which points to this nil
|
# the starting stackIndex is 0, which points to this nil
|
||||||
|
@ -25,3 +29,4 @@ proc compile*(comp: Compiler) =
|
||||||
when debugDumpChunk:
|
when debugDumpChunk:
|
||||||
if not comp.hadError:
|
if not comp.hadError:
|
||||||
comp.chunk.disassembleChunk()
|
comp.chunk.disassembleChunk()
|
||||||
|
enableGC = enableGCOld
|
||||||
|
|
|
@ -14,6 +14,8 @@ const assertionsVM* = defined(debug) or defined(release) # sanity checks in the
|
||||||
const boundsChecks* = defined(debug) or defined(release)
|
const boundsChecks* = defined(debug) or defined(release)
|
||||||
const profileInstructions* = defined(ndsprofile) # if true, the time spent on every opcode is measured
|
const profileInstructions* = defined(ndsprofile) # if true, the time spent on every opcode is measured
|
||||||
const debugClosures* = defined(debug) # specific closure debug switches
|
const debugClosures* = defined(debug) # specific closure debug switches
|
||||||
|
const debugGC* = defined(debug) # debug GC
|
||||||
|
const stressGC* = true # garbage collect on every allocation
|
||||||
|
|
||||||
# choose a line editor for the repl
|
# choose a line editor for the repl
|
||||||
const lineEditor = leRdstdin
|
const lineEditor = leRdstdin
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
# marking for mark'n'sweep
|
||||||
|
|
||||||
|
import types/ndstack
|
||||||
|
import types/ndobject
|
||||||
|
import types/value
|
||||||
|
import types/ndstring
|
||||||
|
import types/hashtable
|
||||||
|
import types/closure
|
||||||
|
import types/ndlist
|
||||||
|
|
||||||
|
import memory
|
||||||
|
import config
|
||||||
|
import strformat
|
||||||
|
import strutils
|
||||||
|
import pointerutils
|
||||||
|
|
||||||
|
# globals
|
||||||
|
var
|
||||||
|
stack*: Stack[NdValue] = newStack[NdValue](256)
|
||||||
|
globals*: Table[NdValue, NdValue]
|
||||||
|
openUpvalues*: Upvalue[NdValue] = nil
|
||||||
|
|
||||||
|
var grayObjects: seq[ptr NdObject]
|
||||||
|
|
||||||
|
proc markObject*(obj: ptr NdObject) =
|
||||||
|
if obj.isMarked:
|
||||||
|
return
|
||||||
|
|
||||||
|
when debugGC:
|
||||||
|
let niceAddr = cast[uint](obj).toHex()
|
||||||
|
echo &"Marking object {niceAddr}."
|
||||||
|
|
||||||
|
obj.isMarked = true
|
||||||
|
grayObjects.add(obj)
|
||||||
|
|
||||||
|
proc markValue*(val: NdValue) =
|
||||||
|
if not val.isObject():
|
||||||
|
return
|
||||||
|
let obj: ptr NdObject = val.asObject()
|
||||||
|
obj.markObject()
|
||||||
|
|
||||||
|
proc markRoots =
|
||||||
|
var slot = 0
|
||||||
|
var stacklen = stack.len()
|
||||||
|
while slot < stacklen:
|
||||||
|
stack.getIndex(slot).markValue()
|
||||||
|
slot.inc
|
||||||
|
cast[NdTable[NdValue, NdValue]](globals.addr).markObject()
|
||||||
|
|
||||||
|
var upvalue = openUpvalues
|
||||||
|
while upvalue != nil:
|
||||||
|
upvalue.markObject()
|
||||||
|
upvalue = upvalue.next
|
||||||
|
|
||||||
|
proc blackenObject(obj: ptr NdObject) =
|
||||||
|
when debugGC:
|
||||||
|
let niceAddr = cast[uint](obj).toHex()
|
||||||
|
echo &"blackening {niceAddr}."
|
||||||
|
case obj.kind:
|
||||||
|
of noInvalid:
|
||||||
|
raise newException(Defect, "Somewhere an object with invalid object type was created.")
|
||||||
|
of noString:
|
||||||
|
discard
|
||||||
|
of noClosure:
|
||||||
|
let clos = cast[Closure[NdValue]](obj)
|
||||||
|
let count = clos.upvalueCount
|
||||||
|
var i = 0
|
||||||
|
while i < count:
|
||||||
|
let upval = clos.get(i)
|
||||||
|
if upval != nil:
|
||||||
|
markObject(upval)
|
||||||
|
i.inc
|
||||||
|
of noUpvalue:
|
||||||
|
let upval = cast[Upvalue[NdValue]](obj)
|
||||||
|
markValue(upval.location[])
|
||||||
|
of noList:
|
||||||
|
let list = cast[List[NdValue]](obj)
|
||||||
|
let count = list.getLength()
|
||||||
|
var i = 0
|
||||||
|
while i < count:
|
||||||
|
let elem = list.getIndex(i)
|
||||||
|
markValue(elem)
|
||||||
|
i.inc
|
||||||
|
of noTable:
|
||||||
|
let tbl = cast[NdTable[NdValue, NdValue]](obj)
|
||||||
|
for elem in tbl[].iterate():
|
||||||
|
markValue(elem)
|
||||||
|
|
||||||
|
proc traceReferences =
|
||||||
|
while grayObjects.len() > 0:
|
||||||
|
let obj = grayObjects.pop()
|
||||||
|
obj.blackenObject()
|
||||||
|
|
||||||
|
proc freeObj(obj: ptr NdObject) =
|
||||||
|
case obj.kind:
|
||||||
|
of noInvalid:
|
||||||
|
raise newException(Defect, "Somewhere an object with invalid object type was created.")
|
||||||
|
of noString:
|
||||||
|
let str = cast[NdString](obj)
|
||||||
|
str.free()
|
||||||
|
of noTable:
|
||||||
|
let tbl = cast[NdTable[NdValue, NdValue]](obj)
|
||||||
|
tbl.free()
|
||||||
|
of noClosure:
|
||||||
|
let clos = cast[Closure[NdValue]](obj)
|
||||||
|
clos.free()
|
||||||
|
of noList:
|
||||||
|
let list = cast[List[NdValue]](obj)
|
||||||
|
list.free()
|
||||||
|
of noUpvalue:
|
||||||
|
let upval = cast[Upvalue[NdValue]](obj)
|
||||||
|
upval.free()
|
||||||
|
|
||||||
|
proc sweep =
|
||||||
|
var previous: ptr NdObject = nil
|
||||||
|
var obj = objects
|
||||||
|
while obj != nil:
|
||||||
|
if obj.isMarked:
|
||||||
|
obj.isMarked = false
|
||||||
|
previous = obj
|
||||||
|
obj = obj.nextObj
|
||||||
|
else:
|
||||||
|
let unreached = obj
|
||||||
|
obj = obj.nextObj
|
||||||
|
if previous != nil:
|
||||||
|
previous.nextObj = obj
|
||||||
|
else:
|
||||||
|
objects = obj
|
||||||
|
|
||||||
|
unreached.freeObj()
|
||||||
|
|
||||||
|
proc icollectGarbage =
|
||||||
|
if not enableGC:
|
||||||
|
return
|
||||||
|
when debugGC:
|
||||||
|
echo "-- gc begin"
|
||||||
|
|
||||||
|
markRoots()
|
||||||
|
traceReferences()
|
||||||
|
tableRemoveWhite() # defined in types/ndstring
|
||||||
|
sweep()
|
||||||
|
|
||||||
|
when debugGC:
|
||||||
|
echo "-- gc end"
|
||||||
|
|
||||||
|
collectGarbage = icollectGarbage
|
|
@ -0,0 +1,41 @@
|
||||||
|
import config
|
||||||
|
import strutils
|
||||||
|
import strformat
|
||||||
|
|
||||||
|
var collectGarbage*: proc: void {.closure.}
|
||||||
|
|
||||||
|
var enableGC* = false
|
||||||
|
# set by the compiler/VM as needed
|
||||||
|
|
||||||
|
proc ndAlloc*(size: int): pointer =
|
||||||
|
when debugGC:
|
||||||
|
echo &"Allocating {$size} bytes."
|
||||||
|
when stressGC:
|
||||||
|
if enableGC:
|
||||||
|
collectGarbage()
|
||||||
|
alloc(size)
|
||||||
|
|
||||||
|
proc ndAlloc0*(size: int): pointer =
|
||||||
|
when debugGC:
|
||||||
|
echo &"Allocating and zeroing {$size} bytes."
|
||||||
|
when stressGC:
|
||||||
|
if enableGC:
|
||||||
|
collectGarbage()
|
||||||
|
alloc0(size)
|
||||||
|
|
||||||
|
proc ndRealloc*[T](source: ptr T, oldsize: int, newsize: int): ptr T =
|
||||||
|
when debugGC:
|
||||||
|
let niceAddr = cast[uint](source).toHex()
|
||||||
|
echo &"Reallocating {niceAddr} from size {$oldsize} to {$newsize}."
|
||||||
|
if newsize > oldsize:
|
||||||
|
when stressGC:
|
||||||
|
if enableGC:
|
||||||
|
collectGarbage()
|
||||||
|
discard
|
||||||
|
cast[ptr T](realloc(source, newsize))
|
||||||
|
|
||||||
|
proc ndDealloc*[T](mem: ptr T) =
|
||||||
|
when debugGC:
|
||||||
|
let niceAddr = cast[uint](mem).toHex()
|
||||||
|
echo &"Freeing {niceAddr}"
|
||||||
|
dealloc(mem)
|
|
@ -1,23 +1,33 @@
|
||||||
import strformat
|
import strformat
|
||||||
import strutils
|
import strutils
|
||||||
|
import ndobject
|
||||||
|
import ../memory
|
||||||
|
|
||||||
type
|
type
|
||||||
UpvalueObj[T] = object
|
UpvalueObj[T] = object of NdObject
|
||||||
location*: ptr T
|
location*: ptr T
|
||||||
next*: Upvalue[T]
|
next*: Upvalue[T]
|
||||||
closed*: T
|
closed*: T
|
||||||
|
|
||||||
Upvalue*[T] = ptr UpvalueObj[T]
|
Upvalue*[T] = ptr UpvalueObj[T]
|
||||||
|
|
||||||
ClosureObj[T] = object
|
ClosureObj[T] = object of NdObject
|
||||||
start: ptr uint8
|
start: ptr uint8
|
||||||
upvalueCount*: int
|
upvalueCount*: int
|
||||||
upvalues: UncheckedArray[Upvalue[T]]
|
upvalues: UncheckedArray[Upvalue[T]]
|
||||||
|
|
||||||
Closure*[T] = ptr ClosureObj[T]
|
Closure*[T] = ptr ClosureObj[T]
|
||||||
|
|
||||||
|
proc free*[T](clos: Closure[T]) =
|
||||||
|
ndDealloc(clos)
|
||||||
|
|
||||||
|
proc free*[T](upval: Upvalue[T]) =
|
||||||
|
ndDealloc(upval)
|
||||||
|
|
||||||
proc newClosure*[T](start: ptr uint8, upvalueCount: int): Closure[T] =
|
proc newClosure*[T](start: ptr uint8, upvalueCount: int): Closure[T] =
|
||||||
result = cast[Closure[T]](alloc0(8 * upvalueCount + sizeof(ClosureObj[T])))
|
result = cast[Closure[T]](ndAlloc0(8 * upvalueCount + sizeof(ClosureObj[T])))
|
||||||
|
result.kind = noClosure
|
||||||
|
result.newObject()
|
||||||
result.start = start
|
result.start = start
|
||||||
result.upvalueCount = upvalueCount
|
result.upvalueCount = upvalueCount
|
||||||
for i in 0 .. upvalueCount:
|
for i in 0 .. upvalueCount:
|
||||||
|
@ -47,7 +57,9 @@ proc debugStr*[T](clos: Closure[T]): string =
|
||||||
result &= ")"
|
result &= ")"
|
||||||
|
|
||||||
proc newUpvalue*[T](location: ptr T): Upvalue[T] =
|
proc newUpvalue*[T](location: ptr T): Upvalue[T] =
|
||||||
result = cast[Upvalue[T]](alloc0(sizeof(UpvalueObj[T])))
|
result = cast[Upvalue[T]](ndAlloc0(sizeof(UpvalueObj[T])))
|
||||||
|
result.kind = noUpvalue
|
||||||
|
result.newObject()
|
||||||
result.location = location
|
result.location = location
|
||||||
result.next = nil
|
result.next = nil
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# The hash table implementation for string interning and globals
|
# The hash table implementation for string interning and globals
|
||||||
|
|
||||||
import bitops
|
import bitops
|
||||||
|
import ndobject
|
||||||
|
import ../memory
|
||||||
|
|
||||||
const tableMaxLoad = 0.75
|
const tableMaxLoad = 0.75
|
||||||
const tableInitSize = 8
|
const tableInitSize = 8
|
||||||
|
@ -14,7 +16,7 @@ type
|
||||||
key: U
|
key: U
|
||||||
value: V
|
value: V
|
||||||
|
|
||||||
Table*[U, V] = object
|
Table*[U, V] = object of NdObject
|
||||||
count: int
|
count: int
|
||||||
cap: int
|
cap: int
|
||||||
entries: ptr UncheckedArray[Entry[U, V]]
|
entries: ptr UncheckedArray[Entry[U, V]]
|
||||||
|
@ -24,16 +26,24 @@ type
|
||||||
proc newTable*[U, V]: Table[U, V] =
|
proc newTable*[U, V]: Table[U, V] =
|
||||||
result.cap = 0
|
result.cap = 0
|
||||||
result.count = 0
|
result.count = 0
|
||||||
|
result.kind = noTable
|
||||||
|
# not calling result.newObject()
|
||||||
|
|
||||||
proc newNdTable*[U, V]: NdTable[U, V] =
|
proc newNdTable*[U, V]: NdTable[U, V] =
|
||||||
result = cast[NdTable[U, V]](alloc(sizeof(Table[U, V])))
|
result = cast[NdTable[U, V]](ndAlloc(sizeof(Table[U, V])))
|
||||||
|
result[].kind = noTable
|
||||||
|
result.newObject()
|
||||||
result[].cap = 0
|
result[].cap = 0
|
||||||
result[].count = 0
|
result[].count = 0
|
||||||
result[].entries = nil # must be set, because dealloc will be ran on it otherwise
|
result[].entries = nil # must be set, because dealloc will be ran on it otherwise
|
||||||
|
|
||||||
proc free*[U, V](tbl: var Table[U, V]) =
|
proc free*[U, V](tbl: Table[U, V]) =
|
||||||
if tbl.entries != nil:
|
if tbl.entries != nil:
|
||||||
dealloc(tbl.entries)
|
ndDealloc(tbl.entries)
|
||||||
|
|
||||||
|
proc free*[U, V](tbl: NdTable[U, V]) =
|
||||||
|
tbl[].free()
|
||||||
|
ndDealloc(tbl)
|
||||||
|
|
||||||
proc findEntry[U, V](entries: ptr UncheckedArray[Entry[U, V]], cap: int, key: U): ptr Entry[U, V] =
|
proc findEntry[U, V](entries: ptr UncheckedArray[Entry[U, V]], cap: int, key: U): ptr Entry[U, V] =
|
||||||
mixin fnv1a, equal
|
mixin fnv1a, equal
|
||||||
|
@ -60,7 +70,7 @@ proc grow[U, V](tbl: var Table[U, V]): int =
|
||||||
tableInitSize
|
tableInitSize
|
||||||
|
|
||||||
proc adjustCapacity[U, V](tbl: var Table[U, V], newcap: int) =
|
proc adjustCapacity[U, V](tbl: var Table[U, V], newcap: int) =
|
||||||
let entries: ptr UncheckedArray[Entry[U, V]] = cast[ptr UncheckedArray[Entry[U, V]]](alloc0(newcap * sizeof(Entry[U, V])))
|
let entries: ptr UncheckedArray[Entry[U, V]] = cast[ptr UncheckedArray[Entry[U, V]]](ndAlloc0(newcap * sizeof(Entry[U, V])))
|
||||||
tbl.count = 0
|
tbl.count = 0
|
||||||
|
|
||||||
if tbl.entries != nil:
|
if tbl.entries != nil:
|
||||||
|
@ -73,7 +83,7 @@ proc adjustCapacity[U, V](tbl: var Table[U, V], newcap: int) =
|
||||||
dest[].entryStatus = esAlive
|
dest[].entryStatus = esAlive
|
||||||
tbl.count.inc
|
tbl.count.inc
|
||||||
|
|
||||||
dealloc(tbl.entries)
|
ndDealloc(tbl.entries)
|
||||||
|
|
||||||
tbl.entries = entries
|
tbl.entries = entries
|
||||||
tbl.cap = newcap
|
tbl.cap = newcap
|
||||||
|
@ -141,6 +151,14 @@ proc tableDelete*[U, V](tbl: Table[U, V], key: U): bool =
|
||||||
proc getLength*[U, V](tbl: NdTable[U, V]): int =
|
proc getLength*[U, V](tbl: NdTable[U, V]): int =
|
||||||
tbl.count.int
|
tbl.count.int
|
||||||
|
|
||||||
|
iterator iterate*[T](tbl: Table[T, T]): T =
|
||||||
|
if tbl.entries != nil:
|
||||||
|
for i in countup(0, tbl.cap-1):
|
||||||
|
let entry = tbl.entries[i]
|
||||||
|
if entry.entryStatus == esAlive:
|
||||||
|
yield entry.key
|
||||||
|
yield entry.value
|
||||||
|
|
||||||
proc `$`*[U, V](tbl: NdTable[U, V], tagged: V): string =
|
proc `$`*[U, V](tbl: NdTable[U, V], tagged: V): string =
|
||||||
if tbl[].count == 0:
|
if tbl[].count == 0:
|
||||||
return "@{}"
|
return "@{}"
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import strformat
|
import strformat
|
||||||
|
import ndobject
|
||||||
|
import ../memory
|
||||||
|
|
||||||
# configure ndlist here
|
# configure ndlist here
|
||||||
import ../config
|
import ../config
|
||||||
|
@ -11,37 +13,41 @@ const growthFactor = 2
|
||||||
const startCap = 8
|
const startCap = 8
|
||||||
|
|
||||||
type
|
type
|
||||||
ListObj[T] = object
|
ListObj[T] = object of NdObject
|
||||||
len: int
|
len: int
|
||||||
cap: int
|
cap: int
|
||||||
entries: ptr UncheckedArray[T]
|
entries: ptr UncheckedArray[T]
|
||||||
List*[T] = ptr ListObj[T]
|
List*[T] = ptr ListObj[T]
|
||||||
|
|
||||||
proc newList*[T](): List[T] =
|
proc newList*[T](): List[T] =
|
||||||
result = cast[List[T]](alloc(sizeof(ListObj[T])))
|
result = cast[List[T]](ndAlloc(sizeof(ListObj[T])))
|
||||||
result.len = 0
|
result.len = 0
|
||||||
result.cap = 0
|
result.cap = 0
|
||||||
|
result.kind = noList
|
||||||
|
result.newObject()
|
||||||
|
|
||||||
proc newListCopymem*[T](start: ptr T, len: int): List[T] =
|
proc newListCopymem*[T](start: ptr T, len: int): List[T] =
|
||||||
result = newList[T]()
|
result = newList[T]()
|
||||||
result.len = len
|
result.len = len
|
||||||
result.cap = len
|
result.cap = len
|
||||||
result.entries = cast[ptr UncheckedArray[T]](alloc(sizeof(T) * len))
|
result.entries = cast[ptr UncheckedArray[T]](ndAlloc(sizeof(T) * len))
|
||||||
copyMem(result.entries, start, len * sizeof(T))
|
copyMem(result.entries, start, len * sizeof(T))
|
||||||
|
|
||||||
proc free*[T](list: var List[T]) =
|
proc free*[T](list: List[T]) =
|
||||||
## dealloc's the list object
|
## dealloc's the list object
|
||||||
list.dealloc()
|
list.ndDealloc()
|
||||||
|
|
||||||
proc grow[T](list: var List[T]) =
|
proc grow[T](list: var List[T]) =
|
||||||
## growth the list's capacity
|
## growth the list's capacity
|
||||||
let newcap = if list.cap == 0: startCap else: list.cap * growthFactor
|
let oldcap = list.cap
|
||||||
|
let oldsize = oldcap * sizeof(T)
|
||||||
|
let newcap = if oldcap == 0: startCap else: oldcap * growthFactor
|
||||||
let size = newcap * sizeof(T)
|
let size = newcap * sizeof(T)
|
||||||
if list.cap == 0:
|
if oldcap == 0:
|
||||||
list.entries = cast[ptr UncheckedArray[T]](size.alloc())
|
list.entries = cast[ptr UncheckedArray[T]](size.ndAlloc())
|
||||||
list.cap = newcap
|
list.cap = newcap
|
||||||
else:
|
else:
|
||||||
list.entries = cast[ptr UncheckedArray[T]](list.entries.realloc(size))
|
list.entries = cast[ptr UncheckedArray[T]](list.entries.ndRealloc(oldsize, size))
|
||||||
list.cap = newcap
|
list.cap = newcap
|
||||||
|
|
||||||
proc add*[T](list: var List[T], item: T) =
|
proc add*[T](list: var List[T], item: T) =
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# generic object type
|
||||||
|
|
||||||
|
import ../config
|
||||||
|
|
||||||
|
type
|
||||||
|
NdObjectKind* = enum
|
||||||
|
noInvalid, noString, noClosure, noUpvalue, noList, noTable
|
||||||
|
NdObject* = object of RootObj
|
||||||
|
kind*: NdObjectKind
|
||||||
|
isMarked*: bool
|
||||||
|
nextObj*: ptr NdObject
|
||||||
|
|
||||||
|
var objects*: ptr NdObject
|
||||||
|
|
||||||
|
template newObject*(obj: ptr NdObject) =
|
||||||
|
when assertionsVM:
|
||||||
|
if obj.kind == noInvalid:
|
||||||
|
raise newException(Defect, "Somewhere an object with invalid object type was created.")
|
||||||
|
|
||||||
|
obj.nextObj = objects
|
||||||
|
objects = obj
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import ../pointerutils
|
import ../pointerutils
|
||||||
|
|
||||||
|
import ndobject
|
||||||
|
import ../memory
|
||||||
|
|
||||||
# configure stacks here
|
# configure stacks here
|
||||||
import ../config
|
import ../config
|
||||||
#const boundsChecks = defined(debug)
|
#const boundsChecks = defined(debug)
|
||||||
|
@ -15,7 +18,7 @@ type
|
||||||
cap*: int
|
cap*: int
|
||||||
|
|
||||||
proc newStack*[T](startingCap: int): Stack[T] =
|
proc newStack*[T](startingCap: int): Stack[T] =
|
||||||
result.start = cast[ptr T](alloc(startingCap * sizeof(T)))
|
result.start = cast[ptr T](ndAlloc(startingCap * sizeof(T)))
|
||||||
result.top = result.start.psub(sizeof(T))
|
result.top = result.start.psub(sizeof(T))
|
||||||
result.cap = startingCap
|
result.cap = startingCap
|
||||||
|
|
||||||
|
@ -23,14 +26,16 @@ proc free*[T](stack: var Stack[T]) =
|
||||||
## dealloc's the stack object
|
## dealloc's the stack object
|
||||||
## if the stack contains pointers, those should be freed before destroying the stack
|
## if the stack contains pointers, those should be freed before destroying the stack
|
||||||
stack.cap = 0
|
stack.cap = 0
|
||||||
stack.start.dealloc()
|
stack.start.ndDealloc()
|
||||||
stack.start = nil
|
stack.start = nil
|
||||||
stack.top = nil
|
stack.top = nil
|
||||||
|
|
||||||
proc grow[T](stack: var Stack[T], len: int) =
|
proc grow[T](stack: var Stack[T], len: int) =
|
||||||
## growth the stack's capacity and increments the top's index by one
|
## growth the stack's capacity and increments the top's index by one
|
||||||
stack.cap *= growthFactor
|
let oldcap = stack.cap
|
||||||
stack.start = cast[ptr T](realloc(stack.start, stack.cap * sizeof(T)))
|
let newcap = stack.cap * growthFactor
|
||||||
|
stack.cap = newcap
|
||||||
|
stack.start = cast[ptr T](ndRealloc(stack.start, oldcap * sizeof(T), newcap * sizeof(T)))
|
||||||
stack.top = stack.start.padd(len * sizeof(T))
|
stack.top = stack.start.padd(len * sizeof(T))
|
||||||
|
|
||||||
proc shrink[T](stack: var Stack[T]) =
|
proc shrink[T](stack: var Stack[T]) =
|
|
@ -1,13 +1,16 @@
|
||||||
import hashtable
|
import hashtable
|
||||||
|
import ndobject
|
||||||
|
import ../memory
|
||||||
|
|
||||||
type
|
type
|
||||||
NdString* = ptr object
|
NdStringObj* = object of NdObject
|
||||||
len*: uint32
|
len*: uint32
|
||||||
hash*: uint32
|
hash*: uint32
|
||||||
chars*: UncheckedArray[char]
|
chars*: UncheckedArray[char]
|
||||||
|
NdString* = ptr NdStringObj
|
||||||
|
|
||||||
proc free*(ndStr: var NdString) =
|
proc free*(ndStr: NdString) =
|
||||||
dealloc(ndStr)
|
ndDealloc(ndStr)
|
||||||
|
|
||||||
# hashes
|
# hashes
|
||||||
|
|
||||||
|
@ -32,7 +35,6 @@ proc fnv1a*(str: char): int = # SHOULD RETURN THE SAME AS A 1 LENGTH STRING
|
||||||
hash *= 16777619
|
hash *= 16777619
|
||||||
return hash.int
|
return hash.int
|
||||||
|
|
||||||
|
|
||||||
# equals
|
# equals
|
||||||
|
|
||||||
proc equal*(left, right: NdString): bool =
|
proc equal*(left, right: NdString): bool =
|
||||||
|
@ -40,6 +42,13 @@ proc equal*(left, right: NdString): bool =
|
||||||
|
|
||||||
var ndStrings = newTable[NdString, NdString]()
|
var ndStrings = newTable[NdString, NdString]()
|
||||||
|
|
||||||
|
proc tableRemoveWhite* =
|
||||||
|
for elem in ndStrings.iterate():
|
||||||
|
if elem != nil:
|
||||||
|
# vals are nil, keys are not
|
||||||
|
if not elem.isMarked:
|
||||||
|
discard tableDelete(ndStrings, elem)
|
||||||
|
|
||||||
proc newString*(str: string): NdString =
|
proc newString*(str: string): NdString =
|
||||||
let strlen = str.len()
|
let strlen = str.len()
|
||||||
let hash = str.fnv1a()
|
let hash = str.fnv1a()
|
||||||
|
@ -53,10 +62,12 @@ proc newString*(str: string): NdString =
|
||||||
if interned != nil:
|
if interned != nil:
|
||||||
return interned
|
return interned
|
||||||
|
|
||||||
let len = 8 + strlen
|
let len = sizeof(NdStringObj) + strlen
|
||||||
result = cast[NdString](alloc(len))
|
result = cast[NdString](ndAlloc(len))
|
||||||
result.len = strlen.uint32
|
result.len = strlen.uint32
|
||||||
result.hash = hash.uint32
|
result.hash = hash.uint32
|
||||||
|
result.kind = noString
|
||||||
|
result.newObject()
|
||||||
if strlen > 0:
|
if strlen > 0:
|
||||||
copyMem(result.chars[0].unsafeAddr, str[0].unsafeAddr, strlen)
|
copyMem(result.chars[0].unsafeAddr, str[0].unsafeAddr, strlen)
|
||||||
|
|
||||||
|
@ -69,15 +80,16 @@ proc newString*(str: char): NdString =
|
||||||
if interned != nil:
|
if interned != nil:
|
||||||
return interned
|
return interned
|
||||||
|
|
||||||
let len = 8 + 1
|
let len = sizeof(NdStringObj) + 1
|
||||||
result = cast[NdString](alloc(len))
|
result = cast[NdString](ndAlloc(len))
|
||||||
result.len = 1.uint32
|
result.len = 1.uint32
|
||||||
result.hash = hash.uint32
|
result.hash = hash.uint32
|
||||||
result.chars[0] = str
|
result.chars[0] = str
|
||||||
|
result.kind = noString
|
||||||
|
result.newObject()
|
||||||
|
|
||||||
discard ndStrings.tableSet(result, nil)
|
discard ndStrings.tableSet(result, nil)
|
||||||
|
|
||||||
|
|
||||||
proc resetInternedStrings* =
|
proc resetInternedStrings* =
|
||||||
ndStrings.free()
|
ndStrings.free()
|
||||||
ndStrings = newTable[NdString, NdString]()
|
ndStrings = newTable[NdString, NdString]()
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import strformat
|
import strformat
|
||||||
import strutils
|
import strutils
|
||||||
import bitops
|
import bitops
|
||||||
|
export bitops
|
||||||
|
|
||||||
|
import ../config
|
||||||
|
|
||||||
|
import ndobject
|
||||||
|
export ndobject
|
||||||
import ndstring
|
import ndstring
|
||||||
import ndlist
|
import ndlist
|
||||||
import hashtable
|
import hashtable
|
||||||
|
@ -21,12 +26,12 @@ type
|
||||||
# bits 49-48 determine type:
|
# bits 49-48 determine type:
|
||||||
# if bit 63 is 0:
|
# if bit 63 is 0:
|
||||||
# 00 -> nil or bool (singletons)
|
# 00 -> nil or bool (singletons)
|
||||||
# 01 -> string
|
# 01 -> object (string, closure, list, table, upvalue) (formerly string)
|
||||||
# 10 -> funct
|
# 10 -> funct
|
||||||
# 11 -> closure
|
# 11 -> (formerly closure) free
|
||||||
# if bit 63 is 1:
|
# if bit 63 is 1:
|
||||||
# 00 -> list
|
# 00 -> (formerly list) free
|
||||||
# 01 -> table
|
# 01 -> (formerly table) free
|
||||||
# 10 -> native funct
|
# 10 -> native funct
|
||||||
# 11 -> unused for now
|
# 11 -> unused for now
|
||||||
|
|
||||||
|
@ -37,13 +42,14 @@ const ndTrue* = 0x7ffc000000000002'u
|
||||||
const ndFalse* = 0x7ffc000000000003'u
|
const ndFalse* = 0x7ffc000000000003'u
|
||||||
|
|
||||||
# 0111 1111 1111 11*01* 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
|
# 0111 1111 1111 11*01* 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
|
||||||
const tagString* = 0x7ffd000000000000'u
|
#const tagString* = 0x7ffd000000000000'u
|
||||||
|
const tagObject* = 0x7ffd000000000000'u
|
||||||
# 0111 1111 1111 11*10* 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
|
# 0111 1111 1111 11*10* 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
|
||||||
const tagFunct* = 0x7ffe000000000000'u
|
const tagFunct* = 0x7ffe000000000000'u
|
||||||
# you can imagine these now...
|
# you can imagine these now...
|
||||||
const tagClosure* = 0x7fff000000000000'u
|
#const tagClosure* = 0x7fff000000000000'u
|
||||||
const tagList* = 0xfffc000000000000'u
|
#const tagList* = 0xfffc000000000000'u
|
||||||
const tagTable* = 0xfffd000000000000'u
|
#const tagTable* = 0xfffd000000000000'u
|
||||||
const tagNative* = 0xfffe000000000000'u
|
const tagNative* = 0xfffe000000000000'u
|
||||||
|
|
||||||
const mask48* = 0xffff000000000000'u
|
const mask48* = 0xffff000000000000'u
|
||||||
|
@ -59,20 +65,26 @@ template isBool*(val: NdValue): bool =
|
||||||
template isFloat*(val: NdValue): bool =
|
template isFloat*(val: NdValue): bool =
|
||||||
val.bitand(qNan) != qNan
|
val.bitand(qNan) != qNan
|
||||||
|
|
||||||
|
template asObject*(val: NdValue): ptr NdObject =
|
||||||
|
cast[ptr NdObject](val.bitand(mask48.bitnot))
|
||||||
|
|
||||||
|
template isObject*(val: NdValue): bool =
|
||||||
|
val.bitand(mask48) == tagObject
|
||||||
|
|
||||||
template isString*(val: NdValue): bool =
|
template isString*(val: NdValue): bool =
|
||||||
val.bitand(mask48) == tagString
|
val.isObject() and val.asObject().kind == noString
|
||||||
|
|
||||||
template isFunct*(val: NdValue): bool =
|
template isFunct*(val: NdValue): bool =
|
||||||
val.bitand(mask48) == tagFunct
|
val.bitand(mask48) == tagFunct
|
||||||
|
|
||||||
template isClosure*(val: NdValue): bool =
|
template isClosure*(val: NdValue): bool =
|
||||||
val.bitand(mask48) == tagClosure
|
val.isObject() and val.asObject().kind == noClosure
|
||||||
|
|
||||||
template isList*(val: NdValue): bool =
|
template isList*(val: NdValue): bool =
|
||||||
val.bitand(mask48) == tagList
|
val.isObject() and val.asObject().kind == noList
|
||||||
|
|
||||||
template isTable*(val: NdValue): bool =
|
template isTable*(val: NdValue): bool =
|
||||||
val.bitand(mask48) == tagTable
|
val.isObject() and val.asObject().kind == noTable
|
||||||
|
|
||||||
template isNative*(val: NdValue): bool =
|
template isNative*(val: NdValue): bool =
|
||||||
val.bitand(mask48) == tagNative
|
val.bitand(mask48) == tagNative
|
||||||
|
@ -112,7 +124,7 @@ template fromFloat*(val: float): NdValue =
|
||||||
cast[NdValue](val)
|
cast[NdValue](val)
|
||||||
|
|
||||||
template fromNdString*(val: NdString): NdValue =
|
template fromNdString*(val: NdString): NdValue =
|
||||||
cast[uint](val).bitor(tagString)
|
cast[uint](val).bitor(tagObject)
|
||||||
|
|
||||||
template fromNimString*(sval: string): NdValue =
|
template fromNimString*(sval: string): NdValue =
|
||||||
fromNdString(newString(sval))
|
fromNdString(newString(sval))
|
||||||
|
@ -121,13 +133,13 @@ template fromFunct*(val: ptr uint8): NdValue =
|
||||||
cast[uint](val).bitor(tagFunct)
|
cast[uint](val).bitor(tagFunct)
|
||||||
|
|
||||||
template fromClosure*(val: Closure[NdValue]): NdValue =
|
template fromClosure*(val: Closure[NdValue]): NdValue =
|
||||||
cast[uint](val).bitor(tagClosure)
|
cast[uint](val).bitor(tagObject)
|
||||||
|
|
||||||
template fromList*(val: List[NdValue]): NdValue =
|
template fromList*(val: List[NdValue]): NdValue =
|
||||||
cast[uint](val).bitor(tagList)
|
cast[uint](val).bitor(tagObject)
|
||||||
|
|
||||||
template fromTable*(val: NdTable[NdValue, NdValue]): NdValue =
|
template fromTable*(val: NdTable[NdValue, NdValue]): NdValue =
|
||||||
cast[uint](val).bitor(tagTable)
|
cast[uint](val).bitor(tagObject)
|
||||||
|
|
||||||
template fromNative*(val: Native): NdValue =
|
template fromNative*(val: Native): NdValue =
|
||||||
cast[uint](val).bitor(tagNative)
|
cast[uint](val).bitor(tagNative)
|
||||||
|
|
|
@ -4,7 +4,8 @@ import chunk
|
||||||
import config
|
import config
|
||||||
import pointerutils
|
import pointerutils
|
||||||
|
|
||||||
import types/stack
|
import types/ndstack
|
||||||
|
import types/ndobject
|
||||||
import types/ndstring
|
import types/ndstring
|
||||||
import bitops # needed for value's templates
|
import bitops # needed for value's templates
|
||||||
import types/value
|
import types/value
|
||||||
|
@ -15,6 +16,9 @@ import types/native
|
||||||
|
|
||||||
import lib/main
|
import lib/main
|
||||||
|
|
||||||
|
import memory
|
||||||
|
import gc
|
||||||
|
|
||||||
when debugVM:
|
when debugVM:
|
||||||
import terminal
|
import terminal
|
||||||
|
|
||||||
|
@ -32,20 +36,17 @@ type
|
||||||
Frame = object
|
Frame = object
|
||||||
stackBottom: int # the absolute index of where 0 inside the frame is
|
stackBottom: int # the absolute index of where 0 inside the frame is
|
||||||
returnIp: ptr uint8
|
returnIp: ptr uint8
|
||||||
# closure: Closure[NdValue]
|
|
||||||
|
|
||||||
InterpretResult* = enum
|
InterpretResult* = enum
|
||||||
irOK, irRuntimeError
|
irOK, irRuntimeError
|
||||||
|
|
||||||
proc run*(chunk: Chunk): InterpretResult =
|
proc run*(chunk: Chunk): InterpretResult =
|
||||||
|
enableGC = false
|
||||||
var
|
var
|
||||||
ip: ptr uint8 = chunk.code[0].unsafeAddr
|
ip: ptr uint8 = chunk.code[0].unsafeAddr
|
||||||
stack: Stack[NdValue] = newStack[NdValue](256)
|
|
||||||
hadError: bool
|
hadError: bool
|
||||||
globals: Table[NdValue, NdValue]
|
|
||||||
frames: Stack[Frame] = newStack[Frame](4)
|
frames: Stack[Frame] = newStack[Frame](4)
|
||||||
openUpvalues: Upvalue[NdValue] = nil
|
objects: ptr NdObject
|
||||||
|
|
||||||
proc runtimeError(msg: string) =
|
proc runtimeError(msg: string) =
|
||||||
let ii = ip.pdiff(chunk.code[0].unsafeAddr)
|
let ii = ip.pdiff(chunk.code[0].unsafeAddr)
|
||||||
|
@ -141,6 +142,7 @@ proc run*(chunk: Chunk): InterpretResult =
|
||||||
let globalsVal = cast[NdTable[NdValue, NdValue]](globals.addr)
|
let globalsVal = cast[NdTable[NdValue, NdValue]](globals.addr)
|
||||||
discard globals.tableSet(globalsKey, globalsVal.fromTable())
|
discard globals.tableSet(globalsKey, globalsVal.fromTable())
|
||||||
|
|
||||||
|
enableGC = true
|
||||||
while true:
|
while true:
|
||||||
{.computedgoto.} # See https://nim-lang.org/docs/manual.html#pragmas-computedgoto-pragma
|
{.computedgoto.} # See https://nim-lang.org/docs/manual.html#pragmas-computedgoto-pragma
|
||||||
|
|
||||||
|
@ -159,15 +161,6 @@ proc run*(chunk: Chunk): InterpretResult =
|
||||||
msg &= "]"
|
msg &= "]"
|
||||||
echo msg
|
echo msg
|
||||||
|
|
||||||
when debugClosures:
|
|
||||||
msg = " Closures: [ "
|
|
||||||
#for i in 0 .. frames.high():
|
|
||||||
#if frames[i].closure != nil:
|
|
||||||
# msg &= debugStr(frames[i].closure) & " "
|
|
||||||
msg &= "]"
|
|
||||||
echo msg
|
|
||||||
|
|
||||||
|
|
||||||
var ii = ip.pdiff(chunk.code[0].unsafeAddr) - 1
|
var ii = ip.pdiff(chunk.code[0].unsafeAddr) - 1
|
||||||
var ll = -1
|
var ll = -1
|
||||||
setForegroundColor(fgYellow)
|
setForegroundColor(fgYellow)
|
||||||
|
@ -361,7 +354,7 @@ proc run*(chunk: Chunk): InterpretResult =
|
||||||
of opSetUpvalue:
|
of opSetUpvalue:
|
||||||
let slot = ip.readDU8()
|
let slot = ip.readDU8()
|
||||||
when debugClosures:
|
when debugClosures:
|
||||||
echo &"CLOSURES - setupvalue is setting {$stack.peek} to slot {slot}, number of slots: {frames.peek().closure.upvalueCount}"
|
echo &"CLOSURES - setupvalue is setting {$stack.peek} to slot {slot}, number of slots: {cclosure.upvalueCount}"
|
||||||
cclosure.get(slot).write(stack.peek())
|
cclosure.get(slot).write(stack.peek())
|
||||||
of opCloseUpvalue:
|
of opCloseUpvalue:
|
||||||
let slot = ip.readDU8()
|
let slot = ip.readDU8()
|
||||||
|
@ -398,9 +391,7 @@ proc run*(chunk: Chunk): InterpretResult =
|
||||||
let times = runcounts[op]
|
let times = runcounts[op]
|
||||||
echo &"OpCode: {op} total duration {dur} ms {times} times"
|
echo &"OpCode: {op} total duration {dur} ms {times} times"
|
||||||
|
|
||||||
stack.free()
|
enableGC = false
|
||||||
frames.free()
|
|
||||||
globals.free()
|
|
||||||
|
|
||||||
if hadError:
|
if hadError:
|
||||||
irRuntimeError
|
irRuntimeError
|
||||||
|
|
Loading…
Reference in New Issue