diff --git a/executor/src/witgen/data_structures/mutable_state.rs b/executor/src/witgen/data_structures/mutable_state.rs index add2f2915e..2d5358ef83 100644 --- a/executor/src/witgen/data_structures/mutable_state.rs +++ b/executor/src/witgen/data_structures/mutable_state.rs @@ -61,7 +61,7 @@ impl<'a, T: FieldElement, Q: QueryCallback> MutableState<'a, T, Q> { // unique machine responsible for handling a bus send), so just answer "false" if the identity // has no responsible machine. let mut machine = self.responsible_machine(identity_id).ok()?; - machine.can_process_call_fully(identity_id, known_inputs, range_constraints) + machine.can_process_call_fully(self, identity_id, known_inputs, range_constraints) } /// Call the machine responsible for the right-hand-side of an identity given its ID diff --git a/executor/src/witgen/jit/block_machine_processor.rs b/executor/src/witgen/jit/block_machine_processor.rs index 22555a2aa0..128384db7c 100644 --- a/executor/src/witgen/jit/block_machine_processor.rs +++ b/executor/src/witgen/jit/block_machine_processor.rs @@ -8,7 +8,7 @@ use powdr_number::FieldElement; use crate::witgen::{jit::processor::Processor, machines::MachineParts, FixedData}; use super::{ - effect::Effect, + processor::ProcessorResult, variable::Variable, witgen_inference::{CanProcessCall, FixedEvaluator, WitgenInference}, }; @@ -46,7 +46,7 @@ impl<'a, T: FieldElement> BlockMachineProcessor<'a, T> { can_process: impl CanProcessCall, identity_id: u64, known_args: &BitVec, - ) -> Result>, String> { + ) -> Result, String> { let connection = self.machine_parts.connections[&identity_id]; assert_eq!(connection.right.expressions.len(), known_args.len()); @@ -92,6 +92,7 @@ impl<'a, T: FieldElement> BlockMachineProcessor<'a, T> { self.block_size, true, requested_known, + (0..known_args.len()).map(Variable::Param), BLOCK_MACHINE_MAX_BRANCH_DEPTH, ) .generate_code(can_process, witgen) @@ -158,11 +159,9 @@ mod test { use crate::witgen::{ data_structures::mutable_state::MutableState, global_constraints, - jit::{ - effect::{format_code, Effect}, - test_util::read_pil, - }, + jit::{effect::format_code, test_util::read_pil}, machines::{machine_extractor::MachineExtractor, KnownMachine, Machine}, + range_constraints::RangeConstraint, FixedData, }; @@ -173,7 +172,7 @@ mod test { machine_name: &str, num_inputs: usize, num_outputs: usize, - ) -> Result>, String> { + ) -> Result, String> { let (analyzed, fixed_col_vals) = read_pil(input_pil); let fixed_data = FixedData::new(&analyzed, &fixed_col_vals, &[], Default::default(), 0); @@ -221,9 +220,9 @@ mod test { col witness sel, a, b, c; c = a + b; "; - let code = generate_for_block_machine(input, "Add", 2, 1); + let code = generate_for_block_machine(input, "Add", 2, 1).unwrap().code; assert_eq!( - format_code(&code.unwrap()), + format_code(&code), "Add::sel[0] = 1; Add::a[0] = params[0]; Add::b[0] = params[1]; @@ -266,9 +265,15 @@ params[2] = Add::c[0];" #[test] fn binary() { let input = read_to_string("../test_data/pil/binary.pil").unwrap(); - let code = generate_for_block_machine(&input, "main_binary", 3, 1).unwrap(); + let result = generate_for_block_machine(&input, "main_binary", 3, 1).unwrap(); + let [op_rc, a_rc, b_rc, c_rc] = &result.range_constraints.try_into().unwrap(); + assert_eq!(op_rc, &RangeConstraint::from_range(0.into(), 2.into())); + // TODO why are they not constrained to four bytes? + assert_eq!(a_rc, &RangeConstraint::default()); + assert_eq!(b_rc, &RangeConstraint::default()); + assert_eq!(c_rc, &RangeConstraint::from_mask(0xffffffffu64)); assert_eq!( - format_code(&code), + format_code(&result.code), "main_binary::sel[0][3] = 1; main_binary::operation_id[3] = params[0]; main_binary::A[3] = params[1]; diff --git a/executor/src/witgen/jit/function_cache.rs b/executor/src/witgen/jit/function_cache.rs index f68dd1ad04..74cd4063da 100644 --- a/executor/src/witgen/jit/function_cache.rs +++ b/executor/src/witgen/jit/function_cache.rs @@ -6,7 +6,9 @@ use powdr_number::{FieldElement, KnownField}; use crate::witgen::{ data_structures::finalizable_data::{ColumnLayout, CompactDataRef}, + jit::processor::ProcessorResult, machines::{LookupCell, MachineParts}, + range_constraints::RangeConstraint, EvalError, FixedData, MutableState, QueryCallback, }; @@ -26,9 +28,9 @@ struct CacheKey { pub struct FunctionCache<'a, T: FieldElement> { /// The processor that generates the JIT code processor: BlockMachineProcessor<'a, T>, - /// The cache of JIT functions. If the entry is None, we attempted to generate the function - /// but failed. - witgen_functions: HashMap>>, + /// The cache of JIT functions and the returned range constraints. + /// If the entry is None, we attempted to generate the function but failed. + witgen_functions: HashMap, Vec>)>>, column_layout: ColumnLayout, block_size: usize, machine_name: String, @@ -58,19 +60,19 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> { } /// Compiles the JIT function for the given identity and known arguments. - /// Returns true if the function was successfully compiled. + /// Returns the function and the output range constraints if the function was successfully compiled. pub fn compile_cached( &mut self, can_process: impl CanProcessCall, identity_id: u64, known_args: &BitVec, - ) -> &Option> { + ) -> Option<&(WitgenFunction, Vec>)> { let cache_key = CacheKey { identity_id, known_args: known_args.clone(), }; self.ensure_cache(can_process, &cache_key); - self.witgen_functions.get(&cache_key).unwrap() + self.witgen_functions.get(&cache_key).unwrap().as_ref() } fn ensure_cache(&mut self, can_process: impl CanProcessCall, cache_key: &CacheKey) { @@ -92,7 +94,7 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> { &self, can_process: impl CanProcessCall, cache_key: &CacheKey, - ) -> Option> { + ) -> Option<(WitgenFunction, Vec>)> { log::debug!( "Compiling JIT function for\n Machine: {}\n Connection: {}\n Inputs: {:?}", self.machine_name, @@ -109,7 +111,7 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> { e }) .ok() - .map(|code| { + .map(|ProcessorResult{code, range_constraints}| { log::debug!("=> Success!"); let is_in_bounds = code .iter() @@ -131,13 +133,15 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> { log::trace!("Compiling effects..."); + let function = compile_effects( self.column_layout.first_column_id, self.column_layout.column_count, &known_inputs, &code, ) - .unwrap() + .unwrap(); + (function, range_constraints) }) } @@ -158,7 +162,7 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> { known_args, }; - let f = self + let (f, _rc) = self .witgen_functions .get(&cache_key) .expect("Need to call compile_cached() first!") diff --git a/executor/src/witgen/jit/interpreter.rs b/executor/src/witgen/jit/interpreter.rs index 556f13bef6..cfe10a0076 100644 --- a/executor/src/witgen/jit/interpreter.rs +++ b/executor/src/witgen/jit/interpreter.rs @@ -497,7 +497,8 @@ mod test { let effects = processor .generate_code(&mutable_state, connection_id, &known_values) - .unwrap(); + .unwrap() + .code; let known_inputs = (0..12).map(Variable::Param).collect::>(); diff --git a/executor/src/witgen/jit/processor.rs b/executor/src/witgen/jit/processor.rs index a31477af1d..ed36e04ddd 100644 --- a/executor/src/witgen/jit/processor.rs +++ b/executor/src/witgen/jit/processor.rs @@ -14,7 +14,7 @@ use powdr_ast::{ }; use powdr_number::FieldElement; -use crate::witgen::{jit::debug_formatter::format_identities, FixedData}; +use crate::witgen::{range_constraints::RangeConstraint, {jit::debug_formatter::format_identities, FixedData}}; use super::{ affine_symbolic_expression, @@ -39,10 +39,20 @@ pub struct Processor<'a, T: FieldElement, FixedEval> { /// List of variables we want to be known at the end. One of them not being known /// is a failure. requested_known_vars: Vec, + /// List of variables we want to know the derived range constraints of at the very end + /// (for every branch). + requested_range_constraints: Vec, /// Maximum branch depth allowed. max_branch_depth: usize, } +pub struct ProcessorResult { + /// Generated code. + pub code: Vec>, + /// Range constrainst of the variables they were requested on. + pub range_constraints: Vec>, +} + impl<'a, T: FieldElement, FixedEval: FixedEvaluator> Processor<'a, T, FixedEval> { pub fn new( fixed_data: &'a FixedData<'a, T>, @@ -51,6 +61,7 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> Processor<'a, T, FixedEv block_size: usize, check_block_shape: bool, requested_known_vars: impl IntoIterator, + requested_range_constraints: impl IntoIterator, max_branch_depth: usize, ) -> Self { let identities = identities.into_iter().collect_vec(); @@ -63,6 +74,7 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> Processor<'a, T, FixedEv block_size, check_block_shape, requested_known_vars: requested_known_vars.into_iter().collect(), + requested_range_constraints: requested_range_constraints.into_iter().collect(), max_branch_depth, } } @@ -71,7 +83,7 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> Processor<'a, T, FixedEv &self, can_process: impl CanProcessCall, witgen: WitgenInference<'a, T, FixedEval>, - ) -> Result>, Error<'a, T, FixedEval>> { + ) -> Result, Error<'a, T, FixedEval>> { let branch_depth = 0; self.generate_code_for_branch(can_process, witgen, branch_depth) } @@ -81,7 +93,7 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> Processor<'a, T, FixedEv can_process: impl CanProcessCall, mut witgen: WitgenInference<'a, T, FixedEval>, branch_depth: usize, - ) -> Result>, Error<'a, T, FixedEval>> { + ) -> Result, Error<'a, T, FixedEval>> { if self .process_until_no_progress(can_process.clone(), &mut witgen) .is_err() @@ -114,7 +126,16 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> Processor<'a, T, FixedEv let incomplete_machine_calls = self.incomplete_machine_calls(&witgen); if missing_variables.is_empty() && incomplete_machine_calls.is_empty() { - return Ok(witgen.finish()); + let range_constraints = self + .requested_range_constraints + .iter() + .map(|var| witgen.range_constraint(var)) + .collect(); + let code = witgen.finish(); + return Ok(ProcessorResult { + code, + range_constraints, + }); } // We need to do some work, try to branch. @@ -169,29 +190,49 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> Processor<'a, T, FixedEv self.generate_code_for_branch(can_process.clone(), first_branch, branch_depth + 1); let second_branch_result = self.generate_code_for_branch(can_process, second_branch, branch_depth + 1); - let result = match (first_branch_result, second_branch_result) { + let mut result = match (first_branch_result, second_branch_result) { (Err(e), other) | (other, Err(e)) if e.reason == ErrorReason::ConflictingConstraints => { // Any branch with a conflicting constraint is not reachable and thus - // can be pruned. We do not branch but still add the range constraint. - // Note that the other branch might also have a conflicting constraint, - // but then it is correct to return it. + // can be pruned. We do not branch but still add the range constraint + // of the branching variable. + // Note that both branches might actually have a conflicting constraint, + // but then it is correct to return one. log::trace!("Branching on {most_constrained_var} resulted in a conflict, we can reduce to a single branch."); - other + other? } // Any other error should be propagated. - (Err(e), _) | (_, Err(e)) => Err(e), - (Ok(first_code), Ok(second_code)) if first_code == second_code => { + (Err(e), _) | (_, Err(e)) => Err(e)?, + (Ok(first_result), Ok(second_result)) if first_result.code == second_result.code => { log::trace!("Branching on {most_constrained_var} resulted in the same code, we can reduce to a single branch."); - Ok(first_code) + ProcessorResult { + code: first_result.code, + range_constraints: combine_range_constraints( + &first_result.range_constraints, + &second_result.range_constraints, + ), + } } - (Ok(first_code), Ok(second_code)) => { - Ok(vec![Effect::Branch(condition, first_code, second_code)]) + (Ok(first_result), Ok(second_result)) => { + let code = vec![Effect::Branch( + condition, + first_result.code, + second_result.code, + )]; + let range_constraints = combine_range_constraints( + &first_result.range_constraints, + &second_result.range_constraints, + ); + ProcessorResult { + code, + range_constraints, + } } }; // Prepend the common code in the success case. - result.map(|code| common_code.into_iter().chain(code).collect()) + result.code = common_code.into_iter().chain(result.code).collect(); + Ok(result) } fn process_until_no_progress( @@ -327,6 +368,14 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> Processor<'a, T, FixedEv .iter() .map(|&r| format!(" row {r}: {}\n", witgen.value(&cell_var(r)))) .format(""); + log::debug!( + "Code generated so far:\n{}\n\ + Column {column_name} is not stackable in a {}-row block, \ + conflict in rows {row} and {}.\n{row_vals}", + format_code(witgen.code()), + self.block_size, + row + self.block_size as i32 + ); panic!( "Column {column_name} is not stackable in a {}-row block, conflict in rows {row} and {}.\n{row_vals}", self.block_size, @@ -476,6 +525,17 @@ fn is_machine_call(identity: &Identity) -> bool { } } +fn combine_range_constraints( + first: &[RangeConstraint], + second: &[RangeConstraint], +) -> Vec> { + first + .iter() + .zip(second.iter()) + .map(|(rc1, rc2)| rc1.disjunction(rc2)) + .collect() +} + pub struct Error<'a, T: FieldElement, FixedEval: FixedEvaluator> { pub reason: ErrorReason, pub witgen: WitgenInference<'a, T, FixedEval>, diff --git a/executor/src/witgen/jit/single_step_processor.rs b/executor/src/witgen/jit/single_step_processor.rs index f9f9d2abfc..b140c927ef 100644 --- a/executor/src/witgen/jit/single_step_processor.rs +++ b/executor/src/witgen/jit/single_step_processor.rs @@ -86,10 +86,12 @@ impl<'a, T: FieldElement> SingleStepProcessor<'a, T> { block_size, false, requested_known, + std::iter::empty(), SINGLE_STEP_MACHINE_MAX_BRANCH_DEPTH, ) .generate_code(can_process, witgen) .map_err(|e| e.to_string()) + .map(|r| r.code) } fn cell(&self, id: PolyID, row_offset: i32) -> Variable { diff --git a/executor/src/witgen/jit/witgen_inference.rs b/executor/src/witgen/jit/witgen_inference.rs index de17d34648..c67a66f6c4 100644 --- a/executor/src/witgen/jit/witgen_inference.rs +++ b/executor/src/witgen/jit/witgen_inference.rs @@ -20,6 +20,7 @@ use crate::witgen::{ use super::{ affine_symbolic_expression::{AffineSymbolicExpression, Error, ProcessResult}, + debug_formatter::format_identities, effect::{BranchCondition, Effect}, variable::{MachineCallVariable, Variable}, }; @@ -173,12 +174,20 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> WitgenInference<'a, T, F row_offset: i32, ) -> Result, Error> { let result = match id { - Identity::Polynomial(PolynomialIdentity { expression, .. }) => self - .process_equality_on_row( + Identity::Polynomial(PolynomialIdentity { expression, .. }) => { + let r = self.process_equality_on_row( expression, row_offset, &VariableOrValue::Value(T::from(0)), - )?, + ); + if id.id() == 2 { + println!( + "{}", + format_identities(&[(id, row_offset)], self, self.fixed_evaluator.clone()) + ); + } + r? + } Identity::Lookup(LookupIdentity { id, left, .. }) | Identity::Permutation(PermutationIdentity { id, left, .. }) | Identity::PhantomPermutation(PhantomPermutationIdentity { id, left, .. }) @@ -237,6 +246,7 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> WitgenInference<'a, T, F let evaluator = Evaluator::new(self); let Some(lhs_evaluated) = evaluator.evaluate(lhs, offset) else { + println!("Cannot evaluate."); return Ok(ProcessResult::empty()); }; @@ -245,17 +255,15 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> WitgenInference<'a, T, F VariableOrValue::Value(v) => (*v).into(), }; - let result = (lhs_evaluated - rhs_evaluated).solve()?; - if result.complete && result.effects.is_empty() { + let mut result = (lhs_evaluated - rhs_evaluated).solve()?; + { // A complete result without effects means that there were no unknowns // in the constraint. // We try again, but this time we treat all non-concrete variables // as unknown and in that way try to find new concrete values for // already known variables. - let result = self.process_equality_on_row_concrete(lhs, offset, rhs)?; - if !result.effects.is_empty() { - return Ok(result); - } + let concrete_result = self.process_equality_on_row_concrete(lhs, offset, rhs)?; + result.effects.extend(concrete_result.effects); } Ok(result) } @@ -444,6 +452,7 @@ impl<'a, T: FieldElement, FixedEval: FixedEvaluator> WitgenInference<'a, T, F if rc == old_rc { return false; } + println!("Range constraint: {variable} <= {rc}"); if !self.known_variables.contains(&variable) { if let Some(v) = rc.try_to_single_value() { // Special case: Variable is fixed to a constant by range constraints only. diff --git a/executor/src/witgen/machines/block_machine.rs b/executor/src/witgen/machines/block_machine.rs index 3e45203ff8..6376c0d40f 100644 --- a/executor/src/witgen/machines/block_machine.rs +++ b/executor/src/witgen/machines/block_machine.rs @@ -13,13 +13,16 @@ use crate::witgen::data_structures::caller_data::CallerData; use crate::witgen::data_structures::finalizable_data::FinalizableData; use crate::witgen::data_structures::mutable_state::MutableState; use crate::witgen::jit::function_cache::FunctionCache; +use crate::witgen::jit::witgen_inference::CanProcessCall; use crate::witgen::processor::{OuterQuery, Processor, SolverState}; +use crate::witgen::range_constraints::RangeConstraint; use crate::witgen::rows::{Row, RowIndex, RowPair}; use crate::witgen::sequence_iterator::{ DefaultSequenceIterator, ProcessingSequenceCache, ProcessingSequenceIterator, }; use crate::witgen::util::try_to_simple_poly; use crate::witgen::{machines::Machine, EvalError, EvalValue, IncompleteCause, QueryCallback}; +use bit_vec::BitVec; use powdr_ast::analyzed::{DegreeRange, PolyID, PolynomialType}; use powdr_number::{DegreeType, FieldElement}; @@ -160,13 +163,44 @@ impl<'a, T: FieldElement> Machine<'a, T> for BlockMachine<'a, T> { self.parts.connections.keys().copied().collect() } + fn can_process_call_fully( + &mut self, + can_process: impl CanProcessCall, + identity_id: u64, + known_arguments: &BitVec, + _range_constraints: &[RangeConstraint], + ) -> Option>> { + // We ignore the incoming range constraints because we would have to + // make them part of the cache key otherwise. + if let Some((_, updated_range_constraints)) = + self.function_cache + .compile_cached(can_process, identity_id, known_arguments) + { + Some(updated_range_constraints.clone()) + } else { + None + } + } + fn process_lookup_direct<'b, 'c, Q: QueryCallback>( &mut self, - _mutable_state: &'b MutableState<'a, T, Q>, - _identity_id: u64, - _values: &mut [LookupCell<'c, T>], + mutable_state: &'b MutableState<'a, T, Q>, + identity_id: u64, + values: &mut [LookupCell<'c, T>], ) -> Result> { - unimplemented!("Direct lookup not supported by machine {}.", self.name()) + if self.rows() + self.block_size as DegreeType > self.degree { + return Err(EvalError::RowsExhausted(self.name.clone())); + } + + self.data.finalize_all(); + let data = self.data.append_new_finalized_rows(self.block_size); + + let success = + self.function_cache + .process_lookup_direct(mutable_state, identity_id, values, data)?; + assert!(success); + self.block_count_jit += 1; + Ok(true) } fn process_plookup<'b, Q: QueryCallback>( diff --git a/executor/src/witgen/machines/double_sorted_witness_machine_32.rs b/executor/src/witgen/machines/double_sorted_witness_machine_32.rs index f947292ba4..a58eaee874 100644 --- a/executor/src/witgen/machines/double_sorted_witness_machine_32.rs +++ b/executor/src/witgen/machines/double_sorted_witness_machine_32.rs @@ -7,6 +7,7 @@ use itertools::Itertools; use super::{LookupCell, Machine, MachineParts}; use crate::witgen::data_structures::caller_data::CallerData; use crate::witgen::data_structures::mutable_state::MutableState; +use crate::witgen::jit::witgen_inference::CanProcessCall; use crate::witgen::machines::compute_size_and_log; use crate::witgen::processor::OuterQuery; use crate::witgen::range_constraints::RangeConstraint; @@ -188,6 +189,7 @@ impl<'a, T: FieldElement> DoubleSortedWitnesses32<'a, T> { impl<'a, T: FieldElement> Machine<'a, T> for DoubleSortedWitnesses32<'a, T> { fn can_process_call_fully( &mut self, + _can_process: impl CanProcessCall, identity_id: u64, known_arguments: &BitVec, range_constraints: &[RangeConstraint], diff --git a/executor/src/witgen/machines/fixed_lookup_machine.rs b/executor/src/witgen/machines/fixed_lookup_machine.rs index 9fda285c5c..a4ca37ace8 100644 --- a/executor/src/witgen/machines/fixed_lookup_machine.rs +++ b/executor/src/witgen/machines/fixed_lookup_machine.rs @@ -11,6 +11,7 @@ use crate::witgen::affine_expression::{AffineExpression, AlgebraicVariable}; use crate::witgen::data_structures::caller_data::CallerData; use crate::witgen::data_structures::mutable_state::MutableState; use crate::witgen::global_constraints::{GlobalConstraints, RangeConstraintSet}; +use crate::witgen::jit::witgen_inference::CanProcessCall; use crate::witgen::processor::OuterQuery; use crate::witgen::range_constraints::RangeConstraint; use crate::witgen::rows::RowPair; @@ -296,6 +297,7 @@ impl<'a, T: FieldElement> Machine<'a, T> for FixedLookup<'a, T> { fn can_process_call_fully( &mut self, + _can_process: impl CanProcessCall, identity_id: u64, known_arguments: &BitVec, range_constraints: &[RangeConstraint], diff --git a/executor/src/witgen/machines/mod.rs b/executor/src/witgen/machines/mod.rs index 91f7cdd86e..be0b53beee 100644 --- a/executor/src/witgen/machines/mod.rs +++ b/executor/src/witgen/machines/mod.rs @@ -22,6 +22,7 @@ use self::second_stage_machine::SecondStageMachine; use self::sorted_witness_machine::SortedWitnesses; use self::write_once_memory::WriteOnceMemory; +use super::jit::witgen_inference::CanProcessCall; use super::range_constraints::RangeConstraint; use super::rows::RowPair; use super::{EvalError, EvalResult, FixedData, QueryCallback}; @@ -67,6 +68,7 @@ pub trait Machine<'a, T: FieldElement>: Send + Sync { /// or something similar. fn can_process_call_fully( &mut self, + _can_process: impl CanProcessCall, _identity_id: u64, _known_arguments: &BitVec, _range_constraints: &[RangeConstraint], @@ -187,35 +189,60 @@ impl<'a, T: FieldElement> Machine<'a, T> for KnownMachine<'a, T> { fn can_process_call_fully( &mut self, + can_process: impl CanProcessCall, identity_id: u64, known_arguments: &BitVec, range_constraints: &[RangeConstraint], ) -> Option>> { match self { - KnownMachine::SecondStageMachine(m) => { - m.can_process_call_fully(identity_id, known_arguments, range_constraints) - } - KnownMachine::SortedWitnesses(m) => { - m.can_process_call_fully(identity_id, known_arguments, range_constraints) - } - KnownMachine::DoubleSortedWitnesses16(m) => { - m.can_process_call_fully(identity_id, known_arguments, range_constraints) - } - KnownMachine::DoubleSortedWitnesses32(m) => { - m.can_process_call_fully(identity_id, known_arguments, range_constraints) - } - KnownMachine::WriteOnceMemory(m) => { - m.can_process_call_fully(identity_id, known_arguments, range_constraints) - } - KnownMachine::BlockMachine(m) => { - m.can_process_call_fully(identity_id, known_arguments, range_constraints) - } - KnownMachine::DynamicMachine(m) => { - m.can_process_call_fully(identity_id, known_arguments, range_constraints) - } - KnownMachine::FixedLookup(m) => { - m.can_process_call_fully(identity_id, known_arguments, range_constraints) - } + KnownMachine::SecondStageMachine(m) => m.can_process_call_fully( + can_process, + identity_id, + known_arguments, + range_constraints, + ), + KnownMachine::SortedWitnesses(m) => m.can_process_call_fully( + can_process, + identity_id, + known_arguments, + range_constraints, + ), + KnownMachine::DoubleSortedWitnesses16(m) => m.can_process_call_fully( + can_process, + identity_id, + known_arguments, + range_constraints, + ), + KnownMachine::DoubleSortedWitnesses32(m) => m.can_process_call_fully( + can_process, + identity_id, + known_arguments, + range_constraints, + ), + KnownMachine::WriteOnceMemory(m) => m.can_process_call_fully( + can_process, + identity_id, + known_arguments, + range_constraints, + ), + KnownMachine::BlockMachine(m) => m.can_process_call_fully( + can_process, + identity_id, + known_arguments, + range_constraints, + ), + KnownMachine::DynamicMachine(m) => m.can_process_call_fully( + can_process, + identity_id, + known_arguments, + range_constraints, + ), + KnownMachine::FixedLookup(m) => m.can_process_call_fully( + can_process, + identity_id, + known_arguments, + range_constraints, + ), } } diff --git a/executor/src/witgen/range_constraints.rs b/executor/src/witgen/range_constraints.rs index e68e07b988..fa828a5891 100644 --- a/executor/src/witgen/range_constraints.rs +++ b/executor/src/witgen/range_constraints.rs @@ -180,6 +180,32 @@ impl RangeConstraint { Self { min, max, mask } } + /// Returns the disjunciton of this constraint and the other. + pub fn disjunction(&self, other: &Self) -> Self { + let mask = self.mask | other.mask; + match (self.min <= self.max, other.min <= other.max) { + (true, true) => Self { + min: cmp::min(self.min, other.min), + max: cmp::max(self.max, other.max), + mask, + }, + (true, false) | (false, true) => { + // These cases are too complicated - we could refine them in the future. + Self::from_mask(mask) + } + (false, false) => { + let min = cmp::min(self.min, other.min); + let max = cmp::max(self.max, other.max); + if min <= max { + // The ranges cover the full field. + Self::from_mask(mask) + } else { + Self { min, max, mask } + } + } + } + } + /// The constraint of an integer multiple of an expression. pub fn multiple(&self, factor: T) -> Self { let mask = log2_exact(factor.to_arbitrary_integer()).and_then(|exponent| {