13#include "mlir/IR/IRMapping.h"
14#include "mlir/IR/ImplicitLocOpBuilder.h"
15#include "mlir/Pass/Pass.h"
16#include "llvm/ADT/SetVector.h"
17#include "llvm/Support/Debug.h"
19#define DEBUG_TYPE "arc-split-loops"
23#define GEN_PASS_DEF_SPLITLOOPS
24#include "circt/Dialect/Arc/ArcPasses.h.inc"
31using mlir::CallOpInterface;
56 Split(MLIRContext *
context,
unsigned index,
const APInt &color)
57 : index(index), color(color), block(std::make_unique<
Block>()),
59 builder.setInsertionPointToStart(block.get());
63 void importInput(BlockArgument arg) {
64 importedValues.push_back({
true, arg.getArgNumber(), 0});
65 mapping.map(arg, block->addArgument(arg.getType(), arg.getLoc()));
69 void importFromOtherSplit(Value value, Split &otherSplit) {
70 auto resultIdx = otherSplit.exportValue(value);
71 importedValues.push_back({
false, resultIdx, otherSplit.index});
72 mapping.map(value, block->addArgument(value.getType(), value.getLoc()));
77 unsigned exportValue(Value value) {
78 value = mapping.lookup(value);
79 auto result = exportedValueIndices.insert({value, exportedValues.size()});
81 exportedValues.push_back(value);
82 return result.first->second;
88 std::unique_ptr<Block> block;
93 SmallVector<ImportedValue> importedValues;
95 SmallVector<Value> exportedValues;
96 SmallDenseMap<Value, unsigned> exportedValueIndices;
101 Splitter(MLIRContext *context, Location loc) : context(context), loc(loc) {}
102 void run(Block &block, DenseMap<Operation *, APInt> &coloring);
103 Split &getSplit(
const APInt &color);
105 MLIRContext *context;
109 SmallVector<Split *> splits;
110 SmallDenseMap<APInt, std::unique_ptr<Split>> splitsByColor;
113 SmallVector<ImportedValue> outputs;
118void Splitter::run(Block &block, DenseMap<Operation *, APInt> &coloring) {
119 for (
auto &op : block.without_terminator()) {
120 auto color = coloring.lookup(&op);
121 auto &split = getSplit(color);
124 SmallSetVector<Value, 4> operands;
125 op.walk([&](Operation *op) {
126 for (
auto operand : op->getOperands())
127 if (operand.getParentBlock() == &block)
128 operands.insert(operand);
134 for (
auto operand : operands) {
135 if (split.mapping.contains(operand))
137 if (
auto blockArg = dyn_cast<BlockArgument>(operand)) {
138 split.importInput(blockArg);
141 auto *operandOp = operand.getDefiningOp();
142 auto operandColor = coloring.lookup(operandOp);
143 assert(operandOp && color != operandColor);
144 auto &operandSplit = getSplit(operandColor);
145 split.importFromOtherSplit(operand, operandSplit);
149 split.builder.clone(op, split.mapping);
153 for (
auto operand : block.getTerminator()->getOperands()) {
154 if (
auto blockArg = dyn_cast<BlockArgument>(operand)) {
155 outputs.push_back({
true, blockArg.getArgNumber(), 0});
158 auto &operandSplit = getSplit(coloring.lookup(operand.getDefiningOp()));
159 auto resultIdx = operandSplit.exportValue(operand);
160 outputs.push_back({
false, resultIdx, operandSplit.index});
164 for (
auto &split : splits)
165 arc::OutputOp::create(split->builder, loc, split->exportedValues);
169Split &Splitter::getSplit(
const APInt &color) {
170 auto &split = splitsByColor[color];
172 auto index = splits.size();
173 LLVM_DEBUG(llvm::dbgs()
174 <<
"- Creating split " << index <<
" for " << color <<
"\n");
175 split = std::make_unique<Split>(
context, index, color);
176 splits.push_back(split.get());
186struct SplitLoopsPass :
public arc::impl::SplitLoopsBase<SplitLoopsPass> {
187 void runOnOperation()
override;
188 void splitArc(Namespace &arcNamespace, DefineOp defOp,
189 ArrayRef<CallOpInterface> arcUses);
190 void replaceArcUse(CallOpInterface arcUse, ArrayRef<DefineOp> splitDefs,
191 ArrayRef<Split *> splits, ArrayRef<ImportedValue> outputs);
192 LogicalResult ensureNoLoops();
194 DenseSet<mlir::CallOpInterface> allArcUses;
198void SplitLoopsPass::runOnOperation() {
199 auto module = getOperation();
203 Namespace arcNamespace;
204 DenseMap<StringAttr, DefineOp> arcDefs;
205 for (
auto arcDef : module.getOps<DefineOp>()) {
206 arcNamespace.
newName(arcDef.getSymName());
207 arcDefs[arcDef.getSymNameAttr()] = arcDef;
211 SetVector<DefineOp> arcsToSplit;
212 DenseMap<DefineOp, SmallVector<CallOpInterface>> arcUses;
213 SetVector<CallOpInterface> allArcUses;
215 auto result =
module.walk([&](CallOpInterface callOp) -> WalkResult {
216 auto refSym = dyn_cast<SymbolRefAttr>(callOp.getCallableForCallee());
220 return WalkResult::advance();
221 StringAttr leafRef = refSym.getLeafReference();
222 if (!arcDefs.contains(leafRef))
223 return WalkResult::advance();
225 auto defOp = arcDefs.lookup(leafRef);
226 arcUses[defOp].push_back(callOp);
227 allArcUses.insert(callOp);
229 auto clockedOp = dyn_cast<ClockedOpInterface>(callOp.getOperation());
230 auto dpiCallOp = dyn_cast<sim::DPICallOp>(callOp.getOperation());
231 if ((!clockedOp || clockedOp.getLatency() == 0) &&
232 (!dpiCallOp || !dpiCallOp.getClock()) && callOp->getNumResults() > 1)
233 arcsToSplit.insert(defOp);
235 return WalkResult::advance();
238 if (result.wasInterrupted())
239 return signalPassFailure();
245 for (
auto defOp : arcsToSplit)
246 splitArc(arcNamespace, defOp, arcUses[defOp]);
249 if (failed(ensureNoLoops()))
250 return signalPassFailure();
254void SplitLoopsPass::splitArc(Namespace &arcNamespace, DefineOp defOp,
255 ArrayRef<CallOpInterface> arcUses) {
256 LLVM_DEBUG(llvm::dbgs() <<
"Splitting arc " << defOp.getSymNameAttr()
261 auto numResults = defOp.getNumResults();
262 DenseMap<Value, APInt> valueColoring;
263 DenseMap<Operation *, APInt> opColoring;
265 for (
auto &operand : defOp.getBodyBlock().getTerminator()->getOpOperands())
266 valueColoring.insert(
268 APInt::getOneBitSet(numResults, operand.getOperandNumber())});
270 for (
auto &op : llvm::reverse(defOp.getBodyBlock().without_terminator())) {
271 auto coloring = APInt::getZero(numResults);
272 for (
auto result : op.getResults())
273 if (
auto it = valueColoring.find(result); it != valueColoring.end())
274 coloring |= it->second;
275 opColoring.insert({&op, coloring});
276 op.walk([&](Operation *op) {
277 for (
auto &operand : op->getOpOperands())
278 valueColoring.try_emplace(operand.get(), numResults, 0).first->second |=
284 Splitter splitter(&getContext(), defOp.getLoc());
285 splitter.run(defOp.getBodyBlock(), opColoring);
288 ImplicitLocOpBuilder builder(defOp.getLoc(), defOp);
289 SmallVector<DefineOp> splitArcs;
290 splitArcs.reserve(splitter.splits.size());
291 for (
auto &split : splitter.splits) {
292 auto splitName = defOp.getSymName();
293 if (splitter.splits.size() > 1)
294 splitName = arcNamespace.
newName(defOp.getSymName() +
"_split_" +
295 Twine(split->index));
297 DefineOp::create(builder, splitName,
298 builder.getFunctionType(
299 split->block->getArgumentTypes(),
300 split->block->getTerminator()->getOperandTypes()));
301 splitArc.getBody().push_back(split->block.release());
302 splitArcs.push_back(splitArc);
307 for (
auto arcUse : arcUses)
308 replaceArcUse(arcUse, splitArcs, splitter.splits, splitter.outputs);
314void SplitLoopsPass::replaceArcUse(CallOpInterface arcUse,
315 ArrayRef<DefineOp> splitDefs,
316 ArrayRef<Split *> splits,
317 ArrayRef<ImportedValue> outputs) {
318 ImplicitLocOpBuilder builder(arcUse.getLoc(), arcUse);
319 SmallVector<CallOp> newUses(splits.size());
323 auto getMappedValue = [&](ImportedValue value) {
325 return arcUse.getArgOperands()[value.index];
326 return newUses[value.split].getResult(value.index);
332 DenseMap<unsigned, unsigned> splitIdxMap;
333 for (
auto [i, split] : llvm::enumerate(splits))
334 splitIdxMap[split->index] = i;
336 DenseSet<unsigned> splitsDone;
337 SmallVector<std::pair<const DefineOp, const Split *>> worklist;
339 auto getMappedValuesOrSchedule = [&](ArrayRef<ImportedValue> importedValues,
340 SmallVector<Value> &operands) {
341 for (
auto importedValue : importedValues) {
342 if (!importedValue.isInput && !splitsDone.contains(importedValue.split)) {
343 unsigned idx = splitIdxMap[importedValue.split];
344 worklist.push_back({splitDefs[idx], splits[idx]});
348 operands.push_back(getMappedValue(importedValue));
355 for (
auto [splitDef, split] : llvm::reverse(llvm::zip(splitDefs, splits)))
356 worklist.push_back({splitDef, split});
359 while (!worklist.empty()) {
360 auto [splitDef, split] = worklist.back();
362 if (splitsDone.contains(split->index)) {
367 SmallVector<Value> operands;
368 if (!getMappedValuesOrSchedule(split->importedValues, operands))
371 auto newUse = CallOp::create(builder, splitDef, operands);
372 allArcUses.insert(newUse);
373 newUses[split->index] = newUse;
375 splitsDone.insert(split->index);
380 for (
auto [result, importedValue] : llvm::zip(arcUse->getResults(), outputs))
381 result.replaceAllUsesWith(getMappedValue(importedValue));
382 allArcUses.erase(arcUse);
387LogicalResult SplitLoopsPass::ensureNoLoops() {
388 SmallVector<std::pair<Operation *, unsigned>, 0> worklist;
389 DenseSet<Operation *> finished;
390 DenseSet<Operation *> seen;
391 for (
auto op : allArcUses) {
392 if (finished.contains(op))
395 worklist.push_back({op, 0});
396 while (!worklist.empty()) {
397 auto [op, idx] = worklist.back();
398 ++worklist.back().second;
399 if (idx == op->getNumOperands()) {
405 auto operand = op->getOperand(idx);
406 auto *def = operand.getDefiningOp();
407 if (!def || finished.contains(def))
409 if (
auto clockedOp = dyn_cast<ClockedOpInterface>(def);
410 clockedOp && clockedOp.getLatency() > 0)
412 if (
auto dpiCallOp = dyn_cast<sim::DPICallOp>(def))
413 if (dpiCallOp.getClock())
415 if (!seen.insert(def).second) {
416 auto d = def->emitError(
417 "loop splitting did not eliminate all loops; loop detected");
418 for (
auto [op, idx] : llvm::reverse(worklist)) {
419 d.attachNote(op->getLoc())
420 <<
"through operand " << (idx - 1) <<
" here:";
426 worklist.push_back({def, 0});
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
false return failure()
Parse an optional keyword or string and set instance into 'result'.
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, List[str] cmdline_args=sys.argv)