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