Skip to content

Commit

Permalink
work on shift
Browse files Browse the repository at this point in the history
  • Loading branch information
zemse committed Oct 3, 2024
1 parent 2c825dd commit 5dd54c2
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 3 deletions.
9 changes: 9 additions & 0 deletions ceno_zkvm/src/chip_handler/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,15 @@ impl<'a, E: ExtensionField> CircuitBuilder<'a, E> {
self.logic_u8(ROMType::Ltu, a, b, c)
}

pub fn lookup_pow(
&mut self,
a: Expression<E>,
b: Expression<E>,
c: Expression<E>,
) -> Result<(), ZKVMError> {
self.logic_u8(ROMType::Pow, a, b, c)
}

/// less_than
pub(crate) fn less_than<N, NR>(
&mut self,
Expand Down
2 changes: 2 additions & 0 deletions ceno_zkvm/src/instructions/riscv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pub mod constants;
pub mod divu;
mod i_insn;
pub mod logic;
pub mod shift;

pub mod sltu;

mod b_insn;
Expand Down
355 changes: 355 additions & 0 deletions ceno_zkvm/src/instructions/riscv/shift.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,355 @@
use std::{marker::PhantomData, mem::MaybeUninit};

use ceno_emul::InsnKind;
use ff_ext::ExtensionField;

use crate::{
expression::{ToExpr, WitIn},
instructions::Instruction,
set_val, Value,
};

use super::{constants::UInt, r_insn::RInstructionConfig, RIVInstruction};

pub struct ShiftConfig<E: ExtensionField> {
r_insn: RInstructionConfig<E>,

rs1_read: UInt<E>,
rs2_read: UInt<E>,
rd_written: UInt<E>,

quotient: UInt<E>,
rs2_low5: WitIn,
multiplier: UInt<E>,

intermediate: Option<UInt<E>>,
remainder: Option<UInt<E>>,
}

pub struct ShiftLogicalInstruction<E, I>(PhantomData<(E, I)>);

struct SllOp;
impl RIVInstruction for SllOp {
const INST_KIND: InsnKind = InsnKind::SLL;
}

struct SrlOp;
impl RIVInstruction for SrlOp {
const INST_KIND: InsnKind = InsnKind::SRL;
}

impl<E: ExtensionField, I: RIVInstruction> Instruction<E> for ShiftLogicalInstruction<E, I> {
type InstructionConfig = ShiftConfig<E>;

fn name() -> String {
format!("{:?}", I::INST_KIND)
}

fn construct_circuit(
circuit_builder: &mut crate::circuit_builder::CircuitBuilder<E>,
) -> Result<Self::InstructionConfig, crate::error::ZKVMError> {
let rs2_read = UInt::new_unchecked(|| "rs2_read", circuit_builder)?;
let rs2_low5 = circuit_builder.create_witin(|| "rs2_low5")?;
let mut multiplier = UInt::new_unchecked(|| "multiplier", circuit_builder)?;
let quotient = UInt::new(|| "quotient", circuit_builder)?;

let (rs1_read, rd_written, intermediate, remainder) = if I::INST_KIND == InsnKind::SLL {
let mut rs1_read = UInt::new_unchecked(|| "rs1_read", circuit_builder)?;
let rd_written = rs1_read.mul(
|| "rd_written = rs1_read * multiplier",
circuit_builder,
&mut multiplier,
true,
)?;
(rs1_read, rd_written, None, None)
} else if I::INST_KIND == InsnKind::SRL {
let mut rd_written = UInt::new_unchecked(|| "rd_written", circuit_builder)?;
let remainder = UInt::new_unchecked(|| "remainder", circuit_builder)?;
let (intermediate, rs1_read) = rd_written.mul_add(
|| "rs1_read = rd_written * multiplier + remainder",
circuit_builder,
&mut multiplier,
&remainder,
true,
)?;
(rs1_read, rd_written, Some(intermediate), Some(remainder))
} else {
unreachable!()
};

let r_insn = RInstructionConfig::<E>::construct_circuit(
circuit_builder,
I::INST_KIND,
rs1_read.register_expr(),
rs2_read.register_expr(),
rd_written.register_expr(),
)?;

circuit_builder.lookup_pow(2.into(), rs2_low5.expr(), multiplier.value())?;
circuit_builder.assert_ux::<_, _, 5>(|| "rs2_low5 in u5", rs2_low5.expr())?;
circuit_builder.require_equal(
|| "rs2 == quotient * 2^5 + rs2_low5",
rs2_read.value(),
quotient.value() * (1 << 5).into() + rs2_low5.expr(),
)?;

Ok(ShiftConfig {
r_insn,
rs1_read,
rs2_read,
rd_written,
quotient,
rs2_low5,
multiplier,
intermediate,
remainder,
})
}

fn assign_instance(
config: &Self::InstructionConfig,
instance: &mut [std::mem::MaybeUninit<<E as ExtensionField>::BaseField>],
lk_multiplicity: &mut crate::witness::LkMultiplicity,
step: &ceno_emul::StepRecord,
) -> Result<(), crate::error::ZKVMError> {
let rs2_read = Value::new_unchecked(step.rs2().unwrap().value);
let rs2_low5 = rs2_read.as_u64() & 0b11111;
let multiplier = Value::new_unchecked((1 << rs2_low5) as u32);
let quotient = Value::new_unchecked(((rs2_read.as_u64() - rs2_low5) >> 5) as u32);

if I::INST_KIND == InsnKind::SLL {
let rs1_read = Value::new_unchecked(step.rs1().unwrap().value);
let rd_written = rs1_read.mul(&multiplier, lk_multiplicity, true);
config.rs1_read.assign_value(instance, rs1_read);
config.rd_written.assign_limb_with_carry_auxiliary(
instance,
lk_multiplicity,
&rd_written,
)?;
} else if I::INST_KIND == InsnKind::SRL {
let rd_written = Value::new_unchecked(step.rd().unwrap().value.after);
let remainder = Value::new_unchecked(
// rs1 - rd * multiplier
step.rs1()
.unwrap()
.value
.wrapping_sub((rd_written.as_u64() * multiplier.as_u64()) as u32),
);
let (rs1_read, intermediate) =
rd_written.mul_add(&multiplier, &remainder, lk_multiplicity, true);

config.rs1_read.assign_limb_with_carry(instance, &rs1_read);
config.rd_written.assign_value(instance, rd_written);
config
.remainder
.as_ref()
.unwrap()
.assign_value(instance, remainder);
config
.intermediate
.as_ref()
.unwrap()
.assign_limb_with_carry_auxiliary(instance, lk_multiplicity, &intermediate)?;
} else {
unreachable!()
};

config
.r_insn
.assign_instance(instance, lk_multiplicity, step)?;
config.rs2_read.assign_value(instance, rs2_read);
set_val!(instance, config.rs2_low5, rs2_low5);
config.quotient.assign_value(instance, quotient);
config.multiplier.assign_value(instance, multiplier);

Ok(())
}
}

#[cfg(test)]
mod tests {
use ceno_emul::{Change, StepRecord};
use goldilocks::GoldilocksExt2;
use itertools::Itertools;
use multilinear_extensions::mle::IntoMLEs;

use crate::{
circuit_builder::{CircuitBuilder, ConstraintSystem},
instructions::Instruction,
scheme::mock_prover::{MockProver, MOCK_PC_SLL, MOCK_PC_SRL, MOCK_PROGRAM},
};

use super::{ShiftLogicalInstruction, SllOp, SrlOp};

#[test]
fn test_opcode_sll_1() {
let mut cs = ConstraintSystem::<GoldilocksExt2>::new(|| "riscv");
let mut cb = CircuitBuilder::new(&mut cs);
let config = cb
.namespace(
|| "sll",
|cb| {
let config =
ShiftLogicalInstruction::<GoldilocksExt2, SllOp>::construct_circuit(cb);
Ok(config)
},
)
.unwrap()
.unwrap();

let (raw_witin, _) = ShiftLogicalInstruction::<GoldilocksExt2, SllOp>::assign_instances(
&config,
cb.cs.num_witin as usize,
vec![StepRecord::new_r_instruction(
3,
MOCK_PC_SLL,
MOCK_PROGRAM[18],
32,
3,
Change::new(0, 32 << 3),
0,
)],
)
.unwrap();

MockProver::assert_satisfied(
&cb,
&raw_witin
.de_interleaving()
.into_mles()
.into_iter()
.map(|v| v.into())
.collect_vec(),
None,
);
}

#[test]
fn test_opcode_sll_2_overflow() {
let mut cs = ConstraintSystem::<GoldilocksExt2>::new(|| "riscv");
let mut cb = CircuitBuilder::new(&mut cs);
let config = cb
.namespace(
|| "sll",
|cb| {
let config =
ShiftLogicalInstruction::<GoldilocksExt2, SllOp>::construct_circuit(cb);
Ok(config)
},
)
.unwrap()
.unwrap();

let (raw_witin, _) = ShiftLogicalInstruction::<GoldilocksExt2, SllOp>::assign_instances(
&config,
cb.cs.num_witin as usize,
vec![StepRecord::new_r_instruction(
3,
MOCK_PC_SLL,
MOCK_PROGRAM[18],
32,
33,
Change::new(0, 32 << 3),
0,
)],
)
.unwrap();

MockProver::assert_satisfied(
&cb,
&raw_witin
.de_interleaving()
.into_mles()
.into_iter()
.map(|v| v.into())
.collect_vec(),
None,
);
}

#[test]
fn test_opcode_srl_1() {
let mut cs = ConstraintSystem::<GoldilocksExt2>::new(|| "riscv");
let mut cb = CircuitBuilder::new(&mut cs);
let config = cb
.namespace(
|| "srl",
|cb| {
let config =
ShiftLogicalInstruction::<GoldilocksExt2, SrlOp>::construct_circuit(cb);
Ok(config)
},
)
.unwrap()
.unwrap();

let (raw_witin, _) = ShiftLogicalInstruction::<GoldilocksExt2, SrlOp>::assign_instances(
&config,
cb.cs.num_witin as usize,
vec![StepRecord::new_r_instruction(
3,
MOCK_PC_SRL,
MOCK_PROGRAM[19],
33,
3,
Change::new(0, 33 >> 3),
0,
)],
)
.unwrap();

MockProver::assert_satisfied(
&cb,
&raw_witin
.de_interleaving()
.into_mles()
.into_iter()
.map(|v| v.into())
.collect_vec(),
None,
);
}

#[test]
fn test_opcode_srl_2_overflow() {
let mut cs = ConstraintSystem::<GoldilocksExt2>::new(|| "riscv");
let mut cb = CircuitBuilder::new(&mut cs);
let config = cb
.namespace(
|| "srl",
|cb| {
let config =
ShiftLogicalInstruction::<GoldilocksExt2, SrlOp>::construct_circuit(cb);
Ok(config)
},
)
.unwrap()
.unwrap();

let (raw_witin, _) = ShiftLogicalInstruction::<GoldilocksExt2, SrlOp>::assign_instances(
&config,
cb.cs.num_witin as usize,
vec![StepRecord::new_r_instruction(
3,
MOCK_PC_SRL,
MOCK_PROGRAM[19],
32,
33,
Change::new(0, 0),
0,
)],
)
.unwrap();

MockProver::assert_satisfied(
&cb,
&raw_witin
.de_interleaving()
.into_mles()
.into_iter()
.map(|v| v.into())
.collect_vec(),
None,
);
}
}
Loading

0 comments on commit 5dd54c2

Please sign in to comment.