Reverted variable hoisting and fixed minor issue during name resolution. Updated comments

This commit is contained in:
Mattia Giambirtone 2023-05-01 15:30:36 +02:00
parent a706fdad7a
commit cd853bb140
5 changed files with 24 additions and 30 deletions

View File

@ -208,19 +208,16 @@ proc markRoots(self: var PeonVM): HashSet[ptr HeapObject] =
# Unlike what Bob does in his book, we keep track
# of objects another way, mainly due to the difference
# of our respective designs. Specifically, our VM only
# handles a single type (uint64), while Lox has a stack
# of heap-allocated structs (which is convenient, but slow).
# The previous implementation would just store all pointers
# allocated by us in a hash set and then check if any source
# of roots contained any of the integer values that it was
# keeping track of, but this meant that if a primitive object's
# value happened to collide with an active pointer the GC would
# mistakenly assume the object was reachable, potentially leading
# to a nasty memory leak. The current implementation uses pointer
# tagging: we know that modern CPUs never use bit 63 in addresses,
# so if it set we know it cannot be a pointer, and if it is set we
# just need to check if it's in our list of active addresses or not.
# This should resolve the potential memory leak (hopefully)
# handles a single type (uint64) while Lox stores all objects
# in heap-allocated structs (which is convenient, but slow).
# What we do is store the pointers to the objects we allocated in
# a hash set and then, at collection time, do a set difference
# between the reachable objects and the whole set and discard
# whatever is left; Unfortunately, this means that if a primitive
# object's value happens to collide with an active pointer the GC
# will mistakenly assume the object to be reachable, potentially
# leading to a nasty memory leak. Let's just hope a 48+ bit address
# space makes this occurrence rare enough not to be a problem
var result = initHashSet[uint64](self.gc.pointers.len())
for obj in self.calls:
if obj in self.gc.pointers:
@ -691,7 +688,7 @@ proc dispatch*(self: var PeonVM) =
while true:
{.computedgoto.} # https://nim-lang.org/docs/manual.html#pragmas-computedgoto-pragma
when debugVM:
if self.ip in self.breakpoints or self.breakpoints.len() == 0 or self.debugNext:
if self.ip in self.breakpoints or self.debugNext:
self.debug()
instruction = OpCode(self.readByte())
case instruction:

View File

@ -341,11 +341,9 @@ proc step*(self: Compiler): ASTNode {.inline.} =
# and can be reused across multiple compilation backends
proc resolve*(self: Compiler, name: string): Name =
## Traverses all existing namespaces and returns
## the first object with the given name. Returns
## nil when the name can't be found. Note that
## when a type or function declaration is first
## resolved, it is also compiled on-the-fly
## Traverses all existing namespaces in reverse order
## and returns the first object with the given name.
## Returns nil when the name can't be found
for obj in reversed(self.names):
if obj.ident.token.lexeme == name:
if obj.owner.path != self.currentModule.path:
@ -361,6 +359,7 @@ proc resolve*(self: Compiler, name: string): Name =
# module and said module has explicitly
# exported it to us: we can use it
result = obj
result.resolved = true
break
# If the name is public but not exported in
# its owner module, then we act as if it's
@ -370,6 +369,7 @@ proc resolve*(self: Compiler, name: string): Name =
# might not want to also have access to C's and D's
# names as they might clash with its own stuff)
continue
# We own this name, so we can definitely access it
result = obj
result.resolved = true
break

View File

@ -189,7 +189,7 @@ type
SysClock64, # Pushes the output of a monotonic clock on the stack
LoadTOS, # Pushes the top of the call stack onto the operand stack
DupTop, # Duplicates the top of the operand stack onto the operand stack
ReplExit, # Exits the VM immediately, leaving its state intact. Used in the REPL
ReplExit, # Exits the VM immediately, leaving its state intact. Used in the REPL
LoadGlobal # Loads a global variable

View File

@ -95,7 +95,7 @@ proc compile*(self: BytecodeCompiler, ast: seq[Declaration], file: string, lines
mode: CompileMode = Debug): Chunk
proc statement(self: BytecodeCompiler, node: Statement)
proc declaration(self: BytecodeCompiler, node: Declaration)
proc varDecl(self: BytecodeCompiler, node: VarDecl, hoist: bool = false)
proc varDecl(self: BytecodeCompiler, node: VarDecl)
proc specialize(self: BytecodeCompiler, typ: Type, args: seq[Expression]): Type {.discardable.}
proc patchReturnAddress(self: BytecodeCompiler, pos: int)
proc handleMagicPragma(self: BytecodeCompiler, pragma: Pragma, name: Name)
@ -1864,10 +1864,8 @@ proc statement(self: BytecodeCompiler, node: Statement) =
self.expression(Expression(node))
proc varDecl(self: BytecodeCompiler, node: VarDecl, hoist: bool = false) =
proc varDecl(self: BytecodeCompiler, node: VarDecl) =
## Compiles variable declarations
if node.name.depth == 0 and not hoist:
return
var typ: Type
# Our parser guarantees that the variable declaration
# will have a type declaration or a value (or both)
@ -2078,9 +2076,6 @@ proc compile*(self: BytecodeCompiler, ast: seq[Declaration], file: string, lines
self.jumps = @[]
let pos = self.beginProgram()
let idx = self.stackIndex
for node in ast:
if node.kind == varDecl and VarDecl(node).name.depth == 0:
self.varDecl(VarDecl(node), hoist=true)
self.stackIndex = idx
while not self.done():
self.declaration(Declaration(self.step()))

View File

@ -51,7 +51,7 @@ proc getLineEditor: LineEditor =
result.bindHistory(history)
proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: CompileMode = Debug) =
proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: CompileMode = Debug, breakpoints: seq[uint64] = @[]) =
styledEcho fgMagenta, "Welcome into the peon REPL!"
var
keep = true
@ -136,7 +136,7 @@ proc repl(warnings: seq[WarningKind] = @[], mismatches: bool = false, mode: Comp
else:
styledEcho fgRed, "Corrupted"
if not first:
vm.run(serialized.chunk, repl=true)
vm.run(serialized.chunk, repl=true, breakpoints=breakpoints)
first = true
else:
vm.resume(serialized.chunk)
@ -407,7 +407,9 @@ when isMainModule:
else:
echo "usage: peon [options] [filename.pn]"
quit()
if breaks.len() == 0 and debugVM:
breaks.add(0)
if file == "":
repl(warnings, mismatches, mode)
repl(warnings, mismatches, mode, breaks)
else:
runFile(file, fromString, dump, breaks, dis, warnings, mismatches, mode, run, backend, output)