diff --git a/main.go b/main.go index 3e2a40e..761d4d6 100644 --- a/main.go +++ b/main.go @@ -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") diff --git a/vm/vm.go b/vm/vm.go index 4a96ce2..c933514 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -105,10 +105,12 @@ type VM struct { stack []object.Object sp int // Always points to the next value. Top of stack is stack[sp-1] - frames []*Frame - framesIndex int + frames []*Frame + 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} @@ -126,8 +128,9 @@ func New(bytecode *compiler.Bytecode) *VM { stack: make([]object.Object, StackSize), sp: 0, - frames: frames, - framesIndex: 1, + frames: frames, + frame: mainFrame, + fp: 1, } } @@ -142,26 +145,25 @@ func NewWithState(bytecode *compiler.Bytecode, state *VMState) *VM { return &VM{ state: state, - frames: frames, - framesIndex: 1, + frames: frames, + 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 } }