use core::{
borrow::{Borrow, BorrowMut},
mem::{size_of, MaybeUninit},
};
use std::num::Wrapping;
use slop_air::{Air, AirBuilder, BaseAir};
use slop_algebra::{AbstractField, PrimeField32};
use slop_matrix::Matrix;
use sp1_core_executor::{
events::{ByteLookupEvent, ByteRecord},
get_msb, get_quotient_and_remainder, is_signed_64bit_operation, is_signed_word_operation,
is_unsigned_64bit_operation, is_unsigned_word_operation, is_word_operation, ExecutionRecord,
Opcode, Program, CLK_INC, PC_INC,
};
use sp1_derive::AlignedBorrow;
use sp1_hypercube::{air::MachineAir, Word};
use sp1_primitives::consts::WORD_SIZE;
use crate::{
adapter::{
register::r_type::{RTypeReader, RTypeReaderInput},
state::{CPUState, CPUStateInput},
},
air::{SP1CoreAirBuilder, SP1Operation, WordAirBuilder},
operations::{
AddOperation, AddOperationInput, IsEqualWordOperation, IsEqualWordOperationInput,
IsZeroWordOperation, IsZeroWordOperationInput, LtOperationUnsigned,
LtOperationUnsignedInput, MulOperation, MulOperationInput, U16MSBOperation,
U16MSBOperationInput,
},
utils::next_multiple_of_32,
};
pub const NUM_DIVREM_COLS: usize = size_of::<DivRemCols<u8>>();
const LONG_WORD_SIZE: usize = 2 * WORD_SIZE;
#[derive(Default)]
pub struct DivRemChip;
#[derive(AlignedBorrow, Default, Debug, Clone, Copy)]
#[repr(C)]
pub struct DivRemCols<T> {
pub state: CPUState<T>,
pub adapter: RTypeReader<T>,
pub a: Word<T>,
pub b: Word<T>,
pub c: Word<T>,
pub quotient: Word<T>,
pub quotient_comp: Word<T>,
pub remainder_comp: Word<T>,
pub remainder: Word<T>,
pub abs_remainder: Word<T>,
pub abs_c: Word<T>,
pub max_abs_c_or_1: Word<T>,
pub c_times_quotient: [T; LONG_WORD_SIZE],
pub c_times_quotient_lower: MulOperation<T>,
pub c_times_quotient_upper: MulOperation<T>,
pub c_neg_operation: AddOperation<T>,
pub rem_neg_operation: AddOperation<T>,
pub remainder_lt_operation: LtOperationUnsigned<T>,
pub carry: [T; LONG_WORD_SIZE],
pub is_c_0: IsZeroWordOperation<T>,
pub is_div: T,
pub is_divu: T,
pub is_rem: T,
pub is_remu: T,
pub is_divw: T,
pub is_remw: T,
pub is_divuw: T,
pub is_remuw: T,
pub is_overflow: T,
pub is_overflow_b: IsEqualWordOperation<T>,
pub is_overflow_c: IsEqualWordOperation<T>,
pub b_msb: U16MSBOperation<T>,
pub rem_msb: U16MSBOperation<T>,
pub c_msb: U16MSBOperation<T>,
pub quot_msb: U16MSBOperation<T>,
pub b_neg: T,
pub b_neg_not_overflow: T,
pub b_not_neg_not_overflow: T,
pub is_real_not_word: T,
pub rem_neg: T,
pub c_neg: T,
pub abs_c_alu_event: T,
pub abs_rem_alu_event: T,
pub is_real: T,
pub remainder_check_multiplicity: T,
}
impl<F: PrimeField32> MachineAir<F> for DivRemChip {
type Record = ExecutionRecord;
type Program = Program;
fn name(&self) -> &'static str {
"DivRem"
}
fn num_rows(&self, input: &Self::Record) -> Option<usize> {
let nb_rows =
next_multiple_of_32(input.divrem_events.len(), input.fixed_log2_rows::<F, _>(self));
Some(nb_rows)
}
fn generate_trace_into(
&self,
input: &ExecutionRecord,
output: &mut ExecutionRecord,
buffer: &mut [MaybeUninit<F>],
) {
let padded_nb_rows = <DivRemChip as MachineAir<F>>::num_rows(self, input).unwrap();
let buffer_ptr = buffer.as_mut_ptr() as *mut F;
let values = unsafe {
core::slice::from_raw_parts_mut(buffer_ptr, padded_nb_rows * NUM_DIVREM_COLS)
};
let divrem_events = input.divrem_events.clone();
for (row_idx, event_record) in divrem_events.iter().enumerate() {
let event = event_record.0;
let r_record = event_record.1;
assert!(
event.opcode == Opcode::DIVU
|| event.opcode == Opcode::REMU
|| event.opcode == Opcode::REM
|| event.opcode == Opcode::DIV
|| event.opcode == Opcode::DIVW
|| event.opcode == Opcode::REMW
|| event.opcode == Opcode::DIVUW
|| event.opcode == Opcode::REMUW
);
let row_start = row_idx * NUM_DIVREM_COLS;
let row = &mut values[row_start..row_start + NUM_DIVREM_COLS];
unsafe {
core::ptr::write_bytes(row.as_mut_ptr(), 0, NUM_DIVREM_COLS);
}
let cols: &mut DivRemCols<F> = row.borrow_mut();
{
let mut blu = vec![];
cols.state.populate(&mut blu, event.clk, event.pc);
cols.adapter.populate(&mut blu, r_record);
output.add_byte_lookup_events(blu);
}
let b = if is_signed_word_operation(event.opcode) {
event.b as i32 as i64 as u64
} else if is_unsigned_word_operation(event.opcode) {
event.b as u32 as u64
} else {
event.b
};
let c = if is_signed_word_operation(event.opcode) {
event.c as i32 as i64 as u64
} else if is_unsigned_word_operation(event.opcode) {
event.c as u32 as u64
} else {
event.c
};
{
cols.a = Word::from(event.a);
cols.b = Word::from(b);
cols.c = Word::from(c);
cols.is_real = F::one();
cols.is_divu = F::from_bool(event.opcode == Opcode::DIVU);
cols.is_remu = F::from_bool(event.opcode == Opcode::REMU);
cols.is_div = F::from_bool(event.opcode == Opcode::DIV);
cols.is_rem = F::from_bool(event.opcode == Opcode::REM);
cols.is_divw = F::from_bool(event.opcode == Opcode::DIVW);
cols.is_divuw = F::from_bool(event.opcode == Opcode::DIVUW);
cols.is_remw = F::from_bool(event.opcode == Opcode::REMW);
cols.is_remuw = F::from_bool(event.opcode == Opcode::REMUW);
let not_word_operation =
F::one() - cols.is_divw - cols.is_remw - cols.is_divuw - cols.is_remuw;
cols.is_real_not_word = cols.is_real * not_word_operation;
cols.is_c_0.populate(c);
}
let (quotient, remainder) = get_quotient_and_remainder(event.b, event.c, event.opcode);
cols.quotient = Word::from(quotient);
cols.remainder = Word::from(remainder);
let quotient_comp = if is_unsigned_word_operation(event.opcode) {
quotient as u32 as u64
} else {
quotient
};
cols.quotient_comp = Word::from(quotient_comp);
let remainder_comp = if is_unsigned_word_operation(event.opcode) {
remainder as u32 as u64
} else {
remainder
};
cols.remainder_comp = Word::from(remainder_comp);
{
if is_signed_64bit_operation(event.opcode) {
cols.rem_neg = F::from_canonical_u8(get_msb(remainder));
cols.b_neg = F::from_canonical_u8(get_msb(event.b));
cols.c_neg = F::from_canonical_u8(get_msb(event.c));
cols.is_overflow =
F::from_bool(event.b as i64 == i64::MIN && event.c as i64 == -1);
cols.abs_remainder = Word::from((remainder as i64).unsigned_abs());
cols.abs_c = Word::from((event.c as i64).unsigned_abs());
cols.max_abs_c_or_1 = Word::from(u64::max(1, (event.c as i64).unsigned_abs()));
} else if is_signed_word_operation(event.opcode) {
cols.rem_neg = F::from_canonical_u8(get_msb((remainder as i32) as i64 as u64));
cols.b_neg = F::from_canonical_u8(get_msb((event.b as i32) as i64 as u64));
cols.c_neg = F::from_canonical_u8(get_msb((event.c as i32) as i64 as u64));
cols.is_overflow =
F::from_bool(event.b as i32 == i32::MIN && event.c as i32 == -1);
cols.abs_remainder = Word::from((remainder as i64).unsigned_abs());
cols.abs_c = Word::from((c as i64).unsigned_abs());
cols.max_abs_c_or_1 = Word::from(u64::max(1, (c as i64).unsigned_abs()));
} else if is_unsigned_word_operation(event.opcode) {
cols.abs_remainder = cols.remainder_comp;
cols.abs_c = Word::from(event.c as u32);
cols.max_abs_c_or_1 = Word::from(u32::max(1, event.c as u32));
} else {
cols.abs_remainder = cols.remainder_comp;
cols.abs_c = Word::from(event.c);
cols.max_abs_c_or_1 = Word::from(u64::max(1, event.c));
}
if is_word_operation(event.opcode) {
cols.is_overflow_b.populate((event.b as u32) as u64, i32::MIN as u32 as u64);
cols.is_overflow_c.populate((event.c as u32) as u64, -1i32 as u32 as u64);
} else {
cols.is_overflow_b.populate(event.b, i64::MIN as u64);
cols.is_overflow_c.populate(event.c, -1i64 as u64);
}
cols.b_neg_not_overflow = cols.b_neg * (F::one() - cols.is_overflow);
cols.b_not_neg_not_overflow =
(F::one() - cols.b_neg) * (F::one() - cols.is_overflow);
cols.abs_c_alu_event = cols.c_neg * cols.is_real;
cols.abs_rem_alu_event = cols.rem_neg * cols.is_real;
output.add_u16_range_checks_field(&cols.abs_c.0);
output.add_u16_range_checks_field(&cols.abs_remainder.0);
{
let mut blu_events = vec![];
if cols.abs_c_alu_event.is_one() {
cols.c_neg_operation.populate(
&mut blu_events,
cols.c.to_u64(),
cols.abs_c.to_u64(),
);
}
if cols.abs_rem_alu_event.is_one() {
cols.rem_neg_operation.populate(
&mut blu_events,
cols.remainder.to_u64(),
cols.abs_remainder.to_u64(),
);
}
output.add_byte_lookup_events(blu_events);
}
{
let mut blu_events: Vec<ByteLookupEvent> = vec![];
if is_word_operation(event.opcode) {
cols.b_msb.populate_msb(&mut blu_events, (event.b >> 16) as u16);
cols.c_msb.populate_msb(&mut blu_events, (event.c >> 16) as u16);
cols.rem_msb.populate_msb(&mut blu_events, (remainder >> 16) as u16);
cols.quot_msb.populate_msb(&mut blu_events, (quotient >> 16) as u16);
} else {
cols.b_msb.populate_msb(&mut blu_events, (b >> 48) as u16);
cols.c_msb.populate_msb(&mut blu_events, (c >> 48) as u16);
cols.rem_msb.populate_msb(&mut blu_events, (remainder >> 48) as u16);
}
output.add_byte_lookup_events(blu_events);
}
}
{
let mut blu_events = vec![];
cols.remainder_check_multiplicity = cols.is_real * (F::one() - cols.is_c_0.result);
if cols.remainder_check_multiplicity.is_one() {
cols.remainder_lt_operation.populate_unsigned(
&mut blu_events,
1u64,
cols.abs_remainder.to_u64(),
cols.max_abs_c_or_1.to_u64(),
);
}
output.add_byte_lookup_events(blu_events);
}
{
let mut blu_events = vec![];
let mut c_times_quotient_byte = [0u8; 16];
let c_times_quotient_byte_lower =
((Wrapping(quotient_comp) * Wrapping(c)).0 as u64).to_le_bytes();
let c_times_quotient_byte_upper = if is_signed_64bit_operation(event.opcode)
|| is_signed_word_operation(event.opcode)
{
((((quotient_comp as i64) as i128).wrapping_mul((c as i64) as i128) >> 64)
as u64)
.to_le_bytes()
} else {
(((quotient_comp as u128 * c as u128) >> 64) as u64).to_le_bytes()
};
c_times_quotient_byte[..8].copy_from_slice(&c_times_quotient_byte_lower);
c_times_quotient_byte[8..].copy_from_slice(&c_times_quotient_byte_upper);
let c_times_quotient_u16: [u16; LONG_WORD_SIZE] = core::array::from_fn(|i| {
u16::from_le_bytes([
c_times_quotient_byte[2 * i],
c_times_quotient_byte[2 * i + 1],
])
});
cols.c_times_quotient = c_times_quotient_u16.map(F::from_canonical_u16);
cols.c_times_quotient_lower.populate(
&mut blu_events,
quotient_comp,
c,
false,
false,
false,
);
if is_signed_64bit_operation(event.opcode) {
cols.c_times_quotient_upper.populate(
&mut blu_events,
quotient_comp,
c,
true,
false,
false,
);
}
if is_unsigned_64bit_operation(event.opcode) {
cols.c_times_quotient_upper.populate(
&mut blu_events,
quotient_comp,
c,
false,
false,
false,
);
}
output.add_byte_lookup_events(blu_events);
let mut remainder_u16 = [0u32; 8];
for i in 0..4 {
remainder_u16[i] = cols.remainder_comp[i].as_canonical_u32();
remainder_u16[i + 4] = cols.rem_neg.as_canonical_u32() * ((1 << 16) - 1);
}
let mut carry = [0u32; 8];
let base = 1 << 16;
for i in 0..LONG_WORD_SIZE {
let mut x = c_times_quotient_u16[i] as u32 + remainder_u16[i];
if i > 0 {
x += carry[i - 1];
}
carry[i] = x / base;
cols.carry[i] = F::from_canonical_u32(carry[i]);
output.add_u16_range_check((x & 0xFFFF) as u16);
}
{
output.add_u16_range_checks(&[
(quotient & 0xFFFF) as u16,
(quotient >> 16) as u16,
(quotient >> 32) as u16,
(quotient >> 48) as u16,
]);
output.add_u16_range_checks(&[
(remainder & 0xFFFF) as u16,
(remainder >> 16) as u16,
(remainder >> 32) as u16,
(remainder >> 48) as u16,
]);
output.add_u16_range_checks(&c_times_quotient_u16);
}
}
}
let padded_row_template = {
let mut row = [F::zero(); NUM_DIVREM_COLS];
let cols: &mut DivRemCols<F> = row.as_mut_slice().borrow_mut();
cols.is_divu = F::one();
cols.adapter.op_c_memory.prev_value = Word::from(1u64);
cols.abs_c[0] = F::one();
cols.c[0] = F::one();
cols.max_abs_c_or_1[0] = F::one();
cols.b_not_neg_not_overflow = F::one();
cols.is_c_0.populate(1);
row
};
debug_assert!(padded_row_template.len() == NUM_DIVREM_COLS);
for row_idx in input.divrem_events.len()..padded_nb_rows {
let row_start = row_idx * NUM_DIVREM_COLS;
values[row_start..row_start + NUM_DIVREM_COLS].copy_from_slice(&padded_row_template);
}
}
fn included(&self, shard: &Self::Record) -> bool {
if let Some(shape) = shard.shape.as_ref() {
shape.included::<F, _>(self)
} else {
!shard.divrem_events.is_empty()
}
}
}
impl<F> BaseAir<F> for DivRemChip {
fn width(&self) -> usize {
NUM_DIVREM_COLS
}
}
impl<AB> Air<AB> for DivRemChip
where
AB: SP1CoreAirBuilder,
{
fn eval(&self, builder: &mut AB) {
let main = builder.main();
let local = main.row_slice(0);
let local: &DivRemCols<AB::Var> = (*local).borrow();
let base = AB::F::from_canonical_u32(1 << 16);
let one: AB::Expr = AB::F::one().into();
let zero: AB::Expr = AB::F::zero().into();
let is_word_operation = local.is_divw + local.is_remw + local.is_divuw + local.is_remuw;
let is_not_word_operation = local.is_divu + local.is_remu + local.is_div + local.is_rem;
let is_signed_word_operation = local.is_divw + local.is_remw;
let is_unsigned_word_operation = local.is_divuw + local.is_remuw;
let is_signed_type = local.is_div + local.is_rem + local.is_divw + local.is_remw;
let u16_max = AB::F::from_canonical_u16(u16::MAX);
builder.assert_eq(
local.is_real_not_word,
local.is_real * (one.clone() - is_word_operation.clone()),
);
{
let msb_sign_pairs = [
(local.b_msb.msb, local.b_neg),
(local.rem_msb.msb, local.rem_neg),
(local.c_msb.msb, local.c_neg),
];
for msb_sign_pair in msb_sign_pairs.iter() {
let msb = msb_sign_pair.0;
let is_negative = msb_sign_pair.1;
builder.assert_eq(msb * is_signed_type.clone(), is_negative);
}
}
{
for i in 0..WORD_SIZE / 2 {
builder.assert_eq(local.adapter.b()[i], local.b[i]);
builder.assert_eq(local.adapter.c()[i], local.c[i]);
}
for i in WORD_SIZE / 2..WORD_SIZE {
builder.assert_eq(
local.b[i],
local.adapter.b()[i] * (one.clone() - is_word_operation.clone())
+ local.b_neg * is_word_operation.clone() * u16_max,
);
builder.assert_eq(
local.c[i],
local.adapter.c()[i] * (one.clone() - is_word_operation.clone())
+ local.c_neg * is_word_operation.clone() * u16_max,
);
}
}
{
for i in 0..WORD_SIZE / 2 {
builder.assert_eq(local.quotient_comp[i], local.quotient[i]);
}
for i in WORD_SIZE / 2..WORD_SIZE {
builder
.when(is_unsigned_word_operation.clone())
.assert_eq(local.quotient_comp[i], AB::Expr::zero());
builder.when(is_signed_word_operation.clone()).assert_eq(
local.quotient_comp[i],
local.quot_msb.msb * AB::F::from_canonical_u16(u16::MAX),
);
builder.when(is_word_operation.clone()).assert_eq(
local.quotient[i],
local.quot_msb.msb * AB::F::from_canonical_u16(u16::MAX),
);
builder
.when(is_not_word_operation.clone())
.assert_eq(local.quotient_comp[i], local.quotient[i]);
}
for i in 0..WORD_SIZE / 2 {
builder.assert_eq(local.remainder_comp[i], local.remainder[i]);
}
for i in WORD_SIZE / 2..WORD_SIZE {
builder
.when(is_unsigned_word_operation.clone())
.assert_eq(local.remainder_comp[i], AB::Expr::zero());
builder.when(is_signed_word_operation.clone()).assert_eq(
local.remainder_comp[i],
local.rem_msb.msb * AB::F::from_canonical_u16(u16::MAX),
);
builder.when(is_word_operation.clone()).assert_eq(
local.remainder[i],
local.rem_msb.msb * AB::F::from_canonical_u16(u16::MAX),
);
builder
.when(is_not_word_operation.clone())
.assert_eq(local.remainder_comp[i], local.remainder[i]);
}
}
{
let lower_half: [AB::Expr; 4] = [
local.c_times_quotient[0].into(),
local.c_times_quotient[1].into(),
local.c_times_quotient[2].into(),
local.c_times_quotient[3].into(),
];
<MulOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
MulOperationInput::new(
Word(lower_half),
local.quotient_comp.map(Into::into),
local.c.map(Into::into),
local.c_times_quotient_lower,
local.is_real.into(),
local.is_real.into(),
AB::Expr::zero(),
AB::Expr::zero(), AB::Expr::zero(),
AB::Expr::zero(),
),
);
let is_mulh = local.is_div + local.is_rem;
let is_mulhu = local.is_divu + local.is_remu;
let upper_half: [AB::Expr; 4] = [
local.c_times_quotient[4].into(),
local.c_times_quotient[5].into(),
local.c_times_quotient[6].into(),
local.c_times_quotient[7].into(),
];
<MulOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
MulOperationInput::new(
Word(upper_half),
local.quotient_comp.map(Into::into),
local.c.map(Into::into),
local.c_times_quotient_upper,
local.is_real_not_word.into(),
AB::Expr::zero(),
is_mulh.clone(),
AB::Expr::zero(),
is_mulhu.clone(),
AB::Expr::zero(),
),
);
}
{
<IsEqualWordOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
IsEqualWordOperationInput::new(
local.adapter.b().map(Into::into),
Word::from(i64::MIN as u64).map(|x: AB::F| x.into()),
local.is_overflow_b,
local.is_real_not_word.into(),
),
);
<IsEqualWordOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
IsEqualWordOperationInput::new(
local.adapter.c().map(Into::into),
Word::from(-1i64 as u64).map(|x: AB::F| x.into()),
local.is_overflow_c,
local.is_real_not_word.into(),
),
);
let mut truncated_b = local.adapter.b().map(Into::into);
let mut truncated_c = local.adapter.c().map(Into::into);
truncated_b[2] = AB::Expr::zero();
truncated_c[2] = AB::Expr::zero();
truncated_b[3] = AB::Expr::zero();
truncated_c[3] = AB::Expr::zero();
IsEqualWordOperation::<AB::F>::eval(
builder,
IsEqualWordOperationInput::new(
truncated_b,
Word::from(i32::MIN as u32 as u64).map(|x: AB::F| x.into()),
local.is_overflow_b,
is_word_operation.clone(),
),
);
IsEqualWordOperation::<AB::F>::eval(
builder,
IsEqualWordOperationInput::new(
truncated_c,
Word::from(-1i32 as u32 as u64).map(|x: AB::F| x.into()),
local.is_overflow_c,
is_word_operation.clone(),
),
);
builder.assert_eq(
local.is_overflow,
local.is_overflow_b.is_diff_zero.result
* local.is_overflow_c.is_diff_zero.result
* is_signed_type.clone(),
);
builder.assert_eq(
local.b_neg_not_overflow,
local.b_neg * (AB::Expr::one() - local.is_overflow),
);
builder.assert_eq(
local.b_not_neg_not_overflow,
(AB::Expr::one() - local.b_neg) * (AB::Expr::one() - local.is_overflow),
);
for i in 0..WORD_SIZE {
builder.when(local.is_overflow).assert_eq(local.quotient[i], local.b[i]);
builder.when(local.is_overflow).assert_eq(local.remainder[i], AB::Expr::zero());
}
}
{
let sign_extension = local.rem_neg * AB::F::from_canonical_u16(u16::MAX);
let mut c_times_quotient_plus_remainder: Vec<AB::Expr> =
vec![AB::Expr::zero(); LONG_WORD_SIZE];
for i in 0..LONG_WORD_SIZE {
c_times_quotient_plus_remainder[i] = local.c_times_quotient[i].into();
if i < WORD_SIZE {
c_times_quotient_plus_remainder[i] =
c_times_quotient_plus_remainder[i].clone() + local.remainder_comp[i].into();
} else {
c_times_quotient_plus_remainder[i] =
c_times_quotient_plus_remainder[i].clone() + sign_extension.clone();
}
c_times_quotient_plus_remainder[i] =
c_times_quotient_plus_remainder[i].clone() - local.carry[i] * base;
if i > 0 {
c_times_quotient_plus_remainder[i] =
c_times_quotient_plus_remainder[i].clone() + local.carry[i - 1].into();
}
}
for i in 0..LONG_WORD_SIZE {
if i < WORD_SIZE {
builder
.when_not(local.is_overflow)
.assert_eq(local.b[i], c_times_quotient_plus_remainder[i].clone());
} else {
builder.when_not(local.is_overflow).assert_eq(
local.b_neg * AB::F::from_canonical_u16(u16::MAX),
c_times_quotient_plus_remainder[i].clone(),
);
}
}
builder.slice_range_check_u16(&c_times_quotient_plus_remainder, local.is_real);
}
for i in 0..WORD_SIZE {
builder
.when(local.is_divu + local.is_div + local.is_divw + local.is_divuw)
.assert_eq(local.quotient[i], local.a[i]);
builder
.when(local.is_remu + local.is_rem + local.is_remw + local.is_remuw)
.assert_eq(local.remainder[i], local.a[i]);
}
{
let mut rem_limb_sum = zero.clone();
for i in 0..WORD_SIZE {
rem_limb_sum = rem_limb_sum.clone() + local.remainder[i].into();
}
builder
.when(local.rem_neg) .assert_one(local.b_neg);
builder
.when(rem_limb_sum.clone()) .when(one.clone() - local.rem_neg) .assert_zero(local.b_neg); }
{
<IsZeroWordOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
IsZeroWordOperationInput::new(
local.c.map(Into::into),
local.is_c_0,
local.is_real.into(),
),
);
for i in 0..WORD_SIZE {
builder
.when(local.is_c_0.result)
.assert_eq(local.quotient[i], AB::F::from_canonical_u16(u16::MAX));
}
for i in 0..WORD_SIZE {
builder.when(local.is_c_0.result).assert_eq(local.remainder_comp[i], local.b[i]);
}
}
{
for i in 0..WORD_SIZE {
builder.when_not(local.c_neg).assert_eq(local.c[i], local.abs_c[i]);
builder
.when_not(local.rem_neg)
.assert_eq(local.remainder_comp[i], local.abs_remainder[i]);
}
<AddOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
AddOperationInput::new(
local.c.map(Into::into),
local.abs_c.map(Into::into),
local.c_neg_operation,
local.abs_c_alu_event.into(),
),
);
builder.slice_range_check_u16(&local.abs_c.0, local.is_real);
builder.when(local.abs_c_alu_event).assert_word_eq(
Word([zero.clone(), zero.clone(), zero.clone(), zero.clone()]),
local.c_neg_operation.value,
);
<AddOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
AddOperationInput::new(
local.remainder_comp.map(Into::into),
local.abs_remainder.map(Into::into),
local.rem_neg_operation,
local.abs_rem_alu_event.into(),
),
);
builder.slice_range_check_u16(&local.abs_remainder.0, local.is_real);
builder.when(local.abs_rem_alu_event).assert_word_eq(
Word([zero.clone(), zero.clone(), zero.clone(), zero.clone()]),
local.rem_neg_operation.value,
);
builder.assert_eq(local.abs_c_alu_event, local.c_neg * local.is_real);
builder.assert_eq(local.abs_rem_alu_event, local.rem_neg * local.is_real);
let max_abs_c_or_1: Word<AB::Expr> = {
let mut v = vec![zero.clone(); WORD_SIZE];
v[0] = local.is_c_0.result * one.clone()
+ (one.clone() - local.is_c_0.result) * local.abs_c[0];
for i in 1..WORD_SIZE {
v[i] = (one.clone() - local.is_c_0.result) * local.abs_c[i];
}
Word(v.try_into().unwrap_or_else(|_| panic!("Incorrect length")))
};
for i in 0..WORD_SIZE {
builder.assert_eq(local.max_abs_c_or_1[i], max_abs_c_or_1[i].clone());
}
builder.assert_eq(
(AB::Expr::one() - local.is_c_0.result) * local.is_real,
local.remainder_check_multiplicity,
);
<LtOperationUnsigned<AB::F> as SP1Operation<AB>>::eval(
builder,
LtOperationUnsignedInput::<AB>::new(
local.abs_remainder.map(Into::into),
local.max_abs_c_or_1.map(Into::into),
local.remainder_lt_operation,
local.remainder_check_multiplicity.into(),
),
);
builder
.when(local.remainder_check_multiplicity)
.assert_eq(one.clone(), local.remainder_lt_operation.u16_compare_operation.bit);
}
{
<U16MSBOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
U16MSBOperationInput::<AB>::new(
local.adapter.b()[WORD_SIZE - 1].into(),
local.b_msb,
local.is_real_not_word.into(),
),
);
<U16MSBOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
U16MSBOperationInput::<AB>::new(
local.adapter.c()[WORD_SIZE - 1].into(),
local.c_msb,
local.is_real_not_word.into(),
),
);
<U16MSBOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
U16MSBOperationInput::<AB>::new(
local.remainder[WORD_SIZE - 1].into(),
local.rem_msb,
local.is_real_not_word.into(),
),
);
<U16MSBOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
U16MSBOperationInput::<AB>::new(
local.adapter.b()[WORD_SIZE / 2 - 1].into(),
local.b_msb,
is_word_operation.clone(),
),
);
<U16MSBOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
U16MSBOperationInput::<AB>::new(
local.adapter.c()[WORD_SIZE / 2 - 1].into(),
local.c_msb,
is_word_operation.clone(),
),
);
<U16MSBOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
U16MSBOperationInput::<AB>::new(
local.remainder[WORD_SIZE / 2 - 1].into(),
local.rem_msb,
is_word_operation.clone(),
),
);
<U16MSBOperation<AB::F> as SP1Operation<AB>>::eval(
builder,
U16MSBOperationInput::<AB>::new(
local.quotient[WORD_SIZE / 2 - 1].into(),
local.quot_msb,
is_word_operation.clone(),
),
);
}
{
builder.slice_range_check_u16(&local.quotient.0, local.is_real);
builder.slice_range_check_u16(&local.remainder.0, local.is_real);
local.carry.iter().for_each(|carry| {
builder.assert_bool(*carry);
});
builder.slice_range_check_u16(&local.c_times_quotient, local.is_real);
}
{
let bool_flags = [
local.is_div,
local.is_divu,
local.is_rem,
local.is_remu,
local.is_divw,
local.is_remw,
local.is_divuw,
local.is_remuw,
local.is_overflow,
local.is_real_not_word,
local.b_neg,
local.b_neg_not_overflow,
local.b_not_neg_not_overflow,
local.rem_neg,
local.c_neg,
local.is_real,
local.abs_c_alu_event,
local.abs_rem_alu_event,
];
for flag in bool_flags.iter() {
builder.assert_bool(*flag);
}
}
{
builder.assert_eq(
one.clone(),
local.is_divu
+ local.is_remu
+ local.is_div
+ local.is_rem
+ local.is_divw
+ local.is_remw
+ local.is_divuw
+ local.is_remuw,
);
let opcode = {
let divu: AB::Expr = AB::F::from_canonical_u32(Opcode::DIVU as u32).into();
let remu: AB::Expr = AB::F::from_canonical_u32(Opcode::REMU as u32).into();
let div: AB::Expr = AB::F::from_canonical_u32(Opcode::DIV as u32).into();
let rem: AB::Expr = AB::F::from_canonical_u32(Opcode::REM as u32).into();
let divw: AB::Expr = AB::F::from_canonical_u32(Opcode::DIVW as u32).into();
let remw: AB::Expr = AB::F::from_canonical_u32(Opcode::REMW as u32).into();
let divuw: AB::Expr = AB::F::from_canonical_u32(Opcode::DIVUW as u32).into();
let remuw: AB::Expr = AB::F::from_canonical_u32(Opcode::REMUW as u32).into();
local.is_divu * divu
+ local.is_remu * remu
+ local.is_div * div
+ local.is_rem * rem
+ local.is_divw * divw
+ local.is_remw * remw
+ local.is_divuw * divuw
+ local.is_remuw * remuw
};
let funct3 = local.is_divu
* AB::Expr::from_canonical_u8(Opcode::DIVU.funct3().unwrap())
+ local.is_remu * AB::Expr::from_canonical_u8(Opcode::REMU.funct3().unwrap())
+ local.is_div * AB::Expr::from_canonical_u8(Opcode::DIV.funct3().unwrap())
+ local.is_rem * AB::Expr::from_canonical_u8(Opcode::REM.funct3().unwrap())
+ local.is_divw * AB::Expr::from_canonical_u8(Opcode::DIVW.funct3().unwrap())
+ local.is_remw * AB::Expr::from_canonical_u8(Opcode::REMW.funct3().unwrap())
+ local.is_divuw * AB::Expr::from_canonical_u8(Opcode::DIVUW.funct3().unwrap())
+ local.is_remuw * AB::Expr::from_canonical_u8(Opcode::REMUW.funct3().unwrap());
let funct7 = local.is_divu
* AB::Expr::from_canonical_u8(Opcode::DIVU.funct7().unwrap())
+ local.is_remu * AB::Expr::from_canonical_u8(Opcode::REMU.funct7().unwrap())
+ local.is_div * AB::Expr::from_canonical_u8(Opcode::DIV.funct7().unwrap())
+ local.is_rem * AB::Expr::from_canonical_u8(Opcode::REM.funct7().unwrap())
+ local.is_divw * AB::Expr::from_canonical_u8(Opcode::DIVW.funct7().unwrap())
+ local.is_remw * AB::Expr::from_canonical_u8(Opcode::REMW.funct7().unwrap())
+ local.is_divuw * AB::Expr::from_canonical_u8(Opcode::DIVUW.funct7().unwrap())
+ local.is_remuw * AB::Expr::from_canonical_u8(Opcode::REMUW.funct7().unwrap());
let divu_base = Opcode::DIVU.base_opcode().0;
let remu_base = Opcode::REMU.base_opcode().0;
let div_base = Opcode::DIV.base_opcode().0;
let rem_base = Opcode::REM.base_opcode().0;
let divw_base = Opcode::DIVW.base_opcode().0;
let remw_base = Opcode::REMW.base_opcode().0;
let divuw_base = Opcode::DIVUW.base_opcode().0;
let remuw_base = Opcode::REMUW.base_opcode().0;
let divu_base_expr = AB::Expr::from_canonical_u32(divu_base);
let remu_base_expr = AB::Expr::from_canonical_u32(remu_base);
let div_base_expr = AB::Expr::from_canonical_u32(div_base);
let rem_base_expr = AB::Expr::from_canonical_u32(rem_base);
let divw_base_expr = AB::Expr::from_canonical_u32(divw_base);
let remw_base_expr = AB::Expr::from_canonical_u32(remw_base);
let divuw_base_expr = AB::Expr::from_canonical_u32(divuw_base);
let remuw_base_expr = AB::Expr::from_canonical_u32(remuw_base);
let calculated_base_opcode = local.is_divu * divu_base_expr
+ local.is_remu * remu_base_expr
+ local.is_div * div_base_expr
+ local.is_rem * rem_base_expr
+ local.is_divw * divw_base_expr
+ local.is_remw * remw_base_expr
+ local.is_divuw * divuw_base_expr
+ local.is_remuw * remuw_base_expr;
let divu_instr_type = Opcode::DIVU.instruction_type().0 as u32;
let remu_instr_type = Opcode::REMU.instruction_type().0 as u32;
let div_instr_type = Opcode::DIV.instruction_type().0 as u32;
let rem_instr_type = Opcode::REM.instruction_type().0 as u32;
let divw_instr_type = Opcode::DIVW.instruction_type().0 as u32;
let remw_instr_type = Opcode::REMW.instruction_type().0 as u32;
let divuw_instr_type = Opcode::DIVUW.instruction_type().0 as u32;
let calculated_instr_type = local.is_divu
* AB::Expr::from_canonical_u32(divu_instr_type)
+ local.is_remu * AB::Expr::from_canonical_u32(remu_instr_type)
+ local.is_div * AB::Expr::from_canonical_u32(div_instr_type)
+ local.is_rem * AB::Expr::from_canonical_u32(rem_instr_type)
+ local.is_divw * AB::Expr::from_canonical_u32(divw_instr_type)
+ local.is_remw * AB::Expr::from_canonical_u32(remw_instr_type)
+ local.is_divuw * AB::Expr::from_canonical_u32(divuw_instr_type);
<CPUState<AB::F> as SP1Operation<AB>>::eval(
builder,
CPUStateInput {
cols: local.state,
next_pc: [
local.state.pc[0] + AB::F::from_canonical_u32(PC_INC),
local.state.pc[1].into(),
local.state.pc[2].into(),
],
clk_increment: AB::Expr::from_canonical_u32(CLK_INC),
is_real: local.is_real.into(),
},
);
let r_reader_input = RTypeReaderInput::<AB, AB::Expr>::new(
local.state.clk_high::<AB>(),
local.state.clk_low::<AB>(),
local.state.pc,
opcode,
[calculated_instr_type, calculated_base_opcode, funct3, funct7],
local.a.map(|x| x.into()),
local.adapter,
local.is_real.into(),
);
<RTypeReader<AB::F> as SP1Operation<AB>>::eval(builder, r_reader_input);
}
}
}