Felix Programming Language

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...

read comments