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 mlir::SymbolTable::setSymbolVisibility(moduleOp.getOperation(),
256 mlir::SymbolTable::Visibility::Public);
257 SmallVector<NamedAttribute> newAnnoAttrs;
258 for (
auto &na : anno)
259 if (na.getName().getValue() !=
"target")
260 newAnnoAttrs.push_back(na);
267 return ::llvm::StringSwitch<::std::optional<Convention>>(str)
268 .Case(
"scalarized", Convention::Scalarized)
269 .Default(std::nullopt);
276 auto loc = op->getLoc();
278 auto diag = mlir::emitError(loc);
279 diag <<
"circuit.ConventionAnnotation ";
283 auto opTarget = dyn_cast<OpAnnoTarget>(target.
ref);
285 return error() <<
"must target a module object";
288 return error() <<
"must be local";
290 auto conventionStrAttr =
291 tryGetAs<StringAttr>(anno, anno,
"convention", loc, conventionAnnoClass);
292 if (!conventionStrAttr)
295 auto conventionStr = conventionStrAttr.getValue();
298 return error() <<
"unknown convention " << conventionStr;
300 auto convention = *conventionOpt;
302 if (
auto moduleOp = dyn_cast<FModuleOp>(op)) {
303 moduleOp.setConvention(convention);
307 if (
auto extModuleOp = dyn_cast<FExtModuleOp>(op)) {
308 extModuleOp.setConvention(convention);
312 return error() <<
"can only target to a module or extmodule";
319 auto loc = op->getLoc();
321 auto diag = mlir::emitError(loc);
322 diag << bodyTypeLoweringAnnoClass;
326 auto opTarget = dyn_cast<OpAnnoTarget>(target.
ref);
328 return error() <<
"must target a module object";
331 return error() <<
"must be local";
333 auto moduleOp = dyn_cast<FModuleOp>(op);
336 return error() <<
"can only target to a module";
338 auto conventionStrAttr =
339 tryGetAs<StringAttr>(anno, anno,
"convention", loc, conventionAnnoClass);
341 if (!conventionStrAttr)
344 auto conventionStr = conventionStrAttr.getValue();
347 return error() <<
"unknown convention " << conventionStr;
349 auto convention = *conventionOpt;
351 if (convention == Convention::Internal)
355 auto conventionAttr = ConventionAttr::get(op->getContext(), convention);
358 bool includeHierarchy =
false;
359 if (
auto includeHierarchyAttr = tryGetAs<BoolAttr>(
360 anno, anno,
"includeHierarchy", loc, conventionAnnoClass))
361 includeHierarchy = includeHierarchyAttr.getValue();
363 if (includeHierarchy) {
370 if (
auto fmodule = dyn_cast<FModuleOp>(*node->getModule()))
371 fmodule->setAttr(
"body_type_lowering", conventionAttr);
375 moduleOp->setAttr(
"body_type_lowering", conventionAttr);
385 auto loc = op->getLoc();
387 auto diag = mlir::emitError(loc);
388 diag << modulePrefixAnnoClass <<
" ";
392 auto opTarget = dyn_cast<OpAnnoTarget>(target.
ref);
394 return error() <<
"must target an operation";
396 if (!isa<SeqMemOp, CombMemOp, MemOp>(opTarget.getOp()))
397 return error() <<
"must target a memory operation";
400 return error() <<
"must be local";
403 tryGetAs<StringAttr>(anno, anno,
"prefix", loc, modulePrefixAnnoClass);
407 if (
auto mem = dyn_cast<SeqMemOp>(op))
408 mem.setPrefixAttr(prefixStrAttr);
409 else if (
auto mem = dyn_cast<CombMemOp>(op))
410 mem.setPrefixAttr(prefixStrAttr);
411 else if (
auto mem = dyn_cast<MemOp>(op))
412 mem.setPrefixAttr(prefixStrAttr);
423 auto diag = mlir::emitError(op->getLoc());
424 diag << anno.getAs<StringAttr>(
"class").getValue() <<
" ";
428 if (!isa<OpAnnoTarget>(target.
ref))
430 <<
"must target an operation. Currently ports are not supported";
433 return error() <<
"must be local";
435 if (!isa<FModuleOp, WireOp, NodeOp, RegOp, RegResetOp>(op))
437 <<
"unhandled operation. The target must be a module, wire, node or "
440 auto name = anno.getAs<StringAttr>(
"description");
441 auto svAttr = sv::SVAttributeAttr::get(name.getContext(), name);
442 sv::addSVAttributes(op, {svAttr});
447template <
bool isInline>
452 mlir::emitError(state.
circuit.getLoc())
453 <<
"has a " << anno.get(
"class")
454 <<
" annotation which is non-local, but this annotation is not allowed "
461 if (!target.
isOpOfType<MemOp, CombMemOp, SeqMemOp>()) {
462 mlir::emitError(op->getLoc())
463 <<
"can only apply a load memory annotation to a memory";
468 StringAttr filename = tryGetAs<StringAttr>(
469 anno, anno, isInline ?
"filename" :
"fileName", op->getLoc(),
470 anno.getAs<StringAttr>(
"class").getValue());
475 tryGetAs<StringAttr>(anno, anno,
"hexOrBinary", op->getLoc(),
476 anno.getAs<StringAttr>(
"class").getValue());
480 auto hexOrBinaryValue = hexOrBinary.getValue();
481 if (hexOrBinaryValue !=
"h" && hexOrBinaryValue !=
"b") {
482 auto diag = mlir::emitError(op->getLoc())
483 <<
"has memory initialization annotation with invalid format, "
484 "'hexOrBinary' field must be either 'h' or 'b'";
485 diag.attachNote() <<
"the full annotation is: " << anno;
489 op->setAttr(
"init", MemoryInitAttr::get(op->getContext(), filename,
490 hexOrBinaryValue ==
"b", isInline));
499 auto *
context = op->getContext();
500 auto loc = op->getLoc();
503 return mlir::emitError(loc) << outputDirAnnoClass <<
" ";
506 auto opTarget = dyn_cast<OpAnnoTarget>(target.
ref);
508 return error() <<
"must target a module";
510 return error() <<
"must be local";
512 auto moduleOp = dyn_cast<FModuleOp>(op);
514 return error() <<
"must target a module";
515 if (!moduleOp.isPublic())
516 return error() <<
"must target a public module";
517 if (moduleOp->hasAttr(
"output_file"))
518 return error() <<
"target already has an output file";
521 tryGetAs<StringAttr>(anno, anno,
"dirname", loc, outputDirAnnoClass);
525 return error() <<
"dirname must not be empty";
528 hw::OutputFileAttr::getAsDirectory(
context, dirname.getValue());
530 moduleOp->setAttr(
"output_file", outputFile);
539 auto *
context = op->getContext();
541 mlir::emitWarning(op->getLoc())
542 <<
"'" << fullAsyncResetAnnoClass <<
"' is deprecated, use '"
543 << fullResetAnnoClass <<
"' instead";
545 NamedAttrList newAnno(anno.getValue());
546 newAnno.set(
"class", StringAttr::get(
context, fullResetAnnoClass));
547 newAnno.append(
"resetType", StringAttr::get(
context,
"async"));
549 DictionaryAttr newDictionary = DictionaryAttr::get(op->getContext(), newAnno);
551 return applyWithoutTarget<false>(target, newDictionary, state);
559 auto *
context = op->getContext();
561 mlir::emitWarning(op->getLoc())
562 <<
"'" << ignoreFullAsyncResetAnnoClass <<
"' is deprecated, use '"
563 << excludeFromFullResetAnnoClass <<
"' instead";
565 NamedAttrList newAnno(anno.getValue());
566 newAnno.set(
"class", StringAttr::get(
context, excludeFromFullResetAnnoClass));
568 DictionaryAttr newDictionary = DictionaryAttr::get(op->getContext(), newAnno);
570 return applyWithoutTarget<true, FModuleOp>(target, newDictionary, state);
591 applyWithoutTarget<false, CircuitOp>};
596 {
"circt.test", {
stdResolve, applyWithoutTarget<true>}},
597 {
"circt.testLocalOnly", {
stdResolve, applyWithoutTarget<>}},
598 {
"circt.testNT", {
noResolve, applyWithoutTarget<>}},
599 {
"circt.missing", {
tryResolve, applyWithoutTarget<true>}},
602#define GET_ANNOTATION_RECORD_LIST
603#include "circt/Dialect/FIRRTL/FIRRTLAnnotationRecords.h.inc"
608 const std::function<
void(llvm::Twine)> &errorHandler) {
611 return LogicalResult::success();
613 errorHandler(
"annotation record '" + annoClass +
"' is registered twice\n");
614 return LogicalResult::failure();
622 bool ignoreAnnotationUnknown) {
626 if (ignoreAnnotationUnknown)
636struct LowerAnnotationsPass
637 :
public circt::firrtl::impl::LowerFIRRTLAnnotationsBase<
638 LowerAnnotationsPass> {
641 void runOnOperation()
override;
642 LogicalResult applyAnnotation(DictionaryAttr anno,
ApplyState &state);
643 LogicalResult legacyToWiringProblems(
ApplyState &state);
644 LogicalResult solveWiringProblems(
ApplyState &state);
646 SmallVector<DictionaryAttr> worklistAttrs;
650LogicalResult LowerAnnotationsPass::applyAnnotation(DictionaryAttr anno,
652 LLVM_DEBUG(llvm::dbgs() <<
" - anno: " << anno <<
"\n";);
655 StringRef annoClassVal;
656 if (
auto annoClass = anno.getNamed(
"class"))
657 annoClassVal = cast<StringAttr>(annoClass->getValue()).getValue();
658 else if (ignoreAnnotationClassless)
659 annoClassVal =
"circt.missing";
661 return mlir::emitError(state.
circuit.getLoc())
662 <<
"Annotation without a class: " << anno;
668 if (!ignoreAnnotationUnknown)
669 return mlir::emitError(state.
circuit.getLoc())
670 <<
"Unhandled annotation: " << anno;
678 auto target = record->resolver(anno, state);
680 return mlir::emitError(state.
circuit.getLoc())
681 <<
"Unable to resolve target of annotation: " << anno;
682 if (record->applier(*target, anno, state).failed())
683 return mlir::emitError(state.
circuit.getLoc())
684 <<
"Unable to apply annotation: " << anno;
690LogicalResult LowerAnnotationsPass::legacyToWiringProblems(
ApplyState &state) {
691 for (
const auto &[name, problem] : state.legacyWiringProblems) {
693 return mlir::emitError(state.
circuit.getLoc())
694 <<
"Unable to resolve source for pin: " << name;
696 if (problem.sinks.empty())
697 return mlir::emitError(state.
circuit.getLoc())
698 <<
"Unable to resolve sink(s) for pin: " << name;
700 for (
const auto &sink : problem.sinks) {
702 {problem.source, sink, {}, WiringProblem::RefTypeUsage::Never});
715LogicalResult LowerAnnotationsPass::solveWiringProblems(
ApplyState &state) {
718 auto getModule = [](Value value) {
719 if (BlockArgument blockArg = dyn_cast<BlockArgument>(value))
720 return cast<FModuleLike>(blockArg.getParentBlock()->getParentOp());
721 return value.getDefiningOp()->getParentOfType<FModuleLike>();
725 auto findInsertionBlock = [&getModule](Value src, Value dest) -> Block * {
727 if (src.getParentBlock() == dest.getParentBlock())
728 return src.getParentBlock();
732 assert(getModule(src) == getModule(dest));
734 auto safelyDoms = [&](Value a, Value b) {
735 if (isa<BlockArgument>(a))
737 if (isa<BlockArgument>(b))
741 a.getParentBlock()->findAncestorOpInBlock(*b.getDefiningOp());
742 return ancestor && a.getDefiningOp()->isBeforeInBlock(ancestor);
744 if (safelyDoms(src, dest))
745 return dest.getParentBlock();
746 if (safelyDoms(dest, src))
747 return src.getParentBlock();
751 auto getNoopCast = [](Value v) -> mlir::UnrealizedConversionCastOp {
753 dyn_cast_or_null<mlir::UnrealizedConversionCastOp>(v.getDefiningOp());
754 if (op && op.getNumResults() == 1 && op.getNumOperands() == 1 &&
755 op.getResultTypes()[0] == op.getOperandTypes()[0])
762 SmallVector<Operation *> opsToErase;
763 auto connect = [&](Value src, Value dest,
764 ImplicitLocOpBuilder &builder) -> LogicalResult {
767 if (
auto op = getNoopCast(dest)) {
768 dest = op.getOperand(0);
769 opsToErase.push_back(op);
770 std::swap(src, dest);
771 }
else if (
auto op = getNoopCast(src)) {
772 src = op.getOperand(0);
773 opsToErase.push_back(op);
777 std::swap(src, dest);
780 auto *insertBlock = findInsertionBlock(src, dest);
782 return emitError(src.getLoc())
783 .append(
"This value is involved with a Wiring Problem where the "
784 "destination is in the same module but neither dominates the "
785 "other, which is not supported.")
786 .attachNote(dest.getLoc())
787 .append(
"The destination is here.");
790 builder.setInsertionPointToEnd(insertBlock);
793 if (type_isa<RefType>(dest.getType()) != type_isa<RefType>(src.getType())) {
794 if (type_isa<RefType>(dest.getType()))
795 src = RefSendOp::create(builder, src);
797 src = RefResolveOp::create(builder, src);
803 if (
auto destOp = dyn_cast_or_null<WireOp>(dest.getDefiningOp());
804 destOp && dest.getUses().empty()) {
806 if (
auto baseType = dyn_cast<FIRRTLBaseType>(src.getType());
807 baseType && baseType.isPassive()) {
810 NodeOp::create(builder, src, destOp.getName())
811 .setAnnotationsAttr(destOp.getAnnotations());
812 opsToErase.push_back(destOp);
828 LLVM_DEBUG({ llvm::dbgs() <<
"Analyzing wiring problems:\n"; });
829 DenseMap<FModuleLike, ModuleModifications> moduleModifications;
830 DenseSet<Value> visitedSinks;
831 for (
auto e :
llvm::enumerate(state.wiringProblems)) {
832 auto index = e.index();
833 auto problem = e.value();
837 auto source = problem.source;
838 auto sink = problem.sink;
842 if (!visitedSinks.insert(sink).second) {
843 auto diag = mlir::emitError(source.getLoc())
844 <<
"This sink is involved with a Wiring Problem which is "
845 "targeted by a source used by another Wiring Problem. "
846 "(This is both illegal and should be impossible.)";
847 diag.attachNote(source.getLoc()) <<
"The source is here";
850 FModuleLike sourceModule = getModule(source);
851 FModuleLike sinkModule = getModule(sink);
852 if (isa<FExtModuleOp>(sourceModule) || isa<FExtModuleOp>(sinkModule)) {
853 auto diag = mlir::emitError(source.getLoc())
854 <<
"This source is involved with a Wiring Problem which "
855 "includes an External Module port and External Module "
856 "ports anre not supported.";
857 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
862 llvm::dbgs() <<
" - index: " << index <<
"\n"
864 <<
" module: " << sourceModule.getModuleName() <<
"\n"
865 <<
" value: " << source <<
"\n"
867 <<
" module: " << sinkModule.getModuleName() <<
"\n"
868 <<
" value: " << sink <<
"\n"
869 <<
" newNameHint: " << problem.newNameHint <<
"\n";
873 if (sink.getParentBlock() == source.getParentBlock()) {
874 auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(
context),
875 sink.getParentBlock());
876 if (failed(
connect(source, sink, builder)))
883 if (sourceModule == sinkModule) {
884 LLVM_DEBUG(llvm::dbgs()
885 <<
" LCA: " << sourceModule.getModuleName() <<
"\n");
886 moduleModifications[sourceModule].connectionMap[index] = source;
887 moduleModifications[sourceModule].uturns.push_back({index, sink});
895 if (sourcePaths.size() != 1 || sinkPaths.size() != 1) {
897 mlir::emitError(source.getLoc())
898 <<
"This source is involved with a Wiring Problem where the source "
899 "or the sink are multiply instantiated and this is not supported.";
900 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
905 cast<FModuleOp>(instanceGraph.getTopLevelNode()->getModule());
906 auto sources = sourcePaths[0];
907 auto sinks = sinkPaths[0];
908 while (!sources.empty() && !sinks.empty()) {
909 if (sources.top() != sinks.top())
911 auto newLCA = cast<InstanceOp>(*sources.top());
912 lca = cast<FModuleOp>(newLCA.getReferencedModule(instanceGraph));
913 sources = sources.dropFront();
914 sinks = sinks.dropFront();
918 llvm::dbgs() <<
" LCA: " << lca.getModuleName() <<
"\n"
919 <<
" sourcePath: " << sourcePaths[0] <<
"\n"
920 <<
" sinkPaths: " << sinkPaths[0] <<
"\n";
924 moduleModifications[sourceModule].connectionMap[index] = source;
925 moduleModifications[sinkModule].connectionMap[index] = sink;
928 Type sourceType, sinkType;
931 problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer;
934 RefType refType = TypeSwitch<Type, RefType>(source.getType())
936 return RefType::get(base.getPassiveType());
938 .Case<RefType>([](RefType ref) {
return ref; });
939 sourceType = refType;
940 sinkType = refType.getType();
943 sourceType = source.getType();
944 sinkType = sink.getType();
947 auto sourceFType = type_dyn_cast<FIRRTLType>(sourceType);
948 auto sinkFType = type_dyn_cast<FIRRTLType>(sinkType);
950 return emitError(source.getLoc())
951 <<
"Wiring Problem source type \"" << sourceType
952 <<
"\" must be a FIRRTL type";
954 return emitError(sink.getLoc())
955 <<
"Wiring Problem sink type \"" << sinkType
956 <<
"\" must be a FIRRTL type";
960 if (sourceFType != sinkFType &&
963 if (
auto sourceBaseType = dyn_cast<FIRRTLBaseType>(sourceFType);
964 problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer &&
973 auto diag = mlir::emitError(source.getLoc())
974 <<
"Wiring Problem source type " << sourceType
975 <<
" does not match sink type " << sinkType;
976 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
983 if (
auto sinkFType = type_dyn_cast<FIRRTLType>(sink.getType());
984 sinkFType && type_isa<RefType>(sourceType) &&
986 return emitError(sink.getLoc())
987 <<
"Wiring Problem sink type \"" << sink.getType()
988 <<
"\" must be passive (no flips) when using references";
993 StringRef name, instName;
994 for (
auto instNode :
llvm::reverse(insts)) {
995 auto inst = cast<InstanceOp>(*instNode);
996 auto mod = inst.getReferencedModule<FModuleOp>(instanceGraph);
997 if (mod.isPublic()) {
998 auto diag = emitError(mod.getLoc(),
999 "cannot wire port through this public module");
1000 diag.attachNote(source.getLoc()) <<
"source here";
1001 diag.attachNote(sink.getLoc()) <<
"sink here";
1005 if (problem.newNameHint.empty())
1015 assert(!instName.empty());
1018 moduleModifications[mod].portsToAdd.push_back(
1019 {index, {StringAttr::get(
context, name), tpe, dir}});
1020 instName = inst.getInstanceName();
1026 if (failed(addPorts(sources, source, sourceType, Direction::Out)) ||
1027 failed(addPorts(sinks, sink, sinkType, Direction::In)))
1033 LLVM_DEBUG({ llvm::dbgs() <<
"Updating modules:\n"; });
1034 for (
auto *op :
llvm::post_order(instanceGraph.getTopLevelNode())) {
1035 auto fmodule = dyn_cast<FModuleOp>(*op->getModule());
1037 if (!fmodule || !moduleModifications.count(fmodule))
1040 auto modifications = moduleModifications[fmodule];
1042 llvm::dbgs() <<
" - module: " << fmodule.getModuleName() <<
"\n";
1043 llvm::dbgs() <<
" ports:\n";
1044 for (
auto [index, port] : modifications.portsToAdd) {
1045 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
1046 <<
" id: " << index <<
"\n"
1047 <<
" type: " << port.type <<
"\n"
1049 << (port.direction == Direction::In ?
"in" :
"out")
1055 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1056 SmallVector<unsigned> problemIndices;
1057 for (
auto [problemIdx, portInfo] : modifications.portsToAdd) {
1059 newPorts.push_back({fmodule.getNumPorts(), portInfo});
1060 problemIndices.push_back(problemIdx);
1062 auto originalNumPorts = fmodule.getNumPorts();
1063 auto portIdx = fmodule.getNumPorts();
1064 fmodule.insertPorts(newPorts);
1066 auto builder = ImplicitLocOpBuilder::atBlockBegin(UnknownLoc::get(
context),
1067 fmodule.getBodyBlock());
1071 for (
auto [problemIdx, portPair] :
llvm::zip(problemIndices, newPorts)) {
1072 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1073 assert(src &&
"there did not exist a driver for the port");
1074 Value dest = fmodule.getArgument(portIdx++);
1075 if (failed(
connect(src, dest, builder)))
1081 for (
auto [problemIdx, dest] : moduleModifications[fmodule].uturns) {
1082 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1083 assert(src &&
"there did not exist a connection for the u-turn");
1084 if (failed(
connect(src, dest, builder)))
1089 for (
auto *inst : instanceGraph.lookup(fmodule)->uses()) {
1090 InstanceOp useInst = cast<InstanceOp>(inst->getInstance());
1091 auto enclosingModule = useInst->getParentOfType<FModuleOp>();
1092 auto clonedInst = useInst.cloneWithInsertedPortsAndReplaceUses(newPorts);
1100 for (
auto [newPortIdx, problemIdx] :
llvm::enumerate(problemIndices)) {
1101 auto &modifications = moduleModifications[enclosingModule];
1102 auto newPort = clonedInst->getResult(newPortIdx + originalNumPorts);
1103 if (modifications.connectionMap.count(problemIdx)) {
1104 modifications.uturns.push_back({problemIdx, newPort});
1107 modifications.connectionMap[problemIdx] = newPort;
1113 for (
auto *op : opsToErase)
1120void LowerAnnotationsPass::runOnOperation() {
1123 CircuitOp circuit = getOperation();
1124 SymbolTable modules(circuit);
1132 auto annotations = circuit->getAttrOfType<ArrayAttr>(
rawAnnotations);
1140 for (
auto anno :
llvm::reverse(annotations.getValue()))
1141 worklistAttrs.push_back(cast<DictionaryAttr>(anno));
1143 size_t numFailures = 0;
1144 size_t numAdded = 0;
1145 auto addToWorklist = [&](DictionaryAttr anno) {
1147 worklistAttrs.push_back(anno);
1150 ApplyState state{circuit, modules, addToWorklist, instancePathCache,
1152 LLVM_DEBUG(llvm::dbgs() <<
"Processing annotations:\n");
1153 while (!worklistAttrs.empty()) {
1154 auto attr = worklistAttrs.pop_back_val();
1155 if (applyAnnotation(attr, state).failed())
1159 if (failed(legacyToWiringProblems(state)))
1162 if (failed(solveWiringProblems(state)))
1166 numRawAnnotations += annotations.size();
1167 numAddedAnnos += numAdded;
1168 numAnnos += numAdded + annotations.size();
1172 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.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs, bool warnOnTruncation=false)
Emit a connect between two values.
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.
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.