diff --git a/README.md b/README.md
index df1afbf..6c14f5f 100644
--- a/README.md
+++ b/README.md
@@ -90,8 +90,8 @@ See the "Known Issues" section for details.
| Program | w2c2
-O0 | w2c2
-O3 | wasmtime | wasmer
(cranelift) | wasmer
(llvm) | WAMR
-O0 | WAMR
-O3 | directly |
|---|---|---|---|---|---|---|---|---|
-| `reva-client-eth` (Rust) | 4,543,397,058 | 1,337,696,305 | 1,074,488,397 | doesn't work | ? | didn't check | ? | 388,564,723 |
-| `stateless` (Go) | 6,039,298,186 | 2,154,036,727 | 874,758,419 | 953,874,491 | ? | 5,427,433,654 | ? | 236,265,327 |
+| `reva-client-eth` (Rust) | 4,543,397,058 | 1,337,696,305 | 1,074,488,397 | doesn't work | ? | 2,472,559,016 | ? | 388,564,723 |
+| `stateless` (Go) | 6,039,298,186 | 2,154,036,727 | 874,758,419 | 953,874,491 | ? | 3,526,934,350 | ? | 236,265,327 |
## Analysis
diff --git a/docker/Dockerfile b/docker/Dockerfile
index c26f545..0942eb4 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -163,7 +163,7 @@ WORKDIR /tmp
# https://github.com/llvm/llvm-project/issues/81440
RUN git clone --branch zkvm https://github.com/psilva261/wasm-micro-runtime-zkvm.git wamr-riscv && \
cd wamr-riscv && \
- git checkout e9df4f8169eaafa387b001c0352aec0ea2080ed3 && \
+ git checkout a93ff4cbbf99d35869fac3e03a012e58fc6cfb9c && \
cmake . \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/opt/wamr-riscv \
diff --git a/examples/build-wasm/go/stateless-by-wasmer/src/main.rs b/examples/build-wasm/go/stateless-by-wasmer/src/main.rs
index f5c75ac..a7873ea 100644
--- a/examples/build-wasm/go/stateless-by-wasmer/src/main.rs
+++ b/examples/build-wasm/go/stateless-by-wasmer/src/main.rs
@@ -1,7 +1,7 @@
use std::sync::Arc;
use anyhow::Result;
-use wasmer::{sys::NativeEngineExt, Engine, Instance, Module, Store};
+use wasmer::{sys::NativeEngineExt, Engine, Function, Instance, Module, Store};
use wasmer_wasix::{runtime::task_manager::tokio::TokioTaskManager, PluggableRuntime, WasiEnv};
fn main() -> Result<()> {
@@ -41,7 +41,13 @@ fn main() -> Result<()> {
.finalize(&mut store)?;
// Create imports from WASI
- let import_object = wasi_env.import_object(&mut store, &module)?;
+ let mut import_object = wasi_env.import_object(&mut store, &module)?;
+
+ let shutdown = Function::new_typed(&mut store, || {
+ println!("shutdown called");
+ });
+
+ import_object.define("testmodule", "shutdown", shutdown);
// Instantiate the module
let instance = Instance::new(&mut store, &module, &import_object)?;
diff --git a/examples/build-wasm/go/stateless-by-wasmtime/src/main.rs b/examples/build-wasm/go/stateless-by-wasmtime/src/main.rs
index 0cd75ca..d232337 100644
--- a/examples/build-wasm/go/stateless-by-wasmtime/src/main.rs
+++ b/examples/build-wasm/go/stateless-by-wasmtime/src/main.rs
@@ -5,12 +5,17 @@ fn main() -> anyhow::Result<()> {
let wasm_bytes = include_bytes!("stateless.cwasm");
let engine = Engine::default();
+
let module = unsafe { Module::deserialize(&engine, wasm_bytes)? };
// Create linker first
let mut linker = Linker::new(&engine);
wasmtime_wasi::p1::add_to_linker_sync(&mut linker, |s| s)?;
+ linker.func_wrap("testmodule", "shutdown", || {
+ println!("shutdown called");
+ })?;
+
// Create WASI context with build_p1()
let wasi = WasiCtx::builder().inherit_stdio().inherit_args().build_p1();
diff --git a/examples/go/empty/example.go b/examples/go/empty/example.go
index da29a2c..5bbaae7 100644
--- a/examples/go/empty/example.go
+++ b/examples/go/empty/example.go
@@ -1,4 +1,8 @@
package main
func main() {
+ // During exit a fatal runtime error occurs in WAMR when
+ // compiled with --bounds-check=0. An early shutdown provides
+ // a reliable workaround.
+ shutdown()
}
diff --git a/examples/go/empty/utils_other.go b/examples/go/empty/utils_other.go
new file mode 100644
index 0000000..575b719
--- /dev/null
+++ b/examples/go/empty/utils_other.go
@@ -0,0 +1,5 @@
+//go:build !wasm
+
+package main
+
+func shutdown() {}
diff --git a/examples/go/empty/utils_wasm.go b/examples/go/empty/utils_wasm.go
new file mode 100644
index 0000000..3bb805e
--- /dev/null
+++ b/examples/go/empty/utils_wasm.go
@@ -0,0 +1,5 @@
+package main
+
+//go:wasmimport testmodule shutdown
+//go:noescape
+func shutdown()
diff --git a/examples/go/fibonacci/example.go b/examples/go/fibonacci/example.go
index 8e0aad1..c2cf7c2 100644
--- a/examples/go/fibonacci/example.go
+++ b/examples/go/fibonacci/example.go
@@ -13,4 +13,9 @@ func main() {
n := 20
result := fibonacci(n)
fmt.Printf("Fibonacci(%d) = %d\n", n, result)
+
+ // During exit a fatal runtime error occurs in WAMR when
+ // compiled with --bounds-check=0. An early shutdown provides
+ // a reliable workaround.
+ shutdown()
}
diff --git a/examples/go/fibonacci/utils_other.go b/examples/go/fibonacci/utils_other.go
new file mode 100644
index 0000000..575b719
--- /dev/null
+++ b/examples/go/fibonacci/utils_other.go
@@ -0,0 +1,5 @@
+//go:build !wasm
+
+package main
+
+func shutdown() {}
diff --git a/examples/go/fibonacci/utils_wasm.go b/examples/go/fibonacci/utils_wasm.go
new file mode 100644
index 0000000..3bb805e
--- /dev/null
+++ b/examples/go/fibonacci/utils_wasm.go
@@ -0,0 +1,5 @@
+package main
+
+//go:wasmimport testmodule shutdown
+//go:noescape
+func shutdown()
diff --git a/examples/go/println/example.go b/examples/go/println/example.go
index b692102..d5a5d19 100644
--- a/examples/go/println/example.go
+++ b/examples/go/println/example.go
@@ -14,5 +14,12 @@ func main() {
debug.SetMemoryLimit(400 * (1 << 20))
fmt.Println("Hello world from golang")
+
+ // During exit a fatal runtime error occurs in WAMR when
+ // compiled with --bounds-check=0. An early shutdown provides
+ // a reliable workaround.
+ shutdown()
+
panic("foo")
+
}
diff --git a/examples/go/println/utils_other.go b/examples/go/println/utils_other.go
new file mode 100644
index 0000000..575b719
--- /dev/null
+++ b/examples/go/println/utils_other.go
@@ -0,0 +1,5 @@
+//go:build !wasm
+
+package main
+
+func shutdown() {}
diff --git a/examples/go/println/utils_wasm.go b/examples/go/println/utils_wasm.go
new file mode 100644
index 0000000..3bb805e
--- /dev/null
+++ b/examples/go/println/utils_wasm.go
@@ -0,0 +1,5 @@
+package main
+
+//go:wasmimport testmodule shutdown
+//go:noescape
+func shutdown()
diff --git a/examples/go/stateless/main.go b/examples/go/stateless/main.go
index fd5f177..36fdd06 100644
--- a/examples/go/stateless/main.go
+++ b/examples/go/stateless/main.go
@@ -110,4 +110,9 @@ func main() {
if receiptRoot == (common.Hash{}) {
panic("Receipt root is empty")
}
+
+ // During exit a fatal runtime error occurs in WAMR when
+ // compiled with --bounds-check=0. An early shutdown provides
+ // a reliable workaround.
+ shutdown()
}
diff --git a/examples/go/stateless/utils_other.go b/examples/go/stateless/utils_other.go
new file mode 100644
index 0000000..575b719
--- /dev/null
+++ b/examples/go/stateless/utils_other.go
@@ -0,0 +1,5 @@
+//go:build !wasm
+
+package main
+
+func shutdown() {}
diff --git a/examples/go/stateless/utils_wasm.go b/examples/go/stateless/utils_wasm.go
new file mode 100644
index 0000000..3bb805e
--- /dev/null
+++ b/examples/go/stateless/utils_wasm.go
@@ -0,0 +1,5 @@
+package main
+
+//go:wasmimport testmodule shutdown
+//go:noescape
+func shutdown()
diff --git a/platform/riscv-qemu-user/custom_imports.c b/platform/riscv-qemu-user/custom_imports.c
new file mode 100644
index 0000000..f22e93b
--- /dev/null
+++ b/platform/riscv-qemu-user/custom_imports.c
@@ -0,0 +1,47 @@
+/* Custom WASM imports for zkvm target
+ *
+ * This file implements custom functions for Go programs using //go:wasmimport.
+ *
+ * This will be used to implement zkvm precompiles and zkvm specific functions that the guest
+ * program needs.
+ *
+ */
+
+#include "w2c2_base.h"
+#include
+
+U32 testmodule__testfunc(void* instance, U32 a, U32 b) {
+ return 1000*a + b;
+}
+
+U32 testmodule__testfunc2(void* instance, U32 a, U32 b) {
+ return a * b;
+}
+
+int testmodule__printk(void* instance, U32 val) {
+ char buf[12];
+ static const char hex[] = "0123456789abcdef";
+
+ buf[0] = '0';
+ buf[1] = 'x';
+ for (int i = 7; i >= 0; --i) {
+ buf[i+2] = hex[val & 0xF];
+ val >>= 4;
+ }
+ buf[10] = '\n';
+ buf[11] = '\0';
+
+ puts(buf);
+}
+
+void testmodule__shutdown(void* instance) {
+ exit(0);
+}
+
+U32 testmodule__input_data_len(void* instance) {
+ return 0;
+}
+
+U32 testmodule__input_data(void* instance, U32 index) {
+ return 0;
+}
\ No newline at end of file
diff --git a/platform/riscv-qemu-user/scripts/c2riscv-qemu-user.sh b/platform/riscv-qemu-user/scripts/c2riscv-qemu-user.sh
index 428cf06..222c8dc 100755
--- a/platform/riscv-qemu-user/scripts/c2riscv-qemu-user.sh
+++ b/platform/riscv-qemu-user/scripts/c2riscv-qemu-user.sh
@@ -93,6 +93,7 @@ INCLUDES=(
# Source files
SOURCES=(
platform/riscv-qemu-user/main.c
+ platform/riscv-qemu-user/custom_imports.c
"$GUEST_DIR/guest.c"
$GUEST_DIR/s0*.c
w2c2/embedded/wasi.c
diff --git a/platform/riscv-wamr-qemu/main.c b/platform/riscv-wamr-qemu/main.c
index 65391fd..3f4e074 100644
--- a/platform/riscv-wamr-qemu/main.c
+++ b/platform/riscv-wamr-qemu/main.c
@@ -2,10 +2,13 @@
#include
#include
#include
+#include
extern const char wasmModuleBuffer[];
extern int wasmModuleBuffer_length;
+void shutdown();
+
int main(void) {
int argc = 0;
char *argv[0];
@@ -18,10 +21,30 @@ int main(void) {
wasm_exec_env_t exec_env;
uint32_t size, stack_size = 16*1024*1024;
+ RuntimeInitArgs init_args;
+ memset(&init_args, 0, sizeof(RuntimeInitArgs));
+
wasm_runtime_set_log_level(WASM_LOG_LEVEL_VERBOSE);
- /* initialize the wasm runtime by default configurations */
- if (!wasm_runtime_init()) {
+ /* Define an array of NativeSymbol for the APIs to be exported. */
+ static NativeSymbol native_symbols[] = {
+ {
+ "shutdown", // the name of WASM function name
+ shutdown, // the native function pointer
+ "()", // the function prototype signature, avoid to use i32
+ NULL // attachment is NULL
+ },
+ };
+
+ init_args.mem_alloc_type = Alloc_With_System_Allocator;
+
+ /* Native symbols need below registration phase */
+ init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol);
+ init_args.native_module_name = "testmodule";
+ init_args.native_symbols = native_symbols;
+
+ /* initialize the wasm runtime with init_args */
+ if (!wasm_runtime_full_init(&init_args)) {
printf("runtime init failed\n");
exit(1);
}
diff --git a/platform/riscv-wamr-qemu/scripts/wasm2wamr-qemu.sh b/platform/riscv-wamr-qemu/scripts/wasm2wamr-qemu.sh
index 795ee68..2d1b754 100755
--- a/platform/riscv-wamr-qemu/scripts/wasm2wamr-qemu.sh
+++ b/platform/riscv-wamr-qemu/scripts/wasm2wamr-qemu.sh
@@ -24,12 +24,12 @@ if [ $# -lt 2 ]; then
echo " output-elf Output RISC-V ELF binary path"
echo ""
echo "Example:"
- echo " $0 build/.c-packages/println build/bin/println.riscv.elf"
+ echo " $0 examples/build-wasm/go/println.wasm build/bin/println.wamr.elf"
echo ""
echo "To run in QEMU:"
echo " ./docker/docker-shell.sh qemu-system-riscv64 -machine virt \\"
echo " -m 1024M -d plugin -plugin /libinsn.so \\"
- echo " -kernel build/bin/println.riscv.elf -nographic \\"
+ echo " -kernel build/bin/println.wamr.elf -nographic \\"
echo " -semihosting-config enable=on,target=native"
echo ""
exit 1
@@ -90,7 +90,7 @@ wamrc \
--cpu-features='+i,+m,+a' \
--opt-level=0 \
--size-level=1 \
- --bounds-checks=1 \
+ --bounds-checks=0 \
-o $OUTPUT.riscv64.wamr $1
gcc platform/riscv-wamr-qemu/file2c/file2c.c \
@@ -182,7 +182,7 @@ if [ $? -eq 0 ] && [ -f "$OUTPUT" ]; then
echo "Size: $SIZE"
echo ""
echo "To run in QEMU:"
- echo " ./docker/docker-shell.sh qemu-system-riscv64 -machine virt \\"
+ echo " ./docker-shell.sh qemu-system-riscv64 -machine virt \\"
echo " -m 1024M -d plugin -plugin /libinsn.so \\"
echo " -kernel $OUTPUT -nographic \\"
echo " -semihosting-config enable=on,target=native"
diff --git a/platform/riscv-wamr-qemu/startup.S b/platform/riscv-wamr-qemu/startup.S
index 0fb4e34..f384bb0 100644
--- a/platform/riscv-wamr-qemu/startup.S
+++ b/platform/riscv-wamr-qemu/startup.S
@@ -1,5 +1,6 @@
.section .text.init
.global _start
+.global shutdown
_start:
la sp, _stack_top
@@ -17,6 +18,7 @@ bss_done:
/* Call main */
call main
+shutdown:
/* https://github.com/xypron/riscv_test_payload (MIT-licensed) */
li a7, 0x53525354 # SBI extension: SRST (System Reset)
li a6, 0 # Function: system reset
diff --git a/platform/riscv-wamr-qemu/syscalls.c b/platform/riscv-wamr-qemu/syscalls.c
index 593ab63..1b9cf19 100644
--- a/platform/riscv-wamr-qemu/syscalls.c
+++ b/platform/riscv-wamr-qemu/syscalls.c
@@ -1,7 +1,8 @@
-#include
-#include
#include
+#include
#include
+#include
+#include
#include "uart.h"
diff --git a/rust_benchmark.sh b/rust_benchmark.sh
index bca8b8a..92df715 100755
--- a/rust_benchmark.sh
+++ b/rust_benchmark.sh
@@ -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
/root/.wasmer/bin/wasmer compile --cranelift --target riscv64gc-unknown-linux-gnu examples/build-wasm/rust/reva-client-eth.wasm -o examples/build-wasm/rust/reva-client-eth-by-wasmer/src/reva-client-eth.wasmu
+echo "Transpiling WASM to WAMR AOT with wamrc..."
+
+./platform/riscv-wamr-qemu/scripts/wasm2wamr-qemu.sh examples/build-wasm/rust/reva-client-eth.wasm build/bin/reva-client-eth.wamr.elf
+
echo "Compiling C to RISCV..."
OPT_LEVEL="-O0" ./platform/riscv-qemu/scripts/c2riscv-qemu.sh build/c-packages/reva-client-eth/ build/bin/reva-client-eth.riscv.O0.elf
@@ -38,6 +42,7 @@ run_qemu "$success_string" "false" "native" "examples/rust/reva-client-e
run_qemu "$success_string" "true" "w2c2-O0" "build/bin/reva-client-eth.riscv.O0.elf"
run_qemu "$success_string" "true" "w2c2-O3" "build/bin/reva-client-eth.riscv.O3.elf"
run_qemu "$success_string" "false" "wasmtime" "examples/build-wasm/rust/reva-client-eth-by-wasmtime/target/riscv64gc-unknown-linux-gnu/release/standalone"
+run_qemu "$success_string" "true" "wamr" "build/bin/reva-client-eth.wamr.elf"
# `reva-client-eth` via `wasmer` does not work for some reason. The root cause is not yet known. The error is:
# Error: RuntimeError: out of bounds memory access