openvm_transpiler/
transpiler.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
use std::rc::Rc;

use openvm_instructions::instruction::Instruction;
use openvm_stark_backend::p3_field::PrimeField32;
use thiserror::Error;

use crate::TranspilerExtension;

/// Collection of [`TranspilerExtension`]s.
/// The transpiler can be configured to transpile any ELF in 32-bit chunks.
pub struct Transpiler<F> {
    processors: Vec<Rc<dyn TranspilerExtension<F>>>,
}

impl<F: PrimeField32> Default for Transpiler<F> {
    fn default() -> Self {
        Self::new()
    }
}

#[derive(Error, Debug)]
pub enum TranspilerError {
    #[error("ambiguous next instruction")]
    AmbiguousNextInstruction,
    #[error("couldn't parse the next instruction: {0:032b}")]
    ParseError(u32),
}

impl<F: PrimeField32> Transpiler<F> {
    pub fn new() -> Self {
        Self { processors: vec![] }
    }

    pub fn with_processor(self, proc: Rc<dyn TranspilerExtension<F>>) -> Self {
        let mut procs = self.processors;
        procs.push(proc);
        Self { processors: procs }
    }

    pub fn with_extension<T: TranspilerExtension<F> + 'static>(self, ext: T) -> Self {
        self.with_processor(Rc::new(ext))
    }

    /// Iterates over a sequence of 32-bit RISC-V instructions `instructions_u32`. The iterator
    /// applies every processor in the [`Transpiler`] to determine if one of them knows how to transpile
    /// the current instruction (and possibly a contiguous section of following instructions).
    /// If so, it advances the iterator by the amount specified by the processor.
    /// The transpiler will panic if two different processors claim to know how to transpile the same instruction
    /// to avoid ambiguity.
    pub fn transpile(
        &self,
        instructions_u32: &[u32],
    ) -> Result<Vec<Instruction<F>>, TranspilerError> {
        let mut instructions = Vec::new();
        let mut ptr = 0;
        while ptr < instructions_u32.len() {
            let mut options = self
                .processors
                .iter()
                .map(|proc| proc.process_custom(&instructions_u32[ptr..]))
                .filter(|opt| opt.is_some())
                .collect::<Vec<_>>();
            if options.is_empty() {
                return Err(TranspilerError::ParseError(instructions_u32[ptr]));
            }
            if options.len() > 1 {
                return Err(TranspilerError::AmbiguousNextInstruction);
            }
            let (instruction, advance) = options.pop().unwrap().unwrap();
            instructions.push(instruction);
            ptr += advance;
        }
        Ok(instructions)
    }
}