48#include "llvm/ADT/MapVector.h"
49#include "llvm/ADT/TypeSwitch.h"
50#include "llvm/Support/Debug.h"
51#include "llvm/Support/Threading.h"
55#define GEN_PASS_DEF_LOWERDOMAINS
56#include "circt/Dialect/FIRRTL/Passes.h.inc"
61using namespace firrtl;
69 impl::LowerDomainsBase<LowerDomainsPass>::getArgumentName().data()
73struct AssociationInfo {
75 DistinctAttr distinctAttr;
92 std::optional<unsigned> inputPort;
100 SmallVector<AssociationInfo> associations{};
120 llvm::once_flag flag;
126 llvm::once_flag flag;
127 StringAttr domainInfoIn;
128 StringAttr domainInfoOut;
129 StringAttr associationsIn;
130 StringAttr associationsOut;
134 Constants(MLIRContext *context) : context(context) {}
137 ArrayAttr getEmptyArrayAttr() {
138 llvm::call_once(emptyArray.flag,
139 [&] { emptyArray.attr = ArrayAttr::get(context, {}); });
140 return emptyArray.attr;
145 void initClassOut() {
146 llvm::call_once(classOut.flag, [&] {
147 classOut.domainInfoIn = StringAttr::get(context,
"domainInfo_in");
148 classOut.domainInfoOut = StringAttr::get(context,
"domainInfo_out");
149 classOut.associationsIn = StringAttr::get(context,
"associations_in");
150 classOut.associationsOut = StringAttr::get(context,
"associations_out");
156 StringAttr getDomainInfoIn() {
158 return classOut.domainInfoIn;
162 StringAttr getDomainInfoOut() {
164 return classOut.domainInfoOut;
168 StringAttr getAssociationsIn() {
170 return classOut.associationsIn;
174 StringAttr getAssociationsOut() {
176 return classOut.associationsOut;
181 MLIRContext *context;
184 EmptyArray emptyArray;
197 LowerModule(FModuleLike op,
const DenseMap<Attribute, Classes> &classes,
199 : op(op), eraseVector(op.
getNumPorts()), domainToClasses(classes),
200 constants(constants), instanceGraph(instanceGraph) {}
204 LogicalResult lowerModule();
208 LogicalResult lowerInstances();
212 LogicalResult eraseDomainUsers(Value value) {
213 for (
auto *user :
llvm::make_early_inc_range(value.getUsers())) {
214 if (
auto castOp = dyn_cast<UnsafeDomainCastOp>(user)) {
215 castOp.getResult().replaceAllUsesWith(castOp.getInput());
219 return user->emitOpError()
220 <<
"has an unimplemented lowering in the LowerDomains pass.";
229 BitVector eraseVector;
233 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
236 SmallVector<std::pair<unsigned, unsigned>> resultMap;
239 const DenseMap<Attribute, Classes> &domainToClasses;
242 Constants &constants;
251LogicalResult LowerModule::lowerModule() {
257 TypeSwitch<Operation *, std::optional<Block *>>(op)
258 .Case<FModuleOp>([](
auto op) {
return op.getBodyBlock(); })
259 .Case<FExtModuleOp>([](
auto) {
return nullptr; })
261 .Default([](
auto) {
return std::nullopt; });
264 Block *body = *shouldProcess;
266 auto *context = op.getContext();
270 llvm::MapVector<unsigned, DomainInfo> indexToDomain;
274 SmallVector<Attribute> portAnnotations;
283 auto ports = op.getPorts();
284 for (
unsigned i = 0, iDel = 0, iIns = 0, e = op.getNumPorts(); i != e; ++i) {
285 auto port = cast<PortInfo>(ports[i]);
288 if (
auto domain = dyn_cast<FlatSymbolRefAttr>(port.domains)) {
292 auto [classIn, classOut] = domainToClasses.at(domain.getAttr());
295 auto builder = ImplicitLocOpBuilder::atBlockEnd(port.loc, body);
296 auto object = ObjectOp::create(
298 StringAttr::get(context, Twine(port.name) +
"_object"));
299 instanceGraph.lookup(op)->addInstance(
object,
300 instanceGraph.lookup(classOut));
301 if (port.direction == Direction::In)
302 indexToDomain[i] = {object, iIns, iIns + 1};
304 indexToDomain[i] = {object, std::nullopt, iIns};
307 if (failed(eraseDomainUsers(body->getArgument(i))))
314 if (port.direction == Direction::In) {
315 newPorts.push_back({iDel,
PortInfo(port.name, classIn.getInstanceType(),
317 portAnnotations.push_back(constants.getEmptyArrayAttr());
321 {iDel,
PortInfo(StringAttr::get(context, Twine(port.name) +
"_out"),
322 classOut.getInstanceType(), Direction::Out)});
323 portAnnotations.push_back(constants.getEmptyArrayAttr());
333 resultMap.emplace_back(iDel++, iIns++);
340 ArrayAttr domainAttr = cast<ArrayAttr>(port.domains);
341 if (domainAttr.empty()) {
342 portAnnotations.push_back(port.annotations.getArrayAttr());
346 SmallVector<Annotation> newAnnotations;
348 for (
auto indexAttr : domainAttr.getAsRange<IntegerAttr>()) {
350 id = DistinctAttr::create(UnitAttr::get(context));
351 newAnnotations.push_back(
Annotation(DictionaryAttr::getWithSorted(
352 context, {{
"class", StringAttr::get(context,
"circt.tracker")},
355 indexToDomain[indexAttr.getUInt()].associations.push_back({id, port.loc});
357 if (!newAnnotations.empty())
358 port.annotations.addAnnotations(newAnnotations);
359 portAnnotations.push_back(port.annotations.getArrayAttr());
363 op.erasePorts(eraseVector);
364 op.setDomainInfoAttr(constants.getEmptyArrayAttr());
368 op.insertPorts(newPorts);
371 for (
auto const &[_, info] : indexToDomain) {
372 auto [object, inputPort, outputPort, associations] =
info;
373 OpBuilder builder(
object);
374 builder.setInsertionPointAfter(
object);
380 auto subDomainInfoIn =
381 ObjectSubfieldOp::create(builder,
object.
getLoc(),
object, 0);
382 PropAssignOp::create(builder,
object.
getLoc(), subDomainInfoIn,
383 body->getArgument(*inputPort));
385 auto subAssociations =
386 ObjectSubfieldOp::create(builder,
object.
getLoc(),
object, 2);
388 SmallVector<Value> paths;
389 for (
auto [
id, loc] : associations) {
390 paths.push_back(PathOp::create(
391 builder, loc, TargetKindAttr::get(context, TargetKind::Reference),
394 auto list = ListCreateOp::create(
396 ListType::get(context, cast<PropertyType>(PathType::get(context))),
398 PropAssignOp::create(builder,
object.
getLoc(), subAssociations, list);
400 PropAssignOp::create(builder,
object.
getLoc(),
401 body->getArgument(outputPort),
object);
406 op.setPortAnnotationsAttr(ArrayAttr::get(context, portAnnotations));
409 llvm::dbgs() <<
" portMap:\n";
410 for (
auto [oldIndex, newIndex] : resultMap)
411 llvm::dbgs() <<
" - " << oldIndex <<
": " << newIndex <<
"\n";
417LogicalResult LowerModule::lowerInstances() {
422 if (!isa<FModuleOp, FExtModuleOp>(op))
425 auto *node = instanceGraph.lookup(cast<igraph::ModuleOpInterface>(*op));
426 for (
auto *use :
llvm::make_early_inc_range(node->uses())) {
427 auto instanceOp = dyn_cast<InstanceOp>(*use->getInstance());
429 use->getInstance().emitOpError()
430 <<
"has an unimplemented lowering in LowerDomains";
433 LLVM_DEBUG(llvm::dbgs()
434 <<
" - " << instanceOp.getInstanceName() <<
"\n");
436 for (
auto bit : eraseVector.set_bits())
437 if (failed(eraseDomainUsers(instanceOp.getResult(bit))))
440 ImplicitLocOpBuilder builder(instanceOp.getLoc(), instanceOp);
441 auto erased = instanceOp.erasePorts(builder, eraseVector);
442 auto inserted = erased.cloneAndInsertPorts(newPorts);
443 for (
auto [oldIndex, newIndex] : resultMap) {
444 auto oldPort = erased.getResult(oldIndex);
445 auto newPort = inserted.getResult(newIndex);
446 oldPort.replaceAllUsesWith(newPort);
448 instanceGraph.replaceInstance(instanceOp, inserted);
463 LowerCircuit(CircuitOp circuit,
InstanceGraph &instanceGraph,
464 llvm::Statistic &numDomains)
465 : circuit(circuit), instanceGraph(instanceGraph),
466 constants(circuit.getContext()), numDomains(numDomains) {}
469 LogicalResult lowerCircuit();
473 LogicalResult lowerDomain(DomainOp);
485 llvm::Statistic &numDomains;
489 DenseMap<Attribute, Classes> classes;
492LogicalResult LowerCircuit::lowerDomain(DomainOp op) {
493 ImplicitLocOpBuilder builder(op.getLoc(), op);
494 auto *context = op.getContext();
495 auto name = op.getNameAttr();
497 auto classIn = ClassOp::create(builder, name, {});
498 auto classInType = classIn.getInstanceType();
500 ListType::get(context, cast<PropertyType>(PathType::get(context)));
502 ClassOp::create(builder, StringAttr::get(context, Twine(name) +
"_out"),
503 {{constants.getDomainInfoIn(),
506 {constants.getDomainInfoOut(),
509 {constants.getAssociationsIn(),
512 {constants.getAssociationsOut(),
515 builder.setInsertionPointToStart(classOut.getBodyBlock());
516 PropAssignOp::create(builder, classOut.getArgument(1),
517 classOut.getArgument(0));
518 PropAssignOp::create(builder, classOut.getArgument(3),
519 classOut.getArgument(2));
520 classes.insert({name, {classIn, classOut}});
521 instanceGraph.addModule(classIn);
522 instanceGraph.addModule(classOut);
528LogicalResult LowerCircuit::lowerCircuit() {
529 LLVM_DEBUG(llvm::dbgs() <<
"Processing domains:\n");
530 for (
auto domain :
llvm::make_early_inc_range(circuit.getOps<DomainOp>())) {
531 LLVM_DEBUG(llvm::dbgs() <<
" - " << domain.getName() <<
"\n");
532 if (failed(lowerDomain(domain)))
536 LLVM_DEBUG(llvm::dbgs() <<
"Processing modules:\n");
538 auto moduleOp = dyn_cast<FModuleLike>(node.
getModule<Operation *>());
541 LLVM_DEBUG(llvm::dbgs() <<
" - module: " << moduleOp.getName() <<
"\n");
542 LowerModule lowerModule(moduleOp, classes, constants, instanceGraph);
543 if (failed(lowerModule.lowerModule()))
545 LLVM_DEBUG(llvm::dbgs() <<
" instances:\n");
546 return lowerModule.lowerInstances();
556 LowerCircuit lowerCircuit(getOperation(), getAnalysis<InstanceGraph>(),
558 if (failed(lowerCircuit.lowerCircuit()))
559 return signalPassFailure();
561 markAllAnalysesPreserved();
static Location getLoc(DefSlot slot)
#define CIRCT_DEBUG_SCOPED_PASS_LOGGER(PASS)
void runOnOperation() override
This class provides a read-only projection of an annotation.
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.
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
This holds the name and type that describes the module's ports.