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 
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 applyAttributeAnnotation(const AnnoPathValue &target,
322  DictionaryAttr anno,
323  ApplyState &state) {
324  auto *op = target.ref.getOp();
325 
326  auto error = [&]() {
327  auto diag = mlir::emitError(op->getLoc());
328  diag << anno.getAs<StringAttr>("class").getValue() << " ";
329  return diag;
330  };
331 
332  if (!isa<OpAnnoTarget>(target.ref))
333  return error()
334  << "must target an operation. Currently ports are not supported";
335 
336  if (!target.isLocal())
337  return error() << "must be local";
338 
339  if (!isa<FModuleOp, WireOp, NodeOp, RegOp, RegResetOp>(op))
340  return error()
341  << "unhandled operation. The target must be a module, wire, node or "
342  "register";
343 
344  auto name = anno.getAs<StringAttr>("description");
345  auto svAttr = sv::SVAttributeAttr::get(name.getContext(), name);
346  sv::addSVAttributes(op, {svAttr});
347  return success();
348 }
349 
350 /// Update a memory op with attributes about memory file loading.
351 template <bool isInline>
352 static LogicalResult applyLoadMemoryAnno(const AnnoPathValue &target,
353  DictionaryAttr anno,
354  ApplyState &state) {
355  if (!target.isLocal()) {
356  mlir::emitError(state.circuit.getLoc())
357  << "has a " << anno.get("class")
358  << " annotation which is non-local, but this annotation is not allowed "
359  "to be non-local";
360  return failure();
361  }
362 
363  auto *op = target.ref.getOp();
364 
365  if (!target.isOpOfType<MemOp, CombMemOp, SeqMemOp>()) {
366  mlir::emitError(op->getLoc())
367  << "can only apply a load memory annotation to a memory";
368  return failure();
369  }
370 
371  // The two annotations have different case usage in "filename".
372  StringAttr filename = tryGetAs<StringAttr>(
373  anno, anno, isInline ? "filename" : "fileName", op->getLoc(),
374  anno.getAs<StringAttr>("class").getValue());
375  if (!filename)
376  return failure();
377 
378  auto hexOrBinary =
379  tryGetAs<StringAttr>(anno, anno, "hexOrBinary", op->getLoc(),
380  anno.getAs<StringAttr>("class").getValue());
381  if (!hexOrBinary)
382  return failure();
383 
384  auto hexOrBinaryValue = hexOrBinary.getValue();
385  if (hexOrBinaryValue != "h" && hexOrBinaryValue != "b") {
386  auto diag = mlir::emitError(op->getLoc())
387  << "has memory initialization annotation with invalid format, "
388  "'hexOrBinary' field must be either 'h' or 'b'";
389  diag.attachNote() << "the full annotation is: " << anno;
390  return failure();
391  }
392 
393  op->setAttr("init", MemoryInitAttr::get(op->getContext(), filename,
394  hexOrBinaryValue == "b", isInline));
395 
396  return success();
397 }
398 
399 static LogicalResult applyOutputDirAnno(const AnnoPathValue &target,
400  DictionaryAttr anno,
401  ApplyState &state) {
402  auto *op = target.ref.getOp();
403  auto *context = op->getContext();
404  auto loc = op->getLoc();
405 
406  auto error = [&]() {
407  return mlir::emitError(loc) << outputDirAnnoClass << " ";
408  };
409 
410  auto opTarget = dyn_cast<OpAnnoTarget>(target.ref);
411  if (!opTarget)
412  return error() << "must target a module";
413  if (!target.isLocal())
414  return error() << "must be local";
415 
416  auto moduleOp = dyn_cast<FModuleOp>(op);
417  if (!moduleOp)
418  return error() << "must target a module";
419  if (!moduleOp.isPublic())
420  return error() << "must target a public module";
421  if (moduleOp->hasAttr("output_file"))
422  return error() << "target already has an output file";
423 
424  auto dirname =
425  tryGetAs<StringAttr>(anno, anno, "dirname", loc, outputDirAnnoClass);
426  if (!dirname)
427  return failure();
428  if (dirname.empty())
429  return error() << "dirname must not be empty";
430 
431  auto outputFile =
432  hw::OutputFileAttr::getAsDirectory(context, dirname.getValue());
433 
434  moduleOp->setAttr("output_file", outputFile);
435  return success();
436 }
437 
438 //===----------------------------------------------------------------------===//
439 // Driving table
440 //===----------------------------------------------------------------------===//
441 
442 namespace circt::firrtl {
443 /// Resolution and application of a "firrtl.annotations.NoTargetAnnotation".
444 /// This should be used for any Annotation which does not apply to anything in
445 /// the FIRRTL Circuit, i.e., an Annotation which has no target. Historically,
446 /// NoTargetAnnotations were used to control the Scala FIRRTL Compiler (SFC) or
447 /// its passes, e.g., to set the output directory or to turn on a pass.
448 /// Examples of these in the SFC are "firrtl.options.TargetDirAnnotation" to set
449 /// the output directory or "firrtl.stage.RunFIRRTLTransformAnnotation" to
450 /// cause the SFC to schedule a specified pass. Instead of leaving these
451 /// floating or attaching them to the top-level MLIR module (which is a purer
452 /// interpretation of "no target"), we choose to attach them to the Circuit even
453 /// they do not "apply" to the Circuit. This gives later passes a common place,
454 /// the Circuit, to search for these control Annotations.
456  applyWithoutTarget<false, CircuitOp>};
457 
458 static llvm::StringMap<AnnoRecord> annotationRecords{{
459 
460  // Testing Annotation
461  {"circt.test", {stdResolve, applyWithoutTarget<true>}},
462  {"circt.testLocalOnly", {stdResolve, applyWithoutTarget<>}},
463  {"circt.testNT", {noResolve, applyWithoutTarget<>}},
464  {"circt.missing", {tryResolve, applyWithoutTarget<true>}},
465  // Grand Central Views/Interfaces Annotations
470  {companionAnnoClass, {stdResolve, applyWithoutTarget<>}},
471  {augmentedGroundTypeClass, {stdResolve, applyWithoutTarget<true>}},
472  // Grand Central Data Tap Annotations
474  {dataTapsBlackboxClass, {stdResolve, applyWithoutTarget<true>}},
475  {referenceKeySourceClass, {stdResolve, applyWithoutTarget<true>}},
476  {referenceKeyPortClass, {stdResolve, applyWithoutTarget<true>}},
477  {internalKeySourceClass, {stdResolve, applyWithoutTarget<true>}},
478  {internalKeyPortClass, {stdResolve, applyWithoutTarget<true>}},
479  {deletedKeyClass, {stdResolve, applyWithoutTarget<true>}},
480  {literalKeyClass, {stdResolve, applyWithoutTarget<true>}},
481  // Grand Central Mem Tap Annotations
483  {memTapSourceClass, {stdResolve, applyWithoutTarget<true>}},
484  {memTapPortClass, {stdResolve, applyWithoutTarget<true>}},
485  {memTapBlackboxClass, {stdResolve, applyWithoutTarget<true>}},
486  // OMIR Annotations
488  {omirTrackerAnnoClass, {stdResolve, applyWithoutTarget<true>}},
490  // Miscellaneous Annotations
493  {stdResolve, applyWithoutTarget<true, true, WireOp, NodeOp, RegOp,
494  RegResetOp, InstanceOp, MemOp, CombMemOp,
495  MemoryPortOp, SeqMemOp>}},
497  {stdResolve,
498  applyWithoutTarget<true, FModuleOp, FExtModuleOp, InstanceOp>}},
504  {stdResolve, applyWithoutTarget<true, MemOp, CombMemOp>}},
510  {stdResolve, applyWithoutTarget<true, FModuleOp, FExtModuleOp>}},
511  {flattenAnnoClass, {stdResolve, applyWithoutTarget<false, FModuleOp>}},
512  {inlineAnnoClass, {stdResolve, applyWithoutTarget<false, FModuleOp>}},
514  {stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
516  {stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
518  {stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
520  {stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
522  {stdResolve, applyWithoutTarget<false, FModuleOp>}},
524  {stdResolve, applyWithoutTarget<false, FExtModuleOp>}},
528  {stdResolve, applyWithoutTarget<false, FModuleOp, FExtModuleOp>}},
545  {extractBlackBoxAnnoClass, {stdResolve, applyWithoutTarget<false>}},
546  {fullAsyncResetAnnoClass, {stdResolve, applyWithoutTarget<false>}},
548  {stdResolve, applyWithoutTarget<true, FModuleOp>}},
552  {traceAnnoClass, {stdResolve, applyWithoutTarget<true>}},
553  {loadMemoryFromFileAnnoClass, {stdResolve, applyLoadMemoryAnno<false>}},
555  {stdResolve, applyLoadMemoryAnno<true>}},
559 
560 LogicalResult
561 registerAnnotationRecord(StringRef annoClass, AnnoRecord annoRecord,
562  const std::function<void(llvm::Twine)> &errorHandler) {
563 
564  if (annotationRecords.insert({annoClass, annoRecord}).second)
565  return LogicalResult::success();
566  if (errorHandler)
567  errorHandler("annotation record '" + annoClass + "' is registered twice\n");
568  return LogicalResult::failure();
569 }
570 
571 } // namespace circt::firrtl
572 
573 /// Lookup a record for a given annotation class. Optionally, returns the
574 /// record for "circuit.missing" if the record doesn't exist.
575 static const AnnoRecord *getAnnotationHandler(StringRef annoStr,
576  bool ignoreAnnotationUnknown) {
577  auto ii = annotationRecords.find(annoStr);
578  if (ii != annotationRecords.end())
579  return &ii->second;
580  if (ignoreAnnotationUnknown)
581  return &annotationRecords.find("circt.missing")->second;
582  return nullptr;
583 }
584 
585 //===----------------------------------------------------------------------===//
586 // Pass Infrastructure
587 //===----------------------------------------------------------------------===//
588 
589 namespace {
590 struct LowerAnnotationsPass
591  : public circt::firrtl::impl::LowerFIRRTLAnnotationsBase<
592  LowerAnnotationsPass> {
593  void runOnOperation() override;
594  LogicalResult applyAnnotation(DictionaryAttr anno, ApplyState &state);
595  LogicalResult legacyToWiringProblems(ApplyState &state);
596  LogicalResult solveWiringProblems(ApplyState &state);
597 
598  using LowerFIRRTLAnnotationsBase::allowAddingPortsOnPublic;
599  using LowerFIRRTLAnnotationsBase::ignoreAnnotationClassless;
600  using LowerFIRRTLAnnotationsBase::ignoreAnnotationUnknown;
601  using LowerFIRRTLAnnotationsBase::noRefTypePorts;
602  SmallVector<DictionaryAttr> worklistAttrs;
603 };
604 } // end anonymous namespace
605 
606 LogicalResult LowerAnnotationsPass::applyAnnotation(DictionaryAttr anno,
607  ApplyState &state) {
608  LLVM_DEBUG(llvm::dbgs() << " - anno: " << anno << "\n";);
609 
610  // Lookup the class
611  StringRef annoClassVal;
612  if (auto annoClass = anno.getNamed("class"))
613  annoClassVal = cast<StringAttr>(annoClass->getValue()).getValue();
614  else if (ignoreAnnotationClassless)
615  annoClassVal = "circt.missing";
616  else
617  return mlir::emitError(state.circuit.getLoc())
618  << "Annotation without a class: " << anno;
619 
620  // See if we handle the class
621  auto *record = getAnnotationHandler(annoClassVal, false);
622  if (!record) {
623  ++numUnhandled;
624  if (!ignoreAnnotationUnknown)
625  return mlir::emitError(state.circuit.getLoc())
626  << "Unhandled annotation: " << anno;
627 
628  // Try again, requesting the fallback handler.
629  record = getAnnotationHandler(annoClassVal, ignoreAnnotationUnknown);
630  assert(record);
631  }
632 
633  // Try to apply the annotation
634  auto target = record->resolver(anno, state);
635  if (!target)
636  return mlir::emitError(state.circuit.getLoc())
637  << "Unable to resolve target of annotation: " << anno;
638  if (record->applier(*target, anno, state).failed())
639  return mlir::emitError(state.circuit.getLoc())
640  << "Unable to apply annotation: " << anno;
641  return success();
642 }
643 
644 /// Convert consumed SourceAnnotation and SinkAnnotation into WiringProblems,
645 /// using the pin attribute as newNameHint
646 LogicalResult LowerAnnotationsPass::legacyToWiringProblems(ApplyState &state) {
647  for (const auto &[name, problem] : state.legacyWiringProblems) {
648  if (!problem.source)
649  return mlir::emitError(state.circuit.getLoc())
650  << "Unable to resolve source for pin: " << name;
651 
652  if (problem.sinks.empty())
653  return mlir::emitError(state.circuit.getLoc())
654  << "Unable to resolve sink(s) for pin: " << name;
655 
656  for (const auto &sink : problem.sinks) {
657  state.wiringProblems.push_back(
658  {problem.source, sink, {}, WiringProblem::RefTypeUsage::Never});
659  }
660  }
661  return success();
662 }
663 
664 /// Modify the circuit to solve and apply all Wiring Problems in the circuit. A
665 /// Wiring Problem is a mapping from a source to a sink that can be connected
666 /// via a base Type or RefType as requested. This uses a two-step approach.
667 /// First, all Wiring Problems are analyzed to compute pending modifications to
668 /// modules. Second, modules are visited from leaves to roots to apply module
669 /// modifications. Module modifications include addings ports and connecting
670 /// things up.
671 LogicalResult LowerAnnotationsPass::solveWiringProblems(ApplyState &state) {
672  // Utility function to extract the defining module from a value which may be
673  // either a BlockArgument or an Operation result.
674  auto getModule = [](Value value) {
675  if (BlockArgument blockArg = dyn_cast<BlockArgument>(value))
676  return cast<FModuleLike>(blockArg.getParentBlock()->getParentOp());
677  return value.getDefiningOp()->getParentOfType<FModuleLike>();
678  };
679 
680  // Utility function to determine where to insert connection operations.
681  auto findInsertionBlock = [&getModule](Value src, Value dest) -> Block * {
682  // Check for easy case: both are in the same block.
683  if (src.getParentBlock() == dest.getParentBlock())
684  return src.getParentBlock();
685 
686  // If connecting across blocks, figure out where to connect.
687  (void)getModule;
688  assert(getModule(src) == getModule(dest));
689  // Helper to determine if 'a' is available at 'b's block.
690  auto safelyDoms = [&](Value a, Value b) {
691  if (isa<BlockArgument>(a))
692  return true;
693  if (isa<BlockArgument>(b))
694  return false;
695  // Handle cases where 'b' is in child op after 'a'.
696  auto *ancestor =
697  a.getParentBlock()->findAncestorOpInBlock(*b.getDefiningOp());
698  return ancestor && a.getDefiningOp()->isBeforeInBlock(ancestor);
699  };
700  if (safelyDoms(src, dest))
701  return dest.getParentBlock();
702  if (safelyDoms(dest, src))
703  return src.getParentBlock();
704  return {};
705  };
706 
707  auto getNoopCast = [](Value v) -> mlir::UnrealizedConversionCastOp {
708  auto op =
709  dyn_cast_or_null<mlir::UnrealizedConversionCastOp>(v.getDefiningOp());
710  if (op && op.getNumResults() == 1 && op.getNumOperands() == 1 &&
711  op.getResultTypes()[0] == op.getOperandTypes()[0])
712  return op;
713  return {};
714  };
715 
716  // Utility function to connect a destination to a source. Always use a
717  // ConnectOp as the widths may be uninferred.
718  SmallVector<Operation *> opsToErase;
719  auto connect = [&](Value src, Value dest,
720  ImplicitLocOpBuilder &builder) -> LogicalResult {
721  // Strip away noop unrealized_conversion_cast's, used as placeholders.
722  // In the future, these should be created/managed as part of creating WP's.
723  if (auto op = getNoopCast(dest)) {
724  dest = op.getOperand(0);
725  opsToErase.push_back(op);
726  std::swap(src, dest);
727  } else if (auto op = getNoopCast(src)) {
728  src = op.getOperand(0);
729  opsToErase.push_back(op);
730  }
731 
732  if (foldFlow(dest) == Flow::Source)
733  std::swap(src, dest);
734 
735  // Figure out where to insert operations.
736  auto *insertBlock = findInsertionBlock(src, dest);
737  if (!insertBlock)
738  return emitError(src.getLoc())
739  .append("This value is involved with a Wiring Problem where the "
740  "destination is in the same module but neither dominates the "
741  "other, which is not supported.")
742  .attachNote(dest.getLoc())
743  .append("The destination is here.");
744 
745  // Insert at end, past invalidation in same block.
746  builder.setInsertionPointToEnd(insertBlock);
747 
748  // Create RefSend/RefResolve if necessary.
749  if (type_isa<RefType>(dest.getType()) != type_isa<RefType>(src.getType())) {
750  if (type_isa<RefType>(dest.getType()))
751  src = builder.create<RefSendOp>(src);
752  else
753  src = builder.create<RefResolveOp>(src);
754  }
755 
756  // If the sink is a wire with no users, then convert this to a node.
757  // This is done to convert the undriven wires created for GCView's
758  // into the NodeOp's they're required to be in GrandCentral.cpp.
759  if (auto destOp = dyn_cast_or_null<WireOp>(dest.getDefiningOp());
760  destOp && dest.getUses().empty()) {
761  // Only perform this if the type is suitable (passive).
762  if (auto baseType = dyn_cast<FIRRTLBaseType>(src.getType());
763  baseType && baseType.isPassive()) {
764  // Note that the wire is replaced with the source type
765  // regardless, continue this behavior.
766  builder.create<NodeOp>(src, destOp.getName())
767  .setAnnotationsAttr(destOp.getAnnotations());
768  opsToErase.push_back(destOp);
769  return success();
770  }
771  }
772 
773  // Otherwise, just connect to the source.
774  emitConnect(builder, dest, src);
775 
776  return success();
777  };
778 
779  auto &instanceGraph = state.instancePathCache.instanceGraph;
780  auto *context = state.circuit.getContext();
781 
782  // Examine all discovered Wiring Problems to determine modifications that need
783  // to be made per-module.
784  LLVM_DEBUG({ llvm::dbgs() << "Analyzing wiring problems:\n"; });
785  DenseMap<FModuleLike, ModuleModifications> moduleModifications;
786  DenseSet<Value> visitedSinks;
787  for (auto e : llvm::enumerate(state.wiringProblems)) {
788  auto index = e.index();
789  auto problem = e.value();
790  // This is a unique index that is assigned to this specific wiring problem
791  // and is used as a key during wiring to know which Values (ports, sources,
792  // or sinks) should be connected.
793  auto source = problem.source;
794  auto sink = problem.sink;
795 
796  // Check that no WiringProblems are trying to use the same sink. This
797  // should never happen.
798  if (!visitedSinks.insert(sink).second) {
799  auto diag = mlir::emitError(source.getLoc())
800  << "This sink is involved with a Wiring Problem which is "
801  "targeted by a source used by another Wiring Problem. "
802  "(This is both illegal and should be impossible.)";
803  diag.attachNote(source.getLoc()) << "The source is here";
804  return failure();
805  }
806  FModuleLike sourceModule = getModule(source);
807  FModuleLike sinkModule = getModule(sink);
808  if (isa<FExtModuleOp>(sourceModule) || isa<FExtModuleOp>(sinkModule)) {
809  auto diag = mlir::emitError(source.getLoc())
810  << "This source is involved with a Wiring Problem which "
811  "includes an External Module port and External Module "
812  "ports anre not supported.";
813  diag.attachNote(sink.getLoc()) << "The sink is here.";
814  return failure();
815  }
816 
817  LLVM_DEBUG({
818  llvm::dbgs() << " - index: " << index << "\n"
819  << " source:\n"
820  << " module: " << sourceModule.getModuleName() << "\n"
821  << " value: " << source << "\n"
822  << " sink:\n"
823  << " module: " << sinkModule.getModuleName() << "\n"
824  << " value: " << sink << "\n"
825  << " newNameHint: " << problem.newNameHint << "\n";
826  });
827 
828  // If the source and sink are in the same block, just wire them up.
829  if (sink.getParentBlock() == source.getParentBlock()) {
830  auto builder = ImplicitLocOpBuilder::atBlockEnd(UnknownLoc::get(context),
831  sink.getParentBlock());
832  if (failed(connect(source, sink, builder)))
833  return failure();
834  continue;
835  }
836  // If both are in the same module but not same block, U-turn.
837  // We may not be able to handle this, but that is checked below while
838  // connecting.
839  if (sourceModule == sinkModule) {
840  LLVM_DEBUG(llvm::dbgs()
841  << " LCA: " << sourceModule.getModuleName() << "\n");
842  moduleModifications[sourceModule].connectionMap[index] = source;
843  moduleModifications[sourceModule].uturns.push_back({index, sink});
844  continue;
845  }
846 
847  // Otherwise, get instance paths for source/sink, and compute LCA.
848  auto sourcePaths = state.instancePathCache.getAbsolutePaths(sourceModule);
849  auto sinkPaths = state.instancePathCache.getAbsolutePaths(sinkModule);
850 
851  if (sourcePaths.size() != 1 || sinkPaths.size() != 1) {
852  auto diag =
853  mlir::emitError(source.getLoc())
854  << "This source is involved with a Wiring Problem where the source "
855  "or the sink are multiply instantiated and this is not supported.";
856  diag.attachNote(sink.getLoc()) << "The sink is here.";
857  return failure();
858  }
859 
860  FModuleOp lca =
861  cast<FModuleOp>(instanceGraph.getTopLevelNode()->getModule());
862  auto sources = sourcePaths[0];
863  auto sinks = sinkPaths[0];
864  while (!sources.empty() && !sinks.empty()) {
865  if (sources.top() != sinks.top())
866  break;
867  auto newLCA = cast<InstanceOp>(*sources.top());
868  lca = cast<FModuleOp>(newLCA.getReferencedModule(instanceGraph));
869  sources = sources.dropFront();
870  sinks = sinks.dropFront();
871  }
872 
873  LLVM_DEBUG({
874  llvm::dbgs() << " LCA: " << lca.getModuleName() << "\n"
875  << " sourcePath: " << sourcePaths[0] << "\n"
876  << " sinkPaths: " << sinkPaths[0] << "\n";
877  });
878 
879  // Pre-populate the connectionMap of the module with the source and sink.
880  moduleModifications[sourceModule].connectionMap[index] = source;
881  moduleModifications[sinkModule].connectionMap[index] = sink;
882 
883  // Record port types that should be added to each module along the LCA path.
884  Type sourceType, sinkType;
885  auto useRefTypes =
886  !noRefTypePorts &&
887  problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer;
888  if (useRefTypes) {
889  // Use RefType ports if possible
890  RefType refType = TypeSwitch<Type, RefType>(source.getType())
891  .Case<FIRRTLBaseType>([](FIRRTLBaseType base) {
892  return RefType::get(base.getPassiveType());
893  })
894  .Case<RefType>([](RefType ref) { return ref; });
895  sourceType = refType;
896  sinkType = refType.getType();
897  } else {
898  // Use specified port types.
899  sourceType = source.getType();
900  sinkType = sink.getType();
901 
902  // Types must be connectable, which means FIRRTLType's.
903  auto sourceFType = type_dyn_cast<FIRRTLType>(sourceType);
904  auto sinkFType = type_dyn_cast<FIRRTLType>(sinkType);
905  if (!sourceFType)
906  return emitError(source.getLoc())
907  << "Wiring Problem source type \"" << sourceType
908  << "\" must be a FIRRTL type";
909  if (!sinkFType)
910  return emitError(sink.getLoc())
911  << "Wiring Problem sink type \"" << sinkType
912  << "\" must be a FIRRTL type";
913 
914  // Otherwise they must be identical or FIRRTL type-equivalent
915  // (connectable).
916  if (sourceFType != sinkFType &&
917  !areTypesEquivalent(sinkFType, sourceFType)) {
918  // Support tapping mixed alignment -> passive , emulate probe behavior.
919  if (auto sourceBaseType = dyn_cast<FIRRTLBaseType>(sourceFType);
920  problem.refTypeUsage == WiringProblem::RefTypeUsage::Prefer &&
921  sourceBaseType &&
922  areTypesEquivalent(sinkFType, sourceBaseType.getPassiveType())) {
923  // Change "sourceType" to the passive version that's type-equivalent,
924  // this will be used for wiring on the "up" side.
925  // This relies on `emitConnect` supporting connecting to the passive
926  // version from the original source.
927  sourceType = sourceBaseType.getPassiveType();
928  } else {
929  auto diag = mlir::emitError(source.getLoc())
930  << "Wiring Problem source type " << sourceType
931  << " does not match sink type " << sinkType;
932  diag.attachNote(sink.getLoc()) << "The sink is here.";
933  return failure();
934  }
935  }
936  }
937  // If wiring using references, check that the sink value we connect to is
938  // passive.
939  if (auto sinkFType = type_dyn_cast<FIRRTLType>(sink.getType());
940  sinkFType && type_isa<RefType>(sourceType) &&
941  !getBaseType(sinkFType).isPassive())
942  return emitError(sink.getLoc())
943  << "Wiring Problem sink type \"" << sink.getType()
944  << "\" must be passive (no flips) when using references";
945 
946  // Record module modifications related to adding ports to modules.
947  auto addPorts = [&](igraph::InstancePath insts, Value val, Type tpe,
948  Direction dir) -> LogicalResult {
949  StringRef name, instName;
950  for (auto instNode : llvm::reverse(insts)) {
951  auto inst = cast<InstanceOp>(*instNode);
952  auto mod = inst.getReferencedModule<FModuleOp>(instanceGraph);
953  if (mod.isPublic()) {
954  if (!allowAddingPortsOnPublic) {
955  auto diag = emitError(
956  mod.getLoc(), "cannot wire port through this public module");
957  diag.attachNote(source.getLoc()) << "source here";
958  diag.attachNote(sink.getLoc()) << "sink here";
959  return diag;
960  }
961  ++numPublicPortsWired;
962  }
963  if (name.empty()) {
964  if (problem.newNameHint.empty())
965  name = state.getNamespace(mod).newName(
966  getFieldName(
967  getFieldRefFromValue(val, /*lookThroughCasts=*/true),
968  /*nameSafe=*/true)
969  .first +
970  "__bore");
971  else
972  name = state.getNamespace(mod).newName(problem.newNameHint);
973  } else {
974  assert(!instName.empty());
975  name = state.getNamespace(mod).newName(instName + "_" + name);
976  }
977  moduleModifications[mod].portsToAdd.push_back(
978  {index, {StringAttr::get(context, name), tpe, dir}});
979  instName = inst.getInstanceName();
980  }
981  return success();
982  };
983 
984  // Record the addition of ports.
985  if (failed(addPorts(sources, source, sourceType, Direction::Out)) ||
986  failed(addPorts(sinks, sink, sinkType, Direction::In)))
987  return failure();
988  }
989 
990  // Iterate over modules from leaves to roots, applying ModuleModifications to
991  // each module.
992  LLVM_DEBUG({ llvm::dbgs() << "Updating modules:\n"; });
993  for (auto *op : llvm::post_order(instanceGraph.getTopLevelNode())) {
994  auto fmodule = dyn_cast<FModuleOp>(*op->getModule());
995  // Skip external modules and modules that have no modifications.
996  if (!fmodule || !moduleModifications.count(fmodule))
997  continue;
998 
999  auto modifications = moduleModifications[fmodule];
1000  LLVM_DEBUG({
1001  llvm::dbgs() << " - module: " << fmodule.getModuleName() << "\n";
1002  llvm::dbgs() << " ports:\n";
1003  for (auto [index, port] : modifications.portsToAdd) {
1004  llvm::dbgs() << " - name: " << port.getName() << "\n"
1005  << " id: " << index << "\n"
1006  << " type: " << port.type << "\n"
1007  << " direction: "
1008  << (port.direction == Direction::In ? "in" : "out")
1009  << "\n";
1010  }
1011  });
1012 
1013  // Add ports to the module after all other existing ports.
1014  SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1015  SmallVector<unsigned> problemIndices;
1016  for (auto [problemIdx, portInfo] : modifications.portsToAdd) {
1017  // Create the port.
1018  newPorts.push_back({fmodule.getNumPorts(), portInfo});
1019  problemIndices.push_back(problemIdx);
1020  }
1021  auto originalNumPorts = fmodule.getNumPorts();
1022  auto portIdx = fmodule.getNumPorts();
1023  fmodule.insertPorts(newPorts);
1024 
1025  auto builder = ImplicitLocOpBuilder::atBlockBegin(UnknownLoc::get(context),
1026  fmodule.getBodyBlock());
1027 
1028  // Connect each port to the value stored in the connectionMap for this
1029  // wiring problem index.
1030  for (auto [problemIdx, portPair] : llvm::zip(problemIndices, newPorts)) {
1031  Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1032  assert(src && "there did not exist a driver for the port");
1033  Value dest = fmodule.getArgument(portIdx++);
1034  if (failed(connect(src, dest, builder)))
1035  return failure();
1036  }
1037 
1038  // If a U-turn exists, this is an LCA and we need a U-turn connection. These
1039  // are the last connections made for this module.
1040  for (auto [problemIdx, dest] : moduleModifications[fmodule].uturns) {
1041  Value src = moduleModifications[fmodule].connectionMap[problemIdx];
1042  assert(src && "there did not exist a connection for the u-turn");
1043  if (failed(connect(src, dest, builder)))
1044  return failure();
1045  }
1046 
1047  // Update the connectionMap of all modules for which we created a port.
1048  for (auto *inst : instanceGraph.lookup(fmodule)->uses()) {
1049  InstanceOp useInst = cast<InstanceOp>(inst->getInstance());
1050  auto enclosingModule = useInst->getParentOfType<FModuleOp>();
1051  auto clonedInst = useInst.cloneAndInsertPorts(newPorts);
1052  state.instancePathCache.replaceInstance(useInst, clonedInst);
1053  // When RAUW-ing, ignore the new ports that we added when replacing (they
1054  // cannot have uses).
1055  useInst->replaceAllUsesWith(
1056  clonedInst.getResults().drop_back(newPorts.size()));
1057  useInst->erase();
1058  // Record information in the moduleModifications strucutre for the module
1059  // _where this is instantiated_. This is done so that when that module is
1060  // visited later, there will be information available for it to find ports
1061  // it needs to wire up. If there is already an existing connection, then
1062  // this is a U-turn.
1063  for (auto [newPortIdx, problemIdx] : llvm::enumerate(problemIndices)) {
1064  auto &modifications = moduleModifications[enclosingModule];
1065  auto newPort = clonedInst.getResult(newPortIdx + originalNumPorts);
1066  if (modifications.connectionMap.count(problemIdx)) {
1067  modifications.uturns.push_back({problemIdx, newPort});
1068  continue;
1069  }
1070  modifications.connectionMap[problemIdx] = newPort;
1071  }
1072  }
1073  }
1074 
1075  // Delete unused WireOps created by producers of WiringProblems.
1076  for (auto *op : opsToErase)
1077  op->erase();
1078 
1079  return success();
1080 }
1081 
1082 // This is the main entrypoint for the lowering pass.
1083 void LowerAnnotationsPass::runOnOperation() {
1084  CircuitOp circuit = getOperation();
1085  SymbolTable modules(circuit);
1086 
1087  LLVM_DEBUG(debugPassHeader(this) << "\n");
1088 
1089  // Grab the annotations from a non-standard attribute called "rawAnnotations".
1090  // This is a temporary location for all annotations that are earmarked for
1091  // processing by this pass as we migrate annotations from being handled by
1092  // FIRAnnotations/FIRParser into this pass. While we do this, this pass is
1093  // not supposed to touch _other_ annotations to enable this pass to be run
1094  // after FIRAnnotations/FIRParser.
1095  auto annotations = circuit->getAttrOfType<ArrayAttr>(rawAnnotations);
1096  if (!annotations)
1097  return;
1098  circuit->removeAttr(rawAnnotations);
1099 
1100  // Populate the worklist in reverse order. This has the effect of causing
1101  // annotations to be processed in the order in which they appear in the
1102  // original JSON.
1103  for (auto anno : llvm::reverse(annotations.getValue()))
1104  worklistAttrs.push_back(cast<DictionaryAttr>(anno));
1105 
1106  size_t numFailures = 0;
1107  size_t numAdded = 0;
1108  auto addToWorklist = [&](DictionaryAttr anno) {
1109  ++numAdded;
1110  worklistAttrs.push_back(anno);
1111  };
1112  InstancePathCache instancePathCache(getAnalysis<InstanceGraph>());
1113  ApplyState state{circuit, modules, addToWorklist, instancePathCache,
1114  noRefTypePorts};
1115  LLVM_DEBUG(llvm::dbgs() << "Processing annotations:\n");
1116  while (!worklistAttrs.empty()) {
1117  auto attr = worklistAttrs.pop_back_val();
1118  if (applyAnnotation(attr, state).failed())
1119  ++numFailures;
1120  }
1121 
1122  if (failed(legacyToWiringProblems(state)))
1123  ++numFailures;
1124 
1125  if (failed(solveWiringProblems(state)))
1126  ++numFailures;
1127 
1128  // Update statistics
1129  numRawAnnotations += annotations.size();
1130  numAddedAnnos += numAdded;
1131  numAnnos += numAdded + annotations.size();
1132  numReusedHierPathOps += state.numReusedHierPaths;
1133 
1134  if (numFailures)
1135  signalPassFailure();
1136 }
1137 
1138 /// This is the pass constructor.
1140  bool ignoreAnnotationUnknown, bool ignoreAnnotationClassless,
1141  bool noRefTypePorts, bool allowAddingPortsOnPublic) {
1142  auto pass = std::make_unique<LowerAnnotationsPass>();
1143  pass->ignoreAnnotationUnknown = ignoreAnnotationUnknown;
1144  pass->ignoreAnnotationClassless = ignoreAnnotationClassless;
1145  pass->noRefTypePorts = noRefTypePorts;
1146  pass->allowAddingPortsOnPublic = allowAddingPortsOnPublic;
1147  return pass;
1148 }
assert(baseType &&"element must be base type")
static LogicalResult applyOutputDirAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static void addAnnotation(AnnoTarget ref, unsigned fieldIdx, ArrayRef< NamedAttribute > anno)
Apply a new annotation to a resolved target.
static ArrayAttr replaceArrayAttrElement(ArrayAttr array, size_t elem, Attribute newVal)
Update an ArrayAttribute by replacing one entry.
static const AnnoRecord * getAnnotationHandler(StringRef annoStr, bool ignoreAnnotationUnknown)
Lookup a record for a given annotation class.
static std::optional< AnnoPathValue > stdResolveImpl(StringRef rawPath, ApplyState &state)
Implementation of standard resolution.
static ArrayAttr appendArrayAttr(ArrayAttr array, Attribute a)
Construct the annotation array with a new thing appended.
static LogicalResult applyDUTAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
LogicalResult drop(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Just drop the annotation.
static LogicalResult applyLoadMemoryAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Update a memory op with attributes about memory file loading.
static std::optional< Convention > parseConvention(llvm::StringRef str)
static std::optional< AnnoPathValue > noResolve(DictionaryAttr anno, ApplyState &state)
Always resolve to the circuit, ignoring the annotation.
static ArrayAttr getAnnotationsFrom(Operation *op)
Get annotations or an empty set of annotations.
static LogicalResult applyConventionAnno(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static LogicalResult applyAttributeAnnotation(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
static FlatSymbolRefAttr scatterNonLocalPath(const AnnoPathValue &target, ApplyState &state)
Scatter breadcrumb annotations corresponding to non-local annotations along the instance path.
static FlatSymbolRefAttr buildNLA(const AnnoPathValue &target, ApplyState &state)
Make an anchor for a non-local annotation.
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Definition: Namespace.h:72
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: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:498
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 * 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:209
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:298
constexpr const char * dataTapsClass
constexpr const char * conventionAnnoClass
constexpr const char * dedupGroupAnnoClass
constexpr const char * omirAnnoClass
constexpr const char * omirTrackerAnnoClass
constexpr const char * viewAnnoClass
constexpr const char * serializedViewAnnoClass
constexpr const char * enumDefAnnoClass
constexpr const char * enumVecAnnoClass
constexpr const char * omirFileAnnoClass
std::string canonicalizeTarget(StringRef target)
Return an input target string in canonical form.
constexpr const char * wiringSourceAnnoClass
LogicalResult applyGCTMemTaps(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * traceNameAnnoClass
constexpr const char * enumComponentAnnoClass
constexpr const char * dataTapsBlackboxClass
constexpr const char * extractAssertAnnoClass
constexpr const char * memTapBlackboxClass
static LogicalResult applyWithoutTarget(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
An applier which puts the annotation on the target and drops the 'target' field from the annotation.
constexpr const char * testHarnessPathAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * addSeqMemPortAnnoClass
LogicalResult applyWiring(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Consume SourceAnnotation and SinkAnnotation, storing into state.
LogicalResult applyTraceName(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Expand a TraceNameAnnotation (which has don't touch semantics) into a TraceAnnotation (which does NOT...
constexpr const char * blackBoxPathAnnoClass
constexpr const char * internalKeyPortClass
constexpr const char * addSeqMemPortsFileAnnoClass
constexpr const char * retimeModulesFileAnnoClass
constexpr const char * prefixModulesAnnoClass
constexpr const char * retimeModuleAnnoClass
constexpr const char * runFIRRTLTransformAnnoClass
constexpr const char * companionAnnoClass
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
constexpr const char * referenceKeySourceClass
constexpr const char * literalKeyClass
constexpr const char * blackBoxTargetDirAnnoClass
constexpr const char * ignoreFullAsyncResetAnnoClass
Annotation that marks a module as not belonging to any reset domain.
LogicalResult registerAnnotationRecord(StringRef annoClass, AnnoRecord annoRecord, const std::function< void(llvm::Twine)> &errorHandler={})
Register external annotation records.
LogicalResult applyGCTDataTaps(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * blackBoxInlineAnnoClass
StringRef getPortAnnotationAttrName()
Return the name of the attribute used for port annotations on FIRRTL ops.
constexpr const char * decodeTableAnnotation
constexpr const char * extractClockGatesAnnoClass
constexpr const char * inlineAnnoClass
constexpr const char * memTapPortClass
LogicalResult applyGCTView(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
constexpr const char * flattenAnnoClass
constexpr const char * prefixInterfacesAnnoClass
std::optional< TokenAnnoTarget > tokenizePath(StringRef origTarget)
Parse a FIRRTL annotation path into its constituent parts.
LogicalResult applyWithoutTargetImpl(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state, bool allowNonLocal)
===-------------------------------------------------------------------—===// Standard Utility Applier...
constexpr const char * extractAssumeAnnoClass
constexpr const char * grandCentralHierarchyFileAnnoClass
std::optional< AnnoPathValue > tryResolve(DictionaryAttr anno, ApplyState &state)
Resolves with target, if it exists. If not, resolves to the circuit.
constexpr const char * dontTouchAnnoClass
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
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.
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.