summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Vertes <marc.vertes@tendermint.com>2023-08-24 09:32:36 +0200
committerGitHub <noreply@github.com>2023-08-24 09:32:36 +0200
commit00cd63b47c0c32be821f80e006ed08ce57020e15 (patch)
treef8952b26e6dd301dfc35df9e0537fb4f92856deb
parentc83cdc374f8d50189c2da3460a6db94a4527c1fc (diff)
vm1: improve function calling (#6)
The "Enter" instruction has been removed and the frame pointer is now saved by the "Call" instruction. The "Return" instruction now takes the number of function input parameters as the second operand. It's used to return the output values at the correct place in the caller frame, no matter the number of input parameters. The tests and the code generator have been updated accordingly.
-rw-r--r--.gitignore1
-rw-r--r--codegen/codegen.go8
-rw-r--r--codegen/codegen_test.go13
-rw-r--r--samples/fib2
-rw-r--r--vm1/vm.go31
-rw-r--r--vm1/vm_test.go89
6 files changed, 76 insertions, 68 deletions
diff --git a/.gitignore b/.gitignore
index 385d6a8..9529633 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
*.out
*.test
+gint
diff --git a/codegen/codegen.go b/codegen/codegen.go
index 048b20f..d7702cd 100644
--- a/codegen/codegen.go
+++ b/codegen/codegen.go
@@ -41,8 +41,7 @@ func (c *Compiler) CodeGen(node *parser.Node) (err error) {
switch n.Kind {
case parser.FuncDecl:
fname := n.Child[0].Content()
- i := c.Emit(n, vm1.Enter)
- c.AddSym(i, scope+fname, false)
+ c.AddSym(len(c.Code), scope+fname, false)
scope = pushScope(scope, fname)
frameNode = append(frameNode, n)
fnote = notes[n]
@@ -124,7 +123,8 @@ func (c *Compiler) CodeGen(node *parser.Node) (err error) {
}
case parser.ReturnStmt:
- c.Emit(n, vm1.Return, int64(len(n.Child)))
+ fun := frameNode[len(frameNode)-1]
+ c.Emit(n, vm1.Return, int64(len(n.Child)), int64(len(fun.Child[1].Child)))
case parser.StmtBloc:
nd.ipend = len(c.Code)
@@ -144,7 +144,7 @@ func (c *Compiler) CodeGen(node *parser.Node) (err error) {
// TODO: Fix this temporary hack to compute an entry point
if c.Entry < 0 && len(scope) == 0 && n.Kind != parser.FuncDecl {
c.Entry = len(c.Code) - 1
- if c.Code[c.Entry][1] == vm1.Return {
+ if c.Entry >= 0 && len(c.Code) > c.Entry && c.Code[c.Entry][1] == vm1.Return {
c.Entry++
}
}
diff --git a/codegen/codegen_test.go b/codegen/codegen_test.go
index d2f8033..7262ddc 100644
--- a/codegen/codegen_test.go
+++ b/codegen/codegen_test.go
@@ -16,7 +16,7 @@ func TestCodeGen(t *testing.T) {
test := test
t.Run("", func(t *testing.T) {
c := New()
- c.AddSym(fmt.Println, "println")
+ c.AddSym(fmt.Println, "println", false)
n := &parser.Node{}
var err error
if n.Child, err = golang.GoParser.Parse(test.src); err != nil {
@@ -31,6 +31,9 @@ func TestCodeGen(t *testing.T) {
}
t.Log("data:", c.Data)
t.Log("code:", vm1.Disassemble(c.Code))
+ if s := vm1.Disassemble(c.Code); s != test.asm {
+ t.Errorf("got error %#v, want error %#v", s, test.asm)
+ }
})
}
}
@@ -45,7 +48,11 @@ var tests = []struct {
asm: "Dup 0\nDup 1\nCallX 1\n",
}, { // #02
src: `a := 2; println(a)`,
- asm: "Push 2\nAssign 1\nDup 0\nDup 1\nCallX 1",
+ asm: "Push 2\nAssign 1\nDup 0\nDup 1\nCallX 1\n",
}, { // #03
src: `a := 2; if a < 3 {println(a)}; println("bye")`,
-}}
+ asm: "Push 2\nAssign 1\nDup 1\nPush 3\nLower\nJumpFalse 4\nDup 0\nDup 1\nCallX 1\nDup 0\nDup 2\nCallX 1\n",
+}, { // #04
+ src: "func add(a int, b int) int { return a + b }",
+ asm: "Fdup -2\nFdup -3\nAdd\nReturn 1 2\n",
+}, {}}
diff --git a/samples/fib b/samples/fib
index 03545d3..654c5c0 100644
--- a/samples/fib
+++ b/samples/fib
@@ -3,4 +3,4 @@ func fib(i int) int {
return fib(i-2) + fib(i-1)
}
-println(fib(5))
+println(fib(6))
diff --git a/vm1/vm.go b/vm1/vm.go
index 54e5223..6b74e7a 100644
--- a/vm1/vm.go
+++ b/vm1/vm.go
@@ -16,7 +16,6 @@ const (
CallX // f [a1 .. ai] -- [r1 .. rj] ; r1, ... = mem[f](a1, ...)
Dup // addr -- value ; value = mem[addr]
Fdup // addr -- value ; value = mem[addr]
- Enter // -- ; enter frame: push(fp), fp = sp
Exit // -- ;
Jump // -- ; ip += $1
JumpTrue // cond -- ; if cond { ip += $1 }
@@ -38,7 +37,6 @@ var strop = [...]string{ // for VM tracing.
CallX: "CallX",
Dup: "Dup",
Fdup: "Fdup",
- Enter: "Enter",
Exit: "Exit",
Jump: "Jump",
JumpTrue: "JumpTrue",
@@ -67,11 +65,15 @@ func (m *Machine) Run() {
defer func() { m.mem, m.ip, m.fp = mem, ip, fp }()
trace := func() {
- var op2 string
- if len(code[ip]) > 2 {
- op2 = strconv.Itoa(int(code[ip][2]))
+ var op2, op3 string
+ c := code[ip]
+ if l := len(c); l > 2 {
+ op2 = strconv.Itoa(int(c[2]))
+ if l > 3 {
+ op3 = strconv.Itoa(int(c[3]))
+ }
}
- fmt.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-9s %-4s] mem:%v\n", ip, sp, fp, strop[code[ip][1]], op2, mem)
+ fmt.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-9s %-4s %-4s] mem:%v\n", ip, sp, fp, strop[c[1]], op2, op3, mem)
}
_ = trace
@@ -86,7 +88,8 @@ func (m *Machine) Run() {
mem[op[2]] = mem[sp-1]
mem = mem[:sp-1]
case Call:
- mem = append(mem, ip+1)
+ mem = append(mem, ip+1, fp)
+ fp = sp + 2
ip += int(op[2])
continue
case CallX: // Should be made optional.
@@ -102,9 +105,6 @@ func (m *Machine) Run() {
}
case Dup:
mem = append(mem, mem[int(op[2])])
- case Enter:
- mem = append(mem, fp)
- fp = sp + 1
case Exit:
return
case Fdup:
@@ -139,7 +139,7 @@ func (m *Machine) Run() {
ip = mem[fp-2].(int)
ofp := fp
fp = mem[fp-1].(int)
- mem = append(mem[:ofp-int(op[2])-2], mem[sp-int(op[2]):]...)
+ mem = append(mem[:ofp-int(op[2])-int(op[3])-1], mem[sp-int(op[2]):]...)
continue
case Sub:
mem[sp-2] = mem[sp-2].(int) - mem[sp-1].(int)
@@ -164,10 +164,13 @@ func (m *Machine) Pop() (v any) { l := len(m.mem) - 1; v = m.mem[l]; m.mem
// Disassemble returns the code as a readable string.
func Disassemble(code [][]int64) (asm string) {
for _, op := range code {
- if len(op) > 2 {
- asm += fmt.Sprintf("%s %d\n", strop[op[1]], op[2])
- } else {
+ switch len(op) {
+ case 2:
asm += strop[op[1]] + "\n"
+ case 3:
+ asm += fmt.Sprintf("%s %d\n", strop[op[1]], op[2])
+ case 4:
+ asm += fmt.Sprintf("%s %d %d\n", strop[op[1]], op[2], op[3])
}
}
return asm
diff --git a/vm1/vm_test.go b/vm1/vm_test.go
index 53b0d07..cea4f29 100644
--- a/vm1/vm_test.go
+++ b/vm1/vm_test.go
@@ -62,60 +62,57 @@ var tests = []struct {
start: 0, end: 2, mem: "[6 <nil>]",
}, { // #02 -- Defining and calling a function in VM.
code: [][]int64{
- {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
+ {0, Jump, 3}, // 0
+ {0, Push, 3}, // 1
+ {0, Return, 1, 1}, // 2
+ {0, Push, 1}, // 3
+ {0, Call, -3}, // 4
+ {0, Exit}, // 5
},
start: 0, end: 1, mem: "[3]",
}, { // #03 -- Fibonacci numbers, hand written. Showcase recursivity.
code: [][]int64{
- {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
+ {0, Jump, 17}, // 0
+ {0, Fdup, -2}, // 1 [i]
+ {0, Push, 2}, // 2 [i 2]
+ {0, Lower}, // 3 [true/false]
+ {0, JumpTrue, 11}, // 4 [], goto 16
+ {0, Fdup, -2}, // 5 [i]
+ {0, Push, 2}, // 6 [i 2]
+ {0, Sub}, // 7 [(i-2)]
+ {0, Call, -7}, // 8 [fib(i-2)]
+ {0, Fdup, -2}, // 9 [fib(i-2) i]
+ {0, Push, 1}, // 10 [(i-2) i 1]
+ {0, Sub}, // 11 [(i-2) (i-1)]
+ {0, Call, -11}, // 12 [fib(i-2) fib(i-1)]
+ {0, Add}, // 13 [fib(i-2)+fib(i-1)]
+ {0, Return, 1, 1}, // 14 return i
+ {0, Fdup, -2}, // 15 [i]
+ {0, Return, 1, 1}, // 16 return i
+ {0, Push, 6}, // 17 [1]
+ {0, Call, -17}, // 18 [fib(*1)]
+ {0, Exit}, // 19
},
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
+ {0, Jump, 14}, // 0
+ {0, Fdup, -2}, // 1 [i]
+ {0, Loweri, 2}, // 2 [true/false]
+ {0, JumpTrue, 9}, // 3 [], goto 13
+ {0, Fdup, -2}, // 4 [i]
+ {0, Subi, 2}, // 5 [(i-2)]
+ {0, Call, -5}, // 6 [fib(i-2)]
+ {0, Fdup, -2}, // 7 [fib(i-2) i]
+ {0, Subi, 1}, // 8 [(i-2) (i-1)]
+ {0, Call, -8}, // 9 [fib(i-2) fib(i-1)], call 1
+ {0, Add}, // 10 [fib(i-2)+fib(i-1)]
+ {0, Return, 1, 1}, // 11 return i
+ {0, Fdup, -2}, // 12 [i]
+ {0, Return, 1, 1}, // 13 return i
+ {0, Push, 6}, // 14 [1]
+ {0, Call, -14}, // 15 [fib(*1)], call 1
+ {0, Exit}, // 16
},
start: 0, end: 1, mem: "[8]",
}}