26#include "mlir/IR/AsmState.h"
27#include "mlir/Interfaces/SideEffectInterfaces.h"
28#include "llvm/ADT/DenseMap.h"
29#include "llvm/ADT/DenseSet.h"
30#include "llvm/ADT/STLExtras.h"
31#include "llvm/ADT/SmallVector.h"
32#include "llvm/ADT/TinyPtrVector.h"
34#define DEBUG_TYPE "firrtl-infer-domains"
38#define GEN_PASS_DEF_INFERDOMAINS
39#include "circt/Dialect/FIRRTL/Passes.h.inc"
44using namespace firrtl;
49using mlir::InFlightDiagnostic;
67 return info.getAsRange<IntegerAttr>();
68 return cast<ArrayAttr>(info[i]).getAsRange<IntegerAttr>();
72static bool isPort(BlockArgument arg) {
73 return isa<FModuleOp>(arg.getOwner()->getParentOp());
78 auto arg = dyn_cast<BlockArgument>(value);
86 for (
auto *user : port.getUsers())
87 if (
auto connect = dyn_cast<FConnectLike>(user))
88 if (connect.getDest() == port)
95 return type_isa<FIRRTLBaseType, RefType>(type);
118struct ModuleUpdateInfo {
120 ArrayAttr portDomainInfo;
128 CircuitState(CircuitOp circuit,
InstanceGraph &instanceGraph,
130 : circuit(circuit), instanceGraph(instanceGraph),
131 innerRefNamespace(innerRefNamespace), mode(mode) {
132 processCircuit(circuit);
137 ArrayRef<DomainOp> getDomains()
const {
return domainTable; }
138 size_t getNumDomains()
const {
return domainTable.size(); }
139 DomainOp
getDomain(DomainTypeID
id)
const {
return domainTable[
id.index]; }
140 DomainTypeID getDomainTypeID(Type type) {
return typeIDTable[type]; }
142 void dirty() { asmState =
nullptr; }
143 AsmState &getAsmState() {
145 asmState = std::make_unique<AsmState>(
146 circuit, mlir::OpPrintingFlags().assumeVerified());
151 size_t getVariableID(VariableTerm *term) {
152 return variableIDTable.insert({term, variableIDTable.size() + 1})
156 DenseMap<StringAttr, ModuleUpdateInfo> &getModuleUpdateTable() {
157 return moduleUpdateTable;
162 DenseSet<Value> inserted;
165 LogicalResult runOnModule(Operation *moduleOp);
167 void processDomain(DomainOp op) {
168 auto index = domainTable.size();
169 auto domainType = DomainType::getFromDomainOp(op);
170 domainTable.push_back(op);
171 typeIDTable.insert({domainType, {index}});
174 void processCircuit(CircuitOp circuit) {
175 for (
auto decl : circuit.getOps<DomainOp>())
183 SmallVector<DomainOp> domainTable;
184 DenseMap<Type, DomainTypeID> typeIDTable;
185 DenseMap<VariableTerm *, size_t> variableIDTable;
186 std::unique_ptr<AsmState> asmState;
187 DenseMap<StringAttr, ModuleUpdateInfo> moduleUpdateTable;
207 constexpr Term(TermKind kind) : kind(kind) {}
215struct TermBase : Term {
216 static bool classof(
const Term *term) {
return term->kind == K; }
217 TermBase() : Term(K) {}
223struct VariableTerm :
public TermBase<TermKind::Variable> {
224 VariableTerm() : leader(nullptr) {}
225 VariableTerm(Term *leader) : leader(leader) {}
232struct ValueTerm :
public TermBase<TermKind::Value> {
240struct RowTerm :
public TermBase<TermKind::Row> {
241 RowTerm(ArrayRef<Term *> elements) : elements(elements) {}
242 ArrayRef<Term *> elements;
261struct PendingUpdates {
271using ExportTable = DenseMap<DomainValue, TinyPtrVector<DomainValue>>;
276 explicit ModuleState(CircuitState &globals) : globals(globals) {}
278 ArrayRef<DomainOp> getDomains() {
return globals.getDomains(); }
279 size_t getNumDomains() {
return globals.getNumDomains(); }
280 DomainOp
getDomain(DomainTypeID
id) {
return globals.getDomain(
id); }
281 DomainTypeID getDomainTypeID(Type type) {
282 return globals.getDomainTypeID(type);
284 DomainTypeID getDomainTypeID(FModuleLike module,
size_t i) {
285 return globals.getDomainTypeID(module.getPortType(i));
287 DomainTypeID getDomainTypeID(FInstanceLike op,
size_t i)
const {
288 return globals.getDomainTypeID(op->getResult(i).getType());
290 DomainTypeID getDomainTypeID(
DomainValue value)
const {
291 return globals.getDomainTypeID(value.getType());
293 auto &getModuleUpdateTable() {
return globals.getModuleUpdateTable(); }
295 mlir::AsmState &getAsmState() {
return globals.getAsmState(); }
296 void dirty() { globals.dirty(); }
298 template <
typename T>
299 void render(Operation *op, T &out);
300 template <
typename T>
301 void render(Value value, T &out);
302 template <
typename T>
303 void renderLong(Value value, T &out);
304 template <
typename T>
305 void render(Term *term, T &out);
306 template <
typename T>
308 template <
typename T>
309 Render<T> render(T &&subject);
311 RenderLong renderLong(Value value);
314 LogicalResult unify(Term *lhs, Term *rhs);
315 LogicalResult unify(VariableTerm *x, Term *y);
316 LogicalResult unify(ValueTerm *xv, Term *y);
317 LogicalResult unify(RowTerm *lhsRow, Term *rhs);
318 void solve(Term *lhs, Term *rhs);
320 [[nodiscard]] RowTerm *allocRow(
size_t size);
321 [[nodiscard]] RowTerm *allocRow(ArrayRef<Term *> elements);
322 [[nodiscard]] VariableTerm *allocVar();
323 [[nodiscard]] ValueTerm *allocVal(
DomainValue value);
324 template <
typename T,
typename... Args>
325 T *alloc(Args &&...args);
326 ArrayRef<Term *> allocArray(ArrayRef<Term *> elements);
331 void setTermForDomain(
DomainValue value, Term *term);
333 Term *getOptDomainAssociation(Value value);
334 Term *getDomainAssociation(Value value);
335 void setDomainAssociation(Value value, Term *term);
343 bool isColorless(Value value);
346 RowTerm *getDomainAssociationAsRow(Value value);
348 void noteLocation(InFlightDiagnostic &diag, Operation *op);
349 void noteDomain(InFlightDiagnostic &diag,
DomainValue domain);
350 void noteDomainSource(InFlightDiagnostic &diag,
DomainValue domain);
351 void noteDomainSource(InFlightDiagnostic &diag, Term *term);
352 void emitDomainCrossingError(Operation *op, Value lhs, Term *lhsTerm,
353 Value rhs, Term *rhsTerm);
354 template <
typename T>
355 void emitDuplicatePortDomainError(T op,
size_t i, DomainTypeID domainTypeID,
356 IntegerAttr domainPortIndexAttr1,
357 IntegerAttr domainPortIndexAttr2);
358 template <
typename T>
359 void emitDomainPortInferenceError(T op,
size_t i);
360 template <
typename T>
361 void emitAmbiguousPortDomainAssociation(
362 T op,
const llvm::TinyPtrVector<DomainValue> &exports,
363 DomainTypeID typeID,
size_t i);
364 template <
typename T>
365 void emitMissingPortDomainAssociationError(T op, DomainTypeID typeID,
368 LogicalResult unifyAssociations(Operation *op, Value lhs, Value rhs);
369 template <
typename T>
370 LogicalResult unifyAssociations(Operation *op, T &&range);
371 LogicalResult unifyAssociations(Operation *op);
373 LogicalResult processModulePorts(FModuleOp moduleOp);
374 template <
typename T>
375 LogicalResult processInstancePorts(T op);
376 FInstanceLike fixInstancePorts(FInstanceLike op,
377 const ModuleUpdateInfo &update);
378 LogicalResult processOp(FInstanceLike op);
379 LogicalResult processOp(UnsafeDomainCastOp op);
380 LogicalResult processOp(DomainDefineOp op);
381 LogicalResult processOp(WireOp op);
382 LogicalResult processOp(RWProbeOp op);
383 LogicalResult processOp(Operation *op);
384 LogicalResult processModuleBody(FModuleOp moduleOp);
385 LogicalResult processModule(FModuleOp moduleOp);
387 ExportTable initializeExportTable(FModuleOp moduleOp);
388 void ensureSolved(
Namespace &ns, DomainTypeID typeID,
size_t ip,
389 LocationAttr loc, VariableTerm *var,
390 PendingUpdates &pending);
392 DomainTypeID typeID,
size_t ip, LocationAttr loc,
393 ValueTerm *val, PendingUpdates &pending);
394 void getUpdatesForDomainAssociationOfPort(
Namespace &ns,
395 PendingUpdates &pending,
396 DomainTypeID typeID,
size_t ip,
397 LocationAttr loc, Term *term,
399 void getUpdatesForDomainAssociationOfPort(
Namespace &ns,
401 size_t ip, LocationAttr loc,
403 PendingUpdates &pending);
404 void getUpdatesForModulePorts(FModuleOp moduleOp,
const ExportTable &exports,
406 void getUpdatesForModule(FModuleOp moduleOp,
const ExportTable &exports,
407 PendingUpdates &pending);
408 void applyUpdatesToModule(FModuleOp moduleOp,
ExportTable &exports,
409 const PendingUpdates &pending);
410 SmallVector<Attribute> copyPortDomainAssociations(FModuleOp moduleOp,
411 ArrayAttr moduleDomainInfo,
413 LogicalResult driveModuleOutputDomainPorts(FModuleOp moduleOp);
414 LogicalResult updateModuleDomainInfo(FModuleOp moduleOp,
418 solveVarWithAnonDomain(OpBuilder &builder,
419 DenseMap<DomainValue, DomainValue> &domainsInScope,
420 Operation *user, DomainType type, VariableTerm *var);
422 getDomainInScope(OpBuilder &builder,
423 DenseMap<DomainValue, DomainValue> &domainsInScope,
426 updateInstance(DenseMap<DomainValue, DomainValue> &domainsInScope,
428 LogicalResult updateWire(DenseMap<DomainValue, DomainValue> &domainsInScope,
430 LogicalResult updateModuleBody(FModuleOp moduleOp);
431 LogicalResult updateModule(FModuleOp moduleOp);
433 LogicalResult checkModulePorts(FModuleLike moduleOp);
434 LogicalResult checkModuleDomainPortDrivers(FModuleOp moduleOp);
435 LogicalResult checkInstanceDomainPortDrivers(FInstanceLike op);
436 LogicalResult checkModuleBody(FModuleOp moduleOp);
438 LogicalResult inferModule(FModuleOp moduleOp);
439 LogicalResult checkModule(FModuleOp moduleOp);
440 LogicalResult checkModule(FExtModuleOp extModuleOp);
441 LogicalResult checkAndInferModule(FModuleOp moduleOp);
444 CircuitState &globals;
445 DenseMap<Value, Term *> termTable;
446 DenseMap<Value, Term *> associationTable;
448 DenseMap<Value, bool> colorlessTable;
449 llvm::BumpPtrAllocator allocator;
454void ModuleState::render(Operation *op, T &out) {
455 op->print(out, getAsmState());
459void ModuleState::render(Value value, T &out) {
467 llvm::raw_string_ostream os(name);
468 value.printAsOperand(os, globals.getAsmState());
474void ModuleState::renderLong(Value value, T &out) {
475 if (
auto arg = dyn_cast<BlockArgument>(value)) {
476 if (
auto moduleOp = llvm::dyn_cast_if_present<FModuleLike>(
477 arg.getOwner()->getParentOp())) {
479 moduleOp.getPortDirection(arg.getArgNumber()));
480 out <<
" module port ";
482 }
else if (
auto result = dyn_cast<OpResult>(value)) {
483 auto *op = result.getOwner();
484 if (
auto inst = dyn_cast<FInstanceLike>(op)) {
486 inst.getPortDirection(result.getResultNumber()));
487 out <<
" instance port ";
496void ModuleState::render(Term *term, T &out) {
502 if (
auto *var = dyn_cast<VariableTerm>(term)) {
503 out <<
"?" << globals.getVariableID(var);
506 if (
auto *val = dyn_cast<ValueTerm>(term)) {
507 auto value = val->value;
511 if (
auto *row = dyn_cast<RowTerm>(term)) {
513 llvm::interleaveComma(
514 llvm::seq(
size_t(0), getNumDomains()), out, [&](
auto i) {
515 render(row->elements[i], out);
516 out <<
" : " <<
getDomain(DomainTypeID{i}).getSymName();
525struct ModuleState::Render {
531ModuleState::Render<T> ModuleState::render(T &&subject) {
532 return Render<T>{
this, std::forward<T>(subject)};
537 ModuleState::Render<T> r) {
538 r.state->render(r.subject, out);
557Term *ModuleState::find(Term *x) {
561 if (
auto *var = dyn_cast<VariableTerm>(x)) {
562 if (var->leader ==
nullptr)
565 auto *leader = find(var->leader);
566 if (leader != var->leader)
567 var->leader = leader;
574LogicalResult ModuleState::unify(VariableTerm *x, Term *y) {
580LogicalResult ModuleState::unify(ValueTerm *xv, Term *y) {
581 if (
auto *yv = dyn_cast<VariableTerm>(y)) {
586 if (
auto *yv = dyn_cast<ValueTerm>(y))
587 return success(xv == yv);
593LogicalResult ModuleState::unify(RowTerm *lhsRow, Term *rhs) {
594 if (
auto *rhsVar = dyn_cast<VariableTerm>(rhs)) {
595 rhsVar->leader = lhsRow;
598 if (
auto *rhsRow = dyn_cast<RowTerm>(rhs)) {
599 for (
auto [x, y] :
llvm::zip_equal(lhsRow->elements, rhsRow->elements))
600 if (failed(unify(x, y)))
608LogicalResult ModuleState::unify(Term *lhs, Term *rhs) {
616 LLVM_DEBUG(llvm::dbgs().indent(6)
617 <<
"unify " << render(lhs) <<
" = " << render(rhs) <<
"\n");
619 if (
auto *lhsVar = dyn_cast<VariableTerm>(lhs))
620 return unify(lhsVar, rhs);
621 if (
auto *lhsVal = dyn_cast<ValueTerm>(lhs))
622 return unify(lhsVal, rhs);
623 if (
auto *lhsRow = dyn_cast<RowTerm>(lhs))
624 return unify(lhsRow, rhs);
628void ModuleState::solve(Term *lhs, Term *rhs) {
629 [[maybe_unused]]
auto result = unify(lhs, rhs);
630 assert(result.succeeded());
633RowTerm *ModuleState::allocRow(
size_t size) {
634 SmallVector<Term *> elements;
635 elements.resize(size);
636 return allocRow(elements);
639RowTerm *ModuleState::allocRow(ArrayRef<Term *> elements) {
640 auto ds = allocArray(elements);
641 return alloc<RowTerm>(ds);
644VariableTerm *ModuleState::allocVar() {
return alloc<VariableTerm>(); }
646ValueTerm *ModuleState::allocVal(
DomainValue value) {
647 return alloc<ValueTerm>(value);
650template <
typename T,
typename... Args>
651T *ModuleState::alloc(Args &&...args) {
652 static_assert(std::is_base_of_v<Term, T>,
"T must be a term");
653 return new (allocator) T(std::forward<Args>(args)...);
656ArrayRef<Term *> ModuleState::allocArray(ArrayRef<Term *> elements) {
657 auto size = elements.size();
661 auto *result = allocator.Allocate<Term *>(size);
662 llvm::uninitialized_copy(elements, result);
663 for (
size_t i = 0; i < size; ++i)
665 result[i] = alloc<VariableTerm>();
667 return ArrayRef(result, size);
671 auto *term = getOptTermForDomain(value);
672 if (
auto *val = llvm::dyn_cast_if_present<ValueTerm>(term))
677Term *ModuleState::getOptTermForDomain(
DomainValue value) {
678 assert(isa<DomainType>(value.getType()));
679 auto it = termTable.find(value);
680 if (it == termTable.end())
682 return find(it->second);
685Term *ModuleState::getTermForDomain(
DomainValue value) {
686 assert(isa<DomainType>(value.getType()));
687 if (
auto *term = getOptTermForDomain(value))
689 auto *term = allocVar();
690 setTermForDomain(value, term);
694void ModuleState::setTermForDomain(
DomainValue value, Term *term) {
696 assert(!termTable.contains(value));
697 termTable.insert({value, term});
698 LLVM_DEBUG(llvm::dbgs().indent(6)
699 <<
"set " << render(value) <<
" := " << render(term) <<
"\n");
702Term *ModuleState::getOptDomainAssociation(Value value) {
704 auto it = associationTable.find(value);
705 if (it == associationTable.end())
707 return find(it->second);
710Term *ModuleState::getDomainAssociation(Value value) {
711 auto *term = getOptDomainAssociation(value);
716void ModuleState::setDomainAssociation(Value value, Term *term) {
720 associationTable.insert({value, term});
722 llvm::dbgs().indent(6) <<
"set domains(" << render(value)
723 <<
") := " << render(term) <<
"\n";
727bool ModuleState::isColorless(Value value) {
734 if (
auto it = colorlessTable.find(value); it != colorlessTable.end())
747 enum class Kind { Colorless, Colored, LookThrough };
748 auto classify = [&](Value v) -> Kind {
750 return Kind::Colorless;
752 auto *op = v.getDefiningOp();
755 return Kind::Colored;
758 if (op->hasTrait<OpTrait::ConstantLike>())
759 return Kind::Colorless;
763 return Kind::LookThrough;
768 if (
auto castOp = dyn_cast<UnsafeDomainCastOp>(op)) {
769 if (!castOp.getDomains().empty())
770 return Kind::Colored;
771 return Kind::LookThrough;
785 if (op->getNumOperands() == 0)
786 return Kind::Colored;
787 for (
auto operand : op->getOperands())
789 return Kind::Colored;
790 return Kind::LookThrough;
795 return Kind::Colored;
820 SmallVector<Frame> stack;
823 switch (classify(value)) {
825 return colorlessTable[value] =
false;
826 case Kind::Colorless:
827 return colorlessTable[value] =
true;
828 case Kind::LookThrough:
829 stack.push_back({value});
834 while (!stack.empty()) {
835 auto &frame = stack.back();
836 auto *op = frame.value.getDefiningOp();
837 bool colored =
false, pushed =
false;
839 while (frame.index < op->getNumOperands()) {
840 Value child = op->getOperand(frame.index);
843 if (
auto it = colorlessTable.find(child); it != colorlessTable.end()) {
855 switch (classify(child)) {
857 colorlessTable[child] =
false;
860 case Kind::Colorless:
861 colorlessTable[child] =
true;
864 case Kind::LookThrough:
865 stack.push_back({child});
879 colorlessTable[frame.value] = !colored;
883 return colorlessTable[value];
886void ModuleState::processDomainDefinition(
DomainValue domain) {
887 assert(isa<DomainType>(domain.getType()));
888 auto *newTerm = allocVal(domain);
889 auto *oldTerm = getOptTermForDomain(domain);
891 setTermForDomain(domain, newTerm);
895 [[maybe_unused]]
auto result = unify(oldTerm, newTerm);
896 assert(result.succeeded());
899RowTerm *ModuleState::getDomainAssociationAsRow(Value value) {
901 auto *term = getOptDomainAssociation(value);
905 auto *row = allocRow(getNumDomains());
906 setDomainAssociation(value, row);
911 if (
auto *row = dyn_cast<RowTerm>(term))
915 if (
auto *var = dyn_cast<VariableTerm>(term)) {
916 auto *row = allocRow(getNumDomains());
921 assert(
false &&
"unhandled term type");
925void ModuleState::noteLocation(InFlightDiagnostic &diag, Operation *op) {
926 auto ¬e = diag.attachNote(op->getLoc());
927 if (
auto mod = dyn_cast<FModuleOp>(op)) {
928 note <<
"in module " << mod.getModuleNameAttr();
931 if (
auto mod = dyn_cast<FExtModuleOp>(op)) {
932 note <<
"in extmodule " << mod.getModuleNameAttr();
935 if (
auto inst = dyn_cast<InstanceOp>(op)) {
936 note <<
"in instance " << inst.getInstanceNameAttr();
939 if (
auto inst = dyn_cast<InstanceChoiceOp>(op)) {
940 note <<
"in instance_choice " << inst.getNameAttr();
947void ModuleState::noteDomain(InFlightDiagnostic &diag,
DomainValue domain) {
948 auto ¬e = diag.attachNote(domain.getLoc());
949 note << renderLong(domain);
951 if (globals.inserted.contains(domain)) {
952 note <<
" automatically inserted here";
956 note <<
" declared here";
959void ModuleState::noteDomainSource(InFlightDiagnostic &diag,
961 auto &irns = globals.getInnerRefNamespace();
962 SmallVector<FInstanceLike> stack;
963 llvm::SmallDenseSet<DomainValue> seen;
967 auto chaseConnect = [&]() {
968 for (
auto *user : domain.getUsers()) {
969 if (
auto defineOp = dyn_cast<DomainDefineOp>(user)) {
970 if (defineOp.getDest() != domain)
972 auto src = defineOp.getSrc();
973 diag.attachNote(defineOp.getLoc())
974 << renderLong(domain) <<
" aliases " << renderLong(src);
975 domain = defineOp.getSrc();
982 auto chaseModulePort = [&]() {
983 auto arg = dyn_cast<BlockArgument>(domain);
988 llvm::dyn_cast_if_present<FModuleOp>(arg.getOwner()->getParentOp());
992 auto name =
module.getModuleNameAttr();
993 while (!stack.empty()) {
994 auto instance = stack.back();
996 auto referenced = instance.getReferencedModuleNamesAttr().getValue();
997 if (llvm::is_contained(referenced, name)) {
998 domain = cast<DomainValue>(instance->getResult(arg.getArgNumber()));
1005 auto chaseInstancePort = [&]() {
1006 auto result = dyn_cast<OpResult>(domain);
1010 auto inst = dyn_cast<FInstanceLike>(result.getOwner());
1014 auto index = result.getResultNumber();
1015 if (inst.getPortDirection(index) == Direction::In)
1018 auto names = inst.getReferencedModuleNamesAttr().getAsRange<StringAttr>();
1019 for (
auto name : names) {
1020 auto moduleLike = cast<FModuleLike>(irns.symTable.lookup(name));
1021 if (
auto moduleOp = dyn_cast<FModuleOp>(moduleLike.getOperation())) {
1022 stack.push_back(inst);
1023 domain = cast<DomainValue>(moduleOp.getArgument(index));
1030 auto chaseUnderlying = [&]() {
1031 if (
auto *term = getOptTermForDomain(domain)) {
1032 if (
auto *val = dyn_cast<ValueTerm>(term)) {
1033 if (domain != val->value) {
1034 diag.attachNote(domain.getLoc())
1035 << renderLong(domain) <<
" aliases " << renderLong(val->value);
1036 domain = val->value;
1045 auto [it, inserted] = seen.insert(domain);
1049 noteDomain(diag, domain);
1050 chaseConnect() || chaseModulePort() || chaseInstancePort() ||
1055void ModuleState::noteDomainSource(InFlightDiagnostic &diag, Term *term) {
1056 auto *val = dyn_cast<ValueTerm>(find(term));
1060 noteDomainSource(diag, val->value);
1063void ModuleState::emitDomainCrossingError(Operation *op, Value lhs,
1064 Term *lhsTerm, Value rhs,
1066 auto *lhsRow = cast<RowTerm>(lhsTerm);
1067 auto *rhsRow = cast<RowTerm>(rhsTerm);
1069 op->emitError(
"illegal domain crossing in operation between operands ");
1073 auto ¬e1 = diag.attachNote(lhs.getLoc());
1075 note1 <<
" has domains ";
1076 render(lhsRow, note1);
1077 auto ¬e2 = diag.attachNote(rhs.getLoc());
1079 note2 <<
" has domains ";
1080 render(rhsRow, note2);
1082 for (
size_t i = 0, e = getNumDomains(); i < e; ++i) {
1083 auto *lhsDomain = find(lhsRow->elements[i]);
1084 auto *rhsDomain = find(rhsRow->elements[i]);
1085 if (lhsDomain == rhsDomain)
1088 noteDomainSource(diag, lhsDomain);
1089 noteDomainSource(diag, rhsDomain);
1093template <
typename T>
1094void ModuleState::emitDuplicatePortDomainError(
1095 T op,
size_t i, DomainTypeID domainTypeID, IntegerAttr domainPortIndexAttr1,
1096 IntegerAttr domainPortIndexAttr2) {
1097 auto portName = op.getPortNameAttr(i);
1098 auto portLoc = op.getPortLocation(i);
1099 auto domainDecl =
getDomain(domainTypeID);
1100 auto domainName = domainDecl.getNameAttr();
1101 auto domainPortIndex1 = domainPortIndexAttr1.getUInt();
1102 auto domainPortIndex2 = domainPortIndexAttr2.getUInt();
1103 auto domainPortName1 = op.getPortNameAttr(domainPortIndex1);
1104 auto domainPortName2 = op.getPortNameAttr(domainPortIndex2);
1105 auto domainPortLoc1 = op.getPortLocation(domainPortIndex1);
1106 auto domainPortLoc2 = op.getPortLocation(domainPortIndex2);
1107 auto diag = emitError(portLoc);
1108 diag <<
"duplicate " << domainName <<
" association for port " << portName;
1109 auto ¬e1 = diag.attachNote(domainPortLoc1);
1110 note1 <<
"associated with " << domainName <<
" port " << domainPortName1;
1111 auto ¬e2 = diag.attachNote(domainPortLoc2);
1112 note2 <<
"associated with " << domainName <<
" port " << domainPortName2;
1113 noteLocation(diag, op);
1118template <
typename T>
1119void ModuleState::emitDomainPortInferenceError(T op,
size_t i) {
1120 auto name = op.getPortNameAttr(i);
1121 auto diag = emitError(op->getLoc());
1122 auto info = op.getDomainInfo();
1123 diag <<
"unable to infer value for undriven domain port " << name;
1124 for (
size_t j = 0, e = op.getNumPorts(); j < e; ++j) {
1125 if (
auto assocs = dyn_cast<ArrayAttr>(info[j])) {
1126 for (
auto assoc : assocs) {
1127 if (i == cast<IntegerAttr>(assoc).getValue()) {
1128 auto name = op.getPortNameAttr(j);
1129 auto loc = op.getPortLocation(j);
1130 diag.attachNote(loc) <<
"associated with hardware port " << name;
1136 noteLocation(diag, op);
1139template <
typename T>
1140void ModuleState::emitAmbiguousPortDomainAssociation(
1141 T op,
const llvm::TinyPtrVector<DomainValue> &exports, DomainTypeID typeID,
1143 auto portName = op.getPortNameAttr(i);
1144 auto portLoc = op.getPortLocation(i);
1146 auto domainName = domainDecl.getNameAttr();
1147 auto diag = emitError(portLoc) <<
"ambiguous " << domainName
1148 <<
" association for port " << portName;
1149 for (
auto e : exports) {
1150 auto arg = cast<BlockArgument>(e);
1151 auto name = op.getPortNameAttr(arg.getArgNumber());
1152 auto loc = op.getPortLocation(arg.getArgNumber());
1153 diag.attachNote(loc) <<
"candidate association " << name;
1155 noteLocation(diag, op);
1158template <
typename T>
1159void ModuleState::emitMissingPortDomainAssociationError(T op,
1160 DomainTypeID typeID,
1162 auto domainName =
getDomain(typeID).getNameAttr();
1163 auto portName = op.getPortNameAttr(i);
1164 auto diag = emitError(op.getPortLocation(i))
1165 <<
"missing " << domainName <<
" association for port "
1167 noteLocation(diag, op);
1170LogicalResult ModuleState::unifyAssociations(Operation *op, Value lhs,
1183 if (isColorless(lhs) || isColorless(rhs))
1187 llvm::dbgs().indent(6) <<
"unify domains(" << render(lhs) <<
") = domains("
1188 << render(rhs) <<
")\n";
1191 auto *lhsTerm = getOptDomainAssociation(lhs);
1192 auto *rhsTerm = getOptDomainAssociation(rhs);
1196 if (failed(unify(lhsTerm, rhsTerm))) {
1197 emitDomainCrossingError(op, lhs, lhsTerm, rhs, rhsTerm);
1202 setDomainAssociation(rhs, lhsTerm);
1207 setDomainAssociation(lhs, rhsTerm);
1211 auto *var = allocVar();
1212 setDomainAssociation(lhs, var);
1213 setDomainAssociation(rhs, var);
1217template <
typename T>
1218LogicalResult ModuleState::unifyAssociations(Operation *op, T &&range) {
1220 for (
auto rhs : std::forward<T>(range)) {
1223 if (failed(unifyAssociations(op, lhs, rhs)))
1231LogicalResult ModuleState::unifyAssociations(Operation *op) {
1232 return unifyAssociations(
1233 op, llvm::concat<Value>(op->getOperands(), op->getResults()));
1236LogicalResult ModuleState::processModulePorts(FModuleOp moduleOp) {
1237 auto numDomains = getNumDomains();
1238 auto domainInfo = moduleOp.getDomainInfoAttr();
1239 auto numPorts = moduleOp.getNumPorts();
1241 DenseMap<unsigned, DomainTypeID> domainTypeIDTable;
1242 for (
size_t i = 0; i < numPorts; ++i) {
1243 auto port = dyn_cast<DomainValue>(moduleOp.getArgument(i));
1247 LLVM_DEBUG(llvm::dbgs().indent(4)
1248 <<
"process port " << render(port) <<
"\n");
1250 if (moduleOp.getPortDirection(i) == Direction::In)
1251 processDomainDefinition(port);
1253 domainTypeIDTable[i] = getDomainTypeID(moduleOp, i);
1256 for (
size_t i = 0; i < numPorts; ++i) {
1257 BlockArgument port = moduleOp.getArgument(i);
1261 LLVM_DEBUG(llvm::dbgs().indent(4)
1262 <<
"process port " << render(port) <<
"\n");
1264 SmallVector<IntegerAttr> associations(numDomains);
1266 auto domainTypeID = domainTypeIDTable.at(domainPortIndex.getUInt());
1267 auto prevDomainPortIndex = associations[domainTypeID.index];
1268 if (prevDomainPortIndex) {
1269 emitDuplicatePortDomainError(moduleOp, i, domainTypeID,
1270 prevDomainPortIndex, domainPortIndex);
1273 associations[domainTypeID.index] = domainPortIndex;
1276 SmallVector<Term *> elements(numDomains);
1277 for (
size_t domainTypeIndex = 0; domainTypeIndex < numDomains;
1278 ++domainTypeIndex) {
1279 auto domainPortIndex = associations[domainTypeIndex];
1280 if (!domainPortIndex)
1282 auto domainPortValue =
1283 cast<DomainValue>(moduleOp.getArgument(domainPortIndex.getUInt()));
1284 elements[domainTypeIndex] = getTermForDomain(domainPortValue);
1287 auto *domainAssociations = allocRow(elements);
1288 setDomainAssociation(port, domainAssociations);
1294template <
typename T>
1295LogicalResult ModuleState::processInstancePorts(T op) {
1296 auto numDomains = getNumDomains();
1297 auto domainInfo = op.getDomainInfoAttr();
1298 auto numPorts = op.getNumPorts();
1300 DenseMap<unsigned, DomainTypeID> domainTypeIDTable;
1301 for (
size_t i = 0; i < numPorts; ++i) {
1302 auto port = dyn_cast<DomainValue>(op->getResult(i));
1306 if (op.getPortDirection(i) == Direction::Out)
1307 processDomainDefinition(port);
1309 domainTypeIDTable[i] = getDomainTypeID(op, i);
1312 for (
size_t i = 0; i < numPorts; ++i) {
1313 Value port = op->getResult(i);
1317 SmallVector<IntegerAttr> associations(numDomains);
1319 auto domainTypeID = domainTypeIDTable.at(domainPortIndex.getUInt());
1320 auto prevDomainPortIndex = associations[domainTypeID.index];
1321 if (prevDomainPortIndex) {
1322 emitDuplicatePortDomainError(op, i, domainTypeID, prevDomainPortIndex,
1326 associations[domainTypeID.index] = domainPortIndex;
1329 SmallVector<Term *> elements(numDomains);
1330 for (
size_t domainTypeIndex = 0; domainTypeIndex < numDomains;
1331 ++domainTypeIndex) {
1332 auto domainPortIndex = associations[domainTypeIndex];
1333 if (!domainPortIndex)
1335 auto domainPortValue =
1336 cast<DomainValue>(op->getResult(domainPortIndex.getUInt()));
1337 elements[domainTypeIndex] = getTermForDomain(domainPortValue);
1340 auto *domainAssociations = allocRow(elements);
1341 setDomainAssociation(port, domainAssociations);
1347FInstanceLike ModuleState::fixInstancePorts(FInstanceLike op,
1348 const ModuleUpdateInfo &update) {
1349 auto clone = op.cloneWithInsertedPortsAndReplaceUses(update.portInsertions);
1350 clone.setDomainInfoAttr(update.portDomainInfo);
1353 LLVM_DEBUG(llvm::dbgs().indent(6) <<
"fixup " << render(clone) <<
"\n");
1357LogicalResult ModuleState::processOp(FInstanceLike op) {
1359 cast<StringAttr>(cast<ArrayAttr>(op.getReferencedModuleNamesAttr())[0]);
1360 auto updateTable = getModuleUpdateTable();
1361 auto lookup = updateTable.find(moduleName);
1362 if (lookup != updateTable.end())
1363 op = fixInstancePorts(op, lookup->second);
1364 return processInstancePorts(op);
1367LogicalResult ModuleState::processOp(UnsafeDomainCastOp op) {
1368 auto domains = op.getDomains();
1369 if (domains.empty())
1370 return unifyAssociations(op, op.getInput(), op.getResult());
1372 auto input = op.getInput();
1374 SmallVector<Term *> elements(getNumDomains());
1375 if (
isHardware(input) && !isColorless(input)) {
1376 auto *inputRow = getDomainAssociationAsRow(input);
1377 elements.assign(inputRow->elements);
1380 for (
auto value : op.getDomains()) {
1381 auto domain = cast<DomainValue>(value);
1382 auto typeID = getDomainTypeID(domain);
1383 elements[typeID.index] = getTermForDomain(domain);
1386 auto *row = allocRow(elements);
1387 setDomainAssociation(op.getResult(), row);
1391LogicalResult ModuleState::processOp(DomainDefineOp op) {
1392 auto src = op.getSrc();
1393 auto dst = op.getDest();
1395 auto *srcTerm = getTermForDomain(src);
1396 auto *dstTerm = getTermForDomain(dst);
1397 if (succeeded(unify(dstTerm, srcTerm)))
1402 <<
"defines a domain value that was inferred to be a different domain '";
1403 render(dstTerm, diag);
1409LogicalResult ModuleState::processOp(WireOp op) {
1416 if (op.getDomains().empty())
1417 return unifyAssociations(op, op.getResults());
1421 SmallVector<Term *> elements(getNumDomains());
1422 for (
auto domain : op.getDomains()) {
1423 auto domainValue = cast<DomainValue>(domain);
1424 auto typeID = getDomainTypeID(domainValue);
1425 elements[typeID.index] = getTermForDomain(domainValue);
1428 auto *row = allocRow(elements);
1429 for (
auto result : op.getResults())
1430 setDomainAssociation(result, row);
1435LogicalResult ModuleState::processOp(RWProbeOp op) {
1436 auto target = globals.getInnerRefNamespace().lookup(op.getTarget());
1438 if (target.isPort()) {
1439 auto targetOp = cast<FModuleOp>(target.getOp());
1440 auto targetValue = targetOp.getArgument(target.getPort());
1441 return unifyAssociations(op, targetValue, op.getResult());
1444 auto targetOp = cast<hw::InnerSymbolOpInterface>(target.getOp());
1445 auto targetValue = targetOp.getTargetResult();
1446 return unifyAssociations(op, targetValue, op.getResult());
1449LogicalResult ModuleState::processOp(Operation *op) {
1450 LLVM_DEBUG(llvm::dbgs().indent(4) <<
"process " << render(op) <<
"\n");
1451 if (
auto instance = dyn_cast<FInstanceLike>(op))
1452 return processOp(instance);
1453 if (
auto wireOp = dyn_cast<WireOp>(op))
1454 return processOp(wireOp);
1455 if (
auto cast = dyn_cast<UnsafeDomainCastOp>(op))
1456 return processOp(cast);
1457 if (
auto def = dyn_cast<DomainDefineOp>(op))
1458 return processOp(def);
1459 if (
auto probe = dyn_cast<RWProbeOp>(op))
1460 return processOp(probe);
1461 if (
auto create = dyn_cast<DomainCreateOp>(op)) {
1462 processDomainDefinition(create);
1465 if (
auto createAnon = dyn_cast<DomainCreateAnonOp>(op)) {
1466 processDomainDefinition(createAnon);
1470 return unifyAssociations(op);
1473LogicalResult ModuleState::processModuleBody(FModuleOp moduleOp) {
1476 .walk([&](Operation *op) -> WalkResult { return processOp(op); })
1480LogicalResult ModuleState::processModule(FModuleOp moduleOp) {
1481 LLVM_DEBUG(llvm::dbgs().indent(2) <<
"processing:\n");
1482 if (failed(processModulePorts(moduleOp)))
1484 if (failed(processModuleBody(moduleOp)))
1489ExportTable ModuleState::initializeExportTable(FModuleOp moduleOp) {
1491 size_t numPorts = moduleOp.getNumPorts();
1492 for (
size_t i = 0; i < numPorts; ++i) {
1493 auto port = dyn_cast<DomainValue>(moduleOp.getArgument(i));
1496 auto value = getOptUnderlyingDomain(port);
1498 exports[value].push_back(port);
1502 llvm::dbgs().indent(2) <<
"domain exports:\n";
1503 for (
auto entry : exports) {
1504 llvm::dbgs().indent(4) << render(entry.first) <<
" exported as ";
1505 llvm::interleaveComma(entry.second, llvm::dbgs(),
1506 [&](
auto e) { llvm::dbgs() << render(e); });
1507 llvm::dbgs() <<
"\n";
1514void ModuleState::ensureSolved(
Namespace &ns, DomainTypeID typeID,
size_t ip,
1515 LocationAttr loc, VariableTerm *var,
1516 PendingUpdates &pending) {
1517 if (pending.solutions.contains(var))
1520 auto *
context = loc.getContext();
1522 auto domainName = domainDecl.getNameAttr();
1524 auto portName = StringAttr::get(
context, ns.
newName(domainName.getValue()));
1525 auto portType = DomainType::getFromDomainOp(domainDecl);
1526 auto portDirection = Direction::In;
1527 auto portSym = StringAttr();
1529 auto portAnnos = std::nullopt;
1531 auto portDomainInfo = ArrayAttr::get(
context, {});
1532 PortInfo portInfo(portName, portType, portDirection, portSym, portLoc,
1533 portAnnos, portDomainInfo);
1535 pending.solutions[var] = pending.insertions.size() + ip;
1536 pending.insertions.push_back({ip, portInfo});
1540 DomainTypeID typeID,
size_t ip,
1541 LocationAttr loc, ValueTerm *val,
1542 PendingUpdates &pending) {
1543 auto value = val->value;
1544 assert(isa<DomainType>(value.getType()));
1545 if (
isPort(value) || exports.contains(value) ||
1546 pending.exports.contains(value))
1549 auto *
context = loc.getContext();
1552 auto domainName = domainDecl.getNameAttr();
1554 auto portName = StringAttr::get(
context, ns.
newName(domainName.getValue()));
1555 auto portType = DomainType::getFromDomainOp(domainDecl);
1556 auto portDirection = Direction::Out;
1557 auto portSym = StringAttr();
1558 auto portAnnos = std::nullopt;
1560 auto portDomainInfo = ArrayAttr::get(
context, {});
1561 PortInfo portInfo(portName, portType, portDirection, portSym, loc, portAnnos,
1563 pending.exports[value] = pending.insertions.size() + ip;
1564 pending.insertions.push_back({ip, portInfo});
1567void ModuleState::getUpdatesForDomainAssociationOfPort(
1568 Namespace &ns, PendingUpdates &pending, DomainTypeID typeID,
size_t ip,
1569 LocationAttr loc, Term *term,
const ExportTable &exports) {
1570 if (
auto *var = dyn_cast<VariableTerm>(term)) {
1571 ensureSolved(ns, typeID, ip, loc, var, pending);
1574 if (
auto *val = dyn_cast<ValueTerm>(term)) {
1575 ensureExported(ns, exports, typeID, ip, loc, val, pending);
1578 llvm_unreachable(
"invalid domain association");
1581void ModuleState::getUpdatesForDomainAssociationOfPort(
1583 RowTerm *row, PendingUpdates &pending) {
1584 for (
auto [index, term] :
llvm::enumerate(row->elements))
1585 getUpdatesForDomainAssociationOfPort(ns, pending, DomainTypeID{index}, ip,
1586 loc, find(term), exports);
1589void ModuleState::getUpdatesForModulePorts(FModuleOp moduleOp,
1592 PendingUpdates &pending) {
1593 for (
size_t i = 0, e = moduleOp.getNumPorts(); i < e; ++i) {
1594 auto port = moduleOp.getArgument(i);
1598 getUpdatesForDomainAssociationOfPort(
1599 ns, exports, i, moduleOp.getPortLocation(i),
1600 getDomainAssociationAsRow(port), pending);
1604void ModuleState::getUpdatesForModule(FModuleOp moduleOp,
1606 PendingUpdates &pending) {
1608 auto names = moduleOp.getPortNamesAttr();
1609 for (
auto name : names.getAsRange<StringAttr>())
1611 getUpdatesForModulePorts(moduleOp, exports, ns, pending);
1614void ModuleState::applyUpdatesToModule(FModuleOp moduleOp,
ExportTable &exports,
1615 const PendingUpdates &pending) {
1616 LLVM_DEBUG(llvm::dbgs().indent(2) <<
"applying updates:\n");
1618 moduleOp.insertPorts(pending.insertions);
1622 for (
auto [var, portIndex] : pending.solutions) {
1623 auto portValue = cast<DomainValue>(moduleOp.getArgument(portIndex));
1624 auto *solution = allocVal(portValue);
1625 LLVM_DEBUG(llvm::dbgs().indent(4)
1626 <<
"new-input " << render(portValue) <<
"\n");
1627 solve(var, solution);
1628 exports[portValue].push_back(portValue);
1629 globals.inserted.insert(portValue);
1633 auto builder = OpBuilder::atBlockEnd(moduleOp.getBodyBlock());
1634 for (
auto [domainValue, portIndex] : pending.exports) {
1635 auto portValue = cast<DomainValue>(moduleOp.getArgument(portIndex));
1636 builder.setInsertionPointAfterValue(domainValue);
1637 DomainDefineOp::create(builder, portValue.getLoc(), portValue, domainValue);
1638 LLVM_DEBUG(llvm::dbgs().indent(4) <<
"new-output " << render(portValue)
1639 <<
" := " << render(domainValue) <<
"\n");
1640 exports[domainValue].push_back(portValue);
1641 globals.inserted.insert(portValue);
1642 setTermForDomain(portValue, allocVal(domainValue));
1646SmallVector<Attribute> ModuleState::copyPortDomainAssociations(
1647 FModuleOp moduleOp, ArrayAttr moduleDomainInfo,
size_t portIndex) {
1648 SmallVector<Attribute> result(getNumDomains());
1650 for (
auto domainPortIndexAttr : oldAssociations) {
1651 auto domainPortIndex = domainPortIndexAttr.getUInt();
1652 auto domainTypeID = getDomainTypeID(moduleOp, domainPortIndex);
1653 result[domainTypeID.index] = domainPortIndexAttr;
1658LogicalResult ModuleState::driveModuleOutputDomainPorts(FModuleOp moduleOp) {
1659 auto builder = OpBuilder::atBlockEnd(moduleOp.getBodyBlock());
1660 for (
size_t i = 0, e = moduleOp.getNumPorts(); i < e; ++i) {
1661 auto port = dyn_cast<DomainValue>(moduleOp.getArgument(i));
1662 if (!port || moduleOp.getPortDirection(i) == Direction::In ||
1666 auto *term = getOptTermForDomain(port);
1667 auto *val = llvm::dyn_cast_if_present<ValueTerm>(term);
1669 emitDomainPortInferenceError(moduleOp, i);
1673 auto loc = port.getLoc();
1674 auto value = val->value;
1675 LLVM_DEBUG(llvm::dbgs().indent(4) <<
"connect " << render(port)
1676 <<
" := " << render(value) <<
"\n");
1677 DomainDefineOp::create(builder, loc, port, value);
1683LogicalResult ModuleState::updateModuleDomainInfo(
1684 FModuleOp moduleOp,
const ExportTable &exportTable, ArrayAttr &result) {
1689 auto *
context = moduleOp.getContext();
1690 auto numDomains = getNumDomains();
1691 auto oldModuleDomainInfo = moduleOp.getDomainInfoAttr();
1692 auto numPorts = moduleOp.getNumPorts();
1693 SmallVector<Attribute> newModuleDomainInfo(numPorts);
1695 for (
size_t i = 0; i < numPorts; ++i) {
1696 auto port = moduleOp.getArgument(i);
1697 auto type = port.getType();
1699 if (isa<DomainType>(type)) {
1701 newModuleDomainInfo[i] = ArrayAttr::get(
context, {});
1706 newModuleDomainInfo[i] = ArrayAttr::get(
context, {});
1711 copyPortDomainAssociations(moduleOp, oldModuleDomainInfo, i);
1712 auto *row = cast<RowTerm>(getDomainAssociation(port));
1713 for (
size_t domainIndex = 0; domainIndex < numDomains; ++domainIndex) {
1714 auto domainTypeID = DomainTypeID{domainIndex};
1715 if (associations[domainIndex])
1718 auto domain = cast<ValueTerm>(find(row->elements[domainIndex]))->value;
1719 auto &exports = exportTable.at(domain);
1720 if (exports.empty()) {
1721 auto portName = moduleOp.getPortNameAttr(i);
1722 auto portLoc = moduleOp.getPortLocation(i);
1723 auto domainDecl =
getDomain(domainTypeID);
1724 auto domainName = domainDecl.getNameAttr();
1725 auto diag = emitError(portLoc) <<
"private " << domainName
1726 <<
" association for port " << portName;
1727 diag.attachNote(domain.getLoc()) <<
"associated domain: " << domain;
1728 noteLocation(diag, moduleOp);
1732 if (exports.size() > 1) {
1733 emitAmbiguousPortDomainAssociation(moduleOp, exports, domainTypeID, i);
1737 auto argument = cast<BlockArgument>(exports[0]);
1738 auto domainPortIndex = argument.getArgNumber();
1739 associations[domainTypeID.index] =
1740 IntegerAttr::get(IntegerType::get(
context, 32, IntegerType::Unsigned),
1744 newModuleDomainInfo[i] = ArrayAttr::get(
context, associations);
1747 result = ArrayAttr::get(moduleOp.getContext(), newModuleDomainInfo);
1748 moduleOp.setDomainInfoAttr(result);
1753 OpBuilder &builder, DenseMap<DomainValue, DomainValue> &domainsInScope,
1754 Operation *user, DomainType type, VariableTerm *var) {
1755 auto name = type.getName().getAttr();
1757 DomainCreateAnonOp::create(builder, user->getLoc(), type, name);
1759 LLVM_DEBUG(llvm::dbgs().indent(6) <<
"create anon " << render(anon) <<
"\n");
1760 solve(var, allocVal(anon));
1761 domainsInScope[anon] = anon;
1762 globals.inserted.insert(anon);
1767 OpBuilder &builder, DenseMap<DomainValue, DomainValue> &domainsInScope,
1769 auto &domainInScope = domainsInScope[domain];
1771 return domainInScope;
1773 domainInScope = cast<DomainValue>(
1774 WireOp::create(builder, domain.getLoc(), domain.getType(),
1775 domain.getType().getName().getAttr())
1778 OpBuilder::InsertionGuard guard(builder);
1779 builder.setInsertionPointAfterValue(domain);
1780 DomainDefineOp::create(builder, domain.getLoc(), domainInScope, domain);
1782 LLVM_DEBUG(llvm::dbgs().indent(6) <<
"bounce wire " << render(domainInScope)
1783 <<
" := " << render(domain) <<
"\n");
1784 return domainInScope;
1788ModuleState::updateInstance(DenseMap<DomainValue, DomainValue> &domainsInScope,
1790 LLVM_DEBUG(llvm::dbgs().indent(4) <<
"update " << render(op) <<
"\n");
1791 OpBuilder builder(op.getContext());
1792 builder.setInsertionPointAfter(op);
1793 auto numPorts = op->getNumResults();
1795 for (
size_t i = 0; i < numPorts; ++i)
1796 if (
auto port = dyn_cast<DomainValue>(op->getResult(i)))
1797 if (op.getPortDirection(i) == Direction::Out)
1798 domainsInScope[port] = port;
1800 for (
size_t i = 0; i < numPorts; ++i) {
1801 auto port = dyn_cast<DomainValue>(op->getResult(i));
1802 auto direction = op.getPortDirection(i);
1806 if (port && direction == Direction::In && !
isDriven(port)) {
1807 auto loc = port.getLoc();
1808 auto *term = getTermForDomain(port);
1809 if (
auto *var = dyn_cast<VariableTerm>(term)) {
1810 auto domain = solveVarWithAnonDomain(builder, domainsInScope, op,
1811 port.getType(), var);
1812 LLVM_DEBUG(llvm::dbgs().indent(6) <<
"connect " << render(port)
1813 <<
" := " << render(domain) <<
"\n");
1814 DomainDefineOp::create(builder, loc, port, domain);
1817 if (
auto *val = dyn_cast<ValueTerm>(term)) {
1818 auto domain = getDomainInScope(builder, domainsInScope, val->value);
1819 LLVM_DEBUG(llvm::dbgs().indent(6) <<
"connect " << render(port)
1820 <<
" := " << render(domain) <<
"\n");
1821 DomainDefineOp::create(builder, loc, port, domain);
1824 llvm_unreachable(
"unhandled domain term type");
1832ModuleState::updateWire(DenseMap<DomainValue, DomainValue> &domainsInScope,
1834 auto result = wireOp.getResult();
1836 if (
auto tgt = dyn_cast<DomainValue>(result)) {
1840 LLVM_DEBUG(llvm::dbgs().indent(4) <<
"update " << render(wireOp) <<
"\n");
1841 OpBuilder builder(wireOp);
1842 builder.setInsertionPointAfter(wireOp);
1843 auto *term = getTermForDomain(tgt);
1844 if (
auto *var = dyn_cast<VariableTerm>(term)) {
1845 auto src = solveVarWithAnonDomain(builder, domainsInScope, wireOp,
1846 tgt.getType(), var);
1847 LLVM_DEBUG(llvm::dbgs().indent(6)
1848 <<
"connect " << render(tgt) <<
" := " << render(src) <<
"\n");
1849 DomainDefineOp::create(builder, wireOp.getLoc(), tgt, src);
1852 if (
auto *val = dyn_cast<ValueTerm>(term)) {
1853 auto src = getDomainInScope(builder, domainsInScope, val->value);
1854 LLVM_DEBUG(llvm::dbgs().indent(6)
1855 <<
"connect " << render(tgt) <<
" := " << render(src) <<
"\n");
1856 DomainDefineOp::create(builder, wireOp.getLoc(), tgt, src);
1859 llvm_unreachable(
"unhandled domain term type");
1862 if (!
isHardware(result) || isColorless(result))
1865 LLVM_DEBUG(llvm::dbgs().indent(4) <<
"update " << render(wireOp) <<
"\n");
1866 OpBuilder builder(wireOp);
1867 auto *row = getDomainAssociationAsRow(wireOp.getResult());
1869 SmallVector<Value> domainOperands;
1870 for (
auto [i, element] :
llvm::enumerate(
1871 llvm::map_range(row->elements, [&](auto e) {
return find(e); }))) {
1872 if (
auto *val = dyn_cast<ValueTerm>(element)) {
1873 domainOperands.push_back(
1874 getDomainInScope(builder, domainsInScope, val->value));
1877 if (
auto *var = dyn_cast<VariableTerm>(element)) {
1878 auto type = DomainType::getFromDomainOp(
getDomain(DomainTypeID{i}));
1880 solveVarWithAnonDomain(builder, domainsInScope, wireOp, type, var);
1881 domainOperands.push_back(domain);
1884 assert(0 &&
"unhandled domain type");
1886 wireOp.getDomainsMutable().assign(domainOperands);
1890LogicalResult ModuleState::updateModuleBody(FModuleOp moduleOp) {
1891 DenseMap<DomainValue, DomainValue> domainsInScope;
1893 for (
size_t i = 0, e = moduleOp.getNumPorts(); i < e; ++i)
1894 if (
auto port = dyn_cast<DomainValue>(moduleOp.getArgument(i)))
1895 if (moduleOp.getPortDirection(i) == Direction::In)
1896 domainsInScope[port] = port;
1898 auto result = moduleOp.getBodyBlock()->walk([&](Operation *op) -> WalkResult {
1899 return TypeSwitch<Operation *, WalkResult>(op)
1901 [&](
auto wire) {
return updateWire(domainsInScope, wire); })
1902 .Case<FInstanceLike>([&](
auto instance) {
1903 return updateInstance(domainsInScope, instance);
1905 .Case<DomainCreateOp, DomainCreateAnonOp>([&](
auto domain) {
1906 domainsInScope[domain] = domain;
1909 .Default([&](
auto op) {
return success(); });
1911 return failure(result.wasInterrupted());
1914LogicalResult ModuleState::updateModule(FModuleOp moduleOp) {
1915 auto exports = initializeExportTable(moduleOp);
1916 PendingUpdates pending;
1917 getUpdatesForModule(moduleOp, exports, pending);
1918 applyUpdatesToModule(moduleOp, exports, pending);
1920 ArrayAttr portDomainInfo;
1921 if (failed(updateModuleDomainInfo(moduleOp, exports, portDomainInfo)))
1924 if (failed(driveModuleOutputDomainPorts(moduleOp)))
1928 auto &entry = getModuleUpdateTable()[moduleOp.getModuleNameAttr()];
1929 entry.portDomainInfo = portDomainInfo;
1930 entry.portInsertions = std::move(pending.insertions);
1932 if (failed(updateModuleBody(moduleOp)))
1936 llvm::dbgs().indent(2) <<
"port summary:\n";
1937 for (
auto port : moduleOp.
getBodyBlock()->getArguments()) {
1938 llvm::dbgs().indent(4) << render(port);
1939 auto info = cast<ArrayAttr>(
1940 moduleOp.getDomainInfoAttrForPort(port.getArgNumber()));
1942 llvm::dbgs() <<
" domains [";
1943 llvm::interleaveComma(
1944 info.getAsRange<IntegerAttr>(), llvm::dbgs(), [&](
auto i) {
1945 llvm::dbgs() << render(moduleOp.getArgument(i.getUInt()));
1947 llvm::dbgs() <<
"]";
1949 llvm::dbgs() <<
"\n";
1956LogicalResult ModuleState::checkModulePorts(FModuleLike moduleOp) {
1957 auto numDomains = getNumDomains();
1958 auto domainInfo = moduleOp.getDomainInfoAttr();
1959 auto numPorts = moduleOp.getNumPorts();
1961 DenseMap<unsigned, DomainTypeID> domainTypeIDTable;
1962 for (
size_t i = 0; i < numPorts; ++i) {
1963 if (isa<DomainType>(moduleOp.getPortType(i)))
1964 domainTypeIDTable[i] = getDomainTypeID(moduleOp, i);
1967 for (
size_t i = 0; i < numPorts; ++i) {
1972 SmallVector<IntegerAttr> associations(numDomains);
1974 auto domainTypeID = domainTypeIDTable.at(domainPortIndex.getUInt());
1975 auto prevDomainPortIndex = associations[domainTypeID.index];
1976 if (prevDomainPortIndex) {
1977 emitDuplicatePortDomainError(moduleOp, i, domainTypeID,
1978 prevDomainPortIndex, domainPortIndex);
1981 associations[domainTypeID.index] = domainPortIndex;
1985 for (
size_t domainIndex = 0; domainIndex < numDomains; ++domainIndex) {
1986 auto typeID = DomainTypeID{domainIndex};
1987 if (!associations[domainIndex]) {
1988 emitMissingPortDomainAssociationError(moduleOp, typeID, i);
1997LogicalResult ModuleState::checkModuleDomainPortDrivers(FModuleOp moduleOp) {
1998 for (
size_t i = 0, e = moduleOp.getNumPorts(); i < e; ++i) {
1999 auto port = dyn_cast<DomainValue>(moduleOp.getArgument(i));
2000 if (!port || moduleOp.getPortDirection(i) != Direction::Out ||
2004 auto name = moduleOp.getPortNameAttr(i);
2005 auto diag = emitError(moduleOp.getPortLocation(i))
2006 <<
"undriven domain port " << name;
2007 noteLocation(diag, moduleOp);
2014LogicalResult ModuleState::checkInstanceDomainPortDrivers(FInstanceLike op) {
2015 for (
size_t i = 0, e = op->getNumResults(); i < e; ++i) {
2016 auto port = dyn_cast<DomainValue>(op->getResult(i));
2017 if (!port || op.getPortDirection(i) != Direction::In ||
isDriven(port))
2020 auto name = op.getPortNameAttr(i);
2021 auto diag = emitError(op.getPortLocation(i))
2022 <<
"undriven domain port " << name;
2023 noteLocation(diag, op);
2030LogicalResult ModuleState::checkModuleBody(FModuleOp moduleOp) {
2031 auto result = moduleOp.getBody().walk([&](FInstanceLike op) -> WalkResult {
2032 return checkInstanceDomainPortDrivers(op);
2034 return failure(result.wasInterrupted());
2037LogicalResult ModuleState::inferModule(FModuleOp moduleOp) {
2038 LLVM_DEBUG(llvm::dbgs() <<
"infer: " << moduleOp.getModuleName() <<
"\n");
2039 if (failed(processModule(moduleOp)))
2042 return updateModule(moduleOp);
2045LogicalResult ModuleState::checkModule(FModuleOp moduleOp) {
2046 LLVM_DEBUG(llvm::dbgs() <<
"check: " << moduleOp.getModuleName() <<
"\n");
2047 if (failed(checkModulePorts(moduleOp)))
2050 if (failed(checkModuleDomainPortDrivers(moduleOp)))
2053 if (failed(checkModuleBody(moduleOp)))
2056 return processModule(moduleOp);
2059LogicalResult ModuleState::checkModule(FExtModuleOp extModuleOp) {
2060 LLVM_DEBUG(llvm::dbgs() <<
"check: " << extModuleOp.getModuleName() <<
"\n");
2061 return checkModulePorts(extModuleOp);
2064LogicalResult ModuleState::checkAndInferModule(FModuleOp moduleOp) {
2065 LLVM_DEBUG(llvm::dbgs() <<
"check/infer: " << moduleOp.getModuleName()
2068 if (failed(checkModulePorts(moduleOp)))
2071 if (failed(processModule(moduleOp)))
2074 if (failed(driveModuleOutputDomainPorts(moduleOp)))
2077 return updateModuleBody(moduleOp);
2084LogicalResult CircuitState::runOnModule(Operation *op) {
2085 ModuleState state(*
this);
2086 if (
auto moduleOp = dyn_cast<FModuleOp>(op)) {
2087 if (mode == InferDomainsMode::Check)
2088 return state.checkModule(moduleOp);
2090 if (mode == InferDomainsMode::InferAll || moduleOp.isPrivate())
2091 return state.inferModule(moduleOp);
2093 return state.checkAndInferModule(moduleOp);
2096 if (
auto extModuleOp = dyn_cast<FExtModuleOp>(op))
2097 return state.checkModule(extModuleOp);
2102LogicalResult CircuitState::run() {
2103 DenseSet<Operation *> errored;
2104 instanceGraph.walkPostOrder([&](
auto &node) {
2105 auto moduleOp = node.getModule();
2106 for (
auto *inst : node) {
2107 if (errored.contains(inst->getTarget()->getModule())) {
2108 errored.insert(moduleOp);
2112 if (failed(runOnModule(node.getModule())))
2113 errored.insert(moduleOp);
2115 return success(errored.empty());
2119struct InferDomainsPass
2120 :
public circt::firrtl::impl::InferDomainsBase<InferDomainsPass> {
2122 void runOnOperation()
override {
2124 auto circuit = getOperation();
2127 if (!skippedDomains.empty()) {
2128 DenseSet<StringAttr> skippedNames;
2129 auto *
context = &getContext();
2130 for (
const auto &name : skippedDomains)
2131 skippedNames.insert(StringAttr::
get(
context, name));
2134 return skippedNames.contains(name);
2136 return signalPassFailure();
2139 auto &instanceGraph = getAnalysis<InstanceGraph>();
2140 auto &symbolTable = getAnalysis<SymbolTable>();
2141 auto &innerSymbolTableCollection =
2142 getAnalysis<InnerSymbolTableCollection>();
2144 innerSymbolTableCollection};
2145 CircuitState state(circuit, instanceGraph, innerRefNamespace, mode);
2146 if (failed(state.run()))
2147 signalPassFailure();
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
SmallVector< std::pair< unsigned, PortInfo > > PortInsertions
mlir::TypedValue< DomainType > DomainValue
DenseMap< VariableTerm *, unsigned > PendingSolutions
A map from unsolved variables to a port index, where that port has not yet been created.
static bool isHardware(Type type)
True if a value of the given type could be associated with a domain.
static bool isPort(BlockArgument arg)
Return true if the value is a port on the module.
DenseMap< DomainValue, TinyPtrVector< DomainValue > > ExportTable
A map from domain IR values defined internal to the moduleOp, to ports that alias that domain.
static auto getPortDomainAssociation(ArrayAttr info, size_t i)
From a domain info attribute, get the row of associated domains for a hardware value at index i.
static bool isDriven(DomainValue port)
Returns true if the value is driven by a connect op.
static Block * getBodyBlock(FModuleLike mod)
static FailureOr< Domain > getDomain(Operation *op)
The domain of an AXI4 op, or failure for one this pass does not know.
#define CIRCT_DEBUG_SCOPED_PASS_LOGGER(PASS)
A namespace that is used to store existing names and generate new names in some scope within the IR.
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
This graph tracks modules and where they are instantiated.
This class represents a collection of InnerSymbolTable's.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
static StringRef toLongString(Direction direction)
LogicalResult stripDomainsFromCircuit(CircuitOp circuit, llvm::function_ref< bool(StringAttr)> shouldStripDomain)
Strip domains whose names match the predicate from a circuit.
InferDomainsMode
The mode for the InferDomains pass.
llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const InstanceInfo::LatticeValue &value)
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, List[str] cmdline_args=sys.argv)
This holds the name and type that describes the module's ports.
This class represents the namespace in which InnerRef's can be resolved.