sp1_core_machine/program/
mod.rsuse core::{
borrow::{Borrow, BorrowMut},
mem::size_of,
};
use std::collections::HashMap;
use crate::{air::ProgramAirBuilder, utils::pad_rows_fixed};
use p3_air::{Air, BaseAir, PairBuilder};
use p3_field::PrimeField;
use p3_matrix::{dense::RowMajorMatrix, Matrix};
use sp1_core_executor::{ExecutionRecord, Program};
use sp1_derive::AlignedBorrow;
use sp1_stark::air::{MachineAir, SP1AirBuilder};
use crate::cpu::columns::{InstructionCols, OpcodeSelectorCols};
pub const NUM_PROGRAM_PREPROCESSED_COLS: usize = size_of::<ProgramPreprocessedCols<u8>>();
pub const NUM_PROGRAM_MULT_COLS: usize = size_of::<ProgramMultiplicityCols<u8>>();
#[derive(AlignedBorrow, Clone, Copy, Default)]
#[repr(C)]
pub struct ProgramPreprocessedCols<T> {
pub pc: T,
pub instruction: InstructionCols<T>,
pub selectors: OpcodeSelectorCols<T>,
}
#[derive(AlignedBorrow, Clone, Copy, Default)]
#[repr(C)]
pub struct ProgramMultiplicityCols<T> {
pub shard: T,
pub multiplicity: T,
}
#[derive(Default)]
pub struct ProgramChip;
impl ProgramChip {
pub const fn new() -> Self {
Self {}
}
}
impl<F: PrimeField> MachineAir<F> for ProgramChip {
type Record = ExecutionRecord;
type Program = Program;
fn name(&self) -> String {
"Program".to_string()
}
fn preprocessed_width(&self) -> usize {
NUM_PROGRAM_PREPROCESSED_COLS
}
fn generate_preprocessed_trace(&self, program: &Self::Program) -> Option<RowMajorMatrix<F>> {
debug_assert!(
!program.instructions.is_empty() || program.preprocessed_shape.is_some(),
"empty program"
);
let mut rows = program
.instructions
.iter()
.enumerate()
.map(|(i, &instruction)| {
let pc = program.pc_base + (i as u32 * 4);
let mut row = [F::zero(); NUM_PROGRAM_PREPROCESSED_COLS];
let cols: &mut ProgramPreprocessedCols<F> = row.as_mut_slice().borrow_mut();
cols.pc = F::from_canonical_u32(pc);
cols.instruction.populate(instruction);
cols.selectors.populate(instruction);
row
})
.collect::<Vec<_>>();
pad_rows_fixed(
&mut rows,
|| [F::zero(); NUM_PROGRAM_PREPROCESSED_COLS],
program.fixed_log2_rows::<F, _>(self),
);
let trace = RowMajorMatrix::new(
rows.into_iter().flatten().collect::<Vec<_>>(),
NUM_PROGRAM_PREPROCESSED_COLS,
);
Some(trace)
}
fn generate_dependencies(&self, _input: &ExecutionRecord, _output: &mut ExecutionRecord) {
}
fn generate_trace(
&self,
input: &ExecutionRecord,
_output: &mut ExecutionRecord,
) -> RowMajorMatrix<F> {
let mut instruction_counts = HashMap::new();
input.cpu_events.iter().for_each(|event| {
let pc = event.pc;
instruction_counts.entry(pc).and_modify(|count| *count += 1).or_insert(1);
});
let mut rows = input
.program
.instructions
.clone()
.into_iter()
.enumerate()
.map(|(i, _)| {
let pc = input.program.pc_base + (i as u32 * 4);
let mut row = [F::zero(); NUM_PROGRAM_MULT_COLS];
let cols: &mut ProgramMultiplicityCols<F> = row.as_mut_slice().borrow_mut();
cols.shard = F::from_canonical_u32(input.public_values.execution_shard);
cols.multiplicity =
F::from_canonical_usize(*instruction_counts.get(&pc).unwrap_or(&0));
row
})
.collect::<Vec<_>>();
pad_rows_fixed(
&mut rows,
|| [F::zero(); NUM_PROGRAM_MULT_COLS],
input.fixed_log2_rows::<F, _>(self),
);
RowMajorMatrix::new(rows.into_iter().flatten().collect::<Vec<_>>(), NUM_PROGRAM_MULT_COLS)
}
fn included(&self, _: &Self::Record) -> bool {
true
}
}
impl<F> BaseAir<F> for ProgramChip {
fn width(&self) -> usize {
NUM_PROGRAM_MULT_COLS
}
}
impl<AB> Air<AB> for ProgramChip
where
AB: SP1AirBuilder + PairBuilder,
{
fn eval(&self, builder: &mut AB) {
let main = builder.main();
let preprocessed = builder.preprocessed();
let prep_local = preprocessed.row_slice(0);
let prep_local: &ProgramPreprocessedCols<AB::Var> = (*prep_local).borrow();
let mult_local = main.row_slice(0);
let mult_local: &ProgramMultiplicityCols<AB::Var> = (*mult_local).borrow();
builder.receive_program(
prep_local.pc,
prep_local.instruction,
prep_local.selectors,
mult_local.shard,
mult_local.multiplicity,
);
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use hashbrown::HashMap;
use p3_baby_bear::BabyBear;
use p3_matrix::dense::RowMajorMatrix;
use sp1_core_executor::{ExecutionRecord, Instruction, Opcode, Program};
use sp1_stark::air::MachineAir;
use crate::program::ProgramChip;
#[test]
fn generate_trace() {
let instructions = vec![
Instruction::new(Opcode::ADD, 29, 0, 5, false, true),
Instruction::new(Opcode::ADD, 30, 0, 37, false, true),
Instruction::new(Opcode::ADD, 31, 30, 29, false, false),
];
let shard = ExecutionRecord {
program: Arc::new(Program {
instructions,
pc_start: 0,
pc_base: 0,
memory_image: HashMap::new(),
preprocessed_shape: None,
}),
..Default::default()
};
let chip = ProgramChip::new();
let trace: RowMajorMatrix<BabyBear> =
chip.generate_trace(&shard, &mut ExecutionRecord::default());
println!("{:?}", trace.values)
}
}