@@ -39,6 +39,14 @@ struct Interpreter<'ssa> {
3939 /// expected to have no effect if there are no such instructions or if the code
4040 /// being executed is an unconstrained function.
4141 side_effects_enabled : bool ,
42+
43+ options : InterpreterOptions ,
44+ }
45+
46+ #[ derive( Copy , Clone , Default ) ]
47+ pub struct InterpreterOptions {
48+ /// If true, the interpreter will print each value definition to stdout.
49+ pub print_definitions : bool ,
4250}
4351
4452struct CallContext {
@@ -66,20 +74,33 @@ type IResults = IResult<Vec<Value>>;
6674#[ allow( unused) ]
6775impl Ssa {
6876 pub fn interpret ( & self , args : Vec < Value > ) -> IResults {
69- self . interpret_function ( self . main_id , args)
77+ self . interpret_with_options ( args, InterpreterOptions :: default ( ) )
78+ }
79+
80+ pub fn interpret_with_options (
81+ & self ,
82+ args : Vec < Value > ,
83+ options : InterpreterOptions ,
84+ ) -> IResults {
85+ self . interpret_function ( self . main_id , args, options)
7086 }
7187
72- pub ( crate ) fn interpret_function ( & self , function : FunctionId , args : Vec < Value > ) -> IResults {
73- let mut interpreter = Interpreter :: new ( self ) ;
88+ fn interpret_function (
89+ & self ,
90+ function : FunctionId ,
91+ args : Vec < Value > ,
92+ options : InterpreterOptions ,
93+ ) -> IResults {
94+ let mut interpreter = Interpreter :: new ( self , options) ;
7495 interpreter. interpret_globals ( ) ?;
7596 interpreter. call_function ( function, args)
7697 }
7798}
7899
79100impl < ' ssa > Interpreter < ' ssa > {
80- fn new ( ssa : & ' ssa Ssa ) -> Self {
101+ fn new ( ssa : & ' ssa Ssa , options : InterpreterOptions ) -> Self {
81102 let call_stack = vec ! [ CallContext :: global_context( ) ] ;
82- Self { ssa, call_stack, side_effects_enabled : true }
103+ Self { ssa, call_stack, side_effects_enabled : true , options }
83104 }
84105
85106 fn call_context ( & self ) -> & CallContext {
@@ -94,12 +115,15 @@ impl<'ssa> Interpreter<'ssa> {
94115 & self . call_stack . first ( ) . expect ( "call_stack should always be non-empty" ) . scope
95116 }
96117
97- fn current_function ( & self ) -> & ' ssa Function {
118+ fn try_current_function ( & self ) -> Option < & ' ssa Function > {
98119 let current_function_id = self . call_context ( ) . called_function ;
99- let current_function_id = current_function_id. expect (
120+ current_function_id. map ( |current_function_id| & self . ssa . functions [ & current_function_id] )
121+ }
122+
123+ fn current_function ( & self ) -> & ' ssa Function {
124+ self . try_current_function ( ) . expect (
100125 "Tried calling `Interpreter::current_function` while evaluating global instructions" ,
101- ) ;
102- & self . ssa . functions [ & current_function_id]
126+ )
103127 }
104128
105129 fn dfg ( & self ) -> & ' ssa DataFlowGraph {
@@ -113,6 +137,9 @@ impl<'ssa> Interpreter<'ssa> {
113137 /// Define or redefine a value.
114138 /// Redefinitions are expected in the case of loops.
115139 fn define ( & mut self , id : ValueId , value : Value ) {
140+ if self . options . print_definitions {
141+ println ! ( "{id} = {value}" ) ;
142+ }
116143 self . call_context_mut ( ) . scope . insert ( id, value) ;
117144 }
118145
@@ -376,9 +403,17 @@ impl<'ssa> Interpreter<'ssa> {
376403 try_vecmap ( ids, |id| self . lookup ( * id) )
377404 }
378405
379- fn side_effects_enabled ( & self ) -> bool {
380- match self . current_function ( ) . runtime ( ) {
381- RuntimeType :: Acir ( _) => self . side_effects_enabled ,
406+ fn side_effects_enabled ( & self , instruction : & Instruction ) -> bool {
407+ let Some ( current_function) = self . try_current_function ( ) else {
408+ // If there's no current function it means we are evaluating global instructions
409+ return true ;
410+ } ;
411+
412+ match current_function. runtime ( ) {
413+ RuntimeType :: Acir ( _) => {
414+ self . side_effects_enabled
415+ || !instruction. requires_acir_gen_predicate ( & current_function. dfg )
416+ }
382417 RuntimeType :: Brillig ( _) => true ,
383418 }
384419 }
@@ -389,9 +424,11 @@ impl<'ssa> Interpreter<'ssa> {
389424 instruction : & Instruction ,
390425 results : & [ ValueId ] ,
391426 ) -> IResult < ( ) > {
427+ let side_effects_enabled = self . side_effects_enabled ( instruction) ;
428+
392429 match instruction {
393430 Instruction :: Binary ( binary) => {
394- let result = self . interpret_binary ( binary) ?;
431+ let result = self . interpret_binary ( binary, side_effects_enabled ) ?;
395432 self . define ( results[ 0 ] , result) ;
396433 Ok ( ( ) )
397434 }
@@ -410,7 +447,7 @@ impl<'ssa> Interpreter<'ssa> {
410447 Instruction :: Constrain ( lhs_id, rhs_id, constrain_error) => {
411448 let lhs = self . lookup ( * lhs_id) ?;
412449 let rhs = self . lookup ( * rhs_id) ?;
413- if self . side_effects_enabled ( ) && lhs != rhs {
450+ if side_effects_enabled && lhs != rhs {
414451 let lhs = lhs. to_string ( ) ;
415452 let rhs = rhs. to_string ( ) ;
416453 let lhs_id = * lhs_id;
@@ -433,7 +470,7 @@ impl<'ssa> Interpreter<'ssa> {
433470 Instruction :: ConstrainNotEqual ( lhs_id, rhs_id, constrain_error) => {
434471 let lhs = self . lookup ( * lhs_id) ?;
435472 let rhs = self . lookup ( * rhs_id) ?;
436- if self . side_effects_enabled ( ) && lhs == rhs {
473+ if side_effects_enabled && lhs == rhs {
437474 let lhs = lhs. to_string ( ) ;
438475 let rhs = rhs. to_string ( ) ;
439476 let lhs_id = * lhs_id;
@@ -453,10 +490,16 @@ impl<'ssa> Interpreter<'ssa> {
453490 }
454491 Ok ( ( ) )
455492 }
456- Instruction :: RangeCheck { value, max_bit_size, assert_message } => {
457- self . interpret_range_check ( * value, * max_bit_size, assert_message. as_ref ( ) )
493+ Instruction :: RangeCheck { value, max_bit_size, assert_message } => self
494+ . interpret_range_check (
495+ * value,
496+ * max_bit_size,
497+ assert_message. as_ref ( ) ,
498+ side_effects_enabled,
499+ ) ,
500+ Instruction :: Call { func, arguments } => {
501+ self . interpret_call ( * func, arguments, results, side_effects_enabled)
458502 }
459- Instruction :: Call { func, arguments } => self . interpret_call ( * func, arguments, results) ,
460503 Instruction :: Allocate => {
461504 self . interpret_allocate ( results[ 0 ] ) ;
462505 Ok ( ( ) )
@@ -468,11 +511,18 @@ impl<'ssa> Interpreter<'ssa> {
468511 Ok ( ( ) )
469512 }
470513 Instruction :: ArrayGet { array, index, offset } => {
471- self . interpret_array_get ( * array, * index, * offset, results[ 0 ] )
472- }
473- Instruction :: ArraySet { array, index, value, mutable, offset } => {
474- self . interpret_array_set ( * array, * index, * value, * mutable, * offset, results[ 0 ] )
514+ self . interpret_array_get ( * array, * index, * offset, results[ 0 ] , side_effects_enabled)
475515 }
516+ Instruction :: ArraySet { array, index, value, mutable, offset } => self
517+ . interpret_array_set (
518+ * array,
519+ * index,
520+ * value,
521+ * mutable,
522+ * offset,
523+ results[ 0 ] ,
524+ side_effects_enabled,
525+ ) ,
476526 Instruction :: IncrementRc { value } => self . interpret_inc_rc ( * value) ,
477527 Instruction :: DecrementRc { value } => self . interpret_dec_rc ( * value) ,
478528 Instruction :: IfElse { then_condition, then_value, else_condition, else_value } => self
@@ -556,8 +606,9 @@ impl<'ssa> Interpreter<'ssa> {
556606 value_id : ValueId ,
557607 max_bit_size : u32 ,
558608 error_message : Option < & String > ,
609+ side_effects_enabled : bool ,
559610 ) -> IResult < ( ) > {
560- if !self . side_effects_enabled ( ) {
611+ if !side_effects_enabled {
561612 return Ok ( ( ) ) ;
562613 }
563614
@@ -628,11 +679,12 @@ impl<'ssa> Interpreter<'ssa> {
628679 function_id : ValueId ,
629680 argument_ids : & [ ValueId ] ,
630681 results : & [ ValueId ] ,
682+ side_effects_enabled : bool ,
631683 ) -> IResult < ( ) > {
632684 let function = self . lookup ( function_id) ?;
633685 let mut arguments = try_vecmap ( argument_ids, |argument| self . lookup ( * argument) ) ?;
634686
635- let new_results = if self . side_effects_enabled ( ) {
687+ let new_results = if side_effects_enabled {
636688 match function {
637689 Value :: Function ( id) => {
638690 // If we're crossing a constrained -> unconstrained boundary we have to wipe
@@ -762,8 +814,9 @@ impl<'ssa> Interpreter<'ssa> {
762814 index : ValueId ,
763815 offset : ArrayOffset ,
764816 result : ValueId ,
817+ side_effects_enabled : bool ,
765818 ) -> IResult < ( ) > {
766- let element = if self . side_effects_enabled ( ) {
819+ let element = if side_effects_enabled {
767820 let array = self . lookup_array_or_slice ( array, "array get" ) ?;
768821 let index = self . lookup_u32 ( index, "array get index" ) ?;
769822 let index = index - offset. to_u32 ( ) ;
@@ -776,6 +829,7 @@ impl<'ssa> Interpreter<'ssa> {
776829 Ok ( ( ) )
777830 }
778831
832+ #[ allow( clippy:: too_many_arguments) ]
779833 fn interpret_array_set (
780834 & mut self ,
781835 array : ValueId ,
@@ -784,10 +838,11 @@ impl<'ssa> Interpreter<'ssa> {
784838 mutable : bool ,
785839 offset : ArrayOffset ,
786840 result : ValueId ,
841+ side_effects_enabled : bool ,
787842 ) -> IResult < ( ) > {
788843 let array = self . lookup_array_or_slice ( array, "array set" ) ?;
789844
790- let result_array = if self . side_effects_enabled ( ) {
845+ let result_array = if side_effects_enabled {
791846 let index = self . lookup_u32 ( index, "array set index" ) ?;
792847 let index = index - offset. to_u32 ( ) ;
793848 let value = self . lookup ( value) ?;
@@ -1047,7 +1102,7 @@ macro_rules! apply_int_comparison_op {
10471102}
10481103
10491104impl Interpreter < ' _ > {
1050- fn interpret_binary ( & mut self , binary : & Binary ) -> IResult < Value > {
1105+ fn interpret_binary ( & mut self , binary : & Binary , side_effects_enabled : bool ) -> IResult < Value > {
10511106 let lhs_id = binary. lhs ;
10521107 let rhs_id = binary. rhs ;
10531108 let lhs = self . lookup_numeric ( lhs_id, "binary op lhs" ) ?;
@@ -1066,7 +1121,7 @@ impl Interpreter<'_> {
10661121 }
10671122
10681123 // Disable this instruction if it is side-effectful and side effects are disabled.
1069- if !self . side_effects_enabled ( ) && binary . requires_acir_gen_predicate ( self . dfg ( ) ) {
1124+ if !side_effects_enabled {
10701125 let zero = NumericValue :: zero ( lhs. get_type ( ) ) ;
10711126 return Ok ( Value :: Numeric ( zero) ) ;
10721127 }
0 commit comments