26#include "mlir/IR/AsmState.h"
27#include "mlir/IR/Iterators.h"
28#include "mlir/IR/Threading.h"
29#include "mlir/Interfaces/SideEffectInterfaces.h"
30#include "llvm/ADT/DenseMap.h"
31#include "llvm/ADT/DenseSet.h"
32#include "llvm/ADT/STLExtras.h"
33#include "llvm/ADT/SmallVector.h"
34#include "llvm/ADT/TinyPtrVector.h"
36#define DEBUG_TYPE "firrtl-infer-domains"
40#define GEN_PASS_DEF_INFERDOMAINS
41#include "circt/Dialect/FIRRTL/Passes.h.inc"
46using namespace firrtl;
52using mlir::InFlightDiagnostic;
53using mlir::ReverseIterator;
71 return info.getAsRange<IntegerAttr>();
72 return cast<ArrayAttr>(info[i]).getAsRange<IntegerAttr>();
76static bool isPort(BlockArgument arg) {
77 return isa<FModuleOp>(arg.getOwner()->getParentOp());
82 auto arg = dyn_cast<BlockArgument>(value);
90 for (
auto *user : port.getUsers())
91 if (
auto connect = dyn_cast<FConnectLike>(user))
92 if (connect.getDest() == port)
99 return type_isa<FIRRTLBaseType, RefType>(type);
122struct ModuleUpdateInfo {
124 ArrayAttr portDomainInfo;
132 CircuitState(CircuitOp circuit,
InstanceGraph &instanceGraph,
134 : circuit(circuit), instanceGraph(instanceGraph),
135 innerRefNamespace(innerRefNamespace), mode(mode) {
136 processCircuit(circuit);
141 ArrayRef<DomainOp> getDomains()
const {
return domainTable; }
142 size_t getNumDomains()
const {
return domainTable.size(); }
143 DomainOp getDomain(DomainTypeID
id)
const {
return domainTable[
id.index]; }
144 DomainTypeID getDomainTypeID(Type type) {
return typeIDTable[type]; }
146 void dirty() { asmState =
nullptr; }
147 AsmState &getAsmState() {
149 asmState = std::make_unique<AsmState>(
150 circuit, mlir::OpPrintingFlags().assumeVerified());
155 size_t getVariableID(VariableTerm *term) {
156 return variableIDTable.insert({term, variableIDTable.size() + 1})
160 DenseMap<StringAttr, ModuleUpdateInfo> &getModuleUpdateTable() {
161 return moduleUpdateTable;
166 DenseSet<Value> inserted;
169 LogicalResult runOnModule(Operation *moduleOp);
171 void processDomain(DomainOp op) {
172 auto index = domainTable.size();
173 auto domainType = DomainType::getFromDomainOp(op);
174 domainTable.push_back(op);
175 typeIDTable.insert({domainType, {index}});
178 void processCircuit(CircuitOp circuit) {
179 for (
auto decl : circuit.getOps<DomainOp>())
187 SmallVector<DomainOp> domainTable;
188 DenseMap<Type, DomainTypeID> typeIDTable;
189 DenseMap<VariableTerm *, size_t> variableIDTable;
190 std::unique_ptr<AsmState> asmState;
191 DenseMap<StringAttr, ModuleUpdateInfo> moduleUpdateTable;
211 constexpr Term(TermKind kind) : kind(kind) {}
219struct TermBase : Term {
220 static bool classof(
const Term *term) {
return term->kind == K; }
221 TermBase() : Term(K) {}
227struct VariableTerm :
public TermBase<TermKind::Variable> {
228 VariableTerm() : leader(nullptr) {}
229 VariableTerm(Term *leader) : leader(leader) {}
236struct ValueTerm :
public TermBase<TermKind::Value> {
244struct RowTerm :
public TermBase<TermKind::Row> {
245 RowTerm(ArrayRef<Term *> elements) : elements(elements) {}
246 ArrayRef<Term *> elements;
265struct PendingUpdates {
275using ExportTable = DenseMap<DomainValue, TinyPtrVector<DomainValue>>;
280 explicit ModuleState(CircuitState &globals) : globals(globals) {}
282 ArrayRef<DomainOp> getDomains() {
return globals.getDomains(); }
283 size_t getNumDomains() {
return globals.getNumDomains(); }
284 DomainOp getDomain(DomainTypeID
id) {
return globals.getDomain(
id); }
285 DomainTypeID getDomainTypeID(Type type) {
286 return globals.getDomainTypeID(type);
288 DomainTypeID getDomainTypeID(FModuleLike module,
size_t i) {
289 return globals.getDomainTypeID(module.getPortType(i));
291 DomainTypeID getDomainTypeID(FInstanceLike op,
size_t i)
const {
292 return globals.getDomainTypeID(op->getResult(i).getType());
294 DomainTypeID getDomainTypeID(
DomainValue value)
const {
295 return globals.getDomainTypeID(value.getType());
297 auto &getModuleUpdateTable() {
return globals.getModuleUpdateTable(); }
299 mlir::AsmState &getAsmState() {
return globals.getAsmState(); }
300 void dirty() { globals.dirty(); }
302 template <
typename T>
303 void render(Operation *op, T &out);
304 template <
typename T>
305 void render(Value value, T &out);
306 template <
typename T>
307 void renderLong(Value value, T &out);
308 template <
typename T>
309 void render(Term *term, T &out);
310 template <
typename T>
312 template <
typename T>
313 Render<T> render(T &&subject);
315 RenderLong renderLong(Value value);
318 LogicalResult unify(Term *lhs, Term *rhs);
319 LogicalResult unify(VariableTerm *x, Term *y);
320 LogicalResult unify(ValueTerm *xv, Term *y);
321 LogicalResult unify(RowTerm *lhsRow, Term *rhs);
322 void solve(Term *lhs, Term *rhs);
324 [[nodiscard]] RowTerm *allocRow(
size_t size);
325 [[nodiscard]] RowTerm *allocRow(ArrayRef<Term *> elements);
326 [[nodiscard]] VariableTerm *allocVar();
327 [[nodiscard]] ValueTerm *allocVal(
DomainValue value);
328 template <
typename T,
typename... Args>
329 T *alloc(Args &&...args);
330 ArrayRef<Term *> allocArray(ArrayRef<Term *> elements);
335 void setTermForDomain(
DomainValue value, Term *term);
337 Term *getOptDomainAssociation(Value value);
338 Term *getDomainAssociation(Value value);
339 void setDomainAssociation(Value value, Term *term);
347 bool isColorless(Value value);
350 RowTerm *getDomainAssociationAsRow(Value value);
352 void noteLocation(InFlightDiagnostic &diag, Operation *op);
353 void noteDomain(InFlightDiagnostic &diag,
DomainValue domain);
354 void noteDomainSource(InFlightDiagnostic &diag,
DomainValue domain);
355 void noteDomainSource(InFlightDiagnostic &diag, Term *term);
356 void emitDomainCrossingError(Operation *op, Value lhs, Term *lhsTerm,
357 Value rhs, Term *rhsTerm);
358 template <
typename T>
359 void emitDuplicatePortDomainError(T op,
size_t i, DomainTypeID domainTypeID,
360 IntegerAttr domainPortIndexAttr1,
361 IntegerAttr domainPortIndexAttr2);
362 template <
typename T>
363 void emitDomainPortInferenceError(T op,
size_t i);
364 template <
typename T>
365 void emitAmbiguousPortDomainAssociation(
366 T op,
const llvm::TinyPtrVector<DomainValue> &exports,
367 DomainTypeID typeID,
size_t i);
368 template <
typename T>
369 void emitMissingPortDomainAssociationError(T op, DomainTypeID typeID,
372 LogicalResult unifyAssociations(Operation *op, Value lhs, Value rhs);
373 template <
typename T>
374 LogicalResult unifyAssociations(Operation *op, T &&range);
375 LogicalResult unifyAssociations(Operation *op);
377 LogicalResult processModulePorts(FModuleOp moduleOp);
378 template <
typename T>
379 LogicalResult processInstancePorts(T op);
380 FInstanceLike fixInstancePorts(FInstanceLike op,
381 const ModuleUpdateInfo &update);
382 LogicalResult processOp(FInstanceLike op);
383 LogicalResult processOp(UnsafeDomainCastOp op);
384 LogicalResult processOp(DomainDefineOp op);
385 LogicalResult processOp(WireOp op);
386 LogicalResult processOp(RWProbeOp op);
387 LogicalResult processOp(Operation *op);
388 LogicalResult processModuleBody(FModuleOp moduleOp);
389 LogicalResult processModule(FModuleOp moduleOp);
391 ExportTable initializeExportTable(FModuleOp moduleOp);
392 void ensureSolved(
Namespace &ns, DomainTypeID typeID,
size_t ip,
393 LocationAttr loc, VariableTerm *var,
394 PendingUpdates &pending);
396 DomainTypeID typeID,
size_t ip, LocationAttr loc,
397 ValueTerm *val, PendingUpdates &pending);
398 void getUpdatesForDomainAssociationOfPort(
Namespace &ns,
399 PendingUpdates &pending,
400 DomainTypeID typeID,
size_t ip,
401 LocationAttr loc, Term *term,
403 void getUpdatesForDomainAssociationOfPort(
Namespace &ns,
405 size_t ip, LocationAttr loc,
407 PendingUpdates &pending);
408 void getUpdatesForModulePorts(FModuleOp moduleOp,
const ExportTable &exports,
410 void getUpdatesForModule(FModuleOp moduleOp,
const ExportTable &exports,
411 PendingUpdates &pending);
412 void applyUpdatesToModule(FModuleOp moduleOp,
ExportTable &exports,
413 const PendingUpdates &pending);
414 SmallVector<Attribute> copyPortDomainAssociations(FModuleOp moduleOp,
415 ArrayAttr moduleDomainInfo,
417 LogicalResult driveModuleOutputDomainPorts(FModuleOp moduleOp);
418 LogicalResult updateModuleDomainInfo(FModuleOp moduleOp,
422 solveVarWithAnonDomain(OpBuilder &builder,
423 DenseMap<DomainValue, DomainValue> &domainsInScope,
424 Operation *user, DomainType type, VariableTerm *var);
426 getDomainInScope(OpBuilder &builder,
427 DenseMap<DomainValue, DomainValue> &domainsInScope,
430 updateInstance(DenseMap<DomainValue, DomainValue> &domainsInScope,
432 LogicalResult updateWire(DenseMap<DomainValue, DomainValue> &domainsInScope,
434 LogicalResult updateModuleBody(FModuleOp moduleOp);
435 LogicalResult updateModule(FModuleOp moduleOp);
437 LogicalResult checkModulePorts(FModuleLike moduleOp);
438 LogicalResult checkModuleDomainPortDrivers(FModuleOp moduleOp);
439 LogicalResult checkInstanceDomainPortDrivers(FInstanceLike op);
440 LogicalResult checkModuleBody(FModuleOp moduleOp);
442 LogicalResult inferModule(FModuleOp moduleOp);
443 LogicalResult checkModule(FModuleOp moduleOp);
444 LogicalResult checkModule(FExtModuleOp extModuleOp);
445 LogicalResult checkAndInferModule(FModuleOp moduleOp);
448 CircuitState &globals;
449 DenseMap<Value, Term *> termTable;
450 DenseMap<Value, Term *> associationTable;
452 DenseMap<Value, bool> colorlessTable;
453 llvm::BumpPtrAllocator allocator;
458void ModuleState::render(Operation *op, T &out) {
459 op->print(out, getAsmState());
463void ModuleState::render(Value value, T &out) {
471 llvm::raw_string_ostream os(name);
472 value.printAsOperand(os, globals.getAsmState());
478void ModuleState::renderLong(Value value, T &out) {
479 if (
auto arg = dyn_cast<BlockArgument>(value)) {
480 if (
auto moduleOp = llvm::dyn_cast_if_present<FModuleLike>(
481 arg.getOwner()->getParentOp())) {
483 moduleOp.getPortDirection(arg.getArgNumber()));
484 out <<
" module port ";
486 }
else if (
auto result = dyn_cast<OpResult>(value)) {
487 auto *op = result.getOwner();
488 if (
auto inst = dyn_cast<FInstanceLike>(op)) {
490 inst.getPortDirection(result.getResultNumber()));
491 out <<
" instance port ";
500void ModuleState::render(Term *term, T &out) {
506 if (
auto *var = dyn_cast<VariableTerm>(term)) {
507 out <<
"?" << globals.getVariableID(var);
510 if (
auto *val = dyn_cast<ValueTerm>(term)) {
511 auto value = val->value;
515 if (
auto *row = dyn_cast<RowTerm>(term)) {
517 llvm::interleaveComma(
518 llvm::seq(
size_t(0), getNumDomains()), out, [&](
auto i) {
519 render(row->elements[i], out);
520 out <<
" : " << getDomain(DomainTypeID{i}).getSymName();
529struct ModuleState::Render {
535ModuleState::Render<T> ModuleState::render(T &&subject) {
536 return Render<T>{
this, std::forward<T>(subject)};
541 ModuleState::Render<T> r) {
542 r.state->render(r.subject, out);
561Term *ModuleState::find(Term *x) {
565 if (
auto *var = dyn_cast<VariableTerm>(x)) {
566 if (var->leader ==
nullptr)
569 auto *leader = find(var->leader);
570 if (leader != var->leader)
571 var->leader = leader;
578LogicalResult ModuleState::unify(VariableTerm *x, Term *y) {
584LogicalResult ModuleState::unify(ValueTerm *xv, Term *y) {
585 if (
auto *yv = dyn_cast<VariableTerm>(y)) {
590 if (
auto *yv = dyn_cast<ValueTerm>(y))
591 return success(xv == yv);
597LogicalResult ModuleState::unify(RowTerm *lhsRow, Term *rhs) {
598 if (
auto *rhsVar = dyn_cast<VariableTerm>(rhs)) {
599 rhsVar->leader = lhsRow;
602 if (
auto *rhsRow = dyn_cast<RowTerm>(rhs)) {
603 for (
auto [x, y] :
llvm::zip_equal(lhsRow->elements, rhsRow->elements))
604 if (failed(unify(x, y)))
612LogicalResult ModuleState::unify(Term *lhs, Term *rhs) {
620 LLVM_DEBUG(llvm::dbgs().indent(6)
621 <<
"unify " << render(lhs) <<
" = " << render(rhs) <<
"\n");
623 if (
auto *lhsVar = dyn_cast<VariableTerm>(lhs))
624 return unify(lhsVar, rhs);
625 if (
auto *lhsVal = dyn_cast<ValueTerm>(lhs))
626 return unify(lhsVal, rhs);
627 if (
auto *lhsRow = dyn_cast<RowTerm>(lhs))
628 return unify(lhsRow, rhs);
632void ModuleState::solve(Term *lhs, Term *rhs) {
633 [[maybe_unused]]
auto result = unify(lhs, rhs);
634 assert(result.succeeded());
637RowTerm *ModuleState::allocRow(
size_t size) {
638 SmallVector<Term *> elements;
639 elements.resize(size);
640 return allocRow(elements);
643RowTerm *ModuleState::allocRow(ArrayRef<Term *> elements) {
644 auto ds = allocArray(elements);
645 return alloc<RowTerm>(ds);
648VariableTerm *ModuleState::allocVar() {
return alloc<VariableTerm>(); }
650ValueTerm *ModuleState::allocVal(
DomainValue value) {
651 return alloc<ValueTerm>(value);
654template <
typename T,
typename... Args>
655T *ModuleState::alloc(Args &&...args) {
656 static_assert(std::is_base_of_v<Term, T>,
"T must be a term");
657 return new (allocator) T(std::forward<Args>(args)...);
660ArrayRef<Term *> ModuleState::allocArray(ArrayRef<Term *> elements) {
661 auto size = elements.size();
665 auto *result = allocator.Allocate<Term *>(size);
666 llvm::uninitialized_copy(elements, result);
667 for (
size_t i = 0; i < size; ++i)
669 result[i] = alloc<VariableTerm>();
671 return ArrayRef(result, size);
675 auto *term = getOptTermForDomain(value);
676 if (
auto *val = llvm::dyn_cast_if_present<ValueTerm>(term))
681Term *ModuleState::getOptTermForDomain(
DomainValue value) {
682 assert(isa<DomainType>(value.getType()));
683 auto it = termTable.find(value);
684 if (it == termTable.end())
686 return find(it->second);
689Term *ModuleState::getTermForDomain(
DomainValue value) {
690 assert(isa<DomainType>(value.getType()));
691 if (
auto *term = getOptTermForDomain(value))
693 auto *term = allocVar();
694 setTermForDomain(value, term);
698void ModuleState::setTermForDomain(
DomainValue value, Term *term) {
700 assert(!termTable.contains(value));
701 termTable.insert({value, term});
702 LLVM_DEBUG(llvm::dbgs().indent(6)
703 <<
"set " << render(value) <<
" := " << render(term) <<
"\n");
706Term *ModuleState::getOptDomainAssociation(Value value) {
708 auto it = associationTable.find(value);
709 if (it == associationTable.end())
711 return find(it->second);
714Term *ModuleState::getDomainAssociation(Value value) {
715 auto *term = getOptDomainAssociation(value);
720void ModuleState::setDomainAssociation(Value value, Term *term) {
724 associationTable.insert({value, term});
726 llvm::dbgs().indent(6) <<
"set domains(" << render(value)
727 <<
") := " << render(term) <<
"\n";
731bool ModuleState::isColorless(Value value) {
738 if (
auto it = colorlessTable.find(value); it != colorlessTable.end())
751 enum class Kind { Colorless, Colored, LookThrough };
752 auto classify = [&](Value v) -> Kind {
754 return Kind::Colorless;
756 auto *op = v.getDefiningOp();
759 return Kind::Colored;
762 if (op->hasTrait<OpTrait::ConstantLike>())
763 return Kind::Colorless;
767 return Kind::LookThrough;
772 if (
auto castOp = dyn_cast<UnsafeDomainCastOp>(op)) {
773 if (!castOp.getDomains().empty())
774 return Kind::Colored;
775 return Kind::LookThrough;
789 if (op->getNumOperands() == 0)
790 return Kind::Colored;
791 for (
auto operand : op->getOperands())
793 return Kind::Colored;
794 return Kind::LookThrough;
799 return Kind::Colored;
824 SmallVector<Frame> stack;
827 switch (classify(value)) {
829 return colorlessTable[value] =
false;
830 case Kind::Colorless:
831 return colorlessTable[value] =
true;
832 case Kind::LookThrough:
833 stack.push_back({value});
838 while (!stack.empty()) {
839 auto &frame = stack.back();
840 auto *op = frame.value.getDefiningOp();
841 bool colored =
false, pushed =
false;
843 while (frame.index < op->getNumOperands()) {
844 Value child = op->getOperand(frame.index);
847 if (
auto it = colorlessTable.find(child); it != colorlessTable.end()) {
859 switch (classify(child)) {
861 colorlessTable[child] =
false;
864 case Kind::Colorless:
865 colorlessTable[child] =
true;
868 case Kind::LookThrough:
869 stack.push_back({child});
883 colorlessTable[frame.value] = !colored;
887 return colorlessTable[value];
890void ModuleState::processDomainDefinition(
DomainValue domain) {
891 assert(isa<DomainType>(domain.getType()));
892 auto *newTerm = allocVal(domain);
893 auto *oldTerm = getOptTermForDomain(domain);
895 setTermForDomain(domain, newTerm);
899 [[maybe_unused]]
auto result = unify(oldTerm, newTerm);
900 assert(result.succeeded());
903RowTerm *ModuleState::getDomainAssociationAsRow(Value value) {
905 auto *term = getOptDomainAssociation(value);
909 auto *row = allocRow(getNumDomains());
910 setDomainAssociation(value, row);
915 if (
auto *row = dyn_cast<RowTerm>(term))
919 if (
auto *var = dyn_cast<VariableTerm>(term)) {
920 auto *row = allocRow(getNumDomains());
925 assert(
false &&
"unhandled term type");
929void ModuleState::noteLocation(InFlightDiagnostic &diag, Operation *op) {
930 auto ¬e = diag.attachNote(op->getLoc());
931 if (
auto mod = dyn_cast<FModuleOp>(op)) {
932 note <<
"in module " << mod.getModuleNameAttr();
935 if (
auto mod = dyn_cast<FExtModuleOp>(op)) {
936 note <<
"in extmodule " << mod.getModuleNameAttr();
939 if (
auto inst = dyn_cast<InstanceOp>(op)) {
940 note <<
"in instance " << inst.getInstanceNameAttr();
943 if (
auto inst = dyn_cast<InstanceChoiceOp>(op)) {
944 note <<
"in instance_choice " << inst.getNameAttr();
951void ModuleState::noteDomain(InFlightDiagnostic &diag,
DomainValue domain) {
952 auto ¬e = diag.attachNote(domain.getLoc());
953 note << renderLong(domain);
955 if (globals.inserted.contains(domain)) {
956 note <<
" automatically inserted here";
960 note <<
" declared here";
963void ModuleState::noteDomainSource(InFlightDiagnostic &diag,
965 auto &irns = globals.getInnerRefNamespace();
966 SmallVector<FInstanceLike> stack;
967 llvm::SmallDenseSet<DomainValue> seen;
971 auto chaseConnect = [&]() {
972 for (
auto *user : domain.getUsers()) {
973 if (
auto defineOp = dyn_cast<DomainDefineOp>(user)) {
974 if (defineOp.getDest() != domain)
976 auto src = defineOp.getSrc();
977 diag.attachNote(defineOp.getLoc())
978 << renderLong(domain) <<
" aliases " << renderLong(src);
979 domain = defineOp.getSrc();
986 auto chaseModulePort = [&]() {
987 auto arg = dyn_cast<BlockArgument>(domain);
992 llvm::dyn_cast_if_present<FModuleOp>(arg.getOwner()->getParentOp());
996 auto name =
module.getModuleNameAttr();
997 while (!stack.empty()) {
998 auto instance = stack.back();
1000 auto referenced = instance.getReferencedModuleNamesAttr().getValue();
1001 if (llvm::is_contained(referenced, name)) {
1002 domain = cast<DomainValue>(instance->getResult(arg.getArgNumber()));
1009 auto chaseInstancePort = [&]() {
1010 auto result = dyn_cast<OpResult>(domain);
1014 auto inst = dyn_cast<FInstanceLike>(result.getOwner());
1018 auto index = result.getResultNumber();
1019 if (inst.getPortDirection(index) == Direction::In)
1022 auto names = inst.getReferencedModuleNamesAttr().getAsRange<StringAttr>();
1023 for (
auto name : names) {
1024 auto moduleLike = cast<FModuleLike>(irns.symTable.lookup(name));
1025 if (
auto moduleOp = dyn_cast<FModuleOp>(moduleLike.getOperation())) {
1026 stack.push_back(inst);
1027 domain = cast<DomainValue>(moduleOp.getArgument(index));
1034 auto chaseUnderlying = [&]() {
1035 if (
auto *term = getOptTermForDomain(domain)) {
1036 if (
auto *val = dyn_cast<ValueTerm>(term)) {
1037 if (domain != val->value) {
1038 diag.attachNote(domain.getLoc())
1039 << renderLong(domain) <<
" aliases " << renderLong(val->value);
1040 domain = val->value;
1049 auto [it, inserted] = seen.insert(domain);
1053 noteDomain(diag, domain);
1054 chaseConnect() || chaseModulePort() || chaseInstancePort() ||
1059void ModuleState::noteDomainSource(InFlightDiagnostic &diag, Term *term) {
1060 auto *val = dyn_cast<ValueTerm>(find(term));
1064 noteDomainSource(diag, val->value);
1067void ModuleState::emitDomainCrossingError(Operation *op, Value lhs,
1068 Term *lhsTerm, Value rhs,
1070 auto *lhsRow = cast<RowTerm>(lhsTerm);
1071 auto *rhsRow = cast<RowTerm>(rhsTerm);
1073 op->emitError(
"illegal domain crossing in operation between operands ");
1077 auto ¬e1 = diag.attachNote(lhs.getLoc());
1079 note1 <<
" has domains ";
1080 render(lhsRow, note1);
1081 auto ¬e2 = diag.attachNote(rhs.getLoc());
1083 note2 <<
" has domains ";
1084 render(rhsRow, note2);
1086 for (
size_t i = 0, e = getNumDomains(); i < e; ++i) {
1087 auto *lhsDomain = find(lhsRow->elements[i]);
1088 auto *rhsDomain = find(rhsRow->elements[i]);
1089 if (lhsDomain == rhsDomain)
1092 noteDomainSource(diag, lhsDomain);
1093 noteDomainSource(diag, rhsDomain);
1097template <
typename T>
1098void ModuleState::emitDuplicatePortDomainError(
1099 T op,
size_t i, DomainTypeID domainTypeID, IntegerAttr domainPortIndexAttr1,
1100 IntegerAttr domainPortIndexAttr2) {
1101 auto portName = op.getPortNameAttr(i);
1102 auto portLoc = op.getPortLocation(i);
1103 auto domainDecl = getDomain(domainTypeID);
1104 auto domainName = domainDecl.getNameAttr();
1105 auto domainPortIndex1 = domainPortIndexAttr1.getUInt();
1106 auto domainPortIndex2 = domainPortIndexAttr2.getUInt();
1107 auto domainPortName1 = op.getPortNameAttr(domainPortIndex1);
1108 auto domainPortName2 = op.getPortNameAttr(domainPortIndex2);
1109 auto domainPortLoc1 = op.getPortLocation(domainPortIndex1);
1110 auto domainPortLoc2 = op.getPortLocation(domainPortIndex2);
1111 auto diag = emitError(portLoc);
1112 diag <<
"duplicate " << domainName <<
" association for port " << portName;
1113 auto ¬e1 = diag.attachNote(domainPortLoc1);
1114 note1 <<
"associated with " << domainName <<
" port " << domainPortName1;
1115 auto ¬e2 = diag.attachNote(domainPortLoc2);
1116 note2 <<
"associated with " << domainName <<
" port " << domainPortName2;
1117 noteLocation(diag, op);
1122template <
typename T>
1123void ModuleState::emitDomainPortInferenceError(T op,
size_t i) {
1124 auto name = op.getPortNameAttr(i);
1125 auto diag = emitError(op->getLoc());
1126 auto info = op.getDomainInfo();
1127 diag <<
"unable to infer value for undriven domain port " << name;
1128 for (
size_t j = 0, e = op.getNumPorts(); j < e; ++j) {
1129 if (
auto assocs = dyn_cast<ArrayAttr>(info[j])) {
1130 for (
auto assoc : assocs) {
1131 if (i == cast<IntegerAttr>(assoc).getValue()) {
1132 auto name = op.getPortNameAttr(j);
1133 auto loc = op.getPortLocation(j);
1134 diag.attachNote(loc) <<
"associated with hardware port " << name;
1140 noteLocation(diag, op);
1143template <
typename T>
1144void ModuleState::emitAmbiguousPortDomainAssociation(
1145 T op,
const llvm::TinyPtrVector<DomainValue> &exports, DomainTypeID typeID,
1147 auto portName = op.getPortNameAttr(i);
1148 auto portLoc = op.getPortLocation(i);
1149 auto domainDecl = getDomain(typeID);
1150 auto domainName = domainDecl.getNameAttr();
1151 auto diag = emitError(portLoc) <<
"ambiguous " << domainName
1152 <<
" association for port " << portName;
1153 for (
auto e : exports) {
1154 auto arg = cast<BlockArgument>(e);
1155 auto name = op.getPortNameAttr(arg.getArgNumber());
1156 auto loc = op.getPortLocation(arg.getArgNumber());
1157 diag.attachNote(loc) <<
"candidate association " << name;
1159 noteLocation(diag, op);
1162template <
typename T>
1163void ModuleState::emitMissingPortDomainAssociationError(T op,
1164 DomainTypeID typeID,
1166 auto domainName = getDomain(typeID).getNameAttr();
1167 auto portName = op.getPortNameAttr(i);
1168 auto diag = emitError(op.getPortLocation(i))
1169 <<
"missing " << domainName <<
" association for port "
1171 noteLocation(diag, op);
1174LogicalResult ModuleState::unifyAssociations(Operation *op, Value lhs,
1187 if (isColorless(lhs) || isColorless(rhs))
1191 llvm::dbgs().indent(6) <<
"unify domains(" << render(lhs) <<
") = domains("
1192 << render(rhs) <<
")\n";
1195 auto *lhsTerm = getOptDomainAssociation(lhs);
1196 auto *rhsTerm = getOptDomainAssociation(rhs);
1200 if (failed(unify(lhsTerm, rhsTerm))) {
1201 emitDomainCrossingError(op, lhs, lhsTerm, rhs, rhsTerm);
1206 setDomainAssociation(rhs, lhsTerm);
1211 setDomainAssociation(lhs, rhsTerm);
1215 auto *var = allocVar();
1216 setDomainAssociation(lhs, var);
1217 setDomainAssociation(rhs, var);
1221template <
typename T>
1222LogicalResult ModuleState::unifyAssociations(Operation *op, T &&range) {
1224 for (
auto rhs : std::forward<T>(range)) {
1227 if (failed(unifyAssociations(op, lhs, rhs)))
1235LogicalResult ModuleState::unifyAssociations(Operation *op) {
1236 return unifyAssociations(
1237 op, llvm::concat<Value>(op->getOperands(), op->getResults()));
1240LogicalResult ModuleState::processModulePorts(FModuleOp moduleOp) {
1241 auto numDomains = getNumDomains();
1242 auto domainInfo = moduleOp.getDomainInfoAttr();
1243 auto numPorts = moduleOp.getNumPorts();
1245 DenseMap<unsigned, DomainTypeID> domainTypeIDTable;
1246 for (
size_t i = 0; i < numPorts; ++i) {
1247 auto port = dyn_cast<DomainValue>(moduleOp.getArgument(i));
1251 LLVM_DEBUG(llvm::dbgs().indent(4)
1252 <<
"process port " << render(port) <<
"\n");
1254 if (moduleOp.getPortDirection(i) == Direction::In)
1255 processDomainDefinition(port);
1257 domainTypeIDTable[i] = getDomainTypeID(moduleOp, i);
1260 for (
size_t i = 0; i < numPorts; ++i) {
1261 BlockArgument port = moduleOp.getArgument(i);
1265 LLVM_DEBUG(llvm::dbgs().indent(4)
1266 <<
"process port " << render(port) <<
"\n");
1268 SmallVector<IntegerAttr> associations(numDomains);
1270 auto domainTypeID = domainTypeIDTable.at(domainPortIndex.getUInt());
1271 auto prevDomainPortIndex = associations[domainTypeID.index];
1272 if (prevDomainPortIndex) {
1273 emitDuplicatePortDomainError(moduleOp, i, domainTypeID,
1274 prevDomainPortIndex, domainPortIndex);
1277 associations[domainTypeID.index] = domainPortIndex;
1280 SmallVector<Term *> elements(numDomains);
1281 for (
size_t domainTypeIndex = 0; domainTypeIndex < numDomains;
1282 ++domainTypeIndex) {
1283 auto domainPortIndex = associations[domainTypeIndex];
1284 if (!domainPortIndex)
1286 auto domainPortValue =
1287 cast<DomainValue>(moduleOp.getArgument(domainPortIndex.getUInt()));
1288 elements[domainTypeIndex] = getTermForDomain(domainPortValue);
1291 auto *domainAssociations = allocRow(elements);
1292 setDomainAssociation(port, domainAssociations);
1298template <
typename T>
1299LogicalResult ModuleState::processInstancePorts(T op) {
1300 auto numDomains = getNumDomains();
1301 auto domainInfo = op.getDomainInfoAttr();
1302 auto numPorts = op.getNumPorts();
1304 DenseMap<unsigned, DomainTypeID> domainTypeIDTable;
1305 for (
size_t i = 0; i < numPorts; ++i) {
1306 auto port = dyn_cast<DomainValue>(op->getResult(i));
1310 if (op.getPortDirection(i) == Direction::Out)
1311 processDomainDefinition(port);
1313 domainTypeIDTable[i] = getDomainTypeID(op, i);
1316 for (
size_t i = 0; i < numPorts; ++i) {
1317 Value port = op->getResult(i);
1321 SmallVector<IntegerAttr> associations(numDomains);
1323 auto domainTypeID = domainTypeIDTable.at(domainPortIndex.getUInt());
1324 auto prevDomainPortIndex = associations[domainTypeID.index];
1325 if (prevDomainPortIndex) {
1326 emitDuplicatePortDomainError(op, i, domainTypeID, prevDomainPortIndex,
1330 associations[domainTypeID.index] = domainPortIndex;
1333 SmallVector<Term *> elements(numDomains);
1334 for (
size_t domainTypeIndex = 0; domainTypeIndex < numDomains;
1335 ++domainTypeIndex) {
1336 auto domainPortIndex = associations[domainTypeIndex];
1337 if (!domainPortIndex)
1339 auto domainPortValue =
1340 cast<DomainValue>(op->getResult(domainPortIndex.getUInt()));
1341 elements[domainTypeIndex] = getTermForDomain(domainPortValue);
1344 auto *domainAssociations = allocRow(elements);
1345 setDomainAssociation(port, domainAssociations);
1351FInstanceLike ModuleState::fixInstancePorts(FInstanceLike op,
1352 const ModuleUpdateInfo &update) {
1353 auto clone = op.cloneWithInsertedPortsAndReplaceUses(update.portInsertions);
1354 clone.setDomainInfoAttr(update.portDomainInfo);
1357 LLVM_DEBUG(llvm::dbgs().indent(6) <<
"fixup " << render(clone) <<
"\n");
1361LogicalResult ModuleState::processOp(FInstanceLike op) {
1363 cast<StringAttr>(cast<ArrayAttr>(op.getReferencedModuleNamesAttr())[0]);
1364 auto updateTable = getModuleUpdateTable();
1365 auto lookup = updateTable.find(moduleName);
1366 if (lookup != updateTable.end())
1367 op = fixInstancePorts(op, lookup->second);
1368 return processInstancePorts(op);
1371LogicalResult ModuleState::processOp(UnsafeDomainCastOp op) {
1372 auto domains = op.getDomains();
1373 if (domains.empty())
1374 return unifyAssociations(op, op.getInput(), op.getResult());
1376 auto input = op.getInput();
1378 SmallVector<Term *> elements(getNumDomains());
1379 if (
isHardware(input) && !isColorless(input)) {
1380 auto *inputRow = getDomainAssociationAsRow(input);
1381 elements.assign(inputRow->elements);
1384 for (
auto value : op.getDomains()) {
1385 auto domain = cast<DomainValue>(value);
1386 auto typeID = getDomainTypeID(domain);
1387 elements[typeID.index] = getTermForDomain(domain);
1390 auto *row = allocRow(elements);
1391 setDomainAssociation(op.getResult(), row);
1395LogicalResult ModuleState::processOp(DomainDefineOp op) {
1396 auto src = op.getSrc();
1397 auto dst = op.getDest();
1399 auto *srcTerm = getTermForDomain(src);
1400 auto *dstTerm = getTermForDomain(dst);
1401 if (succeeded(unify(dstTerm, srcTerm)))
1406 <<
"defines a domain value that was inferred to be a different domain '";
1407 render(dstTerm, diag);
1413LogicalResult ModuleState::processOp(WireOp op) {
1420 if (op.getDomains().empty())
1421 return unifyAssociations(op, op.getResults());
1425 SmallVector<Term *> elements(getNumDomains());
1426 for (
auto domain : op.getDomains()) {
1427 auto domainValue = cast<DomainValue>(domain);
1428 auto typeID = getDomainTypeID(domainValue);
1429 elements[typeID.index] = getTermForDomain(domainValue);
1432 auto *row = allocRow(elements);
1433 for (
auto result : op.getResults())
1434 setDomainAssociation(result, row);
1439LogicalResult ModuleState::processOp(RWProbeOp op) {
1440 auto target = globals.getInnerRefNamespace().lookup(op.getTarget());
1442 if (target.isPort()) {
1443 auto targetOp = cast<FModuleOp>(target.getOp());
1444 auto targetValue = targetOp.getArgument(target.getPort());
1445 return unifyAssociations(op, targetValue, op.getResult());
1448 auto targetOp = cast<hw::InnerSymbolOpInterface>(target.getOp());
1449 auto targetValue = targetOp.getTargetResult();
1450 return unifyAssociations(op, targetValue, op.getResult());
1453LogicalResult ModuleState::processOp(Operation *op) {
1454 LLVM_DEBUG(llvm::dbgs().indent(4) <<
"process " << render(op) <<
"\n");
1455 if (
auto instance = dyn_cast<FInstanceLike>(op))
1456 return processOp(instance);
1457 if (
auto wireOp = dyn_cast<WireOp>(op))
1458 return processOp(wireOp);
1459 if (
auto cast = dyn_cast<UnsafeDomainCastOp>(op))
1460 return processOp(cast);
1461 if (
auto def = dyn_cast<DomainDefineOp>(op))
1462 return processOp(def);
1463 if (
auto probe = dyn_cast<RWProbeOp>(op))
1464 return processOp(probe);
1465 if (
auto create = dyn_cast<DomainCreateOp>(op)) {
1466 processDomainDefinition(create);
1469 if (
auto createAnon = dyn_cast<DomainCreateAnonOp>(op)) {
1470 processDomainDefinition(createAnon);
1474 return unifyAssociations(op);
1477LogicalResult ModuleState::processModuleBody(FModuleOp moduleOp) {
1480 .walk([&](Operation *op) -> WalkResult { return processOp(op); })
1484LogicalResult ModuleState::processModule(FModuleOp moduleOp) {
1485 LLVM_DEBUG(llvm::dbgs().indent(2) <<
"processing:\n");
1486 if (failed(processModulePorts(moduleOp)))
1488 if (failed(processModuleBody(moduleOp)))
1493ExportTable ModuleState::initializeExportTable(FModuleOp moduleOp) {
1495 size_t numPorts = moduleOp.getNumPorts();
1496 for (
size_t i = 0; i < numPorts; ++i) {
1497 auto port = dyn_cast<DomainValue>(moduleOp.getArgument(i));
1500 auto value = getOptUnderlyingDomain(port);
1502 exports[value].push_back(port);
1506 llvm::dbgs().indent(2) <<
"domain exports:\n";
1507 for (
auto entry : exports) {
1508 llvm::dbgs().indent(4) << render(entry.first) <<
" exported as ";
1509 llvm::interleaveComma(entry.second, llvm::dbgs(),
1510 [&](
auto e) { llvm::dbgs() << render(e); });
1511 llvm::dbgs() <<
"\n";
1518void ModuleState::ensureSolved(
Namespace &ns, DomainTypeID typeID,
size_t ip,
1519 LocationAttr loc, VariableTerm *var,
1520 PendingUpdates &pending) {
1521 if (pending.solutions.contains(var))
1524 auto *
context = loc.getContext();
1525 auto domainDecl = getDomain(typeID);
1526 auto domainName = domainDecl.getNameAttr();
1528 auto portName = StringAttr::get(
context, ns.
newName(domainName.getValue()));
1529 auto portType = DomainType::getFromDomainOp(domainDecl);
1530 auto portDirection = Direction::In;
1531 auto portSym = StringAttr();
1533 auto portAnnos = std::nullopt;
1535 auto portDomainInfo = ArrayAttr::get(
context, {});
1536 PortInfo portInfo(portName, portType, portDirection, portSym, portLoc,
1537 portAnnos, portDomainInfo);
1539 pending.solutions[var] = pending.insertions.size() + ip;
1540 pending.insertions.push_back({ip, portInfo});
1544 DomainTypeID typeID,
size_t ip,
1545 LocationAttr loc, ValueTerm *val,
1546 PendingUpdates &pending) {
1547 auto value = val->value;
1548 assert(isa<DomainType>(value.getType()));
1549 if (
isPort(value) || exports.contains(value) ||
1550 pending.exports.contains(value))
1553 auto *
context = loc.getContext();
1555 auto domainDecl = getDomain(typeID);
1556 auto domainName = domainDecl.getNameAttr();
1558 auto portName = StringAttr::get(
context, ns.
newName(domainName.getValue()));
1559 auto portType = DomainType::getFromDomainOp(domainDecl);
1560 auto portDirection = Direction::Out;
1561 auto portSym = StringAttr();
1562 auto portAnnos = std::nullopt;
1564 auto portDomainInfo = ArrayAttr::get(
context, {});
1565 PortInfo portInfo(portName, portType, portDirection, portSym, loc, portAnnos,
1567 pending.exports[value] = pending.insertions.size() + ip;
1568 pending.insertions.push_back({ip, portInfo});
1571void ModuleState::getUpdatesForDomainAssociationOfPort(
1572 Namespace &ns, PendingUpdates &pending, DomainTypeID typeID,
size_t ip,
1573 LocationAttr loc, Term *term,
const ExportTable &exports) {
1574 if (
auto *var = dyn_cast<VariableTerm>(term)) {
1575 ensureSolved(ns, typeID, ip, loc, var, pending);
1578 if (
auto *val = dyn_cast<ValueTerm>(term)) {
1579 ensureExported(ns, exports, typeID, ip, loc, val, pending);
1582 llvm_unreachable(
"invalid domain association");
1585void ModuleState::getUpdatesForDomainAssociationOfPort(
1587 RowTerm *row, PendingUpdates &pending) {
1588 for (
auto [index, term] :
llvm::enumerate(row->elements))
1589 getUpdatesForDomainAssociationOfPort(ns, pending, DomainTypeID{index}, ip,
1590 loc, find(term), exports);
1593void ModuleState::getUpdatesForModulePorts(FModuleOp moduleOp,
1596 PendingUpdates &pending) {
1597 for (
size_t i = 0, e = moduleOp.getNumPorts(); i < e; ++i) {
1598 auto port = moduleOp.getArgument(i);
1602 getUpdatesForDomainAssociationOfPort(
1603 ns, exports, i, moduleOp.getPortLocation(i),
1604 getDomainAssociationAsRow(port), pending);
1608void ModuleState::getUpdatesForModule(FModuleOp moduleOp,
1610 PendingUpdates &pending) {
1612 auto names = moduleOp.getPortNamesAttr();
1613 for (
auto name : names.getAsRange<StringAttr>())
1615 getUpdatesForModulePorts(moduleOp, exports, ns, pending);
1618void ModuleState::applyUpdatesToModule(FModuleOp moduleOp,
ExportTable &exports,
1619 const PendingUpdates &pending) {
1620 LLVM_DEBUG(llvm::dbgs().indent(2) <<
"applying updates:\n");
1622 moduleOp.insertPorts(pending.insertions);
1626 for (
auto [var, portIndex] : pending.solutions) {
1627 auto portValue = cast<DomainValue>(moduleOp.getArgument(portIndex));
1628 auto *solution = allocVal(portValue);
1629 LLVM_DEBUG(llvm::dbgs().indent(4)
1630 <<
"new-input " << render(portValue) <<
"\n");
1631 solve(var, solution);
1632 exports[portValue].push_back(portValue);
1633 globals.inserted.insert(portValue);
1637 auto builder = OpBuilder::atBlockEnd(moduleOp.getBodyBlock());
1638 for (
auto [domainValue, portIndex] : pending.exports) {
1639 auto portValue = cast<DomainValue>(moduleOp.getArgument(portIndex));
1640 builder.setInsertionPointAfterValue(domainValue);
1641 DomainDefineOp::create(builder, portValue.getLoc(), portValue, domainValue);
1642 LLVM_DEBUG(llvm::dbgs().indent(4) <<
"new-output " << render(portValue)
1643 <<
" := " << render(domainValue) <<
"\n");
1644 exports[domainValue].push_back(portValue);
1645 globals.inserted.insert(portValue);
1646 setTermForDomain(portValue, allocVal(domainValue));
1650SmallVector<Attribute> ModuleState::copyPortDomainAssociations(
1651 FModuleOp moduleOp, ArrayAttr moduleDomainInfo,
size_t portIndex) {
1652 SmallVector<Attribute> result(getNumDomains());
1654 for (
auto domainPortIndexAttr : oldAssociations) {
1655 auto domainPortIndex = domainPortIndexAttr.getUInt();
1656 auto domainTypeID = getDomainTypeID(moduleOp, domainPortIndex);
1657 result[domainTypeID.index] = domainPortIndexAttr;
1662LogicalResult ModuleState::driveModuleOutputDomainPorts(FModuleOp moduleOp) {
1663 auto builder = OpBuilder::atBlockEnd(moduleOp.getBodyBlock());
1664 for (
size_t i = 0, e = moduleOp.getNumPorts(); i < e; ++i) {
1665 auto port = dyn_cast<DomainValue>(moduleOp.getArgument(i));
1666 if (!port || moduleOp.getPortDirection(i) == Direction::In ||
1670 auto *term = getOptTermForDomain(port);
1671 auto *val = llvm::dyn_cast_if_present<ValueTerm>(term);
1673 emitDomainPortInferenceError(moduleOp, i);
1677 auto loc = port.getLoc();
1678 auto value = val->value;
1679 LLVM_DEBUG(llvm::dbgs().indent(4) <<
"connect " << render(port)
1680 <<
" := " << render(value) <<
"\n");
1681 DomainDefineOp::create(builder, loc, port, value);
1687LogicalResult ModuleState::updateModuleDomainInfo(
1688 FModuleOp moduleOp,
const ExportTable &exportTable, ArrayAttr &result) {
1693 auto *
context = moduleOp.getContext();
1694 auto numDomains = getNumDomains();
1695 auto oldModuleDomainInfo = moduleOp.getDomainInfoAttr();
1696 auto numPorts = moduleOp.getNumPorts();
1697 SmallVector<Attribute> newModuleDomainInfo(numPorts);
1699 for (
size_t i = 0; i < numPorts; ++i) {
1700 auto port = moduleOp.getArgument(i);
1701 auto type = port.getType();
1703 if (isa<DomainType>(type)) {
1705 newModuleDomainInfo[i] = ArrayAttr::get(
context, {});
1710 newModuleDomainInfo[i] = ArrayAttr::get(
context, {});
1715 copyPortDomainAssociations(moduleOp, oldModuleDomainInfo, i);
1716 auto *row = cast<RowTerm>(getDomainAssociation(port));
1717 for (
size_t domainIndex = 0; domainIndex < numDomains; ++domainIndex) {
1718 auto domainTypeID = DomainTypeID{domainIndex};
1719 if (associations[domainIndex])
1722 auto domain = cast<ValueTerm>(find(row->elements[domainIndex]))->value;
1723 auto &exports = exportTable.at(domain);
1724 if (exports.empty()) {
1725 auto portName = moduleOp.getPortNameAttr(i);
1726 auto portLoc = moduleOp.getPortLocation(i);
1727 auto domainDecl = getDomain(domainTypeID);
1728 auto domainName = domainDecl.getNameAttr();
1729 auto diag = emitError(portLoc) <<
"private " << domainName
1730 <<
" association for port " << portName;
1731 diag.attachNote(domain.getLoc()) <<
"associated domain: " << domain;
1732 noteLocation(diag, moduleOp);
1736 if (exports.size() > 1) {
1737 emitAmbiguousPortDomainAssociation(moduleOp, exports, domainTypeID, i);
1741 auto argument = cast<BlockArgument>(exports[0]);
1742 auto domainPortIndex = argument.getArgNumber();
1743 associations[domainTypeID.index] =
1744 IntegerAttr::get(IntegerType::get(
context, 32, IntegerType::Unsigned),
1748 newModuleDomainInfo[i] = ArrayAttr::get(
context, associations);
1751 result = ArrayAttr::get(moduleOp.getContext(), newModuleDomainInfo);
1752 moduleOp.setDomainInfoAttr(result);
1757 OpBuilder &builder, DenseMap<DomainValue, DomainValue> &domainsInScope,
1758 Operation *user, DomainType type, VariableTerm *var) {
1759 auto name = type.getName().getAttr();
1761 DomainCreateAnonOp::create(builder, user->getLoc(), type, name);
1763 LLVM_DEBUG(llvm::dbgs().indent(6) <<
"create anon " << render(anon) <<
"\n");
1764 solve(var, allocVal(anon));
1765 domainsInScope[anon] = anon;
1766 globals.inserted.insert(anon);
1771 OpBuilder &builder, DenseMap<DomainValue, DomainValue> &domainsInScope,
1773 auto &domainInScope = domainsInScope[domain];
1775 return domainInScope;
1777 domainInScope = cast<DomainValue>(
1778 WireOp::create(builder, domain.getLoc(), domain.getType(),
1779 domain.getType().getName().getAttr())
1782 OpBuilder::InsertionGuard guard(builder);
1783 builder.setInsertionPointAfterValue(domain);
1784 DomainDefineOp::create(builder, domain.getLoc(), domainInScope, domain);
1786 LLVM_DEBUG(llvm::dbgs().indent(6) <<
"bounce wire " << render(domainInScope)
1787 <<
" := " << render(domain) <<
"\n");
1788 return domainInScope;
1792ModuleState::updateInstance(DenseMap<DomainValue, DomainValue> &domainsInScope,
1794 LLVM_DEBUG(llvm::dbgs().indent(4) <<
"update " << render(op) <<
"\n");
1795 OpBuilder builder(op.getContext());
1796 builder.setInsertionPointAfter(op);
1797 auto numPorts = op->getNumResults();
1799 for (
size_t i = 0; i < numPorts; ++i)
1800 if (
auto port = dyn_cast<DomainValue>(op->getResult(i)))
1801 if (op.getPortDirection(i) == Direction::Out)
1802 domainsInScope[port] = port;
1804 for (
size_t i = 0; i < numPorts; ++i) {
1805 auto port = dyn_cast<DomainValue>(op->getResult(i));
1806 auto direction = op.getPortDirection(i);
1810 if (port && direction == Direction::In && !
isDriven(port)) {
1811 auto loc = port.getLoc();
1812 auto *term = getTermForDomain(port);
1813 if (
auto *var = dyn_cast<VariableTerm>(term)) {
1814 auto domain = solveVarWithAnonDomain(builder, domainsInScope, op,
1815 port.getType(), var);
1816 LLVM_DEBUG(llvm::dbgs().indent(6) <<
"connect " << render(port)
1817 <<
" := " << render(domain) <<
"\n");
1818 DomainDefineOp::create(builder, loc, port, domain);
1821 if (
auto *val = dyn_cast<ValueTerm>(term)) {
1822 auto domain = getDomainInScope(builder, domainsInScope, val->value);
1823 LLVM_DEBUG(llvm::dbgs().indent(6) <<
"connect " << render(port)
1824 <<
" := " << render(domain) <<
"\n");
1825 DomainDefineOp::create(builder, loc, port, domain);
1828 llvm_unreachable(
"unhandled domain term type");
1836ModuleState::updateWire(DenseMap<DomainValue, DomainValue> &domainsInScope,
1838 auto result = wireOp.getResult();
1840 if (
auto tgt = dyn_cast<DomainValue>(result)) {
1844 LLVM_DEBUG(llvm::dbgs().indent(4) <<
"update " << render(wireOp) <<
"\n");
1845 OpBuilder builder(wireOp);
1846 builder.setInsertionPointAfter(wireOp);
1847 auto *term = getTermForDomain(tgt);
1848 if (
auto *var = dyn_cast<VariableTerm>(term)) {
1849 auto src = solveVarWithAnonDomain(builder, domainsInScope, wireOp,
1850 tgt.getType(), var);
1851 LLVM_DEBUG(llvm::dbgs().indent(6)
1852 <<
"connect " << render(tgt) <<
" := " << render(src) <<
"\n");
1853 DomainDefineOp::create(builder, wireOp.getLoc(), tgt, src);
1856 if (
auto *val = dyn_cast<ValueTerm>(term)) {
1857 auto src = getDomainInScope(builder, domainsInScope, val->value);
1858 LLVM_DEBUG(llvm::dbgs().indent(6)
1859 <<
"connect " << render(tgt) <<
" := " << render(src) <<
"\n");
1860 DomainDefineOp::create(builder, wireOp.getLoc(), tgt, src);
1863 llvm_unreachable(
"unhandled domain term type");
1866 if (!
isHardware(result) || isColorless(result))
1869 LLVM_DEBUG(llvm::dbgs().indent(4) <<
"update " << render(wireOp) <<
"\n");
1870 OpBuilder builder(wireOp);
1871 auto *row = getDomainAssociationAsRow(wireOp.getResult());
1873 SmallVector<Value> domainOperands;
1874 for (
auto [i, element] :
llvm::enumerate(
1875 llvm::map_range(row->elements, [&](auto e) {
return find(e); }))) {
1876 if (
auto *val = dyn_cast<ValueTerm>(element)) {
1877 domainOperands.push_back(
1878 getDomainInScope(builder, domainsInScope, val->value));
1881 if (
auto *var = dyn_cast<VariableTerm>(element)) {
1882 auto type = DomainType::getFromDomainOp(getDomain(DomainTypeID{i}));
1884 solveVarWithAnonDomain(builder, domainsInScope, wireOp, type, var);
1885 domainOperands.push_back(domain);
1888 assert(0 &&
"unhandled domain type");
1890 wireOp.getDomainsMutable().assign(domainOperands);
1894LogicalResult ModuleState::updateModuleBody(FModuleOp moduleOp) {
1895 DenseMap<DomainValue, DomainValue> domainsInScope;
1897 for (
size_t i = 0, e = moduleOp.getNumPorts(); i < e; ++i)
1898 if (
auto port = dyn_cast<DomainValue>(moduleOp.getArgument(i)))
1899 if (moduleOp.getPortDirection(i) == Direction::In)
1900 domainsInScope[port] = port;
1902 auto result = moduleOp.getBodyBlock()->walk([&](Operation *op) -> WalkResult {
1903 return TypeSwitch<Operation *, WalkResult>(op)
1905 [&](
auto wire) {
return updateWire(domainsInScope, wire); })
1906 .Case<FInstanceLike>([&](
auto instance) {
1907 return updateInstance(domainsInScope, instance);
1909 .Case<DomainCreateOp, DomainCreateAnonOp>([&](
auto domain) {
1910 domainsInScope[domain] = domain;
1913 .Default([&](
auto op) {
return success(); });
1915 return failure(result.wasInterrupted());
1918LogicalResult ModuleState::updateModule(FModuleOp moduleOp) {
1919 auto exports = initializeExportTable(moduleOp);
1920 PendingUpdates pending;
1921 getUpdatesForModule(moduleOp, exports, pending);
1922 applyUpdatesToModule(moduleOp, exports, pending);
1924 ArrayAttr portDomainInfo;
1925 if (failed(updateModuleDomainInfo(moduleOp, exports, portDomainInfo)))
1928 if (failed(driveModuleOutputDomainPorts(moduleOp)))
1932 auto &entry = getModuleUpdateTable()[moduleOp.getModuleNameAttr()];
1933 entry.portDomainInfo = portDomainInfo;
1934 entry.portInsertions = std::move(pending.insertions);
1936 if (failed(updateModuleBody(moduleOp)))
1940 llvm::dbgs().indent(2) <<
"port summary:\n";
1941 for (
auto port : moduleOp.
getBodyBlock()->getArguments()) {
1942 llvm::dbgs().indent(4) << render(port);
1943 auto info = cast<ArrayAttr>(
1944 moduleOp.getDomainInfoAttrForPort(port.getArgNumber()));
1946 llvm::dbgs() <<
" domains [";
1947 llvm::interleaveComma(
1948 info.getAsRange<IntegerAttr>(), llvm::dbgs(), [&](
auto i) {
1949 llvm::dbgs() << render(moduleOp.getArgument(i.getUInt()));
1951 llvm::dbgs() <<
"]";
1953 llvm::dbgs() <<
"\n";
1960LogicalResult ModuleState::checkModulePorts(FModuleLike moduleOp) {
1961 auto numDomains = getNumDomains();
1962 auto domainInfo = moduleOp.getDomainInfoAttr();
1963 auto numPorts = moduleOp.getNumPorts();
1965 DenseMap<unsigned, DomainTypeID> domainTypeIDTable;
1966 for (
size_t i = 0; i < numPorts; ++i) {
1967 if (isa<DomainType>(moduleOp.getPortType(i)))
1968 domainTypeIDTable[i] = getDomainTypeID(moduleOp, i);
1971 for (
size_t i = 0; i < numPorts; ++i) {
1976 SmallVector<IntegerAttr> associations(numDomains);
1978 auto domainTypeID = domainTypeIDTable.at(domainPortIndex.getUInt());
1979 auto prevDomainPortIndex = associations[domainTypeID.index];
1980 if (prevDomainPortIndex) {
1981 emitDuplicatePortDomainError(moduleOp, i, domainTypeID,
1982 prevDomainPortIndex, domainPortIndex);
1985 associations[domainTypeID.index] = domainPortIndex;
1989 for (
size_t domainIndex = 0; domainIndex < numDomains; ++domainIndex) {
1990 auto typeID = DomainTypeID{domainIndex};
1991 if (!associations[domainIndex]) {
1992 emitMissingPortDomainAssociationError(moduleOp, typeID, i);
2001LogicalResult ModuleState::checkModuleDomainPortDrivers(FModuleOp moduleOp) {
2002 for (
size_t i = 0, e = moduleOp.getNumPorts(); i < e; ++i) {
2003 auto port = dyn_cast<DomainValue>(moduleOp.getArgument(i));
2004 if (!port || moduleOp.getPortDirection(i) != Direction::Out ||
2008 auto name = moduleOp.getPortNameAttr(i);
2009 auto diag = emitError(moduleOp.getPortLocation(i))
2010 <<
"undriven domain port " << name;
2011 noteLocation(diag, moduleOp);
2018LogicalResult ModuleState::checkInstanceDomainPortDrivers(FInstanceLike op) {
2019 for (
size_t i = 0, e = op->getNumResults(); i < e; ++i) {
2020 auto port = dyn_cast<DomainValue>(op->getResult(i));
2021 if (!port || op.getPortDirection(i) != Direction::In ||
isDriven(port))
2024 auto name = op.getPortNameAttr(i);
2025 auto diag = emitError(op.getPortLocation(i))
2026 <<
"undriven domain port " << name;
2027 noteLocation(diag, op);
2034LogicalResult ModuleState::checkModuleBody(FModuleOp moduleOp) {
2035 auto result = moduleOp.getBody().walk([&](FInstanceLike op) -> WalkResult {
2036 return checkInstanceDomainPortDrivers(op);
2038 return failure(result.wasInterrupted());
2041LogicalResult ModuleState::inferModule(FModuleOp moduleOp) {
2042 LLVM_DEBUG(llvm::dbgs() <<
"infer: " << moduleOp.getModuleName() <<
"\n");
2043 if (failed(processModule(moduleOp)))
2046 return updateModule(moduleOp);
2049LogicalResult ModuleState::checkModule(FModuleOp moduleOp) {
2050 LLVM_DEBUG(llvm::dbgs() <<
"check: " << moduleOp.getModuleName() <<
"\n");
2051 if (failed(checkModulePorts(moduleOp)))
2054 if (failed(checkModuleDomainPortDrivers(moduleOp)))
2057 if (failed(checkModuleBody(moduleOp)))
2060 return processModule(moduleOp);
2063LogicalResult ModuleState::checkModule(FExtModuleOp extModuleOp) {
2064 LLVM_DEBUG(llvm::dbgs() <<
"check: " << extModuleOp.getModuleName() <<
"\n");
2065 return checkModulePorts(extModuleOp);
2068LogicalResult ModuleState::checkAndInferModule(FModuleOp moduleOp) {
2069 LLVM_DEBUG(llvm::dbgs() <<
"check/infer: " << moduleOp.getModuleName()
2072 if (failed(checkModulePorts(moduleOp)))
2075 if (failed(processModule(moduleOp)))
2078 if (failed(driveModuleOutputDomainPorts(moduleOp)))
2081 return updateModuleBody(moduleOp);
2093 llvm::function_ref<
bool(StringAttr)> shouldStripDomain) {
2094 auto shouldStripType = [&](Type type) {
2095 if (
auto domainType = dyn_cast<DomainType>(type))
2096 return shouldStripDomain(domainType.getName().getAttr());
2099 WalkResult result = op->walk<mlir::WalkOrder::PostOrder, ReverseIterator>(
2100 [&](Operation *op) -> WalkResult {
2101 return TypeSwitch<Operation *, WalkResult>(op)
2102 .Case<FModuleLike>([&](FModuleLike op) {
2103 BitVector erasures(op.getNumPorts());
2104 for (
size_t i = 0, e = op.getNumPorts(); i < e; ++i)
2105 if (shouldStripType(op.getPortType(i)))
2108 op.erasePorts(erasures);
2109 return WalkResult::advance();
2111 .Case<DomainDefineOp>([&](DomainDefineOp op) {
2112 if (shouldStripType(op.getDest().getType()) ||
2113 shouldStripType(op.getSrc().getType()))
2115 return WalkResult::advance();
2117 .Case<DomainCreateOp>([&](DomainCreateOp op) {
2118 if (shouldStripType(op.getType()))
2120 return WalkResult::advance();
2122 .Case<DomainCreateAnonOp>([&](DomainCreateAnonOp op) {
2123 if (shouldStripType(op.getType()))
2125 return WalkResult::advance();
2127 .Case<DomainSubfieldOp>([&](DomainSubfieldOp op) {
2130 if (shouldStripType(op.getInput().getType())) {
2131 if (!op->use_empty()) {
2132 OpBuilder builder(op);
2133 op.replaceAllUsesWith(
2134 UnknownValueOp::create(builder, op.getLoc(), op.getType())
2139 return WalkResult::advance();
2141 .Case<UnsafeDomainCastOp>([&](UnsafeDomainCastOp op) {
2144 if (llvm::any_of(op.getDomains(), [&](Value domain) {
2145 return shouldStripType(domain.getType());
2147 op.replaceAllUsesWith(op.getInput());
2150 return WalkResult::advance();
2152 .Case<WireOp>([&](WireOp op) {
2154 if (shouldStripType(op.getType(0))) {
2156 return WalkResult::advance();
2158 BitVector erasures(op.getDomains().size());
2161 for (
int i = 0, e = op.getDomains().size(); i < e; ++i)
2162 if (shouldStripType(op.getDomains()[i].getType()))
2165 op->eraseOperands(erasures);
2166 return WalkResult::advance();
2168 .Case<FInstanceLike>([&](
auto op) {
2169 auto n = op.getNumPorts();
2170 BitVector erasures(n);
2171 for (
size_t i = 0; i < n; ++i)
2172 if (shouldStripType(op->getResult(i).getType()))
2174 if (erasures.any()) {
2175 op.cloneWithErasedPortsAndReplaceUses(erasures);
2178 return WalkResult::advance();
2180 .Default([&](Operation *op) {
2185 concat<Type>(op->getOperandTypes(), op->getResultTypes())) {
2186 if (isa<DomainType>(type)) {
2187 op->emitOpError(
"cannot be stripped");
2188 return WalkResult::interrupt();
2191 return WalkResult::advance();
2194 return failure(result.wasInterrupted());
2198 MLIRContext *
context, CircuitOp circuit,
2199 llvm::function_ref<
bool(StringAttr)> shouldStripDomain) {
2201 llvm::SmallVector<FModuleLike> modules;
2202 for (Operation &op : make_early_inc_range(*circuit.getBodyBlock())) {
2203 TypeSwitch<Operation *, void>(&op)
2204 .Case<FModuleLike>([&](FModuleLike op) { modules.push_back(op); })
2205 .Case<DomainOp>([&](DomainOp op) {
2207 if (shouldStripDomain(op.getNameAttr()))
2213 return failableParallelForEach(
context, modules, [&](FModuleLike module) {
2222LogicalResult CircuitState::runOnModule(Operation *op) {
2223 assert(mode != InferDomainsMode::Strip);
2224 ModuleState state(*
this);
2225 if (
auto moduleOp = dyn_cast<FModuleOp>(op)) {
2226 if (mode == InferDomainsMode::Check)
2227 return state.checkModule(moduleOp);
2229 if (mode == InferDomainsMode::InferAll || moduleOp.isPrivate())
2230 return state.inferModule(moduleOp);
2232 return state.checkAndInferModule(moduleOp);
2235 if (
auto extModuleOp = dyn_cast<FExtModuleOp>(op))
2236 return state.checkModule(extModuleOp);
2241LogicalResult CircuitState::run() {
2242 DenseSet<Operation *> errored;
2243 instanceGraph.walkPostOrder([&](
auto &node) {
2244 auto moduleOp = node.getModule();
2245 for (
auto *inst : node) {
2246 if (errored.contains(inst->getTarget()->getModule())) {
2247 errored.insert(moduleOp);
2251 if (failed(runOnModule(node.getModule())))
2252 errored.insert(moduleOp);
2254 return success(errored.empty());
2258struct InferDomainsPass
2259 :
public circt::firrtl::impl::InferDomainsBase<InferDomainsPass> {
2261 void runOnOperation()
override {
2263 auto circuit = getOperation();
2265 if (mode == InferDomainsMode::Strip) {
2268 [](StringAttr) {
return true; })))
2269 signalPassFailure();
2274 if (!skippedDomains.empty()) {
2275 DenseSet<StringAttr> skippedNames;
2276 auto *
context = &getContext();
2277 for (
const auto &name : skippedDomains)
2278 skippedNames.insert(StringAttr::
get(
context, name));
2282 return skippedNames.contains(name);
2284 return signalPassFailure();
2287 auto &instanceGraph = getAnalysis<InstanceGraph>();
2288 auto &symbolTable = getAnalysis<SymbolTable>();
2289 auto &innerSymbolTableCollection =
2290 getAnalysis<InnerSymbolTableCollection>();
2292 innerSymbolTableCollection};
2293 CircuitState state(circuit, instanceGraph, innerRefNamespace, mode);
2294 if (failed(state.run()))
2295 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 LogicalResult stripDomainsFromCircuit(MLIRContext *context, CircuitOp circuit, llvm::function_ref< bool(StringAttr)> shouldStripDomain)
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 LogicalResult stripModuleImpl(FModuleLike op, llvm::function_ref< bool(StringAttr)> shouldStripDomain)
A helper for stripping domains from a module based on a predicate.
static bool isDriven(DomainValue port)
Returns true if the value is driven by a connect op.
static Block * getBodyBlock(FModuleLike mod)
#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)
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.