[llvm] [InstCombine] Simplify the overflow result of `umulov X, X` (PR #80796)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 5 20:10:13 PST 2024


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/80796

This patch does the following folds if only the overflow result is used:
```
extractvalue (umul.with.overflow iN X, X), 1 -> icmp ugt X, 2^(N/2)-1
```
Alive2: https://alive2.llvm.org/ce/z/a8yPC6


>From c8955eccf1de8cb7684af25d266e587e9cfed323 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 6 Feb 2024 11:58:49 +0800
Subject: [PATCH 1/2] [InstCombine] Add pre-commit tests. NFC.

---
 .../Transforms/InstCombine/umulo-square.ll    | 74 +++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/umulo-square.ll

diff --git a/llvm/test/Transforms/InstCombine/umulo-square.ll b/llvm/test/Transforms/InstCombine/umulo-square.ll
new file mode 100644
index 0000000000000..24700251e1e1b
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/umulo-square.ll
@@ -0,0 +1,74 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -passes=instcombine -S %s | FileCheck %s
+
+define i1 @umulov_square_i32(i32 %x) {
+; CHECK-LABEL: define i1 @umulov_square_i32(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[X]], i32 [[X]])
+; CHECK-NEXT:    [[RES:%.*]] = extractvalue { i32, i1 } [[RET]], 1
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %ret = call {i32, i1} @llvm.umul.with.overflow.i32(i32 %x, i32 %x)
+  %res = extractvalue {i32, i1} %ret, 1
+  ret i1 %res
+}
+
+define i1 @umulov_square_i16(i16 %x) {
+; CHECK-LABEL: define i1 @umulov_square_i16(
+; CHECK-SAME: i16 [[X:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call { i16, i1 } @llvm.umul.with.overflow.i16(i16 [[X]], i16 [[X]])
+; CHECK-NEXT:    [[RES:%.*]] = extractvalue { i16, i1 } [[RET]], 1
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %ret = call {i16, i1} @llvm.umul.with.overflow.i16(i16 %x, i16 %x)
+  %res = extractvalue {i16, i1} %ret, 1
+  ret i1 %res
+}
+
+; Negative tests
+
+define i1 @umulov_square_i13(i13 %x) {
+; CHECK-LABEL: define i1 @umulov_square_i13(
+; CHECK-SAME: i13 [[X:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call { i13, i1 } @llvm.umul.with.overflow.i13(i13 [[X]], i13 [[X]])
+; CHECK-NEXT:    [[RES:%.*]] = extractvalue { i13, i1 } [[RET]], 1
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %ret = call {i13, i1} @llvm.umul.with.overflow.i13(i13 %x, i13 %x)
+  %res = extractvalue {i13, i1} %ret, 1
+  ret i1 %res
+}
+
+define i1 @umulov_square_i32_multiuse(i32 %x) {
+; CHECK-LABEL: define i1 @umulov_square_i32_multiuse(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[X]], i32 [[X]])
+; CHECK-NEXT:    [[RES:%.*]] = extractvalue { i32, i1 } [[RET]], 1
+; CHECK-NEXT:    [[VAL:%.*]] = extractvalue { i32, i1 } [[RET]], 0
+; CHECK-NEXT:    call void @use(i32 [[VAL]])
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %ret = call {i32, i1} @llvm.umul.with.overflow.i32(i32 %x, i32 %x)
+  %res = extractvalue {i32, i1} %ret, 1
+  %val = extractvalue {i32, i1} %ret, 0
+  call void @use(i32 %val)
+  ret i1 %res
+}
+
+define i1 @smulov_square_i32(i32 %x) {
+; CHECK-LABEL: define i1 @smulov_square_i32(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 [[X]], i32 [[X]])
+; CHECK-NEXT:    [[RES:%.*]] = extractvalue { i32, i1 } [[RET]], 1
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %ret = call {i32, i1} @llvm.smul.with.overflow.i32(i32 %x, i32 %x)
+  %res = extractvalue {i32, i1} %ret, 1
+  ret i1 %res
+}
+
+declare {i32, i1} @llvm.umul.with.overflow.i32(i32, i32)
+declare {i32, i1} @llvm.smul.with.overflow.i32(i32, i32)
+declare {i16, i1} @llvm.umul.with.overflow.i16(i16, i16)
+declare {i13, i1} @llvm.umul.with.overflow.i13(i13, i13)
+declare void @use(i32)

>From 833a9b13d5242cb2d3ef2357dec7efe2389d899b Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 6 Feb 2024 12:00:33 +0800
Subject: [PATCH 2/2] [InstCombine] Simplify the overflow result of `umulov X,
 X`

---
 .../Transforms/InstCombine/InstructionCombining.cpp   | 11 +++++++++++
 llvm/test/Transforms/InstCombine/umulo-square.ll      |  6 ++----
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index dd168917f4dc9..4e88a5cc535b1 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3459,6 +3459,17 @@ InstCombinerImpl::foldExtractOfOverflowIntrinsic(ExtractValueInst &EV) {
       WO->getLHS()->getType()->isIntOrIntVectorTy(1))
     return BinaryOperator::CreateAnd(WO->getLHS(), WO->getRHS());
 
+  // extractvalue (umul_with_overflow X, X), 1 -> X u> 2^(N/2)-1
+  if (OvID == Intrinsic::umul_with_overflow && WO->getLHS() == WO->getRHS()) {
+    unsigned BitWidth = WO->getLHS()->getType()->getScalarSizeInBits();
+    // Only handle even bitwidths for performance reasons.
+    if (BitWidth % 2 == 0)
+      return new ICmpInst(
+          ICmpInst::ICMP_UGT, WO->getLHS(),
+          ConstantInt::get(WO->getLHS()->getType(),
+                           APInt::getLowBitsSet(BitWidth, BitWidth / 2)));
+  }
+
   // If only the overflow result is used, and the right hand side is a
   // constant (or constant splat), we can remove the intrinsic by directly
   // checking for overflow.
diff --git a/llvm/test/Transforms/InstCombine/umulo-square.ll b/llvm/test/Transforms/InstCombine/umulo-square.ll
index 24700251e1e1b..838ebc35875b2 100644
--- a/llvm/test/Transforms/InstCombine/umulo-square.ll
+++ b/llvm/test/Transforms/InstCombine/umulo-square.ll
@@ -4,8 +4,7 @@
 define i1 @umulov_square_i32(i32 %x) {
 ; CHECK-LABEL: define i1 @umulov_square_i32(
 ; CHECK-SAME: i32 [[X:%.*]]) {
-; CHECK-NEXT:    [[RET:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[X]], i32 [[X]])
-; CHECK-NEXT:    [[RES:%.*]] = extractvalue { i32, i1 } [[RET]], 1
+; CHECK-NEXT:    [[RES:%.*]] = icmp ugt i32 [[X]], 65535
 ; CHECK-NEXT:    ret i1 [[RES]]
 ;
   %ret = call {i32, i1} @llvm.umul.with.overflow.i32(i32 %x, i32 %x)
@@ -16,8 +15,7 @@ define i1 @umulov_square_i32(i32 %x) {
 define i1 @umulov_square_i16(i16 %x) {
 ; CHECK-LABEL: define i1 @umulov_square_i16(
 ; CHECK-SAME: i16 [[X:%.*]]) {
-; CHECK-NEXT:    [[RET:%.*]] = call { i16, i1 } @llvm.umul.with.overflow.i16(i16 [[X]], i16 [[X]])
-; CHECK-NEXT:    [[RES:%.*]] = extractvalue { i16, i1 } [[RET]], 1
+; CHECK-NEXT:    [[RES:%.*]] = icmp ugt i16 [[X]], 255
 ; CHECK-NEXT:    ret i1 [[RES]]
 ;
   %ret = call {i16, i1} @llvm.umul.with.overflow.i16(i16 %x, i16 %x)



More information about the llvm-commits mailing list