28 #include "mlir/IR/Diagnostics.h"
29 #include "mlir/Pass/Pass.h"
30 #include "llvm/ADT/PostOrderIterator.h"
31 #include "llvm/ADT/StringExtras.h"
32 #include "llvm/Support/Debug.h"
34 #define DEBUG_TYPE "lower-annos"
38 #define GEN_PASS_DEF_LOWERFIRRTLANNOTATIONS
39 #include "circt/Dialect/FIRRTL/Passes.h.inc"
43 using namespace circt;
44 using namespace firrtl;
45 using namespace chirrtl;
58 SmallVector<Attribute> old(array.begin(), array.end());
66 SmallVector<Attribute> old(array.begin(), array.end());
74 ArrayRef<NamedAttribute> anno) {
75 auto *context = ref.
getOp()->getContext();
76 DictionaryAttr annotation;
78 SmallVector<NamedAttribute> annoField(anno.begin(), anno.end());
79 annoField.emplace_back(
88 if (isa<OpAnnoTarget>(ref)) {
94 auto portRef = cast<PortAnnoTarget>(ref);
96 ArrayAttr portAnno = dyn_cast_or_null<ArrayAttr>(portAnnoRaw);
98 SmallVector<Attribute> emptyPortAttr(
104 portAnno, portRef.getPortNo(),
107 ref.
getOp()->setAttr(
"portAnnotations", portAnno);
114 OpBuilder b(state.
circuit.getBodyRegion());
115 SmallVector<Attribute> insts;
118 state.
getNamespace(inst->getParentOfType<FModuleLike>())));
135 FlatSymbolRefAttr sym =
buildNLA(target, state);
144 static std::optional<AnnoPathValue>
noResolve(DictionaryAttr anno,
154 StringRef path{pathStr};
158 mlir::emitError(state.
circuit.getLoc())
159 <<
"Cannot tokenize annotation path " << rawPath;
172 auto target = anno.getNamed(
"target");
174 mlir::emitError(state.
circuit.getLoc())
175 <<
"No target field in annotation " << anno;
178 if (!isa<StringAttr>(target->getValue())) {
179 mlir::emitError(state.
circuit.getLoc())
180 <<
"Target field in annotation doesn't contain string " << anno;
183 return stdResolveImpl(cast<StringAttr>(target->getValue()).getValue(), state);
189 auto target = anno.getNamed(
"target");
191 return stdResolveImpl(cast<StringAttr>(target->getValue()).getValue(),
206 bool allowNonLocal) {
207 if (!allowNonLocal && !target.
isLocal()) {
209 auto diag = mlir::emitError(target.
ref.
getOp()->getLoc())
210 <<
"is targeted by a non-local annotation \""
211 << annotation.
getClass() <<
"\" with target "
213 <<
", but this annotation cannot be non-local";
214 diag.attachNote() <<
"see current annotation: " << anno <<
"\n";
217 SmallVector<NamedAttribute> newAnnoAttrs;
218 for (
auto &na : anno) {
219 if (na.getName().getValue() !=
"target") {
220 newAnnoAttrs.push_back(na);
221 }
else if (!target.
isLocal()) {
223 newAnnoAttrs.push_back(
244 auto loc = op->getLoc();
247 return mlir::emitError(loc) <<
"must be local";
249 if (!isa<OpAnnoTarget>(target.
ref) || !isa<FModuleLike>(op))
250 return mlir::emitError(loc) <<
"can only target to a module";
252 auto moduleOp = cast<FModuleLike>(op);
255 moduleOp.setPublic();
256 SmallVector<NamedAttribute> newAnnoAttrs;
257 for (
auto &na : anno)
258 if (na.getName().getValue() !=
"target")
259 newAnnoAttrs.push_back(na);
266 return ::llvm::StringSwitch<::std::optional<Convention>>(str)
267 .Case(
"scalarized", Convention::Scalarized)
268 .Default(std::nullopt);
275 auto loc = op->getLoc();
277 auto diag = mlir::emitError(loc);
278 diag <<
"circuit.ConventionAnnotation ";
282 auto opTarget = dyn_cast<OpAnnoTarget>(target.
ref);
284 return error() <<
"must target a module object";
287 return error() <<
"must be local";
289 auto conventionStrAttr =
291 if (!conventionStrAttr)
294 auto conventionStr = conventionStrAttr.getValue();
297 return error() <<
"unknown convention " << conventionStr;
299 auto convention = *conventionOpt;
301 if (
auto moduleOp = dyn_cast<FModuleOp>(op)) {
302 moduleOp.setConvention(convention);
306 if (
auto extModuleOp = dyn_cast<FExtModuleOp>(op)) {
307 extModuleOp.setConvention(convention);
311 return error() <<
"can only target to a module or extmodule";
318 auto loc = op->getLoc();
320 auto diag = mlir::emitError(loc);
325 auto opTarget = dyn_cast<OpAnnoTarget>(target.
ref);
327 return error() <<
"must target a module object";
330 return error() <<
"must be local";
332 auto moduleOp = dyn_cast<FModuleOp>(op);
335 return error() <<
"can only target to a module";
337 auto conventionStrAttr =
340 if (!conventionStrAttr)
343 auto conventionStr = conventionStrAttr.getValue();
346 return error() <<
"unknown convention " << conventionStr;
348 auto convention = *conventionOpt;
350 if (convention == Convention::Internal)
357 bool includeHierarchy =
false;
358 if (
auto includeHierarchyAttr = tryGetAs<BoolAttr>(
360 includeHierarchy = includeHierarchyAttr.getValue();
362 if (includeHierarchy) {
369 if (
auto fmodule = dyn_cast<FModuleOp>(*node->getModule()))
370 fmodule->setAttr(
"body_type_lowering", conventionAttr);
374 moduleOp->setAttr(
"body_type_lowering", conventionAttr);
384 auto loc = op->getLoc();
386 auto diag = mlir::emitError(loc);
391 auto opTarget = dyn_cast<OpAnnoTarget>(target.
ref);
393 return error() <<
"must target an operation";
395 if (!isa<SeqMemOp, CombMemOp, MemOp>(opTarget.getOp()))
396 return error() <<
"must target a memory operation";
399 return error() <<
"must be local";
406 if (
auto mem = dyn_cast<SeqMemOp>(op))
407 mem.setPrefixAttr(prefixStrAttr);
408 else if (
auto mem = dyn_cast<CombMemOp>(op))
409 mem.setPrefixAttr(prefixStrAttr);
410 else if (
auto mem = dyn_cast<MemOp>(op))
411 mem.setPrefixAttr(prefixStrAttr);
422 auto diag = mlir::emitError(op->getLoc());
423 diag << anno.getAs<StringAttr>(
"class").getValue() <<
" ";
427 if (!isa<OpAnnoTarget>(target.
ref))
429 <<
"must target an operation. Currently ports are not supported";
432 return error() <<
"must be local";
434 if (!isa<FModuleOp, WireOp, NodeOp, RegOp, RegResetOp>(op))
436 <<
"unhandled operation. The target must be a module, wire, node or "
439 auto name = anno.getAs<StringAttr>(
"description");
446 template <
bool isInline>
451 mlir::emitError(state.
circuit.getLoc())
452 <<
"has a " << anno.get(
"class")
453 <<
" annotation which is non-local, but this annotation is not allowed "
460 if (!target.
isOpOfType<MemOp, CombMemOp, SeqMemOp>()) {
461 mlir::emitError(op->getLoc())
462 <<
"can only apply a load memory annotation to a memory";
467 StringAttr filename = tryGetAs<StringAttr>(
468 anno, anno, isInline ?
"filename" :
"fileName", op->getLoc(),
469 anno.getAs<StringAttr>(
"class").getValue());
474 tryGetAs<StringAttr>(anno, anno,
"hexOrBinary", op->getLoc(),
475 anno.getAs<StringAttr>(
"class").getValue());
479 auto hexOrBinaryValue = hexOrBinary.getValue();
480 if (hexOrBinaryValue !=
"h" && hexOrBinaryValue !=
"b") {
481 auto diag = mlir::emitError(op->getLoc())
482 <<
"has memory initialization annotation with invalid format, "
483 "'hexOrBinary' field must be either 'h' or 'b'";
484 diag.attachNote() <<
"the full annotation is: " << anno;
489 hexOrBinaryValue ==
"b", isInline));
498 auto *context = op->getContext();
499 auto loc = op->getLoc();
505 auto opTarget = dyn_cast<OpAnnoTarget>(target.
ref);
507 return error() <<
"must target a module";
509 return error() <<
"must be local";
511 auto moduleOp = dyn_cast<FModuleOp>(op);
513 return error() <<
"must target a module";
514 if (!moduleOp.isPublic())
515 return error() <<
"must target a public module";
516 if (moduleOp->hasAttr(
"output_file"))
517 return error() <<
"target already has an output file";
524 return error() <<
"dirname must not be empty";
527 hw::OutputFileAttr::getAsDirectory(context, dirname.getValue());
529 moduleOp->setAttr(
"output_file", outputFile);
538 auto *context = op->getContext();
540 mlir::emitWarning(op->getLoc())
544 NamedAttrList newAnno(anno.getValue());
550 return applyWithoutTarget<false>(target, newDictionary, state);
558 auto *context = op->getContext();
560 mlir::emitWarning(op->getLoc())
564 NamedAttrList newAnno(anno.getValue());
569 return applyWithoutTarget<true, FModuleOp>(target, newDictionary, state);
590 applyWithoutTarget<false, CircuitOp>};
595 {
"circt.test", {
stdResolve, applyWithoutTarget<true>}},
596 {
"circt.testLocalOnly", {
stdResolve, applyWithoutTarget<>}},
597 {
"circt.testNT", {
noResolve, applyWithoutTarget<>}},
598 {
"circt.missing", {
tryResolve, applyWithoutTarget<true>}},
625 RegResetOp, InstanceOp, MemOp, CombMemOp,
626 MemoryPortOp, SeqMemOp>}},
633 {
stdResolve, applyWithoutTarget<true, MemOp, CombMemOp>}},
639 {
stdResolve, applyWithoutTarget<true, FModuleOp, FExtModuleOp>}},
643 {
stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
645 {
stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
647 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
649 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
651 {
stdResolve, applyWithoutTarget<false, FModuleOp>}},
653 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
657 {
stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
676 {
stdResolve, applyWithoutTarget<true, FModuleOp>}},
693 const std::function<
void(llvm::Twine)> &errorHandler) {
696 return LogicalResult::success();
698 errorHandler(
"annotation record '" + annoClass +
"' is registered twice\n");
699 return LogicalResult::failure();
707 bool ignoreAnnotationUnknown) {
711 if (ignoreAnnotationUnknown)
721 struct LowerAnnotationsPass
722 :
public circt::firrtl::impl::LowerFIRRTLAnnotationsBase<
723 LowerAnnotationsPass> {
724 void runOnOperation()
override;
725 LogicalResult applyAnnotation(DictionaryAttr anno,
ApplyState &state);
726 LogicalResult legacyToWiringProblems(
ApplyState &state);
727 LogicalResult solveWiringProblems(
ApplyState &state);
729 using LowerFIRRTLAnnotationsBase::allowAddingPortsOnPublic;
730 using LowerFIRRTLAnnotationsBase::ignoreAnnotationClassless;
731 using LowerFIRRTLAnnotationsBase::ignoreAnnotationUnknown;
732 using LowerFIRRTLAnnotationsBase::noRefTypePorts;
733 SmallVector<DictionaryAttr> worklistAttrs;
737 LogicalResult LowerAnnotationsPass::applyAnnotation(DictionaryAttr anno,
739 LLVM_DEBUG(llvm::dbgs() <<
" - anno: " << anno <<
"\n";);
742 StringRef annoClassVal;
743 if (
auto annoClass = anno.getNamed(
"class"))
744 annoClassVal = cast<StringAttr>(annoClass->getValue()).getValue();
745 else if (ignoreAnnotationClassless)
746 annoClassVal =
"circt.missing";
748 return mlir::emitError(state.
circuit.getLoc())
749 <<
"Annotation without a class: " << anno;
755 if (!ignoreAnnotationUnknown)
756 return mlir::emitError(state.
circuit.getLoc())
757 <<
"Unhandled annotation: " << anno;
765 auto target = record->resolver(anno, state);
767 return mlir::emitError(state.
circuit.getLoc())
768 <<
"Unable to resolve target of annotation: " << anno;
769 if (record->applier(*target, anno, state).failed())
770 return mlir::emitError(state.
circuit.getLoc())
771 <<
"Unable to apply annotation: " << anno;
777 LogicalResult LowerAnnotationsPass::legacyToWiringProblems(
ApplyState &state) {
780 return mlir::emitError(state.
circuit.getLoc())
781 <<
"Unable to resolve source for pin: " << name;
783 if (problem.sinks.empty())
784 return mlir::emitError(state.
circuit.getLoc())
785 <<
"Unable to resolve sink(s) for pin: " << name;
787 for (
const auto &sink : problem.sinks) {
802 LogicalResult LowerAnnotationsPass::solveWiringProblems(
ApplyState &state) {
805 auto getModule = [](Value value) {
806 if (BlockArgument blockArg = dyn_cast<BlockArgument>(value))
807 return cast<FModuleLike>(blockArg.getParentBlock()->getParentOp());
808 return value.getDefiningOp()->getParentOfType<FModuleLike>();
812 auto findInsertionBlock = [&getModule](Value src, Value dest) -> Block * {
814 if (src.getParentBlock() == dest.getParentBlock())
815 return src.getParentBlock();
819 assert(getModule(src) == getModule(dest));
821 auto safelyDoms = [&](Value a, Value b) {
822 if (isa<BlockArgument>(a))
824 if (isa<BlockArgument>(b))
828 a.getParentBlock()->findAncestorOpInBlock(*b.getDefiningOp());
829 return ancestor && a.getDefiningOp()->isBeforeInBlock(ancestor);
831 if (safelyDoms(src, dest))
832 return dest.getParentBlock();
833 if (safelyDoms(dest, src))
834 return src.getParentBlock();
838 auto getNoopCast = [](Value v) -> mlir::UnrealizedConversionCastOp {
840 dyn_cast_or_null<mlir::UnrealizedConversionCastOp>(v.getDefiningOp());
841 if (op && op.getNumResults() == 1 && op.getNumOperands() == 1 &&
842 op.getResultTypes()[0] == op.getOperandTypes()[0])
849 SmallVector<Operation *> opsToErase;
850 auto connect = [&](Value src, Value dest,
851 ImplicitLocOpBuilder &builder) -> LogicalResult {
854 if (
auto op = getNoopCast(dest)) {
855 dest = op.getOperand(0);
856 opsToErase.push_back(op);
857 std::swap(src, dest);
858 }
else if (
auto op = getNoopCast(src)) {
859 src = op.getOperand(0);
860 opsToErase.push_back(op);
864 std::swap(src, dest);
867 auto *insertBlock = findInsertionBlock(src, dest);
869 return emitError(src.getLoc())
870 .append(
"This value is involved with a Wiring Problem where the "
871 "destination is in the same module but neither dominates the "
872 "other, which is not supported.")
873 .attachNote(dest.getLoc())
874 .append(
"The destination is here.");
877 builder.setInsertionPointToEnd(insertBlock);
880 if (type_isa<RefType>(dest.getType()) != type_isa<RefType>(src.getType())) {
881 if (type_isa<RefType>(dest.getType()))
882 src = builder.create<RefSendOp>(src);
884 src = builder.create<RefResolveOp>(src);
890 if (
auto destOp = dyn_cast_or_null<WireOp>(dest.getDefiningOp());
891 destOp && dest.getUses().empty()) {
893 if (
auto baseType = dyn_cast<FIRRTLBaseType>(src.getType());
894 baseType && baseType.isPassive()) {
897 builder.create<NodeOp>(src, destOp.getName())
898 .setAnnotationsAttr(destOp.getAnnotations());
899 opsToErase.push_back(destOp);
911 auto *context = state.
circuit.getContext();
915 LLVM_DEBUG({ llvm::dbgs() <<
"Analyzing wiring problems:\n"; });
916 DenseMap<FModuleLike, ModuleModifications> moduleModifications;
917 DenseSet<Value> visitedSinks;
919 auto index = e.index();
920 auto problem = e.value();
924 auto source = problem.source;
925 auto sink = problem.sink;
929 if (!visitedSinks.insert(sink).second) {
930 auto diag = mlir::emitError(source.getLoc())
931 <<
"This sink is involved with a Wiring Problem which is "
932 "targeted by a source used by another Wiring Problem. "
933 "(This is both illegal and should be impossible.)";
934 diag.attachNote(source.getLoc()) <<
"The source is here";
937 FModuleLike sourceModule = getModule(source);
938 FModuleLike sinkModule = getModule(sink);
939 if (isa<FExtModuleOp>(sourceModule) || isa<FExtModuleOp>(sinkModule)) {
940 auto diag = mlir::emitError(source.getLoc())
941 <<
"This source is involved with a Wiring Problem which "
942 "includes an External Module port and External Module "
943 "ports anre not supported.";
944 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
949 llvm::dbgs() <<
" - index: " << index <<
"\n"
951 <<
" module: " << sourceModule.getModuleName() <<
"\n"
952 <<
" value: " << source <<
"\n"
954 <<
" module: " << sinkModule.getModuleName() <<
"\n"
955 <<
" value: " << sink <<
"\n"
956 <<
" newNameHint: " << problem.newNameHint <<
"\n";
960 if (sink.getParentBlock() == source.getParentBlock()) {
961 auto builder = ImplicitLocOpBuilder::atBlockEnd(
UnknownLoc::get(context),
962 sink.getParentBlock());
963 if (failed(
connect(source, sink, builder)))
970 if (sourceModule == sinkModule) {
971 LLVM_DEBUG(llvm::dbgs()
972 <<
" LCA: " << sourceModule.getModuleName() <<
"\n");
973 moduleModifications[sourceModule].connectionMap[index] = source;
974 moduleModifications[sourceModule].uturns.push_back({index, sink});
982 if (sourcePaths.size() != 1 || sinkPaths.size() != 1) {
984 mlir::emitError(source.getLoc())
985 <<
"This source is involved with a Wiring Problem where the source "
986 "or the sink are multiply instantiated and this is not supported.";
987 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
992 cast<FModuleOp>(instanceGraph.getTopLevelNode()->getModule());
993 auto sources = sourcePaths[0];
994 auto sinks = sinkPaths[0];
995 while (!sources.empty() && !sinks.empty()) {
996 if (sources.top() != sinks.top())
998 auto newLCA = cast<InstanceOp>(*sources.top());
999 lca = cast<FModuleOp>(newLCA.getReferencedModule(instanceGraph));
1000 sources = sources.dropFront();
1001 sinks = sinks.dropFront();
1005 llvm::dbgs() <<
" LCA: " << lca.getModuleName() <<
"\n"
1006 <<
" sourcePath: " << sourcePaths[0] <<
"\n"
1007 <<
" sinkPaths: " << sinkPaths[0] <<
"\n";
1011 moduleModifications[sourceModule].connectionMap[index] = source;
1012 moduleModifications[sinkModule].connectionMap[index] = sink;
1015 Type sourceType, sinkType;
1021 RefType refType = TypeSwitch<Type, RefType>(source.getType())
1025 .Case<RefType>([](RefType ref) {
return ref; });
1026 sourceType = refType;
1027 sinkType = refType.getType();
1030 sourceType = source.getType();
1031 sinkType = sink.getType();
1034 auto sourceFType = type_dyn_cast<FIRRTLType>(sourceType);
1035 auto sinkFType = type_dyn_cast<FIRRTLType>(sinkType);
1037 return emitError(source.getLoc())
1038 <<
"Wiring Problem source type \"" << sourceType
1039 <<
"\" must be a FIRRTL type";
1041 return emitError(sink.getLoc())
1042 <<
"Wiring Problem sink type \"" << sinkType
1043 <<
"\" must be a FIRRTL type";
1047 if (sourceFType != sinkFType &&
1050 if (
auto sourceBaseType = dyn_cast<FIRRTLBaseType>(sourceFType);
1060 auto diag = mlir::emitError(source.getLoc())
1061 <<
"Wiring Problem source type " << sourceType
1062 <<
" does not match sink type " << sinkType;
1063 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
1070 if (
auto sinkFType = type_dyn_cast<FIRRTLType>(sink.getType());
1071 sinkFType && type_isa<RefType>(sourceType) &&
1073 return emitError(sink.getLoc())
1074 <<
"Wiring Problem sink type \"" << sink.getType()
1075 <<
"\" must be passive (no flips) when using references";
1080 StringRef name, instName;
1081 for (
auto instNode : llvm::reverse(insts)) {
1082 auto inst = cast<InstanceOp>(*instNode);
1083 auto mod = inst.getReferencedModule<FModuleOp>(instanceGraph);
1084 if (mod.isPublic()) {
1085 if (!allowAddingPortsOnPublic) {
1086 auto diag = emitError(
1087 mod.getLoc(),
"cannot wire port through this public module");
1088 diag.attachNote(source.getLoc()) <<
"source here";
1089 diag.attachNote(sink.getLoc()) <<
"sink here";
1092 ++numPublicPortsWired;
1095 if (problem.newNameHint.empty())
1105 assert(!instName.empty());
1108 moduleModifications[mod].portsToAdd.push_back(
1110 instName = inst.getInstanceName();
1116 if (failed(addPorts(sources, source, sourceType,
Direction::Out)) ||
1123 LLVM_DEBUG({ llvm::dbgs() <<
"Updating modules:\n"; });
1124 for (
auto *op : llvm::post_order(instanceGraph.getTopLevelNode())) {
1125 auto fmodule = dyn_cast<FModuleOp>(*op->getModule());
1127 if (!fmodule || !moduleModifications.count(fmodule))
1130 auto modifications = moduleModifications[fmodule];
1132 llvm::dbgs() <<
" - module: " << fmodule.getModuleName() <<
"\n";
1133 llvm::dbgs() <<
" ports:\n";
1134 for (
auto [index, port] : modifications.portsToAdd) {
1135 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
1136 <<
" id: " << index <<
"\n"
1137 <<
" type: " << port.type <<
"\n"
1145 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1146 SmallVector<unsigned> problemIndices;
1147 for (
auto [problemIdx, portInfo] : modifications.portsToAdd) {
1149 newPorts.push_back({fmodule.getNumPorts(), portInfo});
1150 problemIndices.push_back(problemIdx);
1152 auto originalNumPorts = fmodule.getNumPorts();
1153 auto portIdx = fmodule.getNumPorts();
1154 fmodule.insertPorts(newPorts);
1156 auto builder = ImplicitLocOpBuilder::atBlockBegin(
UnknownLoc::get(context),
1157 fmodule.getBodyBlock());
1161 for (
auto [problemIdx, portPair] : llvm::zip(problemIndices, newPorts)) {
1162 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1163 assert(src &&
"there did not exist a driver for the port");
1164 Value dest = fmodule.getArgument(portIdx++);
1165 if (failed(
connect(src, dest, builder)))
1171 for (
auto [problemIdx, dest] : moduleModifications[fmodule].uturns) {
1172 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1173 assert(src &&
"there did not exist a connection for the u-turn");
1174 if (failed(
connect(src, dest, builder)))
1179 for (
auto *inst : instanceGraph.lookup(fmodule)->uses()) {
1180 InstanceOp useInst = cast<InstanceOp>(inst->getInstance());
1181 auto enclosingModule = useInst->getParentOfType<FModuleOp>();
1182 auto clonedInst = useInst.cloneAndInsertPorts(newPorts);
1186 useInst->replaceAllUsesWith(
1187 clonedInst.getResults().drop_back(newPorts.size()));
1194 for (
auto [newPortIdx, problemIdx] : llvm::enumerate(problemIndices)) {
1195 auto &modifications = moduleModifications[enclosingModule];
1196 auto newPort = clonedInst.getResult(newPortIdx + originalNumPorts);
1197 if (modifications.connectionMap.count(problemIdx)) {
1198 modifications.uturns.push_back({problemIdx, newPort});
1201 modifications.connectionMap[problemIdx] = newPort;
1207 for (
auto *op : opsToErase)
1214 void LowerAnnotationsPass::runOnOperation() {
1215 CircuitOp circuit = getOperation();
1216 SymbolTable modules(circuit);
1226 auto annotations = circuit->getAttrOfType<ArrayAttr>(
rawAnnotations);
1234 for (
auto anno : llvm::reverse(annotations.getValue()))
1235 worklistAttrs.push_back(cast<DictionaryAttr>(anno));
1237 size_t numFailures = 0;
1238 size_t numAdded = 0;
1239 auto addToWorklist = [&](DictionaryAttr anno) {
1241 worklistAttrs.push_back(anno);
1244 ApplyState state{circuit, modules, addToWorklist, instancePathCache,
1246 LLVM_DEBUG(llvm::dbgs() <<
"Processing annotations:\n");
1247 while (!worklistAttrs.empty()) {
1248 auto attr = worklistAttrs.pop_back_val();
1249 if (applyAnnotation(attr, state).failed())
1253 if (failed(legacyToWiringProblems(state)))
1256 if (failed(solveWiringProblems(state)))
1260 numRawAnnotations += annotations.size();
1261 numAddedAnnos += numAdded;
1262 numAnnos += numAdded + annotations.size();
1266 signalPassFailure();
1271 bool ignoreAnnotationUnknown,
bool ignoreAnnotationClassless,
1272 bool noRefTypePorts,
bool allowAddingPortsOnPublic) {
1273 auto pass = std::make_unique<LowerAnnotationsPass>();
1274 pass->ignoreAnnotationUnknown = ignoreAnnotationUnknown;
1275 pass->ignoreAnnotationClassless = ignoreAnnotationClassless;
1276 pass->noRefTypePorts = noRefTypePorts;
1277 pass->allowAddingPortsOnPublic = allowAddingPortsOnPublic;
assert(baseType &&"element must be base type")
static LogicalResult applyOutputDirAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static LogicalResult convertToExcludeFromFullResetAnnotation(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Convert from IgnoreFullAsyncResetAnnotation to ExcludeFromFullResetAnnotation.
static LogicalResult applyModulePrefixAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static void addAnnotation(AnnoTarget ref, unsigned fieldIdx, ArrayRef< NamedAttribute > anno)
Apply a new annotation to a resolved target.
static ArrayAttr replaceArrayAttrElement(ArrayAttr array, size_t elem, Attribute newVal)
Update an ArrayAttribute by replacing one entry.
static LogicalResult convertToFullResetAnnotation(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Convert from FullAsyncResetAnnotation to FullResetAnnotation.
static const AnnoRecord * getAnnotationHandler(StringRef annoStr, bool ignoreAnnotationUnknown)
Lookup a record for a given annotation class.
static std::optional< AnnoPathValue > stdResolveImpl(StringRef rawPath, ApplyState &state)
Implementation of standard resolution.
static ArrayAttr appendArrayAttr(ArrayAttr array, Attribute a)
Construct the annotation array with a new thing appended.
static LogicalResult applyBodyTypeLoweringAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static LogicalResult applyDUTAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
LogicalResult drop(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Just drop the annotation.
static LogicalResult applyLoadMemoryAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Update a memory op with attributes about memory file loading.
static std::optional< Convention > parseConvention(llvm::StringRef str)
static std::optional< AnnoPathValue > noResolve(DictionaryAttr anno, ApplyState &state)
Always resolve to the circuit, ignoring the annotation.
static ArrayAttr getAnnotationsFrom(Operation *op)
Get annotations or an empty set of annotations.
static LogicalResult applyConventionAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static LogicalResult applyAttributeAnnotation(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static FlatSymbolRefAttr scatterNonLocalPath(const AnnoPathValue &target, ApplyState &state)
Scatter breadcrumb annotations corresponding to non-local annotations along the instance path.
static FlatSymbolRefAttr buildNLA(const AnnoPathValue &target, ApplyState &state)
Make an anchor for a non-local annotation.
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
This class provides a read-only projection of an annotation.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
StringRef getClass() const
Return the 'class' that this annotation is representing.
FIRRTLBaseType getPassiveType()
Return this type with any flip types recursively removed from itself.
An instance path composed of a series of instances.
def connect(destination, source)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
constexpr const char * excludeFromFullResetAnnoClass
Annotation that marks a module as not belonging to any reset domain.
constexpr const char * injectDUTHierarchyAnnoClass
constexpr const char * extractCoverageAnnoClass
constexpr const char * excludeMemToRegAnnoClass
constexpr const char * elaborationArtefactsDirectoryAnnoClass
StringRef getAnnotationAttrName()
Return the name of the attribute used for annotations on FIRRTL ops.
Direction
This represents the direction of a single port.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
constexpr const char * extractGrandCentralClass
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
std::optional< AnnoPathValue > stdResolve(DictionaryAttr anno, ApplyState &state)
===-------------------------------------------------------------------—===// Standard Utility Resolve...
constexpr const char * fullResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
constexpr const char * sitestBlackBoxAnnoClass
constexpr const char * convertMemToRegOfVecAnnoClass
constexpr const char * dontObfuscateModuleAnnoClass
std::unique_ptr< mlir::Pass > createLowerFIRRTLAnnotationsPass(bool ignoreUnhandledAnnotations=false, bool ignoreClasslessAnnotations=false, bool noRefTypePorts=false, bool allowAddingPortsOnPublic=false)
This is the pass constructor.
constexpr const char * metadataDirectoryAttrName
constexpr const char * extractBlackBoxAnnoClass
constexpr const char * fullAsyncResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
constexpr const char * testBenchDirAnnoClass
constexpr const char * sitestTestHarnessBlackBoxAnnoClass
constexpr const char * outputDirAnnoClass
constexpr const char * referenceKeyPortClass
constexpr const char * augmentedGroundTypeClass
constexpr const char * traceAnnoClass
constexpr const char * mustDedupAnnoClass
Flow foldFlow(Value val, Flow accumulatedFlow=Flow::Source)
Compute the flow for a Value, val, as determined by the FIRRTL specification.
constexpr const char * loadMemoryFromFileAnnoClass
constexpr const char * dutAnnoClass
constexpr const char * rawAnnotations
constexpr const char * extractSeqMemsAnnoClass
constexpr const char * attributeAnnoClass
bool areTypesEquivalent(FIRRTLType destType, FIRRTLType srcType, bool destOuterTypeIsConst=false, bool srcOuterTypeIsConst=false, bool requireSameWidths=false)
Returns whether the two types are equivalent.
std::optional< AnnoPathValue > resolveEntities(TokenAnnoTarget path, CircuitOp circuit, SymbolTable &symTbl, CircuitTargetCache &cache)
Convert a parsed target string to a resolved target structure.
constexpr const char * wiringSinkAnnoClass
constexpr const char * memTapSourceClass
constexpr const char * loadMemoryFromFileInlineAnnoClass
constexpr const char * forceNameAnnoClass
constexpr const char * memTapClass
constexpr const char * noDedupAnnoClass
constexpr const char * deletedKeyClass
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
constexpr const char * dataTapsClass
constexpr const char * conventionAnnoClass
constexpr const char * dedupGroupAnnoClass
constexpr const char * viewAnnoClass
constexpr const char * serializedViewAnnoClass
constexpr const char * enumDefAnnoClass
constexpr const char * enumVecAnnoClass
std::string canonicalizeTarget(StringRef target)
Return an input target string in canonical form.
constexpr const char * wiringSourceAnnoClass
LogicalResult applyGCTMemTaps(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * traceNameAnnoClass
constexpr const char * enumComponentAnnoClass
constexpr const char * dataTapsBlackboxClass
constexpr const char * extractAssertAnnoClass
constexpr const char * memTapBlackboxClass
static LogicalResult applyWithoutTarget(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
An applier which puts the annotation on the target and drops the 'target' field from the annotation.
constexpr const char * testHarnessPathAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * addSeqMemPortAnnoClass
LogicalResult applyWiring(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Consume SourceAnnotation and SinkAnnotation, storing into state.
LogicalResult applyTraceName(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Expand a TraceNameAnnotation (which has don't touch semantics) into a TraceAnnotation (which does NOT...
constexpr const char * blackBoxPathAnnoClass
constexpr const char * internalKeyPortClass
constexpr const char * addSeqMemPortsFileAnnoClass
constexpr const char * retimeModulesFileAnnoClass
constexpr const char * retimeModuleAnnoClass
constexpr const char * runFIRRTLTransformAnnoClass
constexpr const char * companionAnnoClass
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
constexpr const char * referenceKeySourceClass
constexpr const char * literalKeyClass
constexpr const char * blackBoxTargetDirAnnoClass
constexpr const char * ignoreFullAsyncResetAnnoClass
Annotation that marks a module as not belonging to any reset domain.
LogicalResult registerAnnotationRecord(StringRef annoClass, AnnoRecord annoRecord, const std::function< void(llvm::Twine)> &errorHandler={})
Register external annotation records.
constexpr const char * modulePrefixAnnoClass
LogicalResult applyGCTDataTaps(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * blackBoxInlineAnnoClass
StringRef getPortAnnotationAttrName()
Return the name of the attribute used for port annotations on FIRRTL ops.
constexpr const char * decodeTableAnnotation
constexpr const char * extractClockGatesAnnoClass
constexpr const char * inlineAnnoClass
constexpr const char * memTapPortClass
LogicalResult applyGCTView(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * flattenAnnoClass
std::optional< TokenAnnoTarget > tokenizePath(StringRef origTarget)
Parse a FIRRTL annotation path into its constituent parts.
LogicalResult applyWithoutTargetImpl(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state, bool allowNonLocal)
===-------------------------------------------------------------------—===// Standard Utility Applier...
constexpr const char * extractAssumeAnnoClass
constexpr const char * grandCentralHierarchyFileAnnoClass
std::optional< AnnoPathValue > tryResolve(DictionaryAttr anno, ApplyState &state)
Resolves with target, if it exists. If not, resolves to the circuit.
constexpr const char * typeLoweringAnnoClass
constexpr const char * dontTouchAnnoClass
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
constexpr const char * testHarnessHierAnnoClass
static llvm::StringMap< AnnoRecord > annotationRecords
constexpr const char * moduleHierAnnoClass
constexpr const char * internalKeySourceClass
static AnnoRecord NoTargetAnnotation
Resolution and application of a "firrtl.annotations.NoTargetAnnotation".
unsigned addSVAttributes(mlir::Operation *op, llvm::ArrayRef< SVAttributeAttr > attrs)
Add a list of SV attributes to an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
SmallVector< InstanceOp > instances
===-------------------------------------------------------------------—===// LowerAnnotations ===----...
An annotation target is used to keep track of something that is targeted by an Annotation.
Operation * getOp() const
FModuleLike getModule() const
Get the parent module of the target.
State threaded through functions for resolving and applying annotations.
HierPathCache hierPathCache
hw::InnerSymbolNamespace & getNamespace(FModuleLike module)
DenseMap< StringAttr, LegacyWiringProblem > legacyWiringProblems
SmallVector< WiringProblem > wiringProblems
size_t numReusedHierPaths
InstancePathCache & instancePathCache
CircuitTargetCache targetCaches
FlatSymbolRefAttr getRefFor(ArrayAttr attr)
This represents an annotation targeting a specific operation.
A data structure that caches and provides absolute paths to module instances in the IR.
ArrayRef< InstancePath > getAbsolutePaths(ModuleOpInterface op)
void replaceInstance(InstanceOpInterface oldOp, InstanceOpInterface newOp)
Replace an InstanceOp. This is required to keep the cache updated.
InstanceGraph & instanceGraph
The instance graph of the IR.