CIRCT  19.0.0git
ExplicitRegs.cpp
Go to the documentation of this file.
1 //===- ExplicitRegs.cpp - Explicit regs pass --------------------*- C++ -*-===//
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 //
9 // Contains the definitions of the explicit regs pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "PassDetails.h"
15 #include "llvm/Support/Debug.h"
16 
17 using namespace mlir;
18 using namespace circt;
19 using namespace pipeline;
20 
21 namespace {
22 
23 struct NamedValue {
24  Value v;
25  StringAttr name;
26  operator Value() const { return v; }
27  operator Attribute() const { return name; }
28  operator llvm::StringRef() const { return name.getValue(); }
29 };
30 
31 class ExplicitRegsPass : public ExplicitRegsBase<ExplicitRegsPass> {
32 public:
33  void runOnOperation() override;
34 
35 private:
36  void runOnPipeline(ScheduledPipelineOp p);
37 
38  // Recursively routes value v backwards through the pipeline, adding new
39  // registers to 'stage' if the value was not already registered in the stage.
40  // Returns the registered version of 'v' through 'stage'.
41  NamedValue routeThroughStage(Value v, Block *stage);
42 
43  // Returns the distance between two stages in the pipeline. The distance is
44  // defined wrt. the ordered stages of the pipeline.
45  int64_t stageDistance(Block *from, Block *to);
46 
47  // Returns a suitable name for a value.
48  StringAttr genValueName(Value v);
49 
50  struct RoutedValue {
51  Backedge v;
52  // If true, this value is routed through a stage as a register, else
53  // it is routed through a stage as a pass-through.e
54  bool isReg;
55  StringAttr name;
56 
57  operator NamedValue() { return NamedValue{v, name}; }
58  };
59  // A mapping storing whether a given stage register constains a registered
60  // version of a given value. The registered version will be a backedge during
61  // pipeline body analysis. Once the entire body has been analyzed, `regs`
62  // operands will be added to the pipeline.stage operation, and the backedge
63  // will be replaced. MapVector ensures deterministic iteration order, which in
64  // turn ensures determinism during stage op IR emission.
65  DenseMap<Block *, llvm::MapVector<Value, RoutedValue>> stageRegOrPassMap;
66 
67  // A mapping between stages and their index in the pipeline.
68  llvm::DenseMap<Block *, unsigned> stageMap;
69 
70  std::shared_ptr<BackedgeBuilder> bb;
71  ScheduledPipelineOp parent;
72 };
73 
74 } // end anonymous namespace
75 
76 int64_t ExplicitRegsPass::stageDistance(Block *from, Block *to) {
77  int64_t fromStage = stageMap[from];
78  int64_t toStage = stageMap[to];
79  return toStage - fromStage;
80 }
81 
82 StringAttr ExplicitRegsPass::genValueName(Value v) {
83  Operation *definingOp = v.getDefiningOp();
84  StringAttr valueName;
85  auto setNameFn = [&](Value other, StringRef name) {
86  if (other == v)
87  valueName = StringAttr::get(other.getContext(), name);
88  };
89  if (definingOp) {
90  if (OpAsmOpInterface asmInterface = dyn_cast<OpAsmOpInterface>(definingOp))
91  asmInterface.getAsmResultNames(setNameFn);
92  // Somewhat comb specific - look for an sv.namehint attribute.
93  else if (auto nameHint =
94  definingOp->getAttrOfType<StringAttr>("sv.namehint"))
95  valueName = nameHint;
96  else
97  valueName = StringAttr::get(v.getContext(), "");
98  } else {
99  // It's a block argument - leverage the OpAsmOpInterface of the pipeline
100  // op.
101  auto asmInterface = cast<OpAsmOpInterface>(parent.getOperation());
102  asmInterface.getAsmBlockArgumentNames(parent.getBody(), setNameFn);
103  }
104 
105  assert(valueName && "Wasn't able to generate a name for a value");
106  return valueName;
107 }
108 
109 // NOLINTNEXTLINE(misc-no-recursion)
110 NamedValue ExplicitRegsPass::routeThroughStage(Value v, Block *stage) {
111  Value retVal = v;
112  Block *definingStage = retVal.getParentBlock();
113 
114  // Is the value defined in the current stage?
115  if (definingStage == stage) {
116  // Value is defined in the current stage - no routing needed, but we need to
117  // define a name.
118  return NamedValue{retVal, genValueName(retVal)};
119  }
120 
121  auto regIt = stageRegOrPassMap[stage].find(retVal);
122  if (regIt != stageRegOrPassMap[stage].end()) {
123  // 'v' is already routed through 'stage' - return the registered/passed
124  // version.
125  return regIt->second;
126  }
127 
128  // Is the value a constant? If so, we allow it; constants are special cases
129  // which are allowed to be used in any stage.
130  auto *definingOp = retVal.getDefiningOp();
131  if (definingOp && definingOp->hasTrait<OpTrait::ConstantLike>())
132  return NamedValue{retVal, StringAttr::get(retVal.getContext(), "")};
133 
134  // Value is defined somewhere before the provided stage - route it through the
135  // stage, and recurse to the predecessor stage.
136  int64_t valueLatency = 0;
137  if (auto latencyOp = dyn_cast_or_null<LatencyOp>(definingOp))
138  valueLatency = latencyOp.getLatency();
139 
140  // A value should be registered in this stage if the latency of the value
141  // is less than the distance between the current stage and the defining stage.
142  bool isReg = valueLatency < stageDistance(definingStage, stage);
143  auto valueBackedge = bb->get(retVal.getType());
144  auto stageRegBE = stageRegOrPassMap[stage].insert(
145  {retVal, RoutedValue{valueBackedge, isReg,
146  StringAttr::get(retVal.getContext(), "")}});
147  retVal = valueBackedge;
148 
149  // Recurse - recursion will only create a new backedge if necessary.
150  Block *stagePred = stage->getSinglePredecessor();
151  assert(stagePred && "Expected stage to have a single predecessor");
152  auto namedV = routeThroughStage(v, stagePred);
153  // And name the backedge using the routed value result name.
154  stageRegBE.first->second.name = namedV.name;
155 
156  return NamedValue{retVal, namedV.name};
157 }
158 
159 void ExplicitRegsPass::runOnPipeline(ScheduledPipelineOp pipeline) {
160  OpBuilder b(pipeline.getContext());
161  parent = pipeline;
162  bb = std::make_shared<BackedgeBuilder>(b, pipeline.getLoc());
163 
164  // Cache external-like inputs in a set for fast lookup. This also includes
165  // clock, reset, and stall.
166  llvm::DenseSet<Value> extLikeInputs;
167  for (auto extInput : pipeline.getExtInputs())
168  extLikeInputs.insert(extInput);
169  extLikeInputs.insert(pipeline.getClock());
170  extLikeInputs.insert(pipeline.getReset());
171  if (pipeline.hasStall())
172  extLikeInputs.insert(pipeline.getStall());
173 
174  // Iterate over the pipeline body in-order (!).
175  stageMap = pipeline.getStageMap();
176  for (Block *stage : pipeline.getOrderedStages()) {
177  // Walk the stage body - we do this since register materialization needs
178  // to consider all levels of nesting within the stage.
179  stage->walk([&](Operation *op) {
180  // Check the operands of this operation to see if any of them cross a
181  // stage boundary.
182  for (OpOperand &operand : op->getOpOperands()) {
183  if (extLikeInputs.contains(operand.get())) {
184  // Never route external inputs through a stage.
185  continue;
186  }
187  if (getParentStageInPipeline(pipeline, operand.get()) == stage) {
188  // The operand is defined by some operation or block which ultimately
189  // resides within the current pipeline stage. No routing needed.
190  continue;
191  }
192  Value reroutedValue = routeThroughStage(operand.get(), stage);
193  if (reroutedValue != operand.get())
194  op->setOperand(operand.getOperandNumber(), reroutedValue);
195  }
196  });
197  }
198 
199  auto *ctx = &getContext();
200 
201  // All values have been recorded through the stages.
202  // Now
203  // 1. Add 'regs' and 'pass' operands to the `pipeline.stage` operations
204  // 2. Add the corresponding block arguments to the stage blocks.
205  for (auto &it : stageRegOrPassMap) {
206  Block *stage = it.first;
207  auto &regOrPassMap = it.second;
208 
209  // Gather register inputs to this stage, either from a predecessor stage
210  // or from the original op.
211  llvm::MapVector<Value, Value> regInsMap, passInsMap;
212  llvm::SmallVector<Attribute> regNames, passNames;
213  Block *predecessorStage = stage->getSinglePredecessor();
214  auto predStageRegOrPassMap = stageRegOrPassMap.find(predecessorStage);
215  assert(predecessorStage && "Stage should always have a single predecessor");
216  for (auto &[value, backedge] : regOrPassMap) {
217  if (backedge.isReg)
218  regNames.push_back(backedge.name);
219  else
220  passNames.push_back(backedge.name);
221 
222  if (predStageRegOrPassMap != stageRegOrPassMap.end()) {
223  // Grab the value if passed through the predecessor stage, else,
224  // use the raw value.
225  auto predRegIt = predStageRegOrPassMap->second.find(value);
226  if (predRegIt != predStageRegOrPassMap->second.end()) {
227  if (backedge.isReg) {
228  regInsMap[value] = predRegIt->second.v;
229  } else {
230  passInsMap[value] = predRegIt->second.v;
231  }
232  continue;
233  }
234  }
235 
236  // Not passed through the stage - must be the original value.
237  if (backedge.isReg)
238  regInsMap[value] = value;
239  else
240  passInsMap[value] = value;
241  }
242 
243  // Replace the predecessor stage terminator, which feeds this stage, with
244  // a new terminator that has materialized arguments.
245  llvm::SmallVector<Value> regIns, passIns;
246  llvm::transform(regInsMap, std::back_inserter(regIns),
247  [](auto &pair) { return pair.second; });
248  llvm::transform(passInsMap, std::back_inserter(passIns),
249  [](auto &pair) { return pair.second; });
250 
251  StageOp terminator = cast<StageOp>(predecessorStage->getTerminator());
252  b.setInsertionPoint(terminator);
253  llvm::SmallVector<llvm::SmallVector<Value>> clockGates;
254  b.create<StageOp>(terminator.getLoc(), terminator.getNextStage(), regIns,
255  passIns, clockGates, b.getArrayAttr(regNames),
256  b.getArrayAttr(passNames));
257  terminator.erase();
258 
259  // ... and add arguments to the next stage. Registers first, then
260  // passthroughs.
261  // While doing so, replace for the next stage arguments with the new
262  // arguments. Doing so wrt. regInsMap ensures that we preserve the
263  // order of operands in the StageOp and the target block.
264  size_t argIdx = 0;
265  auto addArgAndRewriteBE = [&](llvm::MapVector<Value, Value> &map) {
266  for (auto origToActualValue : map) {
267  Value origValue = origToActualValue.first;
268  stage->insertArgument(argIdx, origValue.getType(),
269  UnknownLoc::get(ctx));
270  auto *backedgeIt = regOrPassMap.find(origValue);
271  assert(backedgeIt != regOrPassMap.end() &&
272  "Expected to find backedge for value");
273  backedgeIt->second.v.setValue(stage->getArgument(argIdx));
274  ++argIdx;
275  }
276  };
277  addArgAndRewriteBE(regInsMap);
278  addArgAndRewriteBE(passInsMap);
279  }
280 
281  // Clear internal state. See https://github.com/llvm/circt/issues/3235
282  stageRegOrPassMap.clear();
283 }
284 
285 void ExplicitRegsPass::runOnOperation() {
286  getOperation()->walk(
287  [&](ScheduledPipelineOp pipeline) { runOnPipeline(pipeline); });
288 }
289 
290 std::unique_ptr<mlir::Pass> circt::pipeline::createExplicitRegsPass() {
291  return std::make_unique<ExplicitRegsPass>();
292 }
assert(baseType &&"element must be base type")
static std::string valueName(Operation *scopeOp, Value v)
Convenience function for getting the SSA name of v under the scope of operation scopeOp.
Definition: CalyxOps.cpp:120
static llvm::raw_string_ostream & genValueName(llvm::raw_string_ostream &os, Value value)
Definition: IbisOps.cpp:41
Backedge is a wrapper class around a Value.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
std::unique_ptr< mlir::Pass > createExplicitRegsPass()
Block * getParentStageInPipeline(ScheduledPipelineOp pipeline, Operation *op)
Definition: PipelineOps.cpp:66
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21