17#include "mlir/IR/PatternMatch.h"
18#include "mlir/Pass/Pass.h"
19#include "mlir/Transforms/DialectConversion.h"
20#include "mlir/Transforms/RegionUtils.h"
21#include "llvm/Support/Debug.h"
23#define DEBUG_TYPE "convert-to-arcs"
29using mlir::ConversionConfig;
34 return op->hasTrait<OpTrait::ConstantLike>() ||
36 ClockedOpInterface, seq::InitialOp, seq::ClockGateOp,
37 sim::DPICallOp, llhd::ProbeOp>(op) ||
38 op->getNumResults() > 1 || op->getNumRegions() > 0 ||
39 !mlir::isMemoryEffectFree(op);
43 SmallVectorImpl<Value> &values) {
44 if (!reg.getInitialValue())
45 return values.push_back({}),
success();
49 OpBuilder builder(reg);
50 auto init = seq::FromImmutableOp::create(builder, reg.getLoc(), reg.getType(),
51 reg.getInitialValue());
53 values.push_back(init);
63 LogicalResult
run(ModuleOp module);
64 LogicalResult runOnModule(HWModuleOp module);
65 LogicalResult analyzeFanIn();
66 void extractArcs(HWModuleOp module);
67 LogicalResult absorbRegs(HWModuleOp module);
70 Namespace globalNamespace;
73 SmallVector<Operation *> arcBreakers;
74 SmallDenseMap<Operation *, unsigned> arcBreakerIndices;
77 SmallVector<Operation *> postOrder;
81 MapVector<Operation *, APInt> faninMasks;
84 MapVector<APInt, DenseSet<Operation *>> faninMaskGroups;
87 SmallVector<mlir::CallOpInterface> arcUses;
95LogicalResult Converter::run(ModuleOp module) {
96 for (
auto &op : module.getOps())
97 if (
auto sym = dyn_cast<mlir::SymbolOpInterface>(&op))
98 globalNamespace.
newName(sym.getName());
99 for (
auto module : module.getOps<HWModuleOp>())
100 if (failed(runOnModule(module)))
105LogicalResult Converter::runOnModule(HWModuleOp module) {
108 arcBreakerIndices.clear();
109 for (Operation &op : *module.getBodyBlock()) {
110 if (isa<seq::InitialOp>(&op))
114 arcBreakerIndices[&op] = arcBreakers.size();
115 arcBreakers.push_back(&op);
118 if (module.getBodyBlock()->without_terminator().empty() &&
119 isa<hw::OutputOp>(module.getBodyBlock()->getTerminator()))
121 LLVM_DEBUG(llvm::dbgs() <<
"Analyzing " << module.getModuleNameAttr() <<
" ("
122 << arcBreakers.size() <<
" breakers)\n");
127 if (failed(analyzeFanIn()))
133 if (failed(absorbRegs(module)))
139LogicalResult Converter::analyzeFanIn() {
140 SmallVector<std::tuple<Operation *, SmallVector<Value, 2>>> worklist;
141 SetVector<Value> seenOperands;
142 auto addToWorklist = [&](Operation *op) {
143 seenOperands.clear();
144 for (
auto operand : op->getOperands())
145 seenOperands.insert(operand);
146 mlir::getUsedValuesDefinedAbove(op->getRegions(), seenOperands);
147 worklist.emplace_back(op, seenOperands.getArrayRef());
152 for (
auto *op : arcBreakers) {
153 unsigned index = arcBreakerIndices.lookup(op);
154 auto mask = APInt::getOneBitSet(arcBreakers.size(), index);
155 faninMasks[op] =
mask;
160 DenseSet<Operation *> seen;
161 DenseSet<Operation *> finished;
163 while (!worklist.empty()) {
164 auto &[op, operands] = worklist.back();
165 if (operands.empty()) {
167 postOrder.push_back(op);
173 auto operand = operands.pop_back_val();
174 auto *definingOp = operand.getDefiningOp();
176 finished.contains(definingOp))
178 if (!seen.insert(definingOp).second) {
179 definingOp->emitError(
"combinational loop detected");
182 addToWorklist(definingOp);
184 LLVM_DEBUG(llvm::dbgs() <<
"- Sorted " << postOrder.size() <<
" ops\n");
190 for (
auto *op : llvm::reverse(postOrder)) {
191 auto mask = APInt::getZero(arcBreakers.size());
192 for (
auto *user : op->getUsers()) {
193 while (user->getParentOp() != op->getParentOp())
194 user = user->getParentOp();
195 auto *it = faninMasks.find(user);
196 if (it != faninMasks.end())
200 auto duplicateOp = faninMasks.insert({op,
mask});
202 assert(duplicateOp.second &&
"duplicate op in order");
206 faninMaskGroups.clear();
207 for (
auto [op, mask] : faninMasks)
209 faninMaskGroups[
mask].insert(op);
210 LLVM_DEBUG(llvm::dbgs() <<
"- Found " << faninMaskGroups.size()
211 <<
" fanin mask groups\n");
216void Converter::extractArcs(HWModuleOp module) {
217 DenseMap<Value, Value> valueMapping;
218 SmallVector<Value> inputs;
219 SmallVector<Value> outputs;
220 SmallVector<Type> inputTypes;
221 SmallVector<Type> outputTypes;
222 SmallVector<std::pair<OpOperand *, unsigned>> externalUses;
225 for (
auto &group : faninMaskGroups) {
226 auto &opSet = group.second;
227 OpBuilder builder(module);
229 auto block = std::make_unique<Block>();
230 builder.setInsertionPointToStart(block.get());
231 valueMapping.clear();
236 externalUses.clear();
238 Operation *lastOp =
nullptr;
240 for (
auto *op : postOrder) {
241 if (!opSet.contains(op))
246 for (
auto &operand : op->getOpOperands()) {
247 if (opSet.contains(operand.get().getDefiningOp()))
249 auto &mapped = valueMapping[operand.get()];
251 mapped = block->addArgument(operand.get().getType(),
252 operand.get().getLoc());
253 inputs.push_back(operand.get());
254 inputTypes.push_back(mapped.getType());
258 for (
auto result : op->getResults()) {
259 bool anyExternal =
false;
260 for (
auto &use : result.getUses()) {
261 if (!opSet.contains(use.getOwner())) {
263 externalUses.push_back({&use, outputs.size()});
267 outputs.push_back(result);
268 outputTypes.push_back(result.getType());
273 arc::OutputOp::create(builder, lastOp->getLoc(), outputs);
276 builder.setInsertionPoint(module);
278 DefineOp::create(builder, lastOp->getLoc(),
279 builder.getStringAttr(globalNamespace.
newName(
280 module.getModuleName() +
"_arc")),
281 builder.getFunctionType(inputTypes, outputTypes));
282 defOp.getBody().push_back(block.release());
286 builder.setInsertionPoint(module.getBodyBlock()->getTerminator());
287 auto arcOp = CallOp::create(builder, lastOp->getLoc(), defOp, inputs);
288 arcUses.push_back(arcOp);
289 for (
auto [use, resultIdx] : externalUses)
290 use->set(arcOp.getResult(resultIdx));
294LogicalResult Converter::absorbRegs(HWModuleOp module) {
298 unsigned numTrivialRegs = 0;
299 for (
auto callOp : arcUses) {
300 auto stateOp = dyn_cast<StateOp>(callOp.getOperation());
301 Value clock = stateOp ? stateOp.getClock() : Value{};
303 SmallVector<Value> initialValues;
304 SmallVector<seq::CompRegOp> absorbedRegs;
305 SmallVector<Attribute> absorbedNames(callOp->getNumResults(), {});
306 if (
auto names = callOp->getAttrOfType<ArrayAttr>(
"names"))
307 absorbedNames.assign(names.getValue().begin(), names.getValue().end());
312 bool isTrivial =
true;
313 for (
auto result : callOp->getResults()) {
314 if (!result.hasOneUse()) {
318 auto regOp = dyn_cast<seq::CompRegOp>(result.use_begin()->getOwner());
319 if (!regOp || regOp.getInput() != result ||
320 (clock && clock != regOp.getClk())) {
325 clock = regOp.getClk();
326 reset = regOp.getReset();
330 Value resetValue = regOp.getResetValue();
331 Operation *op = resetValue.getDefiningOp();
333 return regOp->emitOpError(
334 "is reset by an input; not supported by ConvertToArcs");
335 if (
auto constant = dyn_cast<hw::ConstantOp>(op)) {
336 if (constant.getValue() != 0)
337 return regOp->emitOpError(
"is reset to a constant non-zero value; "
338 "not supported by ConvertToArcs");
340 return regOp->emitOpError(
"is reset to a value that is not clearly "
341 "constant; not supported by ConvertToArcs");
348 absorbedRegs.push_back(regOp);
352 absorbedNames[result.getResultNumber()] = regOp.getNameAttr();
357 arcUses[outIdx++] = callOp;
366 auto arc = dyn_cast<StateOp>(callOp.getOperation());
368 arc.getClockMutable().assign(clock);
369 arc.setLatency(arc.getLatency() + 1);
371 mlir::IRRewriter rewriter(module->getContext());
372 rewriter.setInsertionPoint(callOp);
373 arc = rewriter.replaceOpWithNewOp<StateOp>(
374 callOp.getOperation(),
375 llvm::cast<SymbolRefAttr>(callOp.getCallableForCallee()),
376 callOp->getResultTypes(), clock, Value{}, 1, callOp.getArgOperands());
381 return arc.emitError(
382 "StateOp tried to infer reset from CompReg, but already "
384 arc.getResetMutable().assign(reset);
387 bool onlyDefaultInitializers =
388 llvm::all_of(initialValues, [](
auto val) ->
bool {
return !val; });
390 if (!onlyDefaultInitializers) {
391 if (!arc.getInitials().empty()) {
392 return arc.emitError(
393 "StateOp tried to infer initial values from CompReg, but already "
394 "had an initial value.");
397 for (
unsigned i = 0; i < initialValues.size(); ++i) {
398 if (!initialValues[i]) {
399 OpBuilder zeroBuilder(arc);
400 initialValues[i] = zeroBuilder.createOrFold<hw::ConstantOp>(
402 zeroBuilder.getIntegerAttr(arc.getResult(i).getType(), 0));
405 arc.getInitialsMutable().assign(initialValues);
408 if (tapRegisters && llvm::any_of(absorbedNames, [](
auto name) {
409 return !cast<StringAttr>(name).getValue().empty();
411 arc->setAttr(
"names", ArrayAttr::get(module.getContext(), absorbedNames));
412 for (
auto [arcResult, reg] : llvm::zip(arc.getResults(), absorbedRegs)) {
413 auto it = arcBreakerIndices.find(reg);
414 arcBreakers[it->second] = {};
415 arcBreakerIndices.erase(it);
416 reg.replaceAllUsesWith(arcResult);
420 if (numTrivialRegs > 0)
421 LLVM_DEBUG(llvm::dbgs() <<
"- Trivially converted " << numTrivialRegs
422 <<
" regs to arcs\n");
423 arcUses.truncate(outIdx);
428 MapVector<std::tuple<Value, Value, Operation *>, SmallVector<seq::CompRegOp>>
430 for (
auto *op : arcBreakers)
431 if (
auto regOp = dyn_cast_or_null<seq::CompRegOp>(op)) {
432 regsByInput[{regOp.getClk(), regOp.getReset(),
433 regOp.getInput().getDefiningOp()}]
437 unsigned numMappedRegs = 0;
442 SmallVector<std::pair<seq::CompRegOp, Value>> regReplacements;
443 for (
auto [clockAndResetAndOp, regOps] : regsByInput) {
444 numMappedRegs += regOps.size();
445 OpBuilder builder(module);
446 auto block = std::make_unique<Block>();
447 builder.setInsertionPointToStart(block.get());
449 SmallVector<Value> inputs;
450 SmallVector<Value> outputs;
451 SmallVector<Attribute> names;
452 SmallVector<Type> types;
453 SmallVector<Value> initialValues;
454 SmallDenseMap<Value, unsigned> mapping;
455 SmallVector<unsigned> regToOutputMapping;
456 for (
auto regOp : regOps) {
457 auto it = mapping.find(regOp.getInput());
458 if (it == mapping.end()) {
459 it = mapping.insert({regOp.getInput(), inputs.size()}).first;
460 inputs.push_back(regOp.getInput());
461 types.push_back(regOp.getType());
462 outputs.push_back(block->addArgument(regOp.getType(), regOp.getLoc()));
463 names.push_back(regOp->getAttrOfType<StringAttr>(
"name"));
467 regToOutputMapping.push_back(it->second);
470 auto loc = regOps.back().getLoc();
471 arc::OutputOp::create(builder, loc, outputs);
473 builder.setInsertionPoint(module);
474 auto defOp = DefineOp::create(builder, loc,
475 builder.getStringAttr(globalNamespace.
newName(
476 module.getModuleName() +
"_arc")),
477 builder.getFunctionType(types, types));
478 defOp.getBody().push_back(block.release());
480 builder.setInsertionPoint(module.getBodyBlock()->getTerminator());
482 bool onlyDefaultInitializers =
483 llvm::all_of(initialValues, [](
auto val) ->
bool {
return !val; });
485 if (onlyDefaultInitializers)
486 initialValues.clear();
488 for (
unsigned i = 0; i < initialValues.size(); ++i) {
489 if (!initialValues[i])
490 initialValues[i] = builder.createOrFold<hw::ConstantOp>(
491 loc, builder.getIntegerAttr(types[i], 0));
495 StateOp::create(builder, loc, defOp, std::get<0>(clockAndResetAndOp),
496 Value{}, 1, inputs, initialValues);
497 auto reset = std::get<1>(clockAndResetAndOp);
499 arcOp.getResetMutable().assign(reset);
500 if (tapRegisters && llvm::any_of(names, [](
auto name) {
501 return !cast<StringAttr>(name).getValue().empty();
503 arcOp->setAttr(
"names", builder.getArrayAttr(names));
504 for (
auto [reg, resultIdx] : llvm::zip(regOps, regToOutputMapping))
505 regReplacements.push_back({
reg, arcOp.getResult(resultIdx)});
507 for (
auto [reg, arcResult] : regReplacements)
508 reg.replaceAllUsesWith(arcResult);
509 for (
auto [reg, arcResult] : regReplacements)
512 if (numMappedRegs > 0)
513 LLVM_DEBUG(llvm::dbgs() <<
"- Mapped " << numMappedRegs <<
" regs to "
514 << regsByInput.size() <<
" shuffling arcs\n");
524static LogicalResult
convert(llhd::CombinationalOp op,
525 llhd::CombinationalOp::Adaptor adaptor,
526 ConversionPatternRewriter &rewriter,
529 SmallVector<Type> resultTypes;
530 if (failed(converter.convertTypes(op.getResultTypes(), resultTypes)))
534 auto cloneIntoBody = [](Operation *op) {
535 return op->hasTrait<OpTrait::ConstantLike>();
538 mlir::makeRegionIsolatedFromAbove(rewriter, op.getBody(), cloneIntoBody);
542 ExecuteOp::create(rewriter, op.getLoc(), resultTypes, operands);
543 executeOp.getBody().takeBody(op.getBody());
544 rewriter.replaceOp(op, executeOp.getResults());
549static LogicalResult
convert(llhd::YieldOp op, llhd::YieldOp::Adaptor adaptor,
550 ConversionPatternRewriter &rewriter) {
551 rewriter.replaceOpWithNewOp<arc::OutputOp>(op, adaptor.getOperands());
560#define GEN_PASS_DEF_CONVERTTOARCSPASS
561#include "circt/Conversion/Passes.h.inc"
565struct ConvertToArcsPass
566 :
public circt::impl::ConvertToArcsPassBase<ConvertToArcsPass> {
567 using ConvertToArcsPassBase::ConvertToArcsPassBase;
568 void runOnOperation()
override;
572void ConvertToArcsPass::runOnOperation() {
574 TypeConverter converter;
575 converter.addConversion([](Type type) {
return type; });
578 ConversionPatternSet patterns(&getContext(), converter);
579 patterns.add<llhd::CombinationalOp>(
convert);
580 patterns.add<llhd::YieldOp>(
convert);
585 ConversionTarget target(getContext());
586 target.addIllegalOp<llhd::CombinationalOp, llhd::YieldOp>();
587 target.markUnknownOpDynamicallyLegal([](Operation *) {
return true; });
590 ConversionConfig config;
591 config.allowPatternRollback =
false;
594 if (failed(applyPartialConversion(getOperation(), target, std::move(patterns),
596 emitError(getOperation().
getLoc()) <<
"conversion to arcs failed";
597 return signalPassFailure();
602 outliner.tapRegisters = tapRegisters;
603 if (failed(outliner.run(getOperation())))
604 return signalPassFailure();
assert(baseType &&"element must be base type")
static LogicalResult convertInitialValue(seq::CompRegOp reg, SmallVectorImpl< Value > &values)
static bool isArcBreakingOp(Operation *op)
static LogicalResult convert(arc::ExecuteOp op, arc::ExecuteOp::Adaptor adaptor, ConversionPatternRewriter &rewriter, const TypeConverter &converter)
static Location getLoc(DefSlot slot)
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)
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)