conti@564: /* conti@564: * matiec - a compiler for the programming languages defined in IEC 61131-3 conti@564: * conti@564: * Copyright (C) 2003-2011 Mario de Sousa (msousa@fe.up.pt) conti@564: * Copyright (C) 2012 Manuele Conti (conti.ma@alice.it) conti@564: * conti@564: * This program is free software: you can redistribute it and/or modify conti@564: * it under the terms of the GNU General Public License as published by conti@564: * the Free Software Foundation, either version 3 of the License, or conti@564: * (at your option) any later version. conti@564: * conti@564: * This program is distributed in the hope that it will be useful, conti@564: * but WITHOUT ANY WARRANTY; without even the implied warranty of conti@564: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the conti@564: * GNU General Public License for more details. conti@564: * conti@564: * You should have received a copy of the GNU General Public License conti@564: * along with this program. If not, see . conti@564: * conti@564: * conti@564: * This code is made available on the understanding that it will not be conti@564: * used in safety-critical situations without a full and competent review. conti@564: */ conti@564: conti@564: /* conti@564: * An IEC 61131-3 compiler. conti@564: * conti@564: * Based on the conti@564: * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) conti@564: * conti@564: */ conti@564: conti@564: msousa@572: msousa@640: /* TODO: msousa@640: * - Add support for comparison (= and !=) of enumeration literals! msousa@640: * We will need to add another const_value entry to the symbol_c, containing the msousa@640: * possible enumeration value of the enum constant! msousa@640: * Doing this will allow us to more easily implement a constant_propagation_c later on! msousa@640: * msousa@640: * - Add support for comparison (= and !=) of the exact same variable msousa@640: * (e.g. if (int_v = int_v) then ...) msousa@640: */ msousa@640: msousa@572: msousa@572: msousa@569: /* Do constant folding... msousa@569: * msousa@569: * I.e., Determine the value of all expressions in which only constant values (i.e. literals) are used. msousa@569: * The (constant) result of each operation is stored (annotated) in the respective operation symbol msousa@569: * (e.g.: add_expression_c) in the abstract syntax tree, conti@564: * conti@564: * For example: msousa@569: * 2 + 3 -> the constant value '5' is stored in the add_expression_c symbol. msousa@569: * 22.2 - 5.0 -> the constant value '17.2' is stored in the add_expression_c symbol. conti@564: * etc... msousa@569: * msousa@569: * msousa@572: * NOTE 1 msousa@572: * Some operations and constants can have multiple data types. For example, msousa@569: * 1 AND 0 msousa@569: * may be either a BOOL, BYTE, WORD or LWORD. msousa@569: * msousa@569: * The same happens with msousa@569: * 1 + 2 msousa@569: * which may be signed (e.g. INT) or unsigned (UINT) msousa@569: * msousa@569: * For the above reason, instead of storing a single constant value, we actually store 4: msousa@569: * - bool msousa@569: * - uint64 msousa@569: * - int64 msousa@569: * - real64 msousa@569: * msousa@569: * Additionally, since the result of an operation may result in an overflow, we actually msousa@569: * store the result inside a struct (defined in absyntax.hh) msousa@569: * msousa@569: * ** During stage 3 (semantic analysis/checking) we will be doing constant folding. msousa@569: * * That algorithm will anotate the abstract syntax tree with the result of operations msousa@569: * * on literals (i.e. 44 + 55 will store the result 99). msousa@569: * * Since the same source code (e.g. 1 + 0) may actually be a BOOL or an ANY_INT, msousa@569: * * or an ANY_BIT, we need to handle all possibilities, and determine the result of the msousa@569: * * operation assuming each type. msousa@569: * * For this reason, we have one entry for each possible type, with some expressions msousa@569: * * having more than one entry filled in! msousa@569: * ** msousa@569: * typedef enum { cs_undefined, // not defined --> const_value is not valid! msousa@569: * cs_const_value, // const value is valid msousa@569: * cs_overflow // result produced overflow or underflow --> const_value is not valid! msousa@569: * } const_status_t; msousa@569: * msousa@569: * typedef struct { msousa@569: * const_status_t status; msousa@569: * real64_t value; msousa@569: * } const_value_real64_t; msousa@569: * const_value_real64_t *const_value_real64; // when NULL --> UNDEFINED msousa@569: * msousa@569: * typedef struct { msousa@569: * const_status_t status; msousa@569: * int64_t value; msousa@569: * } const_value_int64_t; msousa@569: * const_value_int64_t *const_value_int64; // when NULL --> UNDEFINED msousa@569: * msousa@569: * typedef struct { msousa@569: * const_status_t status; msousa@569: * uint64_t value; msousa@569: * } const_value_uint64_t; msousa@569: * const_value_uint64_t *const_value_uint64; // when NULL --> UNDEFINED msousa@569: * msousa@569: * typedef struct { msousa@569: * const_status_t status; msousa@569: * bool value; msousa@569: * } const_value_bool_t; msousa@569: * const_value_bool_t *const_value_bool; // when NULL --> UNDEFINED msousa@569: * msousa@569: * msousa@569: * msousa@569: * NOTE 2 msousa@569: * This file does not print out any error messages! msousa@569: * We cannot really print out error messages when we find an overflow. Since each operation conti@781: * (symbol in the abstract syntax tree for that operation) will have up to 4 constant results, msousa@569: * it may happen that some of them overflow, while other do not. msousa@569: * We must wait for data type checking to determine the exact data type of each expression msousa@569: * before we can decide whether or not we should print out an overflow error message. msousa@569: * msousa@569: * For this reason, this visitor merely annotates the abstract syntax tree, and leaves the conti@781: * actually printing of errors for the print_datatype_errors_c class! conti@781: * conti@781: * NOTE 3 conti@781: * Constant Folding class is extended with a implementation constant propagation algorithm conti@781: * by Mario de Sousa. conti@781: * Main idea is not to implement a general constant propagation algorithm but to reinterpret it conti@781: * for visitor classes. conti@781: * We declared a hash map, it contains a variables list linked with current constant values. conti@781: * During expression evaluation we can retrieve a constant value to symbolic variables getting it from the map. conti@781: * Also at join source points we use a meet semilattice rules to merge current values between a block conti@781: * and adjacent block. conti@781: * conti@564: */ conti@564: conti@564: #include "constant_folding.hh" msousa@565: #include /* required for malloc() */ conti@564: msousa@596: #include /* required for strlen() */ msousa@596: // #include /* required for atoi() */ msousa@596: #include /* required for errno */ msousa@596: msousa@607: #include "../main.hh" // required for uint8_t, real_64_t, ..., and the macros NAN, INFINITY, INT8_MAX, REAL32_MAX, ... */ msousa@579: msousa@579: msousa@579: msousa@579: msousa@568: msousa@568: conti@564: #define FIRST_(symbol1, symbol2) (((symbol1)->first_order < (symbol2)->first_order) ? (symbol1) : (symbol2)) conti@564: #define LAST_(symbol1, symbol2) (((symbol1)->last_order > (symbol2)->last_order) ? (symbol1) : (symbol2)) conti@564: conti@564: #define STAGE3_ERROR(error_level, symbol1, symbol2, ...) { \ conti@564: if (current_display_error_level >= error_level) { \ conti@564: fprintf(stderr, "%s:%d-%d..%d-%d: error: ", \ conti@564: FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\ conti@564: LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\ conti@564: fprintf(stderr, __VA_ARGS__); \ conti@564: fprintf(stderr, "\n"); \ conti@564: error_count++; \ conti@564: } \ conti@564: } conti@564: conti@564: conti@564: #define STAGE3_WARNING(symbol1, symbol2, ...) { \ conti@564: fprintf(stderr, "%s:%d-%d..%d-%d: warning: ", \ conti@564: FIRST_(symbol1,symbol2)->first_file, FIRST_(symbol1,symbol2)->first_line, FIRST_(symbol1,symbol2)->first_column,\ conti@564: LAST_(symbol1,symbol2) ->last_line, LAST_(symbol1,symbol2) ->last_column);\ conti@564: fprintf(stderr, __VA_ARGS__); \ conti@564: fprintf(stderr, "\n"); \ conti@564: warning_found = true; \ conti@564: } conti@564: conti@564: conti@564: msousa@567: msousa@569: msousa@569: msousa@569: msousa@569: msousa@569: msousa@569: msousa@569: msousa@569: mjsousa@965: #define SET_CVALUE(dtype, symbol, new_value) ((symbol)->const_value._##dtype.set(new_value)) mjsousa@965: #define GET_CVALUE(dtype, symbol) ((symbol)->const_value._##dtype.get()) mjsousa@965: #define SET_OVFLOW(dtype, symbol) ((symbol)->const_value._##dtype.set_overflow()) mjsousa@965: #define SET_NONCONST(dtype, symbol) ((symbol)->const_value._##dtype.set_nonconst()) mjsousa@965: mjsousa@965: #define VALID_CVALUE(dtype, symbol) ((symbol)->const_value._##dtype.is_valid()) mjsousa@965: #define IS_OVFLOW(dtype, symbol) ((symbol)->const_value._##dtype.is_overflow()) mjsousa@965: #define IS_NONCONST(dtype, symbol) ((symbol)->const_value._##dtype.is_nonconst()) mjsousa@983: #define IS_UNDEFINED(dtype, symbol) ((symbol)->const_value._##dtype.is_undefined()) mjsousa@965: #define ISZERO_CVALUE(dtype, symbol) ((symbol)->const_value._##dtype.is_zero()) mjsousa@965: msousa@575: msousa@612: #define ISEQUAL_CVALUE(dtype, symbol1, symbol2) \ msousa@612: (VALID_CVALUE(dtype, symbol1) && VALID_CVALUE(dtype, symbol2) && (GET_CVALUE(dtype, symbol1) == GET_CVALUE(dtype, symbol2))) msousa@612: msousa@648: #define DO_BINARY_OPER(oper_type, operation, res_type, operand1, operand2) { \ msousa@648: if (VALID_CVALUE(oper_type, operand1) && VALID_CVALUE(oper_type, operand2)) \ msousa@648: {SET_CVALUE(res_type, symbol, GET_CVALUE(oper_type, operand1) operation GET_CVALUE(oper_type, operand2));}\ msousa@648: else if (IS_OVFLOW (oper_type, operand1) || IS_OVFLOW (oper_type, operand2)) \ msousa@648: {SET_OVFLOW(res_type, symbol);} /* does it really make sense to set OVFLOW when restype is boolean?? */ \ msousa@648: else if (IS_NONCONST (oper_type, operand1) || IS_NONCONST (oper_type, operand2)) \ msousa@648: {SET_NONCONST(res_type, symbol);} \ msousa@643: } msousa@643: msousa@643: #define DO_UNARY_OPER(dtype, operation, operand) { \ msousa@648: if (VALID_CVALUE(dtype, operand)) \ msousa@648: {SET_CVALUE(dtype, symbol, operation GET_CVALUE(dtype, operand));} \ msousa@648: else if (IS_OVFLOW (dtype, operand)) \ msousa@648: {SET_OVFLOW(dtype, symbol);} \ msousa@648: else if (IS_NONCONST (dtype, operand)) \ msousa@648: {SET_NONCONST(dtype, symbol);} \ msousa@643: } conti@564: conti@776: /* Constant Propagation: Rules for Meet from "Cooper K., Torczon L. - Engineering a Compiler, Second Edition - 2011" conti@776: * at 9.3 Static Single-Assignment Form page 517 conti@776: * - any * undefined = any conti@776: * - any * non_const = non_const conti@776: * - constant * constant = constant (if equal) conti@776: * - constant * constant = non_const (if not equal) conti@774: */ conti@774: #define COMPUTE_MEET_SEMILATTICE(dtype, c1, c2, resValue) {\ mjsousa@965: if (( c1._##dtype.get() != c2._##dtype.get() && c2._##dtype.is_valid() && c1._##dtype.is_valid()) ||\ mjsousa@965: ( c1._##dtype.is_nonconst() && c2._##dtype.is_valid() ) ||\ mjsousa@965: ( c2._##dtype.is_nonconst() && c1._##dtype.is_valid() )) {\ mjsousa@965: resValue._##dtype.set_nonconst();\ conti@774: } else {\ mjsousa@965: resValue._##dtype.set(c1._##dtype.get());\ conti@774: }\ conti@774: } conti@774: conti@788: conti@564: msousa@572: msousa@596: /***********************************************************************/ msousa@596: /***********************************************************************/ msousa@596: /***********************************************************************/ msousa@596: /*** convert string to numerical value ***/ msousa@596: /***********************************************************************/ msousa@596: /***********************************************************************/ msousa@596: /***********************************************************************/ msousa@596: msousa@596: msousa@596: msousa@596: /* To allow the compiler to be portable, we cannot assume that int64_t is mapped onto long long int, msousa@596: * so we cannot call strtoll() and strtoull() in extract_int64() and extract_uint64(). msousa@596: * msousa@596: * So, we create our own strtouint64() and strtoint64() functions. msousa@596: * (We actually call them matiec_strtoint64() so they will not clash with any function msousa@596: * that may be added to the standard library in the future). msousa@596: * We actually create several of each, and let the compiler choose which is the correct one, msousa@596: * by having it resolve the call to the overloaded function. For the C++ compiler to be able msousa@596: * to resolve this ambiguity, we need to add a dummy parameter to each function! msousa@596: * msousa@640: * TODO: support platforms (where the compiler will run) in which int64_t is mapped onto int !! msousa@640: * Is this really needed? msousa@640: * Currently, when trying to compile matiec on sych a platform, the C++ compiler will not msousa@640: * find any apropriate matiec_strtoint64() to call, so matiec will not be able to be compiled. msousa@640: * If you need this, you are welcome to fix it yourself... msousa@596: */ msousa@596: static int64_t matiec_strtoint64 ( long int *dummy, const char *nptr, char **endptr, int base) {return strtol (nptr, endptr, base);} msousa@596: static int64_t matiec_strtoint64 ( long long int *dummy, const char *nptr, char **endptr, int base) {return strtoll (nptr, endptr, base);} msousa@596: msousa@596: static uint64_t matiec_strtouint64(unsigned long int *dummy, const char *nptr, char **endptr, int base) {return strtoul (nptr, endptr, base);} msousa@596: static uint64_t matiec_strtouint64(unsigned long long int *dummy, const char *nptr, char **endptr, int base) {return strtoull(nptr, endptr, base);} msousa@596: msousa@596: msousa@596: /* extract the value of an integer from an integer_c object !! */ msousa@596: /* NOTE: it must ignore underscores! */ msousa@601: /* NOTE: To follow the basic structure used throughout the compiler's code, we should really be msousa@601: * writing this as a visitor_c (and do away with the dynamic casts!), but since we only have 3 distinct msousa@601: * symbol class types to handle, it is probably easier to read if we write it as a standard function... msousa@601: */ msousa@601: int64_t extract_int64_value(symbol_c *sym, bool *overflow) { msousa@601: int64_t ret; msousa@601: std::string str = ""; msousa@601: char *endptr; conti@735: const char *value = NULL; msousa@601: int base; msousa@601: integer_c *integer; msousa@601: hex_integer_c *hex_integer; msousa@601: octal_integer_c *octal_integer; msousa@601: binary_integer_c *binary_integer; msousa@601: msousa@601: if ((integer = dynamic_cast(sym)) != NULL) {value = integer ->value + 0; base = 10;} msousa@601: else if ((hex_integer = dynamic_cast(sym)) != NULL) {value = hex_integer ->value + 3; base = 16;} msousa@601: else if ((octal_integer = dynamic_cast(sym)) != NULL) {value = octal_integer ->value + 2; base = 8;} msousa@601: else if ((binary_integer = dynamic_cast(sym)) != NULL) {value = binary_integer->value + 2; base = 2;} msousa@601: else ERROR; msousa@601: msousa@601: for(unsigned int i = 0; i < strlen(value); i++) msousa@601: if (value[i] != '_') str += value[i]; msousa@596: msousa@596: errno = 0; // since strtoXX() may legally return 0, we must set errno to 0 to detect errors correctly! msousa@600: ret = matiec_strtoint64((int64_t *)NULL, str.c_str(), &endptr, base); msousa@596: if (overflow != NULL) msousa@596: *overflow = (errno == ERANGE); msousa@600: if (((errno != 0) && (errno != ERANGE)) || (*endptr != '\0')) msousa@596: ERROR; msousa@596: msousa@596: return ret; msousa@596: } msousa@596: msousa@601: msousa@601: msousa@601: uint64_t extract_uint64_value(symbol_c *sym, bool *overflow) { msousa@601: uint64_t ret; msousa@601: std::string str = ""; msousa@601: char *endptr; conti@735: const char *value = NULL; msousa@601: int base; msousa@601: integer_c *integer; msousa@601: hex_integer_c *hex_integer; msousa@601: octal_integer_c *octal_integer; msousa@601: binary_integer_c *binary_integer; msousa@601: msousa@601: if ((integer = dynamic_cast(sym)) != NULL) {value = integer ->value + 0; base = 10;} msousa@601: else if ((hex_integer = dynamic_cast(sym)) != NULL) {value = hex_integer ->value + 3; base = 16;} msousa@601: else if ((octal_integer = dynamic_cast(sym)) != NULL) {value = octal_integer ->value + 2; base = 8;} msousa@601: else if ((binary_integer = dynamic_cast(sym)) != NULL) {value = binary_integer->value + 2; base = 2;} msousa@601: else ERROR; msousa@601: msousa@601: for(unsigned int i = 0; i < strlen(value); i++) msousa@601: if (value[i] != '_') str += value[i]; msousa@596: msousa@596: errno = 0; // since strtoXX() may legally return 0, we must set errno to 0 to detect errors correctly! msousa@600: ret = matiec_strtouint64((uint64_t *)NULL, str.c_str(), &endptr, base); msousa@596: if (overflow != NULL) msousa@596: *overflow = (errno == ERANGE); msousa@600: if (((errno != 0) && (errno != ERANGE)) || (*endptr != '\0')) msousa@596: ERROR; msousa@596: msousa@596: return ret; msousa@596: } msousa@596: msousa@596: msousa@596: msousa@596: /* extract the value of a real from an real_c object !! */ msousa@596: /* NOTE: it must ignore underscores! */ msousa@596: /* From iec_bison.yy msousa@596: * real: msousa@596: * real_token {$$ = new real_c($1, locloc(@$));} msousa@596: * | fixed_point_token {$$ = new real_c($1, locloc(@$));} msousa@596: * msousa@596: * From iec_flex.ll msousa@596: * {real} {yylval.ID=strdup(yytext); return real_token;} msousa@596: * {fixed_point} {yylval.ID=strdup(yytext); return fixed_point_token;} msousa@596: * msousa@596: * real {integer}\.{integer}{exponent} msousa@596: * fixed_point {integer}\.{integer} msousa@596: * exponent [Ee]([+-]?){integer} msousa@596: * integer {digit}((_?{digit})*) msousa@596: */ msousa@596: real64_t extract_real_value(symbol_c *sym, bool *overflow) { msousa@596: std::string str = ""; msousa@600: real_c *real_sym; Laurent@633: fixed_point_c *fixed_point_sym; msousa@600: char *endptr; msousa@596: real64_t ret; msousa@596: Laurent@633: if ((real_sym = dynamic_cast(sym)) != NULL) { Laurent@633: for(unsigned int i = 0; i < strlen(real_sym->value); i++) Laurent@633: if (real_sym->value[i] != '_') str += real_sym->value[i]; Laurent@633: } Laurent@633: else if ((fixed_point_sym = dynamic_cast(sym)) != NULL) { Laurent@633: for(unsigned int i = 0; i < strlen(fixed_point_sym->value); i++) Laurent@633: if (fixed_point_sym->value[i] != '_') str += fixed_point_sym->value[i]; Laurent@633: } Laurent@633: else ERROR; msousa@596: msousa@596: errno = 0; // since strtoXX() may legally return 0, we must set errno to 0 to detect errors correctly! mjsousa@890: #if (real64_tX == float) msousa@600: ret = strtof(str.c_str(), &endptr); mjsousa@890: #elif (real64_tX == double) msousa@600: ret = strtod(str.c_str(), &endptr); mjsousa@890: #elif (real64_tX == long_double) msousa@600: ret = strtold(str.c_str(), &endptr); msousa@596: #else mjsousa@890: #error Could not determine which data type is being used for real64_t (defined in main.hh). Aborting! msousa@596: #endif msousa@596: if (overflow != NULL) msousa@596: *overflow = (errno == ERANGE); msousa@600: if (((errno != 0) && (errno != ERANGE)) || (*endptr != '\0')) msousa@596: ERROR; msousa@596: msousa@596: return ret; msousa@596: } msousa@596: msousa@596: msousa@596: msousa@596: msousa@596: msousa@596: /***********************************************************************/ msousa@596: /***********************************************************************/ msousa@596: /***********************************************************************/ msousa@596: /*** Functions to check for overflow situation ***/ msousa@596: /***********************************************************************/ msousa@596: /***********************************************************************/ msousa@596: /***********************************************************************/ msousa@596: msousa@596: msousa@574: /* NOTE: msousa@574: * Most of the conditions to detect overflows on signed and unsigned integer operations were adapted from msousa@574: * https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow?showComments=false msousa@574: * https://www.securecoding.cert.org/confluence/display/seccode/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap msousa@574: */ msousa@574: msousa@574: /* NOTE: If at all possible, all overflow tests are done by pre-condition tests, i.e. tests that msousa@574: * can be run _before_ the operation is executed, and therefore without accessing the result! msousa@574: * msousa@574: * The exception is for real/floating point values, that simply test if the result is NaN (not a number). msousa@574: */ msousa@572: msousa@572: /* res = a + b */ msousa@572: static void CHECK_OVERFLOW_uint64_SUM(symbol_c *res, symbol_c *a, symbol_c *b) { msousa@574: if (!VALID_CVALUE(uint64, res)) msousa@574: return; msousa@574: /* Test by post-condition: If sum is smaller than either operand => overflow! */ msousa@574: // if (GET_CVALUE(uint64, res) < GET_CVALUE(uint64, a)) msousa@574: /* Test by pre-condition: If (UINT64_MAX - a) < b => overflow! */ msousa@574: if ((UINT64_MAX - GET_CVALUE(uint64, a)) < GET_CVALUE(uint64, b)) msousa@574: SET_OVFLOW(uint64, res); msousa@574: } msousa@574: msousa@572: msousa@572: /* res = a - b */ msousa@572: static void CHECK_OVERFLOW_uint64_SUB(symbol_c *res, symbol_c *a, symbol_c *b) { msousa@574: if (!VALID_CVALUE(uint64, res)) msousa@574: return; msousa@574: /* Test by post-condition: If diference is larger than a => overflow! */ msousa@574: // if (GET_CVALUE(uint64, res) > GET_CVALUE(uint64, a)) msousa@574: /* Test by pre-condition: if b > a => overflow! */ msousa@574: if (GET_CVALUE(uint64, b) > GET_CVALUE(uint64, a)) msousa@574: SET_OVFLOW(uint64, res); msousa@574: } msousa@574: msousa@572: msousa@572: /* res = a * b */ msousa@572: static void CHECK_OVERFLOW_uint64_MUL(symbol_c *res, symbol_c *a, symbol_c *b) { msousa@574: if (!VALID_CVALUE(uint64, res)) msousa@574: return; msousa@574: /* Test by pre-condition: If (UINT64_MAX / a) < b => overflow! */ msousa@786: if (0 == GET_CVALUE(uint64, a)) msousa@786: return; // multiplying by 0 will always result in 0, a valid result! msousa@574: if ((UINT64_MAX / GET_CVALUE(uint64, a)) < GET_CVALUE(uint64, b)) msousa@574: SET_OVFLOW(uint64, res); msousa@574: } msousa@574: msousa@572: msousa@572: /* res = a / b */ msousa@572: static void CHECK_OVERFLOW_uint64_DIV(symbol_c *res, symbol_c *a, symbol_c *b) { msousa@574: if (!VALID_CVALUE(uint64, res)) msousa@574: return; msousa@574: if (GET_CVALUE(uint64, b) == 0) /* division by zero! */ msousa@574: SET_OVFLOW(uint64, res); msousa@574: } msousa@574: msousa@572: msousa@572: /* res = a MOD b */ msousa@572: static void CHECK_OVERFLOW_uint64_MOD(symbol_c *res, symbol_c *a, symbol_c *b) { msousa@574: if (!VALID_CVALUE(uint64, res)) msousa@574: return; msousa@574: /* no overflow condition exists, including division by zero, which IEC 61131-3 considers legal for MOD operation! */ msousa@574: if (false) msousa@574: SET_OVFLOW(uint64, res); msousa@574: } msousa@574: msousa@572: msousa@643: /* res = - a */ msousa@643: static void CHECK_OVERFLOW_uint64_NEG(symbol_c *res, symbol_c *a) { msousa@643: /* The only legal operation is res = -0, everything else is an overflow! */ msousa@643: if (VALID_CVALUE(uint64, a) && (GET_CVALUE(uint64, a) != 0)) msousa@643: SET_OVFLOW(uint64, res); msousa@643: } msousa@643: msousa@643: msousa@643: msousa@643: msousa@643: msousa@643: msousa@572: /* res = a + b */ msousa@572: static void CHECK_OVERFLOW_int64_SUM(symbol_c *res, symbol_c *a_ptr, symbol_c *b_ptr) { msousa@574: if (!VALID_CVALUE(int64, res)) msousa@574: return; msousa@572: int64_t a = GET_CVALUE(int64, a_ptr); msousa@572: int64_t b = GET_CVALUE(int64, b_ptr); msousa@574: /* The following test is valid no matter what representation is being used (e.g. two's complement, etc...) */ msousa@574: if (((b > 0) && (a > (INT64_MAX - b))) msousa@574: || ((b < 0) && (a < (INT64_MIN - b)))) msousa@574: SET_OVFLOW(int64, res); msousa@574: } msousa@574: msousa@572: msousa@572: /* res = a - b */ msousa@572: static void CHECK_OVERFLOW_int64_SUB(symbol_c *res, symbol_c *a_ptr, symbol_c *b_ptr) { msousa@574: if (!VALID_CVALUE(int64, res)) msousa@574: return; msousa@572: int64_t a = GET_CVALUE(int64, a_ptr); msousa@572: int64_t b = GET_CVALUE(int64, b_ptr); msousa@574: /* The following test is valid no matter what representation is being used (e.g. two's complement, etc...) */ msousa@574: if (((b > 0) && (a < (INT64_MIN + b))) msousa@574: || ((b < 0) && (a > (INT64_MAX + b)))) msousa@574: SET_OVFLOW(int64, res); msousa@572: } msousa@572: msousa@572: msousa@572: /* res = a * b */ msousa@572: static void CHECK_OVERFLOW_int64_MUL(symbol_c *res, symbol_c *a_ptr, symbol_c *b_ptr) { msousa@574: if (!VALID_CVALUE(int64, res)) msousa@574: return; msousa@572: int64_t a = GET_CVALUE(int64, a_ptr); msousa@572: int64_t b = GET_CVALUE(int64, b_ptr); msousa@574: if ( ( (a > 0) && (b > 0) && (a > (INT64_MAX / b))) msousa@574: || ( (a > 0) && !(b > 0) && (b < (INT64_MIN / a))) msousa@574: || (!(a > 0) && (b > 0) && (a < (INT64_MIN / b))) msousa@574: || (!(a > 0) && !(b > 0) && (a != 0) && (b < (INT64_MAX / a)))) msousa@574: SET_OVFLOW(int64, res); msousa@572: } msousa@572: msousa@572: msousa@572: /* res = a / b */ msousa@572: static void CHECK_OVERFLOW_int64_DIV(symbol_c *res, symbol_c *a_ptr, symbol_c *b_ptr) { msousa@574: if (!VALID_CVALUE(int64, res)) msousa@574: return; msousa@572: int64_t a = GET_CVALUE(int64, a_ptr); msousa@572: int64_t b = GET_CVALUE(int64, b_ptr); msousa@574: if ((b == 0) || ((a == INT64_MIN) && (b == -1))) msousa@574: SET_OVFLOW(int64, res); msousa@572: } msousa@572: msousa@572: msousa@572: /* res = a MOD b */ msousa@572: static void CHECK_OVERFLOW_int64_MOD(symbol_c *res, symbol_c *a_ptr, symbol_c *b_ptr) { msousa@574: if (!VALID_CVALUE(int64, res)) msousa@574: return; msousa@572: int64_t a = GET_CVALUE(int64, a_ptr); msousa@572: int64_t b = GET_CVALUE(int64, b_ptr); msousa@572: /* IEC 61131-3 standard says IN1 MOD IN2 must be equivalent to msousa@572: * IF (IN2 = 0) THEN OUT:=0 ; ELSE OUT:=IN1 - (IN1/IN2)*IN2 ; END_IF msousa@572: * msousa@572: * Note that, when IN1 = INT64_MIN, and IN2 = -1, an overflow occurs in the division, msousa@572: * so although the MOD operation should be OK, acording to the above definition, we actually have an overflow!! msousa@572: * msousa@572: * On the other hand, division by 0 is OK!! msousa@572: */ msousa@574: if ((a == INT64_MIN) && (b == -1)) msousa@574: SET_OVFLOW(int64, res); msousa@572: } msousa@572: msousa@572: msousa@572: /* res = - a */ msousa@643: static void CHECK_OVERFLOW_int64_NEG(symbol_c *res, symbol_c *a) { msousa@574: if (!VALID_CVALUE(int64, res)) msousa@574: return; msousa@643: if (GET_CVALUE(int64, a) == INT64_MIN) msousa@574: SET_OVFLOW(int64, res); msousa@572: } msousa@572: msousa@572: msousa@572: msousa@643: msousa@579: static void CHECK_OVERFLOW_real64(symbol_c *res_ptr) { msousa@579: if (!VALID_CVALUE(real64, res_ptr)) msousa@579: return; msousa@579: real64_t res = GET_CVALUE(real64, res_ptr); msousa@579: /* NaN => underflow, overflow, number is a higher precision format, is a complex number (IEEE standard) */ msousa@579: /* The IEC 61131-3 clearly states in section '2.5.1.5.2 Numerical functions': msousa@579: * "It is an error if the result of evaluation of one of these [numerical] functions exceeds the range of values msousa@579: * specified for the data type of the function output, or if division by zero is attempted." msousa@579: * For this reason, any operation that has as a result a positive or negative inifinity, is also an error! msousa@579: */ msousa@607: if ((isnan(res)) || (res == INFINITY) || (res == -INFINITY)) msousa@579: SET_OVFLOW(real64, res_ptr); msousa@572: } msousa@572: msousa@572: msousa@572: msousa@572: msousa@612: /***********************************************************************/ msousa@612: /***********************************************************************/ msousa@612: /***********************************************************************/ msousa@612: /*** Functions to execute operations on the const values ***/ msousa@612: /***********************************************************************/ msousa@612: /***********************************************************************/ msousa@612: /***********************************************************************/ msousa@612: mjsousa@984: mjsousa@985: /* TODO: FIXME !!!!! mjsousa@985: * The following operation is wrong - it does not handle the comparisons of all possible datatypes correctly. mjsousa@985: * The result of comparig the bool, int64, and uint64 are overwritten by the comparison of the real64 type! mjsousa@985: */ msousa@612: /* static void *handle_cmp(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2, OPERATION) */ msousa@612: #define handle_cmp(symbol, oper1, oper2, operation) { \ msousa@612: if ((NULL == oper1) || (NULL == oper2)) return NULL; \ msousa@643: DO_BINARY_OPER( bool, operation, bool, oper1, oper2); \ msousa@643: DO_BINARY_OPER(uint64, operation, bool, oper1, oper2); \ msousa@643: DO_BINARY_OPER( int64, operation, bool, oper1, oper2); \ msousa@643: DO_BINARY_OPER(real64, operation, bool, oper1, oper2); \ msousa@612: return NULL; \ msousa@612: } msousa@612: msousa@612: msousa@612: /* NOTE: the MOVE standard function is equivalent to the ':=' in ST syntax */ msousa@612: static void *handle_move(symbol_c *to, symbol_c *from) { msousa@612: if (NULL == from) return NULL; msousa@612: to->const_value = from->const_value; msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: /* unary negation (multiply by -1) */ msousa@612: static void *handle_neg(symbol_c *symbol, symbol_c *oper) { msousa@643: if (NULL == oper) return NULL; msousa@643: /* NOTE: The oper may never be an integer/real literal, '-1' and '-2.2' are stored as an neg_integer_c/neg_real_c instead. msousa@643: * Because of this, we MUST NOT handle the INT_MIN special situation that is handled in neg_integer_c visitor! msousa@643: * msousa@643: * VAR v1, v2, v3 : UINT; END_VAR; msousa@643: * v1 = 9223372036854775808 ; (* |INT64_MIN| == -INT64_MIN *) <------ LEGAL msousa@643: * v2 = -(-v1); <------ ILLEGAL (since it -v1 is overflow!) msousa@643: * v2 = -(-9223372036854775808 ); <------ MUST also be ILLEGAL msousa@640: */ msousa@643: DO_UNARY_OPER(uint64, -, oper); CHECK_OVERFLOW_uint64_NEG(symbol, oper); /* handle the uint_v := -0 situation! */ msousa@643: DO_UNARY_OPER( int64, -, oper); CHECK_OVERFLOW_int64_NEG (symbol, oper); msousa@612: DO_UNARY_OPER(real64, -, oper); CHECK_OVERFLOW_real64(symbol); msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: /* unary boolean negation (NOT) */ msousa@612: static void *handle_not(symbol_c *symbol, symbol_c *oper) { msousa@612: if (NULL == oper) return NULL; msousa@612: DO_UNARY_OPER( bool, !, oper); msousa@612: DO_UNARY_OPER(uint64, ~, oper); msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: static void *handle_or (symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { msousa@612: if ((NULL == oper1) || (NULL == oper2)) return NULL; msousa@643: DO_BINARY_OPER( bool, ||, bool , oper1, oper2); msousa@643: DO_BINARY_OPER(uint64, | , uint64, oper1, oper2); msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: static void *handle_xor(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { msousa@612: if ((NULL == oper1) || (NULL == oper2)) return NULL; msousa@643: DO_BINARY_OPER( bool, ^, bool , oper1, oper2); msousa@643: DO_BINARY_OPER(uint64, ^, uint64, oper1, oper2); msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: static void *handle_and(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { msousa@612: if ((NULL == oper1) || (NULL == oper2)) return NULL; msousa@643: DO_BINARY_OPER( bool, &&, bool, oper1, oper2); msousa@643: DO_BINARY_OPER(uint64, & , uint64, oper1, oper2); msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: static void *handle_add(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { msousa@612: if ((NULL == oper1) || (NULL == oper2)) return NULL; msousa@643: DO_BINARY_OPER(uint64, +, uint64, oper1, oper2); CHECK_OVERFLOW_uint64_SUM(symbol, oper1, oper2); msousa@643: DO_BINARY_OPER( int64, +, int64, oper1, oper2); CHECK_OVERFLOW_int64_SUM (symbol, oper1, oper2); msousa@643: DO_BINARY_OPER(real64, +, real64, oper1, oper2); CHECK_OVERFLOW_real64 (symbol); msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: static void *handle_sub(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { msousa@612: if ((NULL == oper1) || (NULL == oper2)) return NULL; msousa@643: DO_BINARY_OPER(uint64, -, uint64, oper1, oper2); CHECK_OVERFLOW_uint64_SUB(symbol, oper1, oper2); msousa@643: DO_BINARY_OPER( int64, -, int64, oper1, oper2); CHECK_OVERFLOW_int64_SUB (symbol, oper1, oper2); msousa@643: DO_BINARY_OPER(real64, -, real64, oper1, oper2); CHECK_OVERFLOW_real64 (symbol); msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: static void *handle_mul(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { msousa@612: if ((NULL == oper1) || (NULL == oper2)) return NULL; msousa@643: DO_BINARY_OPER(uint64, *, uint64, oper1, oper2); CHECK_OVERFLOW_uint64_MUL(symbol, oper1, oper2); msousa@643: DO_BINARY_OPER( int64, *, int64, oper1, oper2); CHECK_OVERFLOW_int64_MUL (symbol, oper1, oper2); msousa@643: DO_BINARY_OPER(real64, *, real64, oper1, oper2); CHECK_OVERFLOW_real64 (symbol); msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: static void *handle_div(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { msousa@612: if ((NULL == oper1) || (NULL == oper2)) return NULL; msousa@643: if (ISZERO_CVALUE(uint64, oper2)) {SET_OVFLOW(uint64, symbol);} else {DO_BINARY_OPER(uint64, /, uint64, oper1, oper2); CHECK_OVERFLOW_uint64_DIV(symbol, oper1, oper2);}; msousa@643: if (ISZERO_CVALUE( int64, oper2)) {SET_OVFLOW( int64, symbol);} else {DO_BINARY_OPER( int64, /, int64, oper1, oper2); CHECK_OVERFLOW_int64_DIV (symbol, oper1, oper2);}; msousa@643: if (ISZERO_CVALUE(real64, oper2)) {SET_OVFLOW(real64, symbol);} else {DO_BINARY_OPER(real64, /, real64, oper1, oper2); CHECK_OVERFLOW_real64(symbol);}; msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: static void *handle_mod(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { msousa@612: if ((NULL == oper1) || (NULL == oper2)) return NULL; msousa@612: /* IEC 61131-3 standard says IN1 MOD IN2 must be equivalent to msousa@612: * IF (IN2 = 0) THEN OUT:=0 ; ELSE OUT:=IN1 - (IN1/IN2)*IN2 ; END_IF msousa@612: * msousa@612: * Note that, when IN1 = INT64_MIN, and IN2 = -1, an overflow occurs in the division, msousa@612: * so although the MOD operation should be OK, acording to the above definition, we actually have an overflow!! msousa@612: */ msousa@643: if (ISZERO_CVALUE(uint64, oper2)) {SET_CVALUE(uint64, symbol, 0);} else {DO_BINARY_OPER(uint64, %, uint64, oper1, oper2); CHECK_OVERFLOW_uint64_MOD(symbol, oper1, oper2);}; msousa@643: if (ISZERO_CVALUE( int64, oper2)) {SET_CVALUE( int64, symbol, 0);} else {DO_BINARY_OPER( int64, %, int64, oper1, oper2); CHECK_OVERFLOW_int64_MOD (symbol, oper1, oper2);}; msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: static void *handle_pow(symbol_c *symbol, symbol_c *oper1, symbol_c *oper2) { msousa@612: /* NOTE: If the const_value in symbol->r_exp is within the limits of both int64 and uint64, then we do both operations. msousa@612: * That is OK, as the result should be identicial (we do create an unnecessary CVALUE variable, but who cares?). msousa@612: * If only one is valid, then that is the oper we will do! msousa@612: */ msousa@612: if (VALID_CVALUE(real64, oper1) && VALID_CVALUE( int64, oper2)) msousa@612: SET_CVALUE(real64, symbol, pow(GET_CVALUE(real64, oper1), GET_CVALUE( int64, oper2))); msousa@612: if (VALID_CVALUE(real64, oper1) && VALID_CVALUE(uint64, oper2)) msousa@612: SET_CVALUE(real64, symbol, pow(GET_CVALUE(real64, oper1), GET_CVALUE(uint64, oper2))); msousa@612: CHECK_OVERFLOW_real64(symbol); msousa@612: return NULL; msousa@612: } msousa@612: mjsousa@984: mjsousa@984: /***********************************************************************/ mjsousa@984: /***********************************************************************/ mjsousa@984: /***********************************************************************/ mjsousa@984: /*** Helper functions for handling IL instruction lists. ***/ mjsousa@984: /***********************************************************************/ mjsousa@984: /***********************************************************************/ mjsousa@984: /***********************************************************************/ mjsousa@984: mjsousa@984: mjsousa@984: /* If the cvalues of all the prev_il_intructions have the same VALID value, then set the local cvalue to that value, otherwise, set it to NONCONST! */ mjsousa@984: #define intersect_prev_CVALUE_(dtype, symbol) { \ mjsousa@984: symbol->const_value._##dtype = symbol->prev_il_instruction[0]->const_value._##dtype; \ mjsousa@984: for (unsigned int i = 1; i < symbol->prev_il_instruction.size(); i++) { \ mjsousa@984: if (!ISEQUAL_CVALUE(dtype, symbol, symbol->prev_il_instruction[i])) \ mjsousa@984: {SET_NONCONST(dtype, symbol); break;} \ mjsousa@984: } \ mjsousa@984: } mjsousa@984: mjsousa@984: static void intersect_prev_cvalues(il_instruction_c *symbol) { mjsousa@984: if (symbol->prev_il_instruction.empty()) mjsousa@984: return; mjsousa@984: intersect_prev_CVALUE_(real64, symbol); mjsousa@984: intersect_prev_CVALUE_(uint64, symbol); mjsousa@984: intersect_prev_CVALUE_( int64, symbol); mjsousa@984: intersect_prev_CVALUE_( bool, symbol); mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: /***********************************************************************/ mjsousa@984: /***********************************************************************/ mjsousa@984: /***********************************************************************/ mjsousa@984: /*** The constant_folding_c ***/ mjsousa@984: /***********************************************************************/ mjsousa@984: /***********************************************************************/ mjsousa@984: /***********************************************************************/ mjsousa@984: mjsousa@984: mjsousa@984: constant_folding_c::constant_folding_c(symbol_c *symbol) { mjsousa@984: error_count = 0; mjsousa@984: warning_found = false; mjsousa@984: current_display_error_level = 0; mjsousa@984: il_operand = NULL; mjsousa@984: prev_il_instruction = NULL; mjsousa@984: mjsousa@984: /* check whether the platform on which the compiler is being run implements IEC 559 floating point data types. */ mjsousa@984: symbol_c null_symbol; mjsousa@984: if (! (std::numeric_limits::is_iec559) ) mjsousa@984: STAGE3_WARNING(&null_symbol, &null_symbol, "The platform running the compiler does not implement IEC 60559 floating point numbers. " mjsousa@984: "Any error and/or warning messages related to overflow/underflow of the result of operations on REAL/LREAL literals " mjsousa@984: "(i.e. constant folding) may themselves be erroneous, although are most probably correct." mjsousa@984: "However, more likely is the possible existance of overflow/underflow errors that are not detected."); mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: constant_folding_c::~constant_folding_c(void) { mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: int constant_folding_c::get_error_count() { mjsousa@984: return error_count; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: /*********************/ mjsousa@984: /* B 1.2 - Constants */ mjsousa@984: /*********************/ mjsousa@984: /******************************/ mjsousa@984: /* B 1.2.1 - Numeric Literals */ mjsousa@984: /******************************/ mjsousa@984: void *constant_folding_c::visit(real_c *symbol) { mjsousa@984: bool overflow; mjsousa@984: SET_CVALUE(real64, symbol, extract_real_value(symbol, &overflow)); mjsousa@984: if (overflow) SET_OVFLOW(real64, symbol); mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: void *constant_folding_c::visit(integer_c *symbol) { mjsousa@984: bool overflow; mjsousa@984: SET_CVALUE( int64, symbol, extract_int64_value (symbol, &overflow)); mjsousa@984: if (overflow) SET_OVFLOW(int64, symbol); mjsousa@984: SET_CVALUE(uint64, symbol, extract_uint64_value(symbol, &overflow)); mjsousa@984: if (overflow) SET_OVFLOW(uint64, symbol); mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: void *constant_folding_c::visit(neg_real_c *symbol) { mjsousa@984: symbol->exp->accept(*this); mjsousa@984: DO_UNARY_OPER(real64, -, symbol->exp); CHECK_OVERFLOW_real64(symbol); mjsousa@984: if (IS_OVFLOW(real64, symbol->exp)) SET_OVFLOW(real64, symbol); mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: /* | '-' integer {$$ = new neg_integer_c($2, locloc(@$));} */ mjsousa@984: void *constant_folding_c::visit(neg_integer_c *symbol) { mjsousa@984: symbol->exp->accept(*this); mjsousa@984: /* Note that due to syntax restrictions, the value of symbol->exp will always be positive. mjsousa@984: * However, the following code does not depend on that restriction. mjsousa@984: */ mjsousa@984: /* The remainder of the code (for example, data type checking) considers the neg_integer_c as a leaf of the mjsousa@984: * abstract syntax tree, and therefore simply ignores the values of neg_integer_c->exp. mjsousa@984: * For this reason only, and in only this situation, we must guarantee that any 'overflow' situation in mjsousa@984: * the cvalue of neg_integer_c->exp is also reflected back to this neg_integer_c symbol. mjsousa@984: * For the rest of the code we do NOT do this, as it would gurantee that a single overflow deep inside mjsousa@984: * an expression would imply that the expression itself would also be set to 'overflow' condition. mjsousa@984: * This in turn would then have the compiler produce a whole load of error messages where they are not wanted! mjsousa@984: */ mjsousa@984: DO_UNARY_OPER(uint64, -, symbol->exp); CHECK_OVERFLOW_uint64_NEG(symbol, symbol->exp); /* handle the uintv := -0 situation */ mjsousa@984: if (IS_OVFLOW(uint64, symbol->exp)) SET_OVFLOW(uint64, symbol); mjsousa@984: DO_UNARY_OPER( int64, -, symbol->exp); CHECK_OVERFLOW_int64_NEG (symbol, symbol->exp); mjsousa@984: if (IS_OVFLOW( int64, symbol->exp)) SET_OVFLOW( int64, symbol); mjsousa@984: /* NOTE 1: INT64_MIN = -(INT64_MAX + 1) ---> assuming two's complement representation!!! mjsousa@984: * NOTE 2: if the user happens to want INT_MIN, that value will first be parsed as a positive integer, before being negated here. mjsousa@984: * However, the positive value cannot be stored inside an int64! So, in this case, we will get the value from the uint64 cvalue. mjsousa@984: * mjsousa@984: * This same situation is usually considered an overflow (check handle_neg() function). However, here we have a special mjsousa@984: * situation. If we do not allow this, then the user would never the able to use the following code: mjsousa@984: * VAR v : LINT; END_VAR mjsousa@984: * v := -9223372036854775809 ; (* - |INT64_MIN| == INT64_MIN *) mjsousa@984: */ mjsousa@984: // if (INT64_MIN == -INT64_MAX - 1) // We do not really need to check that the platform uses two's complement mjsousa@984: if (VALID_CVALUE(uint64, symbol->exp) && (GET_CVALUE(uint64, symbol->exp) == (uint64_t)INT64_MAX+1)) { mjsousa@984: SET_CVALUE(int64, symbol, INT64_MIN); mjsousa@984: } mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: void *constant_folding_c::visit(binary_integer_c *symbol) { mjsousa@984: bool overflow; mjsousa@984: SET_CVALUE( int64, symbol, extract_int64_value (symbol, &overflow)); mjsousa@984: if (overflow) SET_OVFLOW(int64, symbol); mjsousa@984: SET_CVALUE(uint64, symbol, extract_uint64_value(symbol, &overflow)); mjsousa@984: if (overflow) SET_OVFLOW(uint64, symbol); mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: void *constant_folding_c::visit(octal_integer_c *symbol) { mjsousa@984: bool overflow; mjsousa@984: SET_CVALUE( int64, symbol, extract_int64_value (symbol, &overflow)); mjsousa@984: if (overflow) SET_OVFLOW(int64, symbol); mjsousa@984: SET_CVALUE(uint64, symbol, extract_uint64_value(symbol, &overflow)); mjsousa@984: if (overflow) SET_OVFLOW(uint64, symbol); mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: void *constant_folding_c::visit(hex_integer_c *symbol) { mjsousa@984: bool overflow; mjsousa@984: SET_CVALUE( int64, symbol, extract_int64_value (symbol, &overflow)); mjsousa@984: if (overflow) SET_OVFLOW(int64, symbol); mjsousa@984: SET_CVALUE(uint64, symbol, extract_uint64_value(symbol, &overflow)); mjsousa@984: if (overflow) SET_OVFLOW(uint64, symbol); mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: /* mjsousa@984: integer_literal: mjsousa@984: integer_type_name '#' signed_integer {$$ = new integer_literal_c($1, $3, locloc(@$));} mjsousa@984: | integer_type_name '#' binary_integer {$$ = new integer_literal_c($1, $3, locloc(@$));} mjsousa@984: | integer_type_name '#' octal_integer {$$ = new integer_literal_c($1, $3, locloc(@$));} mjsousa@984: | integer_type_name '#' hex_integer {$$ = new integer_literal_c($1, $3, locloc(@$));} mjsousa@984: */ mjsousa@984: // SYM_REF2(integer_literal_c, type, value) mjsousa@984: void *constant_folding_c::visit(integer_literal_c *symbol) { mjsousa@984: symbol->value->accept(*this); mjsousa@984: DO_UNARY_OPER( int64, /* none */, symbol->value); mjsousa@984: DO_UNARY_OPER(uint64, /* none */, symbol->value); mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: void *constant_folding_c::visit(real_literal_c *symbol) { mjsousa@984: symbol->value->accept(*this); mjsousa@984: DO_UNARY_OPER(real64, /* none */, symbol->value); mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: void *constant_folding_c::visit(bit_string_literal_c *symbol) { mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: void *constant_folding_c::visit(boolean_literal_c *symbol) { mjsousa@984: symbol->value->accept(*this); mjsousa@984: DO_UNARY_OPER(bool, /* none */, symbol->value); mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: void *constant_folding_c::visit(boolean_true_c *symbol) { mjsousa@984: SET_CVALUE(bool, symbol, true); mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: void *constant_folding_c::visit(boolean_false_c *symbol) { mjsousa@984: SET_CVALUE(bool, symbol, false); mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: /************************/ mjsousa@984: /* B 1.2.3.1 - Duration */ mjsousa@984: /********* **************/ mjsousa@984: void *constant_folding_c::visit(fixed_point_c *symbol) { mjsousa@984: bool overflow; mjsousa@984: SET_CVALUE(real64, symbol, extract_real_value(symbol, &overflow)); mjsousa@984: if (overflow) SET_OVFLOW(real64, symbol); mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: /****************************************/ mjsousa@984: /* B.2 - Language IL (Instruction List) */ mjsousa@984: /****************************************/ mjsousa@984: /***********************************/ mjsousa@984: /* B 2.1 Instructions and Operands */ mjsousa@984: /***********************************/ mjsousa@984: /* Not needed, since we inherit from iterator_visitor_c */ mjsousa@984: /*| instruction_list il_instruction */ mjsousa@984: // SYM_LIST(instruction_list_c) mjsousa@984: // void *constant_folding_c::visit(instruction_list_c *symbol) {} mjsousa@984: mjsousa@984: /* | label ':' [il_incomplete_instruction] eol_list */ mjsousa@984: // SYM_REF2(il_instruction_c, label, il_instruction) mjsousa@984: // void *visit(instruction_list_c *symbol); mjsousa@984: void *constant_folding_c::visit(il_instruction_c *symbol) { mjsousa@984: if (NULL == symbol->il_instruction) { mjsousa@984: /* This empty/null il_instruction does not change the value of the current/default IL variable. mjsousa@984: * So it inherits the candidate_datatypes from it's previous IL instructions! mjsousa@984: */ mjsousa@984: intersect_prev_cvalues(symbol); mjsousa@984: } else { mjsousa@984: il_instruction_c fake_prev_il_instruction = *symbol; mjsousa@984: intersect_prev_cvalues(&fake_prev_il_instruction); mjsousa@984: mjsousa@984: if (symbol->prev_il_instruction.size() == 0) prev_il_instruction = NULL; mjsousa@984: else prev_il_instruction = &fake_prev_il_instruction; mjsousa@984: symbol->il_instruction->accept(*this); mjsousa@984: prev_il_instruction = NULL; mjsousa@984: mjsousa@984: /* This object has (inherits) the same cvalues as the il_instruction */ mjsousa@984: symbol->const_value = symbol->il_instruction->const_value; mjsousa@984: } mjsousa@984: mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: void *constant_folding_c::visit(il_simple_operation_c *symbol) { mjsousa@984: /* determine the cvalue of the operand */ mjsousa@984: if (NULL != symbol->il_operand) { mjsousa@984: symbol->il_operand->accept(*this); mjsousa@984: } mjsousa@984: /* determine the cvalue resulting from executing the il_operator... */ mjsousa@984: il_operand = symbol->il_operand; mjsousa@984: symbol->il_simple_operator->accept(*this); mjsousa@984: il_operand = NULL; mjsousa@984: /* This object has (inherits) the same cvalues as the il_instruction */ mjsousa@984: symbol->const_value = symbol->il_simple_operator->const_value; mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: /* TODO: handle function invocations... */ mjsousa@984: /* | function_name [il_operand_list] */ mjsousa@984: /* NOTE: The parameters 'called_function_declaration' and 'extensible_param_count' are used to pass data between the stage 3 and stage 4. */ mjsousa@984: // SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;) mjsousa@984: // void *constant_folding_c::visit(il_function_call_c *symbol) {} mjsousa@984: mjsousa@984: mjsousa@984: /* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */ mjsousa@984: // SYM_REF3(il_expression_c, il_expr_operator, il_operand, simple_instr_list); mjsousa@984: void *constant_folding_c::visit(il_expression_c *symbol) { mjsousa@984: symbol_c *prev_il_instruction_backup = prev_il_instruction; mjsousa@984: mjsousa@984: /* Stage2 will insert an artificial (and equivalent) LD to the simple_instr_list if necessary. We can therefore ignore the 'il_operand' entry! */ mjsousa@984: // if (NULL != symbol->il_operand) mjsousa@984: // symbol->il_operand->accept(*this); mjsousa@984: mjsousa@984: if(symbol->simple_instr_list != NULL) mjsousa@984: symbol->simple_instr_list->accept(*this); mjsousa@984: mjsousa@984: /* Now do the operation, */ mjsousa@984: il_operand = symbol->simple_instr_list; mjsousa@984: prev_il_instruction = prev_il_instruction_backup; mjsousa@984: symbol->il_expr_operator->accept(*this); mjsousa@984: il_operand = NULL; mjsousa@984: mjsousa@984: /* This object has (inherits) the same cvalues as the il_instruction */ mjsousa@984: symbol->const_value = symbol->il_expr_operator->const_value; mjsousa@984: mjsousa@984: /* Since stage2 will insert an artificial (and equivalent) LD to the simple_instr_list when an 'il_operand' exists, we know mjsousa@984: * that if (symbol->il_operand != NULL), then the first IL instruction in the simple_instr_list will be the equivalent and artificial mjsousa@984: * 'LD ' IL instruction. mjsousa@984: * Just to be cosistent, we will copy the constant info back into the il_operand, even though this should not be necessary! mjsousa@984: */ mjsousa@984: if ((NULL != symbol->il_operand) && ((NULL == symbol->simple_instr_list) || (0 == ((list_c *)symbol->simple_instr_list)->n))) ERROR; // stage2 is not behaving as we expect it to! mjsousa@984: if (NULL != symbol->il_operand) mjsousa@984: symbol->il_operand->const_value = ((list_c *)symbol->simple_instr_list)->elements[0]->const_value; mjsousa@984: mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: void *constant_folding_c::visit(il_jump_operation_c *symbol) { mjsousa@984: /* recursive call to fill const values... */ mjsousa@984: il_operand = NULL; mjsousa@984: symbol->il_jump_operator->accept(*this); mjsousa@984: il_operand = NULL; mjsousa@984: /* This object has (inherits) the same cvalues as the il_jump_operator */ mjsousa@984: symbol->const_value = symbol->il_jump_operator->const_value; mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: /* FB calls leave the value in the accumulator unchanged */ mjsousa@984: /* il_call_operator prev_declared_fb_name mjsousa@984: * | il_call_operator prev_declared_fb_name '(' ')' mjsousa@984: * | il_call_operator prev_declared_fb_name '(' eol_list ')' mjsousa@984: * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' mjsousa@984: * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' mjsousa@984: */ mjsousa@984: /* NOTE: The parameter 'called_fb_declaration'is used to pass data between stage 3 and stage4 (although currently it is not used in stage 4 */ mjsousa@984: // SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list, symbol_c *called_fb_declaration) mjsousa@984: void *constant_folding_c::visit(il_fb_call_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: mjsousa@984: mjsousa@984: /* TODO: handle function invocations... */ mjsousa@984: /* | function_name '(' eol_list [il_param_list] ')' */ mjsousa@984: /* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4. */ mjsousa@984: // SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;) mjsousa@984: // void *constant_folding_c::visit(il_formal_funct_call_c *symbol) {return NULL;} mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: /* Not needed, since we inherit from iterator_visitor_c */ mjsousa@984: // void *constant_folding_c::visit(il_operand_list_c *symbol); mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: /* | simple_instr_list il_simple_instruction */ mjsousa@984: /* This object is referenced by il_expression_c objects */ mjsousa@984: void *constant_folding_c::visit(simple_instr_list_c *symbol) { mjsousa@984: if (symbol->n <= 0) mjsousa@984: return NULL; /* List is empty! Nothing to do. */ mjsousa@984: mjsousa@984: for(int i = 0; i < symbol->n; i++) mjsousa@984: symbol->elements[i]->accept(*this); mjsousa@984: mjsousa@984: /* This object has (inherits) the same cvalues as the il_jump_operator */ mjsousa@984: symbol->const_value = symbol->elements[symbol->n-1]->const_value; mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: // SYM_REF1(il_simple_instruction_c, il_simple_instruction, symbol_c *prev_il_instruction;) mjsousa@984: void *constant_folding_c::visit(il_simple_instruction_c *symbol) { mjsousa@984: if (symbol->prev_il_instruction.size() > 1) ERROR; /* There should be no labeled insructions inside an IL expression! */ mjsousa@984: if (symbol->prev_il_instruction.size() == 0) prev_il_instruction = NULL; mjsousa@984: else prev_il_instruction = symbol->prev_il_instruction[0]; mjsousa@984: symbol->il_simple_instruction->accept(*this); mjsousa@984: prev_il_instruction = NULL; mjsousa@984: mjsousa@984: /* This object has (inherits) the same cvalues as the il_jump_operator */ mjsousa@984: symbol->const_value = symbol->il_simple_instruction->const_value; mjsousa@984: return NULL; mjsousa@984: } mjsousa@984: mjsousa@984: mjsousa@984: /* mjsousa@984: void *visit(il_param_list_c *symbol); mjsousa@984: void *visit(il_param_assignment_c *symbol); mjsousa@984: void *visit(il_param_out_assignment_c *symbol); mjsousa@984: */ mjsousa@984: mjsousa@984: mjsousa@984: /*******************/ mjsousa@984: /* B 2.2 Operators */ mjsousa@984: /*******************/ mjsousa@984: void *constant_folding_c::visit( LD_operator_c *symbol) {return handle_move(symbol, il_operand);} mjsousa@984: void *constant_folding_c::visit( LDN_operator_c *symbol) {return handle_not (symbol, il_operand);} mjsousa@984: mjsousa@984: /* NOTE: we are implementing a constant folding algorithm, not a constant propagation algorithm. mjsousa@984: * For the constant propagation algorithm, the correct implementation of ST(N)_operator_c would be... mjsousa@984: */ mjsousa@984: //void *constant_folding_c::visit( ST_operator_c *symbol) {return handle_move(il_operand, symbol);} mjsousa@984: //void *constant_folding_c::visit( STN_operator_c *symbol) {return handle_not (il_operand, symbol);} mjsousa@984: void *constant_folding_c::visit( ST_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( STN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: mjsousa@984: /* NOTE: the standard allows syntax in which the NOT operator is followed by an optional mjsousa@984: * NOT [] mjsousa@984: * However, it does not define the semantic of the NOT operation when the is specified. mjsousa@984: * We therefore consider it an error if an il_operand is specified! This error will be caught elsewhere! mjsousa@984: */ mjsousa@984: void *constant_folding_c::visit( NOT_operator_c *symbol) {return handle_not(symbol, prev_il_instruction);} mjsousa@984: mjsousa@984: /* NOTE: Since we are only implementing a constant folding algorithm, and not a constant propagation algorithm, mjsousa@984: * the following IL instructions do not change/set the value of the il_operand! mjsousa@984: */ mjsousa@984: void *constant_folding_c::visit( S_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( R_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: mjsousa@984: /* FB calls leave the value in the accumulator unchanged */ mjsousa@984: void *constant_folding_c::visit( S1_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( R1_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( CLK_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( CU_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( CD_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( PV_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( IN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( PT_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: mjsousa@984: void *constant_folding_c::visit( AND_operator_c *symbol) {return handle_and (symbol, prev_il_instruction, il_operand);} mjsousa@984: void *constant_folding_c::visit( OR_operator_c *symbol) {return handle_or (symbol, prev_il_instruction, il_operand);} mjsousa@984: void *constant_folding_c::visit( XOR_operator_c *symbol) {return handle_xor (symbol, prev_il_instruction, il_operand);} mjsousa@984: void *constant_folding_c::visit( ANDN_operator_c *symbol) { handle_and (symbol, prev_il_instruction, il_operand); return handle_not(symbol, symbol);} mjsousa@984: void *constant_folding_c::visit( ORN_operator_c *symbol) { handle_or (symbol, prev_il_instruction, il_operand); return handle_not(symbol, symbol);} mjsousa@984: void *constant_folding_c::visit( XORN_operator_c *symbol) { handle_xor (symbol, prev_il_instruction, il_operand); return handle_not(symbol, symbol);} mjsousa@984: mjsousa@984: void *constant_folding_c::visit( ADD_operator_c *symbol) {return handle_add (symbol, prev_il_instruction, il_operand);} mjsousa@984: void *constant_folding_c::visit( SUB_operator_c *symbol) {return handle_sub (symbol, prev_il_instruction, il_operand);} mjsousa@984: void *constant_folding_c::visit( MUL_operator_c *symbol) {return handle_mul (symbol, prev_il_instruction, il_operand);} mjsousa@984: void *constant_folding_c::visit( DIV_operator_c *symbol) {return handle_div (symbol, prev_il_instruction, il_operand);} mjsousa@984: void *constant_folding_c::visit( MOD_operator_c *symbol) {return handle_mod (symbol, prev_il_instruction, il_operand);} mjsousa@984: mjsousa@984: void *constant_folding_c::visit( GT_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, > );} mjsousa@984: void *constant_folding_c::visit( GE_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, >=);} mjsousa@984: void *constant_folding_c::visit( EQ_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, ==);} mjsousa@984: void *constant_folding_c::visit( LT_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, < );} mjsousa@984: void *constant_folding_c::visit( LE_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, <=);} mjsousa@984: void *constant_folding_c::visit( NE_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, !=);} mjsousa@984: mjsousa@984: void *constant_folding_c::visit( CAL_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( RET_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( JMP_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( CALC_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit(CALCN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( RETC_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit(RETCN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit( JMPC_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: void *constant_folding_c::visit(JMPCN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: /***************************************/ mjsousa@984: /* B.3 - Language ST (Structured Text) */ mjsousa@984: /***************************************/ mjsousa@984: /***********************/ mjsousa@984: /* B 3.1 - Expressions */ mjsousa@984: /***********************/ mjsousa@984: void *constant_folding_c::visit( or_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_or (symbol, symbol->l_exp, symbol->r_exp);} mjsousa@984: void *constant_folding_c::visit( xor_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_xor(symbol, symbol->l_exp, symbol->r_exp);} mjsousa@984: void *constant_folding_c::visit( and_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_and(symbol, symbol->l_exp, symbol->r_exp);} mjsousa@984: mjsousa@984: void *constant_folding_c::visit( equ_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); handle_cmp (symbol, symbol->l_exp, symbol->r_exp, ==);} mjsousa@984: void *constant_folding_c::visit(notequ_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); handle_cmp (symbol, symbol->l_exp, symbol->r_exp, !=);} mjsousa@984: void *constant_folding_c::visit( lt_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); handle_cmp (symbol, symbol->l_exp, symbol->r_exp, < );} mjsousa@984: void *constant_folding_c::visit( gt_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); handle_cmp (symbol, symbol->l_exp, symbol->r_exp, > );} mjsousa@984: void *constant_folding_c::visit( le_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); handle_cmp (symbol, symbol->l_exp, symbol->r_exp, <=);} mjsousa@984: void *constant_folding_c::visit( ge_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); handle_cmp (symbol, symbol->l_exp, symbol->r_exp, >=);} mjsousa@984: mjsousa@984: void *constant_folding_c::visit( add_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_add(symbol, symbol->l_exp, symbol->r_exp);} mjsousa@984: void *constant_folding_c::visit( sub_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_sub(symbol, symbol->l_exp, symbol->r_exp);} mjsousa@984: void *constant_folding_c::visit( mul_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_mul(symbol, symbol->l_exp, symbol->r_exp);} mjsousa@984: void *constant_folding_c::visit( div_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_div(symbol, symbol->l_exp, symbol->r_exp);} mjsousa@984: void *constant_folding_c::visit( mod_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_mod(symbol, symbol->l_exp, symbol->r_exp);} mjsousa@984: void *constant_folding_c::visit( power_expression_c *symbol) {symbol->l_exp->accept(*this); symbol->r_exp->accept(*this); return handle_pow(symbol, symbol->l_exp, symbol->r_exp);} mjsousa@984: mjsousa@984: void *constant_folding_c::visit( neg_expression_c *symbol) {symbol-> exp->accept(*this); return handle_neg(symbol, symbol->exp);} mjsousa@984: void *constant_folding_c::visit( not_expression_c *symbol) {symbol-> exp->accept(*this); return handle_not(symbol, symbol->exp);} mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: mjsousa@984: /***********************************************************************/ mjsousa@984: /***********************************************************************/ mjsousa@984: /***********************************************************************/ mjsousa@984: /*** The constant_propagation_c ***/ mjsousa@984: /***********************************************************************/ mjsousa@984: /***********************************************************************/ mjsousa@984: /***********************************************************************/ mjsousa@984: mjsousa@984: mjsousa@984: constant_propagation_c::constant_propagation_c(symbol_c *symbol) mjsousa@984: : constant_folding_c(symbol) { mjsousa@984: current_resource = NULL; mjsousa@984: current_configuration = NULL; mjsousa@984: fixed_init_value_ = false; mjsousa@984: function_pou_ = false; mjsousa@988: values = NULL; mjsousa@988: } mjsousa@984: mjsousa@984: mjsousa@984: constant_propagation_c::~constant_propagation_c(void) {} mjsousa@984: mjsousa@984: mjsousa@984: static constant_propagation_c::map_values_t inner_left_join_values(constant_propagation_c::map_values_t m1, constant_propagation_c::map_values_t m2) { mjsousa@984: constant_propagation_c::map_values_t::const_iterator itr; mjsousa@984: constant_propagation_c::map_values_t ret; conti@788: conti@788: itr = m1.begin(); conti@788: for ( ; itr != m1.end(); ++itr) { conti@788: std::string name = itr->first; mjsousa@965: const_value_c value; conti@788: conti@788: if (m2.count(name) > 0) { mjsousa@965: const_value_c c1 = itr->second; mjsousa@965: const_value_c c2 = m2[name]; conti@788: COMPUTE_MEET_SEMILATTICE (real64, c1, c2, value); conti@788: COMPUTE_MEET_SEMILATTICE (uint64, c1, c2, value); conti@788: COMPUTE_MEET_SEMILATTICE ( int64, c1, c2, value); conti@788: COMPUTE_MEET_SEMILATTICE ( bool, c1, c2, value); conti@788: } else conti@788: value = m1[name]; conti@788: ret[name] = value; conti@788: } conti@788: conti@788: return ret; conti@788: } conti@788: mjsousa@982: /***************************/ mjsousa@982: /* B 0 - Programming Model */ mjsousa@982: /***************************/ mjsousa@982: /* 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 */ mjsousa@982: // SYM_LIST(library_c, enumvalue_symtable_t enumvalue_symtable;) mjsousa@982: /* The constant propagation algorithm propagates the constant values to all the locations mjsousa@982: * in the code where expressions can be determined to have a fixed value. mjsousa@982: * e.g.: var1 := 99; mjsousa@982: * var2[var1] := 42; <-- with constant propagation, we know we are accessing var2[99] here! mjsousa@982: * mjsousa@982: * An important question in constant propagation is whether we should use the values of mjsousa@982: * VAR_GLOBAL CONSTANT variables as a constant. The problem here is that the mjsousa@982: * constant value of each global variable can only be declared in a configuration, mjsousa@982: * but a POU may eventually be used from different configurations. mjsousa@982: * mjsousa@982: * For example: mjsousa@982: * CONFIGURATION conf1 mjsousa@982: * VAR_GLOBAL CONSTANT global_const : INT := 42; END_VAR mjsousa@982: * PROGRAM prog1 WITH TaskX : Prog1_t; mjsousa@982: * END_CONFIGURATION mjsousa@982: * mjsousa@982: * CONFIGURATION conf2 mjsousa@982: * VAR_GLOBAL CONSTANT global_const : INT := 18; END_VAR mjsousa@982: * PROGRAM prog1 WITH TaskX : Prog1_t; mjsousa@982: * END_CONFIGURATION mjsousa@982: * mjsousa@982: * PROGRAM Prog1_t mjsousa@982: * VAR_EXTERN COSNTANT global_const : INT; END_VAR <--- NOTE: 61131-3 syntax does not allow const value to be set here! mjsousa@982: * VAR .... END_VAR mjsousa@982: * array_var[global_const] := 0; mjsousa@982: * END_PROGRAM mjsousa@982: * mjsousa@982: * Considering the above code, where Prog1_t is instanciated in both conf1 and conf2, mjsousa@982: * and therefore where the 'constant' var_global may actually take two possible values mjsousa@982: * (42 and 18), it would be incorrect to consider the var_global variable a constant in mjsousa@982: * the constant propagation algorithm. mjsousa@982: * This means that when doing constant propagation of the Prog1_t POU, we should consider mjsousa@982: * all global variables (including the 'constant') as non-constant - this actually makes mjsousa@982: * the algorithm much easier!). mjsousa@982: * mjsousa@982: * However, matiec has implemented an extension where we allow arrays whose size may be mjsousa@982: * defined using symbolic variables, whose value can be determined at compile time mjsousa@982: * (typically VAR CONSTANT variables). To really become usefull, this extension must allow mjsousa@982: * the same var_global constant to be used in declaring arrays in both configuration as mjsousa@982: * well as in some other POUs. mjsousa@982: * mjsousa@982: * e.g.: mjsousa@982: * CONFIGURATION conf2 mjsousa@982: * VAR_GLOBAL CONSTANT global_const : INT := 18; END_VAR mjsousa@982: * VAR_GLOBAL global_array : ARRAY [1..global_const] OF INT; END_VAR mjsousa@982: * PROGRAM prog1 WITH TaskX : Prog1_t; mjsousa@982: * END_CONFIGURATION mjsousa@982: * mjsousa@982: * PROGRAM Prog1_t mjsousa@982: * VAR_EXTERN COSNTANT global_const : INT; END_VAR <--- NOTE: 61131-3 syntax does not allow const value to be set here! mjsousa@982: * VAR_EXTERN global_array : ARRAY [1..global_const] OF INT; END_VAR mjsousa@982: * VAR .... END_VAR mjsousa@982: * global_array[global_const] := 0; mjsousa@982: * END_PROGRAM mjsousa@982: * mjsousa@982: * The above requirement means that we MUST therefore do the propagation of constant values mjsousa@982: * from a var_global constant to the corresponding var_external in the POUs. mjsousa@982: * This implies 3 things: mjsousa@982: * 1) We must detect when two configurations use the same POU and set distinct values mjsousa@982: * to the same var_global - we emit a warning/error (which should we emit?) mjsousa@982: * 2) Even if each POU is used by only one configuration, we must warn the user that mjsousa@982: * the generated code may not be safely turned into a library to be later linked mjsousa@982: * to other configurations mjsousa@982: * 3) To do the propagation of the var_global const value to the var_external, mjsousa@982: * we must first analyse all the configurations in the library. mjsousa@982: * - When analysing a configuration, we store all the constant values in the mjsousa@982: * global_values[] map, and call from within the configuration context all the POUs mjsousa@982: * instantiated inside this configuration (basically, we do the constant propagation mjsousa@982: * of each POU with the global_values[] map preloaded with all the constant values). mjsousa@982: * This means that we may evetually do constant folding of the same POU type multiple mjsousa@982: * times (if it is instantiated multiple times in the same configuration, or once mjsousa@982: * in several configurations). This should not be a problem because the constant mjsousa@982: * propagation algorithm is idem-potent (assuming the same constant values in the mjsousa@982: * beginning), and we can use these multiple calls to the same POU to detect if mjsousa@982: * the situation mentioned in (1) is ocurring. mjsousa@989: * Note too that FBs may also include VAR_EXTERN CONSTANT variables, which must also mjsousa@989: * get their constant value from the corresponding global variable (declared in the mjsousa@989: * configuration, program and FBs currently in scope). For this reason, when a FB mjsousa@989: * variable is instantiated inside a configuration, program or FB, we must recursively mjsousa@989: * visit the FB type declaration! mjsousa@982: * - After analysing all the configurations, we analyse all the other POUs that have mjsousa@982: * not yet been called (because they are not instantiated directly from within mjsousa@982: * any configuration - e.g. functions, and most FBs!). mjsousa@982: * It is for this reason (3) why we have the two loops on the following code! mjsousa@982: */ mjsousa@984: void *constant_propagation_c::visit(library_c *symbol) { mjsousa@982: int i; mjsousa@982: mjsousa@982: for (i = 0; i < symbol->n; i++) { mjsousa@982: // first analyse the configurations mjsousa@982: if (NULL != dynamic_cast(symbol->elements[i])) mjsousa@982: symbol->elements[i]->accept(*this); mjsousa@982: } mjsousa@982: mjsousa@982: for (i = 0; i < symbol->n; i++) { mjsousa@982: /* NOTE: we will be re-visiting all the POUs that were already called indirectly through the mjsousa@982: * visit(program_configuration_c) of vist(fb_task_c) visitors during the previous for mjsousa@982: * loop. However, this is OK as the only difference would be how the VAR_EXTERN are handled, mjsousa@982: * and that is taken care of in the visit(external_declaration_c) visitor! mjsousa@982: */ mjsousa@982: if (NULL == dynamic_cast(symbol->elements[i])) mjsousa@982: symbol->elements[i]->accept(*this); mjsousa@982: } mjsousa@982: mjsousa@982: return NULL; mjsousa@982: } mjsousa@982: msousa@612: conti@774: /*********************/ conti@774: /* B 1.4 - Variables */ conti@774: /*********************/ mjsousa@981: #if DO_CONSTANT_PROPAGATION__ mjsousa@984: void *constant_propagation_c::visit(symbolic_variable_c *symbol) { mjsousa@963: std::string varName = get_var_name_c::get_name(symbol->var_name)->value; mjsousa@988: if (values->count(varName) > 0) mjsousa@988: symbol->const_value = (*values)[varName]; conti@774: return NULL; conti@774: } mjsousa@981: #endif // DO_CONSTANT_PROPAGATION__ conti@774: mjsousa@984: void *constant_propagation_c::visit(symbolic_constant_c *symbol) { mjsousa@963: std::string varName = get_var_name_c::get_name(symbol->var_name)->value; mjsousa@988: if (values->count(varName) > 0) mjsousa@988: symbol->const_value = (*values)[varName]; mjsousa@963: return NULL; mjsousa@963: } mjsousa@963: mjsousa@963: mjsousa@963: /******************************************/ mjsousa@963: /* B 1.4.3 - Declaration & Initialisation */ mjsousa@963: /******************************************/ mjsousa@967: mjsousa@984: void *constant_propagation_c::handle_var_decl(symbol_c *var_list, bool fixed_init_value) { mjsousa@967: fixed_init_value_ = fixed_init_value; mjsousa@967: var_list->accept(*this); mjsousa@967: fixed_init_value_ = false; mjsousa@967: return NULL; mjsousa@967: } mjsousa@967: mjsousa@990: mjsousa@990: #include // std::find mjsousa@988: void *constant_propagation_c::handle_var_list_decl(symbol_c *var_list, symbol_c *type_decl, bool is_global_var) { mjsousa@964: type_decl->accept(*this); // Do constant folding of the initial value, and literals in subranges! (we will probably be doing this multiple times for the same init value, but this is safe as the cvalue is idem-potent) mjsousa@963: symbol_c *init_value = type_initial_value_c::get(type_decl); mjsousa@989: mjsousa@989: /* There are two main possibilities here: either we are instantiating FBs, or some other variable. mjsousa@989: * (1) if it is a FB, we must recursively visit the FB type declaration, to let any VAR_EXTERN mjsousa@989: * variables there get their initial value from the current var_global_values[] map!! mjsousa@989: * (2) if it is a normal variable, we will store the initial value of that variable in the values[] map. mjsousa@989: * (and also store it in the var_global_values[] map is it is a VAR_GLOBAL variable!) mjsousa@989: */ mjsousa@989: mjsousa@989: /* Check whether we have situation (1) mentioned above! */ mjsousa@989: /* find the possible declaration (i.e. the datatype) of the possible FB being instantiated */ mjsousa@989: // NOTE: we do not use symbol->datatype so this const propagation algorithm will not depend on the fill/narrow datatypes algorithm! mjsousa@989: function_block_type_symtable_t::iterator itr = function_block_type_symtable.end(); // assume not a FB! mjsousa@989: symbol_c *type_symbol = spec_init_sperator_c::get_spec(type_decl); mjsousa@989: token_c *type_name = dynamic_cast(type_symbol); mjsousa@989: if (type_name != NULL) mjsousa@989: itr = function_block_type_symtable.find(type_name); mjsousa@989: if (itr != function_block_type_symtable.end()) { mjsousa@989: // Handle the situation (1) mentioned above, i.e. handle the instantiation of FBs. mjsousa@989: // ------------------------------------------------------------------------------- mjsousa@989: // Remmeber that in this case we will recursively visit the FB type declaration!! mjsousa@989: function_block_declaration_c *fb_type = itr->second; mjsousa@989: if (NULL == fb_type) ERROR; // syntax parsing should not allow this! mjsousa@990: // WARNING: Before calling fb_type->accept(*this), we must first determine whether we are already currently visiting this exact mjsousa@990: // same FB declaration (possible with -p option), so we do not get into an infinite loop!! mjsousa@990: // NOTE: We use the std::find() standard algorithm, since the std::stack and std::deque do not have the find() member function. mjsousa@990: // We could alternatively use a std::set instead of std::deque, but then it would not be evident that insertion and deletion mjsousa@990: // of fb_types follows a push() and pop() algorithm typical of stacks. mjsousa@990: if (std::find(fbs_currently_being_visited.begin(), fbs_currently_being_visited.end(), fb_type) == fbs_currently_being_visited.end()) { mjsousa@990: // The fb_type is not in the fbs_currently_being_visited stack, so we push it onto the stack, and then visit it!! mjsousa@990: fbs_currently_being_visited.push_back(fb_type); mjsousa@990: fb_type->accept(*this); mjsousa@990: fbs_currently_being_visited.pop_back(); mjsousa@990: } mjsousa@989: return NULL; mjsousa@989: } mjsousa@989: mjsousa@989: // Handle the situation (2) mentioned above, i.e. handle the instantiation of non-FB variables. mjsousa@989: // -------------------------------------------------------------------------------------------- mjsousa@989: if (NULL == init_value) {return NULL;} // this is some datatype for which no initial value exists! Do nothing and return. mjsousa@968: init_value->accept(*this); // necessary when handling default initial values, that were not constant folded in the call type_decl->accept(*this) mjsousa@968: mjsousa@963: list_c *list = dynamic_cast(var_list); mjsousa@963: if (NULL == list) ERROR; mjsousa@963: for (int i = 0; i < list->n; i++) { mjsousa@963: token_c *var_name = dynamic_cast(list->elements[i]); mjsousa@963: if (NULL == var_name) { mjsousa@963: if (NULL != dynamic_cast(list->elements[i])) mjsousa@963: continue; // this is an extensible standard function. Ignore this variable, and continue! mjsousa@967: // debug_c::print(list->elements[i]); mjsousa@963: ERROR; mjsousa@963: } mjsousa@963: list->elements[i]->const_value = init_value->const_value; mjsousa@967: if (fixed_init_value_) { mjsousa@988: (*values)[var_name->value] = init_value->const_value; mjsousa@988: if (is_global_var) mjsousa@988: // also store it in the var_global_values map!! mjsousa@988: // Notice that global variables are also placed in the values map!! mjsousa@988: var_global_values[var_name->value] = init_value->const_value; mjsousa@967: } mjsousa@963: } mjsousa@963: return NULL; mjsousa@963: } mjsousa@963: mjsousa@963: //SYM_REF0(constant_option_c) // Not needed! mjsousa@963: //SYM_REF0(retain_option_c) // Not needed! mjsousa@963: //SYM_REF0(non_retain_option_c) // Not needed! mjsousa@984: bool constant_propagation_c::is_constant(symbol_c *option) {return (NULL != dynamic_cast(option));} mjsousa@984: bool constant_propagation_c::is_retain (symbol_c *option) {return (NULL != dynamic_cast< retain_option_c *>(option));} mjsousa@967: mjsousa@967: /* | var1_list ',' variable_name */ mjsousa@967: //SYM_LIST(var1_list_c) // Not needed! mjsousa@963: mjsousa@963: /* spec_init is one of the following... mjsousa@963: * simple_spec_init_c * mjsousa@963: * subrange_spec_init_c * mjsousa@963: * enumerated_spec_init_c * mjsousa@963: */ mjsousa@963: // SYM_REF2(var1_init_decl_c, var1_list, spec_init) mjsousa@984: void *constant_propagation_c::visit(var1_init_decl_c *symbol) {return handle_var_list_decl(symbol->var1_list, symbol->spec_init);} mjsousa@963: mjsousa@963: /* | [var1_list ','] variable_name integer '..' */ mjsousa@963: /* NOTE: This is an extension to the standard!!! */ mjsousa@963: //SYM_REF2(extensible_input_parameter_c, var_name, first_index) // Not needed! mjsousa@967: mjsousa@963: /* var1_list ':' array_spec_init */ mjsousa@963: //SYM_REF2(array_var_init_decl_c, var1_list, array_spec_init) // We do not yet handle arrays! mjsousa@967: mjsousa@963: /* var1_list ':' initialized_structure */ mjsousa@963: //SYM_REF2(structured_var_init_decl_c, var1_list, initialized_structure) // We do not yet handle structures! mjsousa@967: mjsousa@963: /* fb_name_list ':' function_block_type_name ASSIGN structure_initialization */ mjsousa@988: //SYM_REF2(fb_name_decl_c, fb_name_list, fb_spec_init) mjsousa@988: void *constant_propagation_c::visit(fb_name_decl_c *symbol) { mjsousa@988: /* A FB has been instantiated inside the POU currently being analysed. We must therefore visit this FB's type declaration mjsousa@989: * and give the VAR_EXTERNs in that FB a chance to get the const values from the global variables currently in scope! mjsousa@988: */ mjsousa@989: // NOTE: The generic handle_var_list_decl() can handle the above situation, so we simply call it! mjsousa@989: // NOTE: The handle_var_list_decl() should not be needing the fb_name_list to do the above, so we call mjsousa@989: // it with NULL to highlight this fact! mjsousa@989: return handle_var_list_decl(NULL, symbol->fb_spec_init); mjsousa@988: } mjsousa@988: mjsousa@967: mjsousa@963: /* fb_name_list ',' fb_name */ mjsousa@963: //SYM_LIST(fb_name_list_c) // Not needed! mjsousa@967: mjsousa@963: /* VAR_INPUT [option] input_declaration_list END_VAR */ mjsousa@963: /* option -> the RETAIN/NON_RETAIN/ directive... */ mjsousa@964: //SYM_REF3(input_declarations_c, option, input_declaration_list, method) // Not needed since we inherit from iterator_visitor_c! mjsousa@967: // NOTE: Input variables can take any initial value, so we can not set the const_value annotation => we set fixed_init_value to false !!! mjsousa@964: // We must still visit it iteratively, to set the const_value of all literals in the type declarations. mjsousa@984: void *constant_propagation_c::visit(input_declarations_c *symbol) {return handle_var_decl(symbol->input_declaration_list, false);} mjsousa@967: mjsousa@963: /* helper symbol for input_declarations */ mjsousa@963: //SYM_LIST(input_declaration_list_c) // Not needed! mjsousa@967: mjsousa@963: /* VAR_OUTPUT [RETAIN | NON_RETAIN] var_init_decl_list END_VAR */ mjsousa@963: /* option -> may be NULL ! */ mjsousa@967: //SYM_REF3(output_declarations_c, option, var_init_decl_list, method) mjsousa@984: void *constant_propagation_c::visit(output_declarations_c *symbol) {return handle_var_decl(symbol->var_init_decl_list, !is_retain(symbol->option) && function_pou_);} mjsousa@963: mjsousa@963: /* VAR_IN_OUT var_declaration_list END_VAR */ mjsousa@967: //SYM_REF1(input_output_declarations_c, var_declaration_list) mjsousa@967: // NOTE: Input variables can take any initial value, so we can not set the const_value annotation => we set fixed_init_value to false !!! mjsousa@964: // We must still visit it iteratively, to set the const_value of all literals in the type declarations. mjsousa@984: void *constant_propagation_c::visit(input_output_declarations_c *symbol) {return handle_var_decl(symbol->var_declaration_list, false);} mjsousa@967: mjsousa@963: /* helper symbol for input_output_declarations */ mjsousa@963: /* var_declaration_list var_declaration ';' */ mjsousa@963: //SYM_LIST(var_declaration_list_c) // Not needed since we inherit from iterator_visitor_c! mjsousa@967: mjsousa@963: /* var1_list ':' array_specification */ mjsousa@963: //SYM_REF2(array_var_declaration_c, var1_list, array_specification) // We do not yet handle arrays! mjsousa@967: mjsousa@963: /* var1_list ':' structure_type_name */ mjsousa@963: //SYM_REF2(structured_var_declaration_c, var1_list, structure_type_name) // We do not yet handle structures! mjsousa@967: mjsousa@963: /* VAR [CONSTANT] var_init_decl_list END_VAR */ mjsousa@963: /* option -> may be NULL ! */ mjsousa@967: //SYM_REF2(var_declarations_c, option, var_init_decl_list) mjsousa@984: void *constant_propagation_c::visit(var_declarations_c *symbol) {return handle_var_decl(symbol->var_init_decl_list, false);} mjsousa@963: mjsousa@963: /* VAR RETAIN var_init_decl_list END_VAR */ mjsousa@964: //SYM_REF1(retentive_var_declarations_c, var_init_decl_list) // Not needed since we inherit from iterator_visitor_c! mjsousa@967: // NOTE: Retentive variables can take any initial value, so we can not set the const_value annotation => we set fixed_init_value to false !!! mjsousa@964: // We must still visit it iteratively, to set the const_value of all literals in the type declarations. mjsousa@984: void *constant_propagation_c::visit(retentive_var_declarations_c *symbol) {return handle_var_decl(symbol->var_init_decl_list, false);} mjsousa@967: mjsousa@967: #if 0 mjsousa@967: // TODO mjsousa@963: /* VAR [CONSTANT|RETAIN|NON_RETAIN] located_var_decl_list END_VAR */ mjsousa@963: /* option -> may be NULL ! */ mjsousa@963: SYM_REF2(located_var_declarations_c, option, located_var_decl_list) mjsousa@963: /* helper symbol for located_var_declarations */ mjsousa@963: /* located_var_decl_list located_var_decl ';' */ mjsousa@963: SYM_LIST(located_var_decl_list_c) mjsousa@963: /* [variable_name] location ':' located_var_spec_init */ mjsousa@963: /* variable_name -> may be NULL ! */ mjsousa@963: SYM_REF3(located_var_decl_c, variable_name, location, located_var_spec_init) mjsousa@963: #endif mjsousa@963: mjsousa@963: /*| VAR_EXTERNAL [CONSTANT] external_declaration_list END_VAR */ mjsousa@963: /* option -> may be NULL ! */ mjsousa@963: // SYM_REF2(external_var_declarations_c, option, external_declaration_list) mjsousa@984: void *constant_propagation_c::visit(external_var_declarations_c *symbol) {return handle_var_decl(symbol->external_declaration_list, is_constant(symbol->option));} mjsousa@963: mjsousa@963: /* helper symbol for external_var_declarations */ mjsousa@963: /*| external_declaration_list external_declaration';' */ mjsousa@963: // SYM_LIST(external_declaration_list_c) mjsousa@984: // void *constant_propagation_c::visit(external_declaration_list_c *symbol) {} // Not needed: we inherit from iterator_c mjsousa@963: mjsousa@963: /* global_var_name ':' (simple_specification|subrange_specification|enumerated_specification|array_specification|prev_declared_structure_type_name|function_block_type_name */ mjsousa@963: //SYM_REF2(external_declaration_c, global_var_name, specification) mjsousa@984: void *constant_propagation_c::visit(external_declaration_c *symbol) { mjsousa@982: // The syntax does not allow VAR_EXTERN to be initialized. We must get the initial value from the corresponding VAR_GLOBAL declaration mjsousa@982: /* 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! mjsousa@982: * When we are called from the visit(library_c) visitor, we do not have the required information to do this! mjsousa@982: */ mjsousa@983: if (NULL != current_configuration) { mjsousa@983: /* before copying the constant value from the VAR_GLOBAL declaration (which is stored in the var_global_values[] map) mjsousa@983: * we check to see if the VAR_EXTERN constant values has already been set by a previous call to constant fold the POU mjsousa@983: * in which this VAR_EXTERN is found we simply check to see if any const value of this VAR_EXTERN variable has already mjsousa@983: * been set previously! mjsousa@983: */ mjsousa@983: if ( !IS_UNDEFINED( int64, symbol->specification) || !IS_UNDEFINED(uint64, symbol->specification) mjsousa@983: || !IS_UNDEFINED(real64, symbol->specification) || !IS_UNDEFINED( bool, symbol->specification)) { mjsousa@983: /* The const_value for this VAR_EXTERN has already been previously set. Lets check to see if it is the exact mjsousa@983: * same const value - if not we produce an error message! mjsousa@983: * mjsousa@983: * NOTE: comparison is inverted with '!' mjsousa@983: */ mjsousa@983: if (! (symbol->specification->const_value == var_global_values[get_var_name_c::get_name(symbol->global_var_name)->value])) mjsousa@983: STAGE3_ERROR(0, symbol, symbol, "The initial value of this external variable is ambiguous (the Program/FB in which " mjsousa@983: "this external variable is declared has been used to instantiate a Program/FB in more " mjsousa@983: "than one configuration and/or resource - and each resource sets the corresponding global " mjsousa@983: "variable to a distinct constant initial value)."); mjsousa@983: } mjsousa@983: mjsousa@983: // only now do we copy the const value from the var_global to the var_external. mjsousa@982: symbol->specification->const_value = var_global_values[get_var_name_c::get_name(symbol->global_var_name)->value]; mjsousa@983: } mjsousa@982: mjsousa@963: symbol->global_var_name->const_value = symbol->specification->const_value; mjsousa@967: if (fixed_init_value_) { mjsousa@988: // (*values)[symbol->global_var_name->get_value()] = symbol->specification->const_value; mjsousa@988: (*values)[get_var_name_c::get_name(symbol->global_var_name)->value] = symbol->specification->const_value; mjsousa@967: } mjsousa@967: // 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) mjsousa@964: symbol->specification->accept(*this); // should never get to change the const_value of the symbol->specification symbol (only its children!). mjsousa@963: return NULL; mjsousa@963: } mjsousa@963: mjsousa@969: /* NOTE that the constant folding of GLOBAL variables is already handled by handle_var_extern_global_pair, which mjsousa@969: * is called from declaration_check_c, mjsousa@963: * This is done like this because we need to know the pairing of external<->global variables to get the cvalue mjsousa@963: * from the global variable. Since the external<->global pairing information is available in the declaration_check_c, mjsousa@984: * we have that class call the constant_propagation_c::handle_var_extern_global_pair(), which will actually do the mjsousa@963: * constant folding of the global and the external variable declarations! mjsousa@963: */ mjsousa@969: /* NOTE: The constant propagation portion of this algorithm must still be done here!! mjsousa@969: * Even though the constant folding of GLOBAL variables are already handled by handle_var_extern_global_pair(), mjsousa@969: * we must still visit them here, since when doing constant propagation of a Configuration or a Resource we need the mjsousa@969: * values of constant variables to be placed in the values[] map, as these same variables may be used to declare mjsousa@969: * arrays of a variable size mjsousa@969: * VAR_GLOBAL CONSTANT max : INT := 42; END_VAR mjsousa@969: * VAR_GLOBAL array_v : ARRAY [1..max] of INT; END_VAR <---- NOTE the use of 'max' in the subrange! mjsousa@969: */ mjsousa@963: /*| VAR_GLOBAL [CONSTANT|RETAIN] global_var_decl_list END_VAR */ mjsousa@963: /* option -> may be NULL ! */ mjsousa@963: // SYM_REF2(global_var_declarations_c, option, global_var_decl_list) mjsousa@969: /* Note that calling handle_var_decl() will result in doing constant folding of literals (of datatype initial values) mjsousa@969: * that were already constant folded by the method handle_var_extern_global_pair() mjsousa@969: * Nevertheless, since constant folding is idem-potent, it is simpler to just call handle_var_decl() instead mjsousa@969: * of writing some code specific for this situation! mjsousa@969: */ mjsousa@984: void *constant_propagation_c::visit(global_var_declarations_c *symbol) {return handle_var_decl(symbol->global_var_decl_list, is_constant(symbol->option));} mjsousa@969: mjsousa@969: mjsousa@963: /* helper symbol for global_var_declarations */ mjsousa@963: /*| global_var_decl_list global_var_decl ';' */ mjsousa@963: // SYM_LIST(global_var_decl_list_c) mjsousa@984: // void *constant_propagation_c::visit(global_var_decl_list_c *symbol) {} // Not needed: we inherit from iterator_c mjsousa@969: mjsousa@963: /*| global_var_spec ':' [located_var_spec_init|function_block_type_name] */ mjsousa@963: /* type_specification ->may be NULL ! */ mjsousa@963: //SYM_REF2(global_var_decl_c, global_var_spec, type_specification) mjsousa@984: void *constant_propagation_c::visit(global_var_decl_c *symbol) { mjsousa@969: /* global_var_spec may be either a global_var_spec_c or a global_var_list_c. mjsousa@969: * Since we already have a nice method that handles var lists (handle_var_list_decl() ) mjsousa@969: * if it is a global_var_spec_c we will create a temporary list so we can call that method! mjsousa@969: */ mjsousa@969: global_var_spec_c *var_spec = dynamic_cast(symbol->global_var_spec); mjsousa@969: if (NULL == var_spec) { mjsousa@969: // global_var_spec is a global_var_list_c mjsousa@988: return handle_var_list_decl(symbol->global_var_spec, symbol->type_specification, true /* is global */); mjsousa@969: } else { mjsousa@969: global_var_list_c var_list; mjsousa@969: var_list.add_element(var_spec->global_var_name); mjsousa@988: return handle_var_list_decl(&var_list, symbol->type_specification, true /* is global */); mjsousa@969: } mjsousa@969: } mjsousa@969: mjsousa@969: mjsousa@963: /*| global_var_name location */ mjsousa@969: //SYM_REF2(global_var_spec_c, global_var_name, location) // Not needed! mjsousa@969: mjsousa@963: /* AT direct_variable */ mjsousa@969: //SYM_REF1(location_c, direct_variable) // Not needed! mjsousa@969: mjsousa@963: /*| global_var_list ',' global_var_name */ mjsousa@969: //SYM_LIST(global_var_list_c) // Not needed! mjsousa@963: mjsousa@963: mjsousa@963: #if 0 mjsousa@963: // TODO mjsousa@969: // We do not do constant folding of strings yet, so there is no need to implement this now! mjsousa@963: /* var1_list ':' single_byte_string_spec */ mjsousa@963: SYM_REF2(single_byte_string_var_declaration_c, var1_list, single_byte_string_spec) mjsousa@963: /* STRING ['[' integer ']'] [ASSIGN single_byte_character_string] */ mjsousa@963: /* integer ->may be NULL ! */ mjsousa@963: /* single_byte_character_string ->may be NULL ! */ mjsousa@963: SYM_REF2(single_byte_string_spec_c, string_spec, single_byte_character_string) mjsousa@963: /* STRING ['[' integer ']'] */ mjsousa@963: /* integer ->may be NULL ! */ mjsousa@963: SYM_REF2(single_byte_limited_len_string_spec_c, string_type_name, character_string_len) mjsousa@963: /* WSTRING ['[' integer ']'] */ mjsousa@963: /* integer ->may be NULL ! */ mjsousa@963: SYM_REF2(double_byte_limited_len_string_spec_c, string_type_name, character_string_len) mjsousa@963: /* var1_list ':' double_byte_string_spec */ mjsousa@963: SYM_REF2(double_byte_string_var_declaration_c, var1_list, double_byte_string_spec) mjsousa@963: /* WSTRING ['[' integer ']'] [ASSIGN double_byte_character_string] */ mjsousa@963: /* integer ->may be NULL ! */ mjsousa@963: /* double_byte_character_string ->may be NULL ! */ mjsousa@963: SYM_REF2(double_byte_string_spec_c, string_spec, double_byte_character_string) mjsousa@963: /*| VAR [RETAIN|NON_RETAIN] incompl_located_var_decl_list END_VAR */ mjsousa@963: /* option ->may be NULL ! */ mjsousa@963: SYM_REF2(incompl_located_var_declarations_c, option, incompl_located_var_decl_list) mjsousa@963: /* helper symbol for incompl_located_var_declarations */ mjsousa@963: /*| incompl_located_var_decl_list incompl_located_var_decl ';' */ mjsousa@963: SYM_LIST(incompl_located_var_decl_list_c) mjsousa@963: /* variable_name incompl_location ':' var_spec */ mjsousa@963: SYM_REF3(incompl_located_var_decl_c, variable_name, incompl_location, var_spec) mjsousa@963: /* AT incompl_location_token */ mjsousa@963: SYM_TOKEN(incompl_location_c) mjsousa@963: /* intermediate helper symbol for: mjsousa@963: * - non_retentive_var_decls mjsousa@963: * - output_declarations mjsousa@963: */ mjsousa@963: SYM_LIST(var_init_decl_list_c) mjsousa@963: #endif mjsousa@963: mjsousa@963: mjsousa@963: /***********************/ mjsousa@963: /* B 1.5.1 - Functions */ mjsousa@963: /***********************/ mjsousa@963: /* 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 */ mjsousa@963: //SYM_REF4(function_declaration_c, derived_function_name, type_name, var_declarations_list, function_body, enumvalue_symtable_t enumvalue_symtable;) mjsousa@984: void *constant_propagation_c::visit(function_declaration_c *symbol) { mjsousa@988: map_values_t local_values, *prev_pou_values; mjsousa@988: prev_pou_values = values; // store the current values map of whoever called this Function (a program, configuration, or resource) mjsousa@988: values = &local_values; mjsousa@988: var_global_values.push(); /* Create inner scope - Not really needed, but do it just to be consistent. */ mjsousa@988: mjsousa@963: /* Add initial value of all declared variables into Values map. */ mjsousa@967: function_pou_ = true; mjsousa@963: symbol->var_declarations_list->accept(*this); mjsousa@967: function_pou_ = false; mjsousa@963: symbol->function_body->accept(*this); mjsousa@988: mjsousa@988: var_global_values.pop(); /* Delete inner scope */ mjsousa@988: values = prev_pou_values; mjsousa@963: return NULL; mjsousa@963: } mjsousa@963: mjsousa@963: mjsousa@967: /* intermediate helper symbol for mjsousa@967: * - function_declaration mjsousa@967: * - function_block_declaration mjsousa@967: * - program_declaration mjsousa@967: */ mjsousa@967: // SYM_LIST(var_declarations_list_c) // Not needed since we inherit from iterator_c mjsousa@967: mjsousa@967: /* option -> storage method, CONSTANT or */ mjsousa@967: // SYM_REF2(function_var_decls_c, option, decl_list) mjsousa@967: // NOTE: function_var_decls_c is only used inside Functions, so it is safe to call with fixed_init_value_ = true mjsousa@984: void *constant_propagation_c::visit(function_var_decls_c *symbol) {return handle_var_decl(symbol->decl_list, true);} mjsousa@967: mjsousa@967: /* intermediate helper symbol for function_var_decls */ mjsousa@967: // SYM_LIST(var2_init_decl_list_c) // Not needed since we inherit from iterator_c mjsousa@967: mjsousa@967: mjsousa@963: /*****************************/ mjsousa@963: /* B 1.5.2 - Function Blocks */ mjsousa@963: /*****************************/ mjsousa@963: /* FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */ mjsousa@963: /* 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 */ mjsousa@963: //SYM_REF3(function_block_declaration_c, fblock_name, var_declarations, fblock_body, enumvalue_symtable_t enumvalue_symtable;) mjsousa@984: void *constant_propagation_c::visit(function_block_declaration_c *symbol) { mjsousa@988: map_values_t local_values, *prev_pou_values; mjsousa@988: prev_pou_values = values; // store the current values map of whoever instantited this FB (a program, configuration, or resource) mjsousa@988: values = &local_values; mjsousa@988: var_global_values.push(); /* Create inner scope */ mjsousa@988: mjsousa@963: /* Add initial value of all declared variables into Values map. */ mjsousa@967: function_pou_ = false; mjsousa@963: symbol->var_declarations->accept(*this); mjsousa@963: symbol->fblock_body->accept(*this); mjsousa@988: mjsousa@988: var_global_values.pop(); /* Delete inner scope */ mjsousa@988: values = prev_pou_values; mjsousa@963: return NULL; mjsousa@963: } mjsousa@963: mjsousa@967: /* VAR_TEMP temp_var_decl_list END_VAR */ mjsousa@967: // SYM_REF1(temp_var_decls_c, var_decl_list) mjsousa@984: void *constant_propagation_c::visit(temp_var_decls_c *symbol) {return handle_var_decl(symbol->var_decl_list, true);} mjsousa@967: mjsousa@967: /* intermediate helper symbol for temp_var_decls */ mjsousa@967: // SYM_LIST(temp_var_decls_list_c) mjsousa@967: mjsousa@967: /* VAR NON_RETAIN var_init_decl_list END_VAR */ mjsousa@967: // SYM_REF1(non_retentive_var_decls_c, var_decl_list) mjsousa@967: // NOTE: non_retentive_var_decls_c is only used inside FBs and Programs, so it is safe to call with fixed_init_value_ = false mjsousa@984: void *constant_propagation_c::visit(non_retentive_var_decls_c *symbol) {return handle_var_decl(symbol->var_decl_list, false);} mjsousa@967: conti@788: conti@774: /**********************/ conti@774: /* B 1.5.3 - Programs */ conti@774: /**********************/ mjsousa@963: /* PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM */ mjsousa@963: //SYM_REF3(program_declaration_c, program_type_name, var_declarations, function_block_body, enumvalue_symtable_t enumvalue_symtable;) mjsousa@984: void *constant_propagation_c::visit(program_declaration_c *symbol) { mjsousa@988: map_values_t local_values, *prev_pou_values; mjsousa@988: prev_pou_values = values; // store the current values map of whoever instantited this Program (a configuration, or resource) mjsousa@988: values = &local_values; mjsousa@988: var_global_values.push(); /* Create inner scope */ mjsousa@988: mjsousa@963: /* Add initial value of all declared variables into Values map. */ mjsousa@967: function_pou_ = false; conti@787: symbol->var_declarations->accept(*this); conti@774: symbol->function_block_body->accept(*this); mjsousa@988: mjsousa@988: var_global_values.pop(); /* Delete inner scope */ mjsousa@988: values = prev_pou_values; conti@774: return NULL; conti@774: } msousa@612: msousa@612: mjsousa@969: /********************************/ mjsousa@969: /* B 1.7 Configuration elements */ mjsousa@969: /********************************/ mjsousa@969: mjsousa@969: /* mjsousa@969: CONFIGURATION configuration_name mjsousa@969: optional_global_var_declarations mjsousa@969: (resource_declaration_list | single_resource_declaration) mjsousa@969: optional_access_declarations mjsousa@969: optional_instance_specific_initializations mjsousa@969: END_CONFIGURATION mjsousa@969: */ mjsousa@969: /* 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 */ mjsousa@969: // SYM_REF5(configuration_declaration_c, configuration_name, global_var_declarations, resource_declarations, access_declarations, instance_specific_initializations, mjsousa@969: // enumvalue_symtable_t enumvalue_symtable; localvar_symbmap_t localvar_symbmap; localvar_symbvec_t localvar_symbvec;) mjsousa@984: void *constant_propagation_c::visit(configuration_declaration_c *symbol) { mjsousa@988: map_values_t local_values; mjsousa@988: values = &local_values; mjsousa@988: var_global_values.clear(); /* Clear global variables map */ mjsousa@982: mjsousa@969: /* Add initial value of all declared variables into Values map. */ mjsousa@969: function_pou_ = false; mjsousa@982: current_configuration = symbol; mjsousa@982: iterator_visitor_c::visit(symbol); // let the base iterator class handle the rest (basically iterate through the whole configuration and do the constant folding! mjsousa@982: current_configuration = NULL; mjsousa@988: mjsousa@988: values = NULL; mjsousa@982: return NULL; mjsousa@969: } mjsousa@969: mjsousa@969: mjsousa@969: /* helper symbol for configuration_declaration */ mjsousa@969: // SYM_LIST(resource_declaration_list_c) // Not needed: we inherit from iterator_c mjsousa@969: mjsousa@969: /* mjsousa@969: RESOURCE resource_name ON resource_type_name mjsousa@969: optional_global_var_declarations mjsousa@969: single_resource_declaration mjsousa@969: END_RESOURCE mjsousa@969: */ mjsousa@969: /* 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 */ mjsousa@969: // SYM_REF4(resource_declaration_c, resource_name, resource_type_name, global_var_declarations, resource_declaration, mjsousa@969: // enumvalue_symtable_t enumvalue_symtable; localvar_symbmap_t localvar_symbmap; localvar_symbvec_t localvar_symbvec;) mjsousa@984: void *constant_propagation_c::visit(resource_declaration_c *symbol) { mjsousa@988: var_global_values.push(); /* Create inner scope */ mjsousa@988: values->push(); /* Create inner scope */ mjsousa@982: mjsousa@969: /* Add initial value of all declared variables into Values map. */ mjsousa@969: function_pou_ = false; mjsousa@982: symbol->global_var_declarations->accept(*this); mjsousa@982: mjsousa@982: current_resource = symbol; mjsousa@982: symbol->resource_declaration->accept(*this); mjsousa@982: current_resource = NULL; mjsousa@982: // iterator_visitor_c::visit(symbol); // let the base iterator class handle the rest (basically iterate through the whole configuration and do the constant folding! mjsousa@982: mjsousa@988: var_global_values.pop(); /* Delete inner scope */ mjsousa@988: values->pop(); /* Delete inner scope */ mjsousa@973: return NULL; mjsousa@969: } mjsousa@969: mjsousa@969: mjsousa@969: mjsousa@969: /* task_configuration_list program_configuration_list */ mjsousa@969: // SYM_REF2(single_resource_declaration_c, task_configuration_list, program_configuration_list) mjsousa@982: mjsousa@969: /* helper symbol for single_resource_declaration */ mjsousa@969: // SYM_LIST(task_configuration_list_c) mjsousa@969: /* helper symbol for single_resource_declaration */ mjsousa@969: // SYM_LIST(program_configuration_list_c) mjsousa@969: /* helper symbol for: (access_path, instance_specific_init) */ mjsousa@969: // SYM_LIST(any_fb_name_list_c) mjsousa@969: /* [resource_name '.'] global_var_name ['.' structure_element_name] */ mjsousa@969: // SYM_REF3(global_var_reference_c, resource_name, global_var_name, structure_element_name) mjsousa@969: /* prev_declared_program_name '.' symbolic_variable */ mjsousa@969: // SYM_REF2(program_output_reference_c, program_name, symbolic_variable) mjsousa@969: /* TASK task_name task_initialization */ mjsousa@969: // SYM_REF2(task_configuration_c, task_name, task_initialization) mjsousa@969: /* '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' */ mjsousa@969: // SYM_REF3(task_initialization_c, single_data_source, interval_data_source, priority_data_source) mjsousa@982: mjsousa@969: /* PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] */ mjsousa@969: /* NOTE: The parameter 'called_prog_declaration'is used to pass data between stage 3 and stage4 */ mjsousa@969: // SYM_REF5(program_configuration_c, retain_option, program_name, task_name, program_type_name, prog_conf_elements, mjsousa@969: // symbol_c *called_prog_declaration;) mjsousa@984: void *constant_propagation_c::visit(program_configuration_c *symbol) { mjsousa@982: /* find the declaration (i.e. the datatype) of the program being instantiated */ mjsousa@982: // NOTE: we do not use symbol->datatype so this cost propagation algorithm will not depend on the fill/narrow datatypes algorithm! mjsousa@982: program_type_symtable_t::iterator itr = program_type_symtable.find(symbol->program_type_name); mjsousa@982: if (itr == program_type_symtable.end()) ERROR; // syntax parsing should not allow this! mjsousa@982: program_declaration_c *prog_type = itr->second; mjsousa@982: if (NULL == prog_type) ERROR; // syntax parsing should not allow this! mjsousa@982: prog_type->accept(*this); mjsousa@982: mjsousa@982: if (NULL != symbol->prog_conf_elements) mjsousa@982: symbol->prog_conf_elements->accept(*this); mjsousa@982: return NULL; mjsousa@982: } mjsousa@982: mjsousa@982: mjsousa@969: /* prog_conf_elements ',' prog_conf_element */ mjsousa@969: // SYM_LIST(prog_conf_elements_c) mjsousa@982: mjsousa@969: /* fb_name WITH task_name */ mjsousa@969: // SYM_REF2(fb_task_c, fb_name, task_name) mjsousa@984: void *constant_propagation_c::visit(fb_task_c *symbol) { mjsousa@982: /* find the declaration (i.e. the datatype) of the FB being instantiated */ mjsousa@982: // NOTE: we do not use symbol->datatype so this cost propagation algorithm will not depend on the fill/narrow datatypes algorithm! mjsousa@982: symbol_c *fb_type_name = NULL; mjsousa@982: mjsousa@982: if ((NULL == fb_type_name) && (NULL != current_configuration)) { mjsousa@982: search_var_instance_decl_c search_scope(current_configuration); mjsousa@982: fb_type_name = search_scope.get_decl(symbol->fb_name); mjsousa@982: } mjsousa@982: if ((NULL == fb_type_name) && (NULL != current_resource)) { mjsousa@982: search_var_instance_decl_c search_scope(current_resource); mjsousa@982: fb_type_name = search_scope.get_decl(symbol->fb_name); mjsousa@982: } mjsousa@982: if (NULL == fb_type_name) ERROR; mjsousa@982: mjsousa@982: function_block_type_symtable_t::iterator itr = function_block_type_symtable.find(fb_type_name); mjsousa@982: if (itr == function_block_type_symtable.end()) ERROR; // syntax parsing should not allow this! mjsousa@982: function_block_declaration_c *fb_type_decl = itr->second; mjsousa@982: if (NULL == fb_type_decl) ERROR; mjsousa@982: fb_type_decl->accept(*this); mjsousa@982: return NULL; mjsousa@982: } mjsousa@982: mjsousa@982: mjsousa@982: mjsousa@969: /* any_symbolic_variable ASSIGN prog_data_source */ mjsousa@969: // SYM_REF2(prog_cnxn_assign_c, symbolic_variable, prog_data_source) mjsousa@969: /* any_symbolic_variable SENDTO data_sink */ mjsousa@969: // SYM_REF2(prog_cnxn_sendto_c, symbolic_variable, data_sink) mjsousa@969: /* VAR_CONFIG instance_specific_init_list END_VAR */ mjsousa@969: // SYM_REF1(instance_specific_initializations_c, instance_specific_init_list) mjsousa@969: /* helper symbol for instance_specific_initializations */ mjsousa@969: // SYM_LIST(instance_specific_init_list_c) mjsousa@969: /* resource_name '.' program_name '.' {fb_name '.'} mjsousa@969: ((variable_name [location] ':' located_var_spec_init) | (fb_name ':' fb_initialization)) mjsousa@969: */ mjsousa@969: // SYM_REF6(instance_specific_init_c, resource_name, program_name, any_fb_name_list, variable_name, location, initialization) mjsousa@969: /* helper symbol for instance_specific_init */ mjsousa@969: /* function_block_type_name ':=' structure_initialization */ mjsousa@969: // SYM_REF2(fb_initialization_c, function_block_type_name, structure_initialization) mjsousa@969: mjsousa@969: mjsousa@969: mjsousa@969: msousa@567: conti@564: /***************************************/ conti@564: /* B.3 - Language ST (Structured Text) */ conti@564: /***************************************/ conti@564: /***********************/ conti@564: /* B 3.1 - Expressions */ conti@564: /***********************/ mjsousa@984: #if DO_CONSTANT_PROPAGATION__ msousa@612: /* TODO: handle function invocations... */ Laurent@633: // void *fill_candidate_datatypes_c::visit(function_invocation_c *symbol) {} conti@774: conti@774: conti@774: /*********************************/ conti@774: /* B 3.2.1 Assignment Statements */ conti@774: /*********************************/ mjsousa@984: void *constant_propagation_c::visit(assignment_statement_c *symbol) { conti@774: std::string varName; conti@774: conti@774: symbol->r_exp->accept(*this); mjsousa@967: symbol->l_exp->accept(*this); // if the lvalue has an array, do contant folding of the array indexes! conti@774: symbol->l_exp->const_value = symbol->r_exp->const_value; mjsousa@988: (*values)[get_var_name_c::get_name(symbol->l_exp)->value] = symbol->l_exp->const_value; mjsousa@967: return NULL; mjsousa@967: } mjsousa@967: conti@774: /********************************/ conti@774: /* B 3.2.3 Selection Statements */ conti@774: /********************************/ mjsousa@984: void *constant_propagation_c::visit(if_statement_c *symbol) { conti@788: map_values_t values_incoming; conti@788: map_values_t values_statement_result; conti@788: map_values_t values_elsestatement_result; conti@788: map_values_t::iterator itr; conti@780: conti@780: /* Optimize dead code */ conti@780: symbol->expression->accept(*this); conti@780: if (VALID_CVALUE(bool, symbol->expression) && GET_CVALUE(bool, symbol->expression) == false) conti@780: return NULL; conti@780: conti@774: values_incoming = values; /* save incoming status */ conti@774: symbol->statement_list->accept(*this); conti@774: values_statement_result = values; conti@774: if (NULL != symbol->else_statement_list) { conti@774: values = values_incoming; conti@774: symbol->else_statement_list->accept(*this); conti@774: values_elsestatement_result = values; conti@774: } else conti@774: values_elsestatement_result = values_incoming; conti@788: values = inner_left_join_values(values_statement_result, values_elsestatement_result); conti@780: conti@780: return NULL; conti@780: } conti@780: conti@780: /********************************/ conti@780: /* B 3.2.4 Iteration Statements */ conti@780: /********************************/ mjsousa@984: void *constant_propagation_c::visit(for_statement_c *symbol) { conti@788: map_values_t values_incoming; conti@788: map_values_t values_statement_result; conti@780: conti@780: values_incoming = values; /* save incoming status */ conti@782: symbol->beg_expression->accept(*this); conti@782: symbol->end_expression->accept(*this); mjsousa@988: (*values)[get_var_name_c::get_name(symbol->control_variable)->value]._int64.status = const_value_c::cs_non_const; conti@782: conti@782: /* Optimize dead code */ conti@788: if (NULL != symbol->by_expression) { conti@788: symbol->by_expression->accept(*this); conti@788: if (VALID_CVALUE(int64, symbol->by_expression ) && GET_CVALUE(int64, symbol->by_expression ) > 0 && conti@788: VALID_CVALUE(int64, symbol->beg_expression) && VALID_CVALUE(int64, symbol->end_expression) && conti@788: GET_CVALUE(int64, symbol->beg_expression) > GET_CVALUE(int64, symbol->end_expression)) conti@788: return NULL; conti@788: conti@788: if (VALID_CVALUE(int64, symbol->by_expression ) && GET_CVALUE(int64, symbol->by_expression ) < 0 && conti@788: VALID_CVALUE(int64, symbol->beg_expression) && VALID_CVALUE(int64, symbol->end_expression) && conti@788: GET_CVALUE(int64, symbol->beg_expression) < GET_CVALUE(int64, symbol->end_expression)) conti@788: return NULL; conti@788: conti@788: } else { conti@788: if (VALID_CVALUE(int64, symbol->beg_expression) && VALID_CVALUE(int64, symbol->end_expression) && conti@788: GET_CVALUE(int64, symbol->beg_expression) > GET_CVALUE(int64, symbol->end_expression)) conti@788: return NULL; conti@788: conti@788: } conti@788: conti@782: conti@780: symbol->statement_list->accept(*this); conti@780: values_statement_result = values; conti@788: values = inner_left_join_values(values_statement_result, values_incoming); conti@780: conti@780: return NULL; conti@780: } conti@780: mjsousa@984: void *constant_propagation_c::visit(while_statement_c *symbol) { conti@788: map_values_t values_incoming; conti@788: map_values_t values_statement_result; conti@780: conti@780: /* Optimize dead code */ conti@780: symbol->expression->accept(*this); conti@780: if (VALID_CVALUE(bool, symbol->expression) && GET_CVALUE(bool, symbol->expression) == false) conti@780: return NULL; conti@780: conti@780: values_incoming = values; /* save incoming status */ conti@780: symbol->statement_list->accept(*this); conti@780: values_statement_result = values; conti@788: values = inner_left_join_values(values_statement_result, values_incoming); conti@780: conti@780: return NULL; conti@780: } conti@780: mjsousa@984: void *constant_propagation_c::visit(repeat_statement_c *symbol) { conti@788: map_values_t values_incoming; conti@788: map_values_t values_statement_result; conti@780: conti@790: values_incoming = values; /* save incoming status */ conti@790: symbol->statement_list->accept(*this); conti@790: conti@780: /* Optimize dead code */ conti@780: symbol->expression->accept(*this); conti@790: if (VALID_CVALUE(bool, symbol->expression) && GET_CVALUE(bool, symbol->expression) == true) conti@780: return NULL; conti@780: conti@780: values_statement_result = values; conti@788: values = inner_left_join_values(values_statement_result, values_incoming); conti@788: conti@788: return NULL; conti@788: } conti@788: msousa@792: #endif // DO_CONSTANT_PROPAGATION__ msousa@792: msousa@792: mjsousa@963: mjsousa@963: mjsousa@963: