26 #include "mlir/IR/ImplicitLocOpBuilder.h"
27 #include "llvm/ADT/TypeSwitch.h"
28 #include "llvm/Support/Debug.h"
29 #include "llvm/Support/JSON.h"
32 #define DEBUG_TYPE "omir"
34 using namespace circt;
35 using namespace firrtl;
36 using mlir::LocationAttr;
58 bool hasFieldID() {
return fieldID > 0; }
61 class EmitOMIRPass :
public EmitOMIRBase<EmitOMIRPass> {
63 using EmitOMIRBase::outputFilename;
66 void runOnOperation()
override;
67 void makeTrackerAbsolute(Tracker &tracker);
69 void emitSourceInfo(Location input, SmallString<64> &into);
70 void emitOMNode(Attribute node, llvm::json::OStream &jsonStream);
71 void emitOMField(StringAttr fieldName, DictionaryAttr field,
72 llvm::json::OStream &jsonStream);
73 void emitOptionalRTLPorts(DictionaryAttr node,
74 llvm::json::OStream &jsonStream);
75 void emitValue(Attribute node, llvm::json::OStream &jsonStream,
77 void emitTrackedTarget(DictionaryAttr node, llvm::json::OStream &jsonStream,
80 SmallString<8> addSymbolImpl(Attribute symbol) {
82 auto it = symbolIndices.find(symbol);
83 if (it != symbolIndices.end()) {
87 symbols.push_back(symbol);
88 symbolIndices.insert({symbol,
id});
91 (
"{{" + Twine(
id) +
"}}").
toVector(str);
94 SmallString<8> addSymbol(hw::InnerRefAttr symbol) {
95 return addSymbolImpl(symbol);
97 SmallString<8> addSymbol(FlatSymbolRefAttr symbol) {
98 return addSymbolImpl(symbol);
100 SmallString<8> addSymbol(StringAttr symbolName) {
103 SmallString<8> addSymbol(Operation *op) {
104 return addSymbol(SymbolTable::getSymbolName(op));
112 hw::InnerRefAttr
getInnerRefTo(FModuleLike module,
size_t portIdx);
117 FIRRTLType getTypeOf(FModuleLike mod,
size_t portIdx);
120 void addFieldID(
FIRRTLType type,
unsigned fieldID,
121 SmallVectorImpl<char> &result);
124 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
125 return moduleNamespaces.try_emplace(module, module).first->second;
134 DenseMap<Attribute, Tracker> trackers;
138 SmallVector<Attribute> symbols;
142 SmallVector<hw::HierPathOp> removeTempNLAs;
143 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
145 DenseMap<hw::InnerRefAttr, InstanceOp> instancesByName;
147 DenseSet<Operation *> tempSymInstances;
149 StringAttr dutModuleName;
161 auto dict = dyn_cast<DictionaryAttr>(node);
164 auto idAttr = dict.getAs<StringAttr>(
"id");
168 if (
auto infoAttr = dict.getAs<DictionaryAttr>(
"fields")) {
169 auto finalPath = infoAttr.getAs<DictionaryAttr>(
"finalPath");
172 finalPath = infoAttr.getAs<DictionaryAttr>(
"instancePath");
174 if (
auto v = finalPath.getAs<DictionaryAttr>(
"value"))
175 if (v.getAs<UnitAttr>(
"omir.tracker"))
176 id = v.getAs<IntegerAttr>(
"id");
177 if (
auto omTy = infoAttr.getAs<DictionaryAttr>(
"omType"))
178 if (
auto valueArr = omTy.getAs<ArrayAttr>(
"value"))
179 for (
auto attr : valueArr)
180 if (
auto str = dyn_cast<StringAttr>(attr))
181 if (str.getValue().equals(
"OMString:OMSRAM"))
212 auto *ctx = original.getContext();
217 auto addID = [&](StringRef tpe, StringRef path,
218 IntegerAttr id) -> DictionaryAttr {
219 NamedAttrList fields;
220 fields.append(
"id",
id);
224 return DictionaryAttr::getWithSorted(ctx, fields);
227 return TypeSwitch<Attribute, std::optional<Attribute>>(original)
236 .Case<StringAttr>([&](StringAttr str) -> std::optional<Attribute> {
238 StringRef tpe,
value;
239 std::tie(tpe,
value) = str.getValue().split(
":");
244 if (tpe ==
"OMReferenceTarget" || tpe ==
"OMMemberReferenceTarget" ||
245 tpe ==
"OMMemberInstanceTarget" || tpe ==
"OMInstanceTarget" ||
246 tpe ==
"OMDontTouchedReferenceTarget") {
247 auto idAttr = state.
newID();
248 NamedAttrList tracker;
250 tracker.append(
"id", idAttr);
256 return addID(tpe,
value, idAttr);
272 if (tpe ==
"OMMap" || tpe ==
"OMArray" || tpe ==
"OMBoolean" ||
273 tpe ==
"OMInt" || tpe ==
"OMDouble" || tpe ==
"OMFrozenTarget") {
275 mlir::emitError(state.
circuit.getLoc())
276 <<
"found known string-encoded OMIR type \"" << tpe
277 <<
"\", but this type should not be seen as it has a defined "
278 "serialization format that does NOT use a string-encoded type";
280 <<
"the problematic OMIR is reproduced here: " << original;
285 auto diag = mlir::emitError(state.
circuit.getLoc())
286 <<
"found unknown string-encoded OMIR type \"" << tpe
287 <<
"\" (Did you misspell it? Is CIRCT missing an Object "
289 diag.attachNote() <<
"the problematic OMIR is reproduced here: "
295 .Case<ArrayAttr>([&](ArrayAttr arr) -> std::optional<Attribute> {
296 SmallVector<Attribute> newArr;
297 for (
auto element : arr) {
301 newArr.push_back(*newElement);
307 .Case<DictionaryAttr>(
308 [&](DictionaryAttr dict) -> std::optional<Attribute> {
309 NamedAttrList newAttrs;
310 for (
auto pairs : dict) {
311 auto maybeValue =
scatterOMIR(pairs.getValue(), state);
314 newAttrs.append(pairs.getName(), *maybeValue);
321 .Case< BoolAttr, FloatAttr,
323 [](
auto passThrough) {
return passThrough; })
325 .Default([&](
auto) -> std::optional<Attribute> {
326 auto diag = mlir::emitError(state.
circuit.getLoc())
327 <<
"found unexpected MLIR attribute \"" << original
328 <<
"\" while trying to scatter OMIR";
352 static std::optional<std::pair<StringRef, DictionaryAttr>>
356 DictionaryAttr dict = dyn_cast<DictionaryAttr>(original);
358 llvm::errs() <<
"OMField is not a dictionary, but should be: " << original
363 auto loc = state.
circuit.getLoc();
364 auto *ctx = state.
circuit.getContext();
369 FileLineColLoc fileLineColLocCache;
372 auto infoAttr = tryGetAs<StringAttr>(dict, root,
"info", loc,
omirAnnoClass);
377 fileLineColLocCache, ctx);
378 mlir::LocationAttr infoLoc;
380 infoLoc = *maybeLoc.second;
385 auto nameAttr = tryGetAs<StringAttr>(dict, root,
"name", loc,
omirAnnoClass);
390 auto valueAttr = tryGetAs<Attribute>(dict, root,
"value", loc,
omirAnnoClass);
397 NamedAttrList values;
401 values.append(
"info", infoLoc);
402 values.append(
"value", *newValue);
404 return {{nameAttr.getValue(), DictionaryAttr::getWithSorted(ctx, values)}};
418 static std::optional<DictionaryAttr>
421 auto loc = state.
circuit.getLoc();
424 DictionaryAttr dict = dyn_cast<DictionaryAttr>(original);
426 llvm::errs() <<
"OMNode is not a dictionary, but should be: " << original
431 NamedAttrList omnode;
432 auto *ctx = state.
circuit.getContext();
437 FileLineColLoc fileLineColLocCache;
440 auto infoAttr = tryGetAs<StringAttr>(dict, root,
"info", loc,
omirAnnoClass);
445 fileLineColLocCache, ctx);
446 mlir::LocationAttr infoLoc;
448 infoLoc = *maybeLoc.second;
453 auto idAttr = tryGetAs<StringAttr>(dict, root,
"id", loc,
omirAnnoClass);
461 auto maybeFields = dict.getAs<ArrayAttr>(
"fields");
462 DictionaryAttr fields;
466 auto fieldAttr = maybeFields.getValue();
467 NamedAttrList fieldAttrs;
468 for (
size_t i = 0, e = fieldAttr.size(); i != e; ++i) {
469 auto field = fieldAttr[i];
471 fieldAttrs.append(newField->first, newField->second);
479 omnode.append(
"fields", fields);
480 omnode.append(
"id", idAttr);
481 omnode.append(
"info", infoLoc);
483 return DictionaryAttr::getWithSorted(ctx, omnode);
492 auto loc = state.
circuit.getLoc();
494 auto nodes = tryGetAs<ArrayAttr>(anno, anno,
"nodes", loc,
omirAnnoClass);
498 SmallVector<Attribute> newNodes;
499 for (
auto node : nodes) {
503 newNodes.push_back(*newNode);
506 auto *ctx = state.
circuit.getContext();
508 NamedAttrList newAnnotation;
523 void EmitOMIRPass::runOnOperation() {
524 MLIRContext *context = &getContext();
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();
695 builder.create<sv::VerbatimOp>(
builder.getUnknownLoc(), jsonBuffer);
696 auto fileAttr = hw::OutputFileAttr::getFromFilename(
697 context, *outputFilename,
true,
false);
698 verbatimOp->setAttr(
"output_file", fileAttr);
701 markAnalysesPreserved<NLATable>();
707 void EmitOMIRPass::makeTrackerAbsolute(Tracker &tracker) {
708 auto builder = OpBuilder::atBlockBegin(getOperation().getBodyBlock());
712 if (
auto module = dyn_cast<FModuleLike>(tracker.op))
713 opName = module.getModuleNameAttr();
715 opName = tracker.op->getAttrOfType<StringAttr>(
"name");
716 auto nlaName = circuitNamespace->newName(
"omir_nla_" + opName.getValue());
721 igraph::ModuleOpInterface mod;
723 mod = instanceGraph->lookup(tracker.nla.root())
724 ->getModule<igraph::ModuleOpInterface>();
726 mod = tracker.op->getParentOfType<FModuleOp>();
729 auto paths = instancePaths->getAbsolutePaths(mod);
731 tracker.op->emitError(
"OMIR node targets uninstantiated component `")
732 << opName.getValue() <<
"`";
736 if (paths.size() > 1) {
737 auto diag = tracker.op->emitError(
"OMIR node targets ambiguous component `")
738 << opName.getValue() <<
"`";
739 diag.attachNote(tracker.op->getLoc())
740 <<
"may refer to the following paths:";
741 for (
auto path : paths)
742 path.print(diag.attachNote(tracker.op->getLoc()) <<
"- ");
749 SmallVector<Attribute> namepath;
750 auto addToPath = [&](Operation *op) {
754 for (
auto inst : paths[0])
758 auto path = tracker.nla.getNamepath().getValue();
759 for (
auto attr : path.drop_back()) {
760 auto ref = attr.cast<hw::InnerRefAttr>();
762 auto *node = instanceGraph->lookup(ref.getModule());
767 assert(it != node->end() &&
768 "Instance referenced by NLA does not exist in module");
769 addToPath((*it)->getInstance());
777 if (
auto module = dyn_cast<FModuleLike>(tracker.op))
783 tracker.nla =
builder.create<hw::HierPathOp>(
builder.getUnknownLoc(),
784 builder.getStringAttr(nlaName),
785 builder.getArrayAttr(namepath));
786 nlaTable->addNLA(tracker.nla);
788 removeTempNLAs.push_back(tracker.nla);
793 void EmitOMIRPass::emitSourceInfo(Location input, SmallString<64> &into) {
795 input->walk([&](Location loc) {
796 if (FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(loc)) {
797 into.append(into.empty() ?
"@[" :
" ");
798 (Twine(fileLoc.getFilename()) +
" " + Twine(fileLoc.getLine()) +
":" +
799 Twine(fileLoc.getColumn()))
802 return WalkResult::advance();
807 into.append(
"UnlocatableSourceInfo");
811 void EmitOMIRPass::emitOMNode(Attribute node, llvm::json::OStream &jsonStream) {
812 auto dict = dyn_cast<DictionaryAttr>(node);
815 .emitError(
"OMNode must be a dictionary")
816 .attachNote(getOperation().getLoc())
823 SmallString<64> info;
824 if (
auto infoAttr = dict.getAs<LocationAttr>(
"info"))
825 emitSourceInfo(infoAttr, info);
830 auto idAttr = dict.getAs<StringAttr>(
"id");
833 .emitError(
"OMNode missing `id` string field")
834 .attachNote(getOperation().getLoc())
841 SmallVector<std::tuple<unsigned, StringAttr, DictionaryAttr>> orderedFields;
842 auto fieldsDict = dict.getAs<DictionaryAttr>(
"fields");
844 for (
auto nameAndField : fieldsDict.getValue()) {
845 auto fieldDict = dyn_cast<DictionaryAttr>(nameAndField.getValue());
848 .emitError(
"OMField must be a dictionary")
849 .attachNote(getOperation().getLoc())
850 << nameAndField.getValue();
856 if (
auto indexAttr = fieldDict.getAs<IntegerAttr>(
"index"))
857 index = indexAttr.getValue().getLimitedValue();
859 orderedFields.push_back({index, nameAndField.getName(), fieldDict});
861 llvm::sort(orderedFields,
862 [](
auto a,
auto b) {
return std::get<0>(a) < std::get<0>(b); });
865 jsonStream.object([&] {
866 jsonStream.attribute(
"info", info);
867 jsonStream.attribute(
"id", idAttr.getValue());
868 jsonStream.attributeArray(
"fields", [&] {
869 for (
auto &orderedField : orderedFields) {
870 emitOMField(std::get<1>(orderedField), std::get<2>(orderedField),
875 if (
auto node = fieldsDict.getAs<DictionaryAttr>(
"containingModule"))
876 if (
auto value = node.getAs<DictionaryAttr>(
"value"))
877 emitOptionalRTLPorts(
value, jsonStream);
885 void EmitOMIRPass::emitOMField(StringAttr fieldName, DictionaryAttr field,
886 llvm::json::OStream &jsonStream) {
888 auto infoAttr = field.getAs<LocationAttr>(
"info");
889 SmallString<64> info;
891 emitSourceInfo(infoAttr, info);
895 jsonStream.object([&] {
896 jsonStream.attribute(
"info", info);
897 jsonStream.attribute(
"name", fieldName.strref());
898 jsonStream.attributeBegin(
"value");
899 emitValue(field.get(
"value"), jsonStream,
900 fieldName.strref().equals(
"dutInstance"));
901 jsonStream.attributeEnd();
908 void EmitOMIRPass::emitOptionalRTLPorts(DictionaryAttr node,
909 llvm::json::OStream &jsonStream) {
912 auto idAttr = node.getAs<IntegerAttr>(
"id");
913 auto trackerIt = trackers.find(idAttr);
914 if (!idAttr || !node.getAs<UnitAttr>(
"omir.tracker") ||
915 trackerIt == trackers.end())
917 auto tracker = trackerIt->second;
922 auto module = dyn_cast<FModuleLike>(tracker.op);
924 module = tracker.op->getParentOfType<FModuleLike>();
926 LLVM_DEBUG(
llvm::dbgs() <<
"Not emitting RTL ports since tracked operation "
927 "does not have a FModuleLike parent: "
928 << *tracker.op <<
"\n");
931 LLVM_DEBUG(
llvm::dbgs() <<
"Emitting RTL ports for module `"
932 << module.getModuleName() <<
"`\n");
936 jsonStream.object([&] {
938 emitSourceInfo(module.getLoc(), buf);
939 jsonStream.attribute(
"info", buf);
940 jsonStream.attribute(
"name",
"ports");
941 jsonStream.attributeArray(
"value", [&] {
942 for (
const auto &port : llvm::enumerate(module.getPorts())) {
943 auto portType = type_dyn_cast<FIRRTLBaseType>(port.value().type);
944 if (!portType || portType.getBitWidthOrSentinel() == 0)
946 jsonStream.object([&] {
948 buf.assign(
"OMDontTouchedReferenceTarget:~");
949 if (module.getModuleNameAttr() == dutModuleName) {
951 buf.append(module.getModuleName());
953 buf.append(getOperation().getName());
956 buf.append(addSymbol(module));
958 buf.append(addSymbol(getInnerRefTo(module, port.index())));
959 jsonStream.attribute(
"ref", buf);
962 buf.assign(
"OMString:");
963 buf.append(port.value().isOutput() ?
"Output" :
"Input");
964 jsonStream.attribute(
"direction", buf);
967 buf.assign(
"OMBigInt:");
968 Twine::utohexstr(portType.getBitWidthOrSentinel()).toVector(buf);
969 jsonStream.attribute(
"width", buf);
976 void EmitOMIRPass::emitValue(Attribute node, llvm::json::OStream &jsonStream,
979 if (!node || isa<UnitAttr>(node))
980 return jsonStream.value(
nullptr);
984 if (
auto attr = dyn_cast<BoolAttr>(node))
985 return jsonStream.value(attr.getValue());
986 if (
auto attr = dyn_cast<IntegerAttr>(node)) {
992 attr.getValue().toStringSigned(val);
993 return jsonStream.rawValue(val);
995 if (
auto attr = dyn_cast<FloatAttr>(node)) {
1000 SmallString<16> val;
1001 attr.getValue().toString(val);
1002 return jsonStream.rawValue(val);
1006 if (
auto attr = dyn_cast<ArrayAttr>(node)) {
1007 jsonStream.array([&] {
1008 for (
auto element : attr.getValue()) {
1009 emitValue(element, jsonStream, dutInstance);
1016 if (
auto attr = dyn_cast<DictionaryAttr>(node)) {
1018 if (attr.getAs<UnitAttr>(
"omir.tracker"))
1019 return emitTrackedTarget(attr, jsonStream, dutInstance);
1022 jsonStream.object([&] {
1023 for (
auto field : attr.getValue()) {
1024 jsonStream.attributeBegin(field.getName());
1025 emitValue(field.getValue(), jsonStream, dutInstance);
1026 jsonStream.attributeEnd();
1035 if (
auto attr = dyn_cast<StringAttr>(node)) {
1036 StringRef val = attr.getValue();
1038 return jsonStream.value(val);
1043 jsonStream.value(
"<unsupported value>");
1044 getOperation().emitError(
"unsupported attribute for OMIR serialization: `")
1049 void EmitOMIRPass::emitTrackedTarget(DictionaryAttr node,
1050 llvm::json::OStream &jsonStream,
1053 auto idAttr = node.getAs<IntegerAttr>(
"id");
1056 .emitError(
"tracked OMIR target missing `id` string field")
1057 .attachNote(getOperation().getLoc())
1060 return jsonStream.value(
"<error>");
1064 auto typeAttr = node.getAs<StringAttr>(
"type");
1067 .emitError(
"tracked OMIR target missing `type` string field")
1068 .attachNote(getOperation().getLoc())
1071 return jsonStream.value(
"<error>");
1073 StringRef type = typeAttr.getValue();
1077 auto trackerIt = trackers.find(idAttr);
1078 if (trackerIt == trackers.end()) {
1081 if (type ==
"OMReferenceTarget" || type ==
"OMMemberReferenceTarget" ||
1082 type ==
"OMMemberInstanceTarget")
1083 return jsonStream.value(
"OMDeleted:");
1086 auto diag = getOperation().emitError(
"tracked OMIR target of type `")
1087 << type <<
"` was deleted";
1088 diag.attachNote(getOperation().getLoc())
1089 <<
"`" << type <<
"` should never be deleted";
1090 if (
auto path = node.getAs<StringAttr>(
"path"))
1091 diag.attachNote(getOperation().getLoc())
1092 <<
"original path: `" << path.getValue() <<
"`";
1094 return jsonStream.value(
"<error>");
1096 auto tracker = trackerIt->second;
1103 if (type ==
"OMMemberReferenceTarget" && isa<InstanceOp, MemOp>(tracker.op))
1104 type =
"OMMemberInstanceTarget";
1107 SmallString<64> target(type);
1108 target.append(
":~");
1109 target.append(getOperation().
getName());
1110 target.push_back(
'|');
1114 bool notFirst =
false;
1115 hw::InnerRefAttr instName;
1116 for (
auto nameRef : tracker.nla.getNamepath()) {
1118 if (
auto innerRef = nameRef.dyn_cast<hw::InnerRefAttr>())
1119 modName = innerRef.getModule();
1120 else if (
auto ref = dyn_cast<FlatSymbolRefAttr>(nameRef))
1121 modName = ref.getAttr();
1122 if (!dutInstance && modName == dutModuleName) {
1128 target.append(
":~");
1129 target.append(dutModuleName);
1130 target.push_back(
'|');
1135 Operation *module = nlaTable->getModule(modName);
1138 target.push_back(
'/');
1141 target.append(addSymbol(instName));
1142 target.push_back(
':');
1144 target.append(addSymbol(module));
1146 if (
auto innerRef = nameRef.dyn_cast<hw::InnerRefAttr>()) {
1149 auto instOp = instancesByName.lookup(innerRef);
1152 LLVM_DEBUG(
llvm::dbgs() <<
"Marking NLA-participating instance "
1153 << innerRef.getName() <<
" in module "
1154 << modName <<
" as dont-touch\n");
1155 tempSymInstances.erase(instOp);
1160 FModuleOp module = dyn_cast<FModuleOp>(tracker.op);
1162 module = tracker.op->getParentOfType<FModuleOp>();
1164 if (module.getNameAttr() == dutModuleName) {
1167 target.append(
":~");
1168 target.append(dutModuleName);
1169 target.push_back(
'|');
1171 target.append(addSymbol(module));
1176 hw::InnerRefAttr componentName;
1178 if (isa<WireOp, RegOp, RegResetOp, InstanceOp, NodeOp, MemOp>(tracker.op)) {
1179 tempSymInstances.erase(tracker.op);
1181 LLVM_DEBUG(
llvm::dbgs() <<
"Marking OMIR-targeted " << componentName
1182 <<
" as dont-touch\n");
1186 if (tracker.hasFieldID()) {
1187 if (isa<WireOp, RegOp, RegResetOp, NodeOp>(tracker.op)) {
1188 componentType = getTypeOf(tracker.op);
1190 tracker.op->emitError(
"does not support OMIR targeting fields");
1192 return jsonStream.value(
"<error>");
1195 }
else if (
auto mod = dyn_cast<FModuleLike>(tracker.op)) {
1196 if (tracker.portNo >= 0) {
1200 if (tracker.hasFieldID())
1201 componentType = getTypeOf(mod, tracker.portNo);
1203 }
else if (!isa<FModuleLike>(tracker.op)) {
1204 tracker.op->emitError(
"invalid target for `") << type <<
"` OMIR";
1206 return jsonStream.value(
"<error>");
1208 if (componentName) {
1214 if (type ==
"OMMemberInstanceTarget") {
1215 if (
auto instOp = dyn_cast<InstanceOp>(tracker.op)) {
1216 target.push_back(
'/');
1217 target.append(addSymbol(componentName));
1218 target.push_back(
':');
1219 target.append(addSymbol(instOp.getModuleNameAttr()));
1222 if (
auto memOp = dyn_cast<MemOp>(tracker.op)) {
1223 target.push_back(
'/');
1224 target.append(addSymbol(componentName));
1225 target.push_back(
':');
1226 target.append(memOp.getSummary().getFirMemoryName());
1230 target.push_back(
'>');
1231 target.append(addSymbol(componentName));
1232 addFieldID(componentType, tracker.fieldID, target);
1236 jsonStream.value(target);
1241 [&](FModuleLike module) -> hw::InnerSymbolNamespace & {
1242 return getModuleNamespace(module);
1249 [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
1250 return getModuleNamespace(mod);
1254 FIRRTLType EmitOMIRPass::getTypeOf(Operation *op) {
1255 if (
auto fop = dyn_cast<Forceable>(op))
1256 return fop.getDataType();
1257 assert(op->getNumResults() == 1 &&
1258 isa<FIRRTLType>(op->getResult(0).getType()) &&
1259 "op must have a single FIRRTLType result");
1260 return type_cast<FIRRTLType>(op->getResult(0).getType());
1263 FIRRTLType EmitOMIRPass::getTypeOf(FModuleLike mod,
size_t portIdx) {
1264 Type portType = mod.getPortType(portIdx);
1265 assert(isa<FIRRTLType>(portType) &&
"port must have a FIRRTLType");
1266 return type_cast<FIRRTLType>(portType);
1272 void EmitOMIRPass::addFieldID(
FIRRTLType type,
unsigned fieldID,
1273 SmallVectorImpl<char> &result) {
1276 .
Case<FVectorType>([&](FVectorType vector) {
1277 size_t index = vector.getIndexForFieldID(fieldID);
1278 type = vector.getElementType();
1279 fieldID -= vector.getFieldID(index);
1280 result.push_back(
'[');
1281 Twine(index).toVector(result);
1282 result.push_back(
']');
1284 .Case<BundleType>([&](BundleType bundle) {
1285 size_t index = bundle.getIndexForFieldID(fieldID);
1286 StringRef name = bundle.getElement(index).name;
1287 type = bundle.getElementType(index);
1288 fieldID -= bundle.getFieldID(index);
1289 result.push_back(
'.');
1290 result.append(name.begin(), name.end());
1298 std::unique_ptr<mlir::Pass>
1300 auto pass = std::make_unique<EmitOMIRPass>();
1301 if (!outputFilename.empty())
1302 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.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
mlir::raw_indented_ostream & errs()
mlir::raw_indented_ostream & dbgs()
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.