47#include "mlir/Analysis/Liveness.h"
48#include "mlir/IR/Dominance.h"
49#include "mlir/IR/SymbolTable.h"
50#include "mlir/Interfaces/ControlFlowInterfaces.h"
51#include "mlir/Pass/Pass.h"
52#include "llvm/ADT/DenseMap.h"
53#include "llvm/ADT/SetVector.h"
54#include "llvm/Support/GenericIteratedDominanceFrontier.h"
58#define GEN_PASS_DEF_LOWERPROCESSESPASS
59#include "circt/Dialect/Arc/ArcPasses.h.inc"
67using llvm::SmallDenseSet;
72struct ProcessLowering {
73 ProcessLowering(llhd::ProcessOp processOp, SymbolTable &symbolTable)
74 : processOp(processOp), symbolTable(symbolTable) {}
79 void collectCaptures();
80 void createCoroutine();
81 void addEntryAndResumeBlockArguments();
82 LogicalResult rewriteTerminators();
83 void renameCoroutineArgument(
unsigned argIdx, Liveness &liveness,
84 DominanceInfo &dominance);
87 llhd::ProcessOp processOp;
88 SymbolTable &symbolTable;
91 CoroutineDefineOp coroOp;
95 SmallVector<Value> captures;
99 SmallVector<Value> entryArgs;
104 SetVector<Block *> resumeBlocks;
116 SmallVector<bool> staticSensitivityMask;
125void ProcessLowering::collectCaptures() {
126 auto &body = processOp.getBody();
127 auto builder = OpBuilder::atBlockBegin(&body.front());
128 SmallDenseSet<Value> captureSet;
129 DenseMap<Operation *, Operation *> clonedConstants;
130 body.walk([&](Operation *op) {
131 for (
auto &operand : op->getOpOperands()) {
132 auto value = operand.get();
135 if (body.isAncestor(value.getParentRegion()))
140 auto result = dyn_cast<OpResult>(value);
141 if (result && result.getOwner()->hasTrait<OpTrait::ConstantLike>()) {
142 auto *&cloned = clonedConstants[result.getOwner()];
144 cloned = builder.clone(*result.getOwner());
145 operand.set(cloned->getResult(result.getResultNumber()));
151 if (captureSet.insert(value).second)
152 captures.push_back(value);
157void ProcessLowering::createCoroutine() {
161 OpBuilder builder(hwModule);
167 auto i64Type = builder.getIntegerType(64);
168 SmallVector<Type> argTypes;
169 for (
auto cap : captures)
170 argTypes.push_back(cap.getType());
171 argTypes.push_back(i64Type);
172 auto maskType = builder.getIntegerType(argTypes.size());
173 SmallVector<Type> resultTypes(processOp.getResultTypes());
174 resultTypes.push_back(maskType);
175 resultTypes.push_back(i64Type);
176 auto funcType = builder.getFunctionType(argTypes, resultTypes);
181 builder.getStringAttr(hwModule.getSymName() +
".llhd.process");
182 coroOp = CoroutineDefineOp::create(builder, processOp.getLoc(), funcName,
184 symbolTable.insert(coroOp);
188 coroOp.getBody().takeBody(processOp.getBody());
209void ProcessLowering::addEntryAndResumeBlockArguments() {
210 auto &body = coroOp.getBody();
211 auto loc = processOp.getLoc();
215 SmallVector<Type> prefixTypes;
216 for (
auto capture : captures)
217 prefixTypes.push_back(capture.getType());
218 prefixTypes.push_back(IntegerType::get(coroOp.getContext(), 64));
222 auto &entry = body.front();
223 for (
auto [index, type] :
llvm::enumerate(prefixTypes)) {
224 entryArgs.push_back(entry.insertArgument(index, type, loc));
225 argIndices[entryArgs.back()] = index;
229 for (
auto &block : body)
230 if (auto wait = dyn_cast<llhd::WaitOp>(block.getTerminator()))
231 resumeBlocks.insert(wait.getDest());
236 auto prefixSize = prefixTypes.size();
237 for (
auto *resumeBlock : resumeBlocks) {
238 for (
auto [index, type] :
llvm::enumerate(prefixTypes))
239 argIndices[resumeBlock->insertArgument(index, type, loc)] = index;
245 for (
auto *pred : resumeBlock->getPredecessors()) {
246 auto *term = pred->getTerminator();
247 if (isa<llhd::WaitOp>(term))
249 auto branchOp = cast<BranchOpInterface>(term);
250 for (
unsigned succIdx = 0, succNum = term->getNumSuccessors();
251 succIdx < succNum; ++succIdx) {
252 if (term->getSuccessor(succIdx) != resumeBlock)
254 auto succOperands = branchOp.getSuccessorOperands(succIdx);
255 SmallVector<Value> newOperands(entryArgs.begin(),
256 entryArgs.begin() + prefixSize);
257 for (
auto v : succOperands.getForwardedOperands())
258 newOperands.push_back(v);
259 succOperands.getMutableForwardedOperands().assign(newOperands);
269 for (
auto [capture, entryArg] :
llvm::zip(captures, entryArgs))
270 capture.replaceUsesWithIf(entryArg, [&](OpOperand &use) {
271 return body.isAncestor(use.getOwner()->getParentRegion());
282LogicalResult ProcessLowering::rewriteTerminators() {
288 auto maskWidth = entryArgs.size();
289 staticSensitivityMask.resize(maskWidth,
false);
290 for (
auto &block : coroOp.getBody()) {
291 auto *term = block.getTerminator();
292 auto loc = term->getLoc();
293 OpBuilder builder(term);
296 if (
auto wait = dyn_cast<llhd::WaitOp>(term)) {
300 APInt maskBits(maskWidth, 0);
301 for (
auto observed : wait.getObserved())
302 if (auto it = argIndices.find(observed); it != argIndices.end()) {
303 maskBits.setBit(it->second);
304 staticSensitivityMask[it->second] =
true;
311 if (!wait.getDelay()) {
314 SmallVector<Value> yieldOperands(wait.getYieldOperands());
315 yieldOperands.push_back(mask);
316 yieldOperands.push_back(never);
317 if (wait.getObserved().empty())
318 CoroutineHaltOp::create(builder, loc, yieldOperands);
320 CoroutineYieldOp::create(builder, loc, yieldOperands,
321 wait.getDestOperands(), wait.getDest());
326 auto now = entryArgs.back();
327 auto delay = llhd::TimeToIntOp::create(builder, loc, wait.getDelay());
328 auto wakeup = comb::AddOp::create(builder, loc, now, delay);
329 SmallVector<Value> yieldOperands(wait.getYieldOperands());
330 yieldOperands.push_back(mask);
331 yieldOperands.push_back(wakeup);
332 CoroutineYieldOp::create(builder, loc, yieldOperands,
333 wait.getDestOperands(), wait.getDest());
339 if (
auto halt = dyn_cast<llhd::HaltOp>(term)) {
342 SmallVector<Value> yieldOperands(halt.getYieldOperands());
343 yieldOperands.push_back(mask);
344 yieldOperands.push_back(never);
345 CoroutineHaltOp::create(builder, loc, yieldOperands);
367void ProcessLowering::renameCoroutineArgument(
unsigned argIdx,
369 DominanceInfo &dominance) {
370 auto &body = coroOp.getBody();
371 auto entryArg = entryArgs[argIdx];
372 auto *entryBlock = &body.front();
375 SmallPtrSet<Block *, 8> definingBlocks;
376 definingBlocks.insert(entryBlock);
377 for (
auto *resumeBlock : resumeBlocks)
378 definingBlocks.insert(resumeBlock);
381 SmallPtrSet<Block *, 16> liveInBlocks;
382 for (
auto &block : body)
383 if (liveness.getLiveness(&block)->isLiveIn(entryArg))
384 liveInBlocks.insert(&block);
386 auto &domTree = dominance.getDomTree(&body);
388 llvm::IDFCalculatorBase<Block, false> idfCalculator(domTree);
389 idfCalculator.setDefiningBlocks(definingBlocks);
390 idfCalculator.setLiveInBlocks(liveInBlocks);
391 SmallVector<Block *> mergeBlocks;
392 idfCalculator.calculate(mergeBlocks);
394 SmallPtrSet<Block *, 16> mergeBlockSet(mergeBlocks.begin(),
399 struct WorklistItem {
400 DominanceInfoNode *domNode;
403 SmallVector<WorklistItem> worklist;
404 worklist.push_back({domTree.getNode(entryBlock), entryArg});
406 while (!worklist.empty()) {
407 auto item = worklist.pop_back_val();
408 auto *block = item.domNode->getBlock();
410 if (resumeBlocks.contains(block)) {
411 item.reachingDef = block->getArgument(argIdx);
412 }
else if (mergeBlockSet.contains(block)) {
414 block->addArgument(entryArg.getType(), entryArg.getLoc());
419 if (item.reachingDef != entryArg)
420 block->walk([&](Operation *nested) {
421 nested->replaceUsesOfWith(entryArg, item.reachingDef);
431 auto *terminator = block->getTerminator();
432 if (
auto branchOp = dyn_cast<BranchOpInterface>(terminator)) {
433 for (
auto &blockOperand : terminator->getBlockOperands()) {
434 auto *succ = blockOperand.get();
435 if (!mergeBlockSet.contains(succ) || resumeBlocks.contains(succ))
437 branchOp.getSuccessorOperands(blockOperand.getOperandNumber())
438 .append(item.reachingDef);
442 for (
auto *child : item.domNode->children())
443 worklist.push_back({child, item.reachingDef});
449void ProcessLowering::buildInstance() {
450 OpBuilder builder(processOp);
451 auto loc = processOp.getLoc();
452 auto now = llhd::CurrentTimeOp::create(builder, loc);
453 auto nowI64 = llhd::TimeToIntOp::create(builder, loc, now);
455 SmallVector<Value> args(captures.begin(), captures.end());
456 args.push_back(nowI64);
458 assert(args.size() == staticSensitivityMask.size());
459 auto instanceOp = CoroutineInstanceOp::create(
460 builder, loc, processOp.getResultTypes(),
461 FlatSymbolRefAttr::get(coroOp.getSymNameAttr()), args,
462 builder.getDenseBoolArrayAttr(staticSensitivityMask));
464 processOp.replaceAllUsesWith(instanceOp.getResults());
468LogicalResult ProcessLowering::run() {
471 addEntryAndResumeBlockArguments();
472 if (failed(rewriteTerminators()))
479 if (!coroOp.getBody().hasOneBlock()) {
480 Liveness liveness(coroOp);
481 DominanceInfo dominance(coroOp);
482 for (
unsigned argIdx = 0, argNum = entryArgs.size(); argIdx != argNum;
484 renameCoroutineArgument(argIdx, liveness, dominance);
496struct LowerProcessesPass
497 :
public arc::impl::LowerProcessesPassBase<LowerProcessesPass> {
498 void runOnOperation()
override;
502void LowerProcessesPass::runOnOperation() {
503 auto module = getOperation();
504 auto &symbolTable = getAnalysis<SymbolTable>();
506 bool anyFailed =
false;
507 module.walk([&](llhd::ProcessOp op) {
508 ProcessLowering lowering(op, symbolTable);
509 if (failed(lowering.run()))
assert(baseType &&"element must be base type")
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, List[str] cmdline_args=sys.argv)