VM to optimize currentFrame and add profiling
Some checks failed
Build / build (push) Failing after 5m50s
Publish Image / publish (push) Failing after 35s
Test / build (push) Failing after 5m30s

This commit is contained in:
Chuck Smith
2024-03-28 15:53:00 -04:00
parent 834de04635
commit 362138ff2e
2 changed files with 61 additions and 52 deletions

13
main.go
View File

@@ -3,6 +3,7 @@ package main
import (
"flag"
"fmt"
"github.com/pkg/profile"
"io"
"log"
"monkey/compiler"
@@ -22,6 +23,9 @@ var (
compile bool
version bool
debug bool
profileCPU bool
profileMem bool
)
func init() {
@@ -37,6 +41,9 @@ func init() {
flag.BoolVar(&interactive, "i", false, "enable interactive mode")
flag.StringVar(&engine, "e", "vm", "engine to use (eval or vm)")
flag.BoolVar(&profileCPU, "profile-cpu", false, "Enable CPU profiling.")
flag.BoolVar(&profileMem, "profile-mem", false, "Enable Memory profiling.")
}
// Indent indents a block of text with an indent string
@@ -70,6 +77,12 @@ func main() {
args := flag.Args()
if profileCPU {
defer profile.Start(profile.CPUProfile).Stop()
} else if profileMem {
defer profile.Start(profile.MemProfile).Stop()
}
if compile {
if len(args) < 1 {
log.Fatal("no source file given to compile")

View File

@@ -106,9 +106,11 @@ type VM struct {
sp int // Always points to the next value. Top of stack is stack[sp-1]
frames []*Frame
framesIndex int
frame *Frame // Current frame or nil
fp int // Always points to the current frame. Current frame is frames[fp-1]
}
// New constructs a new monkey-lang bytecode virtual machine
func New(bytecode *compiler.Bytecode) *VM {
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
mainClosure := &object.Closure{Fn: mainFn}
@@ -127,7 +129,8 @@ func New(bytecode *compiler.Bytecode) *VM {
sp: 0,
frames: frames,
framesIndex: 1,
frame: mainFrame,
fp: 1,
}
}
@@ -143,25 +146,24 @@ func NewWithState(bytecode *compiler.Bytecode, state *VMState) *VM {
state: state,
frames: frames,
framesIndex: 1,
frame: mainFrame,
fp: 1,
stack: make([]object.Object, StackSize),
sp: 0,
}
}
func (vm *VM) currentFrame() *Frame {
return vm.frames[vm.framesIndex-1]
}
func (vm *VM) pushFrame(f *Frame) {
vm.frames[vm.framesIndex] = f
vm.framesIndex++
vm.frame = f
vm.frames[vm.fp] = f
vm.fp++
}
func (vm *VM) popFrame() *Frame {
vm.framesIndex--
return vm.frames[vm.framesIndex]
vm.fp--
vm.frame = vm.frames[vm.fp-1]
return vm.frames[vm.fp]
}
func (vm *VM) loadModule(name object.Object) error {
@@ -200,22 +202,22 @@ func (vm *VM) Run() error {
),
fmt.Sprintf(
"[ip=%02d fp=%02d, sp=%02d]",
ip, vm.framesIndex-1, vm.sp,
ip, vm.fp-1, vm.sp,
),
)
}
for vm.currentFrame().ip < len(vm.currentFrame().Instructions())-1 {
vm.currentFrame().ip++
for vm.frame.ip < len(vm.frame.Instructions())-1 {
vm.frame.ip++
ip = vm.currentFrame().ip
ins = vm.currentFrame().Instructions()
ip = vm.frame.ip
ins = vm.frame.Instructions()
op = code.Opcode(ins[ip])
switch op {
case code.OpConstant:
constIndex := code.ReadUint16(ins[ip+1:])
vm.currentFrame().ip += 2
vm.frame.ip += 2
err := vm.push(vm.state.Constants[constIndex])
if err != nil {
@@ -271,15 +273,15 @@ func (vm *VM) Run() error {
case code.OpJump:
pos := int(code.ReadUint16(ins[ip+1:]))
vm.currentFrame().ip = pos - 1
vm.frame.ip = pos - 1
case code.OpJumpNotTruthy:
pos := int(code.ReadUint16(ins[ip+1:]))
vm.currentFrame().ip += 2
vm.frame.ip += 2
condition := vm.pop()
if !isTruthy(condition) {
vm.currentFrame().ip = pos - 1
vm.frame.ip = pos - 1
}
case code.OpNull:
@@ -290,7 +292,7 @@ func (vm *VM) Run() error {
case code.OpSetGlobal:
globalIndex := code.ReadUint16(ins[ip+1:])
vm.currentFrame().ip += 2
vm.frame.ip += 2
ref := vm.pop()
if immutable, ok := ref.(object.Immutable); ok {
@@ -306,7 +308,7 @@ func (vm *VM) Run() error {
case code.OpAssignGlobal:
globalIndex := code.ReadUint16(ins[ip+1:])
vm.currentFrame().ip += 2
vm.frame.ip += 2
vm.state.Globals[globalIndex] = vm.pop()
err := vm.push(Null)
@@ -316,10 +318,9 @@ func (vm *VM) Run() error {
case code.OpAssignLocal:
localIndex := code.ReadUint8(ins[ip+1:])
vm.currentFrame().ip += 1
vm.frame.ip += 1
frame := vm.currentFrame()
vm.stack[frame.basePointer+int(localIndex)] = vm.pop()
vm.stack[vm.frame.basePointer+int(localIndex)] = vm.pop()
err := vm.push(Null)
if err != nil {
@@ -328,7 +329,7 @@ func (vm *VM) Run() error {
case code.OpGetGlobal:
globalIndex := code.ReadUint16(ins[ip+1:])
vm.currentFrame().ip += 2
vm.frame.ip += 2
err := vm.push(vm.state.Globals[globalIndex])
if err != nil {
@@ -337,7 +338,7 @@ func (vm *VM) Run() error {
case code.OpArray:
numElements := int(code.ReadUint16(ins[ip+1:]))
vm.currentFrame().ip += 2
vm.frame.ip += 2
array := vm.buildArray(vm.sp-numElements, vm.sp)
vm.sp = vm.sp - numElements
@@ -349,7 +350,7 @@ func (vm *VM) Run() error {
case code.OpHash:
numElements := int(code.ReadUint16(ins[ip+1:]))
vm.currentFrame().ip += 2
vm.frame.ip += 2
hash, err := vm.buildHash(vm.sp-numElements, vm.sp)
if err != nil {
@@ -383,7 +384,7 @@ func (vm *VM) Run() error {
case code.OpCall:
numArgs := code.ReadUint8(ins[ip+1:])
vm.currentFrame().ip += 1
vm.frame.ip += 1
err := vm.executeCall(int(numArgs))
if err != nil {
@@ -403,15 +404,13 @@ func (vm *VM) Run() error {
case code.OpSetLocal:
localIndex := code.ReadUint8(ins[ip+1:])
vm.currentFrame().ip += 1
frame := vm.currentFrame()
vm.frame.ip += 1
ref := vm.pop()
if immutable, ok := ref.(object.Immutable); ok {
vm.stack[frame.basePointer+int(localIndex)] = immutable.Clone()
vm.stack[vm.frame.basePointer+int(localIndex)] = immutable.Clone()
} else {
vm.stack[frame.basePointer+int(localIndex)] = ref
vm.stack[vm.frame.basePointer+int(localIndex)] = ref
}
err := vm.push(Null)
@@ -421,18 +420,16 @@ func (vm *VM) Run() error {
case code.OpGetLocal:
localIndex := code.ReadUint8(ins[ip+1:])
vm.currentFrame().ip += 1
vm.frame.ip += 1
frame := vm.currentFrame()
err := vm.push(vm.stack[frame.basePointer+int(localIndex)])
err := vm.push(vm.stack[vm.frame.basePointer+int(localIndex)])
if err != nil {
return err
}
case code.OpGetBuiltin:
builtinIndex := code.ReadUint8(ins[ip+1:])
vm.currentFrame().ip += 1
vm.frame.ip += 1
builtin := builtins.BuiltinsIndex[builtinIndex]
@@ -444,7 +441,7 @@ func (vm *VM) Run() error {
case code.OpClosure:
constIndex := code.ReadUint16(ins[ip+1:])
numFree := code.ReadUint8(ins[ip+3:])
vm.currentFrame().ip += 3
vm.frame.ip += 3
err := vm.pushClosure(int(constIndex), int(numFree))
if err != nil {
@@ -453,16 +450,15 @@ func (vm *VM) Run() error {
case code.OpGetFree:
freeIndex := code.ReadUint8(ins[ip+1:])
vm.currentFrame().ip += 1
vm.frame.ip += 1
currentClosure := vm.currentFrame().cl
err := vm.push(currentClosure.Free[freeIndex])
err := vm.push(vm.frame.cl.Free[freeIndex])
if err != nil {
return err
}
case code.OpCurrentClosure:
currentClosure := vm.currentFrame().cl
currentClosure := vm.frame.cl
err := vm.push(currentClosure)
if err != nil {
return err
@@ -481,7 +477,7 @@ func (vm *VM) Run() error {
if vm.Debug {
log.Printf(
"%-25s [ip=%02d fp=%02d, sp=%02d]",
"", ip, vm.framesIndex-1, vm.sp,
"", ip, vm.fp-1, vm.sp,
)
}
}
@@ -844,14 +840,14 @@ func (vm *VM) callClosure(cl *object.Closure, numArgs int) error {
}
// Optimize tail calls and avoid a new frame
if cl.Fn == vm.currentFrame().cl.Fn {
nextOP := vm.currentFrame().NextOp()
if cl.Fn == vm.frame.cl.Fn {
nextOP := vm.frame.NextOp()
if nextOP == code.OpReturn {
for p := 0; p < numArgs; p++ {
vm.stack[vm.currentFrame().basePointer+p] = vm.stack[vm.sp-numArgs+p]
vm.stack[vm.frame.basePointer+p] = vm.stack[vm.sp-numArgs+p]
}
vm.sp -= numArgs + 1
vm.currentFrame().ip = -1 // reset IP to the beginning of the frame
vm.frame.ip = -1 // reset IP to the beginning of the frame
return nil
}
}