Skip to content

Commit 4e6b554

Browse files
[ValueTracking] Infer exact for udiv and sdiv
This infers the `exact` flag for division instructions based on dominating conditions and assumes. The `exact` flag is inferred from assumes for all targets but it won't be inferred from dominating conditions for targets without a single, unified instruction for division and remainder because DivRemPairs rewrites the remainder instruction in terms of division. Proof: https://alive2.llvm.org/ce/z/XtmMR6
1 parent dddec18 commit 4e6b554

File tree

7 files changed

+141
-13
lines changed

7 files changed

+141
-13
lines changed

llvm/include/llvm/Analysis/ValueTracking.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,14 @@ bool isKnownToBeAPowerOfTwo(const Value *V, const DataLayout &DL,
122122
bool isKnownToBeAPowerOfTwo(const Value *V, bool OrZero, unsigned Depth,
123123
const SimplifyQuery &Q);
124124

125+
/// Return true if `X / Y` is known to be `exact` from dominating
126+
/// conditions.
127+
bool isKnownToBeAnExactDivFromConds(const Value *X, const Value *Y,
128+
bool isSigned, const SimplifyQuery &Q);
129+
130+
/// Return true if `V` is known to be equal to `0` from assumes.
131+
bool isKnownToBeZeroFromAssumes(const Value *V, const SimplifyQuery &Q);
132+
125133
bool isOnlyUsedInZeroComparison(const Instruction *CxtI);
126134

127135
bool isOnlyUsedInZeroEqualityComparison(const Instruction *CxtI);

llvm/include/llvm/Transforms/InstCombine/InstCombiner.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,13 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
447447
SQ.getWithInstruction(CxtI));
448448
}
449449

450+
bool isKnownToBeAnExactDivFromConds(const Value *X, const Value *Y,
451+
bool isSigned,
452+
const Instruction *CxtI = nullptr) {
453+
return llvm::isKnownToBeAnExactDivFromConds(X, Y, isSigned,
454+
SQ.getWithInstruction(CxtI));
455+
}
456+
450457
bool MaskedValueIsZero(const Value *V, const APInt &Mask, unsigned Depth = 0,
451458
const Instruction *CxtI = nullptr) const {
452459
return llvm::MaskedValueIsZero(V, Mask, SQ.getWithInstruction(CxtI), Depth);

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2555,6 +2555,71 @@ bool llvm::isKnownToBeAPowerOfTwo(const Value *V, bool OrZero, unsigned Depth,
25552555
}
25562556
}
25572557

2558+
bool llvm::isKnownToBeZeroFromAssumes(const Value *V, const SimplifyQuery &Q) {
2559+
2560+
if (Q.AC && Q.CxtI) {
2561+
for (auto &AssumeVH : Q.AC->assumptionsFor(V)) {
2562+
if (!AssumeVH)
2563+
continue;
2564+
CallInst *I = cast<CallInst>(AssumeVH);
2565+
const Value *Cond = I->getArgOperand(0);
2566+
if (match(Cond,
2567+
m_SpecificICmp(ICmpInst::ICMP_EQ, m_Specific(V), m_Zero())) &&
2568+
isValidAssumeForContext(I, Q.CxtI, Q.DT)) {
2569+
return true;
2570+
}
2571+
}
2572+
}
2573+
2574+
return false;
2575+
}
2576+
2577+
bool llvm::isKnownToBeAnExactDivFromConds(const Value *X, const Value *Y,
2578+
bool isSigned,
2579+
const SimplifyQuery &Q) {
2580+
if (Q.DC && Q.CxtI && Q.DT) {
2581+
auto MatchRem = [&](Value *DomCond, bool CondIsTrue) -> bool {
2582+
auto Pred = CondIsTrue ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE;
2583+
if (isSigned)
2584+
return match(DomCond,
2585+
m_SpecificICmp(Pred, m_SRem(m_Specific(X), m_Specific(Y)),
2586+
m_Zero()));
2587+
return match(
2588+
DomCond,
2589+
m_SpecificICmp(Pred, m_URem(m_Specific(X), m_Specific(Y)), m_Zero()));
2590+
};
2591+
2592+
auto *BB = Q.CxtI->getParent();
2593+
if (!Q.DT->isReachableFromEntry(BB))
2594+
return false;
2595+
2596+
auto *Node = Q.DT->getNode(BB);
2597+
while (Node->getIDom()) {
2598+
auto *Pred = Node->getIDom();
2599+
Node = Pred;
2600+
2601+
if (auto *BI = dyn_cast<BranchInst>(Pred->getBlock()->getTerminator());
2602+
BI && BI->isConditional()) {
2603+
Value *Cond = BI->getCondition();
2604+
2605+
BasicBlockEdge Edge0(BI->getParent(), BI->getSuccessor(0));
2606+
if (MatchRem(Cond, true) &&
2607+
Q.DT->dominates(Edge0, Q.CxtI->getParent())) {
2608+
return true;
2609+
}
2610+
2611+
BasicBlockEdge Edge1(BI->getParent(), BI->getSuccessor(1));
2612+
if (MatchRem(Cond, false) &&
2613+
Q.DT->dominates(Edge1, Q.CxtI->getParent())) {
2614+
return true;
2615+
}
2616+
}
2617+
}
2618+
}
2619+
2620+
return false;
2621+
}
2622+
25582623
/// Test whether a GEP's result is known to be non-null.
25592624
///
25602625
/// Uses properties inherent in a GEP to try to determine whether it is known

llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,6 +1707,13 @@ Instruction *InstCombinerImpl::visitUDiv(BinaryOperator &I) {
17071707
return replaceInstUsesWith(
17081708
I, Builder.CreateLShr(Op0, Res, I.getName(), I.isExact()));
17091709

1710+
// Infer `exact` from dominating conditions.
1711+
if (!I.isExact() && isKnownToBeAnExactDivFromConds(Op0, Op1,
1712+
/*isSigned=*/false, &I)) {
1713+
I.setIsExact();
1714+
return &I;
1715+
}
1716+
17101717
return nullptr;
17111718
}
17121719

@@ -1804,11 +1811,19 @@ Instruction *InstCombinerImpl::visitSDiv(BinaryOperator &I) {
18041811
}
18051812

18061813
KnownBits KnownDividend = computeKnownBits(Op0, 0, &I);
1807-
if (!I.isExact() &&
1808-
(match(Op1, m_Power2(Op1C)) || match(Op1, m_NegatedPower2(Op1C))) &&
1809-
KnownDividend.countMinTrailingZeros() >= Op1C->countr_zero()) {
1810-
I.setIsExact();
1811-
return &I;
1814+
if (!I.isExact()) {
1815+
1816+
if ((match(Op1, m_Power2(Op1C)) || match(Op1, m_NegatedPower2(Op1C))) &&
1817+
KnownDividend.countMinTrailingZeros() >= Op1C->countr_zero()) {
1818+
I.setIsExact();
1819+
return &I;
1820+
}
1821+
1822+
// Infer `exact` from dominating conditions.
1823+
if (isKnownToBeAnExactDivFromConds(Op0, Op1, /*isSigned=*/true, &I)) {
1824+
I.setIsExact();
1825+
return &I;
1826+
}
18121827
}
18131828

18141829
if (KnownDividend.isNonNegative()) {
@@ -1846,6 +1861,7 @@ Instruction *InstCombinerImpl::visitSDiv(BinaryOperator &I) {
18461861
return SelectInst::Create(Cond, ConstantInt::get(Ty, 1),
18471862
ConstantInt::getAllOnesValue(Ty));
18481863
}
1864+
18491865
return nullptr;
18501866
}
18511867

llvm/lib/Transforms/Scalar/DivRemPairs.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "llvm/ADT/DenseMap.h"
1616
#include "llvm/ADT/MapVector.h"
1717
#include "llvm/ADT/Statistic.h"
18+
#include "llvm/Analysis/AssumptionCache.h"
1819
#include "llvm/Analysis/GlobalsModRef.h"
1920
#include "llvm/Analysis/TargetTransformInfo.h"
2021
#include "llvm/Analysis/ValueTracking.h"
@@ -178,7 +179,7 @@ static DivRemWorklistTy getWorklist(Function &F) {
178179
/// Note: This transform could be an oddball enhancement to EarlyCSE, GVN, or
179180
/// SimplifyCFG, but it's split off on its own because it's different enough
180181
/// that it doesn't quite match the stated objectives of those passes.
181-
static bool optimizeDivRem(Function &F, const TargetTransformInfo &TTI,
182+
static bool optimizeDivRem(Function &F, TargetTransformInfo &TTI,
182183
const DominatorTree &DT) {
183184
bool Changed = false;
184185

@@ -196,6 +197,35 @@ static bool optimizeDivRem(Function &F, const TargetTransformInfo &TTI,
196197
auto &DivInst = E.DivInst;
197198
auto &RemInst = E.RemInst;
198199

200+
// Before any transformation, analyze whether
201+
// RemInst is known to be zero from assumes. If so,
202+
// set `exact` flag on DivInst.
203+
//
204+
// This kind of flag inference is usually handled by
205+
// InstCombine but it will not work for this scenario
206+
// because DivRemPairs is run towards the end of the
207+
// optimization pipeline and removes all poison generating
208+
// flags if the target does not have an unified instruction
209+
// for both division and remainder.
210+
//
211+
// So, we do this flag inference here to ensure that flags
212+
// inferred from assumes are not removed regardless of
213+
// `HasDivRemOp`.
214+
//
215+
// Doing this in the tail end of the pipeline does not
216+
// affect any follow-up optimizations because
217+
// the `exact` flag is mostly used by the backend to
218+
// generate better code.
219+
220+
const DataLayout &DL = DivInst->getDataLayout();
221+
AssumptionCache AC(F, &TTI);
222+
SimplifyQuery SQ(DL, &DT, &AC, DivInst);
223+
bool RemInstIsZero = llvm::isKnownToBeZeroFromAssumes(RemInst, SQ);
224+
225+
if (RemInstIsZero) {
226+
DivInst->setIsExact();
227+
}
228+
199229
const bool RemOriginallyWasInExpandedForm = E.isRemExpanded();
200230
(void)RemOriginallyWasInExpandedForm; // suppress unused variable warning
201231

@@ -371,9 +401,11 @@ static bool optimizeDivRem(Function &F, const TargetTransformInfo &TTI,
371401
Sub->insertAfter(Mul->getIterator());
372402
Sub->setDebugLoc(RemInst->getDebugLoc());
373403

374-
// If DivInst has the exact flag, remove it. Otherwise this optimization
375-
// may replace a well-defined value 'X % Y' with poison.
376-
DivInst->dropPoisonGeneratingFlags();
404+
// Remove the `exact` flag from DivInst if it is not inferred from
405+
// assumes. Otherwise this optimization may replace a well-defined
406+
// value 'X % Y' with poison.
407+
if (!RemInstIsZero)
408+
DivInst->dropPoisonGeneratingFlags();
377409

378410
// If X can be undef, X should be frozen first.
379411
// For example, let's assume that Y = 1 & X = undef:

llvm/test/Transforms/DivRemPairs/PowerPC/div-rem-pairs-infer-exact.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ define i8 @udiv_exact_assume(i8 %x, i8 %y) {
1010
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
1111
; CHECK-NEXT: [[X_FROZEN:%.*]] = freeze i8 [[X]]
1212
; CHECK-NEXT: [[Y_FROZEN:%.*]] = freeze i8 [[Y]]
13-
; CHECK-NEXT: [[DIV3:%.*]] = udiv i8 [[X_FROZEN]], [[Y_FROZEN]]
13+
; CHECK-NEXT: [[DIV3:%.*]] = udiv exact i8 [[X_FROZEN]], [[Y_FROZEN]]
1414
; CHECK-NEXT: [[TMP1:%.*]] = mul i8 [[DIV3]], [[Y_FROZEN]]
1515
; CHECK-NEXT: [[REM:%.*]] = sub i8 [[X_FROZEN]], [[TMP1]]
1616
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM]], 0

llvm/test/Transforms/DivRemPairs/X86/div-rem-pairs-infer-exact.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ define i8 @udiv_exact_assume(i8 %x, i8 %y) {
1010
; CHECK-NEXT: [[REM:%.*]] = urem i8 [[X]], [[Y]]
1111
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM]], 0
1212
; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]])
13-
; CHECK-NEXT: [[DIV3:%.*]] = udiv i8 [[X]], [[Y]]
13+
; CHECK-NEXT: [[DIV3:%.*]] = udiv exact i8 [[X]], [[Y]]
1414
; CHECK-NEXT: ret i8 [[DIV3]]
1515
;
1616
%rem = urem i8 %x, %y
@@ -41,7 +41,7 @@ define i8 @infer_exact_from_dom_cond(i8 %X, i8 %Y) {
4141
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
4242
; CHECK-NEXT: [[ENTRY:.*]]:
4343
; CHECK-NEXT: [[REM:%.*]] = srem i8 [[X]], [[Y]]
44-
; CHECK-NEXT: [[DIV:%.*]] = sdiv i8 [[X]], [[Y]]
44+
; CHECK-NEXT: [[DIV:%.*]] = sdiv exact i8 [[X]], [[Y]]
4545
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM]], 0
4646
; CHECK-NEXT: br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[RETURN:.*]]
4747
; CHECK: [[IF_THEN]]:
@@ -69,7 +69,7 @@ define i8 @infer_exact_from_dom_cond_false_path(i8 %X, i8 %Y) {
6969
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
7070
; CHECK-NEXT: [[ENTRY:.*:]]
7171
; CHECK-NEXT: [[REM:%.*]] = srem i8 [[X]], [[Y]]
72-
; CHECK-NEXT: [[DIV:%.*]] = sdiv i8 [[X]], [[Y]]
72+
; CHECK-NEXT: [[DIV:%.*]] = sdiv exact i8 [[X]], [[Y]]
7373
; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[REM]], 0
7474
; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[IF_ELSE:.*]], label %[[IF_THEN:.*]]
7575
; CHECK: [[IF_THEN]]:

0 commit comments

Comments
 (0)