-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[IA][RISCV] Support VP loads/stores in InterleavedAccessPass #120490
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
156661a
c869bec
3688672
32d8de5
6b7f8e9
f7242df
0e4bf69
1afd2cc
16a88d9
5cd5be4
16915c9
725dfad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -630,11 +630,34 @@ getVectorDeinterleaveFactor(IntrinsicInst *II, | |
| return true; | ||
| } | ||
|
|
||
| /// Check the interleaved mask | ||
| /// | ||
| /// - if a value within the optional is non-nullptr, the value corresponds to | ||
| /// deinterleaved mask | ||
| /// - if a value within the option is nullptr, the value corresponds to all-true | ||
| /// mask | ||
| /// - return nullopt if mask cannot be deinterleaved | ||
lukel97 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| static std::optional<Value *> getMask(Value *WideMask, unsigned Factor) { | ||
| using namespace llvm::PatternMatch; | ||
| if (auto *IMI = dyn_cast<IntrinsicInst>(WideMask)) { | ||
| SmallVector<Value *, 8> Operands; | ||
| SmallVector<Instruction *, 8> DeadInsts; | ||
| if (getVectorInterleaveFactor(IMI, Operands, DeadInsts)) { | ||
| assert(!Operands.empty()); | ||
| if (Operands.size() == Factor && | ||
| std::equal(Operands.begin(), Operands.end(), Operands.begin())) | ||
topperc marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return Operands.front(); | ||
| } | ||
| } | ||
| if (match(WideMask, m_AllOnes())) | ||
| return nullptr; | ||
lukel97 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return std::nullopt; | ||
| } | ||
|
|
||
| bool InterleavedAccessImpl::lowerDeinterleaveIntrinsic( | ||
| IntrinsicInst *DI, SmallSetVector<Instruction *, 32> &DeadInsts) { | ||
| LoadInst *LI = dyn_cast<LoadInst>(DI->getOperand(0)); | ||
|
|
||
| if (!LI || !LI->hasOneUse() || !LI->isSimple()) | ||
| Value *LoadedVal = DI->getOperand(0); | ||
| if (!LoadedVal->hasOneUse() || !isa<LoadInst, VPIntrinsic>(LoadedVal)) | ||
| return false; | ||
|
|
||
| SmallVector<Value *, 8> DeinterleaveValues; | ||
|
|
@@ -643,43 +666,92 @@ bool InterleavedAccessImpl::lowerDeinterleaveIntrinsic( | |
| DeinterleaveDeadInsts)) | ||
| return false; | ||
|
|
||
| LLVM_DEBUG(dbgs() << "IA: Found a deinterleave intrinsic: " << *DI | ||
| << " with factor = " << DeinterleaveValues.size() << "\n"); | ||
| const unsigned Factor = DeinterleaveValues.size(); | ||
|
|
||
| // Try and match this with target specific intrinsics. | ||
| if (!TLI->lowerDeinterleaveIntrinsicToLoad(LI, DeinterleaveValues)) | ||
| return false; | ||
| if (auto *VPLoad = dyn_cast<VPIntrinsic>(LoadedVal)) { | ||
| if (VPLoad->getIntrinsicID() != Intrinsic::vp_load) | ||
| return false; | ||
| // Check mask operand. Handle both all-true and interleaved mask. | ||
| Value *WideMask = VPLoad->getOperand(1); | ||
| std::optional<Value *> Mask = getMask(WideMask, Factor); | ||
| if (!Mask) | ||
| return false; | ||
|
|
||
| LLVM_DEBUG(dbgs() << "IA: Found a vp.load with deinterleave intrinsic " | ||
| << *DI << " and factor = " << Factor << "\n"); | ||
|
|
||
| // Since lowerInterleaveLoad expects Shuffles and LoadInst, use special | ||
| // TLI function to emit target-specific interleaved instruction. | ||
| if (!TLI->lowerInterleavedScalableLoad(VPLoad, *Mask, DI, | ||
| DeinterleaveValues)) | ||
|
||
| return false; | ||
|
|
||
| } else { | ||
| auto *LI = cast<LoadInst>(LoadedVal); | ||
| if (!LI->isSimple()) | ||
| return false; | ||
|
|
||
| LLVM_DEBUG(dbgs() << "IA: Found a load with deinterleave intrinsic " << *DI | ||
| << " and factor = " << Factor << "\n"); | ||
|
|
||
| // Try and match this with target specific intrinsics. | ||
| if (!TLI->lowerDeinterleaveIntrinsicToLoad(LI, DeinterleaveValues)) | ||
| return false; | ||
| } | ||
|
|
||
| DeadInsts.insert(DeinterleaveDeadInsts.begin(), DeinterleaveDeadInsts.end()); | ||
| // We now have a target-specific load, so delete the old one. | ||
| DeadInsts.insert(LI); | ||
| DeadInsts.insert(cast<Instruction>(LoadedVal)); | ||
| return true; | ||
| } | ||
|
|
||
| bool InterleavedAccessImpl::lowerInterleaveIntrinsic( | ||
| IntrinsicInst *II, SmallSetVector<Instruction *, 32> &DeadInsts) { | ||
| if (!II->hasOneUse()) | ||
| return false; | ||
|
|
||
| StoreInst *SI = dyn_cast<StoreInst>(*(II->users().begin())); | ||
|
|
||
| if (!SI || !SI->isSimple()) | ||
| Value *StoredBy = II->user_back(); | ||
| if (!isa<StoreInst, VPIntrinsic>(StoredBy)) | ||
| return false; | ||
|
|
||
| SmallVector<Value *, 8> InterleaveValues; | ||
| SmallVector<Instruction *, 8> InterleaveDeadInsts; | ||
| if (!getVectorInterleaveFactor(II, InterleaveValues, InterleaveDeadInsts)) | ||
| return false; | ||
|
|
||
| LLVM_DEBUG(dbgs() << "IA: Found an interleave intrinsic: " << *II | ||
| << " with factor = " << InterleaveValues.size() << "\n"); | ||
| const unsigned Factor = InterleaveValues.size(); | ||
|
|
||
| // Try and match this with target specific intrinsics. | ||
| if (!TLI->lowerInterleaveIntrinsicToStore(SI, InterleaveValues)) | ||
| return false; | ||
| if (auto *VPStore = dyn_cast<VPIntrinsic>(StoredBy)) { | ||
| if (VPStore->getIntrinsicID() != Intrinsic::vp_store) | ||
| return false; | ||
|
|
||
| Value *WideMask = VPStore->getOperand(2); | ||
| std::optional<Value *> Mask = getMask(WideMask, Factor); | ||
| if (!Mask) | ||
| return false; | ||
|
|
||
| LLVM_DEBUG(dbgs() << "IA: Found a vp.store with interleave intrinsic " | ||
| << *II << " and factor = " << Factor << "\n"); | ||
|
|
||
| // Since lowerInterleavedStore expects Shuffle and StoreInst, use special | ||
| // TLI function to emit target-specific interleaved instruction. | ||
| if (!TLI->lowerInterleavedScalableStore(VPStore, *Mask, II, | ||
| InterleaveValues)) | ||
| return false; | ||
| } else { | ||
| auto *SI = cast<StoreInst>(StoredBy); | ||
| if (!SI->isSimple()) | ||
| return false; | ||
|
|
||
| LLVM_DEBUG(dbgs() << "IA: Found a store with interleave intrinsic " << *II | ||
| << " and factor = " << Factor << "\n"); | ||
|
|
||
| // Try and match this with target specific intrinsics. | ||
| if (!TLI->lowerInterleaveIntrinsicToStore(SI, InterleaveValues)) | ||
| return false; | ||
| } | ||
|
|
||
| // We now have a target-specific store, so delete the old one. | ||
| DeadInsts.insert(SI); | ||
| DeadInsts.insert(cast<Instruction>(StoredBy)); | ||
| DeadInsts.insert(InterleaveDeadInsts.begin(), InterleaveDeadInsts.end()); | ||
| return true; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this doesn't have to be strictly restricted to scalable vectors, would it make sense to rename this something like
lowerDeinterleaveIntrinsicToVPLoadto more closely match thelowerDeinterleaveIntrinsicToLoadhook?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah
lowerDeinterleaveIntrinsicToVPLoadis more consistent with the existing naming. I'd updated it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also updated the list of arguments taken by lowerDeinterleaveIntrinsicToVPLoad / lowerInterleaveIntrinsicToVPStore and they no longer take the (de)interleaved intrinsic argument (because we're not really using it)