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"
40static LogicalResult collectFormatStringFragments(
41 Value formatString, PrintFormattedOp anchorOp,
42 SmallVectorImpl<Operation *> &fragmentList,
45 SmallVector<Value> flatString;
46 if (
auto concatInput = formatString.getDefiningOp<FormatStringConcatOp>()) {
47 auto isAcyclic = concatInput.getFlattenedInputs(flatString);
48 if (failed(isAcyclic)) {
49 anchorOp.emitError(
"Cyclic format string cannot be proceduralized.");
53 flatString.push_back(formatString);
56 assert(fragmentList.empty() &&
"format string visited twice");
57 for (
auto fragment : flatString) {
58 auto *fmtOp = fragment.getDefiningOp();
60 anchorOp.emitError(
"Proceduralization of format strings passed as block "
61 "argument is unsupported.");
64 fragmentList.push_back(fmtOp);
65 allFStringFragments.insert(fmtOp);
69 if (!llvm::isa<FormatLiteralOp>(fmtOp)) {
70 auto fmtVal = getFormattedValue(fmtOp);
71 assert(!!fmtVal &&
"Unexpected formatting fragment op.");
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);
236 cleanupList.push_back(getFileOp.getFileName().getDefiningOp());
244 builder.setInsertionPointToEnd(trigOp.getBodyBlock());
245 for (
auto printOp : livePrintOps) {
246 auto &printFragments = printFragmentMap[printOp];
247 procPrintInputMap[printOp] = ::rematerializeFormatStringFromFragments(
248 printFragments, builder, mapping, printOp.getLoc());
251 Value prevConditionValue;
252 Block *prevConditionBlock =
nullptr;
253 for (
auto printOp : livePrintOps) {
254 auto condArg = mapping.lookup(printOp.getCondition());
256 ::getOrCreateConditionBlock(builder, trigOp, printOp.getLoc(), condArg,
257 prevConditionValue, prevConditionBlock);
259 builder.setInsertionPoint(condBlock->getTerminator());
260 Value procPrintInput = procPrintInputMap[printOp];
262 Value procPrintStream;
263 if (
auto stream = printOp.getStream()) {
264 procPrintStream = mapping.lookupOrNull(stream);
265 if (!procPrintStream) {
266 printOp.emitError(
"proceduralization failed to rematerialize stream");
271 PrintFormattedProcOp::create(builder, printOp.getLoc(), procPrintInput,
273 cleanupList.push_back(printOp.getInput().getDefiningOp());
281void ProceduralizeSimPass::cleanup() {
282 SmallVector<Operation *> cleanupNextList;
283 SmallDenseSet<Operation *> erasedOps;
285 bool noChange =
true;
286 while (!cleanupList.empty() || !cleanupNextList.empty()) {
288 if (cleanupList.empty()) {
291 cleanupList = std::move(cleanupNextList);
292 cleanupNextList = {};
296 auto *opToErase = cleanupList.pop_back_val();
297 if (erasedOps.contains(opToErase))
300 if (opToErase->getUses().empty()) {
302 if (
auto concat = dyn_cast<FormatStringConcatOp>(opToErase))
303 for (
auto operand : concat.getInputs())
304 cleanupNextList.push_back(operand.getDefiningOp());
306 erasedOps.insert(opToErase);
310 cleanupNextList.push_back(opToErase);
315void ProceduralizeSimPass::runOnOperation() {
320 auto theModule = getOperation();
322 theModule.walk<mlir::WalkOrder::PreOrder>(
323 [&](PrintFormattedOp op) { printfOpMap[op.getClock()].push_back(op); });
326 for (
auto &[clock, printOps] : printfOpMap)
327 if (failed(proceduralizePrintOps(clock, printOps))) {
assert(baseType &&"element must be base type")
static Block * getBodyBlock(FModuleLike mod)
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.