What should be done?
The Miden VM does not directly provide any stdio functionality, but it does provide certain escape hatches for hosts to provide custom behavior for MASM programs being executed. The primary ones are emit (for events) and trace (for discardable instrumentation/events). The Host callbacks for these can access the processor state, and in the case of emit, also produce side effects via the advice provider.
The downside to emit, is that it is an actual operation in MAST, which means that when stripping debug info/instrumentation, emit would remain in the program. The trace "instruction" however, is actually a decorator, not an operation, and so it is stripped in those scenarios. This makes it particularly ideal for debug and instrumentation use cases where it is desirable to strip the instrumentation in release builds.
We want to implement the ability to print Rust strings during execution using trace events.
How should it be done?
The implementation I've sketched out consists of implementing a new compiler intrinsic (and corresponding primop in the hir dialect) that prints a Rust string reference (i.e. &str, a fat pointer), by emitting trace.PRINT_TRACE_ID while the string reference is on top of the operand stack.
The intrinsic signature in Rust would be unsafe extern "C" fn __println(ptr: *const u8, len: usize), which would translate to Wasm as func $println (param i32 i32), where the first parameter is the pointer to the start of the string, the second is the length. This would be converted to the primop, i.e. hir.println, by the frontend.
We'd surface the low-level plumbing for this in the SDK via:
pub fn println(s: &str) {
unsafe {
let bytes = s.as_bytes();
__println(bytes.as_ptr(), bytes.len());
}
}
A related task to this one is providing our own println! macro that formats and prints a string using the println intrinsic.
The host side of all this would live in miden-debug, by handling PRINT_TRACE_ID via the on_trace callback as follows:
- Extract the string reference components from the top of the operand stack, accessing it via
ProcessState
- Validate that the string reference meets certain criteria (i.e. the memory address is in the addressable range, the size of the string is no larger than some soft limit)
- Read the raw bytes from linear memory via
ProcessState
- Convert the bytes to a Rust string using
core::str::from_utf8, and then print them to stdout
When is this task done?
With the plumbing above implemented, there are two objectives here:
- Calling
println from Rust prints the string correctly to stdout when executed under the debug executor
- We want to support printing panic messages using
println as follows, and validate it with a test:
#[panic_handler]
fn my_panic(info: &core::panic::PanicInfo) -> ! {
if let Some(message) = info.message().as_str() {
println(message);
}
core::arch::wasm32::unreachable()
}
In the future we may expand on this to format PanicInfo with a non-static message, but we'll punt on that initially.
Additional context
No response
What should be done?
The Miden VM does not directly provide any stdio functionality, but it does provide certain escape hatches for hosts to provide custom behavior for MASM programs being executed. The primary ones are
emit(for events) andtrace(for discardable instrumentation/events). TheHostcallbacks for these can access the processor state, and in the case ofemit, also produce side effects via the advice provider.The downside to
emit, is that it is an actual operation in MAST, which means that when stripping debug info/instrumentation,emitwould remain in the program. Thetrace"instruction" however, is actually a decorator, not an operation, and so it is stripped in those scenarios. This makes it particularly ideal for debug and instrumentation use cases where it is desirable to strip the instrumentation in release builds.We want to implement the ability to print Rust strings during execution using
traceevents.How should it be done?
The implementation I've sketched out consists of implementing a new compiler intrinsic (and corresponding primop in the
hirdialect) that prints a Rust string reference (i.e.&str, a fat pointer), by emittingtrace.PRINT_TRACE_IDwhile the string reference is on top of the operand stack.The intrinsic signature in Rust would be
unsafe extern "C" fn __println(ptr: *const u8, len: usize), which would translate to Wasm asfunc $println (param i32 i32), where the first parameter is the pointer to the start of the string, the second is the length. This would be converted to the primop, i.e.hir.println, by the frontend.We'd surface the low-level plumbing for this in the SDK via:
A related task to this one is providing our own
println!macro that formats and prints a string using theprintlnintrinsic.The host side of all this would live in
miden-debug, by handlingPRINT_TRACE_IDvia theon_tracecallback as follows:ProcessStateProcessStatecore::str::from_utf8, and then print them to stdoutWhen is this task done?
With the plumbing above implemented, there are two objectives here:
printlnfrom Rust prints the string correctly to stdout when executed under the debug executorprintlnas follows, and validate it with a test:In the future we may expand on this to format
PanicInfowith a non-static message, but we'll punt on that initially.Additional context
No response