summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Vertes <marc.vertes@tendermint.com>2023-07-24 14:20:45 +0200
committerGitHub <noreply@github.com>2023-07-24 14:20:45 +0200
commit355750be61fbf4b90d132a9560e01113f22f4c38 (patch)
treea63850914481d2450e20604424dd25a0d97ba8a5
parentbf32256ff3014543ef5dda69c4ee1e94d01361fe (diff)
vm1: add file pos for debug and a few immediate instructions (#4)
* vm1: add file pos for debug and a few immediate instructions `op[0]` is now reserved for storing the file position computed at code compiling. No impact on performances. Added `Subi` and `Infi` which use integer immediate argument instead of stack. Experimental. Improves `fib()` speed by ~ 20%. * vm1: add benchmark as proposed by @ajnavarro
-rw-r--r--cmd/gint/main.go39
-rw-r--r--vm1/vm.go53
-rw-r--r--vm1/vm_test.go105
3 files changed, 144 insertions, 53 deletions
diff --git a/cmd/gint/main.go b/cmd/gint/main.go
index 44f59ac..b36eb5d 100644
--- a/cmd/gint/main.go
+++ b/cmd/gint/main.go
@@ -1,11 +1,15 @@
package main
import (
+ "fmt"
"log"
"os"
+ "github.com/gnolang/parscan/codegen"
"github.com/gnolang/parscan/lang/golang"
+ "github.com/gnolang/parscan/parser"
"github.com/gnolang/parscan/vm0"
+ "github.com/gnolang/parscan/vm1"
)
func main() {
@@ -14,12 +18,18 @@ func main() {
if err != nil {
log.Fatal(err)
}
- if err := runSrc(string(buf)); err != nil {
- log.Fatal(err)
+ if len(os.Args) > 1 {
+ if err := run1(string(buf)); err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ if err := run0(string(buf)); err != nil {
+ log.Fatal(err)
+ }
}
}
-func runSrc(src string) error {
+func run0(src string) error {
i := vm0.New(golang.GoParser)
nodes, err := i.Parse(src)
if err != nil {
@@ -33,3 +43,26 @@ func runSrc(src string) error {
}
return nil
}
+
+func run1(src string) (err error) {
+ m := &vm1.Machine{}
+ c := &codegen.Compiler{Symbols: map[string]int{}}
+ c.AddSym(fmt.Println, "println")
+ n := &parser.Node{}
+ if n.Child, err = golang.GoParser.Parse(src); err != nil {
+ return err
+ }
+ n.Dot(os.Getenv("DOT"), "")
+ if err = c.CodeGen(n); err != nil {
+ return err
+ }
+ c.Emit(n, vm1.Exit)
+ log.Println("data:", c.Data)
+ log.Println("code:", vm1.Disas(c.Code))
+ for _, v := range c.Data {
+ m.Push(v)
+ }
+ m.PushCode(c.Code)
+ m.Run()
+ return
+}
diff --git a/vm1/vm.go b/vm1/vm.go
index 5cea5cf..152fba4 100644
--- a/vm1/vm.go
+++ b/vm1/vm.go
@@ -21,10 +21,12 @@ const (
Jump // -- ; ip += $1
JumpTrue // cond -- ; if cond { ip += $1 }
Lower // n1 n2 -- cond ; cond = n1 < n2
+ Loweri // n1 -- cond ; cond = n1 < $1
Pop // v --
Push // -- v
Return // [r1 .. ri] -- ; exit frame: sp = fp, fp = pop
Sub // n1 n2 -- diff ; diff = n1 - n2
+ Subi // n1 -- diff ; diff = n1 - $1
)
var strop = [...]string{ // for VM tracing.
@@ -40,10 +42,12 @@ var strop = [...]string{ // for VM tracing.
Jump: "Jump",
JumpTrue: "JumpTrue",
Lower: "Lower",
+ Loweri: "Loweri",
Pop: "Pop",
Push: "Push",
Return: "Return",
Sub: "Sub",
+ Subi: "Subi",
}
// Machine represents a virtual machine.
@@ -61,18 +65,18 @@ func (m *Machine) Run() {
defer func() { m.mem, m.ip, m.fp = mem, ip, fp }()
trace := func() {
- var op1 string
- if len(code[ip]) > 1 {
- op1 = strconv.Itoa(int(code[ip][1]))
+ var op2 string
+ if len(code[ip]) > 2 {
+ op2 = strconv.Itoa(int(code[ip][2]))
}
- fmt.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-8s %-4s] mem:%v\n", ip, sp, fp, strop[code[ip][0]], op1, mem)
+ fmt.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-8s %-4s] mem:%v\n", ip, sp, fp, strop[code[ip][1]], op2, mem)
}
_ = trace
for {
sp = len(mem) // stack pointer
trace()
- switch op := code[ip]; op[0] { // TODO: op[0] will contain file pos ?
+ switch op := code[ip]; op[1] {
case Add:
mem[sp-2] = mem[sp-2].(int) + mem[sp-1].(int)
mem = mem[:sp-1]
@@ -81,53 +85,58 @@ func (m *Machine) Run() {
mem = mem[:sp-1]
case Call:
mem = append(mem, ip+1)
- ip += int(op[1])
+ ip += int(op[2])
continue
case CallX: // Should be made optional.
- in := make([]reflect.Value, int(op[1]))
+ l := int(op[2])
+ in := make([]reflect.Value, l)
for i := range in {
- in[i] = reflect.ValueOf(mem[sp-1-i])
+ in[l-1-i] = reflect.ValueOf(mem[sp-1-i])
}
- f := reflect.ValueOf(mem[sp-len(in)-1])
- mem = mem[:sp-len(in)-1]
+ f := reflect.ValueOf(mem[sp-l-1])
+ mem = mem[:sp-l-1]
for _, v := range f.Call(in) {
mem = append(mem, v.Interface())
}
case Dup:
- mem = append(mem, mem[int(op[1])])
+ mem = append(mem, mem[int(op[2])])
case Enter:
mem = append(mem, fp)
fp = sp + 1
case Exit:
return
case Fdup:
- mem = append(mem, mem[int(op[1])+fp-1])
+ mem = append(mem, mem[int(op[2])+fp-1])
case Jump:
- ip += int(op[1])
+ ip += int(op[2])
continue
case JumpTrue:
cond := mem[sp-1].(bool)
mem = mem[:sp-1]
if cond {
- ip += int(op[1])
+ ip += int(op[2])
continue
}
case Lower:
mem[sp-2] = mem[sp-2].(int) < mem[sp-1].(int)
mem = mem[:sp-1]
+ case Loweri:
+ mem[sp-1] = mem[sp-1].(int) < int(op[2])
case Pop:
mem = mem[:sp-1]
case Push:
- mem = append(mem, int(op[1]))
+ mem = append(mem, int(op[2]))
case Return:
ip = mem[fp-2].(int)
ofp := fp
fp = mem[fp-1].(int)
- mem = append(mem[:ofp-int(op[1])-2], mem[sp-int(op[1]):]...)
+ mem = append(mem[:ofp-int(op[2])-2], mem[sp-int(op[2]):]...)
continue
case Sub:
mem[sp-2] = mem[sp-2].(int) - mem[sp-1].(int)
mem = mem[:sp-1]
+ case Subi:
+ mem[sp-1] = mem[sp-1].(int) - int(op[2])
}
ip++
}
@@ -141,3 +150,15 @@ func (m *Machine) PushCode(code [][]int64) (p int) {
func (m *Machine) SetIP(ip int) { m.ip = ip }
func (m *Machine) Push(v any) (l int) { l = len(m.mem); m.mem = append(m.mem, v); return }
func (m *Machine) Pop() (v any) { l := len(m.mem) - 1; v = m.mem[l]; m.mem = m.mem[:l]; return }
+
+// Disas returns the code as a readable string.
+func Disas(code [][]int64) (asm string) {
+ for _, op := range code {
+ if len(op) > 2 {
+ asm += fmt.Sprintf("%s %d\n", strop[op[1]], op[2])
+ } else {
+ asm += strop[op[1]] + "\n"
+ }
+ }
+ return asm
+}
diff --git a/vm1/vm_test.go b/vm1/vm_test.go
index 33ace00..1268bba 100644
--- a/vm1/vm_test.go
+++ b/vm1/vm_test.go
@@ -23,6 +23,21 @@ func TestVM(t *testing.T) {
}
}
+func BenchmarkVM(b *testing.B) {
+ for _, test := range tests {
+ b.Run("", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ m := &Machine{}
+ m.PushCode(test.code)
+ b.StartTimer()
+
+ m.Run()
+ }
+ })
+ }
+}
+
var tests = []struct {
sym []any // initial memory values
code [][]int64 // bytecode to execute
@@ -30,53 +45,75 @@ var tests = []struct {
mem string // expected memory content
}{{ // #00 -- A simple addition.
code: [][]int64{
- {Push, 1},
- {Push, 2},
- {Add},
- {Exit},
+ {0, Push, 1},
+ {0, Push, 2},
+ {0, Add},
+ {0, Exit},
},
start: 0, end: 1, mem: "[3]",
}, { // #01 -- Calling a function defined outside the VM.
sym: []any{fmt.Println, "Hello"},
code: [][]int64{
- {CallX, 1},
- {Exit},
+ {0, CallX, 1},
+ {0, Exit},
},
start: 0, end: 2, mem: "[6 <nil>]",
}, { // #02 -- Defining and calling a function in VM.
code: [][]int64{
- {Jump, 4}, // 0
- {Enter}, // 1
- {Push, 3}, // 2
- {Return, 1}, // 3
- {Push, 1}, // 4
- {Call, -4}, // 5
- {Exit}, // 6
+ {0, Jump, 4}, // 0
+ {0, Enter}, // 1
+ {0, Push, 3}, // 2
+ {0, Return, 1}, // 3
+ {0, Push, 1}, // 4
+ {0, Call, -4}, // 5
+ {0, Exit}, // 6
},
start: 0, end: 1, mem: "[3]",
}, { // #03 -- Fibonacci numbers, hand written. Showcase recursivity.
code: [][]int64{
- {Jump, 18}, // 0, goto 18
- {Enter}, // 1,
- {Fdup, -2}, // 2, [i]
- {Push, 2}, // 3, [i 2]
- {Lower}, // 4, [true/false]
- {JumpTrue, 11}, // 5, [], goto 16
- {Fdup, -2}, // 6 [i]
- {Push, 2}, // 7 [i 2]
- {Sub}, // 8 [(i-2)]
- {Call, -8}, // 9 [fib(i-2)]
- {Fdup, -2}, // 10 [fib(i-2) i]
- {Push, 1}, // 11 [(i-2) i 1]
- {Sub}, // 12 [(i-2) (i-1)]
- {Call, -12}, // 13 [fib(i-2) fib(i-1)]
- {Add}, // 14 [fib(i-2)+fib(i-1)]
- {Return, 1}, // 15 return i
- {Fdup, -2}, // 16 [i]
- {Return, 1}, // 17 return i
- {Push, 6}, // 18 [1]
- {Call, -18}, // 19 [fib(*1)]
- {Exit}, // 20
+ {0, Jump, 18}, // 0, goto 18
+ {0, Enter}, // 1,
+ {0, Fdup, -2}, // 2, [i]
+ {0, Push, 2}, // 3, [i 2]
+ {0, Lower}, // 4, [true/false]
+ {0, JumpTrue, 11}, // 5, [], goto 16
+ {0, Fdup, -2}, // 6 [i]
+ {0, Push, 2}, // 7 [i 2]
+ {0, Sub}, // 8 [(i-2)]
+ {0, Call, -8}, // 9 [fib(i-2)]
+ {0, Fdup, -2}, // 10 [fib(i-2) i]
+ {0, Push, 1}, // 11 [(i-2) i 1]
+ {0, Sub}, // 12 [(i-2) (i-1)]
+ {0, Call, -12}, // 13 [fib(i-2) fib(i-1)]
+ {0, Add}, // 14 [fib(i-2)+fib(i-1)]
+ {0, Return, 1}, // 15 return i
+ {0, Fdup, -2}, // 16 [i]
+ {0, Return, 1}, // 17 return i
+ {0, Push, 6}, // 18 [1]
+ {0, Call, -18}, // 19 [fib(*1)]
+ {0, Exit}, // 20
+ },
+ start: 0, end: 1, mem: "[8]",
+}, { // #04 -- Fibonacci with some immediate instructions.
+ code: [][]int64{
+ {0, Jump, 15}, // 0, goto 15
+ {0, Enter}, // 1,
+ {0, Fdup, -2}, // 2, [i]
+ {0, Loweri, 2}, // 3, [true/false]
+ {0, JumpTrue, 9}, // 4, [], goto 13
+ {0, Fdup, -2}, // 5 [i]
+ {0, Subi, 2}, // 6 [(i-2)]
+ {0, Call, -6}, // 7 [fib(i-2)]
+ {0, Fdup, -2}, // 8 [fib(i-2) i]
+ {0, Subi, 1}, // 9 [(i-2) (i-1)]
+ {0, Call, -9}, // 10 [fib(i-2) fib(i-1)], call 1
+ {0, Add}, // 11 [fib(i-2)+fib(i-1)]
+ {0, Return, 1}, // 12 return i
+ {0, Fdup, -2}, // 13 [i]
+ {0, Return, 1}, // 14 return i
+ {0, Push, 6}, // 15 [1]
+ {0, Call, -15}, // 16 [fib(*1)], call 1
+ {0, Exit}, // 17
},
start: 0, end: 1, mem: "[8]",
}}