CIRCT  19.0.0git
LowerClocksToFuncs.cpp
Go to the documentation of this file.
1 //===- LowerClocksToFuncs.cpp ---------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
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"
15 
16 #define DEBUG_TYPE "arc-lower-clocks-to-funcs"
17 
18 namespace circt {
19 namespace arc {
20 #define GEN_PASS_DEF_LOWERCLOCKSTOFUNCS
21 #include "circt/Dialect/Arc/ArcPasses.h.inc"
22 } // namespace arc
23 } // namespace circt
24 
25 using namespace mlir;
26 using namespace circt;
27 using namespace arc;
28 using namespace hw;
29 using mlir::OpTrait::ConstantLike;
30 
31 //===----------------------------------------------------------------------===//
32 // Pass Implementation
33 //===----------------------------------------------------------------------===//
34 
35 namespace {
36 struct LowerClocksToFuncsPass
37  : public arc::impl::LowerClocksToFuncsBase<LowerClocksToFuncsPass> {
38  LowerClocksToFuncsPass() = default;
39  LowerClocksToFuncsPass(const LowerClocksToFuncsPass &pass)
40  : LowerClocksToFuncsPass() {}
41 
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);
48 
49  SymbolTable *symbolTable;
50 
51  Statistic numOpsCopied{this, "ops-copied", "Ops copied into clock trees"};
52  Statistic numOpsMoved{this, "ops-moved", "Ops moved into clock trees"};
53 };
54 } // namespace
55 
56 void LowerClocksToFuncsPass::runOnOperation() {
57  symbolTable = &getAnalysis<SymbolTable>();
58  for (auto op : getOperation().getOps<ModelOp>())
59  if (failed(lowerModel(op)))
60  return signalPassFailure();
61 }
62 
63 LogicalResult LowerClocksToFuncsPass::lowerModel(ModelOp modelOp) {
64  LLVM_DEBUG(llvm::dbgs() << "Lowering clocks in `" << modelOp.getName()
65  << "`\n");
66 
67  // Find the clocks to extract.
68  SmallVector<Operation *> clocks;
69  modelOp.walk([&](Operation *op) {
70  if (isa<ClockTreeOp, PassThroughOp>(op))
71  clocks.push_back(op);
72  });
73 
74  // Perform the actual extraction.
75  OpBuilder funcBuilder(modelOp);
76  for (auto *op : clocks)
77  if (failed(lowerClock(op, modelOp.getBody().getArgument(0), funcBuilder)))
78  return failure();
79 
80  return success();
81 }
82 
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)));
88 
89  // Add a `StorageType` block argument to the clock's body block which we are
90  // going to use to pass the storage pointer to the clock once it has been
91  // pulled out into a separate function.
92  Region &clockRegion = clockOp->getRegion(0);
93  Value clockStorageArg = clockRegion.addArgument(modelStorageArg.getType(),
94  modelStorageArg.getLoc());
95 
96  // Ensure the clock tree does not use any values defined outside of it.
97  if (failed(isolateClock(clockOp, modelStorageArg, clockStorageArg)))
98  return failure();
99 
100  // Add a return op to the end of the body.
101  auto builder = OpBuilder::atBlockEnd(&clockRegion.front());
102  builder.create<func::ReturnOp>(clockOp->getLoc());
103 
104  // Pick a name for the clock function.
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); // uniquifies the name
112  LLVM_DEBUG(llvm::dbgs() << " - Created function `" << funcOp.getSymName()
113  << "`\n");
114 
115  // Create a call to the function within the model.
116  builder.setInsertionPoint(clockOp);
117  if (auto treeOp = dyn_cast<ClockTreeOp>(clockOp)) {
118  auto ifOp =
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});
123  } else {
124  builder.create<func::CallOp>(clockOp->getLoc(), funcOp,
125  ValueRange{modelStorageArg});
126  }
127 
128  // Move the clock's body block to the function and remove the old clock op.
129  funcOp.getBody().takeBody(clockRegion);
130  clockOp->erase();
131 
132  return success();
133 }
134 
135 /// Copy any external constants that the clock tree might be using into its
136 /// body. Anything besides constants should no longer exist after a proper run
137 /// of the pipeline.
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()) {
146  // Block arguments are okay, since there's nothing we can move.
147  if (operand.get() == modelStorageArg) {
148  operand.set(clockStorageArg);
149  continue;
150  }
151  if (isa<BlockArgument>(operand.get())) {
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();
158  }
159 
160  // Check if the value is defined outside of the clock op.
161  auto *definingOp = operand.get().getDefiningOp();
162  assert(definingOp && "block arguments ruled out above");
163  Region *definingRegion = definingOp->getParentRegion();
164  if (clockRegion->isAncestor(definingRegion))
165  continue;
166 
167  // The op is defined outside the clock, so we need to create a copy of the
168  // defining inside the clock tree.
169  if (auto copiedValue = copiedValues.lookup(operand.get())) {
170  operand.set(copiedValue);
171  continue;
172  }
173 
174  // Check that we can actually copy this definition inside.
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();
181  }
182 
183  // Copy the op inside the clock tree (or move it if all uses are within
184  // the clock tree).
185  bool canMove = llvm::all_of(definingOp->getUsers(), [&](Operation *user) {
186  return clockRegion->isAncestor(user->getParentRegion());
187  });
188  Operation *clonedOp;
189  if (canMove) {
190  definingOp->remove();
191  clonedOp = definingOp;
192  ++numOpsMoved;
193  } else {
194  clonedOp = definingOp->cloneWithoutRegions();
195  ++numOpsCopied;
196  }
197  builder.insert(clonedOp);
198  if (!canMove) {
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);
204  }
205  }
206  }
207  return WalkResult::advance();
208  });
209  return success(!result.wasInterrupted());
210 }
211 
212 std::unique_ptr<Pass> arc::createLowerClocksToFuncsPass() {
213  return std::make_unique<LowerClocksToFuncsPass>();
214 }
assert(baseType &&"element must be base type")
Builder builder
std::unique_ptr< mlir::Pass > createLowerClocksToFuncsPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: hw.py:1