[llvm] [CycleInfo] Store entries in the block layout. NFC (PR #210866)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 20 20:05:31 PDT 2026
https://github.com/MaskRay created https://github.com/llvm/llvm-project/pull/210866
A reducible loop has a single entry (header), which flatten() places at
BlockLayout[IdxBegin], as its minimum-preorder block. A reducible loop
then needs no storage at all.
For the uncommon irreducible loop case, we append its full entry list
(header first) after the Euler tour, referenced by [EntryBegin,
EntryBegin+EntrySize), making `GenericCycleInfo::Cycle` (named only
because "loop" is occupied by LoopInfo) trivially destructible.
>From 59fb1bbb258ebb1ecb596a8997219fa52098aa88 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 18 Jul 2026 16:57:47 -0700
Subject: [PATCH] [CycleInfo] Store entries in the block layout. NFC
A reducible loop has a single entry (header), which flatten() places at
BlockLayout[IdxBegin], as its minimum-preorder block. A reducible loop
then needs no storage at all.
For the uncommon irreducible loop case, we append its full entry list
(header first) after the Euler tour, referenced by [EntryBegin,
EntryBegin+EntrySize), making `GenericCycleInfo::Cycle` (named only
because "loop" is occupied by LoopInfo) trivially destructible.
---
llvm/include/llvm/ADT/GenericCycleImpl.h | 21 +++++++---
llvm/include/llvm/ADT/GenericCycleInfo.h | 50 ++++++++++++++----------
2 files changed, 46 insertions(+), 25 deletions(-)
diff --git a/llvm/include/llvm/ADT/GenericCycleImpl.h b/llvm/include/llvm/ADT/GenericCycleImpl.h
index bbc6230aefe5f..f0a59c386517c 100644
--- a/llvm/include/llvm/ADT/GenericCycleImpl.h
+++ b/llvm/include/llvm/ADT/GenericCycleImpl.h
@@ -242,7 +242,6 @@ template <typename ContextT> class GenericCycleInfoCompute {
// Per-cycle scratch built in run() and consumed by flatten(), keyed by
// header-preorder rank.
struct CycleBuild {
- BlockT *Header;
unsigned ChildHead;
unsigned NextSibling;
unsigned OwnCount;
@@ -329,6 +328,9 @@ void GenericCycleInfo<ContextT>::addBlockToCycle(BlockT *Block, CycleRef C) {
++X.IdxBegin;
++X.IdxEnd;
}
+ // Insert shifts every appended entry list.
+ if (X.EntrySize)
+ ++X.EntryBegin;
}
addToBlockMap(Block, C);
// Cyc and its ancestors gain the new block: extend each one's slice and
@@ -368,7 +370,6 @@ void GenericCycleInfoCompute<ContextT>::flatten(ArrayRef<CycleBuild> Build,
CycleT &Flat = Info.Cycles[ID];
Flat.Parent = Parent;
Flat.Depth = Parent ? Info.deref(Parent).Depth + 1 : 1;
- Flat.appendEntry(Build[C].Header);
Cursor += Build[C].OwnCount;
Flat.IdxBegin = Cursor;
Stack.push_back({ID, Build[C].ChildHead});
@@ -428,7 +429,7 @@ void GenericCycleInfoCompute<ContextT>::run(FunctionT *F) {
unsigned &Head = BI.LoopHeader != NoBlock
? Build[info(BI.LoopHeader).LoopIdx].ChildHead
: TopHead;
- Build.push_back({BI.getBlock(), NoCycle, Head, 1}); // OwnCount 1: header.
+ Build.push_back({NoCycle, Head, 1}); // OwnCount 1: the header.
Head = I;
LLVM_DEBUG(dbgs() << "Found cycle for header: "
<< Info.Context.print(BI.getBlock()) << "\n");
@@ -445,18 +446,28 @@ void GenericCycleInfoCompute<ContextT>::run(FunctionT *F) {
// Add the non-header entries recorded during the DFS. Sorting by (header,
// block) groups each cycle's entries together and in block preorder; a block
- // may re-enter a cycle via several edges, so skip duplicates.
+ // may re-enter a cycle via several edges, so skip duplicates. Each group
+ // opens an entry slice seeded with the header.
SmallVector<unsigned, 8> Rank(BlockInfos.size());
for (auto [R, N] : enumerate(Preorder))
Rank[N] = R;
for (auto &[H, B] : Reentries)
B = Rank[B];
llvm::sort(Reentries);
+ unsigned PrevH = NoBlock;
for (unsigned I = 0, E = Reentries.size(); I != E; ++I) {
if (I && Reentries[I] == Reentries[I - 1])
continue;
auto [H, R] = Reentries[I];
- Info.deref(Info.BlockMap[H]).appendEntry(info(Preorder[R]).getBlock());
+ CycleT &Cyc = Info.deref(Info.BlockMap[H]);
+ if (H != PrevH) {
+ BlockT *Header = Info.BlockLayout[Cyc.IdxBegin];
+ Cyc.EntryBegin = Info.BlockLayout.size();
+ Info.BlockLayout.push_back(Header);
+ PrevH = H;
+ }
+ Info.BlockLayout.push_back(info(Preorder[R]).getBlock());
+ Cyc.EntrySize = Info.BlockLayout.size() - Cyc.EntryBegin;
}
}
diff --git a/llvm/include/llvm/ADT/GenericCycleInfo.h b/llvm/include/llvm/ADT/GenericCycleInfo.h
index 1245ef5d49bb1..6d6f4dd3c0d43 100644
--- a/llvm/include/llvm/ADT/GenericCycleInfo.h
+++ b/llvm/include/llvm/ADT/GenericCycleInfo.h
@@ -39,6 +39,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
+#include <type_traits>
namespace llvm {
@@ -88,10 +89,6 @@ template <typename ContextT> class GenericCycleInfo {
/// The parent cycle; invalid for a top-level cycle.
CycleRef Parent;
- /// The entry block(s) of the cycle. The header is the only entry if this
- /// is a loop.
- SmallVector<BlockT *, 1> Entries;
-
/// This cycle's blocks (its own and its nested cycles') occupy the
/// half-open range [IdxBegin, IdxEnd) of BlockLayout, nested like an Euler
/// tour of the cycle tree, so containment is an interval test (see
@@ -107,17 +104,15 @@ template <typename ContextT> class GenericCycleInfo {
/// [this, this + 1 + NumDescendants) of Cycles.
unsigned NumDescendants = 0;
- void appendEntry(BlockT *Block) { Entries.push_back(Block); }
+ /// A reducible cycle (only entry is its header at IdxBegin) has EntrySize
+ /// 0. Otherwise its full entry list (header first) is appended past the
+ /// Euler tour at [EntryBegin, EntryBegin+EntrySize).
+ unsigned EntryBegin = 0, EntrySize = 0;
/// Whether this cycle has a parent, i.e. is not top-level.
bool hasParent() const { return Parent.isValid(); }
-
- Cycle() = default;
- Cycle(const Cycle &) = delete;
- Cycle &operator=(const Cycle &) = delete;
- Cycle(Cycle &&) = delete;
- Cycle &operator=(Cycle &&) = delete;
};
+ static_assert(std::is_trivially_destructible_v<Cycle>);
using CycleT = Cycle;
ContextT Context;
@@ -128,7 +123,8 @@ template <typename ContextT> class GenericCycleInfo {
SmallVector<CycleRef> BlockMap;
/// Euler tour of the cycle forest: every cycle's blocks form a contiguous
- /// slice [IdxBegin, IdxEnd) of this array, nested inside its parent's.
+ /// slice [IdxBegin, IdxEnd), nested inside its parent's. Entry lists for
+ /// irreducible cycles are appended past the tour (see EntryBegin).
SmallVector<BlockT *, 8> BlockLayout;
/// All cycles in forest preorder: every cycle is immediately followed by
@@ -219,8 +215,13 @@ template <typename ContextT> class GenericCycleInfo {
return BlockMap[Number];
}
- BlockT *getHeader(CycleRef C) const { return deref(C).Entries[0]; }
- bool isReducible(CycleRef C) const { return deref(C).Entries.size() == 1; }
+ BlockT *getHeader(CycleRef C) const {
+ // Usually IdxBegin, but setSingleEntry() can install a header elsewhere.
+ const CycleT &Cyc = deref(C);
+ return BlockLayout[Cyc.EntrySize ? Cyc.EntryBegin : Cyc.IdxBegin];
+ }
+ // Whether there is a single entry: header only (0) or setSingleEntry (1).
+ bool isReducible(CycleRef C) const { return deref(C).EntrySize <= 1; }
CycleRef getParentCycle(CycleRef C) const { return deref(C).Parent; }
unsigned getDepth(CycleRef C) const { return deref(C).Depth; }
size_t getNumBlocks(CycleRef C) const {
@@ -228,14 +229,23 @@ template <typename ContextT> class GenericCycleInfo {
return Cyc.IdxEnd - Cyc.IdxBegin;
}
- ArrayRef<BlockT *> getEntries(CycleRef C) const { return deref(C).Entries; }
+ ArrayRef<BlockT *> getEntries(CycleRef C) const {
+ const CycleT &Cyc = deref(C);
+ if (Cyc.EntrySize)
+ return ArrayRef(BlockLayout).slice(Cyc.EntryBegin, Cyc.EntrySize);
+ return ArrayRef(BlockLayout).slice(Cyc.IdxBegin, 1);
+ }
bool isEntry(CycleRef C, const BlockT *Block) const {
- return is_contained(deref(C).Entries, Block);
+ return is_contained(getEntries(C), Block);
}
+ // Append Block as a one-element entry list (along with irreducible cycles).
+ // getHeader/getEntries read it there, leaving BlockLayout's block order
+ // untouched.
void setSingleEntry(CycleRef C, BlockT *Block) {
- auto &Entries = deref(C).Entries;
- Entries.clear();
- Entries.push_back(Block);
+ CycleT &Cyc = deref(C);
+ Cyc.EntryBegin = BlockLayout.size();
+ BlockLayout.push_back(Block);
+ Cyc.EntrySize = 1;
}
/// Returns true iff \p Outer contains \p Inner. O(1). Non-strict.
bool contains(CycleRef Outer, CycleRef Inner) const {
@@ -252,7 +262,7 @@ template <typename ContextT> class GenericCycleInfo {
Printable printEntries(CycleRef C, const ContextT &Ctx) const {
return Printable([this, C, &Ctx](raw_ostream &Out) {
ListSeparator LS(" ");
- for (auto *Entry : deref(C).Entries)
+ for (auto *Entry : getEntries(C))
Out << LS << Ctx.print(Entry);
});
}
More information about the llvm-commits
mailing list