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