[llvm] [ValueTracking] Improve `isImpliedCondCommonOperandWithConstants` to handle truncated LHS (PR #69829)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Oct 21 02:52:24 PDT 2023
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-analysis
Author: Yingwei Zheng (dtcxzyw)
<details>
<summary>Changes</summary>
This patch improves `isImpliedCondCommonOperandWithConstants` to handle patterns like `L0 == trunc(R0)` or `R0 == trunc(L0)`. I am sure that we don't need to handle `sext/zext` cases since they have been handled by `InstCombinerImpl::foldAndOrOfICmpsUsingRanges`.
Alive2: https://alive2.llvm.org/ce/z/FLcm2h
Fixes #<!-- -->68514.
---
Full diff: https://github.com/llvm/llvm-project/pull/69829.diff
2 Files Affected:
- (modified) llvm/lib/Analysis/ValueTracking.cpp (+34-7)
- (added) llvm/test/Analysis/ValueTracking/pr68514.ll (+70)
``````````diff
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 8824a05e3aa6ccd..188faf00329ece2 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -8231,14 +8231,30 @@ isImpliedCondMatchingOperands(CmpInst::Predicate LPred,
return std::nullopt;
}
-/// Return true if "icmp LPred X, LC" implies "icmp RPred X, RC" is true.
-/// Return false if "icmp LPred X, LC" implies "icmp RPred X, RC" is false.
-/// Otherwise, return std::nullopt if we can't infer anything.
+/// Return true if "icmp LPred X, LC" implies "icmp RPred cast(X), RC" is true.
+/// Return false if "icmp LPred X, LC" implies "icmp RPred cast(X), RC" is
+/// false. Otherwise, return std::nullopt if we can't infer anything.
static std::optional<bool> isImpliedCondCommonOperandWithConstants(
- CmpInst::Predicate LPred, const APInt &LC, CmpInst::Predicate RPred,
- const APInt &RC) {
+ const Value *L0, CmpInst::Predicate LPred, const APInt &LC, const Value *R0,
+ CmpInst::Predicate RPred, const APInt &RC) {
ConstantRange DomCR = ConstantRange::makeExactICmpRegion(LPred, LC);
ConstantRange CR = ConstantRange::makeExactICmpRegion(RPred, RC);
+
+ if (L0 == R0)
+ ; // noop
+ // Example: icmp eq X, 3 --> icmp sgt trunc(X), 2
+ else if (match(R0, m_Trunc(m_Specific(L0))))
+ DomCR = DomCR.truncate(RC.getBitWidth());
+ // Example: icmp slt trunc(X), 3 --> icmp ne X, 3
+ else if (match(L0, m_Trunc(m_Specific(R0)))) {
+ // Try to prove by negation
+ DomCR = DomCR.inverse();
+ CR = CR.inverse();
+ std::swap(DomCR, CR);
+ DomCR = DomCR.truncate(LC.getBitWidth());
+ } else
+ return std::nullopt;
+
ConstantRange Intersection = DomCR.intersectWith(CR);
ConstantRange Difference = DomCR.difference(CR);
if (Intersection.isEmptySet())
@@ -8272,8 +8288,19 @@ static std::optional<bool> isImpliedCondICmps(const ICmpInst *LHS,
// Can we infer anything when the 0-operands match and the 1-operands are
// constants (not necessarily matching)?
const APInt *LC, *RC;
- if (L0 == R0 && match(L1, m_APInt(LC)) && match(R1, m_APInt(RC)))
- return isImpliedCondCommonOperandWithConstants(LPred, *LC, RPred, *RC);
+ if (match(L1, m_APInt(LC)) && match(R1, m_APInt(RC))) {
+ if (auto Res = isImpliedCondCommonOperandWithConstants(L0, LPred, *LC, R0,
+ RPred, *RC))
+ return Res;
+
+ if (match(L0, m_Trunc(m_Specific(R0)))) {
+ // When L0 == trunc(R0), we use the law of excluded middle to cover some
+ // missing cases.
+ if (auto Res = isImpliedCondCommonOperandWithConstants(
+ L0, LPred, *LC, R0, ICmpInst::getInversePredicate(RPred), *RC))
+ return !*Res;
+ }
+ }
// L0 = R0 = L1 + R1, L0 >=u L1 implies R0 >=u R1, L0 <u L1 implies R0 <u R1
if (ICmpInst::isUnsigned(LPred) && ICmpInst::isUnsigned(RPred)) {
diff --git a/llvm/test/Analysis/ValueTracking/pr68514.ll b/llvm/test/Analysis/ValueTracking/pr68514.ll
new file mode 100644
index 000000000000000..482cad942624aaa
--- /dev/null
+++ b/llvm/test/Analysis/ValueTracking/pr68514.ll
@@ -0,0 +1,70 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; Tests from PR68514
+define i1 @f(i32 %a) {
+; CHECK-LABEL: define i1 @f(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: ret i1 false
+;
+entry:
+ %cmp = icmp eq i32 %a, 0
+ %b = trunc i32 %a to i16
+ %cmp2 = icmp sgt i16 %b, 2
+ %and9 = and i1 %cmp, %cmp2
+ ret i1 %and9
+}
+
+define i1 @g(i32 %a) {
+; CHECK-LABEL: define i1 @g(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[B:%.*]] = trunc i32 [[A]] to i16
+; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i16 [[B]], 2
+; CHECK-NEXT: ret i1 [[CMP2]]
+;
+entry:
+ %cmp = icmp eq i32 %a, 3
+ %b = trunc i32 %a to i16
+ %cmp2 = icmp sgt i16 %b, 2
+ %or9 = or i1 %cmp, %cmp2
+ ret i1 %or9
+}
+
+; b = trunc(a)
+
+define i1 @fold_trunc(i32 %a) {
+; CHECK-LABEL: define i1 @fold_trunc(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[A]], 2
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+entry:
+ %cmp = icmp ugt i32 %a, 2
+ %b = trunc i32 %a to i16
+ %cmp2 = icmp ugt i16 %b, 2
+ %and9 = or i1 %cmp, %cmp2
+ ret i1 %and9
+}
+
+; Negative tests
+
+define i1 @nofold_trunc(i32 %a) {
+; CHECK-LABEL: define i1 @nofold_trunc(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[A]], 3
+; CHECK-NEXT: [[B:%.*]] = trunc i32 [[A]] to i16
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i16 [[B]], 2
+; CHECK-NEXT: [[AND9:%.*]] = or i1 [[CMP]], [[CMP2]]
+; CHECK-NEXT: ret i1 [[AND9]]
+;
+entry:
+ %cmp = icmp ugt i32 %a, 3
+ %b = trunc i32 %a to i16
+ %cmp2 = icmp ugt i16 %b, 2
+ %and9 = or i1 %cmp, %cmp2
+ ret i1 %and9
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/69829
More information about the llvm-commits
mailing list