
# Developing a new backend

A backend in the Faust compiler is responsible for generating output code (e.g., C++, LLVM, WASM, or other formats) from an intermediate representation of the signal processing program. Currently, there are two types of backends, classified based on their activation level: either at the signal IR level or at the FIR (Faust Imperative Representation) level: 
 
- [Signal IR](https://faustdoc.grame.fr/tutorials/signal-api/) backends: they operate at the signal IR, where the program is conceptually represented as a graph of computations performed on audio and control inputs (like sliders or buttons) and producing audio and control outputs (like bargraphs). At this stage, audio signals are treated as continuous, infinite data streams defined in `signals.hh` and `signals.cpp`.
 
- FIR (Faust Imperative Representation) backends: they are activated later in the compilation chain, working on the FIR (Faust Imperative Representation), an intermediate, instruction-based representation of the program (with memory operations like load/store, mathematical computations and control structures). FIR backends take these low-level instructions and generate output in a specific format (e.g., C++ code or WebAssembly). The FIR is generated by converting signals to FIR instructions, defined in `instructions.hh` and `instructions.cpp` files. The conversion is done in `InstructionsCompiler` for the *-scalar* mode and `DAGInstructionsCompiler` for *-vec*, *-omp*, *-sch* Direct Acyclic Graph (DAG) based generators.

Backends are implemented in sub-folders in the *generator* folder. They can be added in the compiler (either the `faust` executable or the `libfaust` library) using a compilation time activation model.

## Compiling the backend

Faust compilation and installation is based on CMake, you should look at the [Faust wiki pages](https://github.com/grame-cncm/faust/wiki). 

A set of cmake files have to be adapted for the new backed to be properly compiled and activated:

- targets included into your projects are specified using a configuration file located in the targets folder
- the [backends selection](https://github.com/grame-cncm/faust/wiki/backends#selecting-your-backends) is described using `backends` files located in the [backends](https://github.com/grame-cncm/faust/tree/master-dev/build/backends) folder. **These files will have to be adapted to activate the new backend**
- the backend build flag (like `TEMPLATE_BUILD` in this case) has to be added in the [scan_backends](https://github.com/grame-cncm/faust/blob/master-dev/build/misc/llvm.cmake#L14) function defined in the [llvm.cmake](https://github.com/grame-cncm/faust/blob/master-dev/build/misc/llvm.cmake) file

The new backend may define some globals that have to be added in the `global` class defined in `global.hh`and `global.cpp` files. For instance the unique `TemplateInstVisitor` object is defined and allocated with the appropriate `#ifdef TEMPLATE_BUILD .... #endif` flags.

## Activating the backend

The new backend will have to be allocated and activated in the `libcode.cpp` file with the following kind of code, in the header part of the file:

```c++
#ifdef TEMPLATE_BUILD
#include "template_code_container.hh"
#endif
```

Then the following kind of code to create a FIR backend:

```c++
static void compileTemplate(Tree signals, int numInputs, int numOutputs, ostream* out)
{
#ifdef TEMPLATE_BUILD
    // Backend configuration
    gGlobal->gAllowForeignFunction = true;
    gGlobal->gNeedManualPow        = false;
    gGlobal->gFAUSTFLOAT2Internal  = true;
    container = TemplateCodeContainer::createContainer(gGlobal->gClassName, numInputs, numOutputs, out);
    
    if (gGlobal->gVectorSwitch) {
        new_comp = new DAGInstructionsCompiler(container);
    } else {
        new_comp = new InstructionsCompiler(container);
    }
    
    if (gGlobal->gPrintXMLSwitch || gGlobal->gPrintDocSwitch) new_comp->setDescription(new Description());
    new_comp->compileMultiSignal(signals);
#else
    throw faustexception("ERROR : -lang temp not supported since Template backend is not built\n");
#endif
}
```

or the following kind of code to create a signal backend:

```c++
static void compileSdf3(Tree signals, ostream* out)
{
#ifdef SDF3_BUILD
    signals = simplifyToNormalForm(signals);
    Signal2SDF V;
    V.sigToSDF(signals, *out);
    if (gUseCout) {
        cout << dynamic_cast<ostringstream*>(out)->str();
    }
#else
    throw faustexception("ERROR : -lang sdf3 not supported since SDF3 backend is not built\n");
#endif
}
```

Those function are to be added in `generateCode` function with a new `temp` flag to be used in the `-lang` option (like `-lang temp` with this new FIR template backend):

```c++
else if (gGlobal->gOutputLang == "temp") {
    compileTemplate(signals, numInputs, numOutputs, dst.get());
}
```

or for a signal backend:

```c++
else if (startWith(gGlobal->gOutputLang, "sdf3")) {
    compileSdf3(signals, gDst.get());
    // SDF3 does not create a compiler, code is already generated here.
    return;
}
```

The following kind of code has to be added in the `enumBackends` function in the `global.cpp` file:

```c++
#ifdef TEMPLATE_BUILD
    out << dspto << "Template" << endl;
#endif
```

The `-lang` option in the `printHelp` documentation function has to be manually adapted.

## Signal backend 

A signal backend directly operates on the signal representation of a Faust program, focusing on the structure of audio and control data as a computation graph. To facilitate this, several signal traversal tools are defined in the `treeTraversal.hh`, `treeTraversal.cpp`, `treeTransform.hh`, `treeTransform.cpp files`. These tools, particularly the `TreeTraversal` and `TreeTransform` classes, offer a systematic approach for navigating, processing, and potentially transforming this graph structure.

The `TreeTraversal` class enables recursive, memoized traversal of the signal graph, while `TreeTransform` provides mechanisms for modifying the tree structure itself. Together, they allow a signal backend to extract and transform relevant information within the signal graph, supporting the generation of code in the backend's target format.

For an example of this in action, refer to the SDF3 signal backend in the `generator/sdf3 folder`. This implementation demonstrates how a signal backend can leverage traversal and transformation tools to generate an [SDF3](https://www.es.ele.tue.nl/sdf3/) XML textual representation.

## FIR backend

A FIR backend typically subclasses the `CodeContainer` class which will contain the FIR generated code for the different methods to generate (`init`, `buildUserInterface`, `compute`,etc.). For the template backend, the code in defined in the `template_code_container.hh`and `template_code_container.cpp` files:
- the `TemplateCodeContainer` is  the base class for all backends code generation options (*-scalar*, *-vec*, *-omp*, *-sch*)
- the `TemplateScalarCodeContainer` implements the container used in *-scalar*
- the `TemplateVectorCodeContainer` implements the container used in *-vec*
- for options like *-omp*, *-sch*, the corresponding `TemplateOpenMPCodeContainer` and `TemplateWorkStealingCodeContainer` classes would have to be implemented and activated
    
A `TemplateInstVisitor` visitor pattern implemented in `template_instructions.hh` is used to traverse the FIR code to generate the code. It actually shows an example of a [textual backend](#textual-backend), and is written by subclassing the `TextInstVisitor` base class for textual visitors. Note that this template code is given as an example and will have to be adapted to real use-cases. Looking at already written ones like the C or Julia backends can help.

The `TemplateCodeContainer` base class implements in particular:
- the `produceClass` method responsible to generate the entire piece of code
- the `produceInternal` method possibly used for sub-containers
- the `produceMetadata` method to generate the global metadata
- the waveforms are inlined in the DSP structure (see `TemplateInitFieldsVisitor`usage in the `TemplateCodeContainer::produceClass` method, in the `// Fields` section)

The `TemplateScalarCodeContainer` class implements the `compute` method to generate the specific version of DSP computation in *-scalar* mode. The `TemplateVectorCodeContainer` class implements the `compute` method to generate the specific version of DSP computation in *-vec* mode. 

### Inlining sub-containers

The `rdtable` and `rwtable` primitives are treated as *sub-containers* which possibly generate separated pieces of code (like additional C++ classes in the C++ backend, or LLVM modules in the LLVM backend). It may be easier to have the sub-containers *be inlined* in the main module. This can be done at the appropriate locations using this kind of code:

```c++
// Rename 'sig' in 'dsp', remove 'dsp' allocation, 
// inline subcontainers 'instanceInit' and 'fill' function call
inlineSubcontainersFunCalls(fInitInstructions)->accept(gGlobal->gTemplateVisitor);
``` 

Look at the `TemplateCodeContainer::produceClass()` method for a precise way of using it.

A global `TemplateInstVisitor gTemplateVisitor` object may have to be added in the `Global` class if it has to be shared by the main module and sub containers. 

An adapted subclass of `StringTypeManager` defined in `type_manager.h` may have to be defined. 

### Textual FIR backend

A textual FIR backend will generate code in a C++ stream. It typically uses a subclass of `TextInstVisitor` to define its specific FIR traversing methods.

### Binary FIR backend 

A binary FIR backend will generate binary code using a tailor-made output, which can greatly depend on the use-case. Look at the LLVM backend which outputs LLVM IR code, and the WASM backend which outputs binary WebAssembly code.

## Miscs

### Displaying the FIR

For debugging purpose, the `dump2FIR` function can be used to print a textual version of the FIR code.

