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