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