This patch series teaches GCC to recognize longhand 64x64->128
wide-multiplication idioms and replace them with native multiply
instructions: a widening multiply followed by a right shift for the
high part, and plain MULT_EXPR for the low part.

Portable C/C++ code that needs a 128-bit product on a 64-bit target
often resorts to a longhand decomposition: split operands into 32-bit
halves, compute four partial products, and propagate carries manually.
This pattern appears in a number of real-world codebases, including
SPEC2026's 750.sealcrypto_r (seal/util/uintarith.h) and several
examples from Hacker's Delight.  Targets like AArch64 (mul/umulh) and
x86-64 can compute the full 128-bit product in one or two instructions,
but GCC does not currently fold the longhand sequence back to these.

The series is split into two patches:

  1/2  match.pd, forwprop: Flatten a power-of-two carry-add to a
       shifted compare

       A conditional power-of-two add into a running sum --

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

       -- gets rewritten to branchless straight-line code:

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

       Implementation: two match.pd recognizers (cond_carry_add,
       cond_carry_add_neg), split by gcond polarity, match the 2-arg
       PHI form via cond^.  flatten_cond_carry_add in
       tree-ssa-forwprop.cc 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.

       Independently useful as a branch-elimination peephole.  2/2
       is one consumer: it needs the carry on a branch-free path to
       classify it as the carry summand of a long-multiply variant.

  2/2  forwprop: Match and fold long-multiply patterns [PR107090]

       Adds match.pd atom patterns plus a forwprop framework that
       linearises the outer add/ior chain, classifies summands, and
       matches the multiset against a table of six decomposed
       variants:

         - carry:       single overflow comparison on the cross-sum
         - carry-long:  cross-carry with separate high/low accumulation
         - two-carry:   both cross-carry and low-carry as separate
                        comparisons
         - ladder:      sequential accumulation without explicit carry
                        comparison
         - ladder-long: ladder with separate high/low accumulation
         - low-plus:    low part as a direct sum of partial products

       Emits the canonical widening shape

         (N)(((2N) a * (2N) b) >> N)

       for high parts.  pass_optimize_widening_mul later lowers this
       to WIDEN_MULT_EXPR / MULT_HIGHPART_EXPR on supporting targets.
       Skipped when the target lacks the 2N scalar mode.

On SPEC2026's 750.sealcrypto_r:

  - AArch64 Neoverse-N1: 25% improvement
  - x86-64 Zen4:         59% improvement

Compile-time impact: single-threaded recompile of gcc/*.cc with each
branch's stage3 cc1plus (checking=yes,extra) takes 87:05 on master vs
87:55 on this series -- ~1% overhead.

Bootstrapped/regtested on AArch64, x86-64 and PowerPC.

Changes in v5:
- 1/2:
  - Replace the match.pd simplify on COND_EXPR with cond_carry_add
    / cond_carry_add_neg match recognizers (cond^), split by gcond
    polarity, plus a flatten_cond_carry_add driver in
    tree-ssa-forwprop.cc.  The driver inverts the gcond's
    comparison for the _neg form.  Modelled on match_saturation_add.
  - Remove fold_cond_carry_add_profitable_p and the tm_p.h /
    predict.h includes from gimple-match-head.cc.  The width >
    MAX_FIXED_MODE_SIZE and width % 2 != 0 guards were
    prerequisites for the can_mult_highpart_p fallback path, not
    soundness checks.  type_has_mode_precision_p subsumes them.
  - Retarget the test scans from phiopt2 to forwprop1.  Add
    forwprop-46.c covering all four arm/comparison polarities.
  - forwprop-45.c uses __UINT64_TYPE__ instead of unsigned long
    and drops the lp64 restriction, covering the type > word_mode
    regime on 32-bit targets.
- 2/2:
  - Lower the high-part as (N)(((2N) op1 * (2N) op2) >> N).
    pass_optimize_widening_mul rewrites this to WIDEN_MULT_EXPR /
    MULT_HIGHPART_EXPR on supporting targets.  Removes
    can_mult_highpart_p queries from forwprop.
  - Replace the can_mult_highpart_p prefilter in match_long_mul
    with a targetm.scalar_mode_supported_p check on the 2N mode.
    Test scans select on int128, mirroring the gate, instead of
    lp64.
  - Drop the m_long_mul_fold_p pass parameter and its passes.def
    arguments.  The long-mul fold runs in every forwprop instance.
    Test scans retargeted from forwprop2 to forwprop1.
  - Stop restricting forwprop-44.c to lp64.  With the
    can_mult_highpart_p gating gone, the fold is target-independent
    and the test passes on ilp32 targets too.

Changes in v4:
- 1/2:
  - Rebuild the guard with per-conjunct reasoning: require both
    INTEGRAL_TYPE_P (type) && TYPE_UNSIGNED (type) and
    INTEGRAL_TYPE_P (TREE_TYPE (@0)) && TYPE_UNSIGNED (TREE_TYPE (@0)).
    The v3 swap of TYPE_UNSIGNED (type) for TYPE_UNSIGNED
    (TREE_TYPE (@0)) had let signed result types with @3 == INT_MIN
    slip through into a shift-by-(-1).
  - Drop the "carry diamond" CFG-shape nickname.  Describe the
    transform in terms of the data shape it operates on (conditional
    carry-add) rather than CFG topology.  Rename the profitability
    helper to fold_cond_carry_add_profitable_p; rename phiopt
    regression tests to test_cond_carry_add*.
- 2/2:
  - Pass-parameter gating: pass_forwprop takes a new
    m_long_mul_fold_p, set true for forwprop2 onwards.  This keeps
    the fold out of pre-IPA forwprop1, where target capability
    queries are unsafe under offload compilation, without adding a
    runtime predicate.
  - Lower the high-part to MULT_HIGHPART_EXPR and rely on RTL
    expansion to pick native umul_highpart, a widening multiply, or
    a synthesised sequence.  Emission is gated on
    can_mult_highpart_p.
  - Structural redesign: per-variant fold_mul_* functions
    consolidated into a single linearise + classify + table-lookup
    framework (long_mul_table, match_long_mul,
    long_mul_classify_summand, long_mul_check_consistency).  Each
    variant is now a row in long_mul_table; consistency checks are
    shared across rows.
  - Fast-fail prefilters in match_long_mul: LHS-type prefilter at
    entry (no legitimate long-mul leaf has a signed / pointer /
    float / odd-width type) and a can_mult_highpart_p probe before
    the row loop to skip HIGH_PART rows on unsupported targets.
  - Bound long_mul_linearize_chain mid-walk by LONG_MUL_MAX_SUMMANDS
    so an overlong addition / BIT_IOR chain bails immediately rather
    than after a full traversal.
  - Emit a dump-file hint pointing at the shared inner addition when
    long-mul folding rejects a chain because of a multi-used
    intermediate (caching the partial sum into a single-use SSA
    name normally enables the fold).

Changes in v3:
- Moved carry-diamond flattening from forwprop to match.pd,
replacing ~460 lines of C++ with a 17-line match.pd pattern.
- Two-carry test scans forwprop3 (the first forwprop after phiopt2,
since early phiopt restricts which tree codes are allowed).
- Set location for new sequences.
- Updated mul_carry_low pattern.
- Added the \`mul_low_plus\` pattern.
- Fixed formatting issues.

Changes in v2:
- Fixed the testcases by separating the high part's fold count for
32-bit and 64-bit targets.

Konstantinos Eleftheriou (2):
  match.pd, forwprop: Flatten a power-of-two carry-add to a shifted
    compare
  forwprop: Match and fold long-multiply patterns [PR107090]

 gcc/match.pd                                  | 174 ++++
 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.dg/tree-ssa/long-mul-boundary-64.c    | 276 +++++
 .../gcc.dg/tree-ssa/long-mul-boundary.c       | 272 +++++
 .../gcc.dg/tree-ssa/long-mul-carry.c          | 311 ++++++
 .../gcc.dg/tree-ssa/long-mul-ladder.c         | 329 ++++++
 .../gcc.dg/tree-ssa/long-mul-low-plus.c       |  54 +
 .../gcc.dg/tree-ssa/long-mul-partial.c        | 119 +++
 .../gcc.dg/tree-ssa/long-mul-two-carry.c      | 116 +++
 gcc/testsuite/gcc.target/aarch64/long_mul.c   | 100 ++
 gcc/testsuite/gcc.target/i386/long_mul.c      | 100 ++
 gcc/tree-ssa-forwprop.cc                      | 985 +++++++++++++++++-
 14 files changed, 2941 insertions(+), 8 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
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-boundary-64.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-boundary.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-carry.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-ladder.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-low-plus.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-partial.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-two-carry.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/long_mul.c
 create mode 100644 gcc/testsuite/gcc.target/i386/long_mul.c

-- 

Reply via email to