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