CIRCT 24.0.0git
Loading...
Searching...
No Matches
ExportVerilogInternals.h
Go to the documentation of this file.
1//===- ExportVerilogInternals.h - Shared Internal Impl Details --*- C++ -*-===//
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#ifndef CONVERSION_EXPORTVERILOG_EXPORTVERILOGINTERNAL_H
10#define CONVERSION_EXPORTVERILOG_EXPORTVERILOGINTERNAL_H
11
19#include "mlir/IR/Location.h"
20#include "llvm/ADT/MapVector.h"
21#include "llvm/ADT/SmallPtrSet.h"
22#include "llvm/Support/FormattedStream.h"
23#include <atomic>
24
25namespace circt {
26struct LoweringOptions;
27
28namespace ExportVerilog {
31
32/// Check if the value is from read of a wire or reg or is a port.
33bool isSimpleReadOrPort(Value v);
34
35/// Given an expression that is spilled into a temporary wire, try to
36/// synthesize a better name than "_T_42" based on the structure of the
37/// expression.
38StringAttr inferStructuralNameForTemporary(Value expr);
39
40/// This class keeps track of global names at the module/interface level.
41/// It is built in a global pass over the entire design and then frozen to allow
42/// concurrent accesses.
45
46 /// Return the string to use for the specified parameter name in the specified
47 /// module. Parameters may be renamed for a variety of reasons (e.g.
48 /// conflicting with ports or Verilog keywords), and this returns the
49 /// legalized name to use.
50 StringRef getParameterVerilogName(Operation *module,
51 StringAttr paramName) const {
52 auto it = renamedParams.find(std::make_pair(module, paramName));
53 return (it != renamedParams.end() ? it->second : paramName).getValue();
54 }
55
56 StringAttr getEnumPrefix(Type type) const {
57 auto it = enumPrefixes.find(type);
58 return it != enumPrefixes.end() ? it->second : StringAttr();
59 }
60
61 /// Return the owning package and legalized member name, or nullopt if absent.
62 std::optional<std::pair<Operation *, StringAttr>>
63 getPackageEnumField(hw::EnumFieldAttr field) const;
64
65 // Add the set of reserved names to a resolver.
66 void addReservedNames(NameCollisionResolver &nameResolver) const;
67
68private:
69 friend class GlobalNameResolver;
72 void operator=(const GlobalNameTable &) = delete;
73
74 void addRenamedParam(Operation *module, StringAttr oldName,
75 StringRef newName) {
76 renamedParams[{module, oldName}] =
77 StringAttr::get(oldName.getContext(), newName);
78 }
79
80 /// This contains entries for any parameters that got renamed. The key is a
81 /// moduleop/paramName tuple, the value is the name to use.
82 DenseMap<std::pair<Operation *, Attribute>, StringAttr> renamedParams;
83
84 // This contains prefixes for any typedecl'd enum types. Keys are type-aliases
85 // of enum types.
86 DenseMap<Type, StringAttr> enumPrefixes;
87
88 /// Map (enum alias, original member) to (owning package, legalized member).
89 DenseMap<std::pair<Type, StringAttr>, std::pair<Operation *, StringAttr>>
91
92 /// List of names which are marked as reserved for any name.
93 DenseSet<StringAttr> reservedNames;
94};
95
96//===----------------------------------------------------------------------===//
97// NameCollisionResolver
98//===----------------------------------------------------------------------===//
99
102
103 /// Given a name that may have collisions or invalid symbols, return a
104 /// replacement name to use, or the original name if it was ok.
105 StringRef getLegalName(StringRef originalName);
106 StringRef getLegalName(StringAttr originalName) {
107 return getLegalName(originalName.getValue());
108 }
109
110 /// Insert a string as an already-used name.
111 void insertUsedName(StringRef name) {
112 nextGeneratedNameIDs.insert({name, 0});
113 }
114
115 /// Handle to LoweringOptions.
117
118private:
119 /// A map from used names to numeric suffix used as uniquification agent when
120 /// resolving conflicts.
121 llvm::StringMap<size_t> nextGeneratedNameIDs;
122
124 void operator=(const NameCollisionResolver &) = delete;
125};
126
127//===----------------------------------------------------------------------===//
128// FieldNameResolver
129//===----------------------------------------------------------------------===//
130
135
136 StringAttr getRenamedFieldName(StringAttr fieldName);
137
138 /// Returns the field name for an enum field of a given enum field attr. In
139 /// case a prefix can be inferred for the provided enum type (the enum type is
140 /// a type alias), the prefix will be applied. If not, the raw field name
141 /// is returned.
142 std::string getEnumFieldName(hw::EnumFieldAttr attr,
143 Operation *currentPackage = nullptr);
144
145private:
146 void setRenamedFieldName(StringAttr fieldName, StringAttr newFieldName);
147
148 /// Those contain entries for field names and types respectively. Struct types
149 /// have names as field names, which must be renamed if they conflict with
150 /// verilog keywords.
151 DenseMap<StringAttr, StringAttr> renamedFieldNames;
152
153 /// A map from used names to numeric suffix used as uniquification agent when
154 /// resolving conflicts.
155 llvm::StringMap<size_t> nextGeneratedNameIDs;
156
157 // Handle to the global name table.
159
160 // Handle to lowering options.
162};
163
164//===----------------------------------------------------------------------===//
165// SharedEmitterState
166//===----------------------------------------------------------------------===//
167
168/// Information to control the emission of a single operation into a file.
170 /// The operation to be emitted.
171 Operation *op;
172
173 /// Where among the replicated per-file operations the `op` above should be
174 /// emitted.
175 size_t position = 0;
176};
177
178/// Information to control the emission of a list of operations into a file.
179struct FileInfo {
180 /// The operations to be emitted into a separate file, and where among the
181 /// replicated per-file operations the operation should be emitted.
182 SmallVector<OpFileInfo, 1> ops;
183
184 /// Whether to emit the replicated per-file operations.
185 bool emitReplicatedOps = true;
186
187 /// Whether to include this file as part of the emitted file list.
188 bool addToFilelist = true;
189
190 /// If true, the file is a header.
191 bool isHeader = false;
192
193 /// If true, the file is known to be (system) verilog source code.
194 /// This flag is used to distinguish verilog from other files such as json.
195 bool isVerilog = true;
196};
197
198/// Track the output verilog line,column number information for every op.
199class OpLocMap {
200 /// Record the output location from where the op begins to print.
201 void addBeginLoc(Operation *op) {
202 map[op].emplace_back(LocationRange(LineColPair(*fStream)));
203 }
204 /// Record the output location where the op ends to print.
205 void addEndLoc(Operation *op) {
206 assert(!map[op].empty());
207 assert(map[op].back().begin.isValid());
208 assert(!map[op].back().end.isValid());
209 map[op].back().end = LineColPair(*fStream);
210 }
211
212public:
213 /// Data that is unique to each callback. The op and whether its a begin or
214 /// end location.
215 using DataType = std::pair<Operation *, bool>;
216
217 OpLocMap(llvm::formatted_raw_ostream &fStream) : fStream(&fStream) {}
218 OpLocMap() = default;
219
220 /// Set the output stream.
221 void setStream(llvm::formatted_raw_ostream &f) { fStream = &f; }
222 /// Callback operator, invoked on the print events indicated by `data`.
223 void operator()(DataType data) {
225 auto beginPrint = data.second;
226 auto *op = data.first;
227 if (beginPrint)
228 addBeginLoc(op);
229 else
230 addEndLoc(op);
231 }
232
233 /// Called after the verilog has been exported and the corresponding locations
234 /// are recorded in the map.
235 void updateIRWithLoc(unsigned lineOffset, StringAttr fileName,
236 MLIRContext *context) {
237 if (map.empty())
238 return;
239 if (!verilogLineAttr) {
240 verilogLineAttr = StringAttr::get(context, "verilogLocations");
241 metadataAttr = StringAttr::get(context, "Range");
242 }
243 for (auto &[op, locations] : map) {
244 // An operation can have multiple verilog locations.
245 SmallVector<Location> verilogLocs;
246 for (auto &loc : locations) {
247 // Create a location range attribute.
248 SmallVector<Location, 2> beginEndPair;
249 assert(loc.begin.isValid() && loc.end.isValid());
250 beginEndPair.emplace_back(mlir::FileLineColLoc::get(
251 fileName, loc.begin.line + lineOffset, loc.begin.col));
252 beginEndPair.emplace_back(mlir::FileLineColLoc::get(
253 fileName, loc.end.line + lineOffset, loc.end.col));
254 // Add it to the verilog locations of the op.
255 verilogLocs.emplace_back(
256 mlir::FusedLoc::get(context, beginEndPair, metadataAttr));
257 }
258 // Update the location attribute with a fused loc of the original location
259 // and verilog locations.
260 op->setLoc(mlir::FusedLoc::get(
261 context, {op->getLoc(), mlir::FusedLoc::get(context, verilogLocs,
262 verilogLineAttr)}));
263 }
264 }
265 void clear() { map.clear(); }
266
267private:
268 struct LineColPair {
269 unsigned line = ~0U;
270 unsigned col = ~0U;
271 LineColPair() = default;
272 /// Given an output stream, store the current offset.
273 LineColPair(llvm::formatted_raw_ostream &s)
274 : line(s.getLine()), col(s.getColumn()) {}
275 bool isValid() { return (line != -1U && col != -1U); }
276 };
282 using Locations = SmallVector<LocationRange, 2>;
283 /// Map to store the verilog locations for each op.
284 DenseMap<Operation *, Locations> map;
285 /// The corresponding output stream, which provides the current print location
286 /// on the stream.
287 llvm::formatted_raw_ostream *fStream;
288 /// Cache to store string attributes.
290};
291
292/// This class wraps an operation or a fixed string that should be emitted.
294public:
295 explicit StringOrOpToEmit(Operation *op) : pointerData(op), length(~0ULL) {}
296
297 explicit StringOrOpToEmit(StringRef string) {
298 pointerData = (Operation *)nullptr;
299 setString(string);
300 }
301
303 if (const void *ptr = pointerData.dyn_cast<const void *>())
304 free(const_cast<void *>(ptr));
305 }
306
307 /// If the value is an Operation*, return it. Otherwise return null.
308 Operation *getOperation() const {
309 return pointerData.dyn_cast<Operation *>();
310 }
311
312 /// If the value wraps a string, return it. Otherwise return null.
313 StringRef getStringData() const {
314 if (const void *ptr = pointerData.dyn_cast<const void *>())
315 return StringRef((const char *)ptr, length);
316 return StringRef();
317 }
318
319 /// This method transforms the entry from an operation to a string value.
320 void setString(StringRef value) {
321 assert(llvm::isa<Operation *>(pointerData) &&
322 "shouldn't already be a string");
323 length = value.size();
324 void *data = malloc(length);
325 memcpy(data, value.data(), length);
326 pointerData = (const void *)data;
327 }
328
329 // These move just fine.
331 : pointerData(rhs.pointerData), length(rhs.length) {
332 rhs.pointerData = (Operation *)nullptr;
333 }
334
335 /// Verilog output location information for entry. This is
336 /// required since each entry can be emitted in parallel.
338
339private:
341 void operator=(const StringOrOpToEmit &) = delete;
342 PointerUnion<Operation *, const void *> pointerData;
343 size_t length;
344};
345
346/// Mapping from symbols to file operations.
347using FileMapping = DenseMap<StringAttr, Operation *>;
348
349/// Mapping from symbols to file operations.
350using FragmentMapping = DenseMap<StringAttr, emit::FragmentOp>;
351
352/// This class tracks the top-level state for the emitters, which is built and
353/// then shared across all per-file emissions that happen in parallel.
355 /// The MLIR module to emit.
356 ModuleOp designOp;
357
358 /// The main file that collects all operations that are neither replicated
359 /// per-file ops nor specifically assigned to a file.
361
362 /// The additional files to emit, with the output file name as the key into
363 /// the map.
365
366 /// The various file lists and their contents to emit
367 llvm::StringMap<SmallVector<StringAttr>> fileLists;
368
369 /// A list of operations replicated in each output file (e.g., `sv.verbatim`
370 /// or `sv.ifdef` without dedicated output file).
371 SmallVector<Operation *, 0> replicatedOps;
372
373 /// Whether any error has been encountered during emission.
374 std::atomic<bool> encounteredError = {};
375
376 /// A cache of symbol -> defining ops built once and used by each of the
377 /// verilog module emitters. This is built at "gatherFiles" time.
379
380 // Emitter options extracted from the top-level module.
382
383 /// This is a set is populated at "gather" time, containing the hw.module
384 /// operations that have a sv.bind in them.
385 SmallPtrSet<Operation *, 8> modulesContainingBinds;
386
387 /// Information about renamed global symbols, parameters, etc.
389
390 /// Tracks the referenceable files through their symbol.
392
393 /// Tracks referenceable files through their symbol.
395
400 void gatherFiles(bool separateModules);
401
402 using EmissionList = std::vector<StringOrOpToEmit>;
403
404 void collectOpsForFile(const FileInfo &fileInfo, EmissionList &thingsToEmit,
405 bool emitHeader = false);
406 void emitOps(EmissionList &thingsToEmit, llvm::formatted_raw_ostream &os,
407 StringAttr fileName, bool parallelize);
408};
409
410//===----------------------------------------------------------------------===//
411// Other utilities
412//===----------------------------------------------------------------------===//
413
414/// Return true for operations that must always be inlined into a containing
415/// expression for correctness.
416static inline bool isExpressionAlwaysInline(Operation *op) {
417 // We need to emit array indexes inline per verilog "lvalue" semantics.
418 if (isa<sv::ArrayIndexInOutOp>(op) || isa<sv::StructFieldInOutOp>(op) ||
419 isa<sv::IndexedPartSelectInOutOp>(op) || isa<sv::ReadInOutOp>(op))
420 return true;
421
422 // An SV interface modport is a symbolic name that is always inlined.
423 if (isa<sv::GetModportOp>(op) || isa<sv::ReadInterfaceSignalOp>(op))
424 return true;
425
426 // XMRs can't be spilled if they are on the lhs. Conservatively never spill
427 // them.
428 if (isa<sv::XMROp, sv::XMRRefOp>(op))
429 return true;
430
431 if (isa<sv::SampledOp>(op))
432 return true;
433
434 return false;
435}
436
437StringRef getSymOpName(Operation *symOp);
438
439/// Return whether an operation is a constant.
440static inline bool isConstantExpression(Operation *op) {
441 return isa<hw::ConstantOp, sv::ConstantXOp, sv::ConstantZOp,
442 sv::ConstantStrOp>(op);
443}
444
445/// This predicate returns true if the specified operation is considered a
446/// potentially inlinable Verilog expression. These nodes always have a single
447/// result, but may have side effects (e.g. `sv.verbatim.expr.se`).
448/// MemoryEffects should be checked if a client cares.
449bool isVerilogExpression(Operation *op);
450
451/// Return true if this is a zero bit type, e.g. a zero bit integer or array
452/// thereof.
453bool isZeroBitType(Type type);
454
455/// Return true if this expression should be emitted inline into any statement
456/// that uses it.
457bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options);
458
459/// Generates the macros used by instance choices.
460LogicalResult lowerHWInstanceChoices(mlir::ModuleOp module);
461
462/// For each module we emit, do a prepass over the structure, pre-lowering and
463/// otherwise rewriting operations we don't want to emit.
464LogicalResult prepareHWModule(Block &block, const LoweringOptions &options);
465LogicalResult prepareHWModule(hw::HWEmittableModuleLike module,
466 const LoweringOptions &options);
467
468void pruneZeroValuedLogic(hw::HWEmittableModuleLike module);
469
470/// Rewrite module names and interfaces to not conflict with each other or with
471/// Verilog keywords.
472GlobalNameTable legalizeGlobalNames(ModuleOp topLevel,
473 const LoweringOptions &options);
474
475} // namespace ExportVerilog
476} // namespace circt
477
478#endif // CONVERSION_EXPORTVERILOG_EXPORTVERILOGINTERNAL_H
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static InstancePath empty
This class keeps track of modules and interfaces that need to be renamed, as well as module ports,...
Track the output verilog line,column number information for every op.
llvm::formatted_raw_ostream * fStream
The corresponding output stream, which provides the current print location on the stream.
DenseMap< Operation *, Locations > map
Map to store the verilog locations for each op.
void setStream(llvm::formatted_raw_ostream &f)
Set the output stream.
StringAttr verilogLineAttr
Cache to store string attributes.
void addEndLoc(Operation *op)
Record the output location where the op ends to print.
void operator()(DataType data)
Callback operator, invoked on the print events indicated by data.
std::pair< Operation *, bool > DataType
Data that is unique to each callback.
OpLocMap(llvm::formatted_raw_ostream &fStream)
void updateIRWithLoc(unsigned lineOffset, StringAttr fileName, MLIRContext *context)
Called after the verilog has been exported and the corresponding locations are recorded in the map.
SmallVector< LocationRange, 2 > Locations
void addBeginLoc(Operation *op)
Record the output location from where the op begins to print.
This class wraps an operation or a fixed string that should be emitted.
StringOrOpToEmit(const StringOrOpToEmit &)=delete
void operator=(const StringOrOpToEmit &)=delete
Operation * getOperation() const
If the value is an Operation*, return it. Otherwise return null.
OpLocMap verilogLocs
Verilog output location information for entry.
void setString(StringRef value)
This method transforms the entry from an operation to a string value.
PointerUnion< Operation *, const void * > pointerData
StringRef getStringData() const
If the value wraps a string, return it. Otherwise return null.
This stores lookup tables to make manipulating and working with the IR more efficient.
Definition HWSymCache.h:28
LogicalResult prepareHWModule(Block &block, const LoweringOptions &options)
For each module we emit, do a prepass over the structure, pre-lowering and otherwise rewriting operat...
void pruneZeroValuedLogic(hw::HWEmittableModuleLike module)
bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options)
Return true if this expression should be emitted inline into any statement that uses it.
bool isVerilogExpression(Operation *op)
This predicate returns true if the specified operation is considered a potentially inlinable Verilog ...
GlobalNameTable legalizeGlobalNames(ModuleOp topLevel, const LoweringOptions &options)
Rewrite module names and interfaces to not conflict with each other or with Verilog keywords.
StringAttr inferStructuralNameForTemporary(Value expr)
Given an expression that is spilled into a temporary wire, try to synthesize a better name than "_T_4...
DenseMap< StringAttr, Operation * > FileMapping
Mapping from symbols to file operations.
DenseMap< StringAttr, emit::FragmentOp > FragmentMapping
Mapping from symbols to file operations.
static bool isConstantExpression(Operation *op)
Return whether an operation is a constant.
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
bool isSimpleReadOrPort(Value v)
Check if the value is from read of a wire or reg or is a port.
StringRef getSymOpName(Operation *symOp)
Return the verilog name of the operations that can define a symbol.
LogicalResult lowerHWInstanceChoices(mlir::ModuleOp module)
Generates the macros used by instance choices.
static bool isExpressionAlwaysInline(Operation *op)
Return true for operations that must always be inlined into a containing expression for correctness.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
DenseMap< StringAttr, StringAttr > renamedFieldNames
Those contain entries for field names and types respectively.
StringAttr getRenamedFieldName(StringAttr fieldName)
FieldNameResolver(const GlobalNameTable &globalNames, const LoweringOptions &options)
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.
Information to control the emission of a list of operations into a file.
bool isVerilog
If true, the file is known to be (system) verilog source code.
SmallVector< OpFileInfo, 1 > ops
The operations to be emitted into a separate file, and where among the replicated per-file operations...
bool isHeader
If true, the file is a header.
bool emitReplicatedOps
Whether to emit the replicated per-file operations.
bool addToFilelist
Whether to include this file as part of the emitted file list.
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< Operation *, Attribute >, StringAttr > renamedParams
This contains entries for any parameters that got renamed.
DenseMap< std::pair< Type, StringAttr >, std::pair< Operation *, StringAttr > > packageEnumFields
Map (enum alias, original member) to (owning package, legalized member).
GlobalNameTable(const GlobalNameTable &)=delete
void operator=(const GlobalNameTable &)=delete
GlobalNameTable(GlobalNameTable &&)=default
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 operator=(const NameCollisionResolver &)=delete
void insertUsedName(StringRef name)
Insert a string as an already-used name.
NameCollisionResolver(const LoweringOptions &options)
StringRef getLegalName(StringAttr originalName)
StringRef getLegalName(StringRef originalName)
Given a name that may have collisions or invalid symbols, return a replacement name to use,...
NameCollisionResolver(const NameCollisionResolver &)=delete
const LoweringOptions & options
Handle to LoweringOptions.
Information to control the emission of a single operation into a file.
Operation * op
The operation to be emitted.
size_t position
Where among the replicated per-file operations the op above should be emitted.
LineColPair(llvm::formatted_raw_ostream &s)
Given an output stream, store the current offset.
This class tracks the top-level state for the emitters, which is built and then shared across all per...
llvm::MapVector< StringAttr, FileInfo > files
The additional files to emit, with the output file name as the key into the map.
SharedEmitterState(ModuleOp designOp, const LoweringOptions &options, GlobalNameTable globalNames)
std::vector< StringOrOpToEmit > EmissionList
FileMapping fileMapping
Tracks the referenceable files through their symbol.
hw::HWSymbolCache symbolCache
A cache of symbol -> defining ops built once and used by each of the verilog module emitters.
void collectOpsForFile(const FileInfo &fileInfo, EmissionList &thingsToEmit, bool emitHeader=false)
Given a FileInfo, collect all the replicated and designated operations that go into it and append the...
ModuleOp designOp
The MLIR module to emit.
void emitOps(EmissionList &thingsToEmit, llvm::formatted_raw_ostream &os, StringAttr fileName, bool parallelize)
Actually emit the collected list of operations and strings to the specified file.
FileInfo rootFile
The main file that collects all operations that are neither replicated per-file ops nor specifically ...
llvm::StringMap< SmallVector< StringAttr > > fileLists
The various file lists and their contents to emit.
SmallPtrSet< Operation *, 8 > modulesContainingBinds
This is a set is populated at "gather" time, containing the hw.module operations that have a sv....
std::atomic< bool > encounteredError
Whether any error has been encountered during emission.
FragmentMapping fragmentMapping
Tracks referenceable files through their symbol.
void gatherFiles(bool separateModules)
Organize the operations in the root MLIR module into output files to be generated.
SmallVector< Operation *, 0 > replicatedOps
A list of operations replicated in each output file (e.g., sv.verbatim or sv.ifdef without dedicated ...
const GlobalNameTable globalNames
Information about renamed global symbols, parameters, etc.
Options which control the emission from CIRCT to Verilog.