CIRCT 23.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 (auto genForOp = dyn_cast<GenerateForOp>(op)) {
210 nameEntries.emplace_back(op, genForOp.getInductionVarNameAttr());
211 } else if (isa<AssertOp, AssumeOp, CoverOp, AssertConcurrentOp,
212 AssumeConcurrentOp, CoverConcurrentOp, AssertPropertyOp,
213 AssumePropertyOp, CoverPropertyOp, verif::AssertOp,
214 verif::CoverOp, verif::AssumeOp>(op)) {
215 // Notice and renamify the labels on verification statements.
216 if (auto labelAttr = op->getAttrOfType<StringAttr>("label"))
217 nameEntries.emplace_back(op, labelAttr);
218 else if (options.enforceVerifLabels) {
219 // If labels are required for all verif statements, get a default
220 // name from verificaiton kinds.
221 StringRef defaultName =
222 llvm::TypeSwitch<Operation *, StringRef>(op)
223 .Case<AssertOp, AssertConcurrentOp, AssertPropertyOp,
224 verif::AssertOp>([](auto) { return "assert"; })
225 .Case<CoverOp, CoverConcurrentOp, CoverPropertyOp,
226 verif::CoverOp>([](auto) { return "cover"; })
227 .Case<AssumeOp, AssumeConcurrentOp, AssumePropertyOp,
228 verif::AssumeOp>([](auto) { return "assume"; });
229 nameEntries.emplace_back(
230 op, StringAttr::get(op->getContext(), defaultName));
231 }
232 }
233 }
234 });
235
236 for (auto [op, nameAttr] : nameEntries) {
237 auto newName = nameResolver.getLegalName(nameAttr);
238 assert(!newName.empty() && "must have a valid name");
239 // Add a legalized name to "hw.verilogName" attribute.
240 op->setAttr(verilogNameAttr, nameAttr.getValue() == newName
241 ? nameAttr
242 : StringAttr::get(ctxt, newName));
243 }
244}
245
246/// Construct a GlobalNameResolver and do the initial scan to populate and
247/// unique the module/interfaces and port/parameter names.
249 const LoweringOptions &options)
250 : globalNameResolver(options), options(options) {
251 // Register the names of external modules which we cannot rename. This has to
252 // occur in a first pass separate from the modules and interfaces which we are
253 // actually allowed to rename, in order to ensure that we don't accidentally
254 // rename a module that later collides with an extern module.
255 for (auto &op : *topLevel.getBody()) {
256 // Note that external modules *often* have name collisions, because they
257 // correspond to the same verilog module with different parameters.
258 if (isa<HWModuleExternOp>(op) || isa<HWModuleGeneratedOp>(op)) {
259 auto name = getVerilogModuleNameAttr(&op).getValue();
261 op.emitError("name \"")
262 << name << "\" is not allowed in Verilog output";
264 } else if (auto reservedNamesOp = dyn_cast<sv::ReserveNamesOp>(op)) {
265 for (StringAttr name :
266 reservedNamesOp.getReservedNames().getAsRange<StringAttr>()) {
267 globalNameTable.reservedNames.insert(name);
269 }
270 }
271 }
272
273 // Legalize module and interface names.
274 for (auto &op : *topLevel.getBody()) {
275 if (auto module = dyn_cast<HWModuleOp>(op)) {
276 legalizeModuleNames(module);
277 continue;
278 }
279
280 // Legalize the name of the interface itself, as well as any signals and
281 // modports within it.
282 if (auto interface = dyn_cast<InterfaceOp>(op)) {
283 legalizeInterfaceNames(interface);
284 continue;
285 }
286 }
287
288 // Legalize names in HW modules parallelly.
289 mlir::parallelForEach(
290 topLevel.getContext(), topLevel.getOps<HWEmittableModuleLike>(),
291 [&](auto module) {
292 legalizeModuleLocalNames(module, options, globalNameTable);
293 });
294
295 // Gather enum prefixes.
296 gatherEnumPrefixes(topLevel);
297}
298
299// Gathers prefixes of enum types by investigating typescopes in the module.
300void GlobalNameResolver::gatherEnumPrefixes(mlir::ModuleOp topLevel) {
301 auto *ctx = topLevel.getContext();
302 for (auto typeScope : topLevel.getOps<hw::TypeScopeOp>()) {
303 for (auto typeDecl : typeScope.getOps<hw::TypedeclOp>()) {
304 auto enumType = dyn_cast<hw::EnumType>(typeDecl.getType());
305 if (!enumType)
306 continue;
307
308 // Register the enum type as the alias type of the typedecl, since this is
309 // how users will request the prefix.
310 globalNameTable.enumPrefixes[typeDecl.getAliasType()] =
311 StringAttr::get(ctx, typeDecl.getPreferredName());
312 }
313 }
314}
315
316/// Check to see if the port names of the specified module conflict with
317/// keywords or themselves. If so, add the replacement names to
318/// globalNameTable.
320 MLIRContext *ctxt = module.getContext();
321 // If the module's symbol itself conflicts, then set a "verilogName" attribute
322 // on the module to reflect the name we need to use.
323 StringRef oldName = module.getName();
324 auto newName = globalNameResolver.getLegalName(oldName);
325 if (newName != oldName)
326 module->setAttr("verilogName", StringAttr::get(ctxt, newName));
327
328 NameCollisionResolver nameResolver(options);
329 // Legalize the parameter names.
330 for (auto param : module.getParameters()) {
331 auto paramAttr = cast<ParamDeclAttr>(param);
332 auto newName = nameResolver.getLegalName(paramAttr.getName());
333 if (newName != paramAttr.getName().getValue())
334 globalNameTable.addRenamedParam(module, paramAttr.getName(), newName);
335 }
336}
337
339 MLIRContext *ctxt = interface.getContext();
340 auto verilogNameAttr = StringAttr::get(ctxt, "hw.verilogName");
341 auto newName = globalNameResolver.getLegalName(interface.getName());
342 if (newName != interface.getName())
343 interface->setAttr(verilogNameAttr, StringAttr::get(ctxt, newName));
344
345 NameCollisionResolver localNames(options);
346 // Rename signals and modports.
347 for (auto &op : *interface.getBodyBlock()) {
348 if (isa<InterfaceSignalOp, InterfaceModportOp>(op)) {
349 auto name = SymbolTable::getSymbolName(&op).getValue();
350 auto newName = localNames.getLegalName(name);
351 if (newName != name)
352 op.setAttr(verilogNameAttr, StringAttr::get(ctxt, newName));
353 }
354 }
355}
356
358 MLIRContext *ctxt = func.getContext();
359 if (auto verilogName = func.getVerilogName()) {
361 return;
362 }
363 auto newName = globalNameResolver.getLegalName(func.getName());
364 if (newName != func.getName()) {
365 func.setVerilogName(StringAttr::get(ctxt, newName));
366 }
367}
368
369//===----------------------------------------------------------------------===//
370// Public interface
371//===----------------------------------------------------------------------===//
372
373/// Rewrite module names and interfaces to not conflict with each other or with
374/// Verilog keywords.
377 const LoweringOptions &options) {
378 GlobalNameResolver resolver(topLevel, options);
379 return resolver.takeGlobalNameTable();
380}
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)
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.