# HG changeset patch # User mjsousa # Date 1419977842 0 # Node ID 760b264771937e4f9605433936abd338e64630e1 # Parent aad6aa35ce60c0e3ac33a451f03ed566c47d3f48 Change algorithm for propagating VAR_GLOBAL constant values to corresponding VAR_EXTERNAL. diff -r aad6aa35ce60 -r 760b26477193 stage3/constant_folding.cc --- a/stage3/constant_folding.cc Sun Dec 28 22:20:08 2014 +0000 +++ b/stage3/constant_folding.cc Tue Dec 30 22:17:22 2014 +0000 @@ -803,6 +803,8 @@ constant_folding_c::constant_folding_c(symbol_c *symbol) { + current_resource = NULL; + current_configuration = NULL; fixed_init_value_ = false; function_pou_ = false; error_count = 0; @@ -831,6 +833,113 @@ } +/***************************/ +/* B 0 - Programming Model */ +/***************************/ +/* enumvalue_symtable is filled in by enum_declaration_check_c, during stage3 semantic verification, with a list of all enumerated constants declared inside this POU */ +// SYM_LIST(library_c, enumvalue_symtable_t enumvalue_symtable;) + /* The constant propagation algorithm propagates the constant values to all the locations + * in the code where expressions can be determined to have a fixed value. + * e.g.: var1 := 99; + * var2[var1] := 42; <-- with constant propagation, we know we are accessing var2[99] here! + * + * An important question in constant propagation is whether we should use the values of + * VAR_GLOBAL CONSTANT variables as a constant. The problem here is that the + * constant value of each global variable can only be declared in a configuration, + * but a POU may eventually be used from different configurations. + * + * For example: + * CONFIGURATION conf1 + * VAR_GLOBAL CONSTANT global_const : INT := 42; END_VAR + * PROGRAM prog1 WITH TaskX : Prog1_t; + * END_CONFIGURATION + * + * CONFIGURATION conf2 + * VAR_GLOBAL CONSTANT global_const : INT := 18; END_VAR + * PROGRAM prog1 WITH TaskX : Prog1_t; + * END_CONFIGURATION + * + * PROGRAM Prog1_t + * VAR_EXTERN COSNTANT global_const : INT; END_VAR <--- NOTE: 61131-3 syntax does not allow const value to be set here! + * VAR .... END_VAR + * array_var[global_const] := 0; + * END_PROGRAM + * + * Considering the above code, where Prog1_t is instanciated in both conf1 and conf2, + * and therefore where the 'constant' var_global may actually take two possible values + * (42 and 18), it would be incorrect to consider the var_global variable a constant in + * the constant propagation algorithm. + * This means that when doing constant propagation of the Prog1_t POU, we should consider + * all global variables (including the 'constant') as non-constant - this actually makes + * the algorithm much easier!). + * + * However, matiec has implemented an extension where we allow arrays whose size may be + * defined using symbolic variables, whose value can be determined at compile time + * (typically VAR CONSTANT variables). To really become usefull, this extension must allow + * the same var_global constant to be used in declaring arrays in both configuration as + * well as in some other POUs. + * + * e.g.: + * CONFIGURATION conf2 + * VAR_GLOBAL CONSTANT global_const : INT := 18; END_VAR + * VAR_GLOBAL global_array : ARRAY [1..global_const] OF INT; END_VAR + * PROGRAM prog1 WITH TaskX : Prog1_t; + * END_CONFIGURATION + * + * PROGRAM Prog1_t + * VAR_EXTERN COSNTANT global_const : INT; END_VAR <--- NOTE: 61131-3 syntax does not allow const value to be set here! + * VAR_EXTERN global_array : ARRAY [1..global_const] OF INT; END_VAR + * VAR .... END_VAR + * global_array[global_const] := 0; + * END_PROGRAM + * + * The above requirement means that we MUST therefore do the propagation of constant values + * from a var_global constant to the corresponding var_external in the POUs. + * This implies 3 things: + * 1) We must detect when two configurations use the same POU and set distinct values + * to the same var_global - we emit a warning/error (which should we emit?) + * 2) Even if each POU is used by only one configuration, we must warn the user that + * the generated code may not be safely turned into a library to be later linked + * to other configurations + * 3) To do the propagation of the var_global const value to the var_external, + * we must first analyse all the configurations in the library. + * - When analysing a configuration, we store all the constant values in the + * global_values[] map, and call from within the configuration context all the POUs + * instantiated inside this configuration (basically, we do the constant propagation + * of each POU with the global_values[] map preloaded with all the constant values). + * This means that we may evetually do constant folding of the same POU type multiple + * times (if it is instantiated multiple times in the same configuration, or once + * in several configurations). This should not be a problem because the constant + * propagation algorithm is idem-potent (assuming the same constant values in the + * beginning), and we can use these multiple calls to the same POU to detect if + * the situation mentioned in (1) is ocurring. + * - After analysing all the configurations, we analyse all the other POUs that have + * not yet been called (because they are not instantiated directly from within + * any configuration - e.g. functions, and most FBs!). + * It is for this reason (3) why we have the two loops on the following code! + */ +void *constant_folding_c::visit(library_c *symbol) { + int i; + + for (i = 0; i < symbol->n; i++) { + // first analyse the configurations + if (NULL != dynamic_cast(symbol->elements[i])) + symbol->elements[i]->accept(*this); + } + + for (i = 0; i < symbol->n; i++) { + /* NOTE: we will be re-visiting all the POUs that were already called indirectly through the + * visit(program_configuration_c) of vist(fb_task_c) visitors during the previous for + * loop. However, this is OK as the only difference would be how the VAR_EXTERN are handled, + * and that is taken care of in the visit(external_declaration_c) visitor! + */ + if (NULL == dynamic_cast(symbol->elements[i])) + symbol->elements[i]->accept(*this); + } + + return NULL; +} + /*********************/ /* B 1.2 - Constants */ /*********************/ @@ -1008,32 +1117,6 @@ /******************************************/ /* B 1.4.3 - Declaration & Initialisation */ /******************************************/ - -/* Do the constant folding for VAR_EXTERNAL and VAR_GLOBAL pairs. - * This function is called from the declaration_check_c, since it has easy access to the extern<->global pairing information - * needed for this function to work. - */ -int constant_folding_c::handle_var_extern_global_pair(symbol_c *extern_var_name, symbol_c *extern_var_decl, symbol_c *global_var_name, symbol_c *global_var_decl) { - // the minimum infor we must get to make sense - if (NULL == global_var_decl) ERROR; - if (NULL == extern_var_name) ERROR; - - symbol_c *init_value = type_initial_value_c::get(global_var_decl); - if (NULL == init_value) return 0; // this is probably a FB datatype, for which no initial value exists! Do nothing and return. - - // Do constant folding of the initial value! - // This is required since this function may be called before we do the iterative constant folding of the complete library! - init_value->accept(*this); - - if (NULL != extern_var_name) extern_var_name->const_value = init_value->const_value; - if (NULL != extern_var_decl) extern_var_decl->const_value = init_value->const_value; // Note that each external variable declaration has its own datatype specification, so we can set this symbol's const_value too! - // we could leave the constant folding of the global variable itself for later, when we iteratively visit the whole library, but there is nor harm in doing it now! - if (NULL != global_var_name) global_var_name->const_value = init_value->const_value; - if (NULL != global_var_decl) global_var_decl->const_value = init_value->const_value; // Note that each external variable declaration has its own datatype specification, so we can set this symbol's const_value too! - return 0; -} - - void *constant_folding_c::handle_var_decl(symbol_c *var_list, bool fixed_init_value) { fixed_init_value_ = fixed_init_value; @@ -1167,9 +1250,16 @@ /* global_var_name ':' (simple_specification|subrange_specification|enumerated_specification|array_specification|prev_declared_structure_type_name|function_block_type_name */ //SYM_REF2(external_declaration_c, global_var_name, specification) void *constant_folding_c::visit(external_declaration_c *symbol) { - // Note that specification->const_value will have been set by handle_var_extern_global_pair(), which is called from declaration_check_c + // The syntax does not allow VAR_EXTERN to be initialized. We must get the initial value from the corresponding VAR_GLOBAL declaration + /* However, we only do this is if the visit() method for the Program/FB in which this VAR_EXTERN is found was called from the visit(configurtion/resource) visitor! + * When we are called from the visit(library_c) visitor, we do not have the required information to do this! + */ + if (NULL != current_configuration) + symbol->specification->const_value = var_global_values[get_var_name_c::get_name(symbol->global_var_name)->value]; + symbol->global_var_name->const_value = symbol->specification->const_value; if (fixed_init_value_) { +// values[symbol->global_var_name->get_value()] = symbol->specification->const_value; values[get_var_name_c::get_name(symbol->global_var_name)->value] = symbol->specification->const_value; } // If the datatype specification is a subrange or array, do constant folding of all the literals in that type declaration... (ex: literals in array subrange limits) @@ -1369,9 +1459,13 @@ // enumvalue_symtable_t enumvalue_symtable; localvar_symbmap_t localvar_symbmap; localvar_symbvec_t localvar_symbvec;) void *constant_folding_c::visit(configuration_declaration_c *symbol) { values.clear(); /* Clear global map */ + /* Add initial value of all declared variables into Values map. */ function_pou_ = false; - return iterator_visitor_c::visit(symbol); // let the base iterator class handle the rest (basically iterate through the whole configuration and do the constant folding! + current_configuration = symbol; + iterator_visitor_c::visit(symbol); // let the base iterator class handle the rest (basically iterate through the whole configuration and do the constant folding! + current_configuration = NULL; + return NULL; } @@ -1389,9 +1483,19 @@ // enumvalue_symtable_t enumvalue_symtable; localvar_symbmap_t localvar_symbmap; localvar_symbvec_t localvar_symbvec;) void *constant_folding_c::visit(resource_declaration_c *symbol) { values.push(); /* Create inner scope */ + /* Add initial value of all declared variables into Values map. */ function_pou_ = false; - iterator_visitor_c::visit(symbol); // let the base iterator class handle the rest (basically iterate through the whole configuration and do the constant folding! + symbol->global_var_declarations->accept(*this); + + var_global_values = values; + values.clear(); + current_resource = symbol; + symbol->resource_declaration->accept(*this); + current_resource = NULL; + values = var_global_values; +// iterator_visitor_c::visit(symbol); // let the base iterator class handle the rest (basically iterate through the whole configuration and do the constant folding! + values.pop(); /* Delete inner scope */ return NULL; } @@ -1400,6 +1504,7 @@ /* task_configuration_list program_configuration_list */ // SYM_REF2(single_resource_declaration_c, task_configuration_list, program_configuration_list) + /* helper symbol for single_resource_declaration */ // SYM_LIST(task_configuration_list_c) /* helper symbol for single_resource_declaration */ @@ -1414,14 +1519,56 @@ // SYM_REF2(task_configuration_c, task_name, task_initialization) /* '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' */ // SYM_REF3(task_initialization_c, single_data_source, interval_data_source, priority_data_source) + /* PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] */ /* NOTE: The parameter 'called_prog_declaration'is used to pass data between stage 3 and stage4 */ // SYM_REF5(program_configuration_c, retain_option, program_name, task_name, program_type_name, prog_conf_elements, // symbol_c *called_prog_declaration;) +void *constant_folding_c::visit(program_configuration_c *symbol) { + /* find the declaration (i.e. the datatype) of the program being instantiated */ + // NOTE: we do not use symbol->datatype so this cost propagation algorithm will not depend on the fill/narrow datatypes algorithm! + program_type_symtable_t::iterator itr = program_type_symtable.find(symbol->program_type_name); + if (itr == program_type_symtable.end()) ERROR; // syntax parsing should not allow this! + program_declaration_c *prog_type = itr->second; + if (NULL == prog_type) ERROR; // syntax parsing should not allow this! + prog_type->accept(*this); + + if (NULL != symbol->prog_conf_elements) + symbol->prog_conf_elements->accept(*this); + return NULL; +} + + /* prog_conf_elements ',' prog_conf_element */ // SYM_LIST(prog_conf_elements_c) + /* fb_name WITH task_name */ // SYM_REF2(fb_task_c, fb_name, task_name) +void *constant_folding_c::visit(fb_task_c *symbol) { + /* find the declaration (i.e. the datatype) of the FB being instantiated */ + // NOTE: we do not use symbol->datatype so this cost propagation algorithm will not depend on the fill/narrow datatypes algorithm! + symbol_c *fb_type_name = NULL; + + if ((NULL == fb_type_name) && (NULL != current_configuration)) { + search_var_instance_decl_c search_scope(current_configuration); + fb_type_name = search_scope.get_decl(symbol->fb_name); + } + if ((NULL == fb_type_name) && (NULL != current_resource)) { + search_var_instance_decl_c search_scope(current_resource); + fb_type_name = search_scope.get_decl(symbol->fb_name); + } + if (NULL == fb_type_name) ERROR; + + function_block_type_symtable_t::iterator itr = function_block_type_symtable.find(fb_type_name); + if (itr == function_block_type_symtable.end()) ERROR; // syntax parsing should not allow this! + function_block_declaration_c *fb_type_decl = itr->second; + if (NULL == fb_type_decl) ERROR; + fb_type_decl->accept(*this); + return NULL; +} + + + /* any_symbolic_variable ASSIGN prog_data_source */ // SYM_REF2(prog_cnxn_assign_c, symbolic_variable, prog_data_source) /* any_symbolic_variable SENDTO data_sink */ diff -r aad6aa35ce60 -r 760b26477193 stage3/constant_folding.hh --- a/stage3/constant_folding.hh Sun Dec 28 22:20:08 2014 +0000 +++ b/stage3/constant_folding.hh Tue Dec 30 22:17:22 2014 +0000 @@ -68,10 +68,12 @@ constant_folding_c(symbol_c *symbol = NULL); virtual ~constant_folding_c(void); int get_error_count(); - int handle_var_extern_global_pair(symbol_c *extern_var_name, symbol_c *extern_var_decl, symbol_c *global_var_name, symbol_c *global_var_decl); typedef symtable_c map_values_t; private: + symbol_c *current_resource; + symbol_c *current_configuration; map_values_t values; + map_values_t var_global_values; void *handle_var_list_decl(symbol_c *var_list, symbol_c *type_decl); void *handle_var_decl (symbol_c *var_list, bool fixed_init_value); // Flag to indicate whether the variables in the variable declaration list will always have a fixed value when the POU is executed! @@ -93,6 +95,10 @@ #endif private: + /***************************/ + /* B 0 - Programming Model */ + /***************************/ + void *visit(library_c *symbol); /*********************/ /* B 1.2 - Constants */ /*********************/ @@ -166,6 +172,8 @@ /********************************/ void *visit( configuration_declaration_c *symbol); void *visit( resource_declaration_c *symbol); + void *visit( program_configuration_c *symbol); + void *visit( fb_task_c *symbol); /****************************************/ diff -r aad6aa35ce60 -r 760b26477193 stage3/declaration_check.cc --- a/stage3/declaration_check.cc Sun Dec 28 22:20:08 2014 +0000 +++ b/stage3/declaration_check.cc Tue Dec 30 22:17:22 2014 +0000 @@ -75,7 +75,6 @@ -#include "constant_folding.hh" #include @@ -141,13 +140,6 @@ if ((glo_opt == search_var_instance_decl_c::constant_opt) && (ext_opt != search_var_instance_decl_c::constant_opt)) STAGE3_ERROR(0, glo_decl, glo_decl, "Declaration error. The external variable must be declared as constant, as it maps to a constant global variable."); - /* Copy the const_value from the global to the external variable. - * This is actually part of the constant folding algorithm. It is placed here because it needs the extern<->global pairing - * information to run correctly, which is all available right here! - */ - constant_folding_c constant_folding; - constant_folding.handle_var_extern_global_pair(var_name /* extern_var */, ext_decl /* extern_decl */, NULL /* global_var */, glo_decl /* global_decl */); - /* TODO: Check redefinition data type. * We need a new class (like search_base_type class) to get type id by variable declaration. * symbol_c *glo_type = ????;