Felix Programming Language

flx now with stupid inner functions!

posted on September 25, 2009 - 11:20 PM PDT by Erick Tryzelaar

It's been quite some time since I last posted, but I've been pretty busy. I only wasted 2 days this time wandering down the depths of the felix type system and name binding... shudder. A couple nice things this time around. flxc now partially uses the language independent frontend for symbol lowering. None of the optimizations yet though. Also managed to get trivial non-closured inner functions working, as demonstrated here:

type int = "%i32";
typedef bool = 2;
fun add : int*int -> int = "%add";
fun eq : int*int -> bool = "%eq";
fun lnot : bool -> bool = "%lnot";
proc exit : int = "exit";

fun foo (x:int) = {
  val y = x + 1;
  fun bar (x:int) = {
    if x == 1 do
      return y;
    else
      return 3;
    done;
  }
  return bar y;
}

exit $ foo 2;

This now generates this code:

declare void @exit(i32)

define i32 @foo(i32 %x) {
entry:
  %foo.y = alloca i32                             ; <i32*> [#uses=2]
  %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, 1                              ; <i32> [#uses=1]
  store i32 %1, i32* %foo.y
  %2 = load i32* %foo.y                           ; <i32> [#uses=1]
  %3 = call i32 @foo.bar(i32 %2)                  ; <i32> [#uses=1]
  ret i32 %3
}

define i32 @foo.bar(i32 %x) {
entry:
  %foo.bar.x = alloca i32                         ; <i32*> [#uses=2]
  store i32 %x, i32* %foo.bar.x
  %0 = load i32* %foo.bar.x                       ; <i32> [#uses=1]
  %1 = icmp eq i32 %0, 1                          ; <i1> [#uses=1]
  %2 = icmp eq i1 %1, false                       ; <i1> [#uses=1]
  br i1 %2, label %_ifdoend_1, label %else

_ifdoend_1:                                       ; preds = %entry
  ret i32 3

else:                                             ; preds = %entry
  ret i32 2
}

define void @0() {
entry:
  %0 = call i32 @foo(i32 2)                       ; <i32> [#uses=1]
  call void @exit(i32 %0)
  ret void
}

And if you optimize it with flxc -O 1 ..., then you'll get:

declare void @exit(i32)

define i32 @foo(i32 %x) {
entry:
  %0 = add i32 %x, 1                              ; <i32> [#uses=1]
  %1 = call i32 @foo.bar(i32 %0)                  ; <i32> [#uses=1]
  ret i32 %1
}

define i32 @foo.bar(i32 %x) {
entry:
  %0 = icmp eq i32 %x, 1                          ; <i1> [#uses=1]
  %retval = select i1 %0, i32 2, i32 3            ; <i32> [#uses=1]
  ret i32 %retval
}

define void @0() {
entry:
  %0 = call i32 @foo(i32 2)                       ; <i32> [#uses=1]
  call void @exit(i32 %0)
  ret void
}

Which looks surprisingly readable. You'll also notice that we're prepending the parent's name in the llvm symbol name, so it should hopefully help with debugging.

read comments