sp1_core_machine/syscall/
chip.rs1use crate::utils::next_power_of_two;
2use core::fmt;
3use itertools::Itertools;
4use p3_air::{Air, BaseAir};
5use p3_field::{AbstractField, PrimeField32};
6use p3_matrix::{dense::RowMajorMatrix, Matrix};
7use p3_maybe_rayon::prelude::{IntoParallelRefIterator, ParallelBridge, ParallelIterator};
8use sp1_core_executor::{
9 events::{GlobalInteractionEvent, SyscallEvent},
10 ExecutionRecord, Program,
11};
12use sp1_derive::AlignedBorrow;
13use sp1_stark::{
14 air::{AirInteraction, InteractionScope, MachineAir, SP1AirBuilder},
15 InteractionKind,
16};
17use std::{
18 borrow::{Borrow, BorrowMut},
19 mem::size_of,
20};
21pub const NUM_SYSCALL_COLS: usize = size_of::<SyscallCols<u8>>();
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub enum SyscallShardKind {
26 Core,
27 Precompile,
28}
29
30pub struct SyscallChip {
32 shard_kind: SyscallShardKind,
33}
34
35impl SyscallChip {
36 pub const fn new(shard_kind: SyscallShardKind) -> Self {
37 Self { shard_kind }
38 }
39
40 pub const fn core() -> Self {
41 Self::new(SyscallShardKind::Core)
42 }
43
44 pub const fn precompile() -> Self {
45 Self::new(SyscallShardKind::Precompile)
46 }
47
48 pub fn shard_kind(&self) -> SyscallShardKind {
49 self.shard_kind
50 }
51}
52
53#[derive(AlignedBorrow, Clone, Copy)]
55#[repr(C)]
56pub struct SyscallCols<T: Copy> {
57 pub shard: T,
59
60 pub clk: T,
62
63 pub syscall_id: T,
65
66 pub arg1: T,
68
69 pub arg2: T,
71
72 pub is_real: T,
73}
74
75impl<F: PrimeField32> MachineAir<F> for SyscallChip {
76 type Record = ExecutionRecord;
77
78 type Program = Program;
79
80 fn name(&self) -> String {
81 format!("Syscall{}", self.shard_kind).to_string()
82 }
83
84 fn generate_dependencies(&self, input: &ExecutionRecord, output: &mut ExecutionRecord) {
85 let events = match self.shard_kind {
86 SyscallShardKind::Core => &input
87 .syscall_events
88 .iter()
89 .filter(|e| e.syscall_code.should_send() == 1)
90 .copied()
91 .collect::<Vec<_>>(),
92 SyscallShardKind::Precompile => &input
93 .precompile_events
94 .all_events()
95 .map(|(event, _)| event.to_owned())
96 .collect::<Vec<_>>(),
97 };
98
99 let events = events
100 .iter()
101 .filter(|e| e.syscall_code.should_send() == 1)
102 .map(|event| GlobalInteractionEvent {
103 message: [event.shard, event.clk, event.syscall_id, event.arg1, event.arg2, 0, 0],
104 is_receive: self.shard_kind == SyscallShardKind::Precompile,
105 kind: InteractionKind::Syscall as u8,
106 })
107 .collect_vec();
108 output.global_interaction_events.extend(events);
109 }
110
111 fn num_rows(&self, input: &Self::Record) -> Option<usize> {
112 let events = match self.shard_kind() {
113 SyscallShardKind::Core => &input.syscall_events,
114 SyscallShardKind::Precompile => &input
115 .precompile_events
116 .all_events()
117 .map(|(event, _)| event.to_owned())
118 .collect::<Vec<_>>(),
119 };
120 let nb_rows = events.len();
121 let size_log2 = input.fixed_log2_rows::<F, _>(self);
122 let padded_nb_rows = next_power_of_two(nb_rows, size_log2);
123 Some(padded_nb_rows)
124 }
125
126 fn generate_trace(
127 &self,
128 input: &ExecutionRecord,
129 _output: &mut ExecutionRecord,
130 ) -> RowMajorMatrix<F> {
131 let row_fn = |syscall_event: &SyscallEvent, _: bool| {
132 let mut row = [F::zero(); NUM_SYSCALL_COLS];
133 let cols: &mut SyscallCols<F> = row.as_mut_slice().borrow_mut();
134
135 cols.shard = F::from_canonical_u32(syscall_event.shard);
136 cols.clk = F::from_canonical_u32(syscall_event.clk);
137 cols.syscall_id = F::from_canonical_u32(syscall_event.syscall_code.syscall_id());
138 cols.arg1 = F::from_canonical_u32(syscall_event.arg1);
139 cols.arg2 = F::from_canonical_u32(syscall_event.arg2);
140 cols.is_real = F::one();
141 row
142 };
143
144 let mut rows = match self.shard_kind {
145 SyscallShardKind::Core => input
146 .syscall_events
147 .par_iter()
148 .filter(|event| event.syscall_code.should_send() == 1)
149 .map(|event| row_fn(event, false))
150 .collect::<Vec<_>>(),
151 SyscallShardKind::Precompile => input
152 .precompile_events
153 .all_events()
154 .map(|(event, _)| event)
155 .par_bridge()
156 .map(|event| row_fn(event, true))
157 .collect::<Vec<_>>(),
158 };
159
160 rows.resize(
162 <SyscallChip as MachineAir<F>>::num_rows(self, input).unwrap(),
163 [F::zero(); NUM_SYSCALL_COLS],
164 );
165
166 RowMajorMatrix::new(rows.into_iter().flatten().collect::<Vec<_>>(), NUM_SYSCALL_COLS)
167 }
168
169 fn included(&self, shard: &Self::Record) -> bool {
170 if let Some(shape) = shard.shape.as_ref() {
171 shape.included::<F, _>(self)
172 } else {
173 match self.shard_kind {
174 SyscallShardKind::Core => {
175 shard
176 .syscall_events
177 .iter()
178 .filter(|e| e.syscall_code.should_send() == 1)
179 .take(1)
180 .count() >
181 0
182 }
183 SyscallShardKind::Precompile => {
184 !shard.precompile_events.is_empty() &&
185 shard.cpu_events.is_empty() &&
186 shard.global_memory_initialize_events.is_empty() &&
187 shard.global_memory_finalize_events.is_empty()
188 }
189 }
190 }
191 }
192
193 fn commit_scope(&self) -> InteractionScope {
194 InteractionScope::Local
195 }
196}
197
198impl<AB> Air<AB> for SyscallChip
199where
200 AB: SP1AirBuilder,
201{
202 fn eval(&self, builder: &mut AB) {
203 let main = builder.main();
204 let local = main.row_slice(0);
205 let local: &SyscallCols<AB::Var> = (*local).borrow();
206
207 builder.assert_bool(local.is_real);
209
210 builder.assert_eq(
211 local.is_real * local.is_real * local.is_real,
212 local.is_real * local.is_real * local.is_real,
213 );
214
215 match self.shard_kind {
216 SyscallShardKind::Core => {
217 builder.receive_syscall(
218 local.shard,
219 local.clk,
220 local.syscall_id,
221 local.arg1,
222 local.arg2,
223 local.is_real,
224 InteractionScope::Local,
225 );
226
227 builder.send(
229 AirInteraction::new(
230 vec![
231 local.shard.into(),
232 local.clk.into(),
233 local.syscall_id.into(),
234 local.arg1.into(),
235 local.arg2.into(),
236 AB::Expr::zero(),
237 AB::Expr::zero(),
238 AB::Expr::one(),
239 AB::Expr::zero(),
240 AB::Expr::from_canonical_u8(InteractionKind::Syscall as u8),
241 ],
242 local.is_real.into(),
243 InteractionKind::Global,
244 ),
245 InteractionScope::Local,
246 );
247 }
248 SyscallShardKind::Precompile => {
249 builder.send_syscall(
250 local.shard,
251 local.clk,
252 local.syscall_id,
253 local.arg1,
254 local.arg2,
255 local.is_real,
256 InteractionScope::Local,
257 );
258
259 builder.send(
261 AirInteraction::new(
262 vec![
263 local.shard.into(),
264 local.clk.into(),
265 local.syscall_id.into(),
266 local.arg1.into(),
267 local.arg2.into(),
268 AB::Expr::zero(),
269 AB::Expr::zero(),
270 AB::Expr::zero(),
271 AB::Expr::one(),
272 AB::Expr::from_canonical_u8(InteractionKind::Syscall as u8),
273 ],
274 local.is_real.into(),
275 InteractionKind::Global,
276 ),
277 InteractionScope::Local,
278 );
279 }
280 }
281 }
282}
283
284impl<F> BaseAir<F> for SyscallChip {
285 fn width(&self) -> usize {
286 NUM_SYSCALL_COLS
287 }
288}
289
290impl fmt::Display for SyscallShardKind {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 match self {
293 SyscallShardKind::Core => write!(f, "Core"),
294 SyscallShardKind::Precompile => write!(f, "Precompile"),
295 }
296 }
297}