flx now with stupid inner functions!

0 views
Skip to first unread message

Erick Tryzelaar

unread,
Sep 26, 2009, 2:20:35 AM9/26/09
to felix-l...@googlegroups.com, felix-l...@lists.sourceforge.net
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:

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

john skaller

unread,
Sep 26, 2009, 8:14:31 AM9/26/09
to felix-l...@googlegroups.com, felix-l...@lists.sourceforge.net

On 26/09/2009, at 4:20 PM, Erick Tryzelaar wrote:

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


john skaller

unread,
Sep 26, 2009, 8:17:10 AM9/26/09
to Erick Tryzelaar, felix-l...@googlegroups.com, felix-l...@lists.sourceforge.net

On 26/09/2009, at 4:20 PM, Erick Tryzelaar 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.

--
john skaller
ska...@users.sourceforge.net


Erick Tryzelaar

unread,
Sep 26, 2009, 9:58:13 PM9/26/09
to felix-l...@googlegroups.com, felix-l...@lists.sourceforge.net
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 :)

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.

[1]: http://llvm.org/docs/LangRef.html#int_trampoline

john skaller

unread,
Sep 28, 2009, 3:09:35 AM9/28/09
to Erick Tryzelaar, felix-l...@googlegroups.com, felix-l...@lists.sourceforge.net

On 27/09/2009, at 11:58 AM, Erick Tryzelaar wrote:

> 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


Erick Tryzelaar

unread,
Sep 28, 2009, 5:05:22 AM9/28/09
to felix-l...@googlegroups.com, felix-l...@lists.sourceforge.net
On Mon, Sep 28, 2009 at 12:09 AM, john skaller
<ska...@users.sourceforge.net> wrote:
>
> 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.

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 :)

Reply all
Reply to author
Forward
0 new messages