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_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 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; 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 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 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}