Skip to content
This repository was archived by the owner on Sep 2, 2018. It is now read-only.

Getting Started

Dylan McKay edited this page Feb 17, 2015 · 21 revisions

Getting started with AVR-LLVM

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.

What does LLVM do?

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.

What does AVR-LLVM add?

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.

How do I use it?

LLVM understands only LLVM IR. Thus, to use it, you must supply it with LLVM IR.

Using a frontend like clang

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

Generating IR and manually passing it to AVR-LLVM

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.

Compiling a front end to use AVR-LLVM

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/.

Clone this wiki locally