2022-06-27 07:18:21 -07:00
|
|
|
use plonky2::field::extension::Extendable;
|
2022-06-27 15:07:52 -07:00
|
|
|
use plonky2::field::packed::PackedField;
|
2022-06-27 12:24:09 -07:00
|
|
|
use plonky2::field::types::{Field, PrimeField64};
|
2022-02-10 12:05:04 -08:00
|
|
|
use plonky2::hash::hash_types::RichField;
|
|
|
|
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
|
|
|
|
use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
|
|
|
|
use starky::vars::StarkEvaluationTargets;
|
|
|
|
|
use starky::vars::StarkEvaluationVars;
|
|
|
|
|
|
2022-05-17 11:15:53 +02:00
|
|
|
use crate::alu::addition::{eval_addition, eval_addition_circuit, generate_addition};
|
|
|
|
|
use crate::alu::bitops::{eval_bitop, eval_bitop_circuit, generate_bitop};
|
2022-05-17 11:04:35 +02:00
|
|
|
use crate::alu::division::{eval_division, eval_division_circuit, generate_division};
|
|
|
|
|
use crate::alu::mul_add::{eval_mul_add, eval_mul_add_circuit, generate_mul_add};
|
2022-05-04 15:54:47 +10:00
|
|
|
use crate::alu::rotate_shift::{
|
2022-05-17 11:15:53 +02:00
|
|
|
eval_rotate_left, eval_rotate_left_circuit, eval_rotate_right, eval_rotate_right_circuit,
|
|
|
|
|
eval_shift_left, eval_shift_left_circuit, eval_shift_right, eval_shift_right_circuit,
|
|
|
|
|
generate_rotate_shift,
|
2022-05-04 15:54:47 +10:00
|
|
|
};
|
2022-05-17 11:04:35 +02:00
|
|
|
use crate::alu::subtraction::{eval_subtraction, eval_subtraction_circuit, generate_subtraction};
|
2022-02-10 12:05:04 -08:00
|
|
|
use crate::public_input_layout::NUM_PUBLIC_INPUTS;
|
2022-02-19 18:32:11 -07:00
|
|
|
use crate::registers::alu::*;
|
2022-02-10 12:05:04 -08:00
|
|
|
use crate::registers::NUM_COLUMNS;
|
|
|
|
|
|
|
|
|
|
mod addition;
|
2022-04-08 14:54:33 +10:00
|
|
|
mod bitops;
|
Implement a mul-add circuit in the ALU (#495)
* Implement a mul-add circuit in the ALU
The inputs are assumed to be `u32`s, while the output is encoded as four `u16 limbs`. Each output limb is range-checked.
So, our basic mul-add constraint looks like
out_0 + 2^16 out_1 + 2^32 out_2 + 2^48 out_3 = in_1 * in_2 + in_3
The right hand side will never overflow, since `u32::MAX * u32::MAX + u32::MAX < |F|`. However, the left hand side could overflow, even though we know each limb is less than `2^16`.
For example, an operation like `0 * 0 + 0` could have two possible outputs, 0 and `|F|`, both of which would satisfy the constraint above. To prevent these non-canonical outputs, we need a comparison to enforce that `out < |F|`.
Thankfully, `F::MAX` has all zeros in its low 32 bits, so `x <= F::MAX` is equivalent to `x_lo == 0 || x_hi != u32::MAX`. `x_hi != u32::MAX` can be checked by showing that `u32::MAX - x_hi` has an inverse. If `x_hi != u32::MAX`, the prover provides this (purported) inverse in an advice column.
See @bobbinth's [post](https://hackmd.io/NC-yRmmtRQSvToTHb96e8Q#Checking-element-validity) for details. That post calls the purported inverse column `m`; I named it `canonical_inv` in this code.
* fix
* PR feedback
* naming
2022-02-21 00:39:04 -08:00
|
|
|
mod canonical;
|
2022-02-10 12:05:04 -08:00
|
|
|
mod division;
|
Implement a mul-add circuit in the ALU (#495)
* Implement a mul-add circuit in the ALU
The inputs are assumed to be `u32`s, while the output is encoded as four `u16 limbs`. Each output limb is range-checked.
So, our basic mul-add constraint looks like
out_0 + 2^16 out_1 + 2^32 out_2 + 2^48 out_3 = in_1 * in_2 + in_3
The right hand side will never overflow, since `u32::MAX * u32::MAX + u32::MAX < |F|`. However, the left hand side could overflow, even though we know each limb is less than `2^16`.
For example, an operation like `0 * 0 + 0` could have two possible outputs, 0 and `|F|`, both of which would satisfy the constraint above. To prevent these non-canonical outputs, we need a comparison to enforce that `out < |F|`.
Thankfully, `F::MAX` has all zeros in its low 32 bits, so `x <= F::MAX` is equivalent to `x_lo == 0 || x_hi != u32::MAX`. `x_hi != u32::MAX` can be checked by showing that `u32::MAX - x_hi` has an inverse. If `x_hi != u32::MAX`, the prover provides this (purported) inverse in an advice column.
See @bobbinth's [post](https://hackmd.io/NC-yRmmtRQSvToTHb96e8Q#Checking-element-validity) for details. That post calls the purported inverse column `m`; I named it `canonical_inv` in this code.
* fix
* PR feedback
* naming
2022-02-21 00:39:04 -08:00
|
|
|
mod mul_add;
|
2022-05-04 15:54:47 +10:00
|
|
|
mod rotate_shift;
|
2022-02-10 12:05:04 -08:00
|
|
|
mod subtraction;
|
|
|
|
|
|
2022-02-19 18:32:11 -07:00
|
|
|
pub(crate) fn generate_alu<F: PrimeField64>(values: &mut [F; NUM_COLUMNS]) {
|
2022-02-10 12:05:04 -08:00
|
|
|
if values[IS_ADD].is_one() {
|
|
|
|
|
generate_addition(values);
|
|
|
|
|
} else if values[IS_SUB].is_one() {
|
|
|
|
|
generate_subtraction(values);
|
2022-03-02 22:49:57 -08:00
|
|
|
} else if values[IS_MUL_ADD].is_one() {
|
Implement a mul-add circuit in the ALU (#495)
* Implement a mul-add circuit in the ALU
The inputs are assumed to be `u32`s, while the output is encoded as four `u16 limbs`. Each output limb is range-checked.
So, our basic mul-add constraint looks like
out_0 + 2^16 out_1 + 2^32 out_2 + 2^48 out_3 = in_1 * in_2 + in_3
The right hand side will never overflow, since `u32::MAX * u32::MAX + u32::MAX < |F|`. However, the left hand side could overflow, even though we know each limb is less than `2^16`.
For example, an operation like `0 * 0 + 0` could have two possible outputs, 0 and `|F|`, both of which would satisfy the constraint above. To prevent these non-canonical outputs, we need a comparison to enforce that `out < |F|`.
Thankfully, `F::MAX` has all zeros in its low 32 bits, so `x <= F::MAX` is equivalent to `x_lo == 0 || x_hi != u32::MAX`. `x_hi != u32::MAX` can be checked by showing that `u32::MAX - x_hi` has an inverse. If `x_hi != u32::MAX`, the prover provides this (purported) inverse in an advice column.
See @bobbinth's [post](https://hackmd.io/NC-yRmmtRQSvToTHb96e8Q#Checking-element-validity) for details. That post calls the purported inverse column `m`; I named it `canonical_inv` in this code.
* fix
* PR feedback
* naming
2022-02-21 00:39:04 -08:00
|
|
|
generate_mul_add(values);
|
2022-02-10 12:05:04 -08:00
|
|
|
} else if values[IS_DIV].is_one() {
|
|
|
|
|
generate_division(values);
|
2022-04-08 14:54:33 +10:00
|
|
|
} else if values[IS_AND].is_one() {
|
|
|
|
|
generate_bitop(values, IS_AND);
|
|
|
|
|
} else if values[IS_IOR].is_one() {
|
|
|
|
|
generate_bitop(values, IS_IOR);
|
|
|
|
|
} else if values[IS_XOR].is_one() {
|
|
|
|
|
generate_bitop(values, IS_XOR);
|
|
|
|
|
} else if values[IS_ANDNOT].is_one() {
|
|
|
|
|
generate_bitop(values, IS_ANDNOT);
|
2022-05-04 15:54:47 +10:00
|
|
|
} else if values[IS_ROTATE_LEFT].is_one() {
|
|
|
|
|
generate_rotate_shift(values, IS_ROTATE_LEFT);
|
|
|
|
|
} else if values[IS_ROTATE_RIGHT].is_one() {
|
|
|
|
|
generate_rotate_shift(values, IS_ROTATE_RIGHT);
|
|
|
|
|
} else if values[IS_SHIFT_LEFT].is_one() {
|
|
|
|
|
generate_rotate_shift(values, IS_SHIFT_LEFT);
|
|
|
|
|
} else if values[IS_SHIFT_RIGHT].is_one() {
|
|
|
|
|
generate_rotate_shift(values, IS_SHIFT_RIGHT);
|
|
|
|
|
} else {
|
|
|
|
|
//todo!("the requested operation has not yet been implemented");
|
2022-02-10 12:05:04 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-19 18:32:11 -07:00
|
|
|
pub(crate) fn eval_alu<F: Field, P: PackedField<Scalar = F>>(
|
2022-02-10 12:05:04 -08:00
|
|
|
vars: StarkEvaluationVars<F, P, NUM_COLUMNS, NUM_PUBLIC_INPUTS>,
|
|
|
|
|
yield_constr: &mut ConstraintConsumer<P>,
|
|
|
|
|
) {
|
|
|
|
|
let local_values = &vars.local_values;
|
|
|
|
|
|
|
|
|
|
// Check that the operation flag values are binary.
|
2022-04-08 14:54:33 +10:00
|
|
|
for col in ALL_OPERATIONS {
|
2022-02-10 12:05:04 -08:00
|
|
|
let val = local_values[col];
|
2022-02-20 17:48:31 -07:00
|
|
|
yield_constr.constraint(val * val - val);
|
2022-02-10 12:05:04 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
eval_addition(local_values, yield_constr);
|
|
|
|
|
eval_subtraction(local_values, yield_constr);
|
Implement a mul-add circuit in the ALU (#495)
* Implement a mul-add circuit in the ALU
The inputs are assumed to be `u32`s, while the output is encoded as four `u16 limbs`. Each output limb is range-checked.
So, our basic mul-add constraint looks like
out_0 + 2^16 out_1 + 2^32 out_2 + 2^48 out_3 = in_1 * in_2 + in_3
The right hand side will never overflow, since `u32::MAX * u32::MAX + u32::MAX < |F|`. However, the left hand side could overflow, even though we know each limb is less than `2^16`.
For example, an operation like `0 * 0 + 0` could have two possible outputs, 0 and `|F|`, both of which would satisfy the constraint above. To prevent these non-canonical outputs, we need a comparison to enforce that `out < |F|`.
Thankfully, `F::MAX` has all zeros in its low 32 bits, so `x <= F::MAX` is equivalent to `x_lo == 0 || x_hi != u32::MAX`. `x_hi != u32::MAX` can be checked by showing that `u32::MAX - x_hi` has an inverse. If `x_hi != u32::MAX`, the prover provides this (purported) inverse in an advice column.
See @bobbinth's [post](https://hackmd.io/NC-yRmmtRQSvToTHb96e8Q#Checking-element-validity) for details. That post calls the purported inverse column `m`; I named it `canonical_inv` in this code.
* fix
* PR feedback
* naming
2022-02-21 00:39:04 -08:00
|
|
|
eval_mul_add(local_values, yield_constr);
|
2022-02-10 12:05:04 -08:00
|
|
|
eval_division(local_values, yield_constr);
|
2022-04-08 14:54:33 +10:00
|
|
|
eval_bitop(local_values, yield_constr);
|
2022-05-04 15:54:47 +10:00
|
|
|
eval_rotate_left(local_values, yield_constr);
|
|
|
|
|
eval_rotate_right(local_values, yield_constr);
|
|
|
|
|
eval_shift_left(local_values, yield_constr);
|
|
|
|
|
eval_shift_right(local_values, yield_constr);
|
2022-02-10 12:05:04 -08:00
|
|
|
}
|
|
|
|
|
|
2022-05-17 11:04:35 +02:00
|
|
|
pub(crate) fn eval_alu_circuit<F: RichField + Extendable<D>, const D: usize>(
|
2022-02-10 12:05:04 -08:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
vars: StarkEvaluationTargets<D, NUM_COLUMNS, NUM_PUBLIC_INPUTS>,
|
|
|
|
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
|
|
|
|
) {
|
|
|
|
|
let local_values = &vars.local_values;
|
|
|
|
|
|
|
|
|
|
// Check that the operation flag values are binary.
|
2022-04-08 14:54:33 +10:00
|
|
|
for col in ALL_OPERATIONS {
|
2022-02-10 12:05:04 -08:00
|
|
|
let val = local_values[col];
|
2022-02-11 10:25:51 +01:00
|
|
|
let constraint = builder.mul_sub_extension(val, val, val);
|
2022-02-20 17:48:31 -07:00
|
|
|
yield_constr.constraint(builder, constraint);
|
2022-02-10 12:05:04 -08:00
|
|
|
}
|
|
|
|
|
|
2022-05-17 11:15:53 +02:00
|
|
|
eval_addition_circuit(builder, local_values, yield_constr);
|
2022-05-17 11:04:35 +02:00
|
|
|
eval_subtraction_circuit(builder, local_values, yield_constr);
|
|
|
|
|
eval_mul_add_circuit(builder, local_values, yield_constr);
|
|
|
|
|
eval_division_circuit(builder, local_values, yield_constr);
|
2022-05-17 11:15:53 +02:00
|
|
|
eval_bitop_circuit(builder, local_values, yield_constr);
|
|
|
|
|
eval_rotate_left_circuit(builder, local_values, yield_constr);
|
|
|
|
|
eval_rotate_right_circuit(builder, local_values, yield_constr);
|
|
|
|
|
eval_shift_left_circuit(builder, local_values, yield_constr);
|
|
|
|
|
eval_shift_right_circuit(builder, local_values, yield_constr);
|
2022-02-10 12:05:04 -08:00
|
|
|
}
|