2022-08-16 09:46:10 -07:00
|
|
|
use plonky2::field::extension::Extendable;
|
|
|
|
|
use plonky2::field::packed::PackedField;
|
|
|
|
|
use plonky2::field::types::Field;
|
|
|
|
|
use plonky2::hash::hash_types::RichField;
|
|
|
|
|
use plonky2::iop::ext_target::ExtensionTarget;
|
|
|
|
|
|
|
|
|
|
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
|
|
|
|
use crate::cpu::columns::CpuColumnsView;
|
2022-12-11 11:08:33 -08:00
|
|
|
use crate::cpu::membus::NUM_GP_CHANNELS;
|
|
|
|
|
use crate::memory::segments::Segment;
|
2022-08-16 09:46:10 -07:00
|
|
|
|
|
|
|
|
pub fn eval_packed_exit_kernel<P: PackedField>(
|
|
|
|
|
lv: &CpuColumnsView<P>,
|
|
|
|
|
nv: &CpuColumnsView<P>,
|
|
|
|
|
yield_constr: &mut ConstraintConsumer<P>,
|
|
|
|
|
) {
|
2022-08-26 22:05:16 -07:00
|
|
|
let input = lv.mem_channels[0].value;
|
2023-02-14 22:30:19 -08:00
|
|
|
let filter = lv.is_cpu_cycle * lv.op.exit_kernel;
|
|
|
|
|
|
|
|
|
|
// If we are executing `EXIT_KERNEL` then we simply restore the program counter, kernel mode
|
|
|
|
|
// flag, and gas counter. The middle 4 (32-bit) limbs are ignored (this is not part of the spec,
|
|
|
|
|
// but we trust the kernel to set them to zero).
|
|
|
|
|
yield_constr.constraint_transition(filter * (input[0] - nv.program_counter));
|
|
|
|
|
yield_constr.constraint_transition(filter * (input[1] - nv.is_kernel_mode));
|
|
|
|
|
yield_constr.constraint_transition(filter * (input[6] - nv.gas));
|
|
|
|
|
// High limb of gas must be 0 for convenient detection of overflow.
|
|
|
|
|
yield_constr.constraint(filter * input[7]);
|
2022-08-16 09:46:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn eval_ext_circuit_exit_kernel<F: RichField + Extendable<D>, const D: usize>(
|
|
|
|
|
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
|
|
|
|
|
lv: &CpuColumnsView<ExtensionTarget<D>>,
|
|
|
|
|
nv: &CpuColumnsView<ExtensionTarget<D>>,
|
|
|
|
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
|
|
|
|
) {
|
2022-08-26 22:05:16 -07:00
|
|
|
let input = lv.mem_channels[0].value;
|
2022-09-28 15:18:56 -07:00
|
|
|
let filter = builder.mul_extension(lv.is_cpu_cycle, lv.op.exit_kernel);
|
2022-08-16 09:46:10 -07:00
|
|
|
|
|
|
|
|
// If we are executing `EXIT_KERNEL` then we simply restore the program counter and kernel mode
|
|
|
|
|
// flag. The top 6 (32-bit) limbs are ignored (this is not part of the spec, but we trust the
|
|
|
|
|
// kernel to set them to zero).
|
|
|
|
|
|
2022-08-26 16:39:39 -05:00
|
|
|
let pc_constr = builder.sub_extension(input[0], nv.program_counter);
|
2022-08-16 09:46:10 -07:00
|
|
|
let pc_constr = builder.mul_extension(filter, pc_constr);
|
|
|
|
|
yield_constr.constraint_transition(builder, pc_constr);
|
|
|
|
|
|
2022-08-26 16:39:39 -05:00
|
|
|
let kernel_constr = builder.sub_extension(input[1], nv.is_kernel_mode);
|
2022-08-16 09:46:10 -07:00
|
|
|
let kernel_constr = builder.mul_extension(filter, kernel_constr);
|
|
|
|
|
yield_constr.constraint_transition(builder, kernel_constr);
|
2023-02-14 22:30:19 -08:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let diff = builder.sub_extension(input[6], nv.gas);
|
|
|
|
|
let constr = builder.mul_extension(filter, diff);
|
|
|
|
|
yield_constr.constraint_transition(builder, constr);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
// High limb of gas must be 0 for convenient detection of overflow.
|
|
|
|
|
let constr = builder.mul_extension(filter, input[7]);
|
|
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
2022-08-16 09:46:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn eval_packed_jump_jumpi<P: PackedField>(
|
|
|
|
|
lv: &CpuColumnsView<P>,
|
|
|
|
|
nv: &CpuColumnsView<P>,
|
|
|
|
|
yield_constr: &mut ConstraintConsumer<P>,
|
|
|
|
|
) {
|
|
|
|
|
let jumps_lv = lv.general.jumps();
|
2022-12-11 11:08:33 -08:00
|
|
|
let dst = lv.mem_channels[0].value;
|
|
|
|
|
let cond = lv.mem_channels[1].value;
|
2022-09-28 15:18:56 -07:00
|
|
|
let filter = lv.op.jump + lv.op.jumpi; // `JUMP` or `JUMPI`
|
2022-12-11 11:08:33 -08:00
|
|
|
let jumpdest_flag_channel = lv.mem_channels[NUM_GP_CHANNELS - 1];
|
2022-08-16 09:46:10 -07:00
|
|
|
|
|
|
|
|
// If `JUMP`, re-use the `JUMPI` logic, but setting the second input (the predicate) to be 1.
|
2022-12-11 11:08:33 -08:00
|
|
|
// In other words, we implement `JUMP(dst)` as `JUMPI(dst, cond=1)`.
|
|
|
|
|
yield_constr.constraint(lv.op.jump * (cond[0] - P::ONES));
|
|
|
|
|
for &limb in &cond[1..] {
|
2022-08-16 09:46:10 -07:00
|
|
|
// Set all limbs (other than the least-significant limb) to 0.
|
|
|
|
|
// NB: Technically, they don't have to be 0, as long as the sum
|
2022-12-11 11:08:33 -08:00
|
|
|
// `cond[0] + ... + cond[7]` cannot overflow.
|
2022-09-28 15:18:56 -07:00
|
|
|
yield_constr.constraint(lv.op.jump * limb);
|
2022-08-16 09:46:10 -07:00
|
|
|
}
|
|
|
|
|
|
2022-12-11 11:08:33 -08:00
|
|
|
// Check `should_jump`:
|
|
|
|
|
yield_constr.constraint(filter * jumps_lv.should_jump * (jumps_lv.should_jump - P::ONES));
|
|
|
|
|
let cond_sum: P = cond.into_iter().sum();
|
|
|
|
|
yield_constr.constraint(filter * (jumps_lv.should_jump - P::ONES) * cond_sum);
|
|
|
|
|
yield_constr.constraint(filter * (jumps_lv.cond_sum_pinv * cond_sum - jumps_lv.should_jump));
|
|
|
|
|
|
|
|
|
|
// If we're jumping, then the high 7 limbs of the destination must be 0.
|
|
|
|
|
let dst_hi_sum: P = dst[1..].iter().copied().sum();
|
|
|
|
|
yield_constr.constraint(filter * jumps_lv.should_jump * dst_hi_sum);
|
|
|
|
|
// Check that the destination address holds a `JUMPDEST` instruction. Note that this constraint
|
|
|
|
|
// does not need to be conditioned on `should_jump` because no read takes place if we're not
|
|
|
|
|
// jumping, so we're free to set the channel to 1.
|
|
|
|
|
yield_constr.constraint(filter * (jumpdest_flag_channel.value[0] - P::ONES));
|
|
|
|
|
|
|
|
|
|
// Make sure that the JUMPDEST flag channel is constrained.
|
|
|
|
|
// Only need to read if we're about to jump and we're not in kernel mode.
|
2022-08-16 09:46:10 -07:00
|
|
|
yield_constr.constraint(
|
|
|
|
|
filter
|
2022-12-11 11:08:33 -08:00
|
|
|
* (jumpdest_flag_channel.used - jumps_lv.should_jump * (P::ONES - lv.is_kernel_mode)),
|
2022-08-16 09:46:10 -07:00
|
|
|
);
|
2022-12-11 11:08:33 -08:00
|
|
|
yield_constr.constraint(filter * (jumpdest_flag_channel.is_read - P::ONES));
|
|
|
|
|
yield_constr.constraint(filter * (jumpdest_flag_channel.addr_context - lv.context));
|
2022-08-16 09:46:10 -07:00
|
|
|
yield_constr.constraint(
|
|
|
|
|
filter
|
2022-12-11 11:08:33 -08:00
|
|
|
* (jumpdest_flag_channel.addr_segment
|
|
|
|
|
- P::Scalar::from_canonical_u64(Segment::JumpdestBits as u64)),
|
2022-08-16 09:46:10 -07:00
|
|
|
);
|
2022-12-11 11:08:33 -08:00
|
|
|
yield_constr.constraint(filter * (jumpdest_flag_channel.addr_virtual - dst[0]));
|
2022-08-16 09:46:10 -07:00
|
|
|
|
2022-12-11 11:08:33 -08:00
|
|
|
// Disable unused memory channels
|
|
|
|
|
for &channel in &lv.mem_channels[2..NUM_GP_CHANNELS - 1] {
|
|
|
|
|
yield_constr.constraint(filter * channel.used);
|
|
|
|
|
}
|
|
|
|
|
// Channel 1 is unused by the `JUMP` instruction.
|
|
|
|
|
yield_constr.constraint(lv.op.jump * lv.mem_channels[1].used);
|
2022-08-16 09:46:10 -07:00
|
|
|
|
2022-12-11 11:08:33 -08:00
|
|
|
// Finally, set the next program counter.
|
|
|
|
|
let fallthrough_dst = lv.program_counter + P::ONES;
|
|
|
|
|
let jump_dest = dst[0];
|
2022-08-16 09:46:10 -07:00
|
|
|
yield_constr.constraint_transition(
|
2022-12-11 11:08:33 -08:00
|
|
|
filter * (jumps_lv.should_jump - P::ONES) * (nv.program_counter - fallthrough_dst),
|
2022-08-16 09:46:10 -07:00
|
|
|
);
|
2022-08-26 16:39:39 -05:00
|
|
|
yield_constr
|
2022-12-11 11:08:33 -08:00
|
|
|
.constraint_transition(filter * jumps_lv.should_jump * (nv.program_counter - jump_dest));
|
2022-08-16 09:46:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn eval_ext_circuit_jump_jumpi<F: RichField + Extendable<D>, const D: usize>(
|
|
|
|
|
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
|
|
|
|
|
lv: &CpuColumnsView<ExtensionTarget<D>>,
|
|
|
|
|
nv: &CpuColumnsView<ExtensionTarget<D>>,
|
|
|
|
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
|
|
|
|
) {
|
|
|
|
|
let jumps_lv = lv.general.jumps();
|
2022-12-11 11:08:33 -08:00
|
|
|
let dst = lv.mem_channels[0].value;
|
|
|
|
|
let cond = lv.mem_channels[1].value;
|
2022-09-28 15:18:56 -07:00
|
|
|
let filter = builder.add_extension(lv.op.jump, lv.op.jumpi); // `JUMP` or `JUMPI`
|
2022-12-11 11:08:33 -08:00
|
|
|
let jumpdest_flag_channel = lv.mem_channels[NUM_GP_CHANNELS - 1];
|
2022-08-16 09:46:10 -07:00
|
|
|
|
|
|
|
|
// If `JUMP`, re-use the `JUMPI` logic, but setting the second input (the predicate) to be 1.
|
2022-12-11 11:08:33 -08:00
|
|
|
// In other words, we implement `JUMP(dst)` as `JUMPI(dst, cond=1)`.
|
2022-08-16 09:46:10 -07:00
|
|
|
{
|
2022-12-11 11:08:33 -08:00
|
|
|
let constr = builder.mul_sub_extension(lv.op.jump, cond[0], lv.op.jump);
|
2022-08-16 09:46:10 -07:00
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
2022-12-11 11:08:33 -08:00
|
|
|
for &limb in &cond[1..] {
|
2022-08-16 09:46:10 -07:00
|
|
|
// Set all limbs (other than the least-significant limb) to 0.
|
|
|
|
|
// NB: Technically, they don't have to be 0, as long as the sum
|
2022-12-11 11:08:33 -08:00
|
|
|
// `cond[0] + ... + cond[7]` cannot overflow.
|
2022-09-28 15:18:56 -07:00
|
|
|
let constr = builder.mul_extension(lv.op.jump, limb);
|
2022-08-16 09:46:10 -07:00
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-11 11:08:33 -08:00
|
|
|
// Check `should_jump`:
|
2022-08-16 09:46:10 -07:00
|
|
|
{
|
|
|
|
|
let constr = builder.mul_sub_extension(
|
2022-12-11 11:08:33 -08:00
|
|
|
jumps_lv.should_jump,
|
|
|
|
|
jumps_lv.should_jump,
|
|
|
|
|
jumps_lv.should_jump,
|
2022-08-16 09:46:10 -07:00
|
|
|
);
|
|
|
|
|
let constr = builder.mul_extension(filter, constr);
|
|
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
2022-12-11 11:08:33 -08:00
|
|
|
let cond_sum = builder.add_many_extension(cond);
|
2022-08-16 09:46:10 -07:00
|
|
|
{
|
2022-12-11 11:08:33 -08:00
|
|
|
let constr = builder.mul_sub_extension(cond_sum, jumps_lv.should_jump, cond_sum);
|
2022-08-16 09:46:10 -07:00
|
|
|
let constr = builder.mul_extension(filter, constr);
|
|
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
|
|
|
|
{
|
2022-12-11 11:08:33 -08:00
|
|
|
let constr =
|
|
|
|
|
builder.mul_sub_extension(jumps_lv.cond_sum_pinv, cond_sum, jumps_lv.should_jump);
|
2022-08-16 09:46:10 -07:00
|
|
|
let constr = builder.mul_extension(filter, constr);
|
|
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-11 11:08:33 -08:00
|
|
|
// If we're jumping, then the high 7 limbs of the destination must be 0.
|
|
|
|
|
let dst_hi_sum = builder.add_many_extension(&dst[1..]);
|
2022-08-16 09:46:10 -07:00
|
|
|
{
|
2022-12-11 11:08:33 -08:00
|
|
|
let constr = builder.mul_extension(jumps_lv.should_jump, dst_hi_sum);
|
|
|
|
|
let constr = builder.mul_extension(filter, constr);
|
2022-08-16 09:46:10 -07:00
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
2022-12-11 11:08:33 -08:00
|
|
|
// Check that the destination address holds a `JUMPDEST` instruction. Note that this constraint
|
|
|
|
|
// does not need to be conditioned on `should_jump` because no read takes place if we're not
|
|
|
|
|
// jumping, so we're free to set the channel to 1.
|
2022-08-16 09:46:10 -07:00
|
|
|
{
|
2022-12-11 11:08:33 -08:00
|
|
|
let constr = builder.mul_sub_extension(filter, jumpdest_flag_channel.value[0], filter);
|
2022-08-16 09:46:10 -07:00
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-11 11:08:33 -08:00
|
|
|
// Make sure that the JUMPDEST flag channel is constrained.
|
|
|
|
|
// Only need to read if we're about to jump and we're not in kernel mode.
|
2022-08-16 09:46:10 -07:00
|
|
|
{
|
|
|
|
|
let constr = builder.mul_sub_extension(
|
|
|
|
|
jumps_lv.should_jump,
|
2022-12-11 11:08:33 -08:00
|
|
|
lv.is_kernel_mode,
|
2022-08-16 09:46:10 -07:00
|
|
|
jumps_lv.should_jump,
|
|
|
|
|
);
|
2022-12-11 11:08:33 -08:00
|
|
|
let constr = builder.add_extension(jumpdest_flag_channel.used, constr);
|
2022-08-16 09:46:10 -07:00
|
|
|
let constr = builder.mul_extension(filter, constr);
|
|
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
|
|
|
|
{
|
2022-12-11 11:08:33 -08:00
|
|
|
let constr = builder.mul_sub_extension(filter, jumpdest_flag_channel.is_read, filter);
|
|
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
let constr = builder.sub_extension(jumpdest_flag_channel.addr_context, lv.context);
|
2022-08-16 09:46:10 -07:00
|
|
|
let constr = builder.mul_extension(filter, constr);
|
|
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
let constr = builder.arithmetic_extension(
|
|
|
|
|
F::ONE,
|
2022-12-11 11:08:33 -08:00
|
|
|
-F::from_canonical_u64(Segment::JumpdestBits as u64),
|
|
|
|
|
filter,
|
|
|
|
|
jumpdest_flag_channel.addr_segment,
|
|
|
|
|
filter,
|
2022-08-16 09:46:10 -07:00
|
|
|
);
|
2022-12-11 11:08:33 -08:00
|
|
|
yield_constr.constraint(builder, constr);
|
2022-08-16 09:46:10 -07:00
|
|
|
}
|
|
|
|
|
{
|
2022-12-11 11:08:33 -08:00
|
|
|
let constr = builder.sub_extension(jumpdest_flag_channel.addr_virtual, dst[0]);
|
2022-08-16 09:46:10 -07:00
|
|
|
let constr = builder.mul_extension(filter, constr);
|
2022-12-11 11:08:33 -08:00
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Disable unused memory channels
|
|
|
|
|
for &channel in &lv.mem_channels[2..NUM_GP_CHANNELS - 1] {
|
|
|
|
|
let constr = builder.mul_extension(filter, channel.used);
|
|
|
|
|
yield_constr.constraint(builder, constr);
|
2022-08-16 09:46:10 -07:00
|
|
|
}
|
2022-12-11 11:08:33 -08:00
|
|
|
// Channel 1 is unused by the `JUMP` instruction.
|
2022-08-16 09:46:10 -07:00
|
|
|
{
|
2022-12-11 11:08:33 -08:00
|
|
|
let constr = builder.mul_extension(lv.op.jump, lv.mem_channels[1].used);
|
|
|
|
|
yield_constr.constraint(builder, constr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Finally, set the next program counter.
|
|
|
|
|
let fallthrough_dst = builder.add_const_extension(lv.program_counter, F::ONE);
|
|
|
|
|
let jump_dest = dst[0];
|
|
|
|
|
{
|
|
|
|
|
let constr_a = builder.mul_sub_extension(filter, jumps_lv.should_jump, filter);
|
|
|
|
|
let constr_b = builder.sub_extension(nv.program_counter, fallthrough_dst);
|
|
|
|
|
let constr = builder.mul_extension(constr_a, constr_b);
|
2022-08-16 09:46:10 -07:00
|
|
|
yield_constr.constraint_transition(builder, constr);
|
|
|
|
|
}
|
|
|
|
|
{
|
2022-12-11 11:08:33 -08:00
|
|
|
let constr_a = builder.mul_extension(filter, jumps_lv.should_jump);
|
|
|
|
|
let constr_b = builder.sub_extension(nv.program_counter, jump_dest);
|
|
|
|
|
let constr = builder.mul_extension(constr_a, constr_b);
|
2022-08-16 09:46:10 -07:00
|
|
|
yield_constr.constraint_transition(builder, constr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn eval_packed<P: PackedField>(
|
|
|
|
|
lv: &CpuColumnsView<P>,
|
|
|
|
|
nv: &CpuColumnsView<P>,
|
|
|
|
|
yield_constr: &mut ConstraintConsumer<P>,
|
|
|
|
|
) {
|
|
|
|
|
eval_packed_exit_kernel(lv, nv, yield_constr);
|
|
|
|
|
eval_packed_jump_jumpi(lv, nv, yield_constr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
|
|
|
|
|
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
|
|
|
|
|
lv: &CpuColumnsView<ExtensionTarget<D>>,
|
|
|
|
|
nv: &CpuColumnsView<ExtensionTarget<D>>,
|
|
|
|
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
|
|
|
|
) {
|
|
|
|
|
eval_ext_circuit_exit_kernel(builder, lv, nv, yield_constr);
|
|
|
|
|
eval_ext_circuit_jump_jumpi(builder, lv, nv, yield_constr);
|
|
|
|
|
}
|