CIRCT 24.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, DomainCreateOp>([&](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 source captured from outside the
713 // current layer block. Returns the block argument that should be used in
714 // place of the original source within the layer block. Repeated calls with
715 // the same source reuse the previously created, cached port.
716 DenseMap<Value, BlockArgument> domainInputPorts;
717 auto getOrCreateDomainInputPort =
718 [&](Value src, Location loc) -> FailureOr<BlockArgument> {
719 if (auto it = domainInputPorts.find(src); it != domainInputPorts.end())
720 return it->getSecond();
721
722 Attribute domain;
723 if (failed(getDomain(src, domain)))
724 return failure();
725
726 StringAttr name;
727 auto [nameHint, rootKnown] = getFieldName(FieldRef(src, 0), true);
728 if (rootKnown)
729 name = StringAttr::get(src.getContext(), portNs.newName(nameHint));
730 else
731 name = StringAttr::get(src.getContext(), portNs.newName("anonDomain"));
732 // Domain type ports have no associations (domain info is in the type).
733 auto domainInfo = ArrayAttr::get(src.getContext(), {});
734 PortInfo port(
735 /*name=*/name,
736 /*type=*/src.getType(),
737 /*dir=*/Direction::In,
738 /*symName=*/{},
739 /*location=*/loc,
740 /*annos=*/{},
741 /*domains=*/domainInfo);
742 ports.push_back(port);
743 connectValues.push_back(src);
744 BlockArgument replacement =
745 layerBlock.getBody()->addArgument(port.type, port.loc);
746 domainInputPorts[src] = replacement;
747 return replacement;
748 };
749
750 // Create an input port for a domain-type operand that is connected to a
751 // value inside the current layer block. The source is not in the current
752 // layer block.
753 auto createInputPort = [&](Value src, Location loc) -> LogicalResult {
754 auto replacement = getOrCreateDomainInputPort(src, loc);
755 if (failed(replacement))
756 return failure();
757 src.replaceUsesWithIf(*replacement, [&](OpOperand &use) {
758 auto *user = use.getOwner();
759 if (!layerBlock->isAncestor(user))
760 return false;
761 // Replace if the connection source is the src and if the destination is
762 // _not_ in this layer block. If the destination is a spilled or
763 // to-be-spilled instance, then do not replace this connection as it
764 // will _later_ be spilled.
765 if (auto connectLike = dyn_cast<FConnectLike>(user)) {
766 auto *destDefiningOp = connectLike.getDest().getDefiningOp();
767 return connectLike.getSrc() == src &&
768 !createdInstances.contains(destDefiningOp);
769 }
770 return false;
771 });
772 return success();
773 };
774
775 // Set the location intelligently. Use the location of the capture if this
776 // is a port created for forwarding from a parent layer block to a nested
777 // layer block. Otherwise, use unknown.
778 auto getPortLoc = [&](Value port) -> Location {
779 Location loc = UnknownLoc::get(port.getContext());
780 if (auto *destOp = port.getDefiningOp())
781 if (auto instOp = dyn_cast<InstanceOp>(destOp)) {
782 auto modOpIt = createdInstances.find(instOp);
783 if (modOpIt != createdInstances.end()) {
784 auto portNum = cast<OpResult>(port).getResultNumber();
785 loc = modOpIt->getSecond().getPortLocation(portNum);
786 }
787 }
788 return loc;
789 };
790
791 // Source is in the current layer block. The destination is not in the
792 // current layer block.
793 auto createOutputPort = [&](Value src, Value dest) -> LogicalResult {
794 Attribute domain;
795 if (failed(getDomain(src, domain)))
796 return failure();
797
798 StringAttr name;
799 auto [nameHint, rootKnown] = getFieldName(FieldRef(src, 0), true);
800 if (rootKnown)
801 name = StringAttr::get(src.getContext(), portNs.newName(nameHint));
802 else
803 name = StringAttr::get(src.getContext(), portNs.newName("anonDomain"));
804 // Domain type ports have no associations (domain info is in the type).
805 auto domainInfo = ArrayAttr::get(src.getContext(), {});
806 PortInfo port(
807 /*name=*/name,
808 /*type=*/src.getType(),
809 /*dir=*/Direction::Out,
810 /*symName=*/{},
811 /*location=*/getPortLoc(dest),
812 /*annos=*/{},
813 /*domains=*/domainInfo);
814 ports.push_back(port);
815 connectValues.push_back(dest);
816 BlockArgument replacement =
817 layerBlock.getBody()->addArgument(port.type, port.loc);
818 dest.replaceUsesWithIf(replacement, [&](OpOperand &use) {
819 auto *user = use.getOwner();
820 if (!layerBlock->isAncestor(user))
821 return false;
822 // Replace connection destinations.
823 if (auto connectLike = dyn_cast<FConnectLike>(user))
824 return connectLike.getDest() == dest;
825 return false;
826 });
827 return success();
828 };
829
830 // Clear the replacements so that none are re-used across layer blocks.
831 replacements.clear();
832 OpBuilder builder(moduleOp);
833 SmallVector<hw::InnerSymAttr> innerSyms;
834 SmallVector<sv::VerbatimOp> verbatims;
835 DenseSet<Operation *> spilledSubOps;
836 auto layerBlockWalkResult = layerBlock.walk([&](Operation *op) {
837 // Error if pass preconditions are not met.
838 if (failed(opPreconditionCheck(op)))
839 return WalkResult::interrupt();
840
841 // Specialized handling of subfields, subindexes, and subaccesses which
842 // need to be spilled and nodes that referred to spilled nodes. If these
843 // are kept in the module, then the XMR is going to be bidirectional. Fix
844 // this for subfield and subindex by moving these ops outside the
845 // layerblock. Try to fix this for subaccess and error if the move can't
846 // be made because the index is defined inside the layerblock. (This case
847 // is exceedingly rare given that subaccesses are almost always unexepcted
848 // when this pass runs.) Additionally, if any nodes are seen that are
849 // transparently referencing a spilled op, spill the node, too. The node
850 // provides an anchor for an inner symbol (which subfield, subindex, and
851 // subaccess do not).
852 auto fixSubOp = [&](auto subOp) {
853 auto input = subOp.getInput();
854
855 // If the input is defined in this layerblock, we are done.
856 if (isAncestorOfValueOwner(layerBlock, input))
857 return WalkResult::advance();
858
859 // Otherwise, capture the input operand, if possible.
860 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
861 subOp.getInputMutable().assign(getReplacement(subOp, input));
862 return WalkResult::advance();
863 }
864
865 // Otherwise, move the subfield op out of the layerblock.
866 op->moveBefore(layerBlock);
867 spilledSubOps.insert(op);
868 return WalkResult::advance();
869 };
870
871 if (auto subOp = dyn_cast<SubfieldOp>(op))
872 return fixSubOp(subOp);
873
874 if (auto subOp = dyn_cast<SubindexOp>(op))
875 return fixSubOp(subOp);
876
877 if (auto subOp = dyn_cast<SubaccessOp>(op)) {
878 auto input = subOp.getInput();
879 auto index = subOp.getIndex();
880
881 // If the input is defined in this layerblock, capture the index if
882 // needed, and we are done.
883 if (isAncestorOfValueOwner(layerBlock, input)) {
884 if (!isAncestorOfValueOwner(layerBlock, index)) {
885 subOp.getIndexMutable().assign(getReplacement(subOp, index));
886 }
887 return WalkResult::advance();
888 }
889
890 // Otherwise, capture the input operand, if possible.
891 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive()) {
892 subOp.getInputMutable().assign(getReplacement(subOp, input));
893 if (!isAncestorOfValueOwner(layerBlock, index))
894 subOp.getIndexMutable().assign(getReplacement(subOp, index));
895 return WalkResult::advance();
896 }
897
898 // Otherwise, move the subaccess op out of the layerblock, if possible.
899 if (!isAncestorOfValueOwner(layerBlock, index)) {
900 subOp->moveBefore(layerBlock);
901 spilledSubOps.insert(op);
902 return WalkResult::advance();
903 }
904
905 // When the input is not passive, but the index is defined inside this
906 // layerblock, we are out of options.
907 auto diag = op->emitOpError()
908 << "has a non-passive operand and captures a value defined "
909 "outside its enclosing bind-convention layerblock. The "
910 "'LowerLayers' pass cannot lower this as it would "
911 "create an output port on the resulting module.";
912 diag.attachNote(layerBlock.getLoc())
913 << "the layerblock is defined here";
914 return WalkResult::interrupt();
915 }
916
917 if (auto nodeOp = dyn_cast<NodeOp>(op)) {
918 auto *definingOp = nodeOp.getInput().getDefiningOp();
919 if (definingOp &&
920 spilledSubOps.contains(nodeOp.getInput().getDefiningOp())) {
921 op->moveBefore(layerBlock);
922 return WalkResult::advance();
923 }
924 }
925
926 // Record any operations inside the layer block which have inner symbols.
927 // Theses may have symbol users which need to be updated.
928 //
929 // Note: this needs to _not_ index spilled NodeOps above.
930 if (auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
931 if (auto innerSym = symOp.getInnerSymAttr())
932 innerSyms.push_back(innerSym);
933
934 // Handle instance ops that were created from nested layer blocks. These
935 // ops need to be moved outside the layer block to avoid nested binds.
936 // Nested binds are illegal in the SystemVerilog specification (and
937 // checked by FIRRTL verification).
938 //
939 // For each value defined in this layer block which drives a port of one
940 // of these instances, create an output reference type port on the
941 // to-be-created module and drive it with the value. Move the instance
942 // outside the layer block. We will hook it up later once we replace the
943 // layer block with an instance.
944 if (auto instOp = dyn_cast<InstanceOp>(op)) {
945 // Ignore instances which this pass did not create.
946 if (!createdInstances.contains(instOp))
947 return WalkResult::advance();
948
949 LLVM_DEBUG({
950 llvm::dbgs()
951 << " Found instance created from nested layer block:\n"
952 << " module: " << instOp.getModuleName() << "\n"
953 << " instance: " << instOp.getName() << "\n";
954 });
955 instOp->moveBefore(layerBlock);
956 return WalkResult::advance();
957 }
958
959 // Handle domain define ops. The destination must be within the current
960 // layer block. The source may be outside it. These, unlike other XMR
961 // captures, need to create ports as there is no XMR representation for
962 // domains. When creating these, look through any intermediate wires as
963 // these need to know the domain kind when creating the port and wires do
964 // not presently have this.
965 //
966 // TODO: Stop looking through wires when wires support domain info [1].
967 //
968 // [1]: https://github.com/llvm/circt/issues/9398
969 if (auto domainDefineOp = dyn_cast<DomainDefineOp>(op)) {
970 auto src = domainDefineOp.getSrc();
971 auto dest = domainDefineOp.getDest();
972 auto srcInLayerBlock = isAncestorOfValueOwner(layerBlock, src);
973 auto destInLayerBlock = isAncestorOfValueOwner(layerBlock, dest);
974
975 if (srcInLayerBlock) {
976 // The source and destination are in the current block. Do nothing.
977 if (destInLayerBlock)
978 return WalkResult::advance();
979 // The source is in the current layer block, but the destination is
980 // outside it. This is not possible except in situations where we
981 // have moved an instance out of the layer block. I.e., this is due
982 // to a child layer (which has already been processed) capturing
983 // something from the current layer block.
984 return WalkResult(createOutputPort(src, dest));
985 }
986
987 // The source is _not_ in the current block. Create an input domain
988 // type port with the right kind. To find the right kind, we need to
989 // look through wires to the original source.
990 if (destInLayerBlock)
991 return WalkResult(createInputPort(src, domainDefineOp.getLoc()));
992
993 // The source and destination are outside the layer block. Bubble this
994 // up. Note: this code is only reachable for situations where a prior
995 // instance, created from a bind layer has been bubbled up. This flavor
996 // of construction is otherwise illegal.
997 domainDefineOp->moveBefore(layerBlock);
998 return WalkResult::advance();
999 }
1000
1001 // Handle captures. For any captured operands, convert them to a suitable
1002 // replacement value. The `getReplacement` function will automatically
1003 // reuse values whenever possible.
1004 for (size_t i = 0, e = op->getNumOperands(); i != e; ++i) {
1005 auto operand = op->getOperand(i);
1006
1007 // If the operand is in this layer block, do nothing.
1008 //
1009 // Note: This check is what avoids handling ConnectOp destinations.
1010 if (isAncestorOfValueOwner(layerBlock, operand))
1011 continue;
1012
1013 // Domain-type operands have no XMR representation and need to have
1014 // input ports created.
1015 if (type_isa<DomainType>(operand.getType())) {
1016 auto replacement = getOrCreateDomainInputPort(operand, op->getLoc());
1017 if (failed(replacement))
1018 return WalkResult::interrupt();
1019 op->setOperand(i, *replacement);
1020 continue;
1021 }
1022
1023 op->setOperand(i, getReplacement(op, operand));
1024 }
1025
1026 if (auto verbatim = dyn_cast<sv::VerbatimOp>(op))
1027 verbatims.push_back(verbatim);
1028
1029 return WalkResult::advance();
1030 });
1031
1032 if (layerBlockWalkResult.wasInterrupted())
1033 return WalkResult::interrupt();
1034
1035 // If the layer block is empty, erase it instead of creating an empty
1036 // module. Note: empty leaf layer blocks will be erased by canonicalizers.
1037 // We don't expect to see these here. However, this handles the case of
1038 // empty intermediary layer blocks which are important in the layer block
1039 // representation, but can disappear when lowered to modules.
1040 if (llvm::all_of(layerBlock.getRegion().getBlocks(),
1041 [](auto &a) { return a.empty(); })) {
1042 assert(verbatims.empty());
1043 layerBlock.erase();
1044 return WalkResult::advance();
1045 }
1046
1047 // Create the new module. This grabs a lock to modify the circuit.
1048 FModuleOp newModule = buildNewModule(builder, layerBlock, ports);
1049 newModule.getBody().takeBody(layerBlock.getRegion());
1050 SymbolTable::setSymbolVisibility(newModule,
1051 SymbolTable::Visibility::Private);
1052
1053 LLVM_DEBUG({
1054 llvm::dbgs() << " New Module: "
1055 << layerBlockGlobals.lookup(layerBlock).moduleName << "\n";
1056 llvm::dbgs() << " ports:\n";
1057 for (size_t i = 0, e = ports.size(); i != e; ++i) {
1058 auto port = ports[i];
1059 auto value = connectValues[i];
1060 llvm::dbgs() << " - name: " << port.getName() << "\n"
1061 << " type: " << port.type << "\n"
1062 << " direction: " << port.direction << "\n"
1063 << " value: " << value << "\n";
1064 }
1065 });
1066
1067 // Replace the original layer block with an instance. Hook up the
1068 // instance. Intentionally create instance with probe ports which do
1069 // not have an associated layer. This is illegal IR that will be
1070 // made legal by the end of the pass. This is done to avoid having
1071 // to revisit and rewrite each instance everytime it is moved into a
1072 // parent layer.
1073 builder.setInsertionPointAfter(layerBlock);
1074 auto instanceName = instanceNameForLayer(layerBlock.getLayerName());
1075 auto innerSym =
1076 hw::InnerSymAttr::get(builder.getStringAttr(ns.newName(instanceName)));
1077
1078 auto instanceOp = InstanceOp::create(
1079 builder, layerBlock.getLoc(), /*moduleName=*/newModule,
1080 /*name=*/
1081 instanceName, NameKindEnum::DroppableName,
1082 /*annotations=*/ArrayRef<Attribute>{},
1083 /*portAnnotations=*/ArrayRef<Attribute>{}, /*lowerToBind=*/false,
1084 /*doNotPrint=*/true, innerSym);
1085 for (auto [lhs, rhs] : llvm::zip(instanceOp.getResults(), connectValues))
1086 if (instanceOp.getPortDirection(lhs.getResultNumber()) == Direction::In)
1087 DomainDefineOp::create(builder, builder.getUnknownLoc(), lhs, rhs);
1088 else {
1089 DomainDefineOp::create(builder, builder.getUnknownLoc(), rhs, lhs);
1090 }
1091
1092 auto outputFile = outputFileForLayer(moduleOp.getModuleNameAttr(),
1093 layerBlock.getLayerName());
1094 instanceOp->setAttr("output_file", outputFile);
1095
1096 createdInstances.try_emplace(instanceOp, newModule);
1097
1098 // create the bind op.
1099 {
1100 auto builder = OpBuilder::atBlockEnd(bindFiles[moduleOp][layer].body);
1101 BindOp::create(builder, layerBlock.getLoc(), moduleOp.getModuleNameAttr(),
1102 instanceOp.getInnerSymAttr().getSymName());
1103 }
1104
1105 LLVM_DEBUG(llvm::dbgs() << " moved inner refs:\n");
1106 for (hw::InnerSymAttr innerSym : innerSyms) {
1107 auto oldInnerRef = hw::InnerRefAttr::get(moduleOp.getModuleNameAttr(),
1108 innerSym.getSymName());
1109 auto splice = std::make_pair(instanceOp.getInnerSymAttr(),
1110 newModule.getModuleNameAttr());
1111 innerRefMap.insert({oldInnerRef, splice});
1112 LLVM_DEBUG(llvm::dbgs() << " - ref: " << oldInnerRef << "\n"
1113 << " splice: " << splice.first << ", "
1114 << splice.second << "\n";);
1115 }
1116
1117 // Update verbatims that target operations extracted alongside.
1118 if (!verbatims.empty()) {
1119 mlir::AttrTypeReplacer replacer;
1120 replacer.addReplacement(
1121 [&innerRefMap](hw::InnerRefAttr ref) -> std::optional<Attribute> {
1122 auto it = innerRefMap.find(ref);
1123 if (it != innerRefMap.end())
1124 return hw::InnerRefAttr::get(it->second.second, ref.getName());
1125 return std::nullopt;
1126 });
1127 for (auto verbatim : verbatims)
1128 replacer.replaceElementsIn(verbatim);
1129 }
1130
1131 layerBlock.erase();
1132
1133 return WalkResult::advance();
1134 });
1135 return success(!result.wasInterrupted());
1136}
1137
1139 LayerOp layer, StringRef circuitName,
1140 SmallVector<FlatSymbolRefAttr> &stack) {
1141 stack.emplace_back(FlatSymbolRefAttr::get(layer.getSymNameAttr()));
1142 ArrayRef stackRef(stack);
1143 symbolToLayer.insert(
1144 {SymbolRefAttr::get(stackRef.front().getAttr(), stackRef.drop_front()),
1145 layer});
1146 if (layer.getConvention() == LayerConvention::Inline) {
1147 auto *ctx = &getContext();
1148 auto macName = macroNameForLayer(circuitName, stack);
1149 auto symName = ns.newName(macName);
1150
1151 auto symNameAttr = StringAttr::get(ctx, symName);
1152 auto macNameAttr = StringAttr();
1153 if (macName != symName)
1154 macNameAttr = StringAttr::get(ctx, macName);
1155
1156 sv::MacroDeclOp::create(b, layer->getLoc(), symNameAttr,
1157 /*sym_visibility=*/{}, ArrayAttr(), macNameAttr);
1158 macroNames[layer] = FlatSymbolRefAttr::get(&getContext(), symNameAttr);
1159 }
1160 for (auto child : layer.getOps<LayerOp>())
1161 preprocessLayers(ns, b, child, circuitName, stack);
1162 stack.pop_back();
1163}
1164
1166 auto circuit = getOperation();
1167 auto circuitName = circuit.getName();
1168 for (auto layer : circuit.getOps<LayerOp>()) {
1169 OpBuilder b(layer);
1170 SmallVector<FlatSymbolRefAttr> stack;
1171 preprocessLayers(ns, b, layer, circuitName, stack);
1172 }
1173}
1174
1176 InstanceGraphNode *node, OpBuilder &b,
1177 SymbolRefAttr layerName, LayerOp layer,
1178 bool effectful) {
1179 assert(layer.getConvention() == LayerConvention::Bind);
1180 auto module = node->getModule<FModuleOp>();
1181 auto loc = module.getLoc();
1182
1183 // Compute the include guard macro name.
1184 auto macroName = guardMacroNameForLayer(module.getModuleName(), layerName);
1185 auto macroSymbol = ns.newName(macroName);
1186 auto macroNameAttr = StringAttr::get(&getContext(), macroName);
1187 auto macroSymbolAttr = StringAttr::get(&getContext(), macroSymbol);
1188 auto macroSymbolRefAttr = FlatSymbolRefAttr::get(macroSymbolAttr);
1189
1190 // Compute the base name for the bind file.
1191 auto bindFileName = fileNameForLayer(module.getName(), layerName);
1192
1193 // Build the full output path using the filename of the bindfile and the
1194 // output directory of the layer, if any.
1195 auto dir = layer->getAttrOfType<hw::OutputFileAttr>("output_file");
1196 StringAttr filename = StringAttr::get(&getContext(), bindFileName);
1197 StringAttr path;
1198 if (dir)
1199 path = StringAttr::get(&getContext(),
1200 Twine(dir.getDirectory()) + bindFileName);
1201 else
1202 path = filename;
1203
1204 // Declare the macro for the include guard.
1205 sv::MacroDeclOp::create(b, loc, macroSymbolAttr, /*sym_visibility=*/{},
1206 ArrayAttr{}, macroNameAttr);
1207
1208 // Create the emit op.
1209 auto bindFile = emit::FileOp::create(b, loc, path);
1210 OpBuilder::InsertionGuard _(b);
1211 b.setInsertionPointToEnd(bindFile.getBody());
1212
1213 // Create the #ifndef for the include guard.
1214 auto includeGuard = sv::IfDefOp::create(b, loc, macroSymbolRefAttr);
1215 b.createBlock(&includeGuard.getElseRegion());
1216
1217 // Create the #define for the include guard.
1218 sv::MacroDefOp::create(b, loc, macroSymbolRefAttr);
1219
1220 // Create IR to enable any parent layers.
1221 auto parent = layer->getParentOfType<LayerOp>();
1222 while (parent) {
1223 // If the parent is bound-in, we enable it by including the bindfile.
1224 // The parent bindfile will enable all ancestors.
1225 if (parent.getConvention() == LayerConvention::Bind) {
1226 auto target = bindFiles[module][parent].filename;
1227 sv::IncludeOp::create(b, loc, IncludeStyle::Local, target);
1228 break;
1229 }
1230
1231 // If the parent layer is inline, we can only assert that the parent is
1232 // already enabled.
1233 if (parent.getConvention() == LayerConvention::Inline) {
1234 auto parentMacroSymbolRefAttr = macroNames[parent];
1235 auto parentGuard = sv::IfDefOp::create(b, loc, parentMacroSymbolRefAttr);
1236 OpBuilder::InsertionGuard guard(b);
1237 b.createBlock(&parentGuard.getElseRegion());
1238 auto message = StringAttr::get(&getContext(),
1239 Twine(parent.getName()) + " not enabled");
1240 sv::MacroErrorOp::create(b, loc, message);
1241 parent = parent->getParentOfType<LayerOp>();
1242 continue;
1243 }
1244
1245 // Unknown Layer convention.
1246 llvm_unreachable("unknown layer convention");
1247 }
1248
1249 // Create IR to include bind files for child modules. If a module is
1250 // instantiated more than once, we only need to include the bindfile once.
1251 SmallPtrSet<Operation *, 8> seen;
1252 for (auto *record : *node) {
1253 auto *child = record->getTarget()->getModule().getOperation();
1254 if (!std::get<bool>(seen.insert(child)))
1255 continue;
1256 auto files = bindFiles[child];
1257 auto lookup = files.find(layer);
1258 if (lookup == files.end() || !lookup->second.effectful)
1259 continue;
1260 sv::IncludeOp::create(b, loc, IncludeStyle::Local, lookup->second.filename);
1261 }
1262
1263 // Save the bind file information for later.
1264 auto &info = bindFiles[module][layer];
1265 info.filename = filename;
1266 info.body = includeGuard.getElseBlock();
1267 info.effectful = effectful;
1268}
1269
1271 InstanceGraphNode *node,
1272 FModuleOp module) {
1273 OpBuilder b(&getContext());
1274 b.setInsertionPointAfter(module);
1275
1276 // Create a bind file only if the layer is used under the module.
1277 llvm::SmallDenseMap<LayerOp, bool> layersRequiringBindFiles;
1278
1279 // If the module is public, create a bind file for all layers.
1280 if (module.isPublic() || emitAllBindFiles)
1281 for (auto [_, layer] : symbolToLayer)
1282 if (layer.getConvention() == LayerConvention::Bind)
1283 layersRequiringBindFiles[layer] = false;
1284
1285 // Handle layers used directly in this module.
1286 module->walk([&](LayerBlockOp layerBlock) {
1287 auto layer = symbolToLayer[layerBlock.getLayerNameAttr()];
1288 if (layer.getConvention() == LayerConvention::Inline)
1289 return;
1290
1291 // Create a bindfile for any layer directly used in the module.
1292 layersRequiringBindFiles[layer] = true;
1293
1294 // Determine names for all modules that will be created.
1295 auto moduleName = module.getModuleName();
1296 auto layerName = layerBlock.getLayerName();
1297
1298 // A name hint for the module created from this layerblock.
1299 auto layerBlockModuleName = moduleNameForLayer(moduleName, layerName);
1300
1301 // A name hint for the hier-path-op which targets the bound-in instance of
1302 // the module created from this layerblock.
1303 auto layerBlockHierPathName = hierPathNameForLayer(moduleName, layerName);
1304
1305 LayerBlockGlobals globals;
1306 globals.moduleName = ns.newName(layerBlockModuleName);
1307 globals.hierPathName = ns.newName(layerBlockHierPathName);
1308 layerBlockGlobals.insert({layerBlock, globals});
1309 });
1310
1311 // Create a bindfile for layers used indirectly under this module.
1312 for (auto *record : *node) {
1313 auto *child = record->getTarget()->getModule().getOperation();
1314 for (auto [layer, info] : bindFiles[child])
1315 layersRequiringBindFiles[layer] |= info.effectful;
1316 }
1317
1318 // Build the bindfiles for any layer seen under this module. The bindfiles
1319 // are emitted in the order which they are declared, for readability.
1320 for (auto [sym, layer] : symbolToLayer) {
1321 auto it = layersRequiringBindFiles.find(layer);
1322 if (it == layersRequiringBindFiles.end())
1323 continue;
1324 buildBindFile(ns, node, b, sym, layer, it->second);
1325 }
1326}
1327
1329 InstanceGraphNode *node,
1330 FExtModuleOp extModule) {
1331 // For each known layer of the extmodule, compute and record the bindfile
1332 // name. When a layer is known, its parent layers are implicitly known,
1333 // so compute bindfiles for parent layers too. Use a set to avoid
1334 // repeated work, which can happen if, for example, both a child layer and
1335 // a parent layer are explicitly declared to be known.
1336 auto known = extModule.getKnownLayersAttr().getAsRange<SymbolRefAttr>();
1337 if (known.empty())
1338 return;
1339
1340 auto moduleName = extModule.getExtModuleName();
1341 auto &files = bindFiles[extModule];
1342 SmallPtrSet<Operation *, 8> seen;
1343
1344 for (auto name : known) {
1345 auto layer = symbolToLayer[name];
1346 auto rootLayerName = name.getRootReference();
1347 auto nestedLayerNames = name.getNestedReferences();
1348 while (layer && std::get<bool>(seen.insert(layer))) {
1349 if (layer.getConvention() == LayerConvention::Bind) {
1350 BindFileInfo info;
1351 auto filename =
1352 fileNameForLayer(moduleName, rootLayerName, nestedLayerNames);
1353 info.filename = StringAttr::get(&getContext(), filename);
1354 info.body = nullptr;
1355 info.effectful = true;
1356 files.insert({layer, info});
1357 }
1358 layer = layer->getParentOfType<LayerOp>();
1359 if (!nestedLayerNames.empty())
1360 nestedLayerNames = nestedLayerNames.drop_back();
1361 }
1362 }
1363}
1364
1366 InstanceGraphNode *node) {
1367 auto *op = node->getModule().getOperation();
1368 if (!op)
1369 return;
1370
1371 if (auto module = dyn_cast<FModuleOp>(op))
1372 return preprocessModule(ns, node, module);
1373
1374 if (auto extModule = dyn_cast<FExtModuleOp>(op))
1375 return preprocessExtModule(ns, node, extModule);
1376}
1377
1378/// Create the bind file skeleton for each layer, for each module.
1380 InstanceGraph &ig) {
1381 ig.walkPostOrder([&](auto &node) { preprocessModuleLike(ns, &node); });
1382}
1383
1384/// Process a circuit to remove all layer blocks in each module and top-level
1385/// layer definition.
1387 LLVM_DEBUG(
1388 llvm::dbgs() << "==----- Running LowerLayers "
1389 "-------------------------------------------------===\n");
1390 CircuitOp circuitOp = getOperation();
1391
1392 // Initialize members which cannot be initialized automatically.
1393 llvm::sys::SmartMutex<true> mutex;
1394 circuitMutex = &mutex;
1395
1396 auto *ig = &getAnalysis<InstanceGraph>();
1397 CircuitNamespace ns(circuitOp);
1399 &ns, OpBuilder::InsertPoint(getOperation().getBodyBlock(),
1400 getOperation().getBodyBlock()->begin()));
1401 hierPathCache = &hpc;
1402
1403 preprocessLayers(ns);
1404 preprocessModules(ns, *ig);
1405
1406 auto mergeMaps = [](auto &&a, auto &&b) {
1407 if (failed(a))
1408 return std::forward<decltype(a)>(a);
1409 if (failed(b))
1410 return std::forward<decltype(b)>(b);
1411
1412 for (auto bb : *b)
1413 a->insert(bb);
1414 return std::forward<decltype(a)>(a);
1415 };
1416
1417 // Lower the layer blocks of each module.
1418 SmallVector<FModuleLike> modules(
1419 circuitOp.getBodyBlock()->getOps<FModuleLike>());
1420 auto failureOrInnerRefMap = transformReduce(
1421 circuitOp.getContext(), modules, FailureOr<InnerRefMap>(InnerRefMap{}),
1422 mergeMaps, [this](FModuleLike mod) -> FailureOr<InnerRefMap> {
1423 return runOnModuleLike(mod);
1424 });
1425 if (failed(failureOrInnerRefMap))
1426 return signalPassFailure();
1427 auto &innerRefMap = *failureOrInnerRefMap;
1428
1429 // Rewrite any hw::HierPathOps which have namepaths that contain rewritting
1430 // inner refs.
1431 //
1432 // TODO: This unnecessarily computes a new namepath for every hw::HierPathOp
1433 // even if that namepath is not used. It would be better to only build the
1434 // new namepath when a change is needed, e.g., by recording updates to the
1435 // namepath.
1436 for (hw::HierPathOp hierPathOp : circuitOp.getOps<hw::HierPathOp>()) {
1437 SmallVector<Attribute> newNamepath;
1438 bool modified = false;
1439 for (auto attr : hierPathOp.getNamepath()) {
1440 hw::InnerRefAttr innerRef = dyn_cast<hw::InnerRefAttr>(attr);
1441 if (!innerRef) {
1442 newNamepath.push_back(attr);
1443 continue;
1444 }
1445 auto it = innerRefMap.find(innerRef);
1446 if (it == innerRefMap.end()) {
1447 newNamepath.push_back(attr);
1448 continue;
1449 }
1450
1451 auto &[inst, mod] = it->getSecond();
1452 newNamepath.push_back(
1453 hw::InnerRefAttr::get(innerRef.getModule(), inst.getSymName()));
1454 newNamepath.push_back(hw::InnerRefAttr::get(mod, innerRef.getName()));
1455 modified = true;
1456 }
1457 if (modified)
1458 hierPathOp.setNamepathAttr(
1459 ArrayAttr::get(circuitOp.getContext(), newNamepath));
1460 }
1461
1462 // All layers definitions can now be deleted.
1463 for (auto layerOp :
1464 llvm::make_early_inc_range(circuitOp.getBodyBlock()->getOps<LayerOp>()))
1465 layerOp.erase();
1466
1467 // Cleanup state.
1468 circuitMutex = nullptr;
1469 layerBlockGlobals.clear();
1470 macroNames.clear();
1471 symbolToLayer.clear();
1472 hierPathCache = nullptr;
1473 bindFiles.clear();
1474}
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)
static FailureOr< Domain > getDomain(Operation *op)
The domain of an AXI4 op, or failure for one this pass does not know.
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:86
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.