Skip to content

Commit

Permalink
[ConstraintElim] Add A < B if A is an increasing phi for A != B.
Browse files Browse the repository at this point in the history
This patch adds additional logic to add additional facts for A != B, if
A is a monotonically increasing induction phi. The motivating use case
for this is removing checks when using iterators with hardened libc++,
e.g. https://godbolt.org/z/zhKEP37vG.

The patch pulls in SCEV to detect AddRecs. If possible, the patch adds
the following facts for a AddRec phi PN with StartValue as incoming
value from the loo preheader and B being an upper bound for PN from a
condition in the loop header.

 * (ICMP_UGE, PN, StartValue)
 * (ICMP_ULT, PN, B) [if (ICMP_ULE, StartValue, B)]

The patch also adds an optional precondition to FactOrCheck (the new
DoesHold field) , which can be used to only add a fact if the
precondition holds at the point the fact is added to the constraint
system.

Depends on D151799.

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D152730
  • Loading branch information
fhahn committed Sep 27, 2023
1 parent 3ff7d51 commit e6a1657
Show file tree
Hide file tree
Showing 19 changed files with 251 additions and 213 deletions.
162 changes: 148 additions & 14 deletions llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/ConstraintSystem.h"
#include "llvm/Analysis/GlobalsModRef.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Dominators.h"
Expand Down Expand Up @@ -90,6 +93,8 @@ struct ConditionTy {
Value *Op0;
Value *Op1;

ConditionTy()
: Pred(CmpInst::BAD_ICMP_PREDICATE), Op0(nullptr), Op1(nullptr) {}
ConditionTy(CmpInst::Predicate Pred, Value *Op0, Value *Op1)
: Pred(Pred), Op0(Op0), Op1(Op1) {}
};
Expand All @@ -115,6 +120,10 @@ struct FactOrCheck {
ConditionTy Cond;
};

/// A pre-condition that must hold for the current fact to be added to the
/// system.
ConditionTy DoesHold;

unsigned NumIn;
unsigned NumOut;
EntryTy Ty;
Expand All @@ -124,27 +133,25 @@ struct FactOrCheck {
Ty(Ty) {}

FactOrCheck(DomTreeNode *DTN, Use *U)
: U(U), NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()),
: U(U), DoesHold(CmpInst::BAD_ICMP_PREDICATE, nullptr, nullptr),
NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()),
Ty(EntryTy::UseCheck) {}

FactOrCheck(DomTreeNode *DTN, CmpInst::Predicate Pred, Value *Op0, Value *Op1)
: Cond(Pred, Op0, Op1), NumIn(DTN->getDFSNumIn()),
FactOrCheck(DomTreeNode *DTN, CmpInst::Predicate Pred, Value *Op0, Value *Op1,
ConditionTy Precond = ConditionTy())
: Cond(Pred, Op0, Op1), DoesHold(Precond), NumIn(DTN->getDFSNumIn()),
NumOut(DTN->getDFSNumOut()), Ty(EntryTy::ConditionFact) {}

static FactOrCheck getConditionFact(DomTreeNode *DTN, CmpInst::Predicate Pred,
Value *Op0, Value *Op1) {
return FactOrCheck(DTN, Pred, Op0, Op1);
Value *Op0, Value *Op1,
ConditionTy Precond = ConditionTy()) {
return FactOrCheck(DTN, Pred, Op0, Op1, Precond);
}

static FactOrCheck getInstFact(DomTreeNode *DTN, Instruction *Inst) {
return FactOrCheck(EntryTy::InstFact, DTN, Inst);
}

static FactOrCheck getFact(DomTreeNode *DTN, CmpInst::Predicate Pred,
Value *Op0, Value *Op1) {
return FactOrCheck(DTN, Pred, Op0, Op1);
}

static FactOrCheck getCheck(DomTreeNode *DTN, Use *U) {
return FactOrCheck(DTN, U);
}
Expand Down Expand Up @@ -177,13 +184,20 @@ struct FactOrCheck {
/// Keep state required to build worklist.
struct State {
DominatorTree &DT;
LoopInfo &LI;
ScalarEvolution &SE;
SmallVector<FactOrCheck, 64> WorkList;

State(DominatorTree &DT) : DT(DT) {}
State(DominatorTree &DT, LoopInfo &LI, ScalarEvolution &SE)
: DT(DT), LI(LI), SE(SE) {}

/// Process block \p BB and add known facts to work-list.
void addInfoFor(BasicBlock &BB);

/// Try to add facts for loop inductions (AddRecs) in EQ/NE compares
/// controlling the loop header.
void addInfoForInductions(BasicBlock &BB);

/// Returns true if we can add a known condition from BB to its successor
/// block Succ.
bool canAddSuccessor(BasicBlock &BB, BasicBlock *Succ) const {
Expand Down Expand Up @@ -799,7 +813,114 @@ static void dumpConstraint(ArrayRef<int64_t> C,
}
#endif

void State::addInfoForInductions(BasicBlock &BB) {
auto *L = LI.getLoopFor(&BB);
if (!L || L->getHeader() != &BB)
return;

Value *A;
Value *B;
CmpInst::Predicate Pred;

if (!match(BB.getTerminator(),
m_Br(m_ICmp(Pred, m_Value(A), m_Value(B)), m_Value(), m_Value())))
return;
PHINode *PN = dyn_cast<PHINode>(A);
if (!PN) {
Pred = CmpInst::getSwappedPredicate(Pred);
std::swap(A, B);
PN = dyn_cast<PHINode>(A);
}

if (!PN || PN->getParent() != &BB || PN->getNumIncomingValues() != 2 ||
!SE.isSCEVable(PN->getType()))
return;

BasicBlock *InLoopSucc = nullptr;
if (Pred == CmpInst::ICMP_NE)
InLoopSucc = cast<BranchInst>(BB.getTerminator())->getSuccessor(0);
else if (Pred == CmpInst::ICMP_EQ)
InLoopSucc = cast<BranchInst>(BB.getTerminator())->getSuccessor(1);
else
return;

if (!L->contains(InLoopSucc) || !L->isLoopExiting(&BB) || InLoopSucc == &BB)
return;

auto *AR = dyn_cast_or_null<SCEVAddRecExpr>(SE.getSCEV(PN));
if (!AR)
return;

const SCEV *StartSCEV = AR->getStart();
Value *StartValue = nullptr;
if (auto *C = dyn_cast<SCEVConstant>(StartSCEV))
StartValue = C->getValue();
else if (auto *U = dyn_cast<SCEVUnknown>(StartSCEV))
StartValue = U->getValue();

if (!StartValue)
return;

DomTreeNode *DTN = DT.getNode(InLoopSucc);
auto Inc = SE.getMonotonicPredicateType(AR, CmpInst::ICMP_UGT);
bool MonotonicallyIncreasing =
Inc && *Inc == ScalarEvolution::MonotonicallyIncreasing;
if (MonotonicallyIncreasing) {
// SCEV guarantees that AR does not wrap, so PN >= StartValue can be added
// unconditionally.
WorkList.push_back(
FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_UGE, PN, StartValue));
}

APInt StepOffset;
if (auto *C = dyn_cast<SCEVConstant>(AR->getStepRecurrence(SE)))
StepOffset = C->getAPInt();
else
return;

// Make sure AR either steps by 1 or that the value we compare against is a
// GEP based on the same start value and all offsets are a multiple of the
// step size, to guarantee that the induction will reach the value.
if (StepOffset.isZero() || StepOffset.isNegative())
return;

if (!StepOffset.isOne()) {
auto *UpperGEP = dyn_cast<GetElementPtrInst>(B);
if (!UpperGEP || UpperGEP->getPointerOperand() != StartValue ||
!UpperGEP->isInBounds())
return;

MapVector<Value *, APInt> UpperVariableOffsets;
APInt UpperConstantOffset(StepOffset.getBitWidth(), 0);
const DataLayout &DL = BB.getModule()->getDataLayout();
if (!UpperGEP->collectOffset(DL, StepOffset.getBitWidth(),
UpperVariableOffsets, UpperConstantOffset))
return;
// All variable offsets and the constant offset have to be a multiple of the
// step.
if (!UpperConstantOffset.urem(StepOffset).isZero() ||
any_of(UpperVariableOffsets, [&StepOffset](const auto &P) {
return !P.second.urem(StepOffset).isZero();
}))
return;
}

// AR may wrap. Add PN >= StartValue conditional on StartValue <= B which
// guarantees that the loop exits before wrapping in combination with the
// restrictions on B and the step above.
if (!MonotonicallyIncreasing) {
WorkList.push_back(FactOrCheck::getConditionFact(
DTN, CmpInst::ICMP_UGE, PN, StartValue,
ConditionTy(CmpInst::ICMP_ULE, StartValue, B)));
}
WorkList.push_back(FactOrCheck::getConditionFact(
DTN, CmpInst::ICMP_ULT, PN, B,
ConditionTy(CmpInst::ICMP_ULE, StartValue, B)));
}

void State::addInfoFor(BasicBlock &BB) {
addInfoForInductions(BB);

// True as long as long as the current instruction is guaranteed to execute.
bool GuaranteedToExecute = true;
// Queue conditions and assumes.
Expand Down Expand Up @@ -1179,6 +1300,7 @@ static bool checkAndSecondOpImpliedByFirst(
FactOrCheck &CB, ConstraintInfo &Info, Module *ReproducerModule,
SmallVectorImpl<ReproducerEntry> &ReproducerCondStack,
SmallVectorImpl<StackEntry> &DFSInStack) {

CmpInst::Predicate Pred;
Value *A, *B;
Instruction *And = CB.getContextInst();
Expand Down Expand Up @@ -1322,15 +1444,16 @@ tryToSimplifyOverflowMath(IntrinsicInst *II, ConstraintInfo &Info,
return Changed;
}

static bool eliminateConstraints(Function &F, DominatorTree &DT,
static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
ScalarEvolution &SE,
OptimizationRemarkEmitter &ORE) {
bool Changed = false;
DT.updateDFSNumbers();
SmallVector<Value *> FunctionArgs;
for (Value &Arg : F.args())
FunctionArgs.push_back(&Arg);
ConstraintInfo Info(F.getParent()->getDataLayout(), FunctionArgs);
State S(DT);
State S(DT, LI, SE);
std::unique_ptr<Module> ReproducerModule(
DumpReproducers ? new Module(F.getName(), F.getContext()) : nullptr);

Expand Down Expand Up @@ -1428,6 +1551,10 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT,
}

auto AddFact = [&](CmpInst::Predicate Pred, Value *A, Value *B) {
LLVM_DEBUG(dbgs() << "fact to add to the system: "
<< CmpInst::getPredicateName(Pred) << " ";
A->printAsOperand(dbgs()); dbgs() << ", ";
B->printAsOperand(dbgs()); dbgs() << "\n");
if (Info.getCS(CmpInst::isSigned(Pred)).size() > MaxRows) {
LLVM_DEBUG(
dbgs()
Expand Down Expand Up @@ -1475,6 +1602,9 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT,
Pred = CB.Cond.Pred;
A = CB.Cond.Op0;
B = CB.Cond.Op1;
if (CB.DoesHold.Pred != CmpInst::BAD_ICMP_PREDICATE &&
!Info.doesHold(CB.DoesHold.Pred, CB.DoesHold.Op0, CB.DoesHold.Op1))
continue;
} else {
bool Matched = match(CB.Inst, m_Intrinsic<Intrinsic::assume>(
m_ICmp(Pred, m_Value(A), m_Value(B))));
Expand Down Expand Up @@ -1511,12 +1641,16 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT,
PreservedAnalyses ConstraintEliminationPass::run(Function &F,
FunctionAnalysisManager &AM) {
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
auto &LI = AM.getResult<LoopAnalysis>(F);
auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F);
auto &ORE = AM.getResult<OptimizationRemarkEmitterAnalysis>(F);
if (!eliminateConstraints(F, DT, ORE))
if (!eliminateConstraints(F, DT, LI, SE, ORE))
return PreservedAnalyses::all();

PreservedAnalyses PA;
PA.preserve<DominatorTreeAnalysis>();
PA.preserve<LoopAnalysis>();
PA.preserve<ScalarEvolutionAnalysis>();
PA.preserveSet<CFGAnalyses>();
return PA;
}
6 changes: 4 additions & 2 deletions llvm/test/Other/new-pm-defaults.ll
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,12 @@
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
; CHECK-O-NEXT: Running pass: ReassociatePass
; CHECK-O23SZ-NEXT: Running pass: ConstraintEliminationPass
; CHECK-O23SZ-NEXT: Running analysis: LoopAnalysis
; CHECK-O23SZ-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running analysis: LoopAnalysis
; CHECK-O1-NEXT: Running analysis: LoopAnalysis
; CHECK-O-NEXT: Running pass: LCSSAPass
; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O1-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: LoopInstSimplifyPass
; CHECK-O-NEXT: Running pass: LoopSimplifyCFGPass
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Other/new-pm-lto-defaults.ll
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
; CHECK-O23SZ-NEXT: Running pass: InstCombinePass
; CHECK-EP-Peephole-NEXT: Running pass: NoOpFunctionPass
; CHECK-O23SZ-NEXT: Running pass: ConstraintEliminationPass
; CHECK-O23SZ-NEXT: Running analysis: LoopAnalysis on foo
; CHECK-O23SZ-NEXT: Running analysis: ScalarEvolutionAnalysis on foo
; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass
; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis
; CHECK-O23SZ-NEXT: Running pass: SROAPass on foo
Expand All @@ -93,11 +95,9 @@
; CHECK-O23SZ-NEXT: Invalidating analysis: AAManager on foo
; CHECK-O23SZ-NEXT: Running pass: OpenMPOptCGSCCPass on (foo)
; CHECK-O23SZ-NEXT: Running pass: LoopSimplifyPass on foo
; CHECK-O23SZ-NEXT: Running analysis: LoopAnalysis on foo
; CHECK-O23SZ-NEXT: Running pass: LCSSAPass on foo
; CHECK-O23SZ-NEXT: Running analysis: MemorySSAAnalysis on foo
; CHECK-O23SZ-NEXT: Running analysis: AAManager on foo
; CHECK-O23SZ-NEXT: Running analysis: ScalarEvolutionAnalysis on foo
; CHECK-O23SZ-NEXT: Running analysis: InnerAnalysisManagerProxy
; CHECK-O23SZ-NEXT: Running pass: LICMPass on loop
; CHECK-O23SZ-NEXT: Running pass: GVNPass on foo
Expand Down
6 changes: 4 additions & 2 deletions llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,12 @@
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
; CHECK-O-NEXT: Running pass: ReassociatePass
; CHECK-O23SZ-NEXT: Running pass: ConstraintEliminationPass
; CHECK-O23SZ-NEXT: Running analysis: LoopAnalysis
; CHECK-O23SZ-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running analysis: LoopAnalysis
; CHECK-O1-NEXT: Running analysis: LoopAnalysis
; CHECK-O-NEXT: Running pass: LCSSAPass
; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O1-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: LoopInstSimplifyPass
; CHECK-O-NEXT: Running pass: LoopSimplifyCFGPass
Expand Down
3 changes: 2 additions & 1 deletion llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
; CHECK-O-NEXT: Running pass: ReassociatePass
; CHECK-O23SZ-NEXT: Running pass: ConstraintEliminationPass
; CHECK-O23SZ-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running pass: LCSSAPass
; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O1-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: LoopInstSimplifyPass
; CHECK-O-NEXT: Running pass: LoopSimplifyCFGPass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,10 @@
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
; CHECK-O-NEXT: Running pass: ReassociatePass
; CHECK-O23SZ-NEXT: Running pass: ConstraintEliminationPass
; CHECK-O23SZ-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running pass: LCSSAPass
; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O1-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: LoopInstSimplifyPass
; CHECK-O-NEXT: Running pass: LoopSimplifyCFGPass
Expand Down
6 changes: 4 additions & 2 deletions llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,12 @@
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
; CHECK-O-NEXT: Running pass: ReassociatePass
; CHECK-O23SZ-NEXT: Running pass: ConstraintEliminationPass
; CHECK-O23SZ-NEXT: Running analysis: LoopAnalysis
; CHECK-O23SZ-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running analysis: LoopAnalysis
; CHECK-O1-NEXT: Running analysis: LoopAnalysis
; CHECK-O-NEXT: Running pass: LCSSAPass
; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O1-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: LoopInstSimplifyPass
; CHECK-O-NEXT: Running pass: LoopSimplifyCFGPass
Expand Down
3 changes: 2 additions & 1 deletion llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,10 @@
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
; CHECK-O-NEXT: Running pass: ReassociatePass
; CHECK-O23SZ-NEXT: Running pass: ConstraintEliminationPass
; CHECK-O23SZ-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running pass: LCSSAPass
; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O1-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: LoopInstSimplifyPass
; CHECK-O-NEXT: Running pass: LoopSimplifyCFGPass
Expand Down
3 changes: 2 additions & 1 deletion llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,10 @@
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
; CHECK-O-NEXT: Running pass: ReassociatePass
; CHECK-O23SZ-NEXT: Running pass: ConstraintEliminationPass
; CHECK-O23SZ-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running pass: LoopSimplifyPass
; CHECK-O-NEXT: Running pass: LCSSAPass
; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O1-NEXT: Running analysis: ScalarEvolutionAnalysis
; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy
; CHECK-O-NEXT: Running pass: LoopInstSimplifyPass
; CHECK-O-NEXT: Running pass: LoopSimplifyCFGPass
Expand Down

0 comments on commit e6a1657

Please sign in to comment.