75#include "llvm/IR/IntrinsicsAArch64.h"
86#define DEBUG_TYPE "aarch64-pn-loop-rewrites"
89STATISTIC(LoopsRewritten,
"Number of loops rewritten");
91struct MaskRewriteCandidate {
99 unsigned VectorScale = 0;
101 unsigned ElementSizeInBits = 0;
104static void logLoopBailout(
const Loop &L,
const Twine &Reason) {
106 dbgs() <<
"PN loop rewrite: skipping loop with header ";
107 L.getHeader()->printAsOperand(
dbgs(),
false);
108 dbgs() <<
": " << Reason <<
"\n";
112static void logMatchFailure(
const PHINode &Phi,
const Twine &Reason) {
114 dbgs() <<
"PN loop rewrite: failed to match mask phi ";
115 Phi.printAsOperand(
dbgs(),
false);
116 dbgs() <<
": " << Reason <<
"\n";
122 return DL.getTypeSizeInBits(Ty->getScalarType()).getFixedValue();
126static ElementCount getSVEElementCount(
unsigned ElementSizeInBits) {
134static unsigned getLargestMaskedMemAccessSizeInBits(
const Loop &L,
137 unsigned LargestAccessSizeInBits = 0;
141 if (!
II || !L.contains(
II))
145 if (IID != Intrinsic::masked_load && IID != Intrinsic::masked_store)
148 unsigned MaskOpIdx = IID == Intrinsic::masked_load ? 1 : 2;
149 if (
II->getArgOperand(MaskOpIdx) != &MaskPhi)
153 if (AccessSizeInBits > LargestAccessSizeInBits)
154 LargestAccessSizeInBits = AccessSizeInBits;
157 return LargestAccessSizeInBits;
162static Intrinsic::ID getWhileLOIntrinsic(
unsigned ElementSizeInBits) {
163 switch (ElementSizeInBits) {
165 return Intrinsic::aarch64_sve_whilelo_c8;
167 return Intrinsic::aarch64_sve_whilelo_c16;
169 return Intrinsic::aarch64_sve_whilelo_c32;
171 return Intrinsic::aarch64_sve_whilelo_c64;
182 ElementCount LegalEC = getSVEElementCount(
C.ElementSizeInBits);
186 for (
unsigned PairOffset = 0; PairOffset !=
C.VectorScale / 2; ++PairOffset) {
188 Builder.CreateIntrinsic(Intrinsic::aarch64_sve_pext_x2, {LegalMaskTy},
189 {
Count, Builder.getInt32(PairOffset)},
191 for (
unsigned SliceInPair = 0; SliceInPair != 2; ++SliceInPair) {
192 Value *Part = Builder.CreateExtractValue(Pair, SliceInPair,
"pn.pext");
193 unsigned Slice = PairOffset * 2 + SliceInPair;
194 WideMask = Builder.CreateInsertVector(
195 C.MaskPhi->getType(), WideMask, Part,
205static Value *createWhileLO(
IRBuilder<> &Builder,
unsigned ElementSizeInBits,
207 if (Start->getType()->getIntegerBitWidth() < 64) {
208 Start = Builder.CreateZExt(Start, Builder.getInt64Ty());
209 End = Builder.CreateZExt(
End, Builder.getInt64Ty());
212 Intrinsic::ID WhileLO = getWhileLOIntrinsic(ElementSizeInBits);
213 return Builder.CreateIntrinsic(WhileLO,
214 {Start,
End, Builder.getInt32(VectorScale)},
221static bool tryRewriteMaskedLoad(
Instruction &UserI,
224 if (!
II ||
II->getIntrinsicID() != Intrinsic::masked_load)
227 unsigned ElementSizeInBits =
229 if (ElementSizeInBits !=
C.ElementSizeInBits)
237 ElementCount LegalEC = getSVEElementCount(
C.ElementSizeInBits);
238 Type *ScalarType =
II->getType()->getScalarType();
243 C.VectorScale == 4 ? Intrinsic::aarch64_sve_ld1_pn_x4
244 : Intrinsic::aarch64_sve_ld1_pn_x2,
245 {LegalDataTy, II->getArgOperand(0)->getType()},
246 {Count, II->getArgOperand(0)},
253 PNLoad->copyMetadata(*
II);
257 for (
unsigned Slice = 0; Slice !=
C.VectorScale; ++Slice) {
258 Value *Part = Builder.CreateExtractValue(PNLoad, Slice,
"pn.data");
259 WideData = Builder.CreateInsertVector(WideDataTy, WideData, Part,
264 II->replaceAllUsesWith(WideData);
265 II->eraseFromParent();
272static bool tryRewriteMaskedStore(
Instruction &UserI,
275 if (!
II ||
II->getIntrinsicID() != Intrinsic::masked_store)
279 II->getModule()->getDataLayout(),
II->getArgOperand(0)->getType());
280 if (ElementSizeInBits !=
C.ElementSizeInBits)
285 Type *ScalarType =
II->getArgOperand(0)->getType()->getScalarType();
286 ElementCount LegalEC = getSVEElementCount(
C.ElementSizeInBits);
290 for (
unsigned Slice = 0; Slice !=
C.VectorScale; ++Slice)
291 StoreArgs.
push_back(Builder.CreateExtractVector(
298 C.VectorScale == 4 ? Intrinsic::aarch64_sve_st1_pn_x4
299 : Intrinsic::aarch64_sve_st1_pn_x2,
300 {LegalDataTy, II->getArgOperand(1)->getType()}, StoreArgs));
302 PNStore->copyMetadata(*
II);
306 PNStore->addParamAttr(
C.VectorScale + 1,
ParamAttr);
308 II->eraseFromParent();
317static bool tryRewriteExtractElement(
Instruction &UserI,
318 const MaskRewriteCandidate &
C,
324 ElementCount LegalEC = getSVEElementCount(
C.ElementSizeInBits);
331 auto *ExtractMask = Builder.CreateIntrinsic(
332 Intrinsic::aarch64_sve_pext,
334 {
Count, Builder.getInt32(0)}, {},
"pn.pext");
336 Value *Extracted = Builder.CreateExtractElement(
337 ExtractMask, EEI->getIndexOperand(), EEI->getName() +
".pn");
340 EEI->eraseFromParent();
344class AArch64PredicateAsCounterLoopRewrites :
public LoopPass {
348 AArch64PredicateAsCounterLoopRewrites() :
LoopPass(ID) {}
361 std::optional<MaskRewriteCandidate> matchMaskPhi(
Loop &L,
PHINode &Phi)
const;
362 bool rewriteCandidate(
const MaskRewriteCandidate &
C,
Loop &L)
const;
367char AArch64PredicateAsCounterLoopRewrites::ID = 0;
370 "AArch64 Predicate As Counter Loop Rewrites",
false,
378 return new AArch64PredicateAsCounterLoopRewrites();
381bool AArch64PredicateAsCounterLoopRewrites::runOnLoop(
Loop *L,
384 logLoopBailout(*L,
"skipLoop requested the loop to be skipped");
389 auto &TPC = getAnalysis<TargetPassConfig>();
390 const AArch64Subtarget *
ST =
391 TPC.getTM<AArch64TargetMachine>().getSubtargetImpl(
F);
392 if (!
ST->hasSVE2p1() && !(
ST->hasSME2() &&
ST->isStreaming())) {
393 logLoopBailout(*L,
"neither SVE2.1 nor SME2 is available");
397 if (!
L->getLoopPreheader()) {
398 logLoopBailout(*L,
"loop has no preheader");
401 if (!
L->getLoopLatch()) {
402 logLoopBailout(*L,
"loop has no latch");
409 if (std::optional<MaskRewriteCandidate> Candidate = matchMaskPhi(*L, Phi))
410 Changed |= rewriteCandidate(*Candidate, *L);
421 return II &&
II->getIntrinsicID() == Intrinsic::get_active_lane_mask
426std::optional<MaskRewriteCandidate>
427AArch64PredicateAsCounterLoopRewrites::matchMaskPhi(
Loop &L,
428 PHINode &Phi)
const {
430 if (!PhiTy || !PhiTy->getElementType()->isIntegerTy(1))
433 if (
Phi.getNumIncomingValues() != 2) {
434 logMatchFailure(Phi, Twine(
"phi has ") + Twine(
Phi.getNumIncomingValues()) +
435 " incoming values; expected 2");
439 Value *StartValue =
Phi.getIncomingValueForBlock(
L.getLoopPreheader());
440 Value *NextValue =
Phi.getIncomingValueForBlock(
L.getLoopLatch());
445 "preheader incoming value is not get_active_lane_mask");
449 logMatchFailure(Phi,
"latch incoming value is not get_active_lane_mask");
453 unsigned WideMaskElements = PhiTy->getMinNumElements();
456 Twine(
"wide mask element count is not a power of 2: ") +
457 Twine(WideMaskElements));
462 logMatchFailure(Phi,
"start mask induction operand is wider than i64");
466 logMatchFailure(Phi,
"next mask induction operand is wider than i64");
470 unsigned PreferredMaskElementSizeInBits =
471 getLargestMaskedMemAccessSizeInBits(L, Phi);
473 if (!
is_contained({8u, 16u, 32u, 64u}, PreferredMaskElementSizeInBits)) {
474 logMatchFailure(Phi, Twine(
"unsupported element size in bits: ") +
475 Twine(PreferredMaskElementSizeInBits));
479 unsigned SVEMaskElements =
481 if (WideMaskElements <= SVEMaskElements) {
482 logMatchFailure(Phi, Twine(
"wide mask element count ") +
483 Twine(WideMaskElements) +
484 " is not wider than the legal mask width " +
485 Twine(SVEMaskElements));
489 unsigned VectorScale = WideMaskElements / SVEMaskElements;
490 if (VectorScale != 2 && VectorScale != 4) {
491 logMatchFailure(Phi, Twine(
"unsupported predicate-as-counter scale: ") +
496 return MaskRewriteCandidate{&
Phi, StartMask, NextMask, VectorScale,
497 PreferredMaskElementSizeInBits};
500bool AArch64PredicateAsCounterLoopRewrites::rewriteCandidate(
501 const MaskRewriteCandidate &
C,
Loop &L)
const {
504 createWhileLO(Builder,
C.ElementSizeInBits,
C.StartMask->getArgOperand(0),
505 C.StartMask->getArgOperand(1),
C.VectorScale);
506 Builder.SetInsertPoint(
C.NextMask);
508 createWhileLO(Builder,
C.ElementSizeInBits,
C.NextMask->getArgOperand(0),
509 C.NextMask->getArgOperand(1),
C.VectorScale);
511 Builder.SetInsertPoint(
C.MaskPhi);
513 Builder.CreatePHI(NewStart->
getType(), 2,
C.MaskPhi->getName() +
".pn");
514 NewPhi->addIncoming(NewStart,
L.getLoopPreheader());
515 NewPhi->addIncoming(NewNext,
L.getLoopLatch());
518 function_ref<bool(Use &U)>
Predicate =
nullptr) {
520 for (Use &U : OldMask->
uses()) {
525 Value *WideMask =
nullptr;
526 for (Use *U : UsesToRewrite) {
528 if (tryRewriteMaskedLoad(*UserI,
C,
Count) ||
529 tryRewriteMaskedStore(*UserI,
C,
Count) ||
530 tryRewriteExtractElement(*UserI,
C,
Count))
536 InsertPt = OldMask->
getParent()->getFirstNonPHIIt();
538 Builder.SetInsertPoint(InsertPt);
539 Builder.SetCurrentDebugLocation(OldMask->
getDebugLoc());
540 WideMask = buildWideMask(Builder,
C,
Count);
550 auto IsNonPhiUseInLoop = [&](
Use &
U) {
555 RewriteUses(
C.MaskPhi, NewPhi);
556 RewriteUses(
C.StartMask, NewStart, IsNonPhiUseInLoop);
557 RewriteUses(
C.NextMask, NewNext, IsNonPhiUseInLoop);
static IntrinsicInst * getGetActiveLaneMask(Value *V)
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
This file contains the simple types necessary to represent the attributes associated with functions a...
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
Module.h This file contains the declarations for the Module class.
uint64_t IntrinsicInst * II
#define INITIALIZE_PASS_DEPENDENCY(depName)
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
This file defines the SmallVector class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
Target-Independent Code Generator Pass Configuration Options pass.
Represent the analysis usage information of a pass.
LLVM_ABI AnalysisUsage & addRequiredID(const void *ID)
AnalysisUsage & addPreservedID(const void *ID)
AnalysisUsage & addRequired()
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Functions, function parameters, and return types can have attributes to indicate how they should be t...
InstListType::iterator iterator
Instruction iterators...
Value * getArgOperand(unsigned i) const
A parsed version of the target data layout string in and methods for querying it.
static constexpr ElementCount getScalable(ScalarTy MinVal)
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
LLVM_ABI const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
iterator_range< user_iterator > users()
A wrapper class for inspecting calls to intrinsic functions.
Represents a single loop in the control flow graph.
const DataLayout & getDataLayout() const
Get the data layout for the module's target platform.
Pass interface - Implemented by all 'passes'.
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Target-Independent Code Generator Pass Configuration Options.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
The instances of the Type class are immutable: once they are created, they are never changed.
LLVM_ABI unsigned getIntegerBitWidth() const
LLVM Value Representation.
Type * getType() const
All values are typed, get the type of this value.
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
iterator_range< use_iterator > uses()
static LLVM_ABI VectorType * get(Type *ElementType, ElementCount EC)
This static method is the primary way to construct an VectorType.
constexpr ScalarTy getKnownMinValue() const
Returns the minimum value this quantity can represent.
const ParentTy * getParent() const
self_iterator getIterator()
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
static constexpr unsigned SVEBitsPerBlock
@ BasicBlock
Various leaf nodes.
Predicate
Predicate - These are "(BI << 5) | BO" for various predicates.
NodeAddr< PhiNode * > Phi
NodeAddr< UseNode * > Use
friend class Instruction
Iterator for Instructions in a `BasicBlock.
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI bool RecursivelyDeleteTriviallyDeadInstructions(Value *V, const TargetLibraryInfo *TLI=nullptr, MemorySSAUpdater *MSSAU=nullptr, std::function< void(Value *)> AboutToDeleteCallback=std::function< void(Value *)>())
If the specified value is a trivially dead instruction, delete it.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Pass * createAArch64PredicateAsCounterLoopRewritesPass()
LLVM_ABI char & LoopSimplifyID
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
constexpr bool isPowerOf2_32(uint32_t Value)
Return true if the argument is a power of two > 0.
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
IRBuilder(LLVMContext &, FolderTy, InserterTy) -> IRBuilder< FolderTy, InserterTy >
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
@ End
Run the machine verifier once, at the end of the pipeline.