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