76#include "llvm/IR/IntrinsicsAArch64.h"
87#define DEBUG_TYPE "aarch64-pn-loop-rewrites"
90STATISTIC(LoopsRewritten,
"Number of loops rewritten");
92struct MaskRewriteCandidate {
100 unsigned VectorScale = 0;
102 unsigned ElementSizeInBits = 0;
105static void logLoopBailout(
const Loop &L,
const Twine &Reason) {
107 dbgs() <<
"PN loop rewrite: skipping loop with header ";
108 L.getHeader()->printAsOperand(
dbgs(),
false);
109 dbgs() <<
": " << Reason <<
"\n";
113static void logMatchFailure(
const PHINode &Phi,
const Twine &Reason) {
115 dbgs() <<
"PN loop rewrite: failed to match mask phi ";
116 Phi.printAsOperand(
dbgs(),
false);
117 dbgs() <<
": " << Reason <<
"\n";
123 return DL.getTypeSizeInBits(Ty->getScalarType()).getFixedValue();
127static ElementCount getSVEElementCount(
unsigned ElementSizeInBits) {
135static unsigned getLargestMaskedMemAccessSizeInBits(
const Loop &L,
138 unsigned LargestAccessSizeInBits = 0;
142 if (!
II || !L.contains(
II))
146 if (IID != Intrinsic::masked_load && IID != Intrinsic::masked_store)
149 unsigned MaskOpIdx = IID == Intrinsic::masked_load ? 1 : 2;
150 if (
II->getArgOperand(MaskOpIdx) != &MaskPhi)
154 if (AccessSizeInBits > LargestAccessSizeInBits)
155 LargestAccessSizeInBits = AccessSizeInBits;
158 return LargestAccessSizeInBits;
163static Intrinsic::ID getWhileLOIntrinsic(
unsigned ElementSizeInBits) {
164 switch (ElementSizeInBits) {
166 return Intrinsic::aarch64_sve_whilelo_c8;
168 return Intrinsic::aarch64_sve_whilelo_c16;
170 return Intrinsic::aarch64_sve_whilelo_c32;
172 return Intrinsic::aarch64_sve_whilelo_c64;
183 ElementCount LegalEC = getSVEElementCount(
C.ElementSizeInBits);
187 for (
unsigned PairOffset = 0; PairOffset !=
C.VectorScale / 2; ++PairOffset) {
189 Builder.CreateIntrinsic(Intrinsic::aarch64_sve_pext_x2, {LegalMaskTy},
190 {
Count, Builder.getInt32(PairOffset)},
192 for (
unsigned SliceInPair = 0; SliceInPair != 2; ++SliceInPair) {
193 Value *Part = Builder.CreateExtractValue(Pair, SliceInPair,
"pn.pext");
194 unsigned Slice = PairOffset * 2 + SliceInPair;
195 WideMask = Builder.CreateInsertVector(
196 C.MaskPhi->getType(), WideMask, Part,
206static Value *createWhileLO(
IRBuilder<> &Builder,
unsigned ElementSizeInBits,
208 if (Start->getType()->getIntegerBitWidth() < 64) {
209 Start = Builder.CreateZExt(Start, Builder.getInt64Ty());
210 End = Builder.CreateZExt(
End, Builder.getInt64Ty());
213 Intrinsic::ID WhileLO = getWhileLOIntrinsic(ElementSizeInBits);
214 return Builder.CreateIntrinsic(WhileLO,
215 {Start,
End, Builder.getInt32(VectorScale)},
222static bool tryRewriteMaskedLoad(
Instruction &UserI,
225 if (!
II ||
II->getIntrinsicID() != Intrinsic::masked_load)
228 unsigned ElementSizeInBits =
230 if (ElementSizeInBits !=
C.ElementSizeInBits)
238 ElementCount LegalEC = getSVEElementCount(
C.ElementSizeInBits);
239 Type *ScalarType =
II->getType()->getScalarType();
244 C.VectorScale == 4 ? Intrinsic::aarch64_sve_ld1_pn_x4
245 : Intrinsic::aarch64_sve_ld1_pn_x2,
246 {LegalDataTy, II->getArgOperand(0)->getType()},
247 {Count, II->getArgOperand(0)},
254 PNLoad->copyMetadata(*
II);
258 for (
unsigned Slice = 0; Slice !=
C.VectorScale; ++Slice) {
259 Value *Part = Builder.CreateExtractValue(PNLoad, Slice,
"pn.data");
260 WideData = Builder.CreateInsertVector(WideDataTy, WideData, Part,
265 II->replaceAllUsesWith(WideData);
266 II->eraseFromParent();
273static bool tryRewriteMaskedStore(
Instruction &UserI,
276 if (!
II ||
II->getIntrinsicID() != Intrinsic::masked_store)
280 II->getModule()->getDataLayout(),
II->getArgOperand(0)->getType());
281 if (ElementSizeInBits !=
C.ElementSizeInBits)
286 Type *ScalarType =
II->getArgOperand(0)->getType()->getScalarType();
287 ElementCount LegalEC = getSVEElementCount(
C.ElementSizeInBits);
291 for (
unsigned Slice = 0; Slice !=
C.VectorScale; ++Slice)
292 StoreArgs.
push_back(Builder.CreateExtractVector(
299 C.VectorScale == 4 ? Intrinsic::aarch64_sve_st1_pn_x4
300 : Intrinsic::aarch64_sve_st1_pn_x2,
301 {LegalDataTy, II->getArgOperand(1)->getType()}, StoreArgs));
303 PNStore->copyMetadata(*
II);
307 PNStore->addParamAttr(
C.VectorScale + 1,
ParamAttr);
309 II->eraseFromParent();
318static bool tryRewriteExtractElement(
Instruction &UserI,
319 const MaskRewriteCandidate &
C,
325 ElementCount LegalEC = getSVEElementCount(
C.ElementSizeInBits);
332 auto *ExtractMask = Builder.CreateIntrinsic(
333 Intrinsic::aarch64_sve_pext,
335 {
Count, Builder.getInt32(0)}, {},
"pn.pext");
337 Value *Extracted = Builder.CreateExtractElement(
338 ExtractMask, EEI->getIndexOperand(), EEI->getName() +
".pn");
341 EEI->eraseFromParent();
345class AArch64PredicateAsCounterLoopRewrites :
public LoopPass {
349 AArch64PredicateAsCounterLoopRewrites() :
LoopPass(ID) {}
362 std::optional<MaskRewriteCandidate> matchMaskPhi(
Loop &L,
PHINode &Phi)
const;
363 bool rewriteCandidate(
const MaskRewriteCandidate &
C,
Loop &L)
const;
368char AArch64PredicateAsCounterLoopRewrites::ID = 0;
371 "AArch64 Predicate As Counter Loop Rewrites",
false,
379 return new AArch64PredicateAsCounterLoopRewrites();
382bool AArch64PredicateAsCounterLoopRewrites::runOnLoop(
Loop *L,
385 logLoopBailout(*L,
"skipLoop requested the loop to be skipped");
390 auto &TPC = getAnalysis<TargetPassConfig>();
391 const AArch64Subtarget *
ST =
392 TPC.getTM<AArch64TargetMachine>().getSubtargetImpl(
F);
393 if (!
ST->hasSVE2p1() && !(
ST->hasSME2() &&
ST->isStreaming())) {
394 logLoopBailout(*L,
"neither SVE2.1 nor SME2 is available");
398 if (!
L->getLoopPreheader()) {
399 logLoopBailout(*L,
"loop has no preheader");
402 if (!
L->getLoopLatch()) {
403 logLoopBailout(*L,
"loop has no latch");
410 if (std::optional<MaskRewriteCandidate> Candidate = matchMaskPhi(*L, Phi))
411 Changed |= rewriteCandidate(*Candidate, *L);
422 return II &&
II->getIntrinsicID() == Intrinsic::get_active_lane_mask
427std::optional<MaskRewriteCandidate>
428AArch64PredicateAsCounterLoopRewrites::matchMaskPhi(
Loop &L,
429 PHINode &Phi)
const {
431 if (!PhiTy || !PhiTy->getElementType()->isIntegerTy(1))
434 if (
Phi.getNumIncomingValues() != 2) {
435 logMatchFailure(Phi, Twine(
"phi has ") + Twine(
Phi.getNumIncomingValues()) +
436 " incoming values; expected 2");
440 Value *StartValue =
Phi.getIncomingValueForBlock(
L.getLoopPreheader());
441 Value *NextValue =
Phi.getIncomingValueForBlock(
L.getLoopLatch());
446 "preheader incoming value is not get_active_lane_mask");
450 logMatchFailure(Phi,
"latch incoming value is not get_active_lane_mask");
454 unsigned WideMaskElements = PhiTy->getMinNumElements();
457 Twine(
"wide mask element count is not a power of 2: ") +
458 Twine(WideMaskElements));
463 logMatchFailure(Phi,
"start mask induction operand is wider than i64");
467 logMatchFailure(Phi,
"next mask induction operand is wider than i64");
471 unsigned PreferredMaskElementSizeInBits =
472 getLargestMaskedMemAccessSizeInBits(L, Phi);
474 if (!
is_contained({8u, 16u, 32u, 64u}, PreferredMaskElementSizeInBits)) {
475 logMatchFailure(Phi, Twine(
"unsupported element size in bits: ") +
476 Twine(PreferredMaskElementSizeInBits));
480 unsigned SVEMaskElements =
482 if (WideMaskElements <= SVEMaskElements) {
483 logMatchFailure(Phi, Twine(
"wide mask element count ") +
484 Twine(WideMaskElements) +
485 " is not wider than the legal mask width " +
486 Twine(SVEMaskElements));
490 unsigned VectorScale = WideMaskElements / SVEMaskElements;
491 if (VectorScale != 2 && VectorScale != 4) {
492 logMatchFailure(Phi, Twine(
"unsupported predicate-as-counter scale: ") +
497 return MaskRewriteCandidate{&
Phi, StartMask, NextMask, VectorScale,
498 PreferredMaskElementSizeInBits};
501bool AArch64PredicateAsCounterLoopRewrites::rewriteCandidate(
502 const MaskRewriteCandidate &
C,
Loop &L)
const {
505 createWhileLO(Builder,
C.ElementSizeInBits,
C.StartMask->getArgOperand(0),
506 C.StartMask->getArgOperand(1),
C.VectorScale);
507 Builder.SetInsertPoint(
C.NextMask);
509 createWhileLO(Builder,
C.ElementSizeInBits,
C.NextMask->getArgOperand(0),
510 C.NextMask->getArgOperand(1),
C.VectorScale);
512 Builder.SetInsertPoint(
C.MaskPhi);
514 Builder.CreatePHI(NewStart->
getType(), 2,
C.MaskPhi->getName() +
".pn");
515 NewPhi->addIncoming(NewStart,
L.getLoopPreheader());
516 NewPhi->addIncoming(NewNext,
L.getLoopLatch());
519 function_ref<bool(Use &U)>
Predicate =
nullptr) {
521 for (Use &U : OldMask->
uses()) {
526 Value *WideMask =
nullptr;
527 for (Use *U : UsesToRewrite) {
529 if (tryRewriteMaskedLoad(*UserI,
C,
Count) ||
530 tryRewriteMaskedStore(*UserI,
C,
Count) ||
531 tryRewriteExtractElement(*UserI,
C,
Count))
537 InsertPt = OldMask->
getParent()->getFirstNonPHIIt();
539 Builder.SetInsertPoint(InsertPt);
540 Builder.SetCurrentDebugLocation(OldMask->
getDebugLoc());
541 WideMask = buildWideMask(Builder,
C,
Count);
551 auto IsNonPhiUseInLoop = [&](
Use &
U) {
556 RewriteUses(
C.MaskPhi, NewPhi);
557 RewriteUses(
C.StartMask, NewStart, IsNonPhiUseInLoop);
558 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")
This file defines the DenseMap class.
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.