LLVM backend for Faust


FAUST is a compiled language for real-time audio signal processing. The name FAUST stands for Functional AUdio STream. Its programming model combines two approaches : functional programming and block diagram composition. You can think of FAUST as a structured block diagram language with a textual syntax.

LLVM (Low Level Virtual Machine) and is a very interesting open-source project that provides a set of tools to develop compilers and related technologies. It provides a SSA based Internal Representation format, ObjectiveC/C/C++ front end, JIT, and various backends.

A new backend for Faust that directly produces LLVM IR (instead of the C++ class Faust currently produces) has been developed. With a (yet to come) library version of the Faust compiler, it will allow developers to embed Faust + LLVM JIT to dynamically define, compile on the fly and execute Faust plug-ins. LLVM IR and tools also allows some nice bytecode manipulations like "partial evaluation/specialization" (also called "runtime optimization" by LLVM guys...) that we also currently investigate.

The source code of can be downloaded from the SourceForge site:

Read-only access : git clone git://faudiostream.git.sourceforge.net/gitroot/faudiostream/faudiostream

Developer (Read-Write access) : git clone ssh://USERNAME@faudiostream.git.sourceforge.net/gitroot/faudiostream/faudiostream

cd faudiostream

Now to get the "faust2" branch when the new FIR (Faust Intermediate Representation) based backend model is developed:

git checkout -b faust2 origin/faust2

make && sudo make install


Using LLVM bitcode

The new -lang parameter has to be used to produce LLVM bitcode, like for example:

faust -lang llvm examples/noise.dsp -o noise.bc

You may look at the textual version of the LLVM bitcode if no output file is specified:

faust -lang llvm examples/noise.dsp

Integrating the produced bitcode with an architecture is a bit different compared to what was usually done with C or C++ produced code. Basically one will need to compile the LLVM bicode on one side, the adapted architecture file on the other side and link them together. Look at the architecture/llvm-jack-gtk.cpp file as an example of Faust LLVM bitcode with Jack/GTK architecture file integration. In this case, the C++ wrapper defined a class llvmdsp : public dsp that will use the API exported in the LLVM bitcode file (functions like new_llvm, delete_llvm, getNumInputs_llvm, getNumOutputs_llvm, compute_llvm...etc...). The Faust compiler produce the best LLVM bitcode it can, but does not (yet) apply any optimization passes by itself. LLVM has quite powerfull optimizations passes to be done on LLVM IR and can be tested and applied using the LLVM opt tool:

opt -O3 noise.bc -o noise.bc

The optimized LLVM bitcode file can be compiled using LLVM llc tool to produce an assembly file like:

llc -O3 noise.bc -o noise.s

Then assembly file and architecture file will be linked together to produce the final application:

llvm-g++ architecture/llvm-jack-gtk.cpp noise.s `pkg-config --cflags --libs jack gtk+-2.0` -o noise

An alternative is to prepare a generic Faust LLVM bitcode loader that will be able to load and JIT any Faust generated LLVM bitcode file. Goes in the "examples" folder and type "make llvm-loader" to compile the llvm-jack-gtk-loader. You can then use it to load the previously generated noise.bc bitcode file:

./llvm-jack-gtk-loader ../noise.bc

Compiling in Work Stealing Scheduler (-sch) mode

When compiled with -sch option, the generated LLVM bitcode will contain calls to the WSS API functions, to be compiled separately and linked to the final appplication. So:

faust -lang llvm -sch examples/noise.dsp -o noise.bc

Then:

opt -O3 noise.bc -o noise.bc

llc -O3 noise.bc -o noise.s

llvm-g++ architecture/llvm-jack-gtk.cpp architecture/scheduler.cpp noise.s `pkg-config --cflags --libs jack gtk+-2.0` -o noise

Using the llvm-jack-gtk-loader generic LLVM bitcode loaded cannot be done directly, since it does not contains the object code for WSS API functions. One solution is to compile the "scheduler.cpp" file as bitcode and link it with the Faust generated bitcode. The jackgtkllvm target in examples already does that and produce a "foo_sch.bc" kind of file for each example. Then: ./llvm-jack-gtk-loader jackgtkdirllvm/noise_sch.bc will do the job.