From 506e6250eb8b296dd7b16a5c731a2e51dd88e5a2 Mon Sep 17 00:00:00 2001 From: Georg Wiese Date: Sat, 21 Dec 2024 15:33:20 +0100 Subject: [PATCH] JIT for block machines with non-rectangular shapes --- .../src/witgen/jit/block_machine_processor.rs | 237 +++++++++++++++--- executor/src/witgen/jit/effect.rs | 36 ++- executor/src/witgen/jit/function_cache.rs | 23 ++ .../witgen/jit/includes/field_goldilocks.rs | 9 +- executor/src/witgen/jit/test_util.rs | 43 +--- executor/src/witgen/jit/witgen_inference.rs | 38 ++- executor/src/witgen/machines/block_machine.rs | 10 +- .../block_machine_exact_number_of_rows.asm | 8 +- test_data/pil/binary.pil | 4 +- 9 files changed, 305 insertions(+), 103 deletions(-) diff --git a/executor/src/witgen/jit/block_machine_processor.rs b/executor/src/witgen/jit/block_machine_processor.rs index bff7d669b4..0318e929d3 100644 --- a/executor/src/witgen/jit/block_machine_processor.rs +++ b/executor/src/witgen/jit/block_machine_processor.rs @@ -1,14 +1,18 @@ -use std::collections::HashSet; +use std::collections::{BTreeSet, HashSet}; use bit_vec::BitVec; -use powdr_ast::analyzed::{AlgebraicReference, Identity}; +use powdr_ast::analyzed::{AlgebraicReference, Identity, SelectedExpressions}; use powdr_number::FieldElement; -use crate::witgen::{machines::MachineParts, FixedData}; +use crate::witgen::{ + jit::{effect::format_code, witgen_inference::Value}, + machines::MachineParts, + FixedData, +}; use super::{ effect::Effect, - variable::Variable, + variable::{Cell, Variable}, witgen_inference::{CanProcessCall, FixedEvaluator, WitgenInference}, }; @@ -64,17 +68,21 @@ impl<'a, T: FieldElement> BlockMachineProcessor<'a, T> { // Solve for the block witness. // Fails if any machine call cannot be completed. - self.solve_block(can_process, &mut witgen)?; - - for (index, expr) in connection_rhs.expressions.iter().enumerate() { - if !witgen.is_known(&Variable::Param(index)) { - return Err(format!( - "Unable to derive algorithm to compute output value \"{expr}\"" - )); + match self.solve_block(can_process, &mut witgen, connection_rhs) { + Ok(()) => Ok(witgen.code()), + Err(e) => { + log::debug!("Code generation failed: {e}"); + log::debug!( + "The following code was generated so far:\n{}", + format_code(witgen.code().as_slice()) + ); + Err(format!("Code generation failed: {e}\nRun with RUST_LOG=debug to see the code generated so far.")) } } + } - Ok(witgen.code()) + fn row_range(&self) -> std::ops::Range { + -1..(self.block_size + 1) as i32 } /// Repeatedly processes all identities on all rows, until no progress is made. @@ -83,16 +91,16 @@ impl<'a, T: FieldElement> BlockMachineProcessor<'a, T> { &self, can_process: CanProcess, witgen: &mut WitgenInference<'a, T, &Self>, + connection_rhs: &SelectedExpressions, ) -> Result<(), String> { let mut complete = HashSet::new(); for iteration in 0.. { let mut progress = false; - // TODO: This algorithm is assuming a rectangular block shape. - for row in 0..self.block_size { + for row in self.row_range() { for id in &self.machine_parts.identities { if !complete.contains(&(id.id(), row)) { - let result = witgen.process_identity(can_process.clone(), id, row as i32); + let result = witgen.process_identity(can_process.clone(), id, row); if result.complete { complete.insert((id.id(), row)); } @@ -108,22 +116,106 @@ impl<'a, T: FieldElement> BlockMachineProcessor<'a, T> { } } - // If any machine call could not be completed, that's bad because machine calls typically have side effects. - // So, the underlying lookup / permutation / bus argument likely does not hold. - // TODO: This assumes a rectangular block shape. - let has_incomplete_machine_calls = (0..self.block_size) - .flat_map(|row| { - self.machine_parts - .identities - .iter() - .filter(|id| is_machine_call(id)) - .map(move |id| (id, row)) + self.check_block_shape(witgen)?; + self.check_incomplete_machine_calls(&complete)?; + + for (index, expr) in connection_rhs.expressions.iter().enumerate() { + if !witgen.is_known(&Variable::Param(index)) { + return Err(format!( + "Unable to derive algorithm to compute output value \"{expr}\"" + )); + } + } + + Ok(()) + } + + /// After solving, the known values should be such that we can stack different blocks. + fn check_block_shape(&self, witgen: &mut WitgenInference<'a, T, &Self>) -> Result<(), String> { + let known_columns = witgen + .known_variables() + .iter() + .filter_map(|var| match var { + Variable::Cell(cell) => Some(cell.id), + _ => None, }) - .any(|(identity, row)| !complete.contains(&(identity.id(), row))); + .collect::>(); + + let can_stack = known_columns.iter().all(|column_id| { + let values = self + .row_range() + .map(|row| { + witgen.value(&Variable::Cell(Cell { + id: *column_id, + row_offset: row, + column_name: "".to_string(), + })) + }) + .collect::>(); + // TODO: Improve error message? + // let column_name = self.fixed_data.column_name(&PolyID { + // id: *column_id, + // ptype: PolynomialType::Committed, + // }); + let is_compatible = |v1: Value, v2: Value| match (v1, v2) { + (Value::Unknown, _) | (_, Value::Unknown) => true, + (Value::Concrete(a), Value::Concrete(b)) => a == b, + _ => false, + }; + is_compatible(values[0], values[self.block_size]) + && is_compatible(values[1], values[self.block_size + 1]) + }); - match has_incomplete_machine_calls { - true => Err("Incomplete machine calls".to_string()), - false => Ok(()), + match can_stack { + true => Ok(()), + false => Err("Block machine shape does not allow stacking".to_string()), + } + } + + /// If any machine call could not be completed, that's bad because machine calls typically have side effects. + /// So, the underlying lookup / permutation / bus argument likely does not hold. + /// This function checks that all machine calls are complete, at least for a window of rows. + fn check_incomplete_machine_calls(&self, complete: &HashSet<(u64, i32)>) -> Result<(), String> { + let machine_calls = self + .machine_parts + .identities + .iter() + .filter(|id| is_machine_call(id)); + + let incomplete_machine_calls = machine_calls + .flat_map(|call| { + let complete_rows = self + .row_range() + .filter(|row| complete.contains(&(call.id(), *row))) + .collect::>(); + // Because we process rows -1..block_size+1, it is fine to have two incomplete machine calls, + // as long as consecutive rows are complete. + if complete_rows.len() >= self.block_size { + let is_consecutive = complete_rows.iter().max().unwrap() + - complete_rows.iter().min().unwrap() + == complete_rows.len() as i32 - 1; + if is_consecutive { + return vec![]; + } + } + self.row_range() + .filter(|row| !complete.contains(&(call.id(), *row))) + .map(|row| (call, row)) + .collect::>() + }) + .collect::>(); + + if !incomplete_machine_calls.is_empty() { + Err(format!( + "Incomplete machine calls:\n {}", + incomplete_machine_calls + .iter() + .map(|(id, row)| format!("{id} (row {row})")) + .collect::>() + .join("\n ") + )) + } else { + Ok(()) } } } @@ -143,7 +235,16 @@ impl FixedEvaluator for &BlockMachineProcessor<'_, T> { fn evaluate(&self, var: &AlgebraicReference, row_offset: i32) -> Option { assert!(var.is_fixed()); let values = self.fixed_data.fixed_cols[&var.poly_id].values_max_size(); - let row = (row_offset + var.next as i32 + values.len() as i32) as usize % values.len(); + + // By assumption of the block machine, all fixed columns are cyclic with a period of . + // An exception might be the first and last row. + assert!(row_offset >= -1); + assert!(self.block_size >= 1); + // The current row is guaranteed to be at least 1. + let current_row = (2 * self.block_size as i32 + row_offset) as usize; + let row = current_row + var.next as usize; + + assert!(values.len() >= self.block_size * 4); Some(values[row]) } } @@ -159,10 +260,7 @@ mod test { use crate::witgen::{ data_structures::mutable_state::MutableState, global_constraints, - jit::{ - effect::Effect, - test_util::{format_code, read_pil}, - }, + jit::{effect::Effect, test_util::read_pil}, machines::{machine_extractor::MachineExtractor, KnownMachine, Machine}, }; @@ -241,18 +339,75 @@ params[2] = Add::c[0];" let err_str = generate_for_block_machine(input, "Unconstrained", 2, 1) .err() .unwrap(); - assert_eq!( - err_str, - "Unable to derive algorithm to compute output value \"Unconstrained::c\"" - ); + assert!(err_str + .contains("Unable to derive algorithm to compute output value \"Unconstrained::c\"")); + } + + #[test] + #[should_panic = "Block machine shape does not allow stacking"] + fn not_stackable() { + let input = " + namespace Main(256); + col witness a, b, c; + [a] is NotStackable.sel $ [NotStackable.a]; + namespace NotStackable(256); + col witness sel, a; + a = a'; + "; + generate_for_block_machine(input, "NotStackable", 1, 0).unwrap(); } #[test] - // TODO: Currently fails, because the machine has a non-rectangular block shape. - #[should_panic = "Incomplete machine calls"] fn binary() { let input = read_to_string("../test_data/pil/binary.pil").unwrap(); - generate_for_block_machine(&input, "main_binary", 3, 1).unwrap(); + let code = generate_for_block_machine(&input, "main_binary", 3, 1).unwrap(); + assert_eq!( + format_code(&code), + "main_binary::sel[0][3] = 1; +main_binary::operation_id[3] = params[0]; +main_binary::A[3] = params[1]; +main_binary::B[3] = params[2]; +main_binary::operation_id[2] = main_binary::operation_id[3]; +main_binary::A_byte[2] = ((main_binary::A[3] & 4278190080) // 16777216); +main_binary::A[2] = (main_binary::A[3] & 16777215); +assert (main_binary::A[3] & 18446744069414584320) == 0; +main_binary::B_byte[2] = ((main_binary::B[3] & 4278190080) // 16777216); +main_binary::B[2] = (main_binary::B[3] & 16777215); +assert (main_binary::B[3] & 18446744069414584320) == 0; +main_binary::operation_id_next[2] = main_binary::operation_id[3]; +machine_call(9, [Known(main_binary::operation_id_next[2]), Known(main_binary::A_byte[2]), Known(main_binary::B_byte[2]), Unknown(ret(9, 2, 3))]); +main_binary::C_byte[2] = ret(9, 2, 3); +main_binary::operation_id[1] = main_binary::operation_id[2]; +main_binary::A_byte[1] = ((main_binary::A[2] & 16711680) // 65536); +main_binary::A[1] = (main_binary::A[2] & 65535); +assert (main_binary::A[2] & 18446744073692774400) == 0; +main_binary::B_byte[1] = ((main_binary::B[2] & 16711680) // 65536); +main_binary::B[1] = (main_binary::B[2] & 65535); +assert (main_binary::B[2] & 18446744073692774400) == 0; +main_binary::operation_id_next[1] = main_binary::operation_id[2]; +machine_call(9, [Known(main_binary::operation_id_next[1]), Known(main_binary::A_byte[1]), Known(main_binary::B_byte[1]), Unknown(ret(9, 1, 3))]); +main_binary::C_byte[1] = ret(9, 1, 3); +main_binary::operation_id[0] = main_binary::operation_id[1]; +main_binary::A_byte[0] = ((main_binary::A[1] & 65280) // 256); +main_binary::A[0] = (main_binary::A[1] & 255); +assert (main_binary::A[1] & 18446744073709486080) == 0; +main_binary::B_byte[0] = ((main_binary::B[1] & 65280) // 256); +main_binary::B[0] = (main_binary::B[1] & 255); +assert (main_binary::B[1] & 18446744073709486080) == 0; +main_binary::operation_id_next[0] = main_binary::operation_id[1]; +machine_call(9, [Known(main_binary::operation_id_next[0]), Known(main_binary::A_byte[0]), Known(main_binary::B_byte[0]), Unknown(ret(9, 0, 3))]); +main_binary::C_byte[0] = ret(9, 0, 3); +main_binary::A_byte[-1] = main_binary::A[0]; +main_binary::B_byte[-1] = main_binary::B[0]; +main_binary::operation_id_next[-1] = main_binary::operation_id[0]; +machine_call(9, [Known(main_binary::operation_id_next[-1]), Known(main_binary::A_byte[-1]), Known(main_binary::B_byte[-1]), Unknown(ret(9, -1, 3))]); +main_binary::C_byte[-1] = ret(9, -1, 3); +main_binary::C[0] = main_binary::C_byte[-1]; +main_binary::C[1] = (main_binary::C[0] + (main_binary::C_byte[0] * 256)); +main_binary::C[2] = (main_binary::C[1] + (main_binary::C_byte[1] * 65536)); +main_binary::C[3] = (main_binary::C[2] + (main_binary::C_byte[2] * 16777216)); +params[3] = main_binary::C[3];" + ) } #[test] diff --git a/executor/src/witgen/jit/effect.rs b/executor/src/witgen/jit/effect.rs index 8087f52c11..fc15186799 100644 --- a/executor/src/witgen/jit/effect.rs +++ b/executor/src/witgen/jit/effect.rs @@ -1,8 +1,9 @@ +use itertools::Itertools; use powdr_number::FieldElement; use crate::witgen::range_constraints::RangeConstraint; -use super::symbolic_expression::SymbolicExpression; +use super::{symbolic_expression::SymbolicExpression, variable::Variable}; /// The effect of solving a symbolic equation. pub enum Effect { @@ -55,3 +56,36 @@ pub enum MachineCallArgument { Known(SymbolicExpression), Unknown(V), } + +pub fn format_code(effects: &[Effect]) -> String { + effects + .iter() + .map(|effect| match effect { + Effect::Assignment(v, expr) => format!("{v} = {expr};"), + Effect::Assertion(Assertion { + lhs, + rhs, + expected_equal, + }) => { + format!( + "assert {lhs} {} {rhs};", + if *expected_equal { "==" } else { "!=" } + ) + } + Effect::MachineCall(id, args) => { + format!( + "machine_call({id}, [{}]);", + args.iter() + .map(|arg| match arg { + MachineCallArgument::Known(k) => format!("Known({k})"), + MachineCallArgument::Unknown(u) => format!("Unknown({u})"), + }) + .join(", ") + ) + } + Effect::RangeConstraint(..) => { + panic!("Range constraints should not be part of the code.") + } + }) + .join("\n") +} diff --git a/executor/src/witgen/jit/function_cache.rs b/executor/src/witgen/jit/function_cache.rs index 2a79ba2d1f..4bc7f49283 100644 --- a/executor/src/witgen/jit/function_cache.rs +++ b/executor/src/witgen/jit/function_cache.rs @@ -5,6 +5,7 @@ use powdr_number::{FieldElement, KnownField}; use crate::witgen::{ data_structures::finalizable_data::{ColumnLayout, CompactDataRef}, + jit::effect::Effect, machines::{LookupCell, MachineParts}, EvalError, FixedData, MutableState, QueryCallback, }; @@ -28,6 +29,7 @@ pub struct FunctionCache<'a, T: FieldElement> { /// but failed. witgen_functions: HashMap>>, column_layout: ColumnLayout, + block_size: usize, } impl<'a, T: FieldElement> FunctionCache<'a, T> { @@ -45,6 +47,7 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> { processor, column_layout: metadata, witgen_functions: HashMap::new(), + block_size, } } @@ -89,9 +92,29 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> { cache_key: &CacheKey, ) -> Option> { log::trace!("Compiling JIT function for {:?}", cache_key); + self.processor .generate_code(mutable_state, cache_key.identity_id, &cache_key.known_args) .ok() + .and_then(|code| { + // TODO: Remove this once BlockMachine passes the right amount of context for machines with + // non-rectangular block shapes. + let is_rectangular = code + .iter() + .filter_map(|effect| match effect { + Effect::Assignment(v, _) => Some(v), + _ => None, + }) + .filter_map(|assigned_variable| match assigned_variable { + Variable::Cell(cell) => Some(cell.row_offset), + _ => None, + }) + .all(|row_offset| row_offset >= 0 && row_offset < self.block_size as i32); + if !is_rectangular { + log::debug!("Filtering out code for non-rectangular block shape"); + } + is_rectangular.then_some(code) + }) .map(|code| { log::trace!("Generated code ({} steps)", code.len()); let known_inputs = cache_key diff --git a/executor/src/witgen/jit/includes/field_goldilocks.rs b/executor/src/witgen/jit/includes/field_goldilocks.rs index e2d4a59def..7de720f7d6 100644 --- a/executor/src/witgen/jit/includes/field_goldilocks.rs +++ b/executor/src/witgen/jit/includes/field_goldilocks.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Copy, Default)] +#[derive(Clone, Copy, Default, PartialEq, Eq)] #[repr(transparent)] struct GoldilocksField(u64); @@ -310,6 +310,13 @@ impl std::ops::BitAnd for GoldilocksField { Self(self.0 & b.0) } } +impl std::ops::BitAnd for GoldilocksField { + type Output = Self; + #[inline] + fn bitand(self, b: u64) -> GoldilocksField { + Self(self.0 & b) + } +} impl std::ops::BitOr for GoldilocksField { type Output = Self; #[inline] diff --git a/executor/src/witgen/jit/test_util.rs b/executor/src/witgen/jit/test_util.rs index 6b32a89ec3..2a4d725a20 100644 --- a/executor/src/witgen/jit/test_util.rs +++ b/executor/src/witgen/jit/test_util.rs @@ -1,47 +1,8 @@ -use itertools::Itertools; use powdr_ast::analyzed::Analyzed; use powdr_executor_utils::VariablySizedColumn; -use powdr_number::{FieldElement, GoldilocksField}; +use powdr_number::FieldElement; -use crate::{constant_evaluator, witgen::jit::effect::MachineCallArgument}; - -use super::{ - effect::{Assertion, Effect}, - variable::Variable, -}; - -pub fn format_code(effects: &[Effect]) -> String { - effects - .iter() - .map(|effect| match effect { - Effect::Assignment(v, expr) => format!("{v} = {expr};"), - Effect::Assertion(Assertion { - lhs, - rhs, - expected_equal, - }) => { - format!( - "assert {lhs} {} {rhs};", - if *expected_equal { "==" } else { "!=" } - ) - } - Effect::MachineCall(id, args) => { - format!( - "machine_call({id}, [{}]);", - args.iter() - .map(|arg| match arg { - MachineCallArgument::Known(k) => format!("Known({k})"), - MachineCallArgument::Unknown(u) => format!("Unknown({u})"), - }) - .join(", ") - ) - } - Effect::RangeConstraint(..) => { - panic!("Range constraints should not be part of the code.") - } - }) - .join("\n") -} +use crate::constant_evaluator; pub fn read_pil( input_pil: &str, diff --git a/executor/src/witgen/jit/witgen_inference.rs b/executor/src/witgen/jit/witgen_inference.rs index 3bc6a2d53b..4a8d7660e9 100644 --- a/executor/src/witgen/jit/witgen_inference.rs +++ b/executor/src/witgen/jit/witgen_inference.rs @@ -41,6 +41,13 @@ pub struct WitgenInference<'a, T: FieldElement, FixedEval: FixedEvaluator> { code: Vec>, } +#[derive(Debug, Clone, Copy)] +pub enum Value { + Concrete(T), + Known, + Unknown, +} + impl<'a, T: FieldElement, FixedEval: FixedEvaluator> WitgenInference<'a, T, FixedEval> { pub fn new( fixed_data: &'a FixedData<'a, T>, @@ -61,10 +68,25 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> WitgenInference<'a, T, F self.code } + pub fn known_variables(&self) -> &HashSet { + &self.known_variables + } + pub fn is_known(&self, variable: &Variable) -> bool { self.known_variables.contains(variable) } + pub fn value(&self, variable: &Variable) -> Value { + let rc = self.range_constraint(variable); + if let Some(val) = rc.as_ref().and_then(|rc| rc.try_to_single_value()) { + Value::Concrete(val) + } else if self.known_variables.contains(variable) { + Value::Known + } else { + Value::Unknown + } + } + /// Process an identity on a certain row. pub fn process_identity>( &mut self, @@ -276,7 +298,7 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> WitgenInference<'a, T, F /// Adds a range constraint to the set of derived range constraints. Returns true if progress was made. fn add_range_constraint(&mut self, variable: Variable, rc: RangeConstraint) -> bool { let rc = self - .range_constraint(variable.clone()) + .range_constraint(&variable) .map_or(rc.clone(), |existing_rc| existing_rc.conjunction(&rc)); if !self.known_variables.contains(&variable) { if let Some(v) = rc.try_to_single_value() { @@ -296,7 +318,7 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> WitgenInference<'a, T, F /// Returns the current best-known range constraint on the given variable /// combining global range constraints and newly derived local range constraints. - fn range_constraint(&self, variable: Variable) -> Option> { + fn range_constraint(&self, variable: &Variable) -> Option> { variable .try_to_witness_poly_id() .and_then(|poly_id| { @@ -309,7 +331,7 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> WitgenInference<'a, T, F }) }) .iter() - .chain(self.derived_range_constraints.get(&variable)) + .chain(self.derived_range_constraints.get(variable)) .cloned() .reduce(|gc, rc| gc.conjunction(&rc)) } @@ -386,7 +408,7 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> Evaluator<'a, T, FixedEv pub fn evaluate_variable(&self, variable: Variable) -> AffineSymbolicExpression { // If a variable is known and has a compile-time constant value, // that value is stored in the range constraints. - let rc = self.witgen_inference.range_constraint(variable.clone()); + let rc = self.witgen_inference.range_constraint(&variable); if let Some(val) = rc.as_ref().and_then(|rc| rc.try_to_single_value()) { val.into() } else if !self.only_concrete_known && self.witgen_inference.is_known(&variable) { @@ -473,15 +495,13 @@ impl<'a, T: FieldElement, Q: QueryCallback> CanProcessCall for &MutableSta #[cfg(test)] mod test { + use powdr_number::GoldilocksField; use pretty_assertions::assert_eq; use test_log::test; use crate::witgen::{ global_constraints, - jit::{ - test_util::{format_code, read_pil}, - variable::Cell, - }, + jit::{effect::format_code, test_util::read_pil, variable::Cell}, machines::{Connection, FixedLookup, KnownMachine}, FixedData, }; @@ -504,7 +524,7 @@ mod test { known_cells: Vec<(&str, i32)>, expected_complete: Option, ) -> String { - let (analyzed, fixed_col_vals) = read_pil(input); + let (analyzed, fixed_col_vals) = read_pil::(input); let fixed_data = FixedData::new(&analyzed, &fixed_col_vals, &[], Default::default(), 0); let (fixed_data, retained_identities) = global_constraints::set_global_constraints(fixed_data, &analyzed.identities); diff --git a/executor/src/witgen/machines/block_machine.rs b/executor/src/witgen/machines/block_machine.rs index ec214cbe39..a2305c77a7 100644 --- a/executor/src/witgen/machines/block_machine.rs +++ b/executor/src/witgen/machines/block_machine.rs @@ -401,6 +401,10 @@ impl<'a, T: FieldElement> BlockMachine<'a, T> { } } + if self.rows() + self.block_size as DegreeType > self.degree { + return Err(EvalError::RowsExhausted(self.name.clone())); + } + let known_inputs = outer_query.left.iter().map(|e| e.is_constant()).collect(); if self .function_cache @@ -429,10 +433,6 @@ impl<'a, T: FieldElement> BlockMachine<'a, T> { )); } - if self.rows() + self.block_size as DegreeType > self.degree { - return Err(EvalError::RowsExhausted(self.name.clone())); - } - let process_result = self.process(mutable_state, &mut sequence_iterator, outer_query.clone())?; @@ -481,7 +481,7 @@ impl<'a, T: FieldElement> BlockMachine<'a, T> { let values = outer_query.prepare_for_direct_lookup(&mut input_output_data); assert!( - (self.rows() + self.block_size as DegreeType) < self.degree, + (self.rows() + self.block_size as DegreeType) <= self.degree, "Block machine is full (this should have been checked before)" ); self.data diff --git a/test_data/asm/block_machine_exact_number_of_rows.asm b/test_data/asm/block_machine_exact_number_of_rows.asm index ffff1e1e05..26a1369e23 100644 --- a/test_data/asm/block_machine_exact_number_of_rows.asm +++ b/test_data/asm/block_machine_exact_number_of_rows.asm @@ -17,15 +17,17 @@ machine Main with min_degree: 32, max_degree: 64 { reg A; ByteBinary byte_binary; - // We'll call the binary machine twice and the block size - // is 4, so we need exactly 8 rows. - Binary binary(byte_binary, 8, 8); + // We'll call the binary machine 4 times and the block size + // is 4, so we need exactly 16 rows. + Binary binary(byte_binary, 16, 16); instr and X0, X1 -> X2 link ~> X2 = binary.and(X0, X1); function main { A <== and(0xaaaaaaaa, 0xaaaaaaaa); + A <== and(0x55555555, 0x55555555); + A <== and(0x00000000, 0xffffffff); A <== and(0xffffffff, 0xffffffff); return; diff --git a/test_data/pil/binary.pil b/test_data/pil/binary.pil index ddb29c91f2..0219bdbedc 100644 --- a/test_data/pil/binary.pil +++ b/test_data/pil/binary.pil @@ -1,9 +1,9 @@ // A compiled version of std/machines/large_field/binary.asm namespace main(128); - col witness a, b, c; + col witness binary_op, a, b, c; // Dummy connection constraint - [0, a, b, c] is main_binary::latch * main_binary::sel[0] $ [main_binary::operation_id, main_binary::A, main_binary::B, main_binary::C]; + [binary_op, a, b, c] is main_binary::latch * main_binary::sel[0] $ [main_binary::operation_id, main_binary::A, main_binary::B, main_binary::C]; namespace main_binary(128); col witness operation_id;