Skip to content

Commit 1b68a16

Browse files
committed
Modernize 16 bit memory.
1 parent 28307fb commit 1b68a16

File tree

1 file changed

+129
-54
lines changed

1 file changed

+129
-54
lines changed

executor/src/witgen/machines/double_sorted_witness_machine_16.rs

+129-54
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
use std::collections::{BTreeMap, HashMap, HashSet};
22
use std::iter::once;
33

4+
use bit_vec::BitVec;
45
use itertools::Itertools;
56

67
use super::{LookupCell, Machine, MachineParts};
8+
use crate::witgen::data_structures::caller_data::CallerData;
79
use crate::witgen::data_structures::mutable_state::MutableState;
810
use crate::witgen::global_constraints::RangeConstraintSet;
11+
use crate::witgen::jit::witgen_inference::CanProcessCall;
912
use crate::witgen::machines::compute_size_and_log;
13+
use crate::witgen::processor::OuterQuery;
14+
use crate::witgen::range_constraints::RangeConstraint;
1015
use crate::witgen::util::try_to_simple_poly;
1116
use crate::witgen::{
1217
AffineExpression, AlgebraicVariable, EvalError, EvalResult, FixedData, QueryCallback,
@@ -87,6 +92,7 @@ pub struct DoubleSortedWitnesses16<'a, T: FieldElement> {
8792
//witness_positions: HashMap<String, usize>,
8893
/// (addr, step) -> (value1, value2)
8994
trace: BTreeMap<(Word32<T>, Word32<T>), Operation<T>>,
95+
// TODO make this paged for performance.
9096
data: BTreeMap<u64, u64>,
9197
is_initialized: BTreeMap<Word32<T>, bool>,
9298
namespace: String,
@@ -98,6 +104,7 @@ pub struct DoubleSortedWitnesses16<'a, T: FieldElement> {
98104
has_bootloader_write_column: bool,
99105
/// All selector IDs that are used on the right-hand side connecting identities, by bus ID.
100106
selector_ids: BTreeMap<T, PolyID>,
107+
latest_step: BTreeMap<u64, T>,
101108
}
102109

103110
struct Operation<T> {
@@ -213,18 +220,50 @@ impl<'a, T: FieldElement> DoubleSortedWitnesses16<'a, T> {
213220
data: Default::default(),
214221
is_initialized: Default::default(),
215222
selector_ids,
223+
latest_step: Default::default(),
216224
})
217225
}
218226
}
219227

220228
impl<'a, T: FieldElement> Machine<'a, T> for DoubleSortedWitnesses16<'a, T> {
229+
fn can_process_call_fully(
230+
&mut self,
231+
_can_process: impl CanProcessCall<T>,
232+
bus_id: T,
233+
known_arguments: &BitVec,
234+
range_constraints: Vec<RangeConstraint<T>>,
235+
) -> (bool, Vec<RangeConstraint<T>>) {
236+
assert!(self.parts.bus_receives.contains_key(&bus_id));
237+
assert_eq!(known_arguments.len(), 6);
238+
assert_eq!(range_constraints.len(), 6);
239+
240+
// We blindly assume the parameters to be operation_id, high_addr, low_addr, step, value_high, value_low
241+
242+
// We need to known operation_id, step, high_addr and low_addr for all calls.
243+
if !known_arguments[0] || !known_arguments[1] || !known_arguments[2] || !known_arguments[3]
244+
{
245+
return (false, range_constraints);
246+
}
247+
248+
// For the value, it depends: If we write, we need to know it, if we read we do not need to know it.
249+
let can_answer = if known_arguments[4] && known_arguments[5] {
250+
// It is known, so we are good anyway.
251+
true
252+
} else {
253+
// It is not known, so we can only process if we do not write.
254+
!range_constraints[0].allows_value(T::from(OPERATION_ID_BOOTLOADER_WRITE))
255+
&& !range_constraints[0].allows_value(T::from(OPERATION_ID_WRITE))
256+
};
257+
(can_answer, range_constraints)
258+
}
259+
221260
fn process_lookup_direct<'b, 'c, Q: QueryCallback<T>>(
222261
&mut self,
223262
_mutable_state: &'b MutableState<'a, T, Q>,
224-
_bus_id: T,
225-
_values: &mut [LookupCell<'c, T>],
263+
bus_id: T,
264+
values: &mut [LookupCell<'c, T>],
226265
) -> Result<bool, EvalError<T>> {
227-
unimplemented!("Direct lookup not supported by machine {}.", self.name())
266+
self.process_call_internal(bus_id, values)
228267
}
229268

230269
fn bus_ids(&self) -> Vec<T> {
@@ -237,12 +276,37 @@ impl<'a, T: FieldElement> Machine<'a, T> for DoubleSortedWitnesses16<'a, T> {
237276

238277
fn process_plookup<Q: QueryCallback<T>>(
239278
&mut self,
240-
_mutable_state: &MutableState<'a, T, Q>,
279+
mutable_state: &MutableState<'a, T, Q>,
241280
bus_id: T,
242281
arguments: &[AffineExpression<AlgebraicVariable<'a>, T>],
243282
range_constraints: &dyn RangeConstraintSet<AlgebraicVariable<'a>, T>,
244283
) -> EvalResult<'a, T> {
245-
self.process_plookup_internal(bus_id, arguments, range_constraints)
284+
let receives = self.parts.bus_receives[&bus_id];
285+
let outer_query = OuterQuery::new(arguments, range_constraints, receives);
286+
let mut data = CallerData::from(&outer_query);
287+
if self.process_lookup_direct(mutable_state, bus_id, &mut data.as_lookup_cells())? {
288+
Ok(EvalResult::from(data)?.report_side_effect())
289+
} else {
290+
// One of the required arguments was not set, find out which:
291+
let data = data.as_lookup_cells();
292+
Ok(EvalValue::incomplete(
293+
IncompleteCause::NonConstantRequiredArgument(
294+
match (&data[0], &data[1], &data[2], &data[3], &data[4], &data[5]) {
295+
// m_addr_high, m_addr_low, m_step -> m_value1, m_value2;
296+
(LookupCell::Output(_), _, _, _, _, _) => "operation_id",
297+
(_, LookupCell::Output(_), _, _, _, _) => "m_addr_high",
298+
(_, _, LookupCell::Output(_), _, _, _) => "m_addr_low",
299+
(_, _, _, LookupCell::Output(_), _, _) => "m_step",
300+
// Note that for the mload operation, we'd expect these to be a outputs.
301+
// But since process_lookup_direct returned false and all other arguments are set,
302+
// we must have been in the mstore operation, in which case these values are required.
303+
(_, _, _, _, LookupCell::Output(_), _) => "m_value1",
304+
(_, _, _, _, _, LookupCell::Output(_)) => "m_value2",
305+
_ => unreachable!(),
306+
},
307+
),
308+
))
309+
}
246310
}
247311

248312
fn take_witness_col_values<'b, Q: QueryCallback<T>>(
@@ -413,12 +477,11 @@ impl<'a, T: FieldElement> Machine<'a, T> for DoubleSortedWitnesses16<'a, T> {
413477
}
414478

415479
impl<'a, T: FieldElement> DoubleSortedWitnesses16<'a, T> {
416-
pub fn process_plookup_internal(
480+
fn process_call_internal(
417481
&mut self,
418482
bus_id: T,
419-
arguments: &[AffineExpression<AlgebraicVariable<'a>, T>],
420-
range_constraints: &dyn RangeConstraintSet<AlgebraicVariable<'a>, T>,
421-
) -> EvalResult<'a, T> {
483+
values: &mut [LookupCell<'_, T>],
484+
) -> Result<bool, EvalError<T>> {
422485
// We blindly assume the lookup is of the form
423486
// OP { operation_id, ADDR_high, ADDR_low, STEP, X_high, X_low } is
424487
// <selector> { operation_id, m_addr_high, m_addr_low, m_step_high * 2**16 + m_step_low, m_value_high, m_value_low }
@@ -427,30 +490,33 @@ impl<'a, T: FieldElement> DoubleSortedWitnesses16<'a, T> {
427490
// - operation_id == 1: Write
428491
// - operation_id == 2: Bootloader write
429492

430-
let operation_id = match arguments[0].constant_value() {
431-
Some(v) => v,
432-
None => {
433-
return Ok(EvalValue::incomplete(
434-
IncompleteCause::NonConstantRequiredArgument("operation_id"),
435-
))
436-
}
493+
let operation_id = match values[0] {
494+
LookupCell::Input(v) => v,
495+
LookupCell::Output(_) => return Ok(false),
496+
};
497+
let addr_high = match values[1] {
498+
LookupCell::Input(v) => v,
499+
LookupCell::Output(_) => return Ok(false),
500+
};
501+
let addr_low = match values[2] {
502+
LookupCell::Input(v) => v,
503+
LookupCell::Output(_) => return Ok(false),
504+
};
505+
let addr = Word32(*addr_high, *addr_low);
506+
let addr_int: u64 = addr.into();
507+
let step = match values[3] {
508+
LookupCell::Input(v) => v,
509+
LookupCell::Output(_) => return Ok(false),
510+
};
511+
let [value1_ptr, value2_ptr] = &mut values[4..=5] else {
512+
unreachable!();
437513
};
438514

439515
let selector_id = *self.selector_ids.get(&bus_id).unwrap();
440516

441-
let is_normal_write = operation_id == T::from(OPERATION_ID_WRITE);
442-
let is_bootloader_write = operation_id == T::from(OPERATION_ID_BOOTLOADER_WRITE);
517+
let is_normal_write = *operation_id == T::from(OPERATION_ID_WRITE);
518+
let is_bootloader_write = *operation_id == T::from(OPERATION_ID_BOOTLOADER_WRITE);
443519
let is_write = is_bootloader_write || is_normal_write;
444-
let addr = match (arguments[1].constant_value(), arguments[2].constant_value()) {
445-
(Some(high), Some(low)) => Word32(high, low),
446-
_ => {
447-
return Ok(EvalValue::incomplete(
448-
IncompleteCause::NonConstantRequiredArgument("m_addr"),
449-
))
450-
}
451-
};
452-
453-
let addr_int: u64 = addr.into();
454520

455521
if self.has_bootloader_write_column {
456522
let is_initialized = self.is_initialized.get(&addr).cloned().unwrap_or_default();
@@ -460,28 +526,13 @@ impl<'a, T: FieldElement> DoubleSortedWitnesses16<'a, T> {
460526
self.is_initialized.insert(addr, true);
461527
}
462528

463-
let step = arguments[3]
464-
.constant_value()
465-
.ok_or_else(|| format!("Step must be known but is: {}", arguments[3]))?;
466529
let step_word = Word32::from(step.to_degree());
467530

468-
let value1_expr = &arguments[4];
469-
let value2_expr = &arguments[5];
470-
471-
log::trace!(
472-
"Query addr=0x{addr_int:x}, step={step}, write: {is_write}, value: ({value1_expr} {value2_expr})"
473-
);
474-
475531
// TODO this does not check any of the failure modes
476-
let mut assignments = EvalValue::complete(vec![]);
477532
let has_side_effect = if is_write {
478-
let value = match (value1_expr.constant_value(), value2_expr.constant_value()) {
479-
(Some(high), Some(low)) => Word32(high, low),
480-
_ => {
481-
return Ok(EvalValue::incomplete(
482-
IncompleteCause::NonConstantRequiredArgument("m_value"),
483-
))
484-
}
533+
let value = match (value1_ptr, value2_ptr) {
534+
(LookupCell::Input(high), LookupCell::Input(low)) => Word32(**high, **low),
535+
_ => return Ok(false),
485536
};
486537

487538
let value_int: u64 = value.into();
@@ -509,12 +560,26 @@ impl<'a, T: FieldElement> DoubleSortedWitnesses16<'a, T> {
509560
let value_low_fe: T = value_low.into();
510561
let value_high_fe: T = value_high.into();
511562

512-
let ass = (value1_expr.clone() - value_high_fe.into())
513-
.solve_with_range_constraints(range_constraints)?;
514-
assignments.combine(ass);
515-
let ass2 = (value2_expr.clone() - value_low_fe.into())
516-
.solve_with_range_constraints(range_constraints)?;
517-
assignments.combine(ass2);
563+
match value1_ptr {
564+
LookupCell::Output(v) => **v = value_high_fe,
565+
LookupCell::Input(v) => {
566+
if *v != &value_high_fe {
567+
return Err(EvalError::ConstraintUnsatisfiable(format!(
568+
"{v} != {value_high_fe} (high) (address 0x{addr_int:x}, time step)"
569+
)));
570+
}
571+
}
572+
}
573+
match value2_ptr {
574+
LookupCell::Output(v) => **v = value_low_fe,
575+
LookupCell::Input(v) => {
576+
if *v != &value_low_fe {
577+
return Err(EvalError::ConstraintUnsatisfiable(format!(
578+
"{v} != {value_low_fe} (low) (address 0x{addr_int:x}, time step)"
579+
)));
580+
}
581+
}
582+
}
518583
self.trace
519584
.insert(
520585
(addr, step_word),
@@ -527,16 +592,26 @@ impl<'a, T: FieldElement> DoubleSortedWitnesses16<'a, T> {
527592
)
528593
.is_none()
529594
};
595+
596+
let latest_step = self.latest_step.entry(addr_int).or_insert_with(T::zero);
597+
598+
if step < latest_step {
599+
panic!(
600+
"Expected the step for addr 0x{addr_int:x} to be at least equal to the previous step, but {step} < {latest_step}!\nFrom receive: {}",
601+
self.parts.bus_receives[&bus_id]
602+
);
603+
}
604+
self.latest_step.insert(addr_int, *step);
605+
530606
assert!(
531607
has_side_effect,
532608
"Already had a memory access for address 0x{addr_int:x} and time step {step}!"
533609
);
534-
assignments = assignments.report_side_effect();
535610

536611
if self.trace.len() > (self.degree as usize) {
537612
return Err(EvalError::RowsExhausted(self.name.clone()));
538613
}
539614

540-
Ok(assignments)
615+
Ok(true)
541616
}
542617
}

0 commit comments

Comments
 (0)