LLVM Clojure Bindings and Toy Language.
Clojure
Latest commit c338788 Jul 4, 2011 Jason Jackson ...
Failed to load latest commit information.
src refactoring Jun 14, 2011
README.org ... Jul 3, 2011
input.program refactoring Jun 14, 2011
pic.png refactoring Jun 14, 2011
project.clj refactoring Jun 14, 2011

README.org

What is this?

This is a compiler. When you run it, it compiles ‘input.program’ and produces a binary a.out.

The frontend is written in clojure, and the back-end is LLVM.

How to Run

  1. Install LLVM library.
  2. lein deps
  3. lein run

NOTE: The code assumes LLVM is at /opt/local/lib/libLLVM-2.9.dylib For different paths, update jna.library.path java property in native.clj.

Input Program

Here our toy language is used as input into compiler frontend.

func cos(Double) Double;
func printf(...) Integer;

func bar() Integer {
   printf("foo");
   return 3;
}

func main() Integer {
    printf("hello %d", 3);
    printf("bar ");
    bar();
    return 0;
}

Program Output

  1. The intermediate representation is printed.
  2. The object file and linked binary is created.
  3. Binary is executed, output is shown at bottom.

https://github.com/jasonjckn/llvm-clojure-bindings/raw/master/pic.png

How does it work?

  • parser.clj is for transforming Input=>AST (using clarsec, a combinator parser)
  • ast.clj is for transforming AST=>LLVM IR
  • main.clj is for LLVM IR=>object file=>linking.
  • llvm.clj contains needed llvm bindings for clojure.
  • native.clj is a thin JNA wrapper. It’s like clj-native, but less efficient. (I couldn’t get arrays of pointers working in clj-native.)

If input program is this:

func main() Integer {
    printf("foo");
    printf("hello");
    return 1;
}

AST looks like this:

((:func main [] Integer
        (:call printf "foo")
        (:call printf "hello")
        (:return 1)))

LLVM IR looks like this:

@0 = internal constant [4 x i8] c"foo\00"
@1 = internal constant [9 x i8] c"hello %d\00"

define i32 @main() {
  %1 = call i32 (...)* @printf([9 x i8]* @1, i32 3)
  %2 = call i32 (...)* @printf([5 x i8]* @2)
  ret i32 1
}

x86 Disassembly looks like this:

0000000100000ef0 <_main>:
   100000ef0:	50                   	push   %rax
   100000ef1:	48 8d 3d 3c 00 00 00 	lea    0x3c(%rip),%rdi
   100000ef8:	be 03 00 00 00       	mov    $0x3,%esi
   100000efd:	30 c0                	xor    %al,%al
   100000eff:	e8 26 00 00 00       	callq  100000f2a <_printf$stub>
   100000f04:	48 8d 3d 32 00 00 00 	lea    0x32(%rip),%rdi
   100000f0b:	30 c0                	xor    %al,%al
   100000f0d:	e8 18 00 00 00       	callq  100000f2a <_printf$stub>
   100000f12:	48 8d 3d 17 00 00 00 	lea    0x17(%rip),%rdi
   100000f19:	30 c0                	xor    %al,%al
   100000f1b:	e8 0a 00 00 00       	callq  100000f2a <_printf$stub>
   100000f20:	31 c0                	xor    %eax,%eax
   100000f22:	5a                   	pop    %rdx
   100000f23:	c3                   	retq