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: /* NOTE: msousa@572: * Most of the conditions to detect overflows on signed (but not unsigned) integer operations were adapted from msousa@572: * https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow?showComments=false msousa@572: */ msousa@572: 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" conti@564: #include conti@564: #include /* required for pow function */ msousa@565: #include /* required for malloc() */ conti@564: msousa@572: #if 1 msousa@572: #define INT64_MAX (std::numeric_limits< int64_t >::max()) msousa@572: #define INT64_MIN (std::numeric_limits< int64_t >::min()) msousa@572: #else msousa@572: /* An alternative is to use the std::numeric_limits< uint64_t >::min() / max() methods already defined in #include */ msousa@572: #define __STDC_LIMIT_MACROS /* required for UINT64_MAX, INT64_MAX, INT64_MIN, ... */ msousa@572: #include /* required for UINT64_MAX, INT64_MAX, INT64_MIN, ... */ msousa@572: #endif 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@569: #define NEW_CVALUE(dtype, symbol) \ msousa@569: (symbol->const_value_##dtype) = new(symbol_c::const_value_##dtype##_t); \ msousa@569: if ((symbol->const_value_##dtype) == NULL) ERROR; \ msousa@569: (symbol->const_value_##dtype)->status = symbol_c::cs_undefined; msousa@569: msousa@569: #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@569: #define GET_CVALUE(dtype, symbol) ((symbol)->const_value_##dtype->value) msousa@569: #define SET_OVFLOW(dtype, symbol) ((symbol)->const_value_##dtype->status) = symbol_c::cs_overflow msousa@569: /* The following test is correct in the presence of a NULL pointer, as the logical evaluation will be suspended as soon as the first condition is false! */ msousa@569: #define VALID_CVALUE(dtype, symbol) ((NULL != (symbol)->const_value_##dtype) && (symbol_c::cs_const_value == (symbol)->const_value_##dtype->status)) msousa@569: #define ISZERO_CVALUE(dtype, symbol) ((VALID_CVALUE(dtype, symbol)) && (GET_CVALUE(dtype, symbol) == 0)) msousa@569: #define DO_BIN_OPER(dtype, oper)\ msousa@569: if (VALID_CVALUE(dtype, symbol->r_exp) && VALID_CVALUE(dtype, symbol->l_exp)) { \ msousa@569: NEW_CVALUE(dtype, symbol); \ msousa@569: SET_CVALUE(dtype, symbol, GET_CVALUE(dtype, symbol->l_exp) oper GET_CVALUE(dtype, symbol->r_exp)); \ msousa@569: } msousa@569: conti@564: conti@564: conti@564: conti@564: msousa@572: msousa@572: msousa@572: msousa@572: msousa@572: 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@572: if (VALID_CVALUE(uint64, res)) msousa@572: /* If sum is smaller than either operand => overflow! */ msousa@572: if (GET_CVALUE(uint64, res) < GET_CVALUE(uint64, a)) msousa@572: SET_OVFLOW(uint64, res); msousa@572: } 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@572: if (VALID_CVALUE(uint64, res)) msousa@572: /* If diference is larger than a => overflow! */ msousa@572: if (GET_CVALUE(uint64, res) > GET_CVALUE(uint64, a)) msousa@572: SET_OVFLOW(uint64, res); msousa@572: } 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@572: if (VALID_CVALUE(uint64, res)) msousa@572: if (false /* TODO */) msousa@572: SET_OVFLOW(uint64, res); msousa@572: } 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@572: if (VALID_CVALUE(uint64, res)) msousa@572: if (false /* TODO */) msousa@572: SET_OVFLOW(uint64, res); msousa@572: } 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@572: if (VALID_CVALUE(uint64, res)) msousa@572: if (false /* TODO */) msousa@572: SET_OVFLOW(uint64, res); msousa@572: } msousa@572: 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@572: int64_t a = GET_CVALUE(int64, a_ptr); msousa@572: int64_t b = GET_CVALUE(int64, b_ptr); msousa@572: if (VALID_CVALUE(int64, res)) msousa@572: /* The following test is valid no matter what representation is being used (e.g. two's complement, etc...) */ msousa@572: if (((b > 0) && (a > (INT64_MAX - b))) msousa@572: || ((b < 0) && (a < (INT64_MIN - b)))) msousa@572: SET_OVFLOW(int64, res); msousa@572: } 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@572: int64_t a = GET_CVALUE(int64, a_ptr); msousa@572: int64_t b = GET_CVALUE(int64, b_ptr); msousa@572: if (VALID_CVALUE(int64, res)) msousa@572: /* The following test is valid no matter what representation is being used (e.g. two's complement, etc...) */ msousa@572: if (((b > 0) && (a < (INT64_MIN + b))) msousa@572: || ((b < 0) && (a > (INT64_MAX + b)))) msousa@572: 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@572: int64_t a = GET_CVALUE(int64, a_ptr); msousa@572: int64_t b = GET_CVALUE(int64, b_ptr); msousa@572: if (VALID_CVALUE(int64, res)) msousa@572: if ( ( (a > 0) && (b > 0) && (a > (INT64_MAX / b))) msousa@572: || ( (a > 0) && !(b > 0) && (b < (INT64_MIN / a))) msousa@572: || (!(a > 0) && (b > 0) && (a < (INT64_MIN / b))) msousa@572: || (!(a > 0) && !(b > 0) && (a != 0) && (b < (INT64_MAX / a)))) msousa@572: 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@572: int64_t a = GET_CVALUE(int64, a_ptr); msousa@572: int64_t b = GET_CVALUE(int64, b_ptr); msousa@572: if (VALID_CVALUE(int64, res)) msousa@572: if ((b == 0) || ((a == INT64_MIN) && (b == -1))) msousa@572: 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@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@572: if (VALID_CVALUE(int64, res)) msousa@572: if ((a == INT64_MIN) && (b == -1)) msousa@572: SET_OVFLOW(int64, res); msousa@572: } msousa@572: msousa@572: msousa@572: /* res = - a */ msousa@572: static void CHECK_OVERFLOW_int64_NEG(symbol_c *res, symbol_c *a_ptr) { msousa@572: int64_t a = GET_CVALUE(int64, a_ptr); msousa@572: if (VALID_CVALUE(int64, res)) msousa@572: if (a == INT64_MIN) msousa@572: SET_OVFLOW(int64, res); msousa@572: } msousa@572: msousa@572: msousa@572: msousa@572: static void CHECK_OVERFLOW_real64(symbol_c *res) { msousa@572: if (VALID_CVALUE(real64, res)) msousa@572: /* NaN => underflow, overflow, number is a higher precision format, is a complex number (IEEE standard) */ msousa@572: if (isnan(GET_CVALUE(real64, res))) msousa@572: SET_OVFLOW(real64, res); msousa@572: } msousa@572: msousa@572: msousa@572: msousa@572: msousa@572: msousa@572: msousa@572: msousa@572: 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; 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@568: STAGE3_WARNING(&null_symbol, &null_symbol, "The platform running the compiler does not implement IEC 559 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@569: " (i.e. constant folding) may themselves be erroneous, although are most probably correct."); 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@569: NEW_CVALUE(real64, symbol); msousa@569: SET_CVALUE(real64, symbol, extract_real_value(symbol)); msousa@567: return NULL; msousa@567: } msousa@567: conti@564: conti@564: void *constant_folding_c::visit(integer_c *symbol) { msousa@569: NEW_CVALUE( int64, symbol); msousa@569: SET_CVALUE( int64, symbol, extract_integer_value(symbol)); msousa@569: NEW_CVALUE(uint64, symbol); msousa@569: SET_CVALUE(uint64, symbol, extract_integer_value(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@569: if (!VALID_CVALUE(real64, symbol->exp)) msousa@569: return NULL; msousa@569: NEW_CVALUE(real64, symbol); msousa@569: SET_CVALUE(real64, symbol, - GET_CVALUE( real64, symbol->exp)); msousa@572: CHECK_OVERFLOW_real64(symbol); msousa@569: return NULL; msousa@569: } 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@569: if (VALID_CVALUE(int64, symbol->exp)) { msousa@569: NEW_CVALUE( int64, symbol); msousa@569: SET_CVALUE( int64, symbol, - GET_CVALUE( int64, symbol->exp)); msousa@569: } msousa@572: CHECK_OVERFLOW_int64_NEG(symbol, symbol->exp); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(binary_integer_c *symbol) { conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(octal_integer_c *symbol) { conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(hex_integer_c *symbol) { msousa@569: NEW_CVALUE( int64, symbol); msousa@569: SET_CVALUE( int64, symbol, extract_hex_value(symbol)); msousa@569: NEW_CVALUE(uint64, symbol); msousa@569: SET_CVALUE(uint64, symbol, extract_hex_value(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@569: if (VALID_CVALUE( int64, symbol->value)) { msousa@569: NEW_CVALUE( int64, symbol); msousa@569: SET_CVALUE( int64, symbol, GET_CVALUE( int64, symbol->value)); msousa@569: } msousa@569: if (VALID_CVALUE(uint64, symbol->value)) { msousa@569: NEW_CVALUE(uint64, symbol); msousa@569: SET_CVALUE(uint64, symbol, GET_CVALUE(uint64, symbol->value)); msousa@569: } 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@569: if (VALID_CVALUE(real64, symbol->value)) { msousa@569: NEW_CVALUE(real64, symbol); msousa@569: SET_CVALUE(real64, symbol, GET_CVALUE(real64, symbol->value)); msousa@569: } 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@569: if (VALID_CVALUE(bool, symbol->value)) { msousa@569: NEW_CVALUE(bool, symbol); msousa@569: SET_CVALUE(bool, symbol, GET_CVALUE( bool, symbol->value)); msousa@569: } conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(boolean_true_c *symbol) { msousa@569: NEW_CVALUE(bool, symbol); msousa@569: 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@569: NEW_CVALUE(bool, symbol); msousa@569: SET_CVALUE(bool, symbol, false); msousa@569: return NULL; msousa@569: } 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: /***********************/ conti@564: void *constant_folding_c::visit(or_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@569: DO_BIN_OPER(bool, ||); msousa@569: DO_BIN_OPER(uint64, | ); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(xor_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@569: DO_BIN_OPER(bool, ^); msousa@569: DO_BIN_OPER(uint64, ^); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(and_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@569: DO_BIN_OPER(bool, &&); msousa@569: DO_BIN_OPER(uint64, & ); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(equ_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@569: DO_BIN_OPER(bool, ==); msousa@569: DO_BIN_OPER(uint64, ==); msousa@569: DO_BIN_OPER( int64, ==); msousa@569: DO_BIN_OPER(real64, ==); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(notequ_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@569: DO_BIN_OPER(bool, !=); msousa@569: DO_BIN_OPER(uint64, !=); msousa@569: DO_BIN_OPER( int64, !=); msousa@569: DO_BIN_OPER(real64, !=); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(lt_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@569: DO_BIN_OPER(bool, <); msousa@569: DO_BIN_OPER(uint64, <); msousa@569: DO_BIN_OPER( int64, <); msousa@569: DO_BIN_OPER(real64, <); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(gt_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@569: DO_BIN_OPER(bool, >); msousa@569: DO_BIN_OPER(uint64, >); msousa@569: DO_BIN_OPER( int64, >); msousa@569: DO_BIN_OPER(real64, >); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(le_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@569: DO_BIN_OPER(bool, <=); msousa@569: DO_BIN_OPER(uint64, <=); msousa@569: DO_BIN_OPER( int64, <=); msousa@569: DO_BIN_OPER(real64, <=); conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(ge_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@569: DO_BIN_OPER(bool, >=); msousa@569: DO_BIN_OPER(uint64, >=); msousa@569: DO_BIN_OPER( int64, >=); msousa@569: DO_BIN_OPER(real64, >=); msousa@569: return NULL; msousa@569: } msousa@569: msousa@569: conti@564: void *constant_folding_c::visit(add_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@572: DO_BIN_OPER(uint64, +); CHECK_OVERFLOW_uint64_SUM(symbol, symbol->l_exp, symbol->r_exp); msousa@572: DO_BIN_OPER( int64, +); CHECK_OVERFLOW_int64_SUM (symbol, symbol->l_exp, symbol->r_exp); msousa@572: DO_BIN_OPER(real64, +); CHECK_OVERFLOW_real64 (symbol); msousa@572: return NULL; msousa@572: } msousa@572: msousa@572: conti@564: void *constant_folding_c::visit(sub_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@572: DO_BIN_OPER(uint64, -); CHECK_OVERFLOW_uint64_SUB(symbol, symbol->l_exp, symbol->r_exp); msousa@572: DO_BIN_OPER( int64, -); CHECK_OVERFLOW_int64_SUB (symbol, symbol->l_exp, symbol->r_exp); msousa@572: DO_BIN_OPER(real64, -); CHECK_OVERFLOW_real64 (symbol); msousa@572: return NULL; msousa@572: } msousa@572: msousa@572: conti@564: void *constant_folding_c::visit(mul_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@572: DO_BIN_OPER(uint64, *); CHECK_OVERFLOW_uint64_MUL(symbol, symbol->l_exp, symbol->r_exp); msousa@572: DO_BIN_OPER( int64, *); CHECK_OVERFLOW_int64_MUL (symbol, symbol->l_exp, symbol->r_exp); msousa@572: DO_BIN_OPER(real64, *); CHECK_OVERFLOW_real64 (symbol); msousa@572: return NULL; msousa@572: } msousa@572: msousa@572: msousa@572: conti@564: void *constant_folding_c::visit(div_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@572: if (ISZERO_CVALUE(uint64, symbol->r_exp)) {NEW_CVALUE(uint64, symbol); SET_OVFLOW(uint64, symbol);} else {DO_BIN_OPER(uint64, /); CHECK_OVERFLOW_uint64_DIV(symbol, symbol->l_exp, symbol->r_exp);}; msousa@572: if (ISZERO_CVALUE( int64, symbol->r_exp)) {NEW_CVALUE( int64, symbol); SET_OVFLOW( int64, symbol);} else {DO_BIN_OPER( int64, /); CHECK_OVERFLOW_int64_DIV(symbol, symbol->l_exp, symbol->r_exp);}; msousa@572: if (ISZERO_CVALUE(real64, symbol->r_exp)) {NEW_CVALUE(real64, symbol); SET_OVFLOW(real64, symbol);} else {DO_BIN_OPER(real64, /); CHECK_OVERFLOW_real64(symbol);}; msousa@572: return NULL; msousa@572: } msousa@572: msousa@572: conti@564: void *constant_folding_c::visit(mod_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); 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: if (ISZERO_CVALUE(uint64, symbol->r_exp)) {NEW_CVALUE(uint64, symbol); SET_CVALUE(uint64, symbol, 0);} else {DO_BIN_OPER(uint64, %); CHECK_OVERFLOW_uint64_MOD(symbol, symbol->l_exp, symbol->r_exp);}; msousa@572: if (ISZERO_CVALUE( int64, symbol->r_exp)) {NEW_CVALUE( int64, symbol); SET_CVALUE( int64, symbol, 0);} else {DO_BIN_OPER( int64, %); CHECK_OVERFLOW_int64_MOD(symbol, symbol->l_exp, symbol->r_exp);}; conti@564: return NULL; conti@564: } conti@564: msousa@567: conti@564: void *constant_folding_c::visit(power_expression_c *symbol) { conti@564: symbol->l_exp->accept(*this); conti@564: symbol->r_exp->accept(*this); msousa@569: /* NOTE: If the const_value in symbol->r_exp is within the limits of both int64 and uint64, then we do both operations. msousa@569: * That is OK, as the result should be identicial (we do create an unnecessary CVALUE variable, but who cares?). msousa@569: * If only one is valid, then that is the oper we will do! msousa@569: */ msousa@569: if (VALID_CVALUE(real64, symbol->l_exp) && VALID_CVALUE( int64, symbol->r_exp)) { msousa@569: NEW_CVALUE(real64, symbol); msousa@569: SET_CVALUE(real64, symbol, pow(GET_CVALUE(real64, symbol->l_exp), GET_CVALUE( int64, symbol->r_exp))); msousa@569: } msousa@569: if (VALID_CVALUE(real64, symbol->l_exp) && VALID_CVALUE(uint64, symbol->r_exp)) { msousa@569: NEW_CVALUE(real64, symbol); msousa@569: SET_CVALUE(real64, symbol, pow(GET_CVALUE(real64, symbol->l_exp), GET_CVALUE(uint64, symbol->r_exp))); msousa@569: } msousa@572: CHECK_OVERFLOW_real64(symbol); msousa@572: return NULL; msousa@572: } msousa@572: msousa@572: conti@564: void *constant_folding_c::visit(neg_expression_c *symbol) { conti@564: symbol->exp->accept(*this); msousa@569: if (VALID_CVALUE( int64, symbol->exp)) { msousa@569: NEW_CVALUE( int64, symbol); msousa@569: SET_CVALUE( int64, symbol, - GET_CVALUE( int64, symbol->exp)); msousa@569: } msousa@569: if (VALID_CVALUE(real64, symbol->exp)) { msousa@569: NEW_CVALUE(real64, symbol); msousa@569: SET_CVALUE(real64, symbol, - GET_CVALUE(real64, symbol->exp)); msousa@569: } msousa@572: CHECK_OVERFLOW_int64_NEG(symbol, symbol->exp); msousa@572: CHECK_OVERFLOW_real64(symbol); conti@564: return NULL; conti@564: } conti@564: msousa@567: msousa@567: conti@564: void *constant_folding_c::visit(not_expression_c *symbol) { conti@564: symbol->exp->accept(*this); msousa@569: if (VALID_CVALUE( bool, symbol->exp)) { msousa@569: NEW_CVALUE( bool, symbol); msousa@569: SET_CVALUE( bool, symbol, ! GET_CVALUE( bool, symbol->exp)); msousa@569: } msousa@569: if (VALID_CVALUE(uint64, symbol->exp)) { msousa@569: NEW_CVALUE(uint64, symbol); msousa@569: SET_CVALUE(uint64, symbol, ~ GET_CVALUE(uint64, symbol->exp)); msousa@569: } msousa@569: return NULL; msousa@569: } msousa@569: msousa@569: