Skip to main content

zkvmc_rv32im/
record.rs

1use std::mem::{size_of, transmute};
2
3use crate::opcode::Opcode;
4use zkvmc_trace::{RecordDecoder, RecordEncoder};
5
6pub const RECORD_SIZE: usize = size_of::<Rv32imRecord<u32>>() / 4;
7
8const _: () = assert!(std::mem::size_of::<Rv32imRecord<u32>>() / 4 == RECORD_SIZE);
9
10// Only record enough values to reconstruct the whole trace.
11// The `clock`, `shard`, `prev values` and `timestamp` can be restored in the collect phase.
12#[derive(Debug, Clone, Copy)]
13#[repr(C)]
14pub struct Rv32imRecord<T> {
15    /// The instruction type.
16    pub opcode: Opcode,
17    /// The first operand.
18    pub a: T,
19    /// The first operand memory record.
20    pub a_record: MemoryRecord<T>,
21    /// The second operand.
22    pub b: T,
23    /// The second operand memory record.
24    pub b_record: MemoryRecord<T>,
25    /// The third operand.
26    pub c: T,
27    /// The third operand memory record.
28    pub c_record: MemoryRecord<T>,
29    /// Whether the first operand is register 0.
30    pub op_a_0: OperandA,
31}
32
33#[derive(Debug, Clone, Copy)]
34#[repr(u32)]
35pub enum OperandA {
36    Zero = 0,
37    NonZero = 1,
38}
39
40impl OperandA {
41    pub fn new(r: usize) -> OperandA {
42        if r == 0 {
43            OperandA::Zero
44        } else {
45            OperandA::NonZero
46        }
47    }
48}
49
50#[derive(Debug, Clone, Copy)]
51#[repr(u32)]
52pub enum MemoryRecord<T> {
53    /// No memory access.
54    None,
55    /// Read.
56    Read(T),
57    /// Write.
58    Write(T),
59}
60
61impl RecordDecoder for Rv32imRecord<u32> {
62    type Value = u32;
63
64    fn decode(buf: &[u32]) -> Self {
65        const OFFSET: usize = 0;
66        Self {
67            opcode: unsafe { transmute(buf[OFFSET]) },
68            a: buf[OFFSET + 1],
69            a_record: decode_option_memory_record(buf, OFFSET + 2),
70            b: buf[OFFSET + 4],
71            b_record: decode_option_memory_record(buf, OFFSET + 5),
72            c: buf[OFFSET + 7],
73            c_record: decode_option_memory_record(buf, OFFSET + 8),
74            op_a_0: unsafe { transmute(buf[OFFSET + 10]) },
75        }
76    }
77}
78
79impl<T: Copy> RecordEncoder for Rv32imRecord<T> {
80    type Value = T;
81    const RECORD_SIZE: usize = RECORD_SIZE;
82
83    fn encode(&self, buf: &mut [Self::Value], mut const_value: impl FnMut(u32) -> Self::Value) {
84        const OFFSET: usize = 0;
85        buf[OFFSET] = const_value(self.opcode as u32);
86        buf[OFFSET + 1] = self.a;
87        encode_option_memory_record(buf, OFFSET + 2, self.a_record, &mut const_value);
88        buf[OFFSET + 4] = self.b;
89        encode_option_memory_record(buf, OFFSET + 5, self.b_record, &mut const_value);
90        buf[OFFSET + 7] = self.c;
91        encode_option_memory_record(buf, OFFSET + 8, self.c_record, &mut const_value);
92        buf[OFFSET + 10] = const_value(self.op_a_0 as u32);
93    }
94}
95
96fn encode_option_memory_record<T>(
97    buf: &mut [T],
98    offset: usize,
99    value: MemoryRecord<T>,
100    mut const_value: impl FnMut(u32) -> T,
101) {
102    match value {
103        MemoryRecord::None => {
104            buf[offset] = const_value(0);
105            buf[offset + 1] = const_value(0);
106        }
107        MemoryRecord::Read(v) => {
108            buf[offset] = const_value(1);
109            buf[offset + 1] = v;
110        }
111        MemoryRecord::Write(v) => {
112            buf[offset] = const_value(2);
113            buf[offset + 1] = v;
114        }
115    }
116}
117
118fn decode_option_memory_record(buf: &[u32], offset: usize) -> MemoryRecord<u32> {
119    match buf[offset] {
120        0 => MemoryRecord::None,
121        1 => MemoryRecord::Read(buf[offset + 1]),
122        2 => MemoryRecord::Write(buf[offset + 1]),
123        _ => unreachable!(),
124    }
125}