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) {
75 auto *context = ref.
getOp()->getContext();
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 =
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)
354 auto conventionAttr = ConventionAttr::get(op->getContext(), convention);
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");
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();
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());
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())
564 NamedAttrList newAnno(anno.getValue());
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>}},
613 RegResetOp, InstanceOp, MemOp, CombMemOp,
614 MemoryPortOp, SeqMemOp>}},
622 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
627 {
stdResolve, applyWithoutTarget<true, FModuleOp, FExtModuleOp>}},
631 {
stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
633 {
stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
635 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
637 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
639 {
stdResolve, applyWithoutTarget<false, FModuleOp>}},
641 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
645 {
stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
664 {
stdResolve, applyWithoutTarget<true, FModuleOp>}},
681 const std::function<
void(llvm::Twine)> &errorHandler) {
684 return LogicalResult::success();
686 errorHandler(
"annotation record '" + annoClass +
"' is registered twice\n");
687 return LogicalResult::failure();
695 bool ignoreAnnotationUnknown) {
699 if (ignoreAnnotationUnknown)
709struct LowerAnnotationsPass
710 :
public circt::firrtl::impl::LowerFIRRTLAnnotationsBase<
711 LowerAnnotationsPass> {
714 void runOnOperation()
override;
715 LogicalResult applyAnnotation(DictionaryAttr anno,
ApplyState &state);
716 LogicalResult legacyToWiringProblems(
ApplyState &state);
717 LogicalResult solveWiringProblems(
ApplyState &state);
719 SmallVector<DictionaryAttr> worklistAttrs;
723LogicalResult LowerAnnotationsPass::applyAnnotation(DictionaryAttr anno,
725 LLVM_DEBUG(llvm::dbgs() <<
" - anno: " << anno <<
"\n";);
728 StringRef annoClassVal;
729 if (
auto annoClass = anno.getNamed(
"class"))
730 annoClassVal = cast<StringAttr>(annoClass->getValue()).getValue();
731 else if (ignoreAnnotationClassless)
732 annoClassVal =
"circt.missing";
734 return mlir::emitError(state.
circuit.getLoc())
735 <<
"Annotation without a class: " << anno;
741 if (!ignoreAnnotationUnknown)
742 return mlir::emitError(state.
circuit.getLoc())
743 <<
"Unhandled annotation: " << anno;
751 auto target = record->resolver(anno, state);
753 return mlir::emitError(state.
circuit.getLoc())
754 <<
"Unable to resolve target of annotation: " << anno;
755 if (record->applier(*target, anno, state).failed())
756 return mlir::emitError(state.
circuit.getLoc())
757 <<
"Unable to apply annotation: " << anno;
763LogicalResult LowerAnnotationsPass::legacyToWiringProblems(
ApplyState &state) {
764 for (
const auto &[name, problem] : state.legacyWiringProblems) {
766 return mlir::emitError(state.
circuit.getLoc())
767 <<
"Unable to resolve source for pin: " << name;
769 if (problem.sinks.empty())
770 return mlir::emitError(state.
circuit.getLoc())
771 <<
"Unable to resolve sink(s) for pin: " << name;
773 for (
const auto &sink : problem.sinks) {
775 {problem.source, sink, {}, WiringProblem::RefTypeUsage::Never});
788LogicalResult LowerAnnotationsPass::solveWiringProblems(
ApplyState &state) {
791 auto getModule = [](Value value) {
792 if (BlockArgument blockArg = dyn_cast<BlockArgument>(value))
793 return cast<FModuleLike>(blockArg.getParentBlock()->getParentOp());
794 return value.getDefiningOp()->getParentOfType<FModuleLike>();
798 auto findInsertionBlock = [&getModule](Value src, Value dest) -> Block * {
800 if (src.getParentBlock() == dest.getParentBlock())
801 return src.getParentBlock();
805 assert(getModule(src) == getModule(dest));
807 auto safelyDoms = [&](Value a, Value b) {
808 if (isa<BlockArgument>(a))
810 if (isa<BlockArgument>(b))
814 a.getParentBlock()->findAncestorOpInBlock(*b.getDefiningOp());
815 return ancestor && a.getDefiningOp()->isBeforeInBlock(ancestor);
817 if (safelyDoms(src, dest))
818 return dest.getParentBlock();
819 if (safelyDoms(dest, src))
820 return src.getParentBlock();
824 auto getNoopCast = [](Value v) -> mlir::UnrealizedConversionCastOp {
826 dyn_cast_or_null<mlir::UnrealizedConversionCastOp>(v.getDefiningOp());
827 if (op && op.getNumResults() == 1 && op.getNumOperands() == 1 &&
828 op.getResultTypes()[0] == op.getOperandTypes()[0])
835 SmallVector<Operation *> opsToErase;
836 auto connect = [&](Value src, Value dest,
837 ImplicitLocOpBuilder &builder) -> LogicalResult {
840 if (
auto op = getNoopCast(dest)) {
841 dest = op.getOperand(0);
842 opsToErase.push_back(op);
843 std::swap(src, dest);
844 }
else if (
auto op = getNoopCast(src)) {
845 src = op.getOperand(0);
846 opsToErase.push_back(op);
850 std::swap(src, dest);
853 auto *insertBlock = findInsertionBlock(src, dest);
855 return emitError(src.getLoc())
856 .append(
"This value is involved with a Wiring Problem where the "
857 "destination is in the same module but neither dominates the "
858 "other, which is not supported.")
859 .attachNote(dest.getLoc())
860 .append(
"The destination is here.");
863 builder.setInsertionPointToEnd(insertBlock);
866 if (type_isa<RefType>(dest.getType()) != type_isa<RefType>(src.getType())) {
867 if (type_isa<RefType>(dest.getType()))
868 src = RefSendOp::create(builder, src);
870 src = RefResolveOp::create(builder, src);
876 if (
auto destOp = dyn_cast_or_null<WireOp>(dest.getDefiningOp());
877 destOp && dest.getUses().empty()) {
879 if (
auto baseType = dyn_cast<FIRRTLBaseType>(src.getType());
880 baseType && baseType.isPassive()) {
883 NodeOp::create(builder, src, destOp.getName())
884 .setAnnotationsAttr(destOp.getAnnotations());
885 opsToErase.push_back(destOp);
897 auto *context = state.
circuit.getContext();
901 LLVM_DEBUG({ llvm::dbgs() <<
"Analyzing wiring problems:\n"; });
902 DenseMap<FModuleLike, ModuleModifications> moduleModifications;
903 DenseSet<Value> visitedSinks;
904 for (
auto e :
llvm::enumerate(state.wiringProblems)) {
905 auto index = e.index();
906 auto problem = e.value();
910 auto source = problem.source;
911 auto sink = problem.sink;
915 if (!visitedSinks.insert(sink).second) {
916 auto diag = mlir::emitError(source.getLoc())
917 <<
"This sink is involved with a Wiring Problem which is "
918 "targeted by a source used by another Wiring Problem. "
919 "(This is both illegal and should be impossible.)";
920 diag.attachNote(source.getLoc()) <<
"The source is here";
923 FModuleLike sourceModule = getModule(source);
924 FModuleLike sinkModule = getModule(sink);
925 if (isa<FExtModuleOp>(sourceModule) || isa<FExtModuleOp>(sinkModule)) {
926 auto diag = mlir::emitError(source.getLoc())
927 <<
"This source is involved with a Wiring Problem which "
928 "includes an External Module port and External Module "
929 "ports anre not supported.";
930 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
935 llvm::dbgs() <<
" - index: " << index <<
"\n"
937 <<
" module: " << sourceModule.getModuleName() <<
"\n"
938 <<
" value: " << source <<
"\n"
940 <<
" module: " << sinkModule.getModuleName() <<
"\n"
941 <<
" value: " << sink <<
"\n"
942 <<
" newNameHint: " << problem.newNameHint <<
"\n";
946 if (sink.getParentBlock() == source.getParentBlock()) {
947 auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(context),
948 sink.getParentBlock());
949 if (failed(
connect(source, sink, builder)))
956 if (sourceModule == sinkModule) {
957 LLVM_DEBUG(llvm::dbgs()
958 <<
" LCA: " << sourceModule.getModuleName() <<
"\n");
959 moduleModifications[sourceModule].connectionMap[index] = source;
960 moduleModifications[sourceModule].uturns.push_back({index, sink});
968 if (sourcePaths.size() != 1 || sinkPaths.size() != 1) {
970 mlir::emitError(source.getLoc())
971 <<
"This source is involved with a Wiring Problem where the source "
972 "or the sink are multiply instantiated and this is not supported.";
973 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
978 cast<FModuleOp>(instanceGraph.getTopLevelNode()->getModule());
979 auto sources = sourcePaths[0];
980 auto sinks = sinkPaths[0];
981 while (!sources.empty() && !sinks.empty()) {
982 if (sources.top() != sinks.top())
984 auto newLCA = cast<InstanceOp>(*sources.top());
985 lca = cast<FModuleOp>(newLCA.getReferencedModule(instanceGraph));
986 sources = sources.dropFront();
987 sinks = sinks.dropFront();
991 llvm::dbgs() <<
" LCA: " << lca.getModuleName() <<
"\n"
992 <<
" sourcePath: " << sourcePaths[0] <<
"\n"
993 <<
" sinkPaths: " << sinkPaths[0] <<
"\n";
997 moduleModifications[sourceModule].connectionMap[index] = source;
998 moduleModifications[sinkModule].connectionMap[index] = sink;
1001 Type sourceType, sinkType;
1004 problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer;
1007 RefType refType = TypeSwitch<Type, RefType>(source.getType())
1009 return RefType::get(base.getPassiveType());
1011 .Case<RefType>([](RefType ref) {
return ref; });
1012 sourceType = refType;
1013 sinkType = refType.getType();
1016 sourceType = source.getType();
1017 sinkType = sink.getType();
1020 auto sourceFType = type_dyn_cast<FIRRTLType>(sourceType);
1021 auto sinkFType = type_dyn_cast<FIRRTLType>(sinkType);
1023 return emitError(source.getLoc())
1024 <<
"Wiring Problem source type \"" << sourceType
1025 <<
"\" must be a FIRRTL type";
1027 return emitError(sink.getLoc())
1028 <<
"Wiring Problem sink type \"" << sinkType
1029 <<
"\" must be a FIRRTL type";
1033 if (sourceFType != sinkFType &&
1036 if (
auto sourceBaseType = dyn_cast<FIRRTLBaseType>(sourceFType);
1037 problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer &&
1046 auto diag = mlir::emitError(source.getLoc())
1047 <<
"Wiring Problem source type " << sourceType
1048 <<
" does not match sink type " << sinkType;
1049 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
1056 if (
auto sinkFType = type_dyn_cast<FIRRTLType>(sink.getType());
1057 sinkFType && type_isa<RefType>(sourceType) &&
1059 return emitError(sink.getLoc())
1060 <<
"Wiring Problem sink type \"" << sink.getType()
1061 <<
"\" must be passive (no flips) when using references";
1066 StringRef name, instName;
1067 for (
auto instNode :
llvm::reverse(insts)) {
1068 auto inst = cast<InstanceOp>(*instNode);
1069 auto mod = inst.getReferencedModule<FModuleOp>(instanceGraph);
1070 if (mod.isPublic()) {
1071 if (!allowAddingPortsOnPublic) {
1072 auto diag = emitError(
1073 mod.getLoc(),
"cannot wire port through this public module");
1074 diag.attachNote(source.getLoc()) <<
"source here";
1075 diag.attachNote(sink.getLoc()) <<
"sink here";
1078 ++numPublicPortsWired;
1081 if (problem.newNameHint.empty())
1091 assert(!instName.empty());
1094 moduleModifications[mod].portsToAdd.push_back(
1095 {index, {StringAttr::get(context, name), tpe, dir}});
1096 instName = inst.getInstanceName();
1102 if (failed(addPorts(sources, source, sourceType, Direction::Out)) ||
1103 failed(addPorts(sinks, sink, sinkType, Direction::In)))
1109 LLVM_DEBUG({ llvm::dbgs() <<
"Updating modules:\n"; });
1110 for (
auto *op :
llvm::post_order(instanceGraph.getTopLevelNode())) {
1111 auto fmodule = dyn_cast<FModuleOp>(*op->getModule());
1113 if (!fmodule || !moduleModifications.count(fmodule))
1116 auto modifications = moduleModifications[fmodule];
1118 llvm::dbgs() <<
" - module: " << fmodule.getModuleName() <<
"\n";
1119 llvm::dbgs() <<
" ports:\n";
1120 for (
auto [index, port] : modifications.portsToAdd) {
1121 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
1122 <<
" id: " << index <<
"\n"
1123 <<
" type: " << port.type <<
"\n"
1125 << (port.direction == Direction::In ?
"in" :
"out")
1131 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1132 SmallVector<unsigned> problemIndices;
1133 for (
auto [problemIdx, portInfo] : modifications.portsToAdd) {
1135 newPorts.push_back({fmodule.getNumPorts(), portInfo});
1136 problemIndices.push_back(problemIdx);
1138 auto originalNumPorts = fmodule.getNumPorts();
1139 auto portIdx = fmodule.getNumPorts();
1140 fmodule.insertPorts(newPorts);
1142 auto builder = ImplicitLocOpBuilder::atBlockBegin(UnknownLoc::get(context),
1143 fmodule.getBodyBlock());
1147 for (
auto [problemIdx, portPair] :
llvm::zip(problemIndices, newPorts)) {
1148 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1149 assert(src &&
"there did not exist a driver for the port");
1150 Value dest = fmodule.getArgument(portIdx++);
1151 if (failed(
connect(src, dest, builder)))
1157 for (
auto [problemIdx, dest] : moduleModifications[fmodule].uturns) {
1158 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1159 assert(src &&
"there did not exist a connection for the u-turn");
1160 if (failed(
connect(src, dest, builder)))
1165 for (
auto *inst : instanceGraph.lookup(fmodule)->uses()) {
1166 InstanceOp useInst = cast<InstanceOp>(inst->getInstance());
1167 auto enclosingModule = useInst->getParentOfType<FModuleOp>();
1168 auto clonedInst = useInst.cloneWithInsertedPortsAndReplaceUses(newPorts);
1176 for (
auto [newPortIdx, problemIdx] :
llvm::enumerate(problemIndices)) {
1177 auto &modifications = moduleModifications[enclosingModule];
1178 auto newPort = clonedInst.getResult(newPortIdx + originalNumPorts);
1179 if (modifications.connectionMap.count(problemIdx)) {
1180 modifications.uturns.push_back({problemIdx, newPort});
1183 modifications.connectionMap[problemIdx] = newPort;
1189 for (
auto *op : opsToErase)
1196void LowerAnnotationsPass::runOnOperation() {
1199 CircuitOp circuit = getOperation();
1200 SymbolTable modules(circuit);
1208 auto annotations = circuit->getAttrOfType<ArrayAttr>(
rawAnnotations);
1216 for (
auto anno :
llvm::reverse(annotations.getValue()))
1217 worklistAttrs.push_back(cast<DictionaryAttr>(anno));
1219 size_t numFailures = 0;
1220 size_t numAdded = 0;
1221 auto addToWorklist = [&](DictionaryAttr anno) {
1223 worklistAttrs.push_back(anno);
1226 ApplyState state{circuit, modules, addToWorklist, instancePathCache,
1228 LLVM_DEBUG(llvm::dbgs() <<
"Processing annotations:\n");
1229 while (!worklistAttrs.empty()) {
1230 auto attr = worklistAttrs.pop_back_val();
1231 if (applyAnnotation(attr, state).failed())
1235 if (failed(legacyToWiringProblems(state)))
1238 if (failed(solveWiringProblems(state)))
1242 numRawAnnotations += annotations.size();
1243 numAddedAnnos += numAdded;
1244 numAnnos += numAdded + annotations.size();
1248 signalPassFailure();
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 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)
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 * 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
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 * sitestBlackBoxLibrariesAnnoClass
constexpr const char * sitestTestHarnessBlackBoxAnnoClass
constexpr const char * outputDirAnnoClass
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 * loadMemoryFromFileInlineAnnoClass
constexpr const char * forceNameAnnoClass
constexpr const char * memTapClass
constexpr const char * noDedupAnnoClass
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
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 * extractAssertAnnoClass
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 * 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 * 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
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
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
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.