use std::iter::once;
use itertools::Itertools;
use slop_air::AirBuilder;
use slop_algebra::{AbstractField, Field};
use sp1_core_executor::ByteOpcode;
use sp1_hypercube::{
air::{AirInteraction, BaseAirBuilder, ByteAirBuilder, InteractionScope},
InteractionKind, Word,
};
use crate::memory::{
MemoryAccessCols, MemoryAccessTimestamp, PageProtAccessCols, RegisterAccessCols,
RegisterAccessTimestamp,
};
pub trait MemoryAirBuilder: BaseAirBuilder {
fn eval_memory_access_read<E: Into<Self::Expr> + Clone>(
&mut self,
clk_high: impl Into<Self::Expr>,
clk_low: impl Into<Self::Expr>,
addr: &[Self::Expr; 3],
mem_access: MemoryAccessCols<E>,
do_check: impl Into<Self::Expr>,
) {
let do_check: Self::Expr = do_check.into();
let clk_high: Self::Expr = clk_high.into();
let clk_low: Self::Expr = clk_low.into();
self.assert_bool(do_check.clone());
self.eval_memory_access_timestamp(
&mem_access.access_timestamp,
do_check.clone(),
clk_high.clone(),
clk_low.clone(),
);
let prev_high = mem_access.access_timestamp.prev_high.clone().into();
let prev_low = mem_access.access_timestamp.prev_low.clone().into();
let prev_values = once(prev_high)
.chain(once(prev_low))
.chain(addr.clone())
.chain(mem_access.prev_value.clone().map(Into::into))
.collect();
let current_values = once(clk_high)
.chain(once(clk_low))
.chain(addr.clone())
.chain(mem_access.prev_value.clone().map(Into::into))
.collect();
self.send(
AirInteraction::new(prev_values, do_check.clone(), InteractionKind::Memory),
InteractionScope::Local,
);
self.receive(
AirInteraction::new(current_values, do_check.clone(), InteractionKind::Memory),
InteractionScope::Local,
);
}
fn eval_memory_access_write<E: Into<Self::Expr> + Clone>(
&mut self,
clk_high: impl Into<Self::Expr>,
clk_low: impl Into<Self::Expr>,
addr: &[Self::Expr; 3],
mem_access: MemoryAccessCols<E>,
write_value: Word<impl Into<Self::Expr>>,
do_check: impl Into<Self::Expr>,
) {
let do_check: Self::Expr = do_check.into();
let clk_high: Self::Expr = clk_high.into();
let clk_low: Self::Expr = clk_low.into();
self.assert_bool(do_check.clone());
self.eval_memory_access_timestamp(
&mem_access.access_timestamp,
do_check.clone(),
clk_high.clone(),
clk_low.clone(),
);
let prev_high = mem_access.access_timestamp.prev_high.clone().into();
let prev_low = mem_access.access_timestamp.prev_low.clone().into();
let prev_values = once(prev_high)
.chain(once(prev_low.clone()))
.chain(addr.clone())
.chain(mem_access.prev_value.clone().map(Into::into))
.collect();
let current_values = once(clk_high.clone())
.chain(once(clk_low.clone()))
.chain(addr.clone())
.chain(write_value.map(Into::into))
.collect();
self.send(
AirInteraction::new(prev_values, do_check.clone(), InteractionKind::Memory),
InteractionScope::Local,
);
self.receive(
AirInteraction::new(current_values, do_check.clone(), InteractionKind::Memory),
InteractionScope::Local,
);
}
fn eval_register_access_read<E: Into<Self::Expr> + Clone>(
&mut self,
clk_high: impl Into<Self::Expr>,
clk_low: impl Into<Self::Expr>,
addr: [Self::Expr; 3],
reg_access: RegisterAccessCols<E>,
do_check: impl Into<Self::Expr>,
) {
let do_check: Self::Expr = do_check.into();
let clk_high: Self::Expr = clk_high.into();
let clk_low: Self::Expr = clk_low.into();
self.assert_bool(do_check.clone());
self.eval_register_access_timestamp(
®_access.access_timestamp,
do_check.clone(),
clk_low.clone(),
);
let prev_low = reg_access.access_timestamp.prev_low.clone().into();
let prev_values = once(clk_high.clone())
.chain(once(prev_low))
.chain(addr.clone())
.chain(reg_access.prev_value.clone().map(Into::into))
.collect();
let current_values = once(clk_high)
.chain(once(clk_low))
.chain(addr.clone())
.chain(reg_access.prev_value.clone().map(Into::into))
.collect();
self.send(
AirInteraction::new(prev_values, do_check.clone(), InteractionKind::Memory),
InteractionScope::Local,
);
self.receive(
AirInteraction::new(current_values, do_check.clone(), InteractionKind::Memory),
InteractionScope::Local,
);
}
fn eval_register_access_write<E: Into<Self::Expr> + Clone>(
&mut self,
clk_high: impl Into<Self::Expr>,
clk_low: impl Into<Self::Expr>,
addr: [Self::Expr; 3],
reg_access: RegisterAccessCols<E>,
write_value: Word<impl Into<Self::Expr>>,
do_check: impl Into<Self::Expr>,
) {
let do_check: Self::Expr = do_check.into();
let clk_high: Self::Expr = clk_high.into();
let clk_low: Self::Expr = clk_low.into();
self.assert_bool(do_check.clone());
self.eval_register_access_timestamp(
®_access.access_timestamp,
do_check.clone(),
clk_low.clone(),
);
let prev_low = reg_access.access_timestamp.prev_low.clone().into();
let prev_values = once(clk_high.clone())
.chain(once(prev_low.clone()))
.chain(addr.clone())
.chain(reg_access.prev_value.clone().map(Into::into))
.collect();
let current_values = once(clk_high.clone())
.chain(once(clk_low.clone()))
.chain(addr.clone())
.chain(write_value.map(Into::into))
.collect();
self.send(
AirInteraction::new(prev_values, do_check.clone(), InteractionKind::Memory),
InteractionScope::Local,
);
self.receive(
AirInteraction::new(current_values, do_check.clone(), InteractionKind::Memory),
InteractionScope::Local,
);
}
fn eval_memory_access_slice_read<E: Into<Self::Expr> + Copy>(
&mut self,
clk_high: impl Into<Self::Expr> + Clone,
clk_low: impl Into<Self::Expr> + Clone,
addr_slice: &[[Self::Expr; 3]],
memory_access_slice: &[MemoryAccessCols<E>],
verify_memory_access: impl Into<Self::Expr> + Copy,
) {
for (access_slice, addr) in memory_access_slice.iter().zip(addr_slice) {
self.eval_memory_access_read(
clk_high.clone(),
clk_low.clone(),
addr,
*access_slice,
verify_memory_access,
);
}
}
fn eval_memory_access_slice_write<E: Into<Self::Expr> + Copy>(
&mut self,
clk_high: impl Into<Self::Expr> + Clone,
clk_low: impl Into<Self::Expr> + Clone,
addr_slice: &[[Self::Expr; 3]],
memory_access_slice: &[MemoryAccessCols<E>],
write_values: Vec<Word<impl Into<Self::Expr>>>,
verify_memory_access: impl Into<Self::Expr> + Copy,
) {
for ((access_slice, addr), write_value) in
memory_access_slice.iter().zip_eq(addr_slice).zip_eq(write_values)
{
self.eval_memory_access_write(
clk_high.clone(),
clk_low.clone(),
addr,
*access_slice,
write_value,
verify_memory_access,
);
}
}
fn eval_memory_access_timestamp(
&mut self,
mem_access: &MemoryAccessTimestamp<impl Into<Self::Expr> + Clone>,
do_check: impl Into<Self::Expr>,
clk_high: impl Into<Self::Expr> + Clone,
clk_low: impl Into<Self::Expr>,
) {
let do_check: Self::Expr = do_check.into();
let compare_low: Self::Expr = mem_access.compare_low.clone().into();
let clk_high: Self::Expr = clk_high.clone().into();
let prev_high: Self::Expr = mem_access.prev_high.clone().into();
self.when(do_check.clone()).assert_bool(compare_low.clone());
self.when(do_check.clone())
.when(compare_low.clone())
.assert_eq(clk_high.clone(), prev_high);
let prev_comp_value = self.if_else(
compare_low.clone(),
mem_access.prev_low.clone(),
mem_access.prev_high.clone(),
);
let current_comp_val = self.if_else(compare_low.clone(), clk_low.into(), clk_high.clone());
let diff_minus_one = current_comp_val - prev_comp_value - Self::Expr::one();
self.when(do_check.clone()).assert_eq(
diff_minus_one,
mem_access.diff_low_limb.clone().into()
+ mem_access.diff_high_limb.clone().into()
* Self::Expr::from_canonical_u32(1 << 16),
);
self.send_byte(
Self::Expr::from_canonical_u8(ByteOpcode::Range as u8),
mem_access.diff_low_limb.clone(),
Self::Expr::from_canonical_u32(16),
Self::Expr::zero(),
do_check.clone(),
);
self.send_byte(
Self::Expr::from_canonical_u8(ByteOpcode::U8Range as u8),
Self::Expr::zero(),
mem_access.diff_high_limb.clone(),
Self::Expr::zero(),
do_check,
)
}
fn eval_register_access_timestamp(
&mut self,
reg_access: &RegisterAccessTimestamp<impl Into<Self::Expr> + Clone>,
do_check: impl Into<Self::Expr>,
clk: impl Into<Self::Expr>,
) {
let do_check: Self::Expr = do_check.into();
let diff_minus_one = clk.into() - reg_access.prev_low.clone().into() - Self::Expr::one();
let diff_high_limb = (diff_minus_one.clone() - reg_access.diff_low_limb.clone().into())
* Self::F::from_canonical_u32(1 << 16).inverse();
self.send_byte(
Self::Expr::from_canonical_u8(ByteOpcode::Range as u8),
reg_access.diff_low_limb.clone(),
Self::Expr::from_canonical_u32(16),
Self::Expr::zero(),
do_check.clone(),
);
self.send_byte(
Self::Expr::from_canonical_u8(ByteOpcode::U8Range as u8),
Self::Expr::zero(),
diff_high_limb,
Self::Expr::zero(),
do_check,
)
}
#[allow(clippy::too_many_arguments)]
fn send_page_prot(
&mut self,
clk_high: impl Into<Self::Expr>,
clk_low: impl Into<Self::Expr>,
addr: &[Self::Expr; 3],
permissions: impl Into<Self::Expr>,
multiplicity: impl Into<Self::Expr>,
) {
let values = once(clk_high.into())
.chain(once(clk_low.into()))
.chain(addr.clone())
.chain(once(permissions.into()))
.collect();
self.send(
AirInteraction::new(values, multiplicity.into(), InteractionKind::PageProt),
InteractionScope::Local,
);
}
#[allow(clippy::too_many_arguments)]
fn receive_page_prot(
&mut self,
clk_high: impl Into<Self::Expr>,
clk_low: impl Into<Self::Expr>,
addr: &[Self::Expr; 3],
permissions: impl Into<Self::Expr>,
multiplicity: impl Into<Self::Expr>,
) {
let values = once(clk_high.into())
.chain(once(clk_low.into()))
.chain(addr.clone())
.chain(once(permissions.into()))
.collect();
self.receive(
AirInteraction::new(values, multiplicity.into(), InteractionKind::PageProt),
InteractionScope::Local,
);
}
fn eval_page_prot_access_read<E: Into<Self::Expr> + Clone>(
&mut self,
clk_high: impl Into<Self::Expr>,
clk_low: impl Into<Self::Expr>,
prot_page_idx: &[impl Into<Self::Expr> + Clone; 3],
page_prot_access: PageProtAccessCols<E>,
do_check: impl Into<Self::Expr>,
) {
let do_check: Self::Expr = do_check.into();
let clk_high: Self::Expr = clk_high.into();
let clk_low: Self::Expr = clk_low.into();
self.assert_bool(do_check.clone());
self.eval_memory_access_timestamp(
&page_prot_access.access_timestamp,
do_check.clone(),
clk_high.clone(),
clk_low.clone(),
);
let prev_high = page_prot_access.access_timestamp.prev_high.clone().into();
let prev_low = page_prot_access.access_timestamp.prev_low.clone().into();
let prev_values = once(prev_high)
.chain(once(prev_low))
.chain(prot_page_idx.clone().map(Into::into))
.chain(once(page_prot_access.prev_prot_bitmap.clone().into()))
.collect();
let current_values = once(clk_high)
.chain(once(clk_low))
.chain(prot_page_idx.clone().map(Into::into))
.chain(once(page_prot_access.prev_prot_bitmap.into()))
.collect();
self.send(
AirInteraction::new(prev_values, do_check.clone(), InteractionKind::PageProtAccess),
InteractionScope::Local,
);
self.receive(
AirInteraction::new(current_values, do_check.clone(), InteractionKind::PageProtAccess),
InteractionScope::Local,
);
}
fn eval_page_prot_access_write<E: Into<Self::Expr> + Clone>(
&mut self,
clk_high: impl Into<Self::Expr>,
clk_low: impl Into<Self::Expr>,
prot_page_idx: &[impl Into<Self::Expr> + Clone; 3],
page_prot_access: PageProtAccessCols<E>,
new_prot_bitmap: impl Into<Self::Expr>,
do_check: impl Into<Self::Expr>,
) {
let do_check: Self::Expr = do_check.into();
let clk_high: Self::Expr = clk_high.into();
let clk_low: Self::Expr = clk_low.into();
let new_prot_bitmap: Self::Expr = new_prot_bitmap.into();
self.assert_bool(do_check.clone());
self.eval_memory_access_timestamp(
&page_prot_access.access_timestamp,
do_check.clone(),
clk_high.clone(),
clk_low.clone(),
);
let prev_high = page_prot_access.access_timestamp.prev_high.clone().into();
let prev_low = page_prot_access.access_timestamp.prev_low.clone().into();
let prev_values = once(prev_high)
.chain(once(prev_low))
.chain(prot_page_idx.clone().map(Into::into))
.chain(once(page_prot_access.prev_prot_bitmap.clone().into()))
.collect();
let current_values = once(clk_high)
.chain(once(clk_low))
.chain(prot_page_idx.clone().map(Into::into))
.chain(once(new_prot_bitmap))
.collect();
self.send(
AirInteraction::new(prev_values, do_check.clone(), InteractionKind::PageProtAccess),
InteractionScope::Local,
);
self.receive(
AirInteraction::new(current_values, do_check.clone(), InteractionKind::PageProtAccess),
InteractionScope::Local,
);
}
}