From 2656885390f17cceae142b4265c337fcee2410c0 Mon Sep 17 00:00:00 2001 From: Philip Reames Date: Mon, 7 Dec 2020 14:36:19 -0800 Subject: [PATCH] Teach isKnownNonEqual how to recurse through invertible multiplies Build on the work started in 8f07629, and add the multiply case. In the process, more clearly describe the requirement for the operation we're looking through. Differential Revision: https://reviews.llvm.org/D92726 --- llvm/lib/Analysis/ValueTracking.cpp | 22 ++++++- .../test/Analysis/ValueTracking/known-non-equal.ll | 72 ++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index a1bb6e2..eeb5058 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -2502,6 +2502,7 @@ static bool isAddOfNonZero(const Value *V1, const Value *V2, unsigned Depth, return isKnownNonZero(Op, Depth + 1, Q); } + /// Return true if it is known that V1 != V2. static bool isKnownNonEqual(const Value *V1, const Value *V2, unsigned Depth, const Query &Q) { @@ -2514,7 +2515,9 @@ static bool isKnownNonEqual(const Value *V1, const Value *V2, unsigned Depth, if (Depth >= MaxAnalysisRecursionDepth) return false; - // See if we can recurse through (exactly one of) our operands. + // See if we can recurse through (exactly one of) our operands. This + // requires our operation be 1-to-1 and map every input value to exactly + // one output value. Such an operation is invertible. auto *O1 = dyn_cast(V1); auto *O2 = dyn_cast(V2); if (O1 && O2 && O1->getOpcode() == O2->getOpcode()) { @@ -2530,6 +2533,23 @@ static bool isKnownNonEqual(const Value *V1, const Value *V2, unsigned Depth, return isKnownNonEqual(O1->getOperand(0), O2->getOperand(0), Depth + 1, Q); break; + case Instruction::Mul: + // invertible if A * B == (A * B) mod 2^N where A, and B are integers + // and N is the bitwdith. The nsw case is non-obvious, but proven by + // alive2: https://alive2.llvm.org/ce/z/Z6D5qK + if ((!cast(O1)->hasNoUnsignedWrap() || + !cast(O2)->hasNoUnsignedWrap()) && + (!cast(O1)->hasNoSignedWrap() || + !cast(O2)->hasNoSignedWrap())) + break; + + // Assume operand order has been canonicalized + if (O1->getOperand(1) == O2->getOperand(1) && + isa(O1->getOperand(1)) && + !cast(O1->getOperand(1))->isZero()) + return isKnownNonEqual(O1->getOperand(0), O2->getOperand(0), + Depth + 1, Q); + break; case Instruction::SExt: case Instruction::ZExt: if (O1->getOperand(0)->getType() == O2->getOperand(0)->getType()) diff --git a/llvm/test/Analysis/ValueTracking/known-non-equal.ll b/llvm/test/Analysis/ValueTracking/known-non-equal.ll index 664542f..8bc9a86 100644 --- a/llvm/test/Analysis/ValueTracking/known-non-equal.ll +++ b/llvm/test/Analysis/ValueTracking/known-non-equal.ll @@ -130,4 +130,76 @@ define i1 @sub2(i8 %B, i8 %C) { ret i1 %cmp } +; op could wrap mapping two values to the same output value. +define i1 @mul1(i8 %B) { +; CHECK-LABEL: @mul1( +; CHECK-NEXT: [[A:%.*]] = add i8 [[B:%.*]], 1 +; CHECK-NEXT: [[A_OP:%.*]] = mul i8 [[A]], 27 +; CHECK-NEXT: [[B_OP:%.*]] = mul i8 [[B]], 27 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[A_OP]], [[B_OP]] +; CHECK-NEXT: ret i1 [[CMP]] +; + %A = add i8 %B, 1 + %A.op = mul i8 %A, 27 + %B.op = mul i8 %B, 27 + + %cmp = icmp eq i8 %A.op, %B.op + ret i1 %cmp +} + +define i1 @mul2(i8 %B) { +; CHECK-LABEL: @mul2( +; CHECK-NEXT: ret i1 false +; + %A = add i8 %B, 1 + %A.op = mul nuw i8 %A, 27 + %B.op = mul nuw i8 %B, 27 + + %cmp = icmp eq i8 %A.op, %B.op + ret i1 %cmp +} + +define i1 @mul3(i8 %B) { +; CHECK-LABEL: @mul3( +; CHECK-NEXT: ret i1 false +; + %A = add i8 %B, 1 + %A.op = mul nsw i8 %A, 27 + %B.op = mul nsw i8 %B, 27 + + %cmp = icmp eq i8 %A.op, %B.op + ret i1 %cmp +} + +; Multiply by zero collapses all values to one +define i1 @mul4(i8 %B) { +; CHECK-LABEL: @mul4( +; CHECK-NEXT: ret i1 true +; + %A = add i8 %B, 1 + %A.op = mul nuw i8 %A, 0 + %B.op = mul nuw i8 %B, 0 + + %cmp = icmp eq i8 %A.op, %B.op + ret i1 %cmp +} + +; C might be zero, we can't tell +define i1 @mul5(i8 %B, i8 %C) { +; CHECK-LABEL: @mul5( +; CHECK-NEXT: [[A:%.*]] = add i8 [[B:%.*]], 1 +; CHECK-NEXT: [[A_OP:%.*]] = mul nuw nsw i8 [[A]], [[C:%.*]] +; CHECK-NEXT: [[B_OP:%.*]] = mul nuw nsw i8 [[B]], [[C]] +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[A_OP]], [[B_OP]] +; CHECK-NEXT: ret i1 [[CMP]] +; + %A = add i8 %B, 1 + %A.op = mul nsw nuw i8 %A, %C + %B.op = mul nsw nuw i8 %B, %C + + %cmp = icmp eq i8 %A.op, %B.op + ret i1 %cmp +} + + !0 = !{ i8 1, i8 5 } -- 2.7.4