# HG changeset patch # User mjsousa # Date 1418582420 0 # Node ID e3d4dca7520b2aeff5efffd16c82146ff538a8ab # Parent 98e7a0d3af21a76e205e1ced9744a5a29fba0a9d 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). diff -r 98e7a0d3af21 -r e3d4dca7520b absyntax/absyntax.def --- 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/ 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. diff -r 98e7a0d3af21 -r e3d4dca7520b stage1_2/iec_bison.yy --- 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(@$)); diff -r 98e7a0d3af21 -r e3d4dca7520b stage3/constant_folding.cc --- 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(var_list); + if (NULL == list) ERROR; + for (int i = 0; i < list->n; i++) { + token_c *var_name = dynamic_cast(list->elements[i]); + if (NULL == var_name) { + if (NULL != dynamic_cast(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/ 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__ + + + + diff -r 98e7a0d3af21 -r e3d4dca7520b stage3/constant_folding.hh --- 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) */ diff -r 98e7a0d3af21 -r e3d4dca7520b stage3/declaration_check.cc --- 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 @@ -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 = ????; diff -r 98e7a0d3af21 -r e3d4dca7520b stage4/generate_iec/generate_iec.cc --- 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 */