[SCEV] Move canReuseInstruction() helper into SCEV (NFC)

To allow reusing it in IndVars.

(cherry picked from commit 43dd1e84df1ecdad872e1004af47b489e08fc228)
This commit is contained in:
Nikita Popov 2024-02-02 16:02:46 +01:00 committed by Tom Stellard
parent 4223b2264c
commit dc0ed54ac5
3 changed files with 70 additions and 63 deletions

View File

@ -1314,6 +1314,13 @@ public:
void getPoisonGeneratingValues(SmallPtrSetImpl<const Value *> &Result,
const SCEV *S);
/// Check whether it is poison-safe to represent the expression S using the
/// instruction I. If such a replacement is performed, the poison flags of
/// instructions in DropPoisonGeneratingInsts must be dropped.
bool canReuseInstruction(
const SCEV *S, Instruction *I,
SmallVectorImpl<Instruction *> &DropPoisonGeneratingInsts);
class FoldID {
const SCEV *Op = nullptr;
const Type *Ty = nullptr;

View File

@ -4184,6 +4184,68 @@ void ScalarEvolution::getPoisonGeneratingValues(
Result.insert(SU->getValue());
}
bool ScalarEvolution::canReuseInstruction(
const SCEV *S, Instruction *I,
SmallVectorImpl<Instruction *> &DropPoisonGeneratingInsts) {
// If the instruction cannot be poison, it's always safe to reuse.
if (programUndefinedIfPoison(I))
return true;
// Otherwise, it is possible that I is more poisonous that S. Collect the
// poison-contributors of S, and then check whether I has any additional
// poison-contributors. Poison that is contributed through poison-generating
// flags is handled by dropping those flags instead.
SmallPtrSet<const Value *, 8> PoisonVals;
getPoisonGeneratingValues(PoisonVals, S);
SmallVector<Value *> Worklist;
SmallPtrSet<Value *, 8> Visited;
Worklist.push_back(I);
while (!Worklist.empty()) {
Value *V = Worklist.pop_back_val();
if (!Visited.insert(V).second)
continue;
// Avoid walking large instruction graphs.
if (Visited.size() > 16)
return false;
// Either the value can't be poison, or the S would also be poison if it
// is.
if (PoisonVals.contains(V) || isGuaranteedNotToBePoison(V))
continue;
auto *I = dyn_cast<Instruction>(V);
if (!I)
return false;
// Disjoint or instructions are interpreted as adds by SCEV. However, we
// can't replace an arbitrary add with disjoint or, even if we drop the
// flag. We would need to convert the or into an add.
if (auto *PDI = dyn_cast<PossiblyDisjointInst>(I))
if (PDI->isDisjoint())
return false;
// FIXME: Ignore vscale, even though it technically could be poison. Do this
// because SCEV currently assumes it can't be poison. Remove this special
// case once we proper model when vscale can be poison.
if (auto *II = dyn_cast<IntrinsicInst>(I);
II && II->getIntrinsicID() == Intrinsic::vscale)
continue;
if (canCreatePoison(cast<Operator>(I), /*ConsiderFlagsAndMetadata*/ false))
return false;
// If the instruction can't create poison, we can recurse to its operands.
if (I->hasPoisonGeneratingFlagsOrMetadata())
DropPoisonGeneratingInsts.push_back(I);
for (Value *Op : I->operands())
Worklist.push_back(Op);
}
return true;
}
const SCEV *
ScalarEvolution::getSequentialMinMaxExpr(SCEVTypes Kind,
SmallVectorImpl<const SCEV *> &Ops) {

View File

@ -1366,68 +1366,6 @@ Value *SCEVExpander::expandCodeFor(const SCEV *SH, Type *Ty) {
return V;
}
static bool
canReuseInstruction(ScalarEvolution &SE, const SCEV *S, Instruction *I,
SmallVectorImpl<Instruction *> &DropPoisonGeneratingInsts) {
// If the instruction cannot be poison, it's always safe to reuse.
if (programUndefinedIfPoison(I))
return true;
// Otherwise, it is possible that I is more poisonous that S. Collect the
// poison-contributors of S, and then check whether I has any additional
// poison-contributors. Poison that is contributed through poison-generating
// flags is handled by dropping those flags instead.
SmallPtrSet<const Value *, 8> PoisonVals;
SE.getPoisonGeneratingValues(PoisonVals, S);
SmallVector<Value *> Worklist;
SmallPtrSet<Value *, 8> Visited;
Worklist.push_back(I);
while (!Worklist.empty()) {
Value *V = Worklist.pop_back_val();
if (!Visited.insert(V).second)
continue;
// Avoid walking large instruction graphs.
if (Visited.size() > 16)
return false;
// Either the value can't be poison, or the S would also be poison if it
// is.
if (PoisonVals.contains(V) || isGuaranteedNotToBePoison(V))
continue;
auto *I = dyn_cast<Instruction>(V);
if (!I)
return false;
// Disjoint or instructions are interpreted as adds by SCEV. However, we
// can't replace an arbitrary add with disjoint or, even if we drop the
// flag. We would need to convert the or into an add.
if (auto *PDI = dyn_cast<PossiblyDisjointInst>(I))
if (PDI->isDisjoint())
return false;
// FIXME: Ignore vscale, even though it technically could be poison. Do this
// because SCEV currently assumes it can't be poison. Remove this special
// case once we proper model when vscale can be poison.
if (auto *II = dyn_cast<IntrinsicInst>(I);
II && II->getIntrinsicID() == Intrinsic::vscale)
continue;
if (canCreatePoison(cast<Operator>(I), /*ConsiderFlagsAndMetadata*/ false))
return false;
// If the instruction can't create poison, we can recurse to its operands.
if (I->hasPoisonGeneratingFlagsOrMetadata())
DropPoisonGeneratingInsts.push_back(I);
for (Value *Op : I->operands())
Worklist.push_back(Op);
}
return true;
}
Value *SCEVExpander::FindValueInExprValueMap(
const SCEV *S, const Instruction *InsertPt,
SmallVectorImpl<Instruction *> &DropPoisonGeneratingInsts) {
@ -1455,7 +1393,7 @@ Value *SCEVExpander::FindValueInExprValueMap(
continue;
// Make sure reusing the instruction is poison-safe.
if (canReuseInstruction(SE, S, EntInst, DropPoisonGeneratingInsts))
if (SE.canReuseInstruction(S, EntInst, DropPoisonGeneratingInsts))
return V;
DropPoisonGeneratingInsts.clear();
}