================
@@ -7309,6 +7331,231 @@ void VPlanTransforms::makeMemOpWideningDecisions(VPlan
&Plan, VFRange &Range,
});
}
+void VPlanTransforms::multiversionForUnitStridedMemOps(
+ VPlan &Plan, VPCostContext &CostCtx, VPRecipeBuilder &RecipeBuilder,
+ VFRange &Range, SmallVectorImpl<VPInstruction *> &MemOps) {
+ if (CostCtx.L->getHeader()->getParent()->hasOptSize())
+ return;
+ SmallVector<VPInstruction *> RemainingOps;
+
+ ScalarEvolution *SE = CostCtx.PSE.getSE();
+
+ PredicatedScalarEvolution StrideMVPSE(*SE, const_cast<Loop &>(*CostCtx.L));
+
+ SCEVUnionPredicate StridePredicates({}, *SE);
+
+ // Use `for_each` so that we could do `return Skip();`.
+ for_each(MemOps, [&](VPInstruction *VPI) {
+ auto Skip = [&]() { RemainingOps.push_back(VPI); };
+ if (RecipeBuilder.isConsecutiveWithoutVPlanBasedStrideSpeculation(VPI))
+ return Skip();
+ auto *PtrOp = VPI->getOpcode() == Instruction::Load ? VPI->getOperand(0)
+ : VPI->getOperand(1);
+
+ const SCEV *PtrSCEV =
+ vputils::getSCEVExprForVPValue(PtrOp, CostCtx.PSE, CostCtx.L);
+ const SCEV *Start = nullptr;
+ const SCEV *Stride = nullptr;
+
+ if (!match(PtrSCEV, m_scev_AffineAddRec(m_SCEV(Start), m_SCEV(Stride),
+ m_SpecificLoop(CostCtx.L)))) {
+ return Skip();
+ }
+
+ Type *ScalarTy = VPI->getOpcode() == Instruction::Load
+ ? VPI->getScalarType()
+ : VPI->getOperand(0)->getScalarType();
+
+ if (VPI->getMask()) {
+ auto &TTI = CostCtx.TTI;
+ Instruction *I = VPI->getUnderlyingInstr();
+ unsigned AS = getLoadStoreAddressSpace(I);
+ const Align Alignment = getLoadStoreAlignment(I);
+ if (!LoopVectorizationPlanner::getDecisionAndClampRange(
+ [&](ElementCount VF) -> bool {
+ Type *VTy = VectorType::get(ScalarTy, VF);
+ return VPI->getOpcode() == Instruction::Load
+ ? (TTI.isLegalMaskedLoad(VTy, Alignment, AS) ||
+ TTI.isLegalMaskedGather(VTy, Alignment))
+ : (TTI.isLegalMaskedStore(VTy, Alignment, AS) ||
+ TTI.isLegalMaskedScatter(VTy, Alignment));
+ },
+ Range))
+ return Skip();
+ }
+
+ const auto *TypeSize = cast<SCEVConstant>(SE->getSizeOfExpr(
+ Stride->getType(), SE->getDataLayout().getTypeStoreSize(ScalarTy)));
+
+ auto ReplaceWithUnitStrided = [&]() {
+ VPBuilder Builder(VPI);
+ VPValue *StrideInBytes = Plan.getOrAddLiveIn(TypeSize->getValue());
+ auto *VecPtr =
+ new VPVectorPointerRecipe(PtrOp, ScalarTy, StrideInBytes,
+ GEPNoWrapFlags::none(),
VPI->getDebugLoc());
+ Builder.insert(VecPtr);
+ if (VPI->getOpcode() == Instruction::Load) {
+ auto *WideLoad = new VPWidenLoadRecipe(
+ cast<LoadInst>(*VPI->getUnderlyingInstr()), VecPtr, VPI->getMask(),
+ true, *VPI, VPI->getDebugLoc());
+ Builder.insert(WideLoad);
+ VPI->replaceAllUsesWith(WideLoad);
+ } else {
+ auto *WideStore = new VPWidenStoreRecipe(
+ cast<StoreInst>(*VPI->getUnderlyingInstr()), VecPtr,
+ VPI->getOperand(0), VPI->getMask(), true, *VPI,
VPI->getDebugLoc());
+ Builder.insert(WideStore);
+ }
+ VPI->eraseFromParent();
+ };
+
+ if (isa<SCEVConstant>(Stride)) {
+ if (Stride != TypeSize)
+ return Skip();
+
+ // Earlier MV helped with this memory operation too.
+ ReplaceWithUnitStrided();
+ return;
+ }
+
+ const SCEVConstant *StrideConstantMultiplier;
+ const SCEV *StrideNonConstantMultiplier;
+
+ const SCEV *ToMultiVersion = Stride;
+ const SCEV *MVConst = TypeSize;
+ if (match(Stride, m_scev_c_Mul(m_SCEVConstant(StrideConstantMultiplier),
+ m_SCEV(StrideNonConstantMultiplier)))) {
+ if (TypeSize != StrideConstantMultiplier) {
+ // TODO: Support `TypeSize = N * StrideCosntantMultiplier`,
+ // including negative `N`. For now, only process when they're equal,
+ // which matches the usefull part of the legacy behavior that
+ // multiversiones GEP index for stride one.
+ return Skip();
+ }
+ ToMultiVersion = StrideNonConstantMultiplier;
+ MVConst = SE->getOne(ToMultiVersion->getType());
+ } else if (!TypeSize->isOne()) {
+ // Likewise - try to match legacy behavior.
+ return Skip();
+ }
+
+ while (auto *C = dyn_cast<SCEVIntegralCastExpr>(ToMultiVersion)) {
+ ToMultiVersion = C->getOperand();
+ MVConst = SE->getTruncateOrSignExtend(MVConst,
ToMultiVersion->getType());
+ }
+
+ if (!isa<SCEVUnknown>(ToMultiVersion)) {
+ // Match legacy behavior.
+ // If/when changed, make sure that explicit poison/undef in the defining
+ // expression doesn't cause any issues.
+ return Skip();
+ }
+
+ Value *StrideVal = cast<SCEVUnknown>(ToMultiVersion)->getValue();
+
+ if (isa<UndefValue>(StrideVal))
+ return Skip();
+
+ const SCEVPredicate *NewPred =
+ SE->getComparePredicate(CmpInst::ICMP_EQ, ToMultiVersion, MVConst);
+
+ // Check if new predicate implies that backedge is never taken. If so,
there
+ // is no reason to multiversion for it.
+ SmallVector<const SCEVPredicate *> Preds{&CostCtx.PSE.getPredicate(),
+ &StridePredicates, NewPred};
+ auto *PredicatedMaxBTC = SE->rewriteUsingPredicate(
+ SE->getSymbolicMaxBackedgeTakenCount(CostCtx.L), CostCtx.L,
+ StridePredicates.getUnionWith(NewPred, *SE)
+ .getUnionWith(&CostCtx.PSE.getPredicate(), *SE));
+
+ if (LoopVectorizationPlanner::getDecisionAndClampRange(
+ [&](ElementCount VF) {
+ return SE->isKnownPositive(SE->getMinusSCEV(
+ SE->getConstant(PredicatedMaxBTC->getType(),
+ VF.isScalable() ? 1 : VF.getFixedValue() -
1),
+ PredicatedMaxBTC));
+ },
+ Range))
+ return Skip();
+
+ StridePredicates = StridePredicates.getUnionWith(NewPred, *SE);
+
+ auto ReplaceMVUses = [&](Value *V) {
+ VPValue *From = Plan.getLiveIn(V);
+ if (!From)
+ return;
+ VPValue *To = Plan.getConstantInt(
+ From->getScalarType(),
+ cast<SCEVConstant>(MVConst)->getAPInt().getLimitedValue());
----------------
eas wrote:
I'm not matching it, `MVConst` is created as a constant in the code above
(except `SE::getOne` hides that return type behind just `const SCEV *`)
https://github.com/llvm/llvm-project/pull/182595
_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits