Extend the long-multiply fold to the ladder forms, which propagate
the cross-product carries via a sequence of mask / shift / add steps
on intermediate sums rather than via an explicit overflow compare.
Three structural shapes are recognized:
ladder_sum1 = (hilo & mask) + hilo' + (xl*yl >> N)
ladder_part_sum = (xl*yl >> N) + hilo
ladder_sum2 = (ladder_part_sum & mask) + hilo'
ladder_sum3 = (hilo & mask) + (hilo' & mask) + (xl*yl >> N)
The full HIGH_PART chain looks like:
xh*yh + (hilo >> N) + (ladder_sumN >> N) [+ ...]
The corresponding LOW_PART recovers (xl*yl & mask) | (ladder_sumN << N).
match_long_mul picks up each shape via its table row; classifier
ordering keeps more-constrained ladder forms ahead of mul_low_sum
and mul_cross_sum to avoid shadowing.
gcc/ChangeLog:
* match.pd: Add mul_ladder_sum1, mul_ladder_sum2,
mul_ladder_sum3 and mul_ladder_part_sum atom recognizers.
* tree-ssa-forwprop.cc (gimple_mul_ladder_sum1): Declare.
(gimple_mul_ladder_sum2): Likewise.
(gimple_mul_ladder_sum3): Likewise.
(gimple_mul_ladder_part_sum): Likewise.
(enum long_mul_kind): Add LMK_LADDER_SUM1 / LMK_LADDER_SUM2
/ LMK_LADDER_SUM3 / LMK_LADDER_PART_SUM.
(long_mul_set_summand): Handle the new kinds.
(long_mul_classify_plus_kinds): Try mul_ladder_sum3 and
mul_ladder_sum1 before mul_low_sum (more-constrained
first); add mul_ladder_sum2.
(long_mul_classify_hi_extract): Add mul_ladder_part_sum
branch.
(long_mul_table): Add HIGH_PART and LOW_PART rows for each
ladder form.
gcc/testsuite/ChangeLog:
* gcc.target/aarch64/long_mul.c: Add mulh_ladder and
mulh_ladder_32 functions; bump expected umulh / umull
scan counts from 1 to 2.
* gcc.target/i386/long_mul.c: Same for mulq / imulq.
* gcc.dg/tree-ssa/long-mul-ladder.c: New test.
Signed-off-by: Konstantinos Eleftheriou <[email protected]>
---
(no changes since v1)
gcc/match.pd | 54 +++
.../gcc.dg/tree-ssa/long-mul-ladder.c | 329 ++++++++++++++++++
gcc/testsuite/gcc.target/aarch64/long_mul.c | 50 ++-
gcc/testsuite/gcc.target/i386/long_mul.c | 50 ++-
gcc/tree-ssa-forwprop.cc | 93 ++++-
5 files changed, 561 insertions(+), 15 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-ladder.c
diff --git a/gcc/match.pd b/gcc/match.pd
index 85a60848b2e6..ca6d57814776 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -12256,6 +12256,60 @@ and,
(mul_lolo @op0 @op1 INTEGER_CST@0)
INTEGER_CST@1)
(mul_lo (mul_cross_sum @mul_hilo0 @mul_hilo1) INTEGER_CST@0)))
+/* Ladder sum form 1: (hilo0 & mask) + hilo1 + (xl*yl >> N).
+ First variant: @mul_hilo1 is inside the inner plus:c alongside
+ (mul_lo ...). */
+(match (mul_ladder_sum1 @op0 @op1 @mul_hilo0 @mul_hilo1 @0 @1)
+ (plus:c
+ (plus:c
+ (mul_lo
+ @mul_hilo0
+ INTEGER_CST@0)
+ @mul_hilo1)
+ (mul_hi (mul_lolo @op0 @op1 INTEGER_CST@0) INTEGER_CST@1)))
+/* Second variant: @mul_hilo1 is the outermost addend and could match
+ anything, so guard that its definition is a MULT_EXPR. */
+(match (mul_ladder_sum1 @op0 @op1 @mul_hilo0 @mul_hilo1 @0 @1)
+ (plus:c
+ (plus:c
+ (mul_lo @mul_hilo0 INTEGER_CST@0)
+ (mul_hi (mul_lolo @op0 @op1 INTEGER_CST@0) INTEGER_CST@1))
+ @mul_hilo1)
+ (with {
+ tree_code mul_hilo_code = TREE_CODE (@mul_hilo1);
+ tree_code rhs_code = ERROR_MARK;
+ if (mul_hilo_code == SSA_NAME)
+ {
+ gimple *def = SSA_NAME_DEF_STMT (@mul_hilo1);
+ if (def && gimple_code (def) == GIMPLE_ASSIGN)
+ rhs_code = gimple_assign_rhs_code (def);
+ } }
+ (if (rhs_code == MULT_EXPR))))
+/* Partial ladder sum: (xl*yl >> N) + hilo. */
+(match (mul_ladder_part_sum @op0 @op1 @mul_hilo0 @0 @1)
+ (plus:c
+ (mul_hi (mul_lolo @op0 @op1 INTEGER_CST@0) INTEGER_CST@1)
+ @mul_hilo0))
+/* Ladder sum form 2: (ladder_part_sum & mask) + hilo. */
+(match (mul_ladder_sum2 @op0 @op1 @mul_hilo0 @mul_hilo1 @0 @1)
+ (plus:c
+ (mul_lo
+ (mul_ladder_part_sum @op0 @op1 @mul_hilo0 INTEGER_CST@0 INTEGER_CST@1)
+ INTEGER_CST@0)
+ @mul_hilo1))
+/* Ladder sum form 3: (hilo0 & mask) + (hilo1 & mask) + (xl*yl >> N). */
+(match (mul_ladder_sum3 @op0 @op1 @mul_hilo0 @mul_hilo1 @0 @1)
+ (plus:c
+ (plus:c
+ (mul_lo @mul_hilo0 INTEGER_CST@0)
+ (mul_lo @mul_hilo1 INTEGER_CST@0))
+ (mul_hi (mul_lolo @op0 @op1 INTEGER_CST@0) INTEGER_CST@1)))
+(match (mul_ladder_sum3 @op0 @op1 @mul_hilo0 @mul_hilo1 @0 @1)
+ (plus:c
+ (plus:c
+ (mul_lo @mul_hilo0 INTEGER_CST@0)
+ (mul_hi (mul_lolo @op0 @op1 INTEGER_CST@0) INTEGER_CST@1))
+ (mul_lo @mul_hilo1 INTEGER_CST@0)))
#endif
/* Floatint point/integer comparison and integer->integer
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/long-mul-ladder.c
b/gcc/testsuite/gcc.dg/tree-ssa/long-mul-ladder.c
new file mode 100644
index 000000000000..36eadd05aae1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/long-mul-ladder.c
@@ -0,0 +1,329 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-forwprop-details" } */
+
+typedef __UINT32_TYPE__ uint32_t;
+typedef __UINT64_TYPE__ uint64_t;
+typedef struct { uint32_t v[2]; } v2i32;
+typedef struct { uint64_t v[2]; } v2i64;
+
+uint64_t mulh_ladder (uint64_t x, uint64_t y)
+{
+ uint64_t x_lo = x & 0xFFFFFFFF;
+ uint64_t y_lo = y & 0xFFFFFFFF;
+ uint64_t x_hi = x >> 32;
+ uint64_t y_hi = y >> 32;
+ uint64_t t0 = y_lo * x_lo;
+ uint64_t t1 = y_lo * x_hi;
+ uint64_t t2 = y_hi * x_lo;
+ uint64_t t3 = y_hi * x_hi;
+ uint64_t t0_hi = t0 >> 32;
+ uint64_t u0 = t0_hi + t1;
+ uint64_t u0_lo = u0 & 0xFFFFFFFF;
+ uint64_t u0_hi = u0 >> 32;
+ uint64_t u1 = u0_lo + t2;
+ uint64_t u1_hi = u1 >> 32;
+ uint64_t u2 = u0_hi + t3;
+ uint64_t hw64 = u2 + u1_hi;
+
+ return hw64;
+}
+
+uint64_t mulh_ladder_comm (uint64_t x, uint64_t y)
+{
+ uint64_t x_lo = x & 0xFFFFFFFF;
+ uint64_t y_lo = y & 0xFFFFFFFF;
+ uint64_t x_hi = x >> 32;
+ uint64_t y_hi = y >> 32;
+ uint64_t t0 = x_lo * y_lo;
+ uint64_t t1 = x_lo * y_hi;
+ uint64_t t2 = x_hi * y_lo;
+ uint64_t t3 = x_hi * y_hi;
+ uint64_t t0_hi = t0 >> 32;
+ uint64_t u0 = t1 + t0_hi;
+ uint64_t u0_lo = u0 & 0xFFFFFFFF;
+ uint64_t u0_hi = u0 >> 32;
+ uint64_t u1 = t2 + u0_lo;
+ uint64_t u1_hi = u1 >> 32;
+ uint64_t u2 = u1_hi + u0_hi;
+ uint64_t hw64 = t3 + u2;
+
+ return hw64;
+}
+
+uint32_t mulh_ladder_32 (uint32_t x, uint32_t y)
+{
+ uint32_t x_lo = x & 0xFFFF;
+ uint32_t y_lo = y & 0xFFFF;
+ uint32_t x_hi = x >> 16;
+ uint32_t y_hi = y >> 16;
+ uint32_t t0 = y_lo * x_lo;
+ uint32_t t1 = y_lo * x_hi;
+ uint32_t t2 = y_hi * x_lo;
+ uint32_t t3 = y_hi * x_hi;
+ uint32_t t0_hi = t0 >> 16;
+ uint32_t u0 = t0_hi + t1;
+ uint32_t u0_lo = u0 & 0xFFFF;
+ uint32_t u0_hi = u0 >> 16;
+ uint32_t u1 = u0_lo + t2;
+ uint32_t u1_hi = u1 >> 16;
+ uint32_t u2 = u0_hi + t3;
+ uint32_t hw64 = u2 + u1_hi;
+
+ return hw64;
+}
+
+/* The 128-bit variant lowers to longhand in pass_optimize_widening_mul;
+ no target provides a 256-bit multiply. */
+#ifdef __SIZEOF_INT128__
+__uint128_t umulh_variant_i128 (__uint128_t x, __uint128_t y)
+{
+ __uint128_t x_lo = x & (__uint128_t)0xFFFFFFFFFFFFFFFF;
+ __uint128_t y_lo = y & (__uint128_t)0xFFFFFFFFFFFFFFFF;
+ __uint128_t x_hi = x >> 64;
+ __uint128_t y_hi = y >> 64;
+ __uint128_t t0 = y_lo * x_lo;
+ __uint128_t t1 = y_lo * x_hi;
+ __uint128_t t2 = y_hi * x_lo;
+ __uint128_t t3 = y_hi * x_hi;
+ __uint128_t t0_hi = t0 >> 64;
+ __uint128_t u0 = t0_hi + t1;
+ __uint128_t u0_lo = u0 & (__uint128_t)0xFFFFFFFFFFFFFFFF;
+ __uint128_t u0_hi = u0 >> 64;
+ __uint128_t u1 = u0_lo + t2;
+ __uint128_t u1_hi = u1 >> 64;
+ __uint128_t u2 = u0_hi + t3;
+ __uint128_t hw64 = u2 + u1_hi;
+
+ return hw64;
+}
+#endif
+
+v2i64 full_mul_ladder (uint64_t x, uint64_t y)
+{
+ uint64_t and_x = x & 0xFFFFFFFF;
+ uint64_t and_y = y & 0xFFFFFFFF;
+ uint64_t mul_i = and_y * and_x;
+ uint64_t shr_x = x >> 32;
+ uint64_t mul_i27 = and_y * shr_x;
+ uint64_t shr_y = y >> 32;
+ uint64_t mul_i28 = shr_y * and_x;
+ uint64_t mul_i29 = shr_y * shr_x;
+ uint64_t shr10 = mul_i >> 32;
+ uint64_t and11 = mul_i27 & 0xFFFFFFFF;
+ uint64_t add = and11 + mul_i28;
+ uint64_t add12 = add + shr10;
+ uint64_t shr13 = mul_i27 >> 32;
+ uint64_t shr14 = add12 >> 32;
+ uint64_t add15 = shr13 + mul_i29;
+ uint64_t add16 = add15 + shr14;
+ uint64_t shl = add12 << 32;
+ uint64_t and17 = mul_i & 0xFFFFFFFF;
+ uint64_t or_val = shl | and17;
+ v2i64 result;
+ result.v[0] = or_val;
+ result.v[1] = add16;
+ return result;
+}
+
+/* This will be optimized during the second forwprop run.
+ Disable SLP so the expected fold count is target-independent. */
+__attribute__((optimize("no-tree-slp-vectorize")))
+v2i32 mulh_ladder_v2i32 (v2i32 x, v2i32 y)
+{
+ v2i32 result;
+ for(int i=0; i<2; ++i)
+ {
+ uint32_t x_lo = x.v[i] & 0xFFFF;
+ uint32_t y_lo = y.v[i] & 0xFFFF;
+ uint32_t x_hi = x.v[i] >> 16;
+ uint32_t y_hi = y.v[i] >> 16;
+ uint32_t t0 = y_lo * x_lo;
+ uint32_t t1 = y_lo * x_hi;
+ uint32_t t2 = y_hi * x_lo;
+ uint32_t t3 = y_hi * x_hi;
+ uint32_t t0_hi = t0 >> 16;
+ uint32_t u0 = t0_hi + t1;
+ uint32_t u0_lo = u0 & 0xFFFF;
+ uint32_t u0_hi = u0 >> 16;
+ uint32_t u1 = u0_lo + t2;
+ uint32_t u1_hi = u1 >> 16;
+ uint32_t u2 = u0_hi + t3;
+ result.v[i] = u2 + u1_hi;
+ }
+
+ return result;
+}
+
+/* Ladder-long variants: hi-part sum uses the long form
+ (xh*yh + cross_hi_a + cross_hi_b + mid_hi). */
+
+uint32_t mulh_ladder_long (uint32_t x, uint32_t y)
+{
+ uint32_t xl = x & 0xFFFF;
+ uint32_t xh = x >> 16;
+ uint32_t yl = y & 0xFFFF;
+ uint32_t yh = y >> 16;
+ uint32_t mulll = xl * yl;
+ uint32_t mullh = xl * yh;
+ uint32_t mulhl = xh * yl;
+ uint32_t mulhh = xh * yh;
+ uint32_t shr8 = mulll >> 16;
+ uint32_t conv10 = mullh & 0xFFFF;
+ uint32_t add = shr8 + conv10;
+ uint32_t conv12 = mulhl & 0xFFFF;
+ uint32_t add13 = add + conv12;
+ uint32_t shr14 = add13 >> 16;
+ uint32_t shr15 = mullh >> 16;
+ uint32_t add16 = mulhh + shr15;
+ uint32_t shr17 = mulhl >> 16;
+ uint32_t add18 = add16 + shr17;
+ uint32_t add19 = add18 + shr14;
+
+ return add19;
+}
+
+void full_mul_ladder_long (uint32_t x, uint32_t y, uint32_t *p)
+{
+ uint32_t xl = x & 0xFFFF;
+ uint32_t xh = x >> 16;
+ uint32_t yl = y & 0xFFFF;
+ uint32_t yh = y >> 16;
+ uint32_t mulll = xl * yl;
+ uint32_t mullh = xl * yh;
+ uint32_t mulhl = xh * yl;
+ uint32_t mulhh = xh * yh;
+ uint32_t shr8 = mulll >> 16;
+ uint32_t conv10 = mullh & 0xFFFF;
+ uint32_t add = shr8 + conv10;
+ uint32_t conv12 = mulhl & 0xFFFF;
+ uint32_t add13 = add + conv12;
+ uint32_t shr14 = add13 >> 16;
+ uint32_t shr15 = mullh >> 16;
+ uint32_t add16 = mulhh + shr15;
+ uint32_t shr17 = mulhl >> 16;
+ uint32_t add18 = add16 + shr17;
+ uint32_t add19 = add18 + shr14;
+ p[1] = add19;
+ uint32_t add_13_shl = add13 << 16;
+ uint32_t and17 = mulll & 0xFFFF;
+ uint32_t or_val = add_13_shl | and17;
+ p[0] = or_val;
+}
+
+uint32_t mulh_ladder_long_comm (uint32_t x, uint32_t y)
+{
+ uint32_t xl = x & 0xFFFF;
+ uint32_t xh = x >> 16;
+ uint32_t yl = y & 0xFFFF;
+ uint32_t yh = y >> 16;
+ uint32_t mulll = yl * xl;
+ uint32_t mullh = yh * xl;
+ uint32_t mulhl = yl * xh;
+ uint32_t mulhh = yh * xh;
+ uint32_t shr8 = mulll >> 16;
+ uint32_t conv10 = mullh & 0xFFFF;
+ uint32_t add = conv10 + shr8;
+ uint32_t conv12 = mulhl & 0xFFFF;
+ uint32_t add13 = conv12 + add;
+ uint32_t shr14 = add13 >> 16;
+ uint32_t shr15 = mullh >> 16;
+ uint32_t shr17 = mulhl >> 16;
+ uint32_t add16 = shr14 + shr17;
+ uint32_t add18 = add16 + shr15;
+ uint32_t add19 = mulhh + add18;
+
+ return add19;
+}
+
+/* The 128-bit variant lowers to longhand in pass_optimize_widening_mul;
+ no target provides a 256-bit multiply. */
+#ifdef __SIZEOF_INT128__
+__uint128_t mulh_ladder_long_128 (__uint128_t x, __uint128_t y)
+{
+ __uint128_t xl = x & (__uint128_t)0xFFFFFFFFFFFFFFFF;
+ __uint128_t xh = x >> 64;
+ __uint128_t yl = y & (__uint128_t)0xFFFFFFFFFFFFFFFF;
+ __uint128_t yh = y >> 64;
+ __uint128_t mulll = xl * yl;
+ __uint128_t mullh = xl * yh;
+ __uint128_t mulhl = xh * yl;
+ __uint128_t mulhh = xh * yh;
+ __uint128_t shr8 = mulll >> 64;
+ __uint128_t conv10 = mullh & (__uint128_t)0xFFFFFFFFFFFFFFFF;
+ __uint128_t add = shr8 + conv10;
+ __uint128_t conv12 = mulhl & (__uint128_t)0xFFFFFFFFFFFFFFFF;
+ __uint128_t add13 = add + conv12;
+ __uint128_t shr14 = add13 >> 64;
+ __uint128_t shr15 = mullh >> 64;
+ __uint128_t add16 = mulhh + shr15;
+ __uint128_t shr17 = mulhl >> 64;
+ __uint128_t add18 = add16 + shr17;
+ __uint128_t add19 = add18 + shr14;
+
+ return add19;
+}
+#endif
+
+uint32_t mulh_ladder_long_hllh (uint32_t x, uint32_t y)
+{
+ uint32_t xl = x & 0xFFFF;
+ uint32_t xh = x >> 16;
+ uint32_t yl = y & 0xFFFF;
+ uint32_t yh = y >> 16;
+ uint32_t mulll = xl * yl;
+ uint32_t mullh = xl * yh;
+ uint32_t mulhl = xh * yl;
+ uint32_t mulhh = xh * yh;
+ uint32_t shr8 = mulll >> 16;
+ uint32_t conv10 = mulhl & 0xFFFF;
+ uint32_t add = shr8 + conv10;
+ uint32_t conv12 = mullh & 0xFFFF;
+ uint32_t add13 = add + conv12;
+ uint32_t shr14 = add13 >> 16;
+ uint32_t shr15 = mulhl >> 16;
+ uint32_t add16 = mulhh + shr15;
+ uint32_t shr17 = mullh >> 16;
+ uint32_t add18 = add16 + shr17;
+ uint32_t add19 = add18 + shr14;
+
+ return add19;
+}
+
+/* This will be optimized during the second forwprop run.
+ Disable SLP so the expected fold count is target-independent. */
+__attribute__((optimize("no-tree-slp-vectorize")))
+v2i32 mul_ladder_long_v2i32 (v2i32 x, v2i32 y)
+{
+ v2i32 result;
+ for (int i = 0; i < 2; i++)
+ {
+ uint32_t xl = x.v[i] & 0xFFFF;
+ uint32_t xh = x.v[i] >> 16;
+ uint32_t yl = y.v[i] & 0xFFFF;
+ uint32_t yh = y.v[i] >> 16;
+ uint32_t mulll = xl * yl;
+ uint32_t mullh = xl * yh;
+ uint32_t mulhl = xh * yl;
+ uint32_t mulhh = xh * yh;
+ uint32_t shr8 = mulll >> 16;
+ uint32_t conv10 = mullh & 0xFFFF;
+ uint32_t add = shr8 + conv10;
+ uint32_t conv12 = mulhl & 0xFFFF;
+ uint32_t add13 = add + conv12;
+ uint32_t shr14 = add13 >> 16;
+ uint32_t shr15 = mullh >> 16;
+ uint32_t add16 = mulhh + shr15;
+ uint32_t shr17 = mulhl >> 16;
+ uint32_t add18 = add16 + shr17;
+ result.v[i] = add18 + shr14;
+ }
+
+ return result;
+}
+
+/* On LP64 the __int128-guarded highpart also folds; on non-LP64 it
+ is elided by #ifdef and the count drops by 2. */
+/* { dg-final { scan-tree-dump-times "Long multiplication high part folded." 8
"forwprop1" { target lp64 } } } */
+/* { dg-final { scan-tree-dump-times "Long multiplication high part folded." 5
"forwprop1" { target { ! lp64 } } } } */
+/* { dg-final { scan-tree-dump-times "Long multiplication high part folded." 2
"forwprop2" } } */
+/* { dg-final { scan-tree-dump-times "Long multiplication low part folded." 2
"forwprop1" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/long_mul.c
b/gcc/testsuite/gcc.target/aarch64/long_mul.c
index 2a4709ecbfd0..f9c765380d8c 100644
--- a/gcc/testsuite/gcc.target/aarch64/long_mul.c
+++ b/gcc/testsuite/gcc.target/aarch64/long_mul.c
@@ -4,6 +4,27 @@
typedef __UINT32_TYPE__ uint32_t;
typedef __UINT64_TYPE__ uint64_t;
+/* 64-bit ladder pattern for high part. */
+uint64_t mulh_ladder (uint64_t x, uint64_t y)
+{
+ uint64_t x_lo = x & 0xFFFFFFFF;
+ uint64_t y_lo = y & 0xFFFFFFFF;
+ uint64_t x_hi = x >> 32;
+ uint64_t y_hi = y >> 32;
+ uint64_t t0 = y_lo * x_lo;
+ uint64_t t1 = y_lo * x_hi;
+ uint64_t t2 = y_hi * x_lo;
+ uint64_t t3 = y_hi * x_hi;
+ uint64_t t0_hi = t0 >> 32;
+ uint64_t u0 = t0_hi + t1;
+ uint64_t u0_lo = u0 & 0xFFFFFFFF;
+ uint64_t u0_hi = u0 >> 32;
+ uint64_t u1 = u0_lo + t2;
+ uint64_t u1_hi = u1 >> 32;
+ uint64_t u2 = u0_hi + t3;
+ return u2 + u1_hi;
+}
+
/* 64-bit carry pattern for high part. */
uint64_t mulh_carry (uint64_t x, uint64_t y)
{
@@ -28,6 +49,27 @@ uint64_t mulh_carry (uint64_t x, uint64_t y)
return interm_plus_carry + low_accum_hi;
}
+/* 32-bit ladder pattern for high part. */
+uint32_t mulh_ladder_32 (uint32_t x, uint32_t y)
+{
+ uint32_t x_lo = x & 0xFFFF;
+ uint32_t y_lo = y & 0xFFFF;
+ uint32_t x_hi = x >> 16;
+ uint32_t y_hi = y >> 16;
+ uint32_t t0 = y_lo * x_lo;
+ uint32_t t1 = y_lo * x_hi;
+ uint32_t t2 = y_hi * x_lo;
+ uint32_t t3 = y_hi * x_hi;
+ uint32_t t0_hi = t0 >> 16;
+ uint32_t u0 = t0_hi + t1;
+ uint32_t u0_lo = u0 & 0xFFFF;
+ uint32_t u0_hi = u0 >> 16;
+ uint32_t u1 = u0_lo + t2;
+ uint32_t u1_hi = u1 >> 16;
+ uint32_t u2 = u0_hi + t3;
+ return u2 + u1_hi;
+}
+
/* 32-bit carry pattern for high part. */
uint32_t mulh_carry_32 (uint32_t x, uint32_t y)
{
@@ -52,7 +94,7 @@ uint32_t mulh_carry_32 (uint32_t x, uint32_t y)
return interm_plus_carry + low_accum_hi;
}
-/* 64-bit pattern should emit umulh. */
-/* { dg-final { scan-assembler-times "umulh\t" 1 } } */
-/* 32-bit pattern should emit umull (32x32->64 widening multiply). */
-/* { dg-final { scan-assembler-times "umull\t" 1 } } */
+/* 64-bit patterns should emit umulh. */
+/* { dg-final { scan-assembler-times "umulh\t" 2 } } */
+/* 32-bit patterns should emit umull (32x32->64 widening multiply). */
+/* { dg-final { scan-assembler-times "umull\t" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/long_mul.c
b/gcc/testsuite/gcc.target/i386/long_mul.c
index 6ec87eb8dd21..d4b03ed1beda 100644
--- a/gcc/testsuite/gcc.target/i386/long_mul.c
+++ b/gcc/testsuite/gcc.target/i386/long_mul.c
@@ -4,6 +4,27 @@
typedef __UINT32_TYPE__ uint32_t;
typedef __UINT64_TYPE__ uint64_t;
+/* 64-bit ladder pattern for high part. */
+uint64_t mulh_ladder (uint64_t x, uint64_t y)
+{
+ uint64_t x_lo = x & 0xFFFFFFFF;
+ uint64_t y_lo = y & 0xFFFFFFFF;
+ uint64_t x_hi = x >> 32;
+ uint64_t y_hi = y >> 32;
+ uint64_t t0 = y_lo * x_lo;
+ uint64_t t1 = y_lo * x_hi;
+ uint64_t t2 = y_hi * x_lo;
+ uint64_t t3 = y_hi * x_hi;
+ uint64_t t0_hi = t0 >> 32;
+ uint64_t u0 = t0_hi + t1;
+ uint64_t u0_lo = u0 & 0xFFFFFFFF;
+ uint64_t u0_hi = u0 >> 32;
+ uint64_t u1 = u0_lo + t2;
+ uint64_t u1_hi = u1 >> 32;
+ uint64_t u2 = u0_hi + t3;
+ return u2 + u1_hi;
+}
+
/* 64-bit carry pattern for high part. */
uint64_t mulh_carry (uint64_t x, uint64_t y)
{
@@ -28,6 +49,27 @@ uint64_t mulh_carry (uint64_t x, uint64_t y)
return interm_plus_carry + low_accum_hi;
}
+/* 32-bit ladder pattern for high part. */
+uint32_t mulh_ladder_32 (uint32_t x, uint32_t y)
+{
+ uint32_t x_lo = x & 0xFFFF;
+ uint32_t y_lo = y & 0xFFFF;
+ uint32_t x_hi = x >> 16;
+ uint32_t y_hi = y >> 16;
+ uint32_t t0 = y_lo * x_lo;
+ uint32_t t1 = y_lo * x_hi;
+ uint32_t t2 = y_hi * x_lo;
+ uint32_t t3 = y_hi * x_hi;
+ uint32_t t0_hi = t0 >> 16;
+ uint32_t u0 = t0_hi + t1;
+ uint32_t u0_lo = u0 & 0xFFFF;
+ uint32_t u0_hi = u0 >> 16;
+ uint32_t u1 = u0_lo + t2;
+ uint32_t u1_hi = u1 >> 16;
+ uint32_t u2 = u0_hi + t3;
+ return u2 + u1_hi;
+}
+
/* 32-bit carry pattern for high part. */
uint32_t mulh_carry_32 (uint32_t x, uint32_t y)
{
@@ -52,7 +94,7 @@ uint32_t mulh_carry_32 (uint32_t x, uint32_t y)
return interm_plus_carry + low_accum_hi;
}
-/* 64-bit pattern should emit mulq (unsigned 64x64->128 multiply). */
-/* { dg-final { scan-assembler-times "\tmulq" 1 } } */
-/* 32-bit pattern should emit imulq (64-bit multiply of zero-extended
operands). */
-/* { dg-final { scan-assembler-times "\timulq" 1 } } */
+/* 64-bit patterns should emit mulq (unsigned 64x64->128 multiply). */
+/* { dg-final { scan-assembler-times "\tmulq" 2 } } */
+/* 32-bit patterns should emit imulq (64-bit multiply of zero-extended
operands). */
+/* { dg-final { scan-assembler-times "\timulq" 2 } } */
diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
index ea989c809181..f9c0f1a9854d 100644
--- a/gcc/tree-ssa-forwprop.cc
+++ b/gcc/tree-ssa-forwprop.cc
@@ -3605,6 +3605,10 @@ extern bool gimple_mul_low_accum (tree, tree *, tree
(*)(tree));
extern bool gimple_mul_carry_cross_sum (tree, tree *, tree (*)(tree));
extern bool gimple_mul_carry_low_sum (tree, tree *, tree (*)(tree));
extern bool gimple_mul_carry_low (tree, tree *, tree (*)(tree));
+extern bool gimple_mul_ladder_sum1 (tree, tree *, tree (*)(tree));
+extern bool gimple_mul_ladder_sum2 (tree, tree *, tree (*)(tree));
+extern bool gimple_mul_ladder_sum3 (tree, tree *, tree (*)(tree));
+extern bool gimple_mul_ladder_part_sum (tree, tree *, tree (*)(tree));
/* Replace STMT with a high-part multiply of OP1 and OP2, emitted as
(N)(((2N) op1 * (2N) op2) >> N).
@@ -3674,6 +3678,10 @@ enum long_mul_kind {
LMK_CROSS_SUM,
LMK_LOW_ACCUM,
LMK_LOW_SUM,
+ LMK_LADDER_SUM1,
+ LMK_LADDER_SUM2,
+ LMK_LADDER_SUM3,
+ LMK_LADDER_PART_SUM,
LMK_CARRY_LOW,
LMK_CARRY_CROSS_SUM,
LMK_CARRY_LOW_SUM,
@@ -3792,9 +3800,16 @@ long_mul_set_summand (long_mul_summand *info,
long_mul_kind kind,
break;
case LMK_LOW_ACCUM:
case LMK_LOW_SUM:
+ case LMK_LADDER_SUM1:
+ case LMK_LADDER_SUM2:
+ case LMK_LADDER_SUM3:
n_ops = 2;
n_hilos = 2;
break;
+ case LMK_LADDER_PART_SUM:
+ n_ops = 2;
+ n_hilos = 1;
+ break;
case LMK_CARRY_CROSS_SUM:
n_hilos = 3;
shift_idx = 3;
@@ -3855,9 +3870,10 @@ long_mul_classify_carry (tree leaf, long_mul_summand
*info)
}
/* Plus-based summand kinds shared by the (X >> SHIFT) and (X << SHIFT)
- classifiers. Order is by specificity: mul_low_sum's first arm is
- any plus, so mul_low_accum (which constrains both arms) shadows it
- and must come first. */
+ classifiers. Order is by specificity: mul_low_sum's first arm is any
+ plus, so mul_ladder_sum1/3 (which constrain that arm to a plus
+ containing a mul_lo) and mul_low_accum (which constrains both arms)
+ shadow it and must come first. */
static bool
long_mul_classify_plus_kinds (tree inner, long_mul_summand *info)
@@ -3868,18 +3884,34 @@ long_mul_classify_plus_kinds (tree inner,
long_mul_summand *info)
long_mul_set_summand (info, LMK_LOW_ACCUM, res_ops);
return true;
}
+ if (gimple_mul_ladder_sum3 (inner, res_ops, NULL))
+ {
+ long_mul_set_summand (info, LMK_LADDER_SUM3, res_ops);
+ return true;
+ }
+ if (gimple_mul_ladder_sum1 (inner, res_ops, NULL))
+ {
+ long_mul_set_summand (info, LMK_LADDER_SUM1, res_ops);
+ return true;
+ }
if (gimple_mul_low_sum (inner, res_ops, NULL))
{
long_mul_set_summand (info, LMK_LOW_SUM, res_ops);
return true;
}
+ if (gimple_mul_ladder_sum2 (inner, res_ops, NULL))
+ {
+ long_mul_set_summand (info, LMK_LADDER_SUM2, res_ops);
+ return true;
+ }
return false;
}
/* Classify INNER -- already unwrapped from an outer (X >> SHIFT) -- as
a high-half-extracted summand. mul_hilo (mult-shape) is orthogonal
- to the plus-based kinds and is tried first; mul_cross_sum (any plus)
- is the fallback after the shared plus-based kinds. */
+ to the plus-based kinds and is tried first; ladder_part_sum (one arm
+ unconstrained) and mul_cross_sum (any plus) are the fallbacks after
+ the shared plus-based ladder. */
static bool
long_mul_classify_hi_extract (tree inner, unsigned HOST_WIDE_INT shift,
@@ -3895,6 +3927,11 @@ long_mul_classify_hi_extract (tree inner, unsigned
HOST_WIDE_INT shift,
}
if (long_mul_classify_plus_kinds (inner, info))
return true;
+ if (gimple_mul_ladder_part_sum (inner, res_ops, NULL))
+ {
+ long_mul_set_summand (info, LMK_LADDER_PART_SUM, res_ops);
+ return true;
+ }
if (gimple_mul_cross_sum (inner, res_ops, NULL))
{
long_mul_set_summand (info, LMK_CROSS_SUM, res_ops);
@@ -3922,8 +3959,8 @@ long_mul_classify_lo_extract (tree inner, unsigned
HOST_WIDE_INT mask,
}
/* Classify INNER -- already unwrapped from an outer (X << SHIFT) -- as
- a left-shifted summand. No mul_hilo here -- that shape appears only
- under (X >> SHIFT). */
+ a left-shifted summand. No mul_hilo / ladder_part_sum here -- those
+ shapes appear only under (X >> SHIFT). */
static bool
long_mul_classify_shl_extract (tree inner, unsigned HOST_WIDE_INT shift,
@@ -4272,6 +4309,30 @@ static const long_mul_row long_mul_table[] = {
{ LMK_CARRY_LOW, LMX_NONE },
{ LMK_CARRY_CROSS_SUM, LMX_NONE } },
long_mul_check_two_carries },
+ /* xh*yh + (hilo >> N) + (ladder_sum1 >> N),
+ ladder_sum1 = (hilo & mask) + hilo' + (xl*yl >> N),
+ hilo, hilo' the two cross-half products. */
+ { long_mul_row::HIGH_PART, PLUS_EXPR, 3,
+ { { LMK_MUL_HIHI, LMX_NONE },
+ { LMK_MUL_HILO, LMX_HI },
+ { LMK_LADDER_SUM1, LMX_HI } },
+ NULL },
+ /* xh*yh + (ladder_sum2 >> N) + (ladder_part_sum >> N),
+ ladder_part_sum = (xl*yl >> N) + hilo,
+ ladder_sum2 = (ladder_part_sum & mask) + hilo'. */
+ { long_mul_row::HIGH_PART, PLUS_EXPR, 3,
+ { { LMK_MUL_HIHI, LMX_NONE },
+ { LMK_LADDER_SUM2, LMX_HI },
+ { LMK_LADDER_PART_SUM, LMX_HI } },
+ NULL },
+ /* xh*yh + (hilo >> N) + (hilo' >> N) + (ladder_sum3 >> N),
+ ladder_sum3 = (hilo & mask) + (hilo' & mask) + (xl*yl >> N). */
+ { long_mul_row::HIGH_PART, PLUS_EXPR, 4,
+ { { LMK_MUL_HIHI, LMX_NONE },
+ { LMK_MUL_HILO, LMX_HI },
+ { LMK_MUL_HILO, LMX_HI },
+ { LMK_LADDER_SUM3, LMX_HI } },
+ NULL },
/* LOW-PART folds. Recover the lower 2N bits from xl*yl plus a
shifted cross-half term. */
/* (xl*yl & mask) | (low_accum << N),
@@ -4286,6 +4347,24 @@ static const long_mul_row long_mul_table[] = {
{ { LMK_MUL_LOLO, LMX_LO },
{ LMK_LOW_SUM, LMX_SHL_N } },
NULL },
+ /* (xl*yl & mask) | (ladder_sum1 << N),
+ ladder_sum1 as in the high ladder row above. */
+ { long_mul_row::LOW_PART, BIT_IOR_EXPR, 2,
+ { { LMK_MUL_LOLO, LMX_LO },
+ { LMK_LADDER_SUM1, LMX_SHL_N } },
+ NULL },
+ /* (xl*yl & mask) | (ladder_sum2 << N),
+ ladder_sum2 as in the high ladder row above. */
+ { long_mul_row::LOW_PART, BIT_IOR_EXPR, 2,
+ { { LMK_MUL_LOLO, LMX_LO },
+ { LMK_LADDER_SUM2, LMX_SHL_N } },
+ NULL },
+ /* (xl*yl & mask) | (ladder_sum3 << N),
+ ladder_sum3 as in the high ladder-long row above. */
+ { long_mul_row::LOW_PART, BIT_IOR_EXPR, 2,
+ { { LMK_MUL_LOLO, LMX_LO },
+ { LMK_LADDER_SUM3, LMX_SHL_N } },
+ NULL },
};
/* If a multi-used inner addition (sharing the chain's outer code) blocked
--
2.55.0