[llvm] [VectorCombine][AMDGPU] Shrink demanded vector loads (PR #202501)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 8 20:54:49 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-amdgpu
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