Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ sync-sysroot:
test: prepare-lind-root
# NOTE: `grep` workaround required for lack of meaningful exit code in wasmtestreport.py
LIND_WASM_BASE=. LINDFS_ROOT=$(LINDFS_ROOT) \
./scripts/wasmtestreport.py && \
./scripts/wasmtestreport.py --allow-pre-compiled && \
cat results.json; \
if grep -q '"number_of_failures": [^0]' results.json; then \
echo "E2E_STATUS=fail" > e2e_status; \
Expand Down
47 changes: 42 additions & 5 deletions scripts/lind_compile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -Eeuo pipefail
# Cross-compile C programs to wasm using the lind-wasm toolchain.
#
# Default pipeline:
# .c -> .wasm (clang) -> (wasm-opt)
# .c -> .wasm (clang) -> (wasm-opt) -> .cwasm (lind-boot --precompile)
#
# Usage:
# # full pipeline (default)
Expand All @@ -13,6 +13,7 @@ set -Eeuo pipefail
# # run individual steps
# lind_compile --compile-only hello.c # .c -> .wasm
# lind_compile --opt-only hello.wasm # optimize existing .wasm in place
# lind_compile --precompile-only hello.wasm # .wasm -> .cwasm
#
# # print commands (any mode)
# lind_compile --print-cmd hello.c
Expand All @@ -33,6 +34,7 @@ usage: $(basename "$0") [OPTIONS] <source> [-- <clang args>]
(no mode flag) : .c -> .wasm -> (wasm-opt) -> .cwasm
--compile-only : .c -> .wasm
--opt-only : .wasm -> .wasm (in-place optimization)
--precompile-only : .wasm -> .cwasm (AOT compile via lind-boot)

Common options:
-v, --print-cmd, --verbose print the underlying commands that are executed
Expand All @@ -50,6 +52,7 @@ usage: $(basename "$0") [OPTIONS] <source> [-- <clang args>]
$(basename "$0") --compile-only hello.c
$(basename "$0") --compile-grate hello.c
$(basename "$0") --opt-only build/hello.wasm
$(basename "$0") --precompile-only build/hello.wasm
$(basename "$0") --print-cmd hello.c
$(basename "$0") --no-default-clang-flags hello.c -- -target=wasm32-unknown-wasi
$(basename "$0") hello.c -- -lm
Expand Down Expand Up @@ -92,7 +95,10 @@ while [[ "$#" -gt 0 ]]; do
;;
--opt-only)
set_mode "opt-only"
;;
;;
--precompile-only)
set_mode "precompile-only"
;;
-v|--print-cmd|--verbose)
PRINT_CMDS="true"
;;
Expand Down Expand Up @@ -207,6 +213,15 @@ case "${MODE}" in
fi
OUT_WASM="${SRC}"
;;
"precompile-only")
if [[ "${EXT}" != "wasm" ]]; then
echo "error: ${MODE} mode expects a .wasm file, got: ${SRC}" >&2
usage
exit 2
fi
OUT_WASM="${SRC}"
OUT_CWASM="${SRC%.wasm}.cwasm"
;;
*)
echo "error: internal: unknown mode '${MODE}'" >&2
exit 2
Expand Down Expand Up @@ -237,6 +252,10 @@ if [[ "${MODE}" == "full" || "${MODE}" == "opt-only" ]]; then
[[ -x "${WASM_OPT_BIN}" ]] || { echo "error: wasm-opt not found at ${WASM_OPT_BIN}" >&2; exit 2; }
fi

if [[ "${MODE}" == "full" || "${MODE}" == "precompile-only" ]]; then
[[ -x "${LINDBOOT_BIN}" ]] || { echo "error: lind-boot not found at ${LINDBOOT_BIN}" >&2; exit 2; }
fi

# --- debug output (post-normalization) ---
if [[ "${PRINT_ARGS}" == "true" && ( "${MODE}" == "full" || "${MODE}" == "compile-only" ) ]]; then
debug_mandatory_clang_flags=(
Expand Down Expand Up @@ -324,16 +343,25 @@ do_optimize() {
run_cmd "${WASM_OPT_BIN}" --epoch-injection --asyncify -O2 --debuginfo "${OUT_WASM}" -o "${OUT_WASM}"
}

do_precompile() {
run_cmd "${LINDBOOT_BIN}" --precompile "${OUT_WASM}"
}

cp_to_lindfs() {
mkdir -p "${LINDFS_ROOT}"
cp "${OUT_WASM}" "${LINDFS_ROOT}"
if [[ -n "${OUT_CWASM}" && -f "${OUT_CWASM}" ]]; then
cp "${OUT_CWASM}" "${LINDFS_ROOT}"
else
cp "${OUT_WASM}" "${LINDFS_ROOT}"
fi
}

# --- run selected pipeline ---
case "${MODE}" in
full)
do_compile
do_optimize
do_precompile
cp_to_lindfs
;;
"compile-only")
Expand All @@ -342,12 +370,21 @@ case "${MODE}" in
;;
"opt-only")
do_optimize
;;
;;
"precompile-only")
do_precompile
;;
esac

# --- result summary ---
case "${MODE}" in
full|"compile-only"|"opt-only")
full)
echo "OK: ${OUT_CWASM}"
;;
"compile-only"|"opt-only")
echo "OK: ${OUT_WASM}"
;;
"precompile-only")
echo "OK: ${OUT_CWASM}"
;;
esac
4 changes: 2 additions & 2 deletions src/lind-boot/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ pub struct CliOptions {
#[arg(long)]
pub debug: bool,

/// todo: Allow using precompiled cwasm artifacts
/// AOT-compile a .wasm file to a .cwasm artifact and exit (no runtime needed)
#[arg(long)]
pub allow_precompile: bool,
pub precompile: bool,

/// First item is WASM file (argv[0]), rest are program args (argv[1..])
///
Expand Down
46 changes: 43 additions & 3 deletions src/lind-boot/src/lind_wasmtime/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use std::sync::Arc;
use sysdefs::constants::lind_platform_const::{RAWPOSIX_CAGEID, WASMTIME_CAGEID};
use threei::threei_const;
use wasi_common::sync::WasiCtxBuilder;
use wasmtime::{AsContextMut, Engine, Func, InstantiateType, Linker, Module, Store, Val, ValType};
use wasmtime::{
AsContextMut, Engine, Func, InstantiateType, Linker, Module, Precompiled, Store, Val, ValType,
};
use wasmtime_lind_3i::{VmCtxWrapper, init_vmctx_pool, rm_vmctx, set_vmctx, set_vmctx_thread};
use wasmtime_lind_multi_process::{CAGE_START_ID, LindCtx, THREAD_START_ID};
use wasmtime_lind_utils::LindCageManager;
Expand Down Expand Up @@ -74,7 +76,7 @@ pub fn execute_wasmtime(lindboot_cli: CliOptions) -> anyhow::Result<Vec<Val>> {
// -- Load module and Attach host APIs --
// Set up the WASI. In lind-wasm, we predefine all the features we need are `thread` and `wasipreview1`
// so we manually add them to the linker without checking the input
let module = Module::from_file(&engine, wasm_file_path)?;
let module = read_wasm_or_cwasm(&engine, wasm_file_path)?;
let mut linker = Linker::new(&engine);

attach_api(
Expand Down Expand Up @@ -143,7 +145,7 @@ pub fn execute_with_lind(
// -- Load module and Attach host APIs --
// Set up the WASI. In lind-wasm, we predefine all the features we need are `thread` and `wasipreview1`
// so we manually add them to the linker without checking the input
let module = Module::from_file(&engine, wasm_file_path)?;
let module = read_wasm_or_cwasm(&engine, wasm_file_path)?;
let mut linker = Linker::new(&engine);

attach_api(
Expand Down Expand Up @@ -488,6 +490,44 @@ fn load_main_module(
ret
}

/// AOT-compile a `.wasm` file to a `.cwasm` artifact on disk.
///
/// This only needs a Wasmtime `Engine` — no runtime, cages, or 3i. The output
/// path is the input path with the extension replaced by `.cwasm`.
pub fn precompile_module(cli: &CliOptions) -> Result<()> {
let wasm_path = Path::new(cli.wasm_file());
let cwasm_path = wasm_path.with_extension("cwasm");

let engine = Engine::new(&wasmtime::Config::new()).context("failed to create engine")?;
let wasm_bytes = std::fs::read(wasm_path)
.with_context(|| format!("failed to read {}", wasm_path.display()))?;
let cwasm_bytes = engine
.precompile_module(&wasm_bytes)
.context("failed to precompile module")?;
std::fs::write(&cwasm_path, cwasm_bytes)
.with_context(|| format!("failed to write {}", cwasm_path.display()))?;

eprintln!("OK: {}", cwasm_path.display());
Ok(())
}

/// Load a Wasm module from disk, supporting both `.wasm` and precompiled `.cwasm` files.
///
/// The function probes the file header via `Engine::detect_precompiled_file`.
/// If the file is a precompiled module it is deserialized directly (skipping
/// compilation). Otherwise it is compiled from source via `Module::from_file`.
fn read_wasm_or_cwasm(engine: &Engine, path: &Path) -> Result<Module> {
if let Some(Precompiled::Module) = engine
.detect_precompiled_file(path)
.context("failed to detect precompiled module")?
{
return unsafe { Module::deserialize_file(engine, path) }
.context("failed to deserialize precompiled module");
}

Module::from_file(engine, path).context("failed to compile module")
}

/// This function takes a Wasm function (Func) and a list of string arguments, parses the
/// arguments into Wasm values based on expected types (ValType), and invokes the function
fn invoke_func(store: &mut Store<HostCtx>, func: Func, args: &[String]) -> Result<Vec<Val>> {
Expand Down
2 changes: 1 addition & 1 deletion src/lind-boot/src/lind_wasmtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ pub mod execute;
pub mod host;
pub mod trampoline;

pub use execute::execute_wasmtime;
pub use execute::{execute_wasmtime, precompile_module};
11 changes: 10 additions & 1 deletion src/lind-boot/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
mod cli;
mod lind_wasmtime;

use crate::{cli::CliOptions, lind_wasmtime::execute_wasmtime};
use crate::{
cli::CliOptions,
lind_wasmtime::{execute_wasmtime, precompile_module},
};
use clap::Parser;
use rawposix::init::{rawposix_shutdown, rawposix_start};

Expand All @@ -18,6 +21,12 @@ use rawposix::init::{rawposix_shutdown, rawposix_start};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let lindboot_cli = CliOptions::parse();

// AOT-compile only — no runtime needed
if lindboot_cli.precompile {
precompile_module(&lindboot_cli)?;
return Ok(());
}

// Initialize RawPOSIX and register RawPOSIX syscalls with 3i
rawposix_start(0);

Expand Down