diff --git a/docs/docs/experimental/redline.md b/docs/docs/experimental/redline.md new file mode 100644 index 000000000..74d5f335b --- /dev/null +++ b/docs/docs/experimental/redline.md @@ -0,0 +1,175 @@ +--- +sidebar_position: 2 +sidebar_label: Native Compilation (Redline) +title: Native Compilation with Redline +--- +## Overview + +:::warning[Experimental] +Redline is experimental, see [Why](why.md) for what that means for stability. +It also supports less of the WebAssembly specification than the other execution +modes, so check [Feature support](#feature-support) before adopting it. +::: + +Redline compiles your Wasm module to native machine code using [Cranelift](https://cranelift.dev/), +instead of to JVM bytecode. Compilation happens at build time for every supported platform, and the +right one is selected at runtime. + +It is a substitute for the [Build Time Compiler](../execution/build-time-compiler.md) only, not for +the interpreter or the [Runtime Compiler](../execution/runtime-compiler.md), and it is enabled on +the same Maven plugin. The bytecode is still generated, and is used whenever native code cannot be. + +## Feature support + +Redline supports: + +* the core specification +* bulk memory +* tail call +* threads and atomics +* reference type instructions + +Redline does **not** support: + +* multi memory +* exception handling +* garbage collection +* typed function references +* SIMD +* passing `externref` values to and from host functions + +If your module uses anything from the second list the build fails. There is no per function +fallback, so a single unsupported instruction stops the whole module from compiling. + +Every other execution mode supports all of the above, with the exception of SIMD, +which is available only in the interpreter. + +## Platform support + +Native code is generated for six platforms: + +| | x86_64 | aarch64 | +|---|---|---| +| **Linux** | yes | yes | +| **macOS** | yes | yes | +| **Windows** | yes | yes | + +On any other platform your module still runs, using the compiled bytecode instead. See +[Falling back](#falling-back). + +## Usage + +Enable it on the compiler plugin: + +```xml + + run.endive + endive-compiler-maven-plugin + + + + compile + + + org.acme.wasm.MyModule + src/main/resources/my.wasm + true + + + + +``` + +and add a runner. This is the only dependency you need, everything else comes transitively: + +```xml + + run.endive + redline-runner-experimental + ${endive.version} + +``` + +`redline-runner-experimental` uses the Panama FFM API and requires Java 25 or later. On older +versions use `redline-runner-jffi-experimental`, which needs only Java 11: + +```xml + + run.endive + redline-runner-jffi-experimental + ${endive.version} + +``` + +If both are present, the Panama runner is used wherever the JDK supports it. + +Your module is then used exactly as it would be without redline: + +```text +try (var instance = MyModule.builder().build()) { + var f = instance.export("my_function"); +} +``` + +## Falling back + +When native code cannot be used, `builder()` falls back to the bytecode produced by the +[Build Time Compiler](../execution/build-time-compiler.md), which is always generated alongside it. +This happens on platforms outside the table above, or when no runner is on the classpath. + +Your module keeps working either way, so the fallback is silent. To check which one you got: + +```text +MyModule.nativeProvider().isPresent() +``` + +It is `true` when native code is in use and `false` when the bytecode is. `MyModule.safeBuilder()` +always uses the bytecode, which is useful for comparing the two. + +## What to expect + +**Jar size.** Native code is considerably larger than the Wasm it comes from, and by default one +copy is generated per platform. If you know where you deploy, list only those targets: + +```xml + + x86_64-unknown-linux-gnu + +``` + +The available triples are `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`, +`x86_64-apple-darwin`, `aarch64-apple-darwin`, `x86_64-pc-windows-msvc` and +`aarch64-pc-windows-msvc`. + +**Build time.** Compiling for every platform takes noticeably longer than the build time compiler +alone. Narrowing the target list helps here too. + +**Imported memories and tables.** A memory or table you pass in through `ImportValues` has to be +created by the runner, otherwise the first call fails. Modules that declare their own memory, +including anything built for WASI, are unaffected. + +Create it through the provider: + +```text +var provider = MyModule.nativeProvider().orElseThrow(); + +var memory = provider.createMemory(new MemoryLimits(1, 2)); +var table = provider.createImportTable(new Table(ValType.FuncRef, new TableLimits(1)), REF_NULL_VALUE); + +var imports = ImportValues.builder() + .addMemory(new ImportMemory("env", "memory", memory)) + .addTable(new ImportTable("env", "table", table)) + .build(); + +try (var instance = MyModule.builder().withImportValues(imports).build()) { + var f = instance.export("my_function"); +} +``` + + diff --git a/docs/tests/approvals/docs-experimental-redline.md.approved.txt b/docs/tests/approvals/docs-experimental-redline.md.approved.txt new file mode 100644 index 000000000..c6cac6926 --- /dev/null +++ b/docs/tests/approvals/docs-experimental-redline.md.approved.txt @@ -0,0 +1 @@ +empty