Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ ziskemu -e build/bin/println.zkvm.elf
```

#### QEMU RISC-V

```bash
./platform/riscv-qemu/scripts/c2riscv-qemu.sh \
build/c-packages/println \
Expand All @@ -95,6 +96,21 @@ qemu-system-riscv64 -machine virt -bios none \
-kernel build/bin/println.riscv.elf -nographic
```

#### QEMU RISC-V WAMR

Include OpenSBI BIOS (`-bios default` instead of `-bios none`) such that a shutdown function is present for improved benchmarking.

```bash
./platform/riscv-wamr-qemu/scripts/wasm2wamr-qemu.sh \
examples/build-wasm/go/fibonacci.wasm \
build/bin/fibonacci.wamr.elf

# Run in QEMU
./docker/docker-shell.sh qemu-system-riscv64 -machine virt -m 1024M \
-kernel build/bin/fibonacci.wamr.elf -nographic -d plugin \
-plugin /libinsn.so
```

## Examples

All examples below use Go, but the same principles apply to any language that compiles to WASM with WASI support.
Expand Down Expand Up @@ -195,16 +211,18 @@ Please note that:
- `./platform/riscv-qemu-user/scripts/c2riscv-qemu-user.sh` uses target `-march=rv64imad -march=rv64imad` whereas Go direct compilation uses `rv64gc`.
- `wasmtime` targets rv64gc

|program|through WASM, w2c2, -O0|through WASM, w2c2, optimized|though WASM, wasmtme|through WASM, wasmer (cranelift)|directly|
|program|through WASM, w2c2, -O0|through WASM, w2c2, optimized|through WAMR, -O0|though WASM, wasmtme|through WASM, wasmer (cranelift)|directly|
|---|---|---|---|---|---|
|`stateless`|12,866,052,519|2,110,574,100 (-O3)|874,758,419|953,874,491|236,265,327|
|`stateless`|12,866,052,519|2,110,574,100 (-O3)|5,427,433,654|874,758,419|953,874,491|236,265,327|

## Analysis of the results

`-O3` WASM approach is ~10 times slower than the direct compilation. `-O0` is 6 times slower than `-O3`. These gaps are significantly bigger than for the corresponding gaps for `reva-client-eth` Rust program.

Surprisingly WASM though `wasmtime` is faster than `w2c2`. `wasmtime` approach is ~3-4 times slower than the direct approach.

Unoptimized WAMR AOT is currently in between `w2c2` and wasmtime. Running WAMR with non-zero optimization levels on RISC-V currently fails with a relocation error. https://github.com/bytecodealliance/wasm-micro-runtime/issues/4765

# Size of binaries

```
Expand Down
61 changes: 59 additions & 2 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
python3-tomli \
python3.10-venv \
libmpc-dev \
libmpfr-dev \
libgmp-dev \
Expand All @@ -29,6 +30,11 @@ RUN apt-get update && apt-get install -y \
cmake \
libglib2.0-dev \
libslirp-dev \
libpthread-stubs0-dev \
g++-multilib \
libgcc-9-dev \
lib32gcc-9-dev \
ccache \
&& rm -rf /var/lib/apt/lists/*

# Build RISC-V GNU Toolchain
Expand All @@ -50,17 +56,68 @@ RUN git clone https://github.com/turbolent/w2c2.git /opt/w2c2 && \

RUN apt update && apt install -y git build-essential ninja-build pkg-config libglib2.0-dev libpixman-1-dev

# Include patch for insn.c because otherwise this error appears due to unknown
# instructions:
#
# ERROR:../tests/tcg/plugins/insn.c:97:vcpu_init: assertion failed: (count > 0)
#
# https://lists.gnu.org/archive/html/qemu-discuss/2025-08/msg00001.html
RUN git clone https://git.qemu.org/git/qemu.git && \
cd qemu && \
./configure --target-list=riscv64-linux-user --enable-plugins && \
sed -i 's/g_assert(count > 0)/g_assert(count >= 0)/g' tests/tcg/plugins/insn.c && \
./configure --target-list=riscv64-linux-user,riscv64-softmmu --enable-plugins && \
make -j4 && \
make install && \
cp build/tests/tcg/plugins/libinsn.so /libinsn.so && \
cd .. && \
rm -r qemu

# Build WAMR with ZKVM platform
#
# Some symbols such as __floatundisf, __floatdisf, __ltsf2 need to be
# explicitly registered for working soft float support on the target
# platform
#
# https://github.com/bytecodealliance/wasm-micro-runtime/issues/418
# https://github.com/bytecodealliance/wasm-micro-runtime/issues/531
#
# Additionally use LLVM 20. Profiling wamrc when compiling a large wasm
# file like stateless.wasm shows that AttemptToFoldSymbolOffsetDifference
# consumes most of the compile time. LLVM 19 contains a fix for this
# though. Also large code model is in principle supported for RISC-V
# since version 20.
#
# https://github.com/llvm/llvm-project/issues/81440
RUN git clone https://github.com/psilva261/wasm-micro-runtime-zkvm.git /opt/wamr/ && \
cd /opt/wamr && \
git checkout zkvm && \
cmake . \
-DWAMR_BUILD_PLATFORM=zkvm \
-DWAMR_BUILD_TARGET=AOT \
-DWAMR_BUILD_LIBC_BUILTIN=1 \
-DWAMR_BUILD_LIB_PTHREAD=0 \
-DWAMR_BUILD_THREAD_MGR=0 \
-DWAMR_BUILD_WASI_THREADS=0 \
-DTHREADS_PREFER_PTHREAD_FLAG=OFF \
-DCMAKE_THREAD_LIBS_INIT="" \
-DWAMR_BUILD_FAST_INTERP=1 \
-DWAMR_BUILD_JIT=0 \
-DWAMR_BUILD_AOT=1 \
-DCMAKE_SYSTEM_PROCESSOR=riscv64 \
-DWAMR_BUILD_TARGET=RISCV64_LP64 \
-DCMAKE_C_COMPILER=/opt/riscv-newlib/bin/riscv64-unknown-elf-gcc \
-DCMAKE_CXX_COMPILER=/opt/riscv-newlib/bin/riscv64-unknown-elf-g++ \
-DCMAKE_ASM_COMPILER=/opt/riscv-newlib/bin/riscv64-unknown-elf-gcc \
-DWASM_ENABLE_SIMDE=OFF \
-DWAMR_BUILD_SIMD=0 && \
make && \
cd /opt/wamr/wamr-compiler && \
./build_llvm.sh && \
cmake . && \
make

# Add toolchains to PATH
ENV PATH="/opt/riscv-newlib/bin:/opt/w2c2/w2c2:${PATH}"
ENV PATH="/opt/riscv-newlib/bin:/opt/w2c2/w2c2:/opt/wamr/wamr-compiler:${PATH}"

RUN apt update && apt install -y gcc-riscv64-linux-gnu

Expand Down
5 changes: 5 additions & 0 deletions go_benchmark.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ echo "Transpiling WASM to WASMU with wasmer..."
# TODO: Cranelift is used for now because LLVM support is buggy. Once LLVM is fixed in wasmer use `--llvm` flag instead of `cranelift`; https://github.com/wasmerio/wasmer/issues/5951#issuecomment-3632904384
./docker/docker-shell.sh /root/.wasmer/bin/wasmer compile --cranelift --target riscv64gc-unknown-linux-gnu examples/build-wasm/go/stateless.wasm -o examples/build-wasm/go/stateless-by-wasmer/src/stateless.wasmu

echo "Transpiling WASM to WAMR AOT with wamrc..."

./platform/riscv-wamr-qemu/scripts/wasm2wamr-qemu.sh examples/build-wasm/go/stateless.wasm build/bin/stateless.wamr.elf

echo "Compiling C to RISCV..."

OPT_LEVEL="-O0" ./platform/riscv-qemu-user/scripts/c2riscv-qemu-user.sh build/c-packages/stateless/ build/bin/stateless.riscv.O0.elf
Expand All @@ -36,6 +40,7 @@ echo "" > go_benchmark_results.txt
./docker/docker-shell.sh qemu-riscv64 -plugin /libinsn.so examples/go/stateless/stateless >> go_benchmark_results.txt 2>&1
./docker/docker-shell.sh qemu-riscv64 -plugin /libinsn.so examples/build-wasm/go/stateless-by-wasmtime/target/riscv64gc-unknown-linux-gnu/release/standalone >> go_benchmark_results.txt 2>&1
./docker/docker-shell.sh qemu-riscv64 -plugin /libinsn.so examples/build-wasm/go/stateless-by-wasmer/target/riscv64gc-unknown-linux-gnu/release/standalone >> go_benchmark_results.txt 2>&1
./docker/docker-shell.sh qemu-system-riscv64 -d plugin -machine virt -m 1024M -plugin /libinsn.so -kernel build/bin/stateless.wamr.elf -nographic >> go_benchmark_results.txt 2>&1

echo "Done"

2 changes: 1 addition & 1 deletion platform/riscv-qemu/scripts/c2riscv-qemu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ PREFIX=/opt/riscv-newlib/bin/riscv64-unknown-elf-

# Compiler flags (using -O0 for faster compilation of large generated files)
CFLAGS=(
-march=rv64ima
-march=rv64ima_zicsr
-mabi=lp64
-mcmodel=medany
-specs=nosys.specs
Expand Down
46 changes: 46 additions & 0 deletions platform/riscv-wamr-qemu/file2c/file2c.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* originally from https://github.com/IngwiePhoenix/file2c */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/stat.h>


void help(char* me) {
printf("file2c originally by IngwiePhoenix | Convert a file into a C source file.\n");
printf("Usage: %s inputFile varName > output", me);
}

int main(int argc, char* argv[]) {
if(argc != 3) { help(argv[0]); exit(1); }
else {
char* fn = argv[1];
char* varName = argv[2];
FILE* f = fopen(fn, "rb");
if(!f) { fprintf(stderr, "Error while opening file."); exit(1); }

// Get filesize...
int filesize;
struct stat stat_buf;
int rc = stat(fn, &stat_buf);
filesize = stat_buf.st_size;

printf("const char %s[] = {\n ",varName);
unsigned long n = 0;
while(!feof(f)) {
unsigned char c;
if(fread(&c, 1, 1, f) == 0) break;
++n;
if((int)n == filesize) {
printf("0x%.2X", (int)c);
} else {
printf("0x%.2X, ", (int)c);
}
if(n % 20 == 0) printf("\n ");
}
fclose(f);
printf("\n};\n");
printf("int %s_length=%i;\n",varName,filesize);
}
return 0;
}
52 changes: 52 additions & 0 deletions platform/riscv-wamr-qemu/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include "bh_platform.h"
#include "bh_read_file.h"
#include "wasm_export.h"
#include <stdio.h>

extern const char wasmModuleBuffer[];
extern int wasmModuleBuffer_length;

int main(void) {
int argc = 0;
char *argv[0];

char error_buf[128];

wasm_module_t module;
wasm_module_inst_t module_inst;
wasm_function_inst_t func;
wasm_exec_env_t exec_env;
uint32 size, stack_size = 16*1024*1024;

wasm_runtime_set_log_level(WASM_LOG_LEVEL_VERBOSE);

/* initialize the wasm runtime by default configurations */
if (!wasm_runtime_init()) {
printf("runtime init failed\n");
exit(1);
}

/* parse the WASM file from buffer and create a WASM module */
module = wasm_runtime_load(wasmModuleBuffer, wasmModuleBuffer_length, error_buf, sizeof(error_buf));
if (module == 0) {
printf("runtime load module failed: %s\n", error_buf);
exit(1);
}

/* create an instance of the WASM module (WASM linear memory is ready) */
module_inst = wasm_runtime_instantiate(module, stack_size, 0, error_buf, sizeof(error_buf));
if (module_inst == 0) {
printf("wasm_runtime_instantiate failed as module_inst=%p: %s\n", module_inst, error_buf);
exit(1);
}

if (!wasm_application_execute_main(module_inst, argc, argv)) {
printf("error executing main\n");
printf("exception: %s\n", wasm_runtime_get_exception(module_inst));
}

printf("wasm_runtime_unload...\n");
wasm_runtime_unload(module);

return 0;
}
Loading