Uros suggested I take a look at PR rtl-optimization/3507, a long-standing
issue in GCC around the paradox that describing a target more accurately
in a machine description makes things harder for the RTL optimizers.  In
this case, replacing clobbers of the flags register with a set of the
flags register capturing the semantics, results in multi-set PARALLELs
that are poorly (not as well) handled by combine.

The first part of this patch is a ping of a patch from December 2021
https://gcc.gnu.org/pipermail/gcc-patches/2021-December/586572.html
to take advantage of REG_UNUSED notes to strip unnecessary SETs
from I2 and I3 in combine (originally developed to help powerpc64).

The new contribution is to do something similar for I1.  Interestingly,
recog_for_combine_1 already contains a code path to attempt to
trim and re-recognize a PARALLEL if it isn't initially recognized:

>From combine.cc line 11473
  /* If it isn't, there is the possibility that we previously had an insn
     that clobbered some register as a side effect, but the combined
     insn doesn't need to do that.  So try once more without the clobbers
     unless this represents an ASM insn.  */

Alas, the current implementation is incredibly inefficient, and ends
up calling recog with the same PARALLEL even if there were no changes,
i.e. no CLOBBERS in the original.  This code is reached about 9.6M
times during stages 2 and 3 of a bootstrap on x86_64, and in slightly
over half of these cases (51.3%) there were no CLOBBERS stripped
before calling recog again.  Adding a trivial "i != pos" test avoids
this wasted effort.

If this second attempt to recognize a PARALLEL fails, this patch now
attempts a third time (if there is a REG_NOTE attached to the insn),
hopefully using a REG_UNUSED note to simplify this PARALLEL to a single
set [following the same approach as the I2 and I3 cases mentioned
above].  This third attempt is tried about 24000 times during stages
2 and 3 (about 0.2% of the time), which is trivial given the 51%
improvement above.  In the gcc testsuite, this third attempt works
35 times (other platforms such as powerpc may see bigger benefit),
but most importantly this fix avoids regressions when describing
flag semantics in more detail (to resolve PR rtl-optimization/3507).


This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
and make -k check, both with and without --target_board=unix{-m32},
with no new failures.  Ok for mainline?


2026-11-11  Roger Sayle  <[email protected]>

        * combine.cc (try_combine): When I2 or I3 is a PARALLEL without
        clobbers that is effectively just a single_set, just use that
        SET during the recombination/fusion attempt.
        (dump_combine_attempt): New helper function to log combine's
        recog attempts to the dump_file.
        (recog_for_combine_1): Use new dump_combine_attempt helper.
        Only attempt to call recog again if we removed a CLOBBER
        from the PARALLEL.  Try calling recog one last time for
        PARALLELs with REG_NOTEs if they simplify to a single_set.


Thanks in advance,
Roger
--

diff --git a/gcc/combine.cc b/gcc/combine.cc
index 939c5495e39..2c7bebe93c3 100644
--- a/gcc/combine.cc
+++ b/gcc/combine.cc
@@ -2925,6 +2925,17 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, 
rtx_insn *i0,
                  alloc_insn_link (i1, regno, LOG_LINKS (i2)));
     }
 
+  /* If I2 is a PARALLEL with only one useful SET and without clobbers,
+     transform I2 into that SET.  */
+  if (GET_CODE (PATTERN (i2)) == PARALLEL
+      && GET_CODE (XVECEXP (PATTERN (i2), 0, XVECLEN (PATTERN (i2), 0) - 1))
+        != CLOBBER)
+    {
+      rtx tmp = single_set (i2);
+      if (tmp)
+       SUBST (PATTERN (i2), tmp);
+    }
+
   /* If I2 is a PARALLEL of two SETs of REGs (and perhaps some CLOBBERs),
      make those two SETs separate I1 and I2 insns, and make an I0 that is
      the original I1.  */
@@ -3447,6 +3458,18 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, 
rtx_insn *i0,
       int extra_sets = added_sets_0 + added_sets_1 + added_sets_2;
       combine_extras++;
 
+      /* If I3 was a PARALLEL with only one useful SET, we can discard
+        the other SETs now before constructing the new PARALLEL.  */
+      if (GET_CODE (newpat) == PARALLEL
+         && newpat == PATTERN (i3)
+         && GET_CODE (XVECEXP (newpat, 0, XVECLEN (newpat, 0) - 1))
+            != CLOBBER)
+       {
+         rtx tmp = single_set (i3);
+         if (tmp)
+           newpat = tmp;
+       }
+
       if (GET_CODE (newpat) == PARALLEL)
        {
          rtvec old = XVEC (newpat, 0);
@@ -11430,6 +11453,21 @@ simplify_shift_const (rtx x, enum rtx_code code, 
machine_mode result_mode,
 }
 
 
+/* A subroutine of recog_for_combine_1.  */
+
+static void
+dump_combine_attempt (int insn_code_number, rtx pat)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      if (insn_code_number < 0)
+       fputs ("Failed to match this instruction:\n", dump_file);
+      else
+       fputs ("Successfully matched this instruction:\n", dump_file);
+      print_rtl_single (dump_file, pat);
+    }
+}
+
 /* A subroutine of recog_for_combine.  See there for arguments and
    return value.  */
 
@@ -11461,14 +11499,7 @@ recog_for_combine_1 (rtx *pnewpat, rtx_insn *insn, rtx 
*pnotes,
   REG_NOTES (insn) = NULL_RTX;
 
   insn_code_number = recog (pat, insn, &num_clobbers_to_add);
-  if (dump_file && (dump_flags & TDF_DETAILS))
-    {
-      if (insn_code_number < 0)
-       fputs ("Failed to match this instruction:\n", dump_file);
-      else
-       fputs ("Successfully matched this instruction:\n", dump_file);
-      print_rtl_single (dump_file, pat);
-    }
+  dump_combine_attempt (insn_code_number, pat);
 
   /* If it isn't, there is the possibility that we previously had an insn
      that clobbered some register as a side effect, but the combined
@@ -11488,20 +11519,36 @@ recog_for_combine_1 (rtx *pnewpat, rtx_insn *insn, 
rtx *pnotes,
            pos++;
          }
 
-      SUBST_INT (XVECLEN (pat, 0), pos);
+      /* Call recog if we removed eliminated a CLOBBER.  */
+      if (i != pos)
+       {
+         SUBST_INT (XVECLEN (pat, 0), pos);
 
-      if (pos == 1)
-       pat = XVECEXP (pat, 0, 0);
+         if (pos == 1)
+           pat = XVECEXP (pat, 0, 0);
 
-      PATTERN (insn) = pat;
-      insn_code_number = recog (pat, insn, &num_clobbers_to_add);
-      if (dump_file && (dump_flags & TDF_DETAILS))
+         PATTERN (insn) = pat;
+         insn_code_number = recog (pat, insn, &num_clobbers_to_add);
+         dump_combine_attempt (insn_code_number, pat);
+       }
+
+      /* Try again, using REG_UNUSED notes.  */
+      if (insn_code_number < 0
+         && GET_CODE (pat) == PARALLEL
+         && old_notes != NULL_RTX)
        {
-         if (insn_code_number < 0)
-           fputs ("Failed to match this instruction:\n", dump_file);
-         else
-           fputs ("Successfully matched this instruction:\n", dump_file);
-         print_rtl_single (dump_file, pat);
+         REG_NOTES (insn) = old_notes;
+         rtx set = single_set (insn);
+         REG_NOTES (insn) = NULL_RTX;
+
+         if (set)
+           {
+             PATTERN (insn) = set;
+             insn_code_number = recog (set, insn, &num_clobbers_to_add);
+             dump_combine_attempt (insn_code_number, set);
+             if (insn_code_number >= 0)
+               pat = set;
+           }
        }
     }
 

Reply via email to