27 #include "mlir/IR/ImplicitLocOpBuilder.h"
28 #include "llvm/ADT/TypeSwitch.h"
29 #include "llvm/Support/Debug.h"
30 #include "llvm/Support/JSON.h"
33 #define DEBUG_TYPE "omir"
35 using namespace circt;
36 using namespace firrtl;
37 using mlir::LocationAttr;
59 bool hasFieldID() {
return fieldID > 0; }
62 class EmitOMIRPass :
public EmitOMIRBase<EmitOMIRPass> {
64 using EmitOMIRBase::outputFilename;
67 void runOnOperation()
override;
68 void makeTrackerAbsolute(Tracker &tracker);
70 void emitSourceInfo(Location input, SmallString<64> &into);
71 void emitOMNode(Attribute node, llvm::json::OStream &jsonStream);
72 void emitOMField(StringAttr fieldName, DictionaryAttr field,
73 llvm::json::OStream &jsonStream);
74 void emitOptionalRTLPorts(DictionaryAttr node,
75 llvm::json::OStream &jsonStream);
76 void emitValue(Attribute node, llvm::json::OStream &jsonStream,
78 void emitTrackedTarget(DictionaryAttr node, llvm::json::OStream &jsonStream,
81 SmallString<8> addSymbolImpl(Attribute symbol) {
83 auto it = symbolIndices.find(symbol);
84 if (it != symbolIndices.end()) {
88 symbols.push_back(symbol);
89 symbolIndices.insert({symbol,
id});
92 (
"{{" + Twine(
id) +
"}}").
toVector(str);
95 SmallString<8> addSymbol(hw::InnerRefAttr symbol) {
96 return addSymbolImpl(symbol);
98 SmallString<8> addSymbol(FlatSymbolRefAttr symbol) {
99 return addSymbolImpl(symbol);
101 SmallString<8> addSymbol(StringAttr symbolName) {
104 SmallString<8> addSymbol(Operation *op) {
105 return addSymbol(SymbolTable::getSymbolName(op));
113 hw::InnerRefAttr
getInnerRefTo(FModuleLike module,
size_t portIdx);
118 FIRRTLType getTypeOf(FModuleLike mod,
size_t portIdx);
121 void addFieldID(
FIRRTLType type,
unsigned fieldID,
122 SmallVectorImpl<char> &result);
125 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
126 return moduleNamespaces.try_emplace(module, module).first->second;
135 DenseMap<Attribute, Tracker> trackers;
139 SmallVector<Attribute> symbols;
143 SmallVector<hw::HierPathOp> removeTempNLAs;
144 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
146 DenseMap<hw::InnerRefAttr, InstanceOp> instancesByName;
148 DenseSet<Operation *> tempSymInstances;
150 StringAttr dutModuleName;
162 auto dict = dyn_cast<DictionaryAttr>(node);
165 auto idAttr = dict.getAs<StringAttr>(
"id");
169 if (
auto infoAttr = dict.getAs<DictionaryAttr>(
"fields")) {
170 auto finalPath = infoAttr.getAs<DictionaryAttr>(
"finalPath");
173 finalPath = infoAttr.getAs<DictionaryAttr>(
"instancePath");
175 if (
auto v = finalPath.getAs<DictionaryAttr>(
"value"))
176 if (v.getAs<UnitAttr>(
"omir.tracker"))
177 id = v.getAs<IntegerAttr>(
"id");
178 if (
auto omTy = infoAttr.getAs<DictionaryAttr>(
"omType"))
179 if (
auto valueArr = omTy.getAs<ArrayAttr>(
"value"))
180 for (
auto attr : valueArr)
181 if (
auto str = dyn_cast<StringAttr>(attr))
182 if (str.getValue().equals(
"OMString:OMSRAM"))
213 auto *ctx = original.getContext();
218 auto addID = [&](StringRef tpe, StringRef path,
219 IntegerAttr id) -> DictionaryAttr {
220 NamedAttrList fields;
221 fields.append(
"id",
id);
225 return DictionaryAttr::getWithSorted(ctx, fields);
228 return TypeSwitch<Attribute, std::optional<Attribute>>(original)
237 .Case<StringAttr>([&](StringAttr str) -> std::optional<Attribute> {
239 StringRef tpe, value;
240 std::tie(tpe, value) = str.getValue().split(
":");
245 if (tpe ==
"OMReferenceTarget" || tpe ==
"OMMemberReferenceTarget" ||
246 tpe ==
"OMMemberInstanceTarget" || tpe ==
"OMInstanceTarget" ||
247 tpe ==
"OMDontTouchedReferenceTarget") {
248 auto idAttr = state.
newID();
249 NamedAttrList tracker;
251 tracker.append(
"id", idAttr);
257 return addID(tpe, value, idAttr);
273 if (tpe ==
"OMMap" || tpe ==
"OMArray" || tpe ==
"OMBoolean" ||
274 tpe ==
"OMInt" || tpe ==
"OMDouble" || tpe ==
"OMFrozenTarget") {
276 mlir::emitError(state.
circuit.getLoc())
277 <<
"found known string-encoded OMIR type \"" << tpe
278 <<
"\", but this type should not be seen as it has a defined "
279 "serialization format that does NOT use a string-encoded type";
281 <<
"the problematic OMIR is reproduced here: " << original;
286 auto diag = mlir::emitError(state.
circuit.getLoc())
287 <<
"found unknown string-encoded OMIR type \"" << tpe
288 <<
"\" (Did you misspell it? Is CIRCT missing an Object "
290 diag.attachNote() <<
"the problematic OMIR is reproduced here: "
296 .Case<ArrayAttr>([&](ArrayAttr arr) -> std::optional<Attribute> {
297 SmallVector<Attribute> newArr;
298 for (
auto element : arr) {
302 newArr.push_back(*newElement);
308 .Case<DictionaryAttr>(
309 [&](DictionaryAttr dict) -> std::optional<Attribute> {
310 NamedAttrList newAttrs;
311 for (
auto pairs : dict) {
312 auto maybeValue =
scatterOMIR(pairs.getValue(), state);
315 newAttrs.append(pairs.getName(), *maybeValue);
322 .Case< BoolAttr, FloatAttr,
324 [](
auto passThrough) {
return passThrough; })
326 .Default([&](
auto) -> std::optional<Attribute> {
327 auto diag = mlir::emitError(state.
circuit.getLoc())
328 <<
"found unexpected MLIR attribute \"" << original
329 <<
"\" while trying to scatter OMIR";
353 static std::optional<std::pair<StringRef, DictionaryAttr>>
357 DictionaryAttr dict = dyn_cast<DictionaryAttr>(original);
359 llvm::errs() <<
"OMField is not a dictionary, but should be: " << original
364 auto loc = state.
circuit.getLoc();
365 auto *ctx = state.
circuit.getContext();
370 FileLineColLoc fileLineColLocCache;
373 auto infoAttr = tryGetAs<StringAttr>(dict, root,
"info", loc,
omirAnnoClass);
378 fileLineColLocCache, ctx);
379 mlir::LocationAttr infoLoc;
381 infoLoc = *maybeLoc.second;
386 auto nameAttr = tryGetAs<StringAttr>(dict, root,
"name", loc,
omirAnnoClass);
391 auto valueAttr = tryGetAs<Attribute>(dict, root,
"value", loc,
omirAnnoClass);
398 NamedAttrList values;
402 values.append(
"info", infoLoc);
403 values.append(
"value", *newValue);
405 return {{nameAttr.getValue(), DictionaryAttr::getWithSorted(ctx, values)}};
419 static std::optional<DictionaryAttr>
422 auto loc = state.
circuit.getLoc();
425 DictionaryAttr dict = dyn_cast<DictionaryAttr>(original);
427 llvm::errs() <<
"OMNode is not a dictionary, but should be: " << original
432 NamedAttrList omnode;
433 auto *ctx = state.
circuit.getContext();
438 FileLineColLoc fileLineColLocCache;
441 auto infoAttr = tryGetAs<StringAttr>(dict, root,
"info", loc,
omirAnnoClass);
446 fileLineColLocCache, ctx);
447 mlir::LocationAttr infoLoc;
449 infoLoc = *maybeLoc.second;
454 auto idAttr = tryGetAs<StringAttr>(dict, root,
"id", loc,
omirAnnoClass);
462 auto maybeFields = dict.getAs<ArrayAttr>(
"fields");
463 DictionaryAttr fields;
467 auto fieldAttr = maybeFields.getValue();
468 NamedAttrList fieldAttrs;
469 for (
size_t i = 0, e = fieldAttr.size(); i != e; ++i) {
470 auto field = fieldAttr[i];
472 fieldAttrs.append(newField->first, newField->second);
480 omnode.append(
"fields", fields);
481 omnode.append(
"id", idAttr);
482 omnode.append(
"info", infoLoc);
484 return DictionaryAttr::getWithSorted(ctx, omnode);
493 auto loc = state.
circuit.getLoc();
495 auto nodes = tryGetAs<ArrayAttr>(anno, anno,
"nodes", loc,
omirAnnoClass);
499 SmallVector<Attribute> newNodes;
500 for (
auto node : nodes) {
504 newNodes.push_back(*newNode);
507 auto *ctx = state.
circuit.getContext();
509 NamedAttrList newAnnotation;
524 void EmitOMIRPass::runOnOperation() {
526 circuitNamespace =
nullptr;
527 instanceGraph =
nullptr;
528 instancePaths =
nullptr;
531 symbolIndices.clear();
532 removeTempNLAs.clear();
533 moduleNamespaces.clear();
534 instancesByName.clear();
535 CircuitOp circuitOp = getOperation();
543 SmallVector<ArrayRef<Attribute>> annoNodes;
544 DenseSet<Attribute> sramIDs;
545 std::optional<StringRef> outputFilename;
549 auto pathAttr = anno.getMember<StringAttr>(
"filename");
551 circuitOp.emitError(omirFileAnnoClass)
552 <<
" annotation missing `filename` string attribute";
556 LLVM_DEBUG(llvm::dbgs() <<
"- OMIR path: " << pathAttr <<
"\n");
557 outputFilename = pathAttr.getValue();
561 auto nodesAttr = anno.getMember<ArrayAttr>(
"nodes");
563 circuitOp.emitError(omirAnnoClass)
564 <<
" annotation missing `nodes` array attribute";
568 LLVM_DEBUG(llvm::dbgs() <<
"- OMIR: " << nodesAttr <<
"\n");
569 annoNodes.push_back(nodesAttr.getValue());
570 for (
auto node : nodesAttr) {
572 LLVM_DEBUG(llvm::dbgs() <<
" - is SRAM with tracker " <<
id <<
"\n");
581 return signalPassFailure();
586 if (!this->outputFilename.empty())
587 outputFilename = this->outputFilename;
588 if (!outputFilename) {
589 LLVM_DEBUG(llvm::dbgs() <<
"Not emitting OMIR because no annotation or "
590 "pass parameter specified an output file\n");
591 markAllAnalysesPreserved();
596 InstanceGraph ¤tInstanceGraph = getAnalysis<InstanceGraph>();
597 nlaTable = &getAnalysis<NLATable>();
599 circuitNamespace = ¤tCircuitNamespace;
600 instanceGraph = ¤tInstanceGraph;
601 instancePaths = ¤tInstancePaths;
606 circuitOp.walk([&](Operation *op) {
607 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
611 tempSymInstances.insert(instOp);
613 instancesByName.insert({getInnerRefTo(op), instOp});
615 auto setTracker = [&](
int portNo,
Annotation anno) {
620 tracker.id = anno.
getMember<IntegerAttr>(
"id");
621 tracker.portNo = portNo;
625 <<
" annotation missing `id` integer attribute";
629 if (
auto nlaSym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
630 auto tmp = nlaTable->getNLA(nlaSym.getAttr());
632 op->emitError(
"missing annotation ") << nlaSym.getValue();
636 tracker.nla = cast<hw::HierPathOp>(tmp);
638 if (sramIDs.erase(tracker.id))
639 makeTrackerAbsolute(tracker);
640 if (
auto [it, inserted] = trackers.try_emplace(tracker.id, tracker);
643 <<
" annotation with same ID already found, must resolve "
645 diag.attachNote(it->second.op->getLoc())
646 <<
"tracker with same ID already found here";
652 AnnotationSet::removePortAnnotations(op, setTracker);
653 AnnotationSet::removeAnnotations(
654 op, std::bind(setTracker, -1, std::placeholders::_1));
655 if (
auto modOp = dyn_cast<FModuleOp>(op)) {
659 dutModuleName = modOp.getNameAttr();
664 std::string jsonBuffer;
665 llvm::raw_string_ostream jsonOs(jsonBuffer);
666 llvm::json::OStream
json(jsonOs, 2);
668 for (
auto nodes : annoNodes) {
669 for (
auto node : nodes) {
670 emitOMNode(node,
json);
677 return signalPassFailure();
680 for (
auto nla : removeTempNLAs) {
681 LLVM_DEBUG(llvm::dbgs() <<
"Removing '" << nla <<
"'\n");
682 nlaTable->erase(nla);
685 removeTempNLAs.clear();
688 for (
auto *op : tempSymInstances)
689 cast<InstanceOp>(op).setInnerSymbolAttr({});
690 tempSymInstances.clear();
693 auto builder = circuitOp.getBodyBuilder();
694 auto loc =
builder.getUnknownLoc();
695 builder.create<emit::FileOp>(loc, *outputFilename, [&] {
696 builder.create<sv::VerbatimOp>(
builder.getUnknownLoc(), jsonBuffer,
697 ValueRange{},
builder.getArrayAttr(symbols));
700 markAnalysesPreserved<NLATable>();
706 void EmitOMIRPass::makeTrackerAbsolute(Tracker &tracker) {
707 auto builder = OpBuilder::atBlockBegin(getOperation().getBodyBlock());
711 if (
auto module = dyn_cast<FModuleLike>(tracker.op))
712 opName = module.getModuleNameAttr();
714 opName = tracker.op->getAttrOfType<StringAttr>(
"name");
715 auto nlaName = circuitNamespace->newName(
"omir_nla_" + opName.getValue());
720 igraph::ModuleOpInterface mod;
722 mod = instanceGraph->lookup(tracker.nla.root())
723 ->getModule<igraph::ModuleOpInterface>();
725 mod = tracker.op->getParentOfType<FModuleOp>();
728 auto paths = instancePaths->getAbsolutePaths(mod);
730 tracker.op->emitError(
"OMIR node targets uninstantiated component `")
731 << opName.getValue() <<
"`";
735 if (paths.size() > 1) {
736 auto diag = tracker.op->emitError(
"OMIR node targets ambiguous component `")
737 << opName.getValue() <<
"`";
738 diag.attachNote(tracker.op->getLoc())
739 <<
"may refer to the following paths:";
740 for (
auto path : paths) {
742 llvm::raw_string_ostream os(pathStr);
744 diag.attachNote(tracker.op->getLoc()) << pathStr;
752 SmallVector<Attribute> namepath;
753 auto addToPath = [&](Operation *op) {
757 for (
auto inst : paths[0])
761 auto path = tracker.nla.getNamepath().getValue();
762 for (
auto attr : path.drop_back()) {
763 auto ref = attr.cast<hw::InnerRefAttr>();
765 auto *node = instanceGraph->lookup(ref.getModule());
770 assert(it != node->end() &&
771 "Instance referenced by NLA does not exist in module");
772 addToPath((*it)->getInstance());
780 if (
auto module = dyn_cast<FModuleLike>(tracker.op))
786 tracker.nla =
builder.create<hw::HierPathOp>(
builder.getUnknownLoc(),
787 builder.getStringAttr(nlaName),
788 builder.getArrayAttr(namepath));
789 nlaTable->addNLA(tracker.nla);
791 removeTempNLAs.push_back(tracker.nla);
796 void EmitOMIRPass::emitSourceInfo(Location input, SmallString<64> &into) {
798 input->walk([&](Location loc) {
799 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
800 into.append(into.empty() ?
"@[" :
" ");
801 (Twine(fileLoc.getFilename()) +
" " + Twine(fileLoc.getLine()) +
":" +
802 Twine(fileLoc.getColumn()))
805 return WalkResult::advance();
810 into.append(
"UnlocatableSourceInfo");
814 void EmitOMIRPass::emitOMNode(Attribute node, llvm::json::OStream &jsonStream) {
815 auto dict = dyn_cast<DictionaryAttr>(node);
818 .emitError(
"OMNode must be a dictionary")
819 .attachNote(getOperation().getLoc())
826 SmallString<64> info;
827 if (
auto infoAttr = dict.getAs<LocationAttr>(
"info"))
828 emitSourceInfo(infoAttr, info);
833 auto idAttr = dict.getAs<StringAttr>(
"id");
836 .emitError(
"OMNode missing `id` string field")
837 .attachNote(getOperation().getLoc())
844 SmallVector<std::tuple<unsigned, StringAttr, DictionaryAttr>> orderedFields;
845 auto fieldsDict = dict.getAs<DictionaryAttr>(
"fields");
847 for (
auto nameAndField : fieldsDict.getValue()) {
848 auto fieldDict = dyn_cast<DictionaryAttr>(nameAndField.getValue());
851 .emitError(
"OMField must be a dictionary")
852 .attachNote(getOperation().getLoc())
853 << nameAndField.getValue();
859 if (
auto indexAttr = fieldDict.getAs<IntegerAttr>(
"index"))
860 index = indexAttr.getValue().getLimitedValue();
862 orderedFields.push_back({index, nameAndField.getName(), fieldDict});
864 llvm::sort(orderedFields,
865 [](
auto a,
auto b) {
return std::get<0>(a) < std::get<0>(b); });
868 jsonStream.object([&] {
869 jsonStream.attribute(
"info", info);
870 jsonStream.attribute(
"id", idAttr.getValue());
871 jsonStream.attributeArray(
"fields", [&] {
872 for (
auto &orderedField : orderedFields) {
873 emitOMField(std::get<1>(orderedField), std::get<2>(orderedField),
878 if (
auto node = fieldsDict.getAs<DictionaryAttr>(
"containingModule"))
879 if (
auto value = node.getAs<DictionaryAttr>(
"value"))
880 emitOptionalRTLPorts(value, jsonStream);
888 void EmitOMIRPass::emitOMField(StringAttr fieldName, DictionaryAttr field,
889 llvm::json::OStream &jsonStream) {
891 auto infoAttr = field.getAs<LocationAttr>(
"info");
892 SmallString<64> info;
894 emitSourceInfo(infoAttr, info);
898 jsonStream.object([&] {
899 jsonStream.attribute(
"info", info);
900 jsonStream.attribute(
"name", fieldName.strref());
901 jsonStream.attributeBegin(
"value");
902 emitValue(field.get(
"value"), jsonStream,
903 fieldName.strref().equals(
"dutInstance"));
904 jsonStream.attributeEnd();
911 void EmitOMIRPass::emitOptionalRTLPorts(DictionaryAttr node,
912 llvm::json::OStream &jsonStream) {
915 auto idAttr = node.getAs<IntegerAttr>(
"id");
916 auto trackerIt = trackers.find(idAttr);
917 if (!idAttr || !node.getAs<UnitAttr>(
"omir.tracker") ||
918 trackerIt == trackers.end())
920 auto tracker = trackerIt->second;
925 auto module = dyn_cast<FModuleLike>(tracker.op);
927 module = tracker.op->getParentOfType<FModuleLike>();
929 LLVM_DEBUG(llvm::dbgs() <<
"Not emitting RTL ports since tracked operation "
930 "does not have a FModuleLike parent: "
931 << *tracker.op <<
"\n");
934 LLVM_DEBUG(llvm::dbgs() <<
"Emitting RTL ports for module `"
935 << module.getModuleName() <<
"`\n");
939 jsonStream.object([&] {
941 emitSourceInfo(module.getLoc(), buf);
942 jsonStream.attribute(
"info", buf);
943 jsonStream.attribute(
"name",
"ports");
944 jsonStream.attributeArray(
"value", [&] {
945 for (
const auto &port : llvm::enumerate(module.getPorts())) {
946 auto portType = type_dyn_cast<FIRRTLBaseType>(port.value().type);
947 if (!portType || portType.getBitWidthOrSentinel() == 0)
949 jsonStream.object([&] {
951 buf.assign(
"OMDontTouchedReferenceTarget:~");
952 if (module.getModuleNameAttr() == dutModuleName) {
954 buf.append(module.getModuleName());
956 buf.append(getOperation().getName());
959 buf.append(addSymbol(module));
961 buf.append(addSymbol(getInnerRefTo(module, port.index())));
962 jsonStream.attribute(
"ref", buf);
965 buf.assign(
"OMString:");
966 buf.append(port.value().isOutput() ?
"Output" :
"Input");
967 jsonStream.attribute(
"direction", buf);
970 buf.assign(
"OMBigInt:");
971 Twine::utohexstr(portType.getBitWidthOrSentinel()).toVector(buf);
972 jsonStream.attribute(
"width", buf);
979 void EmitOMIRPass::emitValue(Attribute node, llvm::json::OStream &jsonStream,
982 if (!node || isa<UnitAttr>(node))
983 return jsonStream.value(
nullptr);
987 if (
auto attr = dyn_cast<BoolAttr>(node))
988 return jsonStream.value(attr.getValue());
989 if (
auto attr = dyn_cast<IntegerAttr>(node)) {
995 attr.getValue().toStringSigned(val);
996 return jsonStream.rawValue(val);
998 if (
auto attr = dyn_cast<FloatAttr>(node)) {
1003 SmallString<16> val;
1004 attr.getValue().toString(val);
1005 return jsonStream.rawValue(val);
1009 if (
auto attr = dyn_cast<ArrayAttr>(node)) {
1010 jsonStream.array([&] {
1011 for (
auto element : attr.getValue()) {
1012 emitValue(element, jsonStream, dutInstance);
1019 if (
auto attr = dyn_cast<DictionaryAttr>(node)) {
1021 if (attr.getAs<UnitAttr>(
"omir.tracker"))
1022 return emitTrackedTarget(attr, jsonStream, dutInstance);
1025 jsonStream.object([&] {
1026 for (
auto field : attr.getValue()) {
1027 jsonStream.attributeBegin(field.getName());
1028 emitValue(field.getValue(), jsonStream, dutInstance);
1029 jsonStream.attributeEnd();
1038 if (
auto attr = dyn_cast<StringAttr>(node)) {
1039 StringRef val = attr.getValue();
1041 return jsonStream.value(val);
1046 jsonStream.value(
"<unsupported value>");
1047 getOperation().emitError(
"unsupported attribute for OMIR serialization: `")
1052 void EmitOMIRPass::emitTrackedTarget(DictionaryAttr node,
1053 llvm::json::OStream &jsonStream,
1056 auto idAttr = node.getAs<IntegerAttr>(
"id");
1059 .emitError(
"tracked OMIR target missing `id` string field")
1060 .attachNote(getOperation().getLoc())
1063 return jsonStream.value(
"<error>");
1067 auto typeAttr = node.getAs<StringAttr>(
"type");
1070 .emitError(
"tracked OMIR target missing `type` string field")
1071 .attachNote(getOperation().getLoc())
1074 return jsonStream.value(
"<error>");
1076 StringRef type = typeAttr.getValue();
1080 auto trackerIt = trackers.find(idAttr);
1081 if (trackerIt == trackers.end()) {
1084 if (type ==
"OMReferenceTarget" || type ==
"OMMemberReferenceTarget" ||
1085 type ==
"OMMemberInstanceTarget")
1086 return jsonStream.value(
"OMDeleted:");
1089 auto diag = getOperation().emitError(
"tracked OMIR target of type `")
1090 << type <<
"` was deleted";
1091 diag.attachNote(getOperation().getLoc())
1092 <<
"`" << type <<
"` should never be deleted";
1093 if (
auto path = node.getAs<StringAttr>(
"path"))
1094 diag.attachNote(getOperation().getLoc())
1095 <<
"original path: `" << path.getValue() <<
"`";
1097 return jsonStream.value(
"<error>");
1099 auto tracker = trackerIt->second;
1106 if (type ==
"OMMemberReferenceTarget" && isa<InstanceOp, MemOp>(tracker.op))
1107 type =
"OMMemberInstanceTarget";
1110 SmallString<64> target(type);
1111 target.append(
":~");
1112 target.append(getOperation().
getName());
1113 target.push_back(
'|');
1117 bool notFirst =
false;
1118 hw::InnerRefAttr instName;
1119 for (
auto nameRef : tracker.nla.getNamepath()) {
1121 if (
auto innerRef = nameRef.dyn_cast<hw::InnerRefAttr>())
1122 modName = innerRef.getModule();
1123 else if (
auto ref = dyn_cast<FlatSymbolRefAttr>(nameRef))
1124 modName = ref.getAttr();
1125 if (!dutInstance && modName == dutModuleName) {
1131 target.append(
":~");
1132 target.append(dutModuleName);
1133 target.push_back(
'|');
1138 Operation *module = nlaTable->getModule(modName);
1141 target.push_back(
'/');
1144 target.append(addSymbol(instName));
1145 target.push_back(
':');
1147 target.append(addSymbol(module));
1149 if (
auto innerRef = nameRef.dyn_cast<hw::InnerRefAttr>()) {
1152 auto instOp = instancesByName.lookup(innerRef);
1155 LLVM_DEBUG(llvm::dbgs() <<
"Marking NLA-participating instance "
1156 << innerRef.getName() <<
" in module "
1157 << modName <<
" as dont-touch\n");
1158 tempSymInstances.erase(instOp);
1163 FModuleOp module = dyn_cast<FModuleOp>(tracker.op);
1165 module = tracker.op->getParentOfType<FModuleOp>();
1167 if (module.getNameAttr() == dutModuleName) {
1170 target.append(
":~");
1171 target.append(dutModuleName);
1172 target.push_back(
'|');
1174 target.append(addSymbol(module));
1179 hw::InnerRefAttr componentName;
1181 if (isa<WireOp, RegOp, RegResetOp, InstanceOp, NodeOp, MemOp>(tracker.op)) {
1182 tempSymInstances.erase(tracker.op);
1184 LLVM_DEBUG(llvm::dbgs() <<
"Marking OMIR-targeted " << componentName
1185 <<
" as dont-touch\n");
1189 if (tracker.hasFieldID()) {
1190 if (isa<WireOp, RegOp, RegResetOp, NodeOp>(tracker.op)) {
1191 componentType = getTypeOf(tracker.op);
1193 tracker.op->emitError(
"does not support OMIR targeting fields");
1195 return jsonStream.value(
"<error>");
1198 }
else if (
auto mod = dyn_cast<FModuleLike>(tracker.op)) {
1199 if (tracker.portNo >= 0) {
1203 if (tracker.hasFieldID())
1204 componentType = getTypeOf(mod, tracker.portNo);
1206 }
else if (!isa<FModuleLike>(tracker.op)) {
1207 tracker.op->emitError(
"invalid target for `") << type <<
"` OMIR";
1209 return jsonStream.value(
"<error>");
1211 if (componentName) {
1217 if (type ==
"OMMemberInstanceTarget") {
1218 if (
auto instOp = dyn_cast<InstanceOp>(tracker.op)) {
1219 target.push_back(
'/');
1220 target.append(addSymbol(componentName));
1221 target.push_back(
':');
1222 target.append(addSymbol(instOp.getModuleNameAttr()));
1225 if (
auto memOp = dyn_cast<MemOp>(tracker.op)) {
1226 target.push_back(
'/');
1227 target.append(addSymbol(componentName));
1228 target.push_back(
':');
1229 target.append(memOp.getSummary().getFirMemoryName());
1233 target.push_back(
'>');
1234 target.append(addSymbol(componentName));
1235 addFieldID(componentType, tracker.fieldID, target);
1239 jsonStream.value(target);
1244 [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
1245 return getModuleNamespace(module);
1252 [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
1253 return getModuleNamespace(mod);
1257 FIRRTLType EmitOMIRPass::getTypeOf(Operation *op) {
1258 if (
auto fop = dyn_cast<Forceable>(op))
1259 return fop.getDataType();
1260 assert(op->getNumResults() == 1 &&
1261 isa<FIRRTLType>(op->getResult(0).getType()) &&
1262 "op must have a single FIRRTLType result");
1263 return type_cast<FIRRTLType>(op->getResult(0).getType());
1266 FIRRTLType EmitOMIRPass::getTypeOf(FModuleLike mod,
size_t portIdx) {
1267 Type portType = mod.getPortType(portIdx);
1268 assert(isa<FIRRTLType>(portType) &&
"port must have a FIRRTLType");
1269 return type_cast<FIRRTLType>(portType);
1275 void EmitOMIRPass::addFieldID(
FIRRTLType type,
unsigned fieldID,
1276 SmallVectorImpl<char> &result) {
1279 .
Case<FVectorType>([&](FVectorType vector) {
1280 size_t index = vector.getIndexForFieldID(fieldID);
1281 type = vector.getElementType();
1282 fieldID -= vector.getFieldID(index);
1283 result.push_back(
'[');
1284 Twine(index).toVector(result);
1285 result.push_back(
']');
1287 .Case<BundleType>([&](BundleType bundle) {
1288 size_t index = bundle.getIndexForFieldID(fieldID);
1289 StringRef name = bundle.getElement(index).name;
1290 type = bundle.getElementType(index);
1291 fieldID -= bundle.getFieldID(index);
1292 result.push_back(
'.');
1293 result.append(name.begin(), name.end());
1295 .Default([](
auto val) { llvm::report_fatal_error(
"invalid fieldID"); });
1302 std::unique_ptr<mlir::Pass>
1304 auto pass = std::make_unique<EmitOMIRPass>();
1305 if (!outputFilename.empty())
1306 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)
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.