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 (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pkg/profile"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"monkey/compiler"
|
"monkey/compiler"
|
||||||
@@ -22,6 +23,9 @@ var (
|
|||||||
compile bool
|
compile bool
|
||||||
version bool
|
version bool
|
||||||
debug bool
|
debug bool
|
||||||
|
|
||||||
|
profileCPU bool
|
||||||
|
profileMem bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -37,6 +41,9 @@ func init() {
|
|||||||
|
|
||||||
flag.BoolVar(&interactive, "i", false, "enable interactive mode")
|
flag.BoolVar(&interactive, "i", false, "enable interactive mode")
|
||||||
flag.StringVar(&engine, "e", "vm", "engine to use (eval or vm)")
|
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
|
// Indent indents a block of text with an indent string
|
||||||
@@ -70,6 +77,12 @@ func main() {
|
|||||||
|
|
||||||
args := flag.Args()
|
args := flag.Args()
|
||||||
|
|
||||||
|
if profileCPU {
|
||||||
|
defer profile.Start(profile.CPUProfile).Stop()
|
||||||
|
} else if profileMem {
|
||||||
|
defer profile.Start(profile.MemProfile).Stop()
|
||||||
|
}
|
||||||
|
|
||||||
if compile {
|
if compile {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
log.Fatal("no source file given to compile")
|
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
|
stack []object.Object
|
||||||
sp int // Always points to the next value. Top of stack is stack[sp-1]
|
sp int // Always points to the next value. Top of stack is stack[sp-1]
|
||||||
|
|
||||||
frames []*Frame
|
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 {
|
func New(bytecode *compiler.Bytecode) *VM {
|
||||||
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
|
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
|
||||||
mainClosure := &object.Closure{Fn: mainFn}
|
mainClosure := &object.Closure{Fn: mainFn}
|
||||||
@@ -126,8 +128,9 @@ func New(bytecode *compiler.Bytecode) *VM {
|
|||||||
stack: make([]object.Object, StackSize),
|
stack: make([]object.Object, StackSize),
|
||||||
sp: 0,
|
sp: 0,
|
||||||
|
|
||||||
frames: frames,
|
frames: frames,
|
||||||
framesIndex: 1,
|
frame: mainFrame,
|
||||||
|
fp: 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,26 +145,25 @@ func NewWithState(bytecode *compiler.Bytecode, state *VMState) *VM {
|
|||||||
return &VM{
|
return &VM{
|
||||||
state: state,
|
state: state,
|
||||||
|
|
||||||
frames: frames,
|
frames: frames,
|
||||||
framesIndex: 1,
|
frame: mainFrame,
|
||||||
|
fp: 1,
|
||||||
|
|
||||||
stack: make([]object.Object, StackSize),
|
stack: make([]object.Object, StackSize),
|
||||||
sp: 0,
|
sp: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) currentFrame() *Frame {
|
|
||||||
return vm.frames[vm.framesIndex-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vm *VM) pushFrame(f *Frame) {
|
func (vm *VM) pushFrame(f *Frame) {
|
||||||
vm.frames[vm.framesIndex] = f
|
vm.frame = f
|
||||||
vm.framesIndex++
|
vm.frames[vm.fp] = f
|
||||||
|
vm.fp++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) popFrame() *Frame {
|
func (vm *VM) popFrame() *Frame {
|
||||||
vm.framesIndex--
|
vm.fp--
|
||||||
return vm.frames[vm.framesIndex]
|
vm.frame = vm.frames[vm.fp-1]
|
||||||
|
return vm.frames[vm.fp]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) loadModule(name object.Object) error {
|
func (vm *VM) loadModule(name object.Object) error {
|
||||||
@@ -200,22 +202,22 @@ func (vm *VM) Run() error {
|
|||||||
),
|
),
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"[ip=%02d fp=%02d, sp=%02d]",
|
"[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 {
|
for vm.frame.ip < len(vm.frame.Instructions())-1 {
|
||||||
vm.currentFrame().ip++
|
vm.frame.ip++
|
||||||
|
|
||||||
ip = vm.currentFrame().ip
|
ip = vm.frame.ip
|
||||||
ins = vm.currentFrame().Instructions()
|
ins = vm.frame.Instructions()
|
||||||
op = code.Opcode(ins[ip])
|
op = code.Opcode(ins[ip])
|
||||||
|
|
||||||
switch op {
|
switch op {
|
||||||
case code.OpConstant:
|
case code.OpConstant:
|
||||||
constIndex := code.ReadUint16(ins[ip+1:])
|
constIndex := code.ReadUint16(ins[ip+1:])
|
||||||
vm.currentFrame().ip += 2
|
vm.frame.ip += 2
|
||||||
|
|
||||||
err := vm.push(vm.state.Constants[constIndex])
|
err := vm.push(vm.state.Constants[constIndex])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -271,15 +273,15 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
case code.OpJump:
|
case code.OpJump:
|
||||||
pos := int(code.ReadUint16(ins[ip+1:]))
|
pos := int(code.ReadUint16(ins[ip+1:]))
|
||||||
vm.currentFrame().ip = pos - 1
|
vm.frame.ip = pos - 1
|
||||||
|
|
||||||
case code.OpJumpNotTruthy:
|
case code.OpJumpNotTruthy:
|
||||||
pos := int(code.ReadUint16(ins[ip+1:]))
|
pos := int(code.ReadUint16(ins[ip+1:]))
|
||||||
vm.currentFrame().ip += 2
|
vm.frame.ip += 2
|
||||||
|
|
||||||
condition := vm.pop()
|
condition := vm.pop()
|
||||||
if !isTruthy(condition) {
|
if !isTruthy(condition) {
|
||||||
vm.currentFrame().ip = pos - 1
|
vm.frame.ip = pos - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
case code.OpNull:
|
case code.OpNull:
|
||||||
@@ -290,7 +292,7 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
case code.OpSetGlobal:
|
case code.OpSetGlobal:
|
||||||
globalIndex := code.ReadUint16(ins[ip+1:])
|
globalIndex := code.ReadUint16(ins[ip+1:])
|
||||||
vm.currentFrame().ip += 2
|
vm.frame.ip += 2
|
||||||
|
|
||||||
ref := vm.pop()
|
ref := vm.pop()
|
||||||
if immutable, ok := ref.(object.Immutable); ok {
|
if immutable, ok := ref.(object.Immutable); ok {
|
||||||
@@ -306,7 +308,7 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
case code.OpAssignGlobal:
|
case code.OpAssignGlobal:
|
||||||
globalIndex := code.ReadUint16(ins[ip+1:])
|
globalIndex := code.ReadUint16(ins[ip+1:])
|
||||||
vm.currentFrame().ip += 2
|
vm.frame.ip += 2
|
||||||
vm.state.Globals[globalIndex] = vm.pop()
|
vm.state.Globals[globalIndex] = vm.pop()
|
||||||
|
|
||||||
err := vm.push(Null)
|
err := vm.push(Null)
|
||||||
@@ -316,10 +318,9 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
case code.OpAssignLocal:
|
case code.OpAssignLocal:
|
||||||
localIndex := code.ReadUint8(ins[ip+1:])
|
localIndex := code.ReadUint8(ins[ip+1:])
|
||||||
vm.currentFrame().ip += 1
|
vm.frame.ip += 1
|
||||||
|
|
||||||
frame := vm.currentFrame()
|
vm.stack[vm.frame.basePointer+int(localIndex)] = vm.pop()
|
||||||
vm.stack[frame.basePointer+int(localIndex)] = vm.pop()
|
|
||||||
|
|
||||||
err := vm.push(Null)
|
err := vm.push(Null)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -328,7 +329,7 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
case code.OpGetGlobal:
|
case code.OpGetGlobal:
|
||||||
globalIndex := code.ReadUint16(ins[ip+1:])
|
globalIndex := code.ReadUint16(ins[ip+1:])
|
||||||
vm.currentFrame().ip += 2
|
vm.frame.ip += 2
|
||||||
|
|
||||||
err := vm.push(vm.state.Globals[globalIndex])
|
err := vm.push(vm.state.Globals[globalIndex])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -337,7 +338,7 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
case code.OpArray:
|
case code.OpArray:
|
||||||
numElements := int(code.ReadUint16(ins[ip+1:]))
|
numElements := int(code.ReadUint16(ins[ip+1:]))
|
||||||
vm.currentFrame().ip += 2
|
vm.frame.ip += 2
|
||||||
|
|
||||||
array := vm.buildArray(vm.sp-numElements, vm.sp)
|
array := vm.buildArray(vm.sp-numElements, vm.sp)
|
||||||
vm.sp = vm.sp - numElements
|
vm.sp = vm.sp - numElements
|
||||||
@@ -349,7 +350,7 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
case code.OpHash:
|
case code.OpHash:
|
||||||
numElements := int(code.ReadUint16(ins[ip+1:]))
|
numElements := int(code.ReadUint16(ins[ip+1:]))
|
||||||
vm.currentFrame().ip += 2
|
vm.frame.ip += 2
|
||||||
|
|
||||||
hash, err := vm.buildHash(vm.sp-numElements, vm.sp)
|
hash, err := vm.buildHash(vm.sp-numElements, vm.sp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -383,7 +384,7 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
case code.OpCall:
|
case code.OpCall:
|
||||||
numArgs := code.ReadUint8(ins[ip+1:])
|
numArgs := code.ReadUint8(ins[ip+1:])
|
||||||
vm.currentFrame().ip += 1
|
vm.frame.ip += 1
|
||||||
|
|
||||||
err := vm.executeCall(int(numArgs))
|
err := vm.executeCall(int(numArgs))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -403,15 +404,13 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
case code.OpSetLocal:
|
case code.OpSetLocal:
|
||||||
localIndex := code.ReadUint8(ins[ip+1:])
|
localIndex := code.ReadUint8(ins[ip+1:])
|
||||||
vm.currentFrame().ip += 1
|
vm.frame.ip += 1
|
||||||
|
|
||||||
frame := vm.currentFrame()
|
|
||||||
|
|
||||||
ref := vm.pop()
|
ref := vm.pop()
|
||||||
if immutable, ok := ref.(object.Immutable); ok {
|
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 {
|
} else {
|
||||||
vm.stack[frame.basePointer+int(localIndex)] = ref
|
vm.stack[vm.frame.basePointer+int(localIndex)] = ref
|
||||||
}
|
}
|
||||||
|
|
||||||
err := vm.push(Null)
|
err := vm.push(Null)
|
||||||
@@ -421,18 +420,16 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
case code.OpGetLocal:
|
case code.OpGetLocal:
|
||||||
localIndex := code.ReadUint8(ins[ip+1:])
|
localIndex := code.ReadUint8(ins[ip+1:])
|
||||||
vm.currentFrame().ip += 1
|
vm.frame.ip += 1
|
||||||
|
|
||||||
frame := vm.currentFrame()
|
err := vm.push(vm.stack[vm.frame.basePointer+int(localIndex)])
|
||||||
|
|
||||||
err := vm.push(vm.stack[frame.basePointer+int(localIndex)])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case code.OpGetBuiltin:
|
case code.OpGetBuiltin:
|
||||||
builtinIndex := code.ReadUint8(ins[ip+1:])
|
builtinIndex := code.ReadUint8(ins[ip+1:])
|
||||||
vm.currentFrame().ip += 1
|
vm.frame.ip += 1
|
||||||
|
|
||||||
builtin := builtins.BuiltinsIndex[builtinIndex]
|
builtin := builtins.BuiltinsIndex[builtinIndex]
|
||||||
|
|
||||||
@@ -444,7 +441,7 @@ func (vm *VM) Run() error {
|
|||||||
case code.OpClosure:
|
case code.OpClosure:
|
||||||
constIndex := code.ReadUint16(ins[ip+1:])
|
constIndex := code.ReadUint16(ins[ip+1:])
|
||||||
numFree := code.ReadUint8(ins[ip+3:])
|
numFree := code.ReadUint8(ins[ip+3:])
|
||||||
vm.currentFrame().ip += 3
|
vm.frame.ip += 3
|
||||||
|
|
||||||
err := vm.pushClosure(int(constIndex), int(numFree))
|
err := vm.pushClosure(int(constIndex), int(numFree))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -453,16 +450,15 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
case code.OpGetFree:
|
case code.OpGetFree:
|
||||||
freeIndex := code.ReadUint8(ins[ip+1:])
|
freeIndex := code.ReadUint8(ins[ip+1:])
|
||||||
vm.currentFrame().ip += 1
|
vm.frame.ip += 1
|
||||||
|
|
||||||
currentClosure := vm.currentFrame().cl
|
err := vm.push(vm.frame.cl.Free[freeIndex])
|
||||||
err := vm.push(currentClosure.Free[freeIndex])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case code.OpCurrentClosure:
|
case code.OpCurrentClosure:
|
||||||
currentClosure := vm.currentFrame().cl
|
currentClosure := vm.frame.cl
|
||||||
err := vm.push(currentClosure)
|
err := vm.push(currentClosure)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -481,7 +477,7 @@ func (vm *VM) Run() error {
|
|||||||
if vm.Debug {
|
if vm.Debug {
|
||||||
log.Printf(
|
log.Printf(
|
||||||
"%-25s [ip=%02d fp=%02d, sp=%02d]",
|
"%-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
|
// Optimize tail calls and avoid a new frame
|
||||||
if cl.Fn == vm.currentFrame().cl.Fn {
|
if cl.Fn == vm.frame.cl.Fn {
|
||||||
nextOP := vm.currentFrame().NextOp()
|
nextOP := vm.frame.NextOp()
|
||||||
if nextOP == code.OpReturn {
|
if nextOP == code.OpReturn {
|
||||||
for p := 0; p < numArgs; p++ {
|
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.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
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user