CIRCT 21.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(SymbolRefAttr name, SmallString<32> &output,
98 bool toLower = false,
99 Delimiter delimiter = Delimiter::BindFile) {
100 appendName(name.getRootReference(), output, toLower, delimiter);
101 for (auto nested : name.getNestedReferences())
102 appendName(nested.getValue(), output, toLower, delimiter);
103}
104
105/// For a layer `@A::@B::@C` in module Module,
106/// the generated module is called `Module_A_B_C`.
107static SmallString<32> moduleNameForLayer(StringRef moduleName,
108 SymbolRefAttr layerName) {
109 SmallString<32> result;
110 appendName(moduleName, result, /*toLower=*/false,
111 /*delimiter=*/Delimiter::BindModule);
112 appendName(layerName, result, /*toLower=*/false,
113 /*delimiter=*/Delimiter::BindModule);
114 return result;
115}
116
117static SmallString<32> hierPathNameForLayer(StringRef moduleName,
118 SymbolRefAttr layerName) {
119 SmallString<32> result("__lowerLayers_path");
120 appendName(moduleName, result, /*toLower=*/false,
121 /*delimiter=*/Delimiter::BindModule);
122 appendName(layerName, result, /*toLower=*/false,
123 /*delimiter=*/Delimiter::BindModule);
124 return result;
125}
126
127/// For a layerblock `@A::@B::@C`,
128/// the generated instance is called `a_b_c`.
129static SmallString<32> instanceNameForLayer(SymbolRefAttr layerName) {
130 SmallString<32> result;
131 appendName(layerName, result, /*toLower=*/true,
132 /*delimiter=*/Delimiter::BindModule);
133 return result;
134}
135
136/// For all layerblocks `@A::@B::@C` in a module called Module,
137/// the output filename is `layers-Module-A-B-C.sv`.
138static SmallString<32> fileNameForLayer(StringRef moduleName,
139 SymbolRefAttr layerName) {
140 SmallString<32> result;
141 result.append("layers");
142 appendName(moduleName, result);
143 appendName(layerName, result);
144 result.append(".sv");
145 return result;
146}
147
148/// For all layerblocks `@A::@B::@C` in a module called Module,
149/// the include-guard macro is `layers_Module_A_B_C`.
150static SmallString<32> guardMacroNameForLayer(StringRef moduleName,
151 SymbolRefAttr layerName) {
152 SmallString<32> result;
153 result.append("layers");
154 appendName(moduleName, result, false, Delimiter::BindModule);
155 appendName(layerName, result, false, Delimiter::BindModule);
156 return result;
157}
158
159/// For a layerblock `@A::@B::@C`, the verilog macro is `A_B_C`.
160static SmallString<32>
161macroNameForLayer(StringRef circuitName,
162 ArrayRef<FlatSymbolRefAttr> layerName) {
163 SmallString<32> result("layer_");
164 result.append(circuitName);
165 for (auto part : layerName)
166 appendName(part, result, /*toLower=*/false,
167 /*delimiter=*/Delimiter::InlineMacro);
168 return result;
169}
170
171//===----------------------------------------------------------------------===//
172// LowerLayersPass
173//===----------------------------------------------------------------------===//
174
175namespace {
176/// Information about each bind file we are emitting. During a prepass, we walk
177/// the modules to find layerblocks, creating an emit::FileOp for each bound
178/// layer used under each module. As we do this, we build a table of these info
179/// objects for quick lookup later.
180struct BindFileInfo {
181 /// The filename of the bind file _without_ a directory.
182 StringAttr filename;
183 /// Where to insert bind statements into the bind file.
184 Block *body;
185};
186} // namespace
187
189 : public circt::firrtl::impl::LowerLayersBase<LowerLayersPass> {
190 hw::OutputFileAttr getOutputFile(SymbolRefAttr layerName) {
191 auto layer = symbolToLayer.lookup(layerName);
192 if (!layer)
193 return nullptr;
194 return layer->getAttrOfType<hw::OutputFileAttr>("output_file");
195 }
196
197 hw::OutputFileAttr outputFileForLayer(StringRef moduleName,
198 SymbolRefAttr layerName) {
199 if (auto file = getOutputFile(layerName))
200 return hw::OutputFileAttr::getFromDirectoryAndFilename(
201 &getContext(), file.getDirectory(),
202 fileNameForLayer(moduleName, layerName),
203 /*excludeFromFileList=*/true);
204 return hw::OutputFileAttr::getFromFilename(
205 &getContext(), fileNameForLayer(moduleName, layerName),
206 /*excludeFromFileList=*/true);
207 }
208
209 /// Safely build a new module with a given namehint. This handles geting a
210 /// lock to modify the top-level circuit.
211 FModuleOp buildNewModule(OpBuilder &builder, LayerBlockOp layerBlock);
212
213 /// Strip layer colors from the module's interface.
214 FailureOr<InnerRefMap> runOnModuleLike(FModuleLike moduleLike);
215
216 /// Extract layerblocks and strip probe colors from all ops under the module.
217 LogicalResult runOnModuleBody(FModuleOp moduleOp, InnerRefMap &innerRefMap);
218
219 /// Update the module's port types to remove any explicit layer requirements
220 /// from any probe types.
221 void removeLayersFromPorts(FModuleLike moduleLike);
222
223 /// Update the value's type to remove any layers from any probe types.
224 void removeLayersFromValue(Value value);
225
226 /// Lower an inline layerblock to an ifdef block.
227 void lowerInlineLayerBlock(LayerOp layer, LayerBlockOp layerBlock);
228
229 /// Build macro declarations and cache information about the layers.
230 void preprocessLayers(CircuitNamespace &ns, OpBuilder &b, LayerOp layer,
231 StringRef circuitName,
232 SmallVector<FlatSymbolRefAttr> &stack);
234
235 /// For each module, build a bindfile for each bound-layer, if needed.
237
238 /// Build the bindfile skeletons for each module. Set up a table which tells
239 /// us for each module/layer pair, where to insert the bind operations.
241
242 /// Build a bindfile skeleton for a particular module and layer.
244 OpBuilder &b, SymbolRefAttr layerName, LayerOp layer);
245
246 /// Entry point for the function.
247 void runOnOperation() override;
248
249 /// Indicates exclusive access to modify the circuitNamespace and the circuit.
250 llvm::sys::SmartMutex<true> *circuitMutex;
251
252 /// A map of layer blocks to "safe" global names which are fine to create in
253 /// the circuit namespace.
254 DenseMap<LayerBlockOp, LayerBlockGlobals> layerBlockGlobals;
255
256 /// A map from inline layers to their macro names.
257 DenseMap<LayerOp, FlatSymbolRefAttr> macroNames;
258
259 /// A mapping of symbol name to layer operation. This also serves as an
260 /// iterable list of all layers declared in a circuit. We use a map vector so
261 /// that the iteration order matches the order of declaration in the circuit.
262 /// This order is not required for correctness, it helps with legibility.
263 llvm::MapVector<SymbolRefAttr, LayerOp> symbolToLayer;
264
265 /// Utility for creating hw::HierPathOp.
267
268 /// A mapping from module*layer to bindfile name.
269 DenseMap<Operation *, DenseMap<LayerOp, BindFileInfo>> bindFiles;
270};
271
272/// Multi-process safe function to build a module in the circuit and return it.
273/// The name provided is only a namehint for the module---a unique name will be
274/// generated if there are conflicts with the namehint in the circuit-level
275/// namespace.
276FModuleOp LowerLayersPass::buildNewModule(OpBuilder &builder,
277 LayerBlockOp layerBlock) {
278 auto location = layerBlock.getLoc();
279 auto namehint = layerBlockGlobals.lookup(layerBlock).moduleName;
280 llvm::sys::SmartScopedLock<true> instrumentationLock(*circuitMutex);
281 FModuleOp newModule = builder.create<FModuleOp>(
282 location, builder.getStringAttr(namehint),
283 ConventionAttr::get(builder.getContext(), Convention::Internal),
284 ArrayRef<PortInfo>{}, ArrayAttr{});
285 if (auto dir = getOutputFile(layerBlock.getLayerNameAttr())) {
286 assert(dir.isDirectory());
287 newModule->setAttr("output_file", dir);
288 }
289 SymbolTable::setSymbolVisibility(newModule, SymbolTable::Visibility::Private);
290 return newModule;
291}
292
294 auto type = dyn_cast<RefType>(value.getType());
295 if (!type || !type.getLayer())
296 return;
297 value.setType(type.removeLayer());
298}
299
300void LowerLayersPass::removeLayersFromPorts(FModuleLike moduleLike) {
301 auto oldTypeAttrs = moduleLike.getPortTypesAttr();
302 SmallVector<Attribute> newTypeAttrs;
303 newTypeAttrs.reserve(oldTypeAttrs.size());
304 bool changed = false;
305
306 for (auto typeAttr : oldTypeAttrs.getAsRange<TypeAttr>()) {
307 if (auto refType = dyn_cast<RefType>(typeAttr.getValue())) {
308 if (refType.getLayer()) {
309 typeAttr = TypeAttr::get(refType.removeLayer());
310 changed = true;
311 }
312 }
313 newTypeAttrs.push_back(typeAttr);
314 }
315
316 if (!changed)
317 return;
318
319 moduleLike.setPortTypesAttr(
320 ArrayAttr::get(moduleLike.getContext(), newTypeAttrs));
321
322 if (auto moduleOp = dyn_cast<FModuleOp>(moduleLike.getOperation())) {
323 for (auto arg : moduleOp.getBodyBlock()->getArguments())
325 }
326}
327
328FailureOr<InnerRefMap>
329LowerLayersPass::runOnModuleLike(FModuleLike moduleLike) {
330 LLVM_DEBUG({
331 llvm::dbgs() << "Module: " << moduleLike.getModuleName() << "\n";
332 llvm::dbgs() << " Examining Layer Blocks:\n";
333 });
334
335 // Strip away layers from the interface of the module-like op.
336 InnerRefMap innerRefMap;
337 auto result =
338 TypeSwitch<Operation *, LogicalResult>(moduleLike.getOperation())
339 .Case<FModuleOp>([&](auto op) {
340 op.setLayers({});
342 return runOnModuleBody(op, innerRefMap);
343 })
344 .Case<FExtModuleOp, FIntModuleOp, FMemModuleOp>([&](auto op) {
345 op.setLayers({});
347 return success();
348 })
349 .Case<ClassOp, ExtClassOp>([](auto) { return success(); })
350 .Default(
351 [](auto *op) { return op->emitError("unknown module-like op"); });
352
353 if (failed(result))
354 return failure();
355
356 return innerRefMap;
357}
358
360 LayerBlockOp layerBlock) {
361 OpBuilder builder(layerBlock);
362 auto macroName = macroNames[layer];
363 auto ifDef = builder.create<sv::IfDefOp>(layerBlock.getLoc(), macroName);
364 ifDef.getBodyRegion().takeBody(layerBlock.getBodyRegion());
365 layerBlock.erase();
366}
367
368LogicalResult LowerLayersPass::runOnModuleBody(FModuleOp moduleOp,
369 InnerRefMap &innerRefMap) {
370 hw::InnerSymbolNamespace ns(moduleOp);
371
372 // A cache of values to nameable ops that can be used
373 DenseMap<Value, Operation *> nodeCache;
374
375 // Get or create a node op for a value captured by a layer block.
376 auto getOrCreateNodeOp = [&](Value operand,
377 ImplicitLocOpBuilder &builder) -> Operation * {
378 // Use the cache hit.
379 auto *nodeOp = nodeCache.lookup(operand);
380 if (nodeOp)
381 return nodeOp;
382
383 // Create a new node. Put it in the cache and use it.
384 OpBuilder::InsertionGuard guard(builder);
385 builder.setInsertionPointAfterValue(operand);
386 SmallString<16> nameHint;
387 // Try to generate a "good" name hint to use for the node.
388 if (auto *definingOp = operand.getDefiningOp()) {
389 if (auto instanceOp = dyn_cast<InstanceOp>(definingOp)) {
390 nameHint.append(instanceOp.getName());
391 nameHint.push_back('_');
392 nameHint.append(
393 instanceOp.getPortName(cast<OpResult>(operand).getResultNumber()));
394 } else if (auto opName = definingOp->getAttrOfType<StringAttr>("name")) {
395 nameHint.append(opName);
396 }
397 }
398 return nodeOp = builder.create<NodeOp>(
399 operand.getLoc(), operand,
400 nameHint.empty() ? "_layer_probe" : StringRef(nameHint));
401 };
402
403 // Determine the replacement for an operand within the current region. Keep a
404 // densemap of replacements around to avoid creating the same hardware
405 // multiple times.
406 DenseMap<Value, Value> replacements;
407 auto getReplacement = [&](Operation *user, Value value) -> Value {
408 auto it = replacements.find(value);
409 if (it != replacements.end())
410 return it->getSecond();
411
412 ImplicitLocOpBuilder localBuilder(value.getLoc(), &getContext());
413 Value replacement;
414
415 auto layerBlockOp = user->getParentOfType<LayerBlockOp>();
416 localBuilder.setInsertionPointToStart(layerBlockOp.getBody());
417
418 // If the operand is "special", e.g., it has no XMR representation, then we
419 // need to clone it.
420 //
421 // TODO: Change this to recursively clone. This will matter once FString
422 // operations have operands.
423 if (type_isa<FStringType>(value.getType())) {
424 localBuilder.setInsertionPoint(user);
425 replacement = localBuilder.clone(*value.getDefiningOp())->getResult(0);
426 replacements.insert({value, replacement});
427 return replacement;
428 }
429
430 // If the operand is an XMR ref, then we _have_ to clone it.
431 auto *definingOp = value.getDefiningOp();
432 if (isa_and_present<XMRRefOp>(definingOp)) {
433 replacement = localBuilder.clone(*definingOp)->getResult(0);
434 replacements.insert({value, replacement});
435 return replacement;
436 }
437
438 // Determine the replacement value for the captured operand. There are
439 // three cases that can occur:
440 //
441 // 1. Capturing something zero-width. Create a zero-width constant zero.
442 // 2. Capture an expression or instance port. Drop a node and XMR deref
443 // that.
444 // 3. Capture something that can handle an inner sym. XMR deref that.
445 //
446 // Note: (3) can be either an operation or a _module_ port.
447 auto baseType = type_cast<FIRRTLBaseType>(value.getType());
448 if (baseType && baseType.getBitWidthOrSentinel() == 0) {
449 OpBuilder::InsertionGuard guard(localBuilder);
450 auto zeroUIntType = UIntType::get(localBuilder.getContext(), 0);
451 replacement = localBuilder.createOrFold<BitCastOp>(
452 value.getType(), localBuilder.create<ConstantOp>(
453 zeroUIntType, getIntZerosAttr(zeroUIntType)));
454 } else {
455 auto *definingOp = value.getDefiningOp();
456 hw::InnerRefAttr innerRef;
457 if (definingOp) {
458 // Always create a node. This is a trade-off between optimizations and
459 // dead code. By adding the node, this allows the original path to be
460 // better optimized, but will leave dead code in the design. If the
461 // node is not created, then the output is less optimized. Err on the
462 // side of dead code. This dead node _may_ be eventually inlined by
463 // `ExportVerilog`. However, this is not guaranteed.
464 definingOp = getOrCreateNodeOp(value, localBuilder);
465 innerRef = getInnerRefTo(
466 definingOp, [&](auto) -> hw::InnerSymbolNamespace & { return ns; });
467 } else {
468 auto portIdx = cast<BlockArgument>(value).getArgNumber();
469 innerRef = getInnerRefTo(
470 cast<FModuleLike>(*moduleOp), portIdx,
471 [&](auto) -> hw::InnerSymbolNamespace & { return ns; });
472 }
473
474 hw::HierPathOp hierPathOp;
475 {
476 // TODO: Move to before parallel region to avoid the lock.
477 auto insertPoint = OpBuilder::InsertPoint(moduleOp->getBlock(),
478 Block::iterator(moduleOp));
479 llvm::sys::SmartScopedLock<true> circuitLock(*circuitMutex);
480 hierPathOp = hierPathCache->getOrCreatePath(
481 localBuilder.getArrayAttr({innerRef}), localBuilder.getLoc(),
482 insertPoint, layerBlockGlobals.lookup(layerBlockOp).hierPathName);
483 hierPathOp.setVisibility(SymbolTable::Visibility::Private);
484 }
485
486 replacement = localBuilder.create<XMRDerefOp>(
487 value.getType(), hierPathOp.getSymNameAttr());
488 }
489
490 replacements.insert({value, replacement});
491
492 return replacement;
493 };
494
495 // A map of instance ops to modules that this pass creates. This is used to
496 // check if this was an instance that we created and to do fast module
497 // dereferencing (avoiding a symbol table).
498 DenseMap<InstanceOp, FModuleOp> createdInstances;
499
500 // Check that the preconditions for this pass are met. Reject any ops which
501 // must have been removed before this runs.
502 auto opPreconditionCheck = [](Operation *op) -> LogicalResult {
503 // LowerXMR op removal postconditions.
504 if (isa<RefCastOp, RefDefineOp, RefResolveOp, RefSendOp, RefSubOp,
505 RWProbeOp>(op))
506 return op->emitOpError()
507 << "cannot be handled by the lower-layers pass. This should have "
508 "already been removed by the lower-xmr pass.";
509
510 return success();
511 };
512
513 // Post-order traversal that expands a layer block into its parent. Because of
514 // the pass precondition that this runs _after_ `LowerXMR`, not much has to
515 // happen here. All of the following do happen, though:
516 //
517 // 1. Any layer coloring is stripped.
518 // 2. Layers with Inline convention are converted to SV ifdefs.
519 // 3. Layers with Bind convention are converted to new modules and then
520 // instantiated at their original location. Any captured values are either
521 // moved, cloned, or converted to XMR deref ops.
522 // 4. Move instances created from earlier (3) conversions out of later (3)
523 // conversions. This is necessary to avoid a SystemVerilog-illegal
524 // bind-under-bind. (See Section 23.11 of 1800-2023.)
525 // 5. Keep track of special ops (ops with inner symbols or verbatims) which
526 // need to have something updated because of the new instance hierarchy
527 // being created.
528 //
529 // Remember, this is post-order, in-order. Child layer blocks are visited
530 // before parents. Any nested regions _within_ the layer block are also
531 // visited before the outer layer block.
532 auto result = moduleOp.walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
533 if (failed(opPreconditionCheck(op)))
534 return WalkResult::interrupt();
535
536 // Strip layer requirements from any op that might represent a probe.
537 if (auto wire = dyn_cast<WireOp>(op)) {
538 removeLayersFromValue(wire.getResult());
539 return WalkResult::advance();
540 }
541 if (auto instance = dyn_cast<InstanceOp>(op)) {
542 instance.setLayers({});
543 for (auto result : instance.getResults())
544 removeLayersFromValue(result);
545 return WalkResult::advance();
546 }
547
548 auto layerBlock = dyn_cast<LayerBlockOp>(op);
549 if (!layerBlock)
550 return WalkResult::advance();
551
552 // After this point, we are dealing with a layer block.
553 auto layer = symbolToLayer.lookup(layerBlock.getLayerName());
554
555 if (layer.getConvention() == LayerConvention::Inline) {
556 lowerInlineLayerBlock(layer, layerBlock);
557 return WalkResult::advance();
558 }
559
560 // After this point, we are dealing with a bind convention layer block.
561 assert(layer.getConvention() == LayerConvention::Bind);
562
563 // Clear the replacements so that none are re-used across layer blocks.
564 replacements.clear();
565 OpBuilder builder(moduleOp);
566 SmallVector<hw::InnerSymAttr> innerSyms;
567 SmallVector<sv::VerbatimOp> verbatims;
568 DenseSet<Operation *> spilledSubOps;
569 auto layerBlockWalkResult = layerBlock.walk([&](Operation *op) {
570 // Error if pass preconditions are not met.
571 if (failed(opPreconditionCheck(op)))
572 return WalkResult::interrupt();
573
574 // Specialized handling of subfields, subindexes, and subaccesses which
575 // need to be spilled and nodes that referred to spilled nodes. If these
576 // are kept in the module, then the XMR is going to be bidirectional. Fix
577 // this for subfield and subindex by moving these ops outside the
578 // layerblock. Try to fix this for subaccess and error if the move can't
579 // be made because the index is defined inside the layerblock. (This case
580 // is exceedingly rare given that subaccesses are almost always unexepcted
581 // when this pass runs.) Additionally, if any nodes are seen that are
582 // transparently referencing a spilled op, spill the node, too. The node
583 // provides an anchor for an inner symbol (which subfield, subindex, and
584 // subaccess do not).
585 if (isa<SubfieldOp, SubindexOp>(op)) {
586 auto input = op->getOperand(0);
587 if (!firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive() &&
588 !isAncestorOfValueOwner(layerBlock, input)) {
589 op->moveBefore(layerBlock);
590 spilledSubOps.insert(op);
591 }
592 return WalkResult::advance();
593 }
594 if (auto subOp = dyn_cast<SubaccessOp>(op)) {
595 auto input = subOp.getInput();
596 if (firrtl::type_cast<FIRRTLBaseType>(input.getType()).isPassive())
597 return WalkResult::advance();
598
599 if (!isAncestorOfValueOwner(layerBlock, input) &&
600 !isAncestorOfValueOwner(layerBlock, subOp.getIndex())) {
601 subOp->moveBefore(layerBlock);
602 spilledSubOps.insert(op);
603 return WalkResult::advance();
604 }
605 auto diag = op->emitOpError()
606 << "has a non-passive operand and captures a value defined "
607 "outside its enclosing bind-convention layerblock. The "
608 "'LowerLayers' pass cannot lower this as it would "
609 "create an output port on the resulting module.";
610 diag.attachNote(layerBlock.getLoc())
611 << "the layerblock is defined here";
612 return WalkResult::interrupt();
613 }
614 if (auto nodeOp = dyn_cast<NodeOp>(op)) {
615 auto *definingOp = nodeOp.getInput().getDefiningOp();
616 if (definingOp &&
617 spilledSubOps.contains(nodeOp.getInput().getDefiningOp())) {
618 op->moveBefore(layerBlock);
619 return WalkResult::advance();
620 }
621 }
622
623 // Record any operations inside the layer block which have inner symbols.
624 // Theses may have symbol users which need to be updated.
625 //
626 // Note: this needs to _not_ index spilled NodeOps above.
627 if (auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
628 if (auto innerSym = symOp.getInnerSymAttr())
629 innerSyms.push_back(innerSym);
630
631 // Handle instance ops that were created from nested layer blocks. These
632 // ops need to be moved outside the layer block to avoid nested binds.
633 // Nested binds are illegal in the SystemVerilog specification (and
634 // checked by FIRRTL verification).
635 //
636 // For each value defined in this layer block which drives a port of one
637 // of these instances, create an output reference type port on the
638 // to-be-created module and drive it with the value. Move the instance
639 // outside the layer block. We will hook it up later once we replace the
640 // layer block with an instance.
641 if (auto instOp = dyn_cast<InstanceOp>(op)) {
642 // Ignore instances which this pass did not create.
643 if (!createdInstances.contains(instOp))
644 return WalkResult::advance();
645
646 LLVM_DEBUG({
647 llvm::dbgs()
648 << " Found instance created from nested layer block:\n"
649 << " module: " << instOp.getModuleName() << "\n"
650 << " instance: " << instOp.getName() << "\n";
651 });
652 instOp->moveBefore(layerBlock);
653 return WalkResult::advance();
654 }
655
656 // Handle captures. For any captured operands, convert them to a suitable
657 // replacement value. The `getReplacement` function will automatically
658 // reuse values whenever possible.
659 for (size_t i = 0, e = op->getNumOperands(); i != e; ++i) {
660 auto operand = op->getOperand(i);
661
662 // If the operand is in this layer block, do nothing.
663 //
664 // Note: This check is what avoids handling ConnectOp destinations.
665 if (isAncestorOfValueOwner(layerBlock, operand))
666 continue;
667
668 op->setOperand(i, getReplacement(op, operand));
669 }
670
671 if (auto verbatim = dyn_cast<sv::VerbatimOp>(op))
672 verbatims.push_back(verbatim);
673
674 return WalkResult::advance();
675 });
676
677 if (layerBlockWalkResult.wasInterrupted())
678 return WalkResult::interrupt();
679
680 // Create the new module. This grabs a lock to modify the circuit.
681 FModuleOp newModule = buildNewModule(builder, layerBlock);
682 SymbolTable::setSymbolVisibility(newModule,
683 SymbolTable::Visibility::Private);
684 newModule.getBody().takeBody(layerBlock.getRegion());
685
686 LLVM_DEBUG({
687 llvm::dbgs() << " New Module: "
688 << layerBlockGlobals.lookup(layerBlock).moduleName << "\n";
689 });
690
691 // Replace the original layer block with an instance. Hook up the
692 // instance. Intentionally create instance with probe ports which do
693 // not have an associated layer. This is illegal IR that will be
694 // made legal by the end of the pass. This is done to avoid having
695 // to revisit and rewrite each instance everytime it is moved into a
696 // parent layer.
697 builder.setInsertionPointAfter(layerBlock);
698 auto instanceName = instanceNameForLayer(layerBlock.getLayerName());
699 auto innerSym =
700 hw::InnerSymAttr::get(builder.getStringAttr(ns.newName(instanceName)));
701
702 auto instanceOp = builder.create<InstanceOp>(
703 layerBlock.getLoc(), /*moduleName=*/newModule,
704 /*name=*/
705 instanceName, NameKindEnum::DroppableName,
706 /*annotations=*/ArrayRef<Attribute>{},
707 /*portAnnotations=*/ArrayRef<Attribute>{}, /*lowerToBind=*/false,
708 /*doNotPrint=*/true, innerSym);
709
710 auto outputFile = outputFileForLayer(moduleOp.getModuleNameAttr(),
711 layerBlock.getLayerName());
712 instanceOp->setAttr("output_file", outputFile);
713
714 createdInstances.try_emplace(instanceOp, newModule);
715
716 // create the bind op.
717 OpBuilder::atBlockEnd(bindFiles[moduleOp][layer].body)
718 .create<BindOp>(layerBlock.getLoc(), moduleOp.getModuleNameAttr(),
719 instanceOp.getInnerSymAttr().getSymName());
720
721 LLVM_DEBUG(llvm::dbgs() << " moved inner refs:\n");
722 for (hw::InnerSymAttr innerSym : innerSyms) {
723 auto oldInnerRef = hw::InnerRefAttr::get(moduleOp.getModuleNameAttr(),
724 innerSym.getSymName());
725 auto splice = std::make_pair(instanceOp.getInnerSymAttr(),
726 newModule.getModuleNameAttr());
727 innerRefMap.insert({oldInnerRef, splice});
728 LLVM_DEBUG(llvm::dbgs() << " - ref: " << oldInnerRef << "\n"
729 << " splice: " << splice.first << ", "
730 << splice.second << "\n";);
731 }
732
733 // Update verbatims that target operations extracted alongside.
734 if (!verbatims.empty()) {
735 mlir::AttrTypeReplacer replacer;
736 replacer.addReplacement(
737 [&innerRefMap](hw::InnerRefAttr ref) -> std::optional<Attribute> {
738 auto it = innerRefMap.find(ref);
739 if (it != innerRefMap.end())
740 return hw::InnerRefAttr::get(it->second.second, ref.getName());
741 return std::nullopt;
742 });
743 for (auto verbatim : verbatims)
744 replacer.replaceElementsIn(verbatim);
745 }
746
747 layerBlock.erase();
748
749 return WalkResult::advance();
750 });
751 return success(!result.wasInterrupted());
752}
753
755 LayerOp layer, StringRef circuitName,
756 SmallVector<FlatSymbolRefAttr> &stack) {
757 stack.emplace_back(FlatSymbolRefAttr::get(layer.getSymNameAttr()));
758 ArrayRef stackRef(stack);
759 symbolToLayer.insert(
760 {SymbolRefAttr::get(stackRef.front().getAttr(), stackRef.drop_front()),
761 layer});
762 if (layer.getConvention() == LayerConvention::Inline) {
763 auto *ctx = &getContext();
764 auto macName = macroNameForLayer(circuitName, stack);
765 auto symName = ns.newName(macName);
766
767 auto symNameAttr = StringAttr::get(ctx, symName);
768 auto macNameAttr = StringAttr();
769 if (macName != symName)
770 macNameAttr = StringAttr::get(ctx, macName);
771
772 b.create<sv::MacroDeclOp>(layer->getLoc(), symNameAttr, ArrayAttr(),
773 macNameAttr);
774 macroNames[layer] = FlatSymbolRefAttr::get(&getContext(), symNameAttr);
775 }
776 for (auto child : layer.getOps<LayerOp>())
777 preprocessLayers(ns, b, child, circuitName, stack);
778 stack.pop_back();
779}
780
782 auto circuit = getOperation();
783 auto circuitName = circuit.getName();
784 for (auto layer : circuit.getOps<LayerOp>()) {
785 OpBuilder b(layer);
786 SmallVector<FlatSymbolRefAttr> stack;
787 preprocessLayers(ns, b, layer, circuitName, stack);
788 }
789}
790
792 InstanceGraphNode *node, OpBuilder &b,
793 SymbolRefAttr layerName, LayerOp layer) {
794 assert(layer.getConvention() == LayerConvention::Bind);
795 auto module = node->getModule<FModuleOp>();
796 auto loc = module.getLoc();
797
798 // Compute the include guard macro name.
799 auto macroName = guardMacroNameForLayer(module.getModuleName(), layerName);
800 auto macroSymbol = ns.newName(macroName);
801 auto macroNameAttr = StringAttr::get(&getContext(), macroName);
802 auto macroSymbolAttr = StringAttr::get(&getContext(), macroSymbol);
803 auto macroSymbolRefAttr = FlatSymbolRefAttr::get(macroSymbolAttr);
804
805 // Compute the base name for the bind file.
806 auto bindFileName = fileNameForLayer(module.getName(), layerName);
807
808 // Build the full output path using the filename of the bindfile and the
809 // output directory of the layer, if any.
810 auto dir = layer->getAttrOfType<hw::OutputFileAttr>("output_file");
811 StringAttr filename = StringAttr::get(&getContext(), bindFileName);
812 StringAttr path;
813 if (dir)
814 path = StringAttr::get(&getContext(),
815 Twine(dir.getDirectory()) + bindFileName);
816 else
817 path = filename;
818
819 // Declare the macro for the include guard.
820 b.create<sv::MacroDeclOp>(loc, macroSymbolAttr, ArrayAttr{}, macroNameAttr);
821
822 // Create the emit op.
823 auto bindFile = b.create<emit::FileOp>(loc, path);
824 OpBuilder::InsertionGuard _(b);
825 b.setInsertionPointToEnd(bindFile.getBody());
826
827 // Create the #ifndef for the include guard.
828 auto includeGuard = b.create<sv::IfDefOp>(loc, macroSymbolRefAttr);
829 b.createBlock(&includeGuard.getElseRegion());
830
831 // Create the #define for the include guard.
832 b.create<sv::MacroDefOp>(loc, macroSymbolRefAttr);
833
834 // Create IR to enable any parent layers.
835 auto parent = layer->getParentOfType<LayerOp>();
836 while (parent) {
837 // If the parent is bound-in, we enable it by including the bindfile.
838 // The parent bindfile will enable all ancestors.
839 if (parent.getConvention() == LayerConvention::Bind) {
840 auto target = bindFiles[module][parent].filename;
841 b.create<sv::IncludeOp>(loc, IncludeStyle::Local, target);
842 break;
843 }
844
845 // If the parent layer is inline, we enable it by defining a macro.
846 // We will also have to manually enable any subsequent ancestors.
847 auto parentMacroSymbol = macroNames[parent];
848 b.create<sv::MacroDefOp>(loc, parentMacroSymbol);
849 parent = layer->getParentOfType<LayerOp>();
850 }
851
852 // Create IR to include bind files for child modules. If a module is
853 // instantiated more than once, we only need to include the bindfile once.
854 SmallPtrSet<Operation *, 8> seen;
855 for (auto *record : *node) {
856 auto *child = record->getTarget()->getModule().getOperation();
857 if (!std::get<bool>(seen.insert(child)))
858 continue;
859 auto files = bindFiles[child];
860 auto lookup = files.find(layer);
861 if (lookup != files.end())
862 b.create<sv::IncludeOp>(loc, IncludeStyle::Local,
863 lookup->second.filename);
864 }
865
866 // Save the bind file information for later.
867 auto &info = bindFiles[module][layer];
868 info.filename = filename;
869 info.body = includeGuard.getElseBlock();
870}
871
873 InstanceGraphNode *node) {
874 auto *op = node->getModule().getOperation();
875 if (!op)
876 return;
877
878 auto module = dyn_cast<FModuleOp>(op);
879 if (!module)
880 return;
881
882 OpBuilder b(&getContext());
883 b.setInsertionPointAfter(module);
884
885 // Create a bind file only if the layer is used under the module.
886 llvm::SmallDenseSet<LayerOp> layersRequiringBindFiles;
887
888 // If the module is public, create a bind file for all layers.
889 if (module.isPublic())
890 for (auto [_, layer] : symbolToLayer)
891 if (layer.getConvention() == LayerConvention::Bind)
892 layersRequiringBindFiles.insert(layer);
893
894 // Handle layers used directly in this module.
895 module->walk([&](LayerBlockOp layerBlock) {
896 auto layer = symbolToLayer[layerBlock.getLayerNameAttr()];
897 if (layer.getConvention() == LayerConvention::Inline)
898 return;
899
900 // Create a bindfile for any layer directly used in the module.
901 layersRequiringBindFiles.insert(layer);
902
903 // Determine names for all modules that will be created.
904 auto moduleName = module.getModuleName();
905 auto layerName = layerBlock.getLayerName();
906
907 // A name hint for the module created from this layerblock.
908 auto layerBlockModuleName = moduleNameForLayer(moduleName, layerName);
909
910 // A name hint for the hier-path-op which targets the bound-in instance of
911 // the module created from this layerblock.
912 auto layerBlockHierPathName = hierPathNameForLayer(moduleName, layerName);
913
914 LayerBlockGlobals globals;
915 globals.moduleName = ns.newName(layerBlockModuleName);
916 globals.hierPathName = ns.newName(layerBlockHierPathName);
917 layerBlockGlobals.insert({layerBlock, globals});
918 });
919
920 // Create a bindfile for layers used indirectly under this module.
921 for (auto *record : *node) {
922 auto *child = record->getTarget()->getModule().getOperation();
923 for (auto [layer, _] : bindFiles[child])
924 layersRequiringBindFiles.insert(layer);
925 }
926
927 // Build the bindfiles for any layer seen under this module. The bindfiles are
928 // emitted in the order which they are declared, for readability.
929 for (auto [sym, layer] : symbolToLayer)
930 if (layersRequiringBindFiles.contains(layer))
931 buildBindFile(ns, node, b, sym, layer);
932}
933
934/// Create the bind file skeleton for each layer, for each module.
936 InstanceGraph &ig) {
937 DenseSet<InstanceGraphNode *> visited;
938 for (auto *root : ig)
939 for (auto *node : llvm::post_order_ext(root, visited))
940 preprocessModule(ns, node);
941}
942
943/// Process a circuit to remove all layer blocks in each module and top-level
944/// layer definition.
946 LLVM_DEBUG(
947 llvm::dbgs() << "==----- Running LowerLayers "
948 "-------------------------------------------------===\n");
949 CircuitOp circuitOp = getOperation();
950
951 // Initialize members which cannot be initialized automatically.
952 llvm::sys::SmartMutex<true> mutex;
953 circuitMutex = &mutex;
954
955 auto *ig = &getAnalysis<InstanceGraph>();
956 CircuitNamespace ns(circuitOp);
958 &ns, OpBuilder::InsertPoint(getOperation().getBodyBlock(),
959 getOperation().getBodyBlock()->begin()));
960 hierPathCache = &hpc;
961
963 preprocessModules(ns, *ig);
964
965 auto mergeMaps = [](auto &&a, auto &&b) {
966 if (failed(a))
967 return std::forward<decltype(a)>(a);
968 if (failed(b))
969 return std::forward<decltype(b)>(b);
970
971 for (auto bb : *b)
972 a->insert(bb);
973 return std::forward<decltype(a)>(a);
974 };
975
976 // Lower the layer blocks of each module.
977 SmallVector<FModuleLike> modules(
978 circuitOp.getBodyBlock()->getOps<FModuleLike>());
979 auto failureOrInnerRefMap = transformReduce(
980 circuitOp.getContext(), modules, FailureOr<InnerRefMap>(InnerRefMap{}),
981 mergeMaps, [this](FModuleLike mod) -> FailureOr<InnerRefMap> {
982 return runOnModuleLike(mod);
983 });
984 if (failed(failureOrInnerRefMap))
985 return signalPassFailure();
986 auto &innerRefMap = *failureOrInnerRefMap;
987
988 // Rewrite any hw::HierPathOps which have namepaths that contain rewritting
989 // inner refs.
990 //
991 // TODO: This unnecessarily computes a new namepath for every hw::HierPathOp
992 // even if that namepath is not used. It would be better to only build the
993 // new namepath when a change is needed, e.g., by recording updates to the
994 // namepath.
995 for (hw::HierPathOp hierPathOp : circuitOp.getOps<hw::HierPathOp>()) {
996 SmallVector<Attribute> newNamepath;
997 bool modified = false;
998 for (auto attr : hierPathOp.getNamepath()) {
999 hw::InnerRefAttr innerRef = dyn_cast<hw::InnerRefAttr>(attr);
1000 if (!innerRef) {
1001 newNamepath.push_back(attr);
1002 continue;
1003 }
1004 auto it = innerRefMap.find(innerRef);
1005 if (it == innerRefMap.end()) {
1006 newNamepath.push_back(attr);
1007 continue;
1008 }
1009
1010 auto &[inst, mod] = it->getSecond();
1011 newNamepath.push_back(
1012 hw::InnerRefAttr::get(innerRef.getModule(), inst.getSymName()));
1013 newNamepath.push_back(hw::InnerRefAttr::get(mod, innerRef.getName()));
1014 modified = true;
1015 }
1016 if (modified)
1017 hierPathOp.setNamepathAttr(
1018 ArrayAttr::get(circuitOp.getContext(), newNamepath));
1019 }
1020
1021 // All layers definitions can now be deleted.
1022 for (auto layerOp :
1023 llvm::make_early_inc_range(circuitOp.getBodyBlock()->getOps<LayerOp>()))
1024 layerOp.erase();
1025
1026 // Cleanup state.
1027 circuitMutex = nullptr;
1028 layerBlockGlobals.clear();
1029 macroNames.clear();
1030 symbolToLayer.clear();
1031 hierPathCache = nullptr;
1032 bindFiles.clear();
1033}
1034
1035std::unique_ptr<mlir::Pass> circt::firrtl::createLowerLayersPass() {
1036 return std::make_unique<LowerLayersPass>();
1037}
assert(baseType &&"element must be base type")
Delimiter
Definition HWOps.cpp:115
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)
static SmallString< 32 > fileNameForLayer(StringRef moduleName, SymbolRefAttr layerName)
For all layerblocks @A::@B::@C in a module called Module, the output filename is layers-Module-A-B-C....
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 > 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.
DenseMap< LayerOp, FlatSymbolRefAttr > macroNames
A map from inline layers to their macro names.
hw::OutputFileAttr outputFileForLayer(StringRef moduleName, SymbolRefAttr layerName)
void preprocessModule(CircuitNamespace &ns, InstanceGraphNode *node)
Build the bindfile skeletons for each 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)
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.
hw::OutputFileAttr getOutputFile(SymbolRefAttr layerName)
hw::HierPathCache * hierPathCache
Utility for creating hw::HierPathOp.
FModuleOp buildNewModule(OpBuilder &builder, LayerBlockOp layerBlock)
Safely build a new module with a given namehint.
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.
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.
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.
std::unique_ptr< mlir::Pass > createLowerLayersPass()
static ResultTy transformReduce(MLIRContext *context, IterTy begin, IterTy end, ResultTy init, ReduceFuncTy reduce, TransformFuncTy transform)
Wrapper for llvm::parallelTransformReduce that performs the transform_reduce serially when MLIR multi...
IntegerAttr getIntZerosAttr(Type type)
Utility for generating a constant zero attribute.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
bool isAncestorOfValueOwner(Operation *op, Value value)
Return true if a Value is created "underneath" an operation.
Definition Utils.h:25
The namespace of a CircuitOp, generally inhabited by modules.
Definition Namespace.h:24