19 #include "mlir/Pass/Pass.h"
20 #include "llvm/Support/Debug.h"
21 #include "llvm/Support/Mutex.h"
22 #include "llvm/Support/RWMutex.h"
24 #define DEBUG_TYPE "firrtl-lower-layers"
28 #define GEN_PASS_DEF_LOWERLAYERS
29 #include "circt/Dialect/FIRRTL/Passes.h.inc"
33 using namespace circt;
34 using namespace firrtl;
41 if (
auto result = dyn_cast<OpResult>(value))
42 return op->isAncestor(result.getOwner());
43 auto argument = cast<BlockArgument>(value);
44 return op->isAncestor(argument.getOwner()->getParentOp());
50 enum class ConnectKind {
69 DenseMap<hw::InnerRefAttr, std::pair<hw::InnerSymAttr, StringAttr>>;
75 static void appendName(StringRef name, SmallString<32> &output,
76 bool toLower =
false) {
84 auto i = output.size() - name.size();
85 output[i] = llvm::toLower(output[i]);
88 static void appendName(SymbolRefAttr name, SmallString<32> &output,
89 bool toLower =
false) {
90 appendName(name.getRootReference(), output, toLower);
91 for (
auto nested : name.getNestedReferences())
92 appendName(nested.getValue(), output, toLower);
98 SymbolRefAttr layerName) {
99 SmallString<32> result;
108 SmallString<32> result;
116 SymbolRefAttr layerName) {
117 SmallString<32> result;
118 result.append(
"layers");
121 result.append(
".sv");
127 SmallString<32> result;
128 for (
auto part : layerName)
141 cast<LayerOp>(SymbolTable::lookupSymbolIn(getOperation(), layerName));
144 return layer->getAttrOfType<hw::OutputFileAttr>(
"output_file");
148 SymbolRefAttr layerName) {
150 return hw::OutputFileAttr::getFromDirectoryAndFilename(
151 &getContext(), file.getDirectory(),
154 return hw::OutputFileAttr::getFromFilename(
161 FModuleOp buildNewModule(OpBuilder &builder, LayerBlockOp layerBlock,
162 SmallVectorImpl<PortInfo> &ports);
165 InnerRefMap runOnModuleLike(FModuleLike moduleLike);
168 void runOnModuleBody(FModuleOp moduleOp,
InnerRefMap &innerRefMap);
172 void removeLayersFromPorts(FModuleLike moduleLike);
175 void removeLayersFromValue(Value value);
179 void removeLayersFromRefCast(RefCastOp cast);
182 void lowerInlineLayerBlock(LayerOp layer, LayerBlockOp layerBlock);
186 SmallVector<StringAttr> &stack);
190 void runOnOperation()
override;
207 LayerBlockOp layerBlock,
208 SmallVectorImpl<PortInfo> &ports) {
209 auto location = layerBlock.getLoc();
210 auto namehint = moduleNames.lookup(layerBlock);
211 llvm::sys::SmartScopedLock<true> instrumentationLock(*circuitMutex);
212 FModuleOp newModule = builder.create<FModuleOp>(
213 location, builder.getStringAttr(namehint),
216 if (
auto dir =
getOutputFile(layerBlock.getLayerNameAttr())) {
217 assert(dir.isDirectory());
218 newModule->setAttr(
"output_file", dir);
220 SymbolTable::setSymbolVisibility(newModule, SymbolTable::Visibility::Private);
225 auto type = dyn_cast<RefType>(value.getType());
226 if (!type || !type.getLayer())
228 value.setType(type.removeLayer());
232 auto result = cast.getResult();
233 auto oldType = result.getType();
234 if (oldType.getLayer()) {
235 auto input = cast.getInput();
236 auto srcType = input.getType();
237 auto newType = oldType.removeLayer();
238 if (newType == srcType) {
239 result.replaceAllUsesWith(input);
243 result.setType(newType);
248 auto oldTypeAttrs = moduleLike.getPortTypesAttr();
249 SmallVector<Attribute> newTypeAttrs;
250 newTypeAttrs.reserve(oldTypeAttrs.size());
251 bool changed =
false;
253 for (
auto typeAttr : oldTypeAttrs.getAsRange<TypeAttr>()) {
254 if (
auto refType = dyn_cast<RefType>(typeAttr.getValue())) {
255 if (refType.getLayer()) {
260 newTypeAttrs.push_back(typeAttr);
266 moduleLike->setAttr(FModuleLike::getPortTypesAttrName(),
269 if (
auto moduleOp = dyn_cast<FModuleOp>(moduleLike.getOperation())) {
270 for (
auto arg : moduleOp.getBodyBlock()->getArguments())
271 removeLayersFromValue(arg);
277 llvm::dbgs() <<
"Module: " << moduleLike.getModuleName() <<
"\n";
278 llvm::dbgs() <<
" Examining Layer Blocks:\n";
283 TypeSwitch<Operation *, void>(moduleLike.getOperation())
284 .Case<FModuleOp>([&](
auto op) {
286 removeLayersFromPorts(op);
287 runOnModuleBody(op, innerRefMap);
289 .Case<FExtModuleOp, FIntModuleOp, FMemModuleOp>([&](
auto op) {
291 removeLayersFromPorts(op);
293 .Case<ClassOp, ExtClassOp>([](
auto) {})
294 .Default([](
auto) {
assert(0 &&
"unknown module-like op"); });
300 LayerBlockOp layerBlock) {
301 OpBuilder builder(layerBlock);
302 auto macroName = macroNames[layer];
303 auto ifDef = builder.create<
sv::IfDefOp>(layerBlock.getLoc(), macroName);
304 ifDef.getBodyRegion().takeBody(layerBlock.getBodyRegion());
310 CircuitOp circuitOp = moduleOp->getParentOfType<CircuitOp>();
311 StringRef circuitName = circuitOp.getName();
312 hw::InnerSymbolNamespace ns(moduleOp);
317 DenseMap<InstanceOp, FModuleOp> createdInstances;
330 moduleOp.walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
332 if (
auto wire = dyn_cast<WireOp>(op)) {
333 removeLayersFromValue(wire.getResult());
334 return WalkResult::advance();
336 if (
auto sub = dyn_cast<RefSubOp>(op)) {
337 removeLayersFromValue(sub.getResult());
338 return WalkResult::advance();
340 if (
auto instance = dyn_cast<InstanceOp>(op)) {
341 instance.setLayers({});
342 for (
auto result : instance.getResults())
343 removeLayersFromValue(result);
344 return WalkResult::advance();
346 if (
auto cast = dyn_cast<RefCastOp>(op)) {
347 removeLayersFromRefCast(cast);
348 return WalkResult::advance();
351 auto layerBlock = dyn_cast<LayerBlockOp>(op);
353 return WalkResult::advance();
355 auto layer = cast<LayerOp>(
356 SymbolTable::lookupSymbolIn(getOperation(), layerBlock.getLayerName()));
358 if (layer.getConvention() == LayerConvention::Inline) {
359 lowerInlineLayerBlock(layer, layerBlock);
360 return WalkResult::advance();
363 assert(layer.getConvention() == LayerConvention::Bind);
364 Block *body = layerBlock.getBody(0);
365 OpBuilder builder(moduleOp);
369 SmallVector<PortInfo> ports;
372 SmallVector<ConnectInfo> connectValues;
375 auto createInputPort = [&](Value operand, Location loc) {
381 auto type = operand.getType();
382 if (
auto refType = dyn_cast<RefType>(type))
383 type = refType.getType();
385 ports.push_back({builder.getStringAttr(portName), type,
Direction::In,
391 Value replacement = body->addArgument(type, loc);
392 if (isa<RefType>(operand.getType())) {
393 OpBuilder::InsertionGuard guard(builder);
394 builder.setInsertionPointToStart(body);
395 replacement = builder.create<RefSendOp>(loc, replacement);
397 operand.replaceUsesWithIf(replacement, [&](OpOperand &use) {
398 auto *user = use.getOwner();
399 if (!layerBlock->isAncestor(user))
401 if (
auto connectLike = dyn_cast<FConnectLike>(user)) {
402 if (use.getOperandNumber() == 0)
408 connectValues.push_back({operand, ConnectKind::NonRef});
414 auto getPortLoc = [&](Value port) -> Location {
416 if (
auto *destOp = port.getDefiningOp())
417 if (
auto instOp = dyn_cast<InstanceOp>(destOp)) {
418 auto modOpIt = createdInstances.find(instOp);
419 if (modOpIt != createdInstances.end()) {
420 auto portNum = cast<OpResult>(port).getResultNumber();
421 loc = modOpIt->getSecond().getPortLocation(portNum);
429 auto createOutputPort = [&](Value dest, Value src) {
430 auto loc = getPortLoc(dest);
431 auto portNum = ports.size();
435 if (
auto oldRef = dyn_cast<RefType>(dest.getType()))
439 type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType(),
442 ports.push_back({builder.getStringAttr(portName), refType,
Direction::Out,
444 Value replacement = body->addArgument(refType, loc);
445 if (isa<RefType>(dest.getType())) {
446 dest.replaceUsesWithIf(replacement, [&](OpOperand &use) {
447 auto *user = use.getOwner();
448 if (!layerBlock->isAncestor(user))
450 if (
auto connectLike = dyn_cast<FConnectLike>(user)) {
451 if (use.getOperandNumber() == 0)
456 connectValues.push_back({dest, ConnectKind::Ref});
459 connectValues.push_back({dest, ConnectKind::NonRef});
460 OpBuilder::InsertionGuard guard(builder);
461 builder.setInsertionPointAfterValue(src);
462 builder.create<RefDefineOp>(
463 loc, body->getArgument(portNum),
464 builder.create<RefSendOp>(loc, src)->getResult(0));
467 SmallVector<hw::InnerSymAttr> innerSyms;
468 for (
auto &op : llvm::make_early_inc_range(*body)) {
471 if (
auto symOp = dyn_cast<hw::InnerSymbolOpInterface>(op))
472 if (
auto innerSym = symOp.getInnerSymAttr())
473 innerSyms.push_back(innerSym);
485 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
487 if (!createdInstances.contains(instOp))
492 <<
" Found instance created from nested layer block:\n"
493 <<
" module: " << instOp.getModuleName() <<
"\n"
494 <<
" instance: " << instOp.getName() <<
"\n";
496 instOp->moveBefore(layerBlock);
500 if (
auto refSend = dyn_cast<RefSendOp>(op)) {
501 auto src = refSend.getBase();
503 createInputPort(src, op.getLoc());
507 if (
auto refCast = dyn_cast<RefCastOp>(op)) {
509 createInputPort(refCast.getInput(), op.getLoc());
513 if (
auto connect = dyn_cast<FConnectLike>(op)) {
516 auto srcInLayerBlock =
isAncestor(layerBlock, src);
517 auto dstInLayerBlock =
isAncestor(layerBlock, dst);
518 if (!srcInLayerBlock && !dstInLayerBlock) {
519 connect->moveBefore(layerBlock);
523 if (!srcInLayerBlock) {
524 createInputPort(src, op.getLoc());
528 if (!dstInLayerBlock) {
529 createOutputPort(dst, src);
530 if (!isa<RefType>(dst.getType()))
551 if (
auto refResolve = dyn_cast<RefResolveOp>(op))
552 if (refResolve.getResult().hasOneUse() &&
553 refResolve.getRef().getParentBlock() != body)
554 if (
auto connect = dyn_cast<MatchingConnectOp>(
555 *refResolve.getResult().getUsers().begin()))
556 if (
connect.getDest().getParentBlock() != body) {
557 refResolve->moveBefore(layerBlock);
562 for (
auto operand : op.getOperands()) {
564 createInputPort(operand, op.getLoc());
569 FModuleOp newModule = buildNewModule(builder, layerBlock, ports);
570 SymbolTable::setSymbolVisibility(newModule,
571 SymbolTable::Visibility::Private);
572 newModule.getBody().takeBody(layerBlock.getRegion());
575 llvm::dbgs() <<
" New Module: " << moduleNames.lookup(layerBlock)
577 llvm::dbgs() <<
" ports:\n";
578 for (
size_t i = 0, e = ports.size(); i != e; ++i) {
579 auto port = ports[i];
580 auto value = connectValues[i];
581 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
582 <<
" type: " << port.type <<
"\n"
583 <<
" direction: " << port.direction <<
"\n"
584 <<
" value: " << value.value <<
"\n"
586 << (value.kind == ConnectKind::NonRef ?
"NonRef" :
"Ref")
596 builder.setInsertionPointAfter(layerBlock);
598 auto instanceOp = builder.create<InstanceOp>(
599 layerBlock.getLoc(), newModule,
601 instanceName, NameKindEnum::DroppableName,
602 ArrayRef<Attribute>{},
603 ArrayRef<Attribute>{},
true,
605 (innerSyms.empty() ? hw::InnerSymAttr{}
607 ns.newName(instanceName)))));
610 outputFileForLayer(circuitName, layerBlock.getLayerName());
611 instanceOp->setAttr(
"output_file", outputFile);
613 createdInstances.try_emplace(instanceOp, newModule);
615 LLVM_DEBUG(llvm::dbgs() <<
" moved inner refs:\n");
616 for (hw::InnerSymAttr innerSym : innerSyms) {
618 innerSym.getSymName());
619 auto splice = std::make_pair(instanceOp.getInnerSymAttr(),
620 newModule.getModuleNameAttr());
621 innerRefMap.insert({oldInnerRef, splice});
622 LLVM_DEBUG(llvm::dbgs() <<
" - ref: " << oldInnerRef <<
"\n"
623 <<
" splice: " << splice.first <<
", "
624 << splice.second <<
"\n";);
628 assert(ports.size() == connectValues.size() &&
629 "the number of instance ports and values to connect to them must be "
631 for (
unsigned portNum = 0, e = newModule.getNumPorts(); portNum < e;
633 OpBuilder::InsertionGuard guard(builder);
634 builder.setInsertionPointAfterValue(instanceOp.getResult(portNum));
636 auto src = connectValues[portNum].value;
637 if (isa<RefType>(src.getType()))
638 src = builder.create<RefResolveOp>(
639 newModule.getPortLocationAttr(portNum), src);
640 builder.create<MatchingConnectOp>(
641 newModule.getPortLocationAttr(portNum),
642 instanceOp.getResult(portNum), src);
643 }
else if (isa<RefType>(instanceOp.getResult(portNum).getType()) &&
644 connectValues[portNum].kind == ConnectKind::Ref)
645 builder.create<RefDefineOp>(getPortLoc(connectValues[portNum].value),
646 connectValues[portNum].value,
647 instanceOp.getResult(portNum));
649 builder.create<MatchingConnectOp>(
650 getPortLoc(connectValues[portNum].value),
651 connectValues[portNum].value,
652 builder.create<RefResolveOp>(newModule.getPortLocationAttr(portNum),
653 instanceOp.getResult(portNum)));
657 return WalkResult::advance();
663 SmallVector<StringAttr> &stack) {
664 stack.emplace_back(layer.getSymNameAttr());
665 if (layer.getConvention() == LayerConvention::Inline) {
666 auto *ctx = &getContext();
668 auto symName = ns.
newName(macName);
671 auto macNameAttr = StringAttr();
672 if (macName != symName)
675 b.create<sv::MacroDeclOp>(layer->getLoc(), symNameAttr, ArrayAttr(),
679 for (
auto child : layer.getOps<LayerOp>())
680 createMacroDecls(ns, b, child, stack);
686 SmallVector<StringAttr> stack;
687 createMacroDecls(ns, b, layer, stack);
694 llvm::dbgs() <<
"==----- Running LowerLayers "
695 "-------------------------------------------------===\n");
696 CircuitOp circuitOp = getOperation();
699 llvm::sys::SmartMutex<true> mutex;
700 circuitMutex = &mutex;
703 for (
auto &op : *circuitOp.getBodyBlock()) {
706 if (
auto moduleOp = dyn_cast<FModuleOp>(op)) {
707 moduleOp->walk([&](LayerBlockOp layerBlockOp) {
709 layerBlockOp.getLayerName());
710 moduleNames.insert({layerBlockOp, ns.
newName(name)});
715 if (
auto layerOp = dyn_cast<LayerOp>(op)) {
716 createMacroDecls(ns, layerOp);
721 auto mergeMaps = [](
auto &&a,
auto &&b) {
724 return std::forward<decltype(a)>(a);
728 SmallVector<FModuleLike> modules(
729 circuitOp.getBodyBlock()->getOps<FModuleLike>());
732 [
this](FModuleLike mod) { return runOnModuleLike(mod); });
741 for (hw::HierPathOp hierPathOp : circuitOp.getOps<hw::HierPathOp>()) {
742 SmallVector<Attribute> newNamepath;
743 bool modified =
false;
744 for (
auto attr : hierPathOp.getNamepath()) {
745 hw::InnerRefAttr innerRef = dyn_cast<hw::InnerRefAttr>(attr);
747 newNamepath.push_back(attr);
750 auto it = innerRefMap.find(innerRef);
751 if (it == innerRefMap.end()) {
752 newNamepath.push_back(attr);
756 auto &[inst, mod] = it->getSecond();
757 newNamepath.push_back(
763 hierPathOp.setNamepathAttr(
779 OpBuilder builder(circuitOp);
780 SmallVector<std::pair<LayerOp, StringAttr>> layers;
781 StringRef circuitName = circuitOp.getName();
782 circuitOp.walk<mlir::WalkOrder::PreOrder>([&](LayerOp layerOp) {
783 auto parentOp = layerOp->getParentOfType<LayerOp>();
784 while (parentOp && parentOp != layers.back().first)
787 if (layerOp.getConvention() == LayerConvention::Inline) {
788 layers.emplace_back(layerOp,
nullptr);
792 builder.setInsertionPointToStart(circuitOp.getBodyBlock());
795 SmallString<32> prefix(
"layers_");
796 prefix.append(circuitName);
798 for (
auto [layer, _] : layers) {
799 prefix.append(layer.getSymName());
802 prefix.append(layerOp.getSymName());
804 SmallString<128> includes;
805 for (
auto [_, strAttr] : layers) {
808 includes.append(strAttr);
809 includes.append(
"\n");
812 hw::OutputFileAttr bindFile;
813 if (
auto outputFile =
814 layerOp->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
815 auto dir = outputFile.getDirectory();
816 bindFile = hw::OutputFileAttr::getFromDirectoryAndFilename(
817 &getContext(), dir, prefix +
".sv",
820 bindFile = hw::OutputFileAttr::getFromFilename(
821 &getContext(), prefix +
".sv",
true);
825 auto header = builder.create<sv::VerbatimOp>(
827 includes +
"`ifndef " + prefix +
"\n" +
"`define " + prefix);
828 header->setAttr(
"output_file", bindFile);
831 builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
833 builder.create<sv::VerbatimOp>(layerOp.getLoc(),
"`endif // " + prefix);
834 footer->setAttr(
"output_file", bindFile);
836 if (!layerOp.getBody().getOps<LayerOp>().
empty())
839 builder.getStringAttr(
"`include \"" +
840 bindFile.getFilename().getValue() +
"\""));
845 llvm::make_early_inc_range(circuitOp.getBodyBlock()->getOps<LayerOp>()))
850 return std::make_unique<LowerLayersPass>();
static OutputFileAttr getOutputFile(igraph::ModuleOpInterface op)
assert(baseType &&"element must be base type")
static InstancePath empty
static SmallString< 32 > instanceNameForLayer(SymbolRefAttr layerName)
For a layerblock @A::@B::@C, the generated instance is called a_b_c.
static SmallString< 32 > macroNameForLayer(ArrayRef< StringAttr > layerName)
For a layerblock @A::@B::@C, the verilog macro is A_B_C.
static void appendName(StringRef name, SmallString< 32 > &output, bool toLower=false)
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...
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 bool isAncestor(Operation *op, Value value)
DenseMap< hw::InnerRefAttr, std::pair< hw::InnerSymAttr, StringAttr > > InnerRefMap
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.
void runOnModuleBody(FModuleOp moduleOp, InnerRefMap &innerRefMap)
Extract layerblocks and strip probe colors from all ops under the module.
DenseMap< LayerOp, FlatSymbolRefAttr > macroNames
A map from inline layers to their macro names.
void createMacroDecls(CircuitNamespace &ns, OpBuilder &b, LayerOp layer, SmallVector< StringAttr > &stack)
Create macro declarations for a given layer, and its child layers.
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.
InnerRefMap runOnModuleLike(FModuleLike moduleLike)
Strip layer colors from the module's interface.
hw::OutputFileAttr outputFileForLayer(StringRef circuitName, SymbolRefAttr layerName)
void removeLayersFromRefCast(RefCastOp cast)
Remove any layers from the result of the cast.
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.
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.
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
def connect(destination, source)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
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.
The namespace of a CircuitOp, generally inhabited by modules.