PLC-lang / rusty

Structured Text Parser and LLVM Frontend
GNU Lesser General Public License v3.0
214 stars 53 forks source link

Segfault when passing arguments implicitly #1328

Open volsa opened 2 weeks ago

volsa commented 2 weeks ago

The following code will currently segfault when compiling and running with cargo r -- target/demo.st tests/lit/util/printf.pli --linker=clang && ./demo.st.out due to passing the arr variable implicitly to mainProg.

FUNCTION main
    VAR
        arr : ARRAY[0..9] OF INT;
    END_VAR

    mainProg(arr); // `mainProg(arr := arr);` works
END_FUNCTION

PROGRAM mainProg
    VAR
        i : INT;
    END_VAR

    VAR_IN_OUT
        arr : ARRAY[0..9] OF INT;
    END_VAR

    FOR i := 0 TO 9 DO
        arr[i] := i;
    END_FOR
END_PROGRAM
mhasel commented 2 weeks ago

When trying to compile this to IR, llvm gives the following error:

error: invalid forward reference to function 'mainProg' with wrong type: expected 'void (%mainProg*)*' but was 'void (%mainProg*, [10 x i16])*'
  call void @mainProg(%mainProg* @mainProg_instance, [10 x i16] %load_arr)

This does not happen if the VAR block is declared after the VAR_IN_OUT block. The most likely cause is in generate_nameless_parameter, since it looks for a struct member at a certain index in a stateful POU for non-formally passed parameters. In this case, it is trying to get the parameter at index 0, but since the first member of the POU-struct is not a parameter, self.index.get_declared_parameter(function_name, index) will return None.

    /// generates the appropriate value for the given expression where the expression
    /// is a call's implicit argument (hence: foo(3), not foo(in := 3))
    fn generate_nameless_parameter(
        &self,
        param_context: &CallParameterAssignment,
    ) -> Result<Option<BasicValueEnum<'ink>>, Diagnostic> {
        let builder = &self.llvm.builder;
        let function_name = param_context.function_name;
        let index = param_context.index;
        let parameter_struct = param_context.parameter_struct;
        let expression = param_context.assignment;
        if let Some(parameter) = self.index.get_declared_parameter(function_name, index) {
            // ...

This index comes from for (i, stmt) in passed_parameters.iter().enumerate() { ... in generate_stateful_pou_arguments => only one parameter is passed but the index of this parameter does not match the gep offset in the struct.