Closures!
posted on September 27, 2009 - 05:40 PM PDT by Erick Tryzelaar
flxc now has automatic stack closures! Take that, C99! Check this out!
type tiny = "%i8";
type int = "%i32";
typedef bool = 2;
fun add : int*int -> int = "%add";
fun sub : int*int -> int = "%sub";
fun eq : int*int -> bool = "%eq";
fun lnot : bool -> bool = "%lnot";
proc exit : int = "exit";
noinline fun foo (x:int) = {
val y = 6;
return x + y;
}
noinline proc fake_exit (x:int) {
exit x;
return;
}
noinline fun bar (x:int) = {
var y = 10;
noinline proc baz () {
y = 20;
return;
}
baz ();
return x + y;
}
noinline fun x (a:int, b:int, c:tiny) = {
val x1 = a;
val x2 = b;
val x3 = c;
noinline fun y (d:int, e:int, f:tiny) = {
val y1 = x1;
val y2 = x2;
val y3 = f;
noinline fun z (g:int, h:int, i:tiny) = {
val z1 = x1;
val z2 = x2;
val z3 = i;
return z1;
}
return z (y1,y2,y3);
}
return y (x1,x2,x3);
}
fake_exit $ (foo 2) + (bar 3) + (x (1,2,3t));
And the llvm source:
%0 = type { i32, i32 }
%1 = type { i32, i32, i32, i32, i8 }
%2 = type { i32, i32, i8 }
declare void @exit(i32)
define i32 @foo(i32 %x) {
entry:
%foo.x = alloca i32 ; <i32*> [#uses=2]
store i32 %x, i32* %foo.x
%0 = load i32* %foo.x ; <i32> [#uses=1]
%1 = add i32 %0, 6 ; <i32> [#uses=1]
ret i32 %1
}
define void @fake_exit(i32 %x) {
entry:
%fake_exit.x = alloca i32 ; <i32*> [#uses=2]
store i32 %x, i32* %fake_exit.x
%0 = load i32* %fake_exit.x ; <i32> [#uses=1]
call void @exit(i32 %0)
ret void
}
define i32 @bar(i32 %x) {
entry:
%bar-closure = alloca %0 ; <%0*> [#uses=3]
%bar.y = getelementptr %0* %bar-closure, i32 0, i32 0 ; <i32*> [#uses=2]
%bar.x = getelementptr %0* %bar-closure, i32 0, i32 1 ; <i32*> [#uses=2]
store i32 %x, i32* %bar.x
store i32 10, i32* %bar.y
call void @bar.baz(%0* %bar-closure)
%0 = load i32* %bar.x ; <i32> [#uses=1]
%1 = load i32* %bar.y ; <i32> [#uses=1]
%2 = add i32 %0, %1 ; <i32> [#uses=1]
ret i32 %2
}
define void @bar.baz(%0* %bar-closure) {
entry:
%bar.y = getelementptr %0* %bar-closure, i32 0, i32 0 ; <i32*> [#uses=1]
%bar.x = getelementptr %0* %bar-closure, i32 0, i32 1 ; <i32*> [#uses=0]
store i32 20, i32* %bar.y
ret void
}
define i32 @x(i32 %a, i32 %b, i8 %c) {
entry:
%x-closure = alloca %1 ; <%1*> [#uses=6]
%x.x1 = getelementptr %1* %x-closure, i32 0, i32 0 ; <i32*> [#uses=2]
%x.x2 = getelementptr %1* %x-closure, i32 0, i32 1 ; <i32*> [#uses=2]
%x.a = getelementptr %1* %x-closure, i32 0, i32 2 ; <i32*> [#uses=2]
%x.b = getelementptr %1* %x-closure, i32 0, i32 3 ; <i32*> [#uses=2]
%x.c = getelementptr %1* %x-closure, i32 0, i32 4 ; <i8*> [#uses=2]
store i32 %a, i32* %x.a
store i32 %b, i32* %x.b
store i8 %c, i8* %x.c
%0 = load i32* %x.a ; <i32> [#uses=1]
store i32 %0, i32* %x.x1
%1 = load i32* %x.b ; <i32> [#uses=1]
store i32 %1, i32* %x.x2
%2 = load i32* %x.x1 ; <i32> [#uses=1]
%3 = load i32* %x.x2 ; <i32> [#uses=1]
%4 = load i8* %x.c ; <i8> [#uses=1]
%5 = call i32 @x.y(%1* %x-closure, i32 %2, i32 %3, i8 %4) ; <i32> [#uses=1]
ret i32 %5
}
define i32 @x.y(%1* %x-closure, i32 %d, i32 %e, i8 %f) {
entry:
%x.x1 = getelementptr %1* %x-closure, i32 0, i32 0 ; <i32*> [#uses=1]
%x.x2 = getelementptr %1* %x-closure, i32 0, i32 1 ; <i32*> [#uses=1]
%x.a = getelementptr %1* %x-closure, i32 0, i32 2 ; <i32*> [#uses=0]
%x.b = getelementptr %1* %x-closure, i32 0, i32 3 ; <i32*> [#uses=0]
%x.c = getelementptr %1* %x-closure, i32 0, i32 4 ; <i8*> [#uses=0]
%x.y-closure = alloca %2 ; <%2*> [#uses=4]
%x.y.d = getelementptr %2* %x.y-closure, i32 0, i32 0 ; <i32*> [#uses=1]
%x.y.e = getelementptr %2* %x.y-closure, i32 0, i32 1 ; <i32*> [#uses=1]
%x.y.f = getelementptr %2* %x.y-closure, i32 0, i32 2 ; <i8*> [#uses=2]
store i32 %d, i32* %x.y.d
store i32 %e, i32* %x.y.e
store i8 %f, i8* %x.y.f
%0 = load i32* %x.x1 ; <i32> [#uses=1]
%1 = load i32* %x.x2 ; <i32> [#uses=1]
%2 = load i8* %x.y.f ; <i8> [#uses=1]
%3 = call i32 @x.y.z(%1* %x-closure, %2* %x.y-closure, i32 %0, i32 %1, i8 %2) ; <i32> [#uses=1]
ret i32 %3
}
define i32 @x.y.z(%1* %x-closure, %2* %x.y-closure, i32 %g, i32 %h, i8 %i) {
entry:
%x.x1 = getelementptr %1* %x-closure, i32 0, i32 0 ; <i32*> [#uses=1]
%x.x2 = getelementptr %1* %x-closure, i32 0, i32 1 ; <i32*> [#uses=0]
%x.a = getelementptr %1* %x-closure, i32 0, i32 2 ; <i32*> [#uses=0]
%x.b = getelementptr %1* %x-closure, i32 0, i32 3 ; <i32*> [#uses=0]
%x.c = getelementptr %1* %x-closure, i32 0, i32 4 ; <i8*> [#uses=0]
%x.y.d = getelementptr %2* %x.y-closure, i32 0, i32 0 ; <i32*> [#uses=0]
%x.y.e = getelementptr %2* %x.y-closure, i32 0, i32 1 ; <i32*> [#uses=0]
%x.y.f = getelementptr %2* %x.y-closure, i32 0, i32 2 ; <i8*> [#uses=0]
%x.y.z-closure = alloca %2 ; <%2*> [#uses=3]
%x.y.z.g = getelementptr %2* %x.y.z-closure, i32 0, i32 0 ; <i32*> [#uses=1]
%x.y.z.h = getelementptr %2* %x.y.z-closure, i32 0, i32 1 ; <i32*> [#uses=1]
%x.y.z.i = getelementptr %2* %x.y.z-closure, i32 0, i32 2 ; <i8*> [#uses=1]
store i32 %g, i32* %x.y.z.g
store i32 %h, i32* %x.y.z.h
store i8 %i, i8* %x.y.z.i
%0 = load i32* %x.x1 ; <i32> [#uses=1]
ret i32 %0
}
define void @0() {
entry:
%0 = call i32 @foo(i32 2) ; <i32> [#uses=1]
%1 = call i32 @bar(i32 3) ; <i32> [#uses=1]
%2 = add i32 %0, %1 ; <i32> [#uses=1]
%3 = call i32 @x(i32 1, i32 2, i8 3) ; <i32> [#uses=1]
%4 = add i32 %2, %3 ; <i32> [#uses=1]
call void @fake_exit(i32 %4)
ret void
}
What to work on next? Polymorphism, or strings? I am a bit tired relying on exit to tell me if the code's working or not. What to do, what to do...