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