openvm_deferral_transpiler/
lib.rs1use eyre::Result;
2use openvm_deferral_guest::{COMMIT_NUM_BYTES, DEFERRAL_FUNCT3, MAX_DEF_CIRCUITS, OPCODE};
3use openvm_instructions::{
4 exe::SparseMemoryImage,
5 instruction::Instruction,
6 riscv::{RV32_MEMORY_AS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS},
7 LocalOpcode, DEFERRAL_AS,
8};
9use openvm_instructions_derive::LocalOpcode;
10use openvm_transpiler::{TranspilerExtension, TranspilerOutput};
11use p3_field::PrimeField32;
12use rrs_lib::instruction_formats::IType;
13use serde::{Deserialize, Serialize};
14use strum::{EnumCount, EnumIter, FromRepr};
15
16#[derive(
17 Copy,
18 Clone,
19 Debug,
20 PartialEq,
21 Eq,
22 PartialOrd,
23 Ord,
24 EnumCount,
25 EnumIter,
26 FromRepr,
27 LocalOpcode,
28 Serialize,
29 Deserialize,
30)]
31#[opcode_offset = 0x800]
32#[repr(usize)]
33pub enum DeferralOpcode {
34 CALL,
35 OUTPUT,
36}
37
38#[derive(Default)]
39pub struct DeferralTranspilerExtension {
40 def_circuit_commits: Vec<[u8; COMMIT_NUM_BYTES]>,
41}
42
43impl DeferralTranspilerExtension {
44 pub fn new(def_circuit_commits: Vec<[u8; COMMIT_NUM_BYTES]>) -> Self {
45 assert!(def_circuit_commits.len() <= MAX_DEF_CIRCUITS as usize);
46 Self {
47 def_circuit_commits,
48 }
49 }
50}
51
52impl<F: PrimeField32> TranspilerExtension<F> for DeferralTranspilerExtension {
53 fn process_custom(&self, instruction_stream: &[u32]) -> Option<TranspilerOutput<F>> {
54 if instruction_stream.is_empty() {
55 return None;
56 }
57
58 let instruction_u32 = instruction_stream[0];
59 let opcode = (instruction_u32 & 0x7f) as u8;
60 let funct3 = ((instruction_u32 >> 12) & 0b111) as u8;
61
62 if opcode != OPCODE {
63 return None;
64 }
65 if funct3 != DEFERRAL_FUNCT3 {
66 return None;
67 }
68
69 let imm12 = ((instruction_u32 >> 20) & 0xfff) as usize;
73 let imm_code = imm12 & 0b11;
74 let def_idx = imm12 >> 2;
75 if def_idx >= MAX_DEF_CIRCUITS as usize {
76 return None;
77 }
78
79 let dec_insn = IType::new(instruction_u32);
80 let def_opcode = DeferralOpcode::from_repr(imm_code)?;
81
82 let instruction = match def_opcode {
83 DeferralOpcode::CALL => Instruction::from_usize(
84 DeferralOpcode::CALL.global_opcode(),
85 [
86 RV32_REGISTER_NUM_LIMBS * dec_insn.rd,
87 RV32_REGISTER_NUM_LIMBS * dec_insn.rs1,
88 def_idx,
89 RV32_REGISTER_AS as usize,
90 RV32_MEMORY_AS as usize,
91 ],
92 ),
93 DeferralOpcode::OUTPUT => Instruction::from_usize(
94 DeferralOpcode::OUTPUT.global_opcode(),
95 [
96 RV32_REGISTER_NUM_LIMBS * dec_insn.rd,
97 RV32_REGISTER_NUM_LIMBS * dec_insn.rs1,
98 def_idx,
99 RV32_REGISTER_AS as usize,
100 RV32_MEMORY_AS as usize,
101 ],
102 ),
103 };
104
105 Some(TranspilerOutput::one_to_one(instruction))
106 }
107
108 fn modify_initial_memory(&self, init_memory: &mut SparseMemoryImage) -> Result<()> {
109 const F_NUM_BYTES: usize = 4;
110 const COMMIT_SIZE: usize = COMMIT_NUM_BYTES / F_NUM_BYTES;
111
112 for (def_idx, commit) in self.def_circuit_commits.iter().enumerate() {
116 let start_cell = 2 * def_idx * COMMIT_SIZE;
117 let start_byte = start_cell * F_NUM_BYTES;
118
119 for (byte_offset, b) in commit.iter().copied().enumerate() {
120 init_memory.insert((DEFERRAL_AS, (start_byte + byte_offset) as u32), b);
121 }
122 }
123
124 Ok(())
125 }
126}