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