1use core::iter::{once, repeat};
2use p3_air::{AirBuilder, AirBuilderWithPublicValues};
3use p3_field::AbstractField;
4use sp1_stark::{
5 air::{AirInteraction, BaseAirBuilder, InteractionScope, MachineAirBuilder},
6 InteractionKind,
7};
8
9use super::{
10 Block, InstructionCols, MemoryAccessTimestampCols, MemoryCols, OpcodeSelectorCols,
11 RangeCheckOpcode,
12};
13
14pub trait SP1RecursionAirBuilder:
16 MachineAirBuilder + RecursionMemoryAirBuilder + RecursionInteractionAirBuilder
17{
18}
19
20impl<AB: AirBuilderWithPublicValues + RecursionMemoryAirBuilder> SP1RecursionAirBuilder for AB {}
21impl<AB: BaseAirBuilder> RecursionMemoryAirBuilder for AB {}
22impl<AB: BaseAirBuilder> RecursionInteractionAirBuilder for AB {}
23
24pub trait RecursionMemoryAirBuilder: RecursionInteractionAirBuilder {
25 fn recursion_eval_memory_access<E: Into<Self::Expr> + Clone>(
26 &mut self,
27 timestamp: impl Into<Self::Expr>,
28 addr: impl Into<Self::Expr>,
29 memory_access: &impl MemoryCols<E, Block<E>>,
30 is_real: impl Into<Self::Expr>,
31 ) {
32 let is_real: Self::Expr = is_real.into();
33 self.assert_bool(is_real.clone());
34
35 let timestamp: Self::Expr = timestamp.into();
36 let mem_access = memory_access.access();
37
38 self.eval_memory_access_timestamp(timestamp.clone(), mem_access, is_real.clone());
39
40 let addr = addr.into();
41 let prev_timestamp = mem_access.prev_timestamp.clone().into();
42 let prev_values = once(prev_timestamp)
43 .chain(once(addr.clone()))
44 .chain(memory_access.prev_value().clone().map(Into::into))
45 .collect();
46 let current_values = once(timestamp)
47 .chain(once(addr.clone()))
48 .chain(memory_access.value().clone().map(Into::into))
49 .collect();
50
51 self.receive(
52 AirInteraction::new(prev_values, is_real.clone(), InteractionKind::Memory),
53 InteractionScope::Local,
54 );
55 self.send(
56 AirInteraction::new(current_values, is_real, InteractionKind::Memory),
57 InteractionScope::Local,
58 );
59 }
60
61 #[allow(clippy::manual_repeat_n)]
62 fn recursion_eval_memory_access_single<E: Into<Self::Expr> + Clone>(
63 &mut self,
64 timestamp: impl Into<Self::Expr>,
65 addr: impl Into<Self::Expr>,
66 memory_access: &impl MemoryCols<E, E>,
67 is_real: impl Into<Self::Expr>,
68 ) {
69 let is_real: Self::Expr = is_real.into();
70 self.assert_bool(is_real.clone());
71
72 let timestamp: Self::Expr = timestamp.into();
73 let mem_access = memory_access.access();
74
75 self.eval_memory_access_timestamp(timestamp.clone(), mem_access, is_real.clone());
76
77 let addr = addr.into();
78 let prev_timestamp = mem_access.prev_timestamp.clone().into();
79 let prev_values = once(prev_timestamp)
80 .chain(once(addr.clone()))
81 .chain(once(memory_access.prev_value().clone().into()))
82 .chain(repeat(Self::Expr::zero()).take(3))
83 .collect();
84 let current_values = once(timestamp)
85 .chain(once(addr.clone()))
86 .chain(once(memory_access.value().clone().into()))
87 .chain(repeat(Self::Expr::zero()).take(3))
88 .collect();
89
90 self.receive(
91 AirInteraction::new(prev_values, is_real.clone(), InteractionKind::Memory),
92 InteractionScope::Local,
93 );
94 self.send(
95 AirInteraction::new(current_values, is_real, InteractionKind::Memory),
96 InteractionScope::Local,
97 );
98 }
99
100 fn eval_memory_access_timestamp<E: Into<Self::Expr> + Clone>(
102 &mut self,
103 timestamp: impl Into<Self::Expr>,
104 mem_access: &impl MemoryAccessTimestampCols<E>,
105 is_real: impl Into<Self::Expr> + Clone,
106 ) {
107 let diff_minus_one: Self::Expr =
109 timestamp.into() - mem_access.prev_timestamp().clone().into() - Self::Expr::one();
110
111 self.eval_range_check_28bits(
114 diff_minus_one,
115 mem_access.diff_16bit_limb().clone(),
116 mem_access.diff_12bit_limb().clone(),
117 is_real.clone(),
118 );
119 }
120
121 fn eval_range_check_28bits(
128 &mut self,
129 value: impl Into<Self::Expr>,
130 limb_16: impl Into<Self::Expr> + Clone,
131 limb_12: impl Into<Self::Expr> + Clone,
132 is_real: impl Into<Self::Expr> + Clone,
133 ) {
134 self.when(is_real.clone()).assert_eq(
136 value,
137 limb_16.clone().into() +
138 limb_12.clone().into() * Self::Expr::from_canonical_u32(1 << 16),
139 );
140
141 self.send_range_check(
143 Self::Expr::from_canonical_u8(RangeCheckOpcode::U16 as u8),
144 limb_16,
145 is_real.clone(),
146 );
147
148 self.send_range_check(
149 Self::Expr::from_canonical_u8(RangeCheckOpcode::U12 as u8),
150 limb_12,
151 is_real,
152 )
153 }
154}
155
156pub trait RecursionInteractionAirBuilder: BaseAirBuilder {
158 fn send_range_check(
160 &mut self,
161 range_check_opcode: impl Into<Self::Expr>,
162 val: impl Into<Self::Expr>,
163 is_real: impl Into<Self::Expr>,
164 ) {
165 self.send(
166 AirInteraction::new(
167 vec![range_check_opcode.into(), val.into()],
168 is_real.into(),
169 InteractionKind::Range,
170 ),
171 InteractionScope::Global,
172 );
173 }
174
175 fn receive_range_check(
177 &mut self,
178 range_check_opcode: impl Into<Self::Expr>,
179 val: impl Into<Self::Expr>,
180 is_real: impl Into<Self::Expr>,
181 ) {
182 self.receive(
183 AirInteraction::new(
184 vec![range_check_opcode.into(), val.into()],
185 is_real.into(),
186 InteractionKind::Range,
187 ),
188 InteractionScope::Global,
189 );
190 }
191
192 fn send_program<E: Into<Self::Expr> + Copy>(
193 &mut self,
194 pc: impl Into<Self::Expr>,
195 instruction: InstructionCols<E>,
196 selectors: OpcodeSelectorCols<E>,
197 is_real: impl Into<Self::Expr>,
198 ) {
199 let program_interaction_vals = once(pc.into())
200 .chain(instruction.into_iter().map(|x| x.into()))
201 .chain(selectors.into_iter().map(|x| x.into()))
202 .collect::<Vec<_>>();
203 self.send(
204 AirInteraction::new(program_interaction_vals, is_real.into(), InteractionKind::Program),
205 InteractionScope::Global,
206 );
207 }
208
209 fn receive_program<E: Into<Self::Expr> + Copy>(
210 &mut self,
211 pc: impl Into<Self::Expr>,
212 instruction: InstructionCols<E>,
213 selectors: OpcodeSelectorCols<E>,
214 is_real: impl Into<Self::Expr>,
215 ) {
216 let program_interaction_vals = once(pc.into())
217 .chain(instruction.into_iter().map(|x| x.into()))
218 .chain(selectors.into_iter().map(|x| x.into()))
219 .collect::<Vec<_>>();
220 self.receive(
221 AirInteraction::new(program_interaction_vals, is_real.into(), InteractionKind::Program),
222 InteractionScope::Global,
223 );
224 }
225
226 fn send_table<E: Into<Self::Expr> + Clone>(
227 &mut self,
228 opcode: impl Into<Self::Expr>,
229 table: &[E],
230 is_real: impl Into<Self::Expr>,
231 ) {
232 let table_interaction_vals = table.iter().map(|x| x.clone().into());
233 let values = once(opcode.into()).chain(table_interaction_vals).collect();
234 self.send(
235 AirInteraction::new(values, is_real.into(), InteractionKind::Syscall),
236 InteractionScope::Local,
237 );
238 }
239
240 fn receive_table<E: Into<Self::Expr> + Clone>(
241 &mut self,
242 opcode: impl Into<Self::Expr>,
243 table: &[E],
244 is_real: impl Into<Self::Expr>,
245 ) {
246 let table_interaction_vals = table.iter().map(|x| x.clone().into());
247 let values = once(opcode.into()).chain(table_interaction_vals).collect();
248 self.receive(
249 AirInteraction::new(values, is_real.into(), InteractionKind::Syscall),
250 InteractionScope::Local,
251 );
252 }
253}