CIRCT  20.0.0git
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 
14 #include "circt/Dialect/HW/HWOps.h"
17 #include "circt/Dialect/SV/SVOps.h"
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 
25 namespace circt {
26 struct LoweringOptions;
27 
28 namespace ExportVerilog {
29 class GlobalNameResolver;
31 
32 /// Check if the value is from read of a wire or reg or is a port.
33 bool 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.
38 StringAttr 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 
64 private:
65  friend class GlobalNameResolver;
67  GlobalNameTable(const GlobalNameTable &) = delete;
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 
110 private:
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 
125  const LoweringOptions &options)
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 
136 private:
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.
160 struct OpFileInfo {
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.
170 struct 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.
190 class 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 
203 public:
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) {
215  assert(fStream);
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 
258 private:
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  };
268  struct LocationRange {
272  };
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.
285 public:
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(pointerData.is<Operation *>() && "shouldn't already be a string");
313  length = value.size();
314  void *data = malloc(length);
315  memcpy(data, value.data(), length);
316  pointerData = (const void *)data;
317  }
318 
319  // These move just fine.
321  : pointerData(rhs.pointerData), length(rhs.length) {
322  rhs.pointerData = (Operation *)nullptr;
323  }
324 
325  /// Verilog output location information for entry. This is
326  /// required since each entry can be emitted in parallel.
328 
329 private:
331  void operator=(const StringOrOpToEmit &) = delete;
332  PointerUnion<Operation *, const void *> pointerData;
333  size_t length;
334 };
335 
336 /// Mapping from symbols to file operations.
337 using FileMapping = DenseMap<StringAttr, Operation *>;
338 
339 /// Mapping from symbols to file operations.
340 using FragmentMapping = DenseMap<StringAttr, emit::FragmentOp>;
341 
342 /// This class tracks the top-level state for the emitters, which is built and
343 /// then shared across all per-file emissions that happen in parallel.
345  /// The MLIR module to emit.
346  ModuleOp designOp;
347 
348  /// The main file that collects all operations that are neither replicated
349  /// per-file ops nor specifically assigned to a file.
351 
352  /// The additional files to emit, with the output file name as the key into
353  /// the map.
354  llvm::MapVector<StringAttr, FileInfo> files;
355 
356  /// The various file lists and their contents to emit
357  llvm::StringMap<SmallVector<StringAttr>> fileLists;
358 
359  /// A list of operations replicated in each output file (e.g., `sv.verbatim`
360  /// or `sv.ifdef` without dedicated output file).
361  SmallVector<Operation *, 0> replicatedOps;
362 
363  /// Whether any error has been encountered during emission.
364  std::atomic<bool> encounteredError = {};
365 
366  /// A cache of symbol -> defining ops built once and used by each of the
367  /// verilog module emitters. This is built at "gatherFiles" time.
369 
370  // Emitter options extracted from the top-level module.
372 
373  /// This is a set is populated at "gather" time, containing the hw.module
374  /// operations that have a sv.bind in them.
375  SmallPtrSet<Operation *, 8> modulesContainingBinds;
376 
377  /// Information about renamed global symbols, parameters, etc.
379 
380  /// Tracks the referenceable files through their symbol.
382 
383  /// Tracks referenceable files through their symbol.
385 
389  globalNames(std::move(globalNames)) {}
390  void gatherFiles(bool separateModules);
391 
392  using EmissionList = std::vector<StringOrOpToEmit>;
393 
394  void collectOpsForFile(const FileInfo &fileInfo, EmissionList &thingsToEmit,
395  bool emitHeader = false);
396  void emitOps(EmissionList &thingsToEmit, llvm::formatted_raw_ostream &os,
397  StringAttr fileName, bool parallelize);
398 };
399 
400 //===----------------------------------------------------------------------===//
401 // Other utilities
402 //===----------------------------------------------------------------------===//
403 
404 /// Return true for operations that must always be inlined into a containing
405 /// expression for correctness.
406 static inline bool isExpressionAlwaysInline(Operation *op) {
407  // We need to emit array indexes inline per verilog "lvalue" semantics.
408  if (isa<sv::ArrayIndexInOutOp>(op) || isa<sv::StructFieldInOutOp>(op) ||
409  isa<sv::IndexedPartSelectInOutOp>(op) || isa<sv::ReadInOutOp>(op))
410  return true;
411 
412  // An SV interface modport is a symbolic name that is always inlined.
413  if (isa<sv::GetModportOp>(op) || isa<sv::ReadInterfaceSignalOp>(op))
414  return true;
415 
416  // XMRs can't be spilled if they are on the lhs. Conservatively never spill
417  // them.
418  if (isa<sv::XMROp, sv::XMRRefOp>(op))
419  return true;
420 
421  if (isa<sv::SampledOp>(op))
422  return true;
423 
424  return false;
425 }
426 
427 StringRef getSymOpName(Operation *symOp);
428 
429 /// Return whether an operation is a constant.
430 static inline bool isConstantExpression(Operation *op) {
431  return isa<hw::ConstantOp, sv::ConstantXOp, sv::ConstantZOp,
432  sv::ConstantStrOp>(op);
433 }
434 
435 /// This predicate returns true if the specified operation is considered a
436 /// potentially inlinable Verilog expression. These nodes always have a single
437 /// result, but may have side effects (e.g. `sv.verbatim.expr.se`).
438 /// MemoryEffects should be checked if a client cares.
439 bool isVerilogExpression(Operation *op);
440 
441 /// Return true if this is a zero bit type, e.g. a zero bit integer or array
442 /// thereof.
443 bool isZeroBitType(Type type);
444 
445 /// Return true if this expression should be emitted inline into any statement
446 /// that uses it.
447 bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options);
448 
449 /// Generates the macros used by instance choices.
450 LogicalResult lowerHWInstanceChoices(mlir::ModuleOp module);
451 
452 /// For each module we emit, do a prepass over the structure, pre-lowering and
453 /// otherwise rewriting operations we don't want to emit.
454 LogicalResult prepareHWModule(Block &block, const LoweringOptions &options);
455 LogicalResult prepareHWModule(hw::HWEmittableModuleLike module,
456  const LoweringOptions &options);
457 
458 void pruneZeroValuedLogic(hw::HWEmittableModuleLike module);
459 
460 /// Rewrite module names and interfaces to not conflict with each other or with
461 /// Verilog keywords.
462 GlobalNameTable legalizeGlobalNames(ModuleOp topLevel,
463  const LoweringOptions &options);
464 
465 } // namespace ExportVerilog
466 } // namespace circt
467 
468 #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
Operation * getOperation() const
If the value is an Operation*, return it. Otherwise return null.
void operator=(const StringOrOpToEmit &)=delete
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.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
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
StringAttr getEnumPrefix(Type type) const
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.