CIRCT  18.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 = dyn_cast<FModuleLike>(
209  *instanceGraph->getReferencedModule(instanceOp));
210 
211  // Skip all external modules, unless one of the following conditions
212  // is true:
213  // - This is a Grand Central Data Tap
214  // - This is a Grand Central Mem Tap
215  if (auto *extModule = dyn_cast_or_null<FExtModuleOp>(&target)) {
216  auto isDataTap =
218  auto isMemTap = AnnotationSet::forPort(*extModule, 0)
220  if (!isDataTap && !isMemTap)
221  return;
222  }
223 
224  // Record that we must prefix the target module with the current prefix.
225  recordPrefix(prefixMap, target.getModuleName(), prefix);
226 
227  // Fixup this instance op to use the prefixed module name. Note that the
228  // referenced FModuleOp will be renamed later.
229  auto newTarget = StringAttr::get(context, prefix + getPrefix(target) +
230  target.getModuleName());
231  AnnotationSet instAnnos(instanceOp);
232  // If the instance has HierPathOp, then update its module name also.
233  // There can be multiple HierPathOps attached to the instance op.
234 
235  StringAttr oldModName = instanceOp.getModuleNameAttr().getAttr();
236  // Update the NLAs that apply on this InstanceOp.
237  for (Annotation anno : instAnnos) {
238  if (auto nla = anno.getMember("circt.nonlocal")) {
239  auto nlaName = cast<FlatSymbolRefAttr>(nla).getAttr();
240  nlaTable->updateModuleInNLA(nlaName, oldModName, newTarget);
241  }
242  }
243  // Now get the NLAs that pass through the InstanceOp and update them also.
244  DenseSet<hw::HierPathOp> instNLAs;
245  nlaTable->getInstanceNLAs(instanceOp, instNLAs);
246  for (auto nla : instNLAs)
247  nlaTable->updateModuleInNLA(nla, oldModName, newTarget);
248 
249  instanceOp.setModuleNameAttr(FlatSymbolRefAttr::get(context, newTarget));
250  } else {
251  replacer.replaceElementsIn(op);
252  }
253  });
254 }
255 
256 /// Apply all required renames to the current module. This will update the
257 /// prefix map for any referenced module.
258 void PrefixModulesPass::renameModule(FModuleOp module) {
259  // If the module is annotated to have a prefix, it will be applied after the
260  // parent's prefix.
261  auto prefixInfo = getPrefixInfo(module);
262  auto innerPrefix = prefixInfo.prefix;
263 
264  // Remove the annotation from the module.
266 
267  // We only add the annotated prefix to the module name if it is inclusive.
268  auto oldName = module.getName().str();
269  std::string moduleName =
270  (prefixInfo.inclusive ? innerPrefix + oldName : oldName).str();
271 
272  auto &prefixes = prefixMap[module.getName()];
273 
274  // If there are no required prefixes of this module, then this module is a
275  // top-level module, and there is an implicit requirement that it has an empty
276  // prefix. This empty prefix will be applied to all modules instantiated by
277  // this module.
278  if (prefixes.empty())
279  prefixes.push_back("");
280 
281  auto &firstPrefix = prefixes.front();
282 
283  auto fixNLAsRootedAt = [&](StringAttr oldModName, StringAttr newModuleName) {
284  DenseSet<hw::HierPathOp> nlas;
285  nlaTable->getNLAsInModule(oldModName, nlas);
286  for (auto n : nlas)
287  if (n.root() == oldModName)
288  nlaTable->updateModuleInNLA(n, oldModName, newModuleName);
289  };
290  // Rename the module for each required prefix. This will clone the module
291  // once for each prefix but the first.
292  OpBuilder builder(module);
293  builder.setInsertionPointAfter(module);
294  auto oldModName = module.getNameAttr();
295  for (auto &outerPrefix : llvm::drop_begin(prefixes)) {
296  auto moduleClone = cast<FModuleOp>(builder.clone(*module));
297  std::string newModName = outerPrefix + moduleName;
298  auto newModNameAttr = StringAttr::get(module.getContext(), newModName);
299  moduleClone.setName(newModNameAttr);
300  // It is critical to add the new module to the NLATable, otherwise the
301  // rename operation would fail.
302  nlaTable->addModule(moduleClone);
303  fixNLAsRootedAt(oldModName, newModNameAttr);
304  // Each call to this function could invalidate the `prefixes` reference.
305  renameModuleBody((outerPrefix + innerPrefix).str(), oldName, moduleClone);
306  }
307 
308  auto prefixFull = (firstPrefix + innerPrefix).str();
309  auto newModuleName = firstPrefix + moduleName;
310  auto newModuleNameAttr = StringAttr::get(module.getContext(), newModuleName);
311 
312  // The first prefix renames the module in place. There is always at least 1
313  // prefix.
314  module.setName(newModuleNameAttr);
315  nlaTable->addModule(module);
316  fixNLAsRootedAt(oldModName, newModuleNameAttr);
317  renameModuleBody(prefixFull, oldName, module);
318 
319  AnnotationSet annotations(module);
320  SmallVector<Annotation, 1> newAnnotations;
321  annotations.removeAnnotations([&](Annotation anno) {
322  if (anno.getClass() == dutAnnoClass) {
323  anno.setMember("prefix", builder.getStringAttr(prefixFull));
324  newAnnotations.push_back(anno);
325  return true;
326  }
327 
328  // If this module contains a Grand Central interface, then also apply
329  // renames to that, but only if there are prefixes to apply.
330  if (anno.getClass() == companionAnnoClass)
331  interfacePrefixMap[anno.getMember<IntegerAttr>("id")] = prefixFull;
332  return false;
333  });
334 
335  // If any annotations were updated, then update the annotations on the module.
336  if (!newAnnotations.empty()) {
337  annotations.addAnnotations(newAnnotations);
338  annotations.applyToOperation(module);
339  }
340 }
341 
342 /// Apply prefixes from the `prefixMap` to an external module. No modifications
343 /// are made if there are no prefixes for this external module. If one prefix
344 /// exists, then the external module will be updated in place. If multiple
345 /// prefixes exist, then the original external module will be updated in place
346 /// and prefixes _after_ the first will cause the module to be cloned
347 /// ("duplicated" in Scala FIRRTL Compiler terminology). The logic of this
348 /// member function is the same as `renameModule` except that there is no module
349 /// body to recursively update.
350 void PrefixModulesPass::renameExtModule(FExtModuleOp extModule) {
351  // Lookup prefixes for this module. If none exist, bail out.
352  auto &prefixes = prefixMap[extModule.getName()];
353  if (prefixes.empty())
354  return;
355 
356  OpBuilder builder(extModule);
357  builder.setInsertionPointAfter(extModule);
358 
359  // Function to apply an outer prefix to an external module. If the module has
360  // an optional "defname" (a name that will be used to generate Verilog), also
361  // update the defname.
362  auto applyPrefixToNameAndDefName = [&](FExtModuleOp &extModule,
363  StringRef prefix) {
364  extModule.setName((prefix + extModule.getName()).str());
365  if (auto defname = extModule.getDefname())
366  extModule->setAttr("defname", builder.getStringAttr(prefix + *defname));
367  };
368 
369  // Duplicate the external module if there is more than one prefix.
370  for (auto &prefix : llvm::drop_begin(prefixes)) {
371  auto duplicate = cast<FExtModuleOp>(builder.clone(*extModule));
372  applyPrefixToNameAndDefName(duplicate, prefix);
373  }
374 
375  // Update the original module with a new prefix.
376  applyPrefixToNameAndDefName(extModule, prefixes.front());
377 }
378 
379 /// Apply prefixes from the `prefixMap` to a memory module.
380 void PrefixModulesPass::renameMemModule(FMemModuleOp memModule) {
381  // Lookup prefixes for this module. If none exist, bail out.
382  auto &prefixes = prefixMap[memModule.getName()];
383  if (prefixes.empty())
384  return;
385 
386  OpBuilder builder(memModule);
387  builder.setInsertionPointAfter(memModule);
388 
389  // Duplicate the external module if there is more than one prefix.
390  auto originalName = memModule.getName();
391  for (auto &prefix : llvm::drop_begin(prefixes)) {
392  auto duplicate = cast<FMemModuleOp>(builder.clone(*memModule));
393  duplicate.setName((prefix + originalName).str());
394  removeDeadAnnotations(duplicate.getNameAttr(), duplicate);
395  }
396 
397  // Update the original module with a new prefix.
398  memModule.setName((prefixes.front() + originalName).str());
399  removeDeadAnnotations(memModule.getNameAttr(), memModule);
400 }
401 
402 /// Mutate circuit-level annotations to add prefix information to Grand Central
403 /// (SystemVerilog) interfaces. Add a "prefix" field to each interface
404 /// definition (an annotation with class "AugmentedBundleType") that holds the
405 /// prefix that was determined during runOnModule. It is assumed that this
406 /// field did not exist before.
407 void PrefixModulesPass::prefixGrandCentralInterfaces() {
408  // Early exit if no interfaces need prefixes.
409  if (interfacePrefixMap.empty())
410  return;
411 
412  auto circuit = getOperation();
413  OpBuilder builder(circuit);
414 
415  SmallVector<Attribute> newCircuitAnnotations;
416  for (auto anno : AnnotationSet(circuit)) {
417  // Only mutate this annotation if it is an AugmentedBundleType and
418  // interfacePrefixMap has prefix information for it.
419  StringRef prefix;
420  if (anno.isClass(augmentedBundleTypeClass)) {
421  if (auto id = anno.getMember<IntegerAttr>("id"))
422  prefix = interfacePrefixMap[id];
423  }
424 
425  // Nothing to do. Copy the annotation.
426  if (prefix.empty()) {
427  newCircuitAnnotations.push_back(anno.getDict());
428  continue;
429  }
430 
431  // Add a "prefix" field with the prefix for this interface. This is safe to
432  // put at the back and do a `getWithSorted` because the last field is
433  // conveniently called "name".
434  NamedAttrList newAnno(anno.getDict().getValue());
435  newAnno.append("prefix", builder.getStringAttr(prefix));
436  newCircuitAnnotations.push_back(
437  DictionaryAttr::getWithSorted(builder.getContext(), newAnno));
438  }
439 
440  // Overwrite the old circuit annotation with the new one created here.
441  AnnotationSet(newCircuitAnnotations, builder.getContext())
442  .applyToOperation(circuit);
443 }
444 
445 void PrefixModulesPass::runOnOperation() {
446  auto *context = &getContext();
447  instanceGraph = &getAnalysis<InstanceGraph>();
448  nlaTable = &getAnalysis<NLATable>();
449  auto circuitOp = getOperation();
450 
451  // If the main module is prefixed, we have to update the CircuitOp.
452  auto mainModule = instanceGraph->getTopLevelModule();
453  auto prefix = getPrefix(mainModule);
454  if (!prefix.empty()) {
455  auto oldModName = mainModule.getModuleNameAttr();
456  auto newMainModuleName =
457  StringAttr::get(context, (prefix + circuitOp.getName()).str());
458  circuitOp.setNameAttr(newMainModuleName);
459 
460  // Now update all the NLAs that have the top level module symbol.
461  nlaTable->renameModule(oldModName, newMainModuleName);
462  for (auto n : nlaTable->lookup(oldModName))
463  if (n.root() == oldModName)
464  nlaTable->updateModuleInNLA(n, oldModName, newMainModuleName);
465  }
466 
467  // Walk all Modules in a top-down order. For each module, look at the list of
468  // required prefixes to be applied.
469  DenseSet<InstanceGraphNode *> visited;
470  for (auto *current : *instanceGraph) {
471  for (auto &node : llvm::inverse_post_order_ext(current, visited)) {
472  if (auto module = dyn_cast<FModuleOp>(*node->getModule()))
473  renameModule(module);
474  if (auto extModule = dyn_cast<FExtModuleOp>(*node->getModule()))
475  renameExtModule(extModule);
476  if (auto memModule = dyn_cast<FMemModuleOp>(*node->getModule()))
477  renameMemModule(memModule);
478  }
479  }
480 
481  // Update any interface definitions if needed.
482  prefixGrandCentralInterfaces();
483 
484  prefixMap.clear();
485  interfacePrefixMap.clear();
486  if (!anythingChanged)
487  markAllAnalysesPreserved();
488 }
489 
490 std::unique_ptr<mlir::Pass> circt::firrtl::createPrefixModulesPass() {
491  return std::make_unique<PrefixModulesPass>();
492 }
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:53
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()
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21