828 |
830 |
829 int constant_folding_c::get_error_count() { |
831 int constant_folding_c::get_error_count() { |
830 return error_count; |
832 return error_count; |
831 } |
833 } |
832 |
834 |
|
835 |
|
836 /***************************/ |
|
837 /* B 0 - Programming Model */ |
|
838 /***************************/ |
|
839 /* 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 */ |
|
840 // SYM_LIST(library_c, enumvalue_symtable_t enumvalue_symtable;) |
|
841 /* The constant propagation algorithm propagates the constant values to all the locations |
|
842 * in the code where expressions can be determined to have a fixed value. |
|
843 * e.g.: var1 := 99; |
|
844 * var2[var1] := 42; <-- with constant propagation, we know we are accessing var2[99] here! |
|
845 * |
|
846 * An important question in constant propagation is whether we should use the values of |
|
847 * VAR_GLOBAL CONSTANT variables as a constant. The problem here is that the |
|
848 * constant value of each global variable can only be declared in a configuration, |
|
849 * but a POU may eventually be used from different configurations. |
|
850 * |
|
851 * For example: |
|
852 * CONFIGURATION conf1 |
|
853 * VAR_GLOBAL CONSTANT global_const : INT := 42; END_VAR |
|
854 * PROGRAM prog1 WITH TaskX : Prog1_t; |
|
855 * END_CONFIGURATION |
|
856 * |
|
857 * CONFIGURATION conf2 |
|
858 * VAR_GLOBAL CONSTANT global_const : INT := 18; END_VAR |
|
859 * PROGRAM prog1 WITH TaskX : Prog1_t; |
|
860 * END_CONFIGURATION |
|
861 * |
|
862 * PROGRAM Prog1_t |
|
863 * VAR_EXTERN COSNTANT global_const : INT; END_VAR <--- NOTE: 61131-3 syntax does not allow const value to be set here! |
|
864 * VAR .... END_VAR |
|
865 * array_var[global_const] := 0; |
|
866 * END_PROGRAM |
|
867 * |
|
868 * Considering the above code, where Prog1_t is instanciated in both conf1 and conf2, |
|
869 * and therefore where the 'constant' var_global may actually take two possible values |
|
870 * (42 and 18), it would be incorrect to consider the var_global variable a constant in |
|
871 * the constant propagation algorithm. |
|
872 * This means that when doing constant propagation of the Prog1_t POU, we should consider |
|
873 * all global variables (including the 'constant') as non-constant - this actually makes |
|
874 * the algorithm much easier!). |
|
875 * |
|
876 * However, matiec has implemented an extension where we allow arrays whose size may be |
|
877 * defined using symbolic variables, whose value can be determined at compile time |
|
878 * (typically VAR CONSTANT variables). To really become usefull, this extension must allow |
|
879 * the same var_global constant to be used in declaring arrays in both configuration as |
|
880 * well as in some other POUs. |
|
881 * |
|
882 * e.g.: |
|
883 * CONFIGURATION conf2 |
|
884 * VAR_GLOBAL CONSTANT global_const : INT := 18; END_VAR |
|
885 * VAR_GLOBAL global_array : ARRAY [1..global_const] OF INT; END_VAR |
|
886 * PROGRAM prog1 WITH TaskX : Prog1_t; |
|
887 * END_CONFIGURATION |
|
888 * |
|
889 * PROGRAM Prog1_t |
|
890 * VAR_EXTERN COSNTANT global_const : INT; END_VAR <--- NOTE: 61131-3 syntax does not allow const value to be set here! |
|
891 * VAR_EXTERN global_array : ARRAY [1..global_const] OF INT; END_VAR |
|
892 * VAR .... END_VAR |
|
893 * global_array[global_const] := 0; |
|
894 * END_PROGRAM |
|
895 * |
|
896 * The above requirement means that we MUST therefore do the propagation of constant values |
|
897 * from a var_global constant to the corresponding var_external in the POUs. |
|
898 * This implies 3 things: |
|
899 * 1) We must detect when two configurations use the same POU and set distinct values |
|
900 * to the same var_global - we emit a warning/error (which should we emit?) |
|
901 * 2) Even if each POU is used by only one configuration, we must warn the user that |
|
902 * the generated code may not be safely turned into a library to be later linked |
|
903 * to other configurations |
|
904 * 3) To do the propagation of the var_global const value to the var_external, |
|
905 * we must first analyse all the configurations in the library. |
|
906 * - When analysing a configuration, we store all the constant values in the |
|
907 * global_values[] map, and call from within the configuration context all the POUs |
|
908 * instantiated inside this configuration (basically, we do the constant propagation |
|
909 * of each POU with the global_values[] map preloaded with all the constant values). |
|
910 * This means that we may evetually do constant folding of the same POU type multiple |
|
911 * times (if it is instantiated multiple times in the same configuration, or once |
|
912 * in several configurations). This should not be a problem because the constant |
|
913 * propagation algorithm is idem-potent (assuming the same constant values in the |
|
914 * beginning), and we can use these multiple calls to the same POU to detect if |
|
915 * the situation mentioned in (1) is ocurring. |
|
916 * - After analysing all the configurations, we analyse all the other POUs that have |
|
917 * not yet been called (because they are not instantiated directly from within |
|
918 * any configuration - e.g. functions, and most FBs!). |
|
919 * It is for this reason (3) why we have the two loops on the following code! |
|
920 */ |
|
921 void *constant_folding_c::visit(library_c *symbol) { |
|
922 int i; |
|
923 |
|
924 for (i = 0; i < symbol->n; i++) { |
|
925 // first analyse the configurations |
|
926 if (NULL != dynamic_cast<configuration_declaration_c *>(symbol->elements[i])) |
|
927 symbol->elements[i]->accept(*this); |
|
928 } |
|
929 |
|
930 for (i = 0; i < symbol->n; i++) { |
|
931 /* NOTE: we will be re-visiting all the POUs that were already called indirectly through the |
|
932 * visit(program_configuration_c) of vist(fb_task_c) visitors during the previous for |
|
933 * loop. However, this is OK as the only difference would be how the VAR_EXTERN are handled, |
|
934 * and that is taken care of in the visit(external_declaration_c) visitor! |
|
935 */ |
|
936 if (NULL == dynamic_cast<configuration_declaration_c *>(symbol->elements[i])) |
|
937 symbol->elements[i]->accept(*this); |
|
938 } |
|
939 |
|
940 return NULL; |
|
941 } |
833 |
942 |
834 /*********************/ |
943 /*********************/ |
835 /* B 1.2 - Constants */ |
944 /* B 1.2 - Constants */ |
836 /*********************/ |
945 /*********************/ |
837 /******************************/ |
946 /******************************/ |
1006 |
1115 |
1007 |
1116 |
1008 /******************************************/ |
1117 /******************************************/ |
1009 /* B 1.4.3 - Declaration & Initialisation */ |
1118 /* B 1.4.3 - Declaration & Initialisation */ |
1010 /******************************************/ |
1119 /******************************************/ |
1011 |
|
1012 /* Do the constant folding for VAR_EXTERNAL and VAR_GLOBAL pairs. |
|
1013 * This function is called from the declaration_check_c, since it has easy access to the extern<->global pairing information |
|
1014 * needed for this function to work. |
|
1015 */ |
|
1016 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) { |
|
1017 // the minimum infor we must get to make sense |
|
1018 if (NULL == global_var_decl) ERROR; |
|
1019 if (NULL == extern_var_name) ERROR; |
|
1020 |
|
1021 symbol_c *init_value = type_initial_value_c::get(global_var_decl); |
|
1022 if (NULL == init_value) return 0; // this is probably a FB datatype, for which no initial value exists! Do nothing and return. |
|
1023 |
|
1024 // Do constant folding of the initial value! |
|
1025 // This is required since this function may be called before we do the iterative constant folding of the complete library! |
|
1026 init_value->accept(*this); |
|
1027 |
|
1028 if (NULL != extern_var_name) extern_var_name->const_value = init_value->const_value; |
|
1029 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! |
|
1030 // 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! |
|
1031 if (NULL != global_var_name) global_var_name->const_value = init_value->const_value; |
|
1032 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! |
|
1033 return 0; |
|
1034 } |
|
1035 |
|
1036 |
|
1037 |
1120 |
1038 void *constant_folding_c::handle_var_decl(symbol_c *var_list, bool fixed_init_value) { |
1121 void *constant_folding_c::handle_var_decl(symbol_c *var_list, bool fixed_init_value) { |
1039 fixed_init_value_ = fixed_init_value; |
1122 fixed_init_value_ = fixed_init_value; |
1040 var_list->accept(*this); |
1123 var_list->accept(*this); |
1041 fixed_init_value_ = false; |
1124 fixed_init_value_ = false; |
1165 // void *constant_folding_c::visit(external_declaration_list_c *symbol) {} // Not needed: we inherit from iterator_c |
1248 // void *constant_folding_c::visit(external_declaration_list_c *symbol) {} // Not needed: we inherit from iterator_c |
1166 |
1249 |
1167 /* global_var_name ':' (simple_specification|subrange_specification|enumerated_specification|array_specification|prev_declared_structure_type_name|function_block_type_name */ |
1250 /* global_var_name ':' (simple_specification|subrange_specification|enumerated_specification|array_specification|prev_declared_structure_type_name|function_block_type_name */ |
1168 //SYM_REF2(external_declaration_c, global_var_name, specification) |
1251 //SYM_REF2(external_declaration_c, global_var_name, specification) |
1169 void *constant_folding_c::visit(external_declaration_c *symbol) { |
1252 void *constant_folding_c::visit(external_declaration_c *symbol) { |
1170 // Note that specification->const_value will have been set by handle_var_extern_global_pair(), which is called from declaration_check_c |
1253 // The syntax does not allow VAR_EXTERN to be initialized. We must get the initial value from the corresponding VAR_GLOBAL declaration |
|
1254 /* 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! |
|
1255 * When we are called from the visit(library_c) visitor, we do not have the required information to do this! |
|
1256 */ |
|
1257 if (NULL != current_configuration) |
|
1258 symbol->specification->const_value = var_global_values[get_var_name_c::get_name(symbol->global_var_name)->value]; |
|
1259 |
1171 symbol->global_var_name->const_value = symbol->specification->const_value; |
1260 symbol->global_var_name->const_value = symbol->specification->const_value; |
1172 if (fixed_init_value_) { |
1261 if (fixed_init_value_) { |
|
1262 // values[symbol->global_var_name->get_value()] = symbol->specification->const_value; |
1173 values[get_var_name_c::get_name(symbol->global_var_name)->value] = symbol->specification->const_value; |
1263 values[get_var_name_c::get_name(symbol->global_var_name)->value] = symbol->specification->const_value; |
1174 } |
1264 } |
1175 // 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) |
1265 // 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) |
1176 symbol->specification->accept(*this); // should never get to change the const_value of the symbol->specification symbol (only its children!). |
1266 symbol->specification->accept(*this); // should never get to change the const_value of the symbol->specification symbol (only its children!). |
1177 return NULL; |
1267 return NULL; |
1367 /* 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 */ |
1457 /* 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 */ |
1368 // SYM_REF5(configuration_declaration_c, configuration_name, global_var_declarations, resource_declarations, access_declarations, instance_specific_initializations, |
1458 // SYM_REF5(configuration_declaration_c, configuration_name, global_var_declarations, resource_declarations, access_declarations, instance_specific_initializations, |
1369 // enumvalue_symtable_t enumvalue_symtable; localvar_symbmap_t localvar_symbmap; localvar_symbvec_t localvar_symbvec;) |
1459 // enumvalue_symtable_t enumvalue_symtable; localvar_symbmap_t localvar_symbmap; localvar_symbvec_t localvar_symbvec;) |
1370 void *constant_folding_c::visit(configuration_declaration_c *symbol) { |
1460 void *constant_folding_c::visit(configuration_declaration_c *symbol) { |
1371 values.clear(); /* Clear global map */ |
1461 values.clear(); /* Clear global map */ |
|
1462 |
1372 /* Add initial value of all declared variables into Values map. */ |
1463 /* Add initial value of all declared variables into Values map. */ |
1373 function_pou_ = false; |
1464 function_pou_ = false; |
1374 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! |
1465 current_configuration = symbol; |
|
1466 iterator_visitor_c::visit(symbol); // let the base iterator class handle the rest (basically iterate through the whole configuration and do the constant folding! |
|
1467 current_configuration = NULL; |
|
1468 return NULL; |
1375 } |
1469 } |
1376 |
1470 |
1377 |
1471 |
1378 /* helper symbol for configuration_declaration */ |
1472 /* helper symbol for configuration_declaration */ |
1379 // SYM_LIST(resource_declaration_list_c) // Not needed: we inherit from iterator_c |
1473 // SYM_LIST(resource_declaration_list_c) // Not needed: we inherit from iterator_c |
1387 /* 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 */ |
1481 /* 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 */ |
1388 // SYM_REF4(resource_declaration_c, resource_name, resource_type_name, global_var_declarations, resource_declaration, |
1482 // SYM_REF4(resource_declaration_c, resource_name, resource_type_name, global_var_declarations, resource_declaration, |
1389 // enumvalue_symtable_t enumvalue_symtable; localvar_symbmap_t localvar_symbmap; localvar_symbvec_t localvar_symbvec;) |
1483 // enumvalue_symtable_t enumvalue_symtable; localvar_symbmap_t localvar_symbmap; localvar_symbvec_t localvar_symbvec;) |
1390 void *constant_folding_c::visit(resource_declaration_c *symbol) { |
1484 void *constant_folding_c::visit(resource_declaration_c *symbol) { |
1391 values.push(); /* Create inner scope */ |
1485 values.push(); /* Create inner scope */ |
|
1486 |
1392 /* Add initial value of all declared variables into Values map. */ |
1487 /* Add initial value of all declared variables into Values map. */ |
1393 function_pou_ = false; |
1488 function_pou_ = false; |
1394 iterator_visitor_c::visit(symbol); // let the base iterator class handle the rest (basically iterate through the whole configuration and do the constant folding! |
1489 symbol->global_var_declarations->accept(*this); |
|
1490 |
|
1491 var_global_values = values; |
|
1492 values.clear(); |
|
1493 current_resource = symbol; |
|
1494 symbol->resource_declaration->accept(*this); |
|
1495 current_resource = NULL; |
|
1496 values = var_global_values; |
|
1497 // iterator_visitor_c::visit(symbol); // let the base iterator class handle the rest (basically iterate through the whole configuration and do the constant folding! |
|
1498 |
1395 values.pop(); /* Delete inner scope */ |
1499 values.pop(); /* Delete inner scope */ |
1396 return NULL; |
1500 return NULL; |
1397 } |
1501 } |
1398 |
1502 |
1399 |
1503 |
1400 |
1504 |
1401 /* task_configuration_list program_configuration_list */ |
1505 /* task_configuration_list program_configuration_list */ |
1402 // SYM_REF2(single_resource_declaration_c, task_configuration_list, program_configuration_list) |
1506 // SYM_REF2(single_resource_declaration_c, task_configuration_list, program_configuration_list) |
|
1507 |
1403 /* helper symbol for single_resource_declaration */ |
1508 /* helper symbol for single_resource_declaration */ |
1404 // SYM_LIST(task_configuration_list_c) |
1509 // SYM_LIST(task_configuration_list_c) |
1405 /* helper symbol for single_resource_declaration */ |
1510 /* helper symbol for single_resource_declaration */ |
1406 // SYM_LIST(program_configuration_list_c) |
1511 // SYM_LIST(program_configuration_list_c) |
1407 /* helper symbol for: (access_path, instance_specific_init) */ |
1512 /* helper symbol for: (access_path, instance_specific_init) */ |
1412 // SYM_REF2(program_output_reference_c, program_name, symbolic_variable) |
1517 // SYM_REF2(program_output_reference_c, program_name, symbolic_variable) |
1413 /* TASK task_name task_initialization */ |
1518 /* TASK task_name task_initialization */ |
1414 // SYM_REF2(task_configuration_c, task_name, task_initialization) |
1519 // SYM_REF2(task_configuration_c, task_name, task_initialization) |
1415 /* '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' */ |
1520 /* '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' */ |
1416 // SYM_REF3(task_initialization_c, single_data_source, interval_data_source, priority_data_source) |
1521 // SYM_REF3(task_initialization_c, single_data_source, interval_data_source, priority_data_source) |
|
1522 |
1417 /* PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] */ |
1523 /* PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] */ |
1418 /* NOTE: The parameter 'called_prog_declaration'is used to pass data between stage 3 and stage4 */ |
1524 /* NOTE: The parameter 'called_prog_declaration'is used to pass data between stage 3 and stage4 */ |
1419 // SYM_REF5(program_configuration_c, retain_option, program_name, task_name, program_type_name, prog_conf_elements, |
1525 // SYM_REF5(program_configuration_c, retain_option, program_name, task_name, program_type_name, prog_conf_elements, |
1420 // symbol_c *called_prog_declaration;) |
1526 // symbol_c *called_prog_declaration;) |
|
1527 void *constant_folding_c::visit(program_configuration_c *symbol) { |
|
1528 /* find the declaration (i.e. the datatype) of the program being instantiated */ |
|
1529 // NOTE: we do not use symbol->datatype so this cost propagation algorithm will not depend on the fill/narrow datatypes algorithm! |
|
1530 program_type_symtable_t::iterator itr = program_type_symtable.find(symbol->program_type_name); |
|
1531 if (itr == program_type_symtable.end()) ERROR; // syntax parsing should not allow this! |
|
1532 program_declaration_c *prog_type = itr->second; |
|
1533 if (NULL == prog_type) ERROR; // syntax parsing should not allow this! |
|
1534 prog_type->accept(*this); |
|
1535 |
|
1536 if (NULL != symbol->prog_conf_elements) |
|
1537 symbol->prog_conf_elements->accept(*this); |
|
1538 return NULL; |
|
1539 } |
|
1540 |
|
1541 |
1421 /* prog_conf_elements ',' prog_conf_element */ |
1542 /* prog_conf_elements ',' prog_conf_element */ |
1422 // SYM_LIST(prog_conf_elements_c) |
1543 // SYM_LIST(prog_conf_elements_c) |
|
1544 |
1423 /* fb_name WITH task_name */ |
1545 /* fb_name WITH task_name */ |
1424 // SYM_REF2(fb_task_c, fb_name, task_name) |
1546 // SYM_REF2(fb_task_c, fb_name, task_name) |
|
1547 void *constant_folding_c::visit(fb_task_c *symbol) { |
|
1548 /* find the declaration (i.e. the datatype) of the FB being instantiated */ |
|
1549 // NOTE: we do not use symbol->datatype so this cost propagation algorithm will not depend on the fill/narrow datatypes algorithm! |
|
1550 symbol_c *fb_type_name = NULL; |
|
1551 |
|
1552 if ((NULL == fb_type_name) && (NULL != current_configuration)) { |
|
1553 search_var_instance_decl_c search_scope(current_configuration); |
|
1554 fb_type_name = search_scope.get_decl(symbol->fb_name); |
|
1555 } |
|
1556 if ((NULL == fb_type_name) && (NULL != current_resource)) { |
|
1557 search_var_instance_decl_c search_scope(current_resource); |
|
1558 fb_type_name = search_scope.get_decl(symbol->fb_name); |
|
1559 } |
|
1560 if (NULL == fb_type_name) ERROR; |
|
1561 |
|
1562 function_block_type_symtable_t::iterator itr = function_block_type_symtable.find(fb_type_name); |
|
1563 if (itr == function_block_type_symtable.end()) ERROR; // syntax parsing should not allow this! |
|
1564 function_block_declaration_c *fb_type_decl = itr->second; |
|
1565 if (NULL == fb_type_decl) ERROR; |
|
1566 fb_type_decl->accept(*this); |
|
1567 return NULL; |
|
1568 } |
|
1569 |
|
1570 |
|
1571 |
1425 /* any_symbolic_variable ASSIGN prog_data_source */ |
1572 /* any_symbolic_variable ASSIGN prog_data_source */ |
1426 // SYM_REF2(prog_cnxn_assign_c, symbolic_variable, prog_data_source) |
1573 // SYM_REF2(prog_cnxn_assign_c, symbolic_variable, prog_data_source) |
1427 /* any_symbolic_variable SENDTO data_sink */ |
1574 /* any_symbolic_variable SENDTO data_sink */ |
1428 // SYM_REF2(prog_cnxn_sendto_c, symbolic_variable, data_sink) |
1575 // SYM_REF2(prog_cnxn_sendto_c, symbolic_variable, data_sink) |
1429 /* VAR_CONFIG instance_specific_init_list END_VAR */ |
1576 /* VAR_CONFIG instance_specific_init_list END_VAR */ |