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