sp1_core_machine/adapter/
bump.rs1use std::{
2 borrow::{Borrow, BorrowMut},
3 mem::{size_of, MaybeUninit},
4};
5
6use crate::{air::SP1CoreAirBuilder, utils::next_multiple_of_32};
7use hashbrown::HashMap;
8use itertools::Itertools;
9use slop_air::{Air, AirBuilder, BaseAir};
10use slop_algebra::{AbstractField, Field, PrimeField32};
11use slop_matrix::Matrix;
12use sp1_core_executor::{
13 events::{ByteLookupEvent, ByteRecord},
14 ByteOpcode, ExecutionRecord, Program, PC_INC,
15};
16use sp1_derive::AlignedBorrow;
17use sp1_hypercube::air::MachineAir;
18use struct_reflection::{StructReflection, StructReflectionHelper};
19pub(crate) const NUM_STATE_BUMP_COLS: usize = size_of::<StateBumpCols<u8>>();
20
21#[derive(AlignedBorrow, Clone, Copy, StructReflection)]
22#[repr(C)]
23pub struct StateBumpCols<T: Copy> {
24 pub next_clk_32_48: T,
25 pub next_clk_24_32: T,
26 pub next_clk_16_24: T,
27 pub next_clk_0_16: T,
28 pub clk_high: T,
29 pub clk_low: T,
30 pub next_pc: [T; 3],
31 pub pc: [T; 3],
32 pub is_clk: T,
33 pub is_real: T,
34}
35
36pub struct StateBumpChip {}
37
38impl StateBumpChip {
39 pub const fn new() -> Self {
40 Self {}
41 }
42}
43
44impl<F> BaseAir<F> for StateBumpChip {
45 fn width(&self) -> usize {
46 NUM_STATE_BUMP_COLS
47 }
48}
49
50impl<F: PrimeField32> MachineAir<F> for StateBumpChip {
51 type Record = ExecutionRecord;
52
53 type Program = Program;
54
55 fn name(&self) -> &'static str {
56 "StateBump"
57 }
58
59 fn generate_dependencies(&self, input: &Self::Record, output: &mut Self::Record) {
60 let chunk_size = 1;
61 let event_iter = input.bump_state_events.chunks(chunk_size);
62
63 let blu_batches = event_iter
64 .map(|events| {
65 let mut blu: HashMap<ByteLookupEvent, usize> = HashMap::new();
66 events.iter().for_each(|(clk, increment, _, pc)| {
67 let next_clk = clk + increment;
68 let next_clk_0_16 = (next_clk & 0xFFFF) as u16;
69 let next_clk_16_24 = ((next_clk >> 16) & 0xFF) as u8;
70 let next_clk_24_32 = ((next_clk >> 24) & 0xFF) as u8;
71 let next_clk_32_48 = (next_clk >> 32) as u16;
72 let pc_0 = (pc & 0xFFFF) as u16;
73 let pc_1 = ((pc >> 16) & 0xFFFF) as u16;
74 let pc_2 = ((pc >> 32) & 0xFFFF) as u16;
75
76 blu.add_bit_range_check((next_clk_0_16 - 1) / 8, 13);
77 blu.add_bit_range_check(next_clk_32_48, 16);
78 blu.add_u8_range_checks(&[next_clk_16_24, next_clk_24_32]);
79 blu.add_u16_range_checks(&[pc_0, pc_1, pc_2]);
80 });
81 blu
82 })
83 .collect::<Vec<_>>();
84
85 output.add_byte_lookup_events_from_maps(blu_batches.iter().collect_vec());
86 }
87
88 fn num_rows(&self, input: &Self::Record) -> Option<usize> {
89 let nb_rows = input.bump_state_events.len();
90 let size_log2 = input.fixed_log2_rows::<F, _>(self);
91 Some(next_multiple_of_32(nb_rows, size_log2))
92 }
93
94 fn generate_trace_into(
95 &self,
96 input: &Self::Record,
97 _output: &mut Self::Record,
98 buffer: &mut [MaybeUninit<F>],
99 ) {
100 let chunk_size = 1;
101 let padded_nb_rows = <StateBumpChip as MachineAir<F>>::num_rows(self, input).unwrap();
102
103 let num_event_rows = input.bump_state_events.len();
104
105 unsafe {
106 let padding_start = num_event_rows * NUM_STATE_BUMP_COLS;
107 let padding_size = (padded_nb_rows - num_event_rows) * NUM_STATE_BUMP_COLS;
108 if padding_size > 0 {
109 core::ptr::write_bytes(buffer[padding_start..].as_mut_ptr(), 0, padding_size);
110 }
111 }
112
113 let buffer_ptr = buffer.as_mut_ptr() as *mut F;
114 let values = unsafe {
115 core::slice::from_raw_parts_mut(buffer_ptr, num_event_rows * NUM_STATE_BUMP_COLS)
116 };
117
118 values.chunks_mut(chunk_size * NUM_STATE_BUMP_COLS).enumerate().for_each(|(i, rows)| {
119 rows.chunks_mut(NUM_STATE_BUMP_COLS).enumerate().for_each(|(j, row)| {
120 let idx = i * chunk_size + j;
121 let cols: &mut StateBumpCols<F> = row.borrow_mut();
122
123 if idx < input.bump_state_events.len() {
124 let (clk, increment, bump2, pc) = input.bump_state_events[idx];
125
126 let clk_low = ((clk & 0xFFFFFF) + increment) as u32;
127 let clk_high = (clk >> 24) as u32;
128 let next_clk = clk + increment;
129 let next_clk_0_16 = (next_clk & 0xFFFF) as u16;
130 let next_clk_16_24 = ((next_clk >> 16) & 0xFF) as u8;
131 let next_clk_24_32 = ((next_clk >> 24) & 0xFF) as u8;
132 let next_clk_32_48 = (next_clk >> 32) as u16;
133
134 cols.clk_low = F::from_canonical_u32(clk_low);
135 cols.clk_high = F::from_canonical_u32(clk_high);
136 cols.next_clk_0_16 = F::from_canonical_u16(next_clk_0_16);
137 cols.next_clk_16_24 = F::from_canonical_u8(next_clk_16_24);
138 cols.next_clk_24_32 = F::from_canonical_u8(next_clk_24_32);
139 cols.next_clk_32_48 = F::from_canonical_u16(next_clk_32_48);
140
141 cols.next_pc = [
142 F::from_canonical_u16((pc & 0xFFFF) as u16),
143 F::from_canonical_u16(((pc >> 16) & 0xFFFF) as u16),
144 F::from_canonical_u16(((pc >> 32) & 0xFFFF) as u16),
145 ];
146
147 if bump2 {
148 let prev_pc = pc.wrapping_sub(PC_INC as u64);
151 cols.pc = [
152 F::from_canonical_u16((prev_pc & 0xFFFF) as u16)
153 + F::from_canonical_u16(PC_INC as u16),
154 F::from_canonical_u16(((prev_pc >> 16) & 0xFFFF) as u16),
155 F::from_canonical_u16(((prev_pc >> 32) & 0xFFFF) as u16),
156 ];
157 } else {
158 cols.pc = cols.next_pc;
159 }
160
161 if (next_clk >> 24) != (clk >> 24) {
162 cols.is_clk = F::one();
163 } else {
164 cols.is_clk = F::zero();
165 }
166 cols.is_real = F::one();
167 }
168 });
169 });
170 }
171
172 fn included(&self, shard: &Self::Record) -> bool {
173 shard.cpu_event_count != 0
174 }
175
176 fn column_names(&self) -> Vec<String> {
177 StateBumpCols::<F>::struct_reflection().unwrap()
178 }
179}
180
181impl<AB> Air<AB> for StateBumpChip
182where
183 AB: SP1CoreAirBuilder,
184{
185 fn eval(&self, builder: &mut AB) {
186 let main = builder.main();
187 let local = main.row_slice(0);
188 let local: &StateBumpCols<AB::Var> = (*local).borrow();
189 builder.assert_bool(local.is_real);
191
192 builder.receive_state(local.clk_high, local.clk_low, local.pc, local.is_real);
194 builder.send_state(
196 local.next_clk_24_32 + local.next_clk_32_48 * AB::F::from_canonical_u32(1 << 8),
197 local.next_clk_0_16 + local.next_clk_16_24 * AB::F::from_canonical_u32(1 << 16),
198 local.next_pc,
199 local.is_real,
200 );
201
202 builder.send_byte(
205 AB::Expr::from_canonical_u32(ByteOpcode::Range as u32),
206 (local.next_clk_0_16 - AB::Expr::one()) * AB::F::from_canonical_u8(8).inverse(),
207 AB::Expr::from_canonical_u32(13),
208 AB::Expr::zero(),
209 local.is_real,
210 );
211 builder.send_byte(
213 AB::Expr::from_canonical_u32(ByteOpcode::Range as u32),
214 local.next_clk_32_48.into(),
215 AB::Expr::from_canonical_u32(16),
216 AB::Expr::zero(),
217 local.is_real,
218 );
219 builder.slice_range_check_u8(&[local.next_clk_16_24, local.next_clk_24_32], local.is_real);
221
222 builder.assert_bool(local.is_clk);
226 builder.when(local.is_real).assert_eq(
227 local.next_clk_24_32 + local.next_clk_32_48 * AB::F::from_canonical_u32(1 << 8),
228 local.clk_high + local.is_clk,
229 );
230 builder.when(local.is_real).assert_eq(
231 local.next_clk_0_16
232 + local.next_clk_16_24 * AB::F::from_canonical_u32(1 << 16)
233 + local.is_clk * AB::F::from_canonical_u32(1 << 24),
234 local.clk_low,
235 );
236
237 let mut carry = AB::Expr::zero();
240 for i in 0..3 {
241 carry = (carry.clone() + local.pc[i] - local.next_pc[i])
242 * AB::F::from_canonical_u32(1 << 16).inverse();
243 builder.assert_bool(carry.clone());
244 }
245 builder.assert_zero(carry);
246 builder.slice_range_check_u16(&local.next_pc, local.is_real);
247 }
248}