[InstCombine] reduce sub-with-overflow ==/!= 0
authorSanjay Patel <spatel@rotateright.com>
Mon, 15 Aug 2022 16:25:24 +0000 (12:25 -0400)
committerSanjay Patel <spatel@rotateright.com>
Mon, 15 Aug 2022 17:03:51 +0000 (13:03 -0400)
The basic patterns look like this:
https://alive2.llvm.org/ce/z/MDj9EC

The tests have a use of the overflow value too.
Otherwise, existing folds should reduce already.

This was noted as a missing IR fold in:
926e7312b2f20f2f7b

Hopefully, this makes it easier to implement a backend
fix because we should get the same IR regardless of
whether the source used builtins or inline code.

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
llvm/test/Transforms/InstCombine/ssubo.ll
llvm/test/Transforms/InstCombine/usubo.ll

index 39f526c..46e4f6b 100644 (file)
@@ -3185,6 +3185,20 @@ Instruction *InstCombinerImpl::foldICmpInstWithConstant(ICmpInst &Cmp) {
     if (auto *II = dyn_cast<IntrinsicInst>(Cmp.getOperand(0)))
       if (Instruction *I = foldICmpIntrinsicWithConstant(Cmp, II, *C))
         return I;
+
+    // (extractval ([s/u]subo X, Y), 0) == 0 --> X == Y
+    // (extractval ([s/u]subo X, Y), 0) != 0 --> X != Y
+    // TODO: This checks one-use, but that is not strictly necessary.
+    Value *Cmp0 = Cmp.getOperand(0);
+    Value *X, *Y;
+    if (C->isZero() && Cmp.isEquality() && Cmp0->hasOneUse() &&
+        (match(Cmp0,
+               m_ExtractValue<0>(m_Intrinsic<Intrinsic::ssub_with_overflow>(
+                   m_Value(X), m_Value(Y)))) ||
+         match(Cmp0,
+               m_ExtractValue<0>(m_Intrinsic<Intrinsic::usub_with_overflow>(
+                   m_Value(X), m_Value(Y))))))
+      return new ICmpInst(Cmp.getPredicate(), X, Y);
   }
 
   if (match(Cmp.getOperand(1), m_APIntAllowUndef(C)))
index d8f965d..30749af 100644 (file)
@@ -102,8 +102,7 @@ define i1 @sub_eq0(i8 %x, i8 %y, i1 %b) {
 ; CHECK-NEXT:    [[SS:%.*]] = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
 ; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[SS]], 1
 ; CHECK-NEXT:    call void @use(i1 [[OV]])
-; CHECK-NEXT:    [[SUB:%.*]] = extractvalue { i8, i1 } [[SS]], 0
-; CHECK-NEXT:    [[EQ0:%.*]] = icmp eq i8 [[SUB]], 0
+; CHECK-NEXT:    [[EQ0:%.*]] = icmp eq i8 [[X]], [[Y]]
 ; CHECK-NEXT:    ret i1 [[EQ0]]
 ;
   %ss = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %x, i8 %y)
@@ -119,8 +118,7 @@ define i1 @sub_ne0(i8 %x, i8 %y, i1 %b) {
 ; CHECK-NEXT:    [[SS:%.*]] = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
 ; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[SS]], 1
 ; CHECK-NEXT:    call void @use(i1 [[OV]])
-; CHECK-NEXT:    [[SUB:%.*]] = extractvalue { i8, i1 } [[SS]], 0
-; CHECK-NEXT:    [[NE0:%.*]] = icmp ne i8 [[SUB]], 0
+; CHECK-NEXT:    [[NE0:%.*]] = icmp ne i8 [[X]], [[Y]]
 ; CHECK-NEXT:    ret i1 [[NE0]]
 ;
   %ss = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %x, i8 %y)
@@ -130,3 +128,41 @@ define i1 @sub_ne0(i8 %x, i8 %y, i1 %b) {
   %ne0 = icmp ne i8 %sub, 0
   ret i1 %ne0
 }
+
+; negative test - need zero
+
+define i1 @sub_eq1(i8 %x, i8 %y, i1 %b) {
+; CHECK-LABEL: @sub_eq1(
+; CHECK-NEXT:    [[SS:%.*]] = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
+; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[SS]], 1
+; CHECK-NEXT:    call void @use(i1 [[OV]])
+; CHECK-NEXT:    [[SUB:%.*]] = extractvalue { i8, i1 } [[SS]], 0
+; CHECK-NEXT:    [[EQ1:%.*]] = icmp eq i8 [[SUB]], 1
+; CHECK-NEXT:    ret i1 [[EQ1]]
+;
+  %ss = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %x, i8 %y)
+  %ov = extractvalue { i8, i1 } %ss, 1
+  call void @use(i1 %ov)
+  %sub = extractvalue { i8, i1 } %ss, 0
+  %eq1 = icmp eq i8 %sub, 1
+  ret i1 %eq1
+}
+
+; negative test - need equality pred
+
+define i1 @sub_sgt0(i8 %x, i8 %y, i1 %b) {
+; CHECK-LABEL: @sub_sgt0(
+; CHECK-NEXT:    [[SS:%.*]] = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
+; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[SS]], 1
+; CHECK-NEXT:    call void @use(i1 [[OV]])
+; CHECK-NEXT:    [[SUB:%.*]] = extractvalue { i8, i1 } [[SS]], 0
+; CHECK-NEXT:    [[SGT0:%.*]] = icmp sgt i8 [[SUB]], 0
+; CHECK-NEXT:    ret i1 [[SGT0]]
+;
+  %ss = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %x, i8 %y)
+  %ov = extractvalue { i8, i1 } %ss, 1
+  call void @use(i1 %ov)
+  %sub = extractvalue { i8, i1 } %ss, 0
+  %sgt0 = icmp sgt i8 %sub, 0
+  ret i1 %sgt0
+}
index 7ecbef2..2074190 100644 (file)
@@ -98,11 +98,9 @@ define i1 @test_constant255(i8 %a) {
 
 define i1 @sub_eq0(i8 %x, i8 %y, i1 %b) {
 ; CHECK-LABEL: @sub_eq0(
-; CHECK-NEXT:    [[US:%.*]] = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
-; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[US]], 1
+; CHECK-NEXT:    [[OV:%.*]] = icmp ult i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @use(i1 [[OV]])
-; CHECK-NEXT:    [[SUB:%.*]] = extractvalue { i8, i1 } [[US]], 0
-; CHECK-NEXT:    [[EQ0:%.*]] = icmp eq i8 [[SUB]], 0
+; CHECK-NEXT:    [[EQ0:%.*]] = icmp eq i8 [[X]], [[Y]]
 ; CHECK-NEXT:    ret i1 [[EQ0]]
 ;
   %us = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %x, i8 %y)
@@ -115,11 +113,9 @@ define i1 @sub_eq0(i8 %x, i8 %y, i1 %b) {
 
 define i1 @sub_ne0(i8 %x, i8 %y, i1 %b) {
 ; CHECK-LABEL: @sub_ne0(
-; CHECK-NEXT:    [[US:%.*]] = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
-; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[US]], 1
+; CHECK-NEXT:    [[OV:%.*]] = icmp ult i8 [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    call void @use(i1 [[OV]])
-; CHECK-NEXT:    [[SUB:%.*]] = extractvalue { i8, i1 } [[US]], 0
-; CHECK-NEXT:    [[NE0:%.*]] = icmp ne i8 [[SUB]], 0
+; CHECK-NEXT:    [[NE0:%.*]] = icmp ne i8 [[X]], [[Y]]
 ; CHECK-NEXT:    ret i1 [[NE0]]
 ;
   %us = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %x, i8 %y)
@@ -129,3 +125,41 @@ define i1 @sub_ne0(i8 %x, i8 %y, i1 %b) {
   %ne0 = icmp ne i8 %sub, 0
   ret i1 %ne0
 }
+
+; negative test - need zero
+
+define i1 @sub_eq1(i8 %x, i8 %y, i1 %b) {
+; CHECK-LABEL: @sub_eq1(
+; CHECK-NEXT:    [[SS:%.*]] = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
+; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[SS]], 1
+; CHECK-NEXT:    call void @use(i1 [[OV]])
+; CHECK-NEXT:    [[SUB:%.*]] = extractvalue { i8, i1 } [[SS]], 0
+; CHECK-NEXT:    [[EQ1:%.*]] = icmp eq i8 [[SUB]], 1
+; CHECK-NEXT:    ret i1 [[EQ1]]
+;
+  %ss = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %x, i8 %y)
+  %ov = extractvalue { i8, i1 } %ss, 1
+  call void @use(i1 %ov)
+  %sub = extractvalue { i8, i1 } %ss, 0
+  %eq1 = icmp eq i8 %sub, 1
+  ret i1 %eq1
+}
+
+; negative test - need equality pred
+
+define i1 @sub_sgt0(i8 %x, i8 %y, i1 %b) {
+; CHECK-LABEL: @sub_sgt0(
+; CHECK-NEXT:    [[SS:%.*]] = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[X:%.*]], i8 [[Y:%.*]])
+; CHECK-NEXT:    [[OV:%.*]] = extractvalue { i8, i1 } [[SS]], 1
+; CHECK-NEXT:    call void @use(i1 [[OV]])
+; CHECK-NEXT:    [[SUB:%.*]] = extractvalue { i8, i1 } [[SS]], 0
+; CHECK-NEXT:    [[SGT0:%.*]] = icmp sgt i8 [[SUB]], 0
+; CHECK-NEXT:    ret i1 [[SGT0]]
+;
+  %ss = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %x, i8 %y)
+  %ov = extractvalue { i8, i1 } %ss, 1
+  call void @use(i1 %ov)
+  %sub = extractvalue { i8, i1 } %ss, 0
+  %sgt0 = icmp sgt i8 %sub, 0
+  ret i1 %sgt0
+}