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 "firrtl-lower-annotations"
38#define GEN_PASS_DEF_LOWERFIRRTLANNOTATIONS
39#include "circt/Dialect/FIRRTL/Passes.h.inc"
44using namespace firrtl;
45using namespace chirrtl;
51 return ArrayAttr::get(op->getContext(), {});
57 return ArrayAttr::get(a.getContext(), ArrayRef<Attribute>{a});
58 SmallVector<Attribute> old(array.begin(), array.end());
60 return ArrayAttr::get(a.getContext(), old);
66 SmallVector<Attribute> old(array.begin(), array.end());
68 return ArrayAttr::get(array.getContext(), old);
74 ArrayRef<NamedAttribute> anno) {
76 DictionaryAttr annotation;
78 SmallVector<NamedAttribute> annoField(anno.begin(), anno.end());
79 annoField.emplace_back(
80 StringAttr::get(
context,
"circt.fieldID"),
81 IntegerAttr::get(IntegerType::get(
context, 32, IntegerType::Signless),
83 annotation = DictionaryAttr::get(
context, annoField);
85 annotation = DictionaryAttr::get(
context, anno);
88 if (isa<OpAnnoTarget>(ref)) {
94 auto portRef = cast<PortAnnoTarget>(ref);
96 ArrayAttr portAnno = dyn_cast_or_null<ArrayAttr>(portAnnoRaw);
98 SmallVector<Attribute> emptyPortAttr(
100 ArrayAttr::get(ref.
getOp()->getContext(), {}));
101 portAnno = ArrayAttr::get(ref.
getOp()->getContext(), 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>())));
122 FlatSymbolRefAttr::get(target.
ref.
getModule().getModuleNameAttr()));
124 auto instAttr = ArrayAttr::get(state.
circuit.getContext(), insts);
135 FlatSymbolRefAttr sym =
buildNLA(target, state);
144static 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(
224 {StringAttr::get(anno.getContext(),
"circt.nonlocal"), sym});
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 =
290 tryGetAs<StringAttr>(anno, anno,
"convention", loc, conventionAnnoClass);
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);
321 diag << bodyTypeLoweringAnnoClass;
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 =
338 tryGetAs<StringAttr>(anno, anno,
"convention", loc, conventionAnnoClass);
340 if (!conventionStrAttr)
343 auto conventionStr = conventionStrAttr.getValue();
346 return error() <<
"unknown convention " << conventionStr;
348 auto convention = *conventionOpt;
350 if (convention == Convention::Internal)
354 auto conventionAttr = ConventionAttr::get(op->getContext(), convention);
357 bool includeHierarchy =
false;
358 if (
auto includeHierarchyAttr = tryGetAs<BoolAttr>(
359 anno, anno,
"includeHierarchy", loc, conventionAnnoClass))
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);
387 diag << modulePrefixAnnoClass <<
" ";
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";
402 tryGetAs<StringAttr>(anno, anno,
"prefix", loc, modulePrefixAnnoClass);
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");
440 auto svAttr = sv::SVAttributeAttr::get(name.getContext(), name);
441 sv::addSVAttributes(op, {svAttr});
446template <
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;
488 op->setAttr(
"init", MemoryInitAttr::get(op->getContext(), filename,
489 hexOrBinaryValue ==
"b", isInline));
498 auto *
context = op->getContext();
499 auto loc = op->getLoc();
502 return mlir::emitError(loc) << outputDirAnnoClass <<
" ";
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";
520 tryGetAs<StringAttr>(anno, anno,
"dirname", loc, outputDirAnnoClass);
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())
541 <<
"'" << fullAsyncResetAnnoClass <<
"' is deprecated, use '"
542 << fullResetAnnoClass <<
"' instead";
544 NamedAttrList newAnno(anno.getValue());
545 newAnno.set(
"class", StringAttr::get(
context, fullResetAnnoClass));
546 newAnno.append(
"resetType", StringAttr::get(
context,
"async"));
548 DictionaryAttr newDictionary = DictionaryAttr::get(op->getContext(), newAnno);
550 return applyWithoutTarget<false>(target, newDictionary, state);
558 auto *
context = op->getContext();
560 mlir::emitWarning(op->getLoc())
561 <<
"'" << ignoreFullAsyncResetAnnoClass <<
"' is deprecated, use '"
562 << excludeFromFullResetAnnoClass <<
"' instead";
564 NamedAttrList newAnno(anno.getValue());
565 newAnno.set(
"class", StringAttr::get(
context, excludeFromFullResetAnnoClass));
567 DictionaryAttr newDictionary = DictionaryAttr::get(op->getContext(), newAnno);
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>}},
601#define GET_ANNOTATION_RECORD_LIST
602#include "circt/Dialect/FIRRTL/FIRRTLAnnotationRecords.h.inc"
607 const std::function<
void(llvm::Twine)> &errorHandler) {
610 return LogicalResult::success();
612 errorHandler(
"annotation record '" + annoClass +
"' is registered twice\n");
613 return LogicalResult::failure();
621 bool ignoreAnnotationUnknown) {
625 if (ignoreAnnotationUnknown)
635struct LowerAnnotationsPass
636 :
public circt::firrtl::impl::LowerFIRRTLAnnotationsBase<
637 LowerAnnotationsPass> {
640 void runOnOperation()
override;
641 LogicalResult applyAnnotation(DictionaryAttr anno,
ApplyState &state);
642 LogicalResult legacyToWiringProblems(
ApplyState &state);
643 LogicalResult solveWiringProblems(
ApplyState &state);
645 SmallVector<DictionaryAttr> worklistAttrs;
649LogicalResult LowerAnnotationsPass::applyAnnotation(DictionaryAttr anno,
651 LLVM_DEBUG(llvm::dbgs() <<
" - anno: " << anno <<
"\n";);
654 StringRef annoClassVal;
655 if (
auto annoClass = anno.getNamed(
"class"))
656 annoClassVal = cast<StringAttr>(annoClass->getValue()).getValue();
657 else if (ignoreAnnotationClassless)
658 annoClassVal =
"circt.missing";
660 return mlir::emitError(state.
circuit.getLoc())
661 <<
"Annotation without a class: " << anno;
667 if (!ignoreAnnotationUnknown)
668 return mlir::emitError(state.
circuit.getLoc())
669 <<
"Unhandled annotation: " << anno;
677 auto target = record->resolver(anno, state);
679 return mlir::emitError(state.
circuit.getLoc())
680 <<
"Unable to resolve target of annotation: " << anno;
681 if (record->applier(*target, anno, state).failed())
682 return mlir::emitError(state.
circuit.getLoc())
683 <<
"Unable to apply annotation: " << anno;
689LogicalResult LowerAnnotationsPass::legacyToWiringProblems(
ApplyState &state) {
690 for (
const auto &[name, problem] : state.legacyWiringProblems) {
692 return mlir::emitError(state.
circuit.getLoc())
693 <<
"Unable to resolve source for pin: " << name;
695 if (problem.sinks.empty())
696 return mlir::emitError(state.
circuit.getLoc())
697 <<
"Unable to resolve sink(s) for pin: " << name;
699 for (
const auto &sink : problem.sinks) {
701 {problem.source, sink, {}, WiringProblem::RefTypeUsage::Never});
714LogicalResult LowerAnnotationsPass::solveWiringProblems(
ApplyState &state) {
717 auto getModule = [](Value value) {
718 if (BlockArgument blockArg = dyn_cast<BlockArgument>(value))
719 return cast<FModuleLike>(blockArg.getParentBlock()->getParentOp());
720 return value.getDefiningOp()->getParentOfType<FModuleLike>();
724 auto findInsertionBlock = [&getModule](Value src, Value dest) -> Block * {
726 if (src.getParentBlock() == dest.getParentBlock())
727 return src.getParentBlock();
731 assert(getModule(src) == getModule(dest));
733 auto safelyDoms = [&](Value a, Value b) {
734 if (isa<BlockArgument>(a))
736 if (isa<BlockArgument>(b))
740 a.getParentBlock()->findAncestorOpInBlock(*b.getDefiningOp());
741 return ancestor && a.getDefiningOp()->isBeforeInBlock(ancestor);
743 if (safelyDoms(src, dest))
744 return dest.getParentBlock();
745 if (safelyDoms(dest, src))
746 return src.getParentBlock();
750 auto getNoopCast = [](Value v) -> mlir::UnrealizedConversionCastOp {
752 dyn_cast_or_null<mlir::UnrealizedConversionCastOp>(v.getDefiningOp());
753 if (op && op.getNumResults() == 1 && op.getNumOperands() == 1 &&
754 op.getResultTypes()[0] == op.getOperandTypes()[0])
761 SmallVector<Operation *> opsToErase;
762 auto connect = [&](Value src, Value dest,
763 ImplicitLocOpBuilder &builder) -> LogicalResult {
766 if (
auto op = getNoopCast(dest)) {
767 dest = op.getOperand(0);
768 opsToErase.push_back(op);
769 std::swap(src, dest);
770 }
else if (
auto op = getNoopCast(src)) {
771 src = op.getOperand(0);
772 opsToErase.push_back(op);
776 std::swap(src, dest);
779 auto *insertBlock = findInsertionBlock(src, dest);
781 return emitError(src.getLoc())
782 .append(
"This value is involved with a Wiring Problem where the "
783 "destination is in the same module but neither dominates the "
784 "other, which is not supported.")
785 .attachNote(dest.getLoc())
786 .append(
"The destination is here.");
789 builder.setInsertionPointToEnd(insertBlock);
792 if (type_isa<RefType>(dest.getType()) != type_isa<RefType>(src.getType())) {
793 if (type_isa<RefType>(dest.getType()))
794 src = RefSendOp::create(builder, src);
796 src = RefResolveOp::create(builder, src);
802 if (
auto destOp = dyn_cast_or_null<WireOp>(dest.getDefiningOp());
803 destOp && dest.getUses().empty()) {
805 if (
auto baseType = dyn_cast<FIRRTLBaseType>(src.getType());
806 baseType && baseType.isPassive()) {
809 NodeOp::create(builder, src, destOp.getName())
810 .setAnnotationsAttr(destOp.getAnnotations());
811 opsToErase.push_back(destOp);
827 LLVM_DEBUG({ llvm::dbgs() <<
"Analyzing wiring problems:\n"; });
828 DenseMap<FModuleLike, ModuleModifications> moduleModifications;
829 DenseSet<Value> visitedSinks;
830 for (
auto e :
llvm::enumerate(state.wiringProblems)) {
831 auto index = e.index();
832 auto problem = e.value();
836 auto source = problem.source;
837 auto sink = problem.sink;
841 if (!visitedSinks.insert(sink).second) {
842 auto diag = mlir::emitError(source.getLoc())
843 <<
"This sink is involved with a Wiring Problem which is "
844 "targeted by a source used by another Wiring Problem. "
845 "(This is both illegal and should be impossible.)";
846 diag.attachNote(source.getLoc()) <<
"The source is here";
849 FModuleLike sourceModule = getModule(source);
850 FModuleLike sinkModule = getModule(sink);
851 if (isa<FExtModuleOp>(sourceModule) || isa<FExtModuleOp>(sinkModule)) {
852 auto diag = mlir::emitError(source.getLoc())
853 <<
"This source is involved with a Wiring Problem which "
854 "includes an External Module port and External Module "
855 "ports anre not supported.";
856 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
861 llvm::dbgs() <<
" - index: " << index <<
"\n"
863 <<
" module: " << sourceModule.getModuleName() <<
"\n"
864 <<
" value: " << source <<
"\n"
866 <<
" module: " << sinkModule.getModuleName() <<
"\n"
867 <<
" value: " << sink <<
"\n"
868 <<
" newNameHint: " << problem.newNameHint <<
"\n";
872 if (sink.getParentBlock() == source.getParentBlock()) {
873 auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(
context),
874 sink.getParentBlock());
875 if (failed(
connect(source, sink, builder)))
882 if (sourceModule == sinkModule) {
883 LLVM_DEBUG(llvm::dbgs()
884 <<
" LCA: " << sourceModule.getModuleName() <<
"\n");
885 moduleModifications[sourceModule].connectionMap[index] = source;
886 moduleModifications[sourceModule].uturns.push_back({index, sink});
894 if (sourcePaths.size() != 1 || sinkPaths.size() != 1) {
896 mlir::emitError(source.getLoc())
897 <<
"This source is involved with a Wiring Problem where the source "
898 "or the sink are multiply instantiated and this is not supported.";
899 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
904 cast<FModuleOp>(instanceGraph.getTopLevelNode()->getModule());
905 auto sources = sourcePaths[0];
906 auto sinks = sinkPaths[0];
907 while (!sources.empty() && !sinks.empty()) {
908 if (sources.top() != sinks.top())
910 auto newLCA = cast<InstanceOp>(*sources.top());
911 lca = cast<FModuleOp>(newLCA.getReferencedModule(instanceGraph));
912 sources = sources.dropFront();
913 sinks = sinks.dropFront();
917 llvm::dbgs() <<
" LCA: " << lca.getModuleName() <<
"\n"
918 <<
" sourcePath: " << sourcePaths[0] <<
"\n"
919 <<
" sinkPaths: " << sinkPaths[0] <<
"\n";
923 moduleModifications[sourceModule].connectionMap[index] = source;
924 moduleModifications[sinkModule].connectionMap[index] = sink;
927 Type sourceType, sinkType;
930 problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer;
933 RefType refType = TypeSwitch<Type, RefType>(source.getType())
935 return RefType::get(base.getPassiveType());
937 .Case<RefType>([](RefType ref) {
return ref; });
938 sourceType = refType;
939 sinkType = refType.getType();
942 sourceType = source.getType();
943 sinkType = sink.getType();
946 auto sourceFType = type_dyn_cast<FIRRTLType>(sourceType);
947 auto sinkFType = type_dyn_cast<FIRRTLType>(sinkType);
949 return emitError(source.getLoc())
950 <<
"Wiring Problem source type \"" << sourceType
951 <<
"\" must be a FIRRTL type";
953 return emitError(sink.getLoc())
954 <<
"Wiring Problem sink type \"" << sinkType
955 <<
"\" must be a FIRRTL type";
959 if (sourceFType != sinkFType &&
962 if (
auto sourceBaseType = dyn_cast<FIRRTLBaseType>(sourceFType);
963 problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer &&
972 auto diag = mlir::emitError(source.getLoc())
973 <<
"Wiring Problem source type " << sourceType
974 <<
" does not match sink type " << sinkType;
975 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
982 if (
auto sinkFType = type_dyn_cast<FIRRTLType>(sink.getType());
983 sinkFType && type_isa<RefType>(sourceType) &&
985 return emitError(sink.getLoc())
986 <<
"Wiring Problem sink type \"" << sink.getType()
987 <<
"\" must be passive (no flips) when using references";
992 StringRef name, instName;
993 for (
auto instNode :
llvm::reverse(insts)) {
994 auto inst = cast<InstanceOp>(*instNode);
995 auto mod = inst.getReferencedModule<FModuleOp>(instanceGraph);
996 if (mod.isPublic()) {
997 auto diag = emitError(mod.getLoc(),
998 "cannot wire port through this public module");
999 diag.attachNote(source.getLoc()) <<
"source here";
1000 diag.attachNote(sink.getLoc()) <<
"sink here";
1004 if (problem.newNameHint.empty())
1014 assert(!instName.empty());
1017 moduleModifications[mod].portsToAdd.push_back(
1018 {index, {StringAttr::get(
context, name), tpe, dir}});
1019 instName = inst.getInstanceName();
1025 if (failed(addPorts(sources, source, sourceType, Direction::Out)) ||
1026 failed(addPorts(sinks, sink, sinkType, Direction::In)))
1032 LLVM_DEBUG({ llvm::dbgs() <<
"Updating modules:\n"; });
1033 for (
auto *op :
llvm::post_order(instanceGraph.getTopLevelNode())) {
1034 auto fmodule = dyn_cast<FModuleOp>(*op->getModule());
1036 if (!fmodule || !moduleModifications.count(fmodule))
1039 auto modifications = moduleModifications[fmodule];
1041 llvm::dbgs() <<
" - module: " << fmodule.getModuleName() <<
"\n";
1042 llvm::dbgs() <<
" ports:\n";
1043 for (
auto [index, port] : modifications.portsToAdd) {
1044 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
1045 <<
" id: " << index <<
"\n"
1046 <<
" type: " << port.type <<
"\n"
1048 << (port.direction == Direction::In ?
"in" :
"out")
1054 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1055 SmallVector<unsigned> problemIndices;
1056 for (
auto [problemIdx, portInfo] : modifications.portsToAdd) {
1058 newPorts.push_back({fmodule.getNumPorts(), portInfo});
1059 problemIndices.push_back(problemIdx);
1061 auto originalNumPorts = fmodule.getNumPorts();
1062 auto portIdx = fmodule.getNumPorts();
1063 fmodule.insertPorts(newPorts);
1065 auto builder = ImplicitLocOpBuilder::atBlockBegin(UnknownLoc::get(
context),
1066 fmodule.getBodyBlock());
1070 for (
auto [problemIdx, portPair] :
llvm::zip(problemIndices, newPorts)) {
1071 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1072 assert(src &&
"there did not exist a driver for the port");
1073 Value dest = fmodule.getArgument(portIdx++);
1074 if (failed(
connect(src, dest, builder)))
1080 for (
auto [problemIdx, dest] : moduleModifications[fmodule].uturns) {
1081 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1082 assert(src &&
"there did not exist a connection for the u-turn");
1083 if (failed(
connect(src, dest, builder)))
1088 for (
auto *inst : instanceGraph.lookup(fmodule)->uses()) {
1089 InstanceOp useInst = cast<InstanceOp>(inst->getInstance());
1090 auto enclosingModule = useInst->getParentOfType<FModuleOp>();
1091 auto clonedInst = useInst.cloneWithInsertedPortsAndReplaceUses(newPorts);
1099 for (
auto [newPortIdx, problemIdx] :
llvm::enumerate(problemIndices)) {
1100 auto &modifications = moduleModifications[enclosingModule];
1101 auto newPort = clonedInst.getResult(newPortIdx + originalNumPorts);
1102 if (modifications.connectionMap.count(problemIdx)) {
1103 modifications.uturns.push_back({problemIdx, newPort});
1106 modifications.connectionMap[problemIdx] = newPort;
1112 for (
auto *op : opsToErase)
1119void LowerAnnotationsPass::runOnOperation() {
1122 CircuitOp circuit = getOperation();
1123 SymbolTable modules(circuit);
1131 auto annotations = circuit->getAttrOfType<ArrayAttr>(
rawAnnotations);
1139 for (
auto anno :
llvm::reverse(annotations.getValue()))
1140 worklistAttrs.push_back(cast<DictionaryAttr>(anno));
1142 size_t numFailures = 0;
1143 size_t numAdded = 0;
1144 auto addToWorklist = [&](DictionaryAttr anno) {
1146 worklistAttrs.push_back(anno);
1149 ApplyState state{circuit, modules, addToWorklist, instancePathCache,
1151 LLVM_DEBUG(llvm::dbgs() <<
"Processing annotations:\n");
1152 while (!worklistAttrs.empty()) {
1153 auto attr = worklistAttrs.pop_back_val();
1154 if (applyAnnotation(attr, state).failed())
1158 if (failed(legacyToWiringProblems(state)))
1161 if (failed(solveWiringProblems(state)))
1165 numRawAnnotations += annotations.size();
1166 numAddedAnnos += numAdded;
1167 numAnnos += numAdded + annotations.size();
1171 signalPassFailure();
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
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 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 std::optional< AnnoPathValue > stdResolveImpl(StringRef rawPath, ApplyState &state)
Implementation of standard resolution.
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 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 const AnnoRecord * getAnnotationHandler(StringRef annoStr, bool ignoreAnnotationUnknown)
Lookup a record for a given annotation class.
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 FlatSymbolRefAttr buildNLA(const AnnoPathValue &target, ApplyState &state)
Make an anchor for a non-local annotation.
#define CIRCT_DEBUG_SCOPED_PASS_LOGGER(PASS)
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.
bool isPassive() const
Return true if this is a "passive" type - one that contains no "flip" types recursively within itself...
An instance path composed of a series of instances.
connect(destination, source)
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.
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
std::optional< AnnoPathValue > stdResolve(DictionaryAttr anno, ApplyState &state)
===-------------------------------------------------------------------—===// Standard Utility Resolve...
Flow foldFlow(Value val, Flow accumulatedFlow=Flow::Source)
Compute the flow for a Value, val, as determined by the FIRRTL specification.
constexpr const char * rawAnnotations
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.
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
std::string canonicalizeTarget(StringRef target)
Return an input target string in canonical form.
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
LogicalResult registerAnnotationRecord(StringRef annoClass, AnnoRecord annoRecord, const std::function< void(llvm::Twine)> &errorHandler={})
Register external annotation records.
StringRef getPortAnnotationAttrName()
Return the name of the attribute used for port annotations on FIRRTL ops.
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...
std::optional< AnnoPathValue > tryResolve(DictionaryAttr anno, ApplyState &state)
Resolves with target, if it exists. If not, resolves to the circuit.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
static llvm::StringMap< AnnoRecord > annotationRecords
static AnnoRecord NoTargetAnnotation
Resolution and application of a "firrtl.annotations.NoTargetAnnotation".
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
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
SmallVector< WiringProblem > wiringProblems
size_t numReusedHierPaths
InstancePathCache & instancePathCache
CircuitTargetCache targetCaches
hw::InnerSymbolNamespace & getNamespace(FModuleLike module)
FlatSymbolRefAttr getRefFor(ArrayAttr attr)
This represents an annotation targeting a specific operation.
Attribute getNLAReference(hw::InnerSymbolNamespace &moduleNamespace) const
A data structure that caches and provides 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.