Skip to content

Commit af55a2d

Browse files
committed
fix: for loop predicate
This PR fixes for loops executing once when the predicate already should not be met for decrementing loops. I have also re-implemented the codegen logic for for-loops, resulting in fewer predecessors and hopefully more readable IR. Resolves #1207
1 parent 44df0db commit af55a2d

11 files changed

+256
-677
lines changed

src/codegen/generators/statement_generator.rs

Lines changed: 51 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use inkwell::{
1414
basic_block::BasicBlock,
1515
builder::Builder,
1616
context::Context,
17-
values::{BasicValueEnum, FunctionValue, PointerValue},
17+
values::{FunctionValue, PointerValue},
1818
};
1919
use plc_ast::{
2020
ast::{
@@ -389,115 +389,67 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> {
389389
body: &[AstNode],
390390
) -> Result<(), Diagnostic> {
391391
let (builder, current_function, context) = self.get_llvm_deps();
392-
self.generate_assignment_statement(counter, start)?;
393-
let condition_check = context.append_basic_block(current_function, "condition_check");
394-
let for_body = context.append_basic_block(current_function, "for_body");
395-
let increment_block = context.append_basic_block(current_function, "increment");
396-
let continue_block = context.append_basic_block(current_function, "continue");
397-
398-
//Generate an initial jump to the for condition
399-
builder.build_unconditional_branch(condition_check);
400-
401-
//Check loop condition
402-
builder.position_at_end(condition_check);
403392
let exp_gen = self.create_expr_generator();
404-
let counter_statement = exp_gen.generate_expression(counter)?;
405-
406-
//. / and_2 \
407-
//. / and 1 \
408-
//. (counter_end_le && counter_start_ge) || (counter_end_ge && counter_start_le)
409-
let or_eval = self.generate_compare_expression(counter, end, start, &exp_gen)?;
410-
411-
builder.build_conditional_branch(to_i1(or_eval.into_int_value(), builder), for_body, continue_block);
393+
self.generate_assignment_statement(counter, start)?;
394+
let predicate_incrementing = context.append_basic_block(current_function, "predicate_inc");
395+
let predicate_decrementing = context.append_basic_block(current_function, "predicate_dec");
396+
let loop_body = context.append_basic_block(current_function, "loop");
397+
let afterloop = context.append_basic_block(current_function, "continue");
412398

413-
//Enter the for loop
414-
builder.position_at_end(for_body);
415-
let body_generator = StatementCodeGenerator {
416-
current_loop_exit: Some(continue_block),
417-
current_loop_continue: Some(increment_block),
418-
load_prefix: self.load_prefix.clone(),
419-
load_suffix: self.load_suffix.clone(),
420-
..*self
421-
};
422-
body_generator.generate_body(body)?;
423-
builder.build_unconditional_branch(increment_block);
399+
let counter = exp_gen.generate_lvalue(counter)?;
400+
let end = exp_gen.generate_expression(end)?;
401+
let counter_value = builder.build_load(counter, "");
424402

425-
//Increment
426-
builder.position_at_end(increment_block);
427-
let expression_generator = self.create_expr_generator();
428-
let step_by_value = by_step.as_ref().map_or_else(
429-
|| {
430-
self.llvm.create_const_numeric(
431-
&counter_statement.get_type(),
432-
"1",
433-
SourceLocation::undefined(),
434-
)
435-
},
403+
let by_step = by_step.as_ref().map_or_else(
404+
|| self.llvm.create_const_numeric(&counter_value.get_type(), "1", SourceLocation::undefined()),
436405
|step| {
437406
self.register_debug_location(step);
438-
expression_generator.generate_expression(step)
407+
exp_gen.generate_expression(step)
439408
},
440409
)?;
441-
442-
let next = builder.build_int_add(
443-
counter_statement.into_int_value(),
444-
step_by_value.into_int_value(),
445-
"tmpVar",
410+
let is_incrementing = builder.build_int_compare(
411+
inkwell::IntPredicate::SGT,
412+
counter_value.into_int_value(),
413+
self.llvm.i32_type().const_zero(),
414+
"is_incrementing",
446415
);
447416

448-
let ptr = expression_generator.generate_lvalue(counter)?;
449-
builder.build_store(ptr, next);
450-
451-
//Loop back
452-
builder.build_unconditional_branch(condition_check);
453-
454-
//Continue
455-
builder.position_at_end(continue_block);
456-
457-
Ok(())
458-
}
459-
460-
fn generate_compare_expression(
461-
&'a self,
462-
counter: &AstNode,
463-
end: &AstNode,
464-
start: &AstNode,
465-
exp_gen: &'a ExpressionCodeGenerator,
466-
) -> Result<BasicValueEnum<'a>, Diagnostic> {
467-
let bool_id = self.annotations.get_bool_id();
468-
let counter_end_ge = AstFactory::create_binary_expression(
469-
counter.clone(),
470-
Operator::GreaterOrEqual,
471-
end.clone(),
472-
bool_id,
473-
);
474-
let counter_start_ge = AstFactory::create_binary_expression(
475-
counter.clone(),
476-
Operator::GreaterOrEqual,
477-
start.clone(),
478-
bool_id,
417+
// --check loop predicate--
418+
builder.build_conditional_branch(is_incrementing, predicate_incrementing, predicate_decrementing);
419+
// --incrementing loops--
420+
builder.position_at_end(predicate_incrementing);
421+
let value = builder.build_load(counter, "");
422+
let inc_cmp = builder.build_int_compare(
423+
inkwell::IntPredicate::SLE,
424+
value.into_int_value(),
425+
end.into_int_value(),
426+
"condition",
479427
);
480-
let counter_end_le = AstFactory::create_binary_expression(
481-
counter.clone(),
482-
Operator::LessOrEqual,
483-
end.clone(),
484-
bool_id,
428+
builder.build_conditional_branch(inc_cmp, loop_body, afterloop);
429+
// --decrementing loops--
430+
builder.position_at_end(predicate_decrementing);
431+
let value = builder.build_load(counter, "");
432+
let dec_cmp = builder.build_int_compare(
433+
inkwell::IntPredicate::SGE,
434+
value.into_int_value(),
435+
end.into_int_value(),
436+
"condition",
485437
);
486-
let counter_start_le = AstFactory::create_binary_expression(
487-
counter.clone(),
488-
Operator::LessOrEqual,
489-
start.clone(),
490-
bool_id,
491-
);
492-
let and_1 =
493-
AstFactory::create_binary_expression(counter_end_le, Operator::And, counter_start_ge, bool_id);
494-
let and_2 =
495-
AstFactory::create_binary_expression(counter_end_ge, Operator::And, counter_start_le, bool_id);
496-
let or = AstFactory::create_binary_expression(and_1, Operator::Or, and_2, bool_id);
497-
498-
self.register_debug_location(&or);
499-
let or_eval = exp_gen.generate_expression(&or)?;
500-
Ok(or_eval)
438+
builder.build_conditional_branch(dec_cmp, loop_body, afterloop);
439+
440+
// --body--
441+
builder.position_at_end(loop_body);
442+
self.generate_body(body)?;
443+
// --increment--
444+
let value = builder.build_load(counter, "");
445+
let inc = builder.build_int_add(value.into_int_value(), by_step.into_int_value(), "increment");
446+
builder.build_store(counter, inc);
447+
//--check condition again--
448+
// builder.build_phi(self.llvm.i32_type(), "phi");
449+
builder.build_conditional_branch(is_incrementing, predicate_incrementing, predicate_decrementing);
450+
// --continue--
451+
builder.position_at_end(afterloop);
452+
Ok(())
501453
}
502454

503455
/// genertes a case statement

src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__for_conditions_location_marked.snap

Lines changed: 24 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -11,67 +11,30 @@ entry:
1111
call void @llvm.dbg.declare(metadata i32* %myFunc, metadata !9, metadata !DIExpression()), !dbg !11
1212
store i32 0, i32* %myFunc, align 4, !dbg !8
1313
store i32 1, i32* %myFunc, align 4, !dbg !12
14-
br label %condition_check, !dbg !12
15-
16-
condition_check: ; preds = %increment, %entry
17-
%load_myFunc = load i32, i32* %myFunc, align 4, !dbg !12
18-
%load_myFunc1 = load i32, i32* %myFunc, align 4, !dbg !12
19-
%tmpVar = icmp sle i32 %load_myFunc1, 20, !dbg !12
20-
%0 = zext i1 %tmpVar to i8, !dbg !12
21-
%1 = icmp ne i8 %0, 0, !dbg !12
22-
br i1 %1, label %2, label %5, !dbg !12
23-
24-
for_body: ; preds = %12
25-
store i32 1, i32* %myFunc, align 4, !dbg !13
26-
br label %increment, !dbg !13
27-
28-
increment: ; preds = %for_body
29-
%tmpVar8 = add i32 %load_myFunc, 2, !dbg !14
30-
store i32 %tmpVar8, i32* %myFunc, align 4, !dbg !14
31-
br label %condition_check, !dbg !14
32-
33-
continue: ; preds = %12
14+
%0 = load i32, i32* %myFunc, align 4, !dbg !12
15+
%is_incrementing = icmp sgt i32 %0, 0, !dbg !13
16+
br i1 %is_incrementing, label %predicate_inc, label %predicate_dec, !dbg !13
17+
18+
predicate_inc: ; preds = %loop, %entry
19+
%1 = load i32, i32* %myFunc, align 4, !dbg !13
20+
%condition = icmp sle i32 %1, 20, !dbg !13
21+
br i1 %condition, label %loop, label %continue, !dbg !13
22+
23+
predicate_dec: ; preds = %loop, %entry
24+
%2 = load i32, i32* %myFunc, align 4, !dbg !13
25+
%condition1 = icmp sge i32 %2, 20, !dbg !13
26+
br i1 %condition1, label %loop, label %continue, !dbg !13
27+
28+
loop: ; preds = %predicate_dec, %predicate_inc
29+
store i32 1, i32* %myFunc, align 4, !dbg !14
30+
%3 = load i32, i32* %myFunc, align 4, !dbg !14
31+
%increment = add i32 %3, 2, !dbg !14
32+
store i32 %increment, i32* %myFunc, align 4, !dbg !14
33+
br i1 %is_incrementing, label %predicate_inc, label %predicate_dec, !dbg !14
34+
35+
continue: ; preds = %predicate_dec, %predicate_inc
3436
%myFunc_ret = load i32, i32* %myFunc, align 4, !dbg !14
3537
ret i32 %myFunc_ret, !dbg !14
36-
37-
2: ; preds = %condition_check
38-
%load_myFunc2 = load i32, i32* %myFunc, align 4, !dbg !12
39-
%tmpVar3 = icmp sge i32 %load_myFunc2, 1, !dbg !12
40-
%3 = zext i1 %tmpVar3 to i8, !dbg !12
41-
%4 = icmp ne i8 %3, 0, !dbg !12
42-
br label %5, !dbg !12
43-
44-
5: ; preds = %2, %condition_check
45-
%6 = phi i1 [ %1, %condition_check ], [ %4, %2 ], !dbg !12
46-
%7 = zext i1 %6 to i8, !dbg !12
47-
%8 = icmp ne i8 %7, 0, !dbg !12
48-
br i1 %8, label %12, label %9, !dbg !12
49-
50-
9: ; preds = %5
51-
%load_myFunc4 = load i32, i32* %myFunc, align 4, !dbg !12
52-
%tmpVar5 = icmp sge i32 %load_myFunc4, 20, !dbg !12
53-
%10 = zext i1 %tmpVar5 to i8, !dbg !12
54-
%11 = icmp ne i8 %10, 0, !dbg !12
55-
br i1 %11, label %16, label %19, !dbg !12
56-
57-
12: ; preds = %19, %5
58-
%13 = phi i1 [ %8, %5 ], [ %22, %19 ], !dbg !12
59-
%14 = zext i1 %13 to i8, !dbg !12
60-
%15 = icmp ne i8 %14, 0, !dbg !12
61-
br i1 %15, label %for_body, label %continue, !dbg !12
62-
63-
16: ; preds = %9
64-
%load_myFunc6 = load i32, i32* %myFunc, align 4, !dbg !12
65-
%tmpVar7 = icmp sle i32 %load_myFunc6, 1, !dbg !12
66-
%17 = zext i1 %tmpVar7 to i8, !dbg !12
67-
%18 = icmp ne i8 %17, 0, !dbg !12
68-
br label %19, !dbg !12
69-
70-
19: ; preds = %16, %9
71-
%20 = phi i1 [ %11, %9 ], [ %18, %16 ], !dbg !12
72-
%21 = zext i1 %20 to i8, !dbg !12
73-
%22 = icmp ne i8 %21, 0, !dbg !12
74-
br label %12, !dbg !12
7538
}
7639

7740
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
@@ -95,5 +58,5 @@ attributes #0 = { nofree nosync nounwind readnone speculatable willreturn }
9558
!10 = !DIBasicType(name: "DINT", size: 32, encoding: DW_ATE_signed, flags: DIFlagPublic)
9659
!11 = !DILocation(line: 2, column: 17, scope: !4)
9760
!12 = !DILocation(line: 3, column: 16, scope: !4)
98-
!13 = !DILocation(line: 4, column: 16, scope: !4)
99-
!14 = !DILocation(line: 3, column: 37, scope: !4)
61+
!13 = !DILocation(line: 3, column: 37, scope: !4)
62+
!14 = !DILocation(line: 4, column: 16, scope: !4)

src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__for_statement_continue.snap

Lines changed: 21 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -13,64 +13,27 @@ define void @prg(%prg* %0) section "fn-$RUSTY$prg:v" {
1313
entry:
1414
%x = getelementptr inbounds %prg, %prg* %0, i32 0, i32 0
1515
store i32 3, i32* %x, align 4
16-
br label %condition_check
17-
18-
condition_check: ; preds = %increment, %entry
16+
%1 = load i32, i32* %x, align 4
17+
%is_incrementing = icmp sgt i32 %1, 0
18+
br i1 %is_incrementing, label %predicate_inc, label %predicate_dec
19+
20+
predicate_inc: ; preds = %loop, %entry
21+
%2 = load i32, i32* %x, align 4
22+
%condition = icmp sle i32 %2, 10
23+
br i1 %condition, label %loop, label %continue
24+
25+
predicate_dec: ; preds = %loop, %entry
26+
%3 = load i32, i32* %x, align 4
27+
%condition1 = icmp sge i32 %3, 10
28+
br i1 %condition1, label %loop, label %continue
29+
30+
loop: ; preds = %predicate_dec, %predicate_inc
31+
%4 = load i32, i32* %x, align 4
32+
%increment = add i32 %4, 1
33+
store i32 %increment, i32* %x, align 4
34+
br i1 %is_incrementing, label %predicate_inc, label %predicate_dec
35+
36+
continue: ; preds = %predicate_dec, %predicate_inc
1937
%load_x = load i32, i32* %x, align 4
20-
%load_x1 = load i32, i32* %x, align 4
21-
%tmpVar = icmp sle i32 %load_x1, 10
22-
%1 = zext i1 %tmpVar to i8
23-
%2 = icmp ne i8 %1, 0
24-
br i1 %2, label %3, label %6
25-
26-
for_body: ; preds = %13
27-
br label %increment
28-
29-
increment: ; preds = %for_body
30-
%tmpVar8 = add i32 %load_x, 1
31-
store i32 %tmpVar8, i32* %x, align 4
32-
br label %condition_check
33-
34-
continue: ; preds = %13
35-
%load_x9 = load i32, i32* %x, align 4
3638
ret void
37-
38-
3: ; preds = %condition_check
39-
%load_x2 = load i32, i32* %x, align 4
40-
%tmpVar3 = icmp sge i32 %load_x2, 3
41-
%4 = zext i1 %tmpVar3 to i8
42-
%5 = icmp ne i8 %4, 0
43-
br label %6
44-
45-
6: ; preds = %3, %condition_check
46-
%7 = phi i1 [ %2, %condition_check ], [ %5, %3 ]
47-
%8 = zext i1 %7 to i8
48-
%9 = icmp ne i8 %8, 0
49-
br i1 %9, label %13, label %10
50-
51-
10: ; preds = %6
52-
%load_x4 = load i32, i32* %x, align 4
53-
%tmpVar5 = icmp sge i32 %load_x4, 10
54-
%11 = zext i1 %tmpVar5 to i8
55-
%12 = icmp ne i8 %11, 0
56-
br i1 %12, label %17, label %20
57-
58-
13: ; preds = %20, %6
59-
%14 = phi i1 [ %9, %6 ], [ %23, %20 ]
60-
%15 = zext i1 %14 to i8
61-
%16 = icmp ne i8 %15, 0
62-
br i1 %16, label %for_body, label %continue
63-
64-
17: ; preds = %10
65-
%load_x6 = load i32, i32* %x, align 4
66-
%tmpVar7 = icmp sle i32 %load_x6, 3
67-
%18 = zext i1 %tmpVar7 to i8
68-
%19 = icmp ne i8 %18, 0
69-
br label %20
70-
71-
20: ; preds = %17, %10
72-
%21 = phi i1 [ %12, %10 ], [ %19, %17 ]
73-
%22 = zext i1 %21 to i8
74-
%23 = icmp ne i8 %22, 0
75-
br label %13
7639
}

0 commit comments

Comments
 (0)