17 #include "mlir/Pass/Pass.h"
35 #include "mlir/IR/Diagnostics.h"
36 #include "llvm/ADT/APSInt.h"
37 #include "llvm/ADT/PostOrderIterator.h"
38 #include "llvm/ADT/StringExtras.h"
39 #include "llvm/Support/Debug.h"
41 #define DEBUG_TYPE "lower-annos"
45 #define GEN_PASS_DEF_LOWERFIRRTLANNOTATIONS
46 #include "circt/Dialect/FIRRTL/Passes.h.inc"
50 using namespace circt;
51 using namespace firrtl;
52 using namespace chirrtl;
65 SmallVector<Attribute> old(array.begin(), array.end());
73 SmallVector<Attribute> old(array.begin(), array.end());
81 ArrayRef<NamedAttribute> anno) {
82 auto *context = ref.
getOp()->getContext();
83 DictionaryAttr annotation;
85 SmallVector<NamedAttribute> annoField(anno.begin(), anno.end());
86 annoField.emplace_back(
95 if (isa<OpAnnoTarget>(ref)) {
101 auto portRef = cast<PortAnnoTarget>(ref);
103 ArrayAttr portAnno = dyn_cast_or_null<ArrayAttr>(portAnnoRaw);
105 SmallVector<Attribute> emptyPortAttr(
111 portAnno, portRef.getPortNo(),
114 ref.
getOp()->setAttr(
"portAnnotations", portAnno);
121 OpBuilder b(state.
circuit.getBodyRegion());
122 SmallVector<Attribute> insts;
125 state.
getNamespace(inst->getParentOfType<FModuleLike>())));
142 FlatSymbolRefAttr sym =
buildNLA(target, state);
151 static std::optional<AnnoPathValue>
noResolve(DictionaryAttr anno,
161 StringRef path{pathStr};
165 mlir::emitError(state.
circuit.getLoc())
166 <<
"Cannot tokenize annotation path " << rawPath;
179 auto target = anno.getNamed(
"target");
181 mlir::emitError(state.
circuit.getLoc())
182 <<
"No target field in annotation " << anno;
185 if (!isa<StringAttr>(target->getValue())) {
186 mlir::emitError(state.
circuit.getLoc())
187 <<
"Target field in annotation doesn't contain string " << anno;
190 return stdResolveImpl(cast<StringAttr>(target->getValue()).getValue(), state);
196 auto target = anno.getNamed(
"target");
198 return stdResolveImpl(cast<StringAttr>(target->getValue()).getValue(),
213 bool allowNonLocal) {
214 if (!allowNonLocal && !target.
isLocal()) {
216 auto diag = mlir::emitError(target.
ref.
getOp()->getLoc())
217 <<
"is targeted by a non-local annotation \""
218 << annotation.
getClass() <<
"\" with target "
220 <<
", but this annotation cannot be non-local";
221 diag.attachNote() <<
"see current annotation: " << anno <<
"\n";
224 SmallVector<NamedAttribute> newAnnoAttrs;
225 for (
auto &na : anno) {
226 if (na.getName().getValue() !=
"target") {
227 newAnnoAttrs.push_back(na);
228 }
else if (!target.
isLocal()) {
230 newAnnoAttrs.push_back(
251 auto loc = op->getLoc();
254 return mlir::emitError(loc) <<
"must be local";
256 if (!isa<OpAnnoTarget>(target.
ref) || !isa<FModuleOp>(op))
257 return mlir::emitError(loc) <<
"can only target to a module";
259 auto moduleOp = cast<FModuleOp>(op);
262 moduleOp.setPublic();
263 SmallVector<NamedAttribute> newAnnoAttrs;
264 for (
auto &na : anno)
265 if (na.getName().getValue() !=
"target")
266 newAnnoAttrs.push_back(na);
273 return ::llvm::StringSwitch<::std::optional<Convention>>(str)
274 .Case(
"scalarized", Convention::Scalarized)
275 .Default(std::nullopt);
282 auto loc = op->getLoc();
284 auto diag = mlir::emitError(loc);
285 diag <<
"circuit.ConventionAnnotation ";
289 auto opTarget = dyn_cast<OpAnnoTarget>(target.
ref);
291 return error() <<
"must target a module object";
294 return error() <<
"must be local";
296 auto conventionStrAttr =
298 if (!conventionStrAttr)
301 auto conventionStr = conventionStrAttr.getValue();
304 return error() <<
"unknown convention " << conventionStr;
306 auto convention = *conventionOpt;
308 if (
auto moduleOp = dyn_cast<FModuleOp>(op)) {
309 moduleOp.setConvention(convention);
313 if (
auto extModuleOp = dyn_cast<FExtModuleOp>(op)) {
314 extModuleOp.setConvention(convention);
318 return error() <<
"can only target to a module or extmodule";
325 auto loc = op->getLoc();
327 auto diag = mlir::emitError(loc);
332 auto opTarget = dyn_cast<OpAnnoTarget>(target.
ref);
334 return error() <<
"must target an operation";
336 if (!isa<SeqMemOp, CombMemOp, MemOp>(opTarget.getOp()))
337 return error() <<
"must target a memory operation";
340 return error() <<
"must be local";
347 if (
auto mem = dyn_cast<SeqMemOp>(op))
348 mem.setPrefixAttr(prefixStrAttr);
349 else if (
auto mem = dyn_cast<CombMemOp>(op))
350 mem.setPrefixAttr(prefixStrAttr);
351 else if (
auto mem = dyn_cast<MemOp>(op))
352 mem.setPrefixAttr(prefixStrAttr);
363 auto diag = mlir::emitError(op->getLoc());
364 diag << anno.getAs<StringAttr>(
"class").getValue() <<
" ";
368 if (!isa<OpAnnoTarget>(target.
ref))
370 <<
"must target an operation. Currently ports are not supported";
373 return error() <<
"must be local";
375 if (!isa<FModuleOp, WireOp, NodeOp, RegOp, RegResetOp>(op))
377 <<
"unhandled operation. The target must be a module, wire, node or "
380 auto name = anno.getAs<StringAttr>(
"description");
387 template <
bool isInline>
392 mlir::emitError(state.
circuit.getLoc())
393 <<
"has a " << anno.get(
"class")
394 <<
" annotation which is non-local, but this annotation is not allowed "
401 if (!target.
isOpOfType<MemOp, CombMemOp, SeqMemOp>()) {
402 mlir::emitError(op->getLoc())
403 <<
"can only apply a load memory annotation to a memory";
408 StringAttr filename = tryGetAs<StringAttr>(
409 anno, anno, isInline ?
"filename" :
"fileName", op->getLoc(),
410 anno.getAs<StringAttr>(
"class").getValue());
415 tryGetAs<StringAttr>(anno, anno,
"hexOrBinary", op->getLoc(),
416 anno.getAs<StringAttr>(
"class").getValue());
420 auto hexOrBinaryValue = hexOrBinary.getValue();
421 if (hexOrBinaryValue !=
"h" && hexOrBinaryValue !=
"b") {
422 auto diag = mlir::emitError(op->getLoc())
423 <<
"has memory initialization annotation with invalid format, "
424 "'hexOrBinary' field must be either 'h' or 'b'";
425 diag.attachNote() <<
"the full annotation is: " << anno;
430 hexOrBinaryValue ==
"b", isInline));
439 auto *context = op->getContext();
440 auto loc = op->getLoc();
446 auto opTarget = dyn_cast<OpAnnoTarget>(target.
ref);
448 return error() <<
"must target a module";
450 return error() <<
"must be local";
452 auto moduleOp = dyn_cast<FModuleOp>(op);
454 return error() <<
"must target a module";
455 if (!moduleOp.isPublic())
456 return error() <<
"must target a public module";
457 if (moduleOp->hasAttr(
"output_file"))
458 return error() <<
"target already has an output file";
465 return error() <<
"dirname must not be empty";
468 hw::OutputFileAttr::getAsDirectory(context, dirname.getValue());
470 moduleOp->setAttr(
"output_file", outputFile);
479 auto *context = op->getContext();
481 mlir::emitWarning(op->getLoc())
485 NamedAttrList newAnno(anno.getValue());
491 return applyWithoutTarget<false>(target, newDictionary, state);
499 auto *context = op->getContext();
501 mlir::emitWarning(op->getLoc())
505 NamedAttrList newAnno(anno.getValue());
510 return applyWithoutTarget<true, FModuleOp>(target, newDictionary, state);
531 applyWithoutTarget<false, CircuitOp>};
536 {
"circt.test", {
stdResolve, applyWithoutTarget<true>}},
537 {
"circt.testLocalOnly", {
stdResolve, applyWithoutTarget<>}},
538 {
"circt.testNT", {
noResolve, applyWithoutTarget<>}},
539 {
"circt.missing", {
tryResolve, applyWithoutTarget<true>}},
569 RegResetOp, InstanceOp, MemOp, CombMemOp,
570 MemoryPortOp, SeqMemOp>}},
573 applyWithoutTarget<true, FModuleOp, FExtModuleOp, InstanceOp>}},
580 {
stdResolve, applyWithoutTarget<true, MemOp, CombMemOp>}},
586 {
stdResolve, applyWithoutTarget<true, FModuleOp, FExtModuleOp>}},
590 {
stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
592 {
stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
594 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
596 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
598 {
stdResolve, applyWithoutTarget<false, FModuleOp>}},
600 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
604 {
stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
624 {
stdResolve, applyWithoutTarget<true, FModuleOp>}},
641 const std::function<
void(llvm::Twine)> &errorHandler) {
644 return LogicalResult::success();
646 errorHandler(
"annotation record '" + annoClass +
"' is registered twice\n");
647 return LogicalResult::failure();
655 bool ignoreAnnotationUnknown) {
659 if (ignoreAnnotationUnknown)
669 struct LowerAnnotationsPass
670 :
public circt::firrtl::impl::LowerFIRRTLAnnotationsBase<
671 LowerAnnotationsPass> {
672 void runOnOperation()
override;
673 LogicalResult applyAnnotation(DictionaryAttr anno,
ApplyState &state);
674 LogicalResult legacyToWiringProblems(
ApplyState &state);
675 LogicalResult solveWiringProblems(
ApplyState &state);
677 using LowerFIRRTLAnnotationsBase::allowAddingPortsOnPublic;
678 using LowerFIRRTLAnnotationsBase::ignoreAnnotationClassless;
679 using LowerFIRRTLAnnotationsBase::ignoreAnnotationUnknown;
680 using LowerFIRRTLAnnotationsBase::noRefTypePorts;
681 SmallVector<DictionaryAttr> worklistAttrs;
685 LogicalResult LowerAnnotationsPass::applyAnnotation(DictionaryAttr anno,
687 LLVM_DEBUG(llvm::dbgs() <<
" - anno: " << anno <<
"\n";);
690 StringRef annoClassVal;
691 if (
auto annoClass = anno.getNamed(
"class"))
692 annoClassVal = cast<StringAttr>(annoClass->getValue()).getValue();
693 else if (ignoreAnnotationClassless)
694 annoClassVal =
"circt.missing";
696 return mlir::emitError(state.
circuit.getLoc())
697 <<
"Annotation without a class: " << anno;
703 if (!ignoreAnnotationUnknown)
704 return mlir::emitError(state.
circuit.getLoc())
705 <<
"Unhandled annotation: " << anno;
713 auto target = record->resolver(anno, state);
715 return mlir::emitError(state.
circuit.getLoc())
716 <<
"Unable to resolve target of annotation: " << anno;
717 if (record->applier(*target, anno, state).failed())
718 return mlir::emitError(state.
circuit.getLoc())
719 <<
"Unable to apply annotation: " << anno;
725 LogicalResult LowerAnnotationsPass::legacyToWiringProblems(
ApplyState &state) {
728 return mlir::emitError(state.
circuit.getLoc())
729 <<
"Unable to resolve source for pin: " << name;
731 if (problem.sinks.empty())
732 return mlir::emitError(state.
circuit.getLoc())
733 <<
"Unable to resolve sink(s) for pin: " << name;
735 for (
const auto &sink : problem.sinks) {
750 LogicalResult LowerAnnotationsPass::solveWiringProblems(
ApplyState &state) {
753 auto getModule = [](Value value) {
754 if (BlockArgument blockArg = dyn_cast<BlockArgument>(value))
755 return cast<FModuleLike>(blockArg.getParentBlock()->getParentOp());
756 return value.getDefiningOp()->getParentOfType<FModuleLike>();
760 auto findInsertionBlock = [&getModule](Value src, Value dest) -> Block * {
762 if (src.getParentBlock() == dest.getParentBlock())
763 return src.getParentBlock();
767 assert(getModule(src) == getModule(dest));
769 auto safelyDoms = [&](Value a, Value b) {
770 if (isa<BlockArgument>(a))
772 if (isa<BlockArgument>(b))
776 a.getParentBlock()->findAncestorOpInBlock(*b.getDefiningOp());
777 return ancestor && a.getDefiningOp()->isBeforeInBlock(ancestor);
779 if (safelyDoms(src, dest))
780 return dest.getParentBlock();
781 if (safelyDoms(dest, src))
782 return src.getParentBlock();
786 auto getNoopCast = [](Value v) -> mlir::UnrealizedConversionCastOp {
788 dyn_cast_or_null<mlir::UnrealizedConversionCastOp>(v.getDefiningOp());
789 if (op && op.getNumResults() == 1 && op.getNumOperands() == 1 &&
790 op.getResultTypes()[0] == op.getOperandTypes()[0])
797 SmallVector<Operation *> opsToErase;
798 auto connect = [&](Value src, Value dest,
799 ImplicitLocOpBuilder &builder) -> LogicalResult {
802 if (
auto op = getNoopCast(dest)) {
803 dest = op.getOperand(0);
804 opsToErase.push_back(op);
805 std::swap(src, dest);
806 }
else if (
auto op = getNoopCast(src)) {
807 src = op.getOperand(0);
808 opsToErase.push_back(op);
812 std::swap(src, dest);
815 auto *insertBlock = findInsertionBlock(src, dest);
817 return emitError(src.getLoc())
818 .append(
"This value is involved with a Wiring Problem where the "
819 "destination is in the same module but neither dominates the "
820 "other, which is not supported.")
821 .attachNote(dest.getLoc())
822 .append(
"The destination is here.");
825 builder.setInsertionPointToEnd(insertBlock);
828 if (type_isa<RefType>(dest.getType()) != type_isa<RefType>(src.getType())) {
829 if (type_isa<RefType>(dest.getType()))
830 src = builder.create<RefSendOp>(src);
832 src = builder.create<RefResolveOp>(src);
838 if (
auto destOp = dyn_cast_or_null<WireOp>(dest.getDefiningOp());
839 destOp && dest.getUses().empty()) {
841 if (
auto baseType = dyn_cast<FIRRTLBaseType>(src.getType());
842 baseType && baseType.isPassive()) {
845 builder.create<NodeOp>(src, destOp.getName())
846 .setAnnotationsAttr(destOp.getAnnotations());
847 opsToErase.push_back(destOp);
859 auto *context = state.
circuit.getContext();
863 LLVM_DEBUG({ llvm::dbgs() <<
"Analyzing wiring problems:\n"; });
864 DenseMap<FModuleLike, ModuleModifications> moduleModifications;
865 DenseSet<Value> visitedSinks;
867 auto index = e.index();
868 auto problem = e.value();
872 auto source = problem.source;
873 auto sink = problem.sink;
877 if (!visitedSinks.insert(sink).second) {
878 auto diag = mlir::emitError(source.getLoc())
879 <<
"This sink is involved with a Wiring Problem which is "
880 "targeted by a source used by another Wiring Problem. "
881 "(This is both illegal and should be impossible.)";
882 diag.attachNote(source.getLoc()) <<
"The source is here";
885 FModuleLike sourceModule = getModule(source);
886 FModuleLike sinkModule = getModule(sink);
887 if (isa<FExtModuleOp>(sourceModule) || isa<FExtModuleOp>(sinkModule)) {
888 auto diag = mlir::emitError(source.getLoc())
889 <<
"This source is involved with a Wiring Problem which "
890 "includes an External Module port and External Module "
891 "ports anre not supported.";
892 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
897 llvm::dbgs() <<
" - index: " << index <<
"\n"
899 <<
" module: " << sourceModule.getModuleName() <<
"\n"
900 <<
" value: " << source <<
"\n"
902 <<
" module: " << sinkModule.getModuleName() <<
"\n"
903 <<
" value: " << sink <<
"\n"
904 <<
" newNameHint: " << problem.newNameHint <<
"\n";
908 if (sink.getParentBlock() == source.getParentBlock()) {
909 auto builder = ImplicitLocOpBuilder::atBlockEnd(
UnknownLoc::get(context),
910 sink.getParentBlock());
911 if (failed(
connect(source, sink, builder)))
918 if (sourceModule == sinkModule) {
919 LLVM_DEBUG(llvm::dbgs()
920 <<
" LCA: " << sourceModule.getModuleName() <<
"\n");
921 moduleModifications[sourceModule].connectionMap[index] = source;
922 moduleModifications[sourceModule].uturns.push_back({index, sink});
930 if (sourcePaths.size() != 1 || sinkPaths.size() != 1) {
932 mlir::emitError(source.getLoc())
933 <<
"This source is involved with a Wiring Problem where the source "
934 "or the sink are multiply instantiated and this is not supported.";
935 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
940 cast<FModuleOp>(instanceGraph.getTopLevelNode()->getModule());
941 auto sources = sourcePaths[0];
942 auto sinks = sinkPaths[0];
943 while (!sources.empty() && !sinks.empty()) {
944 if (sources.top() != sinks.top())
946 auto newLCA = cast<InstanceOp>(*sources.top());
947 lca = cast<FModuleOp>(newLCA.getReferencedModule(instanceGraph));
948 sources = sources.dropFront();
949 sinks = sinks.dropFront();
953 llvm::dbgs() <<
" LCA: " << lca.getModuleName() <<
"\n"
954 <<
" sourcePath: " << sourcePaths[0] <<
"\n"
955 <<
" sinkPaths: " << sinkPaths[0] <<
"\n";
959 moduleModifications[sourceModule].connectionMap[index] = source;
960 moduleModifications[sinkModule].connectionMap[index] = sink;
963 Type sourceType, sinkType;
969 RefType refType = TypeSwitch<Type, RefType>(source.getType())
973 .Case<RefType>([](RefType ref) {
return ref; });
974 sourceType = refType;
975 sinkType = refType.getType();
978 sourceType = source.getType();
979 sinkType = sink.getType();
982 auto sourceFType = type_dyn_cast<FIRRTLType>(sourceType);
983 auto sinkFType = type_dyn_cast<FIRRTLType>(sinkType);
985 return emitError(source.getLoc())
986 <<
"Wiring Problem source type \"" << sourceType
987 <<
"\" must be a FIRRTL type";
989 return emitError(sink.getLoc())
990 <<
"Wiring Problem sink type \"" << sinkType
991 <<
"\" must be a FIRRTL type";
995 if (sourceFType != sinkFType &&
998 if (
auto sourceBaseType = dyn_cast<FIRRTLBaseType>(sourceFType);
1008 auto diag = mlir::emitError(source.getLoc())
1009 <<
"Wiring Problem source type " << sourceType
1010 <<
" does not match sink type " << sinkType;
1011 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
1018 if (
auto sinkFType = type_dyn_cast<FIRRTLType>(sink.getType());
1019 sinkFType && type_isa<RefType>(sourceType) &&
1021 return emitError(sink.getLoc())
1022 <<
"Wiring Problem sink type \"" << sink.getType()
1023 <<
"\" must be passive (no flips) when using references";
1028 StringRef name, instName;
1029 for (
auto instNode : llvm::reverse(insts)) {
1030 auto inst = cast<InstanceOp>(*instNode);
1031 auto mod = inst.getReferencedModule<FModuleOp>(instanceGraph);
1032 if (mod.isPublic()) {
1033 if (!allowAddingPortsOnPublic) {
1034 auto diag = emitError(
1035 mod.getLoc(),
"cannot wire port through this public module");
1036 diag.attachNote(source.getLoc()) <<
"source here";
1037 diag.attachNote(sink.getLoc()) <<
"sink here";
1040 ++numPublicPortsWired;
1043 if (problem.newNameHint.empty())
1053 assert(!instName.empty());
1056 moduleModifications[mod].portsToAdd.push_back(
1058 instName = inst.getInstanceName();
1064 if (failed(addPorts(sources, source, sourceType,
Direction::Out)) ||
1071 LLVM_DEBUG({ llvm::dbgs() <<
"Updating modules:\n"; });
1072 for (
auto *op : llvm::post_order(instanceGraph.getTopLevelNode())) {
1073 auto fmodule = dyn_cast<FModuleOp>(*op->getModule());
1075 if (!fmodule || !moduleModifications.count(fmodule))
1078 auto modifications = moduleModifications[fmodule];
1080 llvm::dbgs() <<
" - module: " << fmodule.getModuleName() <<
"\n";
1081 llvm::dbgs() <<
" ports:\n";
1082 for (
auto [index, port] : modifications.portsToAdd) {
1083 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
1084 <<
" id: " << index <<
"\n"
1085 <<
" type: " << port.type <<
"\n"
1093 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1094 SmallVector<unsigned> problemIndices;
1095 for (
auto [problemIdx, portInfo] : modifications.portsToAdd) {
1097 newPorts.push_back({fmodule.getNumPorts(), portInfo});
1098 problemIndices.push_back(problemIdx);
1100 auto originalNumPorts = fmodule.getNumPorts();
1101 auto portIdx = fmodule.getNumPorts();
1102 fmodule.insertPorts(newPorts);
1104 auto builder = ImplicitLocOpBuilder::atBlockBegin(
UnknownLoc::get(context),
1105 fmodule.getBodyBlock());
1109 for (
auto [problemIdx, portPair] : llvm::zip(problemIndices, newPorts)) {
1110 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1111 assert(src &&
"there did not exist a driver for the port");
1112 Value dest = fmodule.getArgument(portIdx++);
1113 if (failed(
connect(src, dest, builder)))
1119 for (
auto [problemIdx, dest] : moduleModifications[fmodule].uturns) {
1120 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1121 assert(src &&
"there did not exist a connection for the u-turn");
1122 if (failed(
connect(src, dest, builder)))
1127 for (
auto *inst : instanceGraph.lookup(fmodule)->uses()) {
1128 InstanceOp useInst = cast<InstanceOp>(inst->getInstance());
1129 auto enclosingModule = useInst->getParentOfType<FModuleOp>();
1130 auto clonedInst = useInst.cloneAndInsertPorts(newPorts);
1134 useInst->replaceAllUsesWith(
1135 clonedInst.getResults().drop_back(newPorts.size()));
1142 for (
auto [newPortIdx, problemIdx] : llvm::enumerate(problemIndices)) {
1143 auto &modifications = moduleModifications[enclosingModule];
1144 auto newPort = clonedInst.getResult(newPortIdx + originalNumPorts);
1145 if (modifications.connectionMap.count(problemIdx)) {
1146 modifications.uturns.push_back({problemIdx, newPort});
1149 modifications.connectionMap[problemIdx] = newPort;
1155 for (
auto *op : opsToErase)
1162 void LowerAnnotationsPass::runOnOperation() {
1163 CircuitOp circuit = getOperation();
1164 SymbolTable modules(circuit);
1174 auto annotations = circuit->getAttrOfType<ArrayAttr>(
rawAnnotations);
1182 for (
auto anno : llvm::reverse(annotations.getValue()))
1183 worklistAttrs.push_back(cast<DictionaryAttr>(anno));
1185 size_t numFailures = 0;
1186 size_t numAdded = 0;
1187 auto addToWorklist = [&](DictionaryAttr anno) {
1189 worklistAttrs.push_back(anno);
1192 ApplyState state{circuit, modules, addToWorklist, instancePathCache,
1194 LLVM_DEBUG(llvm::dbgs() <<
"Processing annotations:\n");
1195 while (!worklistAttrs.empty()) {
1196 auto attr = worklistAttrs.pop_back_val();
1197 if (applyAnnotation(attr, state).failed())
1201 if (failed(legacyToWiringProblems(state)))
1204 if (failed(solveWiringProblems(state)))
1208 numRawAnnotations += annotations.size();
1209 numAddedAnnos += numAdded;
1210 numAnnos += numAdded + annotations.size();
1214 signalPassFailure();
1219 bool ignoreAnnotationUnknown,
bool ignoreAnnotationClassless,
1220 bool noRefTypePorts,
bool allowAddingPortsOnPublic) {
1221 auto pass = std::make_unique<LowerAnnotationsPass>();
1222 pass->ignoreAnnotationUnknown = ignoreAnnotationUnknown;
1223 pass->ignoreAnnotationClassless = ignoreAnnotationClassless;
1224 pass->noRefTypePorts = noRefTypePorts;
1225 pass->allowAddingPortsOnPublic = allowAddingPortsOnPublic;
assert(baseType &&"element must be base type")
static LogicalResult applyOutputDirAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static LogicalResult convertToExcludeFromFullResetAnnotation(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Convert from IgnoreFullAsyncResetAnnotation to ExcludeFromFullResetAnnotation.
static LogicalResult applyModulePrefixAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static void addAnnotation(AnnoTarget ref, unsigned fieldIdx, ArrayRef< NamedAttribute > anno)
Apply a new annotation to a resolved target.
static ArrayAttr replaceArrayAttrElement(ArrayAttr array, size_t elem, Attribute newVal)
Update an ArrayAttribute by replacing one entry.
static LogicalResult convertToFullResetAnnotation(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Convert from FullAsyncResetAnnotation to FullResetAnnotation.
static const AnnoRecord * getAnnotationHandler(StringRef annoStr, bool ignoreAnnotationUnknown)
Lookup a record for a given annotation class.
static std::optional< AnnoPathValue > stdResolveImpl(StringRef rawPath, ApplyState &state)
Implementation of standard resolution.
static ArrayAttr appendArrayAttr(ArrayAttr array, Attribute a)
Construct the annotation array with a new thing appended.
static LogicalResult applyDUTAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
LogicalResult drop(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Just drop the annotation.
static LogicalResult applyLoadMemoryAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Update a memory op with attributes about memory file loading.
static std::optional< Convention > parseConvention(llvm::StringRef str)
static std::optional< AnnoPathValue > noResolve(DictionaryAttr anno, ApplyState &state)
Always resolve to the circuit, ignoring the annotation.
static ArrayAttr getAnnotationsFrom(Operation *op)
Get annotations or an empty set of annotations.
static LogicalResult applyConventionAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static LogicalResult applyAttributeAnnotation(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static FlatSymbolRefAttr scatterNonLocalPath(const AnnoPathValue &target, ApplyState &state)
Scatter breadcrumb annotations corresponding to non-local annotations along the instance path.
static FlatSymbolRefAttr buildNLA(const AnnoPathValue &target, ApplyState &state)
Make an anchor for a non-local annotation.
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
This class provides a read-only projection of an annotation.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
StringRef getClass() const
Return the 'class' that this annotation is representing.
FIRRTLBaseType getPassiveType()
Return this type with any flip types recursively removed from itself.
An instance path composed of a series of instances.
def connect(destination, source)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
LogicalResult applyOMIR(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Main entry point to handle scattering of an OMIRAnnotation.
constexpr const char * excludeFromFullResetAnnoClass
Annotation that marks a module as not belonging to any reset domain.
constexpr const char * injectDUTHierarchyAnnoClass
constexpr const char * extractCoverageAnnoClass
constexpr const char * excludeMemToRegAnnoClass
constexpr const char * elaborationArtefactsDirectoryAnnoClass
StringRef getAnnotationAttrName()
Return the name of the attribute used for annotations on FIRRTL ops.
Direction
This represents the direction of a single port.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
constexpr const char * extractGrandCentralClass
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
std::optional< AnnoPathValue > stdResolve(DictionaryAttr anno, ApplyState &state)
===-------------------------------------------------------------------—===// Standard Utility Resolve...
constexpr const char * fullResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
constexpr const char * sitestBlackBoxAnnoClass
constexpr const char * convertMemToRegOfVecAnnoClass
constexpr const char * dontObfuscateModuleAnnoClass
std::unique_ptr< mlir::Pass > createLowerFIRRTLAnnotationsPass(bool ignoreUnhandledAnnotations=false, bool ignoreClasslessAnnotations=false, bool noRefTypePorts=false, bool allowAddingPortsOnPublic=false)
This is the pass constructor.
constexpr const char * metadataDirectoryAttrName
constexpr const char * extractBlackBoxAnnoClass
constexpr const char * fullAsyncResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
constexpr const char * testBenchDirAnnoClass
constexpr const char * sitestTestHarnessBlackBoxAnnoClass
constexpr const char * outputDirAnnoClass
constexpr const char * referenceKeyPortClass
constexpr const char * augmentedGroundTypeClass
constexpr const char * traceAnnoClass
constexpr const char * mustDedupAnnoClass
Flow foldFlow(Value val, Flow accumulatedFlow=Flow::Source)
Compute the flow for a Value, val, as determined by the FIRRTL specification.
constexpr const char * loadMemoryFromFileAnnoClass
constexpr const char * dutAnnoClass
constexpr const char * rawAnnotations
constexpr const char * extractSeqMemsAnnoClass
constexpr const char * attributeAnnoClass
bool areTypesEquivalent(FIRRTLType destType, FIRRTLType srcType, bool destOuterTypeIsConst=false, bool srcOuterTypeIsConst=false, bool requireSameWidths=false)
Returns whether the two types are equivalent.
std::optional< AnnoPathValue > resolveEntities(TokenAnnoTarget path, CircuitOp circuit, SymbolTable &symTbl, CircuitTargetCache &cache)
Convert a parsed target string to a resolved target structure.
constexpr const char * wiringSinkAnnoClass
constexpr const char * memTapSourceClass
constexpr const char * loadMemoryFromFileInlineAnnoClass
constexpr const char * forceNameAnnoClass
constexpr const char * memTapClass
constexpr const char * noDedupAnnoClass
constexpr const char * deletedKeyClass
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
constexpr const char * dataTapsClass
constexpr const char * conventionAnnoClass
constexpr const char * dedupGroupAnnoClass
constexpr const char * omirAnnoClass
constexpr const char * omirTrackerAnnoClass
constexpr const char * viewAnnoClass
constexpr const char * serializedViewAnnoClass
constexpr const char * enumDefAnnoClass
constexpr const char * enumVecAnnoClass
constexpr const char * omirFileAnnoClass
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 * prefixModulesAnnoClass
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
constexpr const char * prefixInterfacesAnnoClass
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 * dontTouchAnnoClass
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
constexpr const char * testHarnessHierAnnoClass
static llvm::StringMap< AnnoRecord > annotationRecords
constexpr const char * moduleHierAnnoClass
constexpr const char * internalKeySourceClass
static AnnoRecord NoTargetAnnotation
Resolution and application of a "firrtl.annotations.NoTargetAnnotation".
unsigned addSVAttributes(mlir::Operation *op, llvm::ArrayRef< SVAttributeAttr > attrs)
Add a list of SV attributes to an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
SmallVector< InstanceOp > instances
===-------------------------------------------------------------------—===// LowerAnnotations ===----...
An annotation target is used to keep track of something that is targeted by an Annotation.
Operation * getOp() const
FModuleLike getModule() const
Get the parent module of the target.
State threaded through functions for resolving and applying annotations.
HierPathCache hierPathCache
hw::InnerSymbolNamespace & getNamespace(FModuleLike module)
DenseMap< StringAttr, LegacyWiringProblem > legacyWiringProblems
SmallVector< WiringProblem > wiringProblems
size_t numReusedHierPaths
InstancePathCache & instancePathCache
CircuitTargetCache targetCaches
FlatSymbolRefAttr getRefFor(ArrayAttr attr)
This represents an annotation targeting a specific operation.
A data structure that caches and provides absolute paths to module instances in the IR.
ArrayRef< InstancePath > getAbsolutePaths(ModuleOpInterface op)
void replaceInstance(InstanceOpInterface oldOp, InstanceOpInterface newOp)
Replace an InstanceOp. This is required to keep the cache updated.
InstanceGraph & instanceGraph
The instance graph of the IR.