CIRCT  19.0.0git
PrefixModules.cpp
Go to the documentation of this file.
1 //===- PrefixModules.cpp - Prefix module names pass -------------*- 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 PrefixModules pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
20 #include "circt/Support/LLVM.h"
21 #include "mlir/IR/AttrTypeSubElements.h"
22 #include "mlir/Pass/Pass.h"
23 #include "llvm/ADT/DenseMap.h"
24 #include "llvm/ADT/PostOrderIterator.h"
25 #include "llvm/ADT/StringMap.h"
26 
27 namespace circt {
28 namespace firrtl {
29 #define GEN_PASS_DEF_PREFIXMODULES
30 #include "circt/Dialect/FIRRTL/Passes.h.inc"
31 } // namespace firrtl
32 } // namespace circt
33 
34 using namespace circt;
35 using namespace firrtl;
36 
37 /// This maps a FModuleOp to a list of all prefixes that need to be applied.
38 /// When a module has multiple prefixes, it will be cloned for each one. Usually
39 /// there is only a single prefix applied to each module, although there could
40 /// be many.
41 using PrefixMap = llvm::DenseMap<StringRef, std::vector<std::string>>;
42 
43 /// Insert a string into the end of vector if the string is not already present.
44 static void recordPrefix(PrefixMap &prefixMap, StringRef moduleName,
45  std::string prefix) {
46  auto &modulePrefixes = prefixMap[moduleName];
47  if (llvm::find(modulePrefixes, prefix) == modulePrefixes.end())
48  modulePrefixes.push_back(prefix);
49 }
50 
51 namespace {
52 /// This is the prefix which will be applied to a module.
53 struct PrefixInfo {
54 
55  /// The string to prefix on to the module and all of its children.
56  StringRef prefix;
57 
58  /// If true, this prefix applies to the module itself. If false, the prefix
59  /// only applies to the module's children.
60  bool inclusive;
61 };
62 } // end anonymous namespace
63 
64 /// Get the PrefixInfo for a module from a NestedPrefixModulesAnnotation on a
65 /// module. If the module is not annotated, the prefix returned will be empty.
66 static PrefixInfo getPrefixInfo(Operation *module) {
67  AnnotationSet annotations(module);
68 
69  // Get the annotation from the module.
70  auto anno = annotations.getAnnotation(prefixModulesAnnoClass);
71  if (!anno)
72  return {"", false};
73 
74  // Get the prefix from the annotation.
75  StringRef prefix = "";
76  if (auto prefixAttr = anno.getMember<StringAttr>("prefix"))
77  prefix = prefixAttr.getValue();
78 
79  // Get the inclusive flag from the annotation.
80  bool inclusive = false;
81  if (auto inclusiveAttr = anno.getMember<BoolAttr>("inclusive"))
82  inclusive = inclusiveAttr.getValue();
83 
84  return {prefix, inclusive};
85 }
86 
87 /// If there is an inclusive prefix attached to the module, return it.
88 static StringRef getPrefix(Operation *module) {
89  auto prefixInfo = getPrefixInfo(module);
90  if (prefixInfo.inclusive)
91  return prefixInfo.prefix;
92  return "";
93 }
94 
95 /// This pass finds modules annotated with NestedPrefixAnnotation and prefixes
96 /// module names using the string stored in the annotation. This pass prefixes
97 /// every module instantiated under the annotated root module's hierarchy. If a
98 /// module is instantiated under two different prefix hierarchies, it will be
99 /// duplicated and each module will have one prefix applied.
100 namespace {
101 class PrefixModulesPass
102  : public circt::firrtl::impl::PrefixModulesBase<PrefixModulesPass> {
103  void removeDeadAnnotations(StringAttr moduleName, Operation *op);
104  void renameModuleBody(std::string prefix, StringRef oldName,
105  FModuleOp module);
106  void renameModule(FModuleOp module);
107  void renameExtModule(FExtModuleOp extModule);
108  void renameMemModule(FMemModuleOp memModule);
109  void runOnOperation() override;
110 
111  /// Mutate Grand Central Interface definitions (an Annotation on the circuit)
112  /// with a field "prefix" containing the prefix for that annotation. This
113  /// relies on information built up during renameModule and stored in
114  /// interfacePrefixMap.
115  void prefixGrandCentralInterfaces();
116 
117  /// This is a map from a module name to new prefixes to be applied.
118  PrefixMap prefixMap;
119 
120  /// A map of Grand Central interface ID to prefix.
121  DenseMap<Attribute, std::string> interfacePrefixMap;
122 
123  /// Cached instance graph analysis.
124  InstanceGraph *instanceGraph = nullptr;
125 
126  /// Cached nla table analysis.
127  NLATable *nlaTable = nullptr;
128 
129  /// Boolean keeping track of any name changes.
130  bool anythingChanged = false;
131 };
132 } // namespace
133 
134 /// When a module is cloned, it carries with it all non-local annotations. This
135 /// function will remove all non-local annotations from the clone with a path
136 /// that doesn't match.
137 void PrefixModulesPass::removeDeadAnnotations(StringAttr moduleName,
138  Operation *op) {
139  // A predicate to check if an annotation can be removed. If there is a
140  // reference to a NLA, the NLA should either contain this module in its path,
141  // if its an InstanceOp. Else, it must exist at the leaf of the NLA. Otherwise
142  // the NLA reference can be removed, since its a spurious annotation, result
143  // of cloning the original module.
144  auto canRemoveAnno = [&](Annotation anno, Operation *op) -> bool {
145  auto nla = anno.getMember("circt.nonlocal");
146  if (!nla)
147  return false;
148  auto nlaName = cast<FlatSymbolRefAttr>(nla).getAttr();
149  auto nlaOp = nlaTable->getNLA(nlaName);
150  if (!nlaOp) {
151  op->emitError("cannot find HierPathOp :" + nlaName.getValue());
152  signalPassFailure();
153  return false;
154  }
155 
156  bool isValid = false;
157  if (isa<InstanceOp>(op))
158  isValid = nlaOp.hasModule(moduleName);
159  else
160  isValid = nlaOp.leafMod() == moduleName;
161  return !isValid;
162  };
164  op, std::bind(canRemoveAnno, std::placeholders::_2, op));
166  op, std::bind(canRemoveAnno, std::placeholders::_1, op));
167 }
168 
169 /// Applies the prefix to the module. This will update the required prefixes of
170 /// any referenced module in the prefix map.
171 void PrefixModulesPass::renameModuleBody(std::string prefix, StringRef oldName,
172  FModuleOp module) {
173  auto *context = module.getContext();
174 
175  // If we are renaming the body of this module, we need to mark that we have
176  // changed the IR. If we are prefixing with the empty string, then nothing has
177  // changed yet.
178  if (!prefix.empty())
179  anythingChanged = true;
180  StringAttr thisMod = module.getNameAttr();
181 
182  // Remove spurious NLA references from the module ports and the module itself.
183  // Some of the NLA references become invalid after a module is cloned, based
184  // on the instance.
185  removeDeadAnnotations(thisMod, module);
186 
187  mlir::AttrTypeReplacer replacer;
188  replacer.addReplacement(
189  [&](hw::InnerRefAttr innerRef) -> std::pair<Attribute, WalkResult> {
190  StringAttr moduleName = innerRef.getModule();
191  StringAttr symName = innerRef.getName();
192 
193  StringAttr newTarget;
194  if (moduleName == oldName) {
195  newTarget = module.getNameAttr();
196  } else {
197  auto target = instanceGraph->lookup(moduleName)->getModule();
198  newTarget = StringAttr::get(context, prefix + getPrefix(target) +
199  target.getModuleName());
200  }
201  return {hw::InnerRefAttr::get(newTarget, symName), WalkResult::skip()};
202  });
203 
204  module.getBody().walk([&](Operation *op) {
205  // Remove spurious NLA references either on a leaf op, or the InstanceOp.
206  removeDeadAnnotations(thisMod, op);
207 
208  if (auto memOp = dyn_cast<MemOp>(op)) {
209  StringAttr newPrefix;
210  if (auto oldPrefix = memOp->getAttrOfType<StringAttr>("prefix"))
211  newPrefix = StringAttr::get(context, prefix + oldPrefix.getValue());
212  else
213  newPrefix = StringAttr::get(context, prefix);
214  memOp->setAttr("prefix", newPrefix);
215  } else if (auto instanceOp = dyn_cast<InstanceOp>(op)) {
216  auto target = instanceOp.getReferencedModule(*instanceGraph);
217 
218  // Skip all external modules, unless one of the following conditions
219  // is true:
220  // - This is a Grand Central Data Tap
221  // - This is a Grand Central Mem Tap
222  if (auto *extModule = dyn_cast_or_null<FExtModuleOp>(&target)) {
223  auto isDataTap =
225  auto isMemTap = AnnotationSet::forPort(*extModule, 0)
227  if (!isDataTap && !isMemTap)
228  return;
229  }
230 
231  // Record that we must prefix the target module with the current prefix.
232  recordPrefix(prefixMap, target.getModuleName(), prefix);
233 
234  // Fixup this instance op to use the prefixed module name. Note that the
235  // referenced FModuleOp will be renamed later.
236  auto newTarget = StringAttr::get(context, prefix + getPrefix(target) +
237  target.getModuleName());
238  AnnotationSet instAnnos(instanceOp);
239  // If the instance has HierPathOp, then update its module name also.
240  // There can be multiple HierPathOps attached to the instance op.
241 
242  StringAttr oldModName = instanceOp.getModuleNameAttr().getAttr();
243  // Update the NLAs that apply on this InstanceOp.
244  for (Annotation anno : instAnnos) {
245  if (auto nla = anno.getMember("circt.nonlocal")) {
246  auto nlaName = cast<FlatSymbolRefAttr>(nla).getAttr();
247  nlaTable->updateModuleInNLA(nlaName, oldModName, newTarget);
248  }
249  }
250  // Now get the NLAs that pass through the InstanceOp and update them also.
251  DenseSet<hw::HierPathOp> instNLAs;
252  nlaTable->getInstanceNLAs(instanceOp, instNLAs);
253  for (auto nla : instNLAs)
254  nlaTable->updateModuleInNLA(nla, oldModName, newTarget);
255 
256  instanceOp.setModuleNameAttr(FlatSymbolRefAttr::get(context, newTarget));
257  } else {
258  replacer.replaceElementsIn(op);
259  }
260  });
261 }
262 
263 /// Apply all required renames to the current module. This will update the
264 /// prefix map for any referenced module.
265 void PrefixModulesPass::renameModule(FModuleOp module) {
266  // If the module is annotated to have a prefix, it will be applied after the
267  // parent's prefix.
268  auto prefixInfo = getPrefixInfo(module);
269  auto innerPrefix = prefixInfo.prefix;
270 
271  // Remove the annotation from the module.
273 
274  // We only add the annotated prefix to the module name if it is inclusive.
275  auto oldName = module.getName().str();
276  std::string moduleName =
277  (prefixInfo.inclusive ? innerPrefix + oldName : oldName).str();
278 
279  auto &prefixes = prefixMap[module.getName()];
280 
281  // If there are no required prefixes of this module, then this module is a
282  // top-level module, and there is an implicit requirement that it has an empty
283  // prefix. This empty prefix will be applied to all modules instantiated by
284  // this module.
285  if (prefixes.empty())
286  prefixes.push_back("");
287 
288  auto &firstPrefix = prefixes.front();
289 
290  auto fixNLAsRootedAt = [&](StringAttr oldModName, StringAttr newModuleName) {
291  DenseSet<hw::HierPathOp> nlas;
292  nlaTable->getNLAsInModule(oldModName, nlas);
293  for (auto n : nlas)
294  if (n.root() == oldModName)
295  nlaTable->updateModuleInNLA(n, oldModName, newModuleName);
296  };
297  // Rename the module for each required prefix. This will clone the module
298  // once for each prefix but the first.
299  OpBuilder builder(module);
300  builder.setInsertionPointAfter(module);
301  auto oldModName = module.getNameAttr();
302  for (auto &outerPrefix : llvm::drop_begin(prefixes)) {
303  auto moduleClone = cast<FModuleOp>(builder.clone(*module));
304  std::string newModName = outerPrefix + moduleName;
305  auto newModNameAttr = StringAttr::get(module.getContext(), newModName);
306  moduleClone.setName(newModNameAttr);
307  // It is critical to add the new module to the NLATable, otherwise the
308  // rename operation would fail.
309  nlaTable->addModule(moduleClone);
310  fixNLAsRootedAt(oldModName, newModNameAttr);
311  // Each call to this function could invalidate the `prefixes` reference.
312  renameModuleBody((outerPrefix + innerPrefix).str(), oldName, moduleClone);
313  }
314 
315  auto prefixFull = (firstPrefix + innerPrefix).str();
316  auto newModuleName = firstPrefix + moduleName;
317  auto newModuleNameAttr = StringAttr::get(module.getContext(), newModuleName);
318 
319  // The first prefix renames the module in place. There is always at least 1
320  // prefix.
321  module.setName(newModuleNameAttr);
322  nlaTable->addModule(module);
323  fixNLAsRootedAt(oldModName, newModuleNameAttr);
324  renameModuleBody(prefixFull, oldName, module);
325 
326  AnnotationSet annotations(module);
327  SmallVector<Annotation, 1> newAnnotations;
328  annotations.removeAnnotations([&](Annotation anno) {
329  if (anno.getClass() == dutAnnoClass) {
330  anno.setMember("prefix", builder.getStringAttr(prefixFull));
331  newAnnotations.push_back(anno);
332  return true;
333  }
334 
335  // If this module contains a Grand Central interface, then also apply
336  // renames to that, but only if there are prefixes to apply.
337  if (anno.getClass() == companionAnnoClass)
338  interfacePrefixMap[anno.getMember<IntegerAttr>("id")] = prefixFull;
339  return false;
340  });
341 
342  // If any annotations were updated, then update the annotations on the module.
343  if (!newAnnotations.empty()) {
344  annotations.addAnnotations(newAnnotations);
345  annotations.applyToOperation(module);
346  }
347 }
348 
349 /// Apply prefixes from the `prefixMap` to an external module. No modifications
350 /// are made if there are no prefixes for this external module. If one prefix
351 /// exists, then the external module will be updated in place. If multiple
352 /// prefixes exist, then the original external module will be updated in place
353 /// and prefixes _after_ the first will cause the module to be cloned
354 /// ("duplicated" in Scala FIRRTL Compiler terminology). The logic of this
355 /// member function is the same as `renameModule` except that there is no module
356 /// body to recursively update.
357 void PrefixModulesPass::renameExtModule(FExtModuleOp extModule) {
358  // Lookup prefixes for this module. If none exist, bail out.
359  auto &prefixes = prefixMap[extModule.getName()];
360  if (prefixes.empty())
361  return;
362 
363  OpBuilder builder(extModule);
364  builder.setInsertionPointAfter(extModule);
365 
366  // Function to apply an outer prefix to an external module. If the module has
367  // an optional "defname" (a name that will be used to generate Verilog), also
368  // update the defname.
369  auto applyPrefixToNameAndDefName = [&](FExtModuleOp &extModule,
370  StringRef prefix) {
371  extModule.setName((prefix + extModule.getName()).str());
372  if (auto defname = extModule.getDefname())
373  extModule->setAttr("defname", builder.getStringAttr(prefix + *defname));
374  };
375 
376  // Duplicate the external module if there is more than one prefix.
377  for (auto &prefix : llvm::drop_begin(prefixes)) {
378  auto duplicate = cast<FExtModuleOp>(builder.clone(*extModule));
379  applyPrefixToNameAndDefName(duplicate, prefix);
380  }
381 
382  // Update the original module with a new prefix.
383  applyPrefixToNameAndDefName(extModule, prefixes.front());
384 }
385 
386 /// Apply prefixes from the `prefixMap` to a memory module.
387 void PrefixModulesPass::renameMemModule(FMemModuleOp memModule) {
388  // Lookup prefixes for this module. If none exist, bail out.
389  auto &prefixes = prefixMap[memModule.getName()];
390  if (prefixes.empty())
391  return;
392 
393  OpBuilder builder(memModule);
394  builder.setInsertionPointAfter(memModule);
395 
396  // Duplicate the external module if there is more than one prefix.
397  auto originalName = memModule.getName();
398  for (auto &prefix : llvm::drop_begin(prefixes)) {
399  auto duplicate = cast<FMemModuleOp>(builder.clone(*memModule));
400  duplicate.setName((prefix + originalName).str());
401  removeDeadAnnotations(duplicate.getNameAttr(), duplicate);
402  }
403 
404  // Update the original module with a new prefix.
405  memModule.setName((prefixes.front() + originalName).str());
406  removeDeadAnnotations(memModule.getNameAttr(), memModule);
407 }
408 
409 /// Mutate circuit-level annotations to add prefix information to Grand Central
410 /// (SystemVerilog) interfaces. Add a "prefix" field to each interface
411 /// definition (an annotation with class "AugmentedBundleType") that holds the
412 /// prefix that was determined during runOnModule. It is assumed that this
413 /// field did not exist before.
414 void PrefixModulesPass::prefixGrandCentralInterfaces() {
415  // Early exit if no interfaces need prefixes.
416  if (interfacePrefixMap.empty())
417  return;
418 
419  auto circuit = getOperation();
420  OpBuilder builder(circuit);
421 
422  SmallVector<Attribute> newCircuitAnnotations;
423  for (auto anno : AnnotationSet(circuit)) {
424  // Only mutate this annotation if it is an AugmentedBundleType and
425  // interfacePrefixMap has prefix information for it.
426  StringRef prefix;
427  if (anno.isClass(augmentedBundleTypeClass)) {
428  if (auto id = anno.getMember<IntegerAttr>("id"))
429  prefix = interfacePrefixMap[id];
430  }
431 
432  // Nothing to do. Copy the annotation.
433  if (prefix.empty()) {
434  newCircuitAnnotations.push_back(anno.getDict());
435  continue;
436  }
437 
438  // Add a "prefix" field with the prefix for this interface. This is safe to
439  // put at the back and do a `getWithSorted` because the last field is
440  // conveniently called "name".
441  NamedAttrList newAnno(anno.getDict().getValue());
442  newAnno.append("prefix", builder.getStringAttr(prefix));
443  newCircuitAnnotations.push_back(
444  DictionaryAttr::getWithSorted(builder.getContext(), newAnno));
445  }
446 
447  // Overwrite the old circuit annotation with the new one created here.
448  AnnotationSet(newCircuitAnnotations, builder.getContext())
449  .applyToOperation(circuit);
450 }
451 
452 void PrefixModulesPass::runOnOperation() {
453  auto *context = &getContext();
454  instanceGraph = &getAnalysis<InstanceGraph>();
455  nlaTable = &getAnalysis<NLATable>();
456  auto circuitOp = getOperation();
457 
458  // If the main module is prefixed, we have to update the CircuitOp.
459  auto mainModule = instanceGraph->getTopLevelModule();
460  auto prefix = getPrefix(mainModule);
461  if (!prefix.empty()) {
462  auto oldModName = mainModule.getModuleNameAttr();
463  auto newMainModuleName =
464  StringAttr::get(context, (prefix + circuitOp.getName()).str());
465  circuitOp.setNameAttr(newMainModuleName);
466 
467  // Now update all the NLAs that have the top level module symbol.
468  nlaTable->renameModule(oldModName, newMainModuleName);
469  for (auto n : nlaTable->lookup(oldModName))
470  if (n.root() == oldModName)
471  nlaTable->updateModuleInNLA(n, oldModName, newMainModuleName);
472  }
473 
474  // Walk all Modules in a top-down order. For each module, look at the list of
475  // required prefixes to be applied.
476  DenseSet<InstanceGraphNode *> visited;
477  for (auto *current : *instanceGraph) {
478  for (auto &node : llvm::inverse_post_order_ext(current, visited)) {
479  if (auto module = dyn_cast<FModuleOp>(*node->getModule()))
480  renameModule(module);
481  if (auto extModule = dyn_cast<FExtModuleOp>(*node->getModule()))
482  renameExtModule(extModule);
483  if (auto memModule = dyn_cast<FMemModuleOp>(*node->getModule()))
484  renameMemModule(memModule);
485  }
486  }
487 
488  // Update any interface definitions if needed.
489  prefixGrandCentralInterfaces();
490 
491  prefixMap.clear();
492  interfacePrefixMap.clear();
493  if (!anythingChanged)
494  markAllAnalysesPreserved();
495 }
496 
497 std::unique_ptr<mlir::Pass> circt::firrtl::createPrefixModulesPass() {
498  return std::make_unique<PrefixModulesPass>();
499 }
static void recordPrefix(PrefixMap &prefixMap, StringRef moduleName, std::string prefix)
Insert a string into the end of vector if the string is not already present.
llvm::DenseMap< StringRef, std::vector< std::string > > PrefixMap
This maps a FModuleOp to a list of all prefixes that need to be applied.
static PrefixInfo getPrefixInfo(Operation *module)
Get the PrefixInfo for a module from a NestedPrefixModulesAnnotation on a module.
static StringRef getPrefix(Operation *module)
If there is an inclusive prefix attached to the module, return it.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool applyToOperation(Operation *op) const
Store the annotations in this set in an operation's annotations attribute, overwriting any existing a...
bool hasAnnotation(StringRef className) const
Return true if we have an annotation with the specified class name.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
static AnnotationSet forPort(FModuleLike op, size_t portNo)
Get an annotation set for the specified port.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
StringRef getClass() const
Return the 'class' that this annotation is representing.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
This graph tracks modules and where they are instantiated.
This table tracks nlas and what modules participate in them.
Definition: NLATable.h:29
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
constexpr const char * augmentedBundleTypeClass
constexpr const char * dutAnnoClass
constexpr const char * dataTapsBlackboxClass
constexpr const char * prefixModulesAnnoClass
constexpr const char * companionAnnoClass
constexpr const char * memTapPortClass
std::unique_ptr< mlir::Pass > createPrefixModulesPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21