sp1_core_machine/alu/bitwise/
mod.rs1use crate::{
2 adapter::{
3 register::alu_type::{ALUTypeReader, ALUTypeReaderInput},
4 state::{CPUState, CPUStateInput},
5 },
6 air::{SP1CoreAirBuilder, SP1Operation},
7 eval_untrusted_program,
8 operations::{BitwiseU16Operation, BitwiseU16OperationInput},
9 utils::next_multiple_of_32,
10 SupervisorMode, TrustMode, UserMode,
11};
12use core::{
13 borrow::{Borrow, BorrowMut},
14 mem::{size_of, MaybeUninit},
15};
16use std::marker::PhantomData;
17
18use hashbrown::HashMap;
19use itertools::Itertools;
20use rayon::{
21 iter::{IndexedParallelIterator, IntoParallelRefIterator},
22 slice::ParallelSliceMut,
23};
24use slop_air::{Air, BaseAir};
25use slop_algebra::{AbstractField, PrimeField, PrimeField32};
26use slop_matrix::Matrix;
27use slop_maybe_rayon::prelude::{ParallelIterator, ParallelSlice};
28use sp1_core_executor::{
29 events::{AluEvent, ByteLookupEvent, ByteRecord},
30 ByteOpcode, ExecutionRecord, Opcode, Program, CLK_INC, PC_INC,
31};
32use sp1_derive::AlignedBorrow;
33use sp1_hypercube::air::MachineAir;
34use struct_reflection::{StructReflection, StructReflectionHelper};
35
36pub const NUM_BITWISE_COLS_SUPERVISOR: usize = size_of::<BitwiseCols<u8, SupervisorMode>>();
38pub const NUM_BITWISE_COLS_USER: usize = size_of::<BitwiseCols<u8, UserMode>>();
40
41#[derive(Default)]
43pub struct BitwiseChip<M: TrustMode> {
44 pub _phantom: PhantomData<M>,
45}
46
47#[derive(AlignedBorrow, StructReflection, Default, Clone, Copy)]
49#[repr(C)]
50pub struct BitwiseCols<T, M: TrustMode> {
51 pub state: CPUState<T>,
53
54 pub adapter: ALUTypeReader<T>,
56
57 pub bitwise_operation: BitwiseU16Operation<T>,
59
60 pub is_xor: T,
62
63 pub is_or: T,
65
66 pub is_and: T,
68
69 pub adapter_cols: M::AdapterCols<T>,
71}
72
73impl<F: PrimeField32, M: TrustMode> MachineAir<F> for BitwiseChip<M> {
74 type Record = ExecutionRecord;
75
76 type Program = Program;
77
78 fn name(&self) -> &'static str {
79 if M::IS_TRUSTED {
80 "Bitwise"
81 } else {
82 "BitwiseUser"
83 }
84 }
85
86 fn column_names(&self) -> Vec<String> {
87 BitwiseCols::<F, M>::struct_reflection().unwrap()
88 }
89
90 fn num_rows(&self, input: &Self::Record) -> Option<usize> {
91 if input.program.enable_untrusted_programs == M::IS_TRUSTED {
92 return Some(0);
93 }
94 let nb_rows =
95 next_multiple_of_32(input.bitwise_events.len(), input.fixed_log2_rows::<F, _>(self));
96 Some(nb_rows)
97 }
98
99 fn generate_trace_into(
100 &self,
101 input: &ExecutionRecord,
102 _output: &mut ExecutionRecord,
103 buffer: &mut [MaybeUninit<F>],
104 ) {
105 if input.program.enable_untrusted_programs == M::IS_TRUSTED {
106 return;
107 }
108
109 let padded_nb_rows = <BitwiseChip<M> as MachineAir<F>>::num_rows(self, input).unwrap();
110 let nb_rows = input.bitwise_events.len();
111 let width = <BitwiseChip<M> as BaseAir<F>>::width(self);
112
113 unsafe {
114 let padding_start = nb_rows * width;
115 let padding_size = (padded_nb_rows - nb_rows) * width;
116 if padding_size > 0 {
117 core::ptr::write_bytes(buffer[padding_start..].as_mut_ptr(), 0, padding_size);
118 }
119 }
120
121 let buffer_ptr = buffer.as_mut_ptr() as *mut F;
122 let values = unsafe { core::slice::from_raw_parts_mut(buffer_ptr, padded_nb_rows * width) };
123
124 values[..nb_rows * width]
125 .par_chunks_exact_mut(width)
126 .zip(input.bitwise_events.par_iter())
127 .for_each(|(row, event)| {
128 let cols: &mut BitwiseCols<F, M> = row.borrow_mut();
129
130 let mut blu = Vec::new();
131 cols.adapter.populate(&mut blu, event.1);
132 self.event_to_row(&event.0, cols, &mut blu);
133 cols.state.populate(&mut blu, event.0.clk, event.0.pc);
134 if !M::IS_TRUSTED {
135 let cols: &mut BitwiseCols<F, UserMode> = row.borrow_mut();
136 cols.adapter_cols.is_trusted = F::from_bool(!event.1.is_untrusted);
137 }
138 });
139 }
140
141 fn generate_dependencies(&self, input: &Self::Record, output: &mut Self::Record) {
142 if input.program.enable_untrusted_programs == M::IS_TRUSTED {
143 return;
144 }
145
146 let chunk_size = std::cmp::max(input.bitwise_events.len() / num_cpus::get(), 1);
147 let width = <BitwiseChip<M> as BaseAir<F>>::width(self);
148
149 let blu_batches = input
150 .bitwise_events
151 .par_chunks(chunk_size)
152 .map(|events| {
153 let mut blu: HashMap<ByteLookupEvent, usize> = HashMap::new();
154 events.iter().for_each(|event| {
155 let mut row = vec![F::zero(); width];
156 let cols: &mut BitwiseCols<F, M> = row.as_mut_slice().borrow_mut();
157 cols.adapter.populate(&mut blu, event.1);
158 self.event_to_row(&event.0, cols, &mut blu);
159 cols.state.populate(&mut blu, event.0.clk, event.0.pc);
160 });
161 blu
162 })
163 .collect::<Vec<_>>();
164
165 output.add_byte_lookup_events_from_maps(blu_batches.iter().collect_vec());
166 }
167
168 fn included(&self, shard: &Self::Record) -> bool {
169 if let Some(shape) = shard.shape.as_ref() {
170 shape.included::<F, _>(self)
171 } else {
172 !shard.bitwise_events.is_empty()
173 && (M::IS_TRUSTED != shard.program.enable_untrusted_programs)
174 }
175 }
176}
177
178impl<M: TrustMode> BitwiseChip<M> {
179 fn event_to_row<F: PrimeField>(
181 &self,
182 event: &AluEvent,
183 cols: &mut BitwiseCols<F, M>,
184 blu: &mut impl ByteRecord,
185 ) {
186 cols.bitwise_operation.populate_bitwise(blu, event.a, event.b, event.c, event.opcode);
187
188 cols.is_xor = F::from_bool(event.opcode == Opcode::XOR);
189 cols.is_or = F::from_bool(event.opcode == Opcode::OR);
190 cols.is_and = F::from_bool(event.opcode == Opcode::AND);
191 }
192}
193
194impl<F, M: TrustMode> BaseAir<F> for BitwiseChip<M> {
195 fn width(&self) -> usize {
196 if M::IS_TRUSTED {
197 NUM_BITWISE_COLS_SUPERVISOR
198 } else {
199 NUM_BITWISE_COLS_USER
200 }
201 }
202}
203
204impl<AB, M> Air<AB> for BitwiseChip<M>
205where
206 AB: SP1CoreAirBuilder,
207 M: TrustMode,
208{
209 fn eval(&self, builder: &mut AB) {
210 let main = builder.main();
211 let local = main.row_slice(0);
212 let local: &BitwiseCols<AB::Var, M> = (*local).borrow();
213
214 let is_real = local.is_xor + local.is_or + local.is_and;
219 builder.assert_bool(local.is_xor);
220 builder.assert_bool(local.is_or);
221 builder.assert_bool(local.is_and);
222 builder.assert_bool(is_real.clone());
223
224 let byte_opcode = local.is_xor * ByteOpcode::XOR.as_field::<AB::F>()
226 + local.is_or * ByteOpcode::OR.as_field::<AB::F>()
227 + local.is_and * ByteOpcode::AND.as_field::<AB::F>();
228
229 let cpu_opcode = local.is_xor * Opcode::XOR.as_field::<AB::F>()
231 + local.is_or * Opcode::OR.as_field::<AB::F>()
232 + local.is_and * Opcode::AND.as_field::<AB::F>();
233
234 let funct3 = local.is_xor * AB::Expr::from_canonical_u8(Opcode::XOR.funct3().unwrap())
236 + local.is_or * AB::Expr::from_canonical_u8(Opcode::OR.funct3().unwrap())
237 + local.is_and * AB::Expr::from_canonical_u8(Opcode::AND.funct3().unwrap());
238 let funct7 = local.is_xor * AB::Expr::from_canonical_u8(Opcode::XOR.funct7().unwrap_or(0))
239 + local.is_or * AB::Expr::from_canonical_u8(Opcode::OR.funct7().unwrap_or(0))
240 + local.is_and * AB::Expr::from_canonical_u8(Opcode::AND.funct7().unwrap_or(0));
241
242 let (xor_base, xor_imm) = Opcode::XOR.base_opcode();
243 let xor_imm = xor_imm.expect("XOR immediate opcode not found");
244 let (or_base, or_imm) = Opcode::OR.base_opcode();
245 let or_imm = or_imm.expect("OR immediate opcode not found");
246 let (and_base, and_imm) = Opcode::AND.base_opcode();
247 let and_imm = and_imm.expect("AND immediate opcode not found");
248
249 let xor_base_expr = AB::Expr::from_canonical_u32(xor_base);
250 let or_base_expr = AB::Expr::from_canonical_u32(or_base);
251 let and_base_expr = AB::Expr::from_canonical_u32(and_base);
252
253 let imm_base_difference = xor_base.checked_sub(xor_imm).unwrap();
254 assert_eq!(imm_base_difference, or_base.checked_sub(or_imm).unwrap());
255 assert_eq!(imm_base_difference, and_base.checked_sub(and_imm).unwrap());
256
257 let calculated_base_opcode = local.is_xor * xor_base_expr
258 + local.is_or * or_base_expr
259 + local.is_and * and_base_expr
260 - AB::Expr::from_canonical_u32(imm_base_difference) * local.adapter.imm_c;
261
262 let xor_instr_type = Opcode::XOR.instruction_type().0 as u32;
263 let xor_instr_type_imm =
264 Opcode::XOR.instruction_type().1.expect("XOR immediate instruction type not found")
265 as u32;
266 let or_instr_type = Opcode::OR.instruction_type().0 as u32;
267 let or_instr_type_imm =
268 Opcode::OR.instruction_type().1.expect("OR immediate instruction type not found")
269 as u32;
270 let and_instr_type = Opcode::AND.instruction_type().0 as u32;
271 let and_instr_type_imm =
272 Opcode::AND.instruction_type().1.expect("AND immediate instruction type not found")
273 as u32;
274
275 let instr_type_difference = xor_instr_type.checked_sub(xor_instr_type_imm).unwrap();
276 assert_eq!(instr_type_difference, or_instr_type.checked_sub(or_instr_type_imm).unwrap());
277 assert_eq!(instr_type_difference, and_instr_type.checked_sub(and_instr_type_imm).unwrap());
278
279 let calculated_instr_type = local.is_xor * AB::Expr::from_canonical_u32(xor_instr_type)
280 + local.is_or * AB::Expr::from_canonical_u32(or_instr_type)
281 + local.is_and * AB::Expr::from_canonical_u32(and_instr_type)
282 - AB::Expr::from_canonical_u32(instr_type_difference) * local.adapter.imm_c;
283
284 builder.assert_zero(local.adapter.op_a_0);
286
287 let bitwise_u16_input = BitwiseU16OperationInput::<AB>::new(
289 local.adapter.b().map(Into::into),
290 local.adapter.c().map(Into::into),
291 local.bitwise_operation,
292 byte_opcode,
293 is_real.clone(),
294 );
295 let result =
296 <BitwiseU16Operation<AB::F> as SP1Operation<AB>>::eval(builder, bitwise_u16_input);
297
298 let cpu_state_input = CPUStateInput::<AB>::new(
301 local.state,
302 [
303 local.state.pc[0] + AB::F::from_canonical_u32(PC_INC),
304 local.state.pc[1].into(),
305 local.state.pc[2].into(),
306 ],
307 AB::Expr::from_canonical_u32(CLK_INC),
308 is_real.clone(),
309 );
310 <CPUState<AB::F> as SP1Operation<AB>>::eval(builder, cpu_state_input);
311
312 let mut is_trusted: AB::Expr = is_real.clone();
313
314 #[cfg(feature = "mprotect")]
315 builder.assert_eq(
316 builder.extract_public_values().is_untrusted_programs_enabled,
317 AB::Expr::from_bool(!M::IS_TRUSTED),
318 );
319
320 if !M::IS_TRUSTED {
321 let local = main.row_slice(0);
322 let local: &BitwiseCols<AB::Var, UserMode> = (*local).borrow();
323
324 let instruction = local.adapter.instruction::<AB>(cpu_opcode.clone());
325
326 #[cfg(not(feature = "mprotect"))]
327 builder.assert_zero(is_real.clone());
328
329 eval_untrusted_program(
330 builder,
331 local.state.pc,
332 instruction,
333 [
334 calculated_instr_type.clone(),
335 calculated_base_opcode.clone(),
336 funct3.clone(),
337 funct7.clone(),
338 ],
339 [local.state.clk_high::<AB>(), local.state.clk_low::<AB>()],
340 is_real.clone(),
341 local.adapter_cols,
342 );
343
344 is_trusted = local.adapter_cols.is_trusted.into();
345 }
346
347 let alu_reader_input = ALUTypeReaderInput::<AB, AB::Expr>::new(
349 local.state.clk_high::<AB>(),
350 local.state.clk_low::<AB>(),
351 local.state.pc,
352 cpu_opcode,
353 result,
354 local.adapter,
355 is_real,
356 is_trusted,
357 );
358 <ALUTypeReader<AB::F> as SP1Operation<AB>>::eval(builder, alu_reader_input);
359 }
360}