1use core::iter::{once, repeat_n};
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 fn recursion_eval_memory_access_single<E: Into<Self::Expr> + Clone>(
62 &mut self,
63 timestamp: impl Into<Self::Expr>,
64 addr: impl Into<Self::Expr>,
65 memory_access: &impl MemoryCols<E, E>,
66 is_real: impl Into<Self::Expr>,
67 ) {
68 let is_real: Self::Expr = is_real.into();
69 self.assert_bool(is_real.clone());
70
71 let timestamp: Self::Expr = timestamp.into();
72 let mem_access = memory_access.access();
73
74 self.eval_memory_access_timestamp(timestamp.clone(), mem_access, is_real.clone());
75
76 let addr = addr.into();
77 let prev_timestamp = mem_access.prev_timestamp.clone().into();
78 let prev_values = once(prev_timestamp)
79 .chain(once(addr.clone()))
80 .chain(once(memory_access.prev_value().clone().into()))
81 .chain(repeat_n(Self::Expr::zero(), 3))
82 .collect();
83 let current_values = once(timestamp)
84 .chain(once(addr.clone()))
85 .chain(once(memory_access.value().clone().into()))
86 .chain(repeat_n(Self::Expr::zero(), 3))
87 .collect();
88
89 self.receive(
90 AirInteraction::new(prev_values, is_real.clone(), InteractionKind::Memory),
91 InteractionScope::Local,
92 );
93 self.send(
94 AirInteraction::new(current_values, is_real, InteractionKind::Memory),
95 InteractionScope::Local,
96 );
97 }
98
99 fn eval_memory_access_timestamp<E: Into<Self::Expr> + Clone>(
101 &mut self,
102 timestamp: impl Into<Self::Expr>,
103 mem_access: &impl MemoryAccessTimestampCols<E>,
104 is_real: impl Into<Self::Expr> + Clone,
105 ) {
106 let diff_minus_one: Self::Expr =
108 timestamp.into() - mem_access.prev_timestamp().clone().into() - Self::Expr::one();
109
110 self.eval_range_check_28bits(
113 diff_minus_one,
114 mem_access.diff_16bit_limb().clone(),
115 mem_access.diff_12bit_limb().clone(),
116 is_real.clone(),
117 );
118 }
119
120 fn eval_range_check_28bits(
127 &mut self,
128 value: impl Into<Self::Expr>,
129 limb_16: impl Into<Self::Expr> + Clone,
130 limb_12: impl Into<Self::Expr> + Clone,
131 is_real: impl Into<Self::Expr> + Clone,
132 ) {
133 self.when(is_real.clone()).assert_eq(
135 value,
136 limb_16.clone().into() +
137 limb_12.clone().into() * Self::Expr::from_canonical_u32(1 << 16),
138 );
139
140 self.send_range_check(
142 Self::Expr::from_canonical_u8(RangeCheckOpcode::U16 as u8),
143 limb_16,
144 is_real.clone(),
145 );
146
147 self.send_range_check(
148 Self::Expr::from_canonical_u8(RangeCheckOpcode::U12 as u8),
149 limb_12,
150 is_real,
151 )
152 }
153}
154
155pub trait RecursionInteractionAirBuilder: BaseAirBuilder {
157 fn send_range_check(
159 &mut self,
160 range_check_opcode: impl Into<Self::Expr>,
161 val: impl Into<Self::Expr>,
162 is_real: impl Into<Self::Expr>,
163 ) {
164 self.send(
165 AirInteraction::new(
166 vec![range_check_opcode.into(), val.into()],
167 is_real.into(),
168 InteractionKind::Range,
169 ),
170 InteractionScope::Global,
171 );
172 }
173
174 fn receive_range_check(
176 &mut self,
177 range_check_opcode: impl Into<Self::Expr>,
178 val: impl Into<Self::Expr>,
179 is_real: impl Into<Self::Expr>,
180 ) {
181 self.receive(
182 AirInteraction::new(
183 vec![range_check_opcode.into(), val.into()],
184 is_real.into(),
185 InteractionKind::Range,
186 ),
187 InteractionScope::Global,
188 );
189 }
190
191 fn send_program<E: Into<Self::Expr> + Copy>(
192 &mut self,
193 pc: impl Into<Self::Expr>,
194 instruction: InstructionCols<E>,
195 selectors: OpcodeSelectorCols<E>,
196 is_real: impl Into<Self::Expr>,
197 ) {
198 let program_interaction_vals = once(pc.into())
199 .chain(instruction.into_iter().map(|x| x.into()))
200 .chain(selectors.into_iter().map(|x| x.into()))
201 .collect::<Vec<_>>();
202 self.send(
203 AirInteraction::new(program_interaction_vals, is_real.into(), InteractionKind::Program),
204 InteractionScope::Global,
205 );
206 }
207
208 fn receive_program<E: Into<Self::Expr> + Copy>(
209 &mut self,
210 pc: impl Into<Self::Expr>,
211 instruction: InstructionCols<E>,
212 selectors: OpcodeSelectorCols<E>,
213 is_real: impl Into<Self::Expr>,
214 ) {
215 let program_interaction_vals = once(pc.into())
216 .chain(instruction.into_iter().map(|x| x.into()))
217 .chain(selectors.into_iter().map(|x| x.into()))
218 .collect::<Vec<_>>();
219 self.receive(
220 AirInteraction::new(program_interaction_vals, is_real.into(), InteractionKind::Program),
221 InteractionScope::Global,
222 );
223 }
224
225 fn send_table<E: Into<Self::Expr> + Clone>(
226 &mut self,
227 opcode: impl Into<Self::Expr>,
228 table: &[E],
229 is_real: impl Into<Self::Expr>,
230 ) {
231 let table_interaction_vals = table.iter().map(|x| x.clone().into());
232 let values = once(opcode.into()).chain(table_interaction_vals).collect();
233 self.send(
234 AirInteraction::new(values, is_real.into(), InteractionKind::Syscall),
235 InteractionScope::Local,
236 );
237 }
238
239 fn receive_table<E: Into<Self::Expr> + Clone>(
240 &mut self,
241 opcode: impl Into<Self::Expr>,
242 table: &[E],
243 is_real: impl Into<Self::Expr>,
244 ) {
245 let table_interaction_vals = table.iter().map(|x| x.clone().into());
246 let values = once(opcode.into()).chain(table_interaction_vals).collect();
247 self.receive(
248 AirInteraction::new(values, is_real.into(), InteractionKind::Syscall),
249 InteractionScope::Local,
250 );
251 }
252}