Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow optional output assignments, empty assignments will be ignored … #437

Merged
merged 4 commits into from
Jan 19, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 37 additions & 34 deletions src/codegen/generators/expression_generator.rs
Original file line number Diff line number Diff line change
@@ -726,40 +726,43 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> {
let current_block = builder.get_insert_block().expect(INTERNAL_LLVM_ERROR);

builder.position_at_end(*output_block);
if let AstStatement::Reference { name, .. } = left {
let parameter = self
.index
.find_member(function_name, name)
.ok_or_else(|| Diagnostic::unresolved_reference(name, left.get_location()))?;
let index = parameter.get_location_in_parent();
let param_type = self
.index
.find_effective_type(parameter.get_type_name())
.or_else(|| {
self.index
.find_input_parameter(function_name, index as u32)
.and_then(|var| self.index.find_effective_type(var.get_type_name()))
})
.ok_or_else(|| {
Diagnostic::unknown_type(parameter.get_type_name(), left.get_location())
})?;
//load the function prameter
let pointer_to_param = builder
.build_struct_gep(parameter_struct, index as u32, "")
.expect(INTERNAL_LLVM_ERROR);

let l_value = self.generate_element_pointer(right)?;
let loaded_value = builder.build_load(pointer_to_param, parameter.get_name());
let value = cast_if_needed(
self.llvm,
self.index,
self.llvm_index,
self.get_type_hint_for(right)?,
loaded_value,
param_type,
right,
)?;
builder.build_store(l_value, value);
// (output => ) output assignments are optional, in this case ignore codegen
if !matches!(right, AstStatement::EmptyStatement { .. }) {
if let AstStatement::Reference { name, .. } = left {
let parameter = self
.index
.find_member(function_name, name)
.ok_or_else(|| Diagnostic::unresolved_reference(name, left.get_location()))?;
let index = parameter.get_location_in_parent();
let param_type = self
.index
.find_effective_type(parameter.get_type_name())
.or_else(|| {
self.index
.find_input_parameter(function_name, index as u32)
.and_then(|var| self.index.find_effective_type(var.get_type_name()))
})
.ok_or_else(|| {
Diagnostic::unknown_type(parameter.get_type_name(), left.get_location())
})?;
//load the function prameter
let pointer_to_param = builder
.build_struct_gep(parameter_struct, index as u32, "")
.expect(INTERNAL_LLVM_ERROR);

let l_value = self.generate_element_pointer(right)?;
let loaded_value = builder.build_load(pointer_to_param, parameter.get_name());
let value = cast_if_needed(
self.llvm,
self.index,
self.llvm_index,
self.get_type_hint_for(right)?,
loaded_value,
param_type,
right,
)?;
builder.build_store(l_value, value);
}
}
builder.position_at_end(current_block);
Ok(())
27 changes: 27 additions & 0 deletions src/codegen/tests/code_gen_tests.rs
Original file line number Diff line number Diff line change
@@ -2754,3 +2754,30 @@ fn order_var_and_var_temp_block() {
// codegen should be successful
insta::assert_snapshot!(result);
}

#[test]
fn optional_output_assignment() {
// GIVEN a program calling a function and only assigning one output
let result = codegen(
"
PROGRAM foo
VAR_OUTPUT
output1 : DINT;
output2 : DINT;
END_VAR
output1 := 1;
output2 := 2;
END_PROGRAM

PROGRAM main
VAR
var1 : DINT;
var2 : DINT;
END_VAR
foo(output1 =>, output2 => var2);
END_PROGRAM
",
);
// codegen should be successful
insta::assert_snapshot!(result);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
source: src/codegen/tests/code_gen_tests.rs
assertion_line: 2760
expression: result

---
; ModuleID = 'main'
source_filename = "main"

%foo_interface = type { i32, i32 }
%main_interface = type { i32, i32 }

@foo_instance = global %foo_interface zeroinitializer
@main_instance = global %main_interface zeroinitializer

define void @foo(%foo_interface* %0) {
entry:
%output1 = getelementptr inbounds %foo_interface, %foo_interface* %0, i32 0, i32 0
%output2 = getelementptr inbounds %foo_interface, %foo_interface* %0, i32 0, i32 1
store i32 1, i32* %output1, align 4
store i32 2, i32* %output2, align 4
ret void
}

define void @main(%main_interface* %0) {
entry:
%var1 = getelementptr inbounds %main_interface, %main_interface* %0, i32 0, i32 0
%var2 = getelementptr inbounds %main_interface, %main_interface* %0, i32 0, i32 1
br label %input

input: ; preds = %entry
br label %call

call: ; preds = %input
call void @foo(%foo_interface* @foo_instance)
br label %output

output: ; preds = %call
%output2 = load i32, i32* getelementptr inbounds (%foo_interface, %foo_interface* @foo_instance, i32 0, i32 1), align 4
store i32 %output2, i32* %var2, align 4
br label %continue

continue: ; preds = %output
ret void
}

33 changes: 33 additions & 0 deletions tests/correctness/functions.rs
Original file line number Diff line number Diff line change
@@ -435,3 +435,36 @@ fn inouts_behave_like_pointers() {
assert_eq!(8, interface.p2);
assert_eq!(9, interface.p3);
}

#[test]
fn optional_output_assignment() {
struct MainType {
var1: i32,
var2: i32,
}

let function = r#"
PROGRAM foo
VAR_OUTPUT
output1 : DINT;
output2 : DINT;
END_VAR
output1 := 1;
output2 := 2;
END_PROGRAM

PROGRAM main
VAR
var1 : DINT;
var2 : DINT;
END_VAR
foo(output1 =>, output2 => var2);
END_PROGRAM
"#;

let mut interface = MainType { var1: 0, var2: 0 };
let _: i32 = compile_and_run(function.to_string(), &mut interface);

assert_eq!(0, interface.var1);
assert_eq!(2, interface.var2);
}