openvm_transpiler/
transpiler.rs

1use std::rc::Rc;
2
3use eyre::Report;
4use openvm_instructions::{exe::SparseMemoryImage, instruction::Instruction};
5use openvm_stark_backend::p3_field::PrimeField32;
6use thiserror::Error;
7
8use crate::TranspilerExtension;
9
10/// Collection of [`TranspilerExtension`]s.
11/// The transpiler can be configured to transpile any ELF in 32-bit chunks.
12#[derive(Clone)]
13pub struct Transpiler<F> {
14    processors: Vec<Rc<dyn TranspilerExtension<F>>>,
15}
16
17impl<F: PrimeField32> Default for Transpiler<F> {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23#[derive(Error, Debug)]
24pub enum TranspilerError {
25    #[error("ambiguous next instruction")]
26    AmbiguousNextInstruction,
27    #[error("couldn't parse the next instruction: {0:032b}")]
28    ParseError(u32),
29    #[error("processor {processor_index} failed to modify initial memory")]
30    ModifyInitialMemoryFailed {
31        processor_index: usize,
32        #[source]
33        source: Report,
34    },
35}
36
37impl<F: PrimeField32> Transpiler<F> {
38    pub fn new() -> Self {
39        Self { processors: vec![] }
40    }
41
42    pub fn with_processor(self, proc: Rc<dyn TranspilerExtension<F>>) -> Self {
43        let mut procs = self.processors;
44        procs.push(proc);
45        Self { processors: procs }
46    }
47
48    pub fn with_extension<T: TranspilerExtension<F> + 'static>(self, ext: T) -> Self {
49        self.with_processor(Rc::new(ext))
50    }
51
52    /// Iterates over a sequence of 32-bit RISC-V instructions `instructions_u32`. The iterator
53    /// applies every processor in the [`Transpiler`] to determine if one of them knows how to
54    /// transpile the current instruction (and possibly a contiguous section of following
55    /// instructions). If so, it advances the iterator by the amount specified by the processor.
56    /// The transpiler will panic if two different processors claim to know how to transpile the
57    /// same instruction to avoid ambiguity.
58    pub fn transpile(
59        &self,
60        instructions_u32: &[u32],
61    ) -> Result<Vec<Option<Instruction<F>>>, TranspilerError> {
62        let mut instructions = Vec::new();
63        let mut ptr = 0;
64        while ptr < instructions_u32.len() {
65            let mut options = self
66                .processors
67                .iter()
68                .map(|proc| proc.process_custom(&instructions_u32[ptr..]))
69                .filter(|opt| opt.is_some())
70                .collect::<Vec<_>>();
71            if options.is_empty() {
72                return Err(TranspilerError::ParseError(instructions_u32[ptr]));
73            }
74            if options.len() > 1 {
75                return Err(TranspilerError::AmbiguousNextInstruction);
76            }
77            let transpiler_output = options.pop().unwrap().unwrap();
78            instructions.extend(transpiler_output.instructions);
79            ptr += transpiler_output.used_u32s;
80        }
81        Ok(instructions)
82    }
83
84    /// Allows each processor to modify the initial memory state as needed.
85    pub fn modify_initial_memory(
86        &self,
87        init_memory: &mut SparseMemoryImage,
88    ) -> Result<(), TranspilerError> {
89        for (i, processor) in self.processors.iter().enumerate() {
90            processor
91                .modify_initial_memory(init_memory)
92                .map_err(|source| TranspilerError::ModifyInitialMemoryFailed {
93                    processor_index: i,
94                    source,
95                })?;
96        }
97        Ok(())
98    }
99}