CIRCT 20.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
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.
42StringRef NameCollisionResolver::getLegalName(StringRef originalName) {
43 return legalizeName(originalName, nextGeneratedNameIDs,
45}
46
47//===----------------------------------------------------------------------===//
48// FieldNameResolver
49//===----------------------------------------------------------------------===//
50
52 StringAttr newFieldName) {
53 renamedFieldNames[fieldName] = newFieldName;
54 nextGeneratedNameIDs.insert({newFieldName, 0});
55}
56
57StringAttr 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,
75
76 auto newFieldNameAttr = StringAttr::get(fieldName.getContext(), newFieldName);
77
78 setRenamedFieldName(fieldName, newFieldNameAttr);
79 return newFieldNameAttr;
80}
81
82std::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
99namespace circt {
100namespace 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.
108public:
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
115private:
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.
142static 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.
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.
298void 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
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()) {
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 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:68
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:55
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
Definition HWOps.cpp:539
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)
std::string getEnumFieldName(hw::EnumFieldAttr attr)
Returns the field name for an enum field of a given enum field attr.
void setRenamedFieldName(StringAttr fieldName, StringAttr newFieldName)
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.
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
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.