:::felix
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:
:::llvm
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:
:::llvm
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.
>
> 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*.
Yep, it's quite tricky and also somewhat complicated and messy.
The basic idea is simple enough: evaluate everything by recursive
descent,
with tracing the descent to stop recursions. Recursions usually trigger
a hard to decipher error, although occasionally they're legal (eg
recursive
types). It's tricky because at the point recursions are detected it
isn't known which is which, so an exception is thrown and is trapped
if the recursion is legal.
It is also trapped if it is NOT legal, in order to give a sane error
message. This means one has to decide where in the descent
to detect recursions AND where to trap them. There are always
two choices with different boundary conditions: when you have
possibly_recurive_call()
you can either flag and trap the recursion at the call point
or on entry to the function.
The type system is further complicated by an incomplete
and broken meta-typing system. However it can't be got
rid of because type functions really exist, and get overloaded,
similarly, type constructors.
The overload resolution algorithm is also quite hard. Quite apart
from choosing a matching overload, it has to chose the best match
when there are several (most specialised) and also has to
discard candidates which don't meet constraints. As output
it not only returns the chosen function, it also has to return
any type variable bindings.
--
john skaller
ska...@users.sourceforge.net
They're not stupid IMHO. A version of Felix generating LLVM which
does not support closures is still immensely useful.
It is, after all, just as powerful as C but better typed.
--
john skaller
ska...@users.sourceforge.net
True :) Hopefully I'll get closures soon though. I got the frontend
working, so I got all the functions marked if they need stack or heap
closures, now I just got to actually implement them.
Of course now felix keeps optimizing away my closure tests :)
My plan is to just create an explicit structure to store my stack
frame, and use the llvm trampoline intrinsic [1] to hide the pointer
to it in the call to the inner function. Hopefully it won't be too
difficult if I'm ignoring the issue of garbage collection.
> On Sat, Sep 26, 2009 at 5:17 AM, john skaller
> <ska...@users.sourceforge.net> wrote:
>>
>> They're not stupid IMHO. A version of Felix generating LLVM which
>> does not support closures is still immensely useful.
>>
>> It is, after all, just as powerful as C but better typed.
>
> True :) Hopefully I'll get closures soon though. I got the frontend
> working, so I got all the functions marked if they need stack or heap
> closures, now I just got to actually implement them.
>
> Of course now felix keeps optimizing away my closure tests :)
Run the standard test suite. This will fail things that need library
support. This is a good opportunity to split the tests into those
that require library (and/or flx-run) support and those that don't.
--
john skaller
ska...@users.sourceforge.net
I'm almost ready to do it. I think I just need to get polymorphism
working so things can print out their test msgs. Oh and function
pointers. I just got c-strings working, so there's not much left :)