17 #include "llvm/Support/Debug.h"
18 #include "llvm/Support/Mutex.h"
19 #include "llvm/Support/RWMutex.h"
21 #define DEBUG_TYPE "firrtl-lower-groups"
23 using namespace circt;
24 using namespace firrtl;
29 FModuleOp buildNewModule(OpBuilder &
builder, Location location,
30 Twine namehint, SmallVectorImpl<PortInfo> &ports);
33 void runOnModule(FModuleOp moduleOp);
36 void runOnOperation()
override;
51 SmallVectorImpl<PortInfo> &ports) {
52 llvm::sys::SmartScopedLock<true> instrumentationLock(*circuitMutex);
53 FModuleOp newModule =
builder.create<FModuleOp>(
54 location,
builder.getStringAttr(namehint),
57 SymbolTable::setSymbolVisibility(newModule, SymbolTable::Visibility::Private);
64 llvm::dbgs() <<
"Module: " << moduleOp.getModuleName() <<
"\n";
68 CircuitOp circuitOp = moduleOp->getParentOfType<CircuitOp>();
69 StringRef circuitName = circuitOp.getName();
74 DenseMap<InstanceOp, FModuleOp> createdInstances;
87 moduleOp.walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
88 auto group = dyn_cast<GroupOp>(op);
90 return WalkResult::advance();
93 SmallString<32> groupName(group.getGroupName().getRootReference());
94 for (
auto ref : group.getGroupName().getNestedReferences()) {
95 groupName.append(
"_");
96 groupName.append(ref.getValue());
98 LLVM_DEBUG(
llvm::dbgs() <<
" - Group: " << groupName <<
"\n");
100 Block *body = group.getBody(0);
104 SmallVector<PortInfo> ports;
107 SmallVector<Value> connectValues;
113 auto createInputPort = [&](Value operand, Location loc) {
114 auto portNum = ports.size();
117 ports.push_back({
builder.getStringAttr(
"_" + operandName.first),
122 body->addArgument(operand.getType(), loc);
123 operand.replaceUsesWithIf(body->getArgument(portNum),
124 [&](OpOperand &operand) {
125 return operand.getOwner()->getBlock() == body;
128 connectValues.push_back(operand);
134 auto getPortLoc = [&](Value port) -> Location {
136 if (
auto *destOp = port.getDefiningOp())
137 if (
auto instOp = dyn_cast<InstanceOp>(destOp)) {
138 auto modOpIt = createdInstances.find(instOp);
139 if (modOpIt != createdInstances.end()) {
140 auto portNum = port.cast<OpResult>().getResultNumber();
141 loc = modOpIt->getSecond().getPortLocation(portNum);
149 auto createOutputPort = [&](Value dest, Value src) {
150 auto loc = getPortLoc(dest);
151 auto portNum = ports.size();
155 type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType(),
158 ports.push_back({
builder.getStringAttr(
"_" + operandName.first), refType,
160 body->addArgument(refType, loc);
161 connectValues.push_back(dest);
162 OpBuilder::InsertionGuard guard(
builder);
163 builder.setInsertionPointAfterValue(src);
165 loc, body->getArgument(portNum),
166 builder.create<RefSendOp>(loc, src)->getResult(0));
169 for (
auto &op : llvm::make_early_inc_range(*body)) {
180 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
184 if (!createdInstances.contains(instOp))
187 llvm::dbgs() <<
" Found instance created from nested group:\n"
188 <<
" module: " << instOp.getModuleName() <<
"\n"
189 <<
" instance: " << instOp.getName() <<
"\n";
191 instOp->moveBefore(group);
195 if (
auto connect = dyn_cast<FConnectLike>(op)) {
196 auto srcInGroup =
connect.getSrc().getParentBlock() == body;
197 auto destInGroup =
connect.getDest().getParentBlock() == body;
198 if (!srcInGroup && !destInGroup) {
204 createInputPort(
connect.getSrc(), op.getLoc());
226 if (
auto refResolve = dyn_cast<RefResolveOp>(op))
227 if (refResolve.getResult().hasOneUse())
228 if (
auto connect = dyn_cast<StrictConnectOp>(
229 *refResolve.getResult().getUsers().begin()))
230 if (
connect.getDest().getParentBlock() != body) {
231 refResolve->moveBefore(group);
236 for (
auto operand : op.getOperands())
237 if (operand.getParentBlock() != body)
238 createInputPort(operand, op.getLoc());
242 FModuleOp newModule = buildNewModule(
builder, group.getLoc(),
243 moduleNames.lookup(group), ports);
244 SymbolTable::setSymbolVisibility(newModule,
245 SymbolTable::Visibility::Private);
246 newModule.getBody().takeBody(group.getRegion());
249 llvm::dbgs() <<
" New Module: " << moduleNames.lookup(group) <<
"\n";
251 for (
size_t i = 0, e = ports.size(); i != e; ++i) {
252 auto port = ports[i];
253 auto value = connectValues[i];
254 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
255 <<
" type: " << port.type <<
"\n"
256 <<
" direction: " << port.direction <<
"\n"
257 <<
" value: " <<
value <<
"\n";
262 builder.setInsertionPointAfter(group);
263 auto moduleName = newModule.getModuleName();
264 auto instanceOp =
builder.create<InstanceOp>(
265 group.getLoc(), newModule,
267 (Twine((
char)tolower(moduleName[0])) + moduleName.drop_front()).str(),
268 NameKindEnum::DroppableName,
269 ArrayRef<Attribute>{},
270 ArrayRef<Attribute>{},
true);
271 instanceOp->setAttr(
"output_file",
272 hw::OutputFileAttr::getFromFilename(
274 "groups_" + circuitName +
"_" + groupName +
".sv",
276 createdInstances.try_emplace(instanceOp, newModule);
279 assert(ports.size() == connectValues.size() &&
280 "the number of instance ports and values to connect to them must be "
282 for (
unsigned portNum = 0, e = newModule.getNumPorts(); portNum < e;
284 OpBuilder::InsertionGuard guard(
builder);
285 builder.setInsertionPointAfterValue(instanceOp.getResult(portNum));
287 builder.create<StrictConnectOp>(newModule.getPortLocationAttr(portNum),
288 instanceOp.getResult(portNum),
289 connectValues[portNum]);
291 builder.create<StrictConnectOp>(
292 getPortLoc(connectValues[portNum]), connectValues[portNum],
293 builder.create<RefResolveOp>(newModule.getPortLocationAttr(portNum),
294 instanceOp.getResult(portNum)));
298 return WalkResult::advance();
306 llvm::dbgs() <<
"==----- Running LowerGroups "
307 "-------------------------------------------------===\n");
308 CircuitOp circuitOp = getOperation();
311 llvm::sys::SmartMutex<true> mutex;
312 circuitMutex = &mutex;
317 circuitOp->walk([&](FModuleOp moduleOp) {
318 moduleOp->walk([&](GroupOp groupOp) {
319 SmallString<32> groupName(groupOp.getGroupName().getRootReference());
320 for (
auto ref : groupOp.getGroupName().getNestedReferences()) {
321 groupName.append(
"_");
322 groupName.append(ref.getValue());
325 {groupOp, ns.
newName(moduleOp.getModuleName() +
"_" + groupName)});
330 if (moduleNames.empty())
331 return markAllAnalysesPreserved();
334 SmallVector<FModuleOp> modules(circuitOp.getBodyBlock()->getOps<FModuleOp>());
335 llvm::parallelForEach(modules,
336 [&](FModuleOp moduleOp) { runOnModule(moduleOp); });
351 SmallVector<std::pair<GroupDeclOp, StringAttr>> groupDecls;
352 StringRef circuitName = circuitOp.getName();
353 circuitOp.walk<mlir::WalkOrder::PreOrder>([&](GroupDeclOp groupDeclOp) {
354 auto parentOp = groupDeclOp->getParentOfType<GroupDeclOp>();
355 while (parentOp && parentOp != groupDecls.back().first)
356 groupDecls.pop_back();
357 builder.setInsertionPointToStart(circuitOp.getBodyBlock());
360 SmallString<32> prefix(
"groups_");
361 prefix.append(circuitName);
363 for (
auto [group, _] : groupDecls) {
364 prefix.append(group.getSymName());
367 prefix.append(groupDeclOp.getSymName());
369 auto outputFileAttr = hw::OutputFileAttr::getFromFilename(
370 builder.getContext(), prefix +
".sv",
373 SmallString<128> includes;
374 for (
auto [_, strAttr] : groupDecls) {
375 includes.append(strAttr);
376 includes.append(
"\n");
381 .create<sv::VerbatimOp>(groupDeclOp.getLoc(), includes +
"`ifndef " +
384 ->setAttr(
"output_file", outputFileAttr);
387 builder.setInsertionPointToEnd(circuitOp.getBodyBlock());
388 builder.create<sv::VerbatimOp>(groupDeclOp.getLoc(),
"`endif // " + prefix)
389 ->setAttr(
"output_file", outputFileAttr);
391 if (!groupDeclOp.getBody().getOps<GroupDeclOp>().empty())
392 groupDecls.push_back(
394 builder.getStringAttr(
"`include \"" + prefix +
".sv\"")});
398 for (
auto groupDeclOp : llvm::make_early_inc_range(
399 circuitOp.getBodyBlock()->getOps<GroupDeclOp>()))
404 return std::make_unique<LowerGroupsPass>();
assert(baseType &&"element must be base type")
llvm::sys::SmartMutex< true > * circuitMutex
Indicates exclusive access to modify the circuitNamespace and the circuit.
void runOnModule(FModuleOp moduleOp)
Function to process each module.
void runOnOperation() override
Entry point for the function.
FModuleOp buildNewModule(OpBuilder &builder, Location location, Twine namehint, SmallVectorImpl< PortInfo > &ports)
Safely build a new module with a given namehint.
DenseMap< GroupOp, StringRef > moduleNames
A map of group definition to module name that should be created for it.
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::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
std::unique_ptr< mlir::Pass > createLowerGroupsPass()
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
mlir::raw_indented_ostream & dbgs()
The namespace of a CircuitOp, generally inhabited by modules.