diff --git a/src/tests/adr/pou_adr.rs b/src/tests/adr/pou_adr.rs index aeeac71422..d05a63ca61 100644 --- a/src/tests/adr/pou_adr.rs +++ b/src/tests/adr/pou_adr.rs @@ -4,14 +4,26 @@ use crate::test_utils::tests::{annotate, codegen, index}; /// /// POU = Program Organisation Unit /// -/// A Program Organisation Unit is either a PROGRAM, a FUNCTION_BLOCK, a FUNCTION or a CLASS. +/// A Program Organisation Unit is a container for executable code. It is either a PROGRAM, +/// a FUNCTION_BLOCK, a FUNCTION or a CLASS. +/// On the top level we distingish two different types of POUs. There are stateful, and +/// stateless POUs. Stateful POUs are POUs that have a memory across different calls to it. This means +/// that (some of) the variables of the POU keep their value's across different calls. We say that +/// there are `instances` of a POU. Stateless POU start with a fresh set of variables on every call. /// +/// Statefull (instantiatable) POUs are: +/// - Programs +/// - Functionblocks +/// - Classes +/// +/// Stateless POUs are: +/// - Functions /// /// # PROGRAMS /// -/// Programs are static units that have a persistent state for VAR, VAR_INPUT, VAR_OUTPUT and -/// VAR_IN_OUT variables. There is only one instance of a Program available. When calling a program -/// all parameters, except IN_OUT parameters, are optional. +/// Programs are POUs with exactly one (static) instance. Programs have a persistent state for VAR, VAR_INPUT, +/// VAR_OUTPUT and VAR_IN_OUT variables. The instance is statically available and behaves as if there is a callable +/// global variable withe the program's name. When calling a program all parameters, except IN_OUT parameters, are optional. const DEFAULT_PRG: &str = r#" PROGRAM main_prg @@ -23,6 +35,7 @@ const DEFAULT_PRG: &str = r#" END_PROGRAM "#; +/// The state (the memory, the stateful variables) are intnerally saved in a Struct-Type generated for this program. /// Programs register a PouType in the index using the POU's name. The PouType offers information about the /// POU like the name of the struct-type carrying the program's state, the names of all members, vararg and generic /// information etc. @@ -38,17 +51,39 @@ fn programs_state_is_stored_in_a_struct() { .find_pou("main_prg") .and_then(|pou| pou.find_instance_struct_type(&index)); - insta::assert_debug_snapshot!(pou_struct); + insta::assert_debug_snapshot!(pou_struct, @r###" + Some( + DataType { + name: "main_prg", + initial_value: None, + information: Struct { + name: "main_prg_interface", + member_names: [ + "i", + "io", + "o", + "v", + "vt", + ], + varargs: None, + source: Pou( + Program, + ), + }, + nature: Any, + }, + ) + "###); } /// Code-Generating a program does ... -/// ... generate a struct-type with all persistent fields (main_prg_interface) -/// ... generate a global intance variable (@main_prg_instance) of that type -/// ... generate the body of the program (@main_prg). All state variables are auto-loaded into local variables. VAR_TEMPs get -/// allocated on the stack. +/// ... generate a struct-type with all persistent fields (`main_prg_interface`) +/// ... generate a global intance variable (`main_prg_instance`) of that type +/// ... generate the body of the program (`@main_prg`). All state variables are +/// auto-loaded into local variables. VAR_TEMPs (`vt`) get allocated and initialized on the stack. #[test] -fn programs_get_a_method_with_a_self_parameter() { +fn codegen_of_a_program_pou() { insta::assert_snapshot!(codegen(DEFAULT_PRG),@r###" ; ModuleID = 'main' source_filename = "main" @@ -71,9 +106,10 @@ fn programs_get_a_method_with_a_self_parameter() { } /// Calling a program works like this: -/// ... the parameters are assigned to the global instance variable -/// ... the program's method is called, the global instance is passed as a parameter -/// ... after the call the VAR_OUT parameters are assigned back to the local variables +/// ... the parameters are assigned to the global instance variable (`main_prg_instance`) +/// ... parameters that are not defined keep their last assigned value +/// ... the program's method (body) is called, the global instance is passed as a parameter (`void @main_prg`) +/// ... note that VAR_OUT & VAR_IN_OUT parameters are stored as pointers (`i16*`) #[test] fn calling_a_program() { let calling_prg = format!( @@ -86,13 +122,49 @@ fn calling_a_program() { {DEFAULT_PRG} "# ); - insta::assert_snapshot!(codegen(calling_prg.as_str())); + insta::assert_snapshot!(codegen(calling_prg.as_str()), @r###" + ; ModuleID = 'main' + source_filename = "main" + + %main_prg_interface = type { i16, i16*, i16*, i16 } + + @main_prg_instance = global %main_prg_interface zeroinitializer + + define i16 @foo() { + entry: + %x = alloca i16, align 2 + %y = alloca i16, align 2 + %foo = alloca i16, align 2 + store i16 0, i16* %x, align 2 + store i16 0, i16* %y, align 2 + store i16 0, i16* %foo, align 2 + store i16 1, i16* getelementptr inbounds (%main_prg_interface, %main_prg_interface* @main_prg_instance, i32 0, i32 0), align 2 + store i16* %y, i16** getelementptr inbounds (%main_prg_interface, %main_prg_interface* @main_prg_instance, i32 0, i32 1), align 8 + store i16* %x, i16** getelementptr inbounds (%main_prg_interface, %main_prg_interface* @main_prg_instance, i32 0, i32 2), align 8 + call void @main_prg(%main_prg_interface* @main_prg_instance) + %foo_ret = load i16, i16* %foo, align 2 + ret i16 %foo_ret + } + + define void @main_prg(%main_prg_interface* %0) { + entry: + %i = getelementptr inbounds %main_prg_interface, %main_prg_interface* %0, i32 0, i32 0 + %io = getelementptr inbounds %main_prg_interface, %main_prg_interface* %0, i32 0, i32 1 + %o = getelementptr inbounds %main_prg_interface, %main_prg_interface* %0, i32 0, i32 2 + %v = getelementptr inbounds %main_prg_interface, %main_prg_interface* %0, i32 0, i32 3 + %vt = alloca i16, align 2 + store i16 0, i16* %vt, align 2 + ret void + } + "###); } /// # FUNCTION BLOCK /// /// FunctionBlock are instantiable Blocks that behave very similar to programs. The biggest difference is -/// that you can have mulitple instances of a FunctionBlock. +/// that you can have mulitple instances of a FunctionBlock. Therefore a FunctionBlocks instance variable is not +/// auto-generated as a global variable as it would be for a program. FB-instances can be declared as part of your +/// code. A FunctionBlock acts automatically as a DataType. const DEFAULT_FB: &str = r#" FUNCTION_BLOCK main_fb @@ -105,21 +177,39 @@ const DEFAULT_FB: &str = r#" "#; /// Code-Generating a function_block does ... -/// ... generate a struct-type with all persistent fields (main_fb_interface) -/// ... generate the body of the function_block (@main_fb). All state variables are auto-loaded into -/// local variables. VAR_TEMPs get allocated on the stack. -/// ... a global variable with the FB's default values, used to initialize function block instances. +/// ... generate a struct-type with all persistent fields (`main_fb_interface`) +/// ... generate the body of the function_block (`void @main_fb`). All state variables are auto-loaded into +/// local variables. VAR_TEMPs (`vt`) get allocated and initialized on the stack. +/// ... a global variable with the FB's default values (`main_fb__init`), used to initialize function block instances. /// ... note that no global instance is created (as you would get for PROGRAMs) #[test] fn function_blocks_get_a_method_with_a_self_parameter() { - insta::assert_snapshot!(codegen(DEFAULT_FB)); + insta::assert_snapshot!(codegen(DEFAULT_FB), @r###" + ; ModuleID = 'main' + source_filename = "main" + + %main_fb_interface = type { i16, i16*, i16*, i16 } + + @main_fb__init = unnamed_addr constant %main_fb_interface { i16 6, i16* null, i16* null, i16 1 } + + define void @main_fb(%main_fb_interface* %0) { + entry: + %i = getelementptr inbounds %main_fb_interface, %main_fb_interface* %0, i32 0, i32 0 + %io = getelementptr inbounds %main_fb_interface, %main_fb_interface* %0, i32 0, i32 1 + %o = getelementptr inbounds %main_fb_interface, %main_fb_interface* %0, i32 0, i32 2 + %v = getelementptr inbounds %main_fb_interface, %main_fb_interface* %0, i32 0, i32 3 + %vt = alloca i16, align 2 + store i16 2, i16* %vt, align 2 + ret void + } + "###); } /// Calling a function block works like this: -/// ... the parameters are assigned to the fb instance variable -/// ... the function_block's method is called, the called instance is passed as a parameter -/// ... after the call the VAR_OUT parameters are assigned back to the local variables +/// ... the parameters are assigned to the called fb instance variable (`%fb`) +/// ... the function_block's method is called, the called instance is passed as a parameter (`void @main_fb`) +/// ... note that VAR_OUT & VAR_IN_OUT parameters are stored as pointers (`i16*`) #[test] fn calling_a_function_block() { let calling_prg = format!( @@ -174,8 +264,9 @@ fn calling_a_function_block() { /// # FUNCTION /// -/// Functions are stateless methods. They dont get an instance-struct or instance variables. Functions +/// Functions are stateless methods. They dont have an instance-struct or instance variables. Functions /// take all their parameters passed one by one to the function. + const DEFAULT_FUNC: &str = r#" FUNCTION main_fun : DINT VAR_INPUT i : INT; END_VAR @@ -216,7 +307,8 @@ fn function_get_a_method_with_by_ref_parameters() { } /// Calling a function works like this: -/// ... the function is called and all parameters are passed by ref +/// ... the function is called and all parameters are passed (`i32 @main_fun`) +/// ... note that VAR_OUT & VAR_IN_OUT parameters are passed as pointers (`i16*`) #[test] fn calling_a_function() { let calling_prg = format!( @@ -229,5 +321,39 @@ fn calling_a_function() { {DEFAULT_FUNC} "# ); - insta::assert_snapshot!(codegen(calling_prg.as_str())); + insta::assert_snapshot!(codegen(calling_prg.as_str()), @r###" + ; ModuleID = 'main' + source_filename = "main" + + %prg_interface = type { i16, i16 } + + @prg_instance = global %prg_interface zeroinitializer + + define void @prg(%prg_interface* %0) { + entry: + %x = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 0 + %z = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 1 + %load_x = load i16, i16* %x, align 2 + %call = call i32 @main_fun(i16 %load_x, i16* %z) + ret void + } + + define i32 @main_fun(i16 %0, i8* %1, i64* %2) { + entry: + %i = alloca i16, align 2 + store i16 %0, i16* %i, align 2 + %io = alloca i8*, align 8 + store i8* %1, i8** %io, align 8 + %o = alloca i64*, align 8 + store i64* %2, i64** %o, align 8 + %v = alloca i16, align 2 + %vt = alloca i16, align 2 + %main_fun = alloca i32, align 4 + store i16 1, i16* %v, align 2 + store i16 2, i16* %vt, align 2 + store i32 0, i32* %main_fun, align 4 + %main_fun_ret = load i32, i32* %main_fun, align 4 + ret i32 %main_fun_ret + } + "###); } diff --git a/src/tests/adr/snapshots/rusty__tests__adr__pou_adr__calling_a_function.snap b/src/tests/adr/snapshots/rusty__tests__adr__pou_adr__calling_a_function.snap deleted file mode 100644 index 81b83027a6..0000000000 --- a/src/tests/adr/snapshots/rusty__tests__adr__pou_adr__calling_a_function.snap +++ /dev/null @@ -1,38 +0,0 @@ ---- -source: src/tests/adr/pou_adr.rs -expression: codegen(calling_prg.as_str()) ---- -; ModuleID = 'main' -source_filename = "main" - -%prg_interface = type { i16, i16 } - -@prg_instance = global %prg_interface zeroinitializer - -define void @prg(%prg_interface* %0) { -entry: - %x = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 0 - %z = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 1 - %load_x = load i16, i16* %x, align 2 - %call = call i32 @main_fun(i16 %load_x, i16* %z) - ret void -} - -define i32 @main_fun(i16 %0, i8* %1, i64* %2) { -entry: - %i = alloca i16, align 2 - store i16 %0, i16* %i, align 2 - %io = alloca i8*, align 8 - store i8* %1, i8** %io, align 8 - %o = alloca i64*, align 8 - store i64* %2, i64** %o, align 8 - %v = alloca i16, align 2 - %vt = alloca i16, align 2 - %main_fun = alloca i32, align 4 - store i16 1, i16* %v, align 2 - store i16 2, i16* %vt, align 2 - store i32 0, i32* %main_fun, align 4 - %main_fun_ret = load i32, i32* %main_fun, align 4 - ret i32 %main_fun_ret -} - diff --git a/src/tests/adr/snapshots/rusty__tests__adr__pou_adr__calling_a_program.snap b/src/tests/adr/snapshots/rusty__tests__adr__pou_adr__calling_a_program.snap deleted file mode 100644 index 5e13353053..0000000000 --- a/src/tests/adr/snapshots/rusty__tests__adr__pou_adr__calling_a_program.snap +++ /dev/null @@ -1,38 +0,0 @@ ---- -source: src/tests/adr/pou_adr.rs -expression: codegen(calling_prg.as_str()) ---- -; ModuleID = 'main' -source_filename = "main" - -%main_prg_interface = type { i16, i16*, i16*, i16 } - -@main_prg_instance = global %main_prg_interface zeroinitializer - -define i16 @foo() { -entry: - %x = alloca i16, align 2 - %y = alloca i16, align 2 - %foo = alloca i16, align 2 - store i16 0, i16* %x, align 2 - store i16 0, i16* %y, align 2 - store i16 0, i16* %foo, align 2 - store i16 1, i16* getelementptr inbounds (%main_prg_interface, %main_prg_interface* @main_prg_instance, i32 0, i32 0), align 2 - store i16* %y, i16** getelementptr inbounds (%main_prg_interface, %main_prg_interface* @main_prg_instance, i32 0, i32 1), align 8 - store i16* %x, i16** getelementptr inbounds (%main_prg_interface, %main_prg_interface* @main_prg_instance, i32 0, i32 2), align 8 - call void @main_prg(%main_prg_interface* @main_prg_instance) - %foo_ret = load i16, i16* %foo, align 2 - ret i16 %foo_ret -} - -define void @main_prg(%main_prg_interface* %0) { -entry: - %i = getelementptr inbounds %main_prg_interface, %main_prg_interface* %0, i32 0, i32 0 - %io = getelementptr inbounds %main_prg_interface, %main_prg_interface* %0, i32 0, i32 1 - %o = getelementptr inbounds %main_prg_interface, %main_prg_interface* %0, i32 0, i32 2 - %v = getelementptr inbounds %main_prg_interface, %main_prg_interface* %0, i32 0, i32 3 - %vt = alloca i16, align 2 - store i16 0, i16* %vt, align 2 - ret void -} - diff --git a/src/tests/adr/snapshots/rusty__tests__adr__pou_adr__function_blocks_get_a_method_with_a_self_parameter.snap b/src/tests/adr/snapshots/rusty__tests__adr__pou_adr__function_blocks_get_a_method_with_a_self_parameter.snap deleted file mode 100644 index 667dfb4cdd..0000000000 --- a/src/tests/adr/snapshots/rusty__tests__adr__pou_adr__function_blocks_get_a_method_with_a_self_parameter.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: src/tests/adr/pou_adr.rs -expression: codegen(DEFAULT_FB) ---- -; ModuleID = 'main' -source_filename = "main" - -%main_fb_interface = type { i16, i16*, i16*, i16 } - -@main_fb__init = unnamed_addr constant %main_fb_interface { i16 6, i16* null, i16* null, i16 1 } - -define void @main_fb(%main_fb_interface* %0) { -entry: - %i = getelementptr inbounds %main_fb_interface, %main_fb_interface* %0, i32 0, i32 0 - %io = getelementptr inbounds %main_fb_interface, %main_fb_interface* %0, i32 0, i32 1 - %o = getelementptr inbounds %main_fb_interface, %main_fb_interface* %0, i32 0, i32 2 - %v = getelementptr inbounds %main_fb_interface, %main_fb_interface* %0, i32 0, i32 3 - %vt = alloca i16, align 2 - store i16 2, i16* %vt, align 2 - ret void -} - diff --git a/src/tests/adr/snapshots/rusty__tests__adr__pou_adr__programs_state_is_stored_in_a_struct.snap b/src/tests/adr/snapshots/rusty__tests__adr__pou_adr__programs_state_is_stored_in_a_struct.snap deleted file mode 100644 index 6c8eb69943..0000000000 --- a/src/tests/adr/snapshots/rusty__tests__adr__pou_adr__programs_state_is_stored_in_a_struct.snap +++ /dev/null @@ -1,25 +0,0 @@ ---- -source: src/tests/adr/pou_adr.rs -expression: pou_struct ---- -Some( - DataType { - name: "main_prg", - initial_value: None, - information: Struct { - name: "main_prg_interface", - member_names: [ - "i", - "io", - "o", - "v", - "vt", - ], - varargs: None, - source: Pou( - Program, - ), - }, - nature: Any, - }, -)