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";
327 auto diag = mlir::emitError(op->getLoc());
328 diag << anno.getAs<StringAttr>(
"class").getValue() <<
" ";
332 if (!isa<OpAnnoTarget>(target.
ref))
334 <<
"must target an operation. Currently ports are not supported";
337 return error() <<
"must be local";
339 if (!isa<FModuleOp, WireOp, NodeOp, RegOp, RegResetOp>(op))
341 <<
"unhandled operation. The target must be a module, wire, node or "
344 auto name = anno.getAs<StringAttr>(
"description");
351 template <
bool isInline>
356 mlir::emitError(state.
circuit.getLoc())
357 <<
"has a " << anno.get(
"class")
358 <<
" annotation which is non-local, but this annotation is not allowed "
365 if (!target.
isOpOfType<MemOp, CombMemOp, SeqMemOp>()) {
366 mlir::emitError(op->getLoc())
367 <<
"can only apply a load memory annotation to a memory";
372 StringAttr filename = tryGetAs<StringAttr>(
373 anno, anno, isInline ?
"filename" :
"fileName", op->getLoc(),
374 anno.getAs<StringAttr>(
"class").getValue());
379 tryGetAs<StringAttr>(anno, anno,
"hexOrBinary", op->getLoc(),
380 anno.getAs<StringAttr>(
"class").getValue());
384 auto hexOrBinaryValue = hexOrBinary.getValue();
385 if (hexOrBinaryValue !=
"h" && hexOrBinaryValue !=
"b") {
386 auto diag = mlir::emitError(op->getLoc())
387 <<
"has memory initialization annotation with invalid format, "
388 "'hexOrBinary' field must be either 'h' or 'b'";
389 diag.attachNote() <<
"the full annotation is: " << anno;
394 hexOrBinaryValue ==
"b", isInline));
403 auto *context = op->getContext();
404 auto loc = op->getLoc();
410 auto opTarget = dyn_cast<OpAnnoTarget>(target.
ref);
412 return error() <<
"must target a module";
414 return error() <<
"must be local";
416 auto moduleOp = dyn_cast<FModuleOp>(op);
418 return error() <<
"must target a module";
419 if (!moduleOp.isPublic())
420 return error() <<
"must target a public module";
421 if (moduleOp->hasAttr(
"output_file"))
422 return error() <<
"target already has an output file";
429 return error() <<
"dirname must not be empty";
432 hw::OutputFileAttr::getAsDirectory(context, dirname.getValue());
434 moduleOp->setAttr(
"output_file", outputFile);
456 applyWithoutTarget<false, CircuitOp>};
461 {
"circt.test", {
stdResolve, applyWithoutTarget<true>}},
462 {
"circt.testLocalOnly", {
stdResolve, applyWithoutTarget<>}},
463 {
"circt.testNT", {
noResolve, applyWithoutTarget<>}},
464 {
"circt.missing", {
tryResolve, applyWithoutTarget<true>}},
494 RegResetOp, InstanceOp, MemOp, CombMemOp,
495 MemoryPortOp, SeqMemOp>}},
498 applyWithoutTarget<true, FModuleOp, FExtModuleOp, InstanceOp>}},
504 {
stdResolve, applyWithoutTarget<true, MemOp, CombMemOp>}},
510 {
stdResolve, applyWithoutTarget<true, FModuleOp, FExtModuleOp>}},
514 {
stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
516 {
stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
518 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
520 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
522 {
stdResolve, applyWithoutTarget<false, FModuleOp>}},
524 {
stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
528 {
stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
548 {
stdResolve, applyWithoutTarget<true, FModuleOp>}},
562 const std::function<
void(llvm::Twine)> &errorHandler) {
565 return LogicalResult::success();
567 errorHandler(
"annotation record '" + annoClass +
"' is registered twice\n");
568 return LogicalResult::failure();
576 bool ignoreAnnotationUnknown) {
580 if (ignoreAnnotationUnknown)
590 struct LowerAnnotationsPass
591 :
public circt::firrtl::impl::LowerFIRRTLAnnotationsBase<
592 LowerAnnotationsPass> {
593 void runOnOperation()
override;
594 LogicalResult applyAnnotation(DictionaryAttr anno,
ApplyState &state);
595 LogicalResult legacyToWiringProblems(
ApplyState &state);
596 LogicalResult solveWiringProblems(
ApplyState &state);
598 using LowerFIRRTLAnnotationsBase::allowAddingPortsOnPublic;
599 using LowerFIRRTLAnnotationsBase::ignoreAnnotationClassless;
600 using LowerFIRRTLAnnotationsBase::ignoreAnnotationUnknown;
601 using LowerFIRRTLAnnotationsBase::noRefTypePorts;
602 SmallVector<DictionaryAttr> worklistAttrs;
606 LogicalResult LowerAnnotationsPass::applyAnnotation(DictionaryAttr anno,
608 LLVM_DEBUG(llvm::dbgs() <<
" - anno: " << anno <<
"\n";);
611 StringRef annoClassVal;
612 if (
auto annoClass = anno.getNamed(
"class"))
613 annoClassVal = cast<StringAttr>(annoClass->getValue()).getValue();
614 else if (ignoreAnnotationClassless)
615 annoClassVal =
"circt.missing";
617 return mlir::emitError(state.
circuit.getLoc())
618 <<
"Annotation without a class: " << anno;
624 if (!ignoreAnnotationUnknown)
625 return mlir::emitError(state.
circuit.getLoc())
626 <<
"Unhandled annotation: " << anno;
634 auto target = record->resolver(anno, state);
636 return mlir::emitError(state.
circuit.getLoc())
637 <<
"Unable to resolve target of annotation: " << anno;
638 if (record->applier(*target, anno, state).failed())
639 return mlir::emitError(state.
circuit.getLoc())
640 <<
"Unable to apply annotation: " << anno;
646 LogicalResult LowerAnnotationsPass::legacyToWiringProblems(
ApplyState &state) {
649 return mlir::emitError(state.
circuit.getLoc())
650 <<
"Unable to resolve source for pin: " << name;
652 if (problem.sinks.empty())
653 return mlir::emitError(state.
circuit.getLoc())
654 <<
"Unable to resolve sink(s) for pin: " << name;
656 for (
const auto &sink : problem.sinks) {
671 LogicalResult LowerAnnotationsPass::solveWiringProblems(
ApplyState &state) {
674 auto getModule = [](Value value) {
675 if (BlockArgument blockArg = dyn_cast<BlockArgument>(value))
676 return cast<FModuleLike>(blockArg.getParentBlock()->getParentOp());
677 return value.getDefiningOp()->getParentOfType<FModuleLike>();
681 auto findInsertionBlock = [&getModule](Value src, Value dest) -> Block * {
683 if (src.getParentBlock() == dest.getParentBlock())
684 return src.getParentBlock();
688 assert(getModule(src) == getModule(dest));
690 auto safelyDoms = [&](Value a, Value b) {
691 if (isa<BlockArgument>(a))
693 if (isa<BlockArgument>(b))
697 a.getParentBlock()->findAncestorOpInBlock(*b.getDefiningOp());
698 return ancestor && a.getDefiningOp()->isBeforeInBlock(ancestor);
700 if (safelyDoms(src, dest))
701 return dest.getParentBlock();
702 if (safelyDoms(dest, src))
703 return src.getParentBlock();
707 auto getNoopCast = [](Value v) -> mlir::UnrealizedConversionCastOp {
709 dyn_cast_or_null<mlir::UnrealizedConversionCastOp>(v.getDefiningOp());
710 if (op && op.getNumResults() == 1 && op.getNumOperands() == 1 &&
711 op.getResultTypes()[0] == op.getOperandTypes()[0])
718 SmallVector<Operation *> opsToErase;
719 auto connect = [&](Value src, Value dest,
720 ImplicitLocOpBuilder &builder) -> LogicalResult {
723 if (
auto op = getNoopCast(dest)) {
724 dest = op.getOperand(0);
725 opsToErase.push_back(op);
726 std::swap(src, dest);
727 }
else if (
auto op = getNoopCast(src)) {
728 src = op.getOperand(0);
729 opsToErase.push_back(op);
733 std::swap(src, dest);
736 auto *insertBlock = findInsertionBlock(src, dest);
738 return emitError(src.getLoc())
739 .append(
"This value is involved with a Wiring Problem where the "
740 "destination is in the same module but neither dominates the "
741 "other, which is not supported.")
742 .attachNote(dest.getLoc())
743 .append(
"The destination is here.");
746 builder.setInsertionPointToEnd(insertBlock);
749 if (type_isa<RefType>(dest.getType()) != type_isa<RefType>(src.getType())) {
750 if (type_isa<RefType>(dest.getType()))
751 src = builder.create<RefSendOp>(src);
753 src = builder.create<RefResolveOp>(src);
759 if (
auto destOp = dyn_cast_or_null<WireOp>(dest.getDefiningOp());
760 destOp && dest.getUses().empty()) {
762 if (
auto baseType = dyn_cast<FIRRTLBaseType>(src.getType());
763 baseType && baseType.isPassive()) {
766 builder.create<NodeOp>(src, destOp.getName())
767 .setAnnotationsAttr(destOp.getAnnotations());
768 opsToErase.push_back(destOp);
780 auto *context = state.
circuit.getContext();
784 LLVM_DEBUG({ llvm::dbgs() <<
"Analyzing wiring problems:\n"; });
785 DenseMap<FModuleLike, ModuleModifications> moduleModifications;
786 DenseSet<Value> visitedSinks;
788 auto index = e.index();
789 auto problem = e.value();
793 auto source = problem.source;
794 auto sink = problem.sink;
798 if (!visitedSinks.insert(sink).second) {
799 auto diag = mlir::emitError(source.getLoc())
800 <<
"This sink is involved with a Wiring Problem which is "
801 "targeted by a source used by another Wiring Problem. "
802 "(This is both illegal and should be impossible.)";
803 diag.attachNote(source.getLoc()) <<
"The source is here";
806 FModuleLike sourceModule = getModule(source);
807 FModuleLike sinkModule = getModule(sink);
808 if (isa<FExtModuleOp>(sourceModule) || isa<FExtModuleOp>(sinkModule)) {
809 auto diag = mlir::emitError(source.getLoc())
810 <<
"This source is involved with a Wiring Problem which "
811 "includes an External Module port and External Module "
812 "ports anre not supported.";
813 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
818 llvm::dbgs() <<
" - index: " << index <<
"\n"
820 <<
" module: " << sourceModule.getModuleName() <<
"\n"
821 <<
" value: " << source <<
"\n"
823 <<
" module: " << sinkModule.getModuleName() <<
"\n"
824 <<
" value: " << sink <<
"\n"
825 <<
" newNameHint: " << problem.newNameHint <<
"\n";
829 if (sink.getParentBlock() == source.getParentBlock()) {
830 auto builder = ImplicitLocOpBuilder::atBlockEnd(
UnknownLoc::get(context),
831 sink.getParentBlock());
832 if (failed(
connect(source, sink, builder)))
839 if (sourceModule == sinkModule) {
840 LLVM_DEBUG(llvm::dbgs()
841 <<
" LCA: " << sourceModule.getModuleName() <<
"\n");
842 moduleModifications[sourceModule].connectionMap[index] = source;
843 moduleModifications[sourceModule].uturns.push_back({index, sink});
851 if (sourcePaths.size() != 1 || sinkPaths.size() != 1) {
853 mlir::emitError(source.getLoc())
854 <<
"This source is involved with a Wiring Problem where the source "
855 "or the sink are multiply instantiated and this is not supported.";
856 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
861 cast<FModuleOp>(instanceGraph.getTopLevelNode()->getModule());
862 auto sources = sourcePaths[0];
863 auto sinks = sinkPaths[0];
864 while (!sources.empty() && !sinks.empty()) {
865 if (sources.top() != sinks.top())
867 auto newLCA = cast<InstanceOp>(*sources.top());
868 lca = cast<FModuleOp>(newLCA.getReferencedModule(instanceGraph));
869 sources = sources.dropFront();
870 sinks = sinks.dropFront();
874 llvm::dbgs() <<
" LCA: " << lca.getModuleName() <<
"\n"
875 <<
" sourcePath: " << sourcePaths[0] <<
"\n"
876 <<
" sinkPaths: " << sinkPaths[0] <<
"\n";
880 moduleModifications[sourceModule].connectionMap[index] = source;
881 moduleModifications[sinkModule].connectionMap[index] = sink;
884 Type sourceType, sinkType;
890 RefType refType = TypeSwitch<Type, RefType>(source.getType())
894 .Case<RefType>([](RefType ref) {
return ref; });
895 sourceType = refType;
896 sinkType = refType.getType();
899 sourceType = source.getType();
900 sinkType = sink.getType();
903 auto sourceFType = type_dyn_cast<FIRRTLType>(sourceType);
904 auto sinkFType = type_dyn_cast<FIRRTLType>(sinkType);
906 return emitError(source.getLoc())
907 <<
"Wiring Problem source type \"" << sourceType
908 <<
"\" must be a FIRRTL type";
910 return emitError(sink.getLoc())
911 <<
"Wiring Problem sink type \"" << sinkType
912 <<
"\" must be a FIRRTL type";
916 if (sourceFType != sinkFType &&
919 if (
auto sourceBaseType = dyn_cast<FIRRTLBaseType>(sourceFType);
929 auto diag = mlir::emitError(source.getLoc())
930 <<
"Wiring Problem source type " << sourceType
931 <<
" does not match sink type " << sinkType;
932 diag.attachNote(sink.getLoc()) <<
"The sink is here.";
939 if (
auto sinkFType = type_dyn_cast<FIRRTLType>(sink.getType());
940 sinkFType && type_isa<RefType>(sourceType) &&
942 return emitError(sink.getLoc())
943 <<
"Wiring Problem sink type \"" << sink.getType()
944 <<
"\" must be passive (no flips) when using references";
949 StringRef name, instName;
950 for (
auto instNode : llvm::reverse(insts)) {
951 auto inst = cast<InstanceOp>(*instNode);
952 auto mod = inst.getReferencedModule<FModuleOp>(instanceGraph);
953 if (mod.isPublic()) {
954 if (!allowAddingPortsOnPublic) {
955 auto diag = emitError(
956 mod.getLoc(),
"cannot wire port through this public module");
957 diag.attachNote(source.getLoc()) <<
"source here";
958 diag.attachNote(sink.getLoc()) <<
"sink here";
961 ++numPublicPortsWired;
964 if (problem.newNameHint.empty())
974 assert(!instName.empty());
977 moduleModifications[mod].portsToAdd.push_back(
979 instName = inst.getInstanceName();
985 if (failed(addPorts(sources, source, sourceType,
Direction::Out)) ||
992 LLVM_DEBUG({ llvm::dbgs() <<
"Updating modules:\n"; });
993 for (
auto *op : llvm::post_order(instanceGraph.getTopLevelNode())) {
994 auto fmodule = dyn_cast<FModuleOp>(*op->getModule());
996 if (!fmodule || !moduleModifications.count(fmodule))
999 auto modifications = moduleModifications[fmodule];
1001 llvm::dbgs() <<
" - module: " << fmodule.getModuleName() <<
"\n";
1002 llvm::dbgs() <<
" ports:\n";
1003 for (
auto [index, port] : modifications.portsToAdd) {
1004 llvm::dbgs() <<
" - name: " << port.getName() <<
"\n"
1005 <<
" id: " << index <<
"\n"
1006 <<
" type: " << port.type <<
"\n"
1014 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1015 SmallVector<unsigned> problemIndices;
1016 for (
auto [problemIdx, portInfo] : modifications.portsToAdd) {
1018 newPorts.push_back({fmodule.getNumPorts(), portInfo});
1019 problemIndices.push_back(problemIdx);
1021 auto originalNumPorts = fmodule.getNumPorts();
1022 auto portIdx = fmodule.getNumPorts();
1023 fmodule.insertPorts(newPorts);
1025 auto builder = ImplicitLocOpBuilder::atBlockBegin(
UnknownLoc::get(context),
1026 fmodule.getBodyBlock());
1030 for (
auto [problemIdx, portPair] : llvm::zip(problemIndices, newPorts)) {
1031 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1032 assert(src &&
"there did not exist a driver for the port");
1033 Value dest = fmodule.getArgument(portIdx++);
1034 if (failed(
connect(src, dest, builder)))
1040 for (
auto [problemIdx, dest] : moduleModifications[fmodule].uturns) {
1041 Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1042 assert(src &&
"there did not exist a connection for the u-turn");
1043 if (failed(
connect(src, dest, builder)))
1048 for (
auto *inst : instanceGraph.lookup(fmodule)->uses()) {
1049 InstanceOp useInst = cast<InstanceOp>(inst->getInstance());
1050 auto enclosingModule = useInst->getParentOfType<FModuleOp>();
1051 auto clonedInst = useInst.cloneAndInsertPorts(newPorts);
1055 useInst->replaceAllUsesWith(
1056 clonedInst.getResults().drop_back(newPorts.size()));
1063 for (
auto [newPortIdx, problemIdx] : llvm::enumerate(problemIndices)) {
1064 auto &modifications = moduleModifications[enclosingModule];
1065 auto newPort = clonedInst.getResult(newPortIdx + originalNumPorts);
1066 if (modifications.connectionMap.count(problemIdx)) {
1067 modifications.uturns.push_back({problemIdx, newPort});
1070 modifications.connectionMap[problemIdx] = newPort;
1076 for (
auto *op : opsToErase)
1083 void LowerAnnotationsPass::runOnOperation() {
1084 CircuitOp circuit = getOperation();
1085 SymbolTable modules(circuit);
1095 auto annotations = circuit->getAttrOfType<ArrayAttr>(
rawAnnotations);
1103 for (
auto anno : llvm::reverse(annotations.getValue()))
1104 worklistAttrs.push_back(cast<DictionaryAttr>(anno));
1106 size_t numFailures = 0;
1107 size_t numAdded = 0;
1108 auto addToWorklist = [&](DictionaryAttr anno) {
1110 worklistAttrs.push_back(anno);
1113 ApplyState state{circuit, modules, addToWorklist, instancePathCache,
1115 LLVM_DEBUG(llvm::dbgs() <<
"Processing annotations:\n");
1116 while (!worklistAttrs.empty()) {
1117 auto attr = worklistAttrs.pop_back_val();
1118 if (applyAnnotation(attr, state).failed())
1122 if (failed(legacyToWiringProblems(state)))
1125 if (failed(solveWiringProblems(state)))
1129 numRawAnnotations += annotations.size();
1130 numAddedAnnos += numAdded;
1131 numAnnos += numAdded + annotations.size();
1135 signalPassFailure();
1140 bool ignoreAnnotationUnknown,
bool ignoreAnnotationClassless,
1141 bool noRefTypePorts,
bool allowAddingPortsOnPublic) {
1142 auto pass = std::make_unique<LowerAnnotationsPass>();
1143 pass->ignoreAnnotationUnknown = ignoreAnnotationUnknown;
1144 pass->ignoreAnnotationClassless = ignoreAnnotationClassless;
1145 pass->noRefTypePorts = noRefTypePorts;
1146 pass->allowAddingPortsOnPublic = allowAddingPortsOnPublic;
assert(baseType &&"element must be base type")
static LogicalResult applyOutputDirAnno(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 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 * 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 * 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.
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.