Skip to content

Commit

Permalink
Merge branch 'main' into error_vs_panic
Browse files Browse the repository at this point in the history
  • Loading branch information
Nashtare committed Sep 18, 2023
2 parents c4be838 + 15064b3 commit f3ea95c
Show file tree
Hide file tree
Showing 39 changed files with 896 additions and 502 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/continuous-integration-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,5 +124,5 @@ jobs:
command: clippy
args: --all-features --all-targets -- -D warnings -A incomplete-features
env:
CARGO_INCREMENTAL: 1

# Seems necessary until https://github.com/rust-lang/rust/pull/115819 is merged.
CARGO_INCREMENTAL: 0
53 changes: 34 additions & 19 deletions evm/src/arithmetic/arithmetic_stark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,17 @@ use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars};
/// This is done by taking pairs of columns (x, y) of the arithmetic
/// table and combining them as x + y*2^16 to ensure they equal the
/// corresponding 32-bit number in the CPU table.
fn cpu_arith_data_link<F: Field>(ops: &[usize], regs: &[Range<usize>]) -> Vec<Column<F>> {
fn cpu_arith_data_link<F: Field>(
combined_ops: &[(usize, u8)],
regs: &[Range<usize>],
) -> Vec<Column<F>> {
let limb_base = F::from_canonical_u64(1 << columns::LIMB_BITS);

let mut res = Column::singles(ops).collect_vec();
let mut res = vec![Column::linear_combination(
combined_ops
.iter()
.map(|&(col, code)| (col, F::from_canonical_u8(code))),
)];

// The inner for loop below assumes N_LIMBS is even.
const_assert!(columns::N_LIMBS % 2 == 0);
Expand All @@ -49,21 +56,27 @@ fn cpu_arith_data_link<F: Field>(ops: &[usize], regs: &[Range<usize>]) -> Vec<Co
}

pub fn ctl_arithmetic_rows<F: Field>() -> TableWithColumns<F> {
const ARITH_OPS: [usize; 14] = [
columns::IS_ADD,
columns::IS_SUB,
columns::IS_MUL,
columns::IS_LT,
columns::IS_GT,
columns::IS_ADDFP254,
columns::IS_MULFP254,
columns::IS_SUBFP254,
columns::IS_ADDMOD,
columns::IS_MULMOD,
columns::IS_SUBMOD,
columns::IS_DIV,
columns::IS_MOD,
columns::IS_BYTE,
// We scale each filter flag with the associated opcode value.
// If an arithmetic operation is happening on the CPU side,
// the CTL will enforce that the reconstructed opcode value
// from the opcode bits matches.
const COMBINED_OPS: [(usize, u8); 16] = [
(columns::IS_ADD, 0x01),
(columns::IS_MUL, 0x02),
(columns::IS_SUB, 0x03),
(columns::IS_DIV, 0x04),
(columns::IS_MOD, 0x06),
(columns::IS_ADDMOD, 0x08),
(columns::IS_MULMOD, 0x09),
(columns::IS_ADDFP254, 0x0c),
(columns::IS_MULFP254, 0x0d),
(columns::IS_SUBFP254, 0x0e),
(columns::IS_SUBMOD, 0x0f),
(columns::IS_LT, 0x10),
(columns::IS_GT, 0x11),
(columns::IS_BYTE, 0x1a),
(columns::IS_SHL, 0x1b),
(columns::IS_SHR, 0x1c),
];

const REGISTER_MAP: [Range<usize>; 4] = [
Expand All @@ -73,15 +86,17 @@ pub fn ctl_arithmetic_rows<F: Field>() -> TableWithColumns<F> {
columns::OUTPUT_REGISTER,
];

let filter_column = Some(Column::sum(COMBINED_OPS.iter().map(|(c, _v)| *c)));

// Create the Arithmetic Table whose columns are those of the
// operations listed in `ops` whose inputs and outputs are given
// by `regs`, where each element of `regs` is a range of columns
// corresponding to a 256-bit input or output register (also `ops`
// is used as the operation filter).
TableWithColumns::new(
Table::Arithmetic,
cpu_arith_data_link(&ARITH_OPS, &REGISTER_MAP),
Some(Column::sum(ARITH_OPS)),
cpu_arith_data_link(&COMBINED_OPS, &REGISTER_MAP),
filter_column,
)
}

Expand Down
4 changes: 3 additions & 1 deletion evm/src/arithmetic/columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ pub(crate) const IS_SUBMOD: usize = IS_SUBFP254 + 1;
pub(crate) const IS_LT: usize = IS_SUBMOD + 1;
pub(crate) const IS_GT: usize = IS_LT + 1;
pub(crate) const IS_BYTE: usize = IS_GT + 1;
pub(crate) const IS_SHL: usize = IS_BYTE + 1;
pub(crate) const IS_SHR: usize = IS_SHL + 1;

pub(crate) const START_SHARED_COLS: usize = IS_BYTE + 1;
pub(crate) const START_SHARED_COLS: usize = IS_SHR + 1;

/// Within the Arithmetic Unit, there are shared columns which can be
/// used by any arithmetic circuit, depending on which one is active
Expand Down
15 changes: 12 additions & 3 deletions evm/src/arithmetic/divmod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub(crate) fn generate<F: PrimeField64>(
}

match filter {
IS_DIV => {
IS_DIV | IS_SHR => {
debug_assert!(
lv[OUTPUT_REGISTER]
.iter()
Expand Down Expand Up @@ -104,11 +104,14 @@ pub(crate) fn eval_packed<P: PackedField>(
nv: &[P; NUM_ARITH_COLUMNS],
yield_constr: &mut ConstraintConsumer<P>,
) {
// Constrain IS_SHR independently, so that it doesn't impact the
// constraints when combining the flag with IS_DIV.
yield_constr.constraint_last_row(lv[IS_SHR]);
eval_packed_divmod_helper(
lv,
nv,
yield_constr,
lv[IS_DIV],
lv[IS_DIV] + lv[IS_SHR],
OUTPUT_REGISTER,
AUX_INPUT_REGISTER_0,
);
Expand Down Expand Up @@ -161,12 +164,14 @@ pub(crate) fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
nv: &[ExtensionTarget<D>; NUM_ARITH_COLUMNS],
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
) {
yield_constr.constraint_last_row(builder, lv[IS_SHR]);
let div_shr_flag = builder.add_extension(lv[IS_DIV], lv[IS_SHR]);
eval_ext_circuit_divmod_helper(
builder,
lv,
nv,
yield_constr,
lv[IS_DIV],
div_shr_flag,
OUTPUT_REGISTER,
AUX_INPUT_REGISTER_0,
);
Expand Down Expand Up @@ -209,6 +214,8 @@ mod tests {
for op in MODULAR_OPS {
lv[op] = F::ZERO;
}
// Deactivate the SHR flag so that a DIV operation is not triggered.
lv[IS_SHR] = F::ZERO;

let mut constraint_consumer = ConstraintConsumer::new(
vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)],
Expand Down Expand Up @@ -240,6 +247,7 @@ mod tests {
for op in MODULAR_OPS {
lv[op] = F::ZERO;
}
lv[IS_SHR] = F::ZERO;
lv[op_filter] = F::ONE;

let input0 = U256::from(rng.gen::<[u8; 32]>());
Expand Down Expand Up @@ -300,6 +308,7 @@ mod tests {
for op in MODULAR_OPS {
lv[op] = F::ZERO;
}
lv[IS_SHR] = F::ZERO;
lv[op_filter] = F::ONE;

let input0 = U256::from(rng.gen::<[u8; 32]>());
Expand Down
32 changes: 28 additions & 4 deletions evm/src/arithmetic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,17 @@ pub(crate) enum BinaryOperator {
MulFp254,
SubFp254,
Byte,
Shl, // simulated with MUL
Shr, // simulated with DIV
}

impl BinaryOperator {
pub(crate) fn result(&self, input0: U256, input1: U256) -> U256 {
match self {
BinaryOperator::Add => input0.overflowing_add(input1).0,
BinaryOperator::Mul => input0.overflowing_mul(input1).0,
BinaryOperator::Mul | BinaryOperator::Shl => input0.overflowing_mul(input1).0,
BinaryOperator::Sub => input0.overflowing_sub(input1).0,
BinaryOperator::Div => {
BinaryOperator::Div | BinaryOperator::Shr => {
if input1.is_zero() {
U256::zero()
} else {
Expand Down Expand Up @@ -77,6 +79,8 @@ impl BinaryOperator {
BinaryOperator::MulFp254 => columns::IS_MULFP254,
BinaryOperator::SubFp254 => columns::IS_SUBFP254,
BinaryOperator::Byte => columns::IS_BYTE,
BinaryOperator::Shl => columns::IS_SHL,
BinaryOperator::Shr => columns::IS_SHR,
}
}
}
Expand Down Expand Up @@ -107,6 +111,7 @@ impl TernaryOperator {
}
}

/// An enum representing arithmetic operations that can be either binary or ternary.
#[derive(Debug)]
pub(crate) enum Operation {
BinaryOperation {
Expand All @@ -125,6 +130,21 @@ pub(crate) enum Operation {
}

impl Operation {
/// Create a binary operator with given inputs.
///
/// NB: This works as you would expect, EXCEPT for SHL and SHR,
/// whose inputs need a small amount of preprocessing. Specifically,
/// to create `SHL(shift, value)`, call (note the reversal of
/// argument order):
///
/// `Operation::binary(BinaryOperator::Shl, value, 1 << shift)`
///
/// Similarly, to create `SHR(shift, value)`, call
///
/// `Operation::binary(BinaryOperator::Shr, value, 1 << shift)`
///
/// See witness/operation.rs::append_shift() for an example (indeed
/// the only call site for such inputs).
pub(crate) fn binary(operator: BinaryOperator, input0: U256, input1: U256) -> Self {
let result = operator.result(input0, input1);
Self::BinaryOperation {
Expand Down Expand Up @@ -164,6 +184,10 @@ impl Operation {
/// use vectors because that's what utils::transpose (who consumes
/// the result of this function as part of the range check code)
/// expects.
///
/// The `is_simulated` bool indicates whether we use a native arithmetic
/// operation or simulate one with another. This is used to distinguish
/// SHL and SHR operations that are simulated through MUL and DIV respectively.
fn to_rows<F: PrimeField64>(&self) -> (Vec<F>, Option<Vec<F>>) {
match *self {
Operation::BinaryOperation {
Expand Down Expand Up @@ -214,11 +238,11 @@ fn binary_op_to_rows<F: PrimeField64>(
addcy::generate(&mut row, op.row_filter(), input0, input1);
(row, None)
}
BinaryOperator::Mul => {
BinaryOperator::Mul | BinaryOperator::Shl => {
mul::generate(&mut row, input0, input1);
(row, None)
}
BinaryOperator::Div | BinaryOperator::Mod => {
BinaryOperator::Div | BinaryOperator::Mod | BinaryOperator::Shr => {
let mut nv = vec![F::ZERO; columns::NUM_ARITH_COLUMNS];
divmod::generate(&mut row, &mut nv, op.row_filter(), input0, input1, result);
(row, Some(nv))
Expand Down
6 changes: 4 additions & 2 deletions evm/src/arithmetic/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ pub fn eval_packed_generic<P: PackedField>(
) {
let base = P::Scalar::from_canonical_u64(1 << LIMB_BITS);

let is_mul = lv[IS_MUL];
let is_mul = lv[IS_MUL] + lv[IS_SHL];
let input0_limbs = read_value::<N_LIMBS, _>(lv, INPUT_REGISTER_0);
let input1_limbs = read_value::<N_LIMBS, _>(lv, INPUT_REGISTER_1);
let output_limbs = read_value::<N_LIMBS, _>(lv, OUTPUT_REGISTER);
Expand Down Expand Up @@ -173,7 +173,7 @@ pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
lv: &[ExtensionTarget<D>; NUM_ARITH_COLUMNS],
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
) {
let is_mul = lv[IS_MUL];
let is_mul = builder.add_extension(lv[IS_MUL], lv[IS_SHL]);
let input0_limbs = read_value::<N_LIMBS, _>(lv, INPUT_REGISTER_0);
let input1_limbs = read_value::<N_LIMBS, _>(lv, INPUT_REGISTER_1);
let output_limbs = read_value::<N_LIMBS, _>(lv, OUTPUT_REGISTER);
Expand Down Expand Up @@ -229,6 +229,8 @@ mod tests {
// if `IS_MUL == 0`, then the constraints should be met even
// if all values are garbage.
lv[IS_MUL] = F::ZERO;
// Deactivate the SHL flag so that a MUL operation is not triggered.
lv[IS_SHL] = F::ZERO;

let mut constraint_consumer = ConstraintConsumer::new(
vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)],
Expand Down
12 changes: 6 additions & 6 deletions evm/src/cpu/bootstrap_kernel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub(crate) fn generate_bootstrap_kernel<F: Field>(state: &mut GenerationState<F>
for chunk in &KERNEL.code.iter().enumerate().chunks(NUM_GP_CHANNELS) {
let mut cpu_row = CpuColumnsView::default();
cpu_row.clock = F::from_canonical_usize(state.traces.clock());
cpu_row.is_bootstrap_kernel = F::ONE;

// Write this chunk to memory, while simultaneously packing its bytes into a u32 word.
for (channel, (addr, &byte)) in chunk.enumerate() {
Expand All @@ -39,6 +40,7 @@ pub(crate) fn generate_bootstrap_kernel<F: Field>(state: &mut GenerationState<F>

let mut final_cpu_row = CpuColumnsView::default();
final_cpu_row.clock = F::from_canonical_usize(state.traces.clock());
final_cpu_row.is_bootstrap_kernel = F::ONE;
final_cpu_row.is_keccak_sponge = F::ONE;
// The Keccak sponge CTL uses memory value columns for its inputs and outputs.
final_cpu_row.mem_channels[0].value[0] = F::ZERO; // context
Expand All @@ -64,8 +66,8 @@ pub(crate) fn eval_bootstrap_kernel<F: Field, P: PackedField<Scalar = F>>(
let next_values: &CpuColumnsView<_> = vars.next_values.borrow();

// IS_BOOTSTRAP_KERNEL must have an init value of 1, a final value of 0, and a delta in {0, -1}.
let local_is_bootstrap = P::ONES - local_values.op.into_iter().sum::<P>();
let next_is_bootstrap = P::ONES - next_values.op.into_iter().sum::<P>();
let local_is_bootstrap = local_values.is_bootstrap_kernel;
let next_is_bootstrap = next_values.is_bootstrap_kernel;
yield_constr.constraint_first_row(local_is_bootstrap - P::ONES);
yield_constr.constraint_last_row(local_is_bootstrap);
let delta_is_bootstrap = next_is_bootstrap - local_is_bootstrap;
Expand Down Expand Up @@ -111,10 +113,8 @@ pub(crate) fn eval_bootstrap_kernel_circuit<F: RichField + Extendable<D>, const
let one = builder.one_extension();

// IS_BOOTSTRAP_KERNEL must have an init value of 1, a final value of 0, and a delta in {0, -1}.
let local_is_bootstrap = builder.add_many_extension(local_values.op.iter());
let local_is_bootstrap = builder.sub_extension(one, local_is_bootstrap);
let next_is_bootstrap = builder.add_many_extension(next_values.op.iter());
let next_is_bootstrap = builder.sub_extension(one, next_is_bootstrap);
let local_is_bootstrap = local_values.is_bootstrap_kernel;
let next_is_bootstrap = next_values.is_bootstrap_kernel;
let constraint = builder.sub_extension(local_is_bootstrap, one);
yield_constr.constraint_first_row(builder, constraint);
yield_constr.constraint_last_row(builder, local_is_bootstrap);
Expand Down
3 changes: 3 additions & 0 deletions evm/src/cpu/columns/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ pub struct MemoryChannelView<T: Copy> {
#[repr(C)]
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub struct CpuColumnsView<T: Copy> {
/// Filter. 1 if the row is part of bootstrapping the kernel code, 0 otherwise.
pub is_bootstrap_kernel: T,

/// If CPU cycle: Current context.
// TODO: this is currently unconstrained
pub context: T,
Expand Down
34 changes: 8 additions & 26 deletions evm/src/cpu/columns/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,17 @@ use crate::util::{indices_arr, transmute_no_compile_time_size_checks};
#[repr(C)]
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub struct OpsColumnsView<T: Copy> {
// TODO: combine ADD, MUL, SUB, DIV, MOD, ADDFP254, MULFP254, SUBFP254, LT, and GT into one flag
pub add: T,
pub mul: T,
pub sub: T,
pub div: T,
pub mod_: T,
// TODO: combine ADDMOD, MULMOD and SUBMOD into one flag
pub addmod: T,
pub mulmod: T,
pub addfp254: T,
pub mulfp254: T,
pub subfp254: T,
pub submod: T,
pub lt: T,
pub gt: T,
pub eq_iszero: T, // Combines EQ and ISZERO flags.
pub logic_op: T, // Combines AND, OR and XOR flags.
pub binary_op: T, // Combines ADD, MUL, SUB, DIV, MOD, LT, GT and BYTE flags.
pub ternary_op: T, // Combines ADDMOD, MULMOD and SUBMOD flags.
pub fp254_op: T, // Combines ADD_FP254, MUL_FP254 and SUB_FP254 flags.
pub eq_iszero: T, // Combines EQ and ISZERO flags.
pub logic_op: T, // Combines AND, OR and XOR flags.
pub not: T,
pub byte: T,
// TODO: combine SHL and SHR into one flag
pub shl: T,
pub shr: T,
pub shift: T, // Combines SHL and SHR flags.
pub keccak_general: T,
pub prover_input: T,
pub pop: T,
// TODO: combine JUMP and JUMPI into one flag
pub jumps: T, // Note: This column must be 0 when is_cpu_cycle = 0.
pub jumps: T, // Combines JUMP and JUMPI flags.
pub pc: T,
pub jumpdest: T,
pub push0: T,
Expand All @@ -44,9 +28,7 @@ pub struct OpsColumnsView<T: Copy> {
pub mstore_32bytes: T,
pub mload_32bytes: T,
pub exit_kernel: T,
// TODO: combine MLOAD_GENERAL and MSTORE_GENERAL into one flag
pub mload_general: T,
pub mstore_general: T,
pub m_op_general: T,

pub syscall: T,
pub exception: T,
Expand Down
Loading

0 comments on commit f3ea95c

Please sign in to comment.