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};
11pub use openvm_rv32im_guest::{MAX_HINT_BUFFER_WORDS, MAX_HINT_BUFFER_WORDS_BITS};
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                })
93            }
94            (RV32_ALU_OPCODE, _) => {
95                // Exclude RV32M instructions from this transpiler extension
96                let dec_insn = RType::new(instruction_u32);
97                let funct7 = dec_insn.funct7 as u8;
98                match funct7 {
99                    RV32M_FUNCT7 => None,
100                    _ => process_instruction(&mut transpiler, instruction_u32),
101                }
102            }
103            _ => process_instruction(&mut transpiler, instruction_u32),
104        };
105
106        instruction.map(TranspilerOutput::one_to_one)
107    }
108}
109
110impl<F: PrimeField32> TranspilerExtension<F> for Rv32MTranspilerExtension {
111    fn process_custom(&self, instruction_stream: &[u32]) -> Option<TranspilerOutput<F>> {
112        if instruction_stream.is_empty() {
113            return None;
114        }
115        let instruction_u32 = instruction_stream[0];
116
117        let opcode = (instruction_u32 & 0x7f) as u8;
118        if opcode != RV32_ALU_OPCODE {
119            return None;
120        }
121
122        let dec_insn = RType::new(instruction_u32);
123        let funct7 = dec_insn.funct7 as u8;
124        if funct7 != RV32M_FUNCT7 {
125            return None;
126        }
127
128        let instruction = process_instruction(
129            &mut InstructionTranspiler::<F>(PhantomData),
130            instruction_u32,
131        );
132
133        instruction.map(TranspilerOutput::one_to_one)
134    }
135}
136
137impl<F: PrimeField32> TranspilerExtension<F> for Rv32IoTranspilerExtension {
138    fn process_custom(&self, instruction_stream: &[u32]) -> Option<TranspilerOutput<F>> {
139        if instruction_stream.is_empty() {
140            return None;
141        }
142        let instruction_u32 = instruction_stream[0];
143
144        let opcode = (instruction_u32 & 0x7f) as u8;
145        let funct3 = ((instruction_u32 >> 12) & 0b111) as u8; // All our instructions are R-, I- or B-type
146
147        if opcode != SYSTEM_OPCODE {
148            return None;
149        }
150
151        let instruction = match funct3 {
152            HINT_FUNCT3 => {
153                let dec_insn = IType::new(instruction_u32);
154                let imm_u16 = (dec_insn.imm as u32) & 0xffff;
155                match imm_u16 {
156                    HINT_STOREW_IMM => Some(Instruction::from_isize(
157                        Rv32HintStoreOpcode::HINT_STOREW.global_opcode(),
158                        0,
159                        (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
160                        0,
161                        1,
162                        2,
163                    )),
164                    HINT_BUFFER_IMM => Some(Instruction::from_isize(
165                        Rv32HintStoreOpcode::HINT_BUFFER.global_opcode(),
166                        (RV32_REGISTER_NUM_LIMBS * dec_insn.rs1) as isize,
167                        (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
168                        0,
169                        1,
170                        2,
171                    )),
172                    _ => None,
173                }
174            }
175            REVEAL_FUNCT3 => {
176                let dec_insn = IType::new(instruction_u32);
177                let imm_u16 = (dec_insn.imm as u32) & 0xffff;
178                // REVEAL_RV32 is a pseudo-instruction for STOREW_RV32 a,b,c,1,3
179                Some(Instruction::large_from_isize(
180                    Rv32LoadStoreOpcode::STOREW.global_opcode(),
181                    (RV32_REGISTER_NUM_LIMBS * dec_insn.rs1) as isize,
182                    (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
183                    imm_u16 as isize,
184                    1,
185                    3,
186                    1,
187                    (dec_insn.imm < 0) as isize,
188                ))
189            }
190            _ => return None,
191        };
192
193        instruction.map(TranspilerOutput::one_to_one)
194    }
195}