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 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; 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 })
93 }
94 (RV32_ALU_OPCODE, _) => {
95 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; 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 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}