Skip to content

Commit

Permalink
Merge pull request #2926 from o1-labs/martin/open-mips-tests
Browse files Browse the repository at this point in the history
add open mips tests
  • Loading branch information
dannywillems authored Jan 7, 2025
2 parents a340c95 + 4fa1f26 commit 25a8372
Show file tree
Hide file tree
Showing 12 changed files with 211 additions and 67 deletions.
22 changes: 22 additions & 0 deletions .github/actions/build-mips/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: 'Build MIPS Programs'
description: 'Builds MIPS programs for testing'

runs:
using: "composite"
steps:
- name: Cache apt packages
uses: actions/cache@v4
with:
path: |
/var/cache/apt/archives/*.deb
key: ${{ runner.os }}-apt-${{ hashFiles('.github/workflows/o1vm-mips-build.yml') }}

- name: Install MIPS tools
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y binutils-mips-linux-gnu
- name: Build MIPS programs
shell: bash
run: make build-mips-programs
3 changes: 3 additions & 0 deletions .github/workflows/ci-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ jobs:
run: |
make install-test-deps
- name: Build the MIPS binaries
uses: ./.github/actions/build-mips

- name: Run all tests with the code coverage
run: |
eval $(opam env)
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ jobs:
run: |
make install-test-deps
- name: Build the MIPS binaries
uses: ./.github/actions/build-mips

- name: Doc tests
if: ${{ matrix.rust_toolchain_version != env.RUST_TOOLCHAIN_COVERAGE_VERSION }}
run: |
Expand Down
51 changes: 0 additions & 51 deletions .github/workflows/mips-build.yml

This file was deleted.

28 changes: 28 additions & 0 deletions .github/workflows/o1vm-upload-mips-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Upload MIPS Binaries

on:
workflow_dispatch:

jobs:
build_and_upload:
name: Build and Upload MIPS Programs
runs-on: ubuntu-latest
steps:

- uses: actions/checkout@v4
with:
submodules: 'recursive'

- name: Build MIPS binaries
uses: ./.github/actions/build-mips

- name: Create tar archive
run: |
cd o1vm/resources/programs/mips
tar -czf mips-binaries.tar.gz bin/
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: mips-binaries
path: o1vm/resources/programs/mips/mips-binaries.tar.gz
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ ${O1VM_MIPS_SOURCE_DIR}/%.asm: ${OPTIMISM_MIPS_SOURCE_DIR}/%.asm
@echo "Transforming $< to $@, making it compatible for o1vm"
@sed \
-e '/\.balign 4/d' \
-e '/\.set\s*noreorder/d' \
-e 's/^\s*\.set\s*noreorder/.set noreorder/' \
-e '/\.ent\s*test/d' \
-e '/\.end test/d' \
-e 's/\.section .test, "x"/.section .text/' \
Expand Down
3 changes: 3 additions & 0 deletions o1vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ path = "src/lib.rs"
name = "pickles_o1vm"
path = "src/pickles/main.rs"

[features]
open_mips = []

[dependencies]
# FIXME: Only activate this when legacy_o1vm is built
ark-bn254.workspace = true
Expand Down
20 changes: 20 additions & 0 deletions o1vm/src/cannon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,29 @@ pub struct VmConfiguration {
pub proof_fmt: String,
pub snapshot_fmt: String,
pub pprof_cpu: bool,
pub halt_address: Option<u32>,
pub host: Option<HostProgram>,
}

impl Default for VmConfiguration {
fn default() -> Self {
VmConfiguration {
input_state_file: "state.json".to_string(),
output_state_file: "out.json".to_string(),
metadata_file: None,
proof_at: StepFrequency::Never,
stop_at: StepFrequency::Never,
snapshot_state_at: StepFrequency::Never,
info_at: StepFrequency::Never,
proof_fmt: "proof-%d.json".to_string(),
snapshot_fmt: "state-%d.json".to_string(),
pprof_cpu: false,
halt_address: None,
host: None,
}
}
}

#[derive(Debug, Clone)]
pub struct Start {
pub time: std::time::Instant,
Expand Down
11 changes: 11 additions & 0 deletions o1vm/src/cli/cannon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ pub struct MipsVmConfigurationArgs {
)]
snapshot_state_at: StepFrequency,

#[arg(
long = "halt-address",
value_name = "ADDR",
help = "halt address (in hexadecimal). Jumping to this address will halt the program."
)]
halt_address: Option<String>,

#[arg(name = "host", value_name = "HOST", help = "host program specification <host program> [host program arguments]", num_args = 1.., last = true)]
host: Vec<String>,
}
Expand All @@ -78,6 +85,10 @@ impl From<MipsVmConfigurationArgs> for VmConfiguration {
proof_fmt: cfg.proof_fmt,
snapshot_fmt: cfg.snapshot_fmt,
pprof_cpu: cfg.pprof_cpu,
halt_address: cfg.halt_address.map(|s| {
u32::from_str_radix(s.trim_start_matches("0x"), 16)
.expect("Failed to parse halt address as hex")
}),
host: if cfg.host.is_empty() {
None
} else {
Expand Down
9 changes: 8 additions & 1 deletion o1vm/src/interpreters/mips/witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1168,9 +1168,16 @@ impl<Fp: Field, PreImageOracle: PreImageOracleT> Env<Fp, PreImageOracle> {

self.instruction_counter = self.next_instruction_counter();

config.halt_address.iter().for_each(|halt_address: &u32| {
if self.get_instruction_pointer() == (*halt_address as u64) {
debug!("Program jumped to halt address: {:#X}", halt_address);
self.halt = true;
}
});

// Integer division by MAX_ACC to obtain the actual instruction count
if self.halt {
println!(
debug!(
"Halted at step={} instruction={:?}",
self.normalized_instruction_counter(),
opcode
Expand Down
14 changes: 0 additions & 14 deletions o1vm/test-gen-state-json.sh

This file was deleted.

112 changes: 112 additions & 0 deletions o1vm/tests/test_mips_elf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use ark_ff::Field;
use mina_curves::pasta::Fp;
use o1vm::{
cannon::{self, State, VmConfiguration},
elf_loader::Architecture,
interpreters::mips::witness::{self},
preimage_oracle::{NullPreImageOracle, PreImageOracleT},
};
use std::{
fs,
path::{Path, PathBuf},
};

struct MipsTest {
bin_file: PathBuf,
}

// currently excluding any oracle based tests and a select group of tests that are failing
fn is_test_excluded(bin_file: &Path) -> bool {
let file_name = bin_file.file_name().unwrap().to_str().unwrap();
let untested_programs = ["exit_group", "mul"];
file_name.starts_with("oracle") || untested_programs.contains(&file_name)
}

impl MipsTest {
fn parse_state(&self) -> State {
let curr_dir = std::env::current_dir().unwrap();
let path = curr_dir.join(&self.bin_file);
o1vm::elf_loader::parse_elf(Architecture::Mips, &path).unwrap()
}

fn read_word<Fp: Field, T: PreImageOracleT>(env: &mut witness::Env<Fp, T>, addr: u32) -> u32 {
let bytes: [u8; 4] = [
env.get_memory_direct(addr),
env.get_memory_direct(addr + 1),
env.get_memory_direct(addr + 2),
env.get_memory_direct(addr + 3),
];
u32::from_be_bytes(bytes)
}

fn run(&self) -> Result<(), String> {
println!("Running test: {:?}", self.bin_file);
let mut state = self.parse_state();
let halt_address = 0xa7ef00d0_u32;
state.registers[31] = halt_address;

let start = cannon::Start::create(state.step as usize);
let configuration = VmConfiguration {
halt_address: Some(halt_address),
stop_at: cannon::StepFrequency::Exactly(1000),
..Default::default()
};

let mut witness = witness::Env::<Fp, Box<dyn PreImageOracleT>>::create(
cannon::PAGE_SIZE as usize,
state,
Box::new(NullPreImageOracle),
);

while !witness.halt {
witness.step(&configuration, &None, &start);
}

let return_register = 0xbffffff0_u32;
let done_register = return_register + 4;
let result_register = return_register + 8;

let done_value = Self::read_word(&mut witness, done_register);
if done_value != 1 {
return Err(format!(
"Expected done register to be set to 1, got {:#x}",
done_value
));
}

let result_value = Self::read_word(&mut witness, result_register);
if result_value != 1 {
return Err(format!(
"Program {:?} failure: expected result register to contain 1, got {:#x}",
self.bin_file, result_value
));
}

Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
#[cfg_attr(not(feature = "open_mips"), ignore)]
fn open_mips_tests() {
let test_dir = "resources/programs/mips/bin";
let test_files: Vec<MipsTest> = fs::read_dir(test_dir)
.unwrap_or_else(|_| panic!("Error reading directory {}", test_dir))
.filter_map(|entry| entry.ok())
.map(|entry| entry.path())
.filter(|f| f.is_file() && f.extension().is_none() && !is_test_excluded(f))
.map(|f| MipsTest { bin_file: f })
.collect();

for test in test_files {
let test_name = test.bin_file.file_name().unwrap().to_str().unwrap();
if let Err(err) = test.run() {
panic!("Test '{}' failed: {}", test_name, err);
}
}
}
}

0 comments on commit 25a8372

Please sign in to comment.