forked from OSchip/llvm-project
[ConstraintElimination] Improve index handing during constraint building.
This patch improves the index management during constraint building. Previously, the code rejected constraints which used values that were not part of Value2Index, but after combining the coefficients of the new indices were 0 (if ShouldAdd was 0). In those cases, no new indices need to be added. Instead of adding to Value2Index directly, add new indices to the NewIndices map. The caller can then check if it needs to add any new indices. This enables checking constraints like `a + x <= a + n` to `x <= n`, even if there is no constraint for `a` directly.
This commit is contained in:
parent
d8323b1a86
commit
1f1f037ed3
|
@ -106,34 +106,39 @@ struct ConstraintTy {
|
|||
unsigned size() const { return Coefficients.size(); }
|
||||
};
|
||||
|
||||
/// Turn a condition \p CmpI into a constraint vector, using indices from \p
|
||||
/// Value2Index. If \p ShouldAdd is true, new indices are added for values not
|
||||
/// yet in \p Value2Index.
|
||||
/// Turn a condition \p CmpI into a vector of constraints, using indices from \p
|
||||
/// Value2Index. Additional indices for newly discovered values are added to \p
|
||||
/// NewIndices.
|
||||
static SmallVector<ConstraintTy, 4>
|
||||
getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
|
||||
DenseMap<Value *, unsigned> &Value2Index, bool ShouldAdd) {
|
||||
const DenseMap<Value *, unsigned> &Value2Index,
|
||||
DenseMap<Value *, unsigned> &NewIndices) {
|
||||
int64_t Offset1 = 0;
|
||||
int64_t Offset2 = 0;
|
||||
|
||||
auto TryToGetIndex = [ShouldAdd,
|
||||
&Value2Index](Value *V) -> Optional<unsigned> {
|
||||
if (ShouldAdd) {
|
||||
Value2Index.insert({V, Value2Index.size() + 1});
|
||||
return Value2Index[V];
|
||||
}
|
||||
auto I = Value2Index.find(V);
|
||||
if (I == Value2Index.end())
|
||||
return None;
|
||||
return I->second;
|
||||
// First try to look up \p V in Value2Index and NewIndices. Otherwise add a
|
||||
// new entry to NewIndices.
|
||||
auto GetOrAddIndex = [&Value2Index, &NewIndices](Value *V) -> unsigned {
|
||||
auto V2I = Value2Index.find(V);
|
||||
if (V2I != Value2Index.end())
|
||||
return V2I->second;
|
||||
auto NewI = NewIndices.find(V);
|
||||
if (NewI != NewIndices.end())
|
||||
return NewI->second;
|
||||
auto Insert =
|
||||
NewIndices.insert({V, Value2Index.size() + NewIndices.size() + 1});
|
||||
return Insert.first->second;
|
||||
};
|
||||
|
||||
if (Pred == CmpInst::ICMP_UGT || Pred == CmpInst::ICMP_UGE)
|
||||
return getConstraint(CmpInst::getSwappedPredicate(Pred), Op1, Op0,
|
||||
Value2Index, ShouldAdd);
|
||||
Value2Index, NewIndices);
|
||||
|
||||
if (Pred == CmpInst::ICMP_EQ) {
|
||||
auto A = getConstraint(CmpInst::ICMP_UGE, Op0, Op1, Value2Index, ShouldAdd);
|
||||
auto B = getConstraint(CmpInst::ICMP_ULE, Op0, Op1, Value2Index, ShouldAdd);
|
||||
auto A =
|
||||
getConstraint(CmpInst::ICMP_UGE, Op0, Op1, Value2Index, NewIndices);
|
||||
auto B =
|
||||
getConstraint(CmpInst::ICMP_ULE, Op0, Op1, Value2Index, NewIndices);
|
||||
append_range(A, B);
|
||||
return A;
|
||||
}
|
||||
|
@ -160,31 +165,29 @@ getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
|
|||
auto VariablesA = make_range(std::next(ADec.begin()), ADec.end());
|
||||
auto VariablesB = make_range(std::next(BDec.begin()), BDec.end());
|
||||
|
||||
// Check if each referenced value in the constraint is already in the system
|
||||
// or can be added (if ShouldAdd is true).
|
||||
// Make sure all variables have entries in Value2Index or NewIndices.
|
||||
for (const auto &KV :
|
||||
concat<std::pair<int64_t, Value *>>(VariablesA, VariablesB))
|
||||
if (!TryToGetIndex(KV.second))
|
||||
return {};
|
||||
GetOrAddIndex(KV.second);
|
||||
|
||||
// Build result constraint, by first adding all coefficients from A and then
|
||||
// subtracting all coefficients from B.
|
||||
SmallVector<int64_t, 8> R(Value2Index.size() + 1, 0);
|
||||
SmallVector<int64_t, 8> R(Value2Index.size() + NewIndices.size() + 1, 0);
|
||||
for (const auto &KV : VariablesA)
|
||||
R[Value2Index[KV.second]] += KV.first;
|
||||
R[GetOrAddIndex(KV.second)] += KV.first;
|
||||
|
||||
for (const auto &KV : VariablesB)
|
||||
R[Value2Index[KV.second]] -= KV.first;
|
||||
R[GetOrAddIndex(KV.second)] -= KV.first;
|
||||
|
||||
R[0] = Offset1 + Offset2 + (Pred == CmpInst::ICMP_ULT ? -1 : 0);
|
||||
return {R};
|
||||
}
|
||||
|
||||
static SmallVector<ConstraintTy, 4>
|
||||
getConstraint(CmpInst *Cmp, DenseMap<Value *, unsigned> &Value2Index,
|
||||
bool ShouldAdd) {
|
||||
getConstraint(CmpInst *Cmp, const DenseMap<Value *, unsigned> &Value2Index,
|
||||
DenseMap<Value *, unsigned> &NewIndices) {
|
||||
return getConstraint(Cmp->getPredicate(), Cmp->getOperand(0),
|
||||
Cmp->getOperand(1), Value2Index, ShouldAdd);
|
||||
Cmp->getOperand(1), Value2Index, NewIndices);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -343,9 +346,26 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT) {
|
|||
auto *Cmp = dyn_cast<CmpInst>(&I);
|
||||
if (!Cmp)
|
||||
continue;
|
||||
auto R = getConstraint(Cmp, Value2Index, false);
|
||||
if (R.size() != 1 || R[0].size() == 1)
|
||||
|
||||
DenseMap<Value *, unsigned> NewIndices;
|
||||
auto R = getConstraint(Cmp, Value2Index, NewIndices);
|
||||
if (R.size() != 1)
|
||||
continue;
|
||||
|
||||
// Check if all coefficients of new indices are 0 after building the
|
||||
// constraint. Skip if any of the new indices has a non-null
|
||||
// coefficient.
|
||||
bool HasNewIndex = false;
|
||||
for (unsigned I = 0; I < NewIndices.size(); ++I) {
|
||||
int64_t Last = R[0].Coefficients.pop_back_val();
|
||||
if (Last != 0) {
|
||||
HasNewIndex = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (HasNewIndex || R[0].size() == 1)
|
||||
continue;
|
||||
|
||||
if (CS.isConditionImplied(R[0].Coefficients)) {
|
||||
if (!DebugCounter::shouldExecute(EliminatedCounter))
|
||||
continue;
|
||||
|
@ -399,10 +419,14 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT) {
|
|||
|
||||
// Otherwise, add the condition to the system and stack, if we can transform
|
||||
// it into a constraint.
|
||||
auto R = getConstraint(CB.Condition, Value2Index, true);
|
||||
DenseMap<Value *, unsigned> NewIndices;
|
||||
auto R = getConstraint(CB.Condition, Value2Index, NewIndices);
|
||||
if (R.empty())
|
||||
continue;
|
||||
|
||||
for (auto &KV : NewIndices)
|
||||
Value2Index.insert(KV);
|
||||
|
||||
LLVM_DEBUG(dbgs() << "Adding " << *CB.Condition << " " << CB.Not << "\n");
|
||||
bool Added = false;
|
||||
for (auto &C : R) {
|
||||
|
|
|
@ -14,9 +14,9 @@ define void @test_uge_temporary_indices_decompose(i8 %start, i8 %n, i8 %idx) {
|
|||
; CHECK-NEXT: br i1 [[CMP_PRE]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
|
||||
; CHECK: if.then:
|
||||
; CHECK-NEXT: [[T_0:%.*]] = icmp ult i8 [[START_ADD_IDX]], [[START_ADD_N]]
|
||||
; CHECK-NEXT: call void @use(i1 [[T_0]])
|
||||
; CHECK-NEXT: call void @use(i1 true)
|
||||
; CHECK-NEXT: [[F_0:%.*]] = icmp uge i8 [[START_ADD_IDX]], [[START_ADD_N]]
|
||||
; CHECK-NEXT: call void @use(i1 [[F_0]])
|
||||
; CHECK-NEXT: call void @use(i1 false)
|
||||
; CHECK-NEXT: [[C_1:%.*]] = icmp ult i8 [[START_ADD_1]], [[START_ADD_N]]
|
||||
; CHECK-NEXT: call void @use(i1 [[C_1]])
|
||||
; CHECK-NEXT: [[C_2:%.*]] = icmp ult i8 [[START_ADD_IDX]], [[START_ADD_1]]
|
||||
|
@ -24,9 +24,9 @@ define void @test_uge_temporary_indices_decompose(i8 %start, i8 %n, i8 %idx) {
|
|||
; CHECK-NEXT: ret void
|
||||
; CHECK: if.end:
|
||||
; CHECK-NEXT: [[F_1:%.*]] = icmp ult i8 [[START_ADD_IDX]], [[START_ADD_N]]
|
||||
; CHECK-NEXT: call void @use(i1 [[F_1]])
|
||||
; CHECK-NEXT: call void @use(i1 false)
|
||||
; CHECK-NEXT: [[T_1:%.*]] = icmp uge i8 [[START_ADD_IDX]], [[START_ADD_N]]
|
||||
; CHECK-NEXT: call void @use(i1 [[T_1]])
|
||||
; CHECK-NEXT: call void @use(i1 true)
|
||||
; CHECK-NEXT: [[C_3:%.*]] = icmp ult i8 [[START_ADD_1]], [[START_ADD_N]]
|
||||
; CHECK-NEXT: call void @use(i1 [[C_3]])
|
||||
; CHECK-NEXT: [[C_4:%.*]] = icmp ult i8 [[START_ADD_IDX]], [[START_ADD_1]]
|
||||
|
|
|
@ -663,8 +663,8 @@ define void @test_ptr_need_one_upper_check(i32* readonly %src, i32* %dst, i32 %n
|
|||
; CHECK-NEXT: [[DST_UPPER:%.*]] = getelementptr inbounds i32, i32* [[DST:%.*]], i64 [[TMP0]]
|
||||
; CHECK-NEXT: [[DST_IDX:%.*]] = getelementptr inbounds i32, i32* [[DST]], i64 [[IDXPROM]]
|
||||
; CHECK-NEXT: [[CMP_DST_IDX_UPPER:%.*]] = icmp ult i32* [[DST_IDX]], [[DST_UPPER]]
|
||||
; CHECK-NEXT: call void @use(i1 [[CMP_DST_IDX_UPPER]])
|
||||
; CHECK-NEXT: br i1 [[CMP_DST_IDX_UPPER]], label [[LOOP_LATCH_2]], label [[EXIT]]
|
||||
; CHECK-NEXT: call void @use(i1 true)
|
||||
; CHECK-NEXT: br i1 true, label [[LOOP_LATCH_2]], label [[EXIT]]
|
||||
; CHECK: loop.latch.2:
|
||||
; CHECK-NEXT: br label [[LOOP_HEADER]]
|
||||
; CHECK: exit:
|
||||
|
|
Loading…
Reference in New Issue