/* State will store states of variables for a function's single execution path. It will be used for bit-level symbolic execution to determine values of bits of function's return value and symbolic marked arguments. Copyright (C) 2022-2025 Free Software Foundation, Inc. Contributed by Matevos Mehrabyan <matevosmehrabyan@gmail.com> This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ /* This symbolic executor is designed to handle operations on the bit level. It can save values of variables on the bit level. For example byte x = 9 would be represented by the bit-vector x = <0, 0, 0, 0, 1, 0, 1, 0> of size 8. Variables without values will be represented by bit-vectors of symbolic bits: x = <x[size - 1], ..., x[1], x[0]> where x[i] is the value of bit i of variable x. Operations are also performed on the bit level. For example, for operation z = x & y where x = <x[size - 1], ..., x[1], x[0]> y = <y[size - 1], ..., y[1], y[0]> z will have the value z = <x[size - 1] & y[size - 1], ..., x[1] & y[1], x[0] & y[0]> Each bit of variable can be accessed and examined separately if needed. Moreover, it does basic optimizations in place. For example, for operation z = x | y where x = <x[size - 1], ..., x[1], x[0]>, y = <1, ..., 0, 1>, z will have the value z = <1, ..., x[1], 1> as x | 0 == x and x | 1 == 1 Besides variables, the symbolic executor can also store conditions on the bit level. For example, for x == y It would add {x[size - 1] == y[size - 1], ..., x[1] == y[1], x[0] == y[0]} conditions. For a more complex condition x > y, it would add {x[size - 1] > y[size - 1] || (x[size - 1] == y[size -1] && (x[size - 2] > y[size - 2] || (x[size - 2] == y[size - 2] && ... (x[0] >= y[0])...)} The symbolic executor doesn't run by itself. Instead, it must be dictated what to do. This makes it flexible and allows for various pre- and post-processing tasks. Developers adding new operation support must consider that the operation must be represented on the bit level. Because of this restriction, it may be hard to add support for some operations. To use the symbolic executor, you must create a state object. It is the main object that contains variables as bit-vectors and conditions. It is the state object that provides operations for symbolic execution. If you are going to execute multiple execution paths, you should clone the state at branching instructions and execute one state for the execution path where the branching condition evaluates to 'true', and the other state for the execution path where the branching condition evaluates to 'false'. Besides that, you should add the corresponding conditions to states if you need them. Variables are stored in the state's 'var_states' field. It maps the tree object of the variable to its bit-vector. Path conditions are stored in the 'conditions' field. To declare a variable, you should use 'declare_if_needed' method of state. It declares the variable if it was not previously declared. 'create_val_for_const' is used for constant declaration. The list of supported operations can be found in 'state::do_operation' method. It calls the corresponding operation based on the specified tree_code operation. This is the method that you should use to dictate to the symbolic executor what operations to perform. You can execute the desired operations explicitly if needed. Variables for participant operands will be created implicitly if it was not previously declared. To add conditions to the state, you should use 'state::add_*_cond' methods. A sample usage of the symbolic executor: // Example. unsigned char foo (unsigned char x, unsigned char y) { unsigned char D.2352; unsigned char result; result = x & y; result = result | 9; if (result == 23) goto <D.2350>; else goto <D.2351>; <D.2350>: result = result ^ y; <D.2351>: D.2352 = result; return D.2352; } // Now, we create the initial state and add the variables to it. state s; s.declare_if_needed (x, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (x)))); s.declare_if_needed (y, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (y)))); s.declare_if_needed (d_2352, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (d_2352)))); s.declare_if_needed (result, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (result)))); s.do_operation (BIT_AND_EXPR, x, y, result); s.do_operation (BIT_OR_EXPR, result, 9, result); state s2 (s); // We duplicate the state to save values for each branch. s.add_equal_cond (result, 23); s2.add_not_equal_cond (result, 23); s.do_operation (BIT_XOR_EXPR, result, y, result); s.do_assign (result, d_2352); s2.do_assign (result, d_2352); // Now, we have variable values for each execution branch, and we can examine // them to make decisions. value * res = s.get_value (result); if (is_a<bit_expression *> ((*res)[0])) { bit_expression * expr = is_a<bit_expression *> ((*res)[0]); if (is_a<bit *> (expr->get_left ()) && as_a<bit *> (expr->get_left ())->get_val () == 0) { ... // Do something. } } A more general usage would be to iterate over instructions and call the executor: state s; ... for (inst : instructions) { enum tree_code rhs_code = gimple_assign_rhs_code (inst); tree op1 = gimple_assign_rhs1 (gs); tree op2 = gimple_assign_rhs2 (gs); tree lhs = gimple_assign_lhs (gs); s.do_operation (rhs_code, op1, op2, lhs); ... } */ #include "sym-exec-state.h" /* Returns the minimum of A, B, C. */ size_t min (size_t a, size_t b, size_t c) { size_t min = (a < b ? a : b); return min < c ? min : c; } /* Copy constructor for state. It copies all variables and conditions of the given state. */ state::state (const state &s) { for (auto iter = s.var_states.begin (); iter != s.var_states.end (); ++iter) { value val ((*iter).second.length (), (*iter).second.is_unsigned); for (size_t i = 0; i < (*iter).second.length (); i++) val.push ((*iter).second[i]->copy ()); var_states.put ((*iter).first, val); } for (auto iter = s.conditions.begin (); iter != s.conditions.end (); ++iter) conditions.add (as_a<bit_expression *> ((*iter)->copy ())); } /* Destructor for state. */ state::~state () { clear_conditions (); } /* Checks whether state for variable with specified name already exists or not. */ bool state::is_declared (tree var) { return var_states.get (var) != NULL; } /* Declares given variable if it has not been declared yet. */ void state::declare_if_needed (tree var, size_t size) { if (TREE_CODE (var) != INTEGER_CST && !is_declared (var)) { make_symbolic (var, size); if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Declaring var "); print_generic_expr (dump_file, var, dump_flags); fprintf (dump_file, " with size %zd\n", size); } } } /* Get value of the given variable. */ value * state::get_value (tree var) { return var_states.get (var); } /* Get the value of the tree, which is in the beginning of the var_states. */ value * state::get_first_value () { return &(*(var_states.begin ())).second; } /* Returns the list of conditions in the state. */ const hash_set<bit_expression *> & state::get_conditions () { return conditions; } /* Checks if sizes of arguments and destination are compatible. */ bool state::check_args_compatibility (tree arg1, tree arg2, tree dest) { if (!(get_var_size (arg1) == get_var_size (dest) || TREE_CODE (arg1) == INTEGER_CST) || !(get_var_size (arg2) == get_var_size (dest) || TREE_CODE (arg2) == INTEGER_CST)) { if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "Sym-Exec: Incompatible destination" "and argument sizes.\n"); return false; } return true; } /* Creates value for given constant tree. */ value state::create_val_for_const (tree var, size_t size) { unsigned HOST_WIDE_INT val = TYPE_UNSIGNED (TREE_TYPE (var)) ? tree_to_uhwi (var) : tree_to_shwi (var); value result (size, TYPE_UNSIGNED (TREE_TYPE (var))); for (size_t i = 0; i < size; i++) { result.push (new bit (val & 1)); val >>= 1; } return result; } /* Adds the given variable to state. */ bool state::add_var_state (tree var, value *vstate) { size_t size = vstate->length (); value val (size, vstate->is_unsigned); for (size_t i = 0; i < size; i++) val.push ((*vstate)[i]->copy ()); return var_states.put (var, val); } /* Adds the given condition to the state. */ bool state::add_condition (bit_expression *cond) { return conditions.add (as_a<bit_expression *> (cond->copy ())); } /* Bulk add the given conditions to the state. */ bool state::bulk_add_conditions (const hash_set<bit_expression *> &conds) { bool status = true; for (auto iter = conds.begin (); iter != conds.end (); ++iter) status &= add_condition (*iter); return status; } /* Remove all states from the states' vector. */ void state::remove_states (vec<state *> *states) { while (!states->is_empty ()) { delete states->last (); states->pop (); } } /* Remove all states from the states' vector and release the vector. */ void state::clear_states (vec<state *> *states) { remove_states (states); states->release (); } /* Removes all variables added to the state. */ void state::clear_var_states () { var_states.empty (); } /* Removes all conditions added to the state. */ void state::clear_conditions () { for (auto iter = conditions.begin (); iter != conditions.end (); ++iter) delete (*iter); conditions.empty (); } /* Performs AND operation for 2 symbolic_bit operands. */ value_bit * state::and_sym_bits (const value_bit *var1, const value_bit *var2) { return new bit_and_expression (var1->copy (), var2->copy ()); } /* Performs AND operation for a symbolic_bit and const_bit operands. */ value_bit * state::and_var_const (const value_bit *var1, const bit *const_bit) { if (const_bit->get_val () == 1) return var1->copy (); return new bit (0); } /* Performs AND operation for 2 constant bit operands. */ bit * state::and_const_bits (const bit *const_bit1, const bit *const_bit2) { if (const_bit1->get_val () == const_bit2->get_val ()) return new bit (*const_bit1); return new bit (0); } /* Performs OR operation for 2 symbolic_bit operands. */ value_bit * state::or_sym_bits (const value_bit *var1, const value_bit *var2) { return new bit_or_expression (var1->copy (), var2->copy ()); } /* Performs OR operation for a symbolic_bit and a constant bit operands. */ value_bit * state::or_var_const (const value_bit *var1, const bit *const_bit) { if (const_bit->get_val () == 0) return var1->copy (); return new bit (1); } /* Performs OR operation for 2 constant bit operands. */ bit * state::or_const_bits (const bit *const_bit1, const bit *const_bit2) { if (const_bit1->get_val () == const_bit2->get_val ()) return new bit (*const_bit1); return new bit (1); } /* Adds an empty state for the given variable. */ bool state::decl_var (tree var, unsigned size) { if (is_declared (var)) return false; value val (size, TYPE_UNSIGNED (TREE_TYPE (var))); for (unsigned i = 0; i < size; i++) val.push (nullptr); return var_states.put (var, val); } /* Returns size of the given variable. */ unsigned state::get_var_size (tree var) { value *content = var_states.get (var); if (content == NULL) return 0; return content->allocated (); } /* Adds a variable with unknown value to state. Such variables are represented as sequence of symbolic bits. */ bool state::make_symbolic (tree var, unsigned size) { if (is_declared (var)) return false; value val (size, TYPE_UNSIGNED (TREE_TYPE (var))); /* Initialize each bit of a variable with unknown value. */ for (size_t i = 0; i < size; i++) val.push (new symbolic_bit (i, var)); return var_states.put (var, val); } /* Performs AND operation on two bits. */ value_bit * state::and_two_bits (value_bit *arg1, value_bit *arg2) { value_bit *result = nullptr; if (is_a<bit *> (arg1) && is_a<bit *> (arg2)) result = and_const_bits (as_a<bit *> (arg1), as_a<bit *> (arg2)); else if (is_a<bit *> (arg1) && (is_a<symbolic_bit *> (arg2) || (is_a<bit_expression *> (arg2)))) result = and_var_const (arg2, as_a<bit *> (arg1)); else if ((is_a<symbolic_bit *> (arg1) || (is_a<bit_expression *> (arg1))) && is_a<bit *> (arg2)) result = and_var_const (arg1, as_a<bit *> (arg2)); else result = and_sym_bits (arg1, arg2); return result; } /* A wrapper for operations on two bits. Operation and operands are passed as arguments. */ value_bit * state::operate_bits (bit_func bit_op, value_bit *bit1, value_bit *bit2, value_bit **) { return (bit_op) (bit1, bit2); } /* A wrapper for operations on three bits. Operation and operands are passed as arguments. */ value_bit * state::operate_bits (bit_func3 bit_op, value_bit *bit1, value_bit *bit2, value_bit **bit3) { return (bit_op) (bit1, bit2, bit3); } /* Does preprocessing and postprocessing for expressions with tree operands. Handles value creation for constant and their removement in the end. */ bool state::do_binary_operation (tree arg1, tree arg2, tree dest, binary_func bin_func) { declare_if_needed (dest, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (dest)))); declare_if_needed (arg1, var_states.get (dest)->allocated ()); declare_if_needed (arg2, var_states.get (dest)->allocated ()); if (!check_args_compatibility (arg1, arg2, dest)) return false; size_t dest_size = var_states.get (dest)->length (); value *arg1_val = var_states.get (arg1); value arg1_const_val (dest_size, false); if (arg1_val == NULL && TREE_CODE (arg1) == INTEGER_CST) { arg1_const_val = create_val_for_const (arg1, dest_size); arg1_val = &arg1_const_val; } value *arg2_val = var_states.get (arg2); value arg2_const_val (dest_size, false); if (arg2_val == NULL && TREE_CODE (arg2) == INTEGER_CST) { arg2_const_val = create_val_for_const (arg2, dest_size); arg2_val = &arg2_const_val; } (this->*bin_func) (arg1_val, arg2_val, dest); print_value (var_states.get (dest)); return true; } /* Performs AND operation on given values. The result is stored in dest. */ void state::do_and (value *arg1, value *arg2, tree dest) { /* Creating AND expressions for every bit pair of given arguments and store them as a new state for given destination. */ operate (arg1, arg2, nullptr, dest, &state::and_two_bits); } /* Performs OR operation on two bits. */ value_bit * state::or_two_bits (value_bit *arg1_bit, value_bit *arg2_bit) { value_bit *result = nullptr; if (is_a<bit *> (arg1_bit) && is_a<bit *> (arg2_bit)) result = or_const_bits (as_a<bit *> (arg1_bit), as_a<bit *> (arg2_bit)); else if (is_a<bit *> (arg1_bit) && (is_a<symbolic_bit *> (arg2_bit) || is_a<bit_expression *> (arg2_bit))) result = or_var_const (arg2_bit, as_a<bit *> (arg1_bit)); else if ((is_a<symbolic_bit *> (arg1_bit) || is_a<bit_expression *> (arg1_bit)) && is_a<bit *> (arg2_bit)) result = or_var_const (arg1_bit, as_a<bit *> (arg2_bit)); else result = or_sym_bits (arg1_bit, arg2_bit); return result; } /* Performs OR operation on given values. The result is stored in dest. */ void state::do_or (value *arg1, value *arg2, tree dest) { /* Creating OR expressions for every bit pair of given arguments and store them as a new state for given destination. */ operate (arg1, arg2, nullptr, dest, &state::or_two_bits); } /* Performs XOR operation on two bits. */ value_bit * state::xor_two_bits (value_bit *arg1_bit, value_bit *arg2_bit) { value_bit *result = nullptr; if (is_a<bit *> (arg1_bit) && is_a<bit *> (arg2_bit)) result = xor_const_bits (as_a<bit *> (arg1_bit), as_a<bit *> (arg2_bit)); else if (is_a<bit *> (arg1_bit) && (is_a<symbolic_bit *> (arg2_bit) || is_a<bit_expression *> (arg2_bit))) result = xor_var_const (arg2_bit, as_a<bit *> (arg1_bit)); else if ((is_a<symbolic_bit *> (arg1_bit) || is_a<bit_expression *> (arg1_bit)) && is_a<bit *> (arg2_bit)) result = xor_var_const (arg1_bit, as_a<bit *> (arg2_bit)); else result = xor_sym_bits (arg1_bit, arg2_bit); return result; } /* Performs XOR operation on given values. The result is stored in dest. */ void state::do_xor (value *arg1, value *arg2, tree dest) { operate (arg1, arg2, nullptr, dest, &state::xor_two_bits); } /* Shifts value right by size of shift_value. */ value * state::shift_right_by_const (value *var, size_t shift_value) { value *shift_result = new value (var->length (), var->is_unsigned); if (var->length () <= shift_value) for (size_t i = 0; i < var->length (); i++) shift_result->push (new bit (0)); else { size_t i = 0; for (; i < var->length () - shift_value; ++i) shift_result->push (((*var)[shift_value + i])->copy ()); for (; i < var->length (); ++i) shift_result->push (var->is_unsigned ? new bit (0) : var->last ()->copy ()); } return shift_result; } /* Checks if all bits of the given value have constant bit type. */ bool state::is_bit_vector (const value *var) { if (var == nullptr || !var->exists ()) return false; for (size_t i = 0; i < var->length (); i++) if (!(is_a<bit *> ((*var)[i]))) return false; return true; } /* Returns the number represented by the value. */ unsigned HOST_WIDE_INT state::make_number (const value *var) { unsigned HOST_WIDE_INT number = 0; int value_size = var->length (); for (int i = value_size - 1; i >= 0; i--) { if (is_a<bit *> ((*var)[i])) number = (number << 1) | as_a<bit *> ((*var)[i])->get_val (); else return 0; } return number; } /* Shift_left operation. Case: var2 is a symbolic value. */ value_bit * state::shift_left_sym_bits (value_bit *var1, value_bit *var2) { return new shift_left_expression (var1->copy (), var2->copy ()); } /* Performs shift left operation on given values. The result is stored in dest. */ void state::do_shift_left (value *arg1, value *arg2, tree dest) { if (is_bit_vector (arg2)) { size_t shift_value = make_number (arg2); value *result = shift_left_by_const (arg1, shift_value); for (size_t i = 0; i < get_var_size (dest); i++) { delete (*var_states.get (dest))[i]; (*var_states.get (dest))[i] = (*result)[i]->copy (); } delete result; } else operate (arg1, arg2, nullptr, dest, &state::shift_left_sym_bits); } /* Performs shift right operation on given values. The result is stored in dest. */ void state::do_shift_right (value *arg1, value *arg2, tree dest) { if (is_bit_vector (arg2)) { size_t shift_value = make_number (arg2); value *result = shift_right_by_const (arg1, shift_value); for (size_t i = 0; i < get_var_size (dest); i++) { delete (*var_states.get (dest))[i]; (*var_states.get (dest))[i] = (*result)[i]->copy (); } delete result; } else operate (arg1, arg2, nullptr, dest, &state::shift_right_sym_bits); } /* Adds two bits and carry value. Resturn result and stores new carry bit in "carry". */ value_bit * state::full_adder (value_bit *var1, value_bit *var2, value_bit **carry) { value_bit *new_carry = and_two_bits (var1, var2); value_bit *sum = xor_two_bits (var1, var2); value_bit *result = xor_two_bits (sum, *carry); value_bit *sum_and_carry = and_two_bits (sum, *carry); delete *carry; delete sum; *carry = or_two_bits (sum_and_carry, new_carry); delete sum_and_carry; delete new_carry; return result; } /* Adds given values. The result is stored in dest. */ void state::do_add (value *arg1, value *arg2, tree dest) { value_bit *carry = new bit (0); operate (arg1, arg2, &carry, dest, &state::full_adder); delete carry; } /* Returns the additive inverse of the given number. */ value * state::additive_inverse (const value *number) { value *result = new value (number->length (), number->is_unsigned); value one (number->length (), number->is_unsigned); size_t size = number->length (); one.push (new bit (1)); result->push (complement_a_bit ((*number)[0])); for (size_t i = 1; i < size; i++) { one.push (new bit (0)); result->push (complement_a_bit ((*number)[i])); } value_bit *carry = new bit (0); for (size_t i = 0; i < size; ++i) { value_bit *cur_bit = (*result)[i]; (*result)[i] = full_adder (cur_bit, one[i], &carry); delete cur_bit; } delete carry; return result; } /* Subtracks second value from the first. The result is stored in dest. */ void state::do_sub (value *arg1, value *arg2, tree dest) { value *neg_arg2 = additive_inverse (arg2); do_add (arg1, neg_arg2, dest); delete neg_arg2; } /* Performs complement operation on a bit. */ value_bit * state::complement_a_bit (value_bit *var) { value_bit *result = nullptr; if (is_a<bit *> (var)) result = complement_const_bit (as_a<bit *> (var)); else result = complement_sym_bit (var); return result; } /* Negates given variable. */ bool state::do_complement (tree arg, tree dest) { declare_if_needed (dest, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (dest)))); declare_if_needed (arg, var_states.get (dest)->allocated ()); /* Creating complement expressions for every bit the given argument and store it as a new state for given destination. */ size_t iter_count = min (get_var_size (dest), get_var_size (arg), get_var_size (arg)); size_t i = 0; for (; i < iter_count; i++) { value_bit *result = complement_a_bit ((*var_states.get (arg))[i]); delete (*var_states.get (dest))[i]; (*var_states.get (dest))[i] = result; } if (i >= get_var_size (dest)) { print_value (var_states.get (dest)); return true; } for (; i < get_var_size (dest); i++) { delete (*var_states.get (dest))[i]; bit tmp (0); (*var_states.get (dest))[i] = complement_a_bit (&tmp); } print_value (var_states.get (dest)); return true; } /* Does Assignment. */ bool state::do_assign (tree arg, tree dest) { declare_if_needed (dest, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (dest)))); if (TREE_CODE (arg) != INTEGER_CST) declare_if_needed (arg, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (arg)))); else declare_if_needed (arg, var_states.get (dest)->allocated ()); value *dest_val = var_states.get (dest); /* If the argument is already defined, then we must just copy its bits. */ if (auto arg_val = var_states.get (arg)) { for (size_t i = 0; i < dest_val->length (); i++) { value_bit *new_val = nullptr; if (i < arg_val->length ()) new_val = (*arg_val)[i]->copy (); else new_val = new bit (0); delete (*dest_val)[i]; (*dest_val)[i] = new_val; } } /* If the argument is a constant, we must save it as sequence of bits. */ else if (TREE_CODE (arg) == INTEGER_CST) { value arg_val = create_val_for_const (arg, dest_val->length ()); for (size_t i = 0; i < dest_val->length (); i++) { delete (*dest_val)[i]; (*dest_val)[i] = arg_val[i]->copy (); } } else { if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "Sym-Exec: Unsupported assignment" " for given argument.\n"); return false; } print_value (var_states.get (dest)); return true; } /* Assigns pow 2 value. */ bool state::do_assign_pow2 (tree dest, unsigned pow) { value *dest_val = var_states.get (dest); unsigned dest_size = dest_val ? dest_val->allocated () : tree_to_uhwi (TYPE_SIZE (TREE_TYPE (dest))); if (pow > dest_size) { if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "Sym-Exec: pow %u of 2 won't fit in" " specified destination\n", pow); return false; } if (!dest_val) { decl_var (dest, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (dest)))); dest_val = var_states.get (dest); } else dest_val->free_bits (); for (unsigned i = 0; i < dest_val->length (); i++) { if (i == pow) (*dest_val)[i] = new bit (1); else (*dest_val)[i] = new bit (0); } print_value (dest_val); return true; } /* Performs NOT operation for constant bit. */ bit * state::complement_const_bit (const bit *const_bit) { return new bit (1u ^ const_bit->get_val ()); } /* Performs NOT operation for symbolic_bit. */ value_bit * state::complement_sym_bit (const value_bit *var) { return new bit_complement_expression (var->copy ()); } /* Performs XOR operation for 2 symbolic_bit operands. */ value_bit * state::xor_sym_bits (const value_bit *var1, const value_bit *var2) { value_bit *var2_copy = var2->copy (); bit_expression *node2_with_const_child = nullptr; bit_expression *parent_of_node2_with_child = nullptr; get_parent_with_const_child (var2_copy, node2_with_const_child, parent_of_node2_with_child); if (node2_with_const_child != nullptr) { value_bit *var1_copy = var1->copy (); bit_expression *node1_with_const_child = nullptr; bit_expression *parent_of_node1_with_child = nullptr; get_parent_with_const_child (var1_copy, node1_with_const_child, parent_of_node1_with_child); /* If both subtrees have constant bit nodes, we can merge them together. */ if (node1_with_const_child != nullptr) { value_bit *var1_reformed = nullptr; value_bit *var2_reformed = nullptr; /* If var1's const bit is in its left subtree. */ value_bit *var1_left = node1_with_const_child->get_left (); if (var1_left != nullptr && is_a<bit *> (var1_left)) { var1_reformed = node1_with_const_child->get_right ()->copy (); value_bit *var2_left = node2_with_const_child->get_left (); /* If var2's const bit is in its left subtree. */ if (var2_left != nullptr && is_a<bit *> (var2_left)) var2_reformed = node2_with_const_child->get_right ()->copy (); else /* Var2's const bit is in its right subtree. */ var2_reformed = node2_with_const_child->get_left ()->copy (); } else /* Var1's const bit is in its right subtree. */ { var1_reformed = node1_with_const_child->get_left ()->copy (); value_bit *var2_left = node2_with_const_child->get_left (); /* If var2's const bit is in its left subtree. */ if (var2_left != nullptr && is_a<bit *> (var2_left)) var2_reformed = node2_with_const_child->get_right ()->copy (); else /* Var2's const bit is in its right subtree. */ var2_reformed = node2_with_const_child->get_left ()->copy (); } if (parent_of_node1_with_child) { parent_of_node1_with_child->get_left () == node1_with_const_child ? parent_of_node1_with_child->set_left (var1_reformed) : parent_of_node1_with_child->set_right (var1_reformed); delete node1_with_const_child; } else { delete var1_copy; var1_copy = var1_reformed; } if (parent_of_node2_with_child) { parent_of_node2_with_child->get_left () == node2_with_const_child ? parent_of_node2_with_child->set_left (var2_reformed) : parent_of_node2_with_child->set_right (var2_reformed); delete node2_with_const_child; } else { delete var2_copy; var2_copy = var2_reformed; } return new bit_xor_expression (var1_copy, var2_copy); } delete var1_copy; } delete var2_copy; return new bit_xor_expression (var1->copy (), var2->copy ()); } /* Performs XOR operation for 2 constant bit operands. */ bit * state::xor_const_bits (const bit *const_bit1, const bit *const_bit2) { return new bit (const_bit1->get_val () ^ const_bit2->get_val ()); } /* Performs XOR operation for a symbolic_bit and const_bit operands. */ value_bit * state::xor_var_const (const value_bit *var, const bit *const_bit) { if (const_bit->get_val () == 0) return var->copy (); value_bit *var_copy = var->copy (); bit_expression *node_with_const_child = nullptr; bit_expression *tmp = nullptr; get_parent_with_const_child (var_copy, node_with_const_child, tmp); if (node_with_const_child != nullptr) { value_bit *left = node_with_const_child->get_left (); if (left != nullptr && is_a<bit *> (left)) { bit *new_left = xor_const_bits (as_a<bit *> (left), const_bit); delete left; node_with_const_child->set_left (new_left); } else { value_bit *right = node_with_const_child->get_right (); bit *new_right = xor_const_bits (as_a<bit *> (right), const_bit); delete right; node_with_const_child->set_right (new_right); } return var_copy; } delete var_copy; return new bit_xor_expression (var->copy (), const_bit->copy ()); } /* Return node which has a const bit child. Traversal is done based on safe branching. */ void state::get_parent_with_const_child (value_bit *root, bit_expression *&parent, bit_expression *&parent_of_parent) { parent_of_parent = nullptr; parent = nullptr; if (!is_a<bit_expression *> (root)) return; bit_expression *expr_root = as_a<bit_expression *> (root); hash_set < bit_expression * > nodes_to_consider; nodes_to_consider.add (expr_root); hash_map < bit_expression * , bit_expression * > node_to_parent; node_to_parent.put (expr_root, nullptr); /* Traversing expression tree: considering only comutative expression nodes. */ while (!nodes_to_consider.is_empty ()) { bit_expression *cur_element = *nodes_to_consider.begin (); nodes_to_consider.remove (cur_element); value_bit *left = cur_element->get_left (); value_bit *right = cur_element->get_right (); if ((left != nullptr && is_a<bit *> (left)) || (right != nullptr && is_a<bit *> (right))) { parent = cur_element; parent_of_parent = *node_to_parent.get (cur_element); } if (left != nullptr && is_a<bit_xor_expression *> (left)) { nodes_to_consider.add (as_a<bit_expression *> (left)); node_to_parent.put (as_a<bit_expression *> (left), cur_element); } if (right != nullptr && is_a<bit_xor_expression *> (right)) { nodes_to_consider.add (as_a<bit_expression *> (right)); node_to_parent.put (as_a<bit_expression *> (right), cur_element); } } } /* Shifts number left by size of shift_value. */ value * state::shift_left_by_const (const value *number, size_t shift_value) { value *shift_result = new value (number->length (), number->is_unsigned); if (number->length () <= shift_value) for (size_t i = 0; i < number->length (); i++) shift_result->push (new bit (0)); else { size_t i = 0; for (; i < shift_value; ++i) shift_result->push (new bit (0)); for (size_t j = 0; i < number->length (); ++i, j++) shift_result->push (((*number)[j])->copy ()); } return shift_result; } /* Shift_right operation. Case: var2 is a symbolic value. */ value_bit * state::shift_right_sym_bits (value_bit *var1, value_bit *var2) { return new shift_right_expression (var1->copy (), var2->copy ()); } /* Adds two values, stores the result in the first one. */ void state::add_numbers (value *var1, const value *var2) { value_bit *carry = new bit (0); for (unsigned i = 0; i < var1->length (); i++) { value_bit *temp = (*var1)[i]; (*var1)[i] = full_adder ((*var1)[i], (*var2)[i], &carry); delete temp; } delete carry; } /* ANDs every bit of the vector with var_bit, stroes the result in var1. */ void state::and_number_bit (value *var1, value_bit *var_bit) { for (unsigned i = 0; i < var1->length (); i++) { value_bit *tmp = (*var1)[i]; (*var1)[i] = and_two_bits ((*var1)[i], var_bit); delete tmp; } } /* Multiplies given values. The result is stored in dest. */ void state::do_mul (value *arg1, value *arg2, tree dest) { value *shifted = new value (*arg1); value *dest_val = var_states.get (dest); for (unsigned i = 0; i < dest_val->length (); i++) { delete (*dest_val)[i]; (*dest_val)[i] = new bit (0); } for (unsigned i = arg2->length (); i != 0; --i) { if (is_a<bit *> ((*arg2)[i - 1]) && as_a<bit *> ((*arg2)[i - 1])->get_val () != 0) add_numbers (dest_val, shifted); else if (is_a<symbolic_bit *> ((*arg2)[i - 1])) { and_number_bit (shifted, as_a<symbolic_bit *> ((*arg2)[i - 1])); add_numbers (dest_val, shifted); } value *temp = shifted; shifted = shift_left_by_const (shifted, 1); delete temp; } delete shifted; } /* Checks whether the given two constant values are equal. */ bool state::check_const_value_equality (value *arg1, value *arg2) { for (size_t i = 0; i < arg1->length (); i++) if (as_a<bit *> ((*arg1)[i])->get_val () != as_a<bit *> ((*arg2)[i])->get_val ()) return false; return true; } /* Adds EQUAL condition of given variables to state. */ bool state::add_equal_cond (tree arg1, tree arg2) { return add_binary_cond (arg1, arg2, &state::add_equal_cond); } /* Adds equality condition for two values. */ void state::add_equal_cond (value *arg1, value *arg2) { /* If both arguments are constants then we can evaluate it. */ if (is_bit_vector (arg1) && is_bit_vector (arg2)) { bool result = check_const_value_equality (arg1, arg2); last_cond_status = result ? condition_status::CS_TRUE : condition_status::CS_FALSE; return; } /* When some of bits are constants and they differ by value, then we can evalate it to be false. */ for (size_t i = 0; i < arg1->length (); i++) { if (is_a<bit *> ((*arg1)[i]) && is_a<bit *> ((*arg2)[i]) && as_a<bit *> ((*arg1)[i])->get_val () != as_a<bit *> ((*arg2)[i])->get_val ()) { last_cond_status = condition_status::CS_FALSE; return; } } for (size_t i = 0; i < arg1->length (); i++) { /* If there is a constant bit pair, then they are equal as we checked not equality above. */ if (is_a<bit *> ((*arg1)[i]) && is_a<bit *> ((*arg2)[i])) continue; conditions.add (new bit_condition ((*arg1)[i]->copy (), (*arg2)[i]->copy (), EQ_EXPR)); } last_cond_status = condition_status::CS_SYM; } /* Checks whether the given two constant values are not equal. */ bool state::check_const_value_are_not_equal (value *arg1, value *arg2) { for (size_t i = 0; i < arg1->length (); i++) if (as_a<bit *> ((*arg1)[i])->get_val () != as_a<bit *> ((*arg2)[i])->get_val ()) return true; return false; } /* Adds NOT EQUAL condition of given variables to state. */ bool state::add_not_equal_cond (tree arg1, tree arg2) { return add_binary_cond (arg1, arg2, &state::add_not_equal_cond); } /* Adds not equal condition for two values. */ void state::add_not_equal_cond (value *arg1, value *arg2) { if (is_bit_vector (arg1) && is_bit_vector (arg2)) { bool result = check_const_value_are_not_equal (arg1, arg2); last_cond_status = result ? condition_status::CS_TRUE : condition_status::CS_FALSE; return; } /* When some of bits are constants and they differ by value, then we can evalate it to be true. */ for (size_t i = 0; i < arg1->length (); i++) { if (is_a<bit *> ((*arg1)[i]) && is_a<bit *> ((*arg2)[i]) && as_a<bit *> ((*arg1)[i])->get_val () != as_a<bit *> ((*arg2)[i])->get_val ()) { last_cond_status = condition_status::CS_TRUE; return; } } bit_expression *prev = nullptr; for (size_t i = 0; i < arg1->length (); i++) { /* If there is a constant bit pair, then they are equal as we checked not equality above. */ if (is_a<bit *> ((*arg1)[i]) && is_a<bit *> ((*arg2)[i])) continue; bit_condition *new_cond = new bit_condition ((*arg1)[i]->copy (), (*arg2)[i]->copy (), NE_EXPR); if (prev) prev = new bit_or_expression (prev, new_cond); else prev = new_cond; } last_cond_status = condition_status::CS_SYM; conditions.add (prev); } /* Checks whether the first given constant value is greater than the second one. */ bool state::check_const_value_is_greater_than (value *arg1, value *arg2) { for (int i = arg1->length () - 1; i >= 0; i--) { if (as_a<bit *> ((*arg1)[i])->get_val () > as_a<bit *> ((*arg2)[i])->get_val ()) return true; else if (as_a<bit *> ((*arg1)[i])->get_val () < as_a<bit *> ((*arg2)[i])->get_val ()) return false; } return false; } /* Adds GREATER THAN condition of given variables to state. */ bool state::add_greater_than_cond (tree arg1, tree arg2) { return add_binary_cond (arg1, arg2, &state::add_greater_than_cond); } /* Adds greater than condition for two values. */ void state::add_greater_than_cond (value *arg1, value *arg2) { if (is_bit_vector (arg1) && is_bit_vector (arg2)) { bool result = check_const_value_is_greater_than (arg1, arg2); last_cond_status = result ? condition_status::CS_TRUE : condition_status::CS_FALSE; return; } if (is_bit_vector (arg2) && is_a<bit *> (arg1->last ()) && make_number (arg2) == 0 && !arg1->is_unsigned) { if (as_a<bit *> (arg1->last ())->get_val () == 1) last_cond_status = condition_status::CS_FALSE; else { for (size_t i = 0; i < arg1->length (); i++) if (is_a<bit *> ((*arg1)[i]) && as_a<bit *> ((*arg1)[i])->get_val ()) { last_cond_status = condition_status::CS_TRUE; return; } } } bit_expression *gt_cond = construct_great_than_cond (arg1, arg2); if (gt_cond) { /* Otherwise its status is already set. */ last_cond_status = condition_status::CS_SYM; conditions.add (gt_cond); } } /* Constructs expression trees of greater than condition for given values. */ bit_expression * state::construct_great_than_cond (value *arg1, value *arg2) { bit_expression *prev = nullptr; int i = arg1->length () - 1; for (; i >= 0; i--) { if (is_a<bit *> ((*arg1)[i]) && is_a<bit *> ((*arg2)[i])) { if (as_a<bit *> ((*arg1)[i])->get_val () > as_a<bit *> ((*arg2)[i])->get_val ()) { if (!prev) last_cond_status = condition_status::CS_TRUE; return prev; } else if (as_a<bit *> ((*arg1)[i])->get_val () < as_a<bit *> ((*arg2)[i])->get_val ()) { if (prev) { bit_expression *ret_val = as_a<bit_expression *> (prev->get_left ()->copy ()); delete prev; return ret_val; } else { last_cond_status = condition_status::CS_FALSE; return nullptr; } } } else { bit_condition *gt_cond = new bit_condition ((*arg1)[i]->copy (), (*arg2)[i]->copy (), GT_EXPR); bit_expression *expr = nullptr; if (i) { bit_condition *eq_cond = new bit_condition ((*arg1)[i]->copy (), (*arg2)[i]->copy (), EQ_EXPR); expr = new bit_or_expression (gt_cond, eq_cond); } else expr = gt_cond; if (prev) prev = new bit_and_expression (expr, prev); else prev = expr; } } return prev; } /* Checks whether the first given constant value is less than the second one. */ bool state::check_const_value_is_less_than (value *arg1, value *arg2) { for (int i = arg1->length () - 1; i >= 0; i--) { if (as_a<bit *> ((*arg1)[i])->get_val () < as_a<bit *> ((*arg2)[i])->get_val ()) return true; else if (as_a<bit *> ((*arg1)[i])->get_val () > as_a<bit *> ((*arg2)[i])->get_val ()) return false; } return false; } /* Adds LESS THAN condition of given variables to state. */ bool state::add_less_than_cond (tree arg1, tree arg2) { return add_binary_cond (arg1, arg2, &state::add_less_than_cond); } /* Adds less than condition for two values. */ void state::add_less_than_cond (value *arg1, value *arg2) { if (is_bit_vector (arg1) && is_bit_vector (arg2) && (make_number (arg2) != 0 || arg1->is_unsigned)) { bool result = check_const_value_is_less_than (arg1, arg2); last_cond_status = result ? condition_status::CS_TRUE : condition_status::CS_FALSE; return; } last_cond_status = condition_status::CS_SYM; if (is_bit_vector (arg2) && make_number (arg2) == 0 && !arg1->is_unsigned) { if (is_a<bit *> (arg1->last ())) { if (as_a<bit *> (arg1->last ())->get_val () == 1) last_cond_status = condition_status::CS_TRUE; else last_cond_status = condition_status::CS_FALSE; } else conditions.add (new bit_condition (arg1->last ()->copy (), new bit (1), EQ_EXPR)); return; } bit_expression *lt_cond = construct_less_than_cond (arg1, arg2); if (lt_cond) /* Otherwise its status is already set. */ conditions.add (lt_cond); } /* Constructs expression trees of less than condition for given values. */ bit_expression * state::construct_less_than_cond (value *arg1, value *arg2) { bit_expression *prev = nullptr; int i = arg1->length () - 1; for (; i >= 0; i--) { if (is_a<bit *> ((*arg1)[i]) && is_a<bit *> ((*arg2)[i])) { if (as_a<bit *> ((*arg1)[i])->get_val () < as_a<bit *> ((*arg2)[i])->get_val ()) { if (!prev) last_cond_status = condition_status::CS_TRUE; return prev; } else if (as_a<bit *> ((*arg1)[i])->get_val () > as_a<bit *> ((*arg2)[i])->get_val ()) { if (prev) { bit_expression *ret_val = as_a<bit_expression *> (prev->get_left ()->copy ()); delete prev; return ret_val; } else { last_cond_status = condition_status::CS_FALSE; return nullptr; } } } else { bit_condition *lt_cond = new bit_condition ((*arg1)[i]->copy (), (*arg2)[i]->copy (), LT_EXPR); bit_expression *expr = nullptr; if (i) { bit_condition *eq_cond = new bit_condition ((*arg1)[i]->copy (), (*arg2)[i]->copy (), EQ_EXPR); expr = new bit_or_expression (lt_cond, eq_cond); } else expr = lt_cond; if (prev) prev = new bit_and_expression (expr, prev); else prev = expr; } } return prev; } /* Adds GREATER OR EQUAL condition of given variables to state. */ bool state::add_greater_or_equal_cond (tree arg1, tree arg2) { return add_binary_cond (arg1, arg2, &state::add_greater_or_equal_cond); } /* Adds greater or equal condition for two values. */ void state::add_greater_or_equal_cond (value *arg1, value *arg2) { if (is_bit_vector (arg1) && is_bit_vector (arg2) && (make_number (arg2) != 0 || arg1->is_unsigned)) { bool is_greater_than = check_const_value_is_greater_than (arg1, arg2); bool is_equal = check_const_value_equality (arg1, arg2); last_cond_status = (is_greater_than | is_equal) ? condition_status::CS_TRUE : condition_status::CS_FALSE; return; } last_cond_status = condition_status::CS_SYM; if (is_bit_vector (arg2) && make_number (arg2) == 0 && !arg1->is_unsigned) { if (is_a<bit *> (arg1->last ())) { if (as_a<bit *> (arg1->last ())->get_val () == 1) last_cond_status = condition_status::CS_FALSE; else last_cond_status = condition_status::CS_TRUE; } else conditions.add (new bit_condition (arg1->last ()->copy (), new bit (0), EQ_EXPR)); return; } bit_expression *eq_cond = construct_equal_cond (arg1, arg2); if (!eq_cond) return; bit_expression *gt_cond = construct_great_than_cond (arg1, arg2); if (gt_cond) /* Otherwise its status is already set. */ conditions.add (new bit_or_expression (eq_cond, gt_cond)); } /* Adds LESS OR EQUAL condition of given variables to state. */ bool state::add_less_or_equal_cond (tree arg1, tree arg2) { return add_binary_cond (arg1, arg2, &state::add_less_or_equal_cond); } /* Adds less or equal condition for two values. */ void state::add_less_or_equal_cond (value *arg1, value *arg2) { if (is_bit_vector (arg1) && is_bit_vector (arg2)) { bool is_less_than = check_const_value_is_less_than (arg1, arg2); bool is_equal = check_const_value_equality (arg1, arg2); last_cond_status = (is_less_than | is_equal) ? condition_status::CS_TRUE : condition_status::CS_FALSE; return; } last_cond_status = condition_status::CS_SYM; bit_expression *eq_cond = construct_equal_cond (arg1, arg2); if (!eq_cond) return; bit_expression *lt_cond = construct_less_than_cond (arg1, arg2); if (lt_cond) /* Otherwise its status is already set. */ conditions.add (new bit_or_expression (eq_cond, lt_cond)); } /* Adds a bool condition to state. */ bool state::add_bool_cond (tree arg) { if (!is_declared (arg)) { if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "Sym-Exec: Argument must be declared " "for bool condition.\n"); return false; } value *arg_bits = var_states.get (arg); for (size_t i = 0; i < arg_bits->length (); i++) if (is_a<bit *> ((*arg_bits)[i]) && as_a<bit *> ((*arg_bits)[i])->get_val () != 0) { last_cond_status = condition_status::CS_TRUE; print_conditions (); return true; } if (is_bit_vector (arg_bits)) { last_cond_status = condition_status::CS_FALSE; print_conditions (); return true; } bit_expression *prev = nullptr; for (size_t i = 0; i < arg_bits->length (); i++) { if (is_a<bit *> ((*arg_bits)[i])) continue; bit_condition *not_eq_cond = new bit_condition ((*arg_bits)[i], new bit (0), NE_EXPR); if (prev) prev = new bit_or_expression (not_eq_cond, prev); else prev = not_eq_cond; } last_cond_status = condition_status::CS_SYM; conditions.add (prev); print_conditions (); return true; } /* Does preprocessing and postprocessing for condition adding. Handles value creation for constants and their removement in the end. */ bool state::add_binary_cond (tree arg1, tree arg2, binary_cond_func cond_func) { bool arg1_is_declared = is_declared (arg1); bool arg2_is_declared = is_declared (arg2); if (!arg1_is_declared && !arg2_is_declared) { if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "Sym-Exec: At least one of arguments must be" " declared for adding the condition.\n"); return false; } if (arg1_is_declared) declare_if_needed (arg2, var_states.get (arg1)->length ()); if (arg2_is_declared) declare_if_needed (arg1, var_states.get (arg2)->length ()); value *arg1_val = var_states.get (arg1); value arg1_const_val (MAX_VALUE_SIZE, false); if (arg1_val == NULL && TREE_CODE (arg1) == INTEGER_CST) { arg1_const_val = create_val_for_const (arg1, var_states.get (arg2)->length ()); arg1_val = &arg1_const_val; } value *arg2_val = var_states.get (arg2); value arg2_const_val (MAX_VALUE_SIZE, false); if (arg2_val == NULL && TREE_CODE (arg2) == INTEGER_CST) { arg2_const_val = create_val_for_const (arg2, var_states.get (arg1)->length ()); arg2_val = &arg2_const_val; } (this->*cond_func) (arg1_val, arg2_val); print_conditions (); return true; } /* Constructs expression trees of equal condition for given values. */ bit_expression * state::construct_equal_cond (value *arg1, value *arg2) { /* If both arguments are constants then we can evaluate it. */ if (is_bit_vector (arg1) && is_bit_vector (arg2)) { bool result = check_const_value_equality (arg1, arg2); last_cond_status = result ? condition_status::CS_TRUE : condition_status::CS_FALSE; return nullptr; } /* When some bits are constants, and they differ by value, then we can evaluate it to be false. */ for (size_t i = 0; i < arg1->length (); i++) { if (is_a<bit *> ((*arg1)[i]) && is_a<bit *> ((*arg2)[i]) && as_a<bit *> ((*arg1)[i])->get_val () != as_a<bit *> ((*arg2)[i])->get_val ()) { last_cond_status = condition_status::CS_FALSE; return nullptr; } } bit_expression *prev = nullptr; for (size_t i = 0; i < arg1->length (); i++) { bit_condition *eq_expr = new bit_condition ((*arg1)[i]->copy (), (*arg2)[i]->copy (), EQ_EXPR); if (prev) prev = new bit_and_expression (eq_expr, prev); else prev = eq_expr; } return prev; } /* Constructor for value. The first argument is the size of the bit-vector and the second argument is the sign of the number. */ value::value (unsigned size, bool is_unsigned) : is_unsigned (is_unsigned) { number.create (size); } /* Copy constructor for value. */ value::value (const value &other) : is_unsigned (other.is_unsigned) { number.create (other.length ()); for (size_t i = 0; i < other.length (); ++i) { value_bit *temp = other[i] ? other[i]->copy () : other[i]; number.quick_push (temp); } } /* Returns pushed bits count. */ unsigned value::length () const { return number.length (); } /* Wrapper of vec<..>::operator[] for the bit-vector. */ value_bit *& value::operator[] (unsigned i) { return number[i]; } /* Assignment operator. If the specified value's size is smaller, then 0 constant bit will be assigned to the remaining upper bits. */ value & value::operator= (const value &other) { unsigned smallest = number.allocated () < other.length () ? number.allocated () : other.length (); for (size_t i = 0; i < smallest; i++) if (i < number.length ()) { delete number[i]; number[i] = other[i]->copy (); } else number.quick_push (other[i]->copy ()); for (size_t i = smallest; i < number.allocated (); i++) if (i < number.length ()) { delete number[i]; number[i] = other.is_unsigned ? new bit (0) : other[other.length () - 1]->copy (); } else number.quick_push (other.is_unsigned ? new bit (0) : other[other.length () - 1]->copy ()); return (*this); } /* Wrapper of vec<..>::operator[] const for the bit-vector. */ value_bit * value::operator[] (unsigned i) const { return number[i]; } /* Wrapper of vec<..>.exists for the bit-vector. */ bool value::exists () const { return number.exists (); } /* Returns the size in bits. */ unsigned value::allocated () const { return number.allocated (); } /* Returns a reference the last bit. */ value_bit *& value::last () { return number.last (); } /* Make a copy of given bits. */ vec<value_bit *> * state::make_copy (vec<value_bit *> *bits) { vec < value_bit * > *copied_bits = new vec<value_bit *> (); copied_bits->create (bits->length ()); for (size_t i = 0; i < bits->length (); i++) copied_bits->quick_push ((*bits)[i]->copy ()); return copied_bits; } /* Returns status of last added condition. */ condition_status state::get_last_cond_status () { return last_cond_status; } /* Prints the given value. */ void state::print_value (value *var) { if (!dump_file || !(dump_flags & TDF_DETAILS)) return; fprintf (dump_file, "{"); for (int i = var->length () - 1; i >= 0; i--) { (*var)[i]->print (); if (i) fprintf (dump_file, ", "); } fprintf (dump_file, "}\n"); } /* Create LFSR value for the reversed CRC. */ void state::create_reversed_lfsr (value &lfsr, const value &crc, const value &polynomial) { size_t size = polynomial.length (); /* Determine values of all bits, except MSB. */ for (size_t i = 0; i < size - 1; i++) { if (as_a<bit *> (polynomial[i])->get_val ()) lfsr.push (state::xor_two_bits (crc[i + 1], crc[0])); else lfsr.push (crc[i + 1]->copy ()); } /* Determine value of MSB. */ if (as_a<bit *> (polynomial[size - 1])->get_val ()) lfsr.push (crc[0]->copy ()); else lfsr.push (new bit (0)); } /* Create LFSR value for the forward CRC. */ void state::create_forward_lfsr (value &lfsr, const value &crc, const value &polynomial) { size_t size = polynomial.length (); /* Determine value of LSB. */ if (as_a<bit *> (polynomial[0])->get_val ()) lfsr.push (crc[size - 1]->copy ()); else lfsr.push (new bit (0)); /* Determine values of remaining bits. */ for (size_t i = 1; i < size; i++) { if (as_a<bit *> (polynomial[i])->get_val ()) lfsr.push (state::xor_two_bits (crc[i - 1], crc[size - 1])); else lfsr.push (crc[i - 1]->copy ()); } } /* Get the last 1 bit index. */ size_t last_set_bit (const value &polynomial) { for (size_t i = 0; i < polynomial.length (); ++i) { if (as_a<bit *> (polynomial[polynomial.length () - i - 1])->get_val ()) return polynomial.length () - i - 1; } return 0; } /* Create LFSR value. */ value * state::create_lfsr (tree crc, value *polynomial, bool is_bit_forward) { /* Check size compatibility․ */ unsigned HOST_WIDE_INT polynomial_length = polynomial->length (); unsigned HOST_WIDE_INT crc_size = tree_to_uhwi (TYPE_SIZE (TREE_TYPE (crc))); if (crc_size < polynomial_length) { if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "LFSR state creation: " "Polynomial doesn't fit into the crc.\n"); return nullptr; } /* Get the minimal byte size to keep the polynomial. Ie, if the last 1 bit of the polynomial is at 6 index, size will be 8. */ size_t required_polynomial_size = ((last_set_bit (*polynomial)/8) + 1) * 8; /* Polynomial's length actually equals to the CRC variable's size. Now we detect only those CRC calculation algorithms, where leading 1 of the polynomial isn't kept. */ if (required_polynomial_size == 0 || required_polynomial_size != polynomial_length) { if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "Polynomial's all bits are zeros " "or the size of the polynomial is uncertain.\n"); return nullptr; } /* Create vector of symbolic bits for crc. */ value crc_value (polynomial_length, TYPE_UNSIGNED (TREE_TYPE (crc))); for (unsigned HOST_WIDE_INT i = 0; i < polynomial_length; i++) crc_value.push (new symbolic_bit (i, crc)); /* create LFSR vector. */ value *lfsr = new value (polynomial_length, TYPE_UNSIGNED (TREE_TYPE (crc))); /* Calculate values for LFSR. */ if (is_bit_forward) create_forward_lfsr (*lfsr, crc_value, *polynomial); else create_reversed_lfsr (*lfsr, crc_value, *polynomial); return lfsr; } /* Prints added conditions. */ void state::print_conditions () { if (!dump_file || !(dump_flags & TDF_DETAILS)) return; fprintf (dump_file, "Conditions {"); auto iter = conditions.begin (); while (true) { if (iter != conditions.end ()) { (*iter)->print (); ++iter; } if (iter != conditions.end ()) fprintf (dump_file, ", "); else break; } fprintf (dump_file, "}\n"); } /* Pushes the given bit to the end of the bit vector. */ value_bit ** value::push (value_bit *elem) { return number.quick_push (elem); } /* Destructor for value. */ value::~value () { free_bits (); number.release (); } /* Removes given sequence of bits. */ void value::free_bits () { if (!number.exists ()) return; for (size_t i = 0; i < number.length (); i++) { delete number[i]; number[i] = nullptr; } } /* For the given value_bit, iterates over its expression tree, complements those bit which came from the given origin. */ value_bit * state::complement_bits_with_origin (value_bit *root, tree origin) { /* Be careful. This function doesn't make a full copy of the bit. */ if (!is_a<bit_expression *> (root)) { if (is_a<symbolic_bit *> (root) && as_a<symbolic_bit *> (root)->get_origin () == origin) root = new bit_complement_expression (root); return root; } bit_expression *expr_root = as_a<bit_expression *> (root); hash_set <value_bit *> nodes_to_consider; nodes_to_consider.add (expr_root); hash_map <value_bit *, value_bit *> node_to_parent; node_to_parent.put (expr_root, nullptr); /* Traversing expression tree. */ while (!nodes_to_consider.is_empty ()) { value_bit *cur_element = *nodes_to_consider.begin (); nodes_to_consider.remove (cur_element); if (is_a<symbolic_bit *> (cur_element)) { if (as_a<symbolic_bit *> (cur_element)->get_origin () != origin) continue; bit_expression *parent = as_a<bit_expression *> (*node_to_parent.get (cur_element)); if (is_a<bit_complement_expression *> (parent)) { value_bit *parent_of_parent = *node_to_parent.get (parent); if (parent_of_parent) { bit_expression *parent_of_parent_expr = as_a<bit_expression *> (parent_of_parent); parent->set_right (nullptr); delete parent; parent_of_parent_expr->get_left () == parent ? parent_of_parent_expr->set_left (cur_element) : parent_of_parent_expr->set_right (cur_element); } else { /* Parent is our root. */ as_a<bit_expression *> (root)->set_right (nullptr); delete root; root = cur_element; } } else { value_bit* new_bit = new bit_complement_expression (cur_element); parent->get_left () == cur_element ? parent->set_left (new_bit) : parent->set_right (new_bit); } continue; } bit_expression* cur_elem_expr = as_a<bit_expression *> (cur_element); value_bit *left = cur_elem_expr->get_left (); value_bit *right = cur_elem_expr->get_right (); if (left != nullptr && !is_a<bit *> (left)) { nodes_to_consider.add (left); node_to_parent.put (left, cur_element); } if (right != nullptr && !is_a<bit *> (right)) { nodes_to_consider.add (right); node_to_parent.put (right, cur_element); } } return root; } /* Iterates over every bit of the given value and complements their expression trees' those bits, that came from the given origin. */ void state::complement_val_bits_with_origin (value *val, tree origin) { for (size_t i = 0; i < val->length (); i++) { (*val)[i] = complement_bits_with_origin ((*val)[i], origin); } } /* Complements all bits of all values that came from the given origin. */ void state::complement_all_vars_bits_with_origin (tree origin) { for (auto iter = var_states.begin (); iter != var_states.end (); ++iter) { complement_val_bits_with_origin (&(*iter).second, origin); } } /* Complements all bits with the given origin of all added conditions. */ void state::complement_conditions_with_origin (tree origin) { hash_set<bit_expression *> updated_conditions; for (auto iter = conditions.begin (); iter != conditions.end (); ++iter) updated_conditions.add (as_a<bit_expression *> ( complement_bits_with_origin (*iter, origin))); conditions.empty (); for (auto iter = updated_conditions.begin (); iter != updated_conditions.end (); ++iter) conditions.add (*iter); } /* Complements all bits with the given origin of all values and added conditions. */ void state::complement_state_with_origin (tree origin) { complement_all_vars_bits_with_origin (origin); complement_conditions_with_origin (origin); } /* Performs the specified operation on passed variables. */ bool state::do_operation (tree_code op_code, tree arg1, tree arg2, tree dest) { switch (op_code) { case BIT_NOT_EXPR: return do_complement (arg1, dest); case NOP_EXPR: case SSA_NAME: case VAR_DECL: case INTEGER_CST: return do_assign (arg1, dest); case LSHIFT_EXPR: return do_binary_operation (arg1, arg2, dest, &state::do_shift_left); case RSHIFT_EXPR: return do_binary_operation (arg1, arg2, dest, &state::do_shift_right); case BIT_AND_EXPR: return do_binary_operation (arg1, arg2, dest, &state::do_and); case BIT_IOR_EXPR: return do_binary_operation (arg1, arg2, dest, &state::do_or); case BIT_XOR_EXPR: return do_binary_operation (arg1, arg2, dest, &state::do_xor); case PLUS_EXPR: return do_binary_operation (arg1, arg2, dest, &state::do_add); case MINUS_EXPR: return do_binary_operation (arg1, arg2, dest, &state::do_sub); case MULT_EXPR: return do_binary_operation (arg1, arg2, dest, &state::do_mul); default: { if (dump_file) fprintf (dump_file, "Warning, encountered unsupported operation " "with %s code while executing assign statement!\n", get_tree_code_name (op_code)); return false; } } }