Do constant folding of variable's initial value (allows correct C code generation with variables in the subrange of an array declaration: ARRAY [1..max] of INT).
authormjsousa
Sun, 14 Dec 2014 18:40:20 +0000
changeset 963 e3d4dca7520b
parent 962 98e7a0d3af21
child 964 5f4dfe6670da
Do constant folding of variable's initial value (allows correct C code generation with variables in the subrange of an array declaration: ARRAY [1..max] of INT).
absyntax/absyntax.def
stage1_2/iec_bison.yy
stage3/constant_folding.cc
stage3/constant_folding.hh
stage3/declaration_check.cc
stage4/generate_iec/generate_iec.cc
--- a/absyntax/absyntax.def	Wed Dec 10 12:06:45 2014 +0000
+++ b/absyntax/absyntax.def	Sun Dec 14 18:40:20 2014 +0000
@@ -526,6 +526,15 @@
 /*********************/
 SYM_REF1(symbolic_variable_c, var_name)
 
+/* symbolic_constant_c is used only when a variable is used inside the subrange of an array declaration
+ *    e.g.: ARRAY [1 .. maxval] OF INT
+ *  where maxval is a CONSTANT variable. 
+ *  When maxval shows up in the POU body,          it will be stored as a standard symbolic_variable_c in the AST.
+ *  When maxval shows up in the ARRAY declaration, it will be stored as a          symbolic_constant_c in the AST.
+ *  This will allow us to more easily handle this special case, without affecting the remaining working code.
+ */ 
+SYM_REF1(symbolic_constant_c, var_name)  // a non-standard extension!!
+
 /********************************************/
 /* B.1.4.1   Directly Represented Variables */
 /********************************************/
@@ -560,6 +569,7 @@
 SYM_REF0(retain_option_c)
 SYM_REF0(non_retain_option_c)
 
+/* VAR_INPUT [option] input_declaration_list END_VAR */
 /* option -> the RETAIN/NON_RETAIN/<NULL> directive... */
 /* NOTE: We need to implicitly define the EN and ENO function and FB parameters when the user
  *       does not do it explicitly in the IEC 61131-3 source code. 
--- a/stage1_2/iec_bison.yy	Wed Dec 10 12:06:45 2014 +0000
+++ b/stage1_2/iec_bison.yy	Sun Dec 14 18:40:20 2014 +0000
@@ -2776,11 +2776,11 @@
   signed_integer DOTDOT signed_integer
 	{$$ = new subrange_c($1, $3, locloc(@$));}
 | any_identifier DOTDOT signed_integer
-	{$$ = new subrange_c($1, $3, locloc(@$));}
+	{$$ = new subrange_c(new symbolic_constant_c($1, locloc(@1)), $3, locloc(@$));}
 | signed_integer DOTDOT any_identifier
-	{$$ = new subrange_c($1, $3, locloc(@$));}
+	{$$ = new subrange_c($1, new symbolic_constant_c($3, locloc(@3)), locloc(@$));}
 | any_identifier DOTDOT any_identifier
-	{$$ = new subrange_c($1, $3, locloc(@$));}
+	{$$ = new subrange_c(new symbolic_constant_c($1, locloc(@1)), new symbolic_constant_c($3, locloc(@3)), locloc(@$));}
 /* ERROR_CHECK_BEGIN */
 | signed_integer signed_integer
 	{$$ = NULL; print_err_msg(locl(@1), locf(@2), "'..' missing between bounds in subrange definition."); yynerrs++;}
@@ -4333,6 +4333,18 @@
 ;
 
 
+
+/* Warning: When handling VAR_EXTERNAL declarations, the constant folding algorithm may (depending on the command line parameters) 
+ *          set the symbol_c->const_value annotations on both the external_var_name as well as on its VAR_EXTERNAL datatype specification symbol.
+ *          Setting the const_value on the datatype specification symbol of a VAR_EXTERNAL declaration is only possible if the declaration of 
+ *          several external variables in a list is not allowed (as each variable could have a potentially distinct initial value).
+ *           VAR_EXTERNAL
+ *             a, b, c, d: INT;  (* incorrect syntax! *)
+ *           END_VAR
+ *          
+ *          If anybody considers extending this standard syntax to allow the above syntax (several variables in a list), then be sure to go
+ *          and fix the constant folding algorithm (more precisely, the constant_folding_c::handle_var_extern_global_pair() function.
+ */
 external_declaration:
   global_var_name ':' simple_specification
 	{$$ = new external_declaration_c($1, $3, locloc(@$));
--- a/stage3/constant_folding.cc	Wed Dec 10 12:06:45 2014 +0000
+++ b/stage3/constant_folding.cc	Sun Dec 14 18:40:20 2014 +0000
@@ -992,38 +992,264 @@
 /*********************/
 #if DO_CONSTANT_PROPAGATION__
 void *constant_folding_c::visit(symbolic_variable_c *symbol) {
-	std::string varName;
-
-	varName = get_var_name_c::get_name(symbol->var_name)->value;
-	if (values.count(varName) > 0) {
+	std::string varName = get_var_name_c::get_name(symbol->var_name)->value;
+	if (values.count(varName) > 0) 
 		symbol->const_value = values[varName];
-	}
 	return NULL;
 }
 #endif  // DO_CONSTANT_PROPAGATION__
 
+void *constant_folding_c::visit(symbolic_constant_c *symbol) {
+	std::string varName = get_var_name_c::get_name(symbol->var_name)->value;
+	if (values.count(varName) > 0) 
+		symbol->const_value = values[varName];
+	return NULL;
+}
+
+
+/******************************************/
+/* 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_list_decl(symbol_c *var_list, symbol_c *type_decl) {
+  symbol_c *init_value = type_initial_value_c::get(type_decl);  
+  if (NULL == init_value)   return NULL; // this is probably a FB datatype, for which no initial value exists! Do nothing and return.
+  init_value->accept(*this);  // Do constant folding of the initial value! (we will probably be doing this multiple times for the same init value, but this is safe as the cvalue is idem-potent)
+
+  list_c *list = dynamic_cast<list_c *>(var_list);
+  if (NULL == list) ERROR;
+  for (int i = 0; i < list->n; i++) {
+    token_c *var_name = dynamic_cast<token_c *>(list->elements[i]);
+    if (NULL == var_name) {
+      if (NULL != dynamic_cast<extensible_input_parameter_c *>(list->elements[i]))
+        continue; // this is an extensible standard function. Ignore this variable, and continue!
+      debug_c::print(list->elements[i]);
+      ERROR;
+    }
+    std::string varName = var_name->value;
+    values[varName] = init_value->const_value;
+    list->elements[i]->const_value = init_value->const_value;
+  }
+  return NULL;
+}
+
+/* | var1_list ',' variable_name */
+//SYM_LIST(var1_list_c)           // Not needed!
+//SYM_REF0(constant_option_c)     // Not needed!
+//SYM_REF0(retain_option_c)       // Not needed!
+//SYM_REF0(non_retain_option_c)   // Not needed!
+
+
+/* spec_init is one of the following...
+ *    simple_spec_init_c *
+ *    subrange_spec_init_c *
+ *    enumerated_spec_init_c *
+ */
+// SYM_REF2(var1_init_decl_c, var1_list, spec_init)
+void *constant_folding_c::visit(var1_init_decl_c *symbol) {return handle_var_list_decl(symbol->var1_list, symbol->spec_init);}
+
+/* | [var1_list ','] variable_name integer '..' */
+/* NOTE: This is an extension to the standard!!! */
+//SYM_REF2(extensible_input_parameter_c, var_name, first_index)          // Not needed!
+/* var1_list ':' array_spec_init */
+//SYM_REF2(array_var_init_decl_c, var1_list, array_spec_init)            // We do not yet handle arrays!
+/*  var1_list ':' initialized_structure */
+//SYM_REF2(structured_var_init_decl_c, var1_list, initialized_structure) // We do not yet handle structures!
+/* fb_name_list ':' function_block_type_name ASSIGN structure_initialization */
+//SYM_REF2(fb_name_decl_c, fb_name_list, fb_spec_init)                   // We do not yet handle FBs!
+/* fb_name_list ',' fb_name */
+//SYM_LIST(fb_name_list_c)                                               // Not needed!
+/* VAR_INPUT [option] input_declaration_list END_VAR */
+/* option -> the RETAIN/NON_RETAIN/<NULL> directive... */
+//SYM_REF3(input_declarations_c, option, input_declaration_list, method)
+void *constant_folding_c::visit(input_declarations_c *symbol) {return NULL;}  // Input variables can take any initial value, so we can not set the const_value annotation!
+
+/* helper symbol for input_declarations */
+//SYM_LIST(input_declaration_list_c)                                     // Not needed!
+/* VAR_OUTPUT [RETAIN | NON_RETAIN] var_init_decl_list END_VAR */
+/* option -> may be NULL ! */
+//SYM_REF3(output_declarations_c, option, var_init_decl_list, method)    // Not needed since we inherit from iterator_visitor_c!
+
+/*  VAR_IN_OUT var_declaration_list END_VAR */
+//SYM_REF1(input_output_declarations_c, var_declaration_list)
+void *constant_folding_c::visit(input_output_declarations_c *symbol) {return NULL;}  // Input variables can take any initial value, so we can not set the const_value annotation!
+/* helper symbol for input_output_declarations */
+/* var_declaration_list var_declaration ';' */
+//SYM_LIST(var_declaration_list_c)                                       // Not needed since we inherit from iterator_visitor_c!
+/*  var1_list ':' array_specification */
+//SYM_REF2(array_var_declaration_c, var1_list, array_specification)      // We do not yet handle arrays! 
+/*  var1_list ':' structure_type_name */
+//SYM_REF2(structured_var_declaration_c, var1_list, structure_type_name) // We do not yet handle structures!
+/* VAR [CONSTANT] var_init_decl_list END_VAR */
+/* option -> may be NULL ! */
+//SYM_REF2(var_declarations_c, option, var_init_decl_list)               // Not needed: we inherit from iterator_c
+
+/*  VAR RETAIN var_init_decl_list END_VAR */
+//SYM_REF1(retentive_var_declarations_c, var_init_decl_list)
+void *constant_folding_c::visit(retentive_var_declarations_c *symbol) {return NULL;}  // Retentive variables can take any initial value, so we can not set the const_value annotation!
+/*  VAR [CONSTANT|RETAIN|NON_RETAIN] located_var_decl_list END_VAR */
+/* option -> may be NULL ! */
+
+#if 0  
+// TODO
+SYM_REF2(located_var_declarations_c, option, located_var_decl_list)
+/* helper symbol for located_var_declarations */
+/* located_var_decl_list located_var_decl ';' */
+SYM_LIST(located_var_decl_list_c)
+/*  [variable_name] location ':' located_var_spec_init */
+/* variable_name -> may be NULL ! */
+SYM_REF3(located_var_decl_c, variable_name, location, located_var_spec_init)
+#endif
+
+/*| VAR_EXTERNAL [CONSTANT] external_declaration_list END_VAR */
+/* option -> may be NULL ! */
+// SYM_REF2(external_var_declarations_c, option, external_declaration_list)
+// void *constant_folding_c::visit(external_var_declarations_c *symbol) {} // Not needed: we inherit from iterator_c
+
+/* helper symbol for external_var_declarations */
+/*| external_declaration_list external_declaration';' */
+// SYM_LIST(external_declaration_list_c)
+// void *constant_folding_c::visit(external_declaration_list_c *symbol) {} // Not needed: we inherit from iterator_c
+
+/*  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
+  symbol->global_var_name->const_value = symbol->specification->const_value;
+  std::string varName = get_var_name_c::get_name(symbol->global_var_name)->value;
+  values[varName] = symbol->specification->const_value;
+  return NULL;
+}
+
+/* Visitors related to GLOBAL variables are not really needed, 
+ * as they are already handled by handle_var_extern_global_pair, which is called from declaration_check_c 
+ * 
+ * This is done like this because we need to know the pairing of external<->global variables to get the cvalue
+ * from the global variable. Since the external<->global pairing information is available in the declaration_check_c,
+ * we have that class call the constant_folding_c::handle_var_extern_global_pair(), which will actually do the 
+ * constant folding of the global and the external variable declarations!
+ */
+/*| VAR_GLOBAL [CONSTANT|RETAIN] global_var_decl_list END_VAR */
+/* option -> may be NULL ! */
+// SYM_REF2(global_var_declarations_c, option, global_var_decl_list)
+/* helper symbol for global_var_declarations */
+/*| global_var_decl_list global_var_decl ';' */
+// SYM_LIST(global_var_decl_list_c)
+/*| global_var_spec ':' [located_var_spec_init|function_block_type_name] */
+/* type_specification ->may be NULL ! */
+//SYM_REF2(global_var_decl_c, global_var_spec, type_specification)
+/*| global_var_name location */
+//SYM_REF2(global_var_spec_c, global_var_name, location)
+/*  AT direct_variable */
+//SYM_REF1(location_c, direct_variable)
+/*| global_var_list ',' global_var_name */
+//SYM_LIST(global_var_list_c)
+
+
+#if 0  
+// TODO
+/*  var1_list ':' single_byte_string_spec */
+SYM_REF2(single_byte_string_var_declaration_c, var1_list, single_byte_string_spec)
+/*  STRING ['[' integer ']'] [ASSIGN single_byte_character_string] */
+/* integer ->may be NULL ! */
+/* single_byte_character_string ->may be NULL ! */
+SYM_REF2(single_byte_string_spec_c, string_spec, single_byte_character_string)
+/*   STRING ['[' integer ']'] */
+/* integer ->may be NULL ! */
+SYM_REF2(single_byte_limited_len_string_spec_c, string_type_name, character_string_len)
+/*  WSTRING ['[' integer ']'] */
+/* integer ->may be NULL ! */
+SYM_REF2(double_byte_limited_len_string_spec_c, string_type_name, character_string_len)
+/*  var1_list ':' double_byte_string_spec */
+SYM_REF2(double_byte_string_var_declaration_c, var1_list, double_byte_string_spec)
+/*  WSTRING ['[' integer ']'] [ASSIGN double_byte_character_string] */
+/* integer ->may be NULL ! */
+/* double_byte_character_string ->may be NULL ! */
+SYM_REF2(double_byte_string_spec_c, string_spec, double_byte_character_string)
+/*| VAR [RETAIN|NON_RETAIN] incompl_located_var_decl_list END_VAR */
+/* option ->may be NULL ! */
+SYM_REF2(incompl_located_var_declarations_c, option, incompl_located_var_decl_list)
+/* helper symbol for incompl_located_var_declarations */
+/*| incompl_located_var_decl_list incompl_located_var_decl ';' */
+SYM_LIST(incompl_located_var_decl_list_c)
+/*  variable_name incompl_location ':' var_spec */
+SYM_REF3(incompl_located_var_decl_c, variable_name, incompl_location, var_spec)
+/*  AT incompl_location_token */
+SYM_TOKEN(incompl_location_c)
+/* intermediate helper symbol for:
+ *  - non_retentive_var_decls
+ *  - output_declarations
+ */
+SYM_LIST(var_init_decl_list_c)
+#endif
+
+
+/***********************/
+/* B 1.5.1 - Functions */
+/***********************/
+/* 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_REF4(function_declaration_c, derived_function_name, type_name, var_declarations_list, function_body, enumvalue_symtable_t enumvalue_symtable;)
+void *constant_folding_c::visit(function_declaration_c *symbol) {
+	values.clear(); /* Clear global map */
+	/* Add initial value of all declared variables into Values map. */
+	symbol->var_declarations_list->accept(*this);
+	symbol->function_body->accept(*this);
+	return NULL;
+}
+
+
+/*****************************/
+/* B 1.5.2 - Function Blocks */
+/*****************************/
+/*  FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */
+/* 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_REF3(function_block_declaration_c, fblock_name, var_declarations, fblock_body, enumvalue_symtable_t enumvalue_symtable;)
+void *constant_folding_c::visit(function_block_declaration_c *symbol) {
+	values.clear(); /* Clear global map */
+	/* Add initial value of all declared variables into Values map. */
+	symbol->var_declarations->accept(*this);
+	symbol->fblock_body->accept(*this);
+	return NULL;
+}
+
 
 /**********************/
 /* B 1.5.3 - Programs */
 /**********************/
-#if DO_CONSTANT_PROPAGATION__
+/*  PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM */
+//SYM_REF3(program_declaration_c, program_type_name, var_declarations, function_block_body, enumvalue_symtable_t enumvalue_symtable;)
 void *constant_folding_c::visit(program_declaration_c *symbol) {
-	symbol_c *var_name;
-
+	values.clear(); /* Clear global map */
+	/* Add initial value of all declared variables into Values map. */
 	symbol->var_declarations->accept(*this);
-	values.clear(); /* Clear global map */
-	search_var_instance_decl_c search_var_instance_decl(symbol);
-	function_param_iterator_c fpi(symbol);
-	while((var_name = fpi.next()) != NULL) {
-		std::string varName = get_var_name_c::get_name(var_name)->value;
-		symbol_c   *varDecl = search_var_instance_decl.get_decl(var_name);
-		values[varName] = varDecl->const_value;
-	}
-	/* Add all variables declared into Values map and put them to initial value */
 	symbol->function_block_body->accept(*this);
 	return NULL;
 }
-#endif  // DO_CONSTANT_PROPAGATION__
 
 
 /****************************************/
@@ -1419,3 +1645,7 @@
 #endif  // DO_CONSTANT_PROPAGATION__
 
 
+
+
+
+
--- a/stage3/constant_folding.hh	Wed Dec 10 12:06:45 2014 +0000
+++ b/stage3/constant_folding.hh	Sun Dec 14 18:40:20 2014 +0000
@@ -65,7 +65,10 @@
 	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);
+  private:
+	void *handle_var_list_decl(symbol_c *var_list, symbol_c *type_decl);
+  public:
 	#if 0
 	// not currently needed, so comment it out!...
 	/* utility functions for other stage3 algorithms to access the contant folded values */
@@ -105,14 +108,36 @@
     /*********************/
     #if DO_CONSTANT_PROPAGATION__
     void *visit(symbolic_variable_c *symbol);
-    #endif
+    #endif // DO_CONSTANT_PROPAGATION__
+    void *visit(symbolic_constant_c *symbol);
+                             
+    /******************************************/
+    /* B 1.4.3 - Declaration & Initialisation */
+    /******************************************/
+    void *visit(    var1_init_decl_c         *symbol);
+    void *visit(        input_declarations_c *symbol);
+    void *visit( input_output_declarations_c *symbol);
+    void *visit(retentive_var_declarations_c *symbol);
+    void *visit(     external_declaration_c  *symbol);
+
+    /**************************************/
+    /* B.1.5 - Program organization units */
+    /**************************************/
+    /***********************/
+    /* B 1.5.1 - Functions */
+    /***********************/
+    void *visit(function_declaration_c *symbol);
+    
+    /*****************************/
+    /* B 1.5.2 - Function Blocks */
+    /*****************************/
+    void *visit(function_block_declaration_c *symbol);
 
     /**********************/
     /* B 1.5.3 - Programs */
     /**********************/
-    #if DO_CONSTANT_PROPAGATION__
     void *visit(program_declaration_c *symbol);
-    #endif
+
 
     /****************************************/
     /* B.2 - Language IL (Instruction List) */
--- a/stage3/declaration_check.cc	Wed Dec 10 12:06:45 2014 +0000
+++ b/stage3/declaration_check.cc	Sun Dec 14 18:40:20 2014 +0000
@@ -75,6 +75,7 @@
 
 
 
+#include "constant_folding.hh"
 
 
 #include <set>
@@ -140,6 +141,13 @@
           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 = ????;
--- a/stage4/generate_iec/generate_iec.cc	Wed Dec 10 12:06:45 2014 +0000
+++ b/stage4/generate_iec/generate_iec.cc	Sun Dec 14 18:40:20 2014 +0000
@@ -712,10 +712,8 @@
 /*********************/
 /* B 1.4 - Variables */
 /*********************/
-void *visit(symbolic_variable_c *symbol) {
-  symbol->var_name->accept(*this);
-  return NULL;
-}
+void *visit(symbolic_variable_c *symbol) {return symbol->var_name->accept(*this);}
+void *visit(symbolic_constant_c *symbol) {return symbol->var_name->accept(*this);}
 
 /********************************************/
 /* B.1.4.1   Directly Represented Variables */