Skip to main content

sp1_core_executor/minimal/
postprocess.rs

1use hashbrown::HashSet;
2
3use crate::{
4    events::{MemoryInitializeFinalizeEvent, MemoryRecord},
5    ExecutionRecord, MinimalExecutor,
6};
7
8impl MinimalExecutor {
9    /// Postprocess into an existing [`ExecutionRecord`],
10    /// consisting of all the [`MemoryInitializeFinalizeEvent`]s.
11    #[tracing::instrument(name = "emit globals", skip_all)]
12    pub fn emit_globals(
13        &self,
14        record: &mut ExecutionRecord,
15        final_registers: [MemoryRecord; 32],
16        mut touched_addresses: HashSet<u64>,
17    ) {
18        // Add all the finalize addresses to the touched addresses.
19        touched_addresses.extend(self.program().memory_image.keys().copied());
20
21        record.global_memory_initialize_events.extend(
22            final_registers
23                .iter()
24                .enumerate()
25                .filter(|(_, e)| e.timestamp != 0)
26                .map(|(i, _)| MemoryInitializeFinalizeEvent::initialize(i as u64, 0)),
27        );
28
29        record.global_memory_finalize_events.extend(
30            final_registers.iter().enumerate().filter(|(_, e)| e.timestamp != 0).map(
31                |(i, entry)| {
32                    MemoryInitializeFinalizeEvent::finalize(i as u64, entry.value, entry.timestamp)
33                },
34            ),
35        );
36
37        let hint_init_events: Vec<MemoryInitializeFinalizeEvent> = self
38            .hints()
39            .iter()
40            .flat_map(|(addr, value)| chunked_memory_init_events(*addr, value))
41            .collect::<Vec<_>>();
42        let hint_addrs = hint_init_events.iter().map(|event| event.addr).collect::<HashSet<_>>();
43
44        // Initialize the all the hints written during execution.
45        record.global_memory_initialize_events.extend(hint_init_events);
46
47        // Initialize the memory addresses that were touched during execution.
48        // We don't initialize the memory addresses that were in the program image, since they were
49        // initialized in the MemoryProgram chip.
50        let memory_init_events = touched_addresses
51            .iter()
52            .filter(|addr| !self.program().memory_image.contains_key(*addr))
53            .filter(|addr| !hint_addrs.contains(*addr))
54            .map(|addr| MemoryInitializeFinalizeEvent::initialize(*addr, 0));
55        record.global_memory_initialize_events.extend(memory_init_events);
56
57        // Ensure all the hinted addresses are initialized.
58        touched_addresses.extend(hint_addrs);
59
60        // Finalize the memory addresses that were touched during execution.
61        for addr in &touched_addresses {
62            let entry = self.get_memory_value(*addr);
63
64            record.global_memory_finalize_events.push(MemoryInitializeFinalizeEvent::finalize(
65                *addr,
66                entry.value,
67                entry.clk,
68            ));
69        }
70    }
71
72    /// Get set of addresses that were hinted.
73    #[must_use]
74    pub fn get_hint_event_addrs(&self) -> HashSet<u64> {
75        let events = self
76            .hints()
77            .iter()
78            .flat_map(|(addr, value)| chunked_memory_init_events(*addr, value))
79            .collect::<Vec<_>>();
80        let hint_event_addrs = events.iter().map(|event| event.addr).collect::<HashSet<_>>();
81
82        hint_event_addrs
83    }
84}
85
86/// Given some contiguous memory, create a series of initialize and finalize events.
87///
88/// The events are created in chunks of 8 bytes.
89///
90/// The last chunk is not guaranteed to be 8 bytes, so we need to handle that case by padding with
91/// 0s.
92#[must_use]
93pub fn chunked_memory_init_events(start: u64, bytes: &[u8]) -> Vec<MemoryInitializeFinalizeEvent> {
94    let chunks = bytes.chunks_exact(8);
95    let num_chunks = chunks.len();
96    let last = chunks.remainder();
97
98    let mut output = Vec::with_capacity(num_chunks + 1);
99
100    for (i, chunk) in chunks.enumerate() {
101        let addr = start + i as u64 * 8;
102        let value = u64::from_le_bytes(chunk.try_into().unwrap());
103        output.push(MemoryInitializeFinalizeEvent::initialize(addr, value));
104    }
105
106    if !last.is_empty() {
107        let addr = start + num_chunks as u64 * 8;
108        let buf = {
109            let mut buf = [0u8; 8];
110            buf[..last.len()].copy_from_slice(last);
111            buf
112        };
113
114        let value = u64::from_le_bytes(buf);
115        output.push(MemoryInitializeFinalizeEvent::initialize(addr, value));
116    }
117
118    output
119}