#include "mlir/Analysis/SliceAnalysis.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Operation.h"
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
using namespace mlir;
static void getForwardSliceImpl(Operation *op,
SetVector<Operation *> *forwardSlice,
TransitiveFilter filter) {
if (!op)
return;
if (filter && !filter(op))
return;
for (Region ®ion : op->getRegions())
for (Block &block : region)
for (Operation &blockOp : block)
if (forwardSlice->count(&blockOp) == 0)
getForwardSliceImpl(&blockOp, forwardSlice, filter);
for (Value result : op->getResults()) {
for (Operation *userOp : result.getUsers())
if (forwardSlice->count(userOp) == 0)
getForwardSliceImpl(userOp, forwardSlice, filter);
}
forwardSlice->insert(op);
}
void mlir::getForwardSlice(Operation *op, SetVector<Operation *> *forwardSlice,
TransitiveFilter filter) {
getForwardSliceImpl(op, forwardSlice, filter);
forwardSlice->remove(op);
std::vector<Operation *> v(forwardSlice->takeVector());
forwardSlice->insert(v.rbegin(), v.rend());
}
void mlir::getForwardSlice(Value root, SetVector<Operation *> *forwardSlice,
TransitiveFilter filter) {
for (Operation *user : root.getUsers())
getForwardSliceImpl(user, forwardSlice, filter);
std::vector<Operation *> v(forwardSlice->takeVector());
forwardSlice->insert(v.rbegin(), v.rend());
}
static void getBackwardSliceImpl(Operation *op,
SetVector<Operation *> *backwardSlice,
TransitiveFilter filter) {
if (!op || op->hasTrait<OpTrait::IsIsolatedFromAbove>())
return;
if (filter && !filter(op))
return;
for (const auto &en : llvm::enumerate(op->getOperands())) {
auto operand = en.value();
if (auto *definingOp = operand.getDefiningOp()) {
if (backwardSlice->count(definingOp) == 0)
getBackwardSliceImpl(definingOp, backwardSlice, filter);
} else if (auto blockArg = operand.dyn_cast<BlockArgument>()) {
Block *block = blockArg.getOwner();
Operation *parentOp = block->getParentOp();
assert(parentOp->getNumRegions() == 1 &&
parentOp->getRegion(0).getBlocks().size() == 1);
if (backwardSlice->count(parentOp) == 0)
getBackwardSliceImpl(parentOp, backwardSlice, filter);
} else {
llvm_unreachable("No definingOp and not a block argument.");
}
}
backwardSlice->insert(op);
}
void mlir::getBackwardSlice(Operation *op,
SetVector<Operation *> *backwardSlice,
TransitiveFilter filter) {
getBackwardSliceImpl(op, backwardSlice, filter);
backwardSlice->remove(op);
}
void mlir::getBackwardSlice(Value root, SetVector<Operation *> *backwardSlice,
TransitiveFilter filter) {
if (Operation *definingOp = root.getDefiningOp()) {
getBackwardSlice(definingOp, backwardSlice, filter);
return;
}
Operation *bbAargOwner = root.cast<BlockArgument>().getOwner()->getParentOp();
getBackwardSlice(bbAargOwner, backwardSlice, filter);
}
SetVector<Operation *> mlir::getSlice(Operation *op,
TransitiveFilter backwardFilter,
TransitiveFilter forwardFilter) {
SetVector<Operation *> slice;
slice.insert(op);
unsigned currentIndex = 0;
SetVector<Operation *> backwardSlice;
SetVector<Operation *> forwardSlice;
while (currentIndex != slice.size()) {
auto *currentOp = (slice)[currentIndex];
backwardSlice.clear();
getBackwardSlice(currentOp, &backwardSlice, backwardFilter);
slice.insert(backwardSlice.begin(), backwardSlice.end());
forwardSlice.clear();
getForwardSlice(currentOp, &forwardSlice, forwardFilter);
slice.insert(forwardSlice.begin(), forwardSlice.end());
++currentIndex;
}
return topologicalSort(slice);
}
namespace {
struct DFSState {
DFSState(const SetVector<Operation *> &set)
: toSort(set), topologicalCounts(), seen() {}
const SetVector<Operation *> &toSort;
SmallVector<Operation *, 16> topologicalCounts;
DenseSet<Operation *> seen;
};
}
static void dfsPostorder(Operation *root, DFSState *state) {
SmallVector<Operation *> queue(1, root);
std::vector<Operation *> ops;
while (!queue.empty()) {
Operation *current = queue.pop_back_val();
ops.push_back(current);
for (Value result : current->getResults()) {
for (Operation *op : result.getUsers())
queue.push_back(op);
}
for (Region ®ion : current->getRegions()) {
for (Operation &op : region.getOps())
queue.push_back(&op);
}
}
for (Operation *op : llvm::reverse(ops)) {
if (state->seen.insert(op).second && state->toSort.count(op) > 0)
state->topologicalCounts.push_back(op);
}
}
SetVector<Operation *>
mlir::topologicalSort(const SetVector<Operation *> &toSort) {
if (toSort.empty()) {
return toSort;
}
DFSState state(toSort);
for (auto *s : toSort) {
assert(toSort.count(s) == 1 && "NYI: multi-sets not supported");
dfsPostorder(s, &state);
}
SetVector<Operation *> res;
for (auto it = state.topologicalCounts.rbegin(),
eit = state.topologicalCounts.rend();
it != eit; ++it) {
res.insert(*it);
}
return res;
}
static bool dependsOnCarriedVals(Value value,
ArrayRef<BlockArgument> iterCarriedArgs,
Operation *ancestorOp) {
SetVector<Operation *> slice;
getBackwardSlice(value, &slice,
[&](Operation *op) { return !ancestorOp->isAncestor(op); });
SmallPtrSet<Value, 8> iterCarriedValSet(iterCarriedArgs.begin(),
iterCarriedArgs.end());
if (iterCarriedValSet.contains(value))
return true;
for (Operation *op : slice)
for (Value operand : op->getOperands())
if (iterCarriedValSet.contains(operand))
return true;
return false;
}
Value mlir::matchReduction(ArrayRef<BlockArgument> iterCarriedArgs,
unsigned redPos,
SmallVectorImpl<Operation *> &combinerOps) {
assert(redPos < iterCarriedArgs.size() && "'redPos' is out of bounds");
BlockArgument redCarriedVal = iterCarriedArgs[redPos];
if (!redCarriedVal.hasOneUse())
return nullptr;
Operation *combinerOp = *redCarriedVal.getUsers().begin();
if (combinerOp->getNumOperands() != 2)
return nullptr;
Value reducedVal = combinerOp->getOperand(0) == redCarriedVal
? combinerOp->getOperand(1)
: combinerOp->getOperand(0);
Operation *redRegionOp =
iterCarriedArgs.front().getOwner()->getParent()->getParentOp();
if (dependsOnCarriedVals(reducedVal, iterCarriedArgs, redRegionOp))
return nullptr;
while (!combinerOp->mightHaveTrait<OpTrait::IsTerminator>()) {
if (!MemoryEffectOpInterface::hasNoEffect(combinerOp) ||
combinerOp->getNumResults() != 1 || !combinerOp->hasOneUse() ||
combinerOp->getParentOp() != redRegionOp)
return nullptr;
combinerOps.push_back(combinerOp);
combinerOp = *combinerOp->getUsers().begin();
}
if (combinerOps.size() != 1)
return nullptr;
Operation *terminatorOp = combinerOp;
if (terminatorOp->getOperand(redPos) != combinerOps.back()->getResults()[0])
return nullptr;
return reducedVal;
}