From: Mariam Arutunian Date: Fri, 2 Dec 2022 15:04:56 +0000 (+0400) Subject: Changes in Traverse and execute CRC function v6: - Changed symbolic execution, to... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=995dbe65d8373b05eab6dd14fa743a348ed00ba2;p=thirdparty%2Fgcc.git Changes in Traverse and execute CRC function v6: - Changed symbolic execution, to execute only those paths which may be executed. I.e., if the condition is true/false only execute that path, if the condition depends on symbolic values - execute both paths. - Check functions' return values. Stop the analysis if needed. - Added destructor. - Added SSA_NAME and VAR_DECL support for the assign statement. Changes in Testsuit v2: - Changed crc-1.c test name to crc-24.c. - Added crc-1.c test. --- diff --git a/gcc/gimple-crc-optimization.cc b/gcc/gimple-crc-optimization.cc index 501395b69638..dc2074687a86 100644 --- a/gcc/gimple-crc-optimization.cc +++ b/gcc/gimple-crc-optimization.cc @@ -931,7 +931,11 @@ crc_optimization::execute (function *fun) if (function_may_calculate_crc (fun)) { crc_symb_execution symb_exec; - symb_exec.execute_function (fun); + if (!symb_exec.execute_function (fun)) + { + if (dump_file) + fprintf (dump_file, "\nAttention! Not the CRC we want!\n"); + } } return 0; } diff --git a/gcc/symb-execute-all-paths.cc b/gcc/symb-execute-all-paths.cc index 7874121bc6ca..f8843a6d2caa 100644 --- a/gcc/symb-execute-all-paths.cc +++ b/gcc/symb-execute-all-paths.cc @@ -40,12 +40,12 @@ along with GCC; see the file COPYING3. If not see /* This function assigns symbolic values to the arguments of the fun. (Not complete). */ -void +bool crc_symb_execution::make_symbolic_func_args_and_sizes (function *fun, state *initial_state) { if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Making symbolic function's following arguments:\n"); + fprintf (dump_file, "\nMaking symbolic function's following arguments:\n"); /* Get size and name of function's arguments. */ for (tree arg = DECL_ARGUMENTS (fun->decl); arg; arg = DECL_CHAIN (arg)) { @@ -64,15 +64,16 @@ crc_symb_execution::make_symbolic_func_args_and_sizes (function *fun, else if (dump_file) fprintf (dump_file, "Argument not const or no name.\n"); } + return true; } /* Add declared ssa variables to the state. */ -void +bool crc_symb_execution::add_function_local_ssa_vars (function *fun, state *initial_state) { if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "\nAdding following ssa name declarations: \n"); + fprintf (dump_file, "\n\nAdding following ssa name declarations: \n"); unsigned ix; tree name; /* Get ssa names of the function. @@ -119,10 +120,11 @@ crc_symb_execution::add_function_local_ssa_vars (function *fun, assign symbolic value. */ initial_state->make_symbolic (name, size); } + return true; } /* Calculate value of the rhs operation and assign to lhs variable. */ -void +bool crc_symb_execution::execute_assign_statement (const gassign *gs) { enum tree_code rhs_code = gimple_assign_rhs_code (gs); @@ -135,21 +137,32 @@ crc_symb_execution::execute_assign_statement (const gassign *gs) switch (rhs_code) { case BIT_NOT_EXPR: - current_state->do_complement (op1, lhs); - return; + if (current_state->do_complement (op1, lhs)) + return true; + return false; case MEM_REF: - // do_mem_ref - return; + if (current_state->do_mem_ref (op1, lhs)) + return true; + return false; case NOP_EXPR: - current_state->do_assign (op1, lhs); - return; + if (current_state->do_assign (op1, lhs)) + return true; + return false; + case SSA_NAME: + if (current_state->do_assign (op1, lhs)) + return true; + return false; + case VAR_DECL: + if (current_state->do_assign (op1, lhs)) + return true; + return false; default: if (dump_file) fprintf (dump_file, "Warning, encountered unsupported unary operation " "with %s code while executing assign statement!\n", get_tree_code_name (rhs_code)); - return; + return false; } } else if (gimple_num_ops (gs) == 3) @@ -159,42 +172,52 @@ crc_symb_execution::execute_assign_statement (const gassign *gs) switch (rhs_code) { case LSHIFT_EXPR: - current_state->do_shift_left (op1, op2, lhs); - return; + if (current_state->do_shift_left (op1, op2, lhs)) + return true; + return false; case RSHIFT_EXPR: - current_state->do_shift_right (op1, op2, lhs); - return; + if (current_state->do_shift_right (op1, op2, lhs)) + return true; + return false; case BIT_AND_EXPR: - current_state->do_and (op1, op2, lhs); - return; + if (current_state->do_and (op1, op2, lhs)) + return true; + return false; case BIT_IOR_EXPR: - current_state->do_or (op1, op2, lhs); - return; + if (current_state->do_or (op1, op2, lhs)) + return true; + return false; case BIT_XOR_EXPR: - current_state->do_xor (op1, op2, lhs); - return; + if (current_state->do_xor (op1, op2, lhs)) + return true; + return false; case PLUS_EXPR: - current_state->do_add (op1, op2, lhs); - return; + if (current_state->do_add (op1, op2, lhs)) + return true; + return false; case MINUS_EXPR: - current_state->do_sub (op1, op2, lhs); - return; + if (current_state->do_sub (op1, op2, lhs)) + return true; + return false; case MULT_EXPR: - current_state->do_mul (op1, op2, lhs); - return; + if (current_state->do_mul (op1, op2, lhs)) + return true; + return false; case POINTER_PLUS_EXPR: - current_state->do_pointer_plus (op1, op2, lhs); - return; + if (current_state->do_pointer_plus (op1, op2, lhs)) + return true; + return false; case POINTER_DIFF_EXPR: - current_state->do_pointer_diff (op1, op2, lhs); - return; + if (current_state->do_pointer_diff (op1, op2, lhs)) + return true; + return false; default: if (dump_file) fprintf (dump_file, "Warning, encountered unsupported binary operation " "with %s code while executing assign statement!\n", get_tree_code_name (rhs_code)); - return; + return false; } } else @@ -204,36 +227,102 @@ crc_symb_execution::execute_assign_statement (const gassign *gs) "Warning, encountered unsupported operation, " "with %s code while executing assign statement!\n", get_tree_code_name (rhs_code)); + return false; } + return true; } -/* Create new state for true and false branch. - Keep conditions in new created states. */ -void -crc_symb_execution::resolve_condition (const gcond* cond) +/* If the next block of the edge1 dest is a back edge, add in the stack edge2. + Otherwise, add edge1 (the real execution path). + + When loop counter is checked in the if condition, + we mustn't continue on real path, + as we don't want to iterate the loop second time. */ +void add_edge (edge edge1, edge edge2, auto_vec& stack) { - /* Remove last state. */ - state* old_state = states.last (); + edge next_bb_edge = EDGE_SUCC (edge1->dest, 0); + if (next_bb_edge && (next_bb_edge->flags & EDGE_DFS_BACK)) + { + // FIXME: Compared variable's value will be incorrect, + // not satisfiable for the path. + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Won't iterate loop once more.\n"); + stack.quick_push (edge2); + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Adding the edge into the stack.\n"); - /* Add new states for each branch. */ - state* true_branch_state = new state (*old_state); - state* false_branch_state = new state (*old_state); + /* If the result of the condition is true/false, + continue execution only by the true branch. */ + stack.quick_push (edge1); + } +} - delete old_state; - states.pop (); +/* Add next basic blocks of the conditional block + for the execution path into the stack. + If the condition depends on symbolic values, keep both edges. + If the condition is true, keep true edge, else - false edge. */ +void crc_symb_execution::add_next_bbs (basic_block cond_bb, + state *new_branch_state, + auto_vec& stack) +{ + edge true_edge; + edge false_edge; + extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge); + + if (new_branch_state->get_last_cond_status () == CS_SYM) + { + + /* Add true branch's state into the states. + False branch's states will be kept in the current state. */ + states.quick_push (new_branch_state); + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Adding true and false edges into the stack.\n"); + + /* Add outgoing edges to the stack. */ + stack.quick_push (false_edge); + stack.quick_push (true_edge); + + return; + } + else if (new_branch_state->get_last_cond_status () == CS_TRUE) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Condition is true.\n"); + add_edge (true_edge, false_edge, stack); + } + else if (new_branch_state->get_last_cond_status () == CS_FALSE) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Condition is false.\n"); + add_edge (false_edge, true_edge, stack); + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Something went wrong " + "during handling conditional statement.\n"); + } - /* First insert false_branch_state then true_branch_state, - as at first we will examine true branch's basic block, then false branch's, - and state.last () is called to get current paths state. */ - states.quick_push (false_branch_state); - states.quick_push (true_branch_state); + /* When we continue execution of only one path, + there's no need of new state. */ + delete new_branch_state; +} +/* Keep conditions depending on symbolic variables in the states. */ +bool crc_symb_execution::add_condition (const gcond* cond, + state* current_state, + state* new_branch_state) +{ /* Keep conditions of each branch execution in its state. Ex. - if (a == 0) + if (a == 0) // a's value is unknown - true_branch_state.keep (a==0) - false_branch_state.keep (a!=0) + new_branch_state.keep (a==0) + current_state.keep (a!=0) */ tree lhs = gimple_cond_lhs (cond); @@ -241,64 +330,116 @@ crc_symb_execution::resolve_condition (const gcond* cond) switch (gimple_cond_code (cond)) { case EQ_EXPR: - true_branch_state->add_equal_cond (lhs, rhs); - false_branch_state->add_not_equal_cond (lhs, rhs); - break; + new_branch_state->add_equal_cond (lhs, rhs); + if (new_branch_state->get_last_cond_status () == CS_SYM) + current_state->add_not_equal_cond (lhs, rhs); + return true; case NE_EXPR: - true_branch_state->add_not_equal_cond (lhs, rhs); - false_branch_state->add_equal_cond (lhs, rhs); - break; + new_branch_state->add_not_equal_cond (lhs, rhs); + if (new_branch_state->get_last_cond_status () == CS_SYM) + current_state->add_equal_cond (lhs, rhs); + return true; case GT_EXPR: - true_branch_state->add_greater_than_cond (lhs, rhs); - false_branch_state->add_less_or_equal_cond (lhs, rhs); - break; + new_branch_state->add_greater_than_cond (lhs, rhs); + if (new_branch_state->get_last_cond_status () == CS_SYM) + current_state->add_less_or_equal_cond (lhs, rhs); + return true; case LT_EXPR: - true_branch_state->add_less_than_cond (lhs, rhs); - false_branch_state->add_greater_or_equal_cond (lhs, rhs); - break; + new_branch_state->add_less_than_cond (lhs, rhs); + if (new_branch_state->get_last_cond_status () == CS_SYM) + current_state->add_greater_or_equal_cond (lhs, rhs); + return true; case GE_EXPR: - true_branch_state->add_greater_or_equal_cond (lhs, rhs); - false_branch_state->add_less_than_cond (lhs, rhs); - break; + new_branch_state->add_greater_or_equal_cond (lhs, rhs); + if (new_branch_state->get_last_cond_status () == CS_SYM) + current_state->add_less_than_cond (lhs, rhs); + return true; case LE_EXPR: - true_branch_state->add_less_or_equal_cond (lhs, rhs); - false_branch_state->add_greater_than_cond (lhs, rhs); - break; + new_branch_state->add_less_or_equal_cond (lhs, rhs); + if (new_branch_state->get_last_cond_status () == CS_SYM) + current_state->add_greater_than_cond (lhs, rhs); + return true; default: if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "Unsupported condition.\n"); + return false; } } +/* Create new state for true and false branch. + Keep conditions in new created states. */ +bool +crc_symb_execution::resolve_condition (const gcond* cond, + auto_vec& stack) +{ + /* Remove last state. */ + state* current_state = states.last (); + state* new_branch_state = new state (*current_state); + + if (!add_condition (cond, current_state, new_branch_state)) + return false; + + add_next_bbs (cond->bb, new_branch_state, stack); + return true; +} + /* Keep the calculated value of the return value and the conditions of the executed path. */ -void +bool crc_symb_execution::keep_return_val_and_conditions (const greturn* ret) { tree return_op = gimple_return_retval (ret); - if (return_op == nullptr) + if (!return_op) { if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "No return value.\n"); - return; + return false; + } + + if (TREE_CODE (return_op) == INTEGER_CST) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Return value is a constant.\n"); + return false; } /* Get calculated return value. */ state * curr_state = states.last (); + vec * return_value = curr_state->get_bits (return_op); + + if (!return_value) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Return value is not in the state.\n"); + return false; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Return value is "); + state::print_bits (return_value); + } state * final_state = new state; + /* Keep return value's calculated value and conditions in the final state. */ - final_state->add_var_state (return_op, curr_state->get_bits (return_op)); + final_state->add_var_state (return_op, return_value); final_state->bulk_add_conditions (curr_state->get_conditions ()); final_states.quick_push (final_state); + + delete curr_state; + states.pop (); + + return true; } /* Execute gimple statements of BB. Keeping values of variables in the state. */ -void -crc_symb_execution::execute_bb_gimple_statements (basic_block bb) +bool +crc_symb_execution::execute_bb_gimple_statements (basic_block bb, + auto_vec& stack) { for (gimple_stmt_iterator bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi)) @@ -312,27 +453,50 @@ crc_symb_execution::execute_bb_gimple_statements (basic_block bb) switch (gimple_code (gs)) { case GIMPLE_ASSIGN: - execute_assign_statement (as_a (gs)); - break; + if (!execute_assign_statement (as_a (gs))) + return false; + break; case GIMPLE_COND: - resolve_condition (as_a (gs)); - break; + if (!resolve_condition (as_a (gs), stack)) + return false; + return true; case GIMPLE_RETURN: - keep_return_val_and_conditions (as_a (gs)); - break; + if (!keep_return_val_and_conditions (as_a (gs))) + return false; + return true; default: if (dump_file) fprintf (dump_file, "Warning, encountered unsupported statement, " "while executing gimple statements!\n"); - break; + return false; } } + + /* Add each outgoing edge of the current block to the stack, + despite back edges. + This code isn't reachable if the last statement of the basic block + is a conditional statement or return statement. + Those cases are handled separately. */ + edge out_edge; + edge_iterator ei; + FOR_EACH_EDGE (out_edge, ei, bb->succs) + if (!(out_edge->flags & EDGE_DFS_BACK)) + stack.quick_push (out_edge); + else if (!states.is_empty ()) + { + /* Delete the state after executing the full path, + or encountering back edge. */ + delete states.last (); + states.pop (); + } + + return true; } /* Assign values of phi instruction to its result. Keep updated values in the state. */ -void +bool crc_symb_execution::execute_bb_phi_statements (basic_block bb, edge incoming_edge) { @@ -355,29 +519,30 @@ crc_symb_execution::execute_bb_phi_statements (basic_block bb, tree rhs = PHI_ARG_DEF_FROM_EDGE (phi, incoming_edge); - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "Found phi's value.\n\n"); - } state *current_state = states.last (); - current_state->do_assign (rhs, lhs); + if (!current_state->do_assign (rhs, lhs)) + return false; } + return true; } /* Execute all statements of BB. Keeping values of variables in the state. */ -void +bool crc_symb_execution::execute_bb_statements (basic_block bb, - edge incoming_edge) + edge incoming_edge, + auto_vec& stack) { - execute_bb_phi_statements (bb, incoming_edge); - execute_bb_gimple_statements (bb); + if (!execute_bb_phi_statements (bb, incoming_edge)) + return false; + + return execute_bb_gimple_statements (bb, stack); } /* Traverse function fun's all paths from the first basic block to the last. Each time iterate loops only once. Symbolically execute statements of each path. */ -void +bool crc_symb_execution::traverse_function (function *fun) { /* TODO: Check whether back_edges can be determined by BB index, @@ -406,27 +571,13 @@ crc_symb_execution::traverse_function (function *fun) fprintf (dump_file, "\n\nExecuting BB <%d>\n\n", bb->index); /* Symbolically execute statements. */ - execute_bb_statements (bb, e); - - /* Add each outgoing edge of the current block to the stack, - despite back edges. */ - edge out_edge; - edge_iterator ei; - FOR_EACH_EDGE (out_edge, ei, bb->succs) - if (!(out_edge->flags & EDGE_DFS_BACK) - && out_edge->dest != EXIT_BLOCK_PTR_FOR_FN (fun)) - stack.quick_push (out_edge); - else if (!states.is_empty ()) - { - /* Delete the state after executing the full path, - or encountering back edge. */ - delete states.last (); - states.pop (); - } + if (!execute_bb_statements (bb, e, stack)) + return false; } + return true; } -void +bool crc_symb_execution::execute_function (function *fun) { if (dump_file && (dump_flags & TDF_DETAILS)) @@ -442,5 +593,5 @@ crc_symb_execution::execute_function (function *fun) add_function_local_ssa_vars (fun, initial_state); /* Execute function's statements, keeping a state for each path. */ - traverse_function (fun); + return traverse_function (fun); } diff --git a/gcc/symb-execute-all-paths.h b/gcc/symb-execute-all-paths.h index 48d823989cea..ef184cb9bd75 100644 --- a/gcc/symb-execute-all-paths.h +++ b/gcc/symb-execute-all-paths.h @@ -49,47 +49,49 @@ class crc_symb_execution { to keep the returned_value and path conditions. */ vec final_states; - /* This map will contain the calculated return value - and the path conditions for each executed path. */ - hash_map*, const hash_set *> - ret_val_states; - /* Assign symbolic values to the arguments of the function and keep in the state. */ - static void make_symbolic_func_args_and_sizes (function *, state *); + static bool make_symbolic_func_args_and_sizes (function *, state *); /* Add declared ssa variables to the state. */ - static void add_function_local_ssa_vars (function *fun, state *initial_state); + static bool add_function_local_ssa_vars (function *, state *); + + bool execute_assign_statement (const gassign *); + + /* Add next basic blocks of the conditional block + for the execution path into the stack. */ + void add_next_bbs (basic_block, state *, auto_vec&); - void execute_assign_statement (const gassign *); + /* Keep conditions depending on symbolic variables in the states. */ + static bool add_condition (const gcond*, state*, state*); /* Create new state for true and false branch. Keep conditions in new created states. */ - void resolve_condition (const gcond* cond); + bool resolve_condition (const gcond*, auto_vec&); /* Keep the calculated value of the return value and the conditions of the executed path. */ - void keep_return_val_and_conditions (const greturn* ret); + bool keep_return_val_and_conditions (const greturn*); /* Execute gimple statements of the basic block. Keeping values of variables in the state. */ - void execute_bb_gimple_statements (basic_block); + bool execute_bb_gimple_statements (basic_block, auto_vec&); /* Assign values of phi instruction to its result. Keep updated values in the state. */ - void execute_bb_phi_statements (basic_block, edge); + bool execute_bb_phi_statements (basic_block, edge); /* Execute all statements of the basic block. Keeping values of variables in the state. */ - void execute_bb_statements (basic_block, edge); + bool execute_bb_statements (basic_block, edge, auto_vec&); /* Traverse function fun's all paths from the first basic block to the last. Each time iterate loops only once. Symbolically execute statements of each path. */ - void traverse_function (function *); + bool traverse_function (function *); public: - void execute_function (function *); + bool execute_function (function *); crc_symb_execution () { @@ -97,6 +99,13 @@ class crc_symb_execution { states.create (30); final_states.create (30); } + + ~crc_symb_execution () + { + /* Free memory. */ + states.release (); + final_states.release (); + } }; #endif //GCC_EXECUTE_ALL_PATHS_H diff --git a/gcc/testsuite/gcc.dg/crc-1.c b/gcc/testsuite/gcc.dg/crc-1.c index 8f61f15f7986..951e755c17a1 100644 --- a/gcc/testsuite/gcc.dg/crc-1.c +++ b/gcc/testsuite/gcc.dg/crc-1.c @@ -10,8 +10,6 @@ uint16_t gen_crc16(const uint8_t *data, uint16_t size) { uint16_t out = 0; int bits_read = 0, bit_flag; - printf("buffer in function %s\n", data); - if (data == NULL) return 0; @@ -30,7 +28,6 @@ uint16_t gen_crc16(const uint8_t *data, uint16_t size) { if (bit_flag) out ^= CRC16; - } int i; diff --git a/gcc/testsuite/gcc.dg/crc-24.c b/gcc/testsuite/gcc.dg/crc-24.c new file mode 100644 index 000000000000..8f61f15f7986 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-24.c @@ -0,0 +1,59 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-crc" } */ + +#include +#include + +#define CRC16 0x8005 + +uint16_t gen_crc16(const uint8_t *data, uint16_t size) { + uint16_t out = 0; + int bits_read = 0, bit_flag; + + printf("buffer in function %s\n", data); + + if (data == NULL) + return 0; + + while (size > 0) { + bit_flag = out >> 15; + + out <<= 1; + out |= (*data >> bits_read) & 1; + + bits_read++; + if (bits_read > 7) { + bits_read = 0; + data++; + size--; + } + + if (bit_flag) + out ^= CRC16; + + } + + int i; + for (i = 0; i < 16; ++i) { + bit_flag = out >> 15; + out <<= 1; + if (bit_flag) + out ^= CRC16; + } + + uint16_t crc = 0; + i = 0x8000; + int j = 0x0001; + for (; i != 0; i >>= 1, j <<= 1) { + if (i & out) crc |= j; + } + + return crc; +} + +/* { dg-final { scan-tree-dump "Found naive crc implementation in gen_crc16." "crc"} } */ +/* { dg-final { scan-tree-dump "Return size is 16" "crc"} } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 15" "crc"} } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc"} } */ + +