19 #include "mlir/Dialect/SCF/IR/SCF.h"
20 #include "mlir/Pass/Pass.h"
21 #include "llvm/ADT/IndexedMap.h"
22 #include "llvm/ADT/MapVector.h"
23 #include "llvm/ADT/SetVector.h"
24 #include "llvm/Support/Debug.h"
26 #define DEBUG_TYPE "proceduralize-sim"
30 #define GEN_PASS_DEF_PROCEDURALIZESIM
31 #include "circt/Dialect/Sim/SimPasses.h.inc"
36 using namespace circt;
40 struct ProceduralizeSimPass : impl::ProceduralizeSimBase<ProceduralizeSimPass> {
42 void runOnOperation()
override;
45 LogicalResult proceduralizePrintOps(Value clock,
46 ArrayRef<PrintFormattedOp> printOps);
47 SmallVector<Operation *> getPrintFragments(PrintFormattedOp op);
51 SmallMapVector<Value, SmallVector<PrintFormattedOp>, 2> printfOpMap;
54 SmallVector<Operation *> cleanupList;
58 LogicalResult ProceduralizeSimPass::proceduralizePrintOps(
59 Value clock, ArrayRef<PrintFormattedOp> printOps) {
62 SmallSetVector<Value, 4> arguments;
65 SmallVector<Location> locs;
66 SmallDenseSet<Value, 1> alwaysEnabledConditions;
68 locs.reserve(printOps.size());
70 for (
auto printOp : printOps) {
73 if (
auto cstCond = printOp.getCondition().getDefiningOp<
hw::ConstantOp>()) {
74 if (cstCond.getValue().isAllOnes())
75 alwaysEnabledConditions.insert(printOp.getCondition());
79 arguments.insert(printOp.getCondition());
83 locs.push_back(printOp.getLoc());
86 SmallVector<Value> flatString;
87 if (
auto concatInput =
88 printOp.getInput().getDefiningOp<FormatStringConcatOp>()) {
90 auto isAcyclic = concatInput.getFlattenedInputs(flatString);
91 if (failed(isAcyclic)) {
92 printOp.emitError(
"Cyclic format string cannot be proceduralized.");
96 flatString.push_back(printOp.getInput());
99 auto &fragmentList = fragmentMap[printOp];
100 assert(fragmentList.empty() &&
"printf operation visited twice.");
102 for (
auto &fragment : flatString) {
103 auto *fmtOp = fragment.getDefiningOp();
105 printOp.emitError(
"Proceduralization of format strings passed as block "
106 "argument is unsupported.");
109 fragmentList.push_back(fmtOp);
112 if (!llvm::isa<FormatLitOp>(fmtOp)) {
114 assert(!!fmtVal &&
"Unexpected foramtting fragment op.");
115 arguments.insert(fmtVal);
121 OpBuilder builder(printOps.back());
122 auto fusedLoc = builder.getFusedLoc(locs);
124 SmallVector<Value> argVec = arguments.takeVector();
126 auto clockConv = builder.createOrFold<seq::FromClockOp>(fusedLoc, clock);
127 auto trigOp = builder.create<hw::TriggeredOp>(
130 hw::EventControl::AtPosEdge),
134 IRMapping argumentMapper;
136 for (
auto arg : argVec) {
137 argumentMapper.map(arg, trigOp.getBodyBlock()->getArgument(idx));
142 builder.setInsertionPointToStart(trigOp.getBodyBlock());
143 if (!alwaysEnabledConditions.empty()) {
146 for (
auto cstCond : alwaysEnabledConditions)
147 argumentMapper.map(cstCond, cstTrue);
151 Value prevConditionValue;
152 Block *prevConditionBlock;
154 for (
auto printOp : printOps) {
157 if (
auto cstCond = printOp.getCondition().getDefiningOp<
hw::ConstantOp>()) {
158 if (cstCond.getValue().isZero()) {
166 auto fragments = fragmentMap[printOp];
167 SmallVector<Value> clonedOperands;
168 builder.setInsertionPointToStart(trigOp.getBodyBlock());
169 for (
auto *fragment : fragments) {
170 auto &fmtCloned = cloneMap[fragment];
172 fmtCloned = builder.clone(*fragment, argumentMapper);
173 clonedOperands.push_back(fmtCloned->getResult(0));
176 Value procPrintInput;
177 if (clonedOperands.size() != 1)
178 procPrintInput = builder.createOrFold<FormatStringConcatOp>(
179 printOp.getLoc(), clonedOperands);
181 procPrintInput = clonedOperands.front();
184 auto condArg = argumentMapper.lookup(printOp.getCondition());
185 if (condArg != prevConditionValue)
186 prevConditionBlock =
nullptr;
187 auto *condBlock = prevConditionBlock;
191 builder.setInsertionPointToEnd(trigOp.getBodyBlock());
192 auto ifOp = builder.create<mlir::scf::IfOp>(printOp.getLoc(), TypeRange{},
193 condArg,
true,
false);
194 builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
195 builder.create<mlir::scf::YieldOp>(printOp.getLoc());
196 condBlock = builder.getBlock();
197 prevConditionValue = condArg;
198 prevConditionBlock = condBlock;
203 builder.setInsertionPoint(condBlock->getTerminator());
204 builder.create<PrintFormattedProcOp>(printOp.getLoc(), procPrintInput);
205 cleanupList.push_back(printOp.getInput().getDefiningOp());
213 void ProceduralizeSimPass::cleanup() {
214 SmallVector<Operation *> cleanupNextList;
215 SmallDenseSet<Operation *> erasedOps;
217 bool noChange =
true;
218 while (!cleanupList.empty() || !cleanupNextList.empty()) {
220 if (cleanupList.empty()) {
223 cleanupList = std::move(cleanupNextList);
224 cleanupNextList = {};
228 auto *opToErase = cleanupList.pop_back_val();
229 if (erasedOps.contains(opToErase))
232 if (opToErase->getUses().empty()) {
234 if (
auto concat = dyn_cast<FormatStringConcatOp>(opToErase))
235 for (
auto operand :
concat.getInputs())
236 cleanupNextList.push_back(operand.getDefiningOp());
238 erasedOps.insert(opToErase);
242 cleanupNextList.push_back(opToErase);
247 void ProceduralizeSimPass::runOnOperation() {
252 auto theModule = getOperation();
254 theModule.walk<mlir::WalkOrder::PreOrder>(
255 [&](PrintFormattedOp op) { printfOpMap[op.getClock()].push_back(op); });
258 for (
auto &[clock, printOps] : printfOpMap)
259 if (failed(proceduralizePrintOps(clock, printOps))) {
assert(baseType &&"element must be base type")
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
static mlir::Value getFormattedValue(mlir::Operation *fmtOp)
Returns the value operand of a value formatting operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.