-
Notifications
You must be signed in to change notification settings - Fork 12.4k
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
Conversation
@llvm/pr-subscribers-llvm-analysis ChangesThe 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:
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
+}
|
if (const auto *Div = dyn_cast<BinaryOperator>(U); | ||
Div && Div->getOpcode() == BinaryOperator::UDiv && | ||
Div->getOperand(1) == V && | ||
isValidAssumeForContext(cast<Instruction>(U), CtxI, DT)) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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()?
if (const auto *Div = dyn_cast<BinaryOperator>(U); | ||
Div && Div->getOpcode() == BinaryOperator::UDiv && | ||
Div->getOperand(1) == V && | ||
isValidAssumeForContext(cast<Instruction>(U), CtxI, DT)) |
There was a problem hiding this comment.
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/lib/Analysis/ValueTracking.cpp
Outdated
if ((match(U, m_UDiv(m_Value(), m_Specific(V))) || | ||
match(U, m_SDiv(m_Value(), m_Specific(V)))) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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))) && |
There was a problem hiding this comment.
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.*
.
There was a problem hiding this comment.
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.
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 |
@dc03 Yeah, that definitely should not be happening... |
This is a mistake in the test case, I've put up #67641 to fix it. |
#69433 has been merged, so that should fix the sdiv-guard.ll test case. |
…ngCondition 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 llvm#64240.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
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.