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