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: {:b} (opcode = {:07b}, funct3 = {:03b}) to unimp",
60                    instruction_u32, opcode, funct3
61                );
62                return Some(TranspilerOutput::one_to_one(unimp()));
63            }
64            (SYSTEM_OPCODE, TERMINATE_FUNCT3) => {
65                let dec_insn = IType::new(instruction_u32);
66                Some(Instruction {
67                    opcode: SystemOpcode::TERMINATE.global_opcode(),
68                    c: F::from_canonical_u8(
69                        dec_insn.imm.try_into().expect("exit code must be byte"),
70                    ),
71                    ..Default::default()
72                })
73            }
74            (SYSTEM_OPCODE, PHANTOM_FUNCT3) => {
75                let dec_insn = IType::new(instruction_u32);
76                PhantomImm::from_repr(dec_insn.imm as u16).map(|phantom| match phantom {
77                    PhantomImm::HintInput => Instruction::phantom(
78                        PhantomDiscriminant(Rv32Phantom::HintInput as u16),
79                        F::ZERO,
80                        F::ZERO,
81                        0,
82                    ),
83                    PhantomImm::HintRandom => Instruction::phantom(
84                        PhantomDiscriminant(Rv32Phantom::HintRandom as u16),
85                        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
86                        F::ZERO,
87                        0,
88                    ),
89                    PhantomImm::PrintStr => Instruction::phantom(
90                        PhantomDiscriminant(Rv32Phantom::PrintStr as u16),
91                        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
92                        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs1),
93                        0,
94                    ),
95                    PhantomImm::HintLoadByKey => Instruction::phantom(
96                        PhantomDiscriminant(Rv32Phantom::HintLoadByKey as u16),
97                        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
98                        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs1),
99                        0,
100                    ),
101                })
102            }
103            (RV32_ALU_OPCODE, _) => {
104                // Exclude RV32M instructions from this transpiler extension
105                let dec_insn = RType::new(instruction_u32);
106                let funct7 = dec_insn.funct7 as u8;
107                match funct7 {
108                    RV32M_FUNCT7 => None,
109                    _ => process_instruction(&mut transpiler, instruction_u32),
110                }
111            }
112            _ => process_instruction(&mut transpiler, instruction_u32),
113        };
114
115        instruction.map(TranspilerOutput::one_to_one)
116    }
117}
118
119impl<F: PrimeField32> TranspilerExtension<F> for Rv32MTranspilerExtension {
120    fn process_custom(&self, instruction_stream: &[u32]) -> Option<TranspilerOutput<F>> {
121        if instruction_stream.is_empty() {
122            return None;
123        }
124        let instruction_u32 = instruction_stream[0];
125
126        let opcode = (instruction_u32 & 0x7f) as u8;
127        if opcode != RV32_ALU_OPCODE {
128            return None;
129        }
130
131        let dec_insn = RType::new(instruction_u32);
132        let funct7 = dec_insn.funct7 as u8;
133        if funct7 != RV32M_FUNCT7 {
134            return None;
135        }
136
137        let instruction = process_instruction(
138            &mut InstructionTranspiler::<F>(PhantomData),
139            instruction_u32,
140        );
141
142        instruction.map(TranspilerOutput::one_to_one)
143    }
144}
145
146impl<F: PrimeField32> TranspilerExtension<F> for Rv32IoTranspilerExtension {
147    fn process_custom(&self, instruction_stream: &[u32]) -> Option<TranspilerOutput<F>> {
148        if instruction_stream.is_empty() {
149            return None;
150        }
151        let instruction_u32 = instruction_stream[0];
152
153        let opcode = (instruction_u32 & 0x7f) as u8;
154        let funct3 = ((instruction_u32 >> 12) & 0b111) as u8; // All our instructions are R-, I- or B-type
155
156        if opcode != SYSTEM_OPCODE {
157            return None;
158        }
159
160        let instruction = match funct3 {
161            HINT_FUNCT3 => {
162                let dec_insn = IType::new(instruction_u32);
163                let imm_u16 = (dec_insn.imm as u32) & 0xffff;
164                match imm_u16 {
165                    HINT_STOREW_IMM => Some(Instruction::from_isize(
166                        Rv32HintStoreOpcode::HINT_STOREW.global_opcode(),
167                        0,
168                        (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
169                        0,
170                        1,
171                        2,
172                    )),
173                    HINT_BUFFER_IMM => Some(Instruction::from_isize(
174                        Rv32HintStoreOpcode::HINT_BUFFER.global_opcode(),
175                        (RV32_REGISTER_NUM_LIMBS * dec_insn.rs1) as isize,
176                        (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
177                        0,
178                        1,
179                        2,
180                    )),
181                    _ => None,
182                }
183            }
184            REVEAL_FUNCT3 => {
185                let dec_insn = IType::new(instruction_u32);
186                let imm_u16 = (dec_insn.imm as u32) & 0xffff;
187                // REVEAL_RV32 is a pseudo-instruction for STOREW_RV32 a,b,c,1,3
188                Some(Instruction::large_from_isize(
189                    Rv32LoadStoreOpcode::STOREW.global_opcode(),
190                    (RV32_REGISTER_NUM_LIMBS * dec_insn.rs1) as isize,
191                    (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
192                    imm_u16 as isize,
193                    1,
194                    3,
195                    1,
196                    (dec_insn.imm < 0) as isize,
197                ))
198            }
199            NATIVE_STOREW_FUNCT3 => {
200                // NATIVE_STOREW is a pseudo-instruction for STOREW_RV32 a,b,0,1,4
201                let dec_insn = RType::new(instruction_u32);
202                if dec_insn.funct7 != NATIVE_STOREW_FUNCT7 {
203                    return None;
204                }
205                Some(Instruction::large_from_isize(
206                    Rv32LoadStoreOpcode::STOREW.global_opcode(),
207                    (RV32_REGISTER_NUM_LIMBS * dec_insn.rs1) as isize,
208                    (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
209                    0,
210                    1,
211                    4,
212                    1,
213                    0,
214                ))
215            }
216            _ => return None,
217        };
218
219        instruction.map(TranspilerOutput::one_to_one)
220    }
221}