1
1
use std:: collections:: { BTreeMap , HashMap , HashSet } ;
2
2
use std:: iter:: once;
3
3
4
+ use bit_vec:: BitVec ;
4
5
use itertools:: Itertools ;
5
6
6
7
use super :: { LookupCell , Machine , MachineParts } ;
8
+ use crate :: witgen:: data_structures:: caller_data:: CallerData ;
7
9
use crate :: witgen:: data_structures:: mutable_state:: MutableState ;
8
10
use crate :: witgen:: global_constraints:: RangeConstraintSet ;
11
+ use crate :: witgen:: jit:: witgen_inference:: CanProcessCall ;
9
12
use crate :: witgen:: machines:: compute_size_and_log;
13
+ use crate :: witgen:: processor:: OuterQuery ;
14
+ use crate :: witgen:: range_constraints:: RangeConstraint ;
10
15
use crate :: witgen:: util:: try_to_simple_poly;
11
16
use crate :: witgen:: {
12
17
AffineExpression , AlgebraicVariable , EvalError , EvalResult , FixedData , QueryCallback ,
@@ -87,6 +92,7 @@ pub struct DoubleSortedWitnesses16<'a, T: FieldElement> {
87
92
//witness_positions: HashMap<String, usize>,
88
93
/// (addr, step) -> (value1, value2)
89
94
trace : BTreeMap < ( Word32 < T > , Word32 < T > ) , Operation < T > > ,
95
+ // TODO make this paged for performance.
90
96
data : BTreeMap < u64 , u64 > ,
91
97
is_initialized : BTreeMap < Word32 < T > , bool > ,
92
98
namespace : String ,
@@ -98,6 +104,7 @@ pub struct DoubleSortedWitnesses16<'a, T: FieldElement> {
98
104
has_bootloader_write_column : bool ,
99
105
/// All selector IDs that are used on the right-hand side connecting identities, by bus ID.
100
106
selector_ids : BTreeMap < T , PolyID > ,
107
+ latest_step : BTreeMap < u64 , T > ,
101
108
}
102
109
103
110
struct Operation < T > {
@@ -213,18 +220,50 @@ impl<'a, T: FieldElement> DoubleSortedWitnesses16<'a, T> {
213
220
data : Default :: default ( ) ,
214
221
is_initialized : Default :: default ( ) ,
215
222
selector_ids,
223
+ latest_step : Default :: default ( ) ,
216
224
} )
217
225
}
218
226
}
219
227
220
228
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
+
221
260
fn process_lookup_direct < ' b , ' c , Q : QueryCallback < T > > (
222
261
& mut self ,
223
262
_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 > ] ,
226
265
) -> Result < bool , EvalError < T > > {
227
- unimplemented ! ( "Direct lookup not supported by machine {}." , self . name ( ) )
266
+ self . process_call_internal ( bus_id , values )
228
267
}
229
268
230
269
fn bus_ids ( & self ) -> Vec < T > {
@@ -237,12 +276,37 @@ impl<'a, T: FieldElement> Machine<'a, T> for DoubleSortedWitnesses16<'a, T> {
237
276
238
277
fn process_plookup < Q : QueryCallback < T > > (
239
278
& mut self ,
240
- _mutable_state : & MutableState < ' a , T , Q > ,
279
+ mutable_state : & MutableState < ' a , T , Q > ,
241
280
bus_id : T ,
242
281
arguments : & [ AffineExpression < AlgebraicVariable < ' a > , T > ] ,
243
282
range_constraints : & dyn RangeConstraintSet < AlgebraicVariable < ' a > , T > ,
244
283
) -> 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
+ }
246
310
}
247
311
248
312
fn take_witness_col_values < ' b , Q : QueryCallback < T > > (
@@ -413,12 +477,11 @@ impl<'a, T: FieldElement> Machine<'a, T> for DoubleSortedWitnesses16<'a, T> {
413
477
}
414
478
415
479
impl < ' a , T : FieldElement > DoubleSortedWitnesses16 < ' a , T > {
416
- pub fn process_plookup_internal (
480
+ fn process_call_internal (
417
481
& mut self ,
418
482
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 > > {
422
485
// We blindly assume the lookup is of the form
423
486
// OP { operation_id, ADDR_high, ADDR_low, STEP, X_high, X_low } is
424
487
// <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> {
427
490
// - operation_id == 1: Write
428
491
// - operation_id == 2: Bootloader write
429
492
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 ! ( ) ;
437
513
} ;
438
514
439
515
let selector_id = * self . selector_ids . get ( & bus_id) . unwrap ( ) ;
440
516
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 ) ;
443
519
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 ( ) ;
454
520
455
521
if self . has_bootloader_write_column {
456
522
let is_initialized = self . is_initialized . get ( & addr) . cloned ( ) . unwrap_or_default ( ) ;
@@ -460,28 +526,13 @@ impl<'a, T: FieldElement> DoubleSortedWitnesses16<'a, T> {
460
526
self . is_initialized . insert ( addr, true ) ;
461
527
}
462
528
463
- let step = arguments[ 3 ]
464
- . constant_value ( )
465
- . ok_or_else ( || format ! ( "Step must be known but is: {}" , arguments[ 3 ] ) ) ?;
466
529
let step_word = Word32 :: from ( step. to_degree ( ) ) ;
467
530
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
-
475
531
// TODO this does not check any of the failure modes
476
- let mut assignments = EvalValue :: complete ( vec ! [ ] ) ;
477
532
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 ) ,
485
536
} ;
486
537
487
538
let value_int: u64 = value. into ( ) ;
@@ -509,12 +560,26 @@ impl<'a, T: FieldElement> DoubleSortedWitnesses16<'a, T> {
509
560
let value_low_fe: T = value_low. into ( ) ;
510
561
let value_high_fe: T = value_high. into ( ) ;
511
562
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
+ }
518
583
self . trace
519
584
. insert (
520
585
( addr, step_word) ,
@@ -527,16 +592,26 @@ impl<'a, T: FieldElement> DoubleSortedWitnesses16<'a, T> {
527
592
)
528
593
. is_none ( )
529
594
} ;
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}!\n From receive: {}" ,
601
+ self . parts. bus_receives[ & bus_id]
602
+ ) ;
603
+ }
604
+ self . latest_step . insert ( addr_int, * step) ;
605
+
530
606
assert ! (
531
607
has_side_effect,
532
608
"Already had a memory access for address 0x{addr_int:x} and time step {step}!"
533
609
) ;
534
- assignments = assignments. report_side_effect ( ) ;
535
610
536
611
if self . trace . len ( ) > ( self . degree as usize ) {
537
612
return Err ( EvalError :: RowsExhausted ( self . name . clone ( ) ) ) ;
538
613
}
539
614
540
- Ok ( assignments )
615
+ Ok ( true )
541
616
}
542
617
}
0 commit comments