[llvm] [VectorCombine] Support simplification to scalar store for multiple insertelt (PR #132820)
Simon Pilgrim via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 16 23:22:09 PDT 2026
================
@@ -1954,70 +1954,230 @@ static Align computeAlignmentAfterScalarization(Align VectorAlignment,
return commonAlignment(VectorAlignment, DL.getTypeStoreSize(ScalarType));
}
-// Combine patterns like:
-// %0 = load <4 x i32>, <4 x i32>* %a
-// %1 = insertelement <4 x i32> %0, i32 %b, i32 1
-// store <4 x i32> %1, <4 x i32>* %a
-// to:
-// %0 = bitcast <4 x i32>* %a to i32*
-// %1 = getelementptr inbounds i32, i32* %0, i64 0, i64 1
-// store i32 %b, i32* %1
-bool VectorCombine::foldSingleElementStore(Instruction &I) {
+/// Fold a vector store fed by a single-use insertelement chain into scalar
+/// stores.
+///
+/// Before:
+///
+/// %p --> vector load --> insert %x, lane 1 --> insert %y, lane 3
+/// |
+/// v
+/// vector store to %p
+///
+/// Vector lanes: [ 0 ] [ 1 ] [ 2 ] [ 3 ]
+/// Stored value: [ old | x | old | y ] (one vector store)
+///
+/// After:
+///
+/// +--> GEP(%p, lane 1) --> store %x
+/// %p -------------+
+/// +--> GEP(%p, lane 3) --> store %y
+///
+/// Vector lanes: [ 0 ] [ 1 ] [ 2 ] [ 3 ]
+/// Scalar stores: x y
+/// store at 1 store at 3
+///
+/// Step 1. Gate:
+/// target supports vector-element GEP addressing
+///
+/// Step 2. Trace:
+/// vector store <-- insertelement <-- ... <-- insertelement <-- load
+///
+/// Steps 3-5. Validate:
+/// reject unprofitable full overwrites; require simple accesses, a
+/// common address/block, no memory write in between, and scalarizable
+/// indices.
+bool VectorCombine::foldInsertElementsToStores(Instruction &I) {
+ // Step 1: The target must support addressing a vector element with a GEP.
if (!TTI.allowVectorElementIndexingUsingGEP())
return false;
+
auto *SI = cast<StoreInst>(&I);
if (!SI->isSimple() || !isa<VectorType>(SI->getValueOperand()->getType()))
return false;
- // TODO: Combine more complicated patterns (multiple insert) by referencing
- // TargetTransformInfo.
- Instruction *Source;
- Value *NewElement;
- Value *Idx;
- if (!match(SI->getValueOperand(),
- m_InsertElt(m_Instruction(Source), m_Value(NewElement),
- m_Value(Idx))))
- return false;
-
- if (auto *Load = dyn_cast<LoadInst>(Source)) {
- auto VecTy = cast<VectorType>(SI->getValueOperand()->getType());
- Value *SrcAddr = Load->getPointerOperand()->stripPointerCasts();
- // Don't optimize for atomic/volatile load or store. Ensure memory is not
- // modified between, vector type matches store size, and index is inbounds.
- if (!Load->isSimple() || Load->getParent() != SI->getParent() ||
- !DL->typeSizeEqualsStoreSize(Load->getType()->getScalarType()) ||
- SrcAddr != SI->getPointerOperand()->stripPointerCasts())
- return false;
+ // Step 2: Collect a single-use insertelement chain, starting at the vector
+ // store and walking back to the candidate load.
+ Value *Source = SI->getValueOperand();
+ SmallVector<std::pair<Value *, Value *>, 4> InsertElements;
+ Value *Base = Source;
+ while (auto *Insert = dyn_cast<InsertElementInst>(Base)) {
+ if (!Insert->hasOneUse())
+ break;
+ Value *InsertVal = Insert->getOperand(1);
+ Value *Idx = Insert->getOperand(2);
+ InsertElements.push_back({InsertVal, Idx});
+ Base = Insert->getOperand(0);
+ }
- if (isMemModifiedBetween(Load->getIterator(), SI->getIterator(),
- MemoryLocation::get(SI), AA))
- return false;
+ if (InsertElements.empty())
+ return false;
+
+ // The backwards walk collected the inserts in reverse program order. Restore
+ // it now so later scalar stores preserve writes to duplicate/equal indices.
+ std::reverse(InsertElements.begin(), InsertElements.end());
+ auto *Load = dyn_cast<LoadInst>(Base);
+ if (!Load)
+ return false;
+ auto VecTy = cast<VectorType>(SI->getValueOperand()->getType());
----------------
RKSimon wrote:
```suggestion
auto *VecTy = cast<VectorType>(SI->getValueOperand()->getType());
```
https://github.com/llvm/llvm-project/pull/132820
More information about the llvm-commits
mailing list