From 38a3b73dd7c8042e3c1110d3d8c5564518fa9a72 Mon Sep 17 00:00:00 2001 From: MauroFab Date: Mon, 22 Jun 2026 18:05:27 -0300 Subject: [PATCH 1/2] refactor(prover): centralize u64 limb decomposition into dword_wl/dword_hl Replace the open-coded little-endian limb decomposition repeated across ~20 trace tables with two helpers in tables::types: dword_wl(x) -> [FE; 2] 2x32-bit limbs (DWordWL) dword_hl(x) -> [FE; 4] 4x16-bit limbs (DWordHL) Kept as plain prover functions rather than methods on the generic Table: the limb width is field-size-specific (a 32-bit limb only fits because Goldilocks is ~64-bit, since FE::from reduces mod p), so this logic belongs in the prover, not the field-generic storage type. Only consecutive-column FE writes were converted (consecutiveness checked in each cols module). Parametric column indices, DWordWHH/byte/nibble groupings, sign-extended arrays, and bus-value arrays were left as-is. Byte-identical: prover lib tests pass 416 (the 5 ecsm failures are pre-existing/environmental, identical on main). fmt + clippy clean. Supersedes #693 (1/2). --- prover/src/tables/branch.rs | 29 ++++++-------- prover/src/tables/commit.rs | 35 +++++++++-------- prover/src/tables/cpu.rs | 39 +++++++++++-------- prover/src/tables/cpu32.rs | 42 ++++++++++++-------- prover/src/tables/decode.rs | 62 ++++++++++++++++++------------ prover/src/tables/dvrm.rs | 57 +++++++++++++++------------ prover/src/tables/ec_scalar.rs | 12 +++--- prover/src/tables/ecdas.rs | 7 ++-- prover/src/tables/eq.rs | 21 +++++----- prover/src/tables/keccak.rs | 16 ++++---- prover/src/tables/keccak_rnd.rs | 7 ++-- prover/src/tables/load.rs | 12 +++--- prover/src/tables/lt.rs | 15 +++----- prover/src/tables/memw.rs | 12 +++--- prover/src/tables/memw_aligned.rs | 12 +++--- prover/src/tables/memw_register.rs | 7 ++-- prover/src/tables/mul.rs | 38 ++++++++++-------- prover/src/tables/page.rs | 7 ++-- prover/src/tables/register.rs | 7 ++-- prover/src/tables/store.rs | 12 +++--- prover/src/tables/types.rs | 22 +++++++++++ 21 files changed, 274 insertions(+), 197 deletions(-) diff --git a/prover/src/tables/branch.rs b/prover/src/tables/branch.rs index a71e16435..14b64ab40 100644 --- a/prover/src/tables/branch.rs +++ b/prover/src/tables/branch.rs @@ -33,7 +33,7 @@ use stark::lookup::{BusInteraction, BusValue, LinearTerm, Multiplicity, Packing} use stark::table::TableView; use stark::trace::TraceTable; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, SHIFT_16, alu_op}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, SHIFT_16, alu_op, dword_wl}; // ========================================================================= // Column indices for BRANCH table @@ -173,17 +173,10 @@ pub fn generate_branch_trace( for (row_idx, (op, multiplicity)) in unique_ops.iter().enumerate() { let base = row_idx * cols::NUM_COLUMNS; - // Extract pc as DWordWL: [Word, Word] - let pc_0 = (op.pc & 0xFFFF_FFFF) as u32; - let pc_1 = (op.pc >> 32) as u32; - - // Extract offset as DWordWL: [Word, Word] - let offset_0 = (op.offset & 0xFFFF_FFFF) as u32; - let offset_1 = (op.offset >> 32) as u32; - - // Extract register as DWordWL: [Word, Word] - let register_0 = (op.register & 0xFFFF_FFFF) as u32; - let register_1 = (op.register >> 32) as u32; + // Extract pc, offset, register as DWordWL: [Word, Word] + let [pc_0, pc_1] = dword_wl(op.pc); + let [offset_0, offset_1] = dword_wl(op.offset); + let [register_0, register_1] = dword_wl(op.register); // Compute next_pc let next_pc_unmasked = op.compute_next_pc_unmasked(); @@ -203,12 +196,12 @@ pub fn generate_branch_trace( let next_pc_high_2 = ((next_pc >> 48) & 0xFFFF) as u16; // Store columns - data[base + cols::PC_0] = FE::from(pc_0 as u64); - data[base + cols::PC_1] = FE::from(pc_1 as u64); - data[base + cols::OFFSET_0] = FE::from(offset_0 as u64); - data[base + cols::OFFSET_1] = FE::from(offset_1 as u64); - data[base + cols::REGISTER_0] = FE::from(register_0 as u64); - data[base + cols::REGISTER_1] = FE::from(register_1 as u64); + data[base + cols::PC_0] = pc_0; + data[base + cols::PC_1] = pc_1; + data[base + cols::OFFSET_0] = offset_0; + data[base + cols::OFFSET_1] = offset_1; + data[base + cols::REGISTER_0] = register_0; + data[base + cols::REGISTER_1] = register_1; data[base + cols::JALR] = FE::from(if op.jalr { 1u64 } else { 0u64 }); data[base + cols::NEXT_PC_HIGH_0] = FE::from(next_pc_high_0 as u64); data[base + cols::NEXT_PC_HIGH_1] = FE::from(next_pc_high_1 as u64); diff --git a/prover/src/tables/commit.rs b/prover/src/tables/commit.rs index 8c979b664..2929db47b 100644 --- a/prover/src/tables/commit.rs +++ b/prover/src/tables/commit.rs @@ -52,7 +52,7 @@ use stark::trace::TraceTable; use crate::constraints::templates::{AddConstraint, AddOperand}; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, dword_hl, dword_wl}; // ========================================================================= // Column indices for COMMIT table @@ -170,26 +170,30 @@ pub fn generate_commit_trace( let base = row_idx * cols::NUM_COLUMNS; // Timestamp (DWordWL) - data[base + cols::TIMESTAMP_0] = FE::from(op.timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_1] = FE::from(op.timestamp >> 32); + let [ts0, ts1] = dword_wl(op.timestamp); + data[base + cols::TIMESTAMP_0] = ts0; + data[base + cols::TIMESTAMP_1] = ts1; // Index (BaseField) data[base + cols::INDEX] = FE::from(op.index); // Address (DWordWL) - data[base + cols::ADDRESS_0] = FE::from(op.address & 0xFFFF_FFFF); - data[base + cols::ADDRESS_1] = FE::from(op.address >> 32); + let [addr0, addr1] = dword_wl(op.address); + data[base + cols::ADDRESS_0] = addr0; + data[base + cols::ADDRESS_1] = addr1; // address_incr = address + 1 (DWordHL: 4 halfwords) let address_incr = op.address.wrapping_add(1); - data[base + cols::ADDRESS_INCR_0] = FE::from(address_incr & 0xFFFF); - data[base + cols::ADDRESS_INCR_1] = FE::from((address_incr >> 16) & 0xFFFF); - data[base + cols::ADDRESS_INCR_2] = FE::from((address_incr >> 32) & 0xFFFF); - data[base + cols::ADDRESS_INCR_3] = FE::from((address_incr >> 48) & 0xFFFF); + let [ai0, ai1, ai2, ai3] = dword_hl(address_incr); + data[base + cols::ADDRESS_INCR_0] = ai0; + data[base + cols::ADDRESS_INCR_1] = ai1; + data[base + cols::ADDRESS_INCR_2] = ai2; + data[base + cols::ADDRESS_INCR_3] = ai3; // Count (DWordWL) - data[base + cols::COUNT_0] = FE::from(op.count & 0xFFFF_FFFF); - data[base + cols::COUNT_1] = FE::from(op.count >> 32); + let [cnt0, cnt1] = dword_wl(op.count); + data[base + cols::COUNT_0] = cnt0; + data[base + cols::COUNT_1] = cnt1; // count_decr: if count == 0, use 0xFFFF_FFFF_FFFF_FFFF; else count - 1 let count_decr = if op.count == 0 { @@ -197,10 +201,11 @@ pub fn generate_commit_trace( } else { op.count - 1 }; - data[base + cols::COUNT_DECR_0] = FE::from(count_decr & 0xFFFF); - data[base + cols::COUNT_DECR_1] = FE::from((count_decr >> 16) & 0xFFFF); - data[base + cols::COUNT_DECR_2] = FE::from((count_decr >> 32) & 0xFFFF); - data[base + cols::COUNT_DECR_3] = FE::from((count_decr >> 48) & 0xFFFF); + let [cd0, cd1, cd2, cd3] = dword_hl(count_decr); + data[base + cols::COUNT_DECR_0] = cd0; + data[base + cols::COUNT_DECR_1] = cd1; + data[base + cols::COUNT_DECR_2] = cd2; + data[base + cols::COUNT_DECR_3] = cd3; // Control bits data[base + cols::FIRST] = FE::from(op.first as u64); diff --git a/prover/src/tables/cpu.rs b/prover/src/tables/cpu.rs index 450595ec9..f36b8a815 100644 --- a/prover/src/tables/cpu.rs +++ b/prover/src/tables/cpu.rs @@ -24,7 +24,9 @@ //! JALR bit (the memory-width bits are 0), so `mem_flags ∈ {0,1} = JALR` and the //! `mem_flags` column is used directly as `JALR` wherever it is gated by `BRANCH`. -use super::types::{BusId, DecodeEntry, FE, GoldilocksExtension, GoldilocksField, alu_op}; +use super::types::{ + BusId, DecodeEntry, FE, GoldilocksExtension, GoldilocksField, alu_op, dword_wl, +}; use crate::Error; use executor::vm::{ instruction::{decoding::Instruction, execution::SyscallNumbers}, @@ -451,8 +453,9 @@ pub fn generate_cpu_trace( let effective = |flag: bool| (!word && flag) as u64; data[base + cols::TIMESTAMP] = FE::from(op.timestamp); - data[base + cols::PC_0] = FE::from(op.decode.pc & 0xFFFF_FFFF); - data[base + cols::PC_1] = FE::from(op.decode.pc >> 32); + let [pc0, pc1] = dword_wl(op.decode.pc); + data[base + cols::PC_0] = pc0; + data[base + cols::PC_1] = pc1; // rs1/rs2/rd and read/write flags are only present on non-word rows. let (rs1, rs2, rd) = if word { @@ -480,8 +483,9 @@ pub fn generate_cpu_trace( (op.decode.imm, op.rvd, op.rv1, op.rv2, op.arg2, op.res) }; - data[base + cols::IMM_0] = FE::from(imm & 0xFFFF_FFFF); - data[base + cols::IMM_1] = FE::from(imm >> 32); + let [imm0, imm1] = dword_wl(imm); + data[base + cols::IMM_0] = imm0; + data[base + cols::IMM_1] = imm1; data[base + cols::HALF_INSTRUCTION_LENGTH] = FE::from(f.half_instruction_length as u64); data[base + cols::WORD_INSTR] = FE::from(word as u64); @@ -495,19 +499,24 @@ pub fn generate_cpu_trace( data[base + cols::BRANCH] = FE::from(effective(f.branch)); data[base + cols::ECALL] = FE::from(effective(f.ecall)); - data[base + cols::NEXT_PC_0] = FE::from(op.next_pc & 0xFFFF_FFFF); - data[base + cols::NEXT_PC_1] = FE::from(op.next_pc >> 32); + let [npc0, npc1] = dword_wl(op.next_pc); + data[base + cols::NEXT_PC_0] = npc0; + data[base + cols::NEXT_PC_1] = npc1; - data[base + cols::RVD_0] = FE::from(rvd & 0xFFFF_FFFF); - data[base + cols::RVD_1] = FE::from(rvd >> 32); + let [rvd0, rvd1] = dword_wl(rvd); + data[base + cols::RVD_0] = rvd0; + data[base + cols::RVD_1] = rvd1; // rv1/rv2/arg2 as DWordWL (2 × 32-bit words). - data[base + cols::RV1_0] = FE::from(rv1 & 0xFFFF_FFFF); - data[base + cols::RV1_1] = FE::from(rv1 >> 32); - data[base + cols::RV2_0] = FE::from(rv2 & 0xFFFF_FFFF); - data[base + cols::RV2_1] = FE::from(rv2 >> 32); - data[base + cols::ARG2_0] = FE::from(arg2 & 0xFFFF_FFFF); - data[base + cols::ARG2_1] = FE::from(arg2 >> 32); + let [rv1_0, rv1_1] = dword_wl(rv1); + data[base + cols::RV1_0] = rv1_0; + data[base + cols::RV1_1] = rv1_1; + let [rv2_0, rv2_1] = dword_wl(rv2); + data[base + cols::RV2_0] = rv2_0; + data[base + cols::RV2_1] = rv2_1; + let [arg2_0, arg2_1] = dword_wl(arg2); + data[base + cols::ARG2_0] = arg2_0; + data[base + cols::ARG2_1] = arg2_1; // res as DWordHL (4 × 16-bit halves). for i in 0..4 { diff --git a/prover/src/tables/cpu32.rs b/prover/src/tables/cpu32.rs index 2aa9c87a3..c0400b15b 100644 --- a/prover/src/tables/cpu32.rs +++ b/prover/src/tables/cpu32.rs @@ -25,7 +25,8 @@ use stark::table::TableView; use stark::trace::TraceTable; use super::types::{ - BusId, FE, GoldilocksExtension, GoldilocksField, SHIFT_16, alu_op, packed_decode_shrunk, + BusId, FE, GoldilocksExtension, GoldilocksField, SHIFT_16, alu_op, dword_hl, dword_wl, + packed_decode_shrunk, }; use crate::constraints::templates::{AddConstraint, AddOperand, new_is_bit_constraints}; @@ -204,10 +205,12 @@ pub fn generate_cpu32_trace( let aux = op.compute_aux(); // Inputs - data[base + cols::TIMESTAMP_0] = FE::from(op.timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_1] = FE::from(op.timestamp >> 32); - data[base + cols::PC_0] = FE::from(op.pc & 0xFFFF_FFFF); - data[base + cols::PC_1] = FE::from(op.pc >> 32); + let [ts0, ts1] = dword_wl(op.timestamp); + data[base + cols::TIMESTAMP_0] = ts0; + data[base + cols::TIMESTAMP_1] = ts1; + let [pc0, pc1] = dword_wl(op.pc); + data[base + cols::PC_0] = pc0; + data[base + cols::PC_1] = pc1; // rv1 as DWordWHH: [Half, Half, Word] data[base + cols::RS1] = FE::from(op.rs1 as u64); @@ -216,8 +219,9 @@ pub fn generate_cpu32_trace( data[base + cols::RV1_1] = FE::from((op.rv1 >> 16) & 0xFFFF); data[base + cols::RV1_2] = FE::from(op.rv1 >> 32); data[base + cols::RV1_SIGN] = FE::from(aux.rv1_sign as u64); - data[base + cols::ARG1_0] = FE::from(aux.arg1 & 0xFFFF_FFFF); - data[base + cols::ARG1_1] = FE::from(aux.arg1 >> 32); + let [arg1_0, arg1_1] = dword_wl(aux.arg1); + data[base + cols::ARG1_0] = arg1_0; + data[base + cols::ARG1_1] = arg1_1; // rv2 as DWordWHH data[base + cols::RS2] = FE::from(op.rs2 as u64); @@ -226,23 +230,27 @@ pub fn generate_cpu32_trace( data[base + cols::RV2_1] = FE::from((op.rv2 >> 16) & 0xFFFF); data[base + cols::RV2_2] = FE::from(op.rv2 >> 32); data[base + cols::RV2_SIGN] = FE::from(aux.rv2_sign as u64); - data[base + cols::IMM_0] = FE::from(op.imm & 0xFFFF_FFFF); - data[base + cols::IMM_1] = FE::from(op.imm >> 32); - data[base + cols::ARG2_0] = FE::from(aux.arg2 & 0xFFFF_FFFF); - data[base + cols::ARG2_1] = FE::from(aux.arg2 >> 32); + let [imm0, imm1] = dword_wl(op.imm); + data[base + cols::IMM_0] = imm0; + data[base + cols::IMM_1] = imm1; + let [arg2_0, arg2_1] = dword_wl(aux.arg2); + data[base + cols::ARG2_0] = arg2_0; + data[base + cols::ARG2_1] = arg2_1; // res as DWordHL: 4 halves - data[base + cols::RES_0] = FE::from(op.res & 0xFFFF); - data[base + cols::RES_1] = FE::from((op.res >> 16) & 0xFFFF); - data[base + cols::RES_2] = FE::from((op.res >> 32) & 0xFFFF); - data[base + cols::RES_3] = FE::from((op.res >> 48) & 0xFFFF); + let [res0, res1, res2, res3] = dword_hl(op.res); + data[base + cols::RES_0] = res0; + data[base + cols::RES_1] = res1; + data[base + cols::RES_2] = res2; + data[base + cols::RES_3] = res3; data[base + cols::RES_SIGN] = FE::from(aux.res_sign as u64); // rd write data[base + cols::RD] = FE::from(op.rd as u64); data[base + cols::WRITE_REGISTER] = FE::from(op.write_register as u64); - data[base + cols::RVD_0] = FE::from(aux.rvd & 0xFFFF_FFFF); - data[base + cols::RVD_1] = FE::from(aux.rvd >> 32); + let [rvd0, rvd1] = dword_wl(aux.rvd); + data[base + cols::RVD_0] = rvd0; + data[base + cols::RVD_1] = rvd1; // ALU control data[base + cols::ALU] = FE::from(op.alu as u64); diff --git a/prover/src/tables/decode.rs b/prover/src/tables/decode.rs index f1fe14e03..142d29a09 100644 --- a/prover/src/tables/decode.rs +++ b/prover/src/tables/decode.rs @@ -42,7 +42,7 @@ use stark::proof::options::ProofOptions; use stark::prover::evaluate_polynomial_on_lde_domain; use stark::trace::{TraceTable, columns2rows}; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, dword_wl}; // Re-export DecodeEntry from types for backwards compatibility pub use super::types::DecodeEntry; @@ -135,15 +135,17 @@ pub fn generate_decode_trace( let base = row_idx * cols::NUM_COLUMNS; // PC as DWordWL - data[base + cols::PC_0] = FE::from(entry.pc & 0xFFFF_FFFF); - data[base + cols::PC_1] = FE::from(entry.pc >> 32); + let [pc0, pc1] = dword_wl(entry.pc); + data[base + cols::PC_0] = pc0; + data[base + cols::PC_1] = pc1; // packed_decode data[base + cols::PACKED_DECODE] = FE::from(entry.packed_decode()); // imm as DWordWL - data[base + cols::IMM_0] = FE::from(entry.imm & 0xFFFF_FFFF); - data[base + cols::IMM_1] = FE::from(entry.imm >> 32); + let [imm0, imm1] = dword_wl(entry.imm); + data[base + cols::IMM_0] = imm0; + data[base + cols::IMM_1] = imm1; // MU = 0 (already zero from vec initialization) } @@ -151,24 +153,28 @@ pub fn generate_decode_trace( // Write CPU padding entry (pc=1, all flags=0) { let base = cpu_padding_row * cols::NUM_COLUMNS; - data[base + cols::PC_0] = FE::from(cpu_padding_entry.pc & 0xFFFF_FFFF); - data[base + cols::PC_1] = FE::from(cpu_padding_entry.pc >> 32); + let [pc0, pc1] = dword_wl(cpu_padding_entry.pc); + data[base + cols::PC_0] = pc0; + data[base + cols::PC_1] = pc1; data[base + cols::PACKED_DECODE] = FE::from(cpu_padding_entry.packed_decode()); - data[base + cols::IMM_0] = FE::from(cpu_padding_entry.imm & 0xFFFF_FFFF); - data[base + cols::IMM_1] = FE::from(cpu_padding_entry.imm >> 32); + let [imm0, imm1] = dword_wl(cpu_padding_entry.imm); + data[base + cols::IMM_0] = imm0; + data[base + cols::IMM_1] = imm1; } // Fill padding rows with the DECODE padding pattern: odd pc=1, all flags 0 // (unprovable as a fetch target; same row the CPU pads to). let padding_entry = DecodeEntry::padding_entry(); + let [pad_pc0, pad_pc1] = dword_wl(padding_entry.pc); + let [pad_imm0, pad_imm1] = dword_wl(padding_entry.imm); for row_idx in num_entries..num_rows { let base = row_idx * cols::NUM_COLUMNS; - data[base + cols::PC_0] = FE::from(padding_entry.pc & 0xFFFF_FFFF); - data[base + cols::PC_1] = FE::from(padding_entry.pc >> 32); + data[base + cols::PC_0] = pad_pc0; + data[base + cols::PC_1] = pad_pc1; data[base + cols::PACKED_DECODE] = FE::from(padding_entry.packed_decode()); - data[base + cols::IMM_0] = FE::from(padding_entry.imm & 0xFFFF_FFFF); - data[base + cols::IMM_1] = FE::from(padding_entry.imm >> 32); + data[base + cols::IMM_0] = pad_imm0; + data[base + cols::IMM_1] = pad_imm1; // MU = 0 for padding rows (already zero from vec initialization) } @@ -407,32 +413,38 @@ fn build_decode_table( // Fill actual entries for (row_idx, entry) in entries.iter().enumerate() { let base = row_idx * cols::NUM_COLUMNS; - data[base + cols::PC_0] = FE::from(entry.pc & 0xFFFF_FFFF); - data[base + cols::PC_1] = FE::from(entry.pc >> 32); + let [pc0, pc1] = dword_wl(entry.pc); + data[base + cols::PC_0] = pc0; + data[base + cols::PC_1] = pc1; data[base + cols::PACKED_DECODE] = FE::from(entry.packed_decode()); - data[base + cols::IMM_0] = FE::from(entry.imm & 0xFFFF_FFFF); - data[base + cols::IMM_1] = FE::from(entry.imm >> 32); + let [imm0, imm1] = dword_wl(entry.imm); + data[base + cols::IMM_0] = imm0; + data[base + cols::IMM_1] = imm1; } // Write CPU padding entry { let base = cpu_padding_row * cols::NUM_COLUMNS; - data[base + cols::PC_0] = FE::from(cpu_padding_entry.pc & 0xFFFF_FFFF); - data[base + cols::PC_1] = FE::from(cpu_padding_entry.pc >> 32); + let [pc0, pc1] = dword_wl(cpu_padding_entry.pc); + data[base + cols::PC_0] = pc0; + data[base + cols::PC_1] = pc1; data[base + cols::PACKED_DECODE] = FE::from(cpu_padding_entry.packed_decode()); - data[base + cols::IMM_0] = FE::from(cpu_padding_entry.imm & 0xFFFF_FFFF); - data[base + cols::IMM_1] = FE::from(cpu_padding_entry.imm >> 32); + let [imm0, imm1] = dword_wl(cpu_padding_entry.imm); + data[base + cols::IMM_0] = imm0; + data[base + cols::IMM_1] = imm1; } // Fill padding rows with DECODE padding pattern let padding_entry = DecodeEntry::padding_entry(); + let [pad_pc0, pad_pc1] = dword_wl(padding_entry.pc); + let [pad_imm0, pad_imm1] = dword_wl(padding_entry.imm); for row_idx in num_entries..num_rows { let base = row_idx * cols::NUM_COLUMNS; - data[base + cols::PC_0] = FE::from(padding_entry.pc & 0xFFFF_FFFF); - data[base + cols::PC_1] = FE::from(padding_entry.pc >> 32); + data[base + cols::PC_0] = pad_pc0; + data[base + cols::PC_1] = pad_pc1; data[base + cols::PACKED_DECODE] = FE::from(padding_entry.packed_decode()); - data[base + cols::IMM_0] = FE::from(padding_entry.imm & 0xFFFF_FFFF); - data[base + cols::IMM_1] = FE::from(padding_entry.imm >> 32); + data[base + cols::IMM_0] = pad_imm0; + data[base + cols::IMM_1] = pad_imm1; } TraceTable::new_main(data, cols::NUM_COLUMNS, 1) diff --git a/prover/src/tables/dvrm.rs b/prover/src/tables/dvrm.rs index b74416010..2b3da5d58 100644 --- a/prover/src/tables/dvrm.rs +++ b/prover/src/tables/dvrm.rs @@ -40,7 +40,7 @@ use stark::trace::TraceTable; use super::types::{ BusId, FE, GoldilocksExtension, GoldilocksField, NEG_INV_2_16, NEG_INV_2_32, NEG_INV_2_48, - NEG_INV_2_64, SHIFT_16, alu_op, + NEG_INV_2_64, SHIFT_16, alu_op, dword_hl, dword_wl, }; // ========================================================================= @@ -313,46 +313,53 @@ pub fn generate_dvrm_trace( let abs_d = op.abs_d(); // Fill n as DWordHL (4 halfwords) - data[base + cols::N_0] = FE::from(op.n & 0xFFFF); - data[base + cols::N_1] = FE::from((op.n >> 16) & 0xFFFF); - data[base + cols::N_2] = FE::from((op.n >> 32) & 0xFFFF); - data[base + cols::N_3] = FE::from((op.n >> 48) & 0xFFFF); + let [n0, n1, n2, n3] = dword_hl(op.n); + data[base + cols::N_0] = n0; + data[base + cols::N_1] = n1; + data[base + cols::N_2] = n2; + data[base + cols::N_3] = n3; // Fill d as DWordHL (4 halfwords) - data[base + cols::D_0] = FE::from(op.d & 0xFFFF); - data[base + cols::D_1] = FE::from((op.d >> 16) & 0xFFFF); - data[base + cols::D_2] = FE::from((op.d >> 32) & 0xFFFF); - data[base + cols::D_3] = FE::from((op.d >> 48) & 0xFFFF); + let [d0, d1, d2, d3] = dword_hl(op.d); + data[base + cols::D_0] = d0; + data[base + cols::D_1] = d1; + data[base + cols::D_2] = d2; + data[base + cols::D_3] = d3; data[base + cols::SIGNED] = FE::from(op.signed as u64); // Fill q as DWordHL (4 halfwords) - data[base + cols::Q_0] = FE::from(q & 0xFFFF); - data[base + cols::Q_1] = FE::from((q >> 16) & 0xFFFF); - data[base + cols::Q_2] = FE::from((q >> 32) & 0xFFFF); - data[base + cols::Q_3] = FE::from((q >> 48) & 0xFFFF); + let [q0, q1, q2, q3] = dword_hl(q); + data[base + cols::Q_0] = q0; + data[base + cols::Q_1] = q1; + data[base + cols::Q_2] = q2; + data[base + cols::Q_3] = q3; // Fill r as DWordHL (4 halfwords) - data[base + cols::R_0] = FE::from(r & 0xFFFF); - data[base + cols::R_1] = FE::from((r >> 16) & 0xFFFF); - data[base + cols::R_2] = FE::from((r >> 32) & 0xFFFF); - data[base + cols::R_3] = FE::from((r >> 48) & 0xFFFF); + let [r0, r1, r2, r3] = dword_hl(r); + data[base + cols::R_0] = r0; + data[base + cols::R_1] = r1; + data[base + cols::R_2] = r2; + data[base + cols::R_3] = r3; // Fill auxiliary columns data[base + cols::DIV_BY_ZERO] = FE::from(op.is_div_by_zero() as u64); data[base + cols::OVERFLOW] = FE::from(op.is_overflow() as u64); - data[base + cols::ABS_R_0] = FE::from(abs_r & 0xFFFF_FFFF); - data[base + cols::ABS_R_1] = FE::from(abs_r >> 32); + let [abs_r0, abs_r1] = dword_wl(abs_r); + data[base + cols::ABS_R_0] = abs_r0; + data[base + cols::ABS_R_1] = abs_r1; - data[base + cols::ABS_D_0] = FE::from(abs_d & 0xFFFF_FFFF); - data[base + cols::ABS_D_1] = FE::from(abs_d >> 32); + let [abs_d0, abs_d1] = dword_wl(abs_d); + data[base + cols::ABS_D_0] = abs_d0; + data[base + cols::ABS_D_1] = abs_d1; // Fill n_sub_r as DWordHL (4 halfwords) - data[base + cols::N_SUB_R_0] = FE::from(n_sub_r & 0xFFFF); - data[base + cols::N_SUB_R_1] = FE::from((n_sub_r >> 16) & 0xFFFF); - data[base + cols::N_SUB_R_2] = FE::from((n_sub_r >> 32) & 0xFFFF); - data[base + cols::N_SUB_R_3] = FE::from((n_sub_r >> 48) & 0xFFFF); + let [nsr0, nsr1, nsr2, nsr3] = dword_hl(n_sub_r); + data[base + cols::N_SUB_R_0] = nsr0; + data[base + cols::N_SUB_R_1] = nsr1; + data[base + cols::N_SUB_R_2] = nsr2; + data[base + cols::N_SUB_R_3] = nsr3; data[base + cols::SIGN_N_SUB_R] = FE::from(op.sign_n_sub_r() as u64); data[base + cols::SIGN_N] = FE::from(op.sign_n() as u64); diff --git a/prover/src/tables/ec_scalar.rs b/prover/src/tables/ec_scalar.rs index 9ec20377d..f9e449f98 100644 --- a/prover/src/tables/ec_scalar.rs +++ b/prover/src/tables/ec_scalar.rs @@ -23,7 +23,7 @@ use stark::lookup::{BusInteraction, BusValue, LinearTerm, Multiplicity, Packing} use stark::table::TableView; use stark::trace::TraceTable; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, dword_wl}; use crate::constraints::templates::new_is_bit_constraints; // ========================================================================= @@ -91,10 +91,12 @@ pub fn generate_ec_scalar_trace( for (row_idx, op) in ops.iter().enumerate() { let base = row_idx * cols::NUM_COLUMNS; - data[base + cols::TIMESTAMP_0] = FE::from(op.timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_1] = FE::from(op.timestamp >> 32); - data[base + cols::PTR_0] = FE::from(op.ptr & 0xFFFF_FFFF); - data[base + cols::PTR_1] = FE::from(op.ptr >> 32); + let [ts0, ts1] = dword_wl(op.timestamp); + data[base + cols::TIMESTAMP_0] = ts0; + data[base + cols::TIMESTAMP_1] = ts1; + let [p0, p1] = dword_wl(op.ptr); + data[base + cols::PTR_0] = p0; + data[base + cols::PTR_1] = p1; data[base + cols::OFFSET] = FE::from(op.offset as u64); for i in 0..8 { data[base + cols::limb_bit(i)] = FE::from(((op.limb >> i) & 1) as u64); diff --git a/prover/src/tables/ecdas.rs b/prover/src/tables/ecdas.rs index 059245073..0613bf563 100644 --- a/prover/src/tables/ecdas.rs +++ b/prover/src/tables/ecdas.rs @@ -17,7 +17,7 @@ use stark::lookup::{BusInteraction, BusValue, LinearTerm, Multiplicity, Packing} use stark::table::TableView; use stark::trace::TraceTable; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, dword_wl}; use crate::constraints::templates::IsBitConstraint; use crate::tables::ecsm::ecdas_tuple; use ecsm::{EcdasStep, P_BYTES}; @@ -110,8 +110,9 @@ pub fn generate_ecdas_trace( let base = row_idx * cols::NUM_COLUMNS; let s = &op.step; - data[base + cols::TIMESTAMP_0] = FE::from(op.timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_1] = FE::from(op.timestamp >> 32); + let [ts0, ts1] = dword_wl(op.timestamp); + data[base + cols::TIMESTAMP_0] = ts0; + data[base + cols::TIMESTAMP_1] = ts1; write_bytes(&mut data, base, cols::XG, &s.x_g); write_bytes(&mut data, base, cols::YG, &s.y_g); write_bytes(&mut data, base, cols::XA, &s.x_a); diff --git a/prover/src/tables/eq.rs b/prover/src/tables/eq.rs index f60ed2e58..27fc69a71 100644 --- a/prover/src/tables/eq.rs +++ b/prover/src/tables/eq.rs @@ -28,7 +28,7 @@ use stark::lookup::{BusInteraction, BusValue, LinearTerm, Multiplicity, Packing} use stark::table::TableView; use stark::trace::TraceTable; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, alu_op}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, alu_op, dword_hl, dword_wl}; use crate::constraints::templates::{AddConstraint, AddOperand, new_is_bit_constraints}; // ========================================================================= @@ -135,20 +135,23 @@ pub fn generate_eq_trace( let base = row_idx * cols::NUM_COLUMNS; // a, b as DWordWL (2 words each) - data[base + cols::A_0] = FE::from(op.a & 0xFFFF_FFFF); - data[base + cols::A_1] = FE::from(op.a >> 32); - data[base + cols::B_0] = FE::from(op.b & 0xFFFF_FFFF); - data[base + cols::B_1] = FE::from(op.b >> 32); + let [a0, a1] = dword_wl(op.a); + data[base + cols::A_0] = a0; + data[base + cols::A_1] = a1; + let [b0, b1] = dword_wl(op.b); + data[base + cols::B_0] = b0; + data[base + cols::B_1] = b1; data[base + cols::INVERT] = FE::from(op.invert as u64); data[base + cols::RES] = FE::from(op.compute_res() as u64); // diff = a - b (wrapping) as DWordHL (4 halves) let diff = op.a.wrapping_sub(op.b); - data[base + cols::DIFF_0] = FE::from(diff & 0xFFFF); - data[base + cols::DIFF_1] = FE::from((diff >> 16) & 0xFFFF); - data[base + cols::DIFF_2] = FE::from((diff >> 32) & 0xFFFF); - data[base + cols::DIFF_3] = FE::from((diff >> 48) & 0xFFFF); + let [d0, d1, d2, d3] = dword_hl(diff); + data[base + cols::DIFF_0] = d0; + data[base + cols::DIFF_1] = d1; + data[base + cols::DIFF_2] = d2; + data[base + cols::DIFF_3] = d3; data[base + cols::EQ] = FE::from(op.compute_eq() as u64); data[base + cols::MU] = FE::from(*multiplicity); diff --git a/prover/src/tables/keccak.rs b/prover/src/tables/keccak.rs index 0eaf3c6b2..1473a90e0 100644 --- a/prover/src/tables/keccak.rs +++ b/prover/src/tables/keccak.rs @@ -23,7 +23,7 @@ use stark::lookup::{BusInteraction, BusValue, LinearTerm, Multiplicity, Packing} use stark::table::TableView; use stark::trace::TraceTable; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, alu_op}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, alu_op, dword_hl, dword_wl}; use crate::constraints::templates::{AddConstraint, AddOperand, INV_SHIFT_32}; // ========================================================================= @@ -109,8 +109,9 @@ pub fn generate_keccak_trace( let base = row_idx * cols::NUM_COLUMNS; // Timestamp - data[base + cols::TIMESTAMP_0] = FE::from(op.timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_1] = FE::from(op.timestamp >> 32); + let [ts0, ts1] = dword_wl(op.timestamp); + data[base + cols::TIMESTAMP_0] = ts0; + data[base + cols::TIMESTAMP_1] = ts1; // Address as 8 bytes for b in 0..8 { @@ -143,10 +144,11 @@ pub fn generate_keccak_trace( .state_addr .checked_add(lane_idx as u64 * 8) .expect("keccak state address range must be validated by the executor"); - data[base + cols::state_ptr(lane_idx, 0)] = FE::from(ptr & 0xFFFF); - data[base + cols::state_ptr(lane_idx, 1)] = FE::from((ptr >> 16) & 0xFFFF); - data[base + cols::state_ptr(lane_idx, 2)] = FE::from((ptr >> 32) & 0xFFFF); - data[base + cols::state_ptr(lane_idx, 3)] = FE::from((ptr >> 48) & 0xFFFF); + let [p0, p1, p2, p3] = dword_hl(ptr); + data[base + cols::state_ptr(lane_idx, 0)] = p0; + data[base + cols::state_ptr(lane_idx, 1)] = p1; + data[base + cols::state_ptr(lane_idx, 2)] = p2; + data[base + cols::state_ptr(lane_idx, 3)] = p3; } // mu = 1 (real row) diff --git a/prover/src/tables/keccak_rnd.rs b/prover/src/tables/keccak_rnd.rs index 3e9b9815b..b6f0fe553 100644 --- a/prover/src/tables/keccak_rnd.rs +++ b/prover/src/tables/keccak_rnd.rs @@ -33,7 +33,7 @@ use stark::constraints::transition::{TransitionConstraint, TransitionConstraintE use stark::lookup::{BusInteraction, BusValue, LinearTerm, Multiplicity, Packing}; use stark::trace::TraceTable; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, alu_op}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, alu_op, dword_wl}; // ========================================================================= // Column indices @@ -254,8 +254,9 @@ pub fn generate_keccak_rnd_trace( let base = row_idx * cols::NUM_COLUMNS; // Timestamp & round - data[base + cols::TIMESTAMP_0] = FE::from(op.timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_1] = FE::from(op.timestamp >> 32); + let [ts0, ts1] = dword_wl(op.timestamp); + data[base + cols::TIMESTAMP_0] = ts0; + data[base + cols::TIMESTAMP_1] = ts1; data[base + cols::ROUND] = FE::from(round as u64); // start = current state as bytes diff --git a/prover/src/tables/load.rs b/prover/src/tables/load.rs index 8795a6494..ef64689df 100644 --- a/prover/src/tables/load.rs +++ b/prover/src/tables/load.rs @@ -30,7 +30,7 @@ use stark::lookup::{BusInteraction, BusValue, LinearTerm, Multiplicity, Packing} use stark::table::TableView; use stark::trace::TraceTable; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, dword_wl}; // ========================================================================= // Column indices for LOAD table @@ -190,12 +190,14 @@ pub fn generate_load_trace( // Input columns // base_address as DWordWL (2 words) - data[base + cols::BASE_ADDRESS_0] = FE::from(op.base_address & 0xFFFF_FFFF); - data[base + cols::BASE_ADDRESS_1] = FE::from(op.base_address >> 32); + let [ba0, ba1] = dword_wl(op.base_address); + data[base + cols::BASE_ADDRESS_0] = ba0; + data[base + cols::BASE_ADDRESS_1] = ba1; // timestamp as DWordWL (2 words) - data[base + cols::TIMESTAMP_0] = FE::from(op.timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_1] = FE::from(op.timestamp >> 32); + let [ts0, ts1] = dword_wl(op.timestamp); + data[base + cols::TIMESTAMP_0] = ts0; + data[base + cols::TIMESTAMP_1] = ts1; // read flags let (r2, r4, r8) = op.read_flags(); diff --git a/prover/src/tables/lt.rs b/prover/src/tables/lt.rs index 921f6279a..4a4d60b8b 100644 --- a/prover/src/tables/lt.rs +++ b/prover/src/tables/lt.rs @@ -33,7 +33,7 @@ use stark::lookup::{BusInteraction, BusValue, LinearTerm, Multiplicity, Packing} use stark::table::TableView; use stark::trace::TraceTable; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, SHIFT_16, alu_op}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, SHIFT_16, alu_op, dword_hl}; // ========================================================================= // Column indices for LT table @@ -204,14 +204,11 @@ pub fn generate_lt_trace( let lhs_sub_rhs = op.lhs.wrapping_sub(op.rhs); // Store lhs_sub_rhs as DWordHL: [Half, Half, Half, Half] - let sub_0 = (lhs_sub_rhs & 0xFFFF) as u16; - let sub_1 = ((lhs_sub_rhs >> 16) & 0xFFFF) as u16; - let sub_2 = ((lhs_sub_rhs >> 32) & 0xFFFF) as u16; - let sub_3 = ((lhs_sub_rhs >> 48) & 0xFFFF) as u16; - data[base + cols::LHS_SUB_RHS_0] = FE::from(sub_0 as u64); - data[base + cols::LHS_SUB_RHS_1] = FE::from(sub_1 as u64); - data[base + cols::LHS_SUB_RHS_2] = FE::from(sub_2 as u64); - data[base + cols::LHS_SUB_RHS_3] = FE::from(sub_3 as u64); + let [sub0, sub1, sub2, sub3] = dword_hl(lhs_sub_rhs); + data[base + cols::LHS_SUB_RHS_0] = sub0; + data[base + cols::LHS_SUB_RHS_1] = sub1; + data[base + cols::LHS_SUB_RHS_2] = sub2; + data[base + cols::LHS_SUB_RHS_3] = sub3; // Compute MSBs (bit 63 of each value) let lhs_msb = (op.lhs >> 63) & 1; diff --git a/prover/src/tables/memw.rs b/prover/src/tables/memw.rs index 39a02ead4..a1555d806 100644 --- a/prover/src/tables/memw.rs +++ b/prover/src/tables/memw.rs @@ -36,7 +36,7 @@ use stark::lookup::{BusInteraction, BusValue, LinearTerm, Multiplicity, Packing} use stark::table::TableView; use stark::trace::TraceTable; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, alu_op}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, alu_op, dword_wl}; use crate::constraints::templates::IsBitConstraint; /// Maximum number of rows per MEMW table chunk. @@ -185,8 +185,9 @@ pub fn generate_memw_trace( // base_address as DWordWL (2 words) let base_addr_lo = op.base_address & 0xFFFF_FFFF; - data[base + cols::BASE_ADDRESS_0] = FE::from(base_addr_lo); - data[base + cols::BASE_ADDRESS_1] = FE::from(op.base_address >> 32); + let [ba0, ba1] = dword_wl(op.base_address); + data[base + cols::BASE_ADDRESS_0] = ba0; + data[base + cols::BASE_ADDRESS_1] = ba1; // value[8] for i in 0..8 { @@ -194,8 +195,9 @@ pub fn generate_memw_trace( } // timestamp as DWordWL (2 words) - data[base + cols::TIMESTAMP_0] = FE::from(op.timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_1] = FE::from(op.timestamp >> 32); + let [ts0, ts1] = dword_wl(op.timestamp); + data[base + cols::TIMESTAMP_0] = ts0; + data[base + cols::TIMESTAMP_1] = ts1; // write flags let (w2, w4, w8) = op.write_flags(); diff --git a/prover/src/tables/memw_aligned.rs b/prover/src/tables/memw_aligned.rs index 91a9e8fd8..6da4b6848 100644 --- a/prover/src/tables/memw_aligned.rs +++ b/prover/src/tables/memw_aligned.rs @@ -42,7 +42,7 @@ use stark::table::TableView; use stark::trace::TraceTable; use super::memw::MemwOperation; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, alu_op}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, alu_op, dword_wl}; use crate::constraints::templates::IsBitConstraint; /// Maximum number of rows per MEMW_A table chunk. @@ -118,8 +118,9 @@ pub fn generate_memw_aligned_trace( data[base + cols::VALUE[i]] = FE::from(op.value[i]); } - data[base + cols::TIMESTAMP_0] = FE::from(op.timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_1] = FE::from(op.timestamp >> 32); + let [ts0, ts1] = dword_wl(op.timestamp); + data[base + cols::TIMESTAMP_0] = ts0; + data[base + cols::TIMESTAMP_1] = ts1; let (w2, w4, w8) = op.write_flags(); data[base + cols::WRITE2] = FE::from(w2 as u64); @@ -131,8 +132,9 @@ pub fn generate_memw_aligned_trace( } // Single old_timestamp (from old_timestamp[0], verified equal for all bytes) - data[base + cols::OLD_TIMESTAMP_0] = FE::from(op.old_timestamp[0] & 0xFFFF_FFFF); - data[base + cols::OLD_TIMESTAMP_1] = FE::from(op.old_timestamp[0] >> 32); + let [ots0, ots1] = dword_wl(op.old_timestamp[0]); + data[base + cols::OLD_TIMESTAMP_0] = ots0; + data[base + cols::OLD_TIMESTAMP_1] = ots1; data[base + cols::MU_READ] = FE::from(op.is_read as u64); data[base + cols::MU_WRITE] = FE::from(!op.is_read as u64); diff --git a/prover/src/tables/memw_register.rs b/prover/src/tables/memw_register.rs index 599fe7ed5..8d62d3a61 100644 --- a/prover/src/tables/memw_register.rs +++ b/prover/src/tables/memw_register.rs @@ -46,7 +46,7 @@ use stark::table::TableView; use stark::trace::TraceTable; use super::memw::MemwOperation; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, dword_wl}; // ========================================================================= // Column indices (10 columns) @@ -119,8 +119,9 @@ pub fn generate_memw_register_trace( data[base + cols::ADDRESS] = FE::from(op.base_address / 2); // Timestamp split into lo/hi 32-bit words - data[base + cols::TIMESTAMP_0] = FE::from(op.timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_1] = FE::from(op.timestamp >> 32); + let [ts0, ts1] = dword_wl(op.timestamp); + data[base + cols::TIMESTAMP_0] = ts0; + data[base + cols::TIMESTAMP_1] = ts1; // Value: registers are DWordWL = 2 words data[base + cols::VAL_0] = FE::from(op.value[0]); diff --git a/prover/src/tables/mul.rs b/prover/src/tables/mul.rs index ac2329ebd..4cd816898 100644 --- a/prover/src/tables/mul.rs +++ b/prover/src/tables/mul.rs @@ -42,7 +42,7 @@ use stark::trace::TraceTable; use super::types::{ BusId, FE, GoldilocksExtension, GoldilocksField, INV_2_32, INV_2_64, INV_2_96, INV_2_128, NEG_INV_2_16, NEG_INV_2_32, NEG_INV_2_48, NEG_INV_2_64, NEG_INV_2_80, NEG_INV_2_96, - NEG_INV_2_112, NEG_INV_2_128, SHIFT_16, alu_op, + NEG_INV_2_112, NEG_INV_2_128, SHIFT_16, alu_op, dword_hl, }; /// Total row multiplicity (`ALU` bus, lo + hi), used by the internal @@ -318,30 +318,34 @@ pub fn generate_mul_trace( let (lo, hi) = op.compute_product(); // Fill lhs as DWordHL (4 halfwords) - data[base + cols::LHS_0] = FE::from(op.lhs & 0xFFFF); - data[base + cols::LHS_1] = FE::from((op.lhs >> 16) & 0xFFFF); - data[base + cols::LHS_2] = FE::from((op.lhs >> 32) & 0xFFFF); - data[base + cols::LHS_3] = FE::from((op.lhs >> 48) & 0xFFFF); + let [lhs0, lhs1, lhs2, lhs3] = dword_hl(op.lhs); + data[base + cols::LHS_0] = lhs0; + data[base + cols::LHS_1] = lhs1; + data[base + cols::LHS_2] = lhs2; + data[base + cols::LHS_3] = lhs3; data[base + cols::LHS_SIGNED] = FE::from(op.lhs_signed as u64); // Fill rhs as DWordHL (4 halfwords) - data[base + cols::RHS_0] = FE::from(op.rhs & 0xFFFF); - data[base + cols::RHS_1] = FE::from((op.rhs >> 16) & 0xFFFF); - data[base + cols::RHS_2] = FE::from((op.rhs >> 32) & 0xFFFF); - data[base + cols::RHS_3] = FE::from((op.rhs >> 48) & 0xFFFF); + let [rhs0, rhs1, rhs2, rhs3] = dword_hl(op.rhs); + data[base + cols::RHS_0] = rhs0; + data[base + cols::RHS_1] = rhs1; + data[base + cols::RHS_2] = rhs2; + data[base + cols::RHS_3] = rhs3; data[base + cols::RHS_SIGNED] = FE::from(op.rhs_signed as u64); // Fill lo as DWordHL (4 halfwords) - data[base + cols::LO_0] = FE::from(lo & 0xFFFF); - data[base + cols::LO_1] = FE::from((lo >> 16) & 0xFFFF); - data[base + cols::LO_2] = FE::from((lo >> 32) & 0xFFFF); - data[base + cols::LO_3] = FE::from((lo >> 48) & 0xFFFF); + let [lo0, lo1, lo2, lo3] = dword_hl(lo); + data[base + cols::LO_0] = lo0; + data[base + cols::LO_1] = lo1; + data[base + cols::LO_2] = lo2; + data[base + cols::LO_3] = lo3; // Fill hi as DWordHL (4 halfwords) - data[base + cols::HI_0] = FE::from(hi & 0xFFFF); - data[base + cols::HI_1] = FE::from((hi >> 16) & 0xFFFF); - data[base + cols::HI_2] = FE::from((hi >> 32) & 0xFFFF); - data[base + cols::HI_3] = FE::from((hi >> 48) & 0xFFFF); + let [hi0, hi1, hi2, hi3] = dword_hl(hi); + data[base + cols::HI_0] = hi0; + data[base + cols::HI_1] = hi1; + data[base + cols::HI_2] = hi2; + data[base + cols::HI_3] = hi3; // Fill auxiliary columns data[base + cols::LHS_IS_NEGATIVE] = FE::from(op.lhs_is_negative() as u64); diff --git a/prover/src/tables/page.rs b/prover/src/tables/page.rs index 3997e8c22..f27e2e12f 100644 --- a/prover/src/tables/page.rs +++ b/prover/src/tables/page.rs @@ -40,7 +40,7 @@ use stark::proof::options::ProofOptions; use stark::prover::evaluate_polynomial_on_lde_domain; use stark::trace::{TraceTable, columns2rows}; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, dword_wl}; // ========================================================================= // Constants @@ -203,8 +203,9 @@ pub fn generate_page_trace( }; data[base + cols::FINI] = FE::from(fini_value as u64); - data[base + cols::TIMESTAMP_LO] = FE::from(timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_HI] = FE::from(timestamp >> 32); + let [ts_lo, ts_hi] = dword_wl(timestamp); + data[base + cols::TIMESTAMP_LO] = ts_lo; + data[base + cols::TIMESTAMP_HI] = ts_hi; } TraceTable::new_main(data, cols::NUM_COLUMNS, 1) diff --git a/prover/src/tables/register.rs b/prover/src/tables/register.rs index 2907c924a..d816bcd05 100644 --- a/prover/src/tables/register.rs +++ b/prover/src/tables/register.rs @@ -29,7 +29,7 @@ use stark::prover::evaluate_polynomial_on_lde_domain; use stark::trace::{TraceTable, columns2rows}; use super::page::STACK_TOP; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, dword_wl}; // ========================================================================= // Constants @@ -170,8 +170,9 @@ pub fn generate_register_trace( }; data[base + cols::FINI] = FE::from(fini_value as u64); - data[base + cols::TIMESTAMP_LO] = FE::from(timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_HI] = FE::from(timestamp >> 32); + let [ts_lo, ts_hi] = dword_wl(timestamp); + data[base + cols::TIMESTAMP_LO] = ts_lo; + data[base + cols::TIMESTAMP_HI] = ts_hi; } // Padding rows (if num_rows > NUM_REGISTER_ADDRESSES): set TIMESTAMP_LO=1 so diff --git a/prover/src/tables/store.rs b/prover/src/tables/store.rs index 7eea3656f..9e9ded2cb 100644 --- a/prover/src/tables/store.rs +++ b/prover/src/tables/store.rs @@ -26,7 +26,7 @@ use stark::lookup::{BusInteraction, BusValue, LinearTerm, Multiplicity, Packing} use stark::table::TableView; use stark::trace::TraceTable; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, dword_wl}; use crate::constraints::templates::new_is_bit_constraints; // ========================================================================= @@ -103,10 +103,12 @@ pub fn generate_store_trace( for (row_idx, op) in operations.iter().enumerate() { let base = row_idx * cols::NUM_COLUMNS; - data[base + cols::BASE_ADDRESS_0] = FE::from(op.base_address & 0xFFFF_FFFF); - data[base + cols::BASE_ADDRESS_1] = FE::from(op.base_address >> 32); - data[base + cols::TIMESTAMP_0] = FE::from(op.timestamp & 0xFFFF_FFFF); - data[base + cols::TIMESTAMP_1] = FE::from(op.timestamp >> 32); + let [ba0, ba1] = dword_wl(op.base_address); + data[base + cols::BASE_ADDRESS_0] = ba0; + data[base + cols::BASE_ADDRESS_1] = ba1; + let [ts0, ts1] = dword_wl(op.timestamp); + data[base + cols::TIMESTAMP_0] = ts0; + data[base + cols::TIMESTAMP_1] = ts1; data[base + cols::WRITE2] = FE::from(op.write2 as u64); data[base + cols::WRITE4] = FE::from(op.write4 as u64); data[base + cols::WRITE8] = FE::from(op.write8 as u64); diff --git a/prover/src/tables/types.rs b/prover/src/tables/types.rs index bc16ce780..04026af7c 100644 --- a/prover/src/tables/types.rs +++ b/prover/src/tables/types.rs @@ -32,6 +32,28 @@ pub type FE = FieldElement; /// Field element in the Goldilocks extension field pub type FEE = FieldElement; +/// Decompose a `u64` into its two little-endian 32-bit limbs as field elements: +/// `[x[0..32], x[32..64]]` (the `DWordWL` column encoding). +/// +/// Lives in the prover (not the generic `Table`) because the decomposition is +/// field-size-specific: a 32-bit limb only fits because Goldilocks is ~64-bit. +#[inline] +pub fn dword_wl(x: u64) -> [FE; 2] { + [FE::from(x & 0xFFFF_FFFF), FE::from(x >> 32)] +} + +/// Decompose a `u64` into its four little-endian 16-bit limbs as field elements: +/// `[x[0..16], x[16..32], x[32..48], x[48..64]]` (the `DWordHL` column encoding). +#[inline] +pub fn dword_hl(x: u64) -> [FE; 4] { + [ + FE::from(x & 0xFFFF), + FE::from((x >> 16) & 0xFFFF), + FE::from((x >> 32) & 0xFFFF), + FE::from((x >> 48) & 0xFFFF), + ] +} + /// Bus identifiers for LogUp interactions between tables. /// /// Each bus connects senders (tables that produce values) with receivers From e20961328554a911945114df4362fdc43adb8b7f Mon Sep 17 00:00:00 2001 From: MauroFab Date: Mon, 22 Jun 2026 18:25:29 -0300 Subject: [PATCH 2/2] refactor(prover): single-source the limb decomposition in remaining sites Address review on #697: route the last few open-coded decompositions through dword_wl/dword_hl so the split is truly single-source: - ecsm::write_dword_wl now calls dword_wl(value) internally (keeps its parametric-column flexibility, drops the duplicated 2x32 split) - cpu res loop -> zip cols::RES with dword_hl(res) - memw old_timestamp loop -> dword_wl(op.old_timestamp[i]) Byte-identical: prover lib tests 416 pass (the 5 ecsm failures are pre-existing/environmental); fmt + clippy clean. --- prover/src/tables/cpu.rs | 6 +++--- prover/src/tables/ecsm.rs | 7 ++++--- prover/src/tables/memw.rs | 5 +++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/prover/src/tables/cpu.rs b/prover/src/tables/cpu.rs index f36b8a815..2e14d01d7 100644 --- a/prover/src/tables/cpu.rs +++ b/prover/src/tables/cpu.rs @@ -25,7 +25,7 @@ //! `mem_flags` column is used directly as `JALR` wherever it is gated by `BRANCH`. use super::types::{ - BusId, DecodeEntry, FE, GoldilocksExtension, GoldilocksField, alu_op, dword_wl, + BusId, DecodeEntry, FE, GoldilocksExtension, GoldilocksField, alu_op, dword_hl, dword_wl, }; use crate::Error; use executor::vm::{ @@ -519,8 +519,8 @@ pub fn generate_cpu_trace( data[base + cols::ARG2_1] = arg2_1; // res as DWordHL (4 × 16-bit halves). - for i in 0..4 { - data[base + cols::RES[i]] = FE::from((res >> (i * 16)) & 0xFFFF); + for (&col, limb) in cols::RES.iter().zip(dword_hl(res)) { + data[base + col] = limb; } data[base + cols::BRANCH_COND] = FE::from(op.branch_cond as u64); diff --git a/prover/src/tables/ecsm.rs b/prover/src/tables/ecsm.rs index eb23998d5..a2d02c052 100644 --- a/prover/src/tables/ecsm.rs +++ b/prover/src/tables/ecsm.rs @@ -25,7 +25,7 @@ use stark::lookup::{BusInteraction, BusValue, LinearTerm, Multiplicity, Packing} use stark::table::TableView; use stark::trace::TraceTable; -use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField}; +use super::types::{BusId, FE, GoldilocksExtension, GoldilocksField, dword_wl}; use crate::constraints::templates::{INV_SHIFT_32, IsBitConstraint}; use ecsm::{B, EcsmWitness, N_BYTES, P_BYTES}; @@ -138,8 +138,9 @@ fn fe_from_i64(c: i64) -> FE { } fn write_dword_wl(data: &mut [FE], base: usize, lo_col: usize, value: u64) { - data[base + lo_col] = FE::from(value & 0xFFFF_FFFF); - data[base + lo_col + 1] = FE::from(value >> 32); + let [lo, hi] = dword_wl(value); + data[base + lo_col] = lo; + data[base + lo_col + 1] = hi; } fn write_bytes(data: &mut [FE], base: usize, col: usize, bytes: &[u8]) { diff --git a/prover/src/tables/memw.rs b/prover/src/tables/memw.rs index a1555d806..2e3b0e9eb 100644 --- a/prover/src/tables/memw.rs +++ b/prover/src/tables/memw.rs @@ -220,8 +220,9 @@ pub fn generate_memw_trace( // Auxiliary: old_timestamp[8] - each as DWordWL (2 words) for i in 0..8 { let cols_i = cols::old_timestamp(i); - data[base + cols_i[0]] = FE::from(op.old_timestamp[i] & 0xFFFF_FFFF); - data[base + cols_i[1]] = FE::from(op.old_timestamp[i] >> 32); + let [lo, hi] = dword_wl(op.old_timestamp[i]); + data[base + cols_i[0]] = lo; + data[base + cols_i[1]] = hi; } // Multiplicity