@@ -313,7 +313,7 @@ fn build_eval<'ctx, 'this>(
313313 let circuit_data = entry. arg ( 3 ) ?;
314314 let circuit_modulus = entry. arg ( 4 ) ?;
315315
316- // arguments 5 and 6 are used to build the gate 0 (with constant value 1)
316+ // Arguments 5 and 6 are used to build the gate 0 (with constant value 1).
317317 // let zero = entry.argument(5)?;
318318 // let one = entry.argument(6)?;
319319
@@ -361,19 +361,11 @@ fn build_eval<'ctx, 'this>(
361361 circuit_info. mul_offsets . len ( ) * MUL_MOD_BUILTIN_SIZE ,
362362 ) ?;
363363
364- // convert circuit output from integer representation to struct representation
365- let gates = gates
366- . into_iter ( )
367- . map ( |value| u384_integer_to_struct ( context, ok_block, location, value) )
368- . collect :: < Result < Vec < _ > > > ( ) ?;
369-
370- // Calculate full capacity for array.
364+ // Calculate capacity for array.
371365 let outputs_capacity = circuit_info. values . len ( ) ;
372- let u384_struct_layout = layout_repeat ( & get_integer_layout ( 96 ) , 4 ) ?. 0 ;
373- let outputs_capacity_bytes = layout_repeat ( & u384_struct_layout, outputs_capacity) ?
374- . 0
375- . pad_to_align ( )
376- . size ( ) ;
366+ let u384_integer_layout = get_integer_layout ( 384 ) ;
367+ let outputs_layout = layout_repeat ( & u384_integer_layout, outputs_capacity) ?. 0 ;
368+ let outputs_capacity_bytes = outputs_layout. pad_to_align ( ) . size ( ) ;
377369 let outputs_capacity_bytes_value =
378370 ok_block. const_int ( context, location, outputs_capacity_bytes, 64 ) ?;
379371
@@ -387,13 +379,14 @@ fn build_eval<'ctx, 'this>(
387379 location,
388380 ) ?) ?;
389381
382+ // Insert evaluated gates into the array.
390383 for ( i, gate) in gates. into_iter ( ) . enumerate ( ) {
391384 let value_ptr = ok_block. gep (
392385 context,
393386 location,
394387 outputs_ptr,
395388 & [ GepIndex :: Const ( i as i32 ) ] ,
396- build_u384_struct_type ( context) ,
389+ IntegerType :: new ( context, 384 ) . into ( ) ,
397390 ) ?;
398391 ok_block. store ( context, location, value_ptr, gate) ?;
399392 }
@@ -464,12 +457,24 @@ fn build_eval<'ctx, 'this>(
464457 Ok ( ( ) )
465458}
466459
467- /// Builds the evaluation of all circuit gates, returning:
468- /// - An array of two branches, the success block and the error block respectively.
469- /// - The error block contains the index of the first failure as argument.
470- /// - A vector of the gate values. In case of failure, not all values are guaranteed to be computed.
460+ /// Receives the circuit inputs, and builds the evaluation of the full circuit.
461+ ///
462+ /// Returns two branches. The success block and the error block respectively.
463+ /// - The success block receives nothing.
464+ /// - The error block receives:
465+ /// - The index of the first gate that could not be computed.
466+ ///
467+ /// The evaluated gates are returned separately, as a vector of `MLIR` values.
468+ /// Note that in the case of error, not all MLIR values are guaranteed to have been computed,
469+ /// and should not be used carelessly.
471470///
472- /// The original Cairo hint evaluates all gates, even in case of failure. This implementation exits on first error, as there is no need for the partial outputs yet.
471+ /// TODO: Consider returning the evaluated gates through the block directly:
472+ /// - As a pointer to a heap allocated array of gates.
473+ /// - As a llvm struct/array of evaluted gates (its size could get really big).
474+ /// - As arguments to the block (one argument per block).
475+ ///
476+ /// The original Cairo hint evaluates all gates, even in case of failure.
477+ /// This implementation exits on first error, as there is no need for the partial outputs yet.
473478fn build_gate_evaluation < ' ctx , ' this > (
474479 context : & ' this Context ,
475480 mut block : & ' this Block < ' ctx > ,
@@ -479,43 +484,44 @@ fn build_gate_evaluation<'ctx, 'this>(
479484 circuit_data : Value < ' ctx , ' ctx > ,
480485 circuit_modulus : Value < ' ctx , ' ctx > ,
481486) -> Result < ( [ & ' this Block < ' ctx > ; 2 ] , Vec < Value < ' ctx , ' ctx > > ) > {
482- // Throughout the evaluation of the circuit we maintain an array of known gate values
483- // Initially, it only contains the inputs of the circuit.
484- // Unknown values are represented as None
487+ // Each gate is represented as a MLIR value, and identified by an offset in the gate vector.
488+ // - `None` implies that the gate value *has not* been compiled yet.
489+ // - `Some` implies that the gate values *has* already been compiled, and therefore can be safely used.
490+ // Initially, some gate values are already known.
491+ let mut gates = vec ! [ None ; 1 + circuit_info. n_inputs + circuit_info. values. len( ) ] ;
492+
493+ // The first gate always has a value of 1. It is implicity referred by some gate offsets.
494+ gates[ 0 ] = Some ( block. const_int ( context, location, 1 , 384 ) ?) ;
485495
486- let mut values = vec ! [ None ; 1 + circuit_info . n_inputs + circuit_info . values . len ( ) ] ;
487- values [ 0 ] = Some ( block . const_int ( context, location , 1 , 384 ) ? ) ;
496+ // The input gates are also known at the start. We take them from the `circuit_data` array.
497+ let u384_type = IntegerType :: new ( context, 384 ) . into ( ) ;
488498 for i in 0 ..circuit_info. n_inputs {
489499 let value_ptr = block. gep (
490500 context,
491501 location,
492502 circuit_data,
493503 & [ GepIndex :: Const ( i as i32 ) ] ,
494- IntegerType :: new ( context , 384 ) . into ( ) ,
504+ u384_type ,
495505 ) ?;
496- values[ i + 1 ] = Some ( block. load (
497- context,
498- location,
499- value_ptr,
500- IntegerType :: new ( context, 384 ) . into ( ) ,
501- ) ?) ;
506+ gates[ i + 1 ] = Some ( block. load ( context, location, value_ptr, u384_type) ?) ;
502507 }
503508
504509 let err_block = helper. append_block ( Block :: new ( & [ (
505510 IntegerType :: new ( context, 64 ) . into ( ) ,
506511 location,
507512 ) ] ) ) ;
513+ let ok_block = helper. append_block ( Block :: new ( & [ ] ) ) ;
508514
509515 let mut add_offsets = circuit_info. add_offsets . iter ( ) . peekable ( ) ;
510516 let mut mul_offsets = circuit_info. mul_offsets . iter ( ) . enumerate ( ) ;
511517
512518 // We loop until all gates have been solved
513519 loop {
514520 // We iterate the add gate offsets as long as we can
515- while let Some ( & add_gate_offset ) = add_offsets. peek ( ) {
516- let lhs_value = values [ add_gate_offset . lhs ] . to_owned ( ) ;
517- let rhs_value = values [ add_gate_offset . rhs ] . to_owned ( ) ;
518- let output_value = values [ add_gate_offset . output ] . to_owned ( ) ;
521+ while let Some ( & gate_offset ) = add_offsets. peek ( ) {
522+ let lhs_value = gates [ gate_offset . lhs ] . to_owned ( ) ;
523+ let rhs_value = gates [ gate_offset . rhs ] . to_owned ( ) ;
524+ let output_value = gates [ gate_offset . output ] . to_owned ( ) ;
519525
520526 // Depending on the values known at the time, we can deduce if we are dealing with an ADD gate or a SUB gate.
521527 match ( lhs_value, rhs_value, output_value) {
@@ -544,7 +550,7 @@ fn build_gate_evaluation<'ctx, 'this>(
544550 // Truncate back
545551 let value =
546552 block. trunci ( value, IntegerType :: new ( context, 384 ) . into ( ) , location) ?;
547- values [ add_gate_offset . output ] = Some ( value) ;
553+ gates [ gate_offset . output ] = Some ( value) ;
548554 }
549555 // SUB: lhs = out - rhs
550556 ( None , Some ( rhs_value) , Some ( output_value) ) => {
@@ -572,7 +578,7 @@ fn build_gate_evaluation<'ctx, 'this>(
572578 // Truncate back
573579 let value =
574580 block. trunci ( value, IntegerType :: new ( context, 384 ) . into ( ) , location) ?;
575- values [ add_gate_offset . lhs ] = Some ( value) ;
581+ gates [ gate_offset . lhs ] = Some ( value) ;
576582 }
577583 // We can't solve this add gate yet, so we break from the loop
578584 _ => break ,
@@ -582,12 +588,10 @@ fn build_gate_evaluation<'ctx, 'this>(
582588 }
583589
584590 // If we can't advance any more with add gate offsets, then we solve the next mul gate offset and go back to the start of the loop (solving add gate offsets).
585- if let Some ( ( gate_offset_idx, & circuit:: GateOffsets { lhs, rhs, output } ) ) =
586- mul_offsets. next ( )
587- {
588- let lhs_value = values[ lhs] . to_owned ( ) ;
589- let rhs_value = values[ rhs] . to_owned ( ) ;
590- let output_value = values[ output] . to_owned ( ) ;
591+ if let Some ( ( gate_offset_idx, gate_offset) ) = mul_offsets. next ( ) {
592+ let lhs_value = gates[ gate_offset. lhs ] . to_owned ( ) ;
593+ let rhs_value = gates[ gate_offset. rhs ] . to_owned ( ) ;
594+ let output_value = gates[ gate_offset. output ] . to_owned ( ) ;
591595
592596 // Depending on the values known at the time, we can deduce if we are dealing with an MUL gate or a INV gate.
593597 match ( lhs_value, rhs_value, output_value) {
@@ -616,7 +620,7 @@ fn build_gate_evaluation<'ctx, 'this>(
616620 // Truncate back
617621 let value =
618622 block. trunci ( value, IntegerType :: new ( context, 384 ) . into ( ) , location) ?;
619- values [ output] = Some ( value)
623+ gates [ gate_offset . output ] = Some ( value)
620624 }
621625 // INV: lhs = 1 / rhs
622626 ( None , Some ( rhs_value) , Some ( _) ) => {
@@ -691,7 +695,7 @@ fn build_gate_evaluation<'ctx, 'this>(
691695 let inverse =
692696 block. trunci ( inverse, IntegerType :: new ( context, 384 ) . into ( ) , location) ?;
693697
694- values [ lhs] = Some ( inverse) ;
698+ gates [ gate_offset . lhs ] = Some ( inverse) ;
695699 }
696700 // The imposibility to solve this mul gate offset would render the circuit unsolvable
697701 _ => return Err ( SierraAssertError :: ImpossibleCircuit . into ( ) ) ,
@@ -702,15 +706,17 @@ fn build_gate_evaluation<'ctx, 'this>(
702706 }
703707 }
704708
709+ block. append_operation ( cf:: br ( ok_block, & [ ] , location) ) ;
710+
705711 // Validate all values have been calculated
706712 // Should only fail if the circuit is not solvable (bad form)
707- let values = values
713+ let evaluated_gates = gates
708714 . into_iter ( )
709715 . skip ( 1 + circuit_info. n_inputs )
710716 . collect :: < Option < Vec < Value > > > ( )
711717 . ok_or ( SierraAssertError :: ImpossibleCircuit ) ?;
712718
713- Ok ( ( [ block , err_block] , values ) )
719+ Ok ( ( [ ok_block , err_block] , evaluated_gates ) )
714720}
715721
716722/// Generate MLIR operations for the `circuit_failure_guarantee_verify` libfunc.
@@ -876,6 +882,8 @@ fn build_get_output<'ctx, 'this>(
876882 } ;
877883 let output_type_id = & info. output_ty ;
878884
885+ let u384_type = IntegerType :: new ( context, 384 ) . into ( ) ;
886+
879887 let output_offset_idx = * circuit_info
880888 . values
881889 . get ( output_type_id)
@@ -885,7 +893,7 @@ fn build_get_output<'ctx, 'this>(
885893
886894 let outputs = entry. arg ( 0 ) ?;
887895
888- let values_ptr = entry. extract_value (
896+ let circuit_ptr = entry. extract_value (
889897 context,
890898 location,
891899 outputs,
@@ -900,20 +908,15 @@ fn build_get_output<'ctx, 'this>(
900908 1 ,
901909 ) ?;
902910
903- let output_struct_ptr = entry. gep (
911+ let output_integer_ptr = entry. gep (
904912 context,
905913 location,
906- values_ptr ,
914+ circuit_ptr ,
907915 & [ GepIndex :: Const ( output_idx as i32 ) ] ,
908- build_u384_struct_type ( context) ,
909- ) ?;
910-
911- let output_struct = entry. load (
912- context,
913- location,
914- output_struct_ptr,
915- build_u384_struct_type ( context) ,
916+ u384_type,
916917 ) ?;
918+ let output_integer = entry. load ( context, location, output_integer_ptr, u384_type) ?;
919+ let output_struct = u384_integer_to_struct ( context, entry, location, output_integer) ?;
917920
918921 let guarantee_type_id = & info. branch_signatures ( ) [ 0 ] . vars [ 1 ] . ty ;
919922 let guarantee = build_struct_value (
0 commit comments