mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 12:00:11 -05:00
2423 lines
60 KiB
C++
2423 lines
60 KiB
C++
/* 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-2026 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;
|
||
}
|
||
}
|
||
}
|