This patch adds a new compiler pass aimed at identifying naive CRC
implementations,
characterized by the presence of a loop calculating a CRC (polynomial long
division).
Upon detection of a potential CRC, the pass prints an informational
message.
Performs CRC optimization if optimization level is >= 2,
besides optimizations for size and if fno_gimple_crc_optimization given.
This pass is added for the detection and optimization of naive CRC
implementations,
improving the efficiency of CRC-related computations.
This patch includes only initial fast checks for filtering out non-CRCs,
detected possible CRCs verification and optimization parts will be provided
in subsequent patches.
gcc/
* Makefile.in (OBJS): Add gimple-crc-optimization.o.
* common.opt (fgimple-crc-optimization): New option.
* common.opt.urls: Regenerate to add
fgimple-crc-optimization.
* doc/invoke.texi (-fgimple-crc-optimization): Add documentation.
* gimple-crc-optimization.cc: New file.
* gimple.cc (set_phi_stmts_not_visited): New function.
(set_gimple_stmts_not_visited): Likewise.
(set_bbs_stmts_not_visited): Likewise.
* gimple.h (set_gimple_stmts_not_visited): New extern function
declaration.
(set_phi_stmts_not_visited): New extern function declaration.
(set_bbs_stmts_not_visited): New extern function declaration.
* opts.cc (default_options_table): Add OPT_fgimple_crc_optimization.
(enable_fdo_optimizations): Enable gimple-crc-optimization.
* passes.def (pass_crc_optimization): Add new pass.
* timevar.def (TV_GIMPLE_CRC_OPTIMIZATION): New timevar.
* tree-pass.h (make_pass_crc_optimization): New extern function
declaration.
Signed-off-by: Mariam Arutunian <[email protected]>
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index f4bb4a88cf3..0238201981d 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1721,6 +1721,7 @@ OBJS = \
tree-iterator.o \
tree-logical-location.o \
tree-loop-distribution.o \
+ gimple-crc-optimization.o \
tree-nested.o \
tree-nrv.o \
tree-object-size.o \
diff --git a/gcc/common.opt b/gcc/common.opt
index c4bda3b7da8..ea44ad1757a 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1757,6 +1757,16 @@ Common Var(flag_gcse_after_reload) Optimization
Perform global common subexpression elimination after register allocation has
finished.
+fgimple-crc-optimization
+Common Var(flag_gimple_crc_optimization) Optimization
+Detect loops calculating CRC and replace with faster implementation.
+If the target supports CRC instruction and the CRC loop uses the same
+polynomial as the one used in the CRC instruction, directly replace with the
+corresponding CRC instruction.
+Otherwise, if the target supports carry-less-multiplication instruction,
+generate CRC using it.
+If neither case applies, generate table-based CRC.
+
Enum
Name(dwarf_gnat_encodings) Type(int)
diff --git a/gcc/common.opt.urls b/gcc/common.opt.urls
index 0e71bce27c4..afb93d8147f 100644
--- a/gcc/common.opt.urls
+++ b/gcc/common.opt.urls
@@ -703,6 +703,9 @@ UrlSuffix(gcc/Optimize-Options.html#index-fgcse-las)
fgcse-after-reload
UrlSuffix(gcc/Optimize-Options.html#index-fgcse-after-reload)
+fgimple-crc-optimization
+UrlSuffix(gcc/Optimize-Options.html#index-fgimple-crc-optimization)
+
fgraphite-identity
UrlSuffix(gcc/Optimize-Options.html#index-fgraphite-identity)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 4850c7379bf..cba90610027 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -567,8 +567,8 @@ Objective-C and Objective-C++ Dialects}.
-ffast-math -ffinite-math-only -ffloat-store -fexcess-precision=@var{style}
-ffinite-loops
-fforward-propagate -ffp-contract=@var{style} -ffunction-sections
--fgcse -fgcse-after-reload -fgcse-las -fgcse-lm -fgraphite-identity
--fgcse-sm -fhoist-adjacent-loads -fif-conversion
+-fgcse -fgcse-after-reload -fgcse-las -fgcse-lm -fgimple-crc-optimization
+-fgraphite-identity -fgcse-sm -fhoist-adjacent-loads -fif-conversion
-fif-conversion2 -findirect-inlining
-finline-stringops[=@var{fn}]
-finline-functions -finline-functions-called-once -finline-limit=@var{n}
@@ -12574,6 +12574,7 @@ also turns on the following optimization flags:
-fexpensive-optimizations
-ffinite-loops
-fgcse -fgcse-lm
+-fgimple-crc-optimization
-fhoist-adjacent-loads
-finline-functions
-finline-small-functions
@@ -13768,6 +13769,19 @@ This flag is disabled by default.
Note that @option{-flive-patching} is not supported with link-time optimization
(@option{-flto}).
+@opindex fgimple-crc-optimization
+@item -fgimple-crc-optimization
+Detect loops calculating CRC (performing polynomial long division) and
+replace them with a faster implementation. Detect 8, 16, 32, and 64 bit CRC,
+with a constant polynomial without the leading 1 bit,
+for both bit-forward and bit-reversed cases.
+If the target supports a CRC instruction and the polynomial used in the source
+code matches the polynomial used in the CRC instruction, generate that CRC
+instruction. Otherwise, if the target supports a carry-less-multiplication
+instruction, generate CRC using it; otherwise generate table-based CRC.
+This flag is enabled by default at @option{-O2} and higher
+if @option{-Os} is not also specified.
+
@opindex fisolate-erroneous-paths-dereference
@item -fisolate-erroneous-paths-dereference
Detect paths that trigger erroneous or undefined behavior due to
diff --git a/gcc/gimple-crc-optimization.cc b/gcc/gimple-crc-optimization.cc
new file mode 100644
index 00000000000..bea2a6ac1d3
--- /dev/null
+++ b/gcc/gimple-crc-optimization.cc
@@ -0,0 +1,994 @@
+/* CRC optimization.
+ Copyright (C) 2022-2024 Free Software Foundation, Inc.
+ Contributed by Mariam Arutunian <[email protected]>
+
+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 pass performs CRC optimization. */
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "ssa.h"
+#include "gimple-iterator.h"
+#include "tree-cfg.h"
+#include "tree-ssa-loop-niter.h"
+#include "cfgloop.h"
+#include "gimple-range.h"
+#include "tree-scalar-evolution.h"
+#include "hwint.h"
+#include "internal-fn.h"
+#include "tree-into-ssa.h"
+#include "tree-ssa-loop-manip.h"
+#include "predict.h"
+#include "cfghooks.h"
+#include "tree-ssa.h"
+#include "tree-ssa-live.h"
+#include "tree-dfa.h"
+#include "dominance.h"
+#include "tree-ssa-dce.h"
+#include "tree-cfgcleanup.h"
+#include "tree.h"
+#include "gimple-pretty-print.h"
+
+class crc_optimization {
+ private:
+ /* The statement doing shift 1 operation before/after xor operation. */
+ gimple *m_shift_stmt;
+
+ /* Phi statement from the head of the loop for CRC. */
+ gphi *m_phi_for_crc;
+
+ /* Phi statement for the data from the head of the loop if exists,
+ otherwise - nullptr. */
+ gphi *m_phi_for_data;
+
+ /* The loop, which probably calculates CRC. */
+ class loop *m_crc_loop;
+
+ /* Depending on the value of M_IS_BIT_FORWARD, may be forward or reversed CRC.
+ If M_IS_BIT_FORWARD, then it is bit-forward implementation,
+ otherwise bit-reversed. */
+ bool m_is_bit_forward;
+
+ /* Sets initial values for CRC analyses. */
+ void set_initial_values ();
+
+ /* This is the main function that checks whether the given LOOP
+ calculates CRC and extracts details of the CRC calculation.
+
+ The main idea is to find the innermost loop with 8, 16, 24, 32, 64
+ iterations and xor instruction (xor is the key operation for naive CRC
+ calculation). Then, checks that the variable is shifted by one before/after
+ being used in xor.
+ Xor must be done under the condition of MSB/LSB being 1. */
+ bool loop_may_calculate_crc (class loop *loop);
+
+
+ /* Returns true if there is only two conditional blocks in the loop
+ (one may be for the CRC bit check and the other for the loop counter).
+ This may filter out some real CRCs, where more than one condition
+ is checked for the CRC calculation. */
+ static bool loop_contains_two_conditional_bb (basic_block *loop_bbs,
+ unsigned loop_num_nodes);
+
+ /* Checks the FUNC_LOOP loop's iteration number.
+ The loop for CRC calculation may do 8, 16, 24, 32, 64 iterations. */
+ bool satisfies_crc_loop_iteration_count (class loop *func_loop);
+
+ /* This function checks if the XOR_STMT is used for CRC calculation.
+ It verifies the presence of a shift operation in the CRC_FUN function
+ inside the CRC loop. It examines operands of XOR, its dependencies, the
+ relative position of the shift operation, and the existence of a shift
+ operation in the opposite branch of conditional statements. It also
+ checks if XOR is performed when MSB/LSB is one.
+ If these conditions are met, the XOR operation may be part of a CRC
+ calculation. The function returns true if these conditions are fulfilled,
+ otherwise, it returns false. */
+ bool xor_calculates_crc (function *crc_fun, basic_block *loop_bbs,
+ const gimple *xor_stmt);
+
+ /* Returns true if we can get definition of the VARIABLE, and the definition
+ it's not outside the loop. Otherwise, returns false. */
+ bool passes_checks_for_def_chain (tree variable);
+
+ /* This function goes up through the def-use chains of the parameter NAME.
+ Gathers all the statements within the loop,
+ from which the variable depends on and adds to the USE_DEFS.
+ Returns false, if there is a statement that may not exist in the CRC
+ loop. Otherwise, returns true. */
+ bool set_defs (tree name, auto_vec<gimple *>& use_defs,
+ bool keep_only_header_phis);
+
+ /* Set M_PHI_FOR_CRC and M_PHI_FOR_DATA fields.
+ Returns false if there are more than two (as in CRC calculation only CRC's
+ and data's phi may exist) or no phi statements in STMTS (at least there
+ must be CRC's phi).
+ Otherwise, returns true. */
+ bool set_crc_and_data_phi (auto_vec<gimple *>& stmts);
+
+ /* Returns true if the variable checked in the condition depends on possible
+ CRC value. Otherwise, returns false. */
+ bool cond_depends_on_crc (auto_vec<gimple *>& stmts);
+
+
+ /* Checks that the condition is for checking CRC.
+ Returns true if xor is done under the condition of MSB/LSB being 1, and
+ the condition's variable and xor-ed variable depend on the same variable.
+ Otherwise, returns false.
+ XOR_BB is the basic block, where the xor operation is done.
+ PRED_BB is the predecessor basic block of the XOR_BB, it is assumed that
+ the last stmt of PRED_BB checks the condition under which xor is done. */
+ bool crc_cond (basic_block pred_bb, basic_block xor_bb,
+ basic_block *loop_bbs);
+
+ /* Returns true if xor is done in case the MSB/LSB is 1.
+ Otherwise, returns false.
+ In CRC calculation algorithms CRC is xor-ed with the polynomial only
+ if MSB/LSB is 1.
+
+ PRED_BB is the block containing the condition for the xor.
+ XOR_BB is the one of the successor blocks of PRED_BB, it is assumed that
+ CRC is xor-ed with the polynomial in XOR_BB.
+ COND is the condition, which is checked to satisfy the CRC condition. */
+ bool is_crc_satisfiable_cond (basic_block pred_bb, basic_block xor_bb,
+ gcond *cond);
+
+ /* Checks that the variable used in the condition COND is the assumed CRC
+ (or depends on the assumed CRC).
+ Also sets data member m_phi_for_data if it isn't set and exists. */
+ bool is_crc_checked (gcond *cond, basic_block *loop_bbs);
+
+ /* Returns true if condition COND checks MSB/LSB bit is 1.
+ Otherwise, return false. */
+ static bool cond_true_is_checked_for_bit_one (const gcond *cond);
+
+ /* Returns opposite block of the XOR_BB from PRED_BB's dest blocks. */
+ static basic_block get_xor_bb_opposite (basic_block pred_bb,
+ basic_block xor_bb);
+
+ /* Checks whether the pair of xor's shift exists in the opposite
+ basic block (OPPOSITE_BB).
+ If there is a shift and xor in the same block,
+ then in the opposite block must be another shift. */
+ bool exists_shift_for_opp_xor_shift (basic_block opposite_bb);
+
+ /* Follow def-use chains of XORED_CRC and return the statement where
+ XORED_CRC is shifted by one bit position. Only PHI statements are
+ allowed between XORED_CRC and the shift in the def-use chain.
+
+ If no such statement is found, return NULL. */
+ gimple *find_shift_after_xor (tree xored_crc);
+
+ /* Returns the statement which does shift 1 operation.
+ If there is no such statement, returns nullptr.
+ STMTS - are the statements within the loop before xor operation on
+ which possible CRC variable depends. */
+ gimple *find_shift_before_xor (const auto_vec <gimple *> &stmts);
+
+ /* Returns true if ASSIGN_STMT does shift with 1.
+ Otherwise, returns false. */
+ bool can_be_crc_shift (gimple *assign_stmt);
+
+ /* Returns true if the operation done in ASSIGN_STMT can occur during CRC
+ calculation. Otherwise, returns false. */
+ bool can_not_be_crc_stmt (gimple *assign_stmt);
+
+ /* Returns true if the statement with STMT_CODE may occur in CRC calculation.
+ Otherwise, returns false. */
+ static bool is_acceptable_stmt_code (const tree_code &stmt_code);
+
+ /* Prints extracted details of CRC calculation. */
+ void dump_crc_information ();
+
+ public:
+ unsigned int execute (function *fun);
+};
+
+/* Prints extracted details of CRC calculation. */
+
+void
+crc_optimization::dump_crc_information ()
+{
+ if (dump_file)
+ {
+ fprintf (dump_file,
+ "Loop iteration number is " HOST_WIDE_INT_PRINT_UNSIGNED ".\n",
+ tree_to_uhwi (m_crc_loop->nb_iterations));
+ if (m_is_bit_forward)
+ fprintf (dump_file, "Bit forward.\n");
+ else
+ fprintf (dump_file, "Bit reversed.\n");
+ }
+}
+
+/* Goes down by def-use chain (within the CRC loop) and returns the statement
+ where variable (dependent on xor-ed variable) is shifted with 1.
+ Between xor and shift operations only phi statements are allowed.
+ Otherwise, returns nullptr. */
+
+gimple *
+crc_optimization::find_shift_after_xor (tree xored_crc)
+{
+ imm_use_iterator imm_iter;
+ use_operand_p use_p;
+
+ gcc_assert (TREE_CODE (xored_crc) == SSA_NAME);
+
+ /* Iterate through the immediate uses of the XORED_CRC.
+ If there is a shift return true,
+ if before shift there is other instruction (besides phi) return false. */
+ FOR_EACH_IMM_USE_FAST (use_p, imm_iter, xored_crc)
+ {
+ gimple *stmt = USE_STMT (use_p);
+ if (gimple_visited_p (stmt))
+ return nullptr;
+ gimple_set_visited (stmt, true);
+ // Consider only statements within the loop
+ if (!flow_bb_inside_loop_p (m_crc_loop, gimple_bb (stmt)))
+ return nullptr;
+
+ /* If encountered phi statement, check immediate use of its result.
+ Otherwise, if encountered assign statement, check whether it does shift
+ (some other operations are allowed to be between shift and xor). */
+ if (gimple_code (stmt) == GIMPLE_PHI)
+ {
+ /* Don't continue searching if encountered the loop's beginning. */
+ if (bb_loop_header_p (gimple_bb (stmt)))
+ return nullptr;
+
+ return find_shift_after_xor (gimple_phi_result (stmt));
+ }
+ else if (is_gimple_assign (stmt))
+ {
+ /* Check that stmt does shift by 1.
+ There are no other statements between
+ xor and shift, in CRC cases we detect. */
+ if (can_be_crc_shift (stmt))
+ return stmt;
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+/* Returns opposite block of the XOR_BB from PRED_BB's dest blocks. */
+
+basic_block
+crc_optimization::get_xor_bb_opposite (basic_block pred_bb, basic_block xor_bb)
+{
+ /* Check that the predecessor block has exactly two successors. */
+ if (EDGE_COUNT (pred_bb->succs) != 2)
+ return nullptr;
+
+ edge e0 = EDGE_SUCC (pred_bb, 0);
+ edge e1 = EDGE_SUCC (pred_bb, 1);
+
+ /* Ensure neither outgoing edge is marked as complex. */
+ if ((e0->flags & EDGE_COMPLEX)
+ || (e1->flags & EDGE_COMPLEX))
+ return nullptr;
+
+ /* Check that one of the successors is indeed XOR_BB. */
+ gcc_assert ((e0->dest == xor_bb)
+ || (e1->dest == xor_bb));
+
+ /* Return the opposite block of XOR_BB. */
+ if (EDGE_SUCC (pred_bb, 0)->dest != xor_bb)
+ return e0->dest;
+ return e1->dest;
+}
+
+/* Checks whether the pair of xor's shift exists in the opposite
+ basic block (OPPOSITE_BB).
+ If there is a shift and xor in the same block,
+ then in the opposite block must be another shift. */
+
+bool
+crc_optimization::exists_shift_for_opp_xor_shift (basic_block opposite_bb)
+{
+ if (!opposite_bb)
+ return false;
+
+ /* Walk through the instructions of given basic block. */
+ for (gimple_stmt_iterator bsi = gsi_start_nondebug_bb (opposite_bb);
+ !gsi_end_p (bsi); gsi_next_nondebug (&bsi))
+ {
+ gimple *stmt = gsi_stmt (bsi);
+ /* Find assignment statement with shift operation.
+ Check that shift's direction is same with the shift done
+ on the path with xor, and it is a shift by one. */
+ if (is_gimple_assign (stmt))
+ {
+ if (gimple_assign_rhs_code (stmt)
+ == gimple_assign_rhs_code (m_shift_stmt)
+ && integer_onep (gimple_assign_rhs2 (stmt)))
+ return true;
+ }
+ }
+ /* If there is no shift, return false. */
+ return false;
+}
+
+/* Returns true if condition COND checks MSB/LSB bit is 1.
+ Otherwise, return false. */
+
+bool
+crc_optimization::cond_true_is_checked_for_bit_one (const gcond *cond)
+{
+ if (!cond)
+ return false;
+
+ tree rhs = gimple_cond_rhs (cond);
+ enum tree_code code = gimple_cond_code (cond);
+
+ /* If the condition is something == 1 -> return true. */
+ if (code == EQ_EXPR && integer_onep (rhs))
+ return true;
+
+ /* If the condition is something != 0 or something < 0 -> return true. */
+ if ((code == NE_EXPR || code == LT_EXPR)
+ && integer_zerop (rhs))
+ return true;
+
+ return false;
+}
+
+/* Returns true if xor is done in case the MSB/LSB is 1.
+ Otherwise, returns false.
+ In CRC calculation algorithms CRC is xor-ed with the polynomial only
+ if MSB/LSB is 1.
+
+ PRED_BB is the block containing the condition for the xor.
+ XOR_BB is the one of the successor blocks of PRED_BB, it is assumed that CRC
+ is xor-ed with the polynomial in XOR_BB.
+ COND is the condition, which is checked to satisfy the CRC condition. */
+
+bool
+crc_optimization::is_crc_satisfiable_cond (basic_block pred_bb,
+ basic_block xor_bb,
+ gcond *cond)
+{
+ edge true_edge;
+ edge false_edge;
+ extract_true_false_edges_from_block (pred_bb, &true_edge, &false_edge);
+ bool cond_is_checked_for_bit_one = cond_true_is_checked_for_bit_one (cond);
+ /* Check that xor is done in case the MSB/LSB is 1. */
+ if (cond_is_checked_for_bit_one && true_edge->dest == xor_bb)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Xor is done on true branch.\n");
+ }
+ else if (!cond_is_checked_for_bit_one && false_edge->dest == xor_bb)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Xor is done on false branch.\n");
+ }
+ else
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ "Xor is done if MSB/LSB is not one, not CRC.\n");
+ return false;
+ }
+ return true;
+}
+
+/* Checks that the variable used in the condition COND is the assumed CRC
+ (or depends on the assumed CRC).
+ Also sets data member m_phi_for_data if it isn't set and exists. */
+
+bool
+crc_optimization::is_crc_checked (gcond *cond, basic_block *loop_bbs)
+{
+ tree lhs = gimple_cond_lhs (cond);
+ set_bbs_stmts_not_visited (loop_bbs, m_crc_loop->num_nodes);
+
+ /* As conditions are in canonical form, only left part must be an
+ SSA_NAME. */
+ if (TREE_CODE (lhs) == SSA_NAME)
+ {
+ /* Return whether there is a dependence between if condition's variable
+ and xor-ed variable. Also set phi statement of data if it is not
+ determined earlier and is used in the loop. */
+ auto_vec<gimple *> cond_dep_stmts (m_crc_loop->num_nodes);
+ if (!set_defs (lhs, cond_dep_stmts, true))
+ return false;
+ return cond_depends_on_crc (cond_dep_stmts);
+ }
+
+ /* Return false if there is no dependence between if condition's variable
+ and xor-ed variable. */
+ return false;
+}
+
+/* Checks that the condition is for checking CRC.
+ Returns true if xor is done under the condition of MSB/LSB being 1, and
+ the condition's variable and xor-ed variable depend on the same variable.
+ Otherwise, returns false.
+ XOR_BB is the basic block, where the xor operation is done.
+ PRED_BB is the predecessor basic block of the XOR_BB, it is assumed that
+ the last stmt of PRED_BB checks the condition under which xor is done. */
+
+bool
+crc_optimization::crc_cond (basic_block pred_bb, basic_block xor_bb,
+ basic_block *loop_bbs)
+{
+ /* Check whether PRED_BB contains condition. We will consider only those
+ cases when xor is done immediately under the condition. */
+ gcond *cond = safe_dyn_cast<gcond *> (gsi_stmt (gsi_last_bb (pred_bb)));
+ if (!cond)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "No condition.\n");
+ return false;
+ }
+
+ /* Check that xor is done in case the MSB/LSB is 1. */
+ if (!is_crc_satisfiable_cond (pred_bb, xor_bb, cond))
+ return false;
+
+ /* Check that CRC's MSB/LSB is checked in the condition.
+ Set data member if not set and exists. */
+ if (!is_crc_checked (cond, loop_bbs))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "The condition is not related to the CRC check.\n");
+ return false;
+ }
+ return true;
+}
+
+/* Returns true if the statement with STMT_CODE may occur in CRC calculation.
+ Otherwise, returns false. */
+
+bool
+crc_optimization::is_acceptable_stmt_code (const tree_code &stmt_code)
+{
+ return (stmt_code == BIT_IOR_EXPR)
+ || (stmt_code == BIT_AND_EXPR)
+ || (stmt_code == BIT_XOR_EXPR)
+ || (stmt_code == MINUS_EXPR)
+ || (stmt_code == PLUS_EXPR)
+ || (stmt_code == RSHIFT_EXPR)
+ || (stmt_code == LSHIFT_EXPR)
+ || (TREE_CODE_CLASS (stmt_code) == tcc_unary);
+}
+
+/* Returns true if ASSIGN_STMT does shift with 1. Otherwise, returns false. */
+
+bool
+crc_optimization::can_be_crc_shift (gimple *assign_stmt)
+{
+ tree_code stmt_code = gimple_assign_rhs_code (assign_stmt);
+ if (stmt_code == LSHIFT_EXPR || stmt_code == RSHIFT_EXPR)
+ {
+ m_is_bit_forward = (stmt_code == LSHIFT_EXPR);
+ /* Check that shift one is done, keep shift statement. */
+ if (integer_onep (gimple_assign_rhs2 (assign_stmt)))
+ {
+ if (m_shift_stmt)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ "Already there is one shift.\n");
+ return false;
+ }
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ "Found <<1 or >>1.\n");
+ return true;
+ }
+ /* This filters out cases, when xor-ed variable is shifted by values
+ other than 1. */
+ }
+ return false;
+}
+
+/* Returns true if the operation done in ASSIGN_STMT can occur during CRC
+ calculation. Otherwise, returns false. */
+
+bool
+crc_optimization::can_not_be_crc_stmt (gimple *assign_stmt)
+{
+ tree_code stmt_code = gimple_assign_rhs_code (assign_stmt);
+ if (!is_acceptable_stmt_code (stmt_code))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ "\nStmt with the following operation "
+ "code %s between xor and shift, "
+ "may not be CRC.\n", get_tree_code_name (stmt_code));
+
+ return true;
+ }
+ return false;
+}
+
+/* Returns true if we can get definition of the VARIABLE, and the definition
+ is not outside the loop. Otherwise, returns false. */
+
+bool
+crc_optimization::passes_checks_for_def_chain (tree variable)
+{
+ if (!(variable && TREE_CODE (variable) == SSA_NAME))
+ return false;
+
+ /* No definition chain for default defs. */
+ if (SSA_NAME_IS_DEFAULT_DEF (variable))
+ return false;
+
+ gimple *stmt = SSA_NAME_DEF_STMT (variable);
+
+ if (!stmt)
+ return false;
+
+ /* Don't go outside the loop. */
+ if (!flow_bb_inside_loop_p (m_crc_loop, gimple_bb (stmt)))
+ return false;
+
+ return true;
+}
+
+/* This function goes up through the def-use chains of the parameter NAME.
+ Gathers all the statements within the loop,
+ from which the variable depends on and adds to the USE_DEFS.
+ Returns false, if there is a statement that may not exist in the CRC
+ loop. Otherwise, returns true. */
+
+bool
+crc_optimization::set_defs (tree name, auto_vec<gimple *> &use_defs,
+ bool keep_only_header_phis = false)
+{
+ if (!passes_checks_for_def_chain (name))
+ return true;
+
+ gimple *stmt = SSA_NAME_DEF_STMT (name);
+
+ /* Don't consider already visited statement. */
+ if (gimple_visited_p (stmt))
+ return true;
+ gimple_set_visited (stmt, true);
+
+ /* If it's not specified to keep only header phi's,
+ then keep all statements. */
+ if (!keep_only_header_phis)
+ use_defs.safe_push (stmt);
+
+ /* If it is an assignment statement,
+ get and check def-use chain for the first and second operands. */
+ if (is_a<gassign *> (stmt))
+ {
+ if (can_not_be_crc_stmt (stmt))
+ return false;
+
+ tree ssa1 = gimple_assign_rhs1 (stmt);
+ tree ssa2 = gimple_assign_rhs2 (stmt);
+ if (!set_defs (ssa1, use_defs, keep_only_header_phis))
+ return false;
+ if (!set_defs (ssa2, use_defs, keep_only_header_phis))
+ return false;
+ return true;
+ }
+ /* If it's a phi statement, not declared in loop's header,
+ get and check def-use chain for its values. For the one declared in loop's
+ header just return true and keep it, if keep_only_header_phis is true. */
+ else if (is_a<gphi *> (stmt))
+ {
+ if (bb_loop_header_p (gimple_bb (stmt)))
+ {
+ /* If it's specified to keep only header phi's, keep it. */
+ if (keep_only_header_phis)
+ use_defs.safe_push (stmt);
+ }
+ else
+ {
+ for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
+ {
+ tree val = gimple_phi_arg_def (stmt, i);
+ if (!set_defs (val, use_defs, keep_only_header_phis))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /* Return false for other than assigment and phi statement. */
+ return false;
+}
+
+/* Returns the statement which does shift 1 operation.
+ If there is no such statement, returns nullptr.
+ STMTS - are the statements within the loop before xor operation on
+ which possible CRC variable depends. */
+
+gimple *
+crc_optimization::find_shift_before_xor (const auto_vec<gimple *> &stmts)
+{
+ for (auto stmt_it = stmts.begin (); stmt_it != stmts.end (); stmt_it++)
+ {
+ /* If it is an assignment statement, check that is does shift 1. */
+ if (is_a<gassign *> (*stmt_it))
+ {
+ if (can_be_crc_shift (*stmt_it))
+ return *stmt_it;
+ }
+ }
+ return nullptr;
+}
+
+/* This function sets M_PHI_FOR_CRC and M_PHI_FOR_DATA fields.
+ At this step phi nodes for CRC and data may be mixed in places.
+ It is fixed later with the "swap_crc_and_data_if_needed" function.
+ The function returns false if there are more than two (as in CRC calculation
+ only CRC's and data's phi may exist) or no phi statements in STMTS (at least
+ there must be CRC's phi).
+ Otherwise, returns true. */
+
+bool
+crc_optimization::set_crc_and_data_phi (auto_vec<gimple *> &stmts)
+{
+ for (auto stmt_it = stmts.begin (); stmt_it != stmts.end (); stmt_it++)
+ {
+ if (is_a<gphi *> (*stmt_it) && bb_loop_header_p (gimple_bb (*stmt_it)))
+ {
+ if (!m_phi_for_crc)
+ m_phi_for_crc = as_a<gphi *> (*stmt_it);
+ else if (!m_phi_for_data)
+ m_phi_for_data = as_a<gphi *> (*stmt_it);
+ else
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Xor-ed variable depends on more than 2 "
+ "phis.\n");
+ return false;
+ }
+ }
+ }
+ return m_phi_for_crc;
+}
+
+/* Returns true if the variable checked in the condition depends on possible
+ CRC value. Otherwise, returns false. */
+
+bool
+crc_optimization::cond_depends_on_crc (auto_vec<gimple *>& stmts)
+{
+ bool con_depends_on_crc = false;
+
+ /* The condition may depend maximum on data and CRC phi's. */
+ if (stmts.length () > 2)
+ return false;
+
+ for (auto stmt_it = stmts.begin (); stmt_it != stmts.end (); stmt_it++)
+ {
+ if (is_a<gphi *> (*stmt_it) && bb_loop_header_p (gimple_bb (*stmt_it)))
+ {
+ /* Check whether variable checked in the condition depends on
+ M_PHI_FOR_CRC.
+ Here don't stop the check, to set data if needed. */
+ if (m_phi_for_crc == (*stmt_it))
+ con_depends_on_crc = true;
+ else if (m_phi_for_data && m_phi_for_data == (*stmt_it))
+ return true;
+ /* If M_PHI_FOR_DATA isn't determined, the phi statement maybe for the
+ data. Just set it. */
+ else if (!m_phi_for_data)
+ m_phi_for_data = as_a<gphi *> (*stmt_it);
+ }
+ }
+ return con_depends_on_crc;
+}
+
+/* Sets initial values for the CRC analysis.
+ This function is used multiple times during the analyses. */
+
+void
+crc_optimization::set_initial_values ()
+{
+ m_shift_stmt = nullptr;
+ m_phi_for_crc = nullptr;
+ m_phi_for_data = nullptr;
+ m_is_bit_forward = false;
+}
+
+/* This function checks if the XOR_STMT is used for CRC calculation.
+ It verifies the presence of a shift operation in the CRC_FUN function inside
+ the CRC loop. It examines operands of XOR, its dependencies, the relative
+ position of the shift operation, and the existence of a shift operation in
+ the opposite branch of conditional statements. It also checks if XOR is
+ performed when MSB/LSB is one.
+ If these conditions are met, the XOR operation may be part of a CRC
+ calculation. The function returns true if these conditions are fulfilled,
+ otherwise, it returns false. */
+
+bool
+crc_optimization::xor_calculates_crc (function *crc_fun, basic_block *loop_bbs,
+ const gimple *xor_stmt)
+{
+ tree crc_var = gimple_assign_lhs (xor_stmt);
+ set_initial_values ();
+ set_bbs_stmts_not_visited (loop_bbs, m_crc_loop->num_nodes);
+ tree ssa1 = gimple_assign_rhs1 (xor_stmt);
+ tree ssa2 = gimple_assign_rhs2 (xor_stmt);
+ if (TREE_CODE (ssa2) != INTEGER_CST)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Second operand of the "
+ "xor statement isn't an integer constant.\n");
+ return false;
+ }
+
+ /* Get the statements within the loop on which xor-ed variable depends. */
+ auto_vec<gimple *> xor_dep_stmts (m_crc_loop->num_nodes);
+ if (!set_defs (ssa1, xor_dep_stmts))
+ {
+ xor_dep_stmts.release ();
+ return false;
+ }
+
+ m_shift_stmt = find_shift_before_xor (xor_dep_stmts);
+
+ if (!set_crc_and_data_phi (xor_dep_stmts))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Xor isn't used for CRC calculation.\n");
+ return false;
+ }
+
+ /* Check the case when shift is done after xor. */
+ if (!m_shift_stmt)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "No shift before xor, trying to find after xor.\n");
+
+ set_bbs_stmts_not_visited (loop_bbs, m_crc_loop->num_nodes);
+
+ m_shift_stmt = find_shift_after_xor (crc_var);
+ if (!m_shift_stmt)
+ return false;
+ }
+
+ /* Get the basic block where xor operation is done. */
+ basic_block xor_bb = gimple_bb (xor_stmt);
+
+ /* Get the predecessor basic block of xor's block. */
+ if (!single_pred_p (xor_bb))
+ return false;
+ basic_block block_of_condition = single_pred (xor_bb);
+
+
+ /* If the found shift operation is in the same block as the xor operation,
+ verify whether another shift exists in the opposite branch of the
+ conditional statements. */
+ if (m_shift_stmt && gimple_bb (m_shift_stmt) == xor_bb)
+ {
+ basic_block opposite_block = get_xor_bb_opposite (block_of_condition,
+ xor_bb);
+ if (!exists_shift_for_opp_xor_shift (opposite_block))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ "Opposite block doesn't contain shift's pair.\n");
+ return false;
+ }
+ }
+
+ /* Check that xor is done if MSB/LSB is one.
+ If all checks succeed, then it may be a CRC. */
+ if (crc_cond (block_of_condition, xor_bb, loop_bbs))
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "\n%s function maybe contains CRC calculation.\n",
+ function_name (crc_fun));
+ return true;
+ }
+
+ return false;
+}
+
+/* Returns true if there is only two conditional blocks in the loop,
+ one may be for the CRC bit check and the other for the loop counter.
+ This may filter out some real CRCs, where more than one condition
+ is checked for the CRC calculation and branch-less CRCs. */
+
+bool
+crc_optimization::loop_contains_two_conditional_bb (basic_block *loop_bbs,
+ unsigned loop_num_nodes)
+{
+ unsigned conditional_bb_count = 0;
+ /* Iterate through the loop until the conditional branches count
+ is below 3. */
+ for (unsigned i = 0; i < loop_num_nodes && conditional_bb_count <= 2; i++)
+ {
+ basic_block bb = loop_bbs[i];
+ if (!single_succ_p (bb))
+ conditional_bb_count++;
+ }
+ return conditional_bb_count == 2;
+}
+
+/* Checks the FUNC_LOOP loop's iteration number.
+ The loop for CRC calculation may do 8, 16, 24, 32, 64 iterations. */
+
+bool
+crc_optimization::satisfies_crc_loop_iteration_count (class loop *func_loop)
+{
+ /* Calculate the number of times the latch of the loop is executed.
+ The function sets NB_ITERATIONS field of the loop. */
+ number_of_latch_executions (func_loop);
+ tree n_inters = func_loop->nb_iterations;
+ if (n_inters == NULL_TREE || n_inters == chrec_dont_know)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ "Loop iteration number is chrec_dont_know.\n");
+ return false;
+
+ }
+ else if (tree_fits_uhwi_p (n_inters))
+ {
+ unsigned HOST_WIDE_INT
+ loop_iteration_number = tree_to_uhwi (n_inters);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Loop iteration number is "
+ HOST_WIDE_INT_PRINT_UNSIGNED ".\n", loop_iteration_number);
+
+ if ((loop_iteration_number == 7 || loop_iteration_number == 15
+ || loop_iteration_number == 23 || loop_iteration_number == 31
+ || loop_iteration_number == 63))
+ return true;
+ }
+ if (stderr && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Loop iteration number isn't a constant.\n");
+ return false;
+}
+
+/* This is the main function that checks whether the given LOOP
+ calculates CRC and extracts details of the CRC calculation.
+
+ The main idea is to find the innermost loop with 8, 16, 24, 32, 64
+ iterations and xor instruction (xor is the key operation for naive CRC
+ calculation). Then, checks that the variable is shifted by one before/after
+ being used in xor.
+ Xor must be done under the condition of MSB/LSB being 1. */
+
+bool
+crc_optimization::loop_may_calculate_crc (class loop *loop)
+{
+ /* Only examine innermost loops. */
+ if (!loop || loop->inner)
+ return false;
+
+ if (!satisfies_crc_loop_iteration_count (loop))
+ return false;
+
+ m_crc_loop = loop;
+ basic_block *loop_bbs = get_loop_body_in_dom_order (m_crc_loop);
+
+ /* Filter out the cases, which don't have exactly two conditions in the loop.
+ One for the CRC bit check, the other for the loop counter. */
+ if (!loop_contains_two_conditional_bb (loop_bbs, m_crc_loop->num_nodes))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ "The number of conditional "
+ "branches in the loop isn't 2.\n");
+ return false;
+ }
+
+ /* Walk bbs of the loop. */
+ for (unsigned int i = 0; i < m_crc_loop->num_nodes; i++)
+ {
+ basic_block bb = loop_bbs[i];
+ /* Walk instructions of the bb. */
+ for (gimple_stmt_iterator bsi = gsi_start_nondebug_bb (bb);
+ !gsi_end_p (bsi); gsi_next_nondebug (&bsi))
+ {
+ gimple *stmt = gsi_stmt (bsi);
+ /* If there is an xor instruction,
+ check that it is calculating CRC. */
+ if (is_gimple_assign (stmt)
+ && gimple_assign_rhs_code (stmt) == BIT_XOR_EXPR)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ "Found xor, "
+ "checking whether it is for CRC calculation.\n");
+
+ if (xor_calculates_crc (cfun, loop_bbs, stmt))
+ {
+ dump_crc_information ();
+ free (loop_bbs);
+ return true;
+ }
+ }
+ }
+ }
+ free (loop_bbs);
+ return false;
+}
+
+unsigned int
+crc_optimization::execute (function *fun)
+{
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "\nExamining %s function.\n",
+ function_name (fun));
+
+ if (number_of_loops (fun) <= 1)
+ return 0;
+
+ /* Get loops of the function. */
+ auto loop_list = loops_list (fun, LI_ONLY_INNERMOST);
+ for (auto loop: loop_list)
+ {
+ /* Perform initial checks to filter out non-CRCs. */
+ loop_may_calculate_crc (loop);
+ }
+ return 0;
+}
+
+namespace
+{
+
+ const pass_data pass_data_crc_optimization
+ = {
+ GIMPLE_PASS, /* type */
+ "crc", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_GIMPLE_CRC_OPTIMIZATION, /* tv_id */
+ (PROP_cfg | PROP_ssa), /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+ };
+
+ class pass_crc_optimization : public gimple_opt_pass {
+ public:
+ pass_crc_optimization (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_crc_optimization, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ virtual bool gate (function *)
+ {
+ return flag_gimple_crc_optimization && !optimize_size;
+ }
+
+ virtual unsigned int execute (function *);
+
+ }; // class pass_crc_optimization
+
+ unsigned int
+ pass_crc_optimization::execute (function *fun)
+ {
+ return crc_optimization ().execute (fun);
+ }
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_crc_optimization (gcc::context *ctxt)
+{
+ return new pass_crc_optimization (ctxt);
+}
diff --git a/gcc/gimple.cc b/gcc/gimple.cc
index a9f968cb038..76518d8897d 100644
--- a/gcc/gimple.cc
+++ b/gcc/gimple.cc
@@ -3426,6 +3426,44 @@ gimple_or_expr_nonartificial_location (gimple *stmt, tree expr)
return expansion_point_location_if_in_system_header (loc);
}
+/* Set GIMPLE_PHI statements of the BB not visited. */
+
+void
+set_phi_stmts_not_visited (basic_block bb)
+{
+ for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi);
+ gsi_next (&gsi))
+ {
+ gphi *stmt = gsi.phi ();
+ gimple_set_visited (stmt, false);
+ }
+}
+
+/* Set GIMPLE statements of the BB not visited. */
+
+void
+set_gimple_stmts_not_visited (basic_block bb)
+{
+ for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+ gsi_next (&gsi))
+ {
+ gimple *stmt = gsi_stmt (gsi);
+ gimple_set_visited (stmt, false);
+ }
+}
+
+/* Set GIMPLE_PHI and GIMPLE statements of BBS not visited. */
+
+void
+set_bbs_stmts_not_visited (const basic_block *bbs, unsigned bb_num)
+{
+ for (unsigned int i = 0; i < bb_num; i++)
+ {
+ basic_block bb = bbs[i];
+ set_phi_stmts_not_visited (bb);
+ set_gimple_stmts_not_visited (bb);
+ }
+}
#if CHECKING_P
diff --git a/gcc/gimple.h b/gcc/gimple.h
index bd315ffc2dd..6f9b9695d6f 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -1674,6 +1674,9 @@ extern void maybe_remove_unused_call_args (struct function *, gimple *);
extern bool gimple_inexpensive_call_p (gcall *);
extern bool stmt_can_terminate_bb_p (gimple *);
extern location_t gimple_or_expr_nonartificial_location (gimple *, tree);
+extern void set_gimple_stmts_not_visited (basic_block);
+extern void set_phi_stmts_not_visited (basic_block);
+extern void set_bbs_stmts_not_visited (const basic_block *, unsigned);
gcall *gimple_build_builtin_unreachable (location_t);
/* Return the disposition for a warning (or all warnings by default)
diff --git a/gcc/opts.cc b/gcc/opts.cc
index d7e0126e11f..bde7f46eb9c 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -665,6 +665,7 @@ static const struct default_options default_options_table[] =
VECT_COST_MODEL_VERY_CHEAP },
{ OPT_LEVELS_2_PLUS, OPT_finline_functions, NULL, 1 },
{ OPT_LEVELS_2_PLUS, OPT_ftree_loop_distribute_patterns, NULL, 1 },
+ { OPT_LEVELS_2_PLUS, OPT_fgimple_crc_optimization, NULL, 1 },
{ OPT_LEVELS_2_PLUS, OPT_flate_combine_instructions, NULL, 1 },
/* -O2 and above optimizations, but not -Os or -Og. */
@@ -2096,6 +2097,8 @@ enable_fdo_optimizations (struct gcc_options *opts,
SET_OPTION_IF_UNSET (opts, opts_set, flag_loop_interchange, value);
SET_OPTION_IF_UNSET (opts, opts_set, flag_unroll_jam, value);
SET_OPTION_IF_UNSET (opts, opts_set, flag_tree_loop_distribution, value);
+ SET_OPTION_IF_UNSET (opts, opts_set, flag_gimple_crc_optimization, value);
+
}
/* -f{,no-}sanitize{,-recover}= suboptions. */
diff --git a/gcc/passes.def b/gcc/passes.def
index 94386ba5457..3b835b12b46 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -297,6 +297,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_cd_dce, false /* update_address_taken_p */);
NEXT_PASS (pass_iv_canon);
NEXT_PASS (pass_loop_distribution);
+ NEXT_PASS (pass_crc_optimization);
NEXT_PASS (pass_linterchange);
NEXT_PASS (pass_copy_prop);
NEXT_PASS (pass_graphite);
diff --git a/gcc/timevar.def b/gcc/timevar.def
index 0f9d2c0b032..37460def292 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -313,6 +313,7 @@ DEFTIMEVAR (TV_INITIALIZE_RTL , "initialize rtl")
DEFTIMEVAR (TV_GIMPLE_LADDRESS , "address lowering")
DEFTIMEVAR (TV_TREE_LOOP_IFCVT , "tree loop if-conversion")
DEFTIMEVAR (TV_WARN_ACCESS , "access analysis")
+DEFTIMEVAR (TV_GIMPLE_CRC_OPTIMIZATION, "crc optimization")
/* Everything else in rest_of_compilation not included above. */
DEFTIMEVAR (TV_EARLY_LOCAL , "early local passes")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 84bc91b51e9..143e01edc87 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -387,6 +387,7 @@ extern gimple_opt_pass *make_pass_graphite_transforms (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_if_conversion (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_if_to_switch (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_loop_distribution (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_crc_optimization (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_vectorize (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_simduid_cleanup (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_slp_vectorize (gcc::context *ctxt);
--
2.25.1