Skip to main content

sp1_core_machine/operations/
bitwise_u16.rs

1use super::{BitwiseOperation, BitwiseOperationInput, U16toU8Operation};
2use crate::{
3    air::{SP1Operation, SP1OperationBuilder},
4    operations::{U16toU8OperationUnsafe, U16toU8OperationUnsafeInput},
5};
6use serde::{Deserialize, Serialize};
7use slop_air::AirBuilder;
8use slop_algebra::{AbstractField, Field};
9use sp1_core_executor::{events::ByteRecord, Opcode};
10use sp1_derive::{AlignedBorrow, InputExpr, InputParams, IntoShape, SP1OperationBuilder};
11use sp1_hypercube::{air::SP1AirBuilder, Word};
12use struct_reflection::{StructReflection, StructReflectionHelper};
13
14/// A set of columns needed to compute the bitwise operation over `Word` of u16 limbs.
15#[derive(
16    AlignedBorrow,
17    StructReflection,
18    Default,
19    Debug,
20    Clone,
21    Copy,
22    Serialize,
23    Deserialize,
24    IntoShape,
25    SP1OperationBuilder,
26)]
27#[repr(C)]
28pub struct BitwiseU16Operation<T> {
29    /// Lower byte of the limbs of `b`.
30    pub b_low_bytes: U16toU8Operation<T>,
31
32    /// Lower byte of the limbs of `c`.
33    pub c_low_bytes: U16toU8Operation<T>,
34
35    /// The bitwise operation over bytes.
36    pub bitwise_operation: BitwiseOperation<T>,
37}
38
39impl<F: Field> BitwiseU16Operation<F> {
40    pub fn populate_bitwise(
41        &mut self,
42        record: &mut impl ByteRecord,
43        a_u64: u64,
44        b_u64: u64,
45        c_u64: u64,
46        opcode: Opcode,
47    ) {
48        self.b_low_bytes.populate_u16_to_u8_unsafe(b_u64);
49        self.c_low_bytes.populate_u16_to_u8_unsafe(c_u64);
50        self.bitwise_operation.populate_bitwise(record, a_u64, b_u64, c_u64, opcode);
51    }
52
53    /// Evaluate the bitwise operation over two `Word`s of u16 limbs.
54    /// Assumes that the two words are valid `Word`s of u16 limbs.
55    /// Assumes that `opcode` is a valid byte opcode.
56    /// Constrains that `is_real` is boolean.
57    /// If `is_real` is true, the return value is constrained to be correct.
58    fn eval_bitwise_u16<AB>(
59        builder: &mut AB,
60        b: Word<AB::Expr>,
61        c: Word<AB::Expr>,
62        cols: BitwiseU16Operation<AB::Var>,
63        opcode: AB::Expr,
64        is_real: AB::Expr,
65    ) -> Word<AB::Expr>
66    where
67        AB: SP1AirBuilder
68            + SP1OperationBuilder<U16toU8OperationUnsafe>
69            + SP1OperationBuilder<BitwiseOperation<<AB as AirBuilder>::F>>,
70    {
71        // Constrain that `is_real` is boolean.
72        builder.assert_bool(is_real.clone());
73
74        // Convert the two words to bytes using the unsafe API.
75        // SAFETY: This is safe because the `BitwiseOperation` will range check the bytes.
76        let b_input = U16toU8OperationUnsafeInput::new(b.0, cols.b_low_bytes);
77        let b_bytes = U16toU8OperationUnsafe::eval(builder, b_input);
78        let c_input = U16toU8OperationUnsafeInput::new(c.0, cols.c_low_bytes);
79        let c_bytes = U16toU8OperationUnsafe::eval(builder, c_input);
80
81        // SAFETY: This is safe because `is_real` is constrained to be boolean.
82        BitwiseOperation::<AB::F>::eval(
83            builder,
84            BitwiseOperationInput::<AB>::new(
85                b_bytes,
86                c_bytes,
87                cols.bitwise_operation,
88                opcode,
89                is_real,
90            ),
91        );
92
93        // Combine the byte results into u16 limbs.
94        let result_limb0 = cols.bitwise_operation.result[0]
95            + cols.bitwise_operation.result[1] * AB::F::from_canonical_u32(1 << 8);
96        let result_limb1 = cols.bitwise_operation.result[2]
97            + cols.bitwise_operation.result[3] * AB::F::from_canonical_u32(1 << 8);
98        let result_limb2 = cols.bitwise_operation.result[4]
99            + cols.bitwise_operation.result[5] * AB::F::from_canonical_u32(1 << 8);
100        let result_limb3 = cols.bitwise_operation.result[6]
101            + cols.bitwise_operation.result[7] * AB::F::from_canonical_u32(1 << 8);
102        Word([result_limb0, result_limb1, result_limb2, result_limb3])
103    }
104}
105
106#[derive(Clone, InputParams, InputExpr)]
107pub struct BitwiseU16OperationInput<AB: SP1AirBuilder> {
108    pub b: Word<AB::Expr>,
109    pub c: Word<AB::Expr>,
110    pub cols: BitwiseU16Operation<AB::Var>,
111    pub opcode: AB::Expr,
112    pub is_real: AB::Expr,
113}
114
115impl<AB> SP1Operation<AB> for BitwiseU16Operation<AB::F>
116where
117    AB: SP1AirBuilder
118        + SP1OperationBuilder<U16toU8OperationUnsafe>
119        + SP1OperationBuilder<BitwiseOperation<<AB as AirBuilder>::F>>,
120{
121    type Input = BitwiseU16OperationInput<AB>;
122    type Output = Word<AB::Expr>;
123
124    fn lower(builder: &mut AB, input: Self::Input) -> Word<AB::Expr> {
125        Self::eval_bitwise_u16(builder, input.b, input.c, input.cols, input.opcode, input.is_real)
126    }
127}