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;
98 SmallVector<Value> entryArgs;
103 SetVector<Block *> resumeBlocks;
115 SmallVector<bool> staticSensitivityMask;
118 Value inferredContext;
127void ProcessLowering::collectCaptures() {
128 auto &body = processOp.getBody();
129 auto builder = OpBuilder::atBlockBegin(&body.front());
130 SmallDenseSet<Value> captureSet;
131 DenseMap<Operation *, Operation *> clonedConstants;
132 body.walk([&](Operation *op) {
133 for (
auto &operand : op->getOpOperands()) {
134 auto value = operand.get();
137 if (body.isAncestor(value.getParentRegion()))
142 auto result = dyn_cast<OpResult>(value);
143 if (result && result.getOwner()->hasTrait<OpTrait::ConstantLike>()) {
144 auto *&cloned = clonedConstants[result.getOwner()];
146 cloned = builder.clone(*result.getOwner());
147 operand.set(cloned->getResult(result.getResultNumber()));
153 if (captureSet.insert(value).second)
154 captures.push_back(value);
159void ProcessLowering::createCoroutine() {
163 OpBuilder builder(hwModule);
169 auto i64Type = builder.getIntegerType(64);
170 SmallVector<Type> argTypes;
171 for (
auto cap : captures)
172 argTypes.push_back(cap.getType());
173 auto maskType = builder.getIntegerType(argTypes.size());
174 SmallVector<Type> resultTypes(processOp.getResultTypes());
175 resultTypes.push_back(maskType);
176 resultTypes.push_back(i64Type);
177 auto funcType = builder.getFunctionType(argTypes, resultTypes);
182 builder.getStringAttr(hwModule.getSymName() +
".llhd.process");
183 coroOp = CoroutineDefineOp::create(builder, processOp.getLoc(), funcName,
185 symbolTable.insert(coroOp);
189 coroOp.getBody().takeBody(processOp.getBody());
192 builder.setInsertionPointToStart(&coroOp.getBody().front());
193 inferredContext = InferredContextOp::create(builder, coroOp.getLoc());
214void ProcessLowering::addEntryAndResumeBlockArguments() {
215 auto &body = coroOp.getBody();
216 auto loc = processOp.getLoc();
220 auto prefixTypes = TypeRange(captures);
224 auto &entry = body.front();
225 for (
auto [index, type] :
llvm::enumerate(prefixTypes)) {
226 entryArgs.push_back(entry.insertArgument(index, type, loc));
227 argIndices[entryArgs.back()] = index;
231 for (
auto &block : body)
232 if (auto wait = dyn_cast<llhd::WaitOp>(block.getTerminator()))
233 resumeBlocks.insert(wait.getDest());
238 auto prefixSize = prefixTypes.size();
239 for (
auto *resumeBlock : resumeBlocks) {
240 for (
auto [index, type] :
llvm::enumerate(prefixTypes))
241 argIndices[resumeBlock->insertArgument(index, type, loc)] = index;
247 for (
auto *pred : resumeBlock->getPredecessors()) {
248 auto *term = pred->getTerminator();
249 if (isa<llhd::WaitOp>(term))
251 auto branchOp = cast<BranchOpInterface>(term);
252 for (
unsigned succIdx = 0, succNum = term->getNumSuccessors();
253 succIdx < succNum; ++succIdx) {
254 if (term->getSuccessor(succIdx) != resumeBlock)
256 auto succOperands = branchOp.getSuccessorOperands(succIdx);
257 SmallVector<Value> newOperands(entryArgs.begin(),
258 entryArgs.begin() + prefixSize);
259 for (
auto v : succOperands.getForwardedOperands())
260 newOperands.push_back(v);
261 succOperands.getMutableForwardedOperands().assign(newOperands);
271 for (
auto [capture, entryArg] :
llvm::zip(captures, entryArgs))
272 capture.replaceUsesWithIf(entryArg, [&](OpOperand &use) {
273 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 = CurrentTimeOp::create(builder, loc, inferredContext);
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();
453 assert(captures.size() == staticSensitivityMask.size());
454 auto instanceOp = CoroutineInstanceOp::create(
455 builder, loc, processOp.getResultTypes(),
456 FlatSymbolRefAttr::get(coroOp.getSymNameAttr()), captures,
457 builder.getDenseBoolArrayAttr(staticSensitivityMask));
459 processOp.replaceAllUsesWith(instanceOp.getResults());
463LogicalResult ProcessLowering::run() {
466 addEntryAndResumeBlockArguments();
467 if (failed(rewriteTerminators()))
474 if (!coroOp.getBody().hasOneBlock()) {
475 Liveness liveness(coroOp);
476 DominanceInfo dominance(coroOp);
477 for (
unsigned argIdx = 0, argNum = entryArgs.size(); argIdx != argNum;
479 renameCoroutineArgument(argIdx, liveness, dominance);
491struct LowerProcessesPass
492 :
public arc::impl::LowerProcessesPassBase<LowerProcessesPass> {
493 void runOnOperation()
override;
497void LowerProcessesPass::runOnOperation() {
498 auto module = getOperation();
499 auto &symbolTable = getAnalysis<SymbolTable>();
501 bool anyFailed =
false;
502 module.walk([&](llhd::ProcessOp op) {
503 ProcessLowering lowering(op, symbolTable);
504 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)