]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Changes in Traverse and execute CRC function v6: - Changed symbolic execution, to...
authorMariam Arutunian <mariamarutunian@gmail.com>
Fri, 2 Dec 2022 15:04:56 +0000 (19:04 +0400)
committerJeff Law <jlaw@ventanamicro>
Tue, 21 Mar 2023 15:03:19 +0000 (09:03 -0600)
Changes in Testsuit v2:
  - Changed crc-1.c test name to crc-24.c.
  - Added crc-1.c test.

gcc/gimple-crc-optimization.cc
gcc/symb-execute-all-paths.cc
gcc/symb-execute-all-paths.h
gcc/testsuite/gcc.dg/crc-1.c
gcc/testsuite/gcc.dg/crc-24.c [new file with mode: 0644]

index 501395b69638e1fd393ebf1c26493ee5a8b69b6e..dc2074687a867b3f1c44a1320d302278a4380601 100644 (file)
@@ -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;
 }
index 7874121bc6ca98bc963c3dad4f1a6e67e45780d7..f8843a6d2caa3d0a0046aa4f4a7149d2322d6e9e 100644 (file)
@@ -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<edge, 20>& 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<edge, 20>& 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<edge, 20>& 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<value*> * 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<edge, 20>& 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<const gassign *> (gs));
-           break;
+          if (!execute_assign_statement (as_a<const gassign *> (gs)))
+            return false;
+          break;
          case GIMPLE_COND:
-           resolve_condition (as_a<const gcond *> (gs));
-           break;
+           if (!resolve_condition (as_a<const gcond *> (gs), stack))
+             return false;
+           return true;
          case GIMPLE_RETURN:
-           keep_return_val_and_conditions (as_a<const greturn *> (gs));
-           break;
+           if (!keep_return_val_and_conditions (as_a<const greturn *> (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<edge, 20>& 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);
 }
index 48d823989ceae4ae6959256333f2ebbdf17efe30..ef184cb9bd753df195938470f539e0c2469cc622 100644 (file)
@@ -49,47 +49,49 @@ class crc_symb_execution {
      to keep the returned_value and path conditions.  */
   vec<state*> final_states;
 
-  /*  This map will contain the calculated return value
-      and the path conditions for each executed path.  */
-  hash_map<const vec<value *>*, const hash_set<bit_expression *> *>
-      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<edge, 20>&);
 
-  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<edge, 20>&);
 
   /* 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<edge, 20>&);
 
   /* 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<edge, 20>&);
 
 /* 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
index 8f61f15f79868ccf8aaf38f0e2e148ca9de37e8f..951e755c17a15cf06f6b9ccc83f41928fddf48d2 100644 (file)
@@ -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 (file)
index 0000000..8f61f15
--- /dev/null
@@ -0,0 +1,59 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-crc" } */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#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"} } */
+
+