22#include "mlir/IR/Builders.h"
23#include "mlir/IR/BuiltinTypes.h"
24#include "mlir/IR/IRMapping.h"
25#include "mlir/IR/MLIRContext.h"
26#include "mlir/IR/Matchers.h"
27#include "mlir/IR/PatternMatch.h"
28#include "mlir/IR/Types.h"
29#include "mlir/Pass/Pass.h"
30#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
31#include "llvm/Support/Debug.h"
32#include "llvm/Support/raw_ostream.h"
35#define DEBUG_TYPE "find-initial-vectors"
39#define GEN_PASS_DEF_FINDINITIALVECTORS
40#include "circt/Dialect/Arc/ArcPasses.h.inc"
49struct FindInitialVectorsPass
50 :
public impl::FindInitialVectorsBase<FindInitialVectorsPass> {
51 void runOnOperation()
override;
53 struct StatisticVars {
65struct TopologicalOrder {
68 LogicalResult compute(Block *block);
69 unsigned get(Operation *op)
const {
70 const auto *it = opRanks.find(op);
71 assert(it != opRanks.end() &&
"op has no rank");
80LogicalResult TopologicalOrder::compute(Block *block) {
81 LLVM_DEBUG(llvm::dbgs() <<
"- Computing topological order in block " << block
84 WorklistItem(Operation *op) : userIt(op->user_begin()) {}
85 Operation::user_iterator userIt;
89 for (
auto &op : *block) {
90 if (opRanks.contains(&op))
92 worklist.insert({&op, WorklistItem(&op)});
93 while (!worklist.empty()) {
94 auto &[op, item] = worklist.back();
95 if (
auto stateOp = dyn_cast<StateOp>(op)) {
96 if (stateOp.getLatency() > 0)
97 item.userIt = op->user_end();
98 }
else if (
auto writeOp = dyn_cast<MemoryWritePortOp>(op)) {
99 item.userIt = op->user_end();
101 if (item.userIt == op->user_end()) {
102 opRanks.insert({op, item.rank});
106 if (
auto *rankIt = opRanks.find(*item.userIt); rankIt != opRanks.end()) {
107 item.rank = std::max(item.rank, rankIt->second + 1);
111 if (!worklist.insert({*item.userIt, WorklistItem(*item.userIt)}).second)
112 return op->emitError(
"dependency cycle");
119using Key = std::tuple<unsigned, StringRef, SmallVector<Type>,
120 SmallVector<Type>, DictionaryAttr>;
122Key computeKey(Operation *op,
unsigned rank) {
125 return std::make_tuple(
126 rank, op->getName().getStringRef(),
127 SmallVector<Type>(op->operand_type_begin(), op->operand_type_end()),
128 SmallVector<Type>(op->result_type_begin(), op->result_type_end()),
129 op->getAttrDictionary());
133 Vectorizer(Block *block) : block(block) {}
134 LogicalResult collectSeeds(Block *block) {
135 if (failed(order.compute(block)))
138 for (
auto &[op, rank] : order.opRanks)
139 candidates[computeKey(op, rank)].push_back(op);
144 LogicalResult vectorize(FindInitialVectorsPass::StatisticVars &stat);
147 TopologicalOrder order;
155Vectorizer::vectorize(FindInitialVectorsPass::StatisticVars &stat) {
156 LLVM_DEBUG(llvm::dbgs() <<
"- Vectorizing the ops in block" << block <<
"\n");
158 if (failed(collectSeeds(block)))
162 if (candidates.empty())
166 for (
const auto &[key, ops] : candidates) {
171 if (ops.size() == 1 || ops[0]->getNumResults() != 1 ||
172 ops[0]->getNumOperands() == 0)
176 stat.vecOps += ops.size();
177 stat.savedOps += ops.size() - 1;
178 stat.bigSeedVec = std::max(ops.size(), stat.bigSeedVec);
184 SmallVector<SmallVector<Value, 4>> vectorOperands;
185 vectorOperands.resize(ops[0]->getNumOperands());
187 for (auto [into, operand] :
llvm::zip(vectorOperands, op->getOperands()))
188 into.push_back(operand);
189 SmallVector<ValueRange> operandValueRanges;
190 operandValueRanges.assign(vectorOperands.begin(), vectorOperands.end());
192 SmallVector<Type> resultTypes(ops.size(), ops[0]->getResult(0).getType());
195 ImplicitLocOpBuilder builder(ops[0]->
getLoc(), ops[0]);
197 VectorizeOp::create(builder, resultTypes, operandValueRanges);
204 auto &vectorizeBlock = vectorizeOp.getBody().emplaceBlock();
205 builder.setInsertionPointToStart(&vectorizeBlock);
213 IRMapping argMapping;
214 for (
auto [vecOperand, origOpernad] :
215 llvm::zip(vectorOperands, ops[0]->getOperands())) {
216 auto arg = vectorizeBlock.addArgument(vecOperand[0].getType(),
217 origOpernad.getLoc());
218 argMapping.map(origOpernad, arg);
221 auto *clonedOp = builder.clone(*ops[0], argMapping);
223 VectorizeReturnOp::create(builder, clonedOp->getResult(0));
226 for (
auto [op, result] :
llvm::zip(ops, vectorizeOp->getResults())) {
227 op->getResult(0).replaceAllUsesWith(result);
234void FindInitialVectorsPass::runOnOperation() {
235 for (
auto moduleOp : getOperation().getOps<
hw::HWModuleOp>()) {
236 auto result = moduleOp.walk([&](Block *block) {
237 if (!mayHaveSSADominance(*block->getParent()))
238 if (failed(Vectorizer(block).vectorize(stat)))
239 return WalkResult::interrupt();
240 return WalkResult::advance();
242 if (result.wasInterrupted())
243 return signalPassFailure();
246 numOfVectorizedOps = stat.vecOps;
247 numOfSavedOps = stat.savedOps;
248 biggestSeedVector = stat.bigSeedVec;
249 numOfVectorsCreated = stat.vecCreated;
assert(baseType &&"element must be base type")
static Location getLoc(DefSlot slot)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.