11 #include "mlir/Dialect/Func/IR/FuncOps.h"
12 #include "mlir/Dialect/SCF/IR/SCF.h"
13 #include "mlir/Pass/Pass.h"
14 #include "llvm/Support/Debug.h"
16 #define DEBUG_TYPE "arc-lower-clocks-to-funcs"
20 #define GEN_PASS_DEF_LOWERCLOCKSTOFUNCS
21 #include "circt/Dialect/Arc/ArcPasses.h.inc"
26 using namespace circt;
29 using mlir::OpTrait::ConstantLike;
36 struct LowerClocksToFuncsPass
37 :
public arc::impl::LowerClocksToFuncsBase<LowerClocksToFuncsPass> {
38 LowerClocksToFuncsPass() =
default;
39 LowerClocksToFuncsPass(
const LowerClocksToFuncsPass &pass)
40 : LowerClocksToFuncsPass() {}
42 void runOnOperation()
override;
43 LogicalResult lowerModel(ModelOp modelOp);
44 LogicalResult lowerClock(Operation *clockOp, Value modelStorageArg,
45 OpBuilder &funcBuilder);
46 LogicalResult isolateClock(Operation *clockOp, Value modelStorageArg,
47 Value clockStorageArg);
49 SymbolTable *symbolTable;
51 Statistic numOpsCopied{
this,
"ops-copied",
"Ops copied into clock trees"};
52 Statistic numOpsMoved{
this,
"ops-moved",
"Ops moved into clock trees"};
56 void LowerClocksToFuncsPass::runOnOperation() {
57 symbolTable = &getAnalysis<SymbolTable>();
58 for (
auto op : getOperation().getOps<ModelOp>())
59 if (failed(lowerModel(op)))
60 return signalPassFailure();
63 LogicalResult LowerClocksToFuncsPass::lowerModel(ModelOp modelOp) {
64 LLVM_DEBUG(
llvm::dbgs() <<
"Lowering clocks in `" << modelOp.getName()
68 SmallVector<Operation *> clocks;
69 modelOp.walk([&](Operation *op) {
70 if (isa<ClockTreeOp, PassThroughOp>(op))
75 OpBuilder funcBuilder(modelOp);
76 for (
auto *op : clocks)
77 if (failed(lowerClock(op, modelOp.getBody().getArgument(0), funcBuilder)))
83 LogicalResult LowerClocksToFuncsPass::lowerClock(Operation *clockOp,
84 Value modelStorageArg,
85 OpBuilder &funcBuilder) {
86 LLVM_DEBUG(
llvm::dbgs() <<
"- Lowering clock " << clockOp->getName() <<
"\n");
87 assert((isa<ClockTreeOp, PassThroughOp>(clockOp)));
92 Region &clockRegion = clockOp->getRegion(0);
93 Value clockStorageArg = clockRegion.addArgument(modelStorageArg.getType(),
94 modelStorageArg.getLoc());
97 if (failed(isolateClock(clockOp, modelStorageArg, clockStorageArg)))
101 auto builder = OpBuilder::atBlockEnd(&clockRegion.front());
102 builder.create<func::ReturnOp>(clockOp->getLoc());
105 SmallString<32> funcName;
106 funcName.append(clockOp->getParentOfType<ModelOp>().getName());
107 funcName.append(isa<PassThroughOp>(clockOp) ?
"_passthrough" :
"_clock");
108 auto funcOp = funcBuilder.create<func::FuncOp>(
109 clockOp->getLoc(), funcName,
110 builder.getFunctionType({modelStorageArg.getType()}, {}));
111 symbolTable->insert(funcOp);
112 LLVM_DEBUG(
llvm::dbgs() <<
" - Created function `" << funcOp.getSymName()
116 builder.setInsertionPoint(clockOp);
117 if (
auto treeOp = dyn_cast<ClockTreeOp>(clockOp)) {
119 builder.create<scf::IfOp>(clockOp->getLoc(), treeOp.getClock(),
false);
120 auto builder = ifOp.getThenBodyBuilder();
121 builder.create<func::CallOp>(clockOp->getLoc(), funcOp,
122 ValueRange{modelStorageArg});
124 builder.create<func::CallOp>(clockOp->getLoc(), funcOp,
125 ValueRange{modelStorageArg});
129 funcOp.getBody().takeBody(clockRegion);
138 LogicalResult LowerClocksToFuncsPass::isolateClock(Operation *clockOp,
139 Value modelStorageArg,
140 Value clockStorageArg) {
141 auto *clockRegion = &clockOp->getRegion(0);
142 auto builder = OpBuilder::atBlockBegin(&clockRegion->front());
143 DenseMap<Value, Value> copiedValues;
144 auto result = clockRegion->walk([&](Operation *op) {
145 for (
auto &operand : op->getOpOperands()) {
147 if (operand.get() == modelStorageArg) {
148 operand.set(clockStorageArg);
151 if (operand.get().isa<BlockArgument>()) {
152 auto d = op->emitError(
153 "operation in clock tree uses external block argument");
154 d.attachNote() <<
"clock trees can only use external constant values";
155 d.attachNote() <<
"see operand #" << operand.getOperandNumber();
156 d.attachNote(clockOp->getLoc()) <<
"clock tree:";
157 return WalkResult::interrupt();
161 auto *definingOp = operand.get().getDefiningOp();
162 assert(definingOp &&
"block arguments ruled out above");
163 Region *definingRegion = definingOp->getParentRegion();
164 if (clockRegion->isAncestor(definingRegion))
169 if (
auto copiedValue = copiedValues.lookup(operand.get())) {
170 operand.set(copiedValue);
175 if (!definingOp->hasTrait<ConstantLike>()) {
176 auto d = op->emitError(
"operation in clock tree uses external value");
177 d.attachNote() <<
"clock trees can only use external constant values";
178 d.attachNote(definingOp->getLoc()) <<
"external value defined here:";
179 d.attachNote(clockOp->getLoc()) <<
"clock tree:";
180 return WalkResult::interrupt();
185 bool canMove = llvm::all_of(definingOp->getUsers(), [&](Operation *user) {
186 return clockRegion->isAncestor(user->getParentRegion());
190 definingOp->remove();
191 clonedOp = definingOp;
194 clonedOp = definingOp->cloneWithoutRegions();
199 for (
auto [outerResult, innerResult] :
200 llvm::zip(definingOp->getResults(), clonedOp->getResults())) {
201 copiedValues.insert({outerResult, innerResult});
202 if (operand.get() == outerResult)
203 operand.set(innerResult);
207 return WalkResult::advance();
209 return success(!result.wasInterrupted());
213 return std::make_unique<LowerClocksToFuncsPass>();
assert(baseType &&"element must be base type")
std::unique_ptr< mlir::Pass > createLowerClocksToFuncsPass()
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
mlir::raw_indented_ostream & dbgs()