CIRCT  19.0.0git
LegalizeNames.cpp
Go to the documentation of this file.
1 //===- LegalizeNames.cpp - Name Legalization for ExportVerilog ------------===//
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 renames modules and variables to avoid conflicts with keywords and other
10 // declarations.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "ExportVerilogInternals.h"
16 #include "circt/Dialect/HW/HWOps.h"
19 #include "mlir/IR/Threading.h"
20 #include "llvm/ADT/TypeSwitch.h"
21 
22 using namespace circt;
23 using namespace sv;
24 using namespace hw;
25 using namespace ExportVerilog;
26 
27 //===----------------------------------------------------------------------===//
28 // NameCollisionResolver
29 //===----------------------------------------------------------------------===//
30 
31 /// Given a name that may have collisions or invalid symbols, return a
32 /// replacement name to use, or null if the original name was ok.
33 StringRef NameCollisionResolver::getLegalName(StringRef originalName) {
34  return legalizeName(originalName, nextGeneratedNameIDs,
35  options.caseInsensitiveKeywords);
36 }
37 
38 //===----------------------------------------------------------------------===//
39 // FieldNameResolver
40 //===----------------------------------------------------------------------===//
41 
42 void FieldNameResolver::setRenamedFieldName(StringAttr fieldName,
43  StringAttr newFieldName) {
44  renamedFieldNames[fieldName] = newFieldName;
45  nextGeneratedNameIDs.insert({newFieldName, 0});
46 }
47 
48 StringAttr FieldNameResolver::getRenamedFieldName(StringAttr fieldName) {
49  auto it = renamedFieldNames.find(fieldName);
50  if (it != renamedFieldNames.end())
51  return it->second;
52 
53  // If a field name is not verilog name or used already, we have to rename it.
54  bool hasToBeRenamed =
55  !sv::isNameValid(fieldName.getValue(), options.caseInsensitiveKeywords) ||
56  nextGeneratedNameIDs.contains(fieldName.getValue());
57 
58  if (!hasToBeRenamed) {
59  setRenamedFieldName(fieldName, fieldName);
60  return fieldName;
61  }
62 
63  StringRef newFieldName =
64  sv::legalizeName(fieldName.getValue(), nextGeneratedNameIDs,
65  options.caseInsensitiveKeywords);
66 
67  auto newFieldNameAttr = StringAttr::get(fieldName.getContext(), newFieldName);
68 
69  setRenamedFieldName(fieldName, newFieldNameAttr);
70  return newFieldNameAttr;
71 }
72 
73 std::string FieldNameResolver::getEnumFieldName(hw::EnumFieldAttr attr) {
74  auto aliasType = attr.getType().getValue().dyn_cast<hw::TypeAliasType>();
75  if (!aliasType)
76  return attr.getField().getValue().str();
77 
78  auto fieldStr = attr.getField().getValue().str();
79  if (auto prefix = globalNames.getEnumPrefix(aliasType))
80  return (prefix.getValue() + "_" + fieldStr).str();
81 
82  // No prefix registered, just use the bare field name.
83  return fieldStr;
84 }
85 
86 //===----------------------------------------------------------------------===//
87 // GlobalNameResolver
88 //===----------------------------------------------------------------------===//
89 
90 namespace circt {
91 namespace ExportVerilog {
92 /// This class keeps track of modules and interfaces that need to be renamed, as
93 /// well as module ports, parameters, declarations and verif labels that need to
94 /// be renamed. This can happen either due to conflicts between them or due to
95 /// a conflict with a Verilog keyword.
96 ///
97 /// Once constructed, this is immutable.
99 public:
100  /// Construct a GlobalNameResolver and perform name legalization of the
101  /// module/interfaces, port/parameter and declaration names.
102  GlobalNameResolver(mlir::ModuleOp topLevel, const LoweringOptions &options);
103 
104  GlobalNameTable takeGlobalNameTable() { return std::move(globalNameTable); }
105 
106 private:
107  /// Check to see if the port names of the specified module conflict with
108  /// keywords or themselves. If so, add the replacement names to
109  /// globalNameTable.
110  void legalizeModuleNames(HWModuleOp module);
111  void legalizeInterfaceNames(InterfaceOp interface);
112 
113  // Gathers prefixes of enum types by inspecting typescopes in the module.
114  void gatherEnumPrefixes(mlir::ModuleOp topLevel);
115 
116  /// Set of globally visible names, to ensure uniqueness.
118 
119  /// This keeps track of globally visible names like module parameters.
121 
123  void operator=(const GlobalNameResolver &) = delete;
124 
125  // Handle to lowering options.
127 };
128 } // namespace ExportVerilog
129 } // namespace circt
130 
131 // This function legalizes local names in the given module.
133  const LoweringOptions &options,
134  const GlobalNameTable &globalNameTable) {
135  // A resolver for a local name collison.
136  NameCollisionResolver nameResolver(options);
137  // Register names used by parameters.
138  for (auto param : module.getParameters())
139  nameResolver.insertUsedName(globalNameTable.getParameterVerilogName(
140  module, param.cast<ParamDeclAttr>().getName()));
141 
142  auto *ctxt = module.getContext();
143 
144  auto verilogNameAttr = StringAttr::get(ctxt, "hw.verilogName");
145  // Legalize the port names.
146  auto ports = module.getPortList();
147  SmallVector<Attribute> newNames(ports.size());
148  bool updated = false;
149  for (auto [idx, port] : llvm::enumerate(ports)) {
150  auto verilogName = port.attrs.get(verilogNameAttr);
151  if (verilogName) {
152  auto newName = StringAttr::get(
153  ctxt, nameResolver.getLegalName(verilogName.cast<StringAttr>()));
154  newNames[idx] = newName;
155  if (verilogName != newName)
156  updated = true;
157  continue;
158  }
159  auto oldName = ports[idx].name;
160  auto newName = nameResolver.getLegalName(oldName);
161  // Set the verilogName attr only if the name is updated.
162  if (newName != oldName) {
163  newNames[idx] = StringAttr::get(ctxt, newName);
164  updated = true;
165  } else
166  newNames[idx] = {};
167  }
168  if (updated)
169  module.setPortAttrs(verilogNameAttr, newNames);
170 
171  SmallVector<std::pair<Operation *, StringAttr>> nameEntries;
172  // Legalize the value names. We first mark existing hw.verilogName attrs as
173  // being used, and then resolve names of declarations.
174  module.walk([&](Operation *op) {
175  if (!isa<HWModuleOp>(op)) {
176  // If there is a hw.verilogName attr, mark names as used.
177  if (auto name = op->getAttrOfType<StringAttr>(verilogNameAttr)) {
178  nameResolver.insertUsedName(
179  op->getAttrOfType<StringAttr>(verilogNameAttr));
180  } else if (isa<sv::WireOp, hw::WireOp, RegOp, LogicOp, LocalParamOp,
181  hw::InstanceOp, sv::InterfaceInstanceOp, sv::GenerateOp>(
182  op)) {
183  // Otherwise, get a verilog name via `getSymOpName`.
184  nameEntries.emplace_back(
185  op, StringAttr::get(op->getContext(), getSymOpName(op)));
186  } else if (auto forOp = dyn_cast<ForOp>(op)) {
187  nameEntries.emplace_back(op, forOp.getInductionVarNameAttr());
188  } else if (isa<AssertOp, AssumeOp, CoverOp, AssertConcurrentOp,
189  AssumeConcurrentOp, CoverConcurrentOp, verif::AssertOp,
190  verif::CoverOp, verif::AssumeOp>(op)) {
191  // Notice and renamify the labels on verification statements.
192  if (auto labelAttr = op->getAttrOfType<StringAttr>("label"))
193  nameEntries.emplace_back(op, labelAttr);
194  else if (options.enforceVerifLabels) {
195  // If labels are required for all verif statements, get a default
196  // name from verificaiton kinds.
197  StringRef defaultName =
198  llvm::TypeSwitch<Operation *, StringRef>(op)
199  .Case<AssertOp, AssertConcurrentOp, verif::AssertOp>(
200  [](auto) { return "assert"; })
201  .Case<CoverOp, CoverConcurrentOp, verif::CoverOp>(
202  [](auto) { return "cover"; })
203  .Case<AssumeOp, AssumeConcurrentOp, verif::AssumeOp>(
204  [](auto) { return "assume"; });
205  nameEntries.emplace_back(
206  op, StringAttr::get(op->getContext(), defaultName));
207  }
208  }
209  }
210  });
211 
212  for (auto [op, nameAttr] : nameEntries) {
213  auto newName = nameResolver.getLegalName(nameAttr);
214  assert(!newName.empty() && "must have a valid name");
215  // Add a legalized name to "hw.verilogName" attribute.
216  op->setAttr(verilogNameAttr, nameAttr.getValue() == newName
217  ? nameAttr
218  : StringAttr::get(ctxt, newName));
219  }
220 }
221 
222 /// Construct a GlobalNameResolver and do the initial scan to populate and
223 /// unique the module/interfaces and port/parameter names.
224 GlobalNameResolver::GlobalNameResolver(mlir::ModuleOp topLevel,
225  const LoweringOptions &options)
226  : globalNameResolver(options), options(options) {
227  // Register the names of external modules which we cannot rename. This has to
228  // occur in a first pass separate from the modules and interfaces which we are
229  // actually allowed to rename, in order to ensure that we don't accidentally
230  // rename a module that later collides with an extern module.
231  for (auto &op : *topLevel.getBody()) {
232  // Note that external modules *often* have name collisions, because they
233  // correspond to the same verilog module with different parameters.
234  if (isa<HWModuleExternOp>(op) || isa<HWModuleGeneratedOp>(op)) {
235  auto name = getVerilogModuleNameAttr(&op).getValue();
237  op.emitError("name \"")
238  << name << "\" is not allowed in Verilog output";
240  }
241  }
242 
243  // Legalize module and interface names.
244  for (auto &op : *topLevel.getBody()) {
245  if (auto module = dyn_cast<HWModuleOp>(op)) {
246  legalizeModuleNames(module);
247  continue;
248  }
249 
250  // Legalize the name of the interface itself, as well as any signals and
251  // modports within it.
252  if (auto interface = dyn_cast<InterfaceOp>(op)) {
253  legalizeInterfaceNames(interface);
254  continue;
255  }
256  }
257 
258  // Legalize names in HW modules parallelly.
259  mlir::parallelForEach(
260  topLevel.getContext(), topLevel.getOps<HWModuleOp>(), [&](auto module) {
261  legalizeModuleLocalNames(module, options, globalNameTable);
262  });
263 
264  // Gather enum prefixes.
265  gatherEnumPrefixes(topLevel);
266 }
267 
268 // Gathers prefixes of enum types by investigating typescopes in the module.
269 void GlobalNameResolver::gatherEnumPrefixes(mlir::ModuleOp topLevel) {
270  auto *ctx = topLevel.getContext();
271  for (auto typeScope : topLevel.getOps<hw::TypeScopeOp>()) {
272  for (auto typeDecl : typeScope.getOps<hw::TypedeclOp>()) {
273  auto enumType = typeDecl.getType().dyn_cast<hw::EnumType>();
274  if (!enumType)
275  continue;
276 
277  // Register the enum type as the alias type of the typedecl, since this is
278  // how users will request the prefix.
279  globalNameTable.enumPrefixes[typeDecl.getAliasType()] =
280  StringAttr::get(ctx, typeDecl.getPreferredName());
281  }
282  }
283 }
284 
285 /// Check to see if the port names of the specified module conflict with
286 /// keywords or themselves. If so, add the replacement names to
287 /// globalNameTable.
289  MLIRContext *ctxt = module.getContext();
290  // If the module's symbol itself conflicts, then set a "verilogName" attribute
291  // on the module to reflect the name we need to use.
292  StringRef oldName = module.getName();
293  auto newName = globalNameResolver.getLegalName(oldName);
294  if (newName != oldName)
295  module->setAttr("verilogName", StringAttr::get(ctxt, newName));
296 
297  NameCollisionResolver nameResolver(options);
298  // Legalize the parameter names.
299  for (auto param : module.getParameters()) {
300  auto paramAttr = param.cast<ParamDeclAttr>();
301  auto newName = nameResolver.getLegalName(paramAttr.getName());
302  if (newName != paramAttr.getName().getValue())
303  globalNameTable.addRenamedParam(module, paramAttr.getName(), newName);
304  }
305 }
306 
307 void GlobalNameResolver::legalizeInterfaceNames(InterfaceOp interface) {
308  MLIRContext *ctxt = interface.getContext();
309  auto verilogNameAttr = StringAttr::get(ctxt, "hw.verilogName");
310  auto newName = globalNameResolver.getLegalName(interface.getName());
311  if (newName != interface.getName())
312  interface->setAttr(verilogNameAttr, StringAttr::get(ctxt, newName));
313 
314  NameCollisionResolver localNames(options);
315  // Rename signals and modports.
316  for (auto &op : *interface.getBodyBlock()) {
317  if (isa<InterfaceSignalOp, InterfaceModportOp>(op)) {
318  auto name = SymbolTable::getSymbolName(&op).getValue();
319  auto newName = localNames.getLegalName(name);
320  if (newName != name)
321  op.setAttr(verilogNameAttr, StringAttr::get(ctxt, newName));
322  }
323  }
324 }
325 
326 //===----------------------------------------------------------------------===//
327 // Public interface
328 //===----------------------------------------------------------------------===//
329 
330 /// Rewrite module names and interfaces to not conflict with each other or with
331 /// Verilog keywords.
334  const LoweringOptions &options) {
335  GlobalNameResolver resolver(topLevel, options);
336  return resolver.takeGlobalNameTable();
337 }
assert(baseType &&"element must be base type")
static void legalizeModuleLocalNames(HWModuleOp module, const LoweringOptions &options, const GlobalNameTable &globalNameTable)
This class keeps track of modules and interfaces that need to be renamed, as well as module ports,...
GlobalNameTable globalNameTable
This keeps track of globally visible names like module parameters.
void legalizeModuleNames(HWModuleOp module)
Check to see if the port names of the specified module conflict with keywords or themselves.
void operator=(const GlobalNameResolver &)=delete
void gatherEnumPrefixes(mlir::ModuleOp topLevel)
GlobalNameResolver(const GlobalNameResolver &)=delete
NameCollisionResolver globalNameResolver
Set of globally visible names, to ensure uniqueness.
void legalizeInterfaceNames(InterfaceOp interface)
Definition: sv.py:68
Definition: sv.py:35
GlobalNameTable legalizeGlobalNames(ModuleOp topLevel, const LoweringOptions &options)
Rewrite module names and interfaces to not conflict with each other or with Verilog keywords.
StringRef getSymOpName(Operation *symOp)
Return the verilog name of the operations that can define a symbol.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
Definition: HWOps.cpp:534
StringRef legalizeName(llvm::StringRef name, llvm::StringMap< size_t > &nextGeneratedNameIDs, bool caseInsensitiveKeywords)
Legalize the specified name for use in SV output.
bool isNameValid(llvm::StringRef name, bool caseInsensitiveKeywords)
Check if a name is valid for use in SV output by only containing characters allowed in SV identifiers...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: hw.py:1
Definition: sv.py:1
This class keeps track of global names at the module/interface level.
DenseMap< Type, StringAttr > enumPrefixes
StringRef getParameterVerilogName(Operation *module, StringAttr paramName) const
Return the string to use for the specified parameter name in the specified module.
void addRenamedParam(Operation *module, StringAttr oldName, StringRef newName)
void insertUsedName(StringRef name)
Insert a string as an already-used name.
StringRef getLegalName(StringRef originalName)
Given a name that may have collisions or invalid symbols, return a replacement name to use,...
Options which control the emission from CIRCT to Verilog.
bool enforceVerifLabels
If true, verification statements like assert, assume, and cover will always be emitted with a label.
bool caseInsensitiveKeywords
If true, then unique names that collide with keywords case insensitively.