27 #include "mlir/IR/ImplicitLocOpBuilder.h"
28 #include "mlir/Pass/Pass.h"
29 #include "llvm/ADT/TypeSwitch.h"
30 #include "llvm/Support/Debug.h"
31 #include "llvm/Support/JSON.h"
34 #define DEBUG_TYPE "omir"
38 #define GEN_PASS_DEF_EMITOMIR
39 #include "circt/Dialect/FIRRTL/Passes.h.inc"
43 using namespace circt;
44 using namespace firrtl;
45 using mlir::LocationAttr;
67 bool hasFieldID() {
return fieldID > 0; }
70 class EmitOMIRPass :
public circt::firrtl::impl::EmitOMIRBase<EmitOMIRPass> {
72 using EmitOMIRBase::outputFilename;
75 void runOnOperation()
override;
76 void makeTrackerAbsolute(Tracker &tracker);
78 void emitSourceInfo(Location input, SmallString<64> &into);
79 void emitOMNode(Attribute node, llvm::json::OStream &jsonStream);
80 void emitOMField(StringAttr fieldName, DictionaryAttr field,
81 llvm::json::OStream &jsonStream);
82 void emitOptionalRTLPorts(DictionaryAttr node,
83 llvm::json::OStream &jsonStream);
84 void emitValue(Attribute node, llvm::json::OStream &jsonStream,
86 void emitTrackedTarget(DictionaryAttr node, llvm::json::OStream &jsonStream,
89 SmallString<8> addSymbolImpl(Attribute symbol) {
91 auto it = symbolIndices.find(symbol);
92 if (it != symbolIndices.end()) {
96 symbols.push_back(symbol);
97 symbolIndices.insert({symbol,
id});
100 (
"{{" + Twine(
id) +
"}}").
toVector(str);
103 SmallString<8> addSymbol(hw::InnerRefAttr symbol) {
104 return addSymbolImpl(symbol);
106 SmallString<8> addSymbol(FlatSymbolRefAttr symbol) {
107 return addSymbolImpl(symbol);
109 SmallString<8> addSymbol(StringAttr symbolName) {
112 SmallString<8> addSymbol(Operation *op) {
113 return addSymbol(SymbolTable::getSymbolName(op));
121 hw::InnerRefAttr
getInnerRefTo(FModuleLike module,
size_t portIdx);
126 FIRRTLType getTypeOf(FModuleLike mod,
size_t portIdx);
129 void addFieldID(
FIRRTLType type,
unsigned fieldID,
130 SmallVectorImpl<char> &result);
133 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
134 return moduleNamespaces.try_emplace(module, module).first->second;
143 DenseMap<Attribute, Tracker> trackers;
147 SmallVector<Attribute> symbols;
151 SmallVector<hw::HierPathOp> removeTempNLAs;
152 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
154 DenseMap<hw::InnerRefAttr, InstanceOp> instancesByName;
156 DenseSet<Operation *> tempSymInstances;
158 StringAttr dutModuleName;
170 auto dict = dyn_cast<DictionaryAttr>(node);
173 auto idAttr = dict.getAs<StringAttr>(
"id");
177 if (
auto infoAttr = dict.getAs<DictionaryAttr>(
"fields")) {
178 auto finalPath = infoAttr.getAs<DictionaryAttr>(
"finalPath");
181 finalPath = infoAttr.getAs<DictionaryAttr>(
"instancePath");
183 if (
auto v = finalPath.getAs<DictionaryAttr>(
"value"))
184 if (v.getAs<UnitAttr>(
"omir.tracker"))
185 id = v.getAs<IntegerAttr>(
"id");
186 if (
auto omTy = infoAttr.getAs<DictionaryAttr>(
"omType"))
187 if (
auto valueArr = omTy.getAs<ArrayAttr>(
"value"))
188 for (
auto attr : valueArr)
189 if (
auto str = dyn_cast<StringAttr>(attr))
190 if (str.getValue() ==
"OMString:OMSRAM")
221 auto *ctx = original.getContext();
226 auto addID = [&](StringRef tpe, StringRef path,
227 IntegerAttr id) -> DictionaryAttr {
228 NamedAttrList fields;
229 fields.append(
"id",
id);
233 return DictionaryAttr::getWithSorted(ctx, fields);
236 return TypeSwitch<Attribute, std::optional<Attribute>>(original)
245 .Case<StringAttr>([&](StringAttr str) -> std::optional<Attribute> {
247 StringRef tpe, value;
248 std::tie(tpe, value) = str.getValue().split(
":");
253 if (tpe ==
"OMReferenceTarget" || tpe ==
"OMMemberReferenceTarget" ||
254 tpe ==
"OMMemberInstanceTarget" || tpe ==
"OMInstanceTarget" ||
255 tpe ==
"OMDontTouchedReferenceTarget") {
256 auto idAttr = state.
newID();
257 NamedAttrList tracker;
259 tracker.append(
"id", idAttr);
265 return addID(tpe, value, idAttr);
281 if (tpe ==
"OMMap" || tpe ==
"OMArray" || tpe ==
"OMBoolean" ||
282 tpe ==
"OMInt" || tpe ==
"OMDouble" || tpe ==
"OMFrozenTarget") {
284 mlir::emitError(state.
circuit.getLoc())
285 <<
"found known string-encoded OMIR type \"" << tpe
286 <<
"\", but this type should not be seen as it has a defined "
287 "serialization format that does NOT use a string-encoded type";
289 <<
"the problematic OMIR is reproduced here: " << original;
294 auto diag = mlir::emitError(state.
circuit.getLoc())
295 <<
"found unknown string-encoded OMIR type \"" << tpe
296 <<
"\" (Did you misspell it? Is CIRCT missing an Object "
298 diag.attachNote() <<
"the problematic OMIR is reproduced here: "
304 .Case<ArrayAttr>([&](ArrayAttr arr) -> std::optional<Attribute> {
305 SmallVector<Attribute> newArr;
306 for (
auto element : arr) {
310 newArr.push_back(*newElement);
316 .Case<DictionaryAttr>(
317 [&](DictionaryAttr dict) -> std::optional<Attribute> {
318 NamedAttrList newAttrs;
319 for (
auto pairs : dict) {
320 auto maybeValue =
scatterOMIR(pairs.getValue(), state);
323 newAttrs.append(pairs.getName(), *maybeValue);
330 .Case< BoolAttr, FloatAttr,
332 [](
auto passThrough) {
return passThrough; })
334 .Default([&](
auto) -> std::optional<Attribute> {
335 auto diag = mlir::emitError(state.
circuit.getLoc())
336 <<
"found unexpected MLIR attribute \"" << original
337 <<
"\" while trying to scatter OMIR";
361 static std::optional<std::pair<StringRef, DictionaryAttr>>
365 DictionaryAttr dict = dyn_cast<DictionaryAttr>(original);
367 llvm::errs() <<
"OMField is not a dictionary, but should be: " << original
372 auto loc = state.
circuit.getLoc();
373 auto *ctx = state.
circuit.getContext();
378 FileLineColLoc fileLineColLocCache;
381 auto infoAttr = tryGetAs<StringAttr>(dict, root,
"info", loc,
omirAnnoClass);
386 fileLineColLocCache, ctx);
387 mlir::LocationAttr infoLoc;
389 infoLoc = *maybeLoc.second;
394 auto nameAttr = tryGetAs<StringAttr>(dict, root,
"name", loc,
omirAnnoClass);
399 auto valueAttr = tryGetAs<Attribute>(dict, root,
"value", loc,
omirAnnoClass);
406 NamedAttrList values;
410 values.append(
"info", infoLoc);
411 values.append(
"value", *newValue);
413 return {{nameAttr.getValue(), DictionaryAttr::getWithSorted(ctx, values)}};
427 static std::optional<DictionaryAttr>
430 auto loc = state.
circuit.getLoc();
433 DictionaryAttr dict = dyn_cast<DictionaryAttr>(original);
435 llvm::errs() <<
"OMNode is not a dictionary, but should be: " << original
440 NamedAttrList omnode;
441 auto *ctx = state.
circuit.getContext();
446 FileLineColLoc fileLineColLocCache;
449 auto infoAttr = tryGetAs<StringAttr>(dict, root,
"info", loc,
omirAnnoClass);
454 fileLineColLocCache, ctx);
455 mlir::LocationAttr infoLoc;
457 infoLoc = *maybeLoc.second;
462 auto idAttr = tryGetAs<StringAttr>(dict, root,
"id", loc,
omirAnnoClass);
470 auto maybeFields = dict.getAs<ArrayAttr>(
"fields");
471 DictionaryAttr fields;
475 auto fieldAttr = maybeFields.getValue();
476 NamedAttrList fieldAttrs;
477 for (
size_t i = 0, e = fieldAttr.size(); i != e; ++i) {
478 auto field = fieldAttr[i];
480 fieldAttrs.append(newField->first, newField->second);
488 omnode.append(
"fields", fields);
489 omnode.append(
"id", idAttr);
490 omnode.append(
"info", infoLoc);
492 return DictionaryAttr::getWithSorted(ctx, omnode);
501 auto loc = state.
circuit.getLoc();
503 auto nodes = tryGetAs<ArrayAttr>(anno, anno,
"nodes", loc,
omirAnnoClass);
507 SmallVector<Attribute> newNodes;
508 for (
auto node : nodes) {
512 newNodes.push_back(*newNode);
515 auto *ctx = state.
circuit.getContext();
517 NamedAttrList newAnnotation;
532 void EmitOMIRPass::runOnOperation() {
534 circuitNamespace =
nullptr;
535 instanceGraph =
nullptr;
536 instancePaths =
nullptr;
539 symbolIndices.clear();
540 removeTempNLAs.clear();
541 moduleNamespaces.clear();
542 instancesByName.clear();
543 CircuitOp circuitOp = getOperation();
551 SmallVector<ArrayRef<Attribute>> annoNodes;
552 DenseSet<Attribute> sramIDs;
553 std::optional<StringRef> outputFilename;
557 auto pathAttr = anno.getMember<StringAttr>(
"filename");
559 circuitOp.emitError(omirFileAnnoClass)
560 <<
" annotation missing `filename` string attribute";
564 LLVM_DEBUG(llvm::dbgs() <<
"- OMIR path: " << pathAttr <<
"\n");
565 outputFilename = pathAttr.getValue();
569 auto nodesAttr = anno.getMember<ArrayAttr>(
"nodes");
571 circuitOp.emitError(omirAnnoClass)
572 <<
" annotation missing `nodes` array attribute";
576 LLVM_DEBUG(llvm::dbgs() <<
"- OMIR: " << nodesAttr <<
"\n");
577 annoNodes.push_back(nodesAttr.getValue());
578 for (
auto node : nodesAttr) {
580 LLVM_DEBUG(llvm::dbgs() <<
" - is SRAM with tracker " <<
id <<
"\n");
589 return signalPassFailure();
594 if (!this->outputFilename.empty())
595 outputFilename = this->outputFilename;
596 if (!outputFilename) {
597 LLVM_DEBUG(llvm::dbgs() <<
"Not emitting OMIR because no annotation or "
598 "pass parameter specified an output file\n");
599 markAllAnalysesPreserved();
604 InstanceGraph ¤tInstanceGraph = getAnalysis<InstanceGraph>();
605 nlaTable = &getAnalysis<NLATable>();
607 circuitNamespace = ¤tCircuitNamespace;
608 instanceGraph = ¤tInstanceGraph;
609 instancePaths = ¤tInstancePaths;
614 circuitOp.walk([&](Operation *op) {
615 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
619 tempSymInstances.insert(instOp);
621 instancesByName.insert({getInnerRefTo(op), instOp});
623 auto setTracker = [&](
int portNo,
Annotation anno) {
628 tracker.id = anno.
getMember<IntegerAttr>(
"id");
629 tracker.portNo = portNo;
633 <<
" annotation missing `id` integer attribute";
637 if (
auto nlaSym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
638 auto tmp = nlaTable->getNLA(nlaSym.getAttr());
640 op->emitError(
"missing annotation ") << nlaSym.getValue();
644 tracker.nla = cast<hw::HierPathOp>(tmp);
646 if (sramIDs.erase(tracker.id))
647 makeTrackerAbsolute(tracker);
648 if (
auto [it, inserted] = trackers.try_emplace(tracker.id, tracker);
651 <<
" annotation with same ID already found, must resolve "
653 diag.attachNote(it->second.op->getLoc())
654 <<
"tracker with same ID already found here";
660 AnnotationSet::removePortAnnotations(op, setTracker);
661 AnnotationSet::removeAnnotations(
662 op, std::bind(setTracker, -1, std::placeholders::_1));
663 if (
auto modOp = dyn_cast<FModuleOp>(op)) {
666 dutModuleName = modOp.getNameAttr();
671 std::string jsonBuffer;
672 llvm::raw_string_ostream jsonOs(jsonBuffer);
673 llvm::json::OStream
json(jsonOs, 2);
675 for (
auto nodes : annoNodes) {
676 for (
auto node : nodes) {
677 emitOMNode(node,
json);
684 return signalPassFailure();
687 for (
auto nla : removeTempNLAs) {
688 LLVM_DEBUG(llvm::dbgs() <<
"Removing '" << nla <<
"'\n");
689 nlaTable->erase(nla);
692 removeTempNLAs.clear();
695 for (
auto *op : tempSymInstances)
696 cast<InstanceOp>(op).setInnerSymbolAttr({});
697 tempSymInstances.clear();
700 auto builder = circuitOp.getBodyBuilder();
701 auto loc = builder.getUnknownLoc();
702 builder.create<emit::FileOp>(loc, *outputFilename, [&] {
703 builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), jsonBuffer,
704 ValueRange{}, builder.getArrayAttr(symbols));
707 markAnalysesPreserved<NLATable>();
713 void EmitOMIRPass::makeTrackerAbsolute(Tracker &tracker) {
714 auto builder = OpBuilder::atBlockBegin(getOperation().
getBodyBlock());
718 if (
auto module = dyn_cast<FModuleLike>(tracker.op))
719 opName = module.getModuleNameAttr();
721 opName = tracker.op->getAttrOfType<StringAttr>(
"name");
722 auto nlaName = circuitNamespace->newName(
"omir_nla_" + opName.getValue());
727 igraph::ModuleOpInterface mod;
729 mod = instanceGraph->lookup(tracker.nla.root())
730 ->getModule<igraph::ModuleOpInterface>();
732 mod = tracker.op->getParentOfType<FModuleOp>();
735 auto paths = instancePaths->getAbsolutePaths(mod);
737 tracker.op->emitError(
"OMIR node targets uninstantiated component `")
738 << opName.getValue() <<
"`";
742 if (paths.size() > 1) {
743 auto diag = tracker.op->emitError(
"OMIR node targets ambiguous component `")
744 << opName.getValue() <<
"`";
745 diag.attachNote(tracker.op->getLoc())
746 <<
"may refer to the following paths:";
747 for (
auto path : paths) {
749 llvm::raw_string_ostream os(pathStr);
751 diag.attachNote(tracker.op->getLoc()) << pathStr;
759 SmallVector<Attribute> namepath;
760 auto addToPath = [&](Operation *op) {
764 for (
auto inst : paths[0])
768 auto path = tracker.nla.getNamepath().getValue();
769 for (
auto attr : path.drop_back()) {
770 auto ref = cast<hw::InnerRefAttr>(attr);
772 auto *node = instanceGraph->lookup(ref.getModule());
777 assert(it != node->end() &&
778 "Instance referenced by NLA does not exist in module");
779 addToPath((*it)->getInstance());
787 if (
auto module = dyn_cast<FModuleLike>(tracker.op))
793 tracker.nla = builder.create<hw::HierPathOp>(builder.getUnknownLoc(),
794 builder.getStringAttr(nlaName),
795 builder.getArrayAttr(namepath));
796 nlaTable->addNLA(tracker.nla);
798 removeTempNLAs.push_back(tracker.nla);
803 void EmitOMIRPass::emitSourceInfo(Location input, SmallString<64> &into) {
805 input->walk([&](Location loc) {
806 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
807 into.append(into.empty() ?
"@[" :
" ");
808 (Twine(fileLoc.getFilename()) +
" " + Twine(fileLoc.getLine()) +
":" +
809 Twine(fileLoc.getColumn()))
812 return WalkResult::advance();
817 into.append(
"UnlocatableSourceInfo");
821 void EmitOMIRPass::emitOMNode(Attribute node, llvm::json::OStream &jsonStream) {
822 auto dict = dyn_cast<DictionaryAttr>(node);
825 .emitError(
"OMNode must be a dictionary")
826 .attachNote(getOperation().getLoc())
833 SmallString<64> info;
834 if (
auto infoAttr = dict.getAs<LocationAttr>(
"info"))
835 emitSourceInfo(infoAttr, info);
840 auto idAttr = dict.getAs<StringAttr>(
"id");
843 .emitError(
"OMNode missing `id` string field")
844 .attachNote(getOperation().getLoc())
851 SmallVector<std::tuple<unsigned, StringAttr, DictionaryAttr>> orderedFields;
852 auto fieldsDict = dict.getAs<DictionaryAttr>(
"fields");
854 for (
auto nameAndField : fieldsDict.getValue()) {
855 auto fieldDict = dyn_cast<DictionaryAttr>(nameAndField.getValue());
858 .emitError(
"OMField must be a dictionary")
859 .attachNote(getOperation().getLoc())
860 << nameAndField.getValue();
866 if (
auto indexAttr = fieldDict.getAs<IntegerAttr>(
"index"))
867 index = indexAttr.getValue().getLimitedValue();
869 orderedFields.push_back({index, nameAndField.getName(), fieldDict});
871 llvm::sort(orderedFields,
872 [](
auto a,
auto b) {
return std::get<0>(a) < std::get<0>(b); });
875 jsonStream.object([&] {
876 jsonStream.attribute(
"info", info);
877 jsonStream.attribute(
"id", idAttr.getValue());
878 jsonStream.attributeArray(
"fields", [&] {
879 for (
auto &orderedField : orderedFields) {
880 emitOMField(std::get<1>(orderedField), std::get<2>(orderedField),
885 if (
auto node = fieldsDict.getAs<DictionaryAttr>(
"containingModule"))
886 if (
auto value = node.getAs<DictionaryAttr>(
"value"))
887 emitOptionalRTLPorts(value, jsonStream);
895 void EmitOMIRPass::emitOMField(StringAttr fieldName, DictionaryAttr field,
896 llvm::json::OStream &jsonStream) {
898 auto infoAttr = field.getAs<LocationAttr>(
"info");
899 SmallString<64> info;
901 emitSourceInfo(infoAttr, info);
905 jsonStream.object([&] {
906 jsonStream.attribute(
"info", info);
907 jsonStream.attribute(
"name", fieldName.strref());
908 jsonStream.attributeBegin(
"value");
909 emitValue(field.get(
"value"), jsonStream,
910 fieldName.strref() ==
"dutInstance");
911 jsonStream.attributeEnd();
918 void EmitOMIRPass::emitOptionalRTLPorts(DictionaryAttr node,
919 llvm::json::OStream &jsonStream) {
922 auto idAttr = node.getAs<IntegerAttr>(
"id");
923 auto trackerIt = trackers.find(idAttr);
924 if (!idAttr || !node.getAs<UnitAttr>(
"omir.tracker") ||
925 trackerIt == trackers.end())
927 auto tracker = trackerIt->second;
932 auto module = dyn_cast<FModuleLike>(tracker.op);
934 module = tracker.op->getParentOfType<FModuleLike>();
936 LLVM_DEBUG(llvm::dbgs() <<
"Not emitting RTL ports since tracked operation "
937 "does not have a FModuleLike parent: "
938 << *tracker.op <<
"\n");
941 LLVM_DEBUG(llvm::dbgs() <<
"Emitting RTL ports for module `"
942 << module.getModuleName() <<
"`\n");
946 jsonStream.object([&] {
948 emitSourceInfo(module.getLoc(), buf);
949 jsonStream.attribute(
"info", buf);
950 jsonStream.attribute(
"name",
"ports");
951 jsonStream.attributeArray(
"value", [&] {
952 for (
const auto &port : llvm::enumerate(module.getPorts())) {
953 auto portType = type_dyn_cast<FIRRTLBaseType>(port.value().type);
954 if (!portType || portType.getBitWidthOrSentinel() == 0)
956 jsonStream.object([&] {
958 buf.assign(
"OMDontTouchedReferenceTarget:~");
959 if (module.getModuleNameAttr() == dutModuleName) {
961 buf.append(module.getModuleName());
963 buf.append(getOperation().getName());
966 buf.append(addSymbol(module));
968 buf.append(addSymbol(getInnerRefTo(module, port.index())));
969 jsonStream.attribute(
"ref", buf);
972 buf.assign(
"OMString:");
973 buf.append(port.value().isOutput() ?
"Output" :
"Input");
974 jsonStream.attribute(
"direction", buf);
977 buf.assign(
"OMBigInt:");
978 Twine::utohexstr(portType.getBitWidthOrSentinel()).toVector(buf);
979 jsonStream.attribute(
"width", buf);
986 void EmitOMIRPass::emitValue(Attribute node, llvm::json::OStream &jsonStream,
989 if (!node || isa<UnitAttr>(node))
990 return jsonStream.value(
nullptr);
994 if (
auto attr = dyn_cast<BoolAttr>(node))
995 return jsonStream.value(attr.getValue());
996 if (
auto attr = dyn_cast<IntegerAttr>(node)) {
1001 SmallString<16> val;
1002 attr.getValue().toStringSigned(val);
1003 return jsonStream.rawValue(val);
1005 if (
auto attr = dyn_cast<FloatAttr>(node)) {
1010 SmallString<16> val;
1011 attr.getValue().toString(val);
1012 return jsonStream.rawValue(val);
1016 if (
auto attr = dyn_cast<ArrayAttr>(node)) {
1017 jsonStream.array([&] {
1018 for (
auto element : attr.getValue()) {
1019 emitValue(element, jsonStream, dutInstance);
1026 if (
auto attr = dyn_cast<DictionaryAttr>(node)) {
1028 if (attr.getAs<UnitAttr>(
"omir.tracker"))
1029 return emitTrackedTarget(attr, jsonStream, dutInstance);
1032 jsonStream.object([&] {
1033 for (
auto field : attr.getValue()) {
1034 jsonStream.attributeBegin(field.getName());
1035 emitValue(field.getValue(), jsonStream, dutInstance);
1036 jsonStream.attributeEnd();
1045 if (
auto attr = dyn_cast<StringAttr>(node)) {
1046 StringRef val = attr.getValue();
1048 return jsonStream.value(val);
1053 jsonStream.value(
"<unsupported value>");
1054 getOperation().emitError(
"unsupported attribute for OMIR serialization: `")
1059 void EmitOMIRPass::emitTrackedTarget(DictionaryAttr node,
1060 llvm::json::OStream &jsonStream,
1063 auto idAttr = node.getAs<IntegerAttr>(
"id");
1066 .emitError(
"tracked OMIR target missing `id` string field")
1067 .attachNote(getOperation().getLoc())
1070 return jsonStream.value(
"<error>");
1074 auto typeAttr = node.getAs<StringAttr>(
"type");
1077 .emitError(
"tracked OMIR target missing `type` string field")
1078 .attachNote(getOperation().getLoc())
1081 return jsonStream.value(
"<error>");
1083 StringRef type = typeAttr.getValue();
1087 auto trackerIt = trackers.find(idAttr);
1088 if (trackerIt == trackers.end()) {
1091 if (type ==
"OMReferenceTarget" || type ==
"OMMemberReferenceTarget" ||
1092 type ==
"OMMemberInstanceTarget")
1093 return jsonStream.value(
"OMDeleted:");
1096 auto diag = getOperation().emitError(
"tracked OMIR target of type `")
1097 << type <<
"` was deleted";
1098 diag.attachNote(getOperation().getLoc())
1099 <<
"`" << type <<
"` should never be deleted";
1100 if (
auto path = node.getAs<StringAttr>(
"path"))
1101 diag.attachNote(getOperation().getLoc())
1102 <<
"original path: `" << path.getValue() <<
"`";
1104 return jsonStream.value(
"<error>");
1106 auto tracker = trackerIt->second;
1113 if (type ==
"OMMemberReferenceTarget" && isa<InstanceOp, MemOp>(tracker.op))
1114 type =
"OMMemberInstanceTarget";
1117 SmallString<64> target(type);
1118 target.append(
":~");
1119 target.append(getOperation().
getName());
1120 target.push_back(
'|');
1124 bool notFirst =
false;
1125 hw::InnerRefAttr instName;
1126 for (
auto nameRef : tracker.nla.getNamepath()) {
1128 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(nameRef))
1129 modName = innerRef.getModule();
1130 else if (
auto ref = dyn_cast<FlatSymbolRefAttr>(nameRef))
1131 modName = ref.getAttr();
1132 if (!dutInstance && modName == dutModuleName) {
1138 target.append(
":~");
1139 target.append(dutModuleName);
1140 target.push_back(
'|');
1145 Operation *module = nlaTable->getModule(modName);
1148 target.push_back(
'/');
1151 target.append(addSymbol(instName));
1152 target.push_back(
':');
1154 target.append(addSymbol(module));
1156 if (
auto innerRef = dyn_cast<hw::InnerRefAttr>(nameRef)) {
1159 auto instOp = instancesByName.lookup(innerRef);
1162 LLVM_DEBUG(llvm::dbgs() <<
"Marking NLA-participating instance "
1163 << innerRef.getName() <<
" in module "
1164 << modName <<
" as dont-touch\n");
1165 tempSymInstances.erase(instOp);
1170 FModuleOp module = dyn_cast<FModuleOp>(tracker.op);
1172 module = tracker.op->getParentOfType<FModuleOp>();
1174 if (module.getNameAttr() == dutModuleName) {
1177 target.append(
":~");
1178 target.append(dutModuleName);
1179 target.push_back(
'|');
1181 target.append(addSymbol(module));
1186 hw::InnerRefAttr componentName;
1188 if (isa<WireOp, RegOp, RegResetOp, InstanceOp, NodeOp, MemOp>(tracker.op)) {
1189 tempSymInstances.erase(tracker.op);
1191 LLVM_DEBUG(llvm::dbgs() <<
"Marking OMIR-targeted " << componentName
1192 <<
" as dont-touch\n");
1196 if (tracker.hasFieldID()) {
1197 if (isa<WireOp, RegOp, RegResetOp, NodeOp>(tracker.op)) {
1198 componentType = getTypeOf(tracker.op);
1200 tracker.op->emitError(
"does not support OMIR targeting fields");
1202 return jsonStream.value(
"<error>");
1205 }
else if (
auto mod = dyn_cast<FModuleLike>(tracker.op)) {
1206 if (tracker.portNo >= 0) {
1210 if (tracker.hasFieldID())
1211 componentType = getTypeOf(mod, tracker.portNo);
1213 }
else if (!isa<FModuleLike>(tracker.op)) {
1214 tracker.op->emitError(
"invalid target for `") << type <<
"` OMIR";
1216 return jsonStream.value(
"<error>");
1218 if (componentName) {
1224 if (type ==
"OMMemberInstanceTarget") {
1225 if (
auto instOp = dyn_cast<InstanceOp>(tracker.op)) {
1226 target.push_back(
'/');
1227 target.append(addSymbol(componentName));
1228 target.push_back(
':');
1229 target.append(addSymbol(instOp.getModuleNameAttr()));
1232 if (
auto memOp = dyn_cast<MemOp>(tracker.op)) {
1233 target.push_back(
'/');
1234 target.append(addSymbol(componentName));
1235 target.push_back(
':');
1236 target.append(memOp.getSummary().getFirMemoryName());
1240 target.push_back(
'>');
1241 target.append(addSymbol(componentName));
1242 addFieldID(componentType, tracker.fieldID, target);
1246 jsonStream.value(target);
1251 [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
1252 return getModuleNamespace(module);
1259 [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
1260 return getModuleNamespace(mod);
1264 FIRRTLType EmitOMIRPass::getTypeOf(Operation *op) {
1265 if (
auto fop = dyn_cast<Forceable>(op))
1266 return fop.getDataType();
1267 assert(op->getNumResults() == 1 &&
1268 isa<FIRRTLType>(op->getResult(0).getType()) &&
1269 "op must have a single FIRRTLType result");
1270 return type_cast<FIRRTLType>(op->getResult(0).getType());
1273 FIRRTLType EmitOMIRPass::getTypeOf(FModuleLike mod,
size_t portIdx) {
1274 Type portType = mod.getPortType(portIdx);
1275 assert(isa<FIRRTLType>(portType) &&
"port must have a FIRRTLType");
1276 return type_cast<FIRRTLType>(portType);
1282 void EmitOMIRPass::addFieldID(
FIRRTLType type,
unsigned fieldID,
1283 SmallVectorImpl<char> &result) {
1286 .
Case<FVectorType>([&](FVectorType vector) {
1287 size_t index = vector.getIndexForFieldID(fieldID);
1288 type = vector.getElementType();
1289 fieldID -= vector.getFieldID(index);
1290 result.push_back(
'[');
1291 Twine(index).toVector(result);
1292 result.push_back(
']');
1294 .Case<BundleType>([&](BundleType bundle) {
1295 size_t index = bundle.getIndexForFieldID(fieldID);
1296 StringRef name = bundle.getElement(index).name;
1297 type = bundle.getElementType(index);
1298 fieldID -= bundle.getFieldID(index);
1299 result.push_back(
'.');
1300 result.append(name.begin(), name.end());
1302 .Default([](
auto val) { llvm::report_fatal_error(
"invalid fieldID"); });
1309 std::unique_ptr<mlir::Pass>
1311 auto pass = std::make_unique<EmitOMIRPass>();
1312 if (!outputFilename.empty())
1313 pass->outputFilename = outputFilename.str();
assert(baseType &&"element must be base type")
static std::optional< DictionaryAttr > scatterOMNode(Attribute original, const Attribute root, ApplyState &state)
Convert an Object Model Node to an optional dictionary, convert source locator strings to location at...
static IntegerAttr isOMSRAM(Attribute &node)
Check if an OMNode is an OMSRAM and requires special treatment of its instance path field.
static std::optional< std::pair< StringRef, DictionaryAttr > > scatterOMField(Attribute original, const Attribute root, unsigned index, ApplyState &state)
Convert an Object Model Field into an optional pair of a string key and a dictionary attribute.
static std::optional< Attribute > scatterOMIR(Attribute original, ApplyState &state)
Recursively walk Object Model IR and convert FIRRTL targets to identifiers while scattering trackers ...
static std::vector< mlir::Value > toVector(mlir::ValueRange range)
static Block * getBodyBlock(FModuleLike mod)
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool applyToOperation(Operation *op) const
Store the annotations in this set in an operation's annotations attribute, overwriting any existing a...
void addAnnotations(ArrayRef< Annotation > annotations)
Add more annotations to this annotation set.
This class provides a read-only projection of an annotation.
unsigned getFieldID() const
Get the field id this attribute targets.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
This graph tracks modules and where they are instantiated.
This table tracks nlas and what modules participate in them.
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
This is an edge in the InstanceGraph.
auto getInstance()
Get the instance-like op that this is tracking.
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.
std::unique_ptr< mlir::Pass > createEmitOMIRPass(mlir::StringRef outputFilename="")
std::pair< bool, std::optional< mlir::LocationAttr > > maybeStringToLocation(llvm::StringRef spelling, bool skipParsing, mlir::StringAttr &locatorFilenameCache, FileLineColLoc &fileLineColLocCache, MLIRContext *context)
constexpr const char * dutAnnoClass
constexpr const char * omirAnnoClass
constexpr const char * omirTrackerAnnoClass
hw::InnerRefAttr getInnerRefTo(const hw::InnerSymTarget &target, GetNamespaceCallback getNamespace)
Obtain an inner reference to the target (operation or port), adding an inner symbol as necessary.
bool isOMIRStringEncodedPassthrough(StringRef type)
Check if an OMIR type is a string-encoded value that the FIRRTL dialect simply passes through as a st...
constexpr const char * omirFileAnnoClass
StringAttr getInnerSymName(Operation *op)
Return the StringAttr for the inner_sym name, if it exists.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
State threaded through functions for resolving and applying annotations.
AddToWorklistFn addToWorklistFn
The namespace of a CircuitOp, generally inhabited by modules.
A data structure that caches and provides absolute paths to module instances in the IR.