[llvm] Vectorize ld st nop (PR #223412)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 15 02:02:45 PDT 2026


================
@@ -3996,6 +3996,177 @@ void VPlanTransforms::sinkPredicatedStores(VPlan &Plan,
   }
 }
 
+/// Return the recipe defining \p V if it is an unpredicated GEP with an
+/// underlying GetElementPtrInst, i.e. a VPWidenGEPRecipe or a replicating GEP.
+static VPRecipeWithIRFlags *getGEPRecipe(VPValue *V) {
+  auto *R = dyn_cast_or_null<VPRecipeWithIRFlags>(V->getDefiningRecipe());
+  if (!R)
+    return nullptr;
+  if (isa<VPWidenGEPRecipe>(R))
+    return R;
+  auto *RepR = dyn_cast<VPReplicateRecipe>(R);
+  if (RepR && RepR->getOpcode() == Instruction::GetElementPtr &&
+      !RepR->isPredicated())
+    return RepR;
+  return nullptr;
+}
+
+/// Return true if \p A and \p B are GEPs with loop-invariant base pointers
+/// that only differ in their base pointer, i.e. they compute addresses with the
+/// same stride.
+static bool areGEPsWithSameIndices(VPRecipeWithIRFlags *A,
+                                   VPRecipeWithIRFlags *B) {
+  auto *GEPA = cast<GetElementPtrInst>(A->getUnderlyingInstr());
+  auto *GEPB = cast<GetElementPtrInst>(B->getUnderlyingInstr());
+  if (GEPA->getSourceElementType() != GEPB->getSourceElementType() ||
+      A->getNumOperands() != B->getNumOperands())
+    return false;
+  if (!A->getOperand(0)->isDefinedOutsideLoopRegions() ||
+      !B->getOperand(0)->isDefinedOutsideLoopRegions())
+    return false;
+  return std::equal(std::next(A->op_begin()), A->op_end(),
+                    std::next(B->op_begin()));
+}
+
+void VPlanTransforms::convertSelectPtrLoadStoreToMasked(VPlan &Plan) {
+  if (Plan.hasScalarVFOnly())
+    return;
+
+  // Look for
+  //   %v = load (select %c, %p, %q)
+  //   store %v, %q
+  // where %p and %q are consecutive addresses with the same stride. For lanes
+  // with %c == false, the store writes back the value just loaded from %q,
+  // which is a no-op. Hence, the pair is equivalent to a masked load from %p
+  // and a masked store to %q, both using %c as mask.
+
+  VPRegionBlock *LoopRegion = Plan.getVectorLoopRegion();
+  for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(
+           vp_depth_first_shallow(LoopRegion->getEntry()))) {
+    for (VPRecipeBase &R : make_early_inc_range(*VPBB)) {
+      auto *StoreR = dyn_cast<VPWidenStoreRecipe>(&R);
+      if (!StoreR || !StoreR->isConsecutive()) // Should also check if reversed?
----------------
dnsampaio wrote:

I believe it should be required to test that both load and store pointers iterate on the same direction, but I'm not exactly sure how.

https://github.com/llvm/llvm-project/pull/223412


More information about the llvm-commits mailing list