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 msousa@569: * (symbol in the absract 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 msousa@572: * actuall printing of errors for the print_datatype_errors_c class! 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: msousa@643: #define SET_CVALUE(dtype, symbol, new_value) {((symbol)->const_value._##dtype.value) = new_value; ((symbol)->const_value._##dtype.status) = symbol_c::cs_const_value;} msousa@612: #define GET_CVALUE(dtype, symbol) ((symbol)->const_value._##dtype.value) msousa@612: #define SET_OVFLOW(dtype, symbol) ((symbol)->const_value._##dtype.status) = symbol_c::cs_overflow msousa@612: #define SET_NONCONST(dtype, symbol) ((symbol)->const_value._##dtype.status) = symbol_c::cs_non_const msousa@612: msousa@612: #define VALID_CVALUE(dtype, symbol) (symbol_c::cs_const_value == (symbol)->const_value._##dtype.status) msousa@643: #define IS_OVFLOW(dtype, symbol) (symbol_c::cs_overflow == (symbol)->const_value._##dtype.status) msousa@648: #define IS_NONCONST(dtype, symbol) (symbol_c::cs_non_const == (symbol)->const_value._##dtype.status) msousa@569: #define ISZERO_CVALUE(dtype, symbol) ((VALID_CVALUE(dtype, symbol)) && (GET_CVALUE(dtype, symbol) == 0)) 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@564: conti@564: 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; msousa@601: const char *value; 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; msousa@601: const char *value; 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! msousa@596: #if (real64_t == float) msousa@600: ret = strtof(str.c_str(), &endptr); msousa@596: #elif (real64_t == double) msousa@600: ret = strtod(str.c_str(), &endptr); msousa@596: #elif (real64_t == long_double) msousa@600: ret = strtold(str.c_str(), &endptr); msousa@596: #else msousa@596: #error Could not determine which data type is being used for real64_t (defined in absyntax.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@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: 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: msousa@612: /***********************************************************************/ msousa@612: /***********************************************************************/ msousa@612: /***********************************************************************/ msousa@612: /*** Helper functions for handling IL instruction lists. ***/ msousa@612: /***********************************************************************/ msousa@612: /***********************************************************************/ msousa@612: /***********************************************************************/ msousa@612: msousa@612: msousa@612: /* 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! */ msousa@612: #define intersect_prev_CVALUE_(dtype, symbol) { \ msousa@612: symbol->const_value._##dtype = symbol->prev_il_instruction[0]->const_value._##dtype; \ msousa@612: for (unsigned int i = 1; i < symbol->prev_il_instruction.size(); i++) { \ msousa@612: if (!ISEQUAL_CVALUE(dtype, symbol, symbol->prev_il_instruction[i])) \ msousa@612: {SET_NONCONST(dtype, symbol); break;} \ msousa@612: } \ msousa@612: } msousa@612: msousa@612: static void intersect_prev_cvalues(il_instruction_c *symbol) { msousa@612: if (symbol->prev_il_instruction.empty()) msousa@612: return; msousa@612: intersect_prev_CVALUE_(real64, symbol); msousa@612: intersect_prev_CVALUE_(uint64, symbol); msousa@612: intersect_prev_CVALUE_( int64, symbol); msousa@612: intersect_prev_CVALUE_( bool, symbol); msousa@612: } msousa@572: msousa@572: msousa@572: msousa@596: /***********************************************************************/ msousa@596: /***********************************************************************/ msousa@596: /***********************************************************************/ msousa@596: /*** The constant_folding_c ***/ msousa@596: /***********************************************************************/ msousa@596: /***********************************************************************/ msousa@596: /***********************************************************************/ msousa@572: msousa@572: msousa@572: msousa@572: msousa@572: msousa@572: conti@564: constant_folding_c::constant_folding_c(symbol_c *symbol) { conti@564: error_count = 0; msousa@568: warning_found = false; conti@564: current_display_error_level = 0; conti@661: il_operand = NULL; conti@661: search_varfb_instance_type = NULL; conti@661: prev_il_instruction = NULL; msousa@568: msousa@568: /* check whether the platform on which the compiler is being run implements IEC 559 floating point data types. */ msousa@568: symbol_c null_symbol; msousa@568: if (! (std::numeric_limits::is_iec559) ) msousa@579: STAGE3_WARNING(&null_symbol, &null_symbol, "The platform running the compiler does not implement IEC 60559 floating point numbers. " msousa@568: "Any error and/or warning messages related to overflow/underflow of the result of operations on REAL/LREAL literals " msousa@579: "(i.e. constant folding) may themselves be erroneous, although are most probably correct." msousa@579: "However, more likely is the possible existance of overflow/underflow errors that are not detected."); conti@564: } conti@564: msousa@567: conti@564: constant_folding_c::~constant_folding_c(void) { conti@564: } conti@564: msousa@567: conti@564: int constant_folding_c::get_error_count() { conti@564: return error_count; conti@564: } conti@564: conti@564: conti@564: /*********************/ conti@564: /* B 1.2 - Constants */ conti@564: /*********************/ conti@564: /******************************/ conti@564: /* B 1.2.1 - Numeric Literals */ conti@564: /******************************/ conti@564: void *constant_folding_c::visit(real_c *symbol) { msousa@576: bool overflow; msousa@612: SET_CVALUE(real64, symbol, extract_real_value(symbol, &overflow)); msousa@576: if (overflow) SET_OVFLOW(real64, symbol); msousa@567: return NULL; msousa@567: } msousa@567: conti@564: conti@564: void *constant_folding_c::visit(integer_c *symbol) { conti@587: bool overflow; msousa@612: SET_CVALUE( int64, symbol, extract_int64_value (symbol, &overflow)); conti@588: if (overflow) SET_OVFLOW(int64, symbol); msousa@612: SET_CVALUE(uint64, symbol, extract_uint64_value(symbol, &overflow)); conti@588: if (overflow) SET_OVFLOW(uint64, symbol); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(neg_real_c *symbol) { conti@564: symbol->exp->accept(*this); msousa@643: DO_UNARY_OPER(real64, -, symbol->exp); CHECK_OVERFLOW_real64(symbol); msousa@643: if (IS_OVFLOW(real64, symbol->exp)) SET_OVFLOW(real64, symbol); msousa@643: return NULL; msousa@643: } msousa@643: msousa@643: msousa@569: msousa@569: /* | '-' integer {$$ = new neg_integer_c($2, locloc(@$));} */ conti@564: void *constant_folding_c::visit(neg_integer_c *symbol) { msousa@569: symbol->exp->accept(*this); msousa@643: /* Note that due to syntax restrictions, the value of symbol->exp will always be positive. msousa@643: * However, the following code does not depend on that restriction. msousa@643: */ msousa@643: /* The remainder of the code (for example, data type checking) considers the neg_integer_c as a leaf of the msousa@643: * abstract syntax tree, and therefore simply ignores the values of neg_integer_c->exp. msousa@643: * For this reason only, and in only this situation, we must guarantee that any 'overflow' situation in msousa@643: * the cvalue of neg_integer_c->exp is also reflected back to this neg_integer_c symbol. msousa@643: * For the rest of the code we do NOT do this, as it would gurantee that a single overflow deep inside msousa@643: * an expression would imply that the expression itself would also be set to 'overflow' condition. msousa@643: * This in turn would then have the compiler produce a whole load of error messages where they are not wanted! msousa@643: */ msousa@643: DO_UNARY_OPER(uint64, -, symbol->exp); CHECK_OVERFLOW_uint64_NEG(symbol, symbol->exp); /* handle the uintv := -0 situation */ msousa@643: if (IS_OVFLOW(uint64, symbol->exp)) SET_OVFLOW(uint64, symbol); msousa@643: DO_UNARY_OPER( int64, -, symbol->exp); CHECK_OVERFLOW_int64_NEG (symbol, symbol->exp); msousa@643: if (IS_OVFLOW( int64, symbol->exp)) SET_OVFLOW( int64, symbol); msousa@590: /* NOTE 1: INT64_MIN = -(INT64_MAX + 1) ---> assuming two's complement representation!!! msousa@590: * NOTE 2: if the user happens to want INT_MIN, that value will first be parsed as a positive integer, before being negated here. msousa@590: * However, the positive value cannot be stored inside an int64! So, in this case, we will get the value from the uint64 cvalue. msousa@643: * msousa@643: * This same situation is usually considered an overflow (check handle_neg() function). However, here we have a special msousa@643: * situation. If we do not allow this, then the user would never the able to use the following code: msousa@643: * VAR v : LINT; END_VAR msousa@643: * v := -9223372036854775809 ; (* - |INT64_MIN| == INT64_MIN *) msousa@590: */ msousa@590: // if (INT64_MIN == -INT64_MAX - 1) // We do not really need to check that the platform uses two's complement msousa@621: if (VALID_CVALUE(uint64, symbol->exp) && (GET_CVALUE(uint64, symbol->exp) == (uint64_t)INT64_MAX+1)) { msousa@590: SET_CVALUE(int64, symbol, INT64_MIN); msousa@590: } conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(binary_integer_c *symbol) { msousa@600: bool overflow; msousa@612: SET_CVALUE( int64, symbol, extract_int64_value (symbol, &overflow)); msousa@600: if (overflow) SET_OVFLOW(int64, symbol); msousa@612: SET_CVALUE(uint64, symbol, extract_uint64_value(symbol, &overflow)); msousa@600: if (overflow) SET_OVFLOW(uint64, symbol); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(octal_integer_c *symbol) { msousa@600: bool overflow; msousa@612: SET_CVALUE( int64, symbol, extract_int64_value (symbol, &overflow)); msousa@600: if (overflow) SET_OVFLOW(int64, symbol); msousa@612: SET_CVALUE(uint64, symbol, extract_uint64_value(symbol, &overflow)); msousa@600: if (overflow) SET_OVFLOW(uint64, symbol); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(hex_integer_c *symbol) { msousa@600: bool overflow; msousa@612: SET_CVALUE( int64, symbol, extract_int64_value (symbol, &overflow)); msousa@600: if (overflow) SET_OVFLOW(int64, symbol); msousa@612: SET_CVALUE(uint64, symbol, extract_uint64_value(symbol, &overflow)); msousa@600: if (overflow) SET_OVFLOW(uint64, symbol); msousa@569: return NULL; msousa@569: } msousa@569: msousa@569: msousa@569: /* msousa@569: integer_literal: msousa@569: integer_type_name '#' signed_integer {$$ = new integer_literal_c($1, $3, locloc(@$));} msousa@569: | integer_type_name '#' binary_integer {$$ = new integer_literal_c($1, $3, locloc(@$));} msousa@569: | integer_type_name '#' octal_integer {$$ = new integer_literal_c($1, $3, locloc(@$));} msousa@569: | integer_type_name '#' hex_integer {$$ = new integer_literal_c($1, $3, locloc(@$));} msousa@569: */ msousa@569: // SYM_REF2(integer_literal_c, type, value) conti@564: void *constant_folding_c::visit(integer_literal_c *symbol) { conti@564: symbol->value->accept(*this); msousa@575: DO_UNARY_OPER( int64, /* none */, symbol->value); msousa@575: DO_UNARY_OPER(uint64, /* none */, symbol->value); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(real_literal_c *symbol) { conti@564: symbol->value->accept(*this); msousa@575: DO_UNARY_OPER(real64, /* none */, symbol->value); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(bit_string_literal_c *symbol) { conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(boolean_literal_c *symbol) { conti@564: symbol->value->accept(*this); msousa@575: DO_UNARY_OPER(bool, /* none */, symbol->value); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(boolean_true_c *symbol) { msousa@612: SET_CVALUE(bool, symbol, true); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(boolean_false_c *symbol) { msousa@612: SET_CVALUE(bool, symbol, false); msousa@612: return NULL; msousa@612: } msousa@612: Laurent@633: /************************/ Laurent@633: /* B 1.2.3.1 - Duration */ Laurent@633: /********* **************/ Laurent@633: void *constant_folding_c::visit(fixed_point_c *symbol) { Laurent@633: bool overflow; Laurent@633: SET_CVALUE(real64, symbol, extract_real_value(symbol, &overflow)); Laurent@633: if (overflow) SET_OVFLOW(real64, symbol); Laurent@633: return NULL; Laurent@633: } msousa@612: msousa@612: msousa@612: msousa@612: /****************************************/ msousa@612: /* B.2 - Language IL (Instruction List) */ msousa@612: /****************************************/ msousa@612: /***********************************/ msousa@612: /* B 2.1 Instructions and Operands */ msousa@612: /***********************************/ msousa@612: /* Not needed, since we inherit from iterator_visitor_c */ msousa@612: /*| instruction_list il_instruction */ msousa@612: // SYM_LIST(instruction_list_c) msousa@612: // void *constant_folding_c::visit(instruction_list_c *symbol) {} msousa@612: msousa@612: /* | label ':' [il_incomplete_instruction] eol_list */ msousa@612: // SYM_REF2(il_instruction_c, label, il_instruction) msousa@612: // void *visit(instruction_list_c *symbol); msousa@612: void *constant_folding_c::visit(il_instruction_c *symbol) { msousa@612: if (NULL == symbol->il_instruction) { msousa@612: /* This empty/null il_instruction does not change the value of the current/default IL variable. msousa@612: * So it inherits the candidate_datatypes from it's previous IL instructions! msousa@612: */ msousa@612: intersect_prev_cvalues(symbol); msousa@612: } else { msousa@612: il_instruction_c fake_prev_il_instruction = *symbol; msousa@612: intersect_prev_cvalues(&fake_prev_il_instruction); msousa@612: msousa@612: if (symbol->prev_il_instruction.size() == 0) prev_il_instruction = NULL; msousa@612: else prev_il_instruction = &fake_prev_il_instruction; msousa@612: symbol->il_instruction->accept(*this); msousa@612: prev_il_instruction = NULL; msousa@612: msousa@612: /* This object has (inherits) the same cvalues as the il_instruction */ msousa@612: symbol->const_value = symbol->il_instruction->const_value; msousa@612: } msousa@612: msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: void *constant_folding_c::visit(il_simple_operation_c *symbol) { msousa@612: /* determine the cvalue of the operand */ msousa@612: if (NULL != symbol->il_operand) { msousa@612: symbol->il_operand->accept(*this); msousa@612: } msousa@612: /* determine the cvalue resulting from executing the il_operator... */ msousa@612: il_operand = symbol->il_operand; msousa@612: symbol->il_simple_operator->accept(*this); msousa@612: il_operand = NULL; msousa@612: /* This object has (inherits) the same cvalues as the il_instruction */ msousa@612: symbol->const_value = symbol->il_simple_operator->const_value; msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: /* TODO: handle function invocations... */ msousa@612: /* | function_name [il_operand_list] */ msousa@612: /* NOTE: The parameters 'called_function_declaration' and 'extensible_param_count' are used to pass data between the stage 3 and stage 4. */ msousa@612: // SYM_REF2(il_function_call_c, function_name, il_operand_list, symbol_c *called_function_declaration; int extensible_param_count;) msousa@612: // void *constant_folding_c::visit(il_function_call_c *symbol) {} msousa@612: msousa@612: msousa@612: /* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */ msousa@612: // SYM_REF3(il_expression_c, il_expr_operator, il_operand, simple_instr_list); msousa@612: void *constant_folding_c::visit(il_expression_c *symbol) { msousa@612: symbol_c *prev_il_instruction_backup = prev_il_instruction; msousa@612: msousa@690: /* Stage2 will insert an artificial (and equivalent) LD to the simple_instr_list if necessary. We can therefore ignore the 'il_operand' entry! */ msousa@690: // if (NULL != symbol->il_operand) msousa@690: // symbol->il_operand->accept(*this); msousa@612: msousa@612: if(symbol->simple_instr_list != NULL) msousa@612: symbol->simple_instr_list->accept(*this); msousa@612: msousa@612: /* Now do the operation, */ msousa@612: il_operand = symbol->simple_instr_list; msousa@612: prev_il_instruction = prev_il_instruction_backup; msousa@612: symbol->il_expr_operator->accept(*this); msousa@612: il_operand = NULL; msousa@612: msousa@612: /* This object has (inherits) the same cvalues as the il_instruction */ msousa@612: symbol->const_value = symbol->il_expr_operator->const_value; msousa@690: msousa@690: /* Since stage2 will insert an artificial (and equivalent) LD to the simple_instr_list when an 'il_operand' exists, we know msousa@690: * that if (symbol->il_operand != NULL), then the first IL instruction in the simple_instr_list will be the equivalent and artificial msousa@690: * 'LD ' IL instruction. msousa@690: * Just to be cosistent, we will copy the constant info back into the il_operand, even though this should not be necessary! msousa@690: */ msousa@690: 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! msousa@690: if (NULL != symbol->il_operand) msousa@690: symbol->il_operand->const_value = ((list_c *)symbol->simple_instr_list)->elements[0]->const_value; msousa@690: msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: msousa@612: void *constant_folding_c::visit(il_jump_operation_c *symbol) { msousa@612: /* recursive call to fill const values... */ msousa@612: il_operand = NULL; msousa@612: symbol->il_jump_operator->accept(*this); msousa@612: il_operand = NULL; msousa@612: /* This object has (inherits) the same cvalues as the il_jump_operator */ msousa@612: symbol->const_value = symbol->il_jump_operator->const_value; msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: msousa@612: /* FB calls leave the value in the accumulator unchanged */ msousa@612: /* il_call_operator prev_declared_fb_name msousa@612: * | il_call_operator prev_declared_fb_name '(' ')' msousa@612: * | il_call_operator prev_declared_fb_name '(' eol_list ')' msousa@612: * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' msousa@612: * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' msousa@612: */ msousa@612: /* 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 */ msousa@612: // SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list, symbol_c *called_fb_declaration) msousa@612: void *constant_folding_c::visit(il_fb_call_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: msousa@612: msousa@612: /* TODO: handle function invocations... */ msousa@612: /* | function_name '(' eol_list [il_param_list] ')' */ msousa@612: /* NOTE: The parameter 'called_function_declaration' is used to pass data between the stage 3 and stage 4. */ msousa@612: // SYM_REF2(il_formal_funct_call_c, function_name, il_param_list, symbol_c *called_function_declaration; int extensible_param_count;) msousa@612: // void *constant_folding_c::visit(il_formal_funct_call_c *symbol) {return NULL;} msousa@612: msousa@612: msousa@612: msousa@612: /* Not needed, since we inherit from iterator_visitor_c */ msousa@612: // void *constant_folding_c::visit(il_operand_list_c *symbol); msousa@612: msousa@612: msousa@612: msousa@612: /* | simple_instr_list il_simple_instruction */ msousa@612: /* This object is referenced by il_expression_c objects */ msousa@612: void *constant_folding_c::visit(simple_instr_list_c *symbol) { msousa@612: if (symbol->n <= 0) msousa@612: return NULL; /* List is empty! Nothing to do. */ msousa@612: msousa@612: for(int i = 0; i < symbol->n; i++) msousa@612: symbol->elements[i]->accept(*this); msousa@612: msousa@612: /* This object has (inherits) the same cvalues as the il_jump_operator */ msousa@612: symbol->const_value = symbol->elements[symbol->n-1]->const_value; msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: msousa@612: // SYM_REF1(il_simple_instruction_c, il_simple_instruction, symbol_c *prev_il_instruction;) msousa@612: void *constant_folding_c::visit(il_simple_instruction_c *symbol) { msousa@612: if (symbol->prev_il_instruction.size() > 1) ERROR; /* There should be no labeled insructions inside an IL expression! */ msousa@612: if (symbol->prev_il_instruction.size() == 0) prev_il_instruction = NULL; msousa@612: else prev_il_instruction = symbol->prev_il_instruction[0]; msousa@612: symbol->il_simple_instruction->accept(*this); msousa@612: prev_il_instruction = NULL; msousa@612: msousa@612: /* This object has (inherits) the same cvalues as the il_jump_operator */ msousa@612: symbol->const_value = symbol->il_simple_instruction->const_value; msousa@612: return NULL; msousa@612: } msousa@612: msousa@612: msousa@612: /* msousa@612: void *visit(il_param_list_c *symbol); msousa@612: void *visit(il_param_assignment_c *symbol); msousa@612: void *visit(il_param_out_assignment_c *symbol); msousa@612: */ msousa@612: msousa@612: msousa@612: /*******************/ msousa@612: /* B 2.2 Operators */ msousa@612: /*******************/ msousa@612: void *constant_folding_c::visit( LD_operator_c *symbol) {return handle_move(symbol, il_operand);} msousa@612: void *constant_folding_c::visit( LDN_operator_c *symbol) {return handle_not (symbol, il_operand);} msousa@612: msousa@612: /* NOTE: we are implementing a constant folding algorithm, not a constant propagation algorithm. msousa@612: * For the constant propagation algorithm, the correct implementation of ST(N)_operator_c would be... msousa@612: */ msousa@612: //void *constant_folding_c::visit( ST_operator_c *symbol) {return handle_move(il_operand, symbol);} msousa@612: //void *constant_folding_c::visit( STN_operator_c *symbol) {return handle_not (il_operand, symbol);} msousa@612: void *constant_folding_c::visit( ST_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( STN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: msousa@612: /* NOTE: the standard allows syntax in which the NOT operator is followed by an optional msousa@612: * NOT [] msousa@612: * However, it does not define the semantic of the NOT operation when the is specified. msousa@612: * We therefore consider it an error if an il_operand is specified! This error will be caught elsewhere! msousa@612: */ msousa@612: void *constant_folding_c::visit( NOT_operator_c *symbol) {return handle_not(symbol, prev_il_instruction);} msousa@612: msousa@612: /* NOTE: Since we are only implementing a constant folding algorithm, and not a constant propagation algorithm, msousa@612: * the following IL instructions do not change/set the value of the il_operand! msousa@612: */ msousa@612: void *constant_folding_c::visit( S_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( R_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: msousa@612: /* FB calls leave the value in the accumulator unchanged */ msousa@612: void *constant_folding_c::visit( S1_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( R1_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( CLK_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( CU_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( CD_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( PV_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( IN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( PT_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: msousa@612: void *constant_folding_c::visit( AND_operator_c *symbol) {return handle_and (symbol, prev_il_instruction, il_operand);} msousa@612: void *constant_folding_c::visit( OR_operator_c *symbol) {return handle_or (symbol, prev_il_instruction, il_operand);} msousa@612: void *constant_folding_c::visit( XOR_operator_c *symbol) {return handle_xor (symbol, prev_il_instruction, il_operand);} msousa@612: void *constant_folding_c::visit( ANDN_operator_c *symbol) { handle_and (symbol, prev_il_instruction, il_operand); return handle_not(symbol, symbol);} msousa@612: void *constant_folding_c::visit( ORN_operator_c *symbol) { handle_or (symbol, prev_il_instruction, il_operand); return handle_not(symbol, symbol);} msousa@612: void *constant_folding_c::visit( XORN_operator_c *symbol) { handle_xor (symbol, prev_il_instruction, il_operand); return handle_not(symbol, symbol);} msousa@612: msousa@612: void *constant_folding_c::visit( ADD_operator_c *symbol) {return handle_add (symbol, prev_il_instruction, il_operand);} msousa@612: void *constant_folding_c::visit( SUB_operator_c *symbol) {return handle_sub (symbol, prev_il_instruction, il_operand);} msousa@612: void *constant_folding_c::visit( MUL_operator_c *symbol) {return handle_mul (symbol, prev_il_instruction, il_operand);} msousa@612: void *constant_folding_c::visit( DIV_operator_c *symbol) {return handle_div (symbol, prev_il_instruction, il_operand);} msousa@612: void *constant_folding_c::visit( MOD_operator_c *symbol) {return handle_mod (symbol, prev_il_instruction, il_operand);} msousa@612: msousa@612: void *constant_folding_c::visit( GT_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, > );} msousa@612: void *constant_folding_c::visit( GE_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, >=);} msousa@612: void *constant_folding_c::visit( EQ_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, ==);} msousa@612: void *constant_folding_c::visit( LT_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, < );} msousa@612: void *constant_folding_c::visit( LE_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, <=);} msousa@612: void *constant_folding_c::visit( NE_operator_c *symbol) { handle_cmp (symbol, prev_il_instruction, il_operand, !=);} msousa@612: msousa@612: void *constant_folding_c::visit( CAL_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( RET_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( JMP_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( CALC_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit(CALCN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( RETC_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit(RETCN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit( JMPC_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: void *constant_folding_c::visit(JMPCN_operator_c *symbol) {return handle_move(symbol, prev_il_instruction);} msousa@612: msousa@612: msousa@567: msousa@567: conti@564: /***************************************/ conti@564: /* B.3 - Language ST (Structured Text) */ conti@564: /***************************************/ conti@564: /***********************/ conti@564: /* B 3.1 - Expressions */ conti@564: /***********************/ msousa@612: 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);} msousa@612: 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);} msousa@612: 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);} msousa@612: msousa@612: 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, ==);} msousa@612: 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, !=);} msousa@612: 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, < );} msousa@612: 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, > );} msousa@612: 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, <=);} msousa@612: 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, >=);} msousa@612: msousa@612: 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);} msousa@612: 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);} msousa@612: 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);} msousa@612: 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);} msousa@612: 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);} msousa@612: 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);} msousa@612: msousa@612: void *constant_folding_c::visit( neg_expression_c *symbol) {symbol-> exp->accept(*this); return handle_neg(symbol, symbol->exp);} msousa@612: void *constant_folding_c::visit( not_expression_c *symbol) {symbol-> exp->accept(*this); return handle_not(symbol, symbol->exp);} msousa@612: msousa@612: /* TODO: handle function invocations... */ Laurent@633: // void *fill_candidate_datatypes_c::visit(function_invocation_c *symbol) {}