CIRCT 23.0.0git
Loading...
Searching...
No Matches
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
21#include "circt/Support/Utils.h"
22#include "mlir/Pass/Pass.h"
23#include "llvm/ADT/PostOrderIterator.h"
24#include "llvm/ADT/SmallPtrSet.h"
25#include "llvm/Support/Debug.h"
26#include "llvm/Support/Mutex.h"
27
28#define DEBUG_TYPE "firrtl-lower-layers"
29
30namespace circt {
31namespace firrtl {
32#define GEN_PASS_DEF_LOWERLAYERS
33#include "circt/Dialect/FIRRTL/Passes.h.inc"
34} // namespace firrtl
35} // namespace circt
36
37using namespace circt;
38using namespace firrtl;
39
40namespace {
41
42/// Indicates the kind of reference that was captured.
43enum class ConnectKind {
44 /// A normal captured value. This is a read of a value outside the
45 /// layerblock.
46 NonRef,
47 /// A reference. This is a destination of a ref define.
48 Ref
49};
50
51struct ConnectInfo {
52 Value value;
53 ConnectKind kind;
54};
55
56/// The delimiters that should be used for a given generated name. These vary
57/// for modules and files, as well as by convention.
58enum class Delimiter { BindModule = '_', BindFile = '-', InlineMacro = '$' };
59
60/// This struct contains pre-allocated "safe" names that parallel regions can
61/// use to create names in the global namespace. This is allocated per-layer
62/// block.
63struct LayerBlockGlobals {
64 /// If the layer needs to create a module, use this name.
65 StringRef moduleName;
66
67 /// If the layer needs to create a hw::HierPathOp, use this name.
68 StringRef hierPathName;
69};
70
71} // namespace
72
73// A mapping of an old InnerRefAttr to the new inner symbol and module name that
74// need to be spliced into the old InnerRefAttr. This is used to fix
75// hierarchical path operations after layers are converted to modules.
77 DenseMap<hw::InnerRefAttr, std::pair<hw::InnerSymAttr, StringAttr>>;
78
79//===----------------------------------------------------------------------===//
80// Naming Helpers
81//===----------------------------------------------------------------------===//
82
83static void appendName(StringRef name, SmallString<32> &output,
84 bool toLower = false,
85 Delimiter delimiter = Delimiter::BindFile) {
86 if (name.empty())
87 return;
88 if (!output.empty())
89 output.push_back(static_cast<char>(delimiter));
90 output.append(name);
91 if (!toLower)
92 return;
93 auto i = output.size() - name.size();
94 output[i] = llvm::toLower(output[i]);
95}
96
97static void appendName(const ArrayRef<FlatSymbolRefAttr> &names,
98 SmallString<32> &output, bool toLower = false,
99 Delimiter delimiter = Delimiter::BindFile) {
100 for (auto name : names)
101 appendName(name.getValue(), output, toLower, delimiter);
102}
103
104static void appendName(SymbolRefAttr name, SmallString<32> &output,
105 bool toLower = false,
106 Delimiter delimiter = Delimiter::BindFile) {
107 appendName(name.getRootReference(), output, toLower, delimiter);
108 appendName(name.getNestedReferences(), output, toLower, delimiter);
109}
110
111/// For a layer `@A::@B::@C` in module Module,
112/// the generated module is called `Module_A_B_C`.
113static SmallString<32> moduleNameForLayer(StringRef moduleName,
114 SymbolRefAttr layerName) {
115 SmallString<32> result;
116 appendName(moduleName, result, /*toLower=*/false,
117 /*delimiter=*/Delimiter::BindModule);
118 appendName(layerName, result, /*toLower=*/false,
119 /*delimiter=*/Delimiter::BindModule);
120 return result;
121}
122
123static SmallString<32> hierPathNameForLayer(StringRef moduleName,
124 SymbolRefAttr layerName) {
125 SmallString<32> result("__lowerLayers_path");
126 appendName(moduleName, result, /*toLower=*/false,
127 /*delimiter=*/Delimiter::BindModule);
128 appendName(layerName, result, /*toLower=*/false,
129 /*delimiter=*/Delimiter::BindModule);
130 return result;
131}
132
133/// For a layerblock `@A::@B::@C`,
134/// the generated instance is called `a_b_c`.
135static SmallString<32> instanceNameForLayer(SymbolRefAttr layerName) {
136 SmallString<32> result;
137 appendName(layerName, result, /*toLower=*/true,
138 /*delimiter=*/Delimiter::BindModule);
139 return result;
140}
141
142static SmallString<32> fileNameForLayer(StringRef moduleName, StringAttr root,
143 ArrayRef<FlatSymbolRefAttr> nested) {
144 SmallString<32> result;
145 result.append("layers");
146 appendName(moduleName, result);
147 appendName(root, result);
148 appendName(nested, result);
149 result.append(".sv");
150 return result;
151}
152
153/// For all layerblocks `@A::@B::@C` in a module called Module,
154/// the output filename is `layers-Module-A-B-C.sv`.
155static SmallString<32> fileNameForLayer(StringRef moduleName,
156 SymbolRefAttr layerName) {
157 return fileNameForLayer(moduleName, layerName.getRootReference(),
158 layerName.getNestedReferences());
159}
160
161/// For all layerblocks `@A::@B::@C` in a module called Module,
162/// the include-guard macro is `layers_Module_A_B_C`.
163static SmallString<32> guardMacroNameForLayer(StringRef moduleName,
164 SymbolRefAttr layerName) {
165 SmallString<32> result;
166 result.append("layers");
167 appendName(moduleName, result, false, Delimiter::BindModule);
168 appendName(layerName, result, false, Delimiter::BindModule);
169 return result;
170}
171
172/// For a layerblock `@A::@B::@C`, the verilog macro is `A_B_C`.
173static SmallString<32>
174macroNameForLayer(StringRef circuitName,
175 ArrayRef<FlatSymbolRefAttr> layerName) {
176 SmallString<32> result("layer");
177 for (auto part : layerName)
178 appendName(part, result, /*toLower=*/false,
179 /*delimiter=*/Delimiter::InlineMacro);
180 return result;
181}
182
183//===----------------------------------------------------------------------===//
184// LowerLayersPass
185//===----------------------------------------------------------------------===//
186
187namespace {
188/// Information about each bind file we are emitting. During a prepass, we walk
189/// the modules to find layerblocks, creating an emit::FileOp for each bound
190/// layer used under each module. As we do this, we build a table of these info
191/// objects for quick lookup later.
192struct BindFileInfo {
193 /// The filename of the bind file _without_ a directory.
194 StringAttr filename;
195 /// Where to insert bind statements into the bind file.
196 Block *body;
197 /// True if including the bindfile has an effect on the design.
198 bool effectful;
199};
200} // namespace
201
203 : public circt::firrtl::impl::LowerLayersBase<LowerLayersPass> {
204 using Base::Base;
205
206 hw::OutputFileAttr getOutputFile(SymbolRefAttr layerName) {
207 auto layer = symbolToLayer.lookup(layerName);
208 if (!layer)
209 return nullptr;
210 return layer->getAttrOfType<hw::OutputFileAttr>("output_file");
211 }
212
213 hw::OutputFileAttr outputFileForLayer(StringRef moduleName,
214 SymbolRefAttr layerName) {
215 if (auto file = getOutputFile(layerName))
216 return hw::OutputFileAttr::getFromDirectoryAndFilename(
217 &getContext(), file.getDirectory(),
218 fileNameForLayer(moduleName, layerName),
219 /*excludeFromFileList=*/true);
220 return hw::OutputFileAttr::getFromFilename(
221 &getContext(), fileNameForLayer(moduleName, layerName),
222 /*excludeFromFileList=*/true);
223 }
224
225 /// Safely build a new module with a given namehint. This handles geting a
226 /// lock to modify the top-level circuit.
227 FModuleOp buildNewModule(OpBuilder &builder, LayerBlockOp layerBlock,
228 ArrayRef<PortInfo> ports);
229
230 /// Strip layer colors from the module's interface.
231 FailureOr<InnerRefMap> runOnModuleLike(FModuleLike moduleLike);
232
233 /// Extract layerblocks and strip probe colors from all ops under the module.
234 LogicalResult runOnModuleBody(FModuleOp moduleOp, InnerRefMap &innerRefMap);
235
236 /// Update the module's port types to remove any explicit layer requirements
237 /// from any probe types.
238 void removeLayersFromPorts(FModuleLike moduleLike);
239
240 /// Update the value's type to remove any layers from any probe types.
241 void removeLayersFromValue(Value value);
242
243 /// Lower an inline layerblock to an ifdef block.
244 void lowerInlineLayerBlock(LayerOp layer, LayerBlockOp layerBlock);
245
246 /// Build macro declarations and cache information about the layers.
247 void preprocessLayers(CircuitNamespace &ns, OpBuilder &b, LayerOp layer,
248 StringRef circuitName,
249 SmallVector<FlatSymbolRefAttr> &stack);
251
252 /// For each module, build a bindfile for each bound-layer, if needed.
254
255 /// Build the bindfile skeletons for each module. Set up a table which tells
256 /// us for each module/layer pair, where to insert the bind operations.
258
259 /// Build the bindfile skeleton for a module.
261 FModuleOp module);
262
263 /// Record the supposed bindfiles for any known layers of the ext module.
265 FExtModuleOp extModule);
266
267 /// Build a bindfile skeleton for a particular module and layer.
269 OpBuilder &b, SymbolRefAttr layerName, LayerOp layer,
270 bool effectful);
271
272 /// Entry point for the function.
273 void runOnOperation() override;
274
275 /// Indicates exclusive access to modify the circuitNamespace and the circuit.
276 llvm::sys::SmartMutex<true> *circuitMutex;
277
278 /// A map of layer blocks to "safe" global names which are fine to create in
279 /// the circuit namespace.
280 DenseMap<LayerBlockOp, LayerBlockGlobals> layerBlockGlobals;
281
282 /// A map from inline layers to their macro names.
283 DenseMap<LayerOp, FlatSymbolRefAttr> macroNames;
284
285 /// A mapping of symbol name to layer operation. This also serves as an
286 /// iterable list of all layers declared in a circuit. We use a map vector so
287 /// that the iteration order matches the order of declaration in the circuit.
288 /// This order is not required for correctness, it helps with legibility.
290
291 /// Utility for creating hw::HierPathOp.
293
294 /// A mapping from module*layer to bindfile name.
295 DenseMap<Operation *, DenseMap<LayerOp, BindFileInfo>> bindFiles;
296};
297
298/// Multi-process safe function to build a module in the circuit and return it.
299/// The name provided is only a namehint for the module---a unique name will be
300/// generated if there are conflicts with the namehint in the circuit-level
301/// namespace.
302FModuleOp LowerLayersPass::buildNewModule(OpBuilder &builder,
303 LayerBlockOp layerBlock,
304 ArrayRef<PortInfo> ports) {
305 auto location = layerBlock.getLoc();
306 auto namehint = layerBlockGlobals.lookup(layerBlock).moduleName;
307 llvm::sys::SmartScopedLock<true> instrumentationLock(*circuitMutex);
308 FModuleOp newModule = FModuleOp::create(
309 builder, location, builder.getStringAttr(namehint),
310 ConventionAttr::get(builder.getContext(), Convention::Internal), ports,
311 ArrayAttr{});
312 if (auto dir = getOutputFile(layerBlock.getLayerNameAttr())) {
313 assert(dir.isDirectory());
314 newModule->setAttr("output_file", dir);
315 }
316 SymbolTable::setSymbolVisibility(newModule, SymbolTable::Visibility::Private);
317 return newModule;
318}
319
321 auto type = dyn_cast<RefType>(value.getType());
322 if (!type || !type.getLayer())
323 return;
324 value.setType(type.removeLayer());
325}
326
327void LowerLayersPass::removeLayersFromPorts(FModuleLike moduleLike) {
328 auto oldTypeAttrs = moduleLike.getPortTypesAttr();
329 SmallVector<Attribute> newTypeAttrs;
330 newTypeAttrs.reserve(oldTypeAttrs.size());
331 bool changed = false;
332
333 for (auto typeAttr : oldTypeAttrs.getAsRange<TypeAttr>()) {
334 if (auto refType = dyn_cast<RefType>(typeAttr.getValue())) {
335 if (refType.getLayer()) {
336 typeAttr = TypeAttr::get(refType.removeLayer());
337 changed = true;
338 }
339 }
340 newTypeAttrs.push_back(typeAttr);
341 }
342
343 if (!changed)
344 return;
345
346 moduleLike.setPortTypesAttr(
347 ArrayAttr::get(moduleLike.getContext(), newTypeAttrs));
348
349 if (auto moduleOp = dyn_cast<FModuleOp>(moduleLike.getOperation())) {
350 for (auto arg : moduleOp.getBodyBlock()->getArguments())
352 }
353}
354
355FailureOr<InnerRefMap>
356LowerLayersPass::runOnModuleLike(FModuleLike moduleLike) {
357 LLVM_DEBUG({
358 llvm::dbgs() << "Module: " << moduleLike.getModuleName() << "\n";
359 llvm::dbgs() << " Examining Layer Blocks:\n";
360 });
361
362 // Strip away layers from the interface of the module-like op.
363 InnerRefMap innerRefMap;
364 auto result =
365 TypeSwitch<Operation *, LogicalResult>(moduleLike.getOperation())
366 .Case<FModuleOp>([&](auto op) {
367 op.setLayers({});
369 return runOnModuleBody(op, innerRefMap);
370 })
371 .Case<FExtModuleOp>([&](auto op) {
372 op.setKnownLayers({});
373 op.setLayers({});
375 return success();
376 })
377 .Case<FIntModuleOp, FMemModuleOp>([&](auto op) {
378 op.setLayers({});
380 return success();
381 })
382 .Case<ClassOp, ExtClassOp>([](auto) { return success(); })
383 .Default(
384 [](auto *op) { return op->emitError("unknown module-like op"); });
385
386 if (failed(result))
387 return failure();
388
389 return innerRefMap;
390}
391
393 LayerBlockOp layerBlock) {
394 if (!layerBlock.getBody()->empty()) {
395 OpBuilder builder(layerBlock);
396 auto macroName = macroNames[layer];
397 auto ifDef = sv::IfDefOp::create(builder, layerBlock.getLoc(), macroName);
398 ifDef.getBodyRegion().takeBody(layerBlock.getBodyRegion());
399 }
400 layerBlock.erase();
401}
402
403LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
404 InnerRefMap &innerRefMap) {
405 hw::InnerSymbolNamespace ns(moduleOp);
406
407 // Get or create a node op for a value captured by a layer block.
408 auto getOrCreateNodeOp = [&](Value operand,
409 ImplicitLocOpBuilder &builder) -> NodeOp {
410 // Create a new node. Put it in the cache and use it.
411 OpBuilder::InsertionGuard guard(builder);
412 builder.setInsertionPointAfterValue(operand);
413 SmallString<16> nameHint;
414 // Try to generate a "good" name hint to use for the node.
415 if (auto *definingOp = operand.getDefiningOp()) {
416 if (auto instanceOp = dyn_cast<InstanceOp>(definingOp)) {
417 nameHint.append(instanceOp.getName());
418 nameHint.push_back('_');
419 nameHint.append(
420 instanceOp.getPortName(cast<OpResult>(operand).getResultNumber()));
421 } else if (auto opName = definingOp->getAttrOfType<StringAttr>("name")) {
422 nameHint.append(opName);
423 }
424 nameHint.append("_layerCapture");
425 }
426
427 return NodeOp::create(builder, operand.getLoc(), operand,
428 StringRef(nameHint));
429 };
430
431 // Determine the replacement for an operand within the current region. Keep a
432 // densemap of replacements around to avoid creating the same hardware
433 // multiple times.
434 DenseMap<Value, Value> replacements;
435 std::function<Value(Operation *, Value)> getReplacement =
436 [&](Operation *user, Value value) -> Value {
437 auto it = replacements.find(value);
438 if (it != replacements.end())
439 return it->getSecond();
440
441 ImplicitLocOpBuilder localBuilder(value.getLoc(), &getContext());
442 Value replacement;
443
444 auto layerBlockOp = user->getParentOfType<LayerBlockOp>();
445 localBuilder.setInsertionPointToStart(layerBlockOp.getBody());
446
447 // If the operand is "special", e.g., it has no XMR representation, then we
448 // need to clone it.
449 //
450 // TODO: Change this to recursively clone. This will matter once FString
451 // operations have operands.
452 if (type_isa<FStringType>(value.getType())) {
453 localBuilder.setInsertionPoint(user);
454 replacement = localBuilder.clone(*value.getDefiningOp())->getResult(0);
455 replacements.insert({value, replacement});
456 return replacement;
457 }
458
459 // If the operand is an XMR ref, then we _have_ to clone it.
460 auto *definingOp = value.getDefiningOp();
461 if (isa_and_present<XMRRefOp>(definingOp)) {
462 replacement = localBuilder.clone(*definingOp)->getResult(0);
463 replacements.insert({value, replacement});
464 return replacement;
465 }
466
467 // If the value is an instance input port, recurse on its driver instead.
468 // Instance input ports have special flow semantics (sink flow but can be
469 // read from). By recursing on the driver, we avoid creating a node for the
470 // instance port itself and instead directly XMR reference the driver,
471 // creating an intermediary node to dereference if the driver cannot support
472 // an inner symbol. This avoids creating intermediary nodes unless
473 // absolutely required while also avoiding dead code.
474 if (isa_and_present<InstanceOp, InstanceChoiceOp>(definingOp)) {
475 bool isInstanceInputPort =
476 TypeSwitch<Operation *, bool>(definingOp)
477 .Case<InstanceOp, InstanceChoiceOp>([&](auto instOp) {
478 for (auto [idx, result] : llvm::enumerate(instOp.getResults()))
479 if (result == value)
480 return instOp.getPortDirection(idx) == Direction::In;
481 return false;
482 })
483 .Default(false);
484
485 if (isInstanceInputPort) {
486 if (auto driver = getDriverFromConnect(value)) {
487 // Recurse on the driver to get its replacement. The connect stays
488 // as-is (driver -> instance port) in the original module.
489 replacement = getReplacement(user, driver);
490 replacements.insert({value, replacement});
491 return replacement;
492 }
493 }
494 }
495
496 // Determine the replacement value for the captured operand. There are
497 // three cases that can occur:
498 //
499 // 1. Capturing something zero-width. Create a zero-width constant zero.
500 // 2. Capture something that can handle an inner sym. Add the inner sym if
501 // it doesn't exist and XMRderef that.
502 // 3. Capture something that can't handle an inner sym. Add a node and XMR
503 // deref the node.
504 //
505 // The handling of (2) and (3) is diffuse in the code below due to needing
506 // to split things based on whether a value has a defining operation or not.
507 auto baseType = type_cast<FIRRTLBaseType>(value.getType());
508 if (baseType && baseType.getBitWidthOrSentinel() == 0) {
509 OpBuilder::InsertionGuard guard(localBuilder);
510 auto zeroUIntType = UIntType::get(localBuilder.getContext(), 0);
511 replacement = localBuilder.createOrFold<BitCastOp>(
512 value.getType(), ConstantOp::create(localBuilder, zeroUIntType,
513 getIntZerosAttr(zeroUIntType)));
514 } else {
515 hw::InnerRefAttr innerRef;
516 if (auto *definingOp = value.getDefiningOp()) {
517 // Check if the operation can support an inner symbol and targets a
518 // specific result.
519 auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(definingOp);
520 if (innerSymOp && innerSymOp.getTargetResultIndex()) {
521 // The operation can support an inner symbol, so add one directly.
522 innerRef = getInnerRefTo(
523 innerSymOp,
524 [&](auto) -> hw::InnerSymbolNamespace & { return ns; });
525 } else {
526 // The operation cannot support an inner symbol, or it has multiple
527 // results and doesn't target a specific result, so create a node
528 // and XMR deref the node.
529 auto node = getOrCreateNodeOp(value, localBuilder);
530 innerRef = getInnerRefTo(
531 node, [&](auto) -> hw::InnerSymbolNamespace & { return ns; });
532 auto newValue = node.getResult();
533 value.replaceAllUsesExcept(newValue, node);
534 value = newValue;
535 }
536 } else {
537 auto portIdx = cast<BlockArgument>(value).getArgNumber();
538 innerRef = getInnerRefTo(
539 cast<FModuleLike>(*moduleOp), portIdx,
540 [&](auto) -> hw::InnerSymbolNamespace & { return ns; });
541 }
542
543 hw::HierPathOp hierPathOp;
544 {
545 // TODO: Move to before parallel region to avoid the lock.
546 auto insertPoint = OpBuilder::InsertPoint(moduleOp->getBlock(),
547 Block::iterator(moduleOp));
548 llvm::sys::SmartScopedLock<true> circuitLock(*circuitMutex);
549 hierPathOp = hierPathCache->getOrCreatePath(
550 localBuilder.getArrayAttr({innerRef}), localBuilder.getLoc(),
551 insertPoint, layerBlockGlobals.lookup(layerBlockOp).hierPathName);
552 hierPathOp.setVisibility(SymbolTable::Visibility::Private);
553 }
554
555 replacement = XMRDerefOp::create(localBuilder, value.getType(),
556 hierPathOp.getSymNameAttr());
557 }
558
559 replacements.insert({value, replacement});
560
561 return replacement;
562 };
563
564 // A map of instance ops to modules that this pass creates. This is used to
565 // check if this was an instance that we created and to do fast module
566 // dereferencing (avoiding a symbol table).
567 DenseMap<Operation *, FModuleOp> createdInstances;
568
569 // Check that the preconditions for this pass are met. Reject any ops which
570 // must have been removed before this runs.
571 auto opPreconditionCheck = [](Operation *op) -> LogicalResult {
572 // LowerXMR op removal postconditions.
573 if (isa<RefCastOp, RefDefineOp, RefResolveOp, RefSendOp, RefSubOp,
574 RWProbeOp>(op))
575 return op->emitOpError()
576 << "cannot be handled by the lower-layers pass. This should have "
577 "already been removed by the lower-xmr pass.";
578
579 return success();
580 };
581
582 // Utility to determine the domain type of some value. This looks backwards
583 // through connections to find the source driver in the module and gets the
584 // domain type of that. This is necessary as intermediary wires do not track
585 // domain information.
586 //
587 // This cannot use `getModuleScopedDriver` because this can be called while
588 // `LayerBlockOp`s have temporarily gained block arguments while they are
589 // being migrated to modules. This is worked around by caching the known
590 // domain kinds of earlier-visited `WireOp`s to avoid needing to look through
591 // these non-`ModuleOp` block arguments.
592 //
593 // TODO: Simplify this once wires have domain kind information [1].
594 //
595 // [1]: https://github.com/llvm/circt/issues/9398
596 DenseMap<Operation *, Attribute> domainMap;
597 auto getDomain = [&domainMap](Value value,
598 Attribute &domain) -> LogicalResult {
599 SmallVector<Operation *> wires;
600
601 // Use iteration as this is recursive over the IR. `value` is changed for
602 // each iteration.
603 while (!domain) {
604 if (auto arg = dyn_cast<BlockArgument>(value)) {
605 domain = cast<FModuleLike>(arg.getOwner()->getParentOp())
606 .getDomainInfoAttrForPort(arg.getArgNumber());
607 continue;
608 }
609
610 auto result =
611 TypeSwitch<Operation *, LogicalResult>(value.getDefiningOp())
612 .Case<WireOp>([&](WireOp op) {
613 auto it = domainMap.find(op);
614 if (it != domainMap.end()) {
615 domain = it->getSecond();
616 return success();
617 }
618 for (auto *user : op->getUsers()) {
619 auto connect = dyn_cast<FConnectLike>(user);
620 if (!connect || connect.getDest() != value)
621 continue;
622 value = connect.getSrc();
623 wires.push_back(op);
624 return success();
625 }
626 emitError(value.getLoc())
627 << "unable to determine domain kind for source likely "
628 "indicating a "
629 "violation of static-single-connect";
630 return failure();
631 })
632 .Case<InstanceOp>([&](auto op) {
633 domain =
634 op.getPortDomain(cast<OpResult>(value).getResultNumber());
635 return success();
636 })
637 .Case<DomainCreateAnonOp>([&](auto op) {
638 domain = op.getDomainAttr();
639 return success();
640 })
641 .Default([&](auto op) {
642 op->emitOpError() << "unhandled domain source in 'LowerLayers";
643 return failure();
644 });
645 if (failed(result))
646 return failure();
647 }
648
649 // Update the `domainMap` with wire/domain information.
650 for (auto *wire : wires)
651 domainMap[wire] = domain;
652
653 return success();
654 };
655
656 // Post-order traversal that expands a layer block into its parent. Because of
657 // the pass precondition that this runs _after_ `LowerXMR`, not much has to
658 // happen here, other than for domain information. All of the following do
659 // happen, though:
660 //
661 // 1. Any layer coloring is stripped.
662 // 2. Layers with Inline convention are converted to SV ifdefs.
663 // 3. Layers with Bind convention are converted to new modules and then
664 // instantiated at their original location. Any captured values are either
665 // moved, cloned, or converted to XMR deref ops.
666 // 4. Move instances created from earlier (3) conversions out of later (3)
667 // conversions. This is necessary to avoid a SystemVerilog-illegal
668 // bind-under-bind. (See Section 23.11 of 1800-2023.)
669 // 5. Keep track of special ops (ops with inner symbols or verbatims) which
670 // need to have something updated because of the new instance hierarchy
671 // being created.
672 // 6. Any captured domain information result in input/output ports being
673 // created and these being hooked up when new modules are instantiated.
674 //
675 // Remember, this is post-order, in-order. Child layer blocks are visited
676 // before parents. Any nested regions _within_ the layer block are also
677 // visited before the outer layer block.
678 auto result = moduleOp.walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
679 if (failed(opPreconditionCheck(op)))
680 return WalkResult::interrupt();
681
682 // Strip layer requirements from any op that might represent a probe.
683 for (auto result : op->getResults())
684 removeLayersFromValue(result);
685
686 // If the op is an instance, clear the enablelayers attribute.
687 if (auto instance = dyn_cast<InstanceOp>(op))
688 instance.setLayers({});
689
690 auto layerBlock = dyn_cast<LayerBlockOp>(op);
691 if (!layerBlock)
692 return WalkResult::advance();
693
694 // After this point, we are dealing with a layer block.
695 auto layer = symbolToLayer.lookup(layerBlock.getLayerName());
696
697 if (layer.getConvention() == LayerConvention::Inline) {
698 lowerInlineLayerBlock(layer, layerBlock);
699 return WalkResult::advance();
700 }
701
702 // After this point, we are dealing with a bind convention layer block.
703 assert(layer.getConvention() == LayerConvention::Bind);
704
705 // Utilities and mutable state that results from creating ports. Due to the
706 // way in which this pass works and its phase ordering, the only types of
707 // ports that can be created are domain type ports.
708 SmallVector<PortInfo> ports;
709 SmallVector<Value> connectValues;
710 Namespace portNs;
711
712 // Create an input port for a domain-type operand. The source is not in the
713 // current layer block.
714 auto createInputPort = [&](Value src, Location loc) -> LogicalResult {
715 Attribute domain;
716 if (failed(getDomain(src, domain)))
717 return failure();
718
719 StringAttr name;
720 auto [nameHint, rootKnown] = getFieldName(FieldRef(src, 0), true);
721 if (rootKnown)
722 name = StringAttr::get(src.getContext(), portNs.newName(nameHint));
723 else
724 name = StringAttr::get(src.getContext(), portNs.newName("anonDomain"));
725 // Domain type ports have no associations (domain info is in the type).
726 auto domainInfo = ArrayAttr::get(src.getContext(), {});
727 PortInfo port(
728 /*name=*/name,
729 /*type=*/src.getType(),
730 /*dir=*/Direction::In,
731 /*symName=*/{},
732 /*location=*/loc,
733 /*annos=*/{},
734 /*domains=*/domainInfo);
735 ports.push_back(port);
736 connectValues.push_back(src);
737 BlockArgument replacement =
738 layerBlock.getBody()->addArgument(port.type, port.loc);
739 src.replaceUsesWithIf(replacement, [&](OpOperand &use) {
740 auto *user = use.getOwner();
741 if (!layerBlock->isAncestor(user))
742 return false;
743 // Replace if the connection source is the src and if the destination is
744 // _not_ in this layer block. If the destination is a spilled or
745 // to-be-spilled instance, then do not replace this connection as it
746 // will _later_ be spilled.
747 if (auto connectLike = dyn_cast<FConnectLike>(user)) {
748 auto *destDefiningOp = connectLike.getDest().getDefiningOp();
749 return connectLike.getSrc() == src &&
750 !createdInstances.contains(destDefiningOp);
751 }
752 return false;
753 });
754 return success();
755 };
756
757 // Set the location intelligently. Use the location of the capture if this
758 // is a port created for forwarding from a parent layer block to a nested
759 // layer block. Otherwise, use unknown.
760 auto getPortLoc = [&](Value port) -> Location {
761 Location loc = UnknownLoc::get(port.getContext());
762 if (auto *destOp = port.getDefiningOp())
763 if (auto instOp = dyn_cast<InstanceOp>(destOp)) {
764 auto modOpIt = createdInstances.find(instOp);
765 if (modOpIt != createdInstances.end()) {
766 auto portNum = cast<OpResult>(port).getResultNumber();
767 loc = modOpIt->getSecond().getPortLocation(portNum);
768 }
769 }
770 return loc;
771 };
772
773 // Source is in the current layer block. The destination is not in the
774 // current layer block.
775 auto createOutputPort = [&](Value src, Value dest) -> LogicalResult {
776 Attribute domain;
777 if (failed(getDomain(src, domain)))
778 return failure();
779
780 StringAttr name;
781 auto [nameHint, rootKnown] = getFieldName(FieldRef(src, 0), true);
782 if (rootKnown)
783 name = StringAttr::get(src.getContext(), portNs.newName(nameHint));
784 else
785 name = StringAttr::get(src.getContext(), portNs.newName("anonDomain"));
786 // Domain type ports have no associations (domain info is in the type).
787 auto domainInfo = ArrayAttr::get(src.getContext(), {});
788 PortInfo port(
789 /*name=*/name,
790 /*type=*/src.getType(),
791 /*dir=*/Direction::Out,
792 /*symName=*/{},
793 /*location=*/getPortLoc(dest),
794 /*annos=*/{},
795 /*domains=*/domainInfo);
796 ports.push_back(port);
797 connectValues.push_back(dest);
798 BlockArgument replacement =
799 layerBlock.getBody()->addArgument(port.type, port.loc);
800 dest.replaceUsesWithIf(replacement, [&](OpOperand &use) {
801 auto *user = use.getOwner();
802 if (!layerBlock->isAncestor(user))
803 return false;
804 // Replace connection destinations.
805 if (auto connectLike = dyn_cast<FConnectLike>(user))
806 return connectLike.getDest() == dest;
807 return false;
808 });
809 return success();
810 };
811
812 // Clear the replacements so that none are re-used across layer blocks.
813 replacements.clear();
814 OpBuilder builder(moduleOp);
815 SmallVector<hw::InnerSymAttr> innerSyms;
816 SmallVector<sv::VerbatimOp> verbatims;
817 DenseSet<Operation *> spilledSubOps;
818 auto layerBlockWalkResult = layerBlock.walk([&](Operation *op) {
819 // Error if pass preconditions are not met.
820 if (failed(opPreconditionCheck(op)))
821 return WalkResult::interrupt();
822
823 // Specialized handling of subfields, subindexes, and subaccesses which
824 // need to be spilled and nodes that referred to spilled nodes. If these
825 // are kept in the module, then the XMR is going to be bidirectional. Fix
826 // this for subfield and subindex by moving these ops outside the
827 // layerblock. Try to fix this for subaccess and error if the move can't
828 // be made because the index is defined inside the layerblock. (This case
829 // is exceedingly rare given that subaccesses are almost always unexepcted
830 // when this pass runs.) Additionally, if any nodes are seen that are
831 // transparently referencing a spilled op, spill the node, too. The node
832 // provides an anchor for an inner symbol (which subfield, subindex, and
833 // subaccess do not).
834 auto fixSubOp = [&](auto subOp) {
835 auto input = subOp.getInput();
836
837 // If the input is defined in this layerblock, we are done.
838 if (isAncestorOfValueOwner(layerBlock, input))
839 return WalkResult::advance();
840
841 // Otherwise, capture the input operand, if possible.
842 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
843 subOp.getInputMutable().assign(getReplacement(subOp, input));
844 return WalkResult::advance();
845 }
846
847 // Otherwise, move the subfield op out of the layerblock.
848 op->moveBefore(layerBlock);
849 spilledSubOps.insert(op);
850 return WalkResult::advance();
851 };
852
853 if (auto subOp = dyn_cast<SubfieldOp>(op))
854 return fixSubOp(subOp);
855
856 if (auto subOp = dyn_cast<SubindexOp>(op))
857 return fixSubOp(subOp);
858
859 if (auto subOp = dyn_cast<SubaccessOp>(op)) {
860 auto input = subOp.getInput();
861 auto index = subOp.getIndex();
862
863 // If the input is defined in this layerblock, capture the index if
864 // needed, and we are done.
865 if (isAncestorOfValueOwner(layerBlock, input)) {
866 if (!isAncestorOfValueOwner(layerBlock, index)) {
867 subOp.getIndexMutable().assign(getReplacement(subOp, index));
868 }
869 return WalkResult::advance();
870 }
871
872 // Otherwise, capture the input operand, if possible.
873 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
874 subOp.getInputMutable().assign(getReplacement(subOp, input));
875 if (!isAncestorOfValueOwner(layerBlock, index))
876 subOp.getIndexMutable().assign(getReplacement(subOp, index));
877 return WalkResult::advance();
878 }
879
880 // Otherwise, move the subaccess op out of the layerblock, if possible.
881 if (!isAncestorOfValueOwner(layerBlock, index)) {
882 subOp->moveBefore(layerBlock);
883 spilledSubOps.insert(op);
884 return WalkResult::advance();
885 }
886
887 // When the input is not passive, but the index is defined inside this
888 // layerblock, we are out of options.
889 auto diag = op->emitOpError()
890 << "has a non-passive operand and captures a value defined "
891 "outside its enclosing bind-convention layerblock. The "
892 "'LowerLayers' pass cannot lower this as it would "
893 "create an output port on the resulting module.";
894 diag.attachNote(layerBlock.getLoc())
895 << "the layerblock is defined here";
896 return WalkResult::interrupt();
897 }
898
899 if (auto nodeOp = dyn_cast<NodeOp>(op)) {
900 auto *definingOp = nodeOp.getInput().getDefiningOp();
901 if (definingOp &&
902 spilledSubOps.contains(nodeOp.getInput().getDefiningOp())) {
903 op->moveBefore(layerBlock);
904 return WalkResult::advance();
905 }
906 }
907
908 // Record any operations inside the layer block which have inner symbols.
909 // Theses may have symbol users which need to be updated.
910 //
911 // Note: this needs to _not_ index spilled NodeOps above.
912 if (auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
913 if (auto innerSym = symOp.getInnerSymAttr())
914 innerSyms.push_back(innerSym);
915
916 // Handle instance ops that were created from nested layer blocks. These
917 // ops need to be moved outside the layer block to avoid nested binds.
918 // Nested binds are illegal in the SystemVerilog specification (and
919 // checked by FIRRTL verification).
920 //
921 // For each value defined in this layer block which drives a port of one
922 // of these instances, create an output reference type port on the
923 // to-be-created module and drive it with the value. Move the instance
924 // outside the layer block. We will hook it up later once we replace the
925 // layer block with an instance.
926 if (auto instOp = dyn_cast<InstanceOp>(op)) {
927 // Ignore instances which this pass did not create.
928 if (!createdInstances.contains(instOp))
929 return WalkResult::advance();
930
931 LLVM_DEBUG({
932 llvm::dbgs()
933 << " Found instance created from nested layer block:\n"
934 << " module: " << instOp.getModuleName() << "\n"
935 << " instance: " << instOp.getName() << "\n";
936 });
937 instOp->moveBefore(layerBlock);
938 return WalkResult::advance();
939 }
940
941 // Handle domain define ops. The destination must be within the current
942 // layer block. The source may be outside it. These, unlike other XMR
943 // captures, need to create ports as there is no XMR representation for
944 // domains. When creating these, look through any intermediate wires as
945 // these need to know the domain kind when creating the port and wires do
946 // not presently have this.
947 //
948 // TODO: Stop looking through wires when wires support domain info [1].
949 //
950 // [1]: https://github.com/llvm/circt/issues/9398
951 if (auto domainDefineOp = dyn_cast<DomainDefineOp>(op)) {
952 auto src = domainDefineOp.getSrc();
953 auto dest = domainDefineOp.getDest();
954 auto srcInLayerBlock = isAncestorOfValueOwner(layerBlock, src);
955 auto destInLayerBlock = isAncestorOfValueOwner(layerBlock, dest);
956
957 if (srcInLayerBlock) {
958 // The source and destination are in the current block. Do nothing.
959 if (destInLayerBlock)
960 return WalkResult::advance();
961 // The source is in the current layer block, but the destination is
962 // outside it. This is not possible except in situations where we
963 // have moved an instance out of the layer block. I.e., this is due
964 // to a child layer (which has already been processed) capturing
965 // something from the current layer block.
966 return WalkResult(createOutputPort(src, dest));
967 }
968
969 // The source is _not_ in the current block. Create an input domain
970 // type port with the right kind. To find the right kind, we need to
971 // look through wires to the original source.
972 if (destInLayerBlock)
973 return WalkResult(createInputPort(src, domainDefineOp.getLoc()));
974
975 // The source and destination are outside the layer block. Bubble this
976 // up. Note: this code is only reachable for situations where a prior
977 // instance, created from a bind layer has been bubbled up. This flavor
978 // of construction is otherwise illegal.
979 domainDefineOp->moveBefore(layerBlock);
980 return WalkResult::advance();
981 }
982
983 // Handle captures. For any captured operands, convert them to a suitable
984 // replacement value. The `getReplacement` function will automatically
985 // reuse values whenever possible.
986 for (size_t i = 0, e = op->getNumOperands(); i != e; ++i) {
987 auto operand = op->getOperand(i);
988
989 // If the operand is in this layer block, do nothing.
990 //
991 // Note: This check is what avoids handling ConnectOp destinations.
992 if (isAncestorOfValueOwner(layerBlock, operand))
993 continue;
994
995 op->setOperand(i, getReplacement(op, operand));
996 }
997
998 if (auto verbatim = dyn_cast<sv::VerbatimOp>(op))
999 verbatims.push_back(verbatim);
1000
1001 return WalkResult::advance();
1002 });
1003
1004 if (layerBlockWalkResult.wasInterrupted())
1005 return WalkResult::interrupt();
1006
1007 // If the layer block is empty, erase it instead of creating an empty
1008 // module. Note: empty leaf layer blocks will be erased by canonicalizers.
1009 // We don't expect to see these here. However, this handles the case of
1010 // empty intermediary layer blocks which are important in the layer block
1011 // representation, but can disappear when lowered to modules.
1012 if (llvm::all_of(layerBlock.getRegion().getBlocks(),
1013 [](auto &a) { return a.empty(); })) {
1014 assert(verbatims.empty());
1015 layerBlock.erase();
1016 return WalkResult::advance();
1017 }
1018
1019 // Create the new module. This grabs a lock to modify the circuit.
1020 FModuleOp newModule = buildNewModule(builder, layerBlock, ports);
1021 newModule.getBody().takeBody(layerBlock.getRegion());
1022 SymbolTable::setSymbolVisibility(newModule,
1023 SymbolTable::Visibility::Private);
1024
1025 LLVM_DEBUG({
1026 llvm::dbgs() << " New Module: "
1027 << layerBlockGlobals.lookup(layerBlock).moduleName << "\n";
1028 llvm::dbgs() << " ports:\n";
1029 for (size_t i = 0, e = ports.size(); i != e; ++i) {
1030 auto port = ports[i];
1031 auto value = connectValues[i];
1032 llvm::dbgs() << " - name: " << port.getName() << "\n"
1033 << " type: " << port.type << "\n"
1034 << " direction: " << port.direction << "\n"
1035 << " value: " << value << "\n";
1036 }
1037 });
1038
1039 // Replace the original layer block with an instance. Hook up the
1040 // instance. Intentionally create instance with probe ports which do
1041 // not have an associated layer. This is illegal IR that will be
1042 // made legal by the end of the pass. This is done to avoid having
1043 // to revisit and rewrite each instance everytime it is moved into a
1044 // parent layer.
1045 builder.setInsertionPointAfter(layerBlock);
1046 auto instanceName = instanceNameForLayer(layerBlock.getLayerName());
1047 auto innerSym =
1048 hw::InnerSymAttr::get(builder.getStringAttr(ns.newName(instanceName)));
1049
1050 auto instanceOp = InstanceOp::create(
1051 builder, layerBlock.getLoc(), /*moduleName=*/newModule,
1052 /*name=*/
1053 instanceName, NameKindEnum::DroppableName,
1054 /*annotations=*/ArrayRef<Attribute>{},
1055 /*portAnnotations=*/ArrayRef<Attribute>{}, /*lowerToBind=*/false,
1056 /*doNotPrint=*/true, innerSym);
1057 for (auto [lhs, rhs] : llvm::zip(instanceOp.getResults(), connectValues))
1058 if (instanceOp.getPortDirection(lhs.getResultNumber()) == Direction::In)
1059 DomainDefineOp::create(builder, builder.getUnknownLoc(), lhs, rhs);
1060 else {
1061 DomainDefineOp::create(builder, builder.getUnknownLoc(), rhs, lhs);
1062 }
1063
1064 auto outputFile = outputFileForLayer(moduleOp.getModuleNameAttr(),
1065 layerBlock.getLayerName());
1066 instanceOp->setAttr("output_file", outputFile);
1067
1068 createdInstances.try_emplace(instanceOp, newModule);
1069
1070 // create the bind op.
1071 {
1072 auto builder = OpBuilder::atBlockEnd(bindFiles[moduleOp][layer].body);
1073 BindOp::create(builder, layerBlock.getLoc(), moduleOp.getModuleNameAttr(),
1074 instanceOp.getInnerSymAttr().getSymName());
1075 }
1076
1077 LLVM_DEBUG(llvm::dbgs() << " moved inner refs:\n");
1078 for (hw::InnerSymAttr innerSym : innerSyms) {
1079 auto oldInnerRef = hw::InnerRefAttr::get(moduleOp.getModuleNameAttr(),
1080 innerSym.getSymName());
1081 auto splice = std::make_pair(instanceOp.getInnerSymAttr(),
1082 newModule.getModuleNameAttr());
1083 innerRefMap.insert({oldInnerRef, splice});
1084 LLVM_DEBUG(llvm::dbgs() << " - ref: " << oldInnerRef << "\n"
1085 << " splice: " << splice.first << ", "
1086 << splice.second << "\n";);
1087 }
1088
1089 // Update verbatims that target operations extracted alongside.
1090 if (!verbatims.empty()) {
1091 mlir::AttrTypeReplacer replacer;
1092 replacer.addReplacement(
1093 [&innerRefMap](hw::InnerRefAttr ref) -> std::optional<Attribute> {
1094 auto it = innerRefMap.find(ref);
1095 if (it != innerRefMap.end())
1096 return hw::InnerRefAttr::get(it->second.second, ref.getName());
1097 return std::nullopt;
1098 });
1099 for (auto verbatim : verbatims)
1100 replacer.replaceElementsIn(verbatim);
1101 }
1102
1103 layerBlock.erase();
1104
1105 return WalkResult::advance();
1106 });
1107 return success(!result.wasInterrupted());
1108}
1109
1111 LayerOp layer, StringRef circuitName,
1112 SmallVector<FlatSymbolRefAttr> &stack) {
1113 stack.emplace_back(FlatSymbolRefAttr::get(layer.getSymNameAttr()));
1114 ArrayRef stackRef(stack);
1115 symbolToLayer.insert(
1116 {SymbolRefAttr::get(stackRef.front().getAttr(), stackRef.drop_front()),
1117 layer});
1118 if (layer.getConvention() == LayerConvention::Inline) {
1119 auto *ctx = &getContext();
1120 auto macName = macroNameForLayer(circuitName, stack);
1121 auto symName = ns.newName(macName);
1122
1123 auto symNameAttr = StringAttr::get(ctx, symName);
1124 auto macNameAttr = StringAttr();
1125 if (macName != symName)
1126 macNameAttr = StringAttr::get(ctx, macName);
1127
1128 sv::MacroDeclOp::create(b, layer->getLoc(), symNameAttr, ArrayAttr(),
1129 macNameAttr);
1130 macroNames[layer] = FlatSymbolRefAttr::get(&getContext(), symNameAttr);
1131 }
1132 for (auto child : layer.getOps<LayerOp>())
1133 preprocessLayers(ns, b, child, circuitName, stack);
1134 stack.pop_back();
1135}
1136
1138 auto circuit = getOperation();
1139 auto circuitName = circuit.getName();
1140 for (auto layer : circuit.getOps<LayerOp>()) {
1141 OpBuilder b(layer);
1142 SmallVector<FlatSymbolRefAttr> stack;
1143 preprocessLayers(ns, b, layer, circuitName, stack);
1144 }
1145}
1146
1148 InstanceGraphNode *node, OpBuilder &b,
1149 SymbolRefAttr layerName, LayerOp layer,
1150 bool effectful) {
1151 assert(layer.getConvention() == LayerConvention::Bind);
1152 auto module = node->getModule<FModuleOp>();
1153 auto loc = module.getLoc();
1154
1155 // Compute the include guard macro name.
1156 auto macroName = guardMacroNameForLayer(module.getModuleName(), layerName);
1157 auto macroSymbol = ns.newName(macroName);
1158 auto macroNameAttr = StringAttr::get(&getContext(), macroName);
1159 auto macroSymbolAttr = StringAttr::get(&getContext(), macroSymbol);
1160 auto macroSymbolRefAttr = FlatSymbolRefAttr::get(macroSymbolAttr);
1161
1162 // Compute the base name for the bind file.
1163 auto bindFileName = fileNameForLayer(module.getName(), layerName);
1164
1165 // Build the full output path using the filename of the bindfile and the
1166 // output directory of the layer, if any.
1167 auto dir = layer->getAttrOfType<hw::OutputFileAttr>("output_file");
1168 StringAttr filename = StringAttr::get(&getContext(), bindFileName);
1169 StringAttr path;
1170 if (dir)
1171 path = StringAttr::get(&getContext(),
1172 Twine(dir.getDirectory()) + bindFileName);
1173 else
1174 path = filename;
1175
1176 // Declare the macro for the include guard.
1177 sv::MacroDeclOp::create(b, loc, macroSymbolAttr, ArrayAttr{}, macroNameAttr);
1178
1179 // Create the emit op.
1180 auto bindFile = emit::FileOp::create(b, loc, path);
1181 OpBuilder::InsertionGuard _(b);
1182 b.setInsertionPointToEnd(bindFile.getBody());
1183
1184 // Create the #ifndef for the include guard.
1185 auto includeGuard = sv::IfDefOp::create(b, loc, macroSymbolRefAttr);
1186 b.createBlock(&includeGuard.getElseRegion());
1187
1188 // Create the #define for the include guard.
1189 sv::MacroDefOp::create(b, loc, macroSymbolRefAttr);
1190
1191 // Create IR to enable any parent layers.
1192 auto parent = layer->getParentOfType<LayerOp>();
1193 while (parent) {
1194 // If the parent is bound-in, we enable it by including the bindfile.
1195 // The parent bindfile will enable all ancestors.
1196 if (parent.getConvention() == LayerConvention::Bind) {
1197 auto target = bindFiles[module][parent].filename;
1198 sv::IncludeOp::create(b, loc, IncludeStyle::Local, target);
1199 break;
1200 }
1201
1202 // If the parent layer is inline, we can only assert that the parent is
1203 // already enabled.
1204 if (parent.getConvention() == LayerConvention::Inline) {
1205 auto parentMacroSymbolRefAttr = macroNames[parent];
1206 auto parentGuard = sv::IfDefOp::create(b, loc, parentMacroSymbolRefAttr);
1207 OpBuilder::InsertionGuard guard(b);
1208 b.createBlock(&parentGuard.getElseRegion());
1209 auto message = StringAttr::get(&getContext(),
1210 Twine(parent.getName()) + " not enabled");
1211 sv::MacroErrorOp::create(b, loc, message);
1212 parent = parent->getParentOfType<LayerOp>();
1213 continue;
1214 }
1215
1216 // Unknown Layer convention.
1217 llvm_unreachable("unknown layer convention");
1218 }
1219
1220 // Create IR to include bind files for child modules. If a module is
1221 // instantiated more than once, we only need to include the bindfile once.
1222 SmallPtrSet<Operation *, 8> seen;
1223 for (auto *record : *node) {
1224 auto *child = record->getTarget()->getModule().getOperation();
1225 if (!std::get<bool>(seen.insert(child)))
1226 continue;
1227 auto files = bindFiles[child];
1228 auto lookup = files.find(layer);
1229 if (lookup == files.end() || !lookup->second.effectful)
1230 continue;
1231 sv::IncludeOp::create(b, loc, IncludeStyle::Local, lookup->second.filename);
1232 }
1233
1234 // Save the bind file information for later.
1235 auto &info = bindFiles[module][layer];
1236 info.filename = filename;
1237 info.body = includeGuard.getElseBlock();
1238 info.effectful = effectful;
1239}
1240
1242 InstanceGraphNode *node,
1243 FModuleOp module) {
1244 OpBuilder b(&getContext());
1245 b.setInsertionPointAfter(module);
1246
1247 // Create a bind file only if the layer is used under the module.
1248 llvm::SmallDenseMap<LayerOp, bool> layersRequiringBindFiles;
1249
1250 // If the module is public, create a bind file for all layers.
1251 if (module.isPublic() || emitAllBindFiles)
1252 for (auto [_, layer] : symbolToLayer)
1253 if (layer.getConvention() == LayerConvention::Bind)
1254 layersRequiringBindFiles[layer] = false;
1255
1256 // Handle layers used directly in this module.
1257 module->walk([&](LayerBlockOp layerBlock) {
1258 auto layer = symbolToLayer[layerBlock.getLayerNameAttr()];
1259 if (layer.getConvention() == LayerConvention::Inline)
1260 return;
1261
1262 // Create a bindfile for any layer directly used in the module.
1263 layersRequiringBindFiles[layer] = true;
1264
1265 // Determine names for all modules that will be created.
1266 auto moduleName = module.getModuleName();
1267 auto layerName = layerBlock.getLayerName();
1268
1269 // A name hint for the module created from this layerblock.
1270 auto layerBlockModuleName = moduleNameForLayer(moduleName, layerName);
1271
1272 // A name hint for the hier-path-op which targets the bound-in instance of
1273 // the module created from this layerblock.
1274 auto layerBlockHierPathName = hierPathNameForLayer(moduleName, layerName);
1275
1276 LayerBlockGlobals globals;
1277 globals.moduleName = ns.newName(layerBlockModuleName);
1278 globals.hierPathName = ns.newName(layerBlockHierPathName);
1279 layerBlockGlobals.insert({layerBlock, globals});
1280 });
1281
1282 // Create a bindfile for layers used indirectly under this module.
1283 for (auto *record : *node) {
1284 auto *child = record->getTarget()->getModule().getOperation();
1285 for (auto [layer, info] : bindFiles[child])
1286 layersRequiringBindFiles[layer] |= info.effectful;
1287 }
1288
1289 // Build the bindfiles for any layer seen under this module. The bindfiles
1290 // are emitted in the order which they are declared, for readability.
1291 for (auto [sym, layer] : symbolToLayer) {
1292 auto it = layersRequiringBindFiles.find(layer);
1293 if (it == layersRequiringBindFiles.end())
1294 continue;
1295 buildBindFile(ns, node, b, sym, layer, it->second);
1296 }
1297}
1298
1300 InstanceGraphNode *node,
1301 FExtModuleOp extModule) {
1302 // For each known layer of the extmodule, compute and record the bindfile
1303 // name. When a layer is known, its parent layers are implicitly known,
1304 // so compute bindfiles for parent layers too. Use a set to avoid
1305 // repeated work, which can happen if, for example, both a child layer and
1306 // a parent layer are explicitly declared to be known.
1307 auto known = extModule.getKnownLayersAttr().getAsRange<SymbolRefAttr>();
1308 if (known.empty())
1309 return;
1310
1311 auto moduleName = extModule.getExtModuleName();
1312 auto &files = bindFiles[extModule];
1313 SmallPtrSet<Operation *, 8> seen;
1314
1315 for (auto name : known) {
1316 auto layer = symbolToLayer[name];
1317 auto rootLayerName = name.getRootReference();
1318 auto nestedLayerNames = name.getNestedReferences();
1319 while (layer && std::get<bool>(seen.insert(layer))) {
1320 if (layer.getConvention() == LayerConvention::Bind) {
1321 BindFileInfo info;
1322 auto filename =
1323 fileNameForLayer(moduleName, rootLayerName, nestedLayerNames);
1324 info.filename = StringAttr::get(&getContext(), filename);
1325 info.body = nullptr;
1326 info.effectful = true;
1327 files.insert({layer, info});
1328 }
1329 layer = layer->getParentOfType<LayerOp>();
1330 if (!nestedLayerNames.empty())
1331 nestedLayerNames = nestedLayerNames.drop_back();
1332 }
1333 }
1334}
1335
1337 InstanceGraphNode *node) {
1338 auto *op = node->getModule().getOperation();
1339 if (!op)
1340 return;
1341
1342 if (auto module = dyn_cast<FModuleOp>(op))
1343 return preprocessModule(ns, node, module);
1344
1345 if (auto extModule = dyn_cast<FExtModuleOp>(op))
1346 return preprocessExtModule(ns, node, extModule);
1347}
1348
1349/// Create the bind file skeleton for each layer, for each module.
1351 InstanceGraph &ig) {
1352 ig.walkPostOrder([&](auto &node) { preprocessModuleLike(ns, &node); });
1353}
1354
1355/// Process a circuit to remove all layer blocks in each module and top-level
1356/// layer definition.
1358 LLVM_DEBUG(
1359 llvm::dbgs() << "==----- Running LowerLayers "
1360 "-------------------------------------------------===\n");
1361 CircuitOp circuitOp = getOperation();
1362
1363 // Initialize members which cannot be initialized automatically.
1364 llvm::sys::SmartMutex<true> mutex;
1365 circuitMutex = &mutex;
1366
1367 auto *ig = &getAnalysis<InstanceGraph>();
1368 CircuitNamespace ns(circuitOp);
1370 &ns, OpBuilder::InsertPoint(getOperation().getBodyBlock(),
1371 getOperation().getBodyBlock()->begin()));
1372 hierPathCache = &hpc;
1373
1374 preprocessLayers(ns);
1375 preprocessModules(ns, *ig);
1376
1377 auto mergeMaps = [](auto &&a, auto &&b) {
1378 if (failed(a))
1379 return std::forward<decltype(a)>(a);
1380 if (failed(b))
1381 return std::forward<decltype(b)>(b);
1382
1383 for (auto bb : *b)
1384 a->insert(bb);
1385 return std::forward<decltype(a)>(a);
1386 };
1387
1388 // Lower the layer blocks of each module.
1389 SmallVector<FModuleLike> modules(
1390 circuitOp.getBodyBlock()->getOps<FModuleLike>());
1391 auto failureOrInnerRefMap = transformReduce(
1392 circuitOp.getContext(), modules, FailureOr<InnerRefMap>(InnerRefMap{}),
1393 mergeMaps, [this](FModuleLike mod) -> FailureOr<InnerRefMap> {
1394 return runOnModuleLike(mod);
1395 });
1396 if (failed(failureOrInnerRefMap))
1397 return signalPassFailure();
1398 auto &innerRefMap = *failureOrInnerRefMap;
1399
1400 // Rewrite any hw::HierPathOps which have namepaths that contain rewritting
1401 // inner refs.
1402 //
1403 // TODO: This unnecessarily computes a new namepath for every hw::HierPathOp
1404 // even if that namepath is not used. It would be better to only build the
1405 // new namepath when a change is needed, e.g., by recording updates to the
1406 // namepath.
1407 for (hw::HierPathOp hierPathOp : circuitOp.getOps<hw::HierPathOp>()) {
1408 SmallVector<Attribute> newNamepath;
1409 bool modified = false;
1410 for (auto attr : hierPathOp.getNamepath()) {
1411 hw::InnerRefAttr innerRef = dyn_cast<hw::InnerRefAttr>(attr);
1412 if (!innerRef) {
1413 newNamepath.push_back(attr);
1414 continue;
1415 }
1416 auto it = innerRefMap.find(innerRef);
1417 if (it == innerRefMap.end()) {
1418 newNamepath.push_back(attr);
1419 continue;
1420 }
1421
1422 auto &[inst, mod] = it->getSecond();
1423 newNamepath.push_back(
1424 hw::InnerRefAttr::get(innerRef.getModule(), inst.getSymName()));
1425 newNamepath.push_back(hw::InnerRefAttr::get(mod, innerRef.getName()));
1426 modified = true;
1427 }
1428 if (modified)
1429 hierPathOp.setNamepathAttr(
1430 ArrayAttr::get(circuitOp.getContext(), newNamepath));
1431 }
1432
1433 // All layers definitions can now be deleted.
1434 for (auto layerOp :
1435 llvm::make_early_inc_range(circuitOp.getBodyBlock()->getOps<LayerOp>()))
1436 layerOp.erase();
1437
1438 // Cleanup state.
1439 circuitMutex = nullptr;
1440 layerBlockGlobals.clear();
1441 macroNames.clear();
1442 symbolToLayer.clear();
1443 hierPathCache = nullptr;
1444 bindFiles.clear();
1445}
assert(baseType &&"element must be base type")
Delimiter
Definition HWOps.cpp:116
static SmallString< 32 > guardMacroNameForLayer(StringRef moduleName, SymbolRefAttr layerName)
For all layerblocks @A::@B::@C in a module called Module, the include-guard macro is layers_Module_A_...
static SmallString< 32 > instanceNameForLayer(SymbolRefAttr layerName)
For a layerblock @A::@B::@C, the generated instance is called a_b_c.
static void appendName(StringRef name, SmallString< 32 > &output, bool toLower=false, Delimiter delimiter=Delimiter::BindFile)
DenseMap< hw::InnerRefAttr, std::pair< hw::InnerSymAttr, StringAttr > > InnerRefMap
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.
static SmallString< 32 > fileNameForLayer(StringRef moduleName, StringAttr root, ArrayRef< FlatSymbolRefAttr > nested)
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 > hierPathNameForLayer(StringRef moduleName, SymbolRefAttr layerName)
static Block * getBodyBlock(FModuleLike mod)
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.
void preprocessModuleLike(CircuitNamespace &ns, InstanceGraphNode *node)
Build the bindfile skeletons for each module.
void preprocessModule(CircuitNamespace &ns, InstanceGraphNode *node, FModuleOp module)
Build the bindfile skeleton for a module.
DenseMap< LayerOp, FlatSymbolRefAttr > macroNames
A map from inline layers to their macro names.
hw::OutputFileAttr outputFileForLayer(StringRef moduleName, SymbolRefAttr layerName)
void preprocessExtModule(CircuitNamespace &ns, InstanceGraphNode *node, FExtModuleOp extModule)
Record the supposed bindfiles for any known layers of the ext module.
void lowerInlineLayerBlock(LayerOp layer, LayerBlockOp layerBlock)
Lower an inline layerblock to an ifdef block.
llvm::MapVector< SymbolRefAttr, LayerOp > symbolToLayer
A mapping of symbol name to layer operation.
void buildBindFile(CircuitNamespace &ns, InstanceGraphNode *node, OpBuilder &b, SymbolRefAttr layerName, LayerOp layer, bool effectful)
Build a bindfile skeleton for a particular module and layer.
void preprocessModules(CircuitNamespace &ns, InstanceGraph &ig)
For each module, build a bindfile for each bound-layer, if needed.
LogicalResult runOnModuleBody(FModuleOp moduleOp, InnerRefMap &innerRefMap)
Extract layerblocks and strip probe colors from all ops under the module.
FModuleOp buildNewModule(OpBuilder &builder, LayerBlockOp layerBlock, ArrayRef< PortInfo > ports)
Safely build a new module with a given namehint.
hw::OutputFileAttr getOutputFile(SymbolRefAttr layerName)
hw::HierPathCache * hierPathCache
Utility for creating hw::HierPathOp.
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)
Build macro declarations and cache information about the layers.
DenseMap< Operation *, DenseMap< LayerOp, BindFileInfo > > bindFiles
A mapping from module*layer to bindfile name.
DenseMap< LayerBlockOp, LayerBlockGlobals > layerBlockGlobals
A map of layer blocks to "safe" global names which are fine to create in the circuit namespace.
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
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:30
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:87
This graph tracks modules and where they are instantiated.
This is a Node in the InstanceGraph.
auto getModule()
Get the module that this node is tracking.
decltype(auto) walkPostOrder(Fn &&fn)
Perform a post-order walk across the modules.
hw::InnerRefAttr getInnerRefTo(const hw::InnerSymTarget &target, GetNamespaceCallback getNamespace)
Obtain an inner reference to the target (operation or port), adding an inner symbol as necessary.
Value getDriverFromConnect(Value val)
Return the module-scoped driver of a value only looking through one connect.
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
IntegerAttr getIntZerosAttr(Type type)
Utility for generating a constant zero attribute.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
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 Utils.h:81
bool isAncestorOfValueOwner(Operation *op, Value value)
Return true if a Value is created "underneath" an operation.
Definition Utils.h:27
The namespace of a CircuitOp, generally inhabited by modules.
Definition Namespace.h:24
This holds the name and type that describes the module's ports.