Skip to content

[ValueTracking] Infer exact for udiv and sdiv #126438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from

Conversation

veera-sivarajan
Copy link
Contributor

@veera-sivarajan veera-sivarajan commented Feb 9, 2025

Fixes #108449

This infers the exact flag for division instructions based on
dominating conditions and assumes.

The exact flag is inferred from assumes for all targets but it won't
be inferred from dominating conditions for targets without a
single, unified instruction for division and remainder because
DivRemPairs rewrites the remainder instruction in terms of division.

Proof: https://alive2.llvm.org/ce/z/XtmMR6

This infers the `exact` flag for division instructions based on
dominating conditions and assumes.

The `exact` flag is inferred from assumes for all targets but it won't
be inferred from dominating conditions for targets without a
single, unified instruction for division and remainder because
DivRemPairs rewrites the remainder instruction in terms of division.

Proof: https://alive2.llvm.org/ce/z/XtmMR6
@llvmbot llvmbot added backend:PowerPC llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Feb 9, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 9, 2025

@llvm/pr-subscribers-llvm-analysis
@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-backend-powerpc

Author: Veera (veera-sivarajan)

Changes

This infers the exact flag for division instructions based on
dominating conditions and assumes.

The exact flag is inferred from assumes for all targets but it won't
be inferred from dominating conditions for targets without a
single, unified instruction for division and remainder because
DivRemPairs rewrites the remainder instruction in terms of division.

Proof: https://alive2.llvm.org/ce/z/XtmMR6


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

7 Files Affected:

  • (modified) llvm/include/llvm/Analysis/ValueTracking.h (+8)
  • (modified) llvm/include/llvm/Transforms/InstCombine/InstCombiner.h (+7)
  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+65)
  • (modified) llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp (+21-5)
  • (modified) llvm/lib/Transforms/Scalar/DivRemPairs.cpp (+36-4)
  • (added) llvm/test/Transforms/DivRemPairs/PowerPC/div-rem-pairs-infer-exact.ll (+115)
  • (added) llvm/test/Transforms/DivRemPairs/X86/div-rem-pairs-infer-exact.ll (+127)
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index dba54be4c92f838..801dc3931f44c42 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -122,6 +122,14 @@ bool isKnownToBeAPowerOfTwo(const Value *V, const DataLayout &DL,
 bool isKnownToBeAPowerOfTwo(const Value *V, bool OrZero, unsigned Depth,
                             const SimplifyQuery &Q);
 
+/// Return true if `X / Y` is known to be `exact` from dominating
+/// conditions.
+bool isKnownToBeAnExactDivFromConds(const Value *X, const Value *Y,
+                                    bool isSigned, const SimplifyQuery &Q);
+
+/// Return true if `V` is known to be equal to `0` from assumes.
+bool isKnownToBeZeroFromAssumes(const Value *V, const SimplifyQuery &Q);
+
 bool isOnlyUsedInZeroComparison(const Instruction *CxtI);
 
 bool isOnlyUsedInZeroEqualityComparison(const Instruction *CxtI);
diff --git a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
index fa6b60cba15aaf9..f5c64e51c17aeca 100644
--- a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
+++ b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
@@ -447,6 +447,13 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
                                         SQ.getWithInstruction(CxtI));
   }
 
+  bool isKnownToBeAnExactDivFromConds(const Value *X, const Value *Y,
+                                      bool isSigned,
+                                      const Instruction *CxtI = nullptr) {
+    return llvm::isKnownToBeAnExactDivFromConds(X, Y, isSigned,
+                                                SQ.getWithInstruction(CxtI));
+  }
+
   bool MaskedValueIsZero(const Value *V, const APInt &Mask, unsigned Depth = 0,
                          const Instruction *CxtI = nullptr) const {
     return llvm::MaskedValueIsZero(V, Mask, SQ.getWithInstruction(CxtI), Depth);
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 8a9ad55366ee703..2225050beb95f7d 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -2555,6 +2555,71 @@ bool llvm::isKnownToBeAPowerOfTwo(const Value *V, bool OrZero, unsigned Depth,
   }
 }
 
+bool llvm::isKnownToBeZeroFromAssumes(const Value *V, const SimplifyQuery &Q) {
+
+  if (Q.AC && Q.CxtI) {
+    for (auto &AssumeVH : Q.AC->assumptionsFor(V)) {
+      if (!AssumeVH)
+        continue;
+      CallInst *I = cast<CallInst>(AssumeVH);
+      const Value *Cond = I->getArgOperand(0);
+      if (match(Cond,
+                m_SpecificICmp(ICmpInst::ICMP_EQ, m_Specific(V), m_Zero())) &&
+          isValidAssumeForContext(I, Q.CxtI, Q.DT)) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+bool llvm::isKnownToBeAnExactDivFromConds(const Value *X, const Value *Y,
+                                          bool isSigned,
+                                          const SimplifyQuery &Q) {
+  if (Q.DC && Q.CxtI && Q.DT) {
+    auto MatchRem = [&](Value *DomCond, bool CondIsTrue) -> bool {
+      auto Pred = CondIsTrue ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE;
+      if (isSigned)
+        return match(DomCond,
+                     m_SpecificICmp(Pred, m_SRem(m_Specific(X), m_Specific(Y)),
+                                    m_Zero()));
+      return match(
+          DomCond,
+          m_SpecificICmp(Pred, m_URem(m_Specific(X), m_Specific(Y)), m_Zero()));
+    };
+
+    auto *BB = Q.CxtI->getParent();
+    if (!Q.DT->isReachableFromEntry(BB))
+      return false;
+
+    auto *Node = Q.DT->getNode(BB);
+    while (Node->getIDom()) {
+      auto *Pred = Node->getIDom();
+      Node = Pred;
+
+      if (auto *BI = dyn_cast<BranchInst>(Pred->getBlock()->getTerminator());
+          BI && BI->isConditional()) {
+        Value *Cond = BI->getCondition();
+
+        BasicBlockEdge Edge0(BI->getParent(), BI->getSuccessor(0));
+        if (MatchRem(Cond, true) &&
+            Q.DT->dominates(Edge0, Q.CxtI->getParent())) {
+          return true;
+        }
+
+        BasicBlockEdge Edge1(BI->getParent(), BI->getSuccessor(1));
+        if (MatchRem(Cond, false) &&
+            Q.DT->dominates(Edge1, Q.CxtI->getParent())) {
+          return true;
+        }
+      }
+    }
+  }
+
+  return false;
+}
+
 /// Test whether a GEP's result is known to be non-null.
 ///
 /// Uses properties inherent in a GEP to try to determine whether it is known
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index c8bdf029dd71c37..8ebb03f10869dc4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -1707,6 +1707,13 @@ Instruction *InstCombinerImpl::visitUDiv(BinaryOperator &I) {
     return replaceInstUsesWith(
         I, Builder.CreateLShr(Op0, Res, I.getName(), I.isExact()));
 
+  // Infer `exact` from dominating conditions.
+  if (!I.isExact() && isKnownToBeAnExactDivFromConds(Op0, Op1,
+                                                     /*isSigned=*/false, &I)) {
+    I.setIsExact();
+    return &I;
+  }
+
   return nullptr;
 }
 
@@ -1804,11 +1811,19 @@ Instruction *InstCombinerImpl::visitSDiv(BinaryOperator &I) {
   }
 
   KnownBits KnownDividend = computeKnownBits(Op0, 0, &I);
-  if (!I.isExact() &&
-      (match(Op1, m_Power2(Op1C)) || match(Op1, m_NegatedPower2(Op1C))) &&
-      KnownDividend.countMinTrailingZeros() >= Op1C->countr_zero()) {
-    I.setIsExact();
-    return &I;
+  if (!I.isExact()) {
+
+    if ((match(Op1, m_Power2(Op1C)) || match(Op1, m_NegatedPower2(Op1C))) &&
+        KnownDividend.countMinTrailingZeros() >= Op1C->countr_zero()) {
+      I.setIsExact();
+      return &I;
+    }
+
+    // Infer `exact` from dominating conditions.
+    if (isKnownToBeAnExactDivFromConds(Op0, Op1, /*isSigned=*/true, &I)) {
+      I.setIsExact();
+      return &I;
+    }
   }
 
   if (KnownDividend.isNonNegative()) {
@@ -1846,6 +1861,7 @@ Instruction *InstCombinerImpl::visitSDiv(BinaryOperator &I) {
     return SelectInst::Create(Cond, ConstantInt::get(Ty, 1),
                               ConstantInt::getAllOnesValue(Ty));
   }
+
   return nullptr;
 }
 
diff --git a/llvm/lib/Transforms/Scalar/DivRemPairs.cpp b/llvm/lib/Transforms/Scalar/DivRemPairs.cpp
index 3adb3539f5890cf..53cd2bd8d16a75d 100644
--- a/llvm/lib/Transforms/Scalar/DivRemPairs.cpp
+++ b/llvm/lib/Transforms/Scalar/DivRemPairs.cpp
@@ -15,6 +15,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/AssumptionCache.h"
 #include "llvm/Analysis/GlobalsModRef.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
 #include "llvm/Analysis/ValueTracking.h"
@@ -178,7 +179,7 @@ static DivRemWorklistTy getWorklist(Function &F) {
 /// Note: This transform could be an oddball enhancement to EarlyCSE, GVN, or
 /// SimplifyCFG, but it's split off on its own because it's different enough
 /// that it doesn't quite match the stated objectives of those passes.
-static bool optimizeDivRem(Function &F, const TargetTransformInfo &TTI,
+static bool optimizeDivRem(Function &F, TargetTransformInfo &TTI,
                            const DominatorTree &DT) {
   bool Changed = false;
 
@@ -196,6 +197,35 @@ static bool optimizeDivRem(Function &F, const TargetTransformInfo &TTI,
     auto &DivInst = E.DivInst;
     auto &RemInst = E.RemInst;
 
+    // Before any transformation, analyze whether
+    // RemInst is known to be zero from assumes. If so,
+    // set `exact` flag on DivInst.
+    //
+    // This kind of flag inference is usually handled by
+    // InstCombine but it will not work for this scenario
+    // because DivRemPairs is run towards the end of the
+    // optimization pipeline and removes all poison generating
+    // flags if the target does not have an unified instruction
+    // for both division and remainder.
+    //
+    // So, we do this flag inference here to ensure that flags
+    // inferred from assumes are not removed regardless of
+    // `HasDivRemOp`.
+    //
+    // Doing this in the tail end of the pipeline does not
+    // affect any follow-up optimizations because
+    // the `exact` flag is mostly used by the backend to
+    // generate better code.
+
+    const DataLayout &DL = DivInst->getDataLayout();
+    AssumptionCache AC(F, &TTI);
+    SimplifyQuery SQ(DL, &DT, &AC, DivInst);
+    bool RemInstIsZero = llvm::isKnownToBeZeroFromAssumes(RemInst, SQ);
+
+    if (RemInstIsZero) {
+      DivInst->setIsExact();
+    }
+
     const bool RemOriginallyWasInExpandedForm = E.isRemExpanded();
     (void)RemOriginallyWasInExpandedForm; // suppress unused variable warning
 
@@ -371,9 +401,11 @@ static bool optimizeDivRem(Function &F, const TargetTransformInfo &TTI,
       Sub->insertAfter(Mul->getIterator());
       Sub->setDebugLoc(RemInst->getDebugLoc());
 
-      // If DivInst has the exact flag, remove it. Otherwise this optimization
-      // may replace a well-defined value 'X % Y' with poison.
-      DivInst->dropPoisonGeneratingFlags();
+      // Remove the `exact` flag from DivInst if it is not inferred from
+      // assumes. Otherwise this optimization may replace a well-defined
+      // value 'X % Y' with poison.
+      if (!RemInstIsZero)
+        DivInst->dropPoisonGeneratingFlags();
 
       // If X can be undef, X should be frozen first.
       // For example, let's assume that Y = 1 & X = undef:
diff --git a/llvm/test/Transforms/DivRemPairs/PowerPC/div-rem-pairs-infer-exact.ll b/llvm/test/Transforms/DivRemPairs/PowerPC/div-rem-pairs-infer-exact.ll
new file mode 100644
index 000000000000000..a45fbb293e770ee
--- /dev/null
+++ b/llvm/test/Transforms/DivRemPairs/PowerPC/div-rem-pairs-infer-exact.ll
@@ -0,0 +1,115 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes="instcombine,div-rem-pairs" -S -mtriple=powerpc64-unknown-unknown | FileCheck %s
+
+
+; ensure that `exact` flags inferred from assumes are
+; not removed.
+
+define i8 @udiv_exact_assume(i8 %x, i8 %y) {
+; CHECK-LABEL: define i8 @udiv_exact_assume(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:    [[X_FROZEN:%.*]] = freeze i8 [[X]]
+; CHECK-NEXT:    [[Y_FROZEN:%.*]] = freeze i8 [[Y]]
+; CHECK-NEXT:    [[DIV3:%.*]] = udiv exact i8 [[X_FROZEN]], [[Y_FROZEN]]
+; CHECK-NEXT:    [[TMP1:%.*]] = mul i8 [[DIV3]], [[Y_FROZEN]]
+; CHECK-NEXT:    [[REM:%.*]] = sub i8 [[X_FROZEN]], [[TMP1]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[REM]], 0
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    ret i8 [[DIV3]]
+;
+  %rem = urem i8 %x, %y
+  %cmp = icmp eq i8 %rem, 0
+  tail call void @llvm.assume(i1 %cmp)
+  %div3 = udiv i8 %x, %y
+  ret i8 %div3
+}
+
+define i8 @udiv_exact_assume_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i8 @udiv_exact_assume_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:    [[X_FROZEN:%.*]] = freeze i8 [[X]]
+; CHECK-NEXT:    [[Y_FROZEN:%.*]] = freeze i8 [[Y]]
+; CHECK-NEXT:    [[DIV3:%.*]] = udiv i8 [[X_FROZEN]], [[Y_FROZEN]]
+; CHECK-NEXT:    [[TMP1:%.*]] = mul i8 [[DIV3]], [[Y_FROZEN]]
+; CHECK-NEXT:    [[REM_DECOMPOSED:%.*]] = sub i8 [[X_FROZEN]], [[TMP1]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[REM_DECOMPOSED]], 1
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    ret i8 [[DIV3]]
+;
+  %rem = urem i8 %x, %y
+  %cmp = icmp eq i8 %rem, 1
+  tail call void @llvm.assume(i1 %cmp)
+  %div3 = udiv i8 %x, %y
+  ret i8 %div3
+}
+
+
+; exact flag cannot be inferred from dominant conditions
+; because remainder instruction gets decomposed.
+
+define i8 @infer_exact_from_dom_cond_negative(i8 %X, i8 %Y) {
+; CHECK-LABEL: define i8 @infer_exact_from_dom_cond_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[X_FROZEN:%.*]] = freeze i8 [[X]]
+; CHECK-NEXT:    [[Y_FROZEN:%.*]] = freeze i8 [[Y]]
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i8 [[X_FROZEN]], [[Y_FROZEN]]
+; CHECK-NEXT:    [[TMP0:%.*]] = mul i8 [[DIV]], [[Y_FROZEN]]
+; CHECK-NEXT:    [[REM_DECOMPOSED:%.*]] = sub i8 [[X_FROZEN]], [[TMP0]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[REM_DECOMPOSED]], 0
+; CHECK-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[RETURN:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    br label %[[RETURN]]
+; CHECK:       [[RETURN]]:
+; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi i8 [ [[DIV]], %[[IF_THEN]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT:    ret i8 [[RETVAL_0]]
+;
+entry:
+  %rem = srem i8 %X, %Y
+  %cmp = icmp eq i8 %rem, 0
+  br i1 %cmp, label %if.then, label %return
+
+if.then:
+  %div = sdiv i8 %X, %Y
+  br label %return
+
+return:
+  %retval.0 = phi i8 [ %div, %if.then ], [ 0, %entry ]
+  ret i8 %retval.0
+}
+
+define i8 @infer_exact_from_dom_cond_false_path_negative(i8 %X, i8 %Y) {
+; CHECK-LABEL: define i8 @infer_exact_from_dom_cond_false_path_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[X_FROZEN:%.*]] = freeze i8 [[X]]
+; CHECK-NEXT:    [[Y_FROZEN:%.*]] = freeze i8 [[Y]]
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i8 [[X_FROZEN]], [[Y_FROZEN]]
+; CHECK-NEXT:    [[TMP0:%.*]] = mul i8 [[DIV]], [[Y_FROZEN]]
+; CHECK-NEXT:    [[REM_DECOMPOSED:%.*]] = sub i8 [[X_FROZEN]], [[TMP0]]
+; CHECK-NEXT:    [[CMP_NOT:%.*]] = icmp eq i8 [[REM_DECOMPOSED]], 0
+; CHECK-NEXT:    br i1 [[CMP_NOT]], label %[[IF_ELSE:.*]], label %[[IF_THEN:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    br label %[[RETURN:.*]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    br label %[[RETURN]]
+; CHECK:       [[RETURN]]:
+; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi i8 [ 0, %[[IF_THEN]] ], [ [[DIV]], %[[IF_ELSE]] ]
+; CHECK-NEXT:    ret i8 [[RETVAL_0]]
+;
+entry:
+  %rem = srem i8 %X, %Y
+  %cmp = icmp ne i8 %rem, 0
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:
+  br label %return
+
+if.else:
+  %div = sdiv i8 %X, %Y
+  br label %return
+
+return:
+  %retval.0 = phi i8 [ 0, %if.then ], [ %div, %if.else ]
+  ret i8 %retval.0
+}
diff --git a/llvm/test/Transforms/DivRemPairs/X86/div-rem-pairs-infer-exact.ll b/llvm/test/Transforms/DivRemPairs/X86/div-rem-pairs-infer-exact.ll
new file mode 100644
index 000000000000000..fe7b8e18925f3d6
--- /dev/null
+++ b/llvm/test/Transforms/DivRemPairs/X86/div-rem-pairs-infer-exact.ll
@@ -0,0 +1,127 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes="instcombine,div-rem-pairs" -S -mtriple=x86_64-unknown-unknown    | FileCheck %s
+
+; ensure that `exact` flags inferred from assumes and dominant
+; conditions are not removed.
+
+define i8 @udiv_exact_assume(i8 %x, i8 %y) {
+; CHECK-LABEL: define i8 @udiv_exact_assume(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:    [[REM:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[REM]], 0
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    [[DIV3:%.*]] = udiv exact i8 [[X]], [[Y]]
+; CHECK-NEXT:    ret i8 [[DIV3]]
+;
+  %rem = urem i8 %x, %y
+  %cmp = icmp eq i8 %rem, 0
+  tail call void @llvm.assume(i1 %cmp)
+  %div3 = udiv i8 %x, %y
+  ret i8 %div3
+}
+
+define i8 @udiv_exact_assume_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i8 @udiv_exact_assume_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:    [[REM:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[REM]], 1
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    [[DIV3:%.*]] = udiv i8 [[X]], [[Y]]
+; CHECK-NEXT:    ret i8 [[DIV3]]
+;
+  %rem = urem i8 %x, %y
+  %cmp = icmp eq i8 %rem, 1
+  tail call void @llvm.assume(i1 %cmp)
+  %div3 = udiv i8 %x, %y
+  ret i8 %div3
+}
+
+define i8 @infer_exact_from_dom_cond(i8 %X, i8 %Y) {
+; CHECK-LABEL: define i8 @infer_exact_from_dom_cond(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[REM:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv exact i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[REM]], 0
+; CHECK-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[RETURN:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    br label %[[RETURN]]
+; CHECK:       [[RETURN]]:
+; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi i8 [ [[DIV]], %[[IF_THEN]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT:    ret i8 [[RETVAL_0]]
+;
+entry:
+  %rem = srem i8 %X, %Y
+  %cmp = icmp eq i8 %rem, 0
+  br i1 %cmp, label %if.then, label %return
+
+if.then:
+  %div = sdiv i8 %X, %Y
+  br label %return
+
+return:
+  %retval.0 = phi i8 [ %div, %if.then ], [ 0, %entry ]
+  ret i8 %retval.0
+}
+
+define i8 @infer_exact_from_dom_cond_false_path(i8 %X, i8 %Y) {
+; CHECK-LABEL: define i8 @infer_exact_from_dom_cond_false_path(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[REM:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv exact i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP_NOT:%.*]] = icmp eq i8 [[REM]], 0
+; CHECK-NEXT:    br i1 [[CMP_NOT]], label %[[IF_ELSE:.*]], label %[[IF_THEN:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    br label %[[RETURN:.*]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    br label %[[RETURN]]
+; CHECK:       [[RETURN]]:
+; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi i8 [ 0, %[[IF_THEN]] ], [ [[DIV]], %[[IF_ELSE]] ]
+; CHECK-NEXT:    ret i8 [[RETVAL_0]]
+;
+entry:
+  %rem = srem i8 %X, %Y
+  %cmp = icmp ne i8 %rem, 0
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:
+  br label %return
+
+if.else:
+  %div = sdiv i8 %X, %Y
+  br label %return
+
+return:
+  %retval.0 = phi i8 [ 0, %if.then ], [ %div, %if.else ]
+  ret i8 %retval.0
+}
+
+
+define i8 @infer_exact_from_dom_cond_negative(i8 %X, i8 %Y) {
+; CHECK-LABEL: define i8 @infer_exact_from_dom_cond_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[REM:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[REM]], 1
+; CHECK-NEXT:    br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[RETURN:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    br label %[[RETURN]]
+; CHECK:       [[RETURN]]:
+; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi i8 [ [[DIV]], %[[IF_THEN]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT:    ret i8 [[RETVAL_0]]
+;
+entry:
+  %rem = srem i8 %X, %Y
+  %cmp = icmp eq i8 %rem, 1
+  br i1 %cmp, label %if.then, label %return
+
+if.then:
+  %div = sdiv i8 %X, %Y
+  br label %return
+
+return:
+  %retval.0 = phi i8 [ %div, %if.then ], [ 0, %entry ]
+  ret i8 %retval.0
+}

}

return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't imagine this is useful, if we have assume((icmp eq X, 0)) we will replace X with 0.

if (isKnownToBeAnExactDivFromConds(Op0, Op1, /*isSigned=*/true, &I)) {
I.setIsExact();
return &I;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing InstCombine tests?

@@ -1846,6 +1861,7 @@ Instruction *InstCombinerImpl::visitSDiv(BinaryOperator &I) {
return SelectInst::Create(Cond, ConstantInt::get(Ty, 1),
ConstantInt::getAllOnesValue(Ty));
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated.


if (RemInstIsZero) {
DivInst->setIsExact();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit style (no braces for single expression in if).

// generate better code.

const DataLayout &DL = DivInst->getDataLayout();
AssumptionCache AC(F, &TTI);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AC Should probably be initialized outside of the loop.

auto *Node = Q.DT->getNode(BB);
while (Node->getIDom()) {
auto *Pred = Node->getIDom();
Node = Pred;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need a seperate Pred variable here?

if (MatchRem(Cond, false) &&
Q.DT->dominates(Edge1, Q.CxtI->getParent())) {
return true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're iterating by idom, I think equivelent (and simpler/faster) logic would be to see which edge of Node->getIDom() Node belongs too, and only try matchRem on that edge (then you can also drop the dominates check).

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely convinced that this is worthwhile, esp. taking into account that the div/rem will usually be combined.

return false;

auto *Node = Q.DT->getNode(BB);
while (Node->getIDom()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be using DomCondCache, not iterating idoms.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is the right approach. If you want to prevent a urem used only in an assume from interfering with this pass, we should do that by ignoring ephemeral values.

@veera-sivarajan
Copy link
Contributor Author

I'm not entirely convinced that this is worthwhile, esp. taking into account that the div/rem will usually be combined.

Agreed.

Closing this since I'm not able to figure out an elegant fix and there isn't much use anyways.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:PowerPC llvm:analysis Includes value tracking, cost tables and constant folding llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Failure to infer udiv exact/sdiv exact from assume
4 participants