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