aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariam Arutunian <mariamarutunian@gmail.com>2024-11-11 12:59:04 -0700
committerJeff Law <jlaw@ventanamicro.com>2024-12-01 08:54:21 -0700
commit062ad209e496a69925b1ac1d80d9b99c54801830 (patch)
tree8eac0e1b0dd7fe0f5a6a55b5bc0a3b4123a89526
parent75fe4e2932136c3814e7ba9a2620d395d8f18133 (diff)
downloadgcc-062ad209e496a69925b1ac1d80d9b99c54801830.zip
gcc-062ad209e496a69925b1ac1d80d9b99c54801830.tar.gz
gcc-062ad209e496a69925b1ac1d80d9b99c54801830.tar.bz2
[PATCH v7 08/12] Add a new pass for naive CRC loops detection
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 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 (foptimize-crc): New option. * common.opt.urls: Regenerate to add foptimize-crc. * doc/invoke.texi (-foptimize-crc): Add documentation. * gimple-crc-optimization.cc: New file. * opts.cc (default_options_table): Add OPT_foptimize_crc. (enable_fdo_optimizations): Enable optimize_crc. * 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.
-rw-r--r--gcc/Makefile.in1
-rw-r--r--gcc/common.opt10
-rw-r--r--gcc/common.opt.urls3
-rw-r--r--gcc/doc/invoke.texi16
-rw-r--r--gcc/gimple-crc-optimization.cc1003
-rw-r--r--gcc/opts.cc2
-rw-r--r--gcc/passes.def1
-rw-r--r--gcc/timevar.def1
-rw-r--r--gcc/tree-pass.h1
9 files changed, 1037 insertions, 1 deletions
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index d2fe821..4cfe383 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1730,6 +1730,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 bb226ac..a42537c 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -2422,6 +2422,16 @@ fsave-optimization-record
Common Var(flag_save_optimization_record) Optimization
Write a SRCFILE.opt-record.json file detailing what optimizations were performed.
+foptimize-crc
+Common Var(flag_optimize_crc) 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.
+
foptimize-register-move
Common Ignore
Does nothing. Preserved for backward compatibility.
diff --git a/gcc/common.opt.urls b/gcc/common.opt.urls
index e9e818d..01033a9 100644
--- a/gcc/common.opt.urls
+++ b/gcc/common.opt.urls
@@ -1022,6 +1022,9 @@ UrlSuffix(gcc/Developer-Options.html#index-fopt-info)
fsave-optimization-record
UrlSuffix(gcc/Developer-Options.html#index-fsave-optimization-record)
+foptimize-crc
+UrlSuffix(gcc/Optimize-Options.html#index-foptimize-crc)
+
foptimize-sibling-calls
UrlSuffix(gcc/Optimize-Options.html#index-foptimize-sibling-calls)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index fa2532f..e27a92c 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -605,7 +605,7 @@ Objective-C and Objective-C++ Dialects}.
-fno-peephole2 -fno-printf-return-value -fno-sched-interblock
-fno-sched-spec -fno-signed-zeros
-fno-toplevel-reorder -fno-trapping-math -fno-zero-initialized-in-bss
--fomit-frame-pointer -foptimize-sibling-calls
+-fomit-frame-pointer -foptimize-crc -foptimize-sibling-calls
-fpartial-inlining -fpeel-loops -fpredictive-commoning
-fprefetch-loop-arrays
-fprofile-correction
@@ -12687,6 +12687,7 @@ also turns on the following optimization flags:
-fipa-ra -fipa-sra -fipa-vrp
-fisolate-erroneous-paths-dereference
-flra-remat
+-foptimize-crc
-foptimize-sibling-calls
-foptimize-strlen
-fpartial-inlining
@@ -12860,6 +12861,19 @@ leaf functions.
Enabled by default at @option{-O1} and higher.
+@opindex foptimize-crc
+@item -foptimize-crc
+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.
+
+Enabled by default at @option{-O2} and higher.
+
@opindex foptimize-sibling-calls
@item -foptimize-sibling-calls
Optimize sibling and tail recursive calls.
diff --git a/gcc/gimple-crc-optimization.cc b/gcc/gimple-crc-optimization.cc
new file mode 100644
index 0000000..b7c8bda
--- /dev/null
+++ b/gcc/gimple-crc-optimization.cc
@@ -0,0 +1,1003 @@
+/* CRC optimization.
+ Copyright (C) 2022-2024 Free Software Foundation, Inc.
+ Contributed by Mariam Arutunian <mariamarutunian@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 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 "cfgloop.h"
+#include "tree-scalar-evolution.h"
+
+class crc_optimization {
+ private:
+ /* Record of statements already seen. */
+ bitmap m_visited_stmts;
+
+ /* 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, 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);
+
+ /* 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);
+
+ /* 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:
+ crc_optimization () : m_visited_stmts (BITMAP_ALLOC (NULL)),
+ m_crc_loop (nullptr)
+ {
+ set_initial_values ();
+ }
+ ~crc_optimization ()
+ {
+ BITMAP_FREE (m_visited_stmts);
+ }
+ 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);
+
+ unsigned v = SSA_NAME_VERSION (xored_crc);
+ if (bitmap_bit_p (m_visited_stmts, v))
+ return nullptr;
+ bitmap_set_bit (m_visited_stmts, v);
+
+ /* 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);
+ // Consider only statements within the loop
+ if (!flow_bb_inside_loop_p (m_crc_loop, gimple_bb (stmt)))
+ continue;
+
+ /* 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)))
+ continue;
+
+ 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;
+ }
+ else if (!is_gimple_debug (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)
+{
+ tree lhs = gimple_cond_lhs (cond);
+
+ /* 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);
+ bool set_defs_succeeded = set_defs (lhs, cond_dep_stmts, true);
+ bitmap_clear (m_visited_stmts);
+ if (!set_defs_succeeded)
+ 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)
+{
+ /* 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))
+ {
+ 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;
+
+ /* Don't consider already visited names. */
+ unsigned v = SSA_NAME_VERSION (name);
+ if (bitmap_bit_p (m_visited_stmts, v))
+ return true;
+ bitmap_set_bit (m_visited_stmts, v);
+
+ /* In CRC implementations with constant polynomial maximum 12 use_def
+ statements may occur. This limit is based on an analysis of various CRC
+ implementations as well as theoretical possibilities.
+ TODO: Find a better solution. */
+ if (use_defs.length () > 12)
+ return false;
+
+ gimple *stmt = SSA_NAME_DEF_STMT (name);
+
+ /* 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,
+ const gimple *xor_stmt)
+{
+ tree crc_var = gimple_assign_lhs (xor_stmt);
+ set_initial_values ();
+ 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);
+ bool set_defs_succeeded = set_defs (ssa1, xor_dep_stmts);
+ bitmap_clear (m_visited_stmts);
+ if (!set_defs_succeeded)
+ {
+ 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");
+
+ m_shift_stmt = find_shift_after_xor (crc_var);
+ bitmap_clear (m_visited_stmts);
+ 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))
+ {
+ 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;
+ }
+
+ unsigned short checked_xor_count = 0;
+ /* 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, stmt))
+ {
+ dump_crc_information ();
+ free (loop_bbs);
+ return true;
+ }
+
+ if (++checked_xor_count == 2)
+ return false;
+ }
+ }
+ }
+ 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_optimize_crc;
+ }
+
+ 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/opts.cc b/gcc/opts.cc
index 826a822..9909d4a 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -668,6 +668,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_foptimize_crc, NULL, 1 },
{ OPT_LEVELS_2_PLUS, OPT_flate_combine_instructions, NULL, 1 },
/* -O2 and above optimizations, but not -Os or -Og. */
@@ -2099,6 +2100,7 @@ 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_optimize_crc, value);
}
/* -f{,no-}sanitize{,-recover}= suboptions. */
diff --git a/gcc/passes.def b/gcc/passes.def
index b736879..ae85ae7 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -291,6 +291,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 9c4ce0c..574e625 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -315,6 +315,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")
DEFTIMEVAR (TV_EXT_DCE , "ext dce")
/* Everything else in rest_of_compilation not included above. */
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index f8bb875..ce46362 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -388,6 +388,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);