CIRCT  20.0.0git
LowerLayers.cpp
Go to the documentation of this file.
1 //===- LowerLayers.cpp - Lower Layers by Convention -------------*- 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 // This pass lowers FIRRTL layers based on their specified convention.
9 //
10 //===----------------------------------------------------------------------===//
11 
17 #include "circt/Dialect/SV/SVOps.h"
18 #include "circt/Support/Utils.h"
19 #include "mlir/Pass/Pass.h"
20 #include "llvm/Support/Debug.h"
21 #include "llvm/Support/Mutex.h"
22 
23 #define DEBUG_TYPE "firrtl-lower-layers"
24 
25 namespace circt {
26 namespace firrtl {
27 #define GEN_PASS_DEF_LOWERLAYERS
28 #include "circt/Dialect/FIRRTL/Passes.h.inc"
29 } // namespace firrtl
30 } // namespace circt
31 
32 using namespace circt;
33 using namespace firrtl;
34 
35 namespace {
36 
37 /// Indicates the kind of reference that was captured.
38 enum class ConnectKind {
39  /// A normal captured value. This is a read of a value outside the
40  /// layerblock.
41  NonRef,
42  /// A reference. This is a destination of a ref define.
43  Ref
44 };
45 
46 struct ConnectInfo {
47  Value value;
48  ConnectKind kind;
49 };
50 
51 /// The delimiters that should be used for a given generated name. These vary
52 /// for modules and files, as well as by convention.
53 enum class Delimiter { BindModule = '_', BindFile = '-', InlineMacro = '$' };
54 
55 } // namespace
56 
57 // A mapping of an old InnerRefAttr to the new inner symbol and module name that
58 // need to be spliced into the old InnerRefAttr. This is used to fix
59 // hierarchical path operations after layers are converted to modules.
60 using InnerRefMap =
61  DenseMap<hw::InnerRefAttr, std::pair<hw::InnerSymAttr, StringAttr>>;
62 
63 //===----------------------------------------------------------------------===//
64 // Naming Helpers
65 //===----------------------------------------------------------------------===//
66 
67 static void appendName(StringRef name, SmallString<32> &output,
68  bool toLower = false,
69  Delimiter delimiter = Delimiter::BindFile) {
70  if (name.empty())
71  return;
72  if (!output.empty())
73  output.push_back(static_cast<char>(delimiter));
74  output.append(name);
75  if (!toLower)
76  return;
77  auto i = output.size() - name.size();
78  output[i] = llvm::toLower(output[i]);
79 }
80 
81 static void appendName(SymbolRefAttr name, SmallString<32> &output,
82  bool toLower = false,
83  Delimiter delimiter = Delimiter::BindFile) {
84  appendName(name.getRootReference(), output, toLower, delimiter);
85  for (auto nested : name.getNestedReferences())
86  appendName(nested.getValue(), output, toLower, delimiter);
87 }
88 
89 /// For a layer `@A::@B::@C` in module Module,
90 /// the generated module is called `Module_A_B_C`.
91 static SmallString<32> moduleNameForLayer(StringRef moduleName,
92  SymbolRefAttr layerName) {
93  SmallString<32> result;
94  appendName(moduleName, result, /*toLower=*/false,
95  /*delimiter=*/Delimiter::BindModule);
96  appendName(layerName, result, /*toLower=*/false,
97  /*delimiter=*/Delimiter::BindModule);
98  return result;
99 }
100 
101 /// For a layerblock `@A::@B::@C`,
102 /// the generated instance is called `a_b_c`.
103 static SmallString<32> instanceNameForLayer(SymbolRefAttr layerName) {
104  SmallString<32> result;
105  appendName(layerName, result, /*toLower=*/true,
106  /*delimiter=*/Delimiter::BindModule);
107  return result;
108 }
109 
110 /// For all layerblocks `@A::@B::@C` in a circuit called Circuit,
111 /// the output filename is `layers-Circuit-A-B-C.sv`.
112 static SmallString<32> fileNameForLayer(StringRef circuitName,
113  SymbolRefAttr layerName) {
114  SmallString<32> result;
115  result.append("layers");
116  appendName(circuitName, result);
117  appendName(layerName, result);
118  result.append(".sv");
119  return result;
120 }
121 
122 /// For a layerblock `@A::@B::@C`, the verilog macro is `A_B_C`.
123 static SmallString<32>
124 macroNameForLayer(StringRef circuitName,
125  ArrayRef<FlatSymbolRefAttr> layerName) {
126  SmallString<32> result("layer_");
127  result.append(circuitName);
128  for (auto part : layerName)
129  appendName(part, result, /*toLower=*/false,
130  /*delimiter=*/Delimiter::InlineMacro);
131  return result;
132 }
133 
134 //===----------------------------------------------------------------------===//
135 // LowerLayersPass
136 //===----------------------------------------------------------------------===//
137 
139  : public circt::firrtl::impl::LowerLayersBase<LowerLayersPass> {
140  hw::OutputFileAttr getOutputFile(SymbolRefAttr layerName) {
141  auto layer = symbolToLayer.lookup(layerName);
142  if (!layer)
143  return nullptr;
144  return layer->getAttrOfType<hw::OutputFileAttr>("output_file");
145  }
146 
147  hw::OutputFileAttr outputFileForLayer(StringRef circuitName,
148  SymbolRefAttr layerName) {
149  if (auto file = getOutputFile(layerName))
150  return hw::OutputFileAttr::getFromDirectoryAndFilename(
151  &getContext(), file.getDirectory(),
152  fileNameForLayer(circuitName, layerName),
153  /*excludeFromFileList=*/true);
154  return hw::OutputFileAttr::getFromFilename(
155  &getContext(), fileNameForLayer(circuitName, layerName),
156  /*excludeFromFileList=*/true);
157  }
158 
159  /// Safely build a new module with a given namehint. This handles geting a
160  /// lock to modify the top-level circuit.
161  FModuleOp buildNewModule(OpBuilder &builder, LayerBlockOp layerBlock,
162  SmallVectorImpl<PortInfo> &ports);
163 
164  /// Strip layer colors from the module's interface.
165  FailureOr<InnerRefMap> runOnModuleLike(FModuleLike moduleLike);
166 
167  /// Extract layerblocks and strip probe colors from all ops under the module.
168  LogicalResult runOnModuleBody(FModuleOp moduleOp, InnerRefMap &innerRefMap);
169 
170  /// Update the module's port types to remove any explicit layer requirements
171  /// from any probe types.
172  void removeLayersFromPorts(FModuleLike moduleLike);
173 
174  /// Update the value's type to remove any layers from any probe types.
175  void removeLayersFromValue(Value value);
176 
177  /// Remove any layers from the result of the cast. If the cast becomes a nop,
178  /// remove the cast itself from the IR.
179  void removeLayersFromRefCast(RefCastOp cast);
180 
181  /// Lower an inline layerblock to an ifdef block.
182  void lowerInlineLayerBlock(LayerOp layer, LayerBlockOp layerBlock);
183 
184  /// Preprocess layers to build macro declarations and cache information about
185  /// the layers so that this can be quired later.
186  void preprocessLayers(CircuitNamespace &ns, OpBuilder &b, LayerOp layer,
187  StringRef circuitName,
188  SmallVector<FlatSymbolRefAttr> &stack);
189  void preprocessLayers(CircuitNamespace &ns, LayerOp layer,
190  StringRef circuitName);
191 
192  /// Entry point for the function.
193  void runOnOperation() override;
194 
195  /// Indicates exclusive access to modify the circuitNamespace and the circuit.
196  llvm::sys::SmartMutex<true> *circuitMutex;
197 
198  /// A map of layer blocks to module name that should be created for it.
199  DenseMap<LayerBlockOp, StringRef> moduleNames;
200 
201  /// A map from inline layers to their macro names.
202  DenseMap<LayerOp, FlatSymbolRefAttr> macroNames;
203 
204  /// A mapping of symbol name to layer operation.
205  DenseMap<SymbolRefAttr, LayerOp> symbolToLayer;
206 };
207 
208 /// Multi-process safe function to build a module in the circuit and return it.
209 /// The name provided is only a namehint for the module---a unique name will be
210 /// generated if there are conflicts with the namehint in the circuit-level
211 /// namespace.
212 FModuleOp LowerLayersPass::buildNewModule(OpBuilder &builder,
213  LayerBlockOp layerBlock,
214  SmallVectorImpl<PortInfo> &ports) {
215  auto location = layerBlock.getLoc();
216  auto namehint = moduleNames.lookup(layerBlock);
217  llvm::sys::SmartScopedLock<true> instrumentationLock(*circuitMutex);
218  FModuleOp newModule = builder.create<FModuleOp>(
219  location, builder.getStringAttr(namehint),
220  ConventionAttr::get(builder.getContext(), Convention::Internal), ports,
221  ArrayAttr{});
222  if (auto dir = getOutputFile(layerBlock.getLayerNameAttr())) {
223  assert(dir.isDirectory());
224  newModule->setAttr("output_file", dir);
225  }
226  SymbolTable::setSymbolVisibility(newModule, SymbolTable::Visibility::Private);
227  return newModule;
228 }
229 
231  auto type = dyn_cast<RefType>(value.getType());
232  if (!type || !type.getLayer())
233  return;
234  value.setType(type.removeLayer());
235 }
236 
238  auto result = cast.getResult();
239  auto oldType = result.getType();
240  if (oldType.getLayer()) {
241  auto input = cast.getInput();
242  auto srcType = input.getType();
243  auto newType = oldType.removeLayer();
244  if (newType == srcType) {
245  result.replaceAllUsesWith(input);
246  cast->erase();
247  return;
248  }
249  result.setType(newType);
250  }
251 }
252 
253 void LowerLayersPass::removeLayersFromPorts(FModuleLike moduleLike) {
254  auto oldTypeAttrs = moduleLike.getPortTypesAttr();
255  SmallVector<Attribute> newTypeAttrs;
256  newTypeAttrs.reserve(oldTypeAttrs.size());
257  bool changed = false;
258 
259  for (auto typeAttr : oldTypeAttrs.getAsRange<TypeAttr>()) {
260  if (auto refType = dyn_cast<RefType>(typeAttr.getValue())) {
261  if (refType.getLayer()) {
262  typeAttr = TypeAttr::get(refType.removeLayer());
263  changed = true;
264  }
265  }
266  newTypeAttrs.push_back(typeAttr);
267  }
268 
269  if (!changed)
270  return;
271 
272  moduleLike.setPortTypesAttr(
273  ArrayAttr::get(moduleLike.getContext(), newTypeAttrs));
274 
275  if (auto moduleOp = dyn_cast<FModuleOp>(moduleLike.getOperation())) {
276  for (auto arg : moduleOp.getBodyBlock()->getArguments())
277  removeLayersFromValue(arg);
278  }
279 }
280 
281 FailureOr<InnerRefMap>
282 LowerLayersPass::runOnModuleLike(FModuleLike moduleLike) {
283  LLVM_DEBUG({
284  llvm::dbgs() << "Module: " << moduleLike.getModuleName() << "\n";
285  llvm::dbgs() << " Examining Layer Blocks:\n";
286  });
287 
288  // Strip away layers from the interface of the module-like op.
289  InnerRefMap innerRefMap;
290  auto result =
291  TypeSwitch<Operation *, LogicalResult>(moduleLike.getOperation())
292  .Case<FModuleOp>([&](auto op) {
293  op.setLayers({});
294  removeLayersFromPorts(op);
295  return runOnModuleBody(op, innerRefMap);
296  })
297  .Case<FExtModuleOp, FIntModuleOp, FMemModuleOp>([&](auto op) {
298  op.setLayers({});
299  removeLayersFromPorts(op);
300  return success();
301  })
302  .Case<ClassOp, ExtClassOp>([](auto) { return success(); })
303  .Default(
304  [](auto *op) { return op->emitError("unknown module-like op"); });
305 
306  if (failed(result))
307  return failure();
308 
309  return innerRefMap;
310 }
311 
313  LayerBlockOp layerBlock) {
314  OpBuilder builder(layerBlock);
315  auto macroName = macroNames[layer];
316  auto ifDef = builder.create<sv::IfDefOp>(layerBlock.getLoc(), macroName);
317  ifDef.getBodyRegion().takeBody(layerBlock.getBodyRegion());
318  layerBlock.erase();
319 }
320 
321 LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
322  InnerRefMap &innerRefMap) {
323  CircuitOp circuitOp = moduleOp->getParentOfType<CircuitOp>();
324  StringRef circuitName = circuitOp.getName();
325  hw::InnerSymbolNamespace ns(moduleOp);
326 
327  // A map of instance ops to modules that this pass creates. This is used to
328  // check if this was an instance that we created and to do fast module
329  // dereferencing (avoiding a symbol table).
330  DenseMap<InstanceOp, FModuleOp> createdInstances;
331 
332  // Post-order traversal that expands a layer block into its parent. For each
333  // layer block found do the following:
334  //
335  // 1. Create and connect one ref-type output port for each value defined in
336  // this layer block that drives an instance marked lowerToBind and move
337  // this instance outside the layer block.
338  // 2. Create one input port for each value captured by this layer block.
339  // 3. Create a new module for this layer block and move the (mutated) body of
340  // this layer block to the new module.
341  // 4. Instantiate the new module outside the layer block and hook it up.
342  // 5. Erase the layer block.
343  auto result = moduleOp.walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
344  // Strip layer requirements from any op that might represent a probe.
345  if (auto wire = dyn_cast<WireOp>(op)) {
346  removeLayersFromValue(wire.getResult());
347  return WalkResult::advance();
348  }
349  if (auto sub = dyn_cast<RefSubOp>(op)) {
350  removeLayersFromValue(sub.getResult());
351  return WalkResult::advance();
352  }
353  if (auto instance = dyn_cast<InstanceOp>(op)) {
354  instance.setLayers({});
355  for (auto result : instance.getResults())
356  removeLayersFromValue(result);
357  return WalkResult::advance();
358  }
359  if (auto cast = dyn_cast<RefCastOp>(op)) {
360  removeLayersFromRefCast(cast);
361  return WalkResult::advance();
362  }
363 
364  auto layerBlock = dyn_cast<LayerBlockOp>(op);
365  if (!layerBlock)
366  return WalkResult::advance();
367 
368  auto layer = symbolToLayer.lookup(layerBlock.getLayerName());
369 
370  if (layer.getConvention() == LayerConvention::Inline) {
371  lowerInlineLayerBlock(layer, layerBlock);
372  return WalkResult::advance();
373  }
374 
375  assert(layer.getConvention() == LayerConvention::Bind);
376  Block *body = layerBlock.getBody(0);
377  OpBuilder builder(moduleOp);
378 
379  // Ports that need to be created for the module derived from this layer
380  // block.
381  SmallVector<PortInfo> ports;
382 
383  // Connection that need to be made to the instance of the derived module.
384  SmallVector<ConnectInfo> connectValues;
385 
386  // Create an input port for an operand that is captured from outside.
387  auto createInputPort = [&](Value operand, Location loc) {
388  const auto &[portName, _] = getFieldName(FieldRef(operand, 0), true);
389 
390  // If the value is a ref, we must resolve the ref inside the parent,
391  // passing the input as a value instead of a ref. Inside the layer, we
392  // convert (ref.send) the value back into a ref.
393  auto type = operand.getType();
394  if (auto refType = dyn_cast<RefType>(type))
395  type = refType.getType();
396 
397  ports.push_back({builder.getStringAttr(portName), type, Direction::In,
398  /*symName=*/{},
399  /*location=*/loc});
400  // Update the layer block's body with arguments as we will swap this body
401  // into the module when we create it. If this is a ref type, then add a
402  // refsend to convert from the non-ref type input port.
403  Value replacement = body->addArgument(type, loc);
404  if (isa<RefType>(operand.getType())) {
405  OpBuilder::InsertionGuard guard(builder);
406  builder.setInsertionPointToStart(body);
407  replacement = builder.create<RefSendOp>(loc, replacement);
408  }
409  operand.replaceUsesWithIf(replacement, [&](OpOperand &use) {
410  auto *user = use.getOwner();
411  if (!layerBlock->isAncestor(user))
412  return false;
413  if (auto connectLike = dyn_cast<FConnectLike>(user)) {
414  if (use.getOperandNumber() == 0)
415  return false;
416  }
417  return true;
418  });
419 
420  connectValues.push_back({operand, ConnectKind::NonRef});
421  };
422 
423  // Set the location intelligently. Use the location of the capture if this
424  // is a port created for forwarding from a parent layer block to a nested
425  // layer block. Otherwise, use unknown.
426  auto getPortLoc = [&](Value port) -> Location {
427  Location loc = UnknownLoc::get(port.getContext());
428  if (auto *destOp = port.getDefiningOp())
429  if (auto instOp = dyn_cast<InstanceOp>(destOp)) {
430  auto modOpIt = createdInstances.find(instOp);
431  if (modOpIt != createdInstances.end()) {
432  auto portNum = cast<OpResult>(port).getResultNumber();
433  loc = modOpIt->getSecond().getPortLocation(portNum);
434  }
435  }
436  return loc;
437  };
438 
439  // Create an output probe port port and adds a ref.define/ref.send to
440  // drive the port if this was not already capturing a ref type.
441  auto createOutputPort = [&](Value dest, Value src) {
442  auto loc = getPortLoc(dest);
443  auto portNum = ports.size();
444  const auto &[portName, _] = getFieldName(FieldRef(dest, 0), true);
445 
446  RefType refType;
447  if (auto oldRef = dyn_cast<RefType>(dest.getType()))
448  refType = oldRef;
449  else
450  refType = RefType::get(
451  type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType(),
452  /*forceable=*/false);
453 
454  ports.push_back({builder.getStringAttr(portName), refType, Direction::Out,
455  /*symName=*/{}, /*location=*/loc});
456  Value replacement = body->addArgument(refType, loc);
457  if (isa<RefType>(dest.getType())) {
458  dest.replaceUsesWithIf(replacement, [&](OpOperand &use) {
459  auto *user = use.getOwner();
460  if (!layerBlock->isAncestor(user))
461  return false;
462  if (auto connectLike = dyn_cast<FConnectLike>(user)) {
463  if (use.getOperandNumber() == 0)
464  return true;
465  }
466  return false;
467  });
468  connectValues.push_back({dest, ConnectKind::Ref});
469  return;
470  }
471  connectValues.push_back({dest, ConnectKind::NonRef});
472  OpBuilder::InsertionGuard guard(builder);
473  builder.setInsertionPointAfterValue(src);
474  builder.create<RefDefineOp>(
475  loc, body->getArgument(portNum),
476  builder.create<RefSendOp>(loc, src)->getResult(0));
477  };
478 
479  SmallVector<hw::InnerSymAttr> innerSyms;
480  SmallVector<RWProbeOp> rwprobes;
481  auto layerBlockWalkResult = layerBlock.walk([&](Operation *op) {
482  // Record any operations inside the layer block which have inner symbols.
483  // Theses may have symbol users which need to be updated.
484  if (auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
485  if (auto innerSym = symOp.getInnerSymAttr())
486  innerSyms.push_back(innerSym);
487 
488  // Handle instance ops that were created from nested layer blocks. These
489  // ops need to be moved outside the layer block to avoid nested binds.
490  // Nested binds are illegal in the SystemVerilog specification (and
491  // checked by FIRRTL verification).
492  //
493  // For each value defined in this layer block which drives a port of one
494  // of these instances, create an output reference type port on the
495  // to-be-created module and drive it with the value. Move the instance
496  // outside the layer block. We will hook it up later once we replace the
497  // layer block with an instance.
498  if (auto instOp = dyn_cast<InstanceOp>(op)) {
499  // Ignore instances which this pass did not create.
500  if (!createdInstances.contains(instOp))
501  return WalkResult::advance();
502 
503  LLVM_DEBUG({
504  llvm::dbgs()
505  << " Found instance created from nested layer block:\n"
506  << " module: " << instOp.getModuleName() << "\n"
507  << " instance: " << instOp.getName() << "\n";
508  });
509  instOp->moveBefore(layerBlock);
510  return WalkResult::advance();
511  }
512 
513  // Handle subfields, subindexes, and subaccesses which are indexing into
514  // non-passive values. If these are kept in the module, then the module
515  // must create bi-directional ports. This doesn't make sense as the
516  // FIRRTL spec states that no layerblock may write to values outside it.
517  // Fix this for subfield and subindex by moving these ops outside the
518  // layerblock. Try to fix this for subaccess and error if the move can't
519  // be made because the index is defined inside the layerblock. (This case
520  // is exceedingly rare given that subaccesses are almost always unexepcted
521  // when this pass runs.)
522  if (isa<SubfieldOp, SubindexOp>(op)) {
523  auto input = op->getOperand(0);
524  if (!firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive() &&
525  !isAncestorOfValueOwner(layerBlock, input))
526  op->moveBefore(layerBlock);
527  return WalkResult::advance();
528  }
529 
530  if (auto subOp = dyn_cast<SubaccessOp>(op)) {
531  auto input = subOp.getInput();
532  if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive())
533  return WalkResult::advance();
534 
535  if (!isAncestorOfValueOwner(layerBlock, input) &&
536  !isAncestorOfValueOwner(layerBlock, subOp.getIndex())) {
537  subOp->moveBefore(layerBlock);
538  return WalkResult::advance();
539  }
540  auto diag = op->emitOpError()
541  << "has a non-passive operand and captures a value defined "
542  "outside its enclosing bind-convention layerblock. The "
543  "'LowerLayers' pass cannot lower this as it would "
544  "create an output port on the resulting module.";
545  diag.attachNote(layerBlock.getLoc())
546  << "the layerblock is defined here";
547  return WalkResult::interrupt();
548  }
549 
550  if (auto rwprobe = dyn_cast<RWProbeOp>(op)) {
551  rwprobes.push_back(rwprobe);
552  return WalkResult::advance();
553  }
554 
555  if (auto connect = dyn_cast<FConnectLike>(op)) {
556  auto src = connect.getSrc();
557  auto dst = connect.getDest();
558  auto srcInLayerBlock = isAncestorOfValueOwner(layerBlock, src);
559  auto dstInLayerBlock = isAncestorOfValueOwner(layerBlock, dst);
560  if (!srcInLayerBlock && !dstInLayerBlock) {
561  connect->moveBefore(layerBlock);
562  return WalkResult::advance();
563  }
564  // Create an input port.
565  if (!srcInLayerBlock) {
566  createInputPort(src, op->getLoc());
567  return WalkResult::advance();
568  }
569  // Create an output port.
570  if (!dstInLayerBlock) {
571  createOutputPort(dst, src);
572  if (!isa<RefType>(dst.getType()))
573  connect.erase();
574  return WalkResult::advance();
575  }
576  // Source and destination in layer block. Nothing to do.
577  return WalkResult::advance();
578  }
579 
580  // Pre-emptively de-squiggle connections that we are creating. This will
581  // later be cleaned up by the de-squiggling pass. However, there is no
582  // point in creating deeply squiggled connections if we don't have to.
583  //
584  // This pattern matches the following structure. Move the ref.resolve
585  // outside the layer block. The matchingconnect will be moved outside in
586  // the next loop iteration:
587  // %0 = ...
588  // %1 = ...
589  // firrtl.layerblock {
590  // %2 = ref.resolve %0
591  // firrtl.matchingconnect %1, %2
592  // }
593  if (auto refResolve = dyn_cast<RefResolveOp>(op))
594  if (refResolve.getResult().hasOneUse() &&
595  refResolve.getRef().getParentBlock() != body)
596  if (auto connect = dyn_cast<MatchingConnectOp>(
597  *refResolve.getResult().getUsers().begin()))
598  if (connect.getDest().getParentBlock() != body) {
599  refResolve->moveBefore(layerBlock);
600  return WalkResult::advance();
601  }
602 
603  // For any other ops, create input ports for any captured operands.
604  for (auto operand : op->getOperands()) {
605  if (!isAncestorOfValueOwner(layerBlock, operand))
606  createInputPort(operand, op->getLoc());
607  }
608 
609  return WalkResult::advance();
610  });
611 
612  if (layerBlockWalkResult.wasInterrupted())
613  return WalkResult::interrupt();
614 
615  // Create the new module. This grabs a lock to modify the circuit.
616  FModuleOp newModule = buildNewModule(builder, layerBlock, ports);
617  SymbolTable::setSymbolVisibility(newModule,
618  SymbolTable::Visibility::Private);
619  newModule.getBody().takeBody(layerBlock.getRegion());
620 
621  LLVM_DEBUG({
622  llvm::dbgs() << " New Module: " << moduleNames.lookup(layerBlock)
623  << "\n";
624  llvm::dbgs() << " ports:\n";
625  for (size_t i = 0, e = ports.size(); i != e; ++i) {
626  auto port = ports[i];
627  auto value = connectValues[i];
628  llvm::dbgs() << " - name: " << port.getName() << "\n"
629  << " type: " << port.type << "\n"
630  << " direction: " << port.direction << "\n"
631  << " value: " << value.value << "\n"
632  << " kind: "
633  << (value.kind == ConnectKind::NonRef ? "NonRef" : "Ref")
634  << "\n";
635  }
636  });
637 
638  // Replace the original layer block with an instance. Hook up the instance.
639  // Intentionally create instance with probe ports which do not have an
640  // associated layer. This is illegal IR that will be made legal by the end
641  // of the pass. This is done to avoid having to revisit and rewrite each
642  // instance everytime it is moved into a parent layer.
643  builder.setInsertionPointAfter(layerBlock);
644  auto instanceName = instanceNameForLayer(layerBlock.getLayerName());
645  auto instanceOp = builder.create<InstanceOp>(
646  layerBlock.getLoc(), /*moduleName=*/newModule,
647  /*name=*/
648  instanceName, NameKindEnum::DroppableName,
649  /*annotations=*/ArrayRef<Attribute>{},
650  /*portAnnotations=*/ArrayRef<Attribute>{}, /*lowerToBind=*/true,
651  /*innerSym=*/
652  (innerSyms.empty() ? hw::InnerSymAttr{}
653  : hw::InnerSymAttr::get(builder.getStringAttr(
654  ns.newName(instanceName)))));
655 
656  auto outputFile =
657  outputFileForLayer(circuitName, layerBlock.getLayerName());
658  instanceOp->setAttr("output_file", outputFile);
659 
660  createdInstances.try_emplace(instanceOp, newModule);
661 
662  LLVM_DEBUG(llvm::dbgs() << " moved inner refs:\n");
663  for (hw::InnerSymAttr innerSym : innerSyms) {
664  auto oldInnerRef = hw::InnerRefAttr::get(moduleOp.getModuleNameAttr(),
665  innerSym.getSymName());
666  auto splice = std::make_pair(instanceOp.getInnerSymAttr(),
667  newModule.getModuleNameAttr());
668  innerRefMap.insert({oldInnerRef, splice});
669  LLVM_DEBUG(llvm::dbgs() << " - ref: " << oldInnerRef << "\n"
670  << " splice: " << splice.first << ", "
671  << splice.second << "\n";);
672  }
673 
674  // Update RWProbe operations.
675  for (auto rwprobe : rwprobes) {
676  auto targetRef = rwprobe.getTarget();
677  auto mapped = innerRefMap.find(targetRef);
678  if (mapped == innerRefMap.end()) {
679  assert(targetRef.getModule() == moduleOp.getNameAttr());
680  auto ist = hw::InnerSymbolTable::get(moduleOp);
681  if (failed(ist))
682  return WalkResult::interrupt();
683  auto target = ist->lookup(targetRef.getName());
684  assert(target);
685  auto fieldref = getFieldRefForTarget(target);
686  rwprobe
687  .emitError(
688  "rwprobe capture not supported with bind convention layer")
689  .attachNote(fieldref.getLoc())
690  .append("rwprobe target outside of bind layer");
691  return WalkResult::interrupt();
692  }
693 
694  if (mapped->second.second != newModule.getModuleNameAttr())
695  return rwprobe.emitError("rwprobe target refers to different module"),
696  WalkResult::interrupt();
697 
698  rwprobe.setTargetAttr(
699  hw::InnerRefAttr::get(mapped->second.second, targetRef.getName()));
700  }
701 
702  // Connect instance ports to values.
703  assert(ports.size() == connectValues.size() &&
704  "the number of instance ports and values to connect to them must be "
705  "equal");
706  for (unsigned portNum = 0, e = newModule.getNumPorts(); portNum < e;
707  ++portNum) {
708  OpBuilder::InsertionGuard guard(builder);
709  builder.setInsertionPointAfterValue(instanceOp.getResult(portNum));
710  if (instanceOp.getPortDirection(portNum) == Direction::In) {
711  auto src = connectValues[portNum].value;
712  if (isa<RefType>(src.getType()))
713  src = builder.create<RefResolveOp>(
714  newModule.getPortLocationAttr(portNum), src);
715  builder.create<MatchingConnectOp>(
716  newModule.getPortLocationAttr(portNum),
717  instanceOp.getResult(portNum), src);
718  } else if (isa<RefType>(instanceOp.getResult(portNum).getType()) &&
719  connectValues[portNum].kind == ConnectKind::Ref)
720  builder.create<RefDefineOp>(getPortLoc(connectValues[portNum].value),
721  connectValues[portNum].value,
722  instanceOp.getResult(portNum));
723  else
724  builder.create<MatchingConnectOp>(
725  getPortLoc(connectValues[portNum].value),
726  connectValues[portNum].value,
727  builder.create<RefResolveOp>(newModule.getPortLocationAttr(portNum),
728  instanceOp.getResult(portNum)));
729  }
730  layerBlock.erase();
731 
732  return WalkResult::advance();
733  });
734  return success(!result.wasInterrupted());
735 }
736 
738  LayerOp layer, StringRef circuitName,
739  SmallVector<FlatSymbolRefAttr> &stack) {
740  stack.emplace_back(FlatSymbolRefAttr::get(layer.getSymNameAttr()));
741  ArrayRef stackRef(stack);
742  symbolToLayer.insert(
743  {SymbolRefAttr::get(stackRef.front().getAttr(), stackRef.drop_front()),
744  layer});
745  if (layer.getConvention() == LayerConvention::Inline) {
746  auto *ctx = &getContext();
747  auto macName = macroNameForLayer(circuitName, stack);
748  auto symName = ns.newName(macName);
749 
750  auto symNameAttr = StringAttr::get(ctx, symName);
751  auto macNameAttr = StringAttr();
752  if (macName != symName)
753  macNameAttr = StringAttr::get(ctx, macName);
754 
755  b.create<sv::MacroDeclOp>(layer->getLoc(), symNameAttr, ArrayAttr(),
756  macNameAttr);
757  macroNames[layer] = FlatSymbolRefAttr::get(&getContext(), symNameAttr);
758  }
759  for (auto child : layer.getOps<LayerOp>())
760  preprocessLayers(ns, b, child, circuitName, stack);
761  stack.pop_back();
762 }
763 
765  StringRef circuitName) {
766  OpBuilder b(layer);
767  SmallVector<FlatSymbolRefAttr> stack;
768  preprocessLayers(ns, b, layer, circuitName, stack);
769 }
770 
771 /// Process a circuit to remove all layer blocks in each module and top-level
772 /// layer definition.
774  LLVM_DEBUG(
775  llvm::dbgs() << "==----- Running LowerLayers "
776  "-------------------------------------------------===\n");
777  CircuitOp circuitOp = getOperation();
778  StringRef circuitName = circuitOp.getName();
779 
780  // Initialize members which cannot be initialized automatically.
781  llvm::sys::SmartMutex<true> mutex;
782  circuitMutex = &mutex;
783 
784  CircuitNamespace ns(circuitOp);
785  for (auto &op : *circuitOp.getBodyBlock()) {
786  // Determine names for all modules that will be created. Do this serially
787  // to avoid non-determinism from creating these in the parallel region.
788  if (auto moduleOp = dyn_cast<FModuleOp>(op)) {
789  moduleOp->walk([&](LayerBlockOp layerBlockOp) {
790  auto name = moduleNameForLayer(moduleOp.getModuleName(),
791  layerBlockOp.getLayerName());
792  moduleNames.insert({layerBlockOp, ns.newName(name)});
793  });
794  continue;
795  }
796  // Build verilog macro declarations for each inline layer declarations.
797  // Cache layer symbol refs for lookup later.
798  if (auto layerOp = dyn_cast<LayerOp>(op)) {
799  preprocessLayers(ns, layerOp, circuitName);
800  continue;
801  }
802  }
803 
804  auto mergeMaps = [](auto &&a, auto &&b) {
805  if (failed(a))
806  return std::forward<decltype(a)>(a);
807  if (failed(b))
808  return std::forward<decltype(b)>(b);
809 
810  for (auto bb : *b)
811  a->insert(bb);
812  return std::forward<decltype(a)>(a);
813  };
814 
815  // Lower the layer blocks of each module.
816  SmallVector<FModuleLike> modules(
817  circuitOp.getBodyBlock()->getOps<FModuleLike>());
818  auto failureOrInnerRefMap = transformReduce(
819  circuitOp.getContext(), modules, FailureOr<InnerRefMap>(InnerRefMap{}),
820  mergeMaps, [this](FModuleLike mod) -> FailureOr<InnerRefMap> {
821  return runOnModuleLike(mod);
822  });
823  if (failed(failureOrInnerRefMap))
824  return signalPassFailure();
825  auto &innerRefMap = *failureOrInnerRefMap;
826 
827  // Rewrite any hw::HierPathOps which have namepaths that contain rewritting
828  // inner refs.
829  //
830  // TODO: This unnecessarily computes a new namepath for every hw::HierPathOp
831  // even if that namepath is not used. It would be better to only build the
832  // new namepath when a change is needed, e.g., by recording updates to the
833  // namepath.
834  for (hw::HierPathOp hierPathOp : circuitOp.getOps<hw::HierPathOp>()) {
835  SmallVector<Attribute> newNamepath;
836  bool modified = false;
837  for (auto attr : hierPathOp.getNamepath()) {
838  hw::InnerRefAttr innerRef = dyn_cast<hw::InnerRefAttr>(attr);
839  if (!innerRef) {
840  newNamepath.push_back(attr);
841  continue;
842  }
843  auto it = innerRefMap.find(innerRef);
844  if (it == innerRefMap.end()) {
845  newNamepath.push_back(attr);
846  continue;
847  }
848 
849  auto &[inst, mod] = it->getSecond();
850  newNamepath.push_back(
851  hw::InnerRefAttr::get(innerRef.getModule(), inst.getSymName()));
852  newNamepath.push_back(hw::InnerRefAttr::get(mod, innerRef.getName()));
853  modified = true;
854  }
855  if (modified)
856  hierPathOp.setNamepathAttr(
857  ArrayAttr::get(circuitOp.getContext(), newNamepath));
858  }
859 
860  // Generate the header and footer of each bindings file. The body will be
861  // populated later when binds are exported to Verilog. This produces text
862  // like:
863  //
864  // `include "layers-A.sv"
865  // `include "layers-A-B.sv"
866  // `ifndef layers_A_B_C
867  // `define layers_A_B_C
868  // <body>
869  // `endif // layers_A_B_C
870  //
871  // TODO: This would be better handled without the use of verbatim ops.
872  OpBuilder builder(circuitOp);
873  SmallVector<std::pair<LayerOp, StringAttr>> layers;
874  circuitOp.walk<mlir::WalkOrder::PreOrder>([&](LayerOp layerOp) {
875  auto parentOp = layerOp->getParentOfType<LayerOp>();
876  while (!layers.empty() && parentOp != layers.back().first)
877  layers.pop_back();
878 
879  if (layerOp.getConvention() == LayerConvention::Inline) {
880  layers.emplace_back(layerOp, nullptr);
881  return;
882  }
883 
884  builder.setInsertionPointToStart(circuitOp.getBodyBlock());
885 
886  // Save the "layers-<circuit>-<group>" and "layers_<circuit>_<group>" string
887  // as this is reused a bunch.
888  SmallString<32> prefixFile("layers-"), prefixMacro("layers_");
889  prefixFile.append(circuitName);
890  prefixFile.append("-");
891  prefixMacro.append(circuitName);
892  prefixMacro.append("_");
893  for (auto [layer, _] : layers) {
894  prefixFile.append(layer.getSymName());
895  prefixFile.append("-");
896  prefixMacro.append(layer.getSymName());
897  prefixMacro.append("_");
898  }
899  prefixFile.append(layerOp.getSymName());
900  prefixMacro.append(layerOp.getSymName());
901 
902  SmallString<128> includes;
903  for (auto [_, strAttr] : layers) {
904  if (!strAttr)
905  continue;
906  includes.append(strAttr);
907  includes.append("\n");
908  }
909 
910  hw::OutputFileAttr bindFile;
911  if (auto outputFile =
912  layerOp->getAttrOfType<hw::OutputFileAttr>("output_file")) {
913  auto dir = outputFile.getDirectory();
914  bindFile = hw::OutputFileAttr::getFromDirectoryAndFilename(
915  &getContext(), dir, prefixFile + ".sv",
916  /*excludeFromFileList=*/true);
917  } else {
918  bindFile = hw::OutputFileAttr::getFromFilename(
919  &getContext(), prefixFile + ".sv", /*excludeFromFileList=*/true);
920  }
921 
922  // Write header to a verbatim.
923  auto header = builder.create<sv::VerbatimOp>(
924  layerOp.getLoc(),
925  includes + "`ifndef " + prefixMacro + "\n" + "`define " + prefixMacro);
926  header->setAttr("output_file", bindFile);
927 
928  // Write footer to a verbatim.
929  builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
930  auto footer = builder.create<sv::VerbatimOp>(layerOp.getLoc(),
931  "`endif // " + prefixMacro);
932  footer->setAttr("output_file", bindFile);
933 
934  if (!layerOp.getBody().getOps<LayerOp>().empty())
935  layers.emplace_back(
936  layerOp,
937  builder.getStringAttr("`include \"" +
938  bindFile.getFilename().getValue() + "\""));
939  });
940 
941  // All layers definitions can now be deleted.
942  for (auto layerOp :
943  llvm::make_early_inc_range(circuitOp.getBodyBlock()->getOps<LayerOp>()))
944  layerOp.erase();
945 }
946 
947 std::unique_ptr<mlir::Pass> circt::firrtl::createLowerLayersPass() {
948  return std::make_unique<LowerLayersPass>();
949 }
static OutputFileAttr getOutputFile(igraph::ModuleOpInterface op)
assert(baseType &&"element must be base type")
Delimiter
Definition: HWOps.cpp:116
static InstancePath empty
static SmallString< 32 > instanceNameForLayer(SymbolRefAttr layerName)
For a layerblock @A::@B::@C, the generated instance is called a_b_c.
static SmallString< 32 > macroNameForLayer(StringRef circuitName, ArrayRef< FlatSymbolRefAttr > layerName)
For a layerblock @A::@B::@C, the verilog macro is A_B_C.
static SmallString< 32 > fileNameForLayer(StringRef circuitName, SymbolRefAttr layerName)
For all layerblocks @A::@B::@C in a circuit called Circuit, the output filename is layers-Circuit-A-B...
static SmallString< 32 > moduleNameForLayer(StringRef moduleName, SymbolRefAttr layerName)
For a layer @A::@B::@C in module Module, the generated module is called Module_A_B_C.
Definition: LowerLayers.cpp:91
static void appendName(StringRef name, SmallString< 32 > &output, bool toLower=false, Delimiter delimiter=Delimiter::BindFile)
Definition: LowerLayers.cpp:67
DenseMap< hw::InnerRefAttr, std::pair< hw::InnerSymAttr, StringAttr > > InnerRefMap
Definition: LowerLayers.cpp:61
void runOnOperation() override
Entry point for the function.
void removeLayersFromPorts(FModuleLike moduleLike)
Update the module's port types to remove any explicit layer requirements from any probe types.
FailureOr< InnerRefMap > runOnModuleLike(FModuleLike moduleLike)
Strip layer colors from the module's interface.
DenseMap< LayerOp, FlatSymbolRefAttr > macroNames
A map from inline layers to their macro names.
void lowerInlineLayerBlock(LayerOp layer, LayerBlockOp layerBlock)
Lower an inline layerblock to an ifdef block.
DenseMap< LayerBlockOp, StringRef > moduleNames
A map of layer blocks to module name that should be created for it.
hw::OutputFileAttr outputFileForLayer(StringRef circuitName, SymbolRefAttr layerName)
void removeLayersFromRefCast(RefCastOp cast)
Remove any layers from the result of the cast.
LogicalResult runOnModuleBody(FModuleOp moduleOp, InnerRefMap &innerRefMap)
Extract layerblocks and strip probe colors from all ops under the module.
DenseMap< SymbolRefAttr, LayerOp > symbolToLayer
A mapping of symbol name to layer operation.
FModuleOp buildNewModule(OpBuilder &builder, LayerBlockOp layerBlock, SmallVectorImpl< PortInfo > &ports)
Safely build a new module with a given namehint.
hw::OutputFileAttr getOutputFile(SymbolRefAttr layerName)
void removeLayersFromValue(Value value)
Update the value's type to remove any layers from any probe types.
void preprocessLayers(CircuitNamespace &ns, OpBuilder &b, LayerOp layer, StringRef circuitName, SmallVector< FlatSymbolRefAttr > &stack)
Preprocess layers to build macro declarations and cache information about the layers so that this can...
llvm::sys::SmartMutex< true > * circuitMutex
Indicates exclusive access to modify the circuitNamespace and the circuit.
This class represents a reference to a specific field or element of an aggregate value.
Definition: FieldRef.h:28
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Definition: Namespace.h:85
static FailureOr< InnerSymbolTable > get(Operation *op)
Construct an InnerSymbolTable, checking for verification failure.
Definition: sv.py:15
def connect(destination, source)
Definition: support.py:39
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
FieldRef getFieldRefForTarget(const hw::InnerSymTarget &ist)
Get FieldRef pointing to the specified inner symbol target, which must be valid.
std::unique_ptr< mlir::Pass > createLowerLayersPass()
static ResultTy transformReduce(MLIRContext *context, IterTy begin, IterTy end, ResultTy init, ReduceFuncTy reduce, TransformFuncTy transform)
Wrapper for llvm::parallelTransformReduce that performs the transform_reduce serially when MLIR multi...
Definition: FIRRTLUtils.h:295
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
bool isAncestorOfValueOwner(Operation *op, Value value)
Return true if a Value is created "underneath" an operation.
Definition: Utils.h:25
The namespace of a CircuitOp, generally inhabited by modules.
Definition: Namespace.h:24