Installing LLVM and GHC on ARM

| tagged with
  • ghc
  • llvm
  • arm

Recently GHC’s LLVM backend gained registerized build support for ARM. This could result in as much as a doubling of performance on ARM, meaning Haskell might finally be a first-class citizen on this wonderful architecture. If you are as excited about this as me, you’ll want to try it out. This is what I did.

This was done on a BeagleBoard XM running Linaro’s Ubuntu Natty distribution. Be warned that both LLVM and GHC’s builds can require large amounts of memory at times. This can be very taxing on the system and can be quite slow. Swap was necessary at some points. Be prepared to wait the better part of a day as the build proceeds.

Prerequisites

We’ll first need a few packages,

$ sudo apt-get install build-essential groff happy alex

Since LLVM and GHC builds can be quite large, I’m going to be doing everything in /mnt/data/ghc,

$ GHCROOT=/mnt/data/ghc

LLVM

First fetching LLVM,

$ cd $GHCROOT
$ git clone http://llvm.org/git/llvm.git
$ cd llvm

If you want a C/C++ compiler out of the deal as well, you can clone clang into tools/. Note that clang seems to be even worse than LLVM itself in terms of demand on the system. While my BeagleBoard XM (512MB RAM) made it through most of the build, it ultimately ran out of memory while linking the clang executable. If you need a C compiler, you should consider following the LLVM cross-compiling instructions also posted here unless you have a large amount of memory and lots of time.

$ cd tools
$ git clone http://llvm.org/git/clang.git
$ cd ..

Additionally, GHC requires a patch adding its calling convention to LLVM. The patch will require a bit of minor forward porting,

$ wget http://www.gardas.roznovan.cz/llvm/llvm-2011-07-12.patch
$ cd llvm
$ patch -p1 < ../llvm-2011-07-12.patch

Linking C++ with binutils’ default linker, ld, is quite memory intensive (especially the clang executable, for some reason). The build process can be made substantially faster by instead using gold,

$ mkdir $GHCROOT/gold-bin
$ ln -s `which gold` $GHCROOT/gold-bin/ld
$ PATH=$GHCROOT/gold-bin:$PATH

Building is straightforward,

$ ./configure --prefix=$GHCROOT/usr
$ make
$ sudo make install

It’s never a bad idea to run make check as well. In my case (commit 1c36ba50ac7fa2c3e531b3f48407fb2eee93e5ed), this resulted in 12 unexpected errors,

********************
Testing Time: 2886.24s
********************
Failing Tests (12):
    LLVM :: CodeGen/X86/2009-06-05-VariableIndexInsert.ll
    LLVM :: CodeGen/X86/tail-call-got.ll
    LLVM :: ExecutionEngine/2002-12-16-ArgTest.ll
    LLVM :: ExecutionEngine/test-fp.ll
    LLVM-Unit :: ExecutionEngine/JIT/Debug+Asserts/JITTests/JIT.GlobalInFunction
    LLVM-Unit :: ExecutionEngine/JIT/Debug+Asserts/JITTests/JITTest.AvailableExternallyGlobalIsntEmitted
    LLVM-Unit :: ExecutionEngine/JIT/Debug+Asserts/JITTests/JITTest.EscapedLazyStubStillCallable
    LLVM-Unit :: ExecutionEngine/JIT/Debug+Asserts/JITTests/JITTest.FunctionPointersOutliveTheirCreator
    LLVM-Unit :: ExecutionEngine/JIT/Debug+Asserts/JITTests/LazyLoadedJITTest.MaterializableAvailableExternallyFunctionIsntCompiled
    LLVM-Unit :: ExecutionEngine/JIT/Debug+Asserts/JITTests/MultiJitTest.EagerMode
    LLVM-Unit :: ExecutionEngine/JIT/Debug+Asserts/JITTests/MultiJitTest.JitPool
    LLVM-Unit :: ExecutionEngine/JIT/Debug+Asserts/JITTests/MultiJitTest.LazyMode

  Expected Passes    : 5508
  Expected Failures  : 70
  Unsupported Tests  : 28
  Unexpected Failures: 12

An aside: libc++

I’ve started using clang as my primary compiler on my ARM box due to its speed and very modest memory requirements while compiling highly templated C++. Unfortunately, it doesn’t seem particularly happy using GCC’s libstdc++. Thankfully, the LLVM project has also produced its own new C++ standard library implementation, libc++. Building the library is quite straightforward,

$ cd $GHCROOT
$ git clone http://llvm.org/git/libcxx.git
$ cd libcxx/lib
$ ./buildit

Installation is quite straightforward,

$ sudo ln -sf $GHCROOT/libcxx/lib/libc++.so.1.0 /usr/lib/libc++.so.1.0
$ sudo ln -sf libc++.so.1.0 /usr/lib/libc++.so.1
$ sudo ln -sf libc++.so.1 /usr/lib/libc++.so
$ sudo ln -sf $GHCROOT/libcxx/include /usr/include/c++/v1

Of course, we need to test it before use,

$ cd ../test
$ ./testit

To use libc++ with clang, simply say the magic word,

$ clang++ -stdlib=libc++ test.cpp

GHC

First we’ll fetch a few of the prerequisites for building GHC,

$ sudo apt-get install happy alex

Of course, we’ll also need our new LLVM build in PATH,

$ PATH=$GHCROOT/root/bin:$PATH

Fetching GHC and beginning the bootstrap process,

$ cd $GHCROOT
$ git clone http://darcs.haskell.org/ghc.git
$ cd ghc
$ ./sync-all --no-dph get
$ ./boot

It is important to note that by default this will build the master branch. This is where the bleeding edge of GHC’s development takes place. For a slightly more conservative build, one might consider building the next release branch (e.g. ghc-7.4 at time of writing; depending upon timing this may or may not be more stable than master). Either way, it is critically important to ensure that the ghc repository and those of the stage 1 libraries checked out by sync-all are on the same branch. Thankfully, sync-all will take care of this for you. For instance, to build the ghc-7.4 branch,

$ ./sync-all checkout ghc-7.4

Note that due to (bug #5105)[http://hackage.haskell.org/trac/ghc/ticket/5105] we have disabled Data Parallel Haskell.

Like many compilers, GHC’s build is a multi-stage process. First, a stage 1 compiler will be built using the stage 0 compiler already installed on the system (e.g. the ghc6 debian package). After this, a stage 2 compiler will be built using this stage 1 compiler. The stage 2 compiler is the end result of the build.

The build requires a small bit of configuration,

$ cp mk/build.mk.sample mk/build.mk
$ edit mk/build.mk

Now edit mk/config.mk, I chose the quick build flavor. We need to adjust the build method to account for the fact that the stage 0 compiler we will be using does not have a native code generator (since none was available in the 6.12 series). In lieu of the native code generator, we will tell GHC to compile the stage 1 compile into C, which will then be compiled to machine code by gcc. To accomplish this, modify the following values,

  • GhcStage1HcOpts from -fasm to -fvia-C
  • GhcStage2HcOpts from -fasm to -fllvm
  • SRC_HC_OPTS remove -fasm (not quite sure what this should be but this works)

Additionally, if you are planning on hacking on the tree, you may want to follow some of the recommendations at http://hackage.haskell.org/trac/ghc/wiki/Building/Using to speed up the build process. The build then proceeds as usual,

$ ./configure --prefix=$GHCROOT/usr
$ make
$ sudo make install

Test

As with any piece of software under development, it is always a good idea to test,

$ ./sync-all --testsuite get
$ make test

Enjoy

Enjoy your shiny new compiler,

$ PATH=$GHCROOT/bin:$PATH
$ LD_LIBRARY_PATH=$GHCROOT/lib:$LD_LIBRARY_PATH
$ ghc --info

Updating

To update,

$ cd $GHCROOT/ghc
$ ./sync-all --no-dph pull

Make sure your previously built compiler is not in $PATH or else configure will complain. You’ll still want to make sure that LLVM is in $PATH however. We’ll just add the build directory to accomplish this,

$ PATH=$GHCROOT/llvm/Debug+Asserts/bin:$PATH
$ make

Cleaning

In the unfortunate event that your build tree is somehow put into a funky state, you’ll need to clean it,

$ make clean

However, if one of the phases hasn’t been completed the build system will try to finish building it before cleaning. I have yet to figure out how to work around this.