20#include "mlir/Dialect/SCF/IR/SCF.h"
21#include "mlir/Pass/Pass.h"
22#include "llvm/ADT/IndexedMap.h"
23#include "llvm/ADT/MapVector.h"
24#include "llvm/ADT/SetVector.h"
25#include "llvm/Support/Debug.h"
27#define DEBUG_TYPE "proceduralize-sim"
31#define GEN_PASS_DEF_PROCEDURALIZESIM
32#include "circt/Dialect/Sim/SimPasses.h.inc"
41static LogicalResult collectFormatStringFragments(
42 Value formatString, PrintFormattedOp anchorOp,
43 SmallVectorImpl<Operation *> &fragmentList,
46 SmallVector<Value> flatString;
47 if (
auto concatInput = formatString.getDefiningOp<FormatStringConcatOp>()) {
48 auto isAcyclic = concatInput.getFlattenedInputs(flatString);
49 if (failed(isAcyclic)) {
50 anchorOp.emitError(
"Cyclic format string cannot be proceduralized.");
54 flatString.push_back(formatString);
57 assert(fragmentList.empty() &&
"format string visited twice");
58 for (
auto fragment : flatString) {
59 auto *fmtOp = fragment.getDefiningOp();
61 anchorOp.emitError(
"Proceduralization of format strings passed as block "
62 "argument is unsupported.");
65 fragmentList.push_back(fmtOp);
66 allFStringFragments.insert(fmtOp);
71 if (
auto fmtVal = getFormattedValue(fmtOp)) {
72 arguments.insert(fmtVal);
79rematerializeFormatStringFromFragments(ArrayRef<Operation *> fragments,
80 OpBuilder &builder, IRMapping &mapping,
82 SmallVector<Value> operands;
83 operands.reserve(fragments.size());
84 for (
auto *fragment : fragments) {
85 auto cloned = mapping.lookupOrNull(fragment->getResult(0));
86 assert(cloned &&
"missing cloned fragment");
87 operands.push_back(cloned);
89 if (operands.size() == 1)
90 return operands.front();
91 return builder.createOrFold<FormatStringConcatOp>(loc, operands);
94static Block *getOrCreateConditionBlock(OpBuilder &builder,
95 hw::TriggeredOp trigOp, Location loc,
97 Value &prevConditionValue,
98 Block *&prevConditionBlock) {
99 if (condition != prevConditionValue)
100 prevConditionBlock =
nullptr;
102 if (prevConditionBlock)
103 return prevConditionBlock;
105 builder.setInsertionPointToEnd(trigOp.getBodyBlock());
106 auto ifOp = mlir::scf::IfOp::create(builder, loc, TypeRange{}, condition,
108 builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
109 mlir::scf::YieldOp::create(builder, loc);
110 prevConditionValue = condition;
111 prevConditionBlock = builder.getBlock();
112 return prevConditionBlock;
115struct ProceduralizeSimPass : impl::ProceduralizeSimBase<ProceduralizeSimPass> {
117 void runOnOperation()
override;
120 LogicalResult proceduralizePrintOps(Value clock,
121 ArrayRef<PrintFormattedOp> printOps);
128 SmallVector<Operation *> cleanupList;
132LogicalResult ProceduralizeSimPass::proceduralizePrintOps(
133 Value clock, ArrayRef<PrintFormattedOp> printOps) {
145 SmallVector<Location> locs;
146 SmallDenseSet<Value, 1> alwaysEnabledConditions;
147 SmallVector<PrintFormattedOp> livePrintOps;
149 locs.reserve(printOps.size());
150 for (
auto printOp : printOps) {
151 if (
auto cstCond = printOp.getCondition().getDefiningOp<
hw::ConstantOp>()) {
152 if (cstCond.getValue().isZero()) {
156 if (cstCond.getValue().isAllOnes())
157 alwaysEnabledConditions.insert(printOp.getCondition());
159 arguments.insert(printOp.getCondition());
162 livePrintOps.push_back(printOp);
163 locs.push_back(printOp.getLoc());
165 auto &printFragments = printFragmentMap[printOp];
166 if (failed(::collectFormatStringFragments(printOp.getInput(), printOp,
168 allFStringFragments, arguments)))
171 if (
auto stream = printOp.getStream()) {
172 auto getFileOp = stream.getDefiningOp<GetFileOp>();
174 if (!stream.getDefiningOp())
175 printOp.emitError(
"proceduralization requires stream to be produced "
176 "by sim.get_file, block arguments are unsupported");
178 printOp.emitError(
"proceduralization requires stream to be produced "
182 getFileOps.insert(getFileOp);
183 auto &fileNameFragments = fileNameFragmentMap[getFileOp];
184 if (fileNameFragments.empty() &&
185 failed(::collectFormatStringFragments(
186 getFileOp.getFileName(), printOp, fileNameFragments,
187 allFStringFragments, arguments)))
192 if (livePrintOps.empty())
195 OpBuilder builder(livePrintOps.back());
196 auto fusedLoc = builder.getFusedLoc(locs);
197 SmallVector<Value> argVec = arguments.takeVector();
199 auto clockConv = builder.createOrFold<seq::FromClockOp>(fusedLoc, clock);
200 auto trigOp = hw::TriggeredOp::create(
202 hw::EventControlAttr::get(builder.getContext(),
203 hw::EventControl::AtPosEdge),
207 for (
auto [idx, arg] :
llvm::enumerate(argVec))
208 mapping.map(arg, trigOp.
getBodyBlock()->getArgument(idx));
210 builder.setInsertionPointToStart(trigOp.getBodyBlock());
211 if (!alwaysEnabledConditions.empty()) {
213 fusedLoc, IntegerAttr::get(builder.getI1Type(), 1));
214 for (
auto cstCond : alwaysEnabledConditions)
215 mapping.map(cstCond, cstTrue);
218 for (
auto *fragment : allFStringFragments) {
219 auto original = fragment->getResult(0);
220 if (mapping.lookupOrNull(original))
222 auto *cloned = builder.clone(*fragment, mapping);
223 mapping.map(original, cloned->getResult(0));
226 for (
auto getFileOp : getFileOps) {
227 auto &fileNameFragments = fileNameFragmentMap[getFileOp];
228 Value clonedFileName = ::rematerializeFormatStringFromFragments(
229 fileNameFragments, builder, mapping, getFileOp.getLoc());
232 GetFileOp::create(builder, getFileOp.getLoc(), clonedFileName);
233 mapping.map(getFileOp.getResult(), clonedGetFile.getResult());
235 cleanupList.push_back(getFileOp);
243 builder.setInsertionPointToEnd(trigOp.getBodyBlock());
244 for (
auto printOp : livePrintOps) {
245 auto &printFragments = printFragmentMap[printOp];
246 procPrintInputMap[printOp] = ::rematerializeFormatStringFromFragments(
247 printFragments, builder, mapping, printOp.getLoc());
250 Value prevConditionValue;
251 Block *prevConditionBlock =
nullptr;
252 for (
auto printOp : livePrintOps) {
253 auto condArg = mapping.lookup(printOp.getCondition());
255 ::getOrCreateConditionBlock(builder, trigOp, printOp.getLoc(), condArg,
256 prevConditionValue, prevConditionBlock);
258 builder.setInsertionPoint(condBlock->getTerminator());
259 Value procPrintInput = procPrintInputMap[printOp];
261 Value procPrintStream;
262 if (
auto stream = printOp.getStream()) {
263 procPrintStream = mapping.lookupOrNull(stream);
264 if (!procPrintStream) {
265 printOp.emitError(
"proceduralization failed to rematerialize stream");
270 PrintFormattedProcOp::create(builder, printOp.getLoc(), procPrintInput,
272 cleanupList.push_back(printOp);
279void ProceduralizeSimPass::cleanup() {
281 OpOperand &operand) {
282 return isa<FormatStringType, OutputStreamType>(operand.get().getType()) &&
283 isa_and_present<SimDialect>(op->getDialect());
285 sccs.
visit(cleanupList);
286 assert(sccs.getNumCyclicSCCs() == 0 &&
287 "Cyclic graph should have been rejected");
288 for (
auto scc : sccs.reverseTopological()) {
289 auto *op = cast<Operation *>(scc);
293 op->emitWarning(
"Operation has remaining (transitive) users outside of "
294 "procedural regions.");
298void ProceduralizeSimPass::runOnOperation() {
303 auto theModule = getOperation();
305 theModule.walk<mlir::WalkOrder::PreOrder>(
306 [&](PrintFormattedOp op) { printfOpMap[op.getClock()].push_back(op); });
309 for (
auto &[clock, printOps] : printfOpMap)
310 if (failed(proceduralizePrintOps(clock, printOps))) {
assert(baseType &&"element must be base type")
static Block * getBodyBlock(FModuleLike mod)
Iterative Tarjan SCC analysis on a sparse subgraph of MLIR operations.
void visit(mlir::Operation *op)
Seed op into the DFS if it has not already been discovered.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, unsigned width=80)
Write a boilerplate header for a pass to the debug stream.