Skip to content

Commit

Permalink
ConstraintElimination: use DynamicAPInt
Browse files Browse the repository at this point in the history
To automatically handle overflows correctly and make the transform more
powerful, replace uses of int64_t with DynamicAPInt in
ConstraintElimination and ConstraintSystem.
  • Loading branch information
artagnon committed Jul 16, 2024
1 parent c5ee3c0 commit dcd0c32
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 116 deletions.
50 changes: 24 additions & 26 deletions llvm/include/llvm/Analysis/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,35 @@
#ifndef LLVM_ANALYSIS_CONSTRAINTSYSTEM_H
#define LLVM_ANALYSIS_CONSTRAINTSYSTEM_H

#include "llvm/ADT/APInt.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DynamicAPInt.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/MathExtras.h"

#include <string>

namespace llvm {

class Value;
class ConstraintSystem {
struct Entry {
int64_t Coefficient;
DynamicAPInt Coefficient;
uint16_t Id;

Entry(int64_t Coefficient, uint16_t Id)
Entry(const DynamicAPInt &Coefficient, uint16_t Id)
: Coefficient(Coefficient), Id(Id) {}
};

static int64_t getConstPart(const Entry &E) {
static DynamicAPInt getConstPart(const Entry &E) {
if (E.Id == 0)
return E.Coefficient;
return 0;
return DynamicAPInt{0};
}

static int64_t getLastCoefficient(ArrayRef<Entry> Row, uint16_t Id) {
static DynamicAPInt getLastCoefficient(ArrayRef<Entry> Row, uint16_t Id) {
if (Row.empty())
return 0;
return DynamicAPInt{0};
if (Row.back().Id == Id)
return Row.back().Coefficient;
return 0;
return DynamicAPInt{0};
}

size_t NumVariables = 0;
Expand Down Expand Up @@ -74,11 +71,12 @@ class ConstraintSystem {
ConstraintSystem(const DenseMap<Value *, unsigned> &Value2Index)
: NumVariables(Value2Index.size()), Value2Index(Value2Index) {}

bool addVariableRow(ArrayRef<int64_t> R) {
bool addVariableRow(ArrayRef<DynamicAPInt> R) {
assert(Constraints.empty() || R.size() == NumVariables);
// If all variable coefficients are 0, the constraint does not provide any
// usable information.
if (all_of(ArrayRef(R).drop_front(1), [](int64_t C) { return C == 0; }))
if (all_of(ArrayRef(R).drop_front(1),
[](const DynamicAPInt &C) { return C == 0; }))
return false;

SmallVector<Entry, 4> NewRow;
Expand All @@ -98,10 +96,11 @@ class ConstraintSystem {
return Value2Index;
}

bool addVariableRowFill(ArrayRef<int64_t> R) {
bool addVariableRowFill(ArrayRef<DynamicAPInt> R) {
// If all variable coefficients are 0, the constraint does not provide any
// usable information.
if (all_of(ArrayRef(R).drop_front(1), [](int64_t C) { return C == 0; }))
if (all_of(ArrayRef(R).drop_front(1),
[](const DynamicAPInt &C) { return C == 0; }))
return false;

NumVariables = std::max(R.size(), NumVariables);
Expand All @@ -111,7 +110,7 @@ class ConstraintSystem {
/// Returns true if there may be a solution for the constraints in the system.
bool mayHaveSolution();

static SmallVector<int64_t, 8> negate(SmallVector<int64_t, 8> R) {
static SmallVector<DynamicAPInt, 8> negate(SmallVector<DynamicAPInt, 8> R) {
// The negated constraint R is obtained by multiplying by -1 and adding 1 to
// the constant.
R[0] += 1;
Expand All @@ -122,31 +121,30 @@ class ConstraintSystem {
/// original vector.
///
/// \param R The vector of coefficients to be negated.
static SmallVector<int64_t, 8> negateOrEqual(SmallVector<int64_t, 8> R) {
static SmallVector<DynamicAPInt, 8>
negateOrEqual(SmallVector<DynamicAPInt, 8> R) {
// The negated constraint R is obtained by multiplying by -1.
for (auto &C : R)
if (MulOverflow(C, int64_t(-1), C))
return {};
C *= -1;
return R;
}

/// Converts the given vector to form a strict less than inequality. Does not
/// modify the original vector.
///
/// \param R The vector of coefficients to be converted.
static SmallVector<int64_t, 8> toStrictLessThan(SmallVector<int64_t, 8> R) {
static SmallVector<DynamicAPInt, 8>
toStrictLessThan(SmallVector<DynamicAPInt, 8> R) {
// The strict less than is obtained by subtracting 1 from the constant.
if (SubOverflow(R[0], int64_t(1), R[0])) {
return {};
}
R[0] -= 1;
return R;
}

bool isConditionImplied(SmallVector<int64_t, 8> R) const;
bool isConditionImplied(SmallVector<DynamicAPInt, 8> R) const;

SmallVector<int64_t> getLastConstraint() const {
SmallVector<DynamicAPInt> getLastConstraint() const {
assert(!Constraints.empty() && "Constraint system is empty");
SmallVector<int64_t> Result(NumVariables, 0);
SmallVector<DynamicAPInt> Result(NumVariables, DynamicAPInt{0});
for (auto &Entry : Constraints.back())
Result[Entry.Id] = Entry.Coefficient;
return Result;
Expand Down
35 changes: 16 additions & 19 deletions llvm/lib/Analysis/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
//===----------------------------------------------------------------------===//

#include "llvm/Analysis/ConstraintSystem.h"
#include "llvm/ADT/DynamicAPInt.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/Debug.h"
Expand Down Expand Up @@ -55,8 +55,8 @@ bool ConstraintSystem::eliminateUsingFM() {
if (R1 == R2)
continue;

int64_t UpperLast = getLastCoefficient(RemainingRows[R2], LastIdx);
int64_t LowerLast = getLastCoefficient(RemainingRows[R1], LastIdx);
DynamicAPInt UpperLast = getLastCoefficient(RemainingRows[R2], LastIdx);
DynamicAPInt LowerLast = getLastCoefficient(RemainingRows[R1], LastIdx);
assert(
UpperLast != 0 && LowerLast != 0 &&
"RemainingRows should only contain rows where the variable is != 0");
Expand All @@ -79,9 +79,9 @@ bool ConstraintSystem::eliminateUsingFM() {
while (true) {
if (IdxUpper >= UpperRow.size() || IdxLower >= LowerRow.size())
break;
int64_t M1, M2, N;
int64_t UpperV = 0;
int64_t LowerV = 0;
DynamicAPInt M1, M2, N;
DynamicAPInt UpperV{0};
DynamicAPInt LowerV{0};
uint16_t CurrentId = std::numeric_limits<uint16_t>::max();
if (IdxUpper < UpperRow.size()) {
CurrentId = std::min(UpperRow[IdxUpper].Id, CurrentId);
Expand All @@ -94,18 +94,13 @@ bool ConstraintSystem::eliminateUsingFM() {
UpperV = UpperRow[IdxUpper].Coefficient;
IdxUpper++;
}

if (MulOverflow(UpperV, -1 * LowerLast, M1))
return false;
M1 = UpperV * (-LowerLast);
if (IdxLower < LowerRow.size() && LowerRow[IdxLower].Id == CurrentId) {
LowerV = LowerRow[IdxLower].Coefficient;
IdxLower++;
}

if (MulOverflow(LowerV, UpperLast, M2))
return false;
if (AddOverflow(M1, M2, N))
return false;
M2 = LowerV * UpperLast;
N = M1 + M2;
if (N == 0)
continue;
NR.emplace_back(N, CurrentId);
Expand Down Expand Up @@ -170,15 +165,15 @@ void ConstraintSystem::dump() const {
continue;
std::string Coefficient;
if (E.Coefficient != 1)
Coefficient = std::to_string(E.Coefficient) + " * ";
Coefficient = std::to_string(int64_t{E.Coefficient}) + " * ";
Parts.push_back(Coefficient + Names[E.Id - 1]);
}
// assert(!Parts.empty() && "need to have at least some parts");
int64_t ConstPart = 0;
DynamicAPInt ConstPart{0};
if (Row[0].Id == 0)
ConstPart = Row[0].Coefficient;
LLVM_DEBUG(dbgs() << join(Parts, std::string(" + "))
<< " <= " << std::to_string(ConstPart) << "\n");
<< " <= " << std::to_string(int64_t{ConstPart}) << "\n");
}
#endif
}
Expand All @@ -191,10 +186,12 @@ bool ConstraintSystem::mayHaveSolution() {
return HasSolution;
}

bool ConstraintSystem::isConditionImplied(SmallVector<int64_t, 8> R) const {
bool ConstraintSystem::isConditionImplied(
SmallVector<DynamicAPInt, 8> R) const {
// If all variable coefficients are 0, we have 'C >= 0'. If the constant is >=
// 0, R is always true, regardless of the system.
if (all_of(ArrayRef(R).drop_front(1), [](int64_t C) { return C == 0; }))
if (all_of(ArrayRef(R).drop_front(1),
[](const DynamicAPInt &C) { return C == 0; }))
return R[0] >= 0;

// If there is no solution with the negation of R added to the system, the
Expand Down
49 changes: 26 additions & 23 deletions llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Scalar/ConstraintElimination.h"
#include "llvm/ADT/DynamicAPInt.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallVector.h"
Expand Down Expand Up @@ -221,17 +222,17 @@ struct StackEntry {
};

struct ConstraintTy {
SmallVector<int64_t, 8> Coefficients;
SmallVector<DynamicAPInt, 8> Coefficients;
SmallVector<ConditionTy, 2> Preconditions;

SmallVector<SmallVector<int64_t, 8>> ExtraInfo;
SmallVector<SmallVector<DynamicAPInt, 8>> ExtraInfo;

bool IsSigned = false;

ConstraintTy() = default;

ConstraintTy(SmallVector<int64_t, 8> Coefficients, bool IsSigned, bool IsEq,
bool IsNe)
ConstraintTy(SmallVector<DynamicAPInt, 8> Coefficients, bool IsSigned,
bool IsEq, bool IsNe)
: Coefficients(std::move(Coefficients)), IsSigned(IsSigned), IsEq(IsEq),
IsNe(IsNe) {}

Expand Down Expand Up @@ -278,8 +279,9 @@ class ConstraintInfo {
auto &Value2Index = getValue2Index(false);
// Add Arg > -1 constraints to unsigned system for all function arguments.
for (Value *Arg : FunctionArgs) {
ConstraintTy VarPos(SmallVector<int64_t, 8>(Value2Index.size() + 1, 0),
false, false, false);
ConstraintTy VarPos(
SmallVector<DynamicAPInt, 8>(Value2Index.size() + 1, DynamicAPInt{0}),
false, false, false);
VarPos.Coefficients[Value2Index[Arg]] = -1;
UnsignedCS.addVariableRow(VarPos.Coefficients);
}
Expand Down Expand Up @@ -663,8 +665,8 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
Preconditions, IsSigned, DL);
auto BDec = decompose(Op1->stripPointerCastsSameRepresentation(),
Preconditions, IsSigned, DL);
int64_t Offset1 = ADec.Offset;
int64_t Offset2 = BDec.Offset;
DynamicAPInt Offset1{ADec.Offset};
DynamicAPInt Offset2{BDec.Offset};
Offset1 *= -1;

auto &VariablesA = ADec.Vars;
Expand Down Expand Up @@ -692,7 +694,8 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
// Build result constraint, by first adding all coefficients from A and then
// subtracting all coefficients from B.
ConstraintTy Res(
SmallVector<int64_t, 8>(Value2Index.size() + NewVariables.size() + 1, 0),
SmallVector<DynamicAPInt, 8>(Value2Index.size() + NewVariables.size() + 1,
DynamicAPInt{0}),
IsSigned, IsEq, IsNe);
// Collect variables that are known to be positive in all uses in the
// constraint.
Expand All @@ -706,27 +709,24 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
}

for (const auto &KV : VariablesB) {
if (SubOverflow(R[GetOrAddIndex(KV.Variable)], KV.Coefficient,
R[GetOrAddIndex(KV.Variable)]))
return {};
R[GetOrAddIndex(KV.Variable)] =
R[GetOrAddIndex(KV.Variable)] - KV.Coefficient;
auto I =
KnownNonNegativeVariables.insert({KV.Variable, KV.IsKnownNonNegative});
I.first->second &= KV.IsKnownNonNegative;
}

int64_t OffsetSum;
if (AddOverflow(Offset1, Offset2, OffsetSum))
return {};
DynamicAPInt OffsetSum;
OffsetSum = Offset1 + Offset2;
if (Pred == (IsSigned ? CmpInst::ICMP_SLT : CmpInst::ICMP_ULT))
if (AddOverflow(OffsetSum, int64_t(-1), OffsetSum))
return {};
OffsetSum += -1;
R[0] = OffsetSum;
Res.Preconditions = std::move(Preconditions);

// Remove any (Coefficient, Variable) entry where the Coefficient is 0 for new
// variables.
while (!NewVariables.empty()) {
int64_t Last = R.back();
DynamicAPInt Last = R.back();
if (Last != 0)
break;
R.pop_back();
Expand All @@ -739,7 +739,8 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
if (!KV.second ||
(!Value2Index.contains(KV.first) && !NewIndexMap.contains(KV.first)))
continue;
SmallVector<int64_t, 8> C(Value2Index.size() + NewVariables.size() + 1, 0);
SmallVector<DynamicAPInt, 8> C(Value2Index.size() + NewVariables.size() + 1,
DynamicAPInt{0});
C[GetOrAddIndex(KV.first)] = -1;
Res.ExtraInfo.push_back(C);
}
Expand All @@ -756,8 +757,9 @@ ConstraintTy ConstraintInfo::getConstraintForSolving(CmpInst::Predicate Pred,
(Pred == CmpInst::ICMP_UGE && Op1 == NullC)) {
auto &Value2Index = getValue2Index(false);
// Return constraint that's trivially true.
return ConstraintTy(SmallVector<int64_t, 8>(Value2Index.size(), 0), false,
false, false);
return ConstraintTy(
SmallVector<DynamicAPInt, 8>(Value2Index.size(), DynamicAPInt{0}),
false, false, false);
}

// If both operands are known to be non-negative, change signed predicates to
Expand Down Expand Up @@ -892,7 +894,7 @@ void ConstraintInfo::transferToOtherSystem(

#ifndef NDEBUG

static void dumpConstraint(ArrayRef<int64_t> C,
static void dumpConstraint(ArrayRef<DynamicAPInt> C,
const DenseMap<Value *, unsigned> &Value2Index) {
ConstraintSystem CS(Value2Index);
CS.addVariableRowFill(C);
Expand Down Expand Up @@ -1590,7 +1592,8 @@ void ConstraintInfo::addFact(CmpInst::Predicate Pred, Value *A, Value *B,

if (!R.IsSigned) {
for (Value *V : NewVariables) {
ConstraintTy VarPos(SmallVector<int64_t, 8>(Value2Index.size() + 1, 0),
ConstraintTy VarPos(SmallVector<DynamicAPInt, 8>(Value2Index.size() + 1,
DynamicAPInt{0}),
false, false, false);
VarPos.Coefficients[Value2Index[V]] = -1;
CSToUse.addVariableRow(VarPos.Coefficients);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ define i32 @f(i64 %a3, i64 %numElements) {
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i64 [[A1]], [[A3]]
; CHECK-NEXT: br i1 [[CMP]], label [[IF_END_I:%.*]], label [[ABORT:%.*]]
; CHECK: if.end.i:
; CHECK-NEXT: [[CMP2_NOT_I:%.*]] = icmp ult i64 [[A1]], [[A3]]
; CHECK-NEXT: br i1 [[CMP2_NOT_I]], label [[ABORT]], label [[EXIT:%.*]]
; CHECK-NEXT: br i1 false, label [[ABORT]], label [[EXIT:%.*]]
; CHECK: abort:
; CHECK-NEXT: ret i32 -1
; CHECK: exit:
Expand Down
3 changes: 1 addition & 2 deletions llvm/test/Transforms/ConstraintElimination/overflows.ll
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ define i1 @test_overflow_in_negate_constraint(i8 %x, i64 %y) {
; CHECK-NEXT: bb:
; CHECK-NEXT: [[ZEXT:%.*]] = zext i8 [[X]] to i64
; CHECK-NEXT: [[SHL:%.*]] = shl nuw nsw i64 [[ZEXT]], 63
; CHECK-NEXT: [[ICMP:%.*]] = icmp uge i64 [[Y]], [[SHL]]
; CHECK-NEXT: ret i1 [[ICMP]]
; CHECK-NEXT: ret i1 true
;
bb:
%zext = zext i8 %x to i64
Expand Down
Loading

0 comments on commit dcd0c32

Please sign in to comment.