VM to optimize currentFrame and add profiling
This commit is contained in:
13
main.go
13
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")
|
||||
|
||||
100
vm/vm.go
100
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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user