Skip to content

Commit 98e68ef

Browse files
committed
save work
1 parent 7b42e51 commit 98e68ef

8 files changed

Lines changed: 191 additions & 32 deletions

File tree

prover/src/lib.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ use crate::tables::trace_builder::Traces;
4040
use crate::tables::types::BusId;
4141
use crate::test_utils::{
4242
E, F, VmAir, create_bitwise_air, create_branch_air, create_commit_air, create_cpu_air,
43-
create_decode_air, create_dvrm_air, create_halt_air, create_load_air, create_lt_air,
44-
create_memw_air, create_memw_aligned_air, create_memw_register_air, create_mul_air,
45-
create_page_air, create_register_air, create_shift_air,
43+
create_cpu_bitwise_air, create_decode_air, create_dvrm_air, create_halt_air, create_load_air,
44+
create_lt_air, create_memw_air, create_memw_aligned_air, create_memw_register_air,
45+
create_mul_air, create_page_air, create_register_air, create_shift_air,
4646
};
4747

4848
use stark::proof::options::{GoldilocksCubicProofOptions, ProofOptions};
@@ -65,6 +65,7 @@ pub struct RuntimePageRange {
6565
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
6666
pub struct TableCounts {
6767
pub cpu: usize,
68+
pub cpu_bitwise: usize,
6869
pub lt: usize,
6970
pub memw: usize,
7071
pub memw_aligned: usize,
@@ -84,6 +85,7 @@ impl TableCounts {
8485
/// Sum of all chunk counts across split tables.
8586
pub fn total(&self) -> usize {
8687
self.cpu
88+
+ self.cpu_bitwise
8789
+ self.lt
8890
+ self.memw
8991
+ self.memw_aligned
@@ -184,6 +186,7 @@ type AirTracePair<'a> = (
184186
/// All VM AIR instances, grouped by table.
185187
pub(crate) struct VmAirs {
186188
pub cpus: Vec<VmAir>,
189+
pub cpu_bitwises: Vec<VmAir>,
187190
pub bitwise: VmAir,
188191
pub lts: Vec<VmAir>,
189192
pub shifts: Vec<VmAir>,
@@ -215,6 +218,13 @@ impl VmAirs {
215218
for (air, trace) in self.cpus.iter().zip(traces.cpus.iter_mut()) {
216219
pairs.push((air, trace, &()));
217220
}
221+
for (air, trace) in self
222+
.cpu_bitwises
223+
.iter()
224+
.zip(traces.cpu_bitwises.iter_mut())
225+
{
226+
pairs.push((air, trace, &()));
227+
}
218228
for (air, trace) in self.lts.iter().zip(traces.lts.iter_mut()) {
219229
pairs.push((air, trace, &()));
220230
}
@@ -270,6 +280,9 @@ impl VmAirs {
270280
for air in &self.cpus {
271281
refs.push(air);
272282
}
283+
for air in &self.cpu_bitwises {
284+
refs.push(air);
285+
}
273286
for air in &self.lts {
274287
refs.push(air);
275288
}
@@ -320,6 +333,11 @@ impl VmAirs {
320333
let cpus: Vec<_> = (0..table_counts.cpu)
321334
.map(|i| create_cpu_air(proof_options).with_name(&format!("CPU[{}]", i)))
322335
.collect();
336+
let cpu_bitwises: Vec<_> = (0..table_counts.cpu_bitwise)
337+
.map(|i| {
338+
create_cpu_bitwise_air(proof_options).with_name(&format!("CPU_BITWISE[{}]", i))
339+
})
340+
.collect();
323341
let bitwise = if minimal_bitwise {
324342
create_bitwise_air(proof_options)
325343
} else {
@@ -381,6 +399,7 @@ impl VmAirs {
381399

382400
Self {
383401
cpus,
402+
cpu_bitwises,
384403
bitwise,
385404
lts,
386405
shifts,

prover/src/tables/cpu.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,12 @@ impl CpuOperation {
306306
Self::default()
307307
}
308308

309+
/// Returns true if this operation is a bitwise instruction (AND, OR, XOR).
310+
/// Used to route operations to the CPU_BITWISE chip.
311+
pub fn is_bitwise(&self) -> bool {
312+
self.decode.op_and || self.decode.op_or || self.decode.op_xor
313+
}
314+
309315
// =========================================================================
310316
// Convenience accessors for decode fields (reduces verbosity)
311317
// =========================================================================
@@ -2032,6 +2038,44 @@ pub fn bus_interactions() -> Vec<BusInteraction> {
20322038
interactions
20332039
}
20342040

2041+
/// Bus interactions for the CPU table WITHOUT AND/OR/XOR byte operations.
2042+
///
2043+
/// Used when bitwise instructions (AND, OR, XOR) are routed to a separate
2044+
/// CPU_BITWISE chip. This reduces the CPU effective width by 72 (24 × 3).
2045+
pub fn bus_interactions_without_bitwise_bytes() -> Vec<BusInteraction> {
2046+
let and_byte_id: u64 = BusId::AndByte.into();
2047+
let or_byte_id: u64 = BusId::OrByte.into();
2048+
let xor_byte_id: u64 = BusId::XorByte.into();
2049+
2050+
bus_interactions()
2051+
.into_iter()
2052+
.filter(|i| i.bus_id != and_byte_id && i.bus_id != or_byte_id && i.bus_id != xor_byte_id)
2053+
.collect()
2054+
}
2055+
2056+
/// Bus interactions for the CPU_BITWISE chip (AND, OR, XOR instructions).
2057+
///
2058+
/// Includes only interactions needed for bitwise operations:
2059+
/// - DECODE (instruction validation)
2060+
/// - IS_BYTE (range checks for rs1, rs2, rd, arg1, arg2, res)
2061+
/// - AND_BYTE, OR_BYTE, XOR_BYTE (byte-level bitwise operations)
2062+
/// - MEMW (register reads for rs1/rs2, register write for rd, PC update)
2063+
pub fn bus_interactions_bitwise_chip() -> Vec<BusInteraction> {
2064+
let keep_ids: Vec<u64> = vec![
2065+
BusId::Decode.into(),
2066+
BusId::IsByte.into(),
2067+
BusId::AndByte.into(),
2068+
BusId::OrByte.into(),
2069+
BusId::XorByte.into(),
2070+
BusId::Memw.into(),
2071+
];
2072+
2073+
bus_interactions()
2074+
.into_iter()
2075+
.filter(|i| keep_ids.contains(&i.bus_id))
2076+
.collect()
2077+
}
2078+
20352079
// =========================================================================
20362080
// Constraints (placeholder - will be implemented in constraints/)
20372081
// =========================================================================

prover/src/tables/cpu_bitwise.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//! CPU_BITWISE chip — handles AND, OR, XOR instruction execution.
2+
//!
3+
//! Split from the main CPU table to reduce effective width. The CPU table
4+
//! drops 24 bus interactions (AND_BYTE×8, OR_BYTE×8, XOR_BYTE×8), going
5+
//! from 278 to 206 effective width.
6+
//!
7+
//! This chip uses the same 74-column layout as CPU and reuses the same
8+
//! constraints (conditional constraints are automatically satisfied since
9+
//! non-bitwise selectors are always 0).
10+
11+
pub use super::cpu::{cols, generate_cpu_trace, CpuOperation};
12+
13+
use stark::lookup::BusInteraction;
14+
15+
/// Bus interactions for the CPU_BITWISE chip.
16+
///
17+
/// Includes: DECODE, IS_BYTE, AND_BYTE×8, OR_BYTE×8, XOR_BYTE×8, MEMW (register + PC).
18+
/// Excludes: MSB16, ZERO, LT, MUL, DVRM, SHIFT, LOAD, STORE, BRANCH, ECALL.
19+
pub fn bus_interactions() -> Vec<BusInteraction> {
20+
super::cpu::bus_interactions_bitwise_chip()
21+
}

prover/src/tables/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub mod bitwise;
2525
pub mod branch;
2626
pub mod commit;
2727
pub mod cpu;
28+
pub mod cpu_bitwise;
2829
pub mod decode;
2930
pub mod dvrm;
3031
pub mod halt;

prover/src/tables/trace_builder.rs

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,6 +1576,9 @@ pub struct Traces {
15761576
/// CPU execution traces (split into chunks of max_rows::CPU)
15771577
pub cpus: Vec<TraceTable<GoldilocksField, GoldilocksExtension>>,
15781578

1579+
/// CPU_BITWISE traces for AND/OR/XOR instructions (split into chunks)
1580+
pub cpu_bitwises: Vec<TraceTable<GoldilocksField, GoldilocksExtension>>,
1581+
15791582
/// BITWISE precomputed lookup table (2^20 rows)
15801583
pub bitwise: TraceTable<GoldilocksField, GoldilocksExtension>,
15811584

@@ -1646,6 +1649,7 @@ impl Traces {
16461649
pub fn table_counts(&self) -> crate::TableCounts {
16471650
crate::TableCounts {
16481651
cpu: self.cpus.len(),
1652+
cpu_bitwise: self.cpu_bitwises.len(),
16491653
lt: self.lts.len(),
16501654
memw: self.memws.len(),
16511655
memw_aligned: self.memw_aligneds.len(),
@@ -1908,14 +1912,6 @@ impl Traces {
19081912
// COMMIT table sends IsByte and IsHalfword lookups
19091913
bitwise_ops.extend(collect_bitwise_from_commit(&commit_ops));
19101914

1911-
// CPU padding rows send IS_BYTE with all-zero values.
1912-
// Add corresponding ops so the bitwise table multiplicities balance.
1913-
let num_padding_rows: usize = cpu_ops
1914-
.chunks(max_rows.cpu)
1915-
.map(|chunk| chunk.len().next_power_of_two().max(4) - chunk.len())
1916-
.sum();
1917-
bitwise_ops.extend(collect_byte_check_ops_for_padding(num_padding_rows));
1918-
19191915
// =====================================================================
19201916
// PHASE 5: Generate final traces (parallelized)
19211917
// =====================================================================
@@ -1928,7 +1924,28 @@ impl Traces {
19281924
.ok_or(Error::MissingHaltEcall)?;
19291925
let halt_timestamp = halt_op.timestamp;
19301926

1927+
// Route bitwise instructions (AND, OR, XOR) to CPU_BITWISE chip
1928+
let (cpu_bitwise_ops, cpu_ops): (Vec<_>, Vec<_>) =
1929+
cpu_ops.into_iter().partition(|op| op.is_bitwise());
1930+
1931+
// CPU and CPU_BITWISE padding rows send IS_BYTE with all-zero values.
1932+
// Computed after partition so padding reflects actual chunk sizes.
1933+
let num_padding_rows: usize = cpu_ops
1934+
.chunks(max_rows.cpu)
1935+
.map(|chunk| chunk.len().next_power_of_two().max(4) - chunk.len())
1936+
.sum::<usize>()
1937+
+ cpu_bitwise_ops
1938+
.chunks(max_rows.cpu)
1939+
.map(|chunk| chunk.len().next_power_of_two().max(4) - chunk.len())
1940+
.sum::<usize>();
1941+
bitwise_ops.extend(collect_byte_check_ops_for_padding(num_padding_rows));
1942+
19311943
let cpus = chunk_and_generate(&cpu_ops, max_rows.cpu, cpu::generate_cpu_trace);
1944+
let cpu_bitwises = if cpu_bitwise_ops.is_empty() {
1945+
Vec::new()
1946+
} else {
1947+
chunk_and_generate(&cpu_bitwise_ops, max_rows.cpu, cpu::generate_cpu_trace)
1948+
};
19321949
let memws = chunk_and_generate(&memw_ops, max_rows.memw, memw::generate_memw_trace);
19331950
let memw_aligneds = chunk_and_generate(
19341951
&memw_aligned_ops,
@@ -1952,16 +1969,21 @@ impl Traces {
19521969
bitwise::update_multiplicities(&mut bitwise, &bitwise_ops);
19531970

19541971
// Update DECODE multiplicities
1955-
// Each CPU operation looks up the DECODE table once
1956-
// Padding rows also look up pc=1 (the CPU padding entry)
1957-
// When CPU is split, each chunk pads independently
1972+
// Each CPU operation (from both CPU and CPU_BITWISE chips) looks up the DECODE table once.
1973+
// Padding rows also look up pc=1 (the CPU padding entry).
1974+
// When CPU is split, each chunk pads independently.
19581975
let mut decode = decode_trace;
19591976
let pc_to_row = decode_pc_to_row;
19601977
let num_padding_rows: usize = cpu_ops
19611978
.chunks(max_rows.cpu)
19621979
.map(|chunk| chunk.len().next_power_of_two().max(4) - chunk.len())
1963-
.sum();
1980+
.sum::<usize>()
1981+
+ cpu_bitwise_ops
1982+
.chunks(max_rows.cpu)
1983+
.map(|chunk| chunk.len().next_power_of_two().max(4) - chunk.len())
1984+
.sum::<usize>();
19641985
let mut decode_lookups: Vec<u64> = cpu_ops.iter().map(|op| op.decode.pc).collect();
1986+
decode_lookups.extend(cpu_bitwise_ops.iter().map(|op| op.decode.pc));
19651987
decode_lookups.extend(std::iter::repeat_n(cpu::CPU_PADDING_PC, num_padding_rows));
19661988
decode::update_multiplicities(&mut decode, &pc_to_row, &decode_lookups);
19671989

@@ -2006,6 +2028,7 @@ impl Traces {
20062028

20072029
Ok(Traces {
20082030
cpus,
2031+
cpu_bitwises,
20092032
bitwise,
20102033
lts,
20112034
shifts,
@@ -2152,13 +2175,6 @@ impl Traces {
21522175
// COMMIT table sends IsHalfword lookups
21532176
bitwise_ops.extend(collect_bitwise_from_commit(&commit_ops));
21542177

2155-
// CPU padding rows send IS_BYTE with all-zero values.
2156-
let num_padding_rows: usize = cpu_ops
2157-
.chunks(max_rows.cpu)
2158-
.map(|chunk| chunk.len().next_power_of_two().max(4) - chunk.len())
2159-
.sum();
2160-
bitwise_ops.extend(collect_byte_check_ops_for_padding(num_padding_rows));
2161-
21622178
// =====================================================================
21632179
// PHASE 5: Generate final traces (parallelized)
21642180
// =====================================================================
@@ -2171,7 +2187,27 @@ impl Traces {
21712187
.ok_or(Error::MissingHaltEcall)?;
21722188
let halt_timestamp = halt_op.timestamp;
21732189

2190+
// Route bitwise instructions (AND, OR, XOR) to CPU_BITWISE chip
2191+
let (cpu_bitwise_ops, cpu_ops): (Vec<_>, Vec<_>) =
2192+
cpu_ops.into_iter().partition(|op| op.is_bitwise());
2193+
2194+
// CPU and CPU_BITWISE padding rows send IS_BYTE with all-zero values.
2195+
let num_padding_rows: usize = cpu_ops
2196+
.chunks(max_rows.cpu)
2197+
.map(|chunk| chunk.len().next_power_of_two().max(4) - chunk.len())
2198+
.sum::<usize>()
2199+
+ cpu_bitwise_ops
2200+
.chunks(max_rows.cpu)
2201+
.map(|chunk| chunk.len().next_power_of_two().max(4) - chunk.len())
2202+
.sum::<usize>();
2203+
bitwise_ops.extend(collect_byte_check_ops_for_padding(num_padding_rows));
2204+
21742205
let cpus = chunk_and_generate(&cpu_ops, max_rows.cpu, cpu::generate_cpu_trace);
2206+
let cpu_bitwises = if cpu_bitwise_ops.is_empty() {
2207+
Vec::new()
2208+
} else {
2209+
chunk_and_generate(&cpu_bitwise_ops, max_rows.cpu, cpu::generate_cpu_trace)
2210+
};
21752211
let memws = chunk_and_generate(&memw_ops, max_rows.memw, memw::generate_memw_trace);
21762212
let memw_aligneds = chunk_and_generate(
21772213
&memw_aligned_ops,
@@ -2195,15 +2231,18 @@ impl Traces {
21952231
bitwise::update_multiplicities(&mut bitwise, &bitwise_ops);
21962232

21972233
// Generate DECODE trace and update multiplicities
2198-
// Each CPU operation looks up the DECODE table once
2199-
// Padding rows also look up pc=1 (the CPU padding entry)
2200-
// When CPU is split, each chunk pads independently
2234+
// Both CPU and CPU_BITWISE chips look up DECODE once per row
22012235
let (mut decode, pc_to_row) = decode::generate_decode_trace(&instructions);
22022236
let num_padding_rows: usize = cpu_ops
22032237
.chunks(max_rows.cpu)
22042238
.map(|chunk| chunk.len().next_power_of_two().max(4) - chunk.len())
2205-
.sum();
2239+
.sum::<usize>()
2240+
+ cpu_bitwise_ops
2241+
.chunks(max_rows.cpu)
2242+
.map(|chunk| chunk.len().next_power_of_two().max(4) - chunk.len())
2243+
.sum::<usize>();
22062244
let mut decode_lookups: Vec<u64> = cpu_ops.iter().map(|op| op.decode.pc).collect();
2245+
decode_lookups.extend(cpu_bitwise_ops.iter().map(|op| op.decode.pc));
22072246
decode_lookups.extend(std::iter::repeat_n(cpu::CPU_PADDING_PC, num_padding_rows));
22082247
decode::update_multiplicities(&mut decode, &pc_to_row, &decode_lookups);
22092248
let register_final_state = register_state.to_final_state_map();
@@ -2235,6 +2274,7 @@ impl Traces {
22352274

22362275
Ok(Traces {
22372276
cpus,
2277+
cpu_bitwises,
22382278
bitwise,
22392279
lts,
22402280
shifts,

prover/src/test_utils.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ use crate::tables::commit::{
3535
bus_interactions as commit_bus_interactions, cols as commit_cols,
3636
create_constraints as commit_constraints,
3737
};
38-
use crate::tables::cpu::{
39-
CpuOperation, bus_interactions as cpu_bus_interactions, cols as cpu_cols,
40-
};
38+
use crate::tables::cpu::{CpuOperation, cols as cpu_cols};
4139
use crate::tables::decode::{bus_interactions as decode_bus_interactions, cols as decode_cols};
4240
use crate::tables::dvrm::{
4341
bus_interactions as dvrm_bus_interactions, cols as dvrm_cols, dvrm_constraints,
@@ -475,7 +473,7 @@ pub fn create_cpu_air(proof_options: &ProofOptions) -> VmAir {
475473
}
476474

477475
let auxiliary_trace_build_data = AuxiliaryTraceBuildData {
478-
interactions: cpu_bus_interactions(),
476+
interactions: crate::tables::cpu::bus_interactions_without_bitwise_bytes(),
479477
};
480478

481479
AirWithBuses::new(
@@ -488,6 +486,38 @@ pub fn create_cpu_air(proof_options: &ProofOptions) -> VmAir {
488486
.with_name("CPU")
489487
}
490488

489+
/// Create CPU_BITWISE AIR for AND/OR/XOR instructions.
490+
///
491+
/// Uses the same 74 columns and constraints as CPU, but with a different
492+
/// set of bus interactions (only bitwise-relevant ones).
493+
pub fn create_cpu_bitwise_air(proof_options: &ProofOptions) -> VmAir {
494+
let (is_bit, add, other, _) = create_all_cpu_constraints();
495+
496+
let mut transition_constraints: Vec<Box<dyn TransitionConstraint<F, E>>> = Vec::new();
497+
for c in is_bit {
498+
transition_constraints.push(Box::new(c));
499+
}
500+
for c in add {
501+
transition_constraints.push(Box::new(c));
502+
}
503+
for c in other {
504+
transition_constraints.push(c);
505+
}
506+
507+
let auxiliary_trace_build_data = AuxiliaryTraceBuildData {
508+
interactions: crate::tables::cpu_bitwise::bus_interactions(),
509+
};
510+
511+
AirWithBuses::new(
512+
cpu_cols::NUM_COLUMNS,
513+
auxiliary_trace_build_data,
514+
proof_options,
515+
1,
516+
transition_constraints,
517+
)
518+
.with_name("CPU_BITWISE")
519+
}
520+
491521
/// Create Bitwise AIR with bus interactions.
492522
pub fn create_bitwise_air(proof_options: &ProofOptions) -> VmAir {
493523
let transition_constraints: Vec<Box<dyn TransitionConstraint<F, E>>> = vec![];

0 commit comments

Comments
 (0)