CIRCT  19.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 
15 #include "PassDetails.h"
16 
30 #include "circt/Dialect/HW/HWOps.h"
32 #include "circt/Support/Debug.h"
33 #include "mlir/IR/Diagnostics.h"
34 #include "llvm/ADT/APSInt.h"
35 #include "llvm/ADT/PostOrderIterator.h"
36 #include "llvm/ADT/StringExtras.h"
37 #include "llvm/Support/Debug.h"
38 
39 #define DEBUG_TYPE "lower-annos"
40 
41 using namespace circt;
42 using namespace firrtl;
43 using namespace chirrtl;
44 
45 /// Get annotations or an empty set of annotations.
46 static ArrayAttr getAnnotationsFrom(Operation *op) {
47  if (auto annots = op->getAttrOfType<ArrayAttr>(getAnnotationAttrName()))
48  return annots;
49  return ArrayAttr::get(op->getContext(), {});
50 }
51 
52 /// Construct the annotation array with a new thing appended.
53 static ArrayAttr appendArrayAttr(ArrayAttr array, Attribute a) {
54  if (!array)
55  return ArrayAttr::get(a.getContext(), ArrayRef<Attribute>{a});
56  SmallVector<Attribute> old(array.begin(), array.end());
57  old.push_back(a);
58  return ArrayAttr::get(a.getContext(), old);
59 }
60 
61 /// Update an ArrayAttribute by replacing one entry.
62 static ArrayAttr replaceArrayAttrElement(ArrayAttr array, size_t elem,
63  Attribute newVal) {
64  SmallVector<Attribute> old(array.begin(), array.end());
65  old[elem] = newVal;
66  return ArrayAttr::get(array.getContext(), old);
67 }
68 
69 /// Apply a new annotation to a resolved target. This handles ports,
70 /// aggregates, modules, wires, etc.
71 static void addAnnotation(AnnoTarget ref, unsigned fieldIdx,
72  ArrayRef<NamedAttribute> anno) {
73  auto *context = ref.getOp()->getContext();
74  DictionaryAttr annotation;
75  if (fieldIdx) {
76  SmallVector<NamedAttribute> annoField(anno.begin(), anno.end());
77  annoField.emplace_back(
78  StringAttr::get(context, "circt.fieldID"),
79  IntegerAttr::get(IntegerType::get(context, 32, IntegerType::Signless),
80  fieldIdx));
81  annotation = DictionaryAttr::get(context, annoField);
82  } else {
83  annotation = DictionaryAttr::get(context, anno);
84  }
85 
86  if (ref.isa<OpAnnoTarget>()) {
87  auto newAnno = appendArrayAttr(getAnnotationsFrom(ref.getOp()), annotation);
88  ref.getOp()->setAttr(getAnnotationAttrName(), newAnno);
89  return;
90  }
91 
92  auto portRef = ref.cast<PortAnnoTarget>();
93  auto portAnnoRaw = ref.getOp()->getAttr(getPortAnnotationAttrName());
94  ArrayAttr portAnno = portAnnoRaw.dyn_cast_or_null<ArrayAttr>();
95  if (!portAnno || portAnno.size() != getNumPorts(ref.getOp())) {
96  SmallVector<Attribute> emptyPortAttr(
97  getNumPorts(ref.getOp()),
98  ArrayAttr::get(ref.getOp()->getContext(), {}));
99  portAnno = ArrayAttr::get(ref.getOp()->getContext(), emptyPortAttr);
100  }
101  portAnno = replaceArrayAttrElement(
102  portAnno, portRef.getPortNo(),
103  appendArrayAttr(dyn_cast<ArrayAttr>(portAnno[portRef.getPortNo()]),
104  annotation));
105  ref.getOp()->setAttr("portAnnotations", portAnno);
106 }
107 
108 /// Make an anchor for a non-local annotation. Use the expanded path to build
109 /// the module and name list in the anchor.
110 static FlatSymbolRefAttr buildNLA(const AnnoPathValue &target,
111  ApplyState &state) {
112  OpBuilder b(state.circuit.getBodyRegion());
113  SmallVector<Attribute> insts;
114  for (auto inst : target.instances) {
115  insts.push_back(OpAnnoTarget(inst).getNLAReference(
116  state.getNamespace(inst->getParentOfType<FModuleLike>())));
117  }
118 
119  insts.push_back(
120  FlatSymbolRefAttr::get(target.ref.getModule().getModuleNameAttr()));
121 
122  auto instAttr = ArrayAttr::get(state.circuit.getContext(), insts);
123  return state.hierPathCache.getRefFor(instAttr);
124 }
125 
126 /// Scatter breadcrumb annotations corresponding to non-local annotations
127 /// along the instance path. Returns symbol name used to anchor annotations to
128 /// path.
129 // FIXME: uniq annotation chain links
130 static FlatSymbolRefAttr scatterNonLocalPath(const AnnoPathValue &target,
131  ApplyState &state) {
132 
133  FlatSymbolRefAttr sym = buildNLA(target, state);
134  return sym;
135 }
136 
137 //===----------------------------------------------------------------------===//
138 // Standard Utility Resolvers
139 //===----------------------------------------------------------------------===//
140 
141 /// Always resolve to the circuit, ignoring the annotation.
142 static std::optional<AnnoPathValue> noResolve(DictionaryAttr anno,
143  ApplyState &state) {
144  return AnnoPathValue(state.circuit);
145 }
146 
147 /// Implementation of standard resolution. First parses the target path, then
148 /// resolves it.
149 static std::optional<AnnoPathValue> stdResolveImpl(StringRef rawPath,
150  ApplyState &state) {
151  auto pathStr = canonicalizeTarget(rawPath);
152  StringRef path{pathStr};
153 
154  auto tokens = tokenizePath(path);
155  if (!tokens) {
156  mlir::emitError(state.circuit.getLoc())
157  << "Cannot tokenize annotation path " << rawPath;
158  return {};
159  }
160 
161  return resolveEntities(*tokens, state.circuit, state.symTbl,
162  state.targetCaches);
163 }
164 
165 /// (SFC) FIRRTL SingleTargetAnnotation resolver. Uses the 'target' field of
166 /// the annotation with standard parsing to resolve the path. This requires
167 /// 'target' to exist and be normalized (per docs/FIRRTLAnnotations.md).
168 std::optional<AnnoPathValue> circt::firrtl::stdResolve(DictionaryAttr anno,
169  ApplyState &state) {
170  auto target = anno.getNamed("target");
171  if (!target) {
172  mlir::emitError(state.circuit.getLoc())
173  << "No target field in annotation " << anno;
174  return {};
175  }
176  if (!isa<StringAttr>(target->getValue())) {
177  mlir::emitError(state.circuit.getLoc())
178  << "Target field in annotation doesn't contain string " << anno;
179  return {};
180  }
181  return stdResolveImpl(cast<StringAttr>(target->getValue()).getValue(), state);
182 }
183 
184 /// Resolves with target, if it exists. If not, resolves to the circuit.
185 std::optional<AnnoPathValue> circt::firrtl::tryResolve(DictionaryAttr anno,
186  ApplyState &state) {
187  auto target = anno.getNamed("target");
188  if (target)
189  return stdResolveImpl(cast<StringAttr>(target->getValue()).getValue(),
190  state);
191  return AnnoPathValue(state.circuit);
192 }
193 
194 //===----------------------------------------------------------------------===//
195 // Standard Utility Appliers
196 //===----------------------------------------------------------------------===//
197 
198 /// An applier which puts the annotation on the target and drops the 'target'
199 /// field from the annotation. Optionally handles non-local annotations.
201 
202  DictionaryAttr anno,
203  ApplyState &state,
204  bool allowNonLocal) {
205  if (!allowNonLocal && !target.isLocal()) {
206  Annotation annotation(anno);
207  auto diag = mlir::emitError(target.ref.getOp()->getLoc())
208  << "is targeted by a non-local annotation \""
209  << annotation.getClass() << "\" with target "
210  << annotation.getMember("target")
211  << ", but this annotation cannot be non-local";
212  diag.attachNote() << "see current annotation: " << anno << "\n";
213  return failure();
214  }
215  SmallVector<NamedAttribute> newAnnoAttrs;
216  for (auto &na : anno) {
217  if (na.getName().getValue() != "target") {
218  newAnnoAttrs.push_back(na);
219  } else if (!target.isLocal()) {
220  auto sym = scatterNonLocalPath(target, state);
221  newAnnoAttrs.push_back(
222  {StringAttr::get(anno.getContext(), "circt.nonlocal"), sym});
223  }
224  }
225  addAnnotation(target.ref, target.fieldIdx, newAnnoAttrs);
226  return success();
227 }
228 
229 /// Just drop the annotation. This is intended for Annotations which are known,
230 /// but can be safely ignored.
231 LogicalResult drop(const AnnoPathValue &target, DictionaryAttr anno,
232  ApplyState &state) {
233  return success();
234 }
235 //===----------------------------------------------------------------------===//
236 // Customized Appliers
237 //===----------------------------------------------------------------------===//
238 
239 static LogicalResult applyDUTAnno(const AnnoPathValue &target,
240  DictionaryAttr anno, ApplyState &state) {
241  auto *op = target.ref.getOp();
242  auto loc = op->getLoc();
243 
244  if (!target.isLocal())
245  return mlir::emitError(loc) << "must be local";
246 
247  if (!target.ref.isa<OpAnnoTarget>() || !isa<FModuleOp>(op))
248  return mlir::emitError(loc) << "can only target to a module";
249 
250  auto moduleOp = cast<FModuleOp>(op);
251 
252  // DUT has public visibility.
253  moduleOp.setPublic();
254  SmallVector<NamedAttribute> newAnnoAttrs;
255  for (auto &na : anno)
256  if (na.getName().getValue() != "target")
257  newAnnoAttrs.push_back(na);
258  addAnnotation(target.ref, target.fieldIdx, newAnnoAttrs);
259  return success();
260 }
261 
262 // Like symbolizeConvention, but disallows the internal convention.
263 static std::optional<Convention> parseConvention(llvm::StringRef str) {
264  return ::llvm::StringSwitch<::std::optional<Convention>>(str)
265  .Case("scalarized", Convention::Scalarized)
266  .Default(std::nullopt);
267 }
268 
269 static LogicalResult applyConventionAnno(const AnnoPathValue &target,
270  DictionaryAttr anno,
271  ApplyState &state) {
272  auto *op = target.ref.getOp();
273  auto loc = op->getLoc();
274  auto error = [&]() {
275  auto diag = mlir::emitError(loc);
276  diag << "circuit.ConventionAnnotation ";
277  return diag;
278  };
279 
280  auto opTarget = target.ref.dyn_cast<OpAnnoTarget>();
281  if (!opTarget)
282  return error() << "must target a module object";
283 
284  if (!target.isLocal())
285  return error() << "must be local";
286 
287  auto conventionStrAttr =
288  tryGetAs<StringAttr>(anno, anno, "convention", loc, conventionAnnoClass);
289  if (!conventionStrAttr)
290  return failure();
291 
292  auto conventionStr = conventionStrAttr.getValue();
293  auto conventionOpt = parseConvention(conventionStr);
294  if (!conventionOpt)
295  return error() << "unknown convention " << conventionStr;
296 
297  auto convention = *conventionOpt;
298 
299  if (auto moduleOp = dyn_cast<FModuleOp>(op)) {
300  moduleOp.setConvention(convention);
301  return success();
302  }
303 
304  if (auto extModuleOp = dyn_cast<FExtModuleOp>(op)) {
305  extModuleOp.setConvention(convention);
306  return success();
307  }
308 
309  return error() << "can only target to a module or extmodule";
310 }
311 
312 static LogicalResult applyAttributeAnnotation(const AnnoPathValue &target,
313  DictionaryAttr anno,
314  ApplyState &state) {
315  auto *op = target.ref.getOp();
316 
317  auto error = [&]() {
318  auto diag = mlir::emitError(op->getLoc());
319  diag << anno.getAs<StringAttr>("class").getValue() << " ";
320  return diag;
321  };
322 
323  if (!target.ref.isa<OpAnnoTarget>())
324  return error()
325  << "must target an operation. Currently ports are not supported";
326 
327  if (!target.isLocal())
328  return error() << "must be local";
329 
330  if (!isa<FModuleOp, WireOp, NodeOp, RegOp, RegResetOp>(op))
331  return error()
332  << "unhandled operation. The target must be a module, wire, node or "
333  "register";
334 
335  auto name = anno.getAs<StringAttr>("description");
336  auto svAttr = sv::SVAttributeAttr::get(name.getContext(), name);
337  sv::addSVAttributes(op, {svAttr});
338  return success();
339 }
340 
341 /// Update a memory op with attributes about memory file loading.
342 template <bool isInline>
343 static LogicalResult applyLoadMemoryAnno(const AnnoPathValue &target,
344  DictionaryAttr anno,
345  ApplyState &state) {
346  if (!target.isLocal()) {
347  mlir::emitError(state.circuit.getLoc())
348  << "has a " << anno.get("class")
349  << " annotation which is non-local, but this annotation is not allowed "
350  "to be non-local";
351  return failure();
352  }
353 
354  auto *op = target.ref.getOp();
355 
356  if (!target.isOpOfType<MemOp, CombMemOp, SeqMemOp>()) {
357  mlir::emitError(op->getLoc())
358  << "can only apply a load memory annotation to a memory";
359  return failure();
360  }
361 
362  // The two annotations have different case usage in "filename".
363  StringAttr filename = tryGetAs<StringAttr>(
364  anno, anno, isInline ? "filename" : "fileName", op->getLoc(),
365  anno.getAs<StringAttr>("class").getValue());
366  if (!filename)
367  return failure();
368 
369  auto hexOrBinary =
370  tryGetAs<StringAttr>(anno, anno, "hexOrBinary", op->getLoc(),
371  anno.getAs<StringAttr>("class").getValue());
372  if (!hexOrBinary)
373  return failure();
374 
375  auto hexOrBinaryValue = hexOrBinary.getValue();
376  if (hexOrBinaryValue != "h" && hexOrBinaryValue != "b") {
377  auto diag = mlir::emitError(op->getLoc())
378  << "has memory initialization annotation with invalid format, "
379  "'hexOrBinary' field must be either 'h' or 'b'";
380  diag.attachNote() << "the full annotation is: " << anno;
381  return failure();
382  }
383 
384  op->setAttr("init", MemoryInitAttr::get(op->getContext(), filename,
385  hexOrBinaryValue == "b", isInline));
386 
387  return success();
388 }
389 
390 //===----------------------------------------------------------------------===//
391 // Driving table
392 //===----------------------------------------------------------------------===//
393 
394 namespace circt::firrtl {
395 /// Resolution and application of a "firrtl.annotations.NoTargetAnnotation".
396 /// This should be used for any Annotation which does not apply to anything in
397 /// the FIRRTL Circuit, i.e., an Annotation which has no target. Historically,
398 /// NoTargetAnnotations were used to control the Scala FIRRTL Compiler (SFC) or
399 /// its passes, e.g., to set the output directory or to turn on a pass.
400 /// Examples of these in the SFC are "firrtl.options.TargetDirAnnotation" to set
401 /// the output directory or "firrtl.stage.RunFIRRTLTransformAnnotation" to
402 /// cause the SFC to schedule a specified pass. Instead of leaving these
403 /// floating or attaching them to the top-level MLIR module (which is a purer
404 /// interpretation of "no target"), we choose to attach them to the Circuit even
405 /// they do not "apply" to the Circuit. This gives later passes a common place,
406 /// the Circuit, to search for these control Annotations.
408  applyWithoutTarget<false, CircuitOp>};
409 
410 static llvm::StringMap<AnnoRecord> annotationRecords{{
411 
412  // Testing Annotation
413  {"circt.test", {stdResolve, applyWithoutTarget<true>}},
414  {"circt.testLocalOnly", {stdResolve, applyWithoutTarget<>}},
415  {"circt.testNT", {noResolve, applyWithoutTarget<>}},
416  {"circt.missing", {tryResolve, applyWithoutTarget<true>}},
417  // Grand Central Views/Interfaces Annotations
422  {companionAnnoClass, {stdResolve, applyWithoutTarget<>}},
423  {augmentedGroundTypeClass, {stdResolve, applyWithoutTarget<true>}},
424  // Grand Central Data Tap Annotations
426  {dataTapsBlackboxClass, {stdResolve, applyWithoutTarget<true>}},
427  {referenceKeySourceClass, {stdResolve, applyWithoutTarget<true>}},
428  {referenceKeyPortClass, {stdResolve, applyWithoutTarget<true>}},
429  {internalKeySourceClass, {stdResolve, applyWithoutTarget<true>}},
430  {internalKeyPortClass, {stdResolve, applyWithoutTarget<true>}},
431  {deletedKeyClass, {stdResolve, applyWithoutTarget<true>}},
432  {literalKeyClass, {stdResolve, applyWithoutTarget<true>}},
433  // Grand Central Mem Tap Annotations
435  {memTapSourceClass, {stdResolve, applyWithoutTarget<true>}},
436  {memTapPortClass, {stdResolve, applyWithoutTarget<true>}},
437  {memTapBlackboxClass, {stdResolve, applyWithoutTarget<true>}},
438  // OMIR Annotations
440  {omirTrackerAnnoClass, {stdResolve, applyWithoutTarget<true>}},
442  // Miscellaneous Annotations
445  {stdResolve, applyWithoutTarget<true, true, WireOp, NodeOp, RegOp,
446  RegResetOp, InstanceOp, MemOp, CombMemOp,
447  MemoryPortOp, SeqMemOp>}},
449  {stdResolve,
450  applyWithoutTarget<true, FModuleOp, FExtModuleOp, InstanceOp>}},
456  {stdResolve, applyWithoutTarget<true, MemOp, CombMemOp>}},
462  {stdResolve, applyWithoutTarget<true, FModuleOp, FExtModuleOp>}},
463  {flattenAnnoClass, {stdResolve, applyWithoutTarget<false, FModuleOp>}},
464  {inlineAnnoClass, {stdResolve, applyWithoutTarget<false, FModuleOp>}},
466  {stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
468  {stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
470  {stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
472  {stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
474  {stdResolve, applyWithoutTarget<false, FModuleOp>}},
476  {stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
480  {stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
496  {extractBlackBoxAnnoClass, {stdResolve, applyWithoutTarget<false>}},
497  {fullAsyncResetAnnoClass, {stdResolve, applyWithoutTarget<true>}},
499  {stdResolve, applyWithoutTarget<true, FModuleOp>}},
503  {traceAnnoClass, {stdResolve, applyWithoutTarget<true>}},
504  {loadMemoryFromFileAnnoClass, {stdResolve, applyLoadMemoryAnno<false>}},
506  {stdResolve, applyLoadMemoryAnno<true>}},
510 
511 LogicalResult
512 registerAnnotationRecord(StringRef annoClass, AnnoRecord annoRecord,
513  const std::function<void(llvm::Twine)> &errorHandler) {
514 
515  if (annotationRecords.insert({annoClass, annoRecord}).second)
516  return LogicalResult::success();
517  if (errorHandler)
518  errorHandler("annotation record '" + annoClass + "' is registered twice\n");
519  return LogicalResult::failure();
520 }
521 
522 } // namespace circt::firrtl
523 
524 /// Lookup a record for a given annotation class. Optionally, returns the
525 /// record for "circuit.missing" if the record doesn't exist.
526 static const AnnoRecord *getAnnotationHandler(StringRef annoStr,
527  bool ignoreAnnotationUnknown) {
528  auto ii = annotationRecords.find(annoStr);
529  if (ii != annotationRecords.end())
530  return &ii->second;
531  if (ignoreAnnotationUnknown)
532  return &annotationRecords.find("circt.missing")->second;
533  return nullptr;
534 }
535 
536 //===----------------------------------------------------------------------===//
537 // Pass Infrastructure
538 //===----------------------------------------------------------------------===//
539 
540 namespace {
541 struct LowerAnnotationsPass
542  : public LowerFIRRTLAnnotationsBase<LowerAnnotationsPass> {
543  void runOnOperation() override;
544  LogicalResult applyAnnotation(DictionaryAttr anno, ApplyState &state);
545  LogicalResult legacyToWiringProblems(ApplyState &state);
546  LogicalResult solveWiringProblems(ApplyState &state);
547 
548  using LowerFIRRTLAnnotationsBase::ignoreAnnotationClassless;
549  using LowerFIRRTLAnnotationsBase::ignoreAnnotationUnknown;
550  using LowerFIRRTLAnnotationsBase::noRefTypePorts;
551  SmallVector<DictionaryAttr> worklistAttrs;
552 };
553 } // end anonymous namespace
554 
555 LogicalResult LowerAnnotationsPass::applyAnnotation(DictionaryAttr anno,
556  ApplyState &state) {
557  LLVM_DEBUG(llvm::dbgs() << " - anno: " << anno << "\n";);
558 
559  // Lookup the class
560  StringRef annoClassVal;
561  if (auto annoClass = anno.getNamed("class"))
562  annoClassVal = cast<StringAttr>(annoClass->getValue()).getValue();
563  else if (ignoreAnnotationClassless)
564  annoClassVal = "circt.missing";
565  else
566  return mlir::emitError(state.circuit.getLoc())
567  << "Annotation without a class: " << anno;
568 
569  // See if we handle the class
570  auto *record = getAnnotationHandler(annoClassVal, false);
571  if (!record) {
572  ++numUnhandled;
573  if (!ignoreAnnotationUnknown)
574  return mlir::emitError(state.circuit.getLoc())
575  << "Unhandled annotation: " << anno;
576 
577  // Try again, requesting the fallback handler.
578  record = getAnnotationHandler(annoClassVal, ignoreAnnotationUnknown);
579  assert(record);
580  }
581 
582  // Try to apply the annotation
583  auto target = record->resolver(anno, state);
584  if (!target)
585  return mlir::emitError(state.circuit.getLoc())
586  << "Unable to resolve target of annotation: " << anno;
587  if (record->applier(*target, anno, state).failed())
588  return mlir::emitError(state.circuit.getLoc())
589  << "Unable to apply annotation: " << anno;
590  return success();
591 }
592 
593 /// Convert consumed SourceAnnotation and SinkAnnotation into WiringProblems,
594 /// using the pin attribute as newNameHint
595 LogicalResult LowerAnnotationsPass::legacyToWiringProblems(ApplyState &state) {
596  for (const auto &[name, problem] : state.legacyWiringProblems) {
597  if (!problem.source)
598  return mlir::emitError(state.circuit.getLoc())
599  << "Unable to resolve source for pin: " << name;
600 
601  if (problem.sinks.empty())
602  return mlir::emitError(state.circuit.getLoc())
603  << "Unable to resolve sink(s) for pin: " << name;
604 
605  for (const auto &sink : problem.sinks) {
606  state.wiringProblems.push_back(
607  {problem.source, sink, {}, WiringProblem::RefTypeUsage::Never});
608  }
609  }
610  return success();
611 }
612 
613 /// Modify the circuit to solve and apply all Wiring Problems in the circuit. A
614 /// Wiring Problem is a mapping from a source to a sink that can be connected
615 /// via a base Type or RefType as requested. This uses a two-step approach.
616 /// First, all Wiring Problems are analyzed to compute pending modifications to
617 /// modules. Second, modules are visited from leaves to roots to apply module
618 /// modifications. Module modifications include addings ports and connecting
619 /// things up.
620 LogicalResult LowerAnnotationsPass::solveWiringProblems(ApplyState &state) {
621  // Utility function to extract the defining module from a value which may be
622  // either a BlockArgument or an Operation result.
623  auto getModule = [](Value value) {
624  if (BlockArgument blockArg = dyn_cast<BlockArgument>(value))
625  return cast<FModuleLike>(blockArg.getParentBlock()->getParentOp());
626  return value.getDefiningOp()->getParentOfType<FModuleLike>();
627  };
628 
629  // Utility function to determine where to insert connection operations.
630  auto findInsertionBlock = [&getModule](Value src, Value dest) -> Block * {
631  // Check for easy case: both are in the same block.
632  if (src.getParentBlock() == dest.getParentBlock())
633  return src.getParentBlock();
634 
635  // If connecting across blocks, figure out where to connect.
636  (void)getModule;
637  assert(getModule(src) == getModule(dest));
638  // Helper to determine if 'a' is available at 'b's block.
639  auto safelyDoms = [&](Value a, Value b) {
640  if (isa<BlockArgument>(a))
641  return true;
642  if (isa<BlockArgument>(b))
643  return false;
644  // Handle cases where 'b' is in child op after 'a'.
645  auto *ancestor =
646  a.getParentBlock()->findAncestorOpInBlock(*b.getDefiningOp());
647  return ancestor && a.getDefiningOp()->isBeforeInBlock(ancestor);
648  };
649  if (safelyDoms(src, dest))
650  return dest.getParentBlock();
651  if (safelyDoms(dest, src))
652  return src.getParentBlock();
653  return {};
654  };
655 
656  auto getNoopCast = [](Value v) -> mlir::UnrealizedConversionCastOp {
657  auto op =
658  dyn_cast_or_null<mlir::UnrealizedConversionCastOp>(v.getDefiningOp());
659  if (op && op.getNumResults() == 1 && op.getNumOperands() == 1 &&
660  op.getResultTypes()[0] == op.getOperandTypes()[0])
661  return op;
662  return {};
663  };
664 
665  // Utility function to connect a destination to a source. Always use a
666  // ConnectOp as the widths may be uninferred.
667  SmallVector<Operation *> opsToErase;
668  auto connect = [&](Value src, Value dest,
669  ImplicitLocOpBuilder &builder) -> LogicalResult {
670  // Strip away noop unrealized_conversion_cast's, used as placeholders.
671  // In the future, these should be created/managed as part of creating WP's.
672  if (auto op = getNoopCast(dest)) {
673  dest = op.getOperand(0);
674  opsToErase.push_back(op);
675  std::swap(src, dest);
676  } else if (auto op = getNoopCast(src)) {
677  src = op.getOperand(0);
678  opsToErase.push_back(op);
679  }
680 
681  if (foldFlow(dest) == Flow::Source)
682  std::swap(src, dest);
683 
684  // Figure out where to insert operations.
685  auto *insertBlock = findInsertionBlock(src, dest);
686  if (!insertBlock)
687  return emitError(src.getLoc())
688  .append("This value is involved with a Wiring Problem where the "
689  "destination is in the same module but neither dominates the "
690  "other, which is not supported.")
691  .attachNote(dest.getLoc())
692  .append("The destination is here.");
693 
694  // Insert at end, past invalidation in same block.
695  builder.setInsertionPointToEnd(insertBlock);
696 
697  // Create RefSend/RefResolve if necessary.
698  if (type_isa<RefType>(dest.getType()) != type_isa<RefType>(src.getType())) {
699  if (type_isa<RefType>(dest.getType()))
700  src = builder.create<RefSendOp>(src);
701  else
702  src = builder.create<RefResolveOp>(src);
703  }
704 
705  // If the sink is a wire with no users, then convert this to a node.
706  auto destOp = dyn_cast_or_null<WireOp>(dest.getDefiningOp());
707  if (destOp && dest.getUses().empty()) {
708  builder.create<NodeOp>(src, destOp.getName())
709  .setAnnotationsAttr(destOp.getAnnotations());
710  opsToErase.push_back(destOp);
711  return success();
712  }
713 
714  // Otherwise, just connect to the source.
715  emitConnect(builder, dest, src);
716 
717  return success();
718  };
719 
720  auto &instanceGraph = state.instancePathCache.instanceGraph;
721  auto *context = state.circuit.getContext();
722 
723  // Examine all discovered Wiring Problems to determine modifications that need
724  // to be made per-module.
725  LLVM_DEBUG({ llvm::dbgs() << "Analyzing wiring problems:\n"; });
726  DenseMap<FModuleLike, ModuleModifications> moduleModifications;
727  DenseSet<Value> visitedSinks;
728  for (auto e : llvm::enumerate(state.wiringProblems)) {
729  auto index = e.index();
730  auto problem = e.value();
731  // This is a unique index that is assigned to this specific wiring problem
732  // and is used as a key during wiring to know which Values (ports, sources,
733  // or sinks) should be connected.
734  auto source = problem.source;
735  auto sink = problem.sink;
736 
737  // Check that no WiringProblems are trying to use the same sink. This
738  // should never happen.
739  if (!visitedSinks.insert(sink).second) {
740  auto diag = mlir::emitError(source.getLoc())
741  << "This sink is involved with a Wiring Problem which is "
742  "targeted by a source used by another Wiring Problem. "
743  "(This is both illegal and should be impossible.)";
744  diag.attachNote(source.getLoc()) << "The source is here";
745  return failure();
746  }
747  FModuleLike sourceModule = getModule(source);
748  FModuleLike sinkModule = getModule(sink);
749  if (isa<FExtModuleOp>(sourceModule) || isa<FExtModuleOp>(sinkModule)) {
750  auto diag = mlir::emitError(source.getLoc())
751  << "This source is involved with a Wiring Problem which "
752  "includes an External Module port and External Module "
753  "ports anre not supported.";
754  diag.attachNote(sink.getLoc()) << "The sink is here.";
755  return failure();
756  }
757 
758  LLVM_DEBUG({
759  llvm::dbgs() << " - index: " << index << "\n"
760  << " source:\n"
761  << " module: " << sourceModule.getModuleName() << "\n"
762  << " value: " << source << "\n"
763  << " sink:\n"
764  << " module: " << sinkModule.getModuleName() << "\n"
765  << " value: " << sink << "\n"
766  << " newNameHint: " << problem.newNameHint << "\n";
767  });
768 
769  // If the source and sink are in the same block, just wire them up.
770  if (sink.getParentBlock() == source.getParentBlock()) {
771  auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(context),
772  sink.getParentBlock());
773  if (failed(connect(source, sink, builder)))
774  return failure();
775  continue;
776  }
777  // If both are in the same module but not same block, U-turn.
778  // We may not be able to handle this, but that is checked below while
779  // connecting.
780  if (sourceModule == sinkModule) {
781  LLVM_DEBUG(llvm::dbgs()
782  << " LCA: " << sourceModule.getModuleName() << "\n");
783  moduleModifications[sourceModule].connectionMap[index] = source;
784  moduleModifications[sourceModule].uturns.push_back({index, sink});
785  continue;
786  }
787 
788  // Otherwise, get instance paths for source/sink, and compute LCA.
789  auto sourcePaths = state.instancePathCache.getAbsolutePaths(sourceModule);
790  auto sinkPaths = state.instancePathCache.getAbsolutePaths(sinkModule);
791 
792  if (sourcePaths.size() != 1 || sinkPaths.size() != 1) {
793  auto diag =
794  mlir::emitError(source.getLoc())
795  << "This source is involved with a Wiring Problem where the source "
796  "or the sink are multiply instantiated and this is not supported.";
797  diag.attachNote(sink.getLoc()) << "The sink is here.";
798  return failure();
799  }
800 
801  FModuleOp lca =
802  cast<FModuleOp>(instanceGraph.getTopLevelNode()->getModule());
803  auto sources = sourcePaths[0];
804  auto sinks = sinkPaths[0];
805  while (!sources.empty() && !sinks.empty()) {
806  if (sources.top() != sinks.top())
807  break;
808  auto newLCA = cast<InstanceOp>(*sources.top());
809  lca = cast<FModuleOp>(newLCA.getReferencedModule(instanceGraph));
810  sources = sources.dropFront();
811  sinks = sinks.dropFront();
812  }
813 
814  LLVM_DEBUG({
815  llvm::dbgs() << " LCA: " << lca.getModuleName() << "\n"
816  << " sourcePath: " << sourcePaths[0] << "\n"
817  << " sinkPaths: " << sinkPaths[0] << "\n";
818  });
819 
820  // Pre-populate the connectionMap of the module with the source and sink.
821  moduleModifications[sourceModule].connectionMap[index] = source;
822  moduleModifications[sinkModule].connectionMap[index] = sink;
823 
824  // Record port types that should be added to each module along the LCA path.
825  Type sourceType, sinkType;
826  auto useRefTypes =
827  !noRefTypePorts &&
828  problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer;
829  if (useRefTypes) {
830  // Use RefType ports if possible
831  RefType refType = TypeSwitch<Type, RefType>(source.getType())
832  .Case<FIRRTLBaseType>([](FIRRTLBaseType base) {
833  return RefType::get(base.getPassiveType());
834  })
835  .Case<RefType>([](RefType ref) { return ref; });
836  sourceType = refType;
837  sinkType = refType.getType();
838  } else {
839  // Use specified port types.
840  sourceType = source.getType();
841  sinkType = sink.getType();
842 
843  // Types must be connectable, which means FIRRTLType's.
844  auto sourceFType = type_dyn_cast<FIRRTLType>(sourceType);
845  auto sinkFType = type_dyn_cast<FIRRTLType>(sinkType);
846  if (!sourceFType)
847  return emitError(source.getLoc())
848  << "Wiring Problem source type \"" << sourceType
849  << "\" must be a FIRRTL type";
850  if (!sinkFType)
851  return emitError(sink.getLoc())
852  << "Wiring Problem sink type \"" << sinkType
853  << "\" must be a FIRRTL type";
854 
855  // Otherwise they must be identical or FIRRTL type-equivalent
856  // (connectable).
857  if (sourceFType != sinkFType &&
858  !areTypesEquivalent(sinkFType, sourceFType)) {
859  auto diag = mlir::emitError(source.getLoc())
860  << "Wiring Problem source type " << sourceType
861  << " does not match sink type " << sinkType;
862  diag.attachNote(sink.getLoc()) << "The sink is here.";
863  return failure();
864  }
865  }
866  // If wiring using references, check that the sink value we connect to is
867  // passive.
868  if (auto sinkFType = type_dyn_cast<FIRRTLType>(sink.getType());
869  sinkFType && type_isa<RefType>(sourceType) &&
870  !getBaseType(sinkFType).isPassive())
871  return emitError(sink.getLoc())
872  << "Wiring Problem sink type \"" << sink.getType()
873  << "\" must be passive (no flips) when using references";
874 
875  // Record module modifications related to adding ports to modules.
876  auto addPorts = [&](igraph::InstancePath insts, Value val, Type tpe,
877  Direction dir) {
878  StringRef name, instName;
879  for (auto instNode : llvm::reverse(insts)) {
880  auto inst = cast<InstanceOp>(*instNode);
881  auto mod = inst.getReferencedModule<FModuleOp>(instanceGraph);
882  if (name.empty()) {
883  if (problem.newNameHint.empty())
884  name = state.getNamespace(mod).newName(
885  getFieldName(
886  getFieldRefFromValue(val, /*lookThroughCasts=*/true),
887  /*nameSafe=*/true)
888  .first +
889  "__bore");
890  else
891  name = state.getNamespace(mod).newName(problem.newNameHint);
892  } else {
893  assert(!instName.empty());
894  name = state.getNamespace(mod).newName(instName + "_" + name);
895  }
896  moduleModifications[mod].portsToAdd.push_back(
897  {index, {StringAttr::get(context, name), tpe, dir}});
898  instName = inst.getInstanceName();
899  }
900  };
901 
902  // Record the addition of ports.
903  addPorts(sources, source, sourceType, Direction::Out);
904  addPorts(sinks, sink, sinkType, Direction::In);
905  }
906 
907  // Iterate over modules from leaves to roots, applying ModuleModifications to
908  // each module.
909  LLVM_DEBUG({ llvm::dbgs() << "Updating modules:\n"; });
910  for (auto *op : llvm::post_order(instanceGraph.getTopLevelNode())) {
911  auto fmodule = dyn_cast<FModuleOp>(*op->getModule());
912  // Skip external modules and modules that have no modifications.
913  if (!fmodule || !moduleModifications.count(fmodule))
914  continue;
915 
916  auto modifications = moduleModifications[fmodule];
917  LLVM_DEBUG({
918  llvm::dbgs() << " - module: " << fmodule.getModuleName() << "\n";
919  llvm::dbgs() << " ports:\n";
920  for (auto [index, port] : modifications.portsToAdd) {
921  llvm::dbgs() << " - name: " << port.getName() << "\n"
922  << " id: " << index << "\n"
923  << " type: " << port.type << "\n"
924  << " direction: "
925  << (port.direction == Direction::In ? "in" : "out")
926  << "\n";
927  }
928  });
929 
930  // Add ports to the module after all other existing ports.
931  SmallVector<std::pair<unsigned, PortInfo>> newPorts;
932  SmallVector<unsigned> problemIndices;
933  for (auto [problemIdx, portInfo] : modifications.portsToAdd) {
934  // Create the port.
935  newPorts.push_back({fmodule.getNumPorts(), portInfo});
936  problemIndices.push_back(problemIdx);
937  }
938  auto originalNumPorts = fmodule.getNumPorts();
939  auto portIdx = fmodule.getNumPorts();
940  fmodule.insertPorts(newPorts);
941 
942  auto builder = ImplicitLocOpBuilder::atBlockBegin(UnknownLoc::get(context),
943  fmodule.getBodyBlock());
944 
945  // Connect each port to the value stored in the connectionMap for this
946  // wiring problem index.
947  for (auto [problemIdx, portPair] : llvm::zip(problemIndices, newPorts)) {
948  Value src = moduleModifications[fmodule].connectionMap[problemIdx];
949  assert(src && "there did not exist a driver for the port");
950  Value dest = fmodule.getArgument(portIdx++);
951  if (failed(connect(src, dest, builder)))
952  return failure();
953  }
954 
955  // If a U-turn exists, this is an LCA and we need a U-turn connection. These
956  // are the last connections made for this module.
957  for (auto [problemIdx, dest] : moduleModifications[fmodule].uturns) {
958  Value src = moduleModifications[fmodule].connectionMap[problemIdx];
959  assert(src && "there did not exist a connection for the u-turn");
960  if (failed(connect(src, dest, builder)))
961  return failure();
962  }
963 
964  // Update the connectionMap of all modules for which we created a port.
965  for (auto *inst : instanceGraph.lookup(fmodule)->uses()) {
966  InstanceOp useInst = cast<InstanceOp>(inst->getInstance());
967  auto enclosingModule = useInst->getParentOfType<FModuleOp>();
968  auto clonedInst = useInst.cloneAndInsertPorts(newPorts);
969  state.instancePathCache.replaceInstance(useInst, clonedInst);
970  // When RAUW-ing, ignore the new ports that we added when replacing (they
971  // cannot have uses).
972  useInst->replaceAllUsesWith(
973  clonedInst.getResults().drop_back(newPorts.size()));
974  useInst->erase();
975  // Record information in the moduleModifications strucutre for the module
976  // _where this is instantiated_. This is done so that when that module is
977  // visited later, there will be information available for it to find ports
978  // it needs to wire up. If there is already an existing connection, then
979  // this is a U-turn.
980  for (auto [newPortIdx, problemIdx] : llvm::enumerate(problemIndices)) {
981  auto &modifications = moduleModifications[enclosingModule];
982  auto newPort = clonedInst.getResult(newPortIdx + originalNumPorts);
983  if (modifications.connectionMap.count(problemIdx)) {
984  modifications.uturns.push_back({problemIdx, newPort});
985  continue;
986  }
987  modifications.connectionMap[problemIdx] = newPort;
988  }
989  }
990  }
991 
992  // Delete unused WireOps created by producers of WiringProblems.
993  for (auto *op : opsToErase)
994  op->erase();
995 
996  return success();
997 }
998 
999 // This is the main entrypoint for the lowering pass.
1000 void LowerAnnotationsPass::runOnOperation() {
1001  CircuitOp circuit = getOperation();
1002  SymbolTable modules(circuit);
1003 
1004  LLVM_DEBUG(debugPassHeader(this) << "\n");
1005 
1006  // Grab the annotations from a non-standard attribute called "rawAnnotations".
1007  // This is a temporary location for all annotations that are earmarked for
1008  // processing by this pass as we migrate annotations from being handled by
1009  // FIRAnnotations/FIRParser into this pass. While we do this, this pass is
1010  // not supposed to touch _other_ annotations to enable this pass to be run
1011  // after FIRAnnotations/FIRParser.
1012  auto annotations = circuit->getAttrOfType<ArrayAttr>(rawAnnotations);
1013  if (!annotations)
1014  return;
1015  circuit->removeAttr(rawAnnotations);
1016 
1017  // Populate the worklist in reverse order. This has the effect of causing
1018  // annotations to be processed in the order in which they appear in the
1019  // original JSON.
1020  for (auto anno : llvm::reverse(annotations.getValue()))
1021  worklistAttrs.push_back(cast<DictionaryAttr>(anno));
1022 
1023  size_t numFailures = 0;
1024  size_t numAdded = 0;
1025  auto addToWorklist = [&](DictionaryAttr anno) {
1026  ++numAdded;
1027  worklistAttrs.push_back(anno);
1028  };
1029  InstancePathCache instancePathCache(getAnalysis<InstanceGraph>());
1030  ApplyState state{circuit, modules, addToWorklist, instancePathCache,
1031  noRefTypePorts};
1032  LLVM_DEBUG(llvm::dbgs() << "Processing annotations:\n");
1033  while (!worklistAttrs.empty()) {
1034  auto attr = worklistAttrs.pop_back_val();
1035  if (applyAnnotation(attr, state).failed())
1036  ++numFailures;
1037  }
1038 
1039  if (failed(legacyToWiringProblems(state)))
1040  ++numFailures;
1041 
1042  if (failed(solveWiringProblems(state)))
1043  ++numFailures;
1044 
1045  // Update statistics
1046  numRawAnnotations += annotations.size();
1047  numAddedAnnos += numAdded;
1048  numAnnos += numAdded + annotations.size();
1049  numReusedHierPathOps += state.numReusedHierPaths;
1050 
1051  if (numFailures)
1052  signalPassFailure();
1053 }
1054 
1055 /// This is the pass constructor.
1056 std::unique_ptr<mlir::Pass>
1058  bool ignoreAnnotationClassless,
1059  bool noRefTypePorts) {
1060  auto pass = std::make_unique<LowerAnnotationsPass>();
1061  pass->ignoreAnnotationUnknown = ignoreAnnotationUnknown;
1062  pass->ignoreAnnotationClassless = ignoreAnnotationClassless;
1063  pass->noRefTypePorts = noRefTypePorts;
1064  return pass;
1065 }
assert(baseType &&"element must be base type")
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.
Builder builder
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:63
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.
An instance path composed of a series of instances.
def connect(destination, source)
Definition: support.py:37
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
LogicalResult applyOMIR(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Main entry point to handle scattering of an OMIRAnnotation.
Definition: EmitOMIR.cpp:490
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:217
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
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 * 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:182
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:271
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::unique_ptr< mlir::Pass > createLowerFIRRTLAnnotationsPass(bool ignoreUnhandledAnnotations=false, bool ignoreClasslessAnnotations=false, bool noRefTypePorts=false)
This is the pass constructor.
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:24
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.
This represents an annotation targeting a specific port of a module, memory, or instance.
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.