A conditional add of a power-of-two value has the shape

  sum = a + b;
  if (cmp(a, sum)) result = base + pow2;
  else             result = base;

i.e. one of two values selected by a boolean.  Rewrite to straight-line
code:

  result = base + ((type)(cmp(a, sum)) << log2(pow2));

Implementation: two match.pd recognizers (cond_carry_add,
cond_carry_add_neg) match the 2-arg PHI form via cond^.
flatten_cond_carry_add in tree-ssa-forwprop.cc runs inside the
existing degenerate-PHI walk; it lifts the branch by reusing the
dominating gcond's comparison, inverting it via
invert_tree_comparison for the _neg recognizer.  Modelled on
match_saturation_add in tree-ssa-math-opts.cc.

Two recognizers cover the four enumerated relations.  cond_carry_add
matches when the gcond's true edge selects (base + pow2): the direct
unsigned-overflow idiom ((a + b) < a, a > (a + b)).
cond_carry_add_neg matches the inverse senses (a + b > a, a < a + b),
where the gcond's true edge selects base; the driver inverts the
comparison before reuse so the rewrite remains semantically
equivalent.

The transformation is independently useful as a branch-elimination
peephole; the long-multiply fold in 2/2 is one consumer that needs
the carry on a branch-free path.

The fold accepts any unsigned integer type whose precision matches
its mode (type_has_mode_precision_p (type)).  Three regimes are
intentionally covered, without any word_mode-relative restriction:

  - type == word_mode: the canonical 64x64 -> 128 case on 64-bit
    hosts (DImode = word_mode).
  - type > word_mode: hand-rolled 64-bit chains compiled for 32-bit
    hosts (DImode > SImode = word_mode).
  - type < word_mode: hand-rolled 32-bit chains on 64-bit hosts
    (SImode < DImode = word_mode); this is a target workload.

(PHI<pow2, 0>) is already handled by the existing match.pd pattern:
a ? powerof2cst : 0 -> a << log2(powerof2cst).

        PR tree-optimization/107090

gcc/ChangeLog:

        * match.pd (cond_carry_add, cond_carry_add_neg): New match
        recognizers; split by gcond polarity.
        * tree-ssa-forwprop.cc (gimple_cond_carry_add): Declare.
        (gimple_cond_carry_add_neg): Declare.
        (flatten_cond_carry_add): New; inverts the gcond's comparison
        for the _neg recognizer before reuse.
        (pass_forwprop::execute): Flatten conditional carry-add
        patterns inside the existing degenerate-PHI walk.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/forwprop-44.c: New test.
        * gcc.dg/tree-ssa/forwprop-45.c: New test.
        * gcc.dg/tree-ssa/forwprop-46.c: New test.

Co-authored-by: Philipp Tomsich <[email protected]>
Signed-off-by: Konstantinos Eleftheriou <[email protected]>
---

(no changes since v1)

 gcc/match.pd                                | 22 ++++++
 gcc/testsuite/gcc.dg/tree-ssa/forwprop-44.c | 20 +++++
 gcc/testsuite/gcc.dg/tree-ssa/forwprop-45.c | 44 +++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/forwprop-46.c | 49 ++++++++++++
 gcc/tree-ssa-forwprop.cc                    | 83 ++++++++++++++++++++-
 5 files changed, 215 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/forwprop-44.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/forwprop-45.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/forwprop-46.c

diff --git a/gcc/match.pd b/gcc/match.pd
index e0d7ef80e14d..43d56e851172 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -6195,6 +6195,28 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
       (lshift (convert (bit_xor (convert:boolean_type_node @0)
                                { boolean_true_node; })) { shift; })))))))
 
+/* Flatten a conditional carry-add (a power-of-two carry added under
+   a guard) to a shifted compare:
+     (cmp a (plus a b)) ? (base + pow2) : base
+     -> base + ((type)(cmp a (plus a b)) << log2(pow2))
+
+   Two recognizers split the four relations by gcond polarity:
+     cond_carry_add:     the gcond's true edge selects (base + pow2)
+     cond_carry_add_neg: the gcond's true edge selects base
+   The forwprop driver inverts the comparison for the _neg form
+   before reuse.  Matches the 2-arg PHI form via cond^.  */
+(if (INTEGRAL_TYPE_P (type)
+     && TYPE_UNSIGNED (type)
+     && type_has_mode_precision_p (type))
+ (match (cond_carry_add @0 @1 @2 @3)
+  (cond^ (gt @0 (plus:c @0 @1)) (plus @2 integer_pow2p@3) @2))
+ (match (cond_carry_add @0 @1 @2 @3)
+  (cond^ (lt (plus:c @0 @1) @0) (plus @2 integer_pow2p@3) @2))
+ (match (cond_carry_add_neg @0 @1 @2 @3)
+  (cond^ (gt (plus:c @0 @1) @0) @2 (plus @2 integer_pow2p@3)))
+ (match (cond_carry_add_neg @0 @1 @2 @3)
+  (cond^ (lt @0 (plus:c @0 @1)) @2 (plus @2 integer_pow2p@3))))
+
 /* (a > 1) ? 0 : (cast)a is the same as (cast)(a == 1)
    for unsigned types. */
 (simplify
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/forwprop-44.c 
b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-44.c
new file mode 100644
index 000000000000..f9d4e06b8107
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-44.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-forwprop1" } */
+
+/* Test that forwprop flattens a conditional carry-add
+     sum = a + b; if (b > sum) result = base + 1;
+   to straight-line code
+     result = base + (type)(b > sum);  */
+
+unsigned long
+test_cond_carry_add (unsigned long a, unsigned long b,
+                    unsigned long base)
+{
+  unsigned long sum = a + b;
+  unsigned long result = base;
+  if (b > sum)
+    result = base + 1;
+  return result;
+}
+
+/* { dg-final { scan-tree-dump-not "PHI <" "forwprop1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/forwprop-45.c 
b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-45.c
new file mode 100644
index 000000000000..1b2e755febb6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-45.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-forwprop1" } */
+
+typedef __UINT64_TYPE__ uint64_t;
+
+/* Test that forwprop flattens a conditional carry-add with a
+   non-trivial shift:
+     sum = a + b; if (b > sum) result = base + ((uint64_t)1 << 32);
+   to
+     result = base + ((type)(b > sum) << 32);
+   This is the shape that match.pd's mul_carry_cross_sum pattern
+   expects.  */
+
+uint64_t
+test_cond_carry_add_shifted (uint64_t a, uint64_t b, uint64_t base)
+{
+  uint64_t sum = a + b;
+  uint64_t result = base;
+  if (b > sum)
+    result = base + ((uint64_t)1 << 32);
+  return result;
+}
+
+/* Two conditional carry-adds in sequence, as in long-multiply carry
+   chains: one with a shifted constant and one with a plain +1.  */
+
+uint64_t
+test_cond_carry_add_chain (uint64_t a, uint64_t b, uint64_t c,
+                          uint64_t d, uint64_t base)
+{
+  uint64_t sum1 = a + b;
+  uint64_t r1 = base;
+  if (b > sum1)
+    r1 = base + ((uint64_t)1 << 32);
+
+  uint64_t sum2 = c + d;
+  uint64_t r2 = r1;
+  if (d > sum2)
+    r2 = r1 + 1;
+
+  return r2;
+}
+
+/* { dg-final { scan-tree-dump-not "PHI <" "forwprop1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/forwprop-46.c 
b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-46.c
new file mode 100644
index 000000000000..4bc2e7a4cb37
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-46.c
@@ -0,0 +1,49 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+/* Runtime check for cond_carry_add flattening, all four polarities.  */
+
+extern void abort (void);
+
+__attribute__ ((noipa)) unsigned long
+p1 (unsigned long a, unsigned long b, unsigned long base)
+{
+  unsigned long s = a + b, r = base;
+  if (s < a) r = base + 1;
+  return r;
+}
+
+__attribute__ ((noipa)) unsigned long
+p2 (unsigned long a, unsigned long b, unsigned long base)
+{
+  unsigned long s = a + b, r = base;
+  if (a > s) r = base + 1;
+  return r;
+}
+
+__attribute__ ((noipa)) unsigned long
+p3 (unsigned long a, unsigned long b, unsigned long base)
+{
+  unsigned long s = a + b, r = base + 1;
+  if (s > a) r = base;
+  return r;
+}
+
+__attribute__ ((noipa)) unsigned long
+p4 (unsigned long a, unsigned long b, unsigned long base)
+{
+  unsigned long s = a + b, r = base + 1;
+  if (a < s) r = base;
+  return r;
+}
+
+int
+main (void)
+{
+  unsigned long T = ~0UL;
+  if (p1 (5, 3, 100) != 100 || p1 (T, 1, 100) != 101) abort ();
+  if (p2 (5, 3, 100) != 100 || p2 (T, 1, 100) != 101) abort ();
+  if (p3 (5, 3, 100) != 100 || p3 (T, 1, 100) != 101) abort ();
+  if (p4 (5, 3, 100) != 100 || p4 (T, 1, 100) != 101) abort ();
+  return 0;
+}
diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
index 9bb001d2f63b..1e8f3aab48f4 100644
--- a/gcc/tree-ssa-forwprop.cc
+++ b/gcc/tree-ssa-forwprop.cc
@@ -3568,6 +3568,78 @@ simplify_count_zeroes (gimple_stmt_iterator *gsi)
   return true;
 }
 
+/* Match.pd recognizers for the conditional carry-add pattern.  The
+   two names split the gcond polarity: cond_carry_add matches when
+   the true edge selects (base + pow2), cond_carry_add_neg when the
+   true edge selects base.  */
+
+extern bool gimple_cond_carry_add (tree, tree *, tree (*)(tree));
+extern bool gimple_cond_carry_add_neg (tree, tree *, tree (*)(tree));
+
+/* If PHI is a conditional power-of-two carry-add
+     res = cmp (a, a + b) ? base + pow2 : base;
+   replace it with branchless straight-line code:
+     cval    = (type) cmp;
+     shifted = cval << log2 (pow2);
+     res     = base + shifted;
+   The gcond's comparison is reused; for the _neg recognizer (true
+   edge selects base) it is inverted first.  Returns true if the PHI
+   was rewritten.  */
+
+static bool
+flatten_cond_carry_add (gphi *phi)
+{
+  tree phi_res = gimple_phi_result (phi);
+  tree type = TREE_TYPE (phi_res);
+
+  tree ops[4];
+  bool invert_cmp = false;
+  if (!gimple_cond_carry_add (phi_res, ops, NULL))
+    {
+      if (!gimple_cond_carry_add_neg (phi_res, ops, NULL))
+       return false;
+      invert_cmp = true;
+    }
+
+  tree base = ops[2];
+  tree pow2 = ops[3];
+
+  /* cond^ validated the CFG shape, so the PHI block's immediate
+     dominator is the gcond's block.  */
+  basic_block cond_bb
+    = get_immediate_dominator (CDI_DOMINATORS, gimple_bb (phi));
+  gcond *cond_stmt
+    = safe_dyn_cast <gcond *> (*gsi_last_bb (cond_bb));
+
+  if (!cond_stmt)
+    return false;
+
+  enum tree_code cmp_code = gimple_cond_code (cond_stmt);
+  if (invert_cmp)
+    cmp_code = invert_tree_comparison (cmp_code, false);
+
+  location_t loc = gimple_location (phi);
+  gimple_seq seq = NULL;
+  tree cmp
+    = gimple_build (&seq, loc, cmp_code, boolean_type_node,
+                   gimple_cond_lhs (cond_stmt),
+                   gimple_cond_rhs (cond_stmt));
+  tree cval = gimple_build (&seq, loc, NOP_EXPR, type, cmp);
+  tree shifted
+    = gimple_build (&seq, loc, LSHIFT_EXPR, type, cval,
+                   build_int_cst (integer_type_node,
+                                  wi::exact_log2 (wi::to_wide (pow2))));
+  gassign *add = gimple_build_assign (phi_res, PLUS_EXPR, base, shifted);
+  gimple_set_location (add, loc);
+  gimple_seq_add_stmt (&seq, add);
+
+  gimple_stmt_iterator gsi = gsi_after_labels (gimple_bb (phi));
+  gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT);
+  gimple_stmt_iterator psi = gsi_for_stmt (phi);
+  remove_phi_node (&psi, false);
+
+  return true;
+}
 
 /* Determine whether applying the 2 permutations (mask1 then mask2)
    gives back one of the input.  */
@@ -5354,14 +5426,19 @@ pass_forwprop::execute (function *fun)
            }
        }
 
-      /* Record degenerate PHIs in the lattice.  */
-      for (gphi_iterator si = gsi_start_phis (bb); !gsi_end_p (si);
-          gsi_next (&si))
+      /* Flatten conditional carry-add patterns and record degenerate
+        PHIs in the lattice.  Iterator advanced up front so a flattened
+        PHI (a cond_carry_add can never be degenerate, so the two cases
+        are disjoint) can be removed in-flight.  */
+      for (gphi_iterator si = gsi_start_phis (bb); !gsi_end_p (si);)
        {
          gphi *phi = si.phi ();
+         gsi_next (&si);
          tree res = gimple_phi_result (phi);
          if (virtual_operand_p (res))
            continue;
+         if (flatten_cond_carry_add (phi))
+           continue;
 
          tree first = NULL_TREE;
          bool all_same = true;
-- 
2.52.0

Reply via email to