15#include "mlir/Analysis/TopologicalSortUtils.h"
16#include "mlir/IR/SymbolTable.h"
17#include "mlir/Pass/Pass.h"
18#include "llvm/ADT/DenseMap.h"
19#include "llvm/ADT/SetVector.h"
21#define DEBUG_TYPE "arc-outline"
25#define GEN_PASS_DEF_OUTLINEARCSPASS
26#include "circt/Dialect/Arc/ArcPasses.h.inc"
33using llvm::BumpPtrAllocator;
34using llvm::SmallDenseSet;
38 :
public arc::impl::OutlineArcsPassBase<OutlineArcsPass> {
39 void runOnOperation()
override;
40 friend struct Colorer;
41 friend struct Outliner;
46 return isa<ClockedOpInterface, seq::Clocked, CallOp>(op) ||
47 op->hasTrait<OpTrait::IsTerminator>() ||
48 op->hasTrait<OpTrait::ConstantLike>() ||
49 !mlir::isMemoryEffectFree(op) ||
51 isa<llhd::ProbeOp>(op);
67 ArrayRef<unsigned> terminals;
70static llvm::raw_ostream &
operator<<(llvm::raw_ostream &os,
72 os <<
"#" << color.index <<
"[";
73 llvm::interleaveComma(color.terminals, os);
79struct ColorInfo : DenseMapInfo<Color *> {
80 static unsigned getHashValue(
const Color *color) {
81 return llvm::hash_combine_range(color->terminals);
83 static unsigned getHashValue(ArrayRef<unsigned> terminals) {
84 return llvm::hash_combine_range(terminals);
86 static bool isEqual(
const Color *lhs,
const Color *rhs) {
87 return lhs == rhs || lhs->terminals == rhs->terminals;
89 static bool isEqual(ArrayRef<unsigned> lhs,
const Color *rhs) {
90 return lhs == rhs->terminals;
96 DenseSet<Color *, ColorInfo> colors;
97 BumpPtrAllocator alloc;
99 Color *
get(ArrayRef<unsigned> terminals) {
101 if (
auto it = colors.find_as(terminals); it != colors.end())
105 auto *color =
new (alloc) Color{colors.size(), terminals.copy(alloc)};
106 colors.insert(color);
116 DenseMap<Operation *, Color *> colors;
121 Colorer(hw::HWModuleOp moduleOp, OutlineArcsPass &pass)
122 : moduleOp(moduleOp), pass(pass) {}
125 void colorOpFanout(Operation *op);
126 bool updateOpColor(Operation *op);
128 hw::HWModuleOp moduleOp;
129 OutlineArcsPass &pass;
134 DenseMap<OpOperand *, unsigned> terminals;
137 struct WorklistItem {
139 Operation::use_iterator use,
end;
141 SmallVector<WorklistItem> worklist;
146 SetVector<Operation *> dirtyOps;
152Coloring Colorer::run() {
154 for (
auto &op : *moduleOp.getBodyBlock())
160 while (!dirtyOps.empty()) {
161 auto *op = dirtyOps.pop_back_val();
163 if (updateOpColor(op)) {
164 op->walk([&](Operation *op) {
165 for (
auto operand : op->getOperands())
166 if (
auto *defOp = operand.getDefiningOp())
167 if (defOp->getBlock() == moduleOp.getBodyBlock())
169 dirtyOps.insert(defOp);
174 return std::move(coloring);
178void Colorer::colorOpFanout(Operation *op) {
181 if (!coloring.colors.insert({op, nullptr}).second)
186 worklist.push_back({op, op->use_begin(), op->use_end()});
187 while (!worklist.empty()) {
188 auto &item = worklist.back();
189 if (item.use == item.end) {
192 updateOpColor(item.op);
197 auto *userOp = (item.use++)->getOwner();
198 userOp = moduleOp.getBodyBlock()->findAncestorOpInBlock(*userOp);
204 if (coloring.colors.insert({userOp, nullptr}).second)
205 worklist.push_back({userOp, userOp->use_begin(), userOp->use_end()});
212bool Colorer::updateOpColor(Operation *op) {
214 SmallDenseSet<unsigned, 8> fanoutTerminals;
215 for (
auto &use : op->getUses()) {
218 auto *userOp = use.getOwner();
219 userOp = moduleOp.getBodyBlock()->findAncestorOpInBlock(*userOp);
224 auto terminal = terminals.insert({&use, terminals.size()}).first->second;
225 fanoutTerminals.insert(terminal);
226 }
else if (
auto *color = coloring.colors.at(userOp)) {
228 fanoutTerminals.insert_range(color->terminals);
238 SmallVector<unsigned, 8> sortedTerminals;
239 sortedTerminals.reserve(fanoutTerminals.size());
240 sortedTerminals.append(fanoutTerminals.begin(), fanoutTerminals.end());
241 llvm::sort(sortedTerminals);
244 auto &color = coloring.colors[op];
245 auto *oldColor = color;
246 color = coloring.table.get(sortedTerminals);
248 if (color != oldColor)
250 pass.numVacuousUpdates++;
263 std::unique_ptr<Block> block;
264 SmallVector<OpOperand *> forwardRefs;
271 Outliner(hw::HWModuleOp moduleOp, SymbolTable &symbolTable,
272 Coloring &coloring, OutlineArcsPass &pass)
273 : moduleOp(moduleOp), symbolTable(symbolTable), coloring(coloring),
277 void outlineOp(Operation *op);
278 void sortAndBreakCycles(OutlinedArc &arc);
279 void createTerminator(OutlinedArc &arc);
280 void createAndCallArc(OutlinedArc &arc, OpBuilder &arcBuilder,
281 OpBuilder &callBuilder);
282 void useCallResults(OutlinedArc &arc);
284 hw::HWModuleOp moduleOp;
285 SymbolTable &symbolTable;
287 OutlineArcsPass &pass;
290 MapVector<Color *, OutlinedArc> arcs;
295void Outliner::run() {
297 for (
auto &op : llvm::make_early_inc_range(*moduleOp.getBodyBlock()))
300 pass.numArcs += arcs.size();
303 for (
auto &[color, arc] : arcs)
304 sortAndBreakCycles(arc);
307 for (
auto &[color, arc] : arcs)
308 createTerminator(arc);
311 OpBuilder arcBuilder(moduleOp);
312 OpBuilder callBuilder(moduleOp.getBodyBlock()->getTerminator());
313 for (
auto &[color, arc] : arcs)
314 createAndCallArc(arc, arcBuilder, callBuilder);
318 for (
auto &[color, arc] : arcs)
323void Outliner::outlineOp(Operation *op) {
324 auto *color = coloring.colors.at(op);
325 auto &arc = arcs[color];
327 arc.index = arcs.size() - 1;
328 arc.block = std::make_unique<Block>();
329 LLVM_DEBUG(llvm::dbgs() <<
"- Create arc " << arc.index <<
" for color "
332 op->moveBefore(arc.block.get(), arc.block->end());
338void Outliner::sortAndBreakCycles(OutlinedArc &arc) {
342 mlir::sortTopologically(arc.block.get());
345 arc.block->walk([&](Operation *op) {
346 auto *blockOp = arc.block->findAncestorOpInBlock(*op);
347 for (
auto &operand : op->getOpOperands())
348 if (
auto *defOp = operand.get().getDefiningOp())
349 if (defOp->getBlock() == arc.block.get())
350 if (defOp == op || !defOp->isBeforeInBlock(blockOp))
351 arc.forwardRefs.push_back(&operand);
353 pass.numForwardRefs += arc.forwardRefs.size();
356 if (!arc.forwardRefs.empty())
357 llvm::dbgs() <<
"- Arc " << arc.index <<
" has " << arc.forwardRefs.size()
358 <<
" forward references\n";
362void Outliner::createTerminator(OutlinedArc &arc) {
364 SmallSetVector<Value, 8> results;
365 for (
auto &op : *arc.block) {
366 for (
auto result : op.getResults()) {
367 for (
auto *userOp : result.getUsers()) {
368 if (!arc.block->findAncestorOpInBlock(*userOp)) {
369 results.insert(result);
377 for (
auto *operand : arc.forwardRefs)
378 results.insert(operand->get());
381 OpBuilder builder(moduleOp);
382 builder.setInsertionPointToEnd(arc.block.get());
384 OutputOp::create(builder, moduleOp.getLoc(), results.getArrayRef());
387void Outliner::createAndCallArc(OutlinedArc &arc, OpBuilder &arcBuilder,
388 OpBuilder &callBuilder) {
391 SmallVector<Value, 8> values;
392 SmallMapVector<Value, Value, 8> args;
394 auto addArg = [&](Value value) {
395 auto &arg = args[value];
397 arg = arc.block->addArgument(value.getType(), value.getLoc());
398 values.push_back(value);
403 OpBuilder constBuilder(arc.terminator);
404 constBuilder.setInsertionPointToStart(arc.block.get());
406 arc.block->walk([&](Operation *op) {
407 for (
auto &operand : op->getOpOperands()) {
413 auto *defBlock = operand.get().getParentBlock();
414 while (defBlock && defBlock->getParentOp())
415 defBlock = defBlock->getParentOp()->getBlock();
416 if (defBlock == arc.block.get())
420 if (
auto *defOp = operand.get().getDefiningOp();
421 defOp && defOp->hasTrait<OpTrait::ConstantLike>()) {
422 if (!args.count(operand.get())) {
423 pass.numClonedConstants++;
424 auto *clonedOp = constBuilder.clone(*defOp);
425 for (
auto [oldResult, newResult] :
426 llvm::zip(defOp->getResults(), clonedOp->getResults()))
427 args[oldResult] = newResult;
429 operand.set(args.at(operand.get()));
434 operand.set(addArg(operand.get()));
439 for (
auto *operand : arc.forwardRefs)
440 operand->set(addArg(operand->get()));
443 auto arcOp = DefineOp::create(
444 arcBuilder, arc.terminator.getLoc(),
445 arcBuilder.getStringAttr(moduleOp.getModuleName() +
"_arc" +
447 arcBuilder.getFunctionType(arc.block->getArgumentTypes(),
448 arc.terminator->getOperandTypes()));
449 arcOp.getBody().push_back(arc.block.release());
450 symbolTable.insert(arcOp);
453 arc.call = CallOp::create(callBuilder, arcOp.getLoc(), arcOp, values);
458void Outliner::useCallResults(OutlinedArc &arc) {
459 auto *arcBlock = arc.terminator->getBlock();
460 for (
auto [valueInside, valueOutside] :
461 llvm::zip(arc.terminator.getOperands(), arc.call.getResults())) {
462 valueInside.replaceUsesWithIf(valueOutside, [&](OpOperand &use) {
463 return !arcBlock->findAncestorOpInBlock(*use.getOwner());
472void OutlineArcsPass::runOnOperation() {
473 auto &symbolTable = getAnalysis<SymbolTable>();
474 for (
auto moduleOp : getOperation().getOps<hw::HWModuleOp>()) {
475 LLVM_DEBUG(llvm::dbgs()
476 <<
"Outline arcs from @" << moduleOp.getModuleName() <<
"\n");
477 auto coloring = Colorer(moduleOp, *
this).run();
478 Outliner(moduleOp, symbolTable, coloring, *
this).run();
static bool isArcBreaker(Operation *op)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
OS & operator<<(OS &os, const InnerSymTarget &target)
Printing InnerSymTarget's.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, List[str] cmdline_args=sys.argv)