-
Notifications
You must be signed in to change notification settings - Fork 79
fix!(codegen): reset function outputs and default omitted arguments #1958
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
base: master
Are you sure you want to change the base?
Changes from all commits
c1b7028
abc05d1
faf4f52
9bb9b6b
75d231e
5e0e6de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -580,6 +580,7 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { | |
| &function_context, | ||
| debug, | ||
| )?; | ||
| self.generate_initialization_of_output_params(&pou_members, &local_index)?; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This new initialization call makes every function or method output start at its default value, but AGENTS.md reference: AGENTS.md:L42-L42 Useful? React with 👍 / 👎. |
||
| } else { | ||
| //Generate temp variables | ||
| let members = pou_members.into_iter().filter(|it| it.is_temp()).collect::<Vec<_>>(); | ||
|
|
@@ -894,6 +895,64 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { | |
| Ok(()) | ||
| } | ||
|
|
||
| /// resets every by-ref output parameter of a function or method to its initial value, so the | ||
| /// caller never observes a stale value through an output the body does not assign | ||
| fn generate_initialization_of_output_params( | ||
| &self, | ||
| variables: &[&VariableIndexEntry], | ||
| local_llvm_index: &LlvmTypedIndex, | ||
| ) -> Result<(), CodegenError> { | ||
| let exp_gen = ExpressionCodeGenerator::new_context_free( | ||
| &self.llvm, | ||
| self.index, | ||
| self.annotations, | ||
| local_llvm_index, | ||
| ); | ||
| let outputs = variables.iter().filter(|it| it.is_output() && it.get_declaration_type().is_by_ref()); | ||
|
|
||
| for variable in outputs { | ||
| let Some(inner_type_name) = self | ||
| .index | ||
| .find_effective_type_info(variable.get_type_name()) | ||
| .and_then(|it| it.get_inner_pointer_type_name()) | ||
| else { | ||
| continue; | ||
| }; | ||
| // reference and alias outputs are bound by the body, and a variable length array | ||
| // output holds the caller's bounds and data pointer, so neither is reset | ||
| if self | ||
| .index | ||
| .find_effective_type_info(inner_type_name) | ||
| .is_some_and(|it| it.is_reference_to() || it.is_alias() || it.is_vla()) | ||
| { | ||
| continue; | ||
| } | ||
| let Some(pointer_slot) = | ||
| local_llvm_index.find_loaded_associated_variable_value(variable.get_qualified_name()) | ||
| else { | ||
| continue; | ||
| }; | ||
|
|
||
| let ptr_type = self.llvm.context.ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)); | ||
| let output = self.llvm.builder.build_load(ptr_type, pointer_slot, "")?.into_pointer_value(); | ||
| // only a resolved constant is applied here; any other initializer is assigned by the | ||
| // lowered stack initializer, which runs with the instance in scope | ||
| let initializer = variable | ||
| .initial_value | ||
| .as_ref() | ||
| .and_then(|id| self.index.get_const_expressions().get_resolved_constant_statement(id)); | ||
| self.llvm.generate_variable_initializer( | ||
| self.llvm_index, | ||
| self.index, | ||
| (variable.get_qualified_name(), inner_type_name, &variable.source_location), | ||
| output, | ||
| initializer, | ||
| &exp_gen, | ||
| )?; | ||
| } | ||
| Ok(()) | ||
| } | ||
|
|
||
| /// initializes the variable represented by `variable` by storing into the given `variable_to_initialize` pointer using either | ||
| /// the optional `initializer_statement` (hence code like: `variable : type := initializer_statement`), or determine the initial | ||
| /// value with the help of the `variable`'s index entry by e.g. looking for a default value of the variable's type | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a program or function-block call passes an empty
VAR_IN_OUTwhose target type has a nonzero default, for exampleTYPE T : DINT := 20andfb(x := ), this branch always writes LLVM zero into the temporary. The function-call path usesget_initial_value, and the updated codegen documentation promises default-or-zero behavior, so the callee sees0instead of20. Initialize the temporary from the parameter or type default before falling back to zero.AGENTS.md reference: AGENTS.md:L42-L42
Useful? React with 👍 / 👎.