Skip to content
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

[ValueTracking] Implement sdiv/udiv support for isKnownNonNullFromDominatingCondition #67282

Merged
merged 4 commits into from
Oct 20, 2023

Conversation

dc03
Copy link
Member

@dc03 dc03 commented Sep 25, 2023

The second operand of a sdiv/udiv has to be non-null, as division by zero is UB.

Proofs: https://alive2.llvm.org/ce/z/WttZbb

Fixes #64240.

@llvmbot
Copy link
Member

llvmbot commented Sep 25, 2023

@llvm/pr-subscribers-llvm-analysis

Changes

The second operand of a udiv has to be non-null, as division by zero is UB.

Proofs: https://alive2.llvm.org/ce/z/PAWdmP

Fixes #64240.


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

2 Files Affected:

  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+6)
  • (modified) llvm/test/Analysis/ValueTracking/select-known-non-zero.ll (+24)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 973943790c0aad2..f177321f28cfa2e 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -2263,6 +2263,12 @@ static bool isKnownNonNullFromDominatingCondition(const Value *V,
         return true;
     }
 
+    if (const auto *Div = dyn_cast<BinaryOperator>(U);
+        Div && Div->getOpcode() == BinaryOperator::UDiv &&
+        Div->getOperand(1) == V &&
+        isValidAssumeForContext(cast<Instruction>(U), CtxI, DT))
+      return true;
+
     // Consider only compare instructions uniquely controlling a branch
     Value *RHS;
     CmpInst::Predicate Pred;
diff --git a/llvm/test/Analysis/ValueTracking/select-known-non-zero.ll b/llvm/test/Analysis/ValueTracking/select-known-non-zero.ll
index 8b1d2fd0181d66c..a12925ede72ba42 100644
--- a/llvm/test/Analysis/ValueTracking/select-known-non-zero.ll
+++ b/llvm/test/Analysis/ValueTracking/select-known-non-zero.ll
@@ -393,3 +393,27 @@ define i1 @inv_select_v_sle_nonneg(i8 %v, i8 %C, i8 %y) {
   %r = icmp eq i8 %s, 0
   ret i1 %r
 }
+
+; Check dominance of the udiv over the icmp.
+define i64 @incorrect_safe_div(i64 %n, i64 %d) {
+; CHECK-LABEL: @incorrect_safe_div(
+; CHECK-NEXT:    [[TMP1:%.*]] = udiv i64 [[N:%.*]], [[D:%.*]]
+; CHECK-NEXT:    ret i64 [[TMP1]]
+;
+  %1 = udiv i64 %n, %d
+  %2 = icmp eq i64 %d, 0
+  %3 = select i1 %2, i64 -1, i64 %1
+  ret i64 %3
+}
+
+; Check post-dominance of the udiv over the icmp.
+define i64 @incorrect_safe_div_post(i64 %n, i64 %d) {
+; CHECK-LABEL: @incorrect_safe_div_post(
+; CHECK-NEXT:    [[TMP1:%.*]] = udiv i64 [[N:%.*]], [[D:%.*]]
+; CHECK-NEXT:    ret i64 [[TMP1]]
+;
+  %1 = icmp eq i64 %d, 0
+  %2 = udiv i64 %n, %d
+  %3 = select i1 %1, i64 -1, i64 %2
+  ret i64 %3
+}

llvm/lib/Analysis/ValueTracking.cpp Outdated Show resolved Hide resolved
if (const auto *Div = dyn_cast<BinaryOperator>(U);
Div && Div->getOpcode() == BinaryOperator::UDiv &&
Div->getOperand(1) == V &&
isValidAssumeForContext(cast<Instruction>(U), CtxI, DT))
Copy link
Member

Choose a reason for hiding this comment

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

isValidAssumeForContext assumes the first argument is an assume intrinsic. It is incorrect to pass a udiv.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, I am not sure of any other way to check for post-dominance.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe DT->dominates(cast<Instruction>(U), CtxI)? It guarantees we execute udiv before select. Then we can simplify the pattern in simplifySelectWithICmpCond.

Copy link
Member Author

@dc03 dc03 Sep 25, 2023

Choose a reason for hiding this comment

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

In this case, CtxI is always the icmp, so the dominates call will only work when the udiv dominates the icmp and not the other way round.

Copy link
Contributor

Choose a reason for hiding this comment

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

isValidAssumeForContext() should be fine to use for non-assumes as well. The key property of assumes here is that violating the fact results in immediate undefined behavior, which is also true for divisions.

Though I guess the isEphemeralValueOf() part of the function is only relevant for assumes...

Copy link
Member Author

Choose a reason for hiding this comment

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

@nikic Any issues that could occur with isEphemeralValueOf()?

llvm/lib/Analysis/ValueTracking.cpp Outdated Show resolved Hide resolved
if (const auto *Div = dyn_cast<BinaryOperator>(U);
Div && Div->getOpcode() == BinaryOperator::UDiv &&
Div->getOperand(1) == V &&
isValidAssumeForContext(cast<Instruction>(U), CtxI, DT))
Copy link
Contributor

Choose a reason for hiding this comment

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

isValidAssumeForContext() should be fine to use for non-assumes as well. The key property of assumes here is that violating the fact results in immediate undefined behavior, which is also true for divisions.

Though I guess the isEphemeralValueOf() part of the function is only relevant for assumes...

llvm/test/Analysis/ValueTracking/select-known-non-zero.ll Outdated Show resolved Hide resolved
@dtcxzyw dtcxzyw changed the title [ValueTracking] Implement udiv support for isKnownNonNullFromDominatingCondition [ValueTracking] Implement sdiv/udiv support for isKnownNonNullFromDominatingCondition Sep 25, 2023
Comment on lines 2266 to 2267
if ((match(U, m_UDiv(m_Value(), m_Specific(V))) ||
match(U, m_SDiv(m_Value(), m_Specific(V)))) &&
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if ((match(U, m_UDiv(m_Value(), m_Specific(V))) ||
match(U, m_SDiv(m_Value(), m_Specific(V)))) &&
if (match(U, m_IDiv(m_Value(), m_Specific(V))) &&

Copy link
Member

Choose a reason for hiding this comment

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

I think it also holds for srem/urem/llvm.sdiv.fix.*/llvm.udiv.fix.*.

Copy link
Member Author

Choose a reason for hiding this comment

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

@dtcxzyw I think its fine to keep this PR to just IDiv (I'm mainly interested in closing the issue), the others can be implemented separately.

@dc03
Copy link
Member Author

dc03 commented Sep 25, 2023

Is this a problem?

diff --git a/llvm/test/Transforms/InstCombine/sdiv-guard.ll b/llvm/test/Transforms/InstCombine/sdiv-guard.ll
index ba9670924108..4894ed90e359 100644
--- a/llvm/test/Transforms/InstCombine/sdiv-guard.ll
+++ b/llvm/test/Transforms/InstCombine/sdiv-guard.ll
@@ -6,9 +6,7 @@ declare void @llvm.experimental.guard(i1, ...)
 ; Regression test. If %flag is false then %s == 0 and guard should be triggered.
 define i32 @a(i1 %flag, i32 %X) nounwind readnone {
 ; CHECK-LABEL: @a(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[X:%.*]], 0
-; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CMP]]) #[[ATTR2:[0-9]+]] [ "deopt"() ]
-; CHECK-NEXT:    [[R:%.*]] = sdiv i32 100, [[X]]
+; CHECK-NEXT:    [[R:%.*]] = sdiv i32 100, [[X:%.*]]
 ; CHECK-NEXT:    ret i32 [[R]]
 ;
   %s = select i1 %flag, i32 %X, i32 0

@nikic
Copy link
Contributor

nikic commented Sep 25, 2023

@dc03 Yeah, that definitely should not be happening...

@nikic
Copy link
Contributor

nikic commented Sep 28, 2023

This is a mistake in the test case, I've put up #67641 to fix it.

@nikic
Copy link
Contributor

nikic commented Oct 19, 2023

#69433 has been merged, so that should fix the sdiv-guard.ll test case.

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.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missed optimization: failure to use div-by-zero UB to delete redundant select
4 participants