Skip to main content

sp1_core_machine/alu/bitwise/
mod.rs

1use 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
36/// The number of main trace columns for `BitwiseChip` in Supervisor mode.
37pub const NUM_BITWISE_COLS_SUPERVISOR: usize = size_of::<BitwiseCols<u8, SupervisorMode>>();
38/// The number of main trace columns for `BitwiseChip` in User mode.
39pub const NUM_BITWISE_COLS_USER: usize = size_of::<BitwiseCols<u8, UserMode>>();
40
41/// A chip that implements bitwise operations for the opcodes XOR, OR, and AND.
42#[derive(Default)]
43pub struct BitwiseChip<M: TrustMode> {
44    pub _phantom: PhantomData<M>,
45}
46
47/// The column layout for the chip.
48#[derive(AlignedBorrow, StructReflection, Default, Clone, Copy)]
49#[repr(C)]
50pub struct BitwiseCols<T, M: TrustMode> {
51    /// The current shard, timestamp, program counter of the CPU.
52    pub state: CPUState<T>,
53
54    /// The adapter to read program and register information.
55    pub adapter: ALUTypeReader<T>,
56
57    /// Instance of `BitwiseOperation` to handle bitwise logic in `BitwiseChip`'s ALU operations.
58    pub bitwise_operation: BitwiseU16Operation<T>,
59
60    /// If the opcode is XOR.
61    pub is_xor: T,
62
63    // If the opcode is OR.
64    pub is_or: T,
65
66    /// If the opcode is AND.
67    pub is_and: T,
68
69    /// Adapter columns for trust mode specific data.
70    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    /// Create a row from an event.
180    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        // SAFETY: All selectors `is_xor`, `is_or`, `is_and` are checked to be boolean.
215        // Each "real" row has exactly one selector turned on, as `is_real`, the sum of the three
216        // selectors, is boolean. Therefore, the `opcode` and `cpu_opcode` matches the
217        // corresponding opcode.
218        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        // Get the opcode for the operation.
225        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        // Get the cpu opcode, which corresponds to the opcode being sent in the CPU table.
230        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        // Compute instruction field constants for each opcode
235        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        // This chip is for the case `rd != x0`.
285        builder.assert_zero(local.adapter.op_a_0);
286
287        // Constrain the bitwise operation over `op_b` and `op_c`.
288        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        // Constrain the state of the CPU.
299        // The program counter and timestamp increment by `4` and `8`.
300        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        // Constrain the program and register reads.
348        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}