From aa1993a43216fd8895331e9e719dea3090dac4d0 Mon Sep 17 00:00:00 2001 From: Michael Haselberger Date: Wed, 2 Oct 2024 11:23:52 +0200 Subject: [PATCH] feat: add `VAR_EXTERNAL` blocks --- compiler/plc_ast/src/ast.rs | 2 + src/codegen/generators/data_type_generator.rs | 2 +- src/codegen/generators/pou_generator.rs | 4 +- src/codegen/tests/code_gen_tests.rs | 123 +++++++++ .../complex_initializers.rs | 71 +++++ src/index.rs | 21 ++ src/index/tests/index_tests.rs | 54 ++++ ...ex_tests__fb_parameters_variable_type.snap | 3 + ...ts__function_parameters_variable_type.snap | 4 + ...sts__program_parameters_variable_type.snap | 3 + ...r_tests__array_instances_are_repeated.snap | 8 + ...ray_with_const_instances_are_repeated.snap | 5 + ...ests__filter_on_variables_are_applied.snap | 5 + ...ts__global_fb_variables_are_retrieved.snap | 7 + ...global_struct_variables_are_retrieved.snap | 7 + ...lver_tests__global_vars_are_retrieved.snap | 2 + ...global_struct_variables_are_retrieved.snap | 15 ++ ...__pointer_variables_are_not_retrieved.snap | 2 + ...ests__program_variables_are_retrieved.snap | 3 + ...esolver_tests__programs_are_retrieved.snap | 1 + src/index/visitor.rs | 6 +- src/lexer/tokens.rs | 3 + src/lowering.rs | 4 +- src/parser.rs | 13 +- src/parser/tests/variable_parser_tests.rs | 248 ++++++++++++++++++ src/resolver.rs | 6 + src/tests/adr/pou_adr.rs | 5 + src/tests/adr/vla_adr.rs | 4 + src/validation/variable.rs | 7 +- tests/lit/single/var_external/var_external.st | 28 ++ 30 files changed, 656 insertions(+), 10 deletions(-) create mode 100644 tests/lit/single/var_external/var_external.st diff --git a/compiler/plc_ast/src/ast.rs b/compiler/plc_ast/src/ast.rs index e4d06accbe..7b740bb8ed 100644 --- a/compiler/plc_ast/src/ast.rs +++ b/compiler/plc_ast/src/ast.rs @@ -353,6 +353,7 @@ pub enum VariableBlockType { Output, Global, InOut, + External, } impl Display for VariableBlockType { @@ -364,6 +365,7 @@ impl Display for VariableBlockType { VariableBlockType::Output => write!(f, "Output"), VariableBlockType::Global => write!(f, "Global"), VariableBlockType::InOut => write!(f, "InOut"), + VariableBlockType::External => write!(f, "External"), } } } diff --git a/src/codegen/generators/data_type_generator.rs b/src/codegen/generators/data_type_generator.rs index b3d39496cb..5d069653b4 100644 --- a/src/codegen/generators/data_type_generator.rs +++ b/src/codegen/generators/data_type_generator.rs @@ -177,7 +177,7 @@ impl<'ink, 'b> DataTypeGenerator<'ink, 'b> { if let DataTypeInformation::Struct { source, members, .. } = information { let members = members .iter() - .filter(|it| !it.is_temp() && !it.is_return()) + .filter(|it| !it.is_temp() && !(it.is_return() || it.is_var_external())) .map(|m| self.types_index.get_associated_type(m.get_type_name())) .collect::, Diagnostic>>()?; diff --git a/src/codegen/generators/pou_generator.rs b/src/codegen/generators/pou_generator.rs index dbdc935285..e36be6a42c 100644 --- a/src/codegen/generators/pou_generator.rs +++ b/src/codegen/generators/pou_generator.rs @@ -559,7 +559,7 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { } // handle all parameters (without return!) - for m in members.iter().filter(|it| !it.is_return()) { + for m in members.iter().filter(|it| !(it.is_return() || it.is_var_external())) { let parameter_name = m.get_name(); let (name, variable) = if m.is_parameter() { @@ -676,7 +676,7 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { //Generate reference to parameter // cannot use index from members because return and temp variables may not be considered for index in build_struct_gep let mut var_count = 0; - for m in members.iter() { + for m in members.iter().filter(|it| !it.is_var_external()) { let parameter_name = m.get_name(); let (name, variable) = if m.is_temp() || m.is_return() { diff --git a/src/codegen/tests/code_gen_tests.rs b/src/codegen/tests/code_gen_tests.rs index d4c155f7b0..be32824538 100644 --- a/src/codegen/tests/code_gen_tests.rs +++ b/src/codegen/tests/code_gen_tests.rs @@ -3513,3 +3513,126 @@ fn array_of_struct_as_member_of_another_struct_and_variable_declaration_is_initi insta::assert_snapshot!(res); } + +#[test] +// XXX: this behaviour might change in future, for now `VAR_EXTERNAL` variables are ignored +fn variables_in_var_external_block_are_not_generated() { + let res = codegen( + " + VAR_GLOBAL + arr: ARRAY [0..100] OF INT; + END_VAR + + FUNCTION foo + VAR_EXTERNAL + arr : ARRAY [0..100] OF INT; + END_VAR + END_FUNCTION + + FUNCTION_BLOCK bar + VAR_EXTERNAL CONSTANT + arr : ARRAY [0..100] OF INT; + END_VAR + END_FUNCTION_BLOCK + + PROGRAM baz + VAR_EXTERNAL CONSTANT + arr : ARRAY [0..100] OF INT; + END_VAR + END_PROGRAM + + CLASS qux + VAR_EXTERNAL + arr : ARRAY [0..100] OF INT; + END_VAR + END_CLASS + ", + ); + + insta::assert_snapshot!(res, @r###" + ; ModuleID = '' + source_filename = "" + + %bar = type {} + %baz = type {} + %qux = type {} + + @arr = global [101 x i16] zeroinitializer + @__bar__init = unnamed_addr constant %bar zeroinitializer + @baz_instance = global %baz zeroinitializer + @__qux__init = unnamed_addr constant %qux zeroinitializer + + define void @foo() { + entry: + ret void + } + + define void @bar(%bar* %0) { + entry: + ret void + } + + define void @baz(%baz* %0) { + entry: + ret void + } + + define void @qux(%qux* %0) { + entry: + ret void + } + ; ModuleID = '__initializers' + source_filename = "__initializers" + + %baz = type {} + %bar = type {} + %qux = type {} + + @baz_instance = external global %baz + @__bar__init = external global %bar + @__qux__init = external global %qux + + define void @__init_baz(%baz* %0) { + entry: + %self = alloca %baz*, align 8 + store %baz* %0, %baz** %self, align 8 + ret void + } + + declare void @baz(%baz*) + + define void @__init_bar(%bar* %0) { + entry: + %self = alloca %bar*, align 8 + store %bar* %0, %bar** %self, align 8 + ret void + } + + declare void @bar(%bar*) + + define void @__init_qux(%qux* %0) { + entry: + %self = alloca %qux*, align 8 + store %qux* %0, %qux** %self, align 8 + ret void + } + + declare void @qux(%qux*) + ; ModuleID = '__init___testproject' + source_filename = "__init___testproject" + + %baz = type {} + + @baz_instance = external global %baz + + define void @__init___testproject() { + entry: + call void @__init_baz(%baz* @baz_instance) + ret void + } + + declare void @__init_baz(%baz*) + + declare void @baz(%baz*) + "###); +} diff --git a/src/codegen/tests/initialization_test/complex_initializers.rs b/src/codegen/tests/initialization_test/complex_initializers.rs index b7e23994dc..0ad8fad32a 100644 --- a/src/codegen/tests/initialization_test/complex_initializers.rs +++ b/src/codegen/tests/initialization_test/complex_initializers.rs @@ -1283,3 +1283,74 @@ fn var_config_aliased_variables_initialized() { declare void @FB(%FB*) "###); } + +#[test] +fn var_external_blocks_are_ignored_in_init_functions() { + let res = codegen( + r" + VAR_GLOBAL + s: STRING; + refString AT s : STRING; + END_VAR + + FUNCTION_BLOCK foo + VAR_EXTERNAL + refString : STRING; + END_VAR + END_FUNCTION + + FUNCTION bar + VAR_EXTERNAL + refString : STRING; + END_VAR + END_FUNCTION + ", + ); + + insta::assert_snapshot!(res, @r###" + ; ModuleID = '' + source_filename = "" + + %foo = type {} + + @s = global [81 x i8] zeroinitializer + @refString = global [81 x i8]* null + @__foo__init = unnamed_addr constant %foo zeroinitializer + + define void @foo(%foo* %0) { + entry: + ret void + } + + define void @bar() { + entry: + ret void + } + ; ModuleID = '__initializers' + source_filename = "__initializers" + + %foo = type {} + + @__foo__init = external global %foo + + define void @__init_foo(%foo* %0) { + entry: + %self = alloca %foo*, align 8 + store %foo* %0, %foo** %self, align 8 + ret void + } + + declare void @foo(%foo*) + ; ModuleID = '__init___testproject' + source_filename = "__init___testproject" + + @s = external global [81 x i8] + @refString = external global [81 x i8]* + + define void @__init___testproject() { + entry: + store [81 x i8]* @s, [81 x i8]** @refString, align 8 + ret void + } + "###) +} diff --git a/src/index.rs b/src/index.rs index 40f43bccda..ba968559d0 100644 --- a/src/index.rs +++ b/src/index.rs @@ -70,6 +70,8 @@ pub struct VariableIndexEntry { pub argument_type: ArgumentType, /// true if this variable is a compile-time-constant is_constant: bool, + // true if this variable is in a 'VAR_EXTERNAL' block + is_var_external: bool, /// the variable's datatype pub data_type_name: String, /// the index of the member-variable in it's container (e.g. struct). defautls to 0 (Single variables) @@ -130,6 +132,7 @@ pub struct MemberInfo<'b> { variable_type_name: &'b str, binding: Option, is_constant: bool, + is_var_external: bool, varargs: Option, } @@ -148,6 +151,7 @@ impl VariableIndexEntry { initial_value: None, argument_type, is_constant: false, + is_var_external: false, data_type_name: data_type_name.to_string(), location_in_parent, linkage: LinkageType::Internal, @@ -169,6 +173,7 @@ impl VariableIndexEntry { initial_value: None, argument_type: ArgumentType::ByVal(VariableType::Global), is_constant: false, + is_var_external: false, data_type_name: data_type_name.to_string(), location_in_parent: 0, linkage: LinkageType::Internal, @@ -203,6 +208,11 @@ impl VariableIndexEntry { self } + pub fn set_var_external(mut self, var_external: bool) -> Self { + self.is_var_external = var_external; + self + } + /// Creates a new VariableIndexEntry from the current entry with a new container and type /// This is used to create new entries from previously generic entries pub fn into_typed(&self, container: &str, new_type: &str) -> Self { @@ -253,6 +263,14 @@ impl VariableIndexEntry { self.linkage == LinkageType::External } + pub fn is_var_external(&self) -> bool { + self.is_var_external + } + + pub fn is_var_external_constant(&self) -> bool { + self.is_var_external && self.is_constant + } + pub fn get_declaration_type(&self) -> ArgumentType { self.argument_type } @@ -350,6 +368,7 @@ pub enum VariableType { InOut, Global, Return, + External, } impl VariableType { @@ -368,6 +387,7 @@ impl std::fmt::Display for VariableType { VariableType::InOut => write!(f, "InOut"), VariableType::Global => write!(f, "Global"), VariableType::Return => write!(f, "Return"), + VariableType::External => write!(f, "External"), } } } @@ -1496,6 +1516,7 @@ impl Index { .set_initial_value(initial_value) .set_hardware_binding(member_info.binding) .set_varargs(member_info.varargs) + .set_var_external(member_info.is_var_external) } pub fn register_enum_variant( diff --git a/src/index/tests/index_tests.rs b/src/index/tests/index_tests.rs index b3b5b5e4ee..a8c29054fe 100644 --- a/src/index/tests/index_tests.rs +++ b/src/index/tests/index_tests.rs @@ -1359,6 +1359,7 @@ fn a_program_pou_is_indexed() { initial_value: None, argument_type: ArgumentType::ByVal(VariableType::Global), is_constant: false, + is_var_external: false, data_type_name: "myProgram".into(), location_in_parent: 0, linkage: LinkageType::Internal, @@ -1629,6 +1630,7 @@ fn internal_vla_struct_type_is_indexed_correctly() { initial_value: None, argument_type: ArgumentType::ByVal(VariableType::Input), is_constant: false, + is_var_external: false, data_type_name: "__ptr_to___arr_vla_1_int".to_string(), location_in_parent: 0, linkage: LinkageType::Internal, @@ -1642,6 +1644,7 @@ fn internal_vla_struct_type_is_indexed_correctly() { initial_value: None, argument_type: ArgumentType::ByVal(VariableType::Input), is_constant: false, + is_var_external: false, data_type_name: "__bounds___arr_vla_1_int".to_string(), location_in_parent: 1, linkage: LinkageType::Internal, @@ -1710,6 +1713,7 @@ fn aliased_hardware_access_variable_has_implicit_initial_value_declaration() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "__global_foo", location_in_parent: 0, linkage: Internal, @@ -1790,6 +1794,7 @@ fn aliased_hardware_access_variable_creates_global_var_for_address() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "BOOL", location_in_parent: 0, linkage: Internal, @@ -1941,6 +1946,7 @@ fn address_used_in_2_aliases_only_created_once() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "BOOL", location_in_parent: 0, linkage: Internal, @@ -1990,6 +1996,7 @@ fn aliased_variable_with_in_or_out_directions_create_the_same_variable() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "BOOL", location_in_parent: 0, linkage: Internal, @@ -2021,6 +2028,7 @@ fn aliased_variable_with_in_or_out_directions_create_the_same_variable() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "WORD", location_in_parent: 0, linkage: Internal, @@ -2068,6 +2076,7 @@ fn if_two_aliased_var_of_different_types_use_the_same_address_the_first_wins() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "BOOL", location_in_parent: 0, linkage: Internal, @@ -2112,6 +2121,7 @@ fn var_config_hardware_address_creates_global_variable() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "BOOL", location_in_parent: 0, linkage: Internal, @@ -2133,3 +2143,47 @@ fn var_config_hardware_address_creates_global_variable() { } "###); } + +#[test] +fn var_externals_are_distinctly_indexed() { + let (_, index) = index( + " + VAR_GLOBAL + arr: ARRAY [0..100] OF INT; + END_VAR + + FUNCTION foo + VAR_EXTERNAL + arr : ARRAY [0..100] OF INT; + END_VAR + END_FUNCTION + ", + ); + + let external = &index.get_pou_members("foo")[0]; + let global = index.get_globals().get("arr").expect("global 'arr' must exist"); + assert!(external.is_var_external()); + assert_eq!(external.get_name(), global.get_name()); + assert_eq!(external.get_variable_type(), VariableType::External); + assert_ne!(external, global); +} + +#[test] +fn var_externals_constants_are_both_flagged_as_external_and_constant() { + let (_, index) = index( + " + VAR_GLOBAL + arr: ARRAY [0..100] OF INT; + END_VAR + + FUNCTION foo + VAR_EXTERNAL CONSTANT + arr : ARRAY [0..100] OF INT; + END_VAR + END_FUNCTION + ", + ); + + let external = &index.get_pou_members("foo")[0]; + assert!(external.is_var_external_constant()); +} diff --git a/src/index/tests/snapshots/rusty__index__tests__index_tests__fb_parameters_variable_type.snap b/src/index/tests/snapshots/rusty__index__tests__index_tests__fb_parameters_variable_type.snap index e068ad0b1d..97a0e1f54b 100644 --- a/src/index/tests/snapshots/rusty__index__tests__index_tests__fb_parameters_variable_type.snap +++ b/src/index/tests/snapshots/rusty__index__tests__index_tests__fb_parameters_variable_type.snap @@ -11,6 +11,7 @@ expression: members Input, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 0, linkage: Internal, @@ -38,6 +39,7 @@ expression: members Output, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 1, linkage: Internal, @@ -65,6 +67,7 @@ expression: members InOut, ), is_constant: false, + is_var_external: false, data_type_name: "__auto_pointer_to_INT", location_in_parent: 2, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__index_tests__function_parameters_variable_type.snap b/src/index/tests/snapshots/rusty__index__tests__index_tests__function_parameters_variable_type.snap index ff411aff36..e53decb26d 100644 --- a/src/index/tests/snapshots/rusty__index__tests__index_tests__function_parameters_variable_type.snap +++ b/src/index/tests/snapshots/rusty__index__tests__index_tests__function_parameters_variable_type.snap @@ -11,6 +11,7 @@ expression: members Input, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 0, linkage: Internal, @@ -38,6 +39,7 @@ expression: members Output, ), is_constant: false, + is_var_external: false, data_type_name: "__auto_pointer_to_INT", location_in_parent: 1, linkage: Internal, @@ -65,6 +67,7 @@ expression: members InOut, ), is_constant: false, + is_var_external: false, data_type_name: "__auto_pointer_to_INT", location_in_parent: 2, linkage: Internal, @@ -92,6 +95,7 @@ expression: members Return, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 3, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__index_tests__program_parameters_variable_type.snap b/src/index/tests/snapshots/rusty__index__tests__index_tests__program_parameters_variable_type.snap index f37056b140..8f988d5d27 100644 --- a/src/index/tests/snapshots/rusty__index__tests__index_tests__program_parameters_variable_type.snap +++ b/src/index/tests/snapshots/rusty__index__tests__index_tests__program_parameters_variable_type.snap @@ -11,6 +11,7 @@ expression: members Input, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 0, linkage: Internal, @@ -38,6 +39,7 @@ expression: members Output, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 1, linkage: Internal, @@ -65,6 +67,7 @@ expression: members InOut, ), is_constant: false, + is_var_external: false, data_type_name: "__auto_pointer_to_INT", location_in_parent: 2, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_instances_are_repeated.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_instances_are_repeated.snap index 25f154029f..9571dd7551 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_instances_are_repeated.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_instances_are_repeated.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -58,6 +59,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "__MainProg_aFb", location_in_parent: 0, linkage: Internal, @@ -118,6 +120,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -178,6 +181,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -217,6 +221,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "__MainProg_aFb1", location_in_parent: 1, linkage: Internal, @@ -291,6 +296,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -365,6 +371,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -404,6 +411,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "__MainProg_aFb3", location_in_parent: 2, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_with_const_instances_are_repeated.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_with_const_instances_are_repeated.snap index 011abfe075..310a4303b2 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_with_const_instances_are_repeated.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_with_const_instances_are_repeated.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -63,6 +64,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: true, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -102,6 +104,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "__MainProg_aFb", location_in_parent: 1, linkage: Internal, @@ -162,6 +165,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -222,6 +226,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__filter_on_variables_are_applied.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__filter_on_variables_are_applied.snap index a2b720aae3..f701df3f46 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__filter_on_variables_are_applied.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__filter_on_variables_are_applied.snap @@ -24,6 +24,7 @@ expression: "index.filter_instances(|it, _|\n !it.is_constant()).coll Global, ), is_constant: true, + is_var_external: false, data_type_name: "fb", location_in_parent: 0, linkage: Internal, @@ -60,6 +61,7 @@ expression: "index.filter_instances(|it, _|\n !it.is_constant()).coll Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -99,6 +101,7 @@ expression: "index.filter_instances(|it, _|\n !it.is_constant()).coll Local, ), is_constant: false, + is_var_external: false, data_type_name: "fb", location_in_parent: 0, linkage: Internal, @@ -141,6 +144,7 @@ expression: "index.filter_instances(|it, _|\n !it.is_constant()).coll Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -183,6 +187,7 @@ expression: "index.filter_instances(|it, _|\n !it.is_constant()).coll Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_fb_variables_are_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_fb_variables_are_retrieved.snap index 2c62c3f94c..1eca2a81a4 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_fb_variables_are_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_fb_variables_are_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "fb", location_in_parent: 0, linkage: Internal, @@ -58,6 +59,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -97,6 +99,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -133,6 +136,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -172,6 +176,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "fb", location_in_parent: 0, linkage: Internal, @@ -214,6 +219,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -256,6 +262,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_struct_variables_are_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_struct_variables_are_retrieved.snap index 92adee3445..6429b0db75 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_struct_variables_are_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_struct_variables_are_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "str", location_in_parent: 0, linkage: Internal, @@ -58,6 +59,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -97,6 +99,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -133,6 +136,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -172,6 +176,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "str", location_in_parent: 0, linkage: Internal, @@ -214,6 +219,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -256,6 +262,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_vars_are_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_vars_are_retrieved.snap index 0dffe1a309..238571884b 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_vars_are_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_vars_are_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 0, linkage: Internal, @@ -55,6 +56,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 0, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__nested_global_struct_variables_are_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__nested_global_struct_variables_are_retrieved.snap index 8f13a244db..8bb283471e 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__nested_global_struct_variables_are_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__nested_global_struct_variables_are_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "str", location_in_parent: 0, linkage: Internal, @@ -58,6 +59,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "str2", location_in_parent: 0, linkage: Internal, @@ -100,6 +102,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -142,6 +145,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -181,6 +185,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "str2", location_in_parent: 1, linkage: Internal, @@ -223,6 +228,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -265,6 +271,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -301,6 +308,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -340,6 +348,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "str", location_in_parent: 0, linkage: Internal, @@ -382,6 +391,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "str2", location_in_parent: 0, linkage: Internal, @@ -427,6 +437,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -472,6 +483,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -514,6 +526,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "str2", location_in_parent: 1, linkage: Internal, @@ -559,6 +572,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -604,6 +618,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__pointer_variables_are_not_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__pointer_variables_are_not_retrieved.snap index 33902d0def..e134605617 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__pointer_variables_are_not_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__pointer_variables_are_not_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -58,6 +59,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "__MainProg_rFb", location_in_parent: 0, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__program_variables_are_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__program_variables_are_retrieved.snap index 647e98356b..39df1e6921 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__program_variables_are_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__program_variables_are_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -58,6 +59,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -97,6 +99,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__programs_are_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__programs_are_retrieved.snap index 5fbe48e94d..6dec1d8e7b 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__programs_are_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__programs_are_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, diff --git a/src/index/visitor.rs b/src/index/visitor.rs index 1943824076..c49e5893a9 100644 --- a/src/index/visitor.rs +++ b/src/index/visitor.rs @@ -89,6 +89,7 @@ pub fn visit_pou(index: &mut Index, pou: &Pou) { variable_linkage: block_type, variable_type_name: &type_name, is_constant: block.constant, + is_var_external: matches!(block.variable_block_type, VariableBlockType::External), binding, varargs, }, @@ -110,7 +111,8 @@ pub fn visit_pou(index: &mut Index, pou: &Pou) { variable_name: pou.get_return_name(), variable_linkage: ArgumentType::ByVal(VariableType::Return), variable_type_name: return_type_name, - is_constant: false, //return variables are not constants + is_constant: false, //return variables are not constants + is_var_external: false, // see above binding: None, varargs: None, }, @@ -331,6 +333,7 @@ fn get_variable_type_from_block(block: &VariableBlock) -> VariableType { VariableBlockType::Output => VariableType::Output, VariableBlockType::Global => VariableType::Global, VariableBlockType::InOut => VariableType::InOut, + VariableBlockType::External => VariableType::External, } } @@ -805,6 +808,7 @@ fn visit_struct( variable_linkage: ArgumentType::ByVal(VariableType::Input), // struct members act like VAR_INPUT in terms of visibility variable_type_name: member_type, is_constant: false, //struct members are not constants //TODO thats probably not true (you can define a struct in an CONST-block?!) + is_var_external: false, // structs cannot have VAR_EXTERNAL blocks binding, varargs: None, }, diff --git a/src/lexer/tokens.rs b/src/lexer/tokens.rs index 46e67af2fe..86f86bdfec 100644 --- a/src/lexer/tokens.rs +++ b/src/lexer/tokens.rs @@ -99,6 +99,9 @@ pub enum Token { #[token("VARINOUT", ignore(case))] KeywordVarInOut, + #[token("VAR_EXTERNAL", ignore(case))] + KeywordVarExternal, + #[token("END_VAR", ignore(case))] #[token("ENDVAR", ignore(case))] KeywordEndVar, diff --git a/src/lowering.rs b/src/lowering.rs index 4b7216ddf1..885a629a43 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -105,7 +105,9 @@ impl AstLowerer { fn add_init_statements(&mut self, implementation: &mut plc_ast::ast::Implementation) { let predicate = |var: &VariableIndexEntry| { - var.is_temp() || (implementation.pou_type == PouType::Function && var.is_local()) + var.is_temp() + || var.is_var_external() + || (implementation.pou_type == PouType::Function && var.is_local()) }; let strip_temporaries = |inits: &mut InitAssignments| { let mut temps = InitAssignments::default(); diff --git a/src/parser.rs b/src/parser.rs index a70318fafd..e0a12f3adb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -202,8 +202,14 @@ fn parse_pou( // parse variable declarations. note that var in/out/inout // blocks are not allowed inside of class declarations. let mut variable_blocks = vec![]; - let allowed_var_types = - [KeywordVar, KeywordVarInput, KeywordVarOutput, KeywordVarInOut, KeywordVarTemp]; + let allowed_var_types = [ + KeywordVar, + KeywordVarInput, + KeywordVarOutput, + KeywordVarInOut, + KeywordVarTemp, + KeywordVarExternal, + ]; while allowed_var_types.contains(&lexer.token) { variable_blocks.push(parse_variable_block(lexer, LinkageType::Internal)); } @@ -1040,6 +1046,7 @@ fn parse_variable_block_type(lexer: &mut ParseSession) -> VariableBlockType { KeywordVarOutput => VariableBlockType::Output, KeywordVarGlobal => VariableBlockType::Global, KeywordVarInOut => VariableBlockType::InOut, + KeywordVarExternal => VariableBlockType::External, _ => VariableBlockType::Local, } } @@ -1057,7 +1064,7 @@ fn parse_variable_block(lexer: &mut ParseSession, linkage: LinkageType) -> Varia let mut variables = parse_any_in_region(lexer, vec![KeywordEndVar], parse_variable_list); - if constant { + if constant && !matches!(variable_block_type, VariableBlockType::External) { // sneak in the DefaultValue-Statements if no initializers were defined variables.iter_mut().filter(|it| it.initializer.is_none()).for_each(|it| { it.initializer = Some(AstFactory::create_default_value(it.location.clone(), lexer.next_id())); diff --git a/src/parser/tests/variable_parser_tests.rs b/src/parser/tests/variable_parser_tests.rs index 0987b88d98..d6d885c220 100644 --- a/src/parser/tests/variable_parser_tests.rs +++ b/src/parser/tests/variable_parser_tests.rs @@ -422,3 +422,251 @@ fn var_config_location() { assert_eq!("main.instance.foo", &src[result.var_config[0].location.to_range().unwrap()]); } + +#[test] +fn var_external() { + let src = r#" + VAR_GLOBAL + arr: ARRAY [0..100] OF INT; + END_VAR + + FUNCTION foo + VAR_EXTERNAL + arr : ARRAY [0..100] OF INT; + END_VAR + END_FUNCTION + "#; + + let (result, _) = parse(src); + + insta::assert_debug_snapshot!(result, @r###" + CompilationUnit { + global_vars: [ + VariableBlock { + variables: [ + Variable { + name: "arr", + data_type: DataTypeDefinition { + data_type: ArrayType { + name: None, + bounds: RangeStatement { + start: LiteralInteger { + value: 0, + }, + end: LiteralInteger { + value: 100, + }, + }, + referenced_type: DataTypeReference { + referenced_type: "INT", + }, + is_variable_length: false, + }, + }, + }, + ], + variable_block_type: Global, + }, + ], + var_config: [], + units: [ + POU { + name: "foo", + variable_blocks: [ + VariableBlock { + variables: [ + Variable { + name: "arr", + data_type: DataTypeDefinition { + data_type: ArrayType { + name: None, + bounds: RangeStatement { + start: LiteralInteger { + value: 0, + }, + end: LiteralInteger { + value: 100, + }, + }, + referenced_type: DataTypeReference { + referenced_type: "INT", + }, + is_variable_length: false, + }, + }, + }, + ], + variable_block_type: External, + }, + ], + pou_type: Function, + return_type: None, + }, + ], + implementations: [ + Implementation { + name: "foo", + type_name: "foo", + linkage: Internal, + pou_type: Function, + statements: [], + location: SourceLocation { + span: Range( + TextLocation { + line: 9, + column: 4, + offset: 155, + }..TextLocation { + line: 9, + column: 16, + offset: 167, + }, + ), + }, + name_location: SourceLocation { + span: Range( + TextLocation { + line: 5, + column: 13, + offset: 80, + }..TextLocation { + line: 5, + column: 16, + offset: 83, + }, + ), + }, + overriding: false, + generic: false, + access: None, + }, + ], + user_types: [], + file_name: "test.st", + } + "###); +} + +#[test] +fn var_external_constant() { + let src = r#" + VAR_GLOBAL + arr: ARRAY [0..100] OF INT; + END_VAR + + FUNCTION foo + VAR_EXTERNAL CONSTANT + arr : ARRAY [0..100] OF INT; + END_VAR + END_FUNCTION + "#; + + let (result, _) = parse(src); + + insta::assert_debug_snapshot!(result, @r###" + CompilationUnit { + global_vars: [ + VariableBlock { + variables: [ + Variable { + name: "arr", + data_type: DataTypeDefinition { + data_type: ArrayType { + name: None, + bounds: RangeStatement { + start: LiteralInteger { + value: 0, + }, + end: LiteralInteger { + value: 100, + }, + }, + referenced_type: DataTypeReference { + referenced_type: "INT", + }, + is_variable_length: false, + }, + }, + }, + ], + variable_block_type: Global, + }, + ], + var_config: [], + units: [ + POU { + name: "foo", + variable_blocks: [ + VariableBlock { + variables: [ + Variable { + name: "arr", + data_type: DataTypeDefinition { + data_type: ArrayType { + name: None, + bounds: RangeStatement { + start: LiteralInteger { + value: 0, + }, + end: LiteralInteger { + value: 100, + }, + }, + referenced_type: DataTypeReference { + referenced_type: "INT", + }, + is_variable_length: false, + }, + }, + }, + ], + variable_block_type: External, + }, + ], + pou_type: Function, + return_type: None, + }, + ], + implementations: [ + Implementation { + name: "foo", + type_name: "foo", + linkage: Internal, + pou_type: Function, + statements: [], + location: SourceLocation { + span: Range( + TextLocation { + line: 9, + column: 4, + offset: 163, + }..TextLocation { + line: 9, + column: 16, + offset: 175, + }, + ), + }, + name_location: SourceLocation { + span: Range( + TextLocation { + line: 5, + column: 13, + offset: 80, + }..TextLocation { + line: 5, + column: 16, + offset: 83, + }, + ), + }, + overriding: false, + generic: false, + access: None, + }, + ], + user_types: [], + file_name: "test.st", + } + "###); +} diff --git a/src/resolver.rs b/src/resolver.rs index 8f0b0f2825..d6d3d5bb9d 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1979,6 +1979,12 @@ fn to_variable_annotation( index: &Index, constant_override: bool, ) -> StatementAnnotation { + let (v, constant_override) = if v.is_var_external() { + // VAR_EXTERNAL variables should resolve to the global they are referencing + (index.find_global_variable(v.get_name()).unwrap(), v.is_constant()) + } else { + (v, constant_override) + }; let v_type = index.get_effective_type_or_void_by_name(v.get_type_name()); //see if this is an auto-deref variable diff --git a/src/tests/adr/pou_adr.rs b/src/tests/adr/pou_adr.rs index 1481fa4e29..f52b8171b4 100644 --- a/src/tests/adr/pou_adr.rs +++ b/src/tests/adr/pou_adr.rs @@ -68,6 +68,7 @@ fn programs_state_is_stored_in_a_struct() { Input, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 0, linkage: Internal, @@ -95,6 +96,7 @@ fn programs_state_is_stored_in_a_struct() { InOut, ), is_constant: false, + is_var_external: false, data_type_name: "__auto_pointer_to_INT", location_in_parent: 1, linkage: Internal, @@ -122,6 +124,7 @@ fn programs_state_is_stored_in_a_struct() { Output, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 2, linkage: Internal, @@ -149,6 +152,7 @@ fn programs_state_is_stored_in_a_struct() { Local, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 3, linkage: Internal, @@ -176,6 +180,7 @@ fn programs_state_is_stored_in_a_struct() { Temp, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 4, linkage: Internal, diff --git a/src/tests/adr/vla_adr.rs b/src/tests/adr/vla_adr.rs index c86a5323de..83a694e3de 100644 --- a/src/tests/adr/vla_adr.rs +++ b/src/tests/adr/vla_adr.rs @@ -43,6 +43,7 @@ fn representation() { Input, ), is_constant: false, + is_var_external: false, data_type_name: "__ptr_to___arr_vla_1_dint", location_in_parent: 0, linkage: Internal, @@ -60,6 +61,7 @@ fn representation() { Input, ), is_constant: false, + is_var_external: false, data_type_name: "__bounds___arr_vla_1_dint", location_in_parent: 1, linkage: Internal, @@ -269,6 +271,7 @@ fn pass() { Input, ), is_constant: false, + is_var_external: false, data_type_name: "__ptr_to___arr_vla_1_dint", location_in_parent: 0, linkage: Internal, @@ -286,6 +289,7 @@ fn pass() { Input, ), is_constant: false, + is_var_external: false, data_type_name: "__bounds___arr_vla_1_dint", location_in_parent: 1, linkage: Internal, diff --git a/src/validation/variable.rs b/src/validation/variable.rs index 181db7ea90..5599f921c2 100644 --- a/src/validation/variable.rs +++ b/src/validation/variable.rs @@ -135,7 +135,10 @@ pub fn visit_variable_block( fn validate_variable_block(validator: &mut Validator, block: &VariableBlock) { if block.constant - && !matches!(block.variable_block_type, VariableBlockType::Global | VariableBlockType::Local) + && !matches!( + block.variable_block_type, + VariableBlockType::Global | VariableBlockType::Local | VariableBlockType::External + ) { validator.push_diagnostic( Diagnostic::new("This variable block does not support the CONSTANT modifier") @@ -278,7 +281,7 @@ fn validate_variable( .with_location(statement.get_location()), ); } - None if v_entry.is_constant() => { + None if v_entry.is_constant() && !v_entry.is_var_external() => { validator.push_diagnostic( Diagnostic::new(format!("Unresolved constant `{}` variable", variable.name.as_str(),)) .with_error_code("E033") diff --git a/tests/lit/single/var_external/var_external.st b/tests/lit/single/var_external/var_external.st new file mode 100644 index 0000000000..6e408ba64f --- /dev/null +++ b/tests/lit/single/var_external/var_external.st @@ -0,0 +1,28 @@ +// RUN: (%COMPILE %s && %RUN) | %CHECK %s +VAR_GLOBAL + arr : ARRAY [0..10] OF INT; +END_VAR + +FUNCTION main +VAR_EXTERNAL CONSTANT + arr : ARRAY [0..10] OF INT; +END_VAR +VAR + i: DINT; +END_VAR + FOR i := 0 TO 10 DO + arr[i] := i; + END_FOR; + + printf('%d$N', arr[0]); // CHECK: 0 + printf('%d$N', arr[1]); // CHECK: 1 + printf('%d$N', arr[2]); // CHECK: 2 + printf('%d$N', arr[3]); // CHECK: 3 + printf('%d$N', arr[4]); // CHECK: 4 + printf('%d$N', arr[5]); // CHECK: 5 + printf('%d$N', arr[6]); // CHECK: 6 + printf('%d$N', arr[7]); // CHECK: 7 + printf('%d$N', arr[8]); // CHECK: 8 + printf('%d$N', arr[9]); // CHECK: 9 + printf('%d$N', arr[10]); // CHECK: 10 +END_FUNCTION \ No newline at end of file