Change algorithm for propagating VAR_GLOBAL constant values to corresponding VAR_EXTERNAL.
--- 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<configuration_declaration_c *>(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<configuration_declaration_c *>(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 */
--- 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<const_value_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);
/****************************************/
--- 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 <set>
@@ -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 = ????;