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