Compiling Rust and other high-level languages to Miden VM #957
Replies: 4 comments 3 replies
-
Here is how one can build a compiler from Wasm to Miden VM using OmniZK framework: pub fn compile(wasm: &[u8]) -> String {
let frontend_config = WasmFrontendConfig::default();
let target_config = MidenTargetConfig::default();
let mut ctx = Context::new();
frontend_config.register(&mut ctx);
target_config.register(&mut ctx);
let wasm_module_op =
ozk_frontend_wasm::parse_module(&mut ctx, wasm, &frontend_config).unwrap();
let miden_prog = run_conversion_passes(&mut ctx, wasm_module_op);
let inst_buf = emit_prog(&ctx, miden_prog, &target_config).unwrap();
inst_buf.pretty_print()
}
fn run_conversion_passes(ctx: &mut Context, wasm_module: ModuleOp) -> Ptr<Operation> {
// we need to wrap the wasm in an op because passes cannot replace the root op
let wrapper_module = builtin::ops::ModuleOp::new(ctx, "wrapper");
wasm_module
.get_operation()
.insert_at_back(wrapper_module.get_body(ctx, 0), ctx);
let mut pass_manager = PassManager::new();
pass_manager.add_pass(Box::<WasmToMidenCFLoweringPass>::default());
pass_manager.add_pass(Box::<WasmToMidenArithLoweringPass>::default());
pass_manager.add_pass(Box::<WasmToMidenFinalLoweringPass>::default());
pass_manager
.run(ctx, wrapper_module.get_operation())
.unwrap();
let inner_module = wrapper_module
.get_body(ctx, 0)
.deref(ctx)
.iter(ctx)
.collect::<Vec<Ptr<Operation>>>()
.first()
.cloned()
.unwrap();
inner_module
} You can define your custom transformations as passes and extend IRs with your custom ops. |
Beta Was this translation helpful? Give feedback.
-
This is very cool! Thank you! (and the whole OmniZK project sounds very interesting!). I haven't looked too deeply yet, but wanted to ask a couple of questions:
|
Beta Was this translation helpful? Give feedback.
-
From what I saw in the
I'd look into exposing Rust field element ops to Wasm in a way that would allow us to recognize them in Wasm and substitute them with Miden field element ops. EDIT: Something along these lines: extern "C" {
fn felt_add(a: u64, b: u64) -> u64;
}
/// A field element
#[derive(Default, Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct Felt(u64);
impl Add for Felt {
type Output = Felt;
fn add(self, other: Self) -> Self::Output {
#[cfg(not(target_arch = "wasm32"))]
{
const M: u64 = 0xFFFFFFFF00000001;
// We compute a + b = a - (p - b).
let (x1, c1) = self.0.overflowing_sub(M - other.0);
let adj = 0u32.wrapping_sub(c1 as u32);
Self(x1.wrapping_sub(adj as u64))
}
#[cfg(target_arch = "wasm32")]
Self(unsafe { felt_add(self.0, other.0) })
}
}
#[no_mangle]
fn run() {
let a = Felt(1);
let b = Felt(2);
let c = a + b;
} is compiled to the following Wasm:
where we substitute Another option, which I like more, is to look into WASI and build something similar to WASI-crypto https://www.fastly.com/blog/standard-cryptography-api-webassembly but for field element operations and all other custom ops like hashing, etc. |
Beta Was this translation helpful? Give feedback.
-
After a deeper dive into MidenIR, I can see its role in your compilation pipeline, and I think transforming Wasm IR into SSA form and targeting MidenIR in OmniZK might be worth the effort. |
Beta Was this translation helpful? Give feedback.
-
Hi! As a side project, I've been working on a compiler framework for zero-knowledge VMs. It's called OmniZK, and the idea behind it is to help build compilers from high-level languages to various ZK VMs. Its design resembles the MLIR (LLVM) architecture, where IR transformations are implemented generically and can be reused with different custom IR dialects.
I'm working on adding Miden VM support to OmniZK and looking for your feedback.
So far, I have implemented Wasm local and global variables to direct memory access ops and I/O via extern functions in Rust 'pub_input/pub_output' (Wasm imports) with the idea of using the Wasm component model and WASI in the future. I plan to tackle control flow conversion next.
To give you an idea of what it'd be capable of, check out the Rust to Triton VM compilation example I've implemented for Triton VM.
I've implemented a small subset of instructions in the Wasm frontend (parser, IR dialect, etc.) and Triton VM backend (IR dialect, codegen, etc.) with Wasm -> Triton VM conversion.
Check out how the following Fibonacci example in Rust:
is compiled to the following fully executable Triton VM code. Keep in mind that it's in the early stage, there are no optimizations yet so the code is quite lengthy.
Beta Was this translation helpful? Give feedback.
All reactions