Skip to content

Commit

Permalink
Expose stdout/stderr in Session (#2189)
Browse files Browse the repository at this point in the history
This PR:
- Makes explicit the notion that 0=stdin, 1=stdout, 2=stdout in the
QueryCallback's "FS"
- Exposes the outputs in Session
- Removes printing to stdout and stderr in the callback itself. This is
now the responsibility of the host if needed.
- Adds the Fibonacci test with stdout to CI using the write mechanism

The idea is that after this we should also expose the proof's publics
and make a stream mechanism for inputs and outputs
  • Loading branch information
leonardoalt authored Dec 4, 2024
1 parent 6897f9b commit 60860b7
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 23 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/pr-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,16 @@ jobs:
key: ${{ runner.os }}-cargo-pr-tests
- name: Install Rust toolchain nightly-2024-09-21 (with clippy and rustfmt)
run: rustup toolchain install nightly-2024-09-21-x86_64-unknown-linux-gnu && rustup component add clippy --toolchain nightly-2024-09-21-x86_64-unknown-linux-gnu && rustup component add rustfmt --toolchain nightly-2024-09-21-x86_64-unknown-linux-gnu
- name: Install nightly
run: rustup toolchain install nightly-2024-08-01-x86_64-unknown-linux-gnu
- name: Install std source
run: rustup component add rust-src --toolchain nightly-2024-08-01-x86_64-unknown-linux-gnu
- name: Install riscv target
run: rustup target add riscv32imac-unknown-none-elf --toolchain nightly-2024-08-01-x86_64-unknown-linux-gnu
- name: Install test dependencies
run: sudo apt-get update && sudo apt-get install -y binutils-riscv64-unknown-elf lld
- name: Run examples
run: cargo run --example hello_world && cargo run --example sqrt_with_publics
run: cargo run --profile pr-tests --example hello_world && cargo run --profile pr-tests --example sqrt_with_publics && cargo run --profile pr-tests --example fibonacci

test_estark_polygon:
needs: build
Expand Down
4 changes: 3 additions & 1 deletion examples/fibonacci/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ default = []
simd = ["powdr/plonky3-simd"]

[dependencies]
powdr = { git = "https://github.com/powdr-labs/powdr", tag = "v0.1.2", features = ["plonky3"] }
powdr = { git = "https://github.com/powdr-labs/powdr", tag = "v0.1.2", features = [
"plonky3",
] }
serde = { version = "1.0", default-features = false, features = [
"alloc",
"derive",
Expand Down
9 changes: 6 additions & 3 deletions examples/fibonacci/guest/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use powdr_riscv_runtime;
use powdr_riscv_runtime::io::read;
use powdr_riscv_runtime::io::{read, write};

fn fib(n: u32) -> u32 {
if n <= 1 {
Expand All @@ -9,6 +9,9 @@ fn fib(n: u32) -> u32 {
}

fn main() {
let n: u32 = read(1);
let _ = fib(n);
// Read input from stdin.
let n: u32 = read(0);
let r = fib(n);
// Write result to stdout.
write(1, r);
}
2 changes: 1 addition & 1 deletion examples/fibonacci/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ fn main() {
.chunk_size_log2(18)
.build()
// Compute Fibonacci of 21 in the guest.
.write(1, &n);
.write(0, &n);

// Fast dry run to test execution.
session.run();
Expand Down
22 changes: 15 additions & 7 deletions pipeline/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ impl HostContext {
(ctx, cb)
}

pub fn clear(&mut self) {
let mut fs = self.file_data.lock().unwrap();
fs.clear();
}

pub fn read<T: DeserializeOwned>(&self, fd: u32) -> Result<T, String> {
let fs = self.file_data.lock().unwrap();
if let Some(data) = fs.get(&fd) {
Expand All @@ -58,15 +63,19 @@ impl HostContext {
.map_err(|e| format!("Invalid char to print: {e}"))?
as char;
match fd {
// stdin, stdout and stderr are supported by the default callback
0..=2 => return Err(format!("Unsupported file descriptor: {fd}")),
// stdin cannot be used for Output
0 => return Err(format!("Unsupported file descriptor: {fd}")),
_ => {
let mut map = fs.lock().unwrap();
map.entry(fd).or_default().push(byte as u8);
}
}
Ok(Some(0.into()))
}
"Clear" => {
fs.lock().unwrap().clear();
Ok(Some(0.into()))
}
_ => Err(format!("Unsupported query: {query}")),
}
})
Expand Down Expand Up @@ -174,15 +183,14 @@ pub fn handle_simple_queries_callback<'a, T: FieldElement>() -> impl QueryCallba
let fd = data[0]
.parse::<u32>()
.map_err(|e| format!("Invalid fd: {e}"))?;
if fd != 0 {
return Err("Debug print requires output fd 0".to_string());
}
let byte = data[1]
.parse::<u8>()
.map_err(|e| format!("Invalid char to print: {e}"))?
as char;
match fd {
1 => print!("{byte}"),
2 => eprint!("{byte}"),
_ => return Err(format!("Unsupported file descriptor: {fd}")),
}
print!("{byte}");
Ok(Some(0.into()))
}
"Hint" => {
Expand Down
5 changes: 3 additions & 2 deletions pipeline/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,7 @@ where
arguments: Arguments::default(),
host_context: ctx,
}
// We add the basic callback functionalities
// to support PrintChar and Hint.
// We add the basic callback functionalities to support PrintChar and Hint.
.add_query_callback(Arc::new(handle_simple_queries_callback()))
.add_query_callback(cb)
}
Expand Down Expand Up @@ -974,6 +973,8 @@ impl<T: FieldElement> Pipeline<T> {
return Ok(witness.clone());
}

self.host_context.clear();

let pil = self.compute_optimized_pil()?;
let fixed_cols = self.compute_fixed_cols()?;

Expand Down
23 changes: 23 additions & 0 deletions powdr-test/examples/fibonacci.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use powdr::Session;

fn main() {
env_logger::init();

let n = 11;
let mut session = Session::builder()
.guest_path("./examples/fibonacci/guest")
.out_path("powdr-target")
.build()
.write(0, &n);

// Fast dry run to test execution.
session.run();

let r: u32 = session.stdout();
assert_eq!(r, 89);

session.prove();

let r: u32 = session.stdout();
assert_eq!(r, 89);
}
10 changes: 10 additions & 0 deletions powdr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,16 @@ impl Session {

self.pipeline.export_verification_key(file).unwrap();
}

pub fn stdout<S: serde::de::DeserializeOwned>(&self) -> S {
let host = self.pipeline.host_context();
host.read(1).unwrap()
}

pub fn stderr<S: serde::de::DeserializeOwned>(&self) -> S {
let host = self.pipeline.host_context();
host.read(2).unwrap()
}
}

fn pil_file_path(asm_name: &Path) -> PathBuf {
Expand Down
14 changes: 8 additions & 6 deletions riscv-executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2289,7 +2289,7 @@ enum ExecMode {
pub fn execute_fast<F: FieldElement>(
asm: &AnalysisASMFile,
initial_memory: MemoryState,
inputs: &Callback<F>,
prover_ctx: &Callback<F>,
bootloader_inputs: &[F],
profiling: Option<ProfilerOptions>,
) -> usize {
Expand All @@ -2299,7 +2299,7 @@ pub fn execute_fast<F: FieldElement>(
None,
None,
initial_memory,
inputs,
prover_ctx,
bootloader_inputs,
usize::MAX,
ExecMode::Fast,
Expand All @@ -2315,7 +2315,7 @@ pub fn execute<F: FieldElement>(
opt_pil: &Analyzed<F>,
fixed: FixedColumns<F>,
initial_memory: MemoryState,
inputs: &Callback<F>,
prover_ctx: &Callback<F>,
bootloader_inputs: &[F],
max_steps_to_execute: Option<usize>,
profiling: Option<ProfilerOptions>,
Expand All @@ -2326,7 +2326,7 @@ pub fn execute<F: FieldElement>(
Some(opt_pil),
Some(fixed),
initial_memory,
inputs,
prover_ctx,
bootloader_inputs,
max_steps_to_execute.unwrap_or(usize::MAX),
ExecMode::Trace,
Expand All @@ -2346,7 +2346,7 @@ fn execute_inner<F: FieldElement>(
opt_pil: Option<&Analyzed<F>>,
fixed: Option<FixedColumns<F>>,
initial_memory: MemoryState,
inputs: &Callback<F>,
prover_ctx: &Callback<F>,
bootloader_inputs: &[F],
max_steps_to_execute: usize,
mode: ExecMode,
Expand Down Expand Up @@ -2408,10 +2408,12 @@ fn execute_inner<F: FieldElement>(
.map(|v| Elem::try_from_fe_as_bin(v).unwrap_or(Elem::Field(*v)))
.collect();

// We clear the QueryCallback's virtual FS before the execution.
(prover_ctx)("Clear").unwrap();
let mut e = Executor {
proc,
label_map,
inputs,
inputs: prover_ctx,
bootloader_inputs,
fixed: fixed.unwrap_or_default(),
program_cols,
Expand Down
2 changes: 1 addition & 1 deletion riscv-runtime/src/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ fn print_prover_char(c: u8) {
let mut value = c as u32;
#[allow(unused_assignments)]
unsafe {
ecall!(Syscall::Output, lateout("a0") value, in("a0") 1, in("a1") value);
ecall!(Syscall::Output, lateout("a0") value, in("a0") 0, in("a1") value);
}
}
2 changes: 1 addition & 1 deletion std/prelude.asm
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ enum Query {
/// Query a prover input (field element) by channel id and index.
Input(int, int),
/// Writes a field element (second argument) to an output channel (first argument).
/// Channel 1 is stdout, 2 is stderr.
/// It is the host's responsibility to give semantics to each channel.
Output(int, fe),
/// This value is not (additionally) constrained by the query.
None,
Expand Down

0 comments on commit 60860b7

Please sign in to comment.