[llvm] 79d6f52 - [LVI] Use Constant instead of Tristate for predicate results
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 4 08:28:25 PDT 2024
Author: Nikita Popov
Date: 2024-07-04T17:28:09+02:00
New Revision: 79d6f52c4f56fbada3e14fa924c370e27418222e
URL: https://github.com/llvm/llvm-project/commit/79d6f52c4f56fbada3e14fa924c370e27418222e
DIFF: https://github.com/llvm/llvm-project/commit/79d6f52c4f56fbada3e14fa924c370e27418222e.diff
LOG: [LVI] Use Constant instead of Tristate for predicate results
A lot of the users just end up converting it into a Constant
themselves. Doing this in the API leaves less room for error
with vector types, and brings getPredicateResult() closer to
LatticeValueElement::getCompare(), hopefully allowing us to
consolidate them.
Added:
Modified:
llvm/include/llvm/Analysis/LazyValueInfo.h
llvm/lib/Analysis/LazyValueInfo.cpp
llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
llvm/lib/Transforms/Scalar/JumpThreading.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/LazyValueInfo.h b/llvm/include/llvm/Analysis/LazyValueInfo.h
index 1ac355e39cabe..3ca3e5a146e73 100644
--- a/llvm/include/llvm/Analysis/LazyValueInfo.h
+++ b/llvm/include/llvm/Analysis/LazyValueInfo.h
@@ -60,32 +60,29 @@ namespace llvm {
return *this;
}
- /// This is used to return true/false/dunno results.
- enum Tristate { Unknown = -1, False = 0, True = 1 };
-
// Public query interface.
/// Determine whether the specified value comparison with a constant is
/// known to be true or false on the specified CFG edge. Pred is a CmpInst
/// predicate.
- Tristate getPredicateOnEdge(CmpInst::Predicate Pred, Value *V, Constant *C,
- BasicBlock *FromBB, BasicBlock *ToBB,
- Instruction *CxtI = nullptr);
+ Constant *getPredicateOnEdge(CmpInst::Predicate Pred, Value *V, Constant *C,
+ BasicBlock *FromBB, BasicBlock *ToBB,
+ Instruction *CxtI = nullptr);
/// Determine whether the specified value comparison with a constant is
/// known to be true or false at the specified instruction. \p Pred is a
/// CmpInst predicate. If \p UseBlockValue is true, the block value is also
/// taken into account.
- Tristate getPredicateAt(CmpInst::Predicate Pred, Value *V, Constant *C,
- Instruction *CxtI, bool UseBlockValue);
+ Constant *getPredicateAt(CmpInst::Predicate Pred, Value *V, Constant *C,
+ Instruction *CxtI, bool UseBlockValue);
/// Determine whether the specified value comparison is known to be true
/// or false at the specified instruction. While this takes two Value's,
/// it still requires that one of them is a constant.
/// \p Pred is a CmpInst predicate.
/// If \p UseBlockValue is true, the block value is also taken into account.
- Tristate getPredicateAt(CmpInst::Predicate Pred, Value *LHS, Value *RHS,
- Instruction *CxtI, bool UseBlockValue);
+ Constant *getPredicateAt(CmpInst::Predicate Pred, Value *LHS, Value *RHS,
+ Instruction *CxtI, bool UseBlockValue);
/// Determine whether the specified value is known to be a constant at the
/// specified instruction. Return null if not.
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index caa5b2559690a..674c47ebe786a 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -1775,29 +1775,26 @@ ConstantRange LazyValueInfo::getConstantRangeOnEdge(Value *V,
return toConstantRange(Result, V->getType(), /*UndefAllowed*/ true);
}
-static LazyValueInfo::Tristate
-getPredicateResult(CmpInst::Predicate Pred, Constant *C,
- const ValueLatticeElement &Val, const DataLayout &DL) {
+static Constant *getPredicateResult(CmpInst::Predicate Pred, Constant *C,
+ const ValueLatticeElement &Val,
+ const DataLayout &DL) {
// If we know the value is a constant, evaluate the conditional.
- Constant *Res = nullptr;
- if (Val.isConstant()) {
- Res = ConstantFoldCompareInstOperands(Pred, Val.getConstant(), C, DL);
- if (ConstantInt *ResCI = dyn_cast_or_null<ConstantInt>(Res))
- return ResCI->isZero() ? LazyValueInfo::False : LazyValueInfo::True;
- return LazyValueInfo::Unknown;
- }
+ if (Val.isConstant())
+ return ConstantFoldCompareInstOperands(Pred, Val.getConstant(), C, DL);
+ Type *ResTy = CmpInst::makeCmpResultType(C->getType());
if (Val.isConstantRange()) {
ConstantInt *CI = dyn_cast<ConstantInt>(C);
- if (!CI) return LazyValueInfo::Unknown;
+ if (!CI)
+ return nullptr;
const ConstantRange &CR = Val.getConstantRange();
ConstantRange RHS(CI->getValue());
if (CR.icmp(Pred, RHS))
- return LazyValueInfo::True;
+ return ConstantInt::getTrue(ResTy);
if (CR.icmp(CmpInst::getInversePredicate(Pred), RHS))
- return LazyValueInfo::False;
- return LazyValueInfo::Unknown;
+ return ConstantInt::getFalse(ResTy);
+ return nullptr;
}
if (Val.isNotConstant()) {
@@ -1805,29 +1802,29 @@ getPredicateResult(CmpInst::Predicate Pred, Constant *C,
// "V != C1".
if (Pred == ICmpInst::ICMP_EQ) {
// !C1 == C -> false iff C1 == C.
- Res = ConstantFoldCompareInstOperands(ICmpInst::ICMP_NE,
- Val.getNotConstant(), C, DL);
+ Constant *Res = ConstantFoldCompareInstOperands(
+ ICmpInst::ICMP_NE, Val.getNotConstant(), C, DL);
if (Res && Res->isNullValue())
- return LazyValueInfo::False;
+ return ConstantInt::getFalse(ResTy);
} else if (Pred == ICmpInst::ICMP_NE) {
// !C1 != C -> true iff C1 == C.
- Res = ConstantFoldCompareInstOperands(ICmpInst::ICMP_NE,
- Val.getNotConstant(), C, DL);
+ Constant *Res = ConstantFoldCompareInstOperands(
+ ICmpInst::ICMP_NE, Val.getNotConstant(), C, DL);
if (Res && Res->isNullValue())
- return LazyValueInfo::True;
+ return ConstantInt::getTrue(ResTy);
}
- return LazyValueInfo::Unknown;
+ return nullptr;
}
- return LazyValueInfo::Unknown;
+ return nullptr;
}
/// Determine whether the specified value comparison with a constant is known to
/// be true or false on the specified CFG edge. Pred is a CmpInst predicate.
-LazyValueInfo::Tristate
-LazyValueInfo::getPredicateOnEdge(CmpInst::Predicate Pred, Value *V,
- Constant *C, BasicBlock *FromBB,
- BasicBlock *ToBB, Instruction *CxtI) {
+Constant *LazyValueInfo::getPredicateOnEdge(CmpInst::Predicate Pred, Value *V,
+ Constant *C, BasicBlock *FromBB,
+ BasicBlock *ToBB,
+ Instruction *CxtI) {
Module *M = FromBB->getModule();
ValueLatticeElement Result =
getOrCreateImpl(M).getValueOnEdge(V, FromBB, ToBB, CxtI);
@@ -1835,10 +1832,9 @@ LazyValueInfo::getPredicateOnEdge(CmpInst::Predicate Pred, Value *V,
return getPredicateResult(Pred, C, Result, M->getDataLayout());
}
-LazyValueInfo::Tristate LazyValueInfo::getPredicateAt(CmpInst::Predicate Pred,
- Value *V, Constant *C,
- Instruction *CxtI,
- bool UseBlockValue) {
+Constant *LazyValueInfo::getPredicateAt(CmpInst::Predicate Pred, Value *V,
+ Constant *C, Instruction *CxtI,
+ bool UseBlockValue) {
// Is or is not NonNull are common predicates being queried. If
// isKnownNonZero can tell us the result of the predicate, we can
// return it quickly. But this is only a fastpath, and falling
@@ -1847,18 +1843,19 @@ LazyValueInfo::Tristate LazyValueInfo::getPredicateAt(CmpInst::Predicate Pred,
const DataLayout &DL = M->getDataLayout();
if (V->getType()->isPointerTy() && C->isNullValue() &&
isKnownNonZero(V->stripPointerCastsSameRepresentation(), DL)) {
+ Type *ResTy = CmpInst::makeCmpResultType(C->getType());
if (Pred == ICmpInst::ICMP_EQ)
- return LazyValueInfo::False;
+ return ConstantInt::getFalse(ResTy);
else if (Pred == ICmpInst::ICMP_NE)
- return LazyValueInfo::True;
+ return ConstantInt::getTrue(ResTy);
}
auto &Impl = getOrCreateImpl(M);
ValueLatticeElement Result =
UseBlockValue ? Impl.getValueInBlock(V, CxtI->getParent(), CxtI)
: Impl.getValueAt(V, CxtI);
- Tristate Ret = getPredicateResult(Pred, C, Result, DL);
- if (Ret != Unknown)
+ Constant *Ret = getPredicateResult(Pred, C, Result, DL);
+ if (Ret)
return Ret;
// Note: The following bit of code is somewhat distinct from the rest of LVI;
@@ -1889,7 +1886,7 @@ LazyValueInfo::Tristate LazyValueInfo::getPredicateAt(CmpInst::Predicate Pred,
// analysis below.
pred_iterator PI = pred_begin(BB), PE = pred_end(BB);
if (PI == PE)
- return Unknown;
+ return nullptr;
// If V is a PHI node in the same block as the context, we need to ask
// questions about the predicate as applied to the incoming value along
@@ -1897,23 +1894,23 @@ LazyValueInfo::Tristate LazyValueInfo::getPredicateAt(CmpInst::Predicate Pred,
// known along all incoming edges.
if (auto *PHI = dyn_cast<PHINode>(V))
if (PHI->getParent() == BB) {
- Tristate Baseline = Unknown;
+ Constant *Baseline = nullptr;
for (unsigned i = 0, e = PHI->getNumIncomingValues(); i < e; i++) {
Value *Incoming = PHI->getIncomingValue(i);
BasicBlock *PredBB = PHI->getIncomingBlock(i);
// Note that PredBB may be BB itself.
- Tristate Result =
+ Constant *Result =
getPredicateOnEdge(Pred, Incoming, C, PredBB, BB, CxtI);
// Keep going as long as we've seen a consistent known result for
// all inputs.
Baseline = (i == 0) ? Result /* First iteration */
: (Baseline == Result ? Baseline
- : Unknown); /* All others */
- if (Baseline == Unknown)
+ : nullptr); /* All others */
+ if (!Baseline)
break;
}
- if (Baseline != Unknown)
+ if (Baseline)
return Baseline;
}
@@ -1924,11 +1921,11 @@ LazyValueInfo::Tristate LazyValueInfo::getPredicateAt(CmpInst::Predicate Pred,
// For predecessor edge, determine if the comparison is true or false
// on that edge. If they're all true or all false, we can conclude
// the value of the comparison in this block.
- Tristate Baseline = getPredicateOnEdge(Pred, V, C, *PI, BB, CxtI);
- if (Baseline != Unknown) {
+ Constant *Baseline = getPredicateOnEdge(Pred, V, C, *PI, BB, CxtI);
+ if (Baseline) {
// Check that all remaining incoming values match the first one.
while (++PI != PE) {
- Tristate Ret = getPredicateOnEdge(Pred, V, C, *PI, BB, CxtI);
+ Constant *Ret = getPredicateOnEdge(Pred, V, C, *PI, BB, CxtI);
if (Ret != Baseline)
break;
}
@@ -1939,13 +1936,12 @@ LazyValueInfo::Tristate LazyValueInfo::getPredicateAt(CmpInst::Predicate Pred,
}
}
- return Unknown;
+ return nullptr;
}
-LazyValueInfo::Tristate LazyValueInfo::getPredicateAt(CmpInst::Predicate Pred,
- Value *LHS, Value *RHS,
- Instruction *CxtI,
- bool UseBlockValue) {
+Constant *LazyValueInfo::getPredicateAt(CmpInst::Predicate Pred, Value *LHS,
+ Value *RHS, Instruction *CxtI,
+ bool UseBlockValue) {
if (auto *C = dyn_cast<Constant>(RHS))
return getPredicateAt(Pred, LHS, C, CxtI, UseBlockValue);
if (auto *C = dyn_cast<Constant>(LHS))
@@ -1960,19 +1956,14 @@ LazyValueInfo::Tristate LazyValueInfo::getPredicateAt(CmpInst::Predicate Pred,
ValueLatticeElement L =
getOrCreateImpl(M).getValueInBlock(LHS, CxtI->getParent(), CxtI);
if (L.isOverdefined())
- return LazyValueInfo::Unknown;
+ return nullptr;
ValueLatticeElement R =
getOrCreateImpl(M).getValueInBlock(RHS, CxtI->getParent(), CxtI);
Type *Ty = CmpInst::makeCmpResultType(LHS->getType());
- if (Constant *Res = L.getCompare(Pred, Ty, R, M->getDataLayout())) {
- if (Res->isNullValue())
- return LazyValueInfo::False;
- if (Res->isOneValue())
- return LazyValueInfo::True;
- }
+ return L.getCompare(Pred, Ty, R, M->getDataLayout());
}
- return LazyValueInfo::Unknown;
+ return nullptr;
}
void LazyValueInfo::threadEdge(BasicBlock *PredBB, BasicBlock *OldSucc,
diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
index 2bfae0ec28e17..20f5dba413212 100644
--- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
+++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
@@ -109,14 +109,8 @@ static Constant *getConstantAt(Value *V, Instruction *At, LazyValueInfo *LVI) {
if (!Op1)
return nullptr;
- LazyValueInfo::Tristate Result = LVI->getPredicateAt(
- C->getPredicate(), Op0, Op1, At, /*UseBlockValue=*/false);
- if (Result == LazyValueInfo::Unknown)
- return nullptr;
-
- return (Result == LazyValueInfo::True)
- ? ConstantInt::getTrue(C->getContext())
- : ConstantInt::getFalse(C->getContext());
+ return LVI->getPredicateAt(C->getPredicate(), Op0, Op1, At,
+ /*UseBlockValue=*/false);
}
static bool processSelect(SelectInst *S, LazyValueInfo *LVI) {
@@ -243,15 +237,17 @@ static Value *getValueOnEdge(LazyValueInfo *LVI, Value *Incoming,
// The "false" case
if (auto *C = dyn_cast<Constant>(SI->getFalseValue()))
- if (LVI->getPredicateOnEdge(ICmpInst::ICMP_EQ, SI, C, From, To, CxtI) ==
- LazyValueInfo::False)
+ if (auto *Res = dyn_cast_or_null<ConstantInt>(
+ LVI->getPredicateOnEdge(ICmpInst::ICMP_EQ, SI, C, From, To, CxtI));
+ Res && Res->isZero())
return SI->getTrueValue();
// The "true" case,
// similar to the select "false" case, but try the select "true" value
if (auto *C = dyn_cast<Constant>(SI->getTrueValue()))
- if (LVI->getPredicateOnEdge(ICmpInst::ICMP_EQ, SI, C, From, To, CxtI) ==
- LazyValueInfo::False)
+ if (auto *Res = dyn_cast_or_null<ConstantInt>(
+ LVI->getPredicateOnEdge(ICmpInst::ICMP_EQ, SI, C, From, To, CxtI));
+ Res && Res->isZero())
return SI->getFalseValue();
return nullptr;
@@ -320,16 +316,13 @@ static bool processICmp(ICmpInst *Cmp, LazyValueInfo *LVI) {
static bool constantFoldCmp(CmpInst *Cmp, LazyValueInfo *LVI) {
Value *Op0 = Cmp->getOperand(0);
Value *Op1 = Cmp->getOperand(1);
- LazyValueInfo::Tristate Result =
- LVI->getPredicateAt(Cmp->getPredicate(), Op0, Op1, Cmp,
- /*UseBlockValue=*/true);
- if (Result == LazyValueInfo::Unknown)
+ Constant *Res = LVI->getPredicateAt(Cmp->getPredicate(), Op0, Op1, Cmp,
+ /*UseBlockValue=*/true);
+ if (!Res)
return false;
++NumCmps;
- Constant *TorF =
- ConstantInt::get(CmpInst::makeCmpResultType(Op0->getType()), Result);
- Cmp->replaceAllUsesWith(TorF);
+ Cmp->replaceAllUsesWith(Res);
Cmp->eraseFromParent();
return true;
}
@@ -371,11 +364,11 @@ static bool processSwitch(SwitchInst *I, LazyValueInfo *LVI,
for (auto CI = SI->case_begin(), CE = SI->case_end(); CI != CE;) {
ConstantInt *Case = CI->getCaseValue();
- LazyValueInfo::Tristate State =
+ auto *Res = dyn_cast_or_null<ConstantInt>(
LVI->getPredicateAt(CmpInst::ICMP_EQ, Cond, Case, I,
- /* UseBlockValue */ true);
+ /* UseBlockValue */ true));
- if (State == LazyValueInfo::False) {
+ if (Res && Res->isZero()) {
// This case never fires - remove it.
BasicBlock *Succ = CI->getCaseSuccessor();
Succ->removePredecessor(BB);
@@ -392,7 +385,7 @@ static bool processSwitch(SwitchInst *I, LazyValueInfo *LVI,
DTU.applyUpdatesPermissive({{DominatorTree::Delete, BB, Succ}});
continue;
}
- if (State == LazyValueInfo::True) {
+ if (Res && Res->isOne()) {
// This case always fires. Arrange for the switch to be turned into an
// unconditional branch by replacing the switch condition with the case
// value.
@@ -716,11 +709,12 @@ static bool processCallSite(CallBase &CB, LazyValueInfo *LVI) {
// relatively expensive analysis for constants which are obviously either
// null or non-null to start with.
if (Type && !CB.paramHasAttr(ArgNo, Attribute::NonNull) &&
- !isa<Constant>(V) &&
- LVI->getPredicateAt(ICmpInst::ICMP_EQ, V,
- ConstantPointerNull::get(Type), &CB,
- /*UseBlockValue=*/false) == LazyValueInfo::False)
- ArgNos.push_back(ArgNo);
+ !isa<Constant>(V))
+ if (auto *Res = dyn_cast_or_null<ConstantInt>(LVI->getPredicateAt(
+ ICmpInst::ICMP_EQ, V, ConstantPointerNull::get(Type), &CB,
+ /*UseBlockValue=*/false));
+ Res && Res->isZero())
+ ArgNos.push_back(ArgNo);
ArgNo++;
}
diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
index 36c7a3f444212..7a0b661a07799 100644
--- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
@@ -596,11 +596,8 @@ bool JumpThreadingPass::computeValueKnownInPredecessorsImpl(
CmpInst::Predicate Pred;
Value *Val;
Constant *Cst;
- if (!PredCst && match(V, m_Cmp(Pred, m_Value(Val), m_Constant(Cst)))) {
- auto Res = LVI->getPredicateOnEdge(Pred, Val, Cst, P, BB, CxtI);
- if (Res != LazyValueInfo::Unknown)
- PredCst = ConstantInt::getBool(V->getContext(), Res);
- }
+ if (!PredCst && match(V, m_Cmp(Pred, m_Value(Val), m_Constant(Cst))))
+ PredCst = LVI->getPredicateOnEdge(Pred, Val, Cst, P, BB, CxtI);
if (Constant *KC = getKnownConstant(PredCst, Preference))
Result.emplace_back(KC, P);
}
@@ -780,13 +777,8 @@ bool JumpThreadingPass::computeValueKnownInPredecessorsImpl(
if (LHSInst && LHSInst->getParent() == BB)
continue;
- LazyValueInfo::Tristate
- ResT = LVI->getPredicateOnEdge(Pred, LHS,
- cast<Constant>(RHS), PredBB, BB,
- CxtI ? CxtI : Cmp);
- if (ResT == LazyValueInfo::Unknown)
- continue;
- Res = ConstantInt::get(Type::getInt1Ty(LHS->getContext()), ResT);
+ Res = LVI->getPredicateOnEdge(Pred, LHS, cast<Constant>(RHS), PredBB,
+ BB, CxtI ? CxtI : Cmp);
}
if (Constant *KC = getKnownConstant(Res, WantInteger))
@@ -806,14 +798,10 @@ bool JumpThreadingPass::computeValueKnownInPredecessorsImpl(
for (BasicBlock *P : predecessors(BB)) {
// If the value is known by LazyValueInfo to be a constant in a
// predecessor, use that information to try to thread this block.
- LazyValueInfo::Tristate Res =
- LVI->getPredicateOnEdge(Pred, CmpLHS,
- CmpConst, P, BB, CxtI ? CxtI : Cmp);
- if (Res == LazyValueInfo::Unknown)
- continue;
-
- Constant *ResC = ConstantInt::get(CmpType, Res);
- Result.emplace_back(ResC, P);
+ Constant *Res = LVI->getPredicateOnEdge(Pred, CmpLHS, CmpConst, P, BB,
+ CxtI ? CxtI : Cmp);
+ if (Constant *KC = getKnownConstant(Res, WantInteger))
+ Result.emplace_back(KC, P);
}
return !Result.empty();
@@ -1082,11 +1070,11 @@ bool JumpThreadingPass::processBlock(BasicBlock *BB) {
// it's value at the branch instruction. We only handle comparisons
// against a constant at this time.
if (Constant *CondConst = dyn_cast<Constant>(CondCmp->getOperand(1))) {
- LazyValueInfo::Tristate Ret =
+ Constant *Res =
LVI->getPredicateAt(CondCmp->getPredicate(), CondCmp->getOperand(0),
CondConst, BB->getTerminator(),
/*UseBlockValue=*/false);
- if (Ret != LazyValueInfo::Unknown) {
+ if (Res) {
// We can safely replace *some* uses of the CondInst if it has
// exactly one value as returned by LVI. RAUW is incorrect in the
// presence of guards and assumes, that have the `Cond` as the use. This
@@ -1094,10 +1082,7 @@ bool JumpThreadingPass::processBlock(BasicBlock *BB) {
// at the end of block, but RAUW unconditionally replaces all uses
// including the guards/assumes themselves and the uses before the
// guard/assume.
- auto *CI = Ret == LazyValueInfo::True ?
- ConstantInt::getTrue(CondCmp->getType()) :
- ConstantInt::getFalse(CondCmp->getType());
- if (replaceFoldableUses(CondCmp, CI, BB))
+ if (replaceFoldableUses(CondCmp, Res, BB))
return true;
}
@@ -2891,15 +2876,13 @@ bool JumpThreadingPass::tryToUnfoldSelect(CmpInst *CondCmp, BasicBlock *BB) {
// Now check if one of the select values would allow us to constant fold the
// terminator in BB. We don't do the transform if both sides fold, those
// cases will be threaded in any case.
- LazyValueInfo::Tristate LHSFolds =
+ Constant *LHSRes =
LVI->getPredicateOnEdge(CondCmp->getPredicate(), SI->getOperand(1),
CondRHS, Pred, BB, CondCmp);
- LazyValueInfo::Tristate RHSFolds =
+ Constant *RHSRes =
LVI->getPredicateOnEdge(CondCmp->getPredicate(), SI->getOperand(2),
CondRHS, Pred, BB, CondCmp);
- if ((LHSFolds != LazyValueInfo::Unknown ||
- RHSFolds != LazyValueInfo::Unknown) &&
- LHSFolds != RHSFolds) {
+ if ((LHSRes || RHSRes) && LHSRes != RHSRes) {
unfoldSelectInstr(Pred, BB, SI, CondLHS, I);
return true;
}
More information about the llvm-commits
mailing list