@@ -3,7 +3,7 @@ use crate::{
33 ir:: HighLevelOperation ,
44 lang:: {
55 AssumeBoolean , Boolean , Condition , ConstExpression , ConstMallocLabel , ConstantValue , Expression , Function ,
6- Line , Program , SimpleExpr , Var ,
6+ Line , Program , SimpleExpr , Var , Context , Scope ,
77 } ,
88} ;
99use lean_vm:: { SourceLineNumber , Table , TableT } ;
@@ -72,6 +72,7 @@ pub enum SimpleLine {
7272 value : SimpleExpr ,
7373 arms : Vec < Vec < Self > > , // patterns = 0, 1, ...
7474 } ,
75+ ForwardDeclaration { var : Var } ,
7576 Assignment {
7677 var : VarOrConstMallocAccess ,
7778 operation : HighLevelOperation ,
@@ -150,6 +151,7 @@ pub enum SimpleLine {
150151}
151152
152153pub fn simplify_program ( mut program : Program ) -> SimpleProgram {
154+ check_program_scoping ( & program) ;
153155 handle_inlined_functions ( & mut program) ;
154156 handle_const_arguments ( & mut program) ;
155157 let mut new_functions = BTreeMap :: new ( ) ;
@@ -189,6 +191,168 @@ pub fn simplify_program(mut program: Program) -> SimpleProgram {
189191 }
190192}
191193
194+ /// Analyzes the program to verify that each variable is defined in each context where it is used.
195+ fn check_program_scoping ( program : & Program ) {
196+ for ( _, function) in program. functions . iter ( ) {
197+ let mut scope = Scope { vars : BTreeSet :: new ( ) } ;
198+ for ( arg, _) in function. arguments . iter ( ) {
199+ scope. vars . insert ( arg. clone ( ) ) ;
200+ }
201+ let mut ctx = Context { scopes : vec ! [ scope] } ;
202+
203+ check_block_scoping ( & function. body , & mut ctx) ;
204+ }
205+ }
206+
207+ /// Analyzes the block to verify that each variable is defined in each context where it is used.
208+ fn check_block_scoping ( block : & Vec < Line > , ctx : & mut Context ) {
209+ for line in block. iter ( ) {
210+ match line {
211+ Line :: ForwardDeclaration { var } => {
212+ let last_scope = ctx. scopes . last_mut ( ) . unwrap ( ) ;
213+ assert ! ( !last_scope. vars. contains( var) , "Variable declared multiple times in the same scope: {:?}" , var) ;
214+ last_scope. vars . insert ( var. clone ( ) ) ;
215+ } ,
216+ Line :: Match { value, arms } => {
217+ check_expr_scoping ( value, ctx) ;
218+ for ( _, arm) in arms {
219+ ctx. scopes . push ( Scope { vars : BTreeSet :: new ( ) } ) ;
220+ check_block_scoping ( arm, ctx) ;
221+ ctx. scopes . pop ( ) ;
222+ }
223+ } ,
224+ Line :: Assignment { var, value } => {
225+ check_expr_scoping ( value, ctx) ;
226+ let last_scope = ctx. scopes . last_mut ( ) . unwrap ( ) ;
227+ assert ! ( !last_scope. vars. contains( var) , "Variable declared multiple times in the same scope: {:?}" , var) ;
228+ last_scope. vars . insert ( var. clone ( ) ) ;
229+ } ,
230+ Line :: ArrayAssign { array, index, value } => {
231+ check_simple_expr_scoping ( array, ctx) ;
232+ check_expr_scoping ( index, ctx) ;
233+ check_expr_scoping ( value, ctx) ;
234+ } ,
235+ Line :: Assert ( boolean, _) => {
236+ check_boolean_scoping ( boolean, ctx) ;
237+ } ,
238+ Line :: IfCondition { condition, then_branch, else_branch, line_number : _ } => {
239+ check_condition_scoping ( condition, ctx) ;
240+ for branch in [ then_branch, else_branch] {
241+ ctx. scopes . push ( Scope { vars : BTreeSet :: new ( ) } ) ;
242+ check_block_scoping ( branch, ctx) ;
243+ ctx. scopes . pop ( ) ;
244+ }
245+ } ,
246+ Line :: ForLoop { iterator, start, end, body, rev : _, unroll : _, line_number : _ } => {
247+ check_expr_scoping ( start, ctx) ;
248+ check_expr_scoping ( end, ctx) ;
249+ let mut new_scope_vars = BTreeSet :: new ( ) ;
250+ new_scope_vars. insert ( iterator. clone ( ) ) ;
251+ ctx. scopes . push ( Scope { vars : new_scope_vars } ) ;
252+ check_block_scoping ( body, ctx) ;
253+ ctx. scopes . pop ( ) ;
254+ } ,
255+ Line :: FunctionCall { function_name : _, args, return_data, line_number : _ } => {
256+ for arg in args {
257+ check_expr_scoping ( arg, ctx) ;
258+ }
259+ let last_scope = ctx. scopes . last_mut ( ) . unwrap ( ) ;
260+ for var in return_data {
261+ assert ! ( !last_scope. vars. contains( var) , "Variable declared multiple times in the same scope: {:?}" , var) ;
262+ last_scope. vars . insert ( var. clone ( ) ) ;
263+ }
264+ } ,
265+ Line :: FunctionRet { return_data } => {
266+ for expr in return_data {
267+ check_expr_scoping ( expr, ctx) ;
268+ }
269+ } ,
270+ Line :: Precompile { table : _, args } => {
271+ for arg in args {
272+ check_expr_scoping ( arg, ctx) ;
273+ }
274+ } ,
275+ Line :: Break | Line :: Panic | Line :: LocationReport { .. } => { } ,
276+ Line :: Print { line_info : _, content } => {
277+ for expr in content {
278+ check_expr_scoping ( expr, ctx) ;
279+ }
280+ } ,
281+ Line :: MAlloc { var, size, vectorized : _, vectorized_len } => {
282+ check_expr_scoping ( size, ctx) ;
283+ check_expr_scoping ( vectorized_len, ctx) ;
284+ let last_scope = ctx. scopes . last_mut ( ) . unwrap ( ) ;
285+ assert ! ( !last_scope. vars. contains( var) , "Variable declared multiple times in the same scope: {:?}" , var) ;
286+ last_scope. vars . insert ( var. clone ( ) ) ;
287+ } ,
288+ Line :: DecomposeBits { var, to_decompose } | Line :: DecomposeCustom { var, to_decompose } => {
289+ for expr in to_decompose {
290+ check_expr_scoping ( expr, ctx) ;
291+ }
292+ let last_scope = ctx. scopes . last_mut ( ) . unwrap ( ) ;
293+ assert ! ( !last_scope. vars. contains( var) , "Variable declared multiple times in the same scope: {:?}" , var) ;
294+ last_scope. vars . insert ( var. clone ( ) ) ;
295+ } ,
296+ Line :: CounterHint { var } => {
297+ let last_scope = ctx. scopes . last_mut ( ) . unwrap ( ) ;
298+ assert ! ( !last_scope. vars. contains( var) , "Variable declared multiple times in the same scope: {:?}" , var) ;
299+ last_scope. vars . insert ( var. clone ( ) ) ;
300+ } ,
301+ }
302+ }
303+ }
304+
305+ /// Analyzes the expression to verify that each variable is defined in the given context.
306+ fn check_expr_scoping ( expr : & Expression , ctx : & Context ) {
307+ match expr {
308+ Expression :: Value ( simple_expr) => {
309+ check_simple_expr_scoping ( simple_expr, ctx) ;
310+ } ,
311+ Expression :: ArrayAccess { array, index } => {
312+ check_simple_expr_scoping ( array, ctx) ;
313+ check_expr_scoping ( & * index, ctx) ;
314+ } ,
315+ Expression :: Binary { left, operation : _, right } => {
316+ check_expr_scoping ( & * left, ctx) ;
317+ check_expr_scoping ( & * right, ctx) ;
318+ } ,
319+ Expression :: Log2Ceil { value } => {
320+ check_expr_scoping ( & * value, ctx) ;
321+ } ,
322+ }
323+ }
324+
325+ /// Analyzes the simple expression to verify that each variable is defined in the given context.
326+ fn check_simple_expr_scoping ( expr : & SimpleExpr , ctx : & Context ) {
327+ match expr {
328+ SimpleExpr :: Var ( v) => {
329+ assert ! ( ctx. defines( & v) , "Variable defined but not used: {:?}" , v)
330+ } ,
331+ SimpleExpr :: Constant ( _) => { } ,
332+ SimpleExpr :: ConstMallocAccess { .. } => { } ,
333+ }
334+ }
335+
336+ fn check_boolean_scoping ( boolean : & Boolean , ctx : & Context ) {
337+ match boolean {
338+ Boolean :: Equal { left, right } | Boolean :: Different { left, right } => {
339+ check_expr_scoping ( left, ctx) ;
340+ check_expr_scoping ( right, ctx) ;
341+ } ,
342+ }
343+ }
344+
345+ fn check_condition_scoping ( condition : & Condition , ctx : & Context ) {
346+ match condition {
347+ Condition :: Expression ( expr, _) => {
348+ check_expr_scoping ( expr, ctx) ;
349+ } ,
350+ Condition :: Comparison ( boolean) => {
351+ check_boolean_scoping ( boolean, ctx) ;
352+ }
353+ }
354+ }
355+
192356#[ derive( Debug , Clone , Default ) ]
193357struct Counters {
194358 aux_vars : usize ,
@@ -207,7 +371,6 @@ struct ArrayManager {
207371pub struct ConstMalloc {
208372 counter : usize ,
209373 map : BTreeMap < Var , ConstMallocLabel > ,
210- forbidden_vars : BTreeSet < Var > , // vars shared between branches of an if/else
211374}
212375
213376impl ArrayManager {
@@ -233,6 +396,9 @@ fn simplify_lines(
233396 let mut res = Vec :: new ( ) ;
234397 for line in lines {
235398 match line {
399+ Line :: ForwardDeclaration { var } => {
400+ res. push ( SimpleLine :: ForwardDeclaration { var : var. clone ( ) } ) ;
401+ } ,
236402 Line :: Match { value, arms } => {
237403 let simple_value = simplify_expr ( value, & mut res, counters, array_manager, const_malloc) ;
238404 let mut simple_arms = vec ! [ ] ;
@@ -388,17 +554,6 @@ fn simplify_lines(
388554 }
389555 } ;
390556
391- let forbidden_vars_before = const_malloc. forbidden_vars . clone ( ) ;
392-
393- let then_internal_vars = find_variable_usage ( then_branch) . 0 ;
394- let else_internal_vars = find_variable_usage ( else_branch) . 0 ;
395- let new_forbidden_vars = then_internal_vars
396- . intersection ( & else_internal_vars)
397- . cloned ( )
398- . collect :: < BTreeSet < _ > > ( ) ;
399-
400- const_malloc. forbidden_vars . extend ( new_forbidden_vars) ;
401-
402557 let mut array_manager_then = array_manager. clone ( ) ;
403558 let then_branch_simplified = simplify_lines (
404559 then_branch,
@@ -420,8 +575,6 @@ fn simplify_lines(
420575 const_malloc,
421576 ) ;
422577
423- const_malloc. forbidden_vars = forbidden_vars_before;
424-
425578 * array_manager = array_manager_else. clone ( ) ;
426579 // keep the intersection both branches
427580 array_manager. valid = array_manager
@@ -483,6 +636,8 @@ fn simplify_lines(
483636 counter : const_malloc. counter ,
484637 ..ConstMalloc :: default ( )
485638 } ;
639+ // TODO: what is array manager, and does it need to be updated
640+ // to make block-level scoping work?
486641 let valid_aux_vars_in_array_manager_before = array_manager. valid . clone ( ) ;
487642 array_manager. valid . clear ( ) ;
488643 let simplified_body = simplify_lines (
@@ -614,12 +769,8 @@ fn simplify_lines(
614769 let simplified_size = simplify_expr ( size, & mut res, counters, array_manager, const_malloc) ;
615770 let simplified_vectorized_len =
616771 simplify_expr ( vectorized_len, & mut res, counters, array_manager, const_malloc) ;
617- if simplified_size. is_constant ( ) && !* vectorized && const_malloc. forbidden_vars . contains ( var) {
618- println ! ( "TODO: Optimization missed: Requires to align const malloc in if/else branches" ) ;
619- }
620772 match simplified_size {
621- SimpleExpr :: Constant ( const_size) if !* vectorized && !const_malloc. forbidden_vars . contains ( var) => {
622- // TODO do this optimization even if we are in an if/else branch
773+ SimpleExpr :: Constant ( const_size) if !* vectorized => {
623774 let label = const_malloc. counter ;
624775 const_malloc. counter += 1 ;
625776 const_malloc. map . insert ( var. clone ( ) , label) ;
@@ -640,7 +791,6 @@ fn simplify_lines(
640791 }
641792 }
642793 Line :: DecomposeBits { var, to_decompose } => {
643- assert ! ( !const_malloc. forbidden_vars. contains( var) , "TODO" ) ;
644794 let simplified_to_decompose = to_decompose
645795 . iter ( )
646796 . map ( |expr| simplify_expr ( expr, & mut res, counters, array_manager, const_malloc) )
@@ -655,7 +805,6 @@ fn simplify_lines(
655805 } ) ;
656806 }
657807 Line :: DecomposeCustom { var, to_decompose } => {
658- assert ! ( !const_malloc. forbidden_vars. contains( var) , "TODO" ) ;
659808 let simplified_to_decompose = to_decompose
660809 . iter ( )
661810 . map ( |expr| simplify_expr ( expr, & mut res, counters, array_manager, const_malloc) )
@@ -782,6 +931,9 @@ pub fn find_variable_usage(lines: &[Line]) -> (BTreeSet<Var>, BTreeSet<Var>) {
782931
783932 for line in lines {
784933 match line {
934+ Line :: ForwardDeclaration { var } => {
935+ internal_vars. insert ( var. clone ( ) ) ;
936+ } ,
785937 Line :: Match { value, arms } => {
786938 on_new_expr ( value, & internal_vars, & mut external_vars) ;
787939 for ( _, statements) in arms {
@@ -927,14 +1079,17 @@ pub fn inline_lines(lines: &mut Vec<Line>, args: &BTreeMap<Var, SimpleExpr>, res
9271079 let inline_internal_var = |var : & mut Var | {
9281080 assert ! (
9291081 !args. contains_key( var) ,
930- "Variable {var} is both an argument and assigned in the inlined function"
1082+ "Variable {var} is both an argument and declared in the inlined function"
9311083 ) ;
9321084 * var = format ! ( "@inlined_var_{inlining_count}_{var}" ) ;
9331085 } ;
9341086
9351087 let mut lines_to_replace = vec ! [ ] ;
9361088 for ( i, line) in lines. iter_mut ( ) . enumerate ( ) {
9371089 match line {
1090+ Line :: ForwardDeclaration { var } => {
1091+ inline_internal_var ( var) ;
1092+ }
9381093 Line :: Match { value, arms } => {
9391094 inline_expr ( value, args, inlining_count) ;
9401095 for ( _, statements) in arms {
@@ -1240,6 +1395,9 @@ fn replace_vars_for_unroll(
12401395 replace_vars_for_unroll ( statements, iterator, unroll_index, iterator_value, internal_vars) ;
12411396 }
12421397 }
1398+ Line :: ForwardDeclaration { var } => {
1399+ * var = format ! ( "@unrolled_{unroll_index}_{iterator_value}_{var}" ) ;
1400+ }
12431401 Line :: Assignment { var, value } => {
12441402 assert ! ( var != iterator, "Weird" ) ;
12451403 * var = format ! ( "@unrolled_{unroll_index}_{iterator_value}_{var}" ) ;
@@ -1694,6 +1852,7 @@ fn get_function_called(lines: &[Line], function_called: &mut Vec<String>) {
16941852 get_function_called ( body, function_called) ;
16951853 }
16961854 Line :: Assignment { .. }
1855+ | Line :: ForwardDeclaration { .. }
16971856 | Line :: ArrayAssign { .. }
16981857 | Line :: Assert { .. }
16991858 | Line :: FunctionRet { .. }
@@ -1719,6 +1878,9 @@ fn replace_vars_by_const_in_lines(lines: &mut [Line], map: &BTreeMap<Var, F>) {
17191878 replace_vars_by_const_in_lines ( statements, map) ;
17201879 }
17211880 }
1881+ Line :: ForwardDeclaration { var } => {
1882+ assert ! ( !map. contains_key( var) , "Variable {var} is a constant" ) ;
1883+ }
17221884 Line :: Assignment { var, value } => {
17231885 assert ! ( !map. contains_key( var) , "Variable {var} is a constant" ) ;
17241886 replace_vars_by_const_in_expr ( value, map) ;
@@ -1821,6 +1983,9 @@ impl SimpleLine {
18211983 fn to_string_with_indent ( & self , indent : usize ) -> String {
18221984 let spaces = " " . repeat ( indent) ;
18231985 let line_str = match self {
1986+ Self :: ForwardDeclaration { var } => {
1987+ format ! ( "var {var}" )
1988+ }
18241989 Self :: Match { value, arms } => {
18251990 let arms_str = arms
18261991 . iter ( )
@@ -1882,7 +2047,7 @@ impl SimpleLine {
18822047 format ! ( "{result} = counter_hint()" )
18832048 }
18842049 Self :: RawAccess { res, index, shift } => {
1885- format ! ( "memory[{index} + {shift}] = {res} " )
2050+ format ! ( "{res} = memory[{index} + {shift}]" )
18862051 }
18872052 Self :: TestZero { operation, arg0, arg1 } => {
18882053 format ! ( "0 = {arg0} {operation} {arg1}" )
0 commit comments