Skip to content

Commit 2656885

Browse files
committed
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
1 parent 6dad7ec commit 2656885

File tree

2 files changed

+93
-1
lines changed

2 files changed

+93
-1
lines changed

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2502,6 +2502,7 @@ static bool isAddOfNonZero(const Value *V1, const Value *V2, unsigned Depth,
25022502
return isKnownNonZero(Op, Depth + 1, Q);
25032503
}
25042504

2505+
25052506
/// Return true if it is known that V1 != V2.
25062507
static bool isKnownNonEqual(const Value *V1, const Value *V2, unsigned Depth,
25072508
const Query &Q) {
@@ -2514,7 +2515,9 @@ static bool isKnownNonEqual(const Value *V1, const Value *V2, unsigned Depth,
25142515
if (Depth >= MaxAnalysisRecursionDepth)
25152516
return false;
25162517

2517-
// See if we can recurse through (exactly one of) our operands.
2518+
// See if we can recurse through (exactly one of) our operands. This
2519+
// requires our operation be 1-to-1 and map every input value to exactly
2520+
// one output value. Such an operation is invertible.
25182521
auto *O1 = dyn_cast<Operator>(V1);
25192522
auto *O2 = dyn_cast<Operator>(V2);
25202523
if (O1 && O2 && O1->getOpcode() == O2->getOpcode()) {
@@ -2530,6 +2533,23 @@ static bool isKnownNonEqual(const Value *V1, const Value *V2, unsigned Depth,
25302533
return isKnownNonEqual(O1->getOperand(0), O2->getOperand(0),
25312534
Depth + 1, Q);
25322535
break;
2536+
case Instruction::Mul:
2537+
// invertible if A * B == (A * B) mod 2^N where A, and B are integers
2538+
// and N is the bitwdith. The nsw case is non-obvious, but proven by
2539+
// alive2: https://alive2.llvm.org/ce/z/Z6D5qK
2540+
if ((!cast<BinaryOperator>(O1)->hasNoUnsignedWrap() ||
2541+
!cast<BinaryOperator>(O2)->hasNoUnsignedWrap()) &&
2542+
(!cast<BinaryOperator>(O1)->hasNoSignedWrap() ||
2543+
!cast<BinaryOperator>(O2)->hasNoSignedWrap()))
2544+
break;
2545+
2546+
// Assume operand order has been canonicalized
2547+
if (O1->getOperand(1) == O2->getOperand(1) &&
2548+
isa<ConstantInt>(O1->getOperand(1)) &&
2549+
!cast<ConstantInt>(O1->getOperand(1))->isZero())
2550+
return isKnownNonEqual(O1->getOperand(0), O2->getOperand(0),
2551+
Depth + 1, Q);
2552+
break;
25332553
case Instruction::SExt:
25342554
case Instruction::ZExt:
25352555
if (O1->getOperand(0)->getType() == O2->getOperand(0)->getType())

llvm/test/Analysis/ValueTracking/known-non-equal.ll

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,76 @@ define i1 @sub2(i8 %B, i8 %C) {
130130
ret i1 %cmp
131131
}
132132

133+
; op could wrap mapping two values to the same output value.
134+
define i1 @mul1(i8 %B) {
135+
; CHECK-LABEL: @mul1(
136+
; CHECK-NEXT: [[A:%.*]] = add i8 [[B:%.*]], 1
137+
; CHECK-NEXT: [[A_OP:%.*]] = mul i8 [[A]], 27
138+
; CHECK-NEXT: [[B_OP:%.*]] = mul i8 [[B]], 27
139+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[A_OP]], [[B_OP]]
140+
; CHECK-NEXT: ret i1 [[CMP]]
141+
;
142+
%A = add i8 %B, 1
143+
%A.op = mul i8 %A, 27
144+
%B.op = mul i8 %B, 27
145+
146+
%cmp = icmp eq i8 %A.op, %B.op
147+
ret i1 %cmp
148+
}
149+
150+
define i1 @mul2(i8 %B) {
151+
; CHECK-LABEL: @mul2(
152+
; CHECK-NEXT: ret i1 false
153+
;
154+
%A = add i8 %B, 1
155+
%A.op = mul nuw i8 %A, 27
156+
%B.op = mul nuw i8 %B, 27
157+
158+
%cmp = icmp eq i8 %A.op, %B.op
159+
ret i1 %cmp
160+
}
161+
162+
define i1 @mul3(i8 %B) {
163+
; CHECK-LABEL: @mul3(
164+
; CHECK-NEXT: ret i1 false
165+
;
166+
%A = add i8 %B, 1
167+
%A.op = mul nsw i8 %A, 27
168+
%B.op = mul nsw i8 %B, 27
169+
170+
%cmp = icmp eq i8 %A.op, %B.op
171+
ret i1 %cmp
172+
}
173+
174+
; Multiply by zero collapses all values to one
175+
define i1 @mul4(i8 %B) {
176+
; CHECK-LABEL: @mul4(
177+
; CHECK-NEXT: ret i1 true
178+
;
179+
%A = add i8 %B, 1
180+
%A.op = mul nuw i8 %A, 0
181+
%B.op = mul nuw i8 %B, 0
182+
183+
%cmp = icmp eq i8 %A.op, %B.op
184+
ret i1 %cmp
185+
}
186+
187+
; C might be zero, we can't tell
188+
define i1 @mul5(i8 %B, i8 %C) {
189+
; CHECK-LABEL: @mul5(
190+
; CHECK-NEXT: [[A:%.*]] = add i8 [[B:%.*]], 1
191+
; CHECK-NEXT: [[A_OP:%.*]] = mul nuw nsw i8 [[A]], [[C:%.*]]
192+
; CHECK-NEXT: [[B_OP:%.*]] = mul nuw nsw i8 [[B]], [[C]]
193+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[A_OP]], [[B_OP]]
194+
; CHECK-NEXT: ret i1 [[CMP]]
195+
;
196+
%A = add i8 %B, 1
197+
%A.op = mul nsw nuw i8 %A, %C
198+
%B.op = mul nsw nuw i8 %B, %C
199+
200+
%cmp = icmp eq i8 %A.op, %B.op
201+
ret i1 %cmp
202+
}
203+
204+
133205
!0 = !{ i8 1, i8 5 }

0 commit comments

Comments
 (0)