[llvm] [VectorCombine][AMDGPU] Shrink demanded vector loads (PR #202501)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 8 20:54:50 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: tianhbai

<details>
<summary>Changes</summary>

Teach `VectorCombine` to narrow vector loads feeding constant-index `extractelement` users when demanded bits show that only part of the loaded value is used.

The transform builds candidate narrower load plans at whole-element granularity for floating-point vectors and at progressively smaller byte-sized chunks for integer vectors, then asks TTI whether the complete replacement plan is profitable. AMDGPU opts in for simple global/constant/flat/local/private loads whose replacement accesses map to supported `32/64/96/128`-bit load widths; local/private plans are also checked with `TargetLowering::allowsMemoryAccess`.

This turns cases such as a `<6 x i64>` load with unused high dwords into three `<3 x i32>` loads, allowing AMDGPU instruction selection to emit `global_load_b96` instead of `global_load_b128`.


---

Patch is 26.16 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/202501.diff


7 Files Affected:

- (modified) llvm/include/llvm/Analysis/TargetTransformInfo.h (+8) 
- (modified) llvm/include/llvm/Analysis/TargetTransformInfoImpl.h (+7) 
- (modified) llvm/lib/Analysis/TargetTransformInfo.cpp (+7) 
- (modified) llvm/lib/Target/AMDGPU/AMDGPUTargetTransformInfo.cpp (+68) 
- (modified) llvm/lib/Target/AMDGPU/AMDGPUTargetTransformInfo.h (+4) 
- (modified) llvm/lib/Transforms/Vectorize/VectorCombine.cpp (+236-4) 
- (added) llvm/test/Transforms/VectorCombine/AMDGPU/shrink-demanded-load.ll (+206) 


``````````diff
diff --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h b/llvm/include/llvm/Analysis/TargetTransformInfo.h
index 7d58473b81265..fc6dd0088c94a 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfo.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h
@@ -1779,6 +1779,14 @@ class TargetTransformInfo {
   getMemIntrinsicInstrCost(const MemIntrinsicCostAttributes &MICA,
                            TTI::TargetCostKind CostKind) const;
 
+  /// Return true if replacing \p Load with the narrower load plan is profitable.
+  /// \p NewLoadTys and \p NewLoadOffsets are parallel arrays; offsets are bytes
+  /// from \p Load's pointer operand. Targets should evaluate the plan as a whole.
+  LLVM_ABI bool
+  shouldReduceLoadWidth(LoadInst *Load, ArrayRef<Type *> NewLoadTys,
+                        ArrayRef<uint64_t> NewLoadOffsets,
+                        TargetCostKind CostKind = TCK_RecipThroughput) const;
+
   /// \returns The cost of Call instructions.
   LLVM_ABI InstructionCost getCallInstrCost(
       Function *F, Type *RetTy, ArrayRef<Type *> Tys,
diff --git a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
index 89fd4a1e7628e..dffcc18ef8714 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
@@ -981,6 +981,13 @@ class LLVM_ABI TargetTransformInfoImplBase {
     return InstructionCost::getInvalid();
   }
 
+  virtual bool shouldReduceLoadWidth(LoadInst *Load,
+                                     ArrayRef<Type *> NewLoadTys,
+                                     ArrayRef<uint64_t> NewLoadOffsets,
+                                     TTI::TargetCostKind CostKind) const {
+    return false;
+  }
+
   virtual InstructionCost getCallInstrCost(Function *F, Type *RetTy,
                                            ArrayRef<Type *> Tys,
                                            TTI::TargetCostKind CostKind) const {
diff --git a/llvm/lib/Analysis/TargetTransformInfo.cpp b/llvm/lib/Analysis/TargetTransformInfo.cpp
index 856950ccae595..10494d7e166e9 100644
--- a/llvm/lib/Analysis/TargetTransformInfo.cpp
+++ b/llvm/lib/Analysis/TargetTransformInfo.cpp
@@ -1277,6 +1277,13 @@ InstructionCost TargetTransformInfo::getMemIntrinsicInstrCost(
   return Cost;
 }
 
+bool TargetTransformInfo::shouldReduceLoadWidth(
+    LoadInst *Load, ArrayRef<Type *> NewLoadTys,
+    ArrayRef<uint64_t> NewLoadOffsets, TargetCostKind CostKind) const {
+  return TTIImpl->shouldReduceLoadWidth(Load, NewLoadTys, NewLoadOffsets,
+                                        CostKind);
+}
+
 InstructionCost
 TargetTransformInfo::getCallInstrCost(Function *F, Type *RetTy,
                                       ArrayRef<Type *> Tys,
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUTargetTransformInfo.cpp b/llvm/lib/Target/AMDGPU/AMDGPUTargetTransformInfo.cpp
index 7631bb2dc6828..377e9970a4c76 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUTargetTransformInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUTargetTransformInfo.cpp
@@ -1792,6 +1792,74 @@ GCNTTIImpl::fpenvIEEEMode(const Instruction &I) const {
                                                : KnownIEEEMode::On;
 }
 
+bool GCNTTIImpl::shouldReduceLoadWidth(LoadInst *Load,
+                                       ArrayRef<Type *> NewLoadTys,
+                                       ArrayRef<uint64_t> NewLoadOffsets,
+                                       TTI::TargetCostKind CostKind) const {
+  if (!Load || !Load->isSimple() || NewLoadTys.empty() ||
+      NewLoadTys.size() != NewLoadOffsets.size() ||
+      CostKind != TTI::TCK_RecipThroughput)
+    return false;
+
+  unsigned AddrSpace = Load->getPointerAddressSpace();
+  switch (AddrSpace) {
+  case AMDGPUAS::GLOBAL_ADDRESS:
+  case AMDGPUAS::CONSTANT_ADDRESS:
+  case AMDGPUAS::CONSTANT_ADDRESS_32BIT:
+  case AMDGPUAS::FLAT_ADDRESS:
+  case AMDGPUAS::LOCAL_ADDRESS:
+  case AMDGPUAS::PRIVATE_ADDRESS:
+    break;
+  default:
+    return false;
+  }
+
+  TypeSize OldSize = DL.getTypeStoreSize(Load->getType());
+  if (OldSize.isScalable())
+    return false;
+  uint64_t OldBytes = OldSize.getFixedValue();
+  uint64_t NewTotalBytes = 0;
+
+  constexpr uint64_t MaxSingleLoadBytes = 16;
+  // Do not reduce memory width if that would increase the number of memory
+  // operations compared with splitting the old load into the widest single
+  // memory operation this hook opts into.
+  unsigned OldNumLoadChunks = divideCeil(OldBytes, MaxSingleLoadBytes);
+  if (NewLoadTys.size() > OldNumLoadChunks)
+    return false;
+
+  for (auto [NewLoadTy, NewLoadOffset] : zip(NewLoadTys, NewLoadOffsets)) {
+    if (!NewLoadTy)
+      return false;
+
+    TypeSize NewSize = DL.getTypeStoreSize(NewLoadTy);
+    if (NewSize.isScalable())
+      return false;
+
+    uint64_t NewBytes = NewSize.getFixedValue();
+    uint64_t NewBits = NewBytes * 8;
+    Align NewAlign = commonAlignment(Load->getAlign(), NewLoadOffset);
+    if (NewLoadOffset > OldBytes || NewBytes > OldBytes - NewLoadOffset ||
+        NewAlign < Align(4) ||
+        (NewBits != 32 && NewBits != 64 && NewBits != 96 && NewBits != 128))
+      return false;
+
+    if (AddrSpace == AMDGPUAS::LOCAL_ADDRESS ||
+        AddrSpace == AMDGPUAS::PRIVATE_ADDRESS) {
+      EVT NewVT = TLI->getValueType(DL, NewLoadTy);
+      if (!TLI->allowsMemoryAccess(Load->getContext(), DL, NewVT, AddrSpace,
+                                   NewAlign))
+        return false;
+    }
+
+    if (NewBytes >= OldBytes - NewTotalBytes)
+      return false;
+    NewTotalBytes += NewBytes;
+  }
+
+  return true;
+}
+
 InstructionCost GCNTTIImpl::getMemoryOpCost(unsigned Opcode, Type *Src,
                                             Align Alignment,
                                             unsigned AddressSpace,
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUTargetTransformInfo.h b/llvm/lib/Target/AMDGPU/AMDGPUTargetTransformInfo.h
index 4a239f4d6983d..1cb216a33b6e7 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUTargetTransformInfo.h
+++ b/llvm/lib/Target/AMDGPU/AMDGPUTargetTransformInfo.h
@@ -317,6 +317,10 @@ class GCNTTIImpl final : public BasicTTIImplBase<GCNTTIImpl> {
       TTI::OperandValueInfo OpInfo = {TTI::OK_AnyValue, TTI::OP_None},
       const Instruction *I = nullptr) const override;
 
+  bool shouldReduceLoadWidth(LoadInst *Load, ArrayRef<Type *> NewLoadTys,
+                             ArrayRef<uint64_t> NewLoadOffsets,
+                             TTI::TargetCostKind CostKind) const override;
+
   /// When counting parts on AMD GPUs, account for i8s being grouped
   /// together under a single i32 value. Otherwise fall back to base
   /// implementation.
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 99e45bdc8ee21..6a38bf75b0f5d 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -20,6 +20,7 @@
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/AssumptionCache.h"
 #include "llvm/Analysis/BasicAliasAnalysis.h"
+#include "llvm/Analysis/DemandedBits.h"
 #include "llvm/Analysis/GlobalsModRef.h"
 #include "llvm/Analysis/InstSimplifyFolder.h"
 #include "llvm/Analysis/Loads.h"
@@ -76,10 +77,10 @@ class VectorCombine {
 public:
   VectorCombine(Function &F, const TargetTransformInfo &TTI,
                 const DominatorTree &DT, AAResults &AA, AssumptionCache &AC,
-                const DataLayout *DL, TTI::TargetCostKind CostKind,
-                bool TryEarlyFoldsOnly)
+                DemandedBits *DB, const DataLayout *DL,
+                TTI::TargetCostKind CostKind, bool TryEarlyFoldsOnly)
       : F(F), Builder(F.getContext(), InstSimplifyFolder(*DL)), TTI(TTI),
-        DT(DT), AA(AA), DL(DL), CostKind(CostKind),
+        DT(DT), AA(AA), DB(DB), DL(DL), CostKind(CostKind),
         SQ(*DL, /*TLI=*/nullptr, &DT, &AC),
         TryEarlyFoldsOnly(TryEarlyFoldsOnly) {}
 
@@ -91,6 +92,7 @@ class VectorCombine {
   const TargetTransformInfo &TTI;
   const DominatorTree &DT;
   AAResults &AA;
+  DemandedBits *DB;
   const DataLayout *DL;
   TTI::TargetCostKind CostKind;
   const SimplifyQuery SQ;
@@ -133,6 +135,7 @@ class VectorCombine {
   bool foldBinopOfReductions(Instruction &I);
   bool foldSingleElementStore(Instruction &I);
   bool scalarizeLoad(Instruction &I);
+  bool shrinkLoadForExtracts(Instruction &I);
   bool scalarizeLoadExtract(LoadInst *LI, VectorType *VecTy, Value *Ptr);
   bool scalarizeLoadBitcast(LoadInst *LI, VectorType *VecTy, Value *Ptr);
   bool scalarizeExtExtract(Instruction &I);
@@ -2157,6 +2160,231 @@ bool VectorCombine::scalarizeLoadExtract(LoadInst *LI, VectorType *VecTy,
   return true;
 }
 
+/// Try to shrink vector loads feeding extractelement instructions when some
+/// of the loaded lanes are not demanded.
+bool VectorCombine::shrinkLoadForExtracts(Instruction &I) {
+  if (!DB)
+    return false;
+
+  auto *OldLoad = dyn_cast<LoadInst>(&I);
+  if (!OldLoad || !OldLoad->isSimple())
+    return false;
+
+  auto *OldLoadTy = dyn_cast<FixedVectorType>(OldLoad->getType());
+  if (!OldLoadTy)
+    return false;
+
+  Type *OldEltTy = OldLoadTy->getElementType();
+  bool IsIntegerElement = OldEltTy->isIntegerTy();
+  bool IsFloatingPointElement = OldEltTy->isFloatingPointTy();
+  if (!IsIntegerElement && !IsFloatingPointElement)
+    return false;
+
+  constexpr unsigned ByteWidth = 8;
+  TypeSize OldEltSizeInBits = DL->getTypeSizeInBits(OldEltTy);
+  if (OldEltSizeInBits.isScalable())
+    return false;
+  unsigned OldEltSize = OldEltSizeInBits.getFixedValue();
+  if (OldEltSize < ByteWidth || OldEltSize % ByteWidth != 0)
+    return false;
+
+  unsigned NumElts = OldLoadTy->getNumElements();
+  SmallVector<ExtractElementInst *, 8> Extracts;
+  SmallVector<APInt, 8> ExtractDemandedBits;
+
+  for (User *U : OldLoad->users()) {
+    auto *Ext = dyn_cast<ExtractElementInst>(U);
+    if (!Ext || Ext->use_empty())
+      return false;
+
+    auto *Idx = dyn_cast<ConstantInt>(Ext->getIndexOperand());
+    if (!Idx || Idx->getValue().uge(NumElts))
+      return false;
+
+    Extracts.push_back(Ext);
+    ExtractDemandedBits.push_back(DB->getDemandedBits(Ext));
+  }
+
+  TypeSize OldLoadSize = DL->getTypeStoreSize(OldLoadTy);
+  if (OldLoadSize.isScalable())
+    return false;
+
+  struct ChunkRun {
+    unsigned Start;
+    unsigned Length;
+  };
+  SmallVector<ChunkRun, 4> ChunkRuns;
+  SmallVector<Type *, 4> NewLoadTys;
+  SmallVector<uint64_t, 4> NewLoadOffsets;
+  Type *ChunkTy = nullptr;
+  unsigned ChunkBits = 0;
+  unsigned ChunksPerElt = 0;
+  unsigned ChunkBytes = 0;
+
+  // Try progressively smaller byte-sized power-of-two chunks. This keeps the
+  // transform target independent while allowing TTI to opt in to the chunk/load
+  // widths that are profitable for a target.
+  auto TryBuildShrinkLoadPlan = [&](unsigned CandidateChunkBits,
+                                    Type *CandidateChunkTy) {
+    if (OldEltSize % CandidateChunkBits != 0)
+      return false;
+    // Sub-element chunk numbering maps directly to byte offsets below, so only
+    // use it for little-endian targets. Whole-element shrink is endian-neutral.
+    if (CandidateChunkBits != OldEltSize && !DL->isLittleEndian())
+      return false;
+
+    unsigned CandidateChunksPerElt = OldEltSize / CandidateChunkBits;
+    unsigned NumChunks = NumElts * CandidateChunksPerElt;
+    APInt DemandedChunks = APInt::getZero(NumChunks);
+
+    for (auto [Ext, DemandedBits] : zip(Extracts, ExtractDemandedBits)) {
+      auto *Idx = cast<ConstantInt>(Ext->getIndexOperand());
+      unsigned EltIdx = Idx->getZExtValue();
+      for (unsigned Chunk = 0; Chunk != CandidateChunksPerElt; ++Chunk) {
+        if (!DemandedBits
+                 .extractBits(CandidateChunkBits, Chunk * CandidateChunkBits)
+                 .isZero())
+          DemandedChunks.setBit(EltIdx * CandidateChunksPerElt + Chunk);
+      }
+    }
+
+    if (DemandedChunks.isAllOnes())
+      return false;
+
+    SmallVector<ChunkRun, 4> CandidateChunkRuns;
+    for (unsigned I = 0; I != NumChunks;) {
+      if (!DemandedChunks[I]) {
+        ++I;
+        continue;
+      }
+
+      unsigned Start = I;
+      do {
+        ++I;
+      } while (I != NumChunks && DemandedChunks[I]);
+      CandidateChunkRuns.push_back({Start, I - Start});
+    }
+
+    if (CandidateChunkRuns.empty())
+      return false;
+
+    unsigned CandidateChunkBytes = CandidateChunkBits / ByteWidth;
+    SmallVector<Type *, 4> CandidateLoadTys;
+    SmallVector<uint64_t, 4> CandidateLoadOffsets;
+    TypeSize NewSize = TypeSize::getFixed(0);
+    for (const ChunkRun &Run : CandidateChunkRuns) {
+      Type *NewLoadTy = FixedVectorType::get(CandidateChunkTy, Run.Length);
+      CandidateLoadTys.push_back(NewLoadTy);
+      CandidateLoadOffsets.push_back(Run.Start * CandidateChunkBytes);
+      NewSize += DL->getTypeStoreSize(NewLoadTy);
+    }
+
+    if (NewSize >= OldLoadSize)
+      return false;
+
+    if (!TTI.shouldReduceLoadWidth(OldLoad, CandidateLoadTys,
+                                   CandidateLoadOffsets, CostKind))
+      return false;
+
+    ChunkBits = CandidateChunkBits;
+    ChunksPerElt = CandidateChunksPerElt;
+    ChunkBytes = CandidateChunkBytes;
+    ChunkTy = CandidateChunkTy;
+    ChunkRuns = std::move(CandidateChunkRuns);
+    NewLoadTys = std::move(CandidateLoadTys);
+    NewLoadOffsets = std::move(CandidateLoadOffsets);
+    return true;
+  };
+
+  bool FoundPlan = false;
+  if (IsIntegerElement) {
+    for (unsigned CandidateChunkBits = 1u << Log2_32(OldEltSize);
+         CandidateChunkBits >= ByteWidth; CandidateChunkBits >>= 1) {
+      if (TryBuildShrinkLoadPlan(
+              CandidateChunkBits,
+              IntegerType::get(I.getContext(), CandidateChunkBits))) {
+        FoundPlan = true;
+        break;
+      }
+      if (CandidateChunkBits == ByteWidth)
+        break;
+    }
+  } else {
+    // Floating-point elements cannot use DemandedBits to look inside the
+    // element, but they can still shrink at whole-element granularity.
+    FoundPlan = TryBuildShrinkLoadPlan(OldEltSize, OldEltTy);
+  }
+
+  if (!FoundPlan)
+    return false;
+
+  LLVM_DEBUG(dbgs() << "Found a vector load with undemanded elements: " << I
+                    << "\n");
+
+  Builder.SetInsertPoint(OldLoad);
+  Builder.SetCurrentDebugLocation(OldLoad->getDebugLoc());
+
+  SmallVector<LoadInst *, 4> NewLoads;
+  SmallVector<unsigned, 4> RunStarts;
+  for (auto [Idx, Run] : enumerate(ChunkRuns)) {
+    unsigned Start = Run.Start;
+    Type *NewLoadTy = NewLoadTys[Idx];
+    uint64_t NewLoadOffset = NewLoadOffsets[Idx];
+    Value *NewPtr =
+        Builder.CreateGEP(Builder.getInt8Ty(), OldLoad->getPointerOperand(),
+                          Builder.getInt64(NewLoadOffset));
+    auto *NewLoad = cast<LoadInst>(Builder.CreateAlignedLoad(
+        NewLoadTy, NewPtr,
+        commonAlignment(OldLoad->getAlign(), NewLoadOffset)));
+    copyMetadataForLoad(*NewLoad, *OldLoad);
+    AAMDNodes OldAAMD = OldLoad->getAAMetadata();
+    NewLoad->setAAMetadata(
+        OldAAMD.adjustForAccess(NewLoadOffset, NewLoadTy, *DL));
+    NewLoads.push_back(NewLoad);
+    RunStarts.push_back(Start);
+  }
+
+  auto ExtractSubElt = [&](unsigned SubElt, Instruction *InsertPt) -> Value * {
+    for (auto [I, NewLoad] : enumerate(NewLoads)) {
+      unsigned Start = RunStarts[I];
+      unsigned Length =
+          cast<FixedVectorType>(NewLoad->getType())->getNumElements();
+      if (SubElt < Start || SubElt >= Start + Length)
+        continue;
+
+      Builder.SetInsertPoint(InsertPt);
+      return Builder.CreateExtractElement(NewLoad,
+                                          Builder.getInt32(SubElt - Start));
+    }
+    return Constant::getNullValue(ChunkTy);
+  };
+
+  for (ExtractElementInst *Ext : Extracts) {
+    auto *Idx = cast<ConstantInt>(Ext->getIndexOperand());
+    unsigned EltIdx = Idx->getZExtValue();
+
+    Builder.SetInsertPoint(Ext);
+    Builder.SetCurrentDebugLocation(Ext->getDebugLoc());
+
+    Value *NewValue = nullptr;
+    for (unsigned Chunk = 0; Chunk != ChunksPerElt; ++Chunk) {
+      unsigned GlobalChunk = EltIdx * ChunksPerElt + Chunk;
+      Value *Part = ExtractSubElt(GlobalChunk, Ext);
+      Value *ExtPart = Part->getType() == OldEltTy
+                           ? Part
+                           : Builder.CreateZExt(Part, OldEltTy);
+      if (Chunk != 0)
+        ExtPart = Builder.CreateShl(ExtPart, Chunk * ChunkBits);
+      NewValue = NewValue ? Builder.CreateOr(NewValue, ExtPart) : ExtPart;
+    }
+
+    replaceValue(*Ext, *NewValue, false);
+  }
+
+  Worklist.push(OldLoad);
+  return true;
+}
+
 /// Try to scalarize vector loads feeding bitcast instructions.
 bool VectorCombine::scalarizeLoadBitcast(LoadInst *LI, VectorType *VecTy,
                                          Value *Ptr) {
@@ -6277,6 +6505,8 @@ bool VectorCombine::run() {
           return true;
         break;
       case Instruction::Load:
+        if (shrinkLoadForExtracts(I))
+          return true;
         if (shrinkLoadForShuffles(I))
           return true;
         break;
@@ -6391,8 +6621,10 @@ PreservedAnalyses VectorCombinePass::run(Function &F,
   TargetTransformInfo &TTI = FAM.getResult<TargetIRAnalysis>(F);
   DominatorTree &DT = FAM.getResult<DominatorTreeAnalysis>(F);
   AAResults &AA = FAM.getResult<AAManager>(F);
+  DemandedBits *DB =
+      TryEarlyFoldsOnly ? nullptr : &FAM.getResult<DemandedBitsAnalysis>(F);
   const DataLayout *DL = &F.getDataLayout();
-  VectorCombine Combiner(F, TTI, DT, AA, AC, DL, TTI::TCK_RecipThroughput,
+  VectorCombine Combiner(F, TTI, DT, AA, AC, DB, DL, TTI::TCK_RecipThroughput,
                          TryEarlyFoldsOnly);
   if (!Combiner.run())
     return PreservedAnalyses::all();
diff --git a/llvm/test/Transforms/VectorCombine/AMDGPU/shrink-demanded-load.ll b/llvm/test/Transforms/VectorCombine/AMDGPU/shrink-demanded-load.ll
new file mode 100644
index 0000000000000..7b8bea5a356d1
--- /dev/null
+++ b/llvm/test/Transforms/VectorCombine/AMDGPU/shrink-demanded-load.ll
@@ -0,0 +1,206 @@
+; RUN: opt < %s -passes=vector-combine -S -mtriple=amdgcn-amd-amdpal -mcpu=gfx1201 | FileCheck %s
+
+target triple = "amdgcn-amd-amdpal"
+
+define void @shrink_i32_load_prefix(ptr addrspace(1) %out,
+                                    ptr addrspace(4) %p) {
+; CHECK-LABEL: @shrink_i32_load_prefix(
+; CHECK-NOT: load <4 x i32>
+; CHECK: load <3 x i32>, ptr addrspace(4) %p, align 8
+entry:
+  %v = load <4 x i32>, ptr addrspace(4) %p, align 8
+  %e0 = extractelement <4 x i32> %v, i32 0
+  %e1 = extractelement <4 x i32> %v, i32 1
+  %e2 = extractelement <4 x i32> %v, i32 2
+  store volatile i32 %e0, ptr addrspace(1) %out, align 4
+  %out1 = getelementptr i32, ptr addrspace(1) %out, i32 1
+  store volatile i32 %e1, ptr addrspace(1) %out1, align 4
+  %out2 = getelementptr i32, ptr addrspace(1) %out, i32 2
+  store volatile i32 %e2, ptr addrspace(1) %out2, align 4
+  ret void
+}
+
+define void @shrink_i16_load_prefix(ptr addrspace(1) %out,
+                                    ptr addrspace(4) %p) {
+; CHECK-LABEL: @shrink_i16_load_prefix(
+; CHECK-NOT: load <4 x i16>
+; CHECK: load <2 x i16>, ptr addrspace(4) %p, align 4
+entry:
+  %v = load <4 x i16>, ptr addrspace(4) %p, align 4
+  %e0 = extractelement <4 x i16> %v, i32 0
+  %e1 = extractelement <4 x i16> %v, i32 1
+  store volatile i16 %e0, ptr addrspace(1) %out, align 2
+  %out1 = getelementptr i16, ptr addrspace(1) %out, i32 1
+  store volatile i16 %e1, ptr addrspace(1) %out1, align 2
+  ret void
+}
+
+define void @shrink_i64_load_to_i32_chunks(ptr addrspace(1) %out,
+                                           ptr addrspace(4) %p) {
+; CHECK-LABEL: @shrink_i64_load_to_i32_chunks(
+; CHECK-NOT: load <6 x i64>
+; CHECK: [[L0:%.*]] = load <3 x i32>, ptr addrspace(4) %p, align 8{{$}}
+; CHECK: [[P1:%.*]] = getelementptr i8, ptr addrspace(4) %p, i64 16
+; CHECK: [[L1:%.*]] = load <3 x i32>, ptr addrspace(4) [[P1]], align 8{{$}}
+; CHECK: [[P2:%.*]] = getelementptr i8, ptr addrspace(4) %p, i64 32
+; CHECK: [[L2:%.*]] = load <3 x i32>, ptr addrspace(4) [[P2]], align 8{{$}}
+; CHECK: extractelement <3 x i32> [[L0]], i32 0
+; CHECK: extractelement <3 x i32> [[L0]], i32 1
+; CHECK: extractelement <3 x i32> [[L0]], i32 2
+; CHECK: extractelement <3 x i32> [[L1]], i32 0
+; CHECK: extractelement <3 x i32> [[L1]], i32 1
+; CHECK: extractele...
[truncated]

``````````

</details>


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


More information about the llvm-commits mailing list