Skip to content

Commit

Permalink
fix physreg copy instruction error
Browse files Browse the repository at this point in the history
  • Loading branch information
mhasel committed Jun 18, 2024
1 parent e08e138 commit fe2fb9e
Show file tree
Hide file tree
Showing 12 changed files with 264 additions and 105 deletions.
79 changes: 50 additions & 29 deletions src/codegen/generators/statement_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ use super::{
llvm::Llvm,
};
use crate::{
codegen::{debug::Debug, llvm_typesystem::cast_if_needed},
codegen::{debug::DebugBuilderEnum, LlvmTypedIndex},
codegen::{
debug::{Debug, DebugBuilderEnum},
llvm_typesystem::cast_if_needed,
LlvmTypedIndex,
},
index::{ImplementationIndexEntry, Index},
resolver::{AnnotationMap, AstAnnotations, StatementAnnotation},
typesystem::{self, DataTypeInformation},
typesystem::{self, get_bigger_type, DataTypeInformation},
};
use inkwell::{
basic_block::BasicBlock,
Expand Down Expand Up @@ -390,59 +393,73 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> {
) -> Result<(), Diagnostic> {
let (builder, current_function, context) = self.get_llvm_deps();
let exp_gen = self.create_expr_generator();
self.generate_assignment_statement(counter, start)?;
let counter = exp_gen.generate_lvalue(counter)?;
let counter_value = builder.build_load(counter, "");
let by_step = by_step.as_ref().map_or_else(
|| self.llvm.create_const_numeric(&counter_value.get_type(), "1", SourceLocation::undefined()),
|step| {
self.register_debug_location(step);
exp_gen.generate_expression(step)
},
)?;

let end_ty = self.annotations.get_type_or_void(end, self.index);
let counter_ty = self.annotations.get_type_or_void(counter, self.index);
let ty = get_bigger_type(self.index.get_type_or_panic("DINT"), counter_ty, self.index);
let ll_ty = self.llvm_index.find_associated_type(ty.get_name()).unwrap();

let get_step = || {
by_step.as_ref().map_or_else(
|| self.llvm.create_const_numeric(&ll_ty, "1", SourceLocation::undefined()),
|step| {
self.register_debug_location(step);
let step_ty = by_step
.as_ref()
.map(|it| self.annotations.get_type_or_void(it, self.index))
.unwrap_or_else(|| ty);
let step = exp_gen.generate_expression(step)?;
Ok(cast_if_needed!(exp_gen, ty, step_ty, step, None))
},
)
};

let predicate_incrementing = context.append_basic_block(current_function, "predicate_sle");
let predicate_decrementing = context.append_basic_block(current_function, "predicate_sge");
let loop_body = context.append_basic_block(current_function, "loop");
let increment = context.append_basic_block(current_function, "increment");
let afterloop = context.append_basic_block(current_function, "continue");

// XXX(mhasel): could the generated IR be improved by using phi instructions?
// select loop predicate
self.generate_assignment_statement(counter, start)?;
let counter = exp_gen.generate_lvalue(counter)?;

// generate loop predicate selector. since `STEP` can be a reference, this needs to be a runtime eval
// XXX(mhasel): IR could possibly be improved by generating phi instructions.
// Candidate for frontend optimization for builds without optimization when `STEP`
// is a compile-time constant
let is_incrementing = builder.build_int_compare(
inkwell::IntPredicate::SGT,
by_step.into_int_value(),
self.llvm.i32_type().const_zero(),
get_step()?.into_int_value(),
self.llvm.create_const_numeric(&ll_ty, "0", SourceLocation::undefined())?.into_int_value(),
"is_incrementing",
);
builder.build_conditional_branch(is_incrementing, predicate_incrementing, predicate_decrementing);

// generate predicates for incrementing and decrementing counters
let generate_predicate = |predicate| {
builder.position_at_end(match predicate {
inkwell::IntPredicate::SLE => predicate_incrementing,
inkwell::IntPredicate::SGE => predicate_decrementing,
_ => unreachable!(),
});
let end = exp_gen.generate_expression_value(end).expect("");
// XXX: if the end condition is the result of a callstatement, should it be called each iteration or once before entering the loop?
// if the latter is the case, generate expression value before first unconditional jump and store in local alloca variable

let end = exp_gen.generate_expression_value(end).unwrap();
let end_value = match end {
super::expression_generator::ExpressionValue::LValue(ptr) => builder.build_load(ptr, ""),
super::expression_generator::ExpressionValue::RValue(val) => val,
};

let counter_value = builder.build_load(counter, "");
let cmp = builder.build_int_compare(
predicate,
counter_value.into_int_value(),
end_value.into_int_value(),
cast_if_needed!(exp_gen, ty, counter_ty, counter_value, None).into_int_value(),
cast_if_needed!(exp_gen, ty, end_ty, end_value, None).into_int_value(),
"condition",
);
builder.build_conditional_branch(cmp, loop_body, afterloop);
};
generate_predicate(inkwell::IntPredicate::SLE);
generate_predicate(inkwell::IntPredicate::SGE);

// loop body
// generate loop body
builder.position_at_end(loop_body);
let body_builder = StatementCodeGenerator {
current_loop_continue: Some(increment),
Expand All @@ -453,12 +470,16 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> {
};
body_builder.generate_body(body)?;

// --increment--
// increment counter
builder.build_unconditional_branch(increment);
builder.position_at_end(increment);
let value = builder.build_load(counter, "");
let inc = builder.build_int_add(value.into_int_value(), by_step.into_int_value(), "next");
builder.build_store(counter, inc);
let counter_value = builder.build_load(counter, "");
let inc = inkwell::values::BasicValue::as_basic_value_enum(&builder.build_int_add(
get_step()?.into_int_value(),
cast_if_needed!(exp_gen, ty, counter_ty, counter_value, None).into_int_value(),
"next",
));
builder.build_store(counter, cast_if_needed!(exp_gen, counter_ty, ty, inc, None).into_int_value());

// check condition
builder.build_conditional_branch(is_incrementing, predicate_incrementing, predicate_decrementing);
Expand Down
139 changes: 139 additions & 0 deletions src/codegen/tests/code_gen_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,145 @@ fn for_statement_with_references_steps_test() {
insta::assert_snapshot!(result);
}

#[test]
fn for_statement_with_binary_expressions() {
let result = codegen(
"
PROGRAM prg
VAR
step: DINT;
x : DINT;
y : DINT;
z : DINT;
END_VAR
FOR x := y + 1 TO z - 2 BY step * 3 DO
x;
END_FOR
END_PROGRAM
",
);

insta::assert_snapshot!(result, @r###"
; ModuleID = 'main'
source_filename = "main"
%prg = type { i32, i32, i32, i32 }
@prg_instance = global %prg zeroinitializer, section "var-$RUSTY$prg_instance:r4i32i32i32i32"
define void @prg(%prg* %0) section "fn-$RUSTY$prg:v" {
entry:
%step = getelementptr inbounds %prg, %prg* %0, i32 0, i32 0
%x = getelementptr inbounds %prg, %prg* %0, i32 0, i32 1
%y = getelementptr inbounds %prg, %prg* %0, i32 0, i32 2
%z = getelementptr inbounds %prg, %prg* %0, i32 0, i32 3
%load_y = load i32, i32* %y, align 4
%tmpVar = add i32 %load_y, 1
store i32 %tmpVar, i32* %x, align 4
%load_step = load i32, i32* %step, align 4
%tmpVar1 = mul i32 %load_step, 3
%is_incrementing = icmp sgt i32 %tmpVar1, 0
br i1 %is_incrementing, label %predicate_sle, label %predicate_sge
predicate_sle: ; preds = %increment, %entry
%load_z = load i32, i32* %z, align 4
%tmpVar2 = sub i32 %load_z, 2
%1 = load i32, i32* %x, align 4
%condition = icmp sle i32 %1, %tmpVar2
br i1 %condition, label %loop, label %continue
predicate_sge: ; preds = %increment, %entry
%load_z3 = load i32, i32* %z, align 4
%tmpVar4 = sub i32 %load_z3, 2
%2 = load i32, i32* %x, align 4
%condition5 = icmp sge i32 %2, %tmpVar4
br i1 %condition5, label %loop, label %continue
loop: ; preds = %predicate_sge, %predicate_sle
%load_x = load i32, i32* %x, align 4
br label %increment
increment: ; preds = %loop
%3 = load i32, i32* %x, align 4
%load_step6 = load i32, i32* %step, align 4
%tmpVar7 = mul i32 %load_step6, 3
%next = add i32 %tmpVar7, %3
store i32 %next, i32* %x, align 4
br i1 %is_incrementing, label %predicate_sle, label %predicate_sge
continue: ; preds = %predicate_sge, %predicate_sle
ret void
}
"###);
}

#[test]
fn for_statement_type_casting() {
let result = codegen(
"FUNCTION main
VAR
a: USINT;
b: INT := 1;
END_VAR
FOR a := 0 TO 10 BY b DO
b := b * 3;
END_FOR
END_FUNCTION",
);
insta::assert_snapshot!(result, @r###"
; ModuleID = 'main'
source_filename = "main"
define void @main() section "fn-$RUSTY$main:v" {
entry:
%a = alloca i8, align 1
%b = alloca i16, align 2
store i8 0, i8* %a, align 1
store i16 1, i16* %b, align 2
store i8 0, i8* %a, align 1
%load_b = load i16, i16* %b, align 2
%0 = trunc i16 %load_b to i8
%1 = sext i8 %0 to i32
%is_incrementing = icmp sgt i32 %1, 0
br i1 %is_incrementing, label %predicate_sle, label %predicate_sge
predicate_sle: ; preds = %increment, %entry
%2 = load i8, i8* %a, align 1
%3 = zext i8 %2 to i32
%condition = icmp sle i32 %3, 10
br i1 %condition, label %loop, label %continue
predicate_sge: ; preds = %increment, %entry
%4 = load i8, i8* %a, align 1
%5 = zext i8 %4 to i32
%condition1 = icmp sge i32 %5, 10
br i1 %condition1, label %loop, label %continue
loop: ; preds = %predicate_sge, %predicate_sle
%load_b2 = load i16, i16* %b, align 2
%6 = sext i16 %load_b2 to i32
%tmpVar = mul i32 %6, 3
%7 = trunc i32 %tmpVar to i16
store i16 %7, i16* %b, align 2
br label %increment
increment: ; preds = %loop
%8 = load i8, i8* %a, align 1
%load_b3 = load i16, i16* %b, align 2
%9 = trunc i16 %load_b3 to i8
%10 = sext i8 %9 to i32
%11 = zext i8 %8 to i32
%next = add i32 %10, %11
%12 = trunc i32 %next to i8
store i8 %12, i8* %a, align 1
br i1 %is_incrementing, label %predicate_sle, label %predicate_sge
continue: ; preds = %predicate_sge, %predicate_sle
ret void
}
"###);
}

#[test]
fn while_statement() {
let result = codegen(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,31 @@ entry:
call void @llvm.dbg.declare(metadata i32* %myFunc, metadata !9, metadata !DIExpression()), !dbg !11
store i32 0, i32* %myFunc, align 4, !dbg !8
store i32 1, i32* %myFunc, align 4, !dbg !12
%0 = load i32, i32* %myFunc, align 4, !dbg !12
br i1 true, label %predicate_sle, label %predicate_sge, !dbg !13

predicate_sle: ; preds = %increment, %entry
%1 = load i32, i32* %myFunc, align 4, !dbg !13
%condition = icmp sle i32 %1, 20, !dbg !13
%0 = load i32, i32* %myFunc, align 4, !dbg !13
%condition = icmp sle i32 %0, 20, !dbg !13
br i1 %condition, label %loop, label %continue, !dbg !13

predicate_sge: ; preds = %increment, %entry
%2 = load i32, i32* %myFunc, align 4, !dbg !13
%condition1 = icmp sge i32 %2, 20, !dbg !13
%1 = load i32, i32* %myFunc, align 4, !dbg !13
%condition1 = icmp sge i32 %1, 20, !dbg !13
br i1 %condition1, label %loop, label %continue, !dbg !13

loop: ; preds = %predicate_sge, %predicate_sle
store i32 1, i32* %myFunc, align 4, !dbg !14
br label %increment, !dbg !14

increment: ; preds = %loop
%3 = load i32, i32* %myFunc, align 4, !dbg !14
%next = add i32 %3, 2, !dbg !14
store i32 %next, i32* %myFunc, align 4, !dbg !14
br i1 true, label %predicate_sle, label %predicate_sge, !dbg !14
%2 = load i32, i32* %myFunc, align 4, !dbg !14
%next = add i32 2, %2, !dbg !13
store i32 %next, i32* %myFunc, align 4, !dbg !13
br i1 true, label %predicate_sle, label %predicate_sge, !dbg !13

continue: ; preds = %predicate_sge, %predicate_sle
%myFunc_ret = load i32, i32* %myFunc, align 4, !dbg !14
ret i32 %myFunc_ret, !dbg !14
%myFunc_ret = load i32, i32* %myFunc, align 4, !dbg !13
ret i32 %myFunc_ret, !dbg !13
}

; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,24 @@ define void @prg(%prg* %0) section "fn-$RUSTY$prg:v" {
entry:
%x = getelementptr inbounds %prg, %prg* %0, i32 0, i32 0
store i32 3, i32* %x, align 4
%1 = load i32, i32* %x, align 4
br i1 true, label %predicate_sle, label %predicate_sge

predicate_sle: ; preds = %increment, %entry
%2 = load i32, i32* %x, align 4
%condition = icmp sle i32 %2, 10
%1 = load i32, i32* %x, align 4
%condition = icmp sle i32 %1, 10
br i1 %condition, label %loop, label %continue

predicate_sge: ; preds = %increment, %entry
%3 = load i32, i32* %x, align 4
%condition1 = icmp sge i32 %3, 10
%2 = load i32, i32* %x, align 4
%condition1 = icmp sge i32 %2, 10
br i1 %condition1, label %loop, label %continue

loop: ; preds = %predicate_sge, %predicate_sle
br label %increment

increment: ; preds = %loop
%4 = load i32, i32* %x, align 4
%next = add i32 %4, 1
%3 = load i32, i32* %x, align 4
%next = add i32 1, %3
store i32 %next, i32* %x, align 4
br i1 true, label %predicate_sle, label %predicate_sge

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,30 @@ define void @prg(%prg* %0) section "fn-$RUSTY$prg:v" {
entry:
%x = getelementptr inbounds %prg, %prg* %0, i32 0, i32 0
store i16 3, i16* %x, align 2
%1 = load i16, i16* %x, align 2
br i1 true, label %predicate_sle, label %predicate_sge

predicate_sle: ; preds = %increment, %entry
%2 = load i16, i16* %x, align 2
%condition = icmp sle i16 %2, 10
%1 = load i16, i16* %x, align 2
%2 = sext i16 %1 to i32
%condition = icmp sle i32 %2, 10
br i1 %condition, label %loop, label %continue

predicate_sge: ; preds = %increment, %entry
%3 = load i16, i16* %x, align 2
%condition1 = icmp sge i16 %3, 10
%4 = sext i16 %3 to i32
%condition1 = icmp sge i32 %4, 10
br i1 %condition1, label %loop, label %continue

loop: ; preds = %predicate_sge, %predicate_sle
%load_x = load i16, i16* %x, align 2
br label %increment

increment: ; preds = %loop
%4 = load i16, i16* %x, align 2
%next = add i16 %4, 1
store i16 %next, i16* %x, align 2
%5 = load i16, i16* %x, align 2
%6 = sext i16 %5 to i32
%next = add i32 1, %6
%7 = trunc i32 %next to i16
store i16 %7, i16* %x, align 2
br i1 true, label %predicate_sle, label %predicate_sge

continue: ; preds = %predicate_sge, %predicate_sle
Expand Down
Loading

0 comments on commit fe2fb9e

Please sign in to comment.