s/columns/registers

This commit is contained in:
wborgeaud 2022-06-28 02:28:25 +02:00
parent 34e73db42b
commit b228cc72fe
9 changed files with 247 additions and 246 deletions

View File

@ -127,8 +127,8 @@ mod tests {
use crate::all_stark::{all_cross_table_lookups, AllStark};
use crate::config::StarkConfig;
use crate::cpu::columns::{KECCAK_INPUT_LIMBS, KECCAK_OUTPUT_LIMBS};
use crate::cpu::cpu_stark::CpuStark;
use crate::cpu::registers::{KECCAK_INPUT_LIMBS, KECCAK_OUTPUT_LIMBS};
use crate::keccak::keccak_stark::{KeccakStark, NUM_INPUTS, NUM_ROUNDS};
use crate::logic::{self, LogicStark};
use crate::memory::memory_stark::{generate_random_memory_ops, MemoryStark};
@ -165,20 +165,20 @@ mod tests {
) -> Vec<PolynomialValues<F>> {
let mut trace_rows = vec![];
for _ in 0..num_rows {
let mut row = [F::ZERO; logic::columns::NUM_COLUMNS];
let mut row = [F::ZERO; logic::registers::NUM_COLUMNS];
assert_eq!(logic::PACKED_LIMB_BITS, 16);
for col in logic::columns::INPUT0 {
for col in logic::registers::INPUT0 {
row[col] = F::from_bool(rng.gen());
}
for col in logic::columns::INPUT1 {
for col in logic::registers::INPUT1 {
row[col] = F::from_bool(rng.gen());
}
let op: usize = rng.gen_range(0..3);
let op_col = [
logic::columns::IS_AND,
logic::columns::IS_OR,
logic::columns::IS_XOR,
logic::registers::IS_AND,
logic::registers::IS_OR,
logic::registers::IS_XOR,
][op];
row[op_col] = F::ONE;
logic_stark.generate(&mut row);
@ -186,7 +186,7 @@ mod tests {
}
for _ in num_rows..num_rows.next_power_of_two() {
trace_rows.push([F::ZERO; logic::columns::NUM_COLUMNS])
trace_rows.push([F::ZERO; logic::registers::NUM_COLUMNS])
}
trace_rows_to_poly_values(trace_rows)
}
@ -237,7 +237,7 @@ mod tests {
let mut cpu_trace_rows = vec![];
for i in 0..num_keccak_perms {
let mut row = [F::ZERO; CpuStark::<F, D>::COLUMNS];
row[cpu::columns::IS_KECCAK] = F::ONE;
row[cpu::registers::IS_KECCAK] = F::ONE;
for (j, input, output) in
izip!(0..2 * NUM_INPUTS, KECCAK_INPUT_LIMBS, KECCAK_OUTPUT_LIMBS)
{
@ -249,27 +249,27 @@ mod tests {
}
for i in 0..num_logic_rows {
let mut row = [F::ZERO; CpuStark::<F, D>::COLUMNS];
row[cpu::columns::IS_CPU_CYCLE] = F::ONE;
row[cpu::columns::OPCODE] = [
(logic::columns::IS_AND, 0x16),
(logic::columns::IS_OR, 0x17),
(logic::columns::IS_XOR, 0x18),
row[cpu::registers::IS_CPU_CYCLE] = F::ONE;
row[cpu::registers::OPCODE] = [
(logic::registers::IS_AND, 0x16),
(logic::registers::IS_OR, 0x17),
(logic::registers::IS_XOR, 0x18),
]
.into_iter()
.map(|(col, opcode)| logic_trace[col].values[i] * F::from_canonical_u64(opcode))
.sum();
for (cols_cpu, cols_logic) in [
(cpu::columns::LOGIC_INPUT0, logic::columns::INPUT0),
(cpu::columns::LOGIC_INPUT1, logic::columns::INPUT1),
(cpu::registers::LOGIC_INPUT0, logic::registers::INPUT0),
(cpu::registers::LOGIC_INPUT1, logic::registers::INPUT1),
] {
for (col_cpu, limb_cols_logic) in
cols_cpu.zip(logic::columns::limb_bit_cols_for_input(cols_logic))
cols_cpu.zip(logic::registers::limb_bit_cols_for_input(cols_logic))
{
row[col_cpu] =
limb_from_bits_le(limb_cols_logic.map(|col| logic_trace[col].values[i]));
}
}
for (col_cpu, col_logic) in cpu::columns::LOGIC_OUTPUT.zip(logic::columns::RESULT) {
for (col_cpu, col_logic) in cpu::registers::LOGIC_OUTPUT.zip(logic::registers::RESULT) {
row[col_cpu] = logic_trace[col_logic].values[i];
}
cpu_stark.generate(&mut row);
@ -289,18 +289,18 @@ mod tests {
last_timestamp = mem_timestamp;
}
cpu_trace_rows[current_cpu_index][cpu::columns::mem_channel_used(op)] = F::ONE;
cpu_trace_rows[current_cpu_index][cpu::columns::CLOCK] = clock;
cpu_trace_rows[current_cpu_index][cpu::columns::mem_is_read(op)] =
cpu_trace_rows[current_cpu_index][cpu::registers::mem_channel_used(op)] = F::ONE;
cpu_trace_rows[current_cpu_index][cpu::registers::CLOCK] = clock;
cpu_trace_rows[current_cpu_index][cpu::registers::mem_is_read(op)] =
memory_trace[memory::registers::IS_READ].values[i];
cpu_trace_rows[current_cpu_index][cpu::columns::mem_addr_context(op)] =
cpu_trace_rows[current_cpu_index][cpu::registers::mem_addr_context(op)] =
memory_trace[memory::registers::ADDR_CONTEXT].values[i];
cpu_trace_rows[current_cpu_index][cpu::columns::mem_addr_segment(op)] =
cpu_trace_rows[current_cpu_index][cpu::registers::mem_addr_segment(op)] =
memory_trace[memory::registers::ADDR_SEGMENT].values[i];
cpu_trace_rows[current_cpu_index][cpu::columns::mem_addr_virtual(op)] =
cpu_trace_rows[current_cpu_index][cpu::registers::mem_addr_virtual(op)] =
memory_trace[memory::registers::ADDR_VIRTUAL].values[i];
for j in 0..8 {
cpu_trace_rows[current_cpu_index][cpu::columns::mem_value(op, j)] =
cpu_trace_rows[current_cpu_index][cpu::registers::mem_value(op, j)] =
memory_trace[memory::registers::value_limb(j)].values[i];
}
}

View File

@ -7,7 +7,7 @@ use plonky2::field::types::Field;
use plonky2::hash::hash_types::RichField;
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
use crate::cpu::{columns, decode, simple_logic};
use crate::cpu::{decode, registers, simple_logic};
use crate::cross_table_lookup::Column;
use crate::memory::NUM_CHANNELS;
use crate::permutation::PermutationPair;
@ -15,45 +15,46 @@ use crate::stark::Stark;
use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars};
pub fn ctl_data_keccak<F: Field>() -> Vec<Column<F>> {
let mut res: Vec<_> = columns::KECCAK_INPUT_LIMBS.map(Column::single).collect();
res.extend(columns::KECCAK_OUTPUT_LIMBS.map(Column::single));
let mut res: Vec<_> = registers::KECCAK_INPUT_LIMBS.map(Column::single).collect();
res.extend(registers::KECCAK_OUTPUT_LIMBS.map(Column::single));
res
}
pub fn ctl_filter_keccak<F: Field>() -> Column<F> {
Column::single(columns::IS_KECCAK)
Column::single(registers::IS_KECCAK)
}
pub fn ctl_data_logic<F: Field>() -> Vec<Column<F>> {
let mut res = Column::singles([columns::IS_AND, columns::IS_OR, columns::IS_XOR]).collect_vec();
res.extend(columns::LOGIC_INPUT0.map(Column::single));
res.extend(columns::LOGIC_INPUT1.map(Column::single));
res.extend(columns::LOGIC_OUTPUT.map(Column::single));
let mut res =
Column::singles([registers::IS_AND, registers::IS_OR, registers::IS_XOR]).collect_vec();
res.extend(registers::LOGIC_INPUT0.map(Column::single));
res.extend(registers::LOGIC_INPUT1.map(Column::single));
res.extend(registers::LOGIC_OUTPUT.map(Column::single));
res
}
pub fn ctl_filter_logic<F: Field>() -> Column<F> {
Column::sum([columns::IS_AND, columns::IS_OR, columns::IS_XOR])
Column::sum([registers::IS_AND, registers::IS_OR, registers::IS_XOR])
}
pub fn ctl_data_memory<F: Field>(channel: usize) -> Vec<Column<F>> {
debug_assert!(channel < NUM_CHANNELS);
let mut cols: Vec<Column<F>> = Column::singles([
columns::CLOCK,
columns::mem_is_read(channel),
columns::mem_addr_context(channel),
columns::mem_addr_segment(channel),
columns::mem_addr_virtual(channel),
registers::CLOCK,
registers::mem_is_read(channel),
registers::mem_addr_context(channel),
registers::mem_addr_segment(channel),
registers::mem_addr_virtual(channel),
])
.collect_vec();
cols.extend(Column::singles(
(0..8).map(|j| columns::mem_value(channel, j)),
(0..8).map(|j| registers::mem_value(channel, j)),
));
cols
}
pub fn ctl_filter_memory<F: Field>(channel: usize) -> Column<F> {
Column::single(columns::mem_channel_used(channel))
Column::single(registers::mem_channel_used(channel))
}
#[derive(Copy, Clone)]
@ -62,14 +63,14 @@ pub struct CpuStark<F, const D: usize> {
}
impl<F: RichField, const D: usize> CpuStark<F, D> {
pub fn generate(&self, local_values: &mut [F; columns::NUM_CPU_COLUMNS]) {
pub fn generate(&self, local_values: &mut [F; registers::NUM_CPU_COLUMNS]) {
decode::generate(local_values);
simple_logic::generate(local_values);
}
}
impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D> {
const COLUMNS: usize = columns::NUM_CPU_COLUMNS;
const COLUMNS: usize = registers::NUM_CPU_COLUMNS;
const PUBLIC_INPUTS: usize = 0;
fn eval_packed_generic<FE, P, const D2: usize>(

View File

@ -5,7 +5,7 @@ use plonky2::hash::hash_types::RichField;
use plonky2::iop::ext_target::ExtensionTarget;
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
use crate::cpu::columns;
use crate::cpu::registers;
// List of opcode blocks
// Each block corresponds to exactly one flag, and each flag corresponds to exactly one block.
@ -17,125 +17,125 @@ use crate::cpu::columns;
// top 8-n bits.
const OPCODES: [(u64, usize, usize); 102] = [
// (start index of block, number of top bits to check (log2), flag column)
(0x00, 0, columns::IS_STOP),
(0x01, 0, columns::IS_ADD),
(0x02, 0, columns::IS_MUL),
(0x03, 0, columns::IS_SUB),
(0x04, 0, columns::IS_DIV),
(0x05, 0, columns::IS_SDIV),
(0x06, 0, columns::IS_MOD),
(0x07, 0, columns::IS_SMOD),
(0x08, 0, columns::IS_ADDMOD),
(0x09, 0, columns::IS_MULMOD),
(0x0a, 0, columns::IS_EXP),
(0x0b, 0, columns::IS_SIGNEXTEND),
(0x0c, 2, columns::IS_INVALID_0), // 0x0c-0x0f
(0x10, 0, columns::IS_LT),
(0x11, 0, columns::IS_GT),
(0x12, 0, columns::IS_SLT),
(0x13, 0, columns::IS_SGT),
(0x14, 0, columns::IS_EQ),
(0x15, 0, columns::IS_ISZERO),
(0x16, 0, columns::IS_AND),
(0x17, 0, columns::IS_OR),
(0x18, 0, columns::IS_XOR),
(0x19, 0, columns::IS_NOT),
(0x1a, 0, columns::IS_BYTE),
(0x1b, 0, columns::IS_SHL),
(0x1c, 0, columns::IS_SHR),
(0x1d, 0, columns::IS_SAR),
(0x1e, 1, columns::IS_INVALID_1), // 0x1e-0x1f
(0x20, 0, columns::IS_SHA3),
(0x21, 0, columns::IS_INVALID_2),
(0x22, 1, columns::IS_INVALID_3), // 0x22-0x23
(0x24, 2, columns::IS_INVALID_4), // 0x24-0x27
(0x28, 3, columns::IS_INVALID_5), // 0x28-0x2f
(0x30, 0, columns::IS_ADDRESS),
(0x31, 0, columns::IS_BALANCE),
(0x32, 0, columns::IS_ORIGIN),
(0x33, 0, columns::IS_CALLER),
(0x34, 0, columns::IS_CALLVALUE),
(0x35, 0, columns::IS_CALLDATALOAD),
(0x36, 0, columns::IS_CALLDATASIZE),
(0x37, 0, columns::IS_CALLDATACOPY),
(0x38, 0, columns::IS_CODESIZE),
(0x39, 0, columns::IS_CODECOPY),
(0x3a, 0, columns::IS_GASPRICE),
(0x3b, 0, columns::IS_EXTCODESIZE),
(0x3c, 0, columns::IS_EXTCODECOPY),
(0x3d, 0, columns::IS_RETURNDATASIZE),
(0x3e, 0, columns::IS_RETURNDATACOPY),
(0x3f, 0, columns::IS_EXTCODEHASH),
(0x40, 0, columns::IS_BLOCKHASH),
(0x41, 0, columns::IS_COINBASE),
(0x42, 0, columns::IS_TIMESTAMP),
(0x43, 0, columns::IS_NUMBER),
(0x44, 0, columns::IS_DIFFICULTY),
(0x45, 0, columns::IS_GASLIMIT),
(0x46, 0, columns::IS_CHAINID),
(0x47, 0, columns::IS_SELFBALANCE),
(0x48, 0, columns::IS_BASEFEE),
(0x49, 0, columns::IS_INVALID_6),
(0x4a, 1, columns::IS_INVALID_7), // 0x4a-0x4b
(0x4c, 2, columns::IS_INVALID_8), // 0x4c-0x4f
(0x50, 0, columns::IS_POP),
(0x51, 0, columns::IS_MLOAD),
(0x52, 0, columns::IS_MSTORE),
(0x53, 0, columns::IS_MSTORE8),
(0x54, 0, columns::IS_SLOAD),
(0x55, 0, columns::IS_SSTORE),
(0x56, 0, columns::IS_JUMP),
(0x57, 0, columns::IS_JUMPI),
(0x58, 0, columns::IS_PC),
(0x59, 0, columns::IS_MSIZE),
(0x5a, 0, columns::IS_GAS),
(0x5b, 0, columns::IS_JUMPDEST),
(0x5c, 2, columns::IS_INVALID_9), // 0x5c-0x5f
(0x60, 5, columns::IS_PUSH), // 0x60-0x7f
(0x80, 4, columns::IS_DUP), // 0x80-0x8f
(0x90, 4, columns::IS_SWAP), // 0x90-0x9f
(0xa0, 0, columns::IS_LOG0),
(0xa1, 0, columns::IS_LOG1),
(0xa2, 0, columns::IS_LOG2),
(0xa3, 0, columns::IS_LOG3),
(0xa4, 0, columns::IS_LOG4),
(0xa5, 0, columns::IS_INVALID_10),
(0xa6, 1, columns::IS_INVALID_11), // 0xa6-0xa7
(0xa8, 3, columns::IS_INVALID_12), // 0xa8-0xaf
(0xb0, 4, columns::IS_INVALID_13), // 0xb0-0xbf
(0xc0, 5, columns::IS_INVALID_14), // 0xc0-0xdf
(0xe0, 4, columns::IS_INVALID_15), // 0xe0-0xef
(0xf0, 0, columns::IS_CREATE),
(0xf1, 0, columns::IS_CALL),
(0xf2, 0, columns::IS_CALLCODE),
(0xf3, 0, columns::IS_RETURN),
(0xf4, 0, columns::IS_DELEGATECALL),
(0xf5, 0, columns::IS_CREATE2),
(0xf6, 1, columns::IS_INVALID_16), // 0xf6-0xf7
(0xf8, 1, columns::IS_INVALID_17), // 0xf8-0xf9
(0xfa, 0, columns::IS_STATICCALL),
(0xfb, 0, columns::IS_INVALID_18),
(0xfc, 0, columns::IS_INVALID_19),
(0xfd, 0, columns::IS_REVERT),
(0xfe, 0, columns::IS_INVALID_20),
(0xff, 0, columns::IS_SELFDESTRUCT),
(0x00, 0, registers::IS_STOP),
(0x01, 0, registers::IS_ADD),
(0x02, 0, registers::IS_MUL),
(0x03, 0, registers::IS_SUB),
(0x04, 0, registers::IS_DIV),
(0x05, 0, registers::IS_SDIV),
(0x06, 0, registers::IS_MOD),
(0x07, 0, registers::IS_SMOD),
(0x08, 0, registers::IS_ADDMOD),
(0x09, 0, registers::IS_MULMOD),
(0x0a, 0, registers::IS_EXP),
(0x0b, 0, registers::IS_SIGNEXTEND),
(0x0c, 2, registers::IS_INVALID_0), // 0x0c-0x0f
(0x10, 0, registers::IS_LT),
(0x11, 0, registers::IS_GT),
(0x12, 0, registers::IS_SLT),
(0x13, 0, registers::IS_SGT),
(0x14, 0, registers::IS_EQ),
(0x15, 0, registers::IS_ISZERO),
(0x16, 0, registers::IS_AND),
(0x17, 0, registers::IS_OR),
(0x18, 0, registers::IS_XOR),
(0x19, 0, registers::IS_NOT),
(0x1a, 0, registers::IS_BYTE),
(0x1b, 0, registers::IS_SHL),
(0x1c, 0, registers::IS_SHR),
(0x1d, 0, registers::IS_SAR),
(0x1e, 1, registers::IS_INVALID_1), // 0x1e-0x1f
(0x20, 0, registers::IS_SHA3),
(0x21, 0, registers::IS_INVALID_2),
(0x22, 1, registers::IS_INVALID_3), // 0x22-0x23
(0x24, 2, registers::IS_INVALID_4), // 0x24-0x27
(0x28, 3, registers::IS_INVALID_5), // 0x28-0x2f
(0x30, 0, registers::IS_ADDRESS),
(0x31, 0, registers::IS_BALANCE),
(0x32, 0, registers::IS_ORIGIN),
(0x33, 0, registers::IS_CALLER),
(0x34, 0, registers::IS_CALLVALUE),
(0x35, 0, registers::IS_CALLDATALOAD),
(0x36, 0, registers::IS_CALLDATASIZE),
(0x37, 0, registers::IS_CALLDATACOPY),
(0x38, 0, registers::IS_CODESIZE),
(0x39, 0, registers::IS_CODECOPY),
(0x3a, 0, registers::IS_GASPRICE),
(0x3b, 0, registers::IS_EXTCODESIZE),
(0x3c, 0, registers::IS_EXTCODECOPY),
(0x3d, 0, registers::IS_RETURNDATASIZE),
(0x3e, 0, registers::IS_RETURNDATACOPY),
(0x3f, 0, registers::IS_EXTCODEHASH),
(0x40, 0, registers::IS_BLOCKHASH),
(0x41, 0, registers::IS_COINBASE),
(0x42, 0, registers::IS_TIMESTAMP),
(0x43, 0, registers::IS_NUMBER),
(0x44, 0, registers::IS_DIFFICULTY),
(0x45, 0, registers::IS_GASLIMIT),
(0x46, 0, registers::IS_CHAINID),
(0x47, 0, registers::IS_SELFBALANCE),
(0x48, 0, registers::IS_BASEFEE),
(0x49, 0, registers::IS_INVALID_6),
(0x4a, 1, registers::IS_INVALID_7), // 0x4a-0x4b
(0x4c, 2, registers::IS_INVALID_8), // 0x4c-0x4f
(0x50, 0, registers::IS_POP),
(0x51, 0, registers::IS_MLOAD),
(0x52, 0, registers::IS_MSTORE),
(0x53, 0, registers::IS_MSTORE8),
(0x54, 0, registers::IS_SLOAD),
(0x55, 0, registers::IS_SSTORE),
(0x56, 0, registers::IS_JUMP),
(0x57, 0, registers::IS_JUMPI),
(0x58, 0, registers::IS_PC),
(0x59, 0, registers::IS_MSIZE),
(0x5a, 0, registers::IS_GAS),
(0x5b, 0, registers::IS_JUMPDEST),
(0x5c, 2, registers::IS_INVALID_9), // 0x5c-0x5f
(0x60, 5, registers::IS_PUSH), // 0x60-0x7f
(0x80, 4, registers::IS_DUP), // 0x80-0x8f
(0x90, 4, registers::IS_SWAP), // 0x90-0x9f
(0xa0, 0, registers::IS_LOG0),
(0xa1, 0, registers::IS_LOG1),
(0xa2, 0, registers::IS_LOG2),
(0xa3, 0, registers::IS_LOG3),
(0xa4, 0, registers::IS_LOG4),
(0xa5, 0, registers::IS_INVALID_10),
(0xa6, 1, registers::IS_INVALID_11), // 0xa6-0xa7
(0xa8, 3, registers::IS_INVALID_12), // 0xa8-0xaf
(0xb0, 4, registers::IS_INVALID_13), // 0xb0-0xbf
(0xc0, 5, registers::IS_INVALID_14), // 0xc0-0xdf
(0xe0, 4, registers::IS_INVALID_15), // 0xe0-0xef
(0xf0, 0, registers::IS_CREATE),
(0xf1, 0, registers::IS_CALL),
(0xf2, 0, registers::IS_CALLCODE),
(0xf3, 0, registers::IS_RETURN),
(0xf4, 0, registers::IS_DELEGATECALL),
(0xf5, 0, registers::IS_CREATE2),
(0xf6, 1, registers::IS_INVALID_16), // 0xf6-0xf7
(0xf8, 1, registers::IS_INVALID_17), // 0xf8-0xf9
(0xfa, 0, registers::IS_STATICCALL),
(0xfb, 0, registers::IS_INVALID_18),
(0xfc, 0, registers::IS_INVALID_19),
(0xfd, 0, registers::IS_REVERT),
(0xfe, 0, registers::IS_INVALID_20),
(0xff, 0, registers::IS_SELFDESTRUCT),
];
pub fn generate<F: RichField>(lv: &mut [F; columns::NUM_CPU_COLUMNS]) {
let cycle_filter = lv[columns::IS_CPU_CYCLE];
pub fn generate<F: RichField>(lv: &mut [F; registers::NUM_CPU_COLUMNS]) {
let cycle_filter = lv[registers::IS_CPU_CYCLE];
if cycle_filter == F::ZERO {
// These columns cannot be shared.
lv[columns::IS_EQ] = F::ZERO;
lv[columns::IS_ISZERO] = F::ZERO;
lv[registers::IS_EQ] = F::ZERO;
lv[registers::IS_ISZERO] = F::ZERO;
return;
}
// This assert is not _strictly_ necessary, but I include it as a sanity check.
assert_eq!(cycle_filter, F::ONE, "cycle_filter should be 0 or 1");
let opcode = lv[columns::OPCODE].to_canonical_u64();
let opcode = lv[registers::OPCODE].to_canonical_u64();
assert!(opcode < 256, "opcode should be in {{0, ..., 255}}");
for (i, &col) in columns::OPCODE_BITS.iter().enumerate() {
for (i, &col) in registers::OPCODE_BITS.iter().enumerate() {
let bit = (opcode >> (7 - i)) & 1;
lv[col] = F::from_canonical_u64(bit);
}
@ -158,14 +158,14 @@ pub fn generate<F: RichField>(lv: &mut [F; columns::NUM_CPU_COLUMNS]) {
}
pub fn eval_packed_generic<P: PackedField>(
lv: &[P; columns::NUM_CPU_COLUMNS],
lv: &[P; registers::NUM_CPU_COLUMNS],
yield_constr: &mut ConstraintConsumer<P>,
) {
let cycle_filter = lv[columns::IS_CPU_CYCLE];
let cycle_filter = lv[registers::IS_CPU_CYCLE];
// Ensure that the opcode bits are valid: each has to be either 0 or 1, and they must match
// the opcode. Note that this also validates that this implicitly range-checks the opcode.
let bits = columns::OPCODE_BITS.map(|i| lv[i]);
let bits = registers::OPCODE_BITS.map(|i| lv[i]);
// First check that the bits are either 0 or 1.
for bit in bits {
yield_constr.constraint(cycle_filter * bit * (bit - P::ONES));
@ -181,16 +181,16 @@ pub fn eval_packed_generic<P: PackedField>(
};
// Now check that they match the opcode.
let opcode = lv[columns::OPCODE];
let opcode = lv[registers::OPCODE];
yield_constr.constraint(cycle_filter * (opcode - top_bits[8]));
// Check that the instruction flags are valid.
// First, check that they are all either 0 or 1.
for &flag in &lv[columns::START_INSTRUCTION_FLAGS..columns::END_INSTRUCTION_FLAGS] {
for &flag in &lv[registers::START_INSTRUCTION_FLAGS..registers::END_INSTRUCTION_FLAGS] {
yield_constr.constraint(cycle_filter * flag * (flag - P::ONES));
}
// Now check that exactly one is 1.
let flag_sum: P = (columns::START_INSTRUCTION_FLAGS..columns::END_INSTRUCTION_FLAGS)
let flag_sum: P = (registers::START_INSTRUCTION_FLAGS..registers::END_INSTRUCTION_FLAGS)
.into_iter()
.map(|i| lv[i])
.sum();
@ -205,14 +205,14 @@ pub fn eval_packed_generic<P: PackedField>(
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
lv: &[ExtensionTarget<D>; columns::NUM_CPU_COLUMNS],
lv: &[ExtensionTarget<D>; registers::NUM_CPU_COLUMNS],
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
) {
let cycle_filter = lv[columns::IS_CPU_CYCLE];
let cycle_filter = lv[registers::IS_CPU_CYCLE];
// Ensure that the opcode bits are valid: each has to be either 0 or 1, and they must match
// the opcode. Note that this also validates that this implicitly range-checks the opcode.
let bits = columns::OPCODE_BITS.map(|i| lv[i]);
let bits = registers::OPCODE_BITS.map(|i| lv[i]);
// First check that the bits are either 0 or 1.
for bit in bits {
let constr = builder.mul_sub_extension(bit, bit, bit);
@ -234,14 +234,14 @@ pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
// Now check that the bits match the opcode.
{
let constr = builder.sub_extension(lv[columns::OPCODE], top_bits[8]);
let constr = builder.sub_extension(lv[registers::OPCODE], top_bits[8]);
let constr = builder.mul_extension(cycle_filter, constr);
yield_constr.constraint(builder, constr);
};
// Check that the instruction flags are valid.
// First, check that they are all either 0 or 1.
for &flag in &lv[columns::START_INSTRUCTION_FLAGS..columns::END_INSTRUCTION_FLAGS] {
for &flag in &lv[registers::START_INSTRUCTION_FLAGS..registers::END_INSTRUCTION_FLAGS] {
let constr = builder.mul_sub_extension(flag, flag, flag);
let constr = builder.mul_extension(cycle_filter, constr);
yield_constr.constraint(builder, constr);
@ -249,7 +249,7 @@ pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
// Now check that they sum to 1.
{
let mut constr = builder.one_extension();
for &flag in &lv[columns::START_INSTRUCTION_FLAGS..columns::END_INSTRUCTION_FLAGS] {
for &flag in &lv[registers::START_INSTRUCTION_FLAGS..registers::END_INSTRUCTION_FLAGS] {
constr = builder.sub_extension(constr, flag);
}
constr = builder.mul_extension(cycle_filter, constr);

View File

@ -1,5 +1,5 @@
pub(crate) mod columns;
pub mod cpu_stark;
pub(crate) mod decode;
pub mod kernel;
pub(crate) mod registers;
mod simple_logic;

View File

@ -4,13 +4,13 @@ use plonky2::hash::hash_types::RichField;
use plonky2::iop::ext_target::ExtensionTarget;
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
use crate::cpu::columns;
use crate::cpu::registers;
const LIMB_SIZE: usize = 16;
pub fn generate<F: RichField>(lv: &mut [F; columns::NUM_CPU_COLUMNS]) {
let eq_filter = lv[columns::IS_EQ].to_canonical_u64();
let iszero_filter = lv[columns::IS_ISZERO].to_canonical_u64();
pub fn generate<F: RichField>(lv: &mut [F; registers::NUM_CPU_COLUMNS]) {
let eq_filter = lv[registers::IS_EQ].to_canonical_u64();
let iszero_filter = lv[registers::IS_ISZERO].to_canonical_u64();
assert!(eq_filter <= 1);
assert!(iszero_filter <= 1);
assert!(eq_filter + iszero_filter <= 1);
@ -20,8 +20,8 @@ pub fn generate<F: RichField>(lv: &mut [F; columns::NUM_CPU_COLUMNS]) {
}
let diffs = if eq_filter == 1 {
columns::LOGIC_INPUT0
.zip(columns::LOGIC_INPUT1)
registers::LOGIC_INPUT0
.zip(registers::LOGIC_INPUT1)
.map(|(in0_col, in1_col)| {
let in0 = lv[in0_col];
let in1 = lv[in1_col];
@ -32,48 +32,48 @@ pub fn generate<F: RichField>(lv: &mut [F; columns::NUM_CPU_COLUMNS]) {
})
.sum()
} else if iszero_filter == 1 {
columns::LOGIC_INPUT0.map(|i| lv[i]).sum()
registers::LOGIC_INPUT0.map(|i| lv[i]).sum()
} else {
panic!()
};
lv[columns::SIMPLE_LOGIC_DIFF] = diffs;
lv[columns::SIMPLE_LOGIC_DIFF_INV] = diffs.try_inverse().unwrap_or(F::ZERO);
lv[registers::SIMPLE_LOGIC_DIFF] = diffs;
lv[registers::SIMPLE_LOGIC_DIFF_INV] = diffs.try_inverse().unwrap_or(F::ZERO);
lv[columns::LOGIC_OUTPUT.start] = F::from_bool(diffs == F::ZERO);
for i in columns::LOGIC_OUTPUT.start + 1..columns::LOGIC_OUTPUT.end {
lv[registers::LOGIC_OUTPUT.start] = F::from_bool(diffs == F::ZERO);
for i in registers::LOGIC_OUTPUT.start + 1..registers::LOGIC_OUTPUT.end {
lv[i] = F::ZERO;
}
}
pub fn eval_packed<P: PackedField>(
lv: &[P; columns::NUM_CPU_COLUMNS],
lv: &[P; registers::NUM_CPU_COLUMNS],
yield_constr: &mut ConstraintConsumer<P>,
) {
let eq_filter = lv[columns::IS_EQ];
let iszero_filter = lv[columns::IS_ISZERO];
let eq_filter = lv[registers::IS_EQ];
let iszero_filter = lv[registers::IS_ISZERO];
let eq_or_iszero_filter = eq_filter + iszero_filter;
let ls_bit = lv[columns::LOGIC_OUTPUT.start];
let ls_bit = lv[registers::LOGIC_OUTPUT.start];
// Handle EQ and ISZERO. Most limbs of the output are 0, but the least-significant one is
// either 0 or 1.
yield_constr.constraint(eq_or_iszero_filter * ls_bit * (ls_bit - P::ONES));
for bit_col in columns::LOGIC_OUTPUT.start + 1..columns::LOGIC_OUTPUT.end {
for bit_col in registers::LOGIC_OUTPUT.start + 1..registers::LOGIC_OUTPUT.end {
let bit = lv[bit_col];
yield_constr.constraint(eq_or_iszero_filter * bit);
}
// Check SIMPLE_LOGIC_DIFF
let diffs = lv[columns::SIMPLE_LOGIC_DIFF];
let diffs_inv = lv[columns::SIMPLE_LOGIC_DIFF_INV];
let diffs = lv[registers::SIMPLE_LOGIC_DIFF];
let diffs_inv = lv[registers::SIMPLE_LOGIC_DIFF_INV];
{
let input0_sum: P = columns::LOGIC_INPUT0.map(|i| lv[i]).sum();
let input0_sum: P = registers::LOGIC_INPUT0.map(|i| lv[i]).sum();
yield_constr.constraint(iszero_filter * (diffs - input0_sum));
let sum_squared_diffs: P = columns::LOGIC_INPUT0
.zip(columns::LOGIC_INPUT1)
let sum_squared_diffs: P = registers::LOGIC_INPUT0
.zip(registers::LOGIC_INPUT1)
.map(|(in0_col, in1_col)| {
let in0 = lv[in0_col];
let in1 = lv[in1_col];
@ -92,14 +92,14 @@ pub fn eval_packed<P: PackedField>(
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
lv: &[ExtensionTarget<D>; columns::NUM_CPU_COLUMNS],
lv: &[ExtensionTarget<D>; registers::NUM_CPU_COLUMNS],
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
) {
let eq_filter = lv[columns::IS_EQ];
let iszero_filter = lv[columns::IS_ISZERO];
let eq_filter = lv[registers::IS_EQ];
let iszero_filter = lv[registers::IS_ISZERO];
let eq_or_iszero_filter = builder.add_extension(eq_filter, iszero_filter);
let ls_bit = lv[columns::LOGIC_OUTPUT.start];
let ls_bit = lv[registers::LOGIC_OUTPUT.start];
// Handle EQ and ISZERO. Most limbs of the output are 0, but the least-significant one is
// either 0 or 1.
@ -109,24 +109,24 @@ pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
yield_constr.constraint(builder, constr);
}
for bit_col in columns::LOGIC_OUTPUT.start + 1..columns::LOGIC_OUTPUT.end {
for bit_col in registers::LOGIC_OUTPUT.start + 1..registers::LOGIC_OUTPUT.end {
let bit = lv[bit_col];
let constr = builder.mul_extension(eq_or_iszero_filter, bit);
yield_constr.constraint(builder, constr);
}
// Check SIMPLE_LOGIC_DIFF
let diffs = lv[columns::SIMPLE_LOGIC_DIFF];
let diffs_inv = lv[columns::SIMPLE_LOGIC_DIFF_INV];
let diffs = lv[registers::SIMPLE_LOGIC_DIFF];
let diffs_inv = lv[registers::SIMPLE_LOGIC_DIFF_INV];
{
let input0_sum = builder.add_many_extension(columns::LOGIC_INPUT0.map(|i| lv[i]));
let input0_sum = builder.add_many_extension(registers::LOGIC_INPUT0.map(|i| lv[i]));
{
let constr = builder.sub_extension(diffs, input0_sum);
let constr = builder.mul_extension(iszero_filter, constr);
yield_constr.constraint(builder, constr);
}
let sum_squared_diffs = columns::LOGIC_INPUT0.zip(columns::LOGIC_INPUT1).fold(
let sum_squared_diffs = registers::LOGIC_INPUT0.zip(registers::LOGIC_INPUT1).fold(
builder.zero_extension(),
|acc, (in0_col, in1_col)| {
let in0 = lv[in0_col];

View File

@ -7,10 +7,10 @@ use plonky2::hash::hash_types::RichField;
use plonky2::iop::ext_target::ExtensionTarget;
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
use crate::cpu::columns;
use crate::cpu::registers;
pub fn generate<F: RichField>(lv: &mut [F; columns::NUM_CPU_COLUMNS]) {
let cycle_filter = lv[columns::IS_CPU_CYCLE].to_canonical_u64();
pub fn generate<F: RichField>(lv: &mut [F; registers::NUM_CPU_COLUMNS]) {
let cycle_filter = lv[registers::IS_CPU_CYCLE].to_canonical_u64();
if cycle_filter == 0 {
return;
}
@ -21,7 +21,7 @@ pub fn generate<F: RichField>(lv: &mut [F; columns::NUM_CPU_COLUMNS]) {
}
pub fn eval_packed<P: PackedField>(
lv: &[P; columns::NUM_CPU_COLUMNS],
lv: &[P; registers::NUM_CPU_COLUMNS],
yield_constr: &mut ConstraintConsumer<P>,
) {
not::eval_packed(lv, yield_constr);
@ -30,7 +30,7 @@ pub fn eval_packed<P: PackedField>(
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
lv: &[ExtensionTarget<D>; columns::NUM_CPU_COLUMNS],
lv: &[ExtensionTarget<D>; registers::NUM_CPU_COLUMNS],
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
) {
not::eval_ext_circuit(builder, lv, yield_constr);

View File

@ -5,19 +5,19 @@ use plonky2::hash::hash_types::RichField;
use plonky2::iop::ext_target::ExtensionTarget;
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
use crate::cpu::columns;
use crate::cpu::registers;
const LIMB_SIZE: usize = 16;
const ALL_1_LIMB: u64 = (1 << LIMB_SIZE) - 1;
pub fn generate<F: RichField>(lv: &mut [F; columns::NUM_CPU_COLUMNS]) {
let is_not_filter = lv[columns::IS_NOT].to_canonical_u64();
pub fn generate<F: RichField>(lv: &mut [F; registers::NUM_CPU_COLUMNS]) {
let is_not_filter = lv[registers::IS_NOT].to_canonical_u64();
if is_not_filter == 0 {
return;
}
assert_eq!(is_not_filter, 1);
for (input_col, output_col) in columns::LOGIC_INPUT0.zip(columns::LOGIC_OUTPUT) {
for (input_col, output_col) in registers::LOGIC_INPUT0.zip(registers::LOGIC_OUTPUT) {
let input = lv[input_col].to_canonical_u64();
assert_eq!(input >> LIMB_SIZE, 0);
let output = input ^ ALL_1_LIMB;
@ -26,14 +26,14 @@ pub fn generate<F: RichField>(lv: &mut [F; columns::NUM_CPU_COLUMNS]) {
}
pub fn eval_packed<P: PackedField>(
lv: &[P; columns::NUM_CPU_COLUMNS],
lv: &[P; registers::NUM_CPU_COLUMNS],
yield_constr: &mut ConstraintConsumer<P>,
) {
// This is simple: just do output = 0xffff - input.
let cycle_filter = lv[columns::IS_CPU_CYCLE];
let is_not_filter = lv[columns::IS_NOT];
let cycle_filter = lv[registers::IS_CPU_CYCLE];
let is_not_filter = lv[registers::IS_NOT];
let filter = cycle_filter * is_not_filter;
for (input_col, output_col) in columns::LOGIC_INPUT0.zip(columns::LOGIC_OUTPUT) {
for (input_col, output_col) in registers::LOGIC_INPUT0.zip(registers::LOGIC_OUTPUT) {
let input = lv[input_col];
let output = lv[output_col];
yield_constr
@ -43,13 +43,13 @@ pub fn eval_packed<P: PackedField>(
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
lv: &[ExtensionTarget<D>; columns::NUM_CPU_COLUMNS],
lv: &[ExtensionTarget<D>; registers::NUM_CPU_COLUMNS],
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
) {
let cycle_filter = lv[columns::IS_CPU_CYCLE];
let is_not_filter = lv[columns::IS_NOT];
let cycle_filter = lv[registers::IS_CPU_CYCLE];
let is_not_filter = lv[registers::IS_NOT];
let filter = builder.mul_extension(cycle_filter, is_not_filter);
for (input_col, output_col) in columns::LOGIC_INPUT0.zip(columns::LOGIC_OUTPUT) {
for (input_col, output_col) in registers::LOGIC_INPUT0.zip(registers::LOGIC_OUTPUT) {
let input = lv[input_col];
let output = lv[output_col];
let constr = builder.add_extension(output, input);

View File

@ -19,7 +19,7 @@ pub(crate) const PACKED_LIMB_BITS: usize = 16;
// Number of field elements needed to store each input/output at the specified packing.
const PACKED_LEN: usize = (VAL_BITS + PACKED_LIMB_BITS - 1) / PACKED_LIMB_BITS;
pub(crate) mod columns {
pub(crate) mod registers {
use std::cmp::min;
use std::ops::Range;
@ -47,18 +47,18 @@ pub(crate) mod columns {
pub fn ctl_data<F: Field>() -> Vec<Column<F>> {
let mut res = vec![
Column::single(columns::IS_AND),
Column::single(columns::IS_OR),
Column::single(columns::IS_XOR),
Column::single(registers::IS_AND),
Column::single(registers::IS_OR),
Column::single(registers::IS_XOR),
];
res.extend(columns::limb_bit_cols_for_input(columns::INPUT0).map(Column::le_bits));
res.extend(columns::limb_bit_cols_for_input(columns::INPUT1).map(Column::le_bits));
res.extend(columns::RESULT.map(Column::single));
res.extend(registers::limb_bit_cols_for_input(registers::INPUT0).map(Column::le_bits));
res.extend(registers::limb_bit_cols_for_input(registers::INPUT1).map(Column::le_bits));
res.extend(registers::RESULT.map(Column::single));
res
}
pub fn ctl_filter<F: Field>() -> Column<F> {
Column::sum([columns::IS_AND, columns::IS_OR, columns::IS_XOR])
Column::sum([registers::IS_AND, registers::IS_OR, registers::IS_XOR])
}
#[derive(Copy, Clone)]
@ -75,12 +75,12 @@ enum Op {
Xor,
}
fn check_op_flags<F: RichField>(lv: &[F; columns::NUM_COLUMNS]) -> Op {
let is_and = lv[columns::IS_AND].to_canonical_u64();
fn check_op_flags<F: RichField>(lv: &[F; registers::NUM_COLUMNS]) -> Op {
let is_and = lv[registers::IS_AND].to_canonical_u64();
assert!(is_and <= 1);
let is_or = lv[columns::IS_OR].to_canonical_u64();
let is_or = lv[registers::IS_OR].to_canonical_u64();
assert!(is_or <= 1);
let is_xor = lv[columns::IS_XOR].to_canonical_u64();
let is_xor = lv[registers::IS_XOR].to_canonical_u64();
assert!(is_xor <= 1);
assert!(is_and + is_or + is_xor <= 1);
if is_and == 1 {
@ -94,8 +94,8 @@ fn check_op_flags<F: RichField>(lv: &[F; columns::NUM_COLUMNS]) -> Op {
}
}
fn check_bits<F: RichField>(lv: &[F; columns::NUM_COLUMNS]) {
for bit_cols in [columns::INPUT0, columns::INPUT1] {
fn check_bits<F: RichField>(lv: &[F; registers::NUM_COLUMNS]) {
for bit_cols in [registers::INPUT0, registers::INPUT1] {
for bit_col in bit_cols {
let bit = lv[bit_col].to_canonical_u64();
assert!(bit <= 1);
@ -103,11 +103,11 @@ fn check_bits<F: RichField>(lv: &[F; columns::NUM_COLUMNS]) {
}
}
fn make_result<F: RichField>(lv: &mut [F; columns::NUM_COLUMNS], op: Op) {
fn make_result<F: RichField>(lv: &mut [F; registers::NUM_COLUMNS], op: Op) {
for (res_col, limb_in0_cols, limb_in1_cols) in izip!(
columns::RESULT,
columns::limb_bit_cols_for_input(columns::INPUT0),
columns::limb_bit_cols_for_input(columns::INPUT1),
registers::RESULT,
registers::limb_bit_cols_for_input(registers::INPUT0),
registers::limb_bit_cols_for_input(registers::INPUT1),
) {
let limb_in0: u64 = limb_from_bits_le(limb_in0_cols.map(|col| lv[col])).to_canonical_u64();
let limb_in1: u64 = limb_from_bits_le(limb_in1_cols.map(|col| lv[col])).to_canonical_u64();
@ -122,7 +122,7 @@ fn make_result<F: RichField>(lv: &mut [F; columns::NUM_COLUMNS], op: Op) {
}
impl<F: RichField, const D: usize> LogicStark<F, D> {
pub fn generate(&self, lv: &mut [F; columns::NUM_COLUMNS]) {
pub fn generate(&self, lv: &mut [F; registers::NUM_COLUMNS]) {
let op = check_op_flags(lv);
check_bits(lv);
make_result(lv, op);
@ -130,7 +130,7 @@ impl<F: RichField, const D: usize> LogicStark<F, D> {
}
impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for LogicStark<F, D> {
const COLUMNS: usize = columns::NUM_COLUMNS;
const COLUMNS: usize = registers::NUM_COLUMNS;
const PUBLIC_INPUTS: usize = 0;
fn eval_packed_generic<FE, P, const D2: usize>(
@ -144,9 +144,9 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for LogicStark<F,
let lv = &vars.local_values;
// IS_AND, IS_OR, and IS_XOR come from the CPU table, so we assume they're valid.
let is_and = lv[columns::IS_AND];
let is_or = lv[columns::IS_OR];
let is_xor = lv[columns::IS_XOR];
let is_and = lv[registers::IS_AND];
let is_or = lv[registers::IS_OR];
let is_xor = lv[registers::IS_XOR];
// The result will be `in0 OP in1 = sum_coeff * (in0 + in1) + and_coeff * (in0 AND in1)`.
// `AND => sum_coeff = 0, and_coeff = 1`
@ -156,7 +156,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for LogicStark<F,
let and_coeff = is_and - is_or - is_xor * FE::TWO;
// Ensure that all bits are indeed bits.
for input_bits_cols in [columns::INPUT0, columns::INPUT1] {
for input_bits_cols in [registers::INPUT0, registers::INPUT1] {
for i in input_bits_cols {
let bit = lv[i];
yield_constr.constraint(bit * (bit - P::ONES));
@ -165,9 +165,9 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for LogicStark<F,
// Form the result
for (result_col, x_bits_cols, y_bits_cols) in izip!(
columns::RESULT,
columns::limb_bit_cols_for_input(columns::INPUT0),
columns::limb_bit_cols_for_input(columns::INPUT1),
registers::RESULT,
registers::limb_bit_cols_for_input(registers::INPUT0),
registers::limb_bit_cols_for_input(registers::INPUT1),
) {
let x: P = limb_from_bits_le(x_bits_cols.clone().map(|col| lv[col]));
let y: P = limb_from_bits_le(y_bits_cols.clone().map(|col| lv[col]));
@ -193,9 +193,9 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for LogicStark<F,
let lv = &vars.local_values;
// IS_AND, IS_OR, and IS_XOR come from the CPU table, so we assume they're valid.
let is_and = lv[columns::IS_AND];
let is_or = lv[columns::IS_OR];
let is_xor = lv[columns::IS_XOR];
let is_and = lv[registers::IS_AND];
let is_or = lv[registers::IS_OR];
let is_xor = lv[registers::IS_XOR];
// The result will be `in0 OP in1 = sum_coeff * (in0 + in1) + and_coeff * (in0 AND in1)`.
// `AND => sum_coeff = 0, and_coeff = 1`
@ -208,7 +208,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for LogicStark<F,
};
// Ensure that all bits are indeed bits.
for input_bits_cols in [columns::INPUT0, columns::INPUT1] {
for input_bits_cols in [registers::INPUT0, registers::INPUT1] {
for i in input_bits_cols {
let bit = lv[i];
let constr = builder.mul_sub_extension(bit, bit, bit);
@ -218,9 +218,9 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for LogicStark<F,
// Form the result
for (result_col, x_bits_cols, y_bits_cols) in izip!(
columns::RESULT,
columns::limb_bit_cols_for_input(columns::INPUT0),
columns::limb_bit_cols_for_input(columns::INPUT1),
registers::RESULT,
registers::limb_bit_cols_for_input(registers::INPUT0),
registers::limb_bit_cols_for_input(registers::INPUT1),
) {
let x = limb_from_bits_le_recursive(builder, x_bits_cols.clone().map(|i| lv[i]));
let y = limb_from_bits_le_recursive(builder, y_bits_cols.clone().map(|i| lv[i]));