optimizations
This commit is contained in:
@@ -6,9 +6,9 @@ import (
|
||||
)
|
||||
|
||||
type frame struct {
|
||||
cl *object.Closure
|
||||
ip int
|
||||
ip uint16
|
||||
basePointer int
|
||||
cl *object.Closure
|
||||
}
|
||||
|
||||
func newFrame(cl *object.Closure, basePointer int) frame {
|
||||
@@ -30,7 +30,7 @@ func (f *frame) SetFree(idx uint8, obj object.Object) {
|
||||
f.cl.Free[idx] = obj
|
||||
}
|
||||
|
||||
func (f *frame) SetIP(ip int) {
|
||||
func (f *frame) SetIP(ip uint16) {
|
||||
f.ip = ip
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"monkey/internal/builtins"
|
||||
"monkey/internal/code"
|
||||
"monkey/internal/compiler"
|
||||
@@ -13,8 +12,6 @@ import (
|
||||
"monkey/internal/utils"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
@@ -151,20 +148,25 @@ func (vm *VM) popFrame() frame {
|
||||
return vm.frames[vm.fp]
|
||||
}
|
||||
|
||||
// Option defines a function option for the virtual machine.
|
||||
type Option func(*VM)
|
||||
|
||||
// WithContext defines an option to set the context for the virtual machine.
|
||||
func WithContext(ctx context.Context) Option {
|
||||
return func(vm *VM) { vm.ctx = ctx }
|
||||
}
|
||||
|
||||
// WithDebug enables debug mode in the VM.
|
||||
func WithDebug(debug bool) Option {
|
||||
return func(vm *VM) { vm.debug = debug }
|
||||
}
|
||||
|
||||
// WithState sets the state of the VM.
|
||||
func WithState(state *State) Option {
|
||||
return func(vm *VM) { vm.state = state }
|
||||
}
|
||||
|
||||
// WithTrace sets the trace flag for the VM.
|
||||
func WithTrace(trace bool) Option {
|
||||
return func(vm *VM) { vm.trace = trace }
|
||||
}
|
||||
@@ -224,6 +226,29 @@ func (vm *VM) pop() object.Object {
|
||||
return o
|
||||
}
|
||||
|
||||
func (vm *VM) pop2() (object.Object, object.Object) {
|
||||
if vm.sp == 1 {
|
||||
panic("stack underflow")
|
||||
}
|
||||
|
||||
o1 := vm.stack[vm.sp-1]
|
||||
o2 := vm.stack[vm.sp-2]
|
||||
vm.sp -= 2
|
||||
return o1, o2
|
||||
}
|
||||
|
||||
func (vm *VM) pop3() (object.Object, object.Object, object.Object) {
|
||||
if vm.sp == 2 {
|
||||
panic("stack underflow")
|
||||
}
|
||||
|
||||
o1 := vm.stack[vm.sp-1]
|
||||
o2 := vm.stack[vm.sp-2]
|
||||
o3 := vm.stack[vm.sp-3]
|
||||
vm.sp -= 3
|
||||
return o1, o2, o3
|
||||
}
|
||||
|
||||
func (vm *VM) executeSetGlobal() error {
|
||||
globalIndex := vm.currentFrame().ReadUint16()
|
||||
|
||||
@@ -301,8 +326,7 @@ func (vm *VM) executeMakeArray() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeAdd() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -335,8 +359,7 @@ func (vm *VM) executeAdd() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeSub() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -351,8 +374,7 @@ func (vm *VM) executeSub() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeMul() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case *object.Array:
|
||||
@@ -379,8 +401,7 @@ func (vm *VM) executeMul() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeDiv() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -396,8 +417,7 @@ func (vm *VM) executeDiv() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeMod() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -412,8 +432,7 @@ func (vm *VM) executeMod() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeOr() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Boolean:
|
||||
@@ -428,8 +447,7 @@ func (vm *VM) executeOr() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeAnd() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Boolean:
|
||||
@@ -444,8 +462,7 @@ func (vm *VM) executeAnd() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeBitwiseOr() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -460,8 +477,7 @@ func (vm *VM) executeBitwiseOr() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeBitwiseXor() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -476,8 +492,7 @@ func (vm *VM) executeBitwiseXor() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeBitwiseAnd() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -503,8 +518,7 @@ func (vm *VM) executeBitwiseNot() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeLeftShift() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -519,8 +533,7 @@ func (vm *VM) executeLeftShift() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeRightShift() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.Integer:
|
||||
@@ -535,63 +548,39 @@ func (vm *VM) executeRightShift() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeEqual() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
if obj, ok := left.(object.Comparable); ok {
|
||||
val := obj.Compare(right)
|
||||
if val == 0 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
return vm.push(object.FALSE)
|
||||
if left.Compare(right) == 0 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
|
||||
return object.NewBinaryOpError(left, right, "==")
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
func (vm *VM) executeNotEqual() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
if obj, ok := left.(object.Comparable); ok {
|
||||
val := obj.Compare(right)
|
||||
if val != 0 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
return vm.push(object.FALSE)
|
||||
if left.Compare(right) != 0 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
|
||||
return object.NewBinaryOpError(left, right, "!=")
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
func (vm *VM) executeGreaterThan() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
if obj, ok := left.(object.Comparable); ok {
|
||||
val := obj.Compare(right)
|
||||
if val == 1 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
return vm.push(object.FALSE)
|
||||
if left.Compare(right) == 1 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
|
||||
return object.NewBinaryOpError(left, right, ">")
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
func (vm *VM) executeGreaterThanOrEqual() error {
|
||||
right := vm.pop()
|
||||
left := vm.pop()
|
||||
right, left := vm.pop2()
|
||||
|
||||
if obj, ok := left.(object.Comparable); ok {
|
||||
val := obj.Compare(right)
|
||||
if val >= 0 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
return vm.push(object.FALSE)
|
||||
if left.Compare(right) == 1 {
|
||||
return vm.push(object.TRUE)
|
||||
}
|
||||
|
||||
return object.NewBinaryOpError(left, right, ">")
|
||||
return vm.push(object.FALSE)
|
||||
}
|
||||
|
||||
func (vm *VM) executeNot() error {
|
||||
@@ -621,9 +610,7 @@ func (vm *VM) executeMinus() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeSetItem() error {
|
||||
right := vm.pop()
|
||||
index := vm.pop()
|
||||
left := vm.pop()
|
||||
right, index, left := vm.pop3()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case *object.Array:
|
||||
@@ -647,8 +634,7 @@ func (vm *VM) executeSetItem() error {
|
||||
}
|
||||
|
||||
func (vm *VM) executeGetItem() error {
|
||||
index := vm.pop()
|
||||
left := vm.pop()
|
||||
index, left := vm.pop2()
|
||||
|
||||
switch obj := left.(type) {
|
||||
case object.String:
|
||||
@@ -749,7 +735,7 @@ func (vm *VM) pushClosure(constIndex, numFree int) error {
|
||||
for i := 0; i < numFree; i++ {
|
||||
free[i] = vm.stack[vm.sp-numFree+i]
|
||||
}
|
||||
vm.sp = vm.sp - numFree
|
||||
vm.sp -= numFree
|
||||
|
||||
closure := object.Closure{Fn: function, Free: free}
|
||||
return vm.push(&closure)
|
||||
@@ -778,49 +764,9 @@ func (vm *VM) LastPoppedStackElem() object.Object {
|
||||
}
|
||||
|
||||
func (vm *VM) Run() (err error) {
|
||||
var (
|
||||
op code.Opcode
|
||||
opcodeFreqs = make(map[code.Opcode]int)
|
||||
)
|
||||
|
||||
if vm.debug {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
end := time.Now().Sub(start)
|
||||
total := 0
|
||||
opcodes := make([]code.Opcode, 0, len(opcodeFreqs))
|
||||
|
||||
for opcode, count := range opcodeFreqs {
|
||||
opcodes = append(opcodes, opcode)
|
||||
total += count
|
||||
}
|
||||
|
||||
sort.SliceStable(opcodes, func(i, j int) bool {
|
||||
return opcodeFreqs[opcodes[i]] > opcodeFreqs[opcodes[j]]
|
||||
})
|
||||
|
||||
log.Printf("%d instructions executed in %s %d/µs", total, end, total/int(end.Microseconds()))
|
||||
log.Print("Top Instructions:")
|
||||
for _, opcode := range opcodes[:min(len(opcodes), 10)] {
|
||||
log.Printf("%10d %s", opcodeFreqs[opcode], opcode)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for err == nil {
|
||||
op = vm.currentFrame().ReadNextOp()
|
||||
|
||||
if vm.debug {
|
||||
opcodeFreqs[op]++
|
||||
log.Printf(
|
||||
"%-25s %-20s\n",
|
||||
fmt.Sprintf("%04d %s", vm.currentFrame().ip, op),
|
||||
fmt.Sprintf(
|
||||
"[ip=%02d fp=%02d, sp=%02d]",
|
||||
vm.currentFrame().ip, vm.fp-1, vm.sp,
|
||||
),
|
||||
)
|
||||
}
|
||||
op := vm.currentFrame().ReadNextOp()
|
||||
|
||||
switch op {
|
||||
case code.OpConstant:
|
||||
@@ -837,11 +783,11 @@ func (vm *VM) Run() (err error) {
|
||||
err = vm.push(object.FALSE)
|
||||
|
||||
case code.OpJump:
|
||||
pos := int(vm.currentFrame().ReadUint16())
|
||||
pos := vm.currentFrame().ReadUint16()
|
||||
vm.currentFrame().SetIP(pos)
|
||||
|
||||
case code.OpJumpNotTruthy:
|
||||
pos := int(vm.currentFrame().ReadUint16())
|
||||
pos := vm.currentFrame().ReadUint16()
|
||||
if !isTruthy(vm.pop()) {
|
||||
vm.currentFrame().SetIP(pos)
|
||||
}
|
||||
@@ -979,14 +925,7 @@ func (vm *VM) Run() (err error) {
|
||||
default:
|
||||
err = fmt.Errorf("unhandled opcode: %s", op)
|
||||
}
|
||||
|
||||
if vm.trace {
|
||||
log.Printf(
|
||||
"%-25s [ip=%02d fp=%02d, sp=%02d]",
|
||||
"", vm.currentFrame().ip, vm.fp-1, vm.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
@@ -713,12 +713,10 @@ func TestBuiltinFunctions(t *testing.T) {
|
||||
{`len("hello world")`, 11},
|
||||
{
|
||||
`len(1)`,
|
||||
&object.Error{
|
||||
Message: "TypeError: object of type 'int' has no len()",
|
||||
},
|
||||
fmt.Errorf("TypeError: object of type 'int' has no len()"),
|
||||
},
|
||||
{`len("one", "two")`,
|
||||
&object.Error{
|
||||
object.Error{
|
||||
Message: "TypeError: len() takes exactly 1 argument (2 given)",
|
||||
},
|
||||
},
|
||||
@@ -728,14 +726,14 @@ func TestBuiltinFunctions(t *testing.T) {
|
||||
{`first([1, 2, 3])`, 1},
|
||||
{`first([])`, object.NULL},
|
||||
{`first(1)`,
|
||||
&object.Error{
|
||||
object.Error{
|
||||
Message: "TypeError: first() expected argument #1 to be `array` got `int`",
|
||||
},
|
||||
},
|
||||
{`last([1, 2, 3])`, 3},
|
||||
{`last([])`, object.NULL},
|
||||
{`last(1)`,
|
||||
&object.Error{
|
||||
object.Error{
|
||||
Message: "TypeError: last() expected argument #1 to be `array` got `int`",
|
||||
},
|
||||
},
|
||||
@@ -743,12 +741,12 @@ func TestBuiltinFunctions(t *testing.T) {
|
||||
{`rest([])`, []int{}},
|
||||
{`push([], 1)`, []int{1}},
|
||||
{`push(1, 1)`,
|
||||
&object.Error{
|
||||
object.Error{
|
||||
Message: "TypeError: push() expected argument #1 to be `array` got `int`",
|
||||
},
|
||||
},
|
||||
{`input()`, ""},
|
||||
{`pop([])`, &object.Error{
|
||||
{`pop([])`, object.Error{
|
||||
Message: "IndexError: pop from an empty array",
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user