tail call optimization
This commit is contained in:
parent
d870ebf72e
commit
b2a29342b1
3 changed files with 92 additions and 72 deletions
9
vm/vm.go
9
vm/vm.go
|
@ -880,9 +880,9 @@ func (v *VM) callFunction(fn *objects.CompiledFunction, freeVars []*objects.Obje
|
|||
|
||||
// check if this is a tail-call (recursive call right before return)
|
||||
curFrame := &(v.frames[v.framesIndex-1])
|
||||
if fn == curFrame.fn {
|
||||
if fn == curFrame.fn { // recursion
|
||||
nextOp := compiler.Opcode(curFrame.fn.Instructions[curFrame.ip+1])
|
||||
if nextOp == compiler.OpReturnValue ||
|
||||
if nextOp == compiler.OpReturnValue || // tail call
|
||||
(nextOp == compiler.OpPop &&
|
||||
compiler.OpReturn == compiler.Opcode(curFrame.fn.Instructions[curFrame.ip+2])) {
|
||||
|
||||
|
@ -904,10 +904,7 @@ func (v *VM) callFunction(fn *objects.CompiledFunction, freeVars []*objects.Obje
|
|||
// | ARG1 | <- BP for current function
|
||||
// |--------|
|
||||
|
||||
for i := 0; i < numArgs; i++ {
|
||||
v.stack[curFrame.basePointer+i] = v.stack[v.sp-numArgs+i]
|
||||
}
|
||||
|
||||
copy(v.stack[curFrame.basePointer:], v.stack[v.sp-numArgs:v.sp])
|
||||
v.sp -= numArgs
|
||||
curFrame.ip = -1
|
||||
|
||||
|
|
|
@ -194,19 +194,34 @@ out = outer() + g
|
|||
out = f2();
|
||||
`, 99)
|
||||
|
||||
// fibonacci
|
||||
// recursion
|
||||
expect(t, `
|
||||
fib := func(x) {
|
||||
if(x == 0) {
|
||||
return 0;
|
||||
if x == 0 {
|
||||
return 0
|
||||
} else if x == 1 {
|
||||
return 1
|
||||
} else {
|
||||
if(x == 1) {
|
||||
return 1;
|
||||
} else {
|
||||
return fib(x-1) + fib(x-2);
|
||||
}
|
||||
return fib(x-1) + fib(x-2)
|
||||
}
|
||||
};
|
||||
out = fib(15);
|
||||
`, 610)
|
||||
}
|
||||
out = fib(15)`, 610)
|
||||
|
||||
// TODO: currently recursion inside the local scope function definition is not supported.
|
||||
// Workaround is to define the identifier first then assign the function like below.
|
||||
// Want to fix this.
|
||||
expect(t, `
|
||||
func() {
|
||||
fib := 0
|
||||
fib = func(x) {
|
||||
if x == 0 {
|
||||
return 0
|
||||
} else if x == 1 {
|
||||
return 1
|
||||
} else {
|
||||
return fib(x-1) + fib(x-2)
|
||||
}
|
||||
}
|
||||
out = fib(15)
|
||||
}()`, 610)
|
||||
}
|
||||
|
|
|
@ -4,74 +4,82 @@ import "testing"
|
|||
|
||||
func TestTailCall(t *testing.T) {
|
||||
expect(t, `
|
||||
fac := func(n, a) {
|
||||
if n == 1 {
|
||||
return a
|
||||
fac := func(n, a) {
|
||||
if n == 1 {
|
||||
return a
|
||||
}
|
||||
return fac(n-1, n*a)
|
||||
}
|
||||
return fac(n-1, n*a)
|
||||
}
|
||||
out = fac(5, 1)`, 120)
|
||||
out = fac(5, 1)`, 120)
|
||||
|
||||
expect(t, `
|
||||
fac := func(n, a) {
|
||||
if n == 1 {
|
||||
return a
|
||||
fac := func(n, a) {
|
||||
if n == 1 {
|
||||
return a
|
||||
}
|
||||
x := {foo: fac} // indirection for test
|
||||
return x.foo(n-1, n*a)
|
||||
}
|
||||
x := {foo: fac} // indirection for test
|
||||
return x.foo(n-1, n*a)
|
||||
}
|
||||
out = fac(5, 1)`, 120)
|
||||
out = fac(5, 1)`, 120)
|
||||
|
||||
expect(t, `
|
||||
fib := func(x, s) {
|
||||
if x == 0 {
|
||||
return 0 + s
|
||||
} else if x == 1 {
|
||||
return 1 + s
|
||||
fib := func(x, s) {
|
||||
if x == 0 {
|
||||
return 0 + s
|
||||
} else if x == 1 {
|
||||
return 1 + s
|
||||
}
|
||||
return fib(x-1, fib(x-2, s))
|
||||
}
|
||||
return fib(x-1, fib(x-2, s))
|
||||
}
|
||||
out = fib(15, 0)`, 610)
|
||||
out = fib(15, 0)`, 610)
|
||||
|
||||
expect(t, `
|
||||
fib := func(n, a, b) {
|
||||
if n == 0 {
|
||||
return a
|
||||
} else if n == 1 {
|
||||
return b
|
||||
}
|
||||
return fib(n-1, b, a + b)
|
||||
}
|
||||
out = fib(15, 0, 1)`, 610)
|
||||
|
||||
// global variable and no return value
|
||||
expect(t, `
|
||||
out = 0
|
||||
foo := func(a) {
|
||||
if a == 0 {
|
||||
return
|
||||
}
|
||||
out += a
|
||||
foo(a-1)
|
||||
}
|
||||
foo(10)`, 55)
|
||||
out = 0
|
||||
foo := func(a) {
|
||||
if a == 0 {
|
||||
return
|
||||
}
|
||||
out += a
|
||||
foo(a-1)
|
||||
}
|
||||
foo(10)`, 55)
|
||||
|
||||
// TODO: see comment inside the code
|
||||
expect(t, `
|
||||
f1 := func() {
|
||||
f2 := undefined // TODO: this is really inconvenient
|
||||
f2 = func(n, s) {
|
||||
if n == 0 { return s }
|
||||
return f2(n-1, n + s)
|
||||
f1 := func() {
|
||||
f2 := 0 // TODO: this might be fixed in the future
|
||||
f2 = func(n, s) {
|
||||
if n == 0 { return s }
|
||||
return f2(n-1, n + s)
|
||||
}
|
||||
return f2(5, 0)
|
||||
}
|
||||
|
||||
return f2(5, 0)
|
||||
out = f1()`, 15)
|
||||
}
|
||||
|
||||
out = f1()`, 15)
|
||||
|
||||
// tail call with free vars
|
||||
// expect(t, `
|
||||
//f1 := func() {
|
||||
// a := 10
|
||||
// f2 := undefined
|
||||
// f2 = func(n, s) {
|
||||
// a += s
|
||||
// if n == 0 {
|
||||
// return
|
||||
// }
|
||||
// f2(n-1, n + s)
|
||||
// }
|
||||
// return f2
|
||||
//}
|
||||
//out = f1()(5, 0)`, 25)
|
||||
// tail call with free vars
|
||||
func TestTailCallFreeVars(t *testing.T) {
|
||||
expect(t, `
|
||||
func() {
|
||||
a := 10
|
||||
f2 := 0
|
||||
f2 = func(n, s) {
|
||||
if n == 0 {
|
||||
return s + a
|
||||
}
|
||||
return f2(n-1, n+s)
|
||||
}
|
||||
out = f2(5, 0)
|
||||
}()`, 25)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue