CIRCT  20.0.0git
LowerAnnotations.cpp
Go to the documentation of this file.
1 //===- LowerAnnotations.cpp - Lower Annotations -----------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines the LowerAnnotations pass. This pass processes FIRRTL
10 // annotations, rewriting them, scattering them, and dealing with non-local
11 // annotations.
12 //
13 //===----------------------------------------------------------------------===//
14 
17 #include "mlir/Pass/Pass.h"
18 
32 #include "circt/Dialect/HW/HWOps.h"
34 #include "circt/Support/Debug.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"
40 
41 #define DEBUG_TYPE "lower-annos"
42 
43 namespace circt {
44 namespace firrtl {
45 #define GEN_PASS_DEF_LOWERFIRRTLANNOTATIONS
46 #include "circt/Dialect/FIRRTL/Passes.h.inc"
47 } // namespace firrtl
48 } // namespace circt
49 
50 using namespace circt;
51 using namespace firrtl;
52 using namespace chirrtl;
53 
54 /// Get annotations or an empty set of annotations.
55 static ArrayAttr getAnnotationsFrom(Operation *op) {
56  if (auto annots = op->getAttrOfType<ArrayAttr>(getAnnotationAttrName()))
57  return annots;
58  return ArrayAttr::get(op->getContext(), {});
59 }
60 
61 /// Construct the annotation array with a new thing appended.
62 static ArrayAttr appendArrayAttr(ArrayAttr array, Attribute a) {
63  if (!array)
64  return ArrayAttr::get(a.getContext(), ArrayRef<Attribute>{a});
65  SmallVector<Attribute> old(array.begin(), array.end());
66  old.push_back(a);
67  return ArrayAttr::get(a.getContext(), old);
68 }
69 
70 /// Update an ArrayAttribute by replacing one entry.
71 static ArrayAttr replaceArrayAttrElement(ArrayAttr array, size_t elem,
72  Attribute newVal) {
73  SmallVector<Attribute> old(array.begin(), array.end());
74  old[elem] = newVal;
75  return ArrayAttr::get(array.getContext(), old);
76 }
77 
78 /// Apply a new annotation to a resolved target. This handles ports,
79 /// aggregates, modules, wires, etc.
80 static void addAnnotation(AnnoTarget ref, unsigned fieldIdx,
81  ArrayRef<NamedAttribute> anno) {
82  auto *context = ref.getOp()->getContext();
83  DictionaryAttr annotation;
84  if (fieldIdx) {
85  SmallVector<NamedAttribute> annoField(anno.begin(), anno.end());
86  annoField.emplace_back(
87  StringAttr::get(context, "circt.fieldID"),
88  IntegerAttr::get(IntegerType::get(context, 32, IntegerType::Signless),
89  fieldIdx));
90  annotation = DictionaryAttr::get(context, annoField);
91  } else {
92  annotation = DictionaryAttr::get(context, anno);
93  }
94 
95  if (isa<OpAnnoTarget>(ref)) {
96  auto newAnno = appendArrayAttr(getAnnotationsFrom(ref.getOp()), annotation);
97  ref.getOp()->setAttr(getAnnotationAttrName(), newAnno);
98  return;
99  }
100 
101  auto portRef = cast<PortAnnoTarget>(ref);
102  auto portAnnoRaw = ref.getOp()->getAttr(getPortAnnotationAttrName());
103  ArrayAttr portAnno = dyn_cast_or_null<ArrayAttr>(portAnnoRaw);
104  if (!portAnno || portAnno.size() != getNumPorts(ref.getOp())) {
105  SmallVector<Attribute> emptyPortAttr(
106  getNumPorts(ref.getOp()),
107  ArrayAttr::get(ref.getOp()->getContext(), {}));
108  portAnno = ArrayAttr::get(ref.getOp()->getContext(), emptyPortAttr);
109  }
110  portAnno = replaceArrayAttrElement(
111  portAnno, portRef.getPortNo(),
112  appendArrayAttr(dyn_cast<ArrayAttr>(portAnno[portRef.getPortNo()]),
113  annotation));
114  ref.getOp()->setAttr("portAnnotations", portAnno);
115 }
116 
117 /// Make an anchor for a non-local annotation. Use the expanded path to build
118 /// the module and name list in the anchor.
119 static FlatSymbolRefAttr buildNLA(const AnnoPathValue &target,
120  ApplyState &state) {
121  OpBuilder b(state.circuit.getBodyRegion());
122  SmallVector<Attribute> insts;
123  for (auto inst : target.instances) {
124  insts.push_back(OpAnnoTarget(inst).getNLAReference(
125  state.getNamespace(inst->getParentOfType<FModuleLike>())));
126  }
127 
128  insts.push_back(
129  FlatSymbolRefAttr::get(target.ref.getModule().getModuleNameAttr()));
130 
131  auto instAttr = ArrayAttr::get(state.circuit.getContext(), insts);
132  return state.hierPathCache.getRefFor(instAttr);
133 }
134 
135 /// Scatter breadcrumb annotations corresponding to non-local annotations
136 /// along the instance path. Returns symbol name used to anchor annotations to
137 /// path.
138 // FIXME: uniq annotation chain links
139 static FlatSymbolRefAttr scatterNonLocalPath(const AnnoPathValue &target,
140  ApplyState &state) {
141 
142  FlatSymbolRefAttr sym = buildNLA(target, state);
143  return sym;
144 }
145 
146 //===----------------------------------------------------------------------===//
147 // Standard Utility Resolvers
148 //===----------------------------------------------------------------------===//
149 
150 /// Always resolve to the circuit, ignoring the annotation.
151 static std::optional<AnnoPathValue> noResolve(DictionaryAttr anno,
152  ApplyState &state) {
153  return AnnoPathValue(state.circuit);
154 }
155 
156 /// Implementation of standard resolution. First parses the target path, then
157 /// resolves it.
158 static std::optional<AnnoPathValue> stdResolveImpl(StringRef rawPath,
159  ApplyState &state) {
160  auto pathStr = canonicalizeTarget(rawPath);
161  StringRef path{pathStr};
162 
163  auto tokens = tokenizePath(path);
164  if (!tokens) {
165  mlir::emitError(state.circuit.getLoc())
166  << "Cannot tokenize annotation path " << rawPath;
167  return {};
168  }
169 
170  return resolveEntities(*tokens, state.circuit, state.symTbl,
171  state.targetCaches);
172 }
173 
174 /// (SFC) FIRRTL SingleTargetAnnotation resolver. Uses the 'target' field of
175 /// the annotation with standard parsing to resolve the path. This requires
176 /// 'target' to exist and be normalized (per docs/FIRRTLAnnotations.md).
177 std::optional<AnnoPathValue> circt::firrtl::stdResolve(DictionaryAttr anno,
178  ApplyState &state) {
179  auto target = anno.getNamed("target");
180  if (!target) {
181  mlir::emitError(state.circuit.getLoc())
182  << "No target field in annotation " << anno;
183  return {};
184  }
185  if (!isa<StringAttr>(target->getValue())) {
186  mlir::emitError(state.circuit.getLoc())
187  << "Target field in annotation doesn't contain string " << anno;
188  return {};
189  }
190  return stdResolveImpl(cast<StringAttr>(target->getValue()).getValue(), state);
191 }
192 
193 /// Resolves with target, if it exists. If not, resolves to the circuit.
194 std::optional<AnnoPathValue> circt::firrtl::tryResolve(DictionaryAttr anno,
195  ApplyState &state) {
196  auto target = anno.getNamed("target");
197  if (target)
198  return stdResolveImpl(cast<StringAttr>(target->getValue()).getValue(),
199  state);
200  return AnnoPathValue(state.circuit);
201 }
202 
203 //===----------------------------------------------------------------------===//
204 // Standard Utility Appliers
205 //===----------------------------------------------------------------------===//
206 
207 /// An applier which puts the annotation on the target and drops the 'target'
208 /// field from the annotation. Optionally handles non-local annotations.
210 
211  DictionaryAttr anno,
212  ApplyState &state,
213  bool allowNonLocal) {
214  if (!allowNonLocal && !target.isLocal()) {
215  Annotation annotation(anno);
216  auto diag = mlir::emitError(target.ref.getOp()->getLoc())
217  << "is targeted by a non-local annotation \""
218  << annotation.getClass() << "\" with target "
219  << annotation.getMember("target")
220  << ", but this annotation cannot be non-local";
221  diag.attachNote() << "see current annotation: " << anno << "\n";
222  return failure();
223  }
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()) {
229  auto sym = scatterNonLocalPath(target, state);
230  newAnnoAttrs.push_back(
231  {StringAttr::get(anno.getContext(), "circt.nonlocal"), sym});
232  }
233  }
234  addAnnotation(target.ref, target.fieldIdx, newAnnoAttrs);
235  return success();
236 }
237 
238 /// Just drop the annotation. This is intended for Annotations which are known,
239 /// but can be safely ignored.
240 LogicalResult drop(const AnnoPathValue &target, DictionaryAttr anno,
241  ApplyState &state) {
242  return success();
243 }
244 //===----------------------------------------------------------------------===//
245 // Customized Appliers
246 //===----------------------------------------------------------------------===//
247 
248 static LogicalResult applyDUTAnno(const AnnoPathValue &target,
249  DictionaryAttr anno, ApplyState &state) {
250  auto *op = target.ref.getOp();
251  auto loc = op->getLoc();
252 
253  if (!target.isLocal())
254  return mlir::emitError(loc) << "must be local";
255 
256  if (!isa<OpAnnoTarget>(target.ref) || !isa<FModuleOp>(op))
257  return mlir::emitError(loc) << "can only target to a module";
258 
259  auto moduleOp = cast<FModuleOp>(op);
260 
261  // DUT has public visibility.
262  moduleOp.setPublic();
263  SmallVector<NamedAttribute> newAnnoAttrs;
264  for (auto &na : anno)
265  if (na.getName().getValue() != "target")
266  newAnnoAttrs.push_back(na);
267  addAnnotation(target.ref, target.fieldIdx, newAnnoAttrs);
268  return success();
269 }
270 
271 // Like symbolizeConvention, but disallows the internal convention.
272 static std::optional<Convention> parseConvention(llvm::StringRef str) {
273  return ::llvm::StringSwitch<::std::optional<Convention>>(str)
274  .Case("scalarized", Convention::Scalarized)
275  .Default(std::nullopt);
276 }
277 
278 static LogicalResult applyConventionAnno(const AnnoPathValue &target,
279  DictionaryAttr anno,
280  ApplyState &state) {
281  auto *op = target.ref.getOp();
282  auto loc = op->getLoc();
283  auto error = [&]() {
284  auto diag = mlir::emitError(loc);
285  diag << "circuit.ConventionAnnotation ";
286  return diag;
287  };
288 
289  auto opTarget = dyn_cast<OpAnnoTarget>(target.ref);
290  if (!opTarget)
291  return error() << "must target a module object";
292 
293  if (!target.isLocal())
294  return error() << "must be local";
295 
296  auto conventionStrAttr =
297  tryGetAs<StringAttr>(anno, anno, "convention", loc, conventionAnnoClass);
298  if (!conventionStrAttr)
299  return failure();
300 
301  auto conventionStr = conventionStrAttr.getValue();
302  auto conventionOpt = parseConvention(conventionStr);
303  if (!conventionOpt)
304  return error() << "unknown convention " << conventionStr;
305 
306  auto convention = *conventionOpt;
307 
308  if (auto moduleOp = dyn_cast<FModuleOp>(op)) {
309  moduleOp.setConvention(convention);
310  return success();
311  }
312 
313  if (auto extModuleOp = dyn_cast<FExtModuleOp>(op)) {
314  extModuleOp.setConvention(convention);
315  return success();
316  }
317 
318  return error() << "can only target to a module or extmodule";
319 }
320 
321 static LogicalResult applyModulePrefixAnno(const AnnoPathValue &target,
322  DictionaryAttr anno,
323  ApplyState &state) {
324  auto *op = target.ref.getOp();
325  auto loc = op->getLoc();
326  auto error = [&]() {
327  auto diag = mlir::emitError(loc);
328  diag << modulePrefixAnnoClass << " ";
329  return diag;
330  };
331 
332  auto opTarget = dyn_cast<OpAnnoTarget>(target.ref);
333  if (!opTarget)
334  return error() << "must target an operation";
335 
336  if (!isa<SeqMemOp, CombMemOp, MemOp>(opTarget.getOp()))
337  return error() << "must target a memory operation";
338 
339  if (!target.isLocal())
340  return error() << "must be local";
341 
342  auto prefixStrAttr =
343  tryGetAs<StringAttr>(anno, anno, "prefix", loc, modulePrefixAnnoClass);
344  if (!prefixStrAttr)
345  return failure();
346 
347  if (auto mem = dyn_cast<SeqMemOp>(op))
348  mem.setPrefixAttr(prefixStrAttr);
349  else if (auto mem = dyn_cast<CombMemOp>(op))
350  mem.setPrefixAttr(prefixStrAttr);
351  else if (auto mem = dyn_cast<MemOp>(op))
352  mem.setPrefixAttr(prefixStrAttr);
353 
354  return success();
355 }
356 
357 static LogicalResult applyAttributeAnnotation(const AnnoPathValue &target,
358  DictionaryAttr anno,
359  ApplyState &state) {
360  auto *op = target.ref.getOp();
361 
362  auto error = [&]() {
363  auto diag = mlir::emitError(op->getLoc());
364  diag << anno.getAs<StringAttr>("class").getValue() << " ";
365  return diag;
366  };
367 
368  if (!isa<OpAnnoTarget>(target.ref))
369  return error()
370  << "must target an operation. Currently ports are not supported";
371 
372  if (!target.isLocal())
373  return error() << "must be local";
374 
375  if (!isa<FModuleOp, WireOp, NodeOp, RegOp, RegResetOp>(op))
376  return error()
377  << "unhandled operation. The target must be a module, wire, node or "
378  "register";
379 
380  auto name = anno.getAs<StringAttr>("description");
381  auto svAttr = sv::SVAttributeAttr::get(name.getContext(), name);
382  sv::addSVAttributes(op, {svAttr});
383  return success();
384 }
385 
386 /// Update a memory op with attributes about memory file loading.
387 template <bool isInline>
388 static LogicalResult applyLoadMemoryAnno(const AnnoPathValue &target,
389  DictionaryAttr anno,
390  ApplyState &state) {
391  if (!target.isLocal()) {
392  mlir::emitError(state.circuit.getLoc())
393  << "has a " << anno.get("class")
394  << " annotation which is non-local, but this annotation is not allowed "
395  "to be non-local";
396  return failure();
397  }
398 
399  auto *op = target.ref.getOp();
400 
401  if (!target.isOpOfType<MemOp, CombMemOp, SeqMemOp>()) {
402  mlir::emitError(op->getLoc())
403  << "can only apply a load memory annotation to a memory";
404  return failure();
405  }
406 
407  // The two annotations have different case usage in "filename".
408  StringAttr filename = tryGetAs<StringAttr>(
409  anno, anno, isInline ? "filename" : "fileName", op->getLoc(),
410  anno.getAs<StringAttr>("class").getValue());
411  if (!filename)
412  return failure();
413 
414  auto hexOrBinary =
415  tryGetAs<StringAttr>(anno, anno, "hexOrBinary", op->getLoc(),
416  anno.getAs<StringAttr>("class").getValue());
417  if (!hexOrBinary)
418  return failure();
419 
420  auto hexOrBinaryValue = hexOrBinary.getValue();
421  if (hexOrBinaryValue != "h" && hexOrBinaryValue != "b") {
422  auto diag = mlir::emitError(op->getLoc())
423  << "has memory initialization annotation with invalid format, "
424  "'hexOrBinary' field must be either 'h' or 'b'";
425  diag.attachNote() << "the full annotation is: " << anno;
426  return failure();
427  }
428 
429  op->setAttr("init", MemoryInitAttr::get(op->getContext(), filename,
430  hexOrBinaryValue == "b", isInline));
431 
432  return success();
433 }
434 
435 static LogicalResult applyOutputDirAnno(const AnnoPathValue &target,
436  DictionaryAttr anno,
437  ApplyState &state) {
438  auto *op = target.ref.getOp();
439  auto *context = op->getContext();
440  auto loc = op->getLoc();
441 
442  auto error = [&]() {
443  return mlir::emitError(loc) << outputDirAnnoClass << " ";
444  };
445 
446  auto opTarget = dyn_cast<OpAnnoTarget>(target.ref);
447  if (!opTarget)
448  return error() << "must target a module";
449  if (!target.isLocal())
450  return error() << "must be local";
451 
452  auto moduleOp = dyn_cast<FModuleOp>(op);
453  if (!moduleOp)
454  return error() << "must target a module";
455  if (!moduleOp.isPublic())
456  return error() << "must target a public module";
457  if (moduleOp->hasAttr("output_file"))
458  return error() << "target already has an output file";
459 
460  auto dirname =
461  tryGetAs<StringAttr>(anno, anno, "dirname", loc, outputDirAnnoClass);
462  if (!dirname)
463  return failure();
464  if (dirname.empty())
465  return error() << "dirname must not be empty";
466 
467  auto outputFile =
468  hw::OutputFileAttr::getAsDirectory(context, dirname.getValue());
469 
470  moduleOp->setAttr("output_file", outputFile);
471  return success();
472 }
473 
474 /// Convert from FullAsyncResetAnnotation to FullResetAnnotation
475 static LogicalResult convertToFullResetAnnotation(const AnnoPathValue &target,
476  DictionaryAttr anno,
477  ApplyState &state) {
478  auto *op = target.ref.getOp();
479  auto *context = op->getContext();
480 
481  mlir::emitWarning(op->getLoc())
482  << "'" << fullAsyncResetAnnoClass << "' is deprecated, use '"
483  << fullResetAnnoClass << "' instead";
484 
485  NamedAttrList newAnno(anno.getValue());
486  newAnno.set("class", StringAttr::get(context, fullResetAnnoClass));
487  newAnno.append("resetType", StringAttr::get(context, "async"));
488 
489  DictionaryAttr newDictionary = DictionaryAttr::get(op->getContext(), newAnno);
490 
491  return applyWithoutTarget<false>(target, newDictionary, state);
492 }
493 
494 /// Convert from IgnoreFullAsyncResetAnnotation to
495 /// ExcludeFromFullResetAnnotation
497  const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state) {
498  auto *op = target.ref.getOp();
499  auto *context = op->getContext();
500 
501  mlir::emitWarning(op->getLoc())
502  << "'" << ignoreFullAsyncResetAnnoClass << "' is deprecated, use '"
503  << excludeFromFullResetAnnoClass << "' instead";
504 
505  NamedAttrList newAnno(anno.getValue());
506  newAnno.set("class", StringAttr::get(context, excludeFromFullResetAnnoClass));
507 
508  DictionaryAttr newDictionary = DictionaryAttr::get(op->getContext(), newAnno);
509 
510  return applyWithoutTarget<true, FModuleOp>(target, newDictionary, state);
511 }
512 
513 //===----------------------------------------------------------------------===//
514 // Driving table
515 //===----------------------------------------------------------------------===//
516 
517 namespace circt::firrtl {
518 /// Resolution and application of a "firrtl.annotations.NoTargetAnnotation".
519 /// This should be used for any Annotation which does not apply to anything in
520 /// the FIRRTL Circuit, i.e., an Annotation which has no target. Historically,
521 /// NoTargetAnnotations were used to control the Scala FIRRTL Compiler (SFC) or
522 /// its passes, e.g., to set the output directory or to turn on a pass.
523 /// Examples of these in the SFC are "firrtl.options.TargetDirAnnotation" to set
524 /// the output directory or "firrtl.stage.RunFIRRTLTransformAnnotation" to
525 /// cause the SFC to schedule a specified pass. Instead of leaving these
526 /// floating or attaching them to the top-level MLIR module (which is a purer
527 /// interpretation of "no target"), we choose to attach them to the Circuit even
528 /// they do not "apply" to the Circuit. This gives later passes a common place,
529 /// the Circuit, to search for these control Annotations.
531  applyWithoutTarget<false, CircuitOp>};
532 
533 static llvm::StringMap<AnnoRecord> annotationRecords{{
534 
535  // Testing Annotation
536  {"circt.test", {stdResolve, applyWithoutTarget<true>}},
537  {"circt.testLocalOnly", {stdResolve, applyWithoutTarget<>}},
538  {"circt.testNT", {noResolve, applyWithoutTarget<>}},
539  {"circt.missing", {tryResolve, applyWithoutTarget<true>}},
540  // Grand Central Views/Interfaces Annotations
545  {companionAnnoClass, {stdResolve, applyWithoutTarget<>}},
546  {augmentedGroundTypeClass, {stdResolve, applyWithoutTarget<true>}},
547  // Grand Central Data Tap Annotations
549  {dataTapsBlackboxClass, {stdResolve, applyWithoutTarget<true>}},
550  {referenceKeySourceClass, {stdResolve, applyWithoutTarget<true>}},
551  {referenceKeyPortClass, {stdResolve, applyWithoutTarget<true>}},
552  {internalKeySourceClass, {stdResolve, applyWithoutTarget<true>}},
553  {internalKeyPortClass, {stdResolve, applyWithoutTarget<true>}},
554  {deletedKeyClass, {stdResolve, applyWithoutTarget<true>}},
555  {literalKeyClass, {stdResolve, applyWithoutTarget<true>}},
556  // Grand Central Mem Tap Annotations
558  {memTapSourceClass, {stdResolve, applyWithoutTarget<true>}},
559  {memTapPortClass, {stdResolve, applyWithoutTarget<true>}},
560  {memTapBlackboxClass, {stdResolve, applyWithoutTarget<true>}},
561  // OMIR Annotations
563  {omirTrackerAnnoClass, {stdResolve, applyWithoutTarget<true>}},
565  // Miscellaneous Annotations
568  {stdResolve, applyWithoutTarget<true, true, WireOp, NodeOp, RegOp,
569  RegResetOp, InstanceOp, MemOp, CombMemOp,
570  MemoryPortOp, SeqMemOp>}},
572  {stdResolve,
573  applyWithoutTarget<true, FModuleOp, FExtModuleOp, InstanceOp>}},
580  {stdResolve, applyWithoutTarget<true, MemOp, CombMemOp>}},
586  {stdResolve, applyWithoutTarget<true, FModuleOp, FExtModuleOp>}},
587  {flattenAnnoClass, {stdResolve, applyWithoutTarget<false, FModuleOp>}},
588  {inlineAnnoClass, {stdResolve, applyWithoutTarget<false, FModuleOp>}},
590  {stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
592  {stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
594  {stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
596  {stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
598  {stdResolve, applyWithoutTarget<false, FModuleOp>}},
600  {stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
604  {stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
621  {extractBlackBoxAnnoClass, {stdResolve, applyWithoutTarget<false>}},
622  {fullResetAnnoClass, {stdResolve, applyWithoutTarget<false>}},
624  {stdResolve, applyWithoutTarget<true, FModuleOp>}},
631  {traceAnnoClass, {stdResolve, applyWithoutTarget<true>}},
632  {loadMemoryFromFileAnnoClass, {stdResolve, applyLoadMemoryAnno<false>}},
634  {stdResolve, applyLoadMemoryAnno<true>}},
638 
639 LogicalResult
640 registerAnnotationRecord(StringRef annoClass, AnnoRecord annoRecord,
641  const std::function<void(llvm::Twine)> &errorHandler) {
642 
643  if (annotationRecords.insert({annoClass, annoRecord}).second)
644  return LogicalResult::success();
645  if (errorHandler)
646  errorHandler("annotation record '" + annoClass + "' is registered twice\n");
647  return LogicalResult::failure();
648 }
649 
650 } // namespace circt::firrtl
651 
652 /// Lookup a record for a given annotation class. Optionally, returns the
653 /// record for "circuit.missing" if the record doesn't exist.
654 static const AnnoRecord *getAnnotationHandler(StringRef annoStr,
655  bool ignoreAnnotationUnknown) {
656  auto ii = annotationRecords.find(annoStr);
657  if (ii != annotationRecords.end())
658  return &ii->second;
659  if (ignoreAnnotationUnknown)
660  return &annotationRecords.find("circt.missing")->second;
661  return nullptr;
662 }
663 
664 //===----------------------------------------------------------------------===//
665 // Pass Infrastructure
666 //===----------------------------------------------------------------------===//
667 
668 namespace {
669 struct LowerAnnotationsPass
670  : public circt::firrtl::impl::LowerFIRRTLAnnotationsBase<
671  LowerAnnotationsPass> {
672  void runOnOperation() override;
673  LogicalResult applyAnnotation(DictionaryAttr anno, ApplyState &state);
674  LogicalResult legacyToWiringProblems(ApplyState &state);
675  LogicalResult solveWiringProblems(ApplyState &state);
676 
677  using LowerFIRRTLAnnotationsBase::allowAddingPortsOnPublic;
678  using LowerFIRRTLAnnotationsBase::ignoreAnnotationClassless;
679  using LowerFIRRTLAnnotationsBase::ignoreAnnotationUnknown;
680  using LowerFIRRTLAnnotationsBase::noRefTypePorts;
681  SmallVector<DictionaryAttr> worklistAttrs;
682 };
683 } // end anonymous namespace
684 
685 LogicalResult LowerAnnotationsPass::applyAnnotation(DictionaryAttr anno,
686  ApplyState &state) {
687  LLVM_DEBUG(llvm::dbgs() << " - anno: " << anno << "\n";);
688 
689  // Lookup the class
690  StringRef annoClassVal;
691  if (auto annoClass = anno.getNamed("class"))
692  annoClassVal = cast<StringAttr>(annoClass->getValue()).getValue();
693  else if (ignoreAnnotationClassless)
694  annoClassVal = "circt.missing";
695  else
696  return mlir::emitError(state.circuit.getLoc())
697  << "Annotation without a class: " << anno;
698 
699  // See if we handle the class
700  auto *record = getAnnotationHandler(annoClassVal, false);
701  if (!record) {
702  ++numUnhandled;
703  if (!ignoreAnnotationUnknown)
704  return mlir::emitError(state.circuit.getLoc())
705  << "Unhandled annotation: " << anno;
706 
707  // Try again, requesting the fallback handler.
708  record = getAnnotationHandler(annoClassVal, ignoreAnnotationUnknown);
709  assert(record);
710  }
711 
712  // Try to apply the annotation
713  auto target = record->resolver(anno, state);
714  if (!target)
715  return mlir::emitError(state.circuit.getLoc())
716  << "Unable to resolve target of annotation: " << anno;
717  if (record->applier(*target, anno, state).failed())
718  return mlir::emitError(state.circuit.getLoc())
719  << "Unable to apply annotation: " << anno;
720  return success();
721 }
722 
723 /// Convert consumed SourceAnnotation and SinkAnnotation into WiringProblems,
724 /// using the pin attribute as newNameHint
725 LogicalResult LowerAnnotationsPass::legacyToWiringProblems(ApplyState &state) {
726  for (const auto &[name, problem] : state.legacyWiringProblems) {
727  if (!problem.source)
728  return mlir::emitError(state.circuit.getLoc())
729  << "Unable to resolve source for pin: " << name;
730 
731  if (problem.sinks.empty())
732  return mlir::emitError(state.circuit.getLoc())
733  << "Unable to resolve sink(s) for pin: " << name;
734 
735  for (const auto &sink : problem.sinks) {
736  state.wiringProblems.push_back(
737  {problem.source, sink, {}, WiringProblem::RefTypeUsage::Never});
738  }
739  }
740  return success();
741 }
742 
743 /// Modify the circuit to solve and apply all Wiring Problems in the circuit. A
744 /// Wiring Problem is a mapping from a source to a sink that can be connected
745 /// via a base Type or RefType as requested. This uses a two-step approach.
746 /// First, all Wiring Problems are analyzed to compute pending modifications to
747 /// modules. Second, modules are visited from leaves to roots to apply module
748 /// modifications. Module modifications include addings ports and connecting
749 /// things up.
750 LogicalResult LowerAnnotationsPass::solveWiringProblems(ApplyState &state) {
751  // Utility function to extract the defining module from a value which may be
752  // either a BlockArgument or an Operation result.
753  auto getModule = [](Value value) {
754  if (BlockArgument blockArg = dyn_cast<BlockArgument>(value))
755  return cast<FModuleLike>(blockArg.getParentBlock()->getParentOp());
756  return value.getDefiningOp()->getParentOfType<FModuleLike>();
757  };
758 
759  // Utility function to determine where to insert connection operations.
760  auto findInsertionBlock = [&getModule](Value src, Value dest) -> Block * {
761  // Check for easy case: both are in the same block.
762  if (src.getParentBlock() == dest.getParentBlock())
763  return src.getParentBlock();
764 
765  // If connecting across blocks, figure out where to connect.
766  (void)getModule;
767  assert(getModule(src) == getModule(dest));
768  // Helper to determine if 'a' is available at 'b's block.
769  auto safelyDoms = [&](Value a, Value b) {
770  if (isa<BlockArgument>(a))
771  return true;
772  if (isa<BlockArgument>(b))
773  return false;
774  // Handle cases where 'b' is in child op after 'a'.
775  auto *ancestor =
776  a.getParentBlock()->findAncestorOpInBlock(*b.getDefiningOp());
777  return ancestor && a.getDefiningOp()->isBeforeInBlock(ancestor);
778  };
779  if (safelyDoms(src, dest))
780  return dest.getParentBlock();
781  if (safelyDoms(dest, src))
782  return src.getParentBlock();
783  return {};
784  };
785 
786  auto getNoopCast = [](Value v) -> mlir::UnrealizedConversionCastOp {
787  auto op =
788  dyn_cast_or_null<mlir::UnrealizedConversionCastOp>(v.getDefiningOp());
789  if (op && op.getNumResults() == 1 && op.getNumOperands() == 1 &&
790  op.getResultTypes()[0] == op.getOperandTypes()[0])
791  return op;
792  return {};
793  };
794 
795  // Utility function to connect a destination to a source. Always use a
796  // ConnectOp as the widths may be uninferred.
797  SmallVector<Operation *> opsToErase;
798  auto connect = [&](Value src, Value dest,
799  ImplicitLocOpBuilder &builder) -> LogicalResult {
800  // Strip away noop unrealized_conversion_cast's, used as placeholders.
801  // In the future, these should be created/managed as part of creating WP's.
802  if (auto op = getNoopCast(dest)) {
803  dest = op.getOperand(0);
804  opsToErase.push_back(op);
805  std::swap(src, dest);
806  } else if (auto op = getNoopCast(src)) {
807  src = op.getOperand(0);
808  opsToErase.push_back(op);
809  }
810 
811  if (foldFlow(dest) == Flow::Source)
812  std::swap(src, dest);
813 
814  // Figure out where to insert operations.
815  auto *insertBlock = findInsertionBlock(src, dest);
816  if (!insertBlock)
817  return emitError(src.getLoc())
818  .append("This value is involved with a Wiring Problem where the "
819  "destination is in the same module but neither dominates the "
820  "other, which is not supported.")
821  .attachNote(dest.getLoc())
822  .append("The destination is here.");
823 
824  // Insert at end, past invalidation in same block.
825  builder.setInsertionPointToEnd(insertBlock);
826 
827  // Create RefSend/RefResolve if necessary.
828  if (type_isa<RefType>(dest.getType()) != type_isa<RefType>(src.getType())) {
829  if (type_isa<RefType>(dest.getType()))
830  src = builder.create<RefSendOp>(src);
831  else
832  src = builder.create<RefResolveOp>(src);
833  }
834 
835  // If the sink is a wire with no users, then convert this to a node.
836  // This is done to convert the undriven wires created for GCView's
837  // into the NodeOp's they're required to be in GrandCentral.cpp.
838  if (auto destOp = dyn_cast_or_null<WireOp>(dest.getDefiningOp());
839  destOp && dest.getUses().empty()) {
840  // Only perform this if the type is suitable (passive).
841  if (auto baseType = dyn_cast<FIRRTLBaseType>(src.getType());
842  baseType && baseType.isPassive()) {
843  // Note that the wire is replaced with the source type
844  // regardless, continue this behavior.
845  builder.create<NodeOp>(src, destOp.getName())
846  .setAnnotationsAttr(destOp.getAnnotations());
847  opsToErase.push_back(destOp);
848  return success();
849  }
850  }
851 
852  // Otherwise, just connect to the source.
853  emitConnect(builder, dest, src);
854 
855  return success();
856  };
857 
858  auto &instanceGraph = state.instancePathCache.instanceGraph;
859  auto *context = state.circuit.getContext();
860 
861  // Examine all discovered Wiring Problems to determine modifications that need
862  // to be made per-module.
863  LLVM_DEBUG({ llvm::dbgs() << "Analyzing wiring problems:\n"; });
864  DenseMap<FModuleLike, ModuleModifications> moduleModifications;
865  DenseSet<Value> visitedSinks;
866  for (auto e : llvm::enumerate(state.wiringProblems)) {
867  auto index = e.index();
868  auto problem = e.value();
869  // This is a unique index that is assigned to this specific wiring problem
870  // and is used as a key during wiring to know which Values (ports, sources,
871  // or sinks) should be connected.
872  auto source = problem.source;
873  auto sink = problem.sink;
874 
875  // Check that no WiringProblems are trying to use the same sink. This
876  // should never happen.
877  if (!visitedSinks.insert(sink).second) {
878  auto diag = mlir::emitError(source.getLoc())
879  << "This sink is involved with a Wiring Problem which is "
880  "targeted by a source used by another Wiring Problem. "
881  "(This is both illegal and should be impossible.)";
882  diag.attachNote(source.getLoc()) << "The source is here";
883  return failure();
884  }
885  FModuleLike sourceModule = getModule(source);
886  FModuleLike sinkModule = getModule(sink);
887  if (isa<FExtModuleOp>(sourceModule) || isa<FExtModuleOp>(sinkModule)) {
888  auto diag = mlir::emitError(source.getLoc())
889  << "This source is involved with a Wiring Problem which "
890  "includes an External Module port and External Module "
891  "ports anre not supported.";
892  diag.attachNote(sink.getLoc()) << "The sink is here.";
893  return failure();
894  }
895 
896  LLVM_DEBUG({
897  llvm::dbgs() << " - index: " << index << "\n"
898  << " source:\n"
899  << " module: " << sourceModule.getModuleName() << "\n"
900  << " value: " << source << "\n"
901  << " sink:\n"
902  << " module: " << sinkModule.getModuleName() << "\n"
903  << " value: " << sink << "\n"
904  << " newNameHint: " << problem.newNameHint << "\n";
905  });
906 
907  // If the source and sink are in the same block, just wire them up.
908  if (sink.getParentBlock() == source.getParentBlock()) {
909  auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(context),
910  sink.getParentBlock());
911  if (failed(connect(source, sink, builder)))
912  return failure();
913  continue;
914  }
915  // If both are in the same module but not same block, U-turn.
916  // We may not be able to handle this, but that is checked below while
917  // connecting.
918  if (sourceModule == sinkModule) {
919  LLVM_DEBUG(llvm::dbgs()
920  << " LCA: " << sourceModule.getModuleName() << "\n");
921  moduleModifications[sourceModule].connectionMap[index] = source;
922  moduleModifications[sourceModule].uturns.push_back({index, sink});
923  continue;
924  }
925 
926  // Otherwise, get instance paths for source/sink, and compute LCA.
927  auto sourcePaths = state.instancePathCache.getAbsolutePaths(sourceModule);
928  auto sinkPaths = state.instancePathCache.getAbsolutePaths(sinkModule);
929 
930  if (sourcePaths.size() != 1 || sinkPaths.size() != 1) {
931  auto diag =
932  mlir::emitError(source.getLoc())
933  << "This source is involved with a Wiring Problem where the source "
934  "or the sink are multiply instantiated and this is not supported.";
935  diag.attachNote(sink.getLoc()) << "The sink is here.";
936  return failure();
937  }
938 
939  FModuleOp lca =
940  cast<FModuleOp>(instanceGraph.getTopLevelNode()->getModule());
941  auto sources = sourcePaths[0];
942  auto sinks = sinkPaths[0];
943  while (!sources.empty() && !sinks.empty()) {
944  if (sources.top() != sinks.top())
945  break;
946  auto newLCA = cast<InstanceOp>(*sources.top());
947  lca = cast<FModuleOp>(newLCA.getReferencedModule(instanceGraph));
948  sources = sources.dropFront();
949  sinks = sinks.dropFront();
950  }
951 
952  LLVM_DEBUG({
953  llvm::dbgs() << " LCA: " << lca.getModuleName() << "\n"
954  << " sourcePath: " << sourcePaths[0] << "\n"
955  << " sinkPaths: " << sinkPaths[0] << "\n";
956  });
957 
958  // Pre-populate the connectionMap of the module with the source and sink.
959  moduleModifications[sourceModule].connectionMap[index] = source;
960  moduleModifications[sinkModule].connectionMap[index] = sink;
961 
962  // Record port types that should be added to each module along the LCA path.
963  Type sourceType, sinkType;
964  auto useRefTypes =
965  !noRefTypePorts &&
966  problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer;
967  if (useRefTypes) {
968  // Use RefType ports if possible
969  RefType refType = TypeSwitch<Type, RefType>(source.getType())
970  .Case<FIRRTLBaseType>([](FIRRTLBaseType base) {
971  return RefType::get(base.getPassiveType());
972  })
973  .Case<RefType>([](RefType ref) { return ref; });
974  sourceType = refType;
975  sinkType = refType.getType();
976  } else {
977  // Use specified port types.
978  sourceType = source.getType();
979  sinkType = sink.getType();
980 
981  // Types must be connectable, which means FIRRTLType's.
982  auto sourceFType = type_dyn_cast<FIRRTLType>(sourceType);
983  auto sinkFType = type_dyn_cast<FIRRTLType>(sinkType);
984  if (!sourceFType)
985  return emitError(source.getLoc())
986  << "Wiring Problem source type \"" << sourceType
987  << "\" must be a FIRRTL type";
988  if (!sinkFType)
989  return emitError(sink.getLoc())
990  << "Wiring Problem sink type \"" << sinkType
991  << "\" must be a FIRRTL type";
992 
993  // Otherwise they must be identical or FIRRTL type-equivalent
994  // (connectable).
995  if (sourceFType != sinkFType &&
996  !areTypesEquivalent(sinkFType, sourceFType)) {
997  // Support tapping mixed alignment -> passive , emulate probe behavior.
998  if (auto sourceBaseType = dyn_cast<FIRRTLBaseType>(sourceFType);
999  problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer &&
1000  sourceBaseType &&
1001  areTypesEquivalent(sinkFType, sourceBaseType.getPassiveType())) {
1002  // Change "sourceType" to the passive version that's type-equivalent,
1003  // this will be used for wiring on the "up" side.
1004  // This relies on `emitConnect` supporting connecting to the passive
1005  // version from the original source.
1006  sourceType = sourceBaseType.getPassiveType();
1007  } else {
1008  auto diag = mlir::emitError(source.getLoc())
1009  << "Wiring Problem source type " << sourceType
1010  << " does not match sink type " << sinkType;
1011  diag.attachNote(sink.getLoc()) << "The sink is here.";
1012  return failure();
1013  }
1014  }
1015  }
1016  // If wiring using references, check that the sink value we connect to is
1017  // passive.
1018  if (auto sinkFType = type_dyn_cast<FIRRTLType>(sink.getType());
1019  sinkFType && type_isa<RefType>(sourceType) &&
1020  !getBaseType(sinkFType).isPassive())
1021  return emitError(sink.getLoc())
1022  << "Wiring Problem sink type \"" << sink.getType()
1023  << "\" must be passive (no flips) when using references";
1024 
1025  // Record module modifications related to adding ports to modules.
1026  auto addPorts = [&](igraph::InstancePath insts, Value val, Type tpe,
1027  Direction dir) -> LogicalResult {
1028  StringRef name, instName;
1029  for (auto instNode : llvm::reverse(insts)) {
1030  auto inst = cast<InstanceOp>(*instNode);
1031  auto mod = inst.getReferencedModule<FModuleOp>(instanceGraph);
1032  if (mod.isPublic()) {
1033  if (!allowAddingPortsOnPublic) {
1034  auto diag = emitError(
1035  mod.getLoc(), "cannot wire port through this public module");
1036  diag.attachNote(source.getLoc()) << "source here";
1037  diag.attachNote(sink.getLoc()) << "sink here";
1038  return diag;
1039  }
1040  ++numPublicPortsWired;
1041  }
1042  if (name.empty()) {
1043  if (problem.newNameHint.empty())
1044  name = state.getNamespace(mod).newName(
1045  getFieldName(
1046  getFieldRefFromValue(val, /*lookThroughCasts=*/true),
1047  /*nameSafe=*/true)
1048  .first +
1049  "__bore");
1050  else
1051  name = state.getNamespace(mod).newName(problem.newNameHint);
1052  } else {
1053  assert(!instName.empty());
1054  name = state.getNamespace(mod).newName(instName + "_" + name);
1055  }
1056  moduleModifications[mod].portsToAdd.push_back(
1057  {index, {StringAttr::get(context, name), tpe, dir}});
1058  instName = inst.getInstanceName();
1059  }
1060  return success();
1061  };
1062 
1063  // Record the addition of ports.
1064  if (failed(addPorts(sources, source, sourceType, Direction::Out)) ||
1065  failed(addPorts(sinks, sink, sinkType, Direction::In)))
1066  return failure();
1067  }
1068 
1069  // Iterate over modules from leaves to roots, applying ModuleModifications to
1070  // each module.
1071  LLVM_DEBUG({ llvm::dbgs() << "Updating modules:\n"; });
1072  for (auto *op : llvm::post_order(instanceGraph.getTopLevelNode())) {
1073  auto fmodule = dyn_cast<FModuleOp>(*op->getModule());
1074  // Skip external modules and modules that have no modifications.
1075  if (!fmodule || !moduleModifications.count(fmodule))
1076  continue;
1077 
1078  auto modifications = moduleModifications[fmodule];
1079  LLVM_DEBUG({
1080  llvm::dbgs() << " - module: " << fmodule.getModuleName() << "\n";
1081  llvm::dbgs() << " ports:\n";
1082  for (auto [index, port] : modifications.portsToAdd) {
1083  llvm::dbgs() << " - name: " << port.getName() << "\n"
1084  << " id: " << index << "\n"
1085  << " type: " << port.type << "\n"
1086  << " direction: "
1087  << (port.direction == Direction::In ? "in" : "out")
1088  << "\n";
1089  }
1090  });
1091 
1092  // Add ports to the module after all other existing ports.
1093  SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1094  SmallVector<unsigned> problemIndices;
1095  for (auto [problemIdx, portInfo] : modifications.portsToAdd) {
1096  // Create the port.
1097  newPorts.push_back({fmodule.getNumPorts(), portInfo});
1098  problemIndices.push_back(problemIdx);
1099  }
1100  auto originalNumPorts = fmodule.getNumPorts();
1101  auto portIdx = fmodule.getNumPorts();
1102  fmodule.insertPorts(newPorts);
1103 
1104  auto builder = ImplicitLocOpBuilder::atBlockBegin(UnknownLoc::get(context),
1105  fmodule.getBodyBlock());
1106 
1107  // Connect each port to the value stored in the connectionMap for this
1108  // wiring problem index.
1109  for (auto [problemIdx, portPair] : llvm::zip(problemIndices, newPorts)) {
1110  Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1111  assert(src && "there did not exist a driver for the port");
1112  Value dest = fmodule.getArgument(portIdx++);
1113  if (failed(connect(src, dest, builder)))
1114  return failure();
1115  }
1116 
1117  // If a U-turn exists, this is an LCA and we need a U-turn connection. These
1118  // are the last connections made for this module.
1119  for (auto [problemIdx, dest] : moduleModifications[fmodule].uturns) {
1120  Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1121  assert(src && "there did not exist a connection for the u-turn");
1122  if (failed(connect(src, dest, builder)))
1123  return failure();
1124  }
1125 
1126  // Update the connectionMap of all modules for which we created a port.
1127  for (auto *inst : instanceGraph.lookup(fmodule)->uses()) {
1128  InstanceOp useInst = cast<InstanceOp>(inst->getInstance());
1129  auto enclosingModule = useInst->getParentOfType<FModuleOp>();
1130  auto clonedInst = useInst.cloneAndInsertPorts(newPorts);
1131  state.instancePathCache.replaceInstance(useInst, clonedInst);
1132  // When RAUW-ing, ignore the new ports that we added when replacing (they
1133  // cannot have uses).
1134  useInst->replaceAllUsesWith(
1135  clonedInst.getResults().drop_back(newPorts.size()));
1136  useInst->erase();
1137  // Record information in the moduleModifications strucutre for the module
1138  // _where this is instantiated_. This is done so that when that module is
1139  // visited later, there will be information available for it to find ports
1140  // it needs to wire up. If there is already an existing connection, then
1141  // this is a U-turn.
1142  for (auto [newPortIdx, problemIdx] : llvm::enumerate(problemIndices)) {
1143  auto &modifications = moduleModifications[enclosingModule];
1144  auto newPort = clonedInst.getResult(newPortIdx + originalNumPorts);
1145  if (modifications.connectionMap.count(problemIdx)) {
1146  modifications.uturns.push_back({problemIdx, newPort});
1147  continue;
1148  }
1149  modifications.connectionMap[problemIdx] = newPort;
1150  }
1151  }
1152  }
1153 
1154  // Delete unused WireOps created by producers of WiringProblems.
1155  for (auto *op : opsToErase)
1156  op->erase();
1157 
1158  return success();
1159 }
1160 
1161 // This is the main entrypoint for the lowering pass.
1162 void LowerAnnotationsPass::runOnOperation() {
1163  CircuitOp circuit = getOperation();
1164  SymbolTable modules(circuit);
1165 
1166  LLVM_DEBUG(debugPassHeader(this) << "\n");
1167 
1168  // Grab the annotations from a non-standard attribute called "rawAnnotations".
1169  // This is a temporary location for all annotations that are earmarked for
1170  // processing by this pass as we migrate annotations from being handled by
1171  // FIRAnnotations/FIRParser into this pass. While we do this, this pass is
1172  // not supposed to touch _other_ annotations to enable this pass to be run
1173  // after FIRAnnotations/FIRParser.
1174  auto annotations = circuit->getAttrOfType<ArrayAttr>(rawAnnotations);
1175  if (!annotations)
1176  return;
1177  circuit->removeAttr(rawAnnotations);
1178 
1179  // Populate the worklist in reverse order. This has the effect of causing
1180  // annotations to be processed in the order in which they appear in the
1181  // original JSON.
1182  for (auto anno : llvm::reverse(annotations.getValue()))
1183  worklistAttrs.push_back(cast<DictionaryAttr>(anno));
1184 
1185  size_t numFailures = 0;
1186  size_t numAdded = 0;
1187  auto addToWorklist = [&](DictionaryAttr anno) {
1188  ++numAdded;
1189  worklistAttrs.push_back(anno);
1190  };
1191  InstancePathCache instancePathCache(getAnalysis<InstanceGraph>());
1192  ApplyState state{circuit, modules, addToWorklist, instancePathCache,
1193  noRefTypePorts};
1194  LLVM_DEBUG(llvm::dbgs() << "Processing annotations:\n");
1195  while (!worklistAttrs.empty()) {
1196  auto attr = worklistAttrs.pop_back_val();
1197  if (applyAnnotation(attr, state).failed())
1198  ++numFailures;
1199  }
1200 
1201  if (failed(legacyToWiringProblems(state)))
1202  ++numFailures;
1203 
1204  if (failed(solveWiringProblems(state)))
1205  ++numFailures;
1206 
1207  // Update statistics
1208  numRawAnnotations += annotations.size();
1209  numAddedAnnos += numAdded;
1210  numAnnos += numAdded + annotations.size();
1211  numReusedHierPathOps += state.numReusedHierPaths;
1212 
1213  if (numFailures)
1214  signalPassFailure();
1215 }
1216 
1217 /// This is the pass constructor.
1219  bool ignoreAnnotationUnknown, bool ignoreAnnotationClassless,
1220  bool noRefTypePorts, bool allowAddingPortsOnPublic) {
1221  auto pass = std::make_unique<LowerAnnotationsPass>();
1222  pass->ignoreAnnotationUnknown = ignoreAnnotationUnknown;
1223  pass->ignoreAnnotationClassless = ignoreAnnotationClassless;
1224  pass->noRefTypePorts = noRefTypePorts;
1225  pass->allowAddingPortsOnPublic = allowAddingPortsOnPublic;
1226  return pass;
1227 }
assert(baseType &&"element must be base type")
static LogicalResult applyOutputDirAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static LogicalResult convertToExcludeFromFullResetAnnotation(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Convert from IgnoreFullAsyncResetAnnotation to ExcludeFromFullResetAnnotation.
static LogicalResult applyModulePrefixAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static void addAnnotation(AnnoTarget ref, unsigned fieldIdx, ArrayRef< NamedAttribute > anno)
Apply a new annotation to a resolved target.
static ArrayAttr replaceArrayAttrElement(ArrayAttr array, size_t elem, Attribute newVal)
Update an ArrayAttribute by replacing one entry.
static LogicalResult convertToFullResetAnnotation(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Convert from FullAsyncResetAnnotation to FullResetAnnotation.
static const AnnoRecord * getAnnotationHandler(StringRef annoStr, bool ignoreAnnotationUnknown)
Lookup a record for a given annotation class.
static std::optional< AnnoPathValue > stdResolveImpl(StringRef rawPath, ApplyState &state)
Implementation of standard resolution.
static ArrayAttr appendArrayAttr(ArrayAttr array, Attribute a)
Construct the annotation array with a new thing appended.
static LogicalResult applyDUTAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
LogicalResult drop(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Just drop the annotation.
static LogicalResult applyLoadMemoryAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Update a memory op with attributes about memory file loading.
static std::optional< Convention > parseConvention(llvm::StringRef str)
static std::optional< AnnoPathValue > noResolve(DictionaryAttr anno, ApplyState &state)
Always resolve to the circuit, ignoring the annotation.
static ArrayAttr getAnnotationsFrom(Operation *op)
Get annotations or an empty set of annotations.
static LogicalResult applyConventionAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static LogicalResult applyAttributeAnnotation(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static FlatSymbolRefAttr scatterNonLocalPath(const AnnoPathValue &target, ApplyState &state)
Scatter breadcrumb annotations corresponding to non-local annotations along the instance path.
static FlatSymbolRefAttr buildNLA(const AnnoPathValue &target, ApplyState &state)
Make an anchor for a non-local annotation.
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Definition: Namespace.h:85
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)
Definition: support.py:39
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
LogicalResult applyOMIR(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Main entry point to handle scattering of an OMIRAnnotation.
Definition: EmitOMIR.cpp:498
constexpr const char * excludeFromFullResetAnnoClass
Annotation that marks a module as not belonging to any reset domain.
constexpr const char * injectDUTHierarchyAnnoClass
constexpr const char * extractCoverageAnnoClass
constexpr const char * excludeMemToRegAnnoClass
constexpr const char * elaborationArtefactsDirectoryAnnoClass
StringRef getAnnotationAttrName()
Return the name of the attribute used for annotations on FIRRTL ops.
Direction
This represents the direction of a single port.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
Definition: FIRRTLUtils.h:222
constexpr const char * extractGrandCentralClass
FieldRef getFieldRefFromValue(Value value, bool lookThroughCasts=false)
Get the FieldRef from a value.
std::optional< AnnoPathValue > stdResolve(DictionaryAttr anno, ApplyState &state)
===-------------------------------------------------------------------—===// Standard Utility Resolve...
constexpr const char * fullResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
constexpr const char * sitestBlackBoxAnnoClass
constexpr const char * convertMemToRegOfVecAnnoClass
constexpr const char * dontObfuscateModuleAnnoClass
std::unique_ptr< mlir::Pass > createLowerFIRRTLAnnotationsPass(bool ignoreUnhandledAnnotations=false, bool ignoreClasslessAnnotations=false, bool noRefTypePorts=false, bool allowAddingPortsOnPublic=false)
This is the pass constructor.
constexpr const char * metadataDirectoryAttrName
constexpr const char * extractBlackBoxAnnoClass
constexpr const char * fullAsyncResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
constexpr const char * testBenchDirAnnoClass
constexpr const char * sitestTestHarnessBlackBoxAnnoClass
constexpr const char * outputDirAnnoClass
constexpr const char * referenceKeyPortClass
constexpr const char * augmentedGroundTypeClass
constexpr const char * traceAnnoClass
constexpr const char * mustDedupAnnoClass
Flow foldFlow(Value val, Flow accumulatedFlow=Flow::Source)
Compute the flow for a Value, val, as determined by the FIRRTL specification.
Definition: FIRRTLOps.cpp:212
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)
Definition: FIRRTLOps.cpp:301
constexpr const char * dataTapsClass
constexpr const char * conventionAnnoClass
constexpr const char * dedupGroupAnnoClass
constexpr const char * omirAnnoClass
constexpr const char * omirTrackerAnnoClass
constexpr const char * viewAnnoClass
constexpr const char * serializedViewAnnoClass
constexpr const char * enumDefAnnoClass
constexpr const char * enumVecAnnoClass
constexpr const char * omirFileAnnoClass
std::string canonicalizeTarget(StringRef target)
Return an input target string in canonical form.
constexpr const char * wiringSourceAnnoClass
LogicalResult applyGCTMemTaps(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * traceNameAnnoClass
constexpr const char * enumComponentAnnoClass
constexpr const char * dataTapsBlackboxClass
constexpr const char * extractAssertAnnoClass
constexpr const char * memTapBlackboxClass
static LogicalResult applyWithoutTarget(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
An applier which puts the annotation on the target and drops the 'target' field from the annotation.
constexpr const char * testHarnessPathAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * addSeqMemPortAnnoClass
LogicalResult applyWiring(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Consume SourceAnnotation and SinkAnnotation, storing into state.
LogicalResult applyTraceName(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Expand a TraceNameAnnotation (which has don't touch semantics) into a TraceAnnotation (which does NOT...
constexpr const char * blackBoxPathAnnoClass
constexpr const char * internalKeyPortClass
constexpr const char * addSeqMemPortsFileAnnoClass
constexpr const char * retimeModulesFileAnnoClass
constexpr const char * prefixModulesAnnoClass
constexpr const char * retimeModuleAnnoClass
constexpr const char * runFIRRTLTransformAnnoClass
constexpr const char * companionAnnoClass
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
constexpr const char * referenceKeySourceClass
constexpr const char * literalKeyClass
constexpr const char * blackBoxTargetDirAnnoClass
constexpr const char * ignoreFullAsyncResetAnnoClass
Annotation that marks a module as not belonging to any reset domain.
LogicalResult registerAnnotationRecord(StringRef annoClass, AnnoRecord annoRecord, const std::function< void(llvm::Twine)> &errorHandler={})
Register external annotation records.
constexpr const char * modulePrefixAnnoClass
LogicalResult applyGCTDataTaps(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * blackBoxInlineAnnoClass
StringRef getPortAnnotationAttrName()
Return the name of the attribute used for port annotations on FIRRTL ops.
constexpr const char * decodeTableAnnotation
constexpr const char * extractClockGatesAnnoClass
constexpr const char * inlineAnnoClass
constexpr const char * memTapPortClass
LogicalResult applyGCTView(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * flattenAnnoClass
constexpr const char * prefixInterfacesAnnoClass
std::optional< TokenAnnoTarget > tokenizePath(StringRef origTarget)
Parse a FIRRTL annotation path into its constituent parts.
LogicalResult applyWithoutTargetImpl(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state, bool allowNonLocal)
===-------------------------------------------------------------------—===// Standard Utility Applier...
constexpr const char * extractAssumeAnnoClass
constexpr const char * grandCentralHierarchyFileAnnoClass
std::optional< AnnoPathValue > tryResolve(DictionaryAttr anno, ApplyState &state)
Resolves with target, if it exists. If not, resolves to the circuit.
constexpr const char * dontTouchAnnoClass
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
Definition: FIRRTLUtils.cpp:25
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.
Definition: DebugAnalysis.h:21
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
Definition: Debug.cpp:31
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.
hw::InnerSymbolNamespace & getNamespace(FModuleLike module)
DenseMap< StringAttr, LegacyWiringProblem > legacyWiringProblems
SmallVector< WiringProblem > wiringProblems
InstancePathCache & instancePathCache
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.