Skip to content

Commit 6cba4ce

Browse files
committed
fix: function block calls with inherited parameters
1 parent 964c0c7 commit 6cba4ce

21 files changed

+700
-137
lines changed

compiler/plc_ast/src/ast.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,6 +1484,15 @@ impl AstNode {
14841484
pub fn is_real(&self) -> bool {
14851485
matches!(self.stmt, AstStatement::Literal(AstLiteral::Real(_), ..))
14861486
}
1487+
1488+
pub fn get_name_of_lhs_of_assignment(&self) -> Option<&str> {
1489+
match &self.stmt {
1490+
AstStatement::Assignment(Assignment { left, .. })
1491+
| AstStatement::OutputAssignment(Assignment { left, .. })
1492+
| AstStatement::RefAssignment(Assignment { left, .. }) => left.get_flat_reference_name(),
1493+
_ => None,
1494+
}
1495+
}
14871496
}
14881497

14891498
#[derive(Clone, Copy, Debug, PartialEq, Eq)]

src/codegen/generators/expression_generator.rs

Lines changed: 74 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ struct CallParameterAssignment<'a, 'b> {
7474
function_name: &'b str,
7575
/// the position of the argument in the POU's argument's list
7676
index: u32,
77+
depth: u32,
78+
arg_pou: &'b str,
7779
/// a pointer to the struct instance that carries the call's arguments
7880
parameter_struct: PointerValue<'a>,
7981
}
@@ -702,14 +704,18 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
702704
let is_output = pou_info.get(index).is_some_and(|param| param.get_variable_type().is_output());
703705

704706
if assignment_statement.is_output_assignment() || (implicit && is_output) {
707+
let Some(StatementAnnotation::Argument { position, depth, pou, .. }) =
708+
self.annotations.get_hint(assignment_statement)
709+
else {
710+
panic!()
711+
};
712+
705713
self.assign_output_value(&CallParameterAssignment {
706714
assignment: assignment_statement,
707715
function_name,
708-
index: self
709-
.annotations
710-
.get_hint(assignment_statement)
711-
.and_then(StatementAnnotation::get_location_in_parent)
712-
.expect("arguments must have a type hint"),
716+
index: *position as u32,
717+
depth: *depth as u32,
718+
arg_pou: pou,
713719
parameter_struct,
714720
})?
715721
}
@@ -720,11 +726,9 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
720726

721727
fn assign_output_value(&self, param_context: &CallParameterAssignment) -> Result<(), CodegenError> {
722728
match &param_context.assignment.stmt {
723-
AstStatement::OutputAssignment(assignment) => self.generate_explicit_output_assignment(
724-
param_context.parameter_struct,
725-
param_context.function_name,
726-
assignment,
727-
),
729+
AstStatement::OutputAssignment(assignment) => {
730+
self.generate_explicit_output_assignment(param_context, assignment)
731+
}
728732

729733
_ => self.generate_output_assignment(param_context),
730734
}
@@ -840,15 +844,23 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
840844
}
841845

842846
fn generate_output_assignment(&self, context: &CallParameterAssignment) -> Result<(), CodegenError> {
843-
let &CallParameterAssignment { assignment: expr, function_name, index, parameter_struct } = context;
847+
let &CallParameterAssignment {
848+
assignment: expr,
849+
function_name,
850+
index,
851+
depth: _,
852+
arg_pou,
853+
parameter_struct,
854+
} = context;
855+
844856
let builder = &self.llvm.builder;
845857

846858
// We don't want to generate any code if the right side of an assignment is empty, e.g. `foo(out =>)`
847859
if expr.is_empty_statement() {
848860
return Ok(());
849861
}
850862

851-
let parameter = self.index.get_declared_parameter(function_name, index).expect("must exist");
863+
let parameter = self.index.get_declared_parameter(arg_pou, index).expect("must exist");
852864

853865
match expr.get_stmt() {
854866
AstStatement::ReferenceExpr(_) if expr.has_direct_access() => {
@@ -889,12 +901,18 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
889901
let assigned_output = self.generate_lvalue(expr)?;
890902
let assigned_output_type =
891903
self.annotations.get_type_or_void(expr, self.index).get_type_information();
892-
let output = builder.build_struct_gep(parameter_struct, index, "").map_err(|_| {
893-
Diagnostic::codegen_error(
894-
format!("Cannot build generate parameter: {parameter:#?}"),
895-
&parameter.source_location,
896-
)
897-
})?;
904+
let output = unsafe {
905+
let mut gep_index = vec![0; (context.depth + 1) as usize];
906+
gep_index.push(context.index);
907+
908+
let i32_type = self.llvm.context.i32_type();
909+
let gep_index = gep_index
910+
.into_iter()
911+
.map(|value| i32_type.const_int(value as u64, false))
912+
.collect::<Vec<_>>();
913+
914+
builder.build_gep(parameter_struct, &gep_index, "").unwrap()
915+
};
898916

899917
let output_value_type = self.index.get_type_information_or_void(parameter.get_type_name());
900918

@@ -919,24 +937,21 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
919937

920938
fn generate_explicit_output_assignment(
921939
&self,
922-
parameter_struct: PointerValue<'ink>,
923-
function_name: &str,
940+
param_context: &CallParameterAssignment,
924941
assignment: &Assignment,
925942
) -> Result<(), CodegenError> {
943+
let parameter_struct = param_context.parameter_struct;
944+
let function_name = param_context.function_name;
926945
let Assignment { left, right } = assignment;
927946

928-
if let Some(StatementAnnotation::Variable { qualified_name, .. }) = self.annotations.get(left) {
929-
let parameter = self
930-
.index
931-
.find_fully_qualified_variable(qualified_name)
932-
.ok_or_else(|| Diagnostic::unresolved_reference(qualified_name, left.as_ref()))?;
933-
let index = parameter.get_location_in_parent();
934-
947+
if let Some(StatementAnnotation::Variable { .. }) = self.annotations.get(left) {
935948
self.generate_output_assignment(&CallParameterAssignment {
936949
assignment: right,
937950
function_name,
938-
index,
951+
index: param_context.index,
952+
depth: param_context.depth,
939953
parameter_struct,
954+
arg_pou: param_context.arg_pou,
940955
})?
941956
};
942957

@@ -1280,14 +1295,18 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
12801295
})
12811296
.unwrap_or_else(|| vec![parameter_struct.as_basic_value_enum().into()]);
12821297
for argument in arguments.iter() {
1298+
let Some(StatementAnnotation::Argument { position, depth, pou, .. }) =
1299+
self.annotations.get_hint(argument)
1300+
else {
1301+
panic!()
1302+
};
1303+
12831304
let parameter = self.generate_call_struct_argument_assignment(&CallParameterAssignment {
12841305
assignment: argument,
12851306
function_name: pou_name,
1286-
index: self
1287-
.annotations
1288-
.get_hint(argument)
1289-
.and_then(StatementAnnotation::get_location_in_parent)
1290-
.expect("arguments must have a type hint"),
1307+
index: *position as u32,
1308+
depth: *depth as u32,
1309+
arg_pou: pou,
12911310
parameter_struct,
12921311
})?;
12931312
if let Some(parameter) = parameter {
@@ -1378,28 +1397,35 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
13781397
param_context: &CallParameterAssignment,
13791398
) -> Result<Option<BasicValueEnum<'ink>>, CodegenError> {
13801399
let builder = &self.llvm.builder;
1381-
let function_name = param_context.function_name;
1400+
// let function_name = param_context.function_name;
13821401
let index = param_context.index;
13831402
let parameter_struct = param_context.parameter_struct;
13841403
let expression = param_context.assignment;
1385-
if let Some(parameter) = self.index.get_declared_parameter(function_name, index) {
1404+
1405+
if let Some(parameter) = self.index.get_declared_parameter(param_context.arg_pou, index) {
13861406
// this happens before the pou call
13871407
// before the call statement we may only consider inputs and inouts
13881408
// after the call we need to copy the output values to the correct assigned variables
13891409
if matches!(parameter.get_variable_type(), VariableType::Output) {
13901410
return Ok(None);
13911411
}
13921412

1393-
let pointer_to_param = builder.build_struct_gep(parameter_struct, index, "").map_err(|_| {
1394-
Diagnostic::codegen_error(
1395-
format!("Cannot build generate parameter: {expression:#?}"),
1396-
expression,
1397-
)
1398-
})?;
1413+
let pointer_to_param = unsafe {
1414+
let mut gep_index = vec![0; (param_context.depth + 1) as usize];
1415+
gep_index.push(param_context.index);
1416+
1417+
let i32_type = self.llvm.context.i32_type();
1418+
let gep_index = gep_index
1419+
.into_iter()
1420+
.map(|value| i32_type.const_int(value as u64, false))
1421+
.collect::<Vec<_>>();
1422+
1423+
builder.build_gep(parameter_struct, &gep_index, "").unwrap()
1424+
};
13991425

14001426
let parameter = self
14011427
.index
1402-
.find_parameter(function_name, index)
1428+
.find_parameter(param_context.arg_pou, index)
14031429
.and_then(|var| self.index.find_effective_type_by_name(var.get_type_name()))
14041430
.map(|var| var.get_type_information())
14051431
.unwrap_or_else(|| self.index.get_void_type().get_type_information());
@@ -1444,21 +1470,23 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
14441470
.index
14451471
.find_fully_qualified_variable(qualified_name)
14461472
.ok_or_else(|| Diagnostic::unresolved_reference(qualified_name, left))?;
1447-
let index = parameter.get_location_in_parent();
14481473

14491474
// don't generate param assignments for empty statements, with the exception
14501475
// of VAR_IN_OUT params - they need an address to point to
14511476
let is_auto_deref = self
14521477
.index
14531478
.find_effective_type_by_name(parameter.get_type_name())
1454-
.map(|var| var.get_type_information())
1455-
.unwrap_or(self.index.get_void_type().get_type_information())
1479+
.map(DataType::get_type_information)
1480+
.unwrap_or_else(|| self.index.get_void_type().get_type_information())
14561481
.is_auto_deref();
1482+
14571483
if !right.is_empty_statement() || is_auto_deref {
14581484
self.generate_call_struct_argument_assignment(&CallParameterAssignment {
14591485
assignment: right,
14601486
function_name,
1461-
index,
1487+
index: param_context.index,
1488+
depth: param_context.depth,
1489+
arg_pou: param_context.arg_pou,
14621490
parameter_struct,
14631491
})?;
14641492
};

src/codegen/tests/directaccess_test.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ fn direct_acess_in_output_assignment_implicit_explicit_and_mixed() {
169169
%0 = bitcast %FOO* %f to i8*
170170
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %0, i8* align 1 getelementptr inbounds (%FOO, %FOO* @__FOO__init, i32 0, i32 0), i64 ptrtoint (%FOO* getelementptr (%FOO, %FOO* null, i32 1) to i64), i1 false)
171171
store i32 0, i32* %main, align 4
172-
%1 = getelementptr inbounds %FOO, %FOO* %f, i32 0, i32 0
172+
%1 = getelementptr %FOO, %FOO* %f, i32 0, i32 0
173173
%load_error_bits = load i8, i8* %error_bits, align 1
174174
%shift = lshr i8 %load_error_bits, 0
175175
%2 = and i8 %shift, 1
@@ -182,7 +182,7 @@ fn direct_acess_in_output_assignment_implicit_explicit_and_mixed() {
182182
%value = shl i8 %5, 0
183183
%or = or i8 %erase, %value
184184
store i8 %or, i8* %error_bits, align 1
185-
%6 = getelementptr inbounds %FOO, %FOO* %f, i32 0, i32 0
185+
%6 = getelementptr %FOO, %FOO* %f, i32 0, i32 0
186186
%load_error_bits1 = load i8, i8* %error_bits, align 1
187187
%shift2 = lshr i8 %load_error_bits1, 0
188188
%7 = and i8 %shift2, 1
@@ -195,7 +195,7 @@ fn direct_acess_in_output_assignment_implicit_explicit_and_mixed() {
195195
%value4 = shl i8 %10, 0
196196
%or5 = or i8 %erase3, %value4
197197
store i8 %or5, i8* %error_bits, align 1
198-
%11 = getelementptr inbounds %FOO, %FOO* %f, i32 0, i32 0
198+
%11 = getelementptr %FOO, %FOO* %f, i32 0, i32 0
199199
%load_error_bits6 = load i8, i8* %error_bits, align 1
200200
%shift7 = lshr i8 %load_error_bits6, 0
201201
%12 = and i8 %shift7, 1
@@ -208,7 +208,7 @@ fn direct_acess_in_output_assignment_implicit_explicit_and_mixed() {
208208
%value9 = shl i8 %15, 0
209209
%or10 = or i8 %erase8, %value9
210210
store i8 %or10, i8* %error_bits, align 1
211-
%16 = getelementptr inbounds %FOO, %FOO* %f, i32 0, i32 0
211+
%16 = getelementptr %FOO, %FOO* %f, i32 0, i32 0
212212
%load_error_bits11 = load i8, i8* %error_bits, align 1
213213
%shift12 = lshr i8 %load_error_bits11, 0
214214
%17 = and i8 %shift12, 1

src/codegen/tests/fnptr.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -412,13 +412,13 @@ fn function_block_body() {
412412
store i32 0, i32* %localOut, align 4
413413
store i64 0, i64* %localInout, align 8
414414
%1 = load void (%A*)*, void (%A*)** %bodyPtr, align 8
415-
%2 = getelementptr inbounds %A, %A* %instanceA, i32 0, i32 1
415+
%2 = getelementptr %A, %A* %instanceA, i32 0, i32 1
416416
%load_localIn = load i16, i16* %localIn, align 2
417417
store i16 %load_localIn, i16* %2, align 2
418-
%3 = getelementptr inbounds %A, %A* %instanceA, i32 0, i32 3
418+
%3 = getelementptr %A, %A* %instanceA, i32 0, i32 3
419419
store i64* %localInout, i64** %3, align 8
420420
call void %1(%A* %instanceA)
421-
%4 = getelementptr inbounds %A, %A* %instanceA, i32 0, i32 2
421+
%4 = getelementptr %A, %A* %instanceA, i32 0, i32 2
422422
%5 = load i32, i32* %4, align 4
423423
store i32 %5, i32* %localOut, align 4
424424
ret void

0 commit comments

Comments
 (0)