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

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


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Yingwei Zheng (dtcxzyw)

<details>
<summary>Changes</summary>

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


---
Full diff: https://github.com/llvm/llvm-project/pull/80796.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstructionCombining.cpp (+11) 
- (added) llvm/test/Transforms/InstCombine/umulo-square.ll (+72) 


``````````diff
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
new file mode 100644
index 0000000000000..838ebc35875b2
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/umulo-square.ll
@@ -0,0 +1,72 @@
+; 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:    [[RES:%.*]] = icmp ugt i32 [[X]], 65535
+; 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:    [[RES:%.*]] = icmp ugt i16 [[X]], 255
+; 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)

``````````

</details>


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


More information about the llvm-commits mailing list