From 17b46862606629fb8697dd6c722732ba3f87911f Mon Sep 17 00:00:00 2001 From: David Mak Date: Tue, 5 Sep 2023 09:38:50 +0800 Subject: [PATCH 1/7] standalone: Adapt loop example to output loop variable --- nac3standalone/demo/src/loop.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/nac3standalone/demo/src/loop.py b/nac3standalone/demo/src/loop.py index 4129365..8f383fe 100644 --- a/nac3standalone/demo/src/loop.py +++ b/nac3standalone/demo/src/loop.py @@ -1,9 +1,12 @@ +# For Loop using an increasing range() expression as its iterable + @extern def output_int32(x: int32): ... def run() -> int32: - for _ in range(10): - output_int32(_) - _ = 0 + i = 0 + for i in range(10): + output_int32(i) + output_int32(i) return 0 -- 2.44.1 From 19915bac79b2ca0728ea4e9fda819517bb9a2448 Mon Sep 17 00:00:00 2001 From: David Mak Date: Tue, 5 Sep 2023 09:41:05 +0800 Subject: [PATCH 2/7] core: Prepend statement type to basic block label names Aids debugging IR. --- nac3core/src/codegen/stmt.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index cf339bd..b71a071 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -221,13 +221,13 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>( let int32 = ctx.ctx.i32_type(); let size_t = generator.get_size_type(ctx.ctx); let zero = int32.const_zero(); - let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap(); - let test_bb = ctx.ctx.append_basic_block(current, "test"); - let body_bb = ctx.ctx.append_basic_block(current, "body"); - let cont_bb = ctx.ctx.append_basic_block(current, "cont"); + let current = ctx.builder.get_insert_block().and_then(|bb| bb.get_parent()).unwrap(); + let test_bb = ctx.ctx.append_basic_block(current, "for.cond"); + let body_bb = ctx.ctx.append_basic_block(current, "for.body"); + let cont_bb = ctx.ctx.append_basic_block(current, "for.end"); // if there is no orelse, we just go to cont_bb let orelse_bb = - if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "orelse") }; + if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "for.orelse") }; // store loop bb information and restore it later let loop_bb = ctx.loop_target.replace((test_bb, cont_bb)); @@ -338,12 +338,12 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>( let var_assignment = ctx.var_assignment.clone(); let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap(); - let test_bb = ctx.ctx.append_basic_block(current, "test"); - let body_bb = ctx.ctx.append_basic_block(current, "body"); - let cont_bb = ctx.ctx.append_basic_block(current, "cont"); + let test_bb = ctx.ctx.append_basic_block(current, "while.test"); + let body_bb = ctx.ctx.append_basic_block(current, "while.body"); + let cont_bb = ctx.ctx.append_basic_block(current, "while.cont"); // if there is no orelse, we just go to cont_bb let orelse_bb = - if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "orelse") }; + if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "while.orelse") }; // store loop bb information and restore it later let loop_bb = ctx.loop_target.replace((test_bb, cont_bb)); ctx.builder.build_unconditional_branch(test_bb); @@ -401,15 +401,15 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>( let var_assignment = ctx.var_assignment.clone(); let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap(); - let test_bb = ctx.ctx.append_basic_block(current, "test"); - let body_bb = ctx.ctx.append_basic_block(current, "body"); + let test_bb = ctx.ctx.append_basic_block(current, "if.test"); + let body_bb = ctx.ctx.append_basic_block(current, "if.body"); let mut cont_bb = None; // if there is no orelse, we just go to cont_bb let orelse_bb = if orelse.is_empty() { - cont_bb = Some(ctx.ctx.append_basic_block(current, "cont")); + cont_bb = Some(ctx.ctx.append_basic_block(current, "if.cont")); cont_bb.unwrap() } else { - ctx.ctx.append_basic_block(current, "orelse") + ctx.ctx.append_basic_block(current, "if.orelse") }; ctx.builder.build_unconditional_branch(test_bb); ctx.builder.position_at_end(test_bb); -- 2.44.1 From 6805253515c587b9bd8dffbc21e6d1eb43e5bb81 Mon Sep 17 00:00:00 2001 From: David Mak Date: Tue, 5 Sep 2023 15:15:47 +0800 Subject: [PATCH 3/7] core: Use AST var name for IR name Aids debugging IR. --- nac3core/src/codegen/stmt.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index b71a071..47ba743 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -193,7 +193,13 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>( } } _ => { - let ptr = generator.gen_store_target(ctx, target, Some("target.addr"))?; + let name = if let ExprKind::Name { id, .. } = &target.node { + format!("{}.addr", id.to_string()) + } else { + String::from("target.addr") + }; + let ptr = generator.gen_store_target(ctx, target, Some(name.as_str()))?; + if let ExprKind::Name { id, .. } = &target.node { let (_, static_value, counter) = ctx.var_assignment.get_mut(id).unwrap(); *counter += 1; -- 2.44.1 From e0de82993f8366911130b302294482c535134bed Mon Sep 17 00:00:00 2001 From: David Mak Date: Tue, 5 Sep 2023 12:10:52 +0800 Subject: [PATCH 4/7] core: Preserve value of variable shadowed by for loop Previously, the final value of the target expression would be one after the last element of the loop, which does not match Python's behavior. This commit fixes this problem while also preserving the last assigned value of the loop beyond the loop, matching Python's behavior. --- nac3core/src/codegen/mod.rs | 3 +- nac3core/src/codegen/stmt.rs | 124 +++++++++++++++------ nac3standalone/demo/src/loop_decr.py | 12 ++ nac3standalone/demo/src/loop_mutate_var.py | 14 +++ 4 files changed, 115 insertions(+), 38 deletions(-) create mode 100644 nac3standalone/demo/src/loop_decr.py create mode 100644 nac3standalone/demo/src/loop_mutate_var.py diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index 72ba85e..3c213b3 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -77,7 +77,8 @@ pub struct CodeGenContext<'ctx, 'a> { pub const_strings: HashMap>, // stores the alloca for variables pub init_bb: BasicBlock<'ctx>, - // the first one is the test_bb, and the second one is bb after the loop + /// The header and exit basic blocks of a loop in this context. See + /// https://llvm.org/docs/LoopTerminology.html for explanation of these terminology. pub loop_target: Option<(BasicBlock<'ctx>, BasicBlock<'ctx>)>, // unwind target bb pub unwind_target: Option>, diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index 47ba743..5b9910d 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -13,8 +13,8 @@ use inkwell::{ attributes::{Attribute, AttributeLoc}, basic_block::BasicBlock, types::BasicTypeEnum, - values::{BasicValue, BasicValueEnum, FunctionValue, PointerValue}, - IntPredicate::EQ, + values::{BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue}, + IntPredicate, }; use nac3parser::ast::{ Constant, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef, @@ -107,7 +107,7 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>( ); // handle negative index let is_negative = ctx.builder.build_int_compare( - inkwell::IntPredicate::SLT, + IntPredicate::SLT, raw_index, generator.get_size_type(ctx.ctx).const_zero(), "is_neg", @@ -120,7 +120,7 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>( // unsigned less than is enough, because negative index after adjustment is // bigger than the length (for unsigned cmp) let bound_check = ctx.builder.build_int_compare( - inkwell::IntPredicate::ULT, + IntPredicate::ULT, index, len, "inbound", @@ -214,6 +214,26 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>( Ok(()) } +/// Generates a sequence of IR which checks whether [value] does not exceed the upper bound of the +/// range as defined by [stop] and [step]. +/// +/// Note that the generated IR will **not** check whether value is part of the range or whether +/// value exceeds the lower bound of the range (as evident by the missing `start` argument). +/// +/// Returns an [IntValue] representing the result of whether the [value] is in the range. +fn gen_in_range_check<'ctx, 'a>( + ctx: &CodeGenContext<'ctx, 'a>, + value: IntValue<'ctx>, + stop: IntValue<'ctx>, + step: IntValue<'ctx>, +) -> IntValue<'ctx> { + let sign = ctx.builder.build_int_compare(IntPredicate::SGT, step, ctx.ctx.i32_type().const_zero(), ""); + let lo = ctx.builder.build_select(sign, value, stop, "").into_int_value(); + let hi = ctx.builder.build_select(sign, stop, value, "").into_int_value(); + + ctx.builder.build_int_compare(IntPredicate::SLT, lo, hi, "cmp") +} + pub fn gen_for<'ctx, 'a, G: CodeGenerator>( generator: &mut G, ctx: &mut CodeGenContext<'ctx, 'a>, @@ -234,49 +254,62 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>( // if there is no orelse, we just go to cont_bb let orelse_bb = if orelse.is_empty() { cont_bb } else { ctx.ctx.append_basic_block(current, "for.orelse") }; + + // Whether the iterable is a range() expression + let is_iterable_range_expr = ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range); + // store loop bb information and restore it later - let loop_bb = ctx.loop_target.replace((test_bb, cont_bb)); + let loop_bb = if is_iterable_range_expr { + ctx.loop_target.replace((body_bb, cont_bb)) + } else { + ctx.loop_target.replace((test_bb, cont_bb)) + }; let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum( ctx, generator, iter.custom.unwrap(), )?; - if ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range) { - // setup + if is_iterable_range_expr { let iter_val = iter_val.into_pointer_value(); + // Internal variable for loop; Cannot be assigned let i = generator.gen_var_alloc(ctx, int32.into(), Some("for.i.addr"))?; - let user_i = generator.gen_store_target(ctx, target, Some("for.user_i.addr"))?; - let (start, end, step) = destructure_range(ctx, iter_val); - ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init")); - ctx.builder.build_unconditional_branch(test_bb); - ctx.builder.position_at_end(test_bb); - let sign = ctx.builder.build_int_compare( - inkwell::IntPredicate::SGT, - step, - int32.const_zero(), - "sign", - ); - // add and test - let tmp = ctx.builder.build_int_add( - ctx.builder.build_load(i, "i").into_int_value(), - step, - "start_loop", - ); - ctx.builder.build_store(i, tmp); - ctx.builder.build_store(user_i, tmp); - // // if step > 0, continue when i < end - let cmp1 = ctx.builder.build_int_compare(inkwell::IntPredicate::SLT, tmp, end, "cmp1"); - // if step < 0, continue when i > end - let cmp2 = ctx.builder.build_int_compare(inkwell::IntPredicate::SGT, tmp, end, "cmp2"); - let pos = ctx.builder.build_and(sign, cmp1, "pos"); - let neg = ctx.builder.build_and(ctx.builder.build_not(sign, "inv"), cmp2, "neg"); + // Variable declared in "target" expression of the loop; Can be reassigned *or* shadowed + let target_i = generator.gen_store_target(ctx, target, Some("for.target.addr"))?; + let (start, stop, step) = destructure_range(ctx, iter_val); + + ctx.builder.build_store(i, start); + + // Pre-Loop Checks: + // - step == 0 -> ValueError + // - start < stop for step > 0 || start > stop for step < 0 + // TODO: Generate step == 0 -> raise ValueError + ctx.builder.build_conditional_branch( - ctx.builder.build_or(pos, neg, "or"), + gen_in_range_check(ctx, start, stop, step), body_bb, orelse_bb, ); + ctx.builder.position_at_end(body_bb); + ctx.builder.build_store(target_i, ctx.builder.build_load(i, "").into_int_value()); + gen_block(generator, ctx, body.iter())?; + + // Test if next element is still in range + let next_i = ctx.builder.build_int_add( + ctx.builder.build_load(i, "").into_int_value(), + step, + "next_i", + ); + let cond_cont_bb = ctx.ctx.append_basic_block(current, "for.cond.cont"); + ctx.builder.build_conditional_branch( + gen_in_range_check(ctx, next_i, stop, step), + cond_cont_bb, + orelse_bb, + ); + + ctx.builder.position_at_end(cond_cont_bb); + ctx.builder.build_store(i, next_i); } else { let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("for.counter.addr"))?; // counter = -1 @@ -288,30 +321,39 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>( ) .into_int_value(); ctx.builder.build_unconditional_branch(test_bb); + ctx.builder.position_at_end(test_bb); let tmp = ctx.builder.build_load(counter, "i").into_int_value(); let tmp = ctx.builder.build_int_add(tmp, size_t.const_int(1, false), "inc"); ctx.builder.build_store(counter, tmp); - let cmp = ctx.builder.build_int_compare(inkwell::IntPredicate::SLT, tmp, len, "cmp"); + let cmp = ctx.builder.build_int_compare(IntPredicate::SLT, tmp, len, "cmp"); ctx.builder.build_conditional_branch(cmp, body_bb, orelse_bb); + ctx.builder.position_at_end(body_bb); let arr_ptr = ctx .build_gep_and_load(iter_val.into_pointer_value(), &[zero, zero]) .into_pointer_value(); let val = ctx.build_gep_and_load(arr_ptr, &[tmp]); generator.gen_assign(ctx, target, val.into())?; + + gen_block(generator, ctx, body.iter())?; } - gen_block(generator, ctx, body.iter())?; for (k, (_, _, counter)) in var_assignment.iter() { let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap(); if counter != counter2 { *static_val = None; } } + if !ctx.is_terminated() { - ctx.builder.build_unconditional_branch(test_bb); + if is_iterable_range_expr { + ctx.builder.build_unconditional_branch(body_bb); + } else { + ctx.builder.build_unconditional_branch(test_bb); + } } + if !orelse.is_empty() { ctx.builder.position_at_end(orelse_bb); gen_block(generator, ctx, orelse.iter())?; @@ -319,12 +361,20 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>( ctx.builder.build_unconditional_branch(cont_bb); } } + for (k, (_, _, counter)) in var_assignment.iter() { let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap(); if counter != counter2 { *static_val = None; } } + + // Clear test_bb if unused + if is_iterable_range_expr { + ctx.builder.position_at_end(test_bb); + ctx.builder.build_unreachable(); + } + ctx.builder.position_at_end(cont_bb); ctx.loop_target = loop_bb; } else { @@ -850,7 +900,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>( .builder .build_load(exn_type.into_pointer_value(), "expected_id") .into_int_value(); - let result = ctx.builder.build_int_compare(EQ, actual_id, expected_id, "exncheck"); + let result = ctx.builder.build_int_compare(IntPredicate::EQ, actual_id, expected_id, "exncheck"); ctx.builder.build_conditional_branch(result, handler_bb, dispatcher_cont); dispatcher_end = dispatcher_cont; } else { diff --git a/nac3standalone/demo/src/loop_decr.py b/nac3standalone/demo/src/loop_decr.py new file mode 100644 index 0000000..59afb1b --- /dev/null +++ b/nac3standalone/demo/src/loop_decr.py @@ -0,0 +1,12 @@ +# For Loop using a decreasing range() expression as its iterable + +@extern +def output_int32(x: int32): + ... + +def run() -> int32: + i = 0 + for i in range(10, 0, -1): + output_int32(i) + output_int32(i) + return 0 diff --git a/nac3standalone/demo/src/loop_mutate_var.py b/nac3standalone/demo/src/loop_mutate_var.py new file mode 100644 index 0000000..3ac5c2c --- /dev/null +++ b/nac3standalone/demo/src/loop_mutate_var.py @@ -0,0 +1,14 @@ +# For Loop using an range() expression as its iterable, additionally reassigning the target on each iteration + +@extern +def output_int32(x: int32): + ... + +def run() -> int32: + i = 0 + for i in range(10): + output_int32(i) + i = 0 + output_int32(i) + output_int32(i) + return 0 -- 2.44.1 From b4a9616648cf467e65a41ad78fb194d72a378573 Mon Sep 17 00:00:00 2001 From: David Mak Date: Tue, 5 Sep 2023 15:39:11 +0800 Subject: [PATCH 5/7] core: Add assertion for when `range` has step of 0 Aligns with the behavior in Python. --- nac3core/src/codegen/stmt.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index 5b9910d..93bd7eb 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -280,10 +280,16 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>( ctx.builder.build_store(i, start); - // Pre-Loop Checks: - // - step == 0 -> ValueError - // - start < stop for step > 0 || start > stop for step < 0 - // TODO: Generate step == 0 -> raise ValueError + // Check "If step is zero, ValueError is raised." + let rangenez = ctx.builder.build_int_compare(IntPredicate::NE, step, int32.const_zero(), ""); + ctx.make_assert( + generator, + rangenez, + "ValueError", + "range() arg 3 must not be zero", + [None, None, None], + ctx.current_loc + ); ctx.builder.build_conditional_branch( gen_in_range_check(ctx, start, stop, step), -- 2.44.1 From b4983526bd5a098b69a84556fb5ccd3ab3b233f8 Mon Sep 17 00:00:00 2001 From: David Mak Date: Tue, 5 Sep 2023 17:07:48 +0800 Subject: [PATCH 6/7] core: Remove redundant for.cond BB for iterable loops Simplifies logic for creating basic blocks. --- nac3core/src/codegen/stmt.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index 93bd7eb..f1edf77 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -248,7 +248,6 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>( let size_t = generator.get_size_type(ctx.ctx); let zero = int32.const_zero(); let current = ctx.builder.get_insert_block().and_then(|bb| bb.get_parent()).unwrap(); - let test_bb = ctx.ctx.append_basic_block(current, "for.cond"); let body_bb = ctx.ctx.append_basic_block(current, "for.body"); let cont_bb = ctx.ctx.append_basic_block(current, "for.end"); // if there is no orelse, we just go to cont_bb @@ -258,13 +257,16 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>( // Whether the iterable is a range() expression let is_iterable_range_expr = ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range); - // store loop bb information and restore it later - let loop_bb = if is_iterable_range_expr { - ctx.loop_target.replace((body_bb, cont_bb)) + // The target BB of the loop backedge + let backedge_bb_target = if is_iterable_range_expr { + body_bb } else { - ctx.loop_target.replace((test_bb, cont_bb)) + ctx.ctx.append_basic_block(current, "for.cond") }; + // store loop bb information and restore it later + let loop_bb = ctx.loop_target.replace((backedge_bb_target, cont_bb)); + let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum( ctx, generator, @@ -317,6 +319,8 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>( ctx.builder.position_at_end(cond_cont_bb); ctx.builder.build_store(i, next_i); } else { + let test_bb = backedge_bb_target; + let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("for.counter.addr"))?; // counter = -1 ctx.builder.build_store(counter, size_t.const_int(u64::max_value(), true)); @@ -353,11 +357,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>( } if !ctx.is_terminated() { - if is_iterable_range_expr { - ctx.builder.build_unconditional_branch(body_bb); - } else { - ctx.builder.build_unconditional_branch(test_bb); - } + ctx.builder.build_unconditional_branch(backedge_bb_target); } if !orelse.is_empty() { @@ -375,12 +375,6 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>( } } - // Clear test_bb if unused - if is_iterable_range_expr { - ctx.builder.position_at_end(test_bb); - ctx.builder.build_unreachable(); - } - ctx.builder.position_at_end(cont_bb); ctx.loop_target = loop_bb; } else { -- 2.44.1 From 4481d48709ecc2eda7f6eaba86e9b94511d418c4 Mon Sep 17 00:00:00 2001 From: David Mak Date: Tue, 5 Sep 2023 18:05:05 +0800 Subject: [PATCH 7/7] core: Use C-style for loop logic for iterables Index increment is now performed at the end of the loop body. --- nac3core/src/codegen/stmt.rs | 18 +++++++++--------- nac3standalone/demo/src/loop_iterable.py | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 nac3standalone/demo/src/loop_iterable.py diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index f1edf77..b310097 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -321,9 +321,8 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>( } else { let test_bb = backedge_bb_target; - let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("for.counter.addr"))?; - // counter = -1 - ctx.builder.build_store(counter, size_t.const_int(u64::max_value(), true)); + let index_addr = generator.gen_var_alloc(ctx, size_t.into(), Some("for.index.addr"))?; + ctx.builder.build_store(index_addr, size_t.const_zero()); let len = ctx .build_gep_and_load( iter_val.into_pointer_value(), @@ -333,20 +332,21 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>( ctx.builder.build_unconditional_branch(test_bb); ctx.builder.position_at_end(test_bb); - let tmp = ctx.builder.build_load(counter, "i").into_int_value(); - let tmp = ctx.builder.build_int_add(tmp, size_t.const_int(1, false), "inc"); - ctx.builder.build_store(counter, tmp); - let cmp = ctx.builder.build_int_compare(IntPredicate::SLT, tmp, len, "cmp"); + let index = ctx.builder.build_load(index_addr, "for.index").into_int_value(); + let cmp = ctx.builder.build_int_compare(IntPredicate::SLT, index, len, "cond"); ctx.builder.build_conditional_branch(cmp, body_bb, orelse_bb); ctx.builder.position_at_end(body_bb); let arr_ptr = ctx .build_gep_and_load(iter_val.into_pointer_value(), &[zero, zero]) .into_pointer_value(); - let val = ctx.build_gep_and_load(arr_ptr, &[tmp]); + let val = ctx.build_gep_and_load(arr_ptr, &[index]); generator.gen_assign(ctx, target, val.into())?; - gen_block(generator, ctx, body.iter())?; + + let index = ctx.builder.build_load(index_addr, "for.index").into_int_value(); + let inc = ctx.builder.build_int_add(index, size_t.const_int(1, true), ""); + ctx.builder.build_store(index_addr, inc); } for (k, (_, _, counter)) in var_assignment.iter() { diff --git a/nac3standalone/demo/src/loop_iterable.py b/nac3standalone/demo/src/loop_iterable.py new file mode 100644 index 0000000..d97ab30 --- /dev/null +++ b/nac3standalone/demo/src/loop_iterable.py @@ -0,0 +1,17 @@ +# For Loop using a list as its iterable + +@extern +def output_int32(x: int32): + ... + +def run() -> int32: + l = [0, 1, 2, 3, 4] + + # i: int32 # declaration-without-initializer not yet supported + i = 0 # i must be declared before the loop; this is not necessary in Python + for i in l: + output_int32(i) + i = 0 + output_int32(i) + output_int32(i) + return 0 -- 2.44.1