-
Notifications
You must be signed in to change notification settings - Fork 21
Getting Started
Caveat: Do not trust AVR-LLVM to generate correct, unbroken, and working output files. It is a work in progress, and in using it, it will more than likely bite off your ear. Be warned.
Note: Bug #15 causes compilation failure unless optimisations are turned on. The llc
tool has optimisations on by default, but clang
does not. Pass the -O
flag to clang
in the meantime, until this bug is fixed.
LLVM is a compiler middle end and backend.
A compiler can usually be generalised into three stages:
|-----------| |-------------| |-------------|
| | | Middle End | | Back End |
| Front End | ---> | (Optimizer) | ---> | (Generates |
| (Parser) | | | | code for a |
|-----------| |-------------| | real CPU) |
|-------------|
The front end parses a specific programming language. For one end to interface with each another, they must use a common language. A naive way of implementing this would be to write a middle end and a backend for each front end, i.e programming language. Concretely, the C++ parser gives parsed C++ code to the middle end, when then optimises it into simpler, faster C++, and the backend then converts it to machine code.
This does not scale well. Rewriiting two thirds of the code for each source language would be a nightmare - imagine GCC, with the dozen or so programming languages it supports - that's the bulk of the code duplicated 12 times!
Most compiler authors solve this the same way that most computer scientists solve problems - by simply adding a layer of abstraction.
LLVM defines its own programming language for interoperation between the various ends of the compiler. This language is called LLVM Intermediate Representation (LLVM IR). LLVM IR looks quite a bit like assembly language, but it is designed to work independent of any CPU or programming language.
This way, each front end has the job of converting its source language into LLVM IR, and then passing it to the middle end which then optimises it, and the backend when then outputs some target-specific file. This allows us to write one middle end for all programming languages. Because of this, we only need one middle end and one backend -- these should then be able to work with any programming language, providing that it can be converted to IR.
Due to all of this saved time, we can place much more time and effort on creating a good middle and back end. The LLVM optimiser is normally on par with GCC's, and every now and then comes up on top. LLVM is a high quality compiler library, and due to this, and the ease of use, any language can use it to produce high-quality, fast, and reliable code.
Of course, if you are reading this, you probably already know all of this.
Once IR is simplified and transformed in the middle end, it is then the back end's job to convert into assembly or machine code. AVR-LLVM simply adds another backend to LLVM - one that can lower IR into assembler code for the AVR architecure.
LLVM understands only LLVM IR. Thus, to use it, you must supply it with LLVM IR.
You can use various existing frontends in order to generate IR. If you already have LLVM's clang
C/C++ front end installed, it probably doesn't include the AVR backend.
There are two solutions
Because IR is independent of language or CPU, we can use a regular, stock version of clang
to create it.
For clang, it is possible to output IR as a text file using the following command:
# convert program.c into LLVM IR
clang -S -emit-llvm program.c -o program.ll
We then have an IR file which is semantically equivalent to the original program.
We can then pass this file onto the IR compiler bundled with AVR-LLVM:
# compile program.ll into an AVR ELF object file
llc program.ll -o program.o -arch=avr -filetype=obj
# compile program.ll into a textual AVR assembly file
llc program.ll -o program.o -arch=avr -filetype=asm
NOTE: Make sure to use the llc
executable that you compiled from AVR-LLVM - accidentally using the LLVM install already on your system will cause an "unknown architecture: avr" error.
Alternatively, you can compile a frontend, using AVR-LLVM instead of a stock version of LLVM. However, the frontend will probably require a few (small) changes.
We have a clang
fork which supports AVR, located here.
Almost mirroring the regular clang
build instructions (barring a few path changes), you must first put the files in place:
# if you have not yet cloned AVR-LLVM, do so now
git clone https://github.com/avr-llvm/llvm.git
# clone 4ntoine's clang fork into `tools/clang`
cd llvm/tools
git clone https://github.com/avr-llvm/clang.git
cd ../
# clone compiler-rt into `projects/compiler-rt`
cd projects/
git clone https://github.com/avr-llvm/compiler-rt.git
cd ../
It is possible to compile with CMake or configure
:
CMake:
# don't pollute the source with object files -
# place all output files in `llvm/build`
mkdir build
cd build/
# generate makefiles for LLVM and clang
cmake -G "Unix Makefiles" ../
# OR, don't compile LLVM for other targets - we only
# want to use the AVR backend. this saves us compile time.
cmake -G "Unix Makefiles" -DLLVM_TARGETS_TO_BUILD="AVR" ../
# compile LLVM and clang
make
./configure
:
# don't pollute the source with object files -
# place all output files in `llvm/build`
mkdir build
cd build/
# generate makefiles for LLVM and clang
../configure --target=avr-none
# OR, don't compile LLVM for other targets - we only
# want to use the AVR backend. this saves us compile time.
../configure --enable-targets=avr
# compile LLVM and clang
make
The binaries should then be located in llvm/build/bin/
.