123#ifndef CIRCT_SUPPORT_SPARSEOPSCC_H
124#define CIRCT_SUPPORT_SPARSEOPSCC_H
126#include "mlir/IR/Operation.h"
127#include "llvm/ADT/ArrayRef.h"
128#include "llvm/ADT/DenseMap.h"
129#include "llvm/ADT/PointerEmbeddedInt.h"
130#include "llvm/ADT/PointerUnion.h"
131#include "llvm/ADT/STLFunctionalExtras.h"
132#include "llvm/ADT/SetVector.h"
133#include "llvm/ADT/SmallVector.h"
134#include <type_traits>
144using OpSCCFilter = std::function<bool(mlir::Operation *, mlir::OpOperand &)>;
157 using iterator = detail::CyclicOpSCCStorage::const_iterator;
168 operator bool()
const {
return storage !=
nullptr; }
198 static constexpr int NumLowBitsAvailable =
211using OpSCC = llvm::PointerUnion<void *, mlir::Operation *, CyclicOpSCC>;
218template <OpSCCDirection,
unsigned>
223using OpOrIndex = llvm::PointerUnion<mlir::Operation *, OpSccEmbeddedIndex>;
226template <
typename BaseIteratorT>
228 :
public llvm::mapped_iterator_base<OpSCCIterator<BaseIteratorT>,
229 BaseIteratorT, OpSCC> {
232 OpSCC>::mapped_iterator_base;
235 if (llvm::isa<mlir::Operation *>(opOrIndex))
236 return llvm::cast<mlir::Operation *>(opOrIndex);
237 unsigned index = llvm::cast<OpSccEmbeddedIndex>(opOrIndex);
242 template <OpSCCDirection,
unsigned>
246 const llvm::ArrayRef<CyclicOpSCCStorage>
cyclicSccs)
269template <OpSCCDirection Direction,
unsigned NumInlineElts = 32>
289 void visit(llvm::ArrayRef<mlir::Operation *> ops) {
305 return OpSCC(
nullptr);
307 if (llvm::isa<mlir::Operation *>(entry))
308 return OpSCC(llvm::cast<mlir::Operation *>(entry));
309 unsigned cyclicIdx = llvm::cast<detail::OpSccEmbeddedIndex>(entry);
332 typename decltype(
sccs)::const_reverse_iterator>(
sccs.rbegin(),
343 typename decltype(
sccs)::const_reverse_iterator>(
sccs.rend(),
360 typename decltype(
sccs)::const_reverse_iterator>(
sccs.rbegin(),
371 typename decltype(
sccs)::const_reverse_iterator>(
sccs.rend(),
379 std::optional<mlir::Value::use_iterator>
useIt;
385 if (
op->getNumResults() > 0)
386 useIt =
op->getResult(0).use_begin();
390 while (resultIdx < op->getNumResults()) {
392 while (*
useIt != useEnd) {
393 mlir::OpOperand &use = **
useIt;
396 return use.getOwner();
399 if (resultIdx < op->getNumResults())
415 while (operandIdx < op->getNumOperands()) {
416 mlir::OpOperand &operand =
op->getOpOperand(
operandIdx++);
417 auto *defOp = operand.get().getDefiningOp();
429 unsigned nextIdx = 0;
432 llvm::SetVector<mlir::Operation *> sccStack;
433 llvm::SmallVector<FrameT> dfsStack;
435 auto pushFrame = [&](mlir::Operation *op) {
436 idxAndLowLinkMap[op] = {nextIdx, nextIdx};
439 dfsStack.push_back(
FrameT(op));
444 while (!dfsStack.empty()) {
445 FrameT &frame = dfsStack.back();
446 mlir::Operation *op = frame.op;
451 frame.hasSelfLoop =
true;
453 auto it = idxAndLowLinkMap.find(child);
454 if (it != idxAndLowLinkMap.end()) {
456 if (sccStack.contains(child))
458 idxAndLowLinkMap[op].second =
459 std::min(idxAndLowLinkMap[op].second, it->second.first);
471 bool selfLoop = frame.hasSelfLoop;
472 auto [opIndex, opLowLink] = idxAndLowLinkMap.at(op);
476 if (opLowLink == opIndex) {
479 sccOps.push_back(sccStack.pop_back_val());
480 }
while (sccOps.back() != op);
483 unsigned sccIdx =
sccs.size();
484 for (
auto *sccOp : sccOps) {
485 bool inserted =
opToSccIndex.insert({sccOp, sccIdx}).second;
487 assert(inserted &&
"Unexpectedly revisited node");
491 if (sccOps.size() == 1 && !selfLoop) {
502 auto &parentLowLink = idxAndLowLinkMap.at(dfsStack.back().op).second;
503 parentLowLink = std::min(parentLowLink, opLowLink);
517 llvm::SmallVector<detail::OpOrIndex, NumInlineElts>
sccs;
assert(baseType &&"element must be base type")
A cyclic SCC: a pointer-sized, directly-iterable reference to a group of mutually-reachable operation...
bool operator==(CyclicOpSCC other) const
CyclicOpSCC(const detail::CyclicOpSCCStorage *storage)
static constexpr int NumLowBitsAvailable
detail::CyclicOpSCCStorage::const_iterator iterator
static CyclicOpSCC getFromVoidPointer(void *p)
bool operator!=(CyclicOpSCC other) const
void * getAsVoidPointer() const
mlir::Operation *const * data() const
mlir::Operation * operator[](size_t i) const
const detail::CyclicOpSCCStorage * storage
Iterative Tarjan SCC analysis on a sparse subgraph of MLIR operations.
void reset()
Clear all accumulated state.
auto reverseTopological_end() const
void visit(mlir::Operation *op)
Seed op into the DFS if it has not already been discovered.
llvm::SmallDenseMap< mlir::Operation *, unsigned, NumInlineElts > opToSccIndex
Maps each visited op to the index of its SCC in sccs.
auto reverseTopological() const
Iterate over SCCs in reverse topological order (leaves first).
unsigned getNumCyclicSCCs() const
Number of cyclic SCC groups (excludes trivial ops).
OpSCC getSCC(mlir::Operation *op) const
Return the SCC that op belongs to.
std::conditional_t< Direction==OpSCCDirection::Forward, ForwardFrame, BackwardFrame > FrameT
void tarjanImpl(mlir::Operation *startOp)
llvm::SmallVector< detail::CyclicOpSCCStorage, 0 > cyclicSccs
Backing storage for cyclic SCCs; CyclicOpSCC holds a pointer into here.
llvm::SmallVector< detail::OpOrIndex, NumInlineElts > sccs
Flat list of SCC entries emitted by Tarjan, in emission order.
auto topological_end() const
void visit(llvm::ArrayRef< mlir::Operation * > ops)
Visit each operation in ops, skipping already-discovered ones.
bool hasDiscovered(mlir::Operation *op) const
Return true if op was discovered (as a seed or transitively) by any previous visit() call.
unsigned getNumDiscovered() const
Number of operations discovered so far across all visit() calls.
auto topological() const
Iterate over SCCs in topological order (sources/seeds first, leaves last).
SparseOpSCC(OpSCCFilter shouldTraverseFn={})
unsigned getNumSCCs() const
Total number of SCC entries emitted (trivial ops + cyclic groups).
auto reverseTopological_begin() const
OpSCCFilter shouldTraverseFn
Optional edge filter supplied at construction time.
auto topological_begin() const
OpSCCIterator(BaseIteratorT it, const llvm::ArrayRef< CyclicOpSCCStorage > cyclicSccs)
OpSCC mapElement(OpOrIndex opOrIndex) const
const llvm::ArrayRef< CyclicOpSCCStorage > cyclicSccs
llvm::PointerUnion< mlir::Operation *, OpSccEmbeddedIndex > OpOrIndex
llvm::PointerEmbeddedInt< unsigned, 31 > OpSccEmbeddedIndex
llvm::SmallVector< mlir::Operation *, 4 > CyclicOpSCCStorage
Backing storage for a cyclic SCC (implementation detail).
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::function< bool(mlir::Operation *, mlir::OpOperand &)> OpSCCFilter
Filter predicate passed to the SparseOpSCC constructor.
OpSCCDirection
Traversal direction for SparseOpSCC.
llvm::PointerUnion< void *, mlir::Operation *, CyclicOpSCC > OpSCC
One entry in the SCC output: a null sentinel, a trivial (non-cyclic) operation, or a cyclic group.
mlir::Operation * nextChild(OpSCCFilter shouldTraverseFn)
BackwardFrame(mlir::Operation *op)
ForwardFrame(mlir::Operation *op)
std::optional< mlir::Value::use_iterator > useIt
mlir::Operation * nextChild(OpSCCFilter shouldTraverseFn)
static circt::CyclicOpSCC getFromVoidPointer(void *p)
static void * getAsVoidPointer(circt::CyclicOpSCC scc)