Skip to content

Conversation

qwang98
Copy link
Collaborator

@qwang98 qwang98 commented Sep 17, 2025

This PR updated the openvm dependency to 1.4.0. A lot of changes were introduced, hence the size of this PR.

Not covered in this PR:

  • gpu: this PR brings us to parity with existing functionality, so only CPU is supported. See [Ovm GPU] WIP feature branch #3347 for GPU support.
  • plonk: since the main proving route for openvm is using a single row, plonk support was deprioritized for now and will be eventually fixed in another PR.

Changes to openvm here
Changes to stark-backend here

Tasks:

  • make compile
  • pil export test
  • implement all extensions (Circuit, Executor, (Prover))
  • make tracegen work
  • re-enable Send + Sync bound on Executor in ovm (instead, removed the need for that bound in ovm)
  • support tco flag

@qwang98 qwang98 marked this pull request as draft September 17, 2025 05:51
[OVM 1.4] `VmConfig` related trait implementations
@Schaeff Schaeff changed the title [DO NOT MERGE] discussion board for OVM-1.4 Update openvm to 1.4.0 Oct 13, 2025
@Schaeff Schaeff marked this pull request as ready for review October 13, 2025 10:42
Copy link
Collaborator

@pacheco pacheco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Still doing the review, these are some initial comments

run: |
rustup toolchain install 1.88
cargo +1.88 install --git 'http://github.com/powdr-labs/openvm.git' --rev fbd69da cargo-openvm
cargo +1.88 install --git 'http://github.com/openvm-org/openvm.git' cargo-openvm
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for the fork here anymore?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't think it was really needed before either. cargo-openvm is used to compile the guest, and our fork does not touch that part of openvm.

with:
repository: powdr-labs/openvm-reth-benchmark
ref: main
ref: 15a1cb1533036f1b5284ddf79b3cadd644778398
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess this needs to get in first, then reth updated, then this can go back to main?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's gonna be a few commits to merge all of this..

openvm-stark-backend = { git = "https://github.com/powdr-labs/stark-backend.git", rev = "e27de8b", default-features = false, features = [
"parallel",
"jemalloc",
"bench-metrics",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bench-metrics not needed anymore for metrics.json?

"jemalloc",
"nightly-features",
"bench-metrics",
"evm-prove",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's this feature for?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It enables verifying in evm, so the prover and verifier for the last recursion step which outputs a bn128 snark iirc.

.iter()
.fold(HashMap::new(), |mut acc, instruction| {
let (air_name, _) = air_by_opcode_id.get_instruction_air_and_id(instruction);
// TODO: main_columns might not be correct, as the RA::with_capacity() uses the following `main_width()`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for now or later PR?

Copy link
Collaborator Author

@qwang98 qwang98 Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's all working properly now, so I'll say for a later PR where we EITHER investigate this and find it to be proper and just remove this comment OR find a "proper" solution.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still keeping this comment here to flag a potential issue though.

.or_insert(RecordArenaDimension {
height: 0,
width: air_by_opcode_id
.get_instruction_air_stats(instruction)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: i think it doesn't matter much, but the calls of get_instruction_air_and_id and get_instruction_air_stats are both searching for the same entry in the OriginalAirs maps... not sure how feasible it is to avoid this due to the trait though

Copy link
Collaborator

@Schaeff Schaeff Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. We don't need to go through the trait as we have access to the concrete type. I refactored to accessing the maps inside OriginalAirs directly.

.filter(|op| {
// Filter out the opcode that we are not interested in
instruction_allowlist.contains(op)
let res = instruction_allowlist
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for the let res i think since we're immediately returning it

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


// Generate an AppProvingKey
let app_pk = Arc::new(sdk.app_keygen(app_config)?);
sdk.app_keygen();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it right to ignore the returned values here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was called for caching, but it's not required, removed!

// Create the SDK
let sdk: GenericSdk<_, SpecializedConfigCpuBuilder, _> = GenericSdk::new(app_config).unwrap();

let mut app_prover = sdk.app_prover(exe.clone())?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be moved inside the else?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


// All gate constraints should be satisfied, but bus interactions are not implemented yet.
#[test]
#[ignore = "Pending bug fix"]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for a future PR? does this need a TODO?

Copy link
Collaborator Author

@qwang98 qwang98 Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. This is PLONK and the bug is described in #3333. We decided to make it run first before fixing PLONK, which is not our default path.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refined the bug in this PR. mock works, but verification fails.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually mock does not work with debug_assert on. Refined and still ignored in this PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah...--release flag can be kind of confusing...

@Schaeff Schaeff mentioned this pull request Oct 14, 2025
7 tasks
original_airs: OriginalAirs<BabyBear>,
base_config: OriginalVmConfig,
periphery: PowdrPeripheryInstances,
record_arena_by_air_name: Rc<RefCell<OriginalArenas>>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this parameter is already inside the precompile being passed?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very good catch...

impl OriginalArenas {
/// Given an estimate of how many times the APC is called in this segment, and the original airs and apc,
/// initializes the arenas iff not already initialized.
fn ensure_initialized(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this gonna be called multiple times? could it just be initialize and fail if already init?

Copy link
Collaborator

@Schaeff Schaeff Oct 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is called each time the apc instruction is executed, but actually does the initialization only at the beginning of each segment.
The state machine is

Uninitialized
   execute_preflight_seg_0
      execute_apc_call (this is where init happens)
      Initialized
      execute_apc_call (here it just adds to the arena)
      ... (more calls to execute_apc_call)
    trace_gen_seg_0 (read from the arenas and create the final table, then reset to Uninitialized)
    Uninitialized
    execute_preflight_seg_1 (now we need to initialize again for seg_1)
    ...

We do need to check if it's initialized at each execute_apc_call because we have no way to know we just started a new segment based on PreflightExecutor::execute. So this check happens in ensure_initialized.

apc,
pub fn arenas(&self) -> &HashMap<String, MatrixRecordArena<BabyBear>> {
match self {
OriginalArenas::Uninitialized => unreachable!(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd use panic since this is not unreachable by construction, its more like an "unwrap"

/// - read from during trace generation
/// - reset to uninitialized after trace generation
#[derive(Default)]
pub enum OriginalArenas {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: could be a struct wrapping an Option?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I agree it is basically an option, but I thought it was clearer to name the possible states explicitly rather than relying on Some/None

apc: Arc<Apc<F, Instr<F>>>,
}
/// Returns a mutable reference to the arenas.
/// Should only be called after `initialize` is called.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no method named initialize

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch!

if let Some(ref ecc) = this.sdk_vm_config.ecc {
complex = complex.extend(ecc)?;

fn get_opcode_name(&self, opcode: usize) -> String {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused i think

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's required for the trait and is used when the metrics flag is on iirc

original_airs: OriginalAirs<BabyBear>,
base_config: OriginalVmConfig,
periphery: PowdrPeripheryInstances,
record_arena_by_air_name: Rc<RefCell<OriginalArenas>>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

already part of precompile i think

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes!

assert!(inventory
.find_air::<VariableRangeCheckerAir>()
.nth(1)
.is_none());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't ensure the air is present, only that there is at most one instance?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point

assert!(inventory
.find_chip::<SharedVariableRangeCheckerChip>()
.nth(1)
.is_none());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants