-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
392 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
); | ||
} | ||
} |
Oops, something went wrong.