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};
11use openvm_stark_backend::p3_field::PrimeField32;
12use openvm_transpiler::{
13 util::{nop, unimp},
14 TranspilerExtension, TranspilerOutput,
15};
16use rrs::InstructionTranspiler;
17use rrs_lib::{
18 instruction_formats::{IType, RType},
19 process_instruction,
20};
21
22mod instructions;
23pub mod rrs;
24pub use instructions::*;
25
26#[derive(Default)]
27pub struct Rv32ITranspilerExtension;
28
29#[derive(Default)]
30pub struct Rv32MTranspilerExtension;
31
32#[derive(Default)]
33pub struct Rv32IoTranspilerExtension;
34
35impl<F: PrimeField32> TranspilerExtension<F> for Rv32ITranspilerExtension {
36 fn process_custom(&self, instruction_stream: &[u32]) -> Option<TranspilerOutput<F>> {
37 let mut transpiler = InstructionTranspiler::<F>(PhantomData);
38 if instruction_stream.is_empty() {
39 return None;
40 }
41 let instruction_u32 = instruction_stream[0];
42
43 let opcode = (instruction_u32 & 0x7f) as u8;
44 let funct3 = ((instruction_u32 >> 12) & 0b111) as u8; let instruction = match (opcode, funct3) {
47 (CSR_OPCODE, _) => {
48 let dec_insn = IType::new(instruction_u32);
49 if dec_insn.funct3 as u8 == CSRRW_FUNCT3 {
50 if dec_insn.rs1 == 0 && dec_insn.rd == 0 {
52 return Some(TranspilerOutput::one_to_one(nop()));
54 }
55 }
56 eprintln!(
57 "Transpiling system / CSR instruction: {:b} (opcode = {:07b}, funct3 = {:03b}) to unimp",
58 instruction_u32, opcode, funct3
59 );
60 return Some(TranspilerOutput::one_to_one(unimp()));
61 }
62 (SYSTEM_OPCODE, TERMINATE_FUNCT3) => {
63 let dec_insn = IType::new(instruction_u32);
64 Some(Instruction {
65 opcode: SystemOpcode::TERMINATE.global_opcode(),
66 c: F::from_canonical_u8(
67 dec_insn.imm.try_into().expect("exit code must be byte"),
68 ),
69 ..Default::default()
70 })
71 }
72 (SYSTEM_OPCODE, PHANTOM_FUNCT3) => {
73 let dec_insn = IType::new(instruction_u32);
74 PhantomImm::from_repr(dec_insn.imm as u16).map(|phantom| match phantom {
75 PhantomImm::HintInput => Instruction::phantom(
76 PhantomDiscriminant(Rv32Phantom::HintInput as u16),
77 F::ZERO,
78 F::ZERO,
79 0,
80 ),
81 PhantomImm::HintRandom => Instruction::phantom(
82 PhantomDiscriminant(Rv32Phantom::HintRandom as u16),
83 F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
84 F::ZERO,
85 0,
86 ),
87 PhantomImm::PrintStr => Instruction::phantom(
88 PhantomDiscriminant(Rv32Phantom::PrintStr as u16),
89 F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
90 F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs1),
91 0,
92 ),
93 })
94 }
95 (RV32_ALU_OPCODE, _) => {
96 let dec_insn = RType::new(instruction_u32);
98 let funct7 = dec_insn.funct7 as u8;
99 match funct7 {
100 RV32M_FUNCT7 => None,
101 _ => process_instruction(&mut transpiler, instruction_u32),
102 }
103 }
104 _ => process_instruction(&mut transpiler, instruction_u32),
105 };
106
107 instruction.map(TranspilerOutput::one_to_one)
108 }
109}
110
111impl<F: PrimeField32> TranspilerExtension<F> for Rv32MTranspilerExtension {
112 fn process_custom(&self, instruction_stream: &[u32]) -> Option<TranspilerOutput<F>> {
113 if instruction_stream.is_empty() {
114 return None;
115 }
116 let instruction_u32 = instruction_stream[0];
117
118 let opcode = (instruction_u32 & 0x7f) as u8;
119 if opcode != RV32_ALU_OPCODE {
120 return None;
121 }
122
123 let dec_insn = RType::new(instruction_u32);
124 let funct7 = dec_insn.funct7 as u8;
125 if funct7 != RV32M_FUNCT7 {
126 return None;
127 }
128
129 let instruction = process_instruction(
130 &mut InstructionTranspiler::<F>(PhantomData),
131 instruction_u32,
132 );
133
134 instruction.map(TranspilerOutput::one_to_one)
135 }
136}
137
138impl<F: PrimeField32> TranspilerExtension<F> for Rv32IoTranspilerExtension {
139 fn process_custom(&self, instruction_stream: &[u32]) -> Option<TranspilerOutput<F>> {
140 if instruction_stream.is_empty() {
141 return None;
142 }
143 let instruction_u32 = instruction_stream[0];
144
145 let opcode = (instruction_u32 & 0x7f) as u8;
146 let funct3 = ((instruction_u32 >> 12) & 0b111) as u8; if opcode != SYSTEM_OPCODE {
149 return None;
150 }
151 if funct3 != HINT_FUNCT3 && funct3 != REVEAL_FUNCT3 {
152 return None;
153 }
154
155 let instruction = match funct3 {
156 HINT_FUNCT3 => {
157 let dec_insn = IType::new(instruction_u32);
158 let imm_u16 = (dec_insn.imm as u32) & 0xffff;
159 match imm_u16 {
160 HINT_STOREW_IMM => Some(Instruction::from_isize(
161 Rv32HintStoreOpcode::HINT_STOREW.global_opcode(),
162 0,
163 (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
164 0,
165 1,
166 2,
167 )),
168 HINT_BUFFER_IMM => Some(Instruction::from_isize(
169 Rv32HintStoreOpcode::HINT_BUFFER.global_opcode(),
170 (RV32_REGISTER_NUM_LIMBS * dec_insn.rs1) as isize,
171 (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
172 0,
173 1,
174 2,
175 )),
176 _ => None,
177 }
178 }
179 REVEAL_FUNCT3 => {
180 let dec_insn = IType::new(instruction_u32);
181 let imm_u16 = (dec_insn.imm as u32) & 0xffff;
182 Some(Instruction::large_from_isize(
184 Rv32LoadStoreOpcode::STOREW.global_opcode(),
185 (RV32_REGISTER_NUM_LIMBS * dec_insn.rs1) as isize,
186 (RV32_REGISTER_NUM_LIMBS * dec_insn.rd) as isize,
187 imm_u16 as isize,
188 1,
189 3,
190 1,
191 (dec_insn.imm < 0) as isize,
192 ))
193 }
194 _ => return None,
195 };
196
197 instruction.map(TranspilerOutput::one_to_one)
198 }
199}