Felix Programming Language

Latest Posts

FBuild 0.2

posted on August 30, 2010 - 10:19 AM PDT by Erick Tryzelaar
filed under: FBuild

I'm pleased to announce FBuild version 0.2. It's been way too long since the last FBuild release, but a lot of features have been added over the past year. Overall, the core concept of FBuild is still the same, but there have been some structural changes in response to some suggestions I've had.

The most obvious change is that there is now a context object that you must pass around in order to interact with cacheable functions. Now, to write a simple builder, you must do:

import fbuild.builders.c

def build(ctx):
    shared = fbuild.builders.c.guess_shared(ctx)
    shared.build_exe('foo', ['foo.c', 'bar.c'])

Next is the support for command line targets. This allows you to easily call a specifically marked function, as in:

import fbuild.builders.ocaml
def build(ctx):
    ocamlc = fbuild.builders.ocaml.Ocamlc(ctx)
    ocamlc.build_exe('foo', ['foo.ml'])

import fbuild.target
@fbuild.target.register(name='run-test', help='run the foo test')
def run_test(ctx):
    # make sure 'foo' is built
    build(ctx)
    ctx.execute([ctx.buildroot / 'foo', 'world'])
    ctx.execute([ctx.buildroot / 'foo', 'fbuild'])

Which can be run by:

% fbuild run-test
looking for program ocamlc.opt : ok /opt/local/bin/ocamlc.opt
looking for program ocamldep.opt : ok /opt/local/bin/ocamldep.opt
determining platform             : {'bsd', 'darwin', 'macosx', 'posix'}
checking if ocamlc.opt can make objects : ok
checking if ocamlc.opt can make libraries: ok
checking if ocamlc.opt can make exes    : ok
checking if ocamlc.opt can link lib to exe: ok
 * ocamldep.opt                         : foo.ml
 * ocamlc.opt                           : build/foo.ml -> build/foo.cmo
 * ocamlc.opt                           : build/foo.cmo -> build/foo
Hello world
Hello fbuild!

One nice feature of the targets is that they are automatically integrated into the "--help" option:

% fbuild -h
Usage: fbuild-light [options] target1 [target2 target3 ...]

...

Targets:
  build     
  run-test  run the foo test

Next up is another removal of magic, this time in fbuild.config.c. Now, if you want to define a config test that requires a header, you must explicitly define it yourself, as in:

import fbuild.config.c as c
class assert_h(c.Test):
    header = c.header_test('assert.h')

Finally, the last major addition was support for Linux's LD_LIBRARY_PATH and OS X's DYLD_LIBRARY_PATH for shared libraries. Now it's much easier to execute test code that depends on a shared library. To use it, just do:

ctx.execute(cmd, runtime_libpaths=['lib1', 'lib2'])

Beyond that, we added builders for ghc, avr-gcc, gcc/iOS, O'Caml Findlib, and O'Caml Batteries. For configuration support, we now can check for SDL, LLVM, GNU Readline, GNU GMP, Google Test Framework, and OpenSSL. And, of course, lots of miscellaneous bug fixes. For a more detailed breakdown, please look at our git history.

Oh and one last thing. FBuild-0.2's build/ directory is incompatible with FBuild-0.1's build/ directory, so you'll have to remove the old directory before you can compile.

Please let us know if you run into any problems or need any help. You can:

  • File a bug at http://github.com/erickt/fbuild/issues
  • Contact our mailing list at http://groups.google.com/group/fbuild
  • Or find us on IRC on freenode.net in #felix

read comments

Transitioning over to github.

posted on August 16, 2010 - 12:05 PM PDT by Erick Tryzelaar
filed under: FBuild, Felix

I just wanted to let everyone know that I'm transitioning the hosting of felix and fbuild over to github. You can find the following repositories over there:

I'll still be maintaining the http://git.felix-lang.org mirrors, but I'm considering github to be the primary location to get the code, as well as for bug tickets and etc.

read comments

We finally own #felix!

posted on August 16, 2010 - 11:20 AM PDT by Erick Tryzelaar
filed under: FBuild, Felix

Hi all, long time no see! I just wanted to say that we now officially own #felix for all your felix and fbuild related stuff. So hop on by if you want to chat :)

read comments

Example python extension builder

posted on December 30, 2009 - 02:03 PM PST by Erick Tryzelaar
filed under: FBuild

Holger on the fbuild mailing list was trying to use fbuild to create python c extensions, so I mocked up a simple builder for him. There's a little work looking up the include directory, and I'm hardcoding the gcc option "-undefined dynamic_lookup", but once done it's pretty simple:

import fbuild.builders.c
import fbuild.db
import fbuild.path

@fbuild.db.caches
def python_c_builder(ctx, python='python'):
    """Creates and returns a python C extension builder."""

    # If we didn't explicitly chose which version of python to use, search the
    # environment for python.
    python = fbuild.builders.find_program(ctx, [python])

    # Create a helper program that calls out to python to get the include and
    # lib directories.
    stdout, stderr = ctx.execute([python, '-c',
        'import distutils.sysconfig; '
        'print(distutils.sysconfig.get_python_inc())'],
        quieter=1)
    includepath = stdout.decode().strip()

    # Return a fully specified python c extension builder.
    return fbuild.builders.c.guess_shared(ctx,
        includes=[includepath],
        flags=['-undefined', 'dynamic_lookup'],
        lib_prefix='',
        lib_suffix='.so')

def build(ctx):
    for python in ('python', 'python2.5', 'python2.6', 'python3.1'):
        # Configure the python.
        shared = python_c_builder(ctx, python)

        # Copy the src file so we don't have collisions.
        fbuild.path.Path('build/%s' % python).makedirs()
        fbuild.path.Path('spam.c').copy('build/%s' % python)

        # Build the extension module.
        shared.build_lib('%s/spam' % python, ['build/%s/spam.c' % python])

        # Test if we can actually import the module.
        ctx.execute([python, '-c', 'import spam; spam.system("echo hello world!")'],
            cwd='build/%s' % python)

Which results in:

looking for program python : ok /opt/local/bin/python
determining platform       : {'bsd', 'darwin', 'macosx', 'posix'}
looking for program gcc    : ok /usr/bin/gcc
checking gcc               : ok
checking gcc with -g       : ok
checking gcc with -O2      : ok
checking gcc with -undefined dynamic_lookup -fPIC: ok
checking gcc with -undefined dynamic_lookup -dynamiclib: ok
checking gcc with -undefined dynamic_lookup: ok
checking if gcc -undefined dynamic_lookup -fPIC can make objects: ok
checking if gcc -undefined dynamic_lookup -fPIC can make libraries: ok
checking if gcc -undefined dynamic_lookup -fPIC can make exes: ok
checking if gcc -undefined dynamic_lookup -fPIC can link lib to exe: ok
 * gcc -undefined dynamic_lookup -fPIC  : build/python/spam.c -> build/python/spam.os
 * gcc -undefined dynamic_lookup -dynamiclib: build/python/spam.os -> build/python/spam.so
hello world!
looking for program python2.5           : ok /opt/local/bin/python2.5
checking gcc                            : ok
checking gcc with -g                    : ok
checking gcc with -O2                   : ok
checking gcc with -undefined dynamic_lookup -fPIC: ok
checking gcc with -undefined dynamic_lookup -dynamiclib: ok
checking gcc with -undefined dynamic_lookup: ok
checking if gcc -undefined dynamic_lookup -fPIC can make objects: ok
checking if gcc -undefined dynamic_lookup -fPIC can make libraries: ok
checking if gcc -undefined dynamic_lookup -fPIC can make exes: ok
checking if gcc -undefined dynamic_lookup -fPIC can link lib to exe: ok
 * gcc -undefined dynamic_lookup -fPIC  : build/python2.5/spam.c -> build/python2.5/spam.os
 * gcc -undefined dynamic_lookup -dynamiclib: build/python2.5/spam.os -> build/python2.5/spam.so
hello world!
looking for program python2.6           : ok /opt/local/bin/python2.6
 * gcc -undefined dynamic_lookup -fPIC  : build/python2.6/spam.c -> build/python2.6/spam.os
 * gcc -undefined dynamic_lookup -dynamiclib: build/python2.6/spam.os -> build/python2.6/spam.so
hello world!
looking for program python3.1           : ok /opt/local/bin/python3.1
checking gcc                            : ok
checking gcc with -g                    : ok
checking gcc with -O2                   : ok
checking gcc with -undefined dynamic_lookup -fPIC: ok
checking gcc with -undefined dynamic_lookup -dynamiclib: ok
checking gcc with -undefined dynamic_lookup: ok
checking if gcc -undefined dynamic_lookup -fPIC can make objects: ok
checking if gcc -undefined dynamic_lookup -fPIC can make libraries: ok
checking if gcc -undefined dynamic_lookup -fPIC can make exes: ok
checking if gcc -undefined dynamic_lookup -fPIC can link lib to exe: ok
 * gcc -undefined dynamic_lookup -fPIC  : build/python3.1/spam.c -> build/python3.1/spam.os
 * gcc -undefined dynamic_lookup -dynamiclib: build/python3.1/spam.os -> build/python3.1/spam.so
hello world!

I should be able to get a prototype builder out soon.

read comments

Why I find felix interesting

posted on September 28, 2009 - 12:53 PM PDT by Erick Tryzelaar

This is a repost from a reddit that's about why I find neat about felix. Maybe this will inspire someone to try it out some time :)


So felix has a couple nice things going for it. The main things I find interesting is the concurrency model, the dynamic syntax, the type system, c++ integration. That space is getting a bit busy, with Scala probably being the closest relative, but I think there's still plenty of room for exploration.

My personal favorite thing is our concurrency model. Similar to Erlang and Scala, we've got an actor-ish concurrency model with user space fibers that communicate through messages. We call them fthreads. It's a little lower level than those languages though, we only provide synchronous no-copy message passing. I'm planning on layering a mailbox layer on top of that to add asynchronous messaging though.

We've also got an async io library that's intended to work seamlessly with fthreads. The intention is that you should be able to write standard blocking batch-style code and let the compiler/runtime convert your code into event driven code. We've got a prototype web server that's built on top of this, but haven't gotten too far with it yet though.

Next up our dynamic syntax. Felix's grammar is essentially defined at runtime. We have a basic shim grammar that lets us load up a grammar specification at runtime to parse the rest of the language. This then gets compiled in memory to scheme, which then gets compiled to the ast for code generation. The neat thing with this is that it allows you to have scoped syntax extensions. This could allow for a lot of neat lisp-style macro metaprogramming, or even theoretically allow you to write, say, a python-compatible grammar and let felix handle the rest of the language.

We've got a pretty advanced type system. It's a static language with support for both ad-hoc polymorphism and haskell-style typeclasses. Since the grammar's dynamic, we don't really have hardcoded concepts of types like ints. They're specified in the standard library. So that means we can't really have the compiler know that 1+0 can be optimized down to 1. So we've got hooks in order to specify the reductions like this. Similarly, we tell the compiler axioms about types, like this type is symmetric, and that one is associative. This then can be outputted to the proof assistant generator Why in order to help prove things about your program, though I don't know too much about how that works.

Finally, the c++ integration. Our main backend is c++ source. This lets us directly support binding to c++ libraries without needing to write external shim bindings. For instance, we use this and typeclasses to directly expose a good portion of STL to felix. We'll see if I can figure out how to integrate this with my LLVM backend though. Maybe once Clang gets c++ working we could somehow use that to generate the right code for us.

read comments

Hello Felix!

posted on September 28, 2009 - 02:10 AM PDT by Erick Tryzelaar
filed under: Felix, LLVM

Basic pointer types and c strings are now working!

type char = "%i8";
typedef charp = &char;    
proc puts : charp = "puts";

puts c"Hello world!";

Generates:

@0 = internal global [13 x i8] c"Hello world!\00" ; <[13 x i8]*> [#uses=1]

declare void @puts(i8*)

define void @1() {
entry:
  call void @puts(i8* getelementptr inbounds ([13 x i8]* @0, i32 0, i32 0))
  ret void
}

And prints out:

Hello World!

read comments

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

tail calls, baby!

posted on September 26, 2009 - 05:51 PM PDT by Erick Tryzelaar

Check this out! I just got the standard felix optimizations working.

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

fun foo (x:int) = {
  if x == 0 do
    return 10;
  done;

  return foo (x - 1);
}

exit $ foo 2;

Which compiles down to this code:

declare void @exit(i32)

define i32 @foo(i32 %x) {
entry:
  %foo.x = alloca i32                             ; <i32*> [#uses=4]
  store i32 %x, i32* %foo.x
  br label %start_21

start_21:                                         ; preds = %_ifdoend_1, %entry
  %0 = load i32* %foo.x                           ; <i32> [#uses=1]
  %1 = icmp eq i32 %0, 0                          ; <i1> [#uses=1]
  %2 = icmp eq i1 %1, false                       ; <i1> [#uses=1]
  br i1 %2, label %_ifdoend_1, label %else

_ifdoend_1:                                       ; preds = %start_21
  %3 = load i32* %foo.x                           ; <i32> [#uses=1]
  %4 = sub i32 %3, 1                              ; <i32> [#uses=1]
  store i32 %4, i32* %foo.x
  br label %start_21

else:                                             ; preds = %start_21
  ret i32 10
}

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

read comments

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

flxc is slightly more than useless with conditional branching

posted on September 16, 2009 - 12:33 AM PDT by Erick Tryzelaar

Just got basic conditional branching working. With this code:

>>> 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) = {
...  if x == 1 do
...    return 2;
...  else
...    return 3;
...  done;
... }
>>> exit $ foo 1;

flxc will generate and execute this code:

declare void @exit(i32)

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

The program will exit with 2. It'll even return 3 if you call foo with 2. Unfortunately, branching only works inside functions.

read comments

flxc can now JIT the most useless program ever!

posted on September 15, 2009 - 02:27 AM PDT by Erick Tryzelaar

I finally got the llvm jit working with felix. Here's the proof. With this input:

type int = "%i32";
typedef array[t,n] = t ^ n;
fun add : int*int -> int = "%add";
fun subscript[t,n] : array[t,n] * int -> t = "%subscript";
proc exit : int = "exit";

var x = 5, 6;
var y = x.[1] + 1;
y = y + 3;
val z = x.[0], y;
exit (z.[0] + z.[1]);

Generates this llvm module and executes each numbered function at a time:

@x = weak_odr global [2 x i32] undef              ; <[2 x i32]*> [#uses=2]
@y = weak_odr global i32 undef                    ; <i32*> [#uses=4]
@z = weak_odr global [2 x i32] undef              ; <[2 x i32]*> [#uses=2]

declare void @exit(i32)

define void @0() {
entry:
  store i32 5, i32* getelementptr inbounds ([2 x i32]* @x, i32 0, i32 0)
  store i32 6, i32* getelementptr inbounds ([2 x i32]* @x, i32 0, i32 1)
  ret void
}

define void @1() {
entry:
  %0 = load i32* getelementptr inbounds ([2 x i32]* @x, i32 0, i32 1) ; <i32> [#uses=1]
  %1 = add i32 %0, 1                              ; <i32> [#uses=1]
  store i32 %1, i32* @y
  ret void
}

define void @2() {
entry:
  %y = load i32* @y                               ; <i32> [#uses=1]
  %0 = add i32 %y, 3                              ; <i32> [#uses=1]
  store i32 %0, i32* @y
  ret void
}

define void @3() {
entry:
  %0 = load i32* getelementptr inbounds ([2 x i32]* @x, i32 0, i32 0) ; <i32> [#uses=1]
  store i32 %0, i32* getelementptr inbounds ([2 x i32]* @z, i32 0, i32 0)
  %y = load i32* @y                               ; <i32> [#uses=1]
  store i32 %y, i32* getelementptr inbounds ([2 x i32]* @z, i32 0, i32 1)
  ret void
}

define void @4() {
entry:
  %0 = load i32* getelementptr inbounds ([2 x i32]* @z, i32 0, i32 0) ; <i32> [#uses=1]
  %1 = load i32* getelementptr inbounds ([2 x i32]* @z, i32 0, i32 1) ; <i32> [#uses=1]
  %2 = add i32 %1, %0                             ; <i32> [#uses=1]
  call void @exit(i32 %2)
  ret void
}

If everything works out, the program should exit with the returncode 15. You'll need a properly configured and built llvm from svn to get this to work though.

read comments

LLVM code generator support for flxc

posted on August 27, 2009 - 04:03 PM PDT by Erick Tryzelaar
filed under: Felix, LLVM

I've just committed a LLVM code generator for flxc.

#!build/debug/bin/flxc -I build/debug/lib --import nugram.flxh --import flx.flxh
>>> type int = "int";
... BOUND SYM:     type int<3> = "int";
>>> fun add : int*int -> int = "%add";
... BOUND SYM:     fun add<4>: int<3>^2 -> int<3> = "%add";
>>> fun foo (a:int, b:int, c:int) = { val d = a + b; return d + 1; }
... BOUND SYM:     fun foo<5>(val a<8>: int<3>,val b<9>: int<3>,val c<10>: int<3>): int<3>{
...   d<7> := (add<4> (a<8>, b<9>));
...   return (add<4> (d<7>, 1));}
>>> val x = 1;
... BOUND SYM:     val x<11>: int<3>;
... BOUND EXE:     x<11> := 1;
>>> val y = 2;
... BOUND SYM:     val y<12>: int<3>;
... BOUND EXE:     y<12> := 2;
>>> val z = foo (x, y, 3);
... BOUND SYM:     val z<13>: int<3>;
... BOUND EXE:     z<13> := (foo<5> (x<11>, y<12>, 3));
>>> ^D

And here's what it generates:

define void @__init__() {
entry:
  %z = alloca i32                                 ; <i32*> [#uses=1]
  %y = alloca i32                                 ; <i32*> [#uses=1]
  %x = alloca i32                                 ; <i32*> [#uses=1]
  store i32 1, i32* %x
  store i32 2, i32* %y
  %foo = call i32 @foo(i32 1, i32 2, i32 3)       ; <i32> [#uses=1]
  store i32 %foo, i32* %z
  ret void
}

define i32 @foo(i32 %a, i32 %b, i32 %c) {
entry:
  %d = alloca i32                                 ; <i32*> [#uses=1]
  %add = add i32 %a, %b                           ; <i32> [#uses=2]
  store i32 %add, i32* %d
  %add1 = add i32 %add, 1                         ; <i32> [#uses=1]
  ret i32 %add1
}

Of course it doesn't actually execute the commands yet, but the output's so pretty!

read comments

FBuild now has better autoconf-esque substitution

posted on August 23, 2009 - 04:50 PM PDT by Erick Tryzelaar
filed under: FBuild

bohan on reddit pointed out that my text.m4_substitute doesn't actually have anything to do with m4. So I replaced it with two autoconf inspired functions: text.autoconfig_config_file and text.autoconfig_config_header. Consider this source:

const char* foo = @foo@;
#undef HAVE_FOO
#undef HAVE_BAR
#undef HAVE_BAZ

If you use this function:

text.autoconf_config_file(ctx, 'foo.py', 'foo.h.in', {
    'foo': '"FOO"',
})

which replicates the functionality of AC_CONFIG_FILES, you'll get:

const char* foo = "FOO";
#undef HAVE_FOO
#undef HAVE_BAR
#undef HAVE_BAZ

And if you process it with this:

text.autoconf_config_header(ctx, 'baz.h', 'baz.h.in', {
    'foo': '"FOO"',
    'HAVE_FOO': 1,
    'HAVE_BAR': 0,
    'HAVE_BAZ': '"BAZ"',
})

which replicates AC_CONFIG_HEADERS, you'll get:

const char* foo = "FOO";
#define HAVE_FOO 1
/* #undef HAVE_BAR */
#define HAVE_BAZ "BAZ"

read comments

FBuild now has no global state!

posted on August 23, 2009 - 02:40 AM PDT by Erick Tryzelaar
filed under: FBuild

Well that wasn't that terrible. I've removed all the global state from fbuild. Now a context object is passed around that contains this information. Now you must write your build system like:

import fbuild.db
import fbuild.builders.c

@fbuild.db.caches
def foo(ctx, value):
    ctx.logger.log('got: %s' % value)
    return value

def build(ctx);
    foo(ctx, 1)

    static = fbuild.builders.c.guess_static(ctx)
    static.build_exe('foo', ['a.c', 'b.c'])

As you can see, you now must pass the context around as the first argument to any cached function or object.

read comments

Future FBuild Projects

posted on August 22, 2009 - 04:00 PM PDT by Erick Tryzelaar

I've been participating in a lengthy build system discussion on reddit, which has inspired me to start sketching out my ideas for FBuild:

New scheduler: I'm not too happy with the current scheduler, as I had to introduce a timeout on checking if there are done tasks to work around a flaw with python's threadsafe queue structure. It's possible for a KeyboardInterrupt to happen in between incrementing/decrementing the semaphore, and this was leading to a deadlock that needs a "kill -9" to exit. There has to be a better way to do this. I'm experimenting with multiprocessing to see if that'll be faster. In order to do that though we're going to have to implement our own pickling/unpickling of MethodType, and possibly other things too.

A new database: FBuild's a bit faster with Python 3.1's new io module, but it may be even faster if we could use a SQLite database.

Get rid of the global state: For ease of implementation, FBuild has a couple global state objects: the scheduler, the database, the logger, and the commandline options. We could fold all of these into some context object and pass it around. This would mean you'd have to write code like this though:

def build(ctx):
    static = fbuild.builders.guess_static(ctx)

Simplify the builders more: webon on reddit pointed out that the ar builder is a bit more complicated than it needs to be. Part of the reason for this is that we're writing the option list twice. First at the builder level, second at the file level. Perhaps we could simplify this by doing something like:

class Ar(fbuild.builders.Builder):
    options = {
        flags: list,
        libs: list,
        libpaths: list,
        ...
    }

    def __init__(self, exe='ar', flags=['-rc'], **kwargs):
        ...
        super().__init__(flags=flags, **kwargs)

    def __call__(self, dst, srcs, **kwargs):
        options = self.process_options(**kwargs)
        cmd = [self.exe]
        cmd.extend('-l' + lib for lib in options.libs)
        ...

Where the superclass would somehow process the option list and automatically merge the options without us needing to copy the code everywhere all the time.

Speed up FBuild: bohan on the fbuild mailing list pointed me to a benchmark he wrote to test out his build system, wonderbuild. FBuild is beating SCons and Autotools in many tests, but I think we could to a bunch better. For instance, is there any way we could get the number of system calls down?

Finish support for avr-gcc and haskell

Optionally turn of md5ing of files: It'd be pretty easy to add a flag to fbuild.path.Path to turn off using md5 for file digesting. This should help folks like bohan who are willing to sacrifice doing rebuilds of files in order to have a faster build system.

edit: I forgot to include automatic threading. We could use futures transparently thread computation without needing to explicitly use scheduler.map. Unfortunately this can get a little complicated unless we could come up with a good way to use the futures without littering scheduler.future(f, *args, **kwargs) and scheduler.force everywhere.

edit 2: Some more projects:

targets: The idea is to have simple make-like targets, possibly something like:

@fbuild.target
def configure():
    ...

def build():
    conf = configure()
    ...

@fbuild.target
def test():
    builder = build()
    ...

installation: No ideas for that yet!

read comments