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"
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,
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>}},
625 RegResetOp, InstanceOp, MemOp, CombMemOp,
626 MemoryPortOp, SeqMemOp>}},
634 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
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)
721struct LowerAnnotationsPass
722 :
public circt::firrtl::impl::LowerFIRRTLAnnotationsBase<
723 LowerAnnotationsPass> {
726 void runOnOperation()
override;
727 LogicalResult applyAnnotation(DictionaryAttr anno,
ApplyState &state);
728 LogicalResult legacyToWiringProblems(
ApplyState &state);
729 LogicalResult solveWiringProblems(
ApplyState &state);
731 SmallVector<DictionaryAttr> worklistAttrs;
735LogicalResult LowerAnnotationsPass::applyAnnotation(DictionaryAttr anno,
737 LLVM_DEBUG(llvm::dbgs() <<
" - anno: " << anno <<
"\n";);
740 StringRef annoClassVal;
741 if (
auto annoClass = anno.getNamed(
"class"))
742 annoClassVal = cast<StringAttr>(annoClass->getValue()).getValue();
743 else if (ignoreAnnotationClassless)
744 annoClassVal =
"circt.missing";
746 return mlir::emitError(state.
circuit.getLoc())
747 <<
"Annotation without a class: " << anno;
753 if (!ignoreAnnotationUnknown)
754 return mlir::emitError(state.
circuit.getLoc())
755 <<
"Unhandled annotation: " << anno;
763 auto target = record->resolver(anno, state);
765 return mlir::emitError(state.
circuit.getLoc())
766 <<
"Unable to resolve target of annotation: " << anno;
767 if (record->applier(*target, anno, state).failed())
768 return mlir::emitError(state.
circuit.getLoc())
769 <<
"Unable to apply annotation: " << anno;
775LogicalResult LowerAnnotationsPass::legacyToWiringProblems(
ApplyState &state) {
776 for (
const auto &[name, problem] : state.legacyWiringProblems) {
778 return mlir::emitError(state.
circuit.getLoc())
779 <<
"Unable to resolve source for pin: " << name;
781 if (problem.sinks.empty())
782 return mlir::emitError(state.
circuit.getLoc())
783 <<
"Unable to resolve sink(s) for pin: " << name;
785 for (
const auto &sink : problem.sinks) {
787 {problem.source, sink, {}, WiringProblem::RefTypeUsage::Never});
800LogicalResult LowerAnnotationsPass::solveWiringProblems(
ApplyState &state) {
803 auto getModule = [](Value value) {
804 if (BlockArgument blockArg = dyn_cast<BlockArgument>(value))
805 return cast<FModuleLike>(blockArg.getParentBlock()->getParentOp());
806 return value.getDefiningOp()->getParentOfType<FModuleLike>();
810 auto findInsertionBlock = [&getModule](Value src, Value dest) -> Block * {
812 if (src.getParentBlock() == dest.getParentBlock())
813 return src.getParentBlock();
817 assert(getModule(src) == getModule(dest));
819 auto safelyDoms = [&](Value a, Value b) {
820 if (isa<BlockArgument>(a))
822 if (isa<BlockArgument>(b))
826 a.getParentBlock()->findAncestorOpInBlock(*b.getDefiningOp());
827 return ancestor && a.getDefiningOp()->isBeforeInBlock(ancestor);
829 if (safelyDoms(src, dest))
830 return dest.getParentBlock();
831 if (safelyDoms(dest, src))
832 return src.getParentBlock();
836 auto getNoopCast = [](Value v) -> mlir::UnrealizedConversionCastOp {
838 dyn_cast_or_null<mlir::UnrealizedConversionCastOp>(v.getDefiningOp());
839 if (op && op.getNumResults() == 1 && op.getNumOperands() == 1 &&
840 op.getResultTypes()[0] == op.getOperandTypes()[0])
847 SmallVector<Operation *> opsToErase;
848 auto connect = [&](Value src, Value dest,
849 ImplicitLocOpBuilder &builder) -> LogicalResult {
852 if (
auto op = getNoopCast(dest)) {
853 dest = op.getOperand(0);
854 opsToErase.push_back(op);
855 std::swap(src, dest);
856 }
else if (
auto op = getNoopCast(src)) {
857 src = op.getOperand(0);
858 opsToErase.push_back(op);
862 std::swap(src, dest);
865 auto *insertBlock = findInsertionBlock(src, dest);
867 return emitError(src.getLoc())
868 .append(
"This value is involved with a Wiring Problem where the "
869 "destination is in the same module but neither dominates the "
870 "other, which is not supported.")
871 .attachNote(dest.getLoc())
872 .append(
"The destination is here.");
875 builder.setInsertionPointToEnd(insertBlock);
878 if (type_isa<RefType>(dest.getType()) != type_isa<RefType>(src.getType())) {
879 if (type_isa<RefType>(dest.getType()))
880 src = RefSendOp::create(builder, src);
882 src = RefResolveOp::create(builder, src);
888 if (
auto destOp = dyn_cast_or_null<WireOp>(dest.getDefiningOp());
889 destOp && dest.getUses().empty()) {
891 if (
auto baseType = dyn_cast<FIRRTLBaseType>(src.getType());
892 baseType && baseType.isPassive()) {
895 NodeOp::create(builder, src, destOp.getName())
896 .setAnnotationsAttr(destOp.getAnnotations());
897 opsToErase.push_back(destOp);
909 auto *context = state.
circuit.getContext();
913 LLVM_DEBUG({ llvm::dbgs() <<
"Analyzing wiring problems:\n"; });
914 DenseMap<FModuleLike, ModuleModifications> moduleModifications;
915 DenseSet<Value> visitedSinks;
916 for (
auto e :
llvm::enumerate(state.wiringProblems)) {
917 auto index = e.index();
918 auto problem = e.value();
922 auto source = problem.source;
923 auto sink = problem.sink;
927 if (!visitedSinks.insert(sink).second) {
928 auto diag = mlir::emitError(source.getLoc())
929 <<
"This sink is involved with a Wiring Problem which is "
930 "targeted by a source used by another Wiring Problem. "
931 "(This is both illegal and should be impossible.)";
932 diag.attachNote(source.getLoc()) <<
"The source is here";
935 FModuleLike sourceModule = getModule(source);
936 FModuleLike sinkModule = getModule(sink);
937 if (isa<FExtModuleOp>(sourceModule) || isa<FExtModuleOp>(sinkModule)) {
938 auto diag = mlir::emitError(source.getLoc())
939 <<
"This source is involved with a Wiring Problem which "
940 "includes an External Module port and External Module "
941 "ports anre not supported.";
942 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
947 llvm::dbgs() <<
" - index: " << index <<
"\n"
949 <<
" module: " << sourceModule.getModuleName() <<
"\n"
950 <<
" value: " << source <<
"\n"
952 <<
" module: " << sinkModule.getModuleName() <<
"\n"
953 <<
" value: " << sink <<
"\n"
954 <<
" newNameHint: " << problem.newNameHint <<
"\n";
958 if (sink.getParentBlock() == source.getParentBlock()) {
959 auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(context),
960 sink.getParentBlock());
961 if (failed(
connect(source, sink, builder)))
968 if (sourceModule == sinkModule) {
969 LLVM_DEBUG(llvm::dbgs()
970 <<
" LCA: " << sourceModule.getModuleName() <<
"\n");
971 moduleModifications[sourceModule].connectionMap[index] = source;
972 moduleModifications[sourceModule].uturns.push_back({index, sink});
980 if (sourcePaths.size() != 1 || sinkPaths.size() != 1) {
982 mlir::emitError(source.getLoc())
983 <<
"This source is involved with a Wiring Problem where the source "
984 "or the sink are multiply instantiated and this is not supported.";
985 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
990 cast<FModuleOp>(instanceGraph.getTopLevelNode()->getModule());
991 auto sources = sourcePaths[0];
992 auto sinks = sinkPaths[0];
993 while (!sources.empty() && !sinks.empty()) {
994 if (sources.top() != sinks.top())
996 auto newLCA = cast<InstanceOp>(*sources.top());
997 lca = cast<FModuleOp>(newLCA.getReferencedModule(instanceGraph));
998 sources = sources.dropFront();
999 sinks = sinks.dropFront();
1003 llvm::dbgs() <<
" LCA: " << lca.getModuleName() <<
"\n"
1004 <<
" sourcePath: " << sourcePaths[0] <<
"\n"
1005 <<
" sinkPaths: " << sinkPaths[0] <<
"\n";
1009 moduleModifications[sourceModule].connectionMap[index] = source;
1010 moduleModifications[sinkModule].connectionMap[index] = sink;
1013 Type sourceType, sinkType;
1016 problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer;
1019 RefType refType = TypeSwitch<Type, RefType>(source.getType())
1021 return RefType::get(base.getPassiveType());
1023 .Case<RefType>([](RefType ref) {
return ref; });
1024 sourceType = refType;
1025 sinkType = refType.getType();
1028 sourceType = source.getType();
1029 sinkType = sink.getType();
1032 auto sourceFType = type_dyn_cast<FIRRTLType>(sourceType);
1033 auto sinkFType = type_dyn_cast<FIRRTLType>(sinkType);
1035 return emitError(source.getLoc())
1036 <<
"Wiring Problem source type \"" << sourceType
1037 <<
"\" must be a FIRRTL type";
1039 return emitError(sink.getLoc())
1040 <<
"Wiring Problem sink type \"" << sinkType
1041 <<
"\" must be a FIRRTL type";
1045 if (sourceFType != sinkFType &&
1048 if (
auto sourceBaseType = dyn_cast<FIRRTLBaseType>(sourceFType);
1049 problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer &&
1058 auto diag = mlir::emitError(source.getLoc())
1059 <<
"Wiring Problem source type " << sourceType
1060 <<
" does not match sink type " << sinkType;
1061 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
1068 if (
auto sinkFType = type_dyn_cast<FIRRTLType>(sink.getType());
1069 sinkFType && type_isa<RefType>(sourceType) &&
1071 return emitError(sink.getLoc())
1072 <<
"Wiring Problem sink type \"" << sink.getType()
1073 <<
"\" must be passive (no flips) when using references";
1078 StringRef name, instName;
1079 for (
auto instNode :
llvm::reverse(insts)) {
1080 auto inst = cast<InstanceOp>(*instNode);
1081 auto mod = inst.getReferencedModule<FModuleOp>(instanceGraph);
1082 if (mod.isPublic()) {
1083 if (!allowAddingPortsOnPublic) {
1084 auto diag = emitError(
1085 mod.getLoc(),
"cannot wire port through this public module");
1086 diag.attachNote(source.getLoc()) <<
"source here";
1087 diag.attachNote(sink.getLoc()) <<
"sink here";
1090 ++numPublicPortsWired;
1093 if (problem.newNameHint.empty())
1103 assert(!instName.empty());
1106 moduleModifications[mod].portsToAdd.push_back(
1107 {index, {StringAttr::get(context, name), tpe, dir}});
1108 instName = inst.getInstanceName();
1114 if (failed(addPorts(sources, source, sourceType, Direction::Out)) ||
1115 failed(addPorts(sinks, sink, sinkType, Direction::In)))
1121 LLVM_DEBUG({ llvm::dbgs() <<
"Updating modules:\n"; });
1122 for (
auto *op :
llvm::post_order(instanceGraph.getTopLevelNode())) {
1123 auto fmodule = dyn_cast<FModuleOp>(*op->getModule());
1125 if (!fmodule || !moduleModifications.count(fmodule))
1128 auto modifications = moduleModifications[fmodule];
1130 llvm::dbgs() <<
" - module: " << fmodule.getModuleName() <<
"\n";
1131 llvm::dbgs() <<
" ports:\n";
1132 for (
auto [index, port] : modifications.portsToAdd) {
1133 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
1134 <<
" id: " << index <<
"\n"
1135 <<
" type: " << port.type <<
"\n"
1137 << (port.direction == Direction::In ?
"in" :
"out")
1143 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1144 SmallVector<unsigned> problemIndices;
1145 for (
auto [problemIdx, portInfo] : modifications.portsToAdd) {
1147 newPorts.push_back({fmodule.getNumPorts(), portInfo});
1148 problemIndices.push_back(problemIdx);
1150 auto originalNumPorts = fmodule.getNumPorts();
1151 auto portIdx = fmodule.getNumPorts();
1152 fmodule.insertPorts(newPorts);
1154 auto builder = ImplicitLocOpBuilder::atBlockBegin(UnknownLoc::get(context),
1155 fmodule.getBodyBlock());
1159 for (
auto [problemIdx, portPair] :
llvm::zip(problemIndices, newPorts)) {
1160 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1161 assert(src &&
"there did not exist a driver for the port");
1162 Value dest = fmodule.getArgument(portIdx++);
1163 if (failed(
connect(src, dest, builder)))
1169 for (
auto [problemIdx, dest] : moduleModifications[fmodule].uturns) {
1170 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1171 assert(src &&
"there did not exist a connection for the u-turn");
1172 if (failed(
connect(src, dest, builder)))
1177 for (
auto *inst : instanceGraph.lookup(fmodule)->uses()) {
1178 InstanceOp useInst = cast<InstanceOp>(inst->getInstance());
1179 auto enclosingModule = useInst->getParentOfType<FModuleOp>();
1180 auto clonedInst = useInst.cloneAndInsertPorts(newPorts);
1184 useInst->replaceAllUsesWith(
1185 clonedInst.getResults().drop_back(newPorts.size()));
1192 for (
auto [newPortIdx, problemIdx] :
llvm::enumerate(problemIndices)) {
1193 auto &modifications = moduleModifications[enclosingModule];
1194 auto newPort = clonedInst.getResult(newPortIdx + originalNumPorts);
1195 if (modifications.connectionMap.count(problemIdx)) {
1196 modifications.uturns.push_back({problemIdx, newPort});
1199 modifications.connectionMap[problemIdx] = newPort;
1205 for (
auto *op : opsToErase)
1212void LowerAnnotationsPass::runOnOperation() {
1213 CircuitOp circuit = getOperation();
1214 SymbolTable modules(circuit);
1224 auto annotations = circuit->getAttrOfType<ArrayAttr>(
rawAnnotations);
1232 for (
auto anno :
llvm::reverse(annotations.getValue()))
1233 worklistAttrs.push_back(cast<DictionaryAttr>(anno));
1235 size_t numFailures = 0;
1236 size_t numAdded = 0;
1237 auto addToWorklist = [&](DictionaryAttr anno) {
1239 worklistAttrs.push_back(anno);
1242 ApplyState state{circuit, modules, addToWorklist, instancePathCache,
1244 LLVM_DEBUG(llvm::dbgs() <<
"Processing annotations:\n");
1245 while (!worklistAttrs.empty()) {
1246 auto attr = worklistAttrs.pop_back_val();
1247 if (applyAnnotation(attr, state).failed())
1251 if (failed(legacyToWiringProblems(state)))
1254 if (failed(solveWiringProblems(state)))
1258 numRawAnnotations += annotations.size();
1259 numAddedAnnos += numAdded;
1260 numAnnos += numAdded + annotations.size();
1264 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.
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 * 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".
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
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.