openvm_rv32im_transpiler/
lib.rs

1use std::marker::PhantomData;
2
3use openvm_instructions::{
4    instruction::Instruction, riscv::RV32_REGISTER_NUM_LIMBS, LocalOpcode, PhantomDiscriminant,
5    SystemOpcode,
6};
7use openvm_rv32im_guest::{
8    PhantomImm, CSRRW_FUNCT3, CSR_OPCODE, HINT_BUFFER_IMM, HINT_FUNCT3, HINT_STOREW_IMM,
9    PHANTOM_FUNCT3, REVEAL_FUNCT3, RV32M_FUNCT7, RV32_ALU_OPCODE, SYSTEM_OPCODE, TERMINATE_FUNCT3,
10};
11use openvm_stark_backend::p3_field::PrimeField32;
12use openvm_transpiler::{
13    util::{nop, unimp},
14    TranspilerExtension, TranspilerOutput,
15};
16use rrs::InstructionTranspiler;
17use rrs_lib::{
18    instruction_formats::{IType, RType},
19    process_instruction,
20};
21
22mod instructions;
23pub mod rrs;
24pub use instructions::*;
25
26#[derive(Default)]
27pub struct Rv32ITranspilerExtension;
28
29#[derive(Default)]
30pub struct Rv32MTranspilerExtension;
31
32#[derive(Default)]
33pub struct Rv32IoTranspilerExtension;
34
35impl<F: PrimeField32> TranspilerExtension<F> for Rv32ITranspilerExtension {
36    fn process_custom(&self, instruction_stream: &[u32]) -> Option<TranspilerOutput<F>> {
37        let mut transpiler = InstructionTranspiler::<F>(PhantomData);
38        if instruction_stream.is_empty() {
39            return None;
40        }
41        let instruction_u32 = instruction_stream[0];
42
43        let opcode = (instruction_u32 & 0x7f) as u8;
44        let funct3 = ((instruction_u32 >> 12) & 0b111) as u8; // All our instructions are R-, I- or B-type
45
46        let instruction = match (opcode, funct3) {
47            (CSR_OPCODE, _) => {
48                let dec_insn = IType::new(instruction_u32);
49                if dec_insn.funct3 as u8 == CSRRW_FUNCT3 {
50                    // CSRRW
51                    if dec_insn.rs1 == 0 && dec_insn.rd == 0 {
52                        // This resets the CSR counter to zero. Since we don't have any CSR registers, this is a nop.
53                        return Some(TranspilerOutput::one_to_one(nop()));
54                    }
55                }
56                eprintln!(
57                    "Transpiling system / CSR instruction: {:b} (opcode = {:07b}, funct3 = {:03b}) to unimp",
58                    instruction_u32, opcode, funct3
59                );
60                return Some(TranspilerOutput::one_to_one(unimp()));
61            }
62            (SYSTEM_OPCODE, TERMINATE_FUNCT3) => {
63                let dec_insn = IType::new(instruction_u32);
64                Some(Instruction {
65                    opcode: SystemOpcode::TERMINATE.global_opcode(),
66                    c: F::from_canonical_u8(
67                        dec_insn.imm.try_into().expect("exit code must be byte"),
68                    ),
69                    ..Default::default()
70                })
71            }
72            (SYSTEM_OPCODE, PHANTOM_FUNCT3) => {
73                let dec_insn = IType::new(instruction_u32);
74                PhantomImm::from_repr(dec_insn.imm as u16).map(|phantom| match phantom {
75                    PhantomImm::HintInput => Instruction::phantom(
76                        PhantomDiscriminant(Rv32Phantom::HintInput as u16),
77                        F::ZERO,
78                        F::ZERO,
79                        0,
80                    ),
81                    PhantomImm::HintRandom => Instruction::phantom(
82                        PhantomDiscriminant(Rv32Phantom::HintRandom as u16),
83                        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
84                        F::ZERO,
85                        0,
86                    ),
87                    PhantomImm::PrintStr => Instruction::phantom(
88                        PhantomDiscriminant(Rv32Phantom::PrintStr as u16),
89                        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
90                        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs1),
91                        0,
92                    ),
93                })
94            }
95            (RV32_ALU_OPCODE, _) => {
96                // Exclude RV32M instructions from this transpiler extension
97                let dec_insn = RType::new(instruction_u32);
98                let funct7 = dec_insn.funct7 as u8;
99                match funct7 {
100                    RV32M_FUNCT7 => None,
101                    _ => process_instruction(&mut transpiler, instruction_u32),
102                }
103            }
104            _ => process_instruction(&mut transpiler, instruction_u32),
105        };
106
107        instruction.map(TranspilerOutput::one_to_one)
108    }
109}
110
111impl<F: PrimeField32> TranspilerExtension<F> for Rv32MTranspilerExtension {
112    fn process_custom(&self, instruction_stream: &[u32]) -> Option<TranspilerOutput<F>> {
113        if instruction_stream.is_empty() {
114            return None;
115        }
116        let instruction_u32 = instruction_stream[0];
117
118        let opcode = (instruction_u32 & 0x7f) as u8;
119        if opcode != RV32_ALU_OPCODE {
120            return None;
121        }
122
123        let dec_insn = RType::new(instruction_u32);
124        let funct7 = dec_insn.funct7 as u8;
125        if funct7 != RV32M_FUNCT7 {
126            return None;
127        }
128
129        let instruction = process_instruction(
130            &mut InstructionTranspiler::<F>(PhantomData),
131            instruction_u32,
132        );
133
134        instruction.map(TranspilerOutput::one_to_one)
135    }
136}
137
138impl<F: PrimeField32> TranspilerExtension<F> for Rv32IoTranspilerExtension {
139    fn process_custom(&self, instruction_stream: &[u32]) -> Option<TranspilerOutput<F>> {
140        if instruction_stream.is_empty() {
141            return None;
142        }
143        let instruction_u32 = instruction_stream[0];
144
145        let opcode = (instruction_u32 & 0x7f) as u8;
146        let funct3 = ((instruction_u32 >> 12) & 0b111) as u8; // All our instructions are R-, I- or B-type
147
148        if opcode != SYSTEM_OPCODE {
149            return None;
150        }
151        if funct3 != HINT_FUNCT3 && funct3 != REVEAL_FUNCT3 {
152            return None;
153        }
154
155        let instruction = match funct3 {
156            HINT_FUNCT3 => {
157                let dec_insn = IType::new(instruction_u32);
158                let imm_u16 = (dec_insn.imm as u32) & 0xffff;
159                match imm_u16 {
160                    HINT_STOREW_IMM => Some(Instruction::from_isize(
161                        Rv32HintStoreOpcode::HINT_STOREW.global_opcode(),
162                        0,
163                        (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
164                        0,
165                        1,
166                        2,
167                    )),
168                    HINT_BUFFER_IMM => Some(Instruction::from_isize(
169                        Rv32HintStoreOpcode::HINT_BUFFER.global_opcode(),
170                        (RV32_REGISTER_NUM_LIMBS * dec_insn.rs1) as isize,
171                        (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
172                        0,
173                        1,
174                        2,
175                    )),
176                    _ => None,
177                }
178            }
179            REVEAL_FUNCT3 => {
180                let dec_insn = IType::new(instruction_u32);
181                let imm_u16 = (dec_insn.imm as u32) & 0xffff;
182                // REVEAL_RV32 is a pseudo-instruction for STOREW_RV32 a,b,c,1,3
183                Some(Instruction::large_from_isize(
184                    Rv32LoadStoreOpcode::STOREW.global_opcode(),
185                    (RV32_REGISTER_NUM_LIMBS * dec_insn.rs1) as isize,
186                    (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
187                    imm_u16 as isize,
188                    1,
189                    3,
190                    1,
191                    (dec_insn.imm < 0) as isize,
192                ))
193            }
194            _ => return None,
195        };
196
197        instruction.map(TranspilerOutput::one_to_one)
198    }
199}