Skip to content

Commit 4828dc6

Browse files
committed
# This is a combination of 8 commits.
# This is the 1st commit message: wip: issue/88 # This is the commit message leanEthereum#2: wip: issue/88 # This is the commit message leanEthereum#3: wip: issue/88 # This is the commit message leanEthereum#4: wip: issue/88 # This is the commit message leanEthereum#5: wip: issue/88 # This is the commit message leanEthereum#6: shrink test_match # This is the commit message leanEthereum#7: wip: issue/88 # This is the commit message leanEthereum#8: remove mark_vars_as_declared
1 parent 5ad5619 commit 4828dc6

File tree

10 files changed

+401
-151
lines changed

10 files changed

+401
-151
lines changed

crates/lean_compiler/src/a_simplify_lang.rs

Lines changed: 189 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -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
};
99
use 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

152153
pub 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)]
193357
struct Counters {
194358
aux_vars: usize,
@@ -207,7 +371,6 @@ struct ArrayManager {
207371
pub 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

213376
impl 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

Comments
 (0)