Add a usmulsi3 expansion that uses UMULL to form the full 32-bit
product, then selects the low part or UINT32_MAX according to whether
the high part is zero.
PR target/127565
gcc/ChangeLog:
* config/aarch64/aarch64.md (usmulsi3): New.
gcc/testsuite/ChangeLog:
* gcc.target/aarch64/pr127565.c: New test.
Signed-off-by: Eikansh Gupta <[email protected]>
---
gcc/config/aarch64/aarch64.md | 22 ++++++++++
gcc/testsuite/gcc.target/aarch64/pr127565.c | 45 +++++++++++++++++++++
2 files changed, 67 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/aarch64/pr127565.c
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index 4abf2b7baae..f6b53696a56 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -4399,6 +4399,28 @@ (define_insn "*mulsi3_uxtw"
[(set_attr "type" "mul")]
)
+;; Unsigned saturating multiplication: test the upper half of the full
+;; product to select either its low half or UINT32_MAX.
+(define_expand "usmulsi3"
+ [(set (match_operand:SI 0 "register_operand")
+ (us_mult:SI (match_operand:SI 1 "register_operand")
+ (match_operand:SI 2 "register_operand")))]
+ ""
+{
+ rtx product = gen_reg_rtx (DImode);
+ emit_insn (gen_umulsidi3 (product, operands[1], operands[2]));
+
+ rtx high = gen_rtx_ZERO_EXTRACT (DImode, product, GEN_INT (32),
+ GEN_INT (32));
+ rtx cc_reg = aarch64_gen_compare_reg (EQ, high, const0_rtx);
+ rtx cond = gen_rtx_EQ (VOIDmode, cc_reg, const0_rtx);
+ rtx low = gen_lowpart (SImode, product);
+ emit_insn (gen_rtx_SET (operands[0],
+ gen_rtx_IF_THEN_ELSE (SImode, cond, low,
+ constm1_rtx)));
+ DONE;
+})
+
(define_insn "madd<mode>"
[(set (match_operand:GPI 0 "register_operand" "=r")
(plus:GPI (mult:GPI (match_operand:GPI 1 "register_operand" "r")
diff --git a/gcc/testsuite/gcc.target/aarch64/pr127565.c
b/gcc/testsuite/gcc.target/aarch64/pr127565.c
new file mode 100644
index 00000000000..2e2bd7b72cc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pr127565.c
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef unsigned int u32;
+typedef unsigned long long u64;
+typedef unsigned __int128 u128;
+
+__attribute__((noinline)) u32
+sat_mul_shift (u32 a, u32 b)
+{
+ u128 product = (u128) a * b;
+ return product >> 32 ? -1 : (u32) product;
+}
+
+__attribute__((noinline)) u32
+sat_mul_min (u32 a, u32 b)
+{
+ u128 product = (u128) a * b;
+ return product > (u128) -1U ? -1 : (u32) product;
+}
+
+__attribute__((noinline)) u32
+sat_mul_builtin (u32 a, u32 b)
+{
+ u32 product;
+ return __builtin_umul_overflow (a, b, &product) ? -1 : product;
+}
+
+__attribute__((noinline)) u32
+sat_mul_u64_shift (u32 a, u32 b)
+{
+ u64 product = (u64) a * b;
+ return product >> 32 ? -1 : (u32) product;
+}
+
+__attribute__((noinline)) u32
+sat_mul_u64_min (u32 a, u32 b)
+{
+ u64 product = (u64) a * b;
+ return product > (u64) -1U ? -1 : (u32) product;
+}
+
+/* { dg-final { scan-assembler-times {umull\tx[0-9]+, w[0-9]+, w[0-9]+} 5 } }
*/
+/* { dg-final { scan-assembler-times {tst\tx[0-9]+,} 5 } } */
+/* { dg-final { scan-assembler-times {csinv\tw[0-9]+, w[0-9]+, wzr, eq} 5 } }
*/
--
2.34.1