# WebAssembly AOT code generation

## Basic operation

The LLVM backend of the Mono JIT is used to generate an llvm .bc file for each assembly, then the .bc files are
compiled to webassembly using emscripten, then the resulting wasm files are linked into the final app. The 'bitcode'/'llvmonly'
variant of the LLVM backend is used since webassembly doesn't support inline assembly etc.

## Source code structure

`mini-llvm.c`: The LLVM backend.
`mini-wasm.h/c`: The wasm backend. This is a minimal version of a normal mono JIT backend which only supports llvm.
`llvm-runtime.cpp`: Code to throw/catch C++ exceptions.
`aot-runtime-wasm.c`: Code related to interpreter/native transitions on wasm.
`llvmonly-runtime.c`:  Runtime support for the generated AOT code.

WASM specific code is inside `HOST_WASM/TARGET_WASM` defines.

## GC Support

On wasm, the execution stack is not stored in linear memory, so its not possible to scan it for GC references. However, there
is an additional C stack in linear memory which is managed explicitly by the generated wasm code. This stack is already
scanned by the mono GC as on other platforms.
To make GC references in AOTed methods visible to the GC, every method allocates a gc_pin area in its prolog, and
stores arguments/locals with a reference type into it. This will cause the GC to pin those references so the rest of
the generated code can treat them normally as LLVM values.

## Interpreter support

On wasm, the two supported execution modes are interpreter, or aot+interpreter. This means its always
possible to fall back to the interpreter if needed.
For the AOT -> interpreter case, every call from AOTed code which might end up in the interpreter is
emitted as an indirect call. When the callee is not found, a wrapper function is used which
packages up the arguments into an array and passes control to the interpreter.
For the interpreter -> AOT case, and similar wrapper function is used which receives the
arguments and a return value pointer from the interpreter in an array, and calls the
AOTed code. There is usually one aot->interp and interp->aot wrapper for each signature, with
some sharing. These wrappers are generated by the AOT compiler when the 'interp' aot option
is used.

## Exception handling

On wasm, its not possible to walk the stack so the normal mono exception handling/unwind code
cannot be used as is. Its also hard to map the .NET exception handling concepts like filter clauses
to the llvm concepts. Instead, c++/wasm exceptions are used to implement unwinding, and the
interpreter is used to execute EH code.
When an exception needs to be thrown, we store the exception info in TLS, and throw a dummy C++ exception instead.
Internally, this is implemented by emscripten either by calling into JS, or by using the wasm exception handling
spec.
The c++ exception is caught in the generated AOT code using the relevant llvm catch instructions. Then execution is
transferred to the interpreter. This is done by creating a data structure on the stack containing all the IL level state like
the IL offset and the values of all the IL level variables. The generated code continuously updates this state during
execution. When an exception is caught, this IL state is passed to the interpreter which continues execution from
that point.  This process is called `deopt` in the runtime code.
Exceptions are also caught in various other places like the interpreter-aot boundary.

## Null checks

Since wasm has no signal support, we generate explicit null checks.

## Issues

The generated code is in general much bigger than the code generated on ios etc. Some of the
current issues are described below.

### Function pointers

The runtime needs to be able to do a IL method -> wasm function lookup. To do this, every
AOT image includes a table mapping from a method index to wasm functions. This means that
every generated AOT method has its address taken, which severely limits the interprocedural
optimizations that LLVM can do, since it cannot determine the set of callers for a function.
This means that it cannot remove functions corresponding to unused IL methods, cannot
specialize functions for constant/nonnull arguments, etc.
The dotnet ILLink tool includes some support for adding a [DisablePrivateReflection] attribute to
methods which cannot be called using reflection, and the AOT compiler could use this
to avoid generating function pointers for methods which are not called from outside the
AOT image. This is not enabled right now because the ILLink tool support is not complete.

### Null checks

The explicit null checking code adds a lot of size overhead since null checks are very common.

### Virtual calls

Vtable slots are lazily initialized on the first call, i.e. every virtual call looks like this:
```C
vt_entry = vtable [slot];
if (vt_entry == null)
	vt_entry = init_vt_entry ();
```

### Exception handling

It might be possible to implement EH in the generated code without involving the interpreter. The
current design adds a lot of overhead to methods which contain IL clauses.
