Installing LLVM and GHC on 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.