CIRCT 23.0.0git
Loading...
Searching...
No Matches
ExportVerilog.cpp
Go to the documentation of this file.
1//===- ExportVerilog.cpp - Verilog Emitter --------------------------------===//
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// This is the main Verilog emitter implementation.
10//
11// CAREFUL: This file covers the emission phase of `ExportVerilog` which mainly
12// walks the IR and produces output. Do NOT modify the IR during this walk, as
13// emission occurs in a highly parallel fashion. If you need to modify the IR,
14// do so during the preparation phase which lives in `PrepareForEmission.cpp`.
15//
16//===----------------------------------------------------------------------===//
17
35#include "circt/Support/LLVM.h"
37#include "circt/Support/Path.h"
42#include "mlir/IR/BuiltinOps.h"
43#include "mlir/IR/ImplicitLocOpBuilder.h"
44#include "mlir/IR/Location.h"
45#include "mlir/IR/Threading.h"
46#include "mlir/Interfaces/FunctionImplementation.h"
47#include "mlir/Pass/PassManager.h"
48#include "mlir/Support/FileUtilities.h"
49#include "llvm/ADT/MapVector.h"
50#include "llvm/ADT/STLExtras.h"
51#include "llvm/ADT/StringSet.h"
52#include "llvm/ADT/TypeSwitch.h"
53#include "llvm/Support/FileSystem.h"
54#include "llvm/Support/FormattedStream.h"
55#include "llvm/Support/Path.h"
56#include "llvm/Support/SaveAndRestore.h"
57#include "llvm/Support/ToolOutputFile.h"
58#include "llvm/Support/raw_ostream.h"
59
60namespace circt {
61#define GEN_PASS_DEF_EXPORTSPLITVERILOG
62#define GEN_PASS_DEF_EXPORTVERILOG
63#include "circt/Conversion/Passes.h.inc"
64} // namespace circt
65
66using namespace circt;
67using namespace comb;
68using namespace hw;
69using namespace sv;
70using namespace ExportVerilog;
71
72using namespace pretty;
73
74#define DEBUG_TYPE "export-verilog"
75
76StringRef circtHeader = "circt_header.svh";
77StringRef circtHeaderInclude = "`include \"circt_header.svh\"\n";
78
79namespace {
80/// This enum keeps track of the precedence level of various binary operators,
81/// where a lower number binds tighter.
82enum VerilogPrecedence {
83 // Normal precedence levels.
84 Symbol, // Atomic symbol like "foo" and {a,b}
85 Selection, // () , [] , :: , ., $signed()
86 Unary, // Unary operators like ~foo
87 Multiply, // * , / , %
88 Addition, // + , -
89 Shift, // << , >>, <<<, >>>
90 Comparison, // > , >= , < , <=
91 Equality, // == , !=
92 And, // &
93 Xor, // ^ , ^~
94 Or, // |
95 AndShortCircuit, // &&
96 Conditional, // ? :
97
98 LowestPrecedence, // Sentinel which is always the lowest precedence.
99};
100
101/// This enum keeps track of whether the emitted subexpression is signed or
102/// unsigned as seen from the Verilog language perspective.
103enum SubExprSignResult { IsSigned, IsUnsigned };
104
105/// This is information precomputed about each subexpression in the tree we
106/// are emitting as a unit.
107struct SubExprInfo {
108 /// The precedence of this expression.
109 VerilogPrecedence precedence;
110
111 /// The signedness of the expression.
112 SubExprSignResult signedness;
113
114 SubExprInfo(VerilogPrecedence precedence, SubExprSignResult signedness)
115 : precedence(precedence), signedness(signedness) {}
116};
117
118} // end anonymous namespace
119
120//===----------------------------------------------------------------------===//
121// Helper routines
122//===----------------------------------------------------------------------===//
123
124static TypedAttr getInt32Attr(MLIRContext *ctx, uint32_t value) {
125 return Builder(ctx).getI32IntegerAttr(value);
126}
127
128static TypedAttr getIntAttr(MLIRContext *ctx, Type t, const APInt &value) {
129 return Builder(ctx).getIntegerAttr(t, value);
130}
131
132/// Return true for nullary operations that are better emitted multiple
133/// times as inline expression (when they have multiple uses) rather than having
134/// a temporary wire.
135///
136/// This can only handle nullary expressions, because we don't want to replicate
137/// subtrees arbitrarily.
138static bool isDuplicatableNullaryExpression(Operation *op) {
139 // We don't want wires that are just constants aesthetically.
140 if (isConstantExpression(op))
141 return true;
142
143 // If this is a small verbatim expression with no side effects, duplicate it
144 // inline.
145 if (isa<VerbatimExprOp>(op)) {
146 if (op->getNumOperands() == 0 &&
147 op->getAttrOfType<StringAttr>("format_string").getValue().size() <= 32)
148 return true;
149 }
150
151 // Always duplicate XMRs into their use site.
152 if (isa<XMRRefOp>(op))
153 return true;
154
155 // If this is a macro reference without side effects, allow duplication.
156 if (isa<MacroRefExprOp>(op))
157 return true;
158
159 return false;
160}
161
162// Return true if the expression can be inlined even when the op has multiple
163// uses. Be careful to add operations here since it might cause exponential
164// emission without proper restrictions.
165static bool isDuplicatableExpression(Operation *op) {
166 if (op->getNumOperands() == 0)
168
169 // It is cheap to inline extract op.
170 if (isa<comb::ExtractOp, hw::StructExtractOp, hw::UnionExtractOp>(op))
171 return true;
172
173 // We only inline array_get with a constant, port or wire index.
174 if (auto array = dyn_cast<hw::ArrayGetOp>(op)) {
175 auto *indexOp = array.getIndex().getDefiningOp();
176 if (!indexOp || isa<ConstantOp>(indexOp))
177 return true;
178 if (auto read = dyn_cast<ReadInOutOp>(indexOp)) {
179 auto *readSrc = read.getInput().getDefiningOp();
180 // A port or wire is ok to duplicate reads.
181 return !readSrc || isa<sv::WireOp, LogicOp>(readSrc);
182 }
183
184 return false;
185 }
186
187 return false;
188}
189
190/// Return the verilog name of the operations that can define a symbol.
191/// Legalized names are added to "hw.verilogName" so look up it when the
192/// attribute already exists.
193StringRef ExportVerilog::getSymOpName(Operation *symOp) {
194 // Typeswitch of operation types which can define a symbol.
195 // If legalizeNames has renamed it, then the attribute must be set.
196 if (auto attr = symOp->getAttrOfType<StringAttr>("hw.verilogName"))
197 return attr.getValue();
198 return TypeSwitch<Operation *, StringRef>(symOp)
199 .Case<HWModuleOp, HWModuleExternOp, HWModuleGeneratedOp,
200 sv::SVVerbatimModuleOp, FuncOp>(
201 [](Operation *op) { return getVerilogModuleName(op); })
202 .Case<SVVerbatimSourceOp>([](SVVerbatimSourceOp op) {
203 return op.getVerilogNameAttr().getValue();
204 })
205 .Case<InterfaceOp>([&](InterfaceOp op) {
206 return getVerilogModuleNameAttr(op).getValue();
207 })
208 .Case<InterfaceSignalOp>(
209 [&](InterfaceSignalOp op) { return op.getSymName(); })
210 .Case<InterfaceModportOp>(
211 [&](InterfaceModportOp op) { return op.getSymName(); })
212 .Default([&](Operation *op) {
213 if (auto attr = op->getAttrOfType<StringAttr>("name"))
214 return attr.getValue();
215 if (auto attr = op->getAttrOfType<StringAttr>("instanceName"))
216 return attr.getValue();
217 if (auto attr = op->getAttrOfType<StringAttr>("sv.namehint"))
218 return attr.getValue();
219 if (auto attr =
220 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
221 return attr.getValue();
222 return StringRef("");
223 });
224}
225
226/// Emits a known-safe token that is legal when indexing into singleton arrays.
227template <typename PPS>
228static void emitZeroWidthIndexingValue(PPS &os) {
229 os << "/*Zero width*/ 1\'b0";
230}
231
232/// Return the verilog name of the port for the module.
233static StringRef getPortVerilogName(Operation *module, size_t portArgNum) {
234 auto hml = cast<HWModuleLike>(module);
235 return hml.getPort(portArgNum).getVerilogName();
236}
237
238/// Return the verilog name of the port for the module.
239static StringRef getInputPortVerilogName(Operation *module, size_t portArgNum) {
240 auto hml = cast<HWModuleLike>(module);
241 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
242 if (auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
243 if (auto updatedName = attrs.getAs<StringAttr>("hw.verilogName"))
244 return updatedName.getValue();
245 return hml.getHWModuleType().getPortName(pId);
246}
247
248/// This predicate returns true if the specified operation is considered a
249/// potentially inlinable Verilog expression. These nodes always have a single
250/// result, but may have side effects (e.g. `sv.verbatim.expr.se`).
251/// MemoryEffects should be checked if a client cares.
253 // These are SV dialect expressions.
254 if (isa<ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
255 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
256 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp, SFormatFOp,
257 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
258 UnpackedOpenArrayCastOp, ConcatStrOp>(op))
259 return true;
260
261 // These are Verif dialect expressions.
262 if (isa<verif::ContractOp>(op))
263 return true;
264
265 // All HW combinational logic ops and SV expression ops are Verilog
266 // expressions.
267 return isCombinational(op) || isExpression(op);
268}
269
270// NOLINTBEGIN(misc-no-recursion)
271/// Push this type's dimension into a vector.
272static void getTypeDims(
273 SmallVectorImpl<Attribute> &dims, Type type, Location loc,
274 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
275 if (auto integer = hw::type_dyn_cast<IntegerType>(type)) {
276 if (integer.getWidth() != 1)
277 dims.push_back(getInt32Attr(type.getContext(), integer.getWidth()));
278 return;
279 }
280 if (auto array = hw::type_dyn_cast<ArrayType>(type)) {
281 dims.push_back(getInt32Attr(type.getContext(), array.getNumElements()));
282 getTypeDims(dims, array.getElementType(), loc, errorHandler);
283
284 return;
285 }
286 if (auto intType = hw::type_dyn_cast<IntType>(type)) {
287 dims.push_back(intType.getWidth());
288 return;
289 }
290
291 if (auto inout = hw::type_dyn_cast<InOutType>(type))
292 return getTypeDims(dims, inout.getElementType(), loc, errorHandler);
293 if (auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
294 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
295 if (auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
296 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
297 if (hw::type_isa<InterfaceType, StructType, EnumType, UnionType>(type))
298 return;
299
300 errorHandler(loc) << "value has an unsupported verilog type " << type;
301}
302// NOLINTEND(misc-no-recursion)
303
304/// True iff 'a' and 'b' have the same wire dims.
306 Type a, Type b, Location loc,
307 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
308 SmallVector<Attribute, 4> aDims;
309 getTypeDims(aDims, a, loc, errorHandler);
310
311 SmallVector<Attribute, 4> bDims;
312 getTypeDims(bDims, b, loc, errorHandler);
313
314 return aDims == bDims;
315}
316
317// NOLINTBEGIN(misc-no-recursion)
319 type = getCanonicalType(type);
320 if (auto intType = dyn_cast<IntegerType>(type))
321 return intType.getWidth() == 0;
322 if (auto inout = dyn_cast<hw::InOutType>(type))
323 return isZeroBitType(inout.getElementType());
324 if (auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
325 return uarray.getNumElements() == 0 ||
326 isZeroBitType(uarray.getElementType());
327 if (auto array = dyn_cast<hw::ArrayType>(type))
328 return array.getNumElements() == 0 || isZeroBitType(array.getElementType());
329 if (auto structType = dyn_cast<hw::StructType>(type))
330 return llvm::all_of(structType.getElements(),
331 [](auto elem) { return isZeroBitType(elem.type); });
332 if (auto enumType = dyn_cast<hw::EnumType>(type))
333 return enumType.getFields().empty();
334 if (auto unionType = dyn_cast<hw::UnionType>(type))
335 return hw::getBitWidth(unionType) == 0;
336
337 // We have an open type system, so assume it is ok.
338 return false;
339}
340// NOLINTEND(misc-no-recursion)
341
342/// Given a set of known nested types (those supported by this pass), strip off
343/// leading unpacked types. This strips off portions of the type that are
344/// printed to the right of the name in verilog.
345// NOLINTBEGIN(misc-no-recursion)
346static Type stripUnpackedTypes(Type type) {
347 return TypeSwitch<Type, Type>(type)
348 .Case<InOutType>([](InOutType inoutType) {
349 return stripUnpackedTypes(inoutType.getElementType());
350 })
351 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](auto arrayType) {
352 return stripUnpackedTypes(arrayType.getElementType());
353 })
354 .Default([](Type type) { return type; });
355}
356
357/// Return true if the type has a leading unpacked type.
358static bool hasLeadingUnpackedType(Type type) {
359 assert(isa<hw::InOutType>(type) && "inout type is expected");
360 auto elementType = cast<hw::InOutType>(type).getElementType();
362}
363
364/// Return true if type has a struct type as a subtype.
365static bool hasStructType(Type type) {
366 return TypeSwitch<Type, bool>(type)
367 .Case<InOutType, UnpackedArrayType, ArrayType>([](auto parentType) {
368 return hasStructType(parentType.getElementType());
369 })
370 .Case<StructType>([](auto) { return true; })
371 .Default([](auto) { return false; });
372}
373// NOLINTEND(misc-no-recursion)
374
375//===----------------------------------------------------------------------===//
376// Location comparison
377//===----------------------------------------------------------------------===//
378
379// NOLINTBEGIN(misc-no-recursion)
380
381static int compareLocs(Location lhs, Location rhs);
382
383// NameLoc comparator - compare names, then child locations.
384static int compareLocsImpl(mlir::NameLoc lhs, mlir::NameLoc rhs) {
385 if (auto name = lhs.getName().compare(rhs.getName()))
386 return name;
387 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
388}
389
390// FileLineColLoc comparator.
391static int compareLocsImpl(mlir::FileLineColLoc lhs, mlir::FileLineColLoc rhs) {
392 if (auto fn = lhs.getFilename().compare(rhs.getFilename()))
393 return fn;
394 if (lhs.getLine() != rhs.getLine())
395 return lhs.getLine() < rhs.getLine() ? -1 : 1;
396 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
397}
398
399// CallSiteLoc comparator. Compare first on the callee, then on the caller.
400static int compareLocsImpl(mlir::CallSiteLoc lhs, mlir::CallSiteLoc rhs) {
401 Location lhsCallee = lhs.getCallee();
402 Location rhsCallee = rhs.getCallee();
403 if (auto res = compareLocs(lhsCallee, rhsCallee))
404 return res;
405
406 Location lhsCaller = lhs.getCaller();
407 Location rhsCaller = rhs.getCaller();
408 return compareLocs(lhsCaller, rhsCaller);
409}
410
411template <typename TTargetLoc>
412FailureOr<int> dispatchCompareLocations(Location lhs, Location rhs) {
413 auto lhsT = dyn_cast<TTargetLoc>(lhs);
414 auto rhsT = dyn_cast<TTargetLoc>(rhs);
415 if (lhsT && rhsT) {
416 // Both are of the target location type, compare them directly.
417 return compareLocsImpl(lhsT, rhsT);
418 }
419 if (lhsT) {
420 // lhs is TTargetLoc => it comes before rhs.
421 return -1;
422 }
423 if (rhsT) {
424 // rhs is TTargetLoc => it comes before lhs.
425 return 1;
426 }
427
428 return failure();
429}
430
431// Top-level comparator for two arbitrarily typed locations.
432// First order comparison by location type:
433// 1. FileLineColLoc
434// 2. NameLoc
435// 3. CallSiteLoc
436// 4. Anything else...
437// Intra-location type comparison is delegated to the corresponding
438// compareLocsImpl() function.
439static int compareLocs(Location lhs, Location rhs) {
440 // FileLineColLoc
441 if (auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
442 succeeded(res))
443 return *res;
444
445 // NameLoc
446 if (auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
447 succeeded(res))
448 return *res;
449
450 // CallSiteLoc
451 if (auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
452 succeeded(res))
453 return *res;
454
455 // Anything else...
456 return 0;
457}
458
459// NOLINTEND(misc-no-recursion)
460
461//===----------------------------------------------------------------------===//
462// Location printing
463//===----------------------------------------------------------------------===//
464
465/// Pull apart any fused locations into the location set, such that they are
466/// uniqued. Any other location type will be added as-is.
467static void collectAndUniqueLocations(Location loc,
468 SmallPtrSetImpl<Attribute> &locationSet) {
469 llvm::TypeSwitch<Location, void>(loc)
470 .Case<FusedLoc>([&](auto fusedLoc) {
471 for (auto subLoc : fusedLoc.getLocations())
472 collectAndUniqueLocations(subLoc, locationSet);
473 })
474 .Default([&](auto loc) { locationSet.insert(loc); });
475}
476
477// Sorts a vector of locations in-place.
478template <typename TVector>
479static void sortLocationVector(TVector &vec) {
480 llvm::array_pod_sort(
481 vec.begin(), vec.end(), [](const auto *lhs, const auto *rhs) -> int {
482 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
483 });
484}
485
487public:
488 // Generates location info for a single location in the specified style.
490 SmallPtrSet<Attribute, 8> locationSet;
491 locationSet.insert(loc);
492 llvm::raw_string_ostream os(output);
493 emitLocationSetInfo(os, style, locationSet);
494 }
495
496 // Generates location info for a set of operations in the specified style.
498 const SmallPtrSetImpl<Operation *> &ops) {
499 // Multiple operations may come from the same location or may not have
500 // useful
501 // location info. Unique it now.
502 SmallPtrSet<Attribute, 8> locationSet;
503 for (auto *op : ops)
504 collectAndUniqueLocations(op->getLoc(), locationSet);
505 llvm::raw_string_ostream os(output);
506 emitLocationSetInfo(os, style, locationSet);
507 }
508
509 StringRef strref() { return output; }
510
511private:
512 void emitLocationSetInfo(llvm::raw_string_ostream &os,
514 const SmallPtrSetImpl<Attribute> &locationSet) {
515 if (style == LoweringOptions::LocationInfoStyle::None)
516 return;
517 std::string resstr;
518 llvm::raw_string_ostream sstr(resstr);
519 LocationEmitter::Impl(sstr, style, locationSet);
520 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
521 os << resstr;
522 return;
523 }
524 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
525 "other styles must be already handled");
526 os << "@[" << resstr << "]";
527 }
528
529 std::string output;
530
531 struct Impl {
532
533 // NOLINTBEGIN(misc-no-recursion)
535 const SmallPtrSetImpl<Attribute> &locationSet)
536 : os(os), style(style) {
537 emitLocationSetInfoImpl(locationSet);
538 }
539
540 // Emit CallSiteLocs.
541 void emitLocationInfo(mlir::CallSiteLoc loc) {
542 os << "{";
543 emitLocationInfo(loc.getCallee());
544 os << " <- ";
545 emitLocationInfo(loc.getCaller());
546 os << "}";
547 }
548
549 // Emit NameLocs.
550 void emitLocationInfo(mlir::NameLoc loc) {
551 bool withName = !loc.getName().empty();
552 if (withName)
553 os << "'" << loc.getName().strref() << "'(";
554 emitLocationInfo(loc.getChildLoc());
555
556 if (withName)
557 os << ")";
558 }
559
560 // Emit FileLineColLocs.
561 void emitLocationInfo(FileLineColLoc loc) {
562 os << loc.getFilename().getValue();
563 if (auto line = loc.getLine()) {
564 os << ':' << line;
565 if (auto col = loc.getColumn())
566 os << ':' << col;
567 }
568 }
569
570 // Generates a string representation of a set of FileLineColLocs.
571 // The entries are sorted by filename, line, col. Try to merge together
572 // entries to reduce verbosity on the column info.
573 void
574 printFileLineColSetInfo(llvm::SmallVector<FileLineColLoc, 8> locVector) {
575 // The entries are sorted by filename, line, col. Try to merge together
576 // entries to reduce verbosity on the column info.
577 StringRef lastFileName;
578 for (size_t i = 0, e = locVector.size(); i != e;) {
579 if (i != 0)
580 os << ", ";
581
582 // Print the filename if it changed.
583 auto first = locVector[i];
584 if (first.getFilename() != lastFileName) {
585 lastFileName = first.getFilename();
586 os << lastFileName;
587 }
588
589 // Scan for entries with the same file/line.
590 size_t end = i + 1;
591 while (end != e &&
592 first.getFilename() == locVector[end].getFilename() &&
593 first.getLine() == locVector[end].getLine())
594 ++end;
595
596 // If we have one entry, print it normally.
597 if (end == i + 1) {
598 if (auto line = first.getLine()) {
599 os << ':' << line;
600 if (auto col = first.getColumn())
601 os << ':' << col;
602 }
603 ++i;
604 continue;
605 }
606
607 // Otherwise print a brace enclosed list.
608 os << ':' << first.getLine() << ":{";
609 while (i != end) {
610 os << locVector[i++].getColumn();
611
612 if (i != end)
613 os << ',';
614 }
615 os << '}';
616 }
617 }
618
619 /// Return the location information in the specified style. This is the main
620 /// dispatch function for calling the location-specific routines.
621 void emitLocationInfo(Location loc) {
622 llvm::TypeSwitch<Location, void>(loc)
623 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
624 [&](auto loc) { emitLocationInfo(loc); })
625 .Case<mlir::FusedLoc>([&](auto loc) {
626 SmallPtrSet<Attribute, 8> locationSet;
627 collectAndUniqueLocations(loc, locationSet);
628 emitLocationSetInfoImpl(locationSet);
629 })
630 .Default([&](auto loc) {
631 // Don't print anything for unhandled locations.
632 });
633 }
634
635 /// Emit the location information of `locationSet` to `sstr`. The emitted
636 /// string
637 /// may potentially be an empty string given the contents of the
638 /// `locationSet`.
639 void
640 emitLocationSetInfoImpl(const SmallPtrSetImpl<Attribute> &locationSet) {
641 // Fast pass some common cases.
642 switch (locationSet.size()) {
643 case 1:
644 emitLocationInfo(cast<LocationAttr>(*locationSet.begin()));
645 [[fallthrough]];
646 case 0:
647 return;
648 default:
649 break;
650 }
651
652 // Sort the entries into distinct location printing kinds.
653 SmallVector<FileLineColLoc, 8> flcLocs;
654 SmallVector<Attribute, 8> otherLocs;
655 flcLocs.reserve(locationSet.size());
656 otherLocs.reserve(locationSet.size());
657 for (Attribute loc : locationSet) {
658 if (auto flcLoc = dyn_cast<FileLineColLoc>(loc))
659 flcLocs.push_back(flcLoc);
660 else
661 otherLocs.push_back(loc);
662 }
663
664 // SmallPtrSet iteration is non-deterministic, so sort the location
665 // vectors to ensure deterministic output.
666 sortLocationVector(otherLocs);
667 sortLocationVector(flcLocs);
668
669 // To detect whether something actually got emitted, we inspect the stream
670 // for size changes. This is due to the possiblity of locations which are
671 // not supposed to be emitted (e.g. `loc("")`).
672 size_t sstrSize = os.tell();
673 bool emittedAnything = false;
674 auto recheckEmittedSomething = [&]() {
675 size_t currSize = os.tell();
676 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
677 emittedAnything |= emittedSomethingSinceLastCheck;
678 sstrSize = currSize;
679 return emittedSomethingSinceLastCheck;
680 };
681
682 // First, emit the other locations through the generic location dispatch
683 // function.
684 llvm::interleave(
685 otherLocs,
686 [&](Attribute loc) { emitLocationInfo(cast<LocationAttr>(loc)); },
687 [&] {
688 if (recheckEmittedSomething()) {
689 os << ", ";
690 recheckEmittedSomething(); // reset detector to reflect the comma.
691 }
692 });
693
694 // If we emitted anything, and we have FileLineColLocs, then emit a
695 // location-separating comma.
696 if (emittedAnything && !flcLocs.empty())
697 os << ", ";
698 // Then, emit the FileLineColLocs.
700 }
701 llvm::raw_string_ostream &os;
703
704 // NOLINTEND(misc-no-recursion)
705 };
706};
707
708/// Most expressions are invalid to bit-select from in Verilog, but some
709/// things are ok. Return true if it is ok to inline bitselect from the
710/// result of this expression. It is conservatively correct to return false.
711static bool isOkToBitSelectFrom(Value v) {
712 // Module ports are always ok to bit select from.
713 if (isa<BlockArgument>(v))
714 return true;
715
716 // Read_inout is valid to inline for bit-select. See `select` syntax on
717 // SV spec A.8.4 (P1174).
718 if (auto read = v.getDefiningOp<ReadInOutOp>())
719 return true;
720
721 // Aggregate access can be inlined.
722 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
723 v.getDefiningOp()))
724 return true;
725
726 // Interface signal can be inlined.
727 if (v.getDefiningOp<ReadInterfaceSignalOp>())
728 return true;
729
730 // TODO: We could handle concat and other operators here.
731 return false;
732}
733
734/// Return true if we are unable to ever inline the specified operation. This
735/// happens because not all Verilog expressions are composable, notably you
736/// can only use bit selects like x[4:6] on simple expressions, you cannot use
737/// expressions in the sensitivity list of always blocks, etc.
738static bool isExpressionUnableToInline(Operation *op,
739 const LoweringOptions &options) {
740 if (auto cast = dyn_cast<BitcastOp>(op))
741 if (!haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
742 op->getLoc(),
743 [&](Location loc) { return emitError(loc); })) {
744 // Even if dimentions don't match, we can inline when its user doesn't
745 // rely on the type.
746 if (op->hasOneUse() &&
747 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
748 return false;
749 // Bitcasts rely on the type being assigned to, so we cannot inline.
750 return true;
751 }
752
753 // StructCreateOp needs to be assigning to a named temporary so that types
754 // are inferred properly by verilog
755 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp, ArrayInjectOp>(
756 op))
757 return true;
758
759 // Aggregate literal syntax only works in an assignment expression, where
760 // the Verilog expression's type is determined by the LHS.
761 if (auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
762 return true;
763
764 // Verbatim with a long string should be emitted as an out-of-line declration.
765 if (auto verbatim = dyn_cast<VerbatimExprOp>(op))
766 if (verbatim.getFormatString().size() > 32)
767 return true;
768
769 // Scan the users of the operation to see if any of them need this to be
770 // emitted out-of-line.
771 for (auto &use : op->getUses()) {
772 auto *user = use.getOwner();
773
774 // Verilog bit selection is required by the standard to be:
775 // "a vector, packed array, packed structure, parameter or concatenation".
776 //
777 // It cannot be an arbitrary expression, e.g. this is invalid:
778 // assign bar = {{a}, {b}, {c}, {d}}[idx];
779 //
780 // To handle these, we push the subexpression into a temporary.
781 if (isa<ExtractOp, ArraySliceOp, ArrayGetOp, ArrayInjectOp, StructExtractOp,
782 StructInjectOp, StructExplodeOp, UnionExtractOp,
783 IndexedPartSelectOp>(user))
784 if (use.getOperandNumber() == 0 && // ignore index operands.
785 !isOkToBitSelectFrom(use.get()))
786 return true;
787
788 // Handle option disallowing expressions in event control.
789 if (!options.allowExprInEventControl) {
790 // Check operations used for event control, anything other than
791 // a read of a wire must be out of line.
792
793 // Helper to determine if the use will be part of "event control",
794 // based on what the operation using it is and as which operand.
795 auto usedInExprControl = [user, &use]() {
796 return TypeSwitch<Operation *, bool>(user)
797 .Case<ltl::ClockOp>([&](auto clockOp) {
798 // LTL Clock op's clock operand must be a name.
799 return clockOp.getClock() == use.get();
800 })
801 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
802 sv::CoverConcurrentOp>(
803 [&](auto op) { return op.getClock() == use.get(); })
804 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
805 sv::CoverPropertyOp>([&](auto op) {
806 return op.getDisable() == use.get() || op.getClock() == use.get();
807 })
808 .Case<AlwaysOp, AlwaysFFOp>([](auto) {
809 // Always blocks must have a name in their sensitivity list.
810 // (all operands)
811 return true;
812 })
813 .Default([](auto) { return false; });
814 };
815
816 if (!usedInExprControl())
817 continue;
818
819 // Otherwise, this can only be inlined if is (already) a read of a wire.
820 auto read = dyn_cast<ReadInOutOp>(op);
821 if (!read)
822 return true;
823 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
824 return true;
825 }
826 }
827 return false;
828}
829
831
832/// Compute how many statements are within this block, for begin/end markers.
834 unsigned numStatements = 0;
835 block.walk([&](Operation *op) {
836 if (isVerilogExpression(op) ||
837 isa_and_nonnull<ltl::LTLDialect>(op->getDialect()))
838 return WalkResult::advance();
839 numStatements +=
840 TypeSwitch<Operation *, unsigned>(op)
841 .Case<VerbatimOp>([&](auto) {
842 // We don't know how many statements we emitted, so assume
843 // conservatively that a lot got put out. This will make sure we
844 // get a begin/end block around this.
845 return 3;
846 })
847 .Case<IfOp>([&](auto) {
848 // We count if as multiple statements to make sure it is always
849 // surrounded by a begin/end so we don't get if/else confusion in
850 // cases like this:
851 // if (cond)
852 // if (otherCond) // This should force a begin!
853 // stmt
854 // else // Goes with the outer if!
855 // thing;
856 return 2;
857 })
858 .Case<IfDefOp, IfDefProceduralOp>([&](auto) { return 3; })
859 .Case<OutputOp>([&](OutputOp oop) {
860 // Skip single-use instance outputs, they don't get statements.
861 // Keep this synchronized with visitStmt(InstanceOp,OutputOp).
862 return llvm::count_if(oop->getOperands(), [&](auto operand) {
863 Operation *op = operand.getDefiningOp();
864 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
865 });
866 })
867 .Default([](auto) { return 1; });
868 if (numStatements > 1)
869 return WalkResult::interrupt();
870 return WalkResult::advance();
871 });
872 if (numStatements == 0)
874 if (numStatements == 1)
877}
878
879/// Return true if this expression should be emitted inline into any statement
880/// that uses it.
882 const LoweringOptions &options) {
883 // Never create a temporary for a dead expression.
884 if (op->getResult(0).use_empty())
885 return true;
886
887 // Never create a temporary which is only going to be assigned to an output
888 // port, wire, or reg.
889 if (op->hasOneUse() &&
890 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
891 *op->getUsers().begin()))
892 return true;
893
894 // If mux inlining is dissallowed, we cannot inline muxes.
895 if (options.disallowMuxInlining && isa<MuxOp>(op))
896 return false;
897
898 // If this operation has multiple uses, we can't generally inline it unless
899 // the op is duplicatable.
900 if (!op->getResult(0).hasOneUse() && !isDuplicatableExpression(op))
901 return false;
902
903 // If it isn't structurally possible to inline this expression, emit it out
904 // of line.
905 return !isExpressionUnableToInline(op, options);
906}
907
908/// Find a nested IfOp in an else block that can be printed as `else if`
909/// instead of nesting it into a new `begin` - `end` block. The block must
910/// contain a single IfOp and optionally expressions which can be hoisted out.
911static IfOp findNestedElseIf(Block *elseBlock) {
912 IfOp ifOp;
913 for (auto &op : *elseBlock) {
914 if (auto opIf = dyn_cast<IfOp>(op)) {
915 if (ifOp)
916 return {};
917 ifOp = opIf;
918 continue;
919 }
920 if (!isVerilogExpression(&op))
921 return {};
922 }
923 // SV attributes cannot be attached to `else if` so reject when ifOp has SV
924 // attributes.
925 if (ifOp && hasSVAttributes(ifOp))
926 return {};
927 return ifOp;
928}
929
930/// Emit SystemVerilog attributes.
931template <typename PPS>
932static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak) {
933 enum Container { NoContainer, InComment, InAttr };
934 Container currentContainer = NoContainer;
935
936 auto closeContainer = [&] {
937 if (currentContainer == NoContainer)
938 return;
939 if (currentContainer == InComment)
940 ps << " */";
941 else if (currentContainer == InAttr)
942 ps << " *)";
943 ps << PP::end << PP::end;
944
945 currentContainer = NoContainer;
946 };
947
948 bool isFirstContainer = true;
949 auto openContainer = [&](Container newContainer) {
950 assert(newContainer != NoContainer);
951 if (currentContainer == newContainer)
952 return false;
953 closeContainer();
954 // If not first container, insert break point but no space.
955 if (!isFirstContainer)
956 ps << (mayBreak ? PP::space : PP::nbsp);
957 isFirstContainer = false;
958 // fit container on one line if possible, break if needed.
959 ps << PP::ibox0;
960 if (newContainer == InComment)
961 ps << "/* ";
962 else if (newContainer == InAttr)
963 ps << "(* ";
964 currentContainer = newContainer;
965 // Pack attributes within to fit, align to current column when breaking.
966 ps << PP::ibox0;
967 return true;
968 };
969
970 // Break containers to starting column (0), put all on same line OR
971 // put each on their own line (cbox).
972 ps.scopedBox(PP::cbox0, [&]() {
973 for (auto attr : attrs.getAsRange<SVAttributeAttr>()) {
974 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
975 : InAttr))
976 ps << "," << (mayBreak ? PP::space : PP::nbsp);
977 ps << PPExtString(attr.getName().getValue());
978 if (attr.getExpression())
979 ps << " = " << PPExtString(attr.getExpression().getValue());
980 }
981 closeContainer();
982 });
983}
984
985/// Retrieve value's verilog name from IR. The name must already have been
986/// added in pre-pass and passed through "hw.verilogName" attr.
987StringRef getVerilogValueName(Value val) {
988 if (auto *op = val.getDefiningOp())
989 return getSymOpName(op);
990
991 if (auto port = dyn_cast<BlockArgument>(val)) {
992 // If the value is defined by for op, use its associated verilog name.
993 auto parent = port.getParentBlock()->getParentOp();
994 if (isa<ForOp, GenerateForOp>(parent))
995 return parent->getAttrOfType<StringAttr>("hw.verilogName");
996 return getInputPortVerilogName(port.getParentBlock()->getParentOp(),
997 port.getArgNumber());
998 }
999 assert(false && "unhandled value");
1000 return {};
1001}
1002
1003//===----------------------------------------------------------------------===//
1004// VerilogEmitterState
1005//===----------------------------------------------------------------------===//
1006
1007namespace {
1008
1009/// This class maintains the mutable state that cross-cuts and is shared by the
1010/// various emitters.
1011class VerilogEmitterState {
1012public:
1013 explicit VerilogEmitterState(ModuleOp designOp,
1014 const SharedEmitterState &shared,
1015 const LoweringOptions &options,
1016 const HWSymbolCache &symbolCache,
1017 const GlobalNameTable &globalNames,
1018 const FileMapping &fileMapping,
1019 llvm::formatted_raw_ostream &os,
1020 StringAttr fileName, OpLocMap &verilogLocMap)
1021 : designOp(designOp), shared(shared), options(options),
1022 symbolCache(symbolCache), globalNames(globalNames),
1023 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1024 pp(os, options.emittedLineLength), fileName(fileName) {
1025 pp.setListener(&saver);
1026 }
1027 /// This is the root mlir::ModuleOp that holds the whole design being emitted.
1028 ModuleOp designOp;
1029
1030 const SharedEmitterState &shared;
1031
1032 /// The emitter options which control verilog emission.
1033 const LoweringOptions &options;
1034
1035 /// This is a cache of various information about the IR, in frozen state.
1036 const HWSymbolCache &symbolCache;
1037
1038 /// This tracks global names where the Verilog name needs to be different than
1039 /// the IR name.
1040 const GlobalNameTable &globalNames;
1041
1042 /// Tracks the referenceable files through their symbol.
1043 const FileMapping &fileMapping;
1044
1045 /// The stream to emit to. Use a formatted_raw_ostream, to easily get the
1046 /// current location(line,column) on the stream. This is required to record
1047 /// the verilog output location information corresponding to any op.
1048 llvm::formatted_raw_ostream &os;
1049
1050 bool encounteredError = false;
1051
1052 /// Pretty printing:
1053
1054 /// Whether a newline is expected, emitted late to provide opportunity to
1055 /// open/close boxes we don't know we need at level of individual statement.
1056 /// Every statement should set this instead of directly emitting (last)
1057 /// newline. Most statements end with emitLocationInfoAndNewLine which handles
1058 /// this.
1059 bool pendingNewline = false;
1060
1061 /// Used to record the verilog output file location of an op.
1062 OpLocMap &verilogLocMap;
1063 /// String storage backing Tokens built from temporary strings.
1064 /// PrettyPrinter will clear this as appropriate.
1067 verilogLocMap);
1068
1069 /// Pretty printer.
1070 PrettyPrinter pp;
1071
1072 /// Name of the output file, used for debug information.
1073 StringAttr fileName;
1074
1075 /// Update the location attribute of the ops with the verilog locations
1076 /// recorded in `verilogLocMap` and clear the map. `lineOffset` is added to
1077 /// all the line numbers, this is required when the modules are exported in
1078 /// parallel.
1079 void addVerilogLocToOps(unsigned int lineOffset, StringAttr fileName) {
1080 verilogLocMap.updateIRWithLoc(lineOffset, fileName,
1081 shared.designOp->getContext());
1082 verilogLocMap.clear();
1083 }
1084
1085private:
1086 VerilogEmitterState(const VerilogEmitterState &) = delete;
1087 void operator=(const VerilogEmitterState &) = delete;
1088};
1089} // namespace
1090
1091//===----------------------------------------------------------------------===//
1092// EmitterBase
1093//===----------------------------------------------------------------------===//
1094
1095namespace {
1096
1097/// The data that is unique to each callback. The operation and a flag to
1098/// indicate if the callback is for begin or end of the operation print
1099/// location.
1100using CallbackDataTy = std::pair<Operation *, bool>;
1101class EmitterBase {
1102public:
1103 // All of the mutable state we are maintaining.
1104 VerilogEmitterState &state;
1105
1106 /// Stream helper (pp, saver).
1108
1109 explicit EmitterBase(VerilogEmitterState &state)
1110 : state(state),
1111 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1112
1113 InFlightDiagnostic emitError(Operation *op, const Twine &message) {
1114 state.encounteredError = true;
1115 return op->emitError(message);
1116 }
1117
1118 InFlightDiagnostic emitOpError(Operation *op, const Twine &message) {
1119 state.encounteredError = true;
1120 return op->emitOpError(message);
1121 }
1122
1123 InFlightDiagnostic emitError(Location loc, const Twine &message = "") {
1124 state.encounteredError = true;
1125 return mlir::emitError(loc, message);
1126 }
1127
1128 void emitLocationImpl(llvm::StringRef location) {
1129 // Break so previous content is not impacted by following,
1130 // but use a 'neverbreak' so it always fits.
1131 ps << PP::neverbreak;
1132 if (!location.empty())
1133 ps << "\t// " << location; // (don't use tabs in normal pretty-printing)
1134 }
1135
1136 void emitLocationInfo(Location loc) {
1137 emitLocationImpl(
1138 LocationEmitter(state.options.locationInfoStyle, loc).strref());
1139 }
1140
1141 /// If we have location information for any of the specified operations,
1142 /// aggregate it together and print a pretty comment specifying where the
1143 /// operations came from. In any case, print a newline.
1144 void emitLocationInfoAndNewLine(const SmallPtrSetImpl<Operation *> &ops) {
1145 emitLocationImpl(
1146 LocationEmitter(state.options.locationInfoStyle, ops).strref());
1147 setPendingNewline();
1148 }
1149
1150 template <typename PPS>
1151 void emitTextWithSubstitutions(PPS &ps, StringRef string, Operation *op,
1152 llvm::function_ref<void(Value)> operandEmitter,
1153 ArrayAttr symAttrs);
1154
1155 /// Emit the value of a StringAttr as one or more Verilog "one-line" comments
1156 /// ("//"). Break the comment to respect the emittedLineLength and trim
1157 /// whitespace after a line break. Do nothing if the StringAttr is null or
1158 /// the value is empty.
1159 void emitComment(StringAttr comment);
1160
1161 /// If previous emission requires a newline, emit it now.
1162 /// This gives us opportunity to open/close boxes before linebreak.
1163 void emitPendingNewlineIfNeeded() {
1164 if (state.pendingNewline) {
1165 state.pendingNewline = false;
1166 ps << PP::newline;
1167 }
1168 }
1169 void setPendingNewline() {
1170 assert(!state.pendingNewline);
1171 state.pendingNewline = true;
1172 }
1173
1174 void startStatement() { emitPendingNewlineIfNeeded(); }
1175
1176private:
1177 void operator=(const EmitterBase &) = delete;
1178 EmitterBase(const EmitterBase &) = delete;
1179};
1180} // end anonymous namespace
1181
1182template <typename PPS>
1183void EmitterBase::emitTextWithSubstitutions(
1184 PPS &ps, StringRef string, Operation *op,
1185 llvm::function_ref<void(Value)> operandEmitter, ArrayAttr symAttrs) {
1186
1187 // Perform operand substitions as we emit the line string. We turn {{42}}
1188 // into the value of operand 42.
1189 auto namify = [&](Attribute sym, HWSymbolCache::Item item) {
1190 // CAVEAT: These accesses can reach into other modules through inner name
1191 // references, which are currently being processed. Do not add those remote
1192 // operations to this module's `names`, which is reserved for things named
1193 // *within* this module. Instead, you have to rely on those remote
1194 // operations to have been named inside the global names table. If they
1195 // haven't, take a look at name legalization first.
1196 if (auto *itemOp = item.getOp()) {
1197 if (item.hasPort()) {
1198 return getPortVerilogName(itemOp, item.getPort());
1199 }
1200 StringRef symOpName = getSymOpName(itemOp);
1201 if (!symOpName.empty())
1202 return symOpName;
1203 emitError(itemOp, "cannot get name for symbol ") << sym;
1204 } else {
1205 emitError(op, "cannot get name for symbol ") << sym;
1206 }
1207 return StringRef("<INVALID>");
1208 };
1209
1210 // Scan 'line' for a substitution, emitting any non-substitution prefix,
1211 // then the mentioned operand, chopping the relevant text off 'line' and
1212 // returning true. This returns false if no substitution is found.
1213 unsigned numSymOps = symAttrs.size();
1214 auto emitUntilSubstitution = [&](size_t next = 0) -> bool {
1215 size_t start = 0;
1216 while (true) {
1217 next = string.find("{{", next);
1218 if (next == StringRef::npos)
1219 return false;
1220
1221 // Check to make sure we have a number followed by }}. If not, we
1222 // ignore the {{ sequence as something that could happen in Verilog.
1223 next += 2;
1224 start = next;
1225 while (next < string.size() && isdigit(string[next]))
1226 ++next;
1227 // We need at least one digit.
1228 if (start == next) {
1229 next--;
1230 continue;
1231 }
1232 size_t operandNoLength = next - start;
1233
1234 // Format string options follow a ':'.
1235 StringRef fmtOptsStr;
1236 if (string[next] == ':') {
1237 size_t startFmtOpts = next + 1;
1238 while (next < string.size() && string[next] != '}')
1239 ++next;
1240 fmtOptsStr = string.substr(startFmtOpts, next - startFmtOpts);
1241 }
1242
1243 // We must have a }} right after the digits.
1244 if (!string.substr(next).starts_with("}}"))
1245 continue;
1246
1247 // We must be able to decode the integer into an unsigned.
1248 unsigned operandNo = 0;
1249 if (string.drop_front(start)
1250 .take_front(operandNoLength)
1251 .getAsInteger(10, operandNo)) {
1252 emitError(op, "operand substitution too large");
1253 continue;
1254 }
1255 next += 2;
1256
1257 // Emit any text before the substitution.
1258 auto before = string.take_front(start - 2);
1259 if (!before.empty())
1260 ps << PPExtString(before);
1261
1262 // operandNo can either refer to Operands or symOps. symOps are
1263 // numbered after the operands.
1264 if (operandNo < op->getNumOperands())
1265 // Emit the operand.
1266 operandEmitter(op->getOperand(operandNo));
1267 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1268 unsigned symOpNum = operandNo - op->getNumOperands();
1269 auto sym = symAttrs[symOpNum];
1270 StringRef symVerilogName;
1271 if (auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1272 if (auto *symOp = state.symbolCache.getDefinition(fsym)) {
1273 if (auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1274 auto namepath = globalRef.getNamepathAttr().getValue();
1275 for (auto [index, sym] : llvm::enumerate(namepath)) {
1276 // Emit the seperator string.
1277 if (index > 0)
1278 ps << (fmtOptsStr.empty() ? "." : fmtOptsStr);
1279
1280 auto innerRef = cast<InnerRefAttr>(sym);
1281 auto ref = state.symbolCache.getInnerDefinition(
1282 innerRef.getModule(), innerRef.getName());
1283 ps << namify(innerRef, ref);
1284 }
1285 } else {
1286 symVerilogName = namify(sym, symOp);
1287 }
1288 }
1289 } else if (auto isym = dyn_cast<InnerRefAttr>(sym)) {
1290 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1291 isym.getName());
1292 symVerilogName = namify(sym, symOp);
1293 }
1294 if (!symVerilogName.empty())
1295 ps << PPExtString(symVerilogName);
1296 } else {
1297 emitError(op, "operand " + llvm::utostr(operandNo) + " isn't valid");
1298 continue;
1299 }
1300 // Forget about the part we emitted.
1301 string = string.drop_front(next);
1302 return true;
1303 }
1304 };
1305
1306 // Emit all the substitutions.
1307 while (emitUntilSubstitution())
1308 ;
1309
1310 // Emit any text after the last substitution.
1311 if (!string.empty())
1312 ps << PPExtString(string);
1313}
1314
1315void EmitterBase::emitComment(StringAttr comment) {
1316 if (!comment)
1317 return;
1318
1319 // Set a line length for the comment. Subtract off the leading comment and
1320 // space ("// ") as well as the current indent level to simplify later
1321 // arithmetic. Ensure that this line length doesn't go below zero.
1322 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1323
1324 // Process the comment in line chunks extracted from manually specified line
1325 // breaks. This is done to preserve user-specified line breaking if used.
1326 auto ref = comment.getValue();
1327 StringRef line;
1328 while (!ref.empty()) {
1329 std::tie(line, ref) = ref.split("\n");
1330 // Emit each comment line breaking it if it exceeds the emittedLineLength.
1331 for (;;) {
1332 startStatement();
1333 ps << "// ";
1334
1335 // Base case 1: the entire comment fits on one line.
1336 if (line.size() <= lineLength) {
1337 ps << PPExtString(line);
1338 setPendingNewline();
1339 break;
1340 }
1341
1342 // The comment does NOT fit on one line. Use a simple algorithm to find
1343 // a position to break the line:
1344 // 1) Search backwards for whitespace and break there if you find it.
1345 // 2) If no whitespace exists in (1), search forward for whitespace
1346 // and break there.
1347 // This algorithm violates the emittedLineLength if (2) ever occurrs,
1348 // but it's dead simple.
1349 auto breakPos = line.rfind(' ', lineLength);
1350 // No whitespace exists looking backwards.
1351 if (breakPos == StringRef::npos) {
1352 breakPos = line.find(' ', lineLength);
1353 // No whitespace exists looking forward (you hit the end of the
1354 // string).
1355 if (breakPos == StringRef::npos)
1356 breakPos = line.size();
1357 }
1358
1359 // Emit up to the break position. Trim any whitespace after the break
1360 // position. Exit if nothing is left to emit. Otherwise, update the
1361 // comment ref and continue;
1362 ps << PPExtString(line.take_front(breakPos));
1363 setPendingNewline();
1364 breakPos = line.find_first_not_of(' ', breakPos);
1365 // Base Case 2: nothing left except whitespace.
1366 if (breakPos == StringRef::npos)
1367 break;
1368
1369 line = line.drop_front(breakPos);
1370 }
1371 }
1372}
1373
1374/// Given an expression that is spilled into a temporary wire, try to synthesize
1375/// a better name than "_T_42" based on the structure of the expression.
1376// NOLINTBEGIN(misc-no-recursion)
1378 StringAttr result;
1379 bool addPrefixUnderScore = true;
1380
1381 // Look through read_inout.
1382 if (auto read = expr.getDefiningOp<ReadInOutOp>())
1383 return inferStructuralNameForTemporary(read.getInput());
1384
1385 // Module ports carry names!
1386 if (auto blockArg = dyn_cast<BlockArgument>(expr)) {
1387 auto moduleOp =
1388 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1389 StringRef name = getPortVerilogName(moduleOp, blockArg.getArgNumber());
1390 result = StringAttr::get(expr.getContext(), name);
1391
1392 } else if (auto *op = expr.getDefiningOp()) {
1393 // Uses of a wire, register or logic can be done inline.
1394 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1395 StringRef name = getSymOpName(op);
1396 result = StringAttr::get(expr.getContext(), name);
1397
1398 } else if (auto nameHint = op->getAttrOfType<StringAttr>("sv.namehint")) {
1399 // Use a dialect (sv) attribute to get a hint for the name if the op
1400 // doesn't explicitly specify it. Do this last
1401 result = nameHint;
1402
1403 // If there is a namehint, don't add underscores to the name.
1404 addPrefixUnderScore = false;
1405 } else {
1406 TypeSwitch<Operation *>(op)
1407 // Generate a pretty name for VerbatimExpr's that look macro-like
1408 // using the same logic that generates the MLIR syntax name.
1409 .Case([&result](VerbatimExprOp verbatim) {
1410 verbatim.getAsmResultNames([&](Value, StringRef name) {
1411 result = StringAttr::get(verbatim.getContext(), name);
1412 });
1413 })
1414 .Case([&result](VerbatimExprSEOp verbatim) {
1415 verbatim.getAsmResultNames([&](Value, StringRef name) {
1416 result = StringAttr::get(verbatim.getContext(), name);
1417 });
1418 })
1419
1420 // If this is an extract from a namable object, derive a name from it.
1421 .Case([&result](ExtractOp extract) {
1422 if (auto operandName =
1423 inferStructuralNameForTemporary(extract.getInput())) {
1424 unsigned numBits =
1425 cast<IntegerType>(extract.getType()).getWidth();
1426 if (numBits == 1)
1427 result = StringAttr::get(extract.getContext(),
1428 operandName.strref() + "_" +
1429 Twine(extract.getLowBit()));
1430 else
1431 result = StringAttr::get(
1432 extract.getContext(),
1433 operandName.strref() + "_" +
1434 Twine(extract.getLowBit() + numBits - 1) + "to" +
1435 Twine(extract.getLowBit()));
1436 }
1437 });
1438 // TODO: handle other common patterns.
1439 }
1440 }
1441
1442 // Make sure any synthesized name starts with an _.
1443 if (!result || result.strref().empty())
1444 return {};
1445
1446 // Make sure that all temporary names start with an underscore.
1447 if (addPrefixUnderScore && result.strref().front() != '_')
1448 result = StringAttr::get(expr.getContext(), "_" + result.strref());
1449
1450 return result;
1451}
1452// NOLINTEND(misc-no-recursion)
1453
1454//===----------------------------------------------------------------------===//
1455// ModuleEmitter
1456//===----------------------------------------------------------------------===//
1457
1458namespace {
1459
1460class ModuleEmitter : public EmitterBase {
1461public:
1462 explicit ModuleEmitter(VerilogEmitterState &state)
1463 : EmitterBase(state), currentModuleOp(nullptr),
1464 fieldNameResolver(FieldNameResolver(state.globalNames, state.options)) {
1465 }
1466 ~ModuleEmitter() {
1467 emitPendingNewlineIfNeeded();
1468 ps.eof();
1469 };
1470
1471 void emitParameters(Operation *module, ArrayAttr params);
1472 void emitPortList(Operation *module, const ModulePortInfo &portInfo,
1473 bool emitAsTwoStateType = false);
1474
1475 void emitHWModule(HWModuleOp module);
1476 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1477 void emitFunc(FuncOp);
1478
1479 // Statements.
1480 void emitStatement(Operation *op);
1481 void emitBind(BindOp op);
1482 void emitBindInterface(BindInterfaceOp op);
1483
1484 void emitSVAttributes(Operation *op);
1485
1486 /// Legalize the given field name if it is an invalid verilog name.
1487 StringRef getVerilogStructFieldName(StringAttr field) {
1488 return fieldNameResolver.getRenamedFieldName(field).getValue();
1489 }
1490
1491 //===--------------------------------------------------------------------===//
1492 // Methods for formatting types.
1493
1494 /// Emit a type's packed dimensions.
1495 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1496
1497 /// Print the specified packed portion of the type to the specified stream,
1498 ///
1499 /// * 'optionalAliasType' can be provided to perform any alias-aware printing
1500 /// of the inner type.
1501 /// * When `implicitIntType` is false, a "logic" is printed. This is used in
1502 /// struct fields and typedefs.
1503 /// * When `singleBitDefaultType` is false, single bit values are printed as
1504 /// `[0:0]`. This is used in parameter lists.
1505 ///
1506 /// This returns true if anything was printed.
1507 bool printPackedType(Type type, raw_ostream &os, Location loc,
1508 Type optionalAliasType = {}, bool implicitIntType = true,
1509 bool singleBitDefaultType = true,
1510 bool emitAsTwoStateType = false);
1511
1512 /// Output the unpacked array dimensions. This is the part of the type that
1513 /// is to the right of the name.
1514 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1515
1516 //===--------------------------------------------------------------------===//
1517 // Methods for formatting parameters.
1518
1519 /// Prints a parameter attribute expression in a Verilog compatible way to the
1520 /// specified stream. This returns the precedence of the generated string.
1521 SubExprInfo printParamValue(Attribute value, raw_ostream &os,
1522 function_ref<InFlightDiagnostic()> emitError);
1523
1524 SubExprInfo printParamValue(Attribute value, raw_ostream &os,
1525 VerilogPrecedence parenthesizeIfLooserThan,
1526 function_ref<InFlightDiagnostic()> emitError);
1527
1528 //===--------------------------------------------------------------------===//
1529 // Mutable state while emitting a module body.
1530
1531 /// This is the current module being emitted for a HWModuleOp.
1532 Operation *currentModuleOp;
1533
1534 /// This set keeps track of expressions that were emitted into their
1535 /// 'automatic logic' or 'localparam' declaration. This is only used for
1536 /// expressions in a procedural region, because we otherwise just emit wires
1537 /// on demand.
1538 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1539
1540 /// This class keeps track of field name renamings in the module scope.
1541 FieldNameResolver fieldNameResolver;
1542
1543 /// This keeps track of assignments folded into wire emissions
1544 SmallPtrSet<Operation *, 16> assignsInlined;
1545};
1546
1547} // end anonymous namespace
1548
1549/// Return the word (e.g. "reg") in Verilog to declare the specified thing.
1550/// If `stripAutomatic` is true, "automatic" is not used even for a declaration
1551/// in a non-procedural region.
1552static StringRef getVerilogDeclWord(Operation *op,
1553 const ModuleEmitter &emitter) {
1554 if (isa<RegOp>(op)) {
1555 // Check if the type stored in this register is a struct or array of
1556 // structs. In this case, according to spec section 6.8, the "reg" prefix
1557 // should be left off.
1558 auto elementType =
1559 cast<InOutType>(op->getResult(0).getType()).getElementType();
1560 // Unwrap arrays. Since packed arrays cannot contain unpacked arrays, we can
1561 // unpack unpacked arrays first.
1562 while (auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(elementType))
1563 elementType = arrayType.getElementType();
1564 while (auto arrayType = hw::type_dyn_cast<ArrayType>(elementType))
1565 elementType = arrayType.getElementType();
1566
1567 if (isa<StructType, UnionType, EnumType, TypeAliasType>(elementType))
1568 return "";
1569
1570 return "reg";
1571 }
1572 if (isa<sv::WireOp>(op))
1573 return "wire";
1574 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1575 return "localparam";
1576
1577 // Interfaces instances use the name of the declared interface.
1578 if (auto interface = dyn_cast<InterfaceInstanceOp>(op))
1579 return interface.getInterfaceType().getInterface().getValue();
1580
1581 // If 'op' is in a module, output 'wire'. If 'op' is in a procedural block,
1582 // fall through to default.
1583 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
1584
1585 // If this decl is within a function, "automatic" is not needed because
1586 // "automatic" is added to its definition.
1587 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1588
1589 if (isa<LogicOp>(op)) {
1590 // If the logic op is defined in a procedural region, add 'automatic'
1591 // keyword. If the op has a struct type, 'logic' keyword is already emitted
1592 // within a struct type definition (e.g. struct packed {logic foo;}). So we
1593 // should not emit extra 'logic'.
1594 bool hasStruct = hasStructType(op->getResult(0).getType());
1595 if (isProcedural && !stripAutomatic)
1596 return hasStruct ? "automatic" : "automatic logic";
1597 return hasStruct ? "" : "logic";
1598 }
1599
1600 if (!isProcedural)
1601 return "wire";
1602
1603 if (stripAutomatic)
1604 return hasStructType(op->getResult(0).getType()) ? "" : "logic";
1605
1606 // "automatic" values aren't allowed in disallowLocalVariables mode.
1607 assert(!emitter.state.options.disallowLocalVariables &&
1608 "automatic variables not allowed");
1609
1610 // If the type contains a struct type, we have to use only "automatic" because
1611 // "automatic struct" is syntactically correct.
1612 return hasStructType(op->getResult(0).getType()) ? "automatic"
1613 : "automatic logic";
1614}
1615
1616//===----------------------------------------------------------------------===//
1617// Methods for formatting types.
1618
1619/// Emit a single dimension.
1620static void emitDim(Attribute width, raw_ostream &os, Location loc,
1621 ModuleEmitter &emitter, bool downTo) {
1622 if (!width) {
1623 os << "<<invalid type>>";
1624 return;
1625 }
1626 if (auto intAttr = dyn_cast<IntegerAttr>(width)) {
1627 if (intAttr.getValue().isZero()) {
1628 os << "/*Zero Width*/";
1629 } else {
1630 os << '[';
1631 if (!downTo)
1632 os << "0:";
1633 os << (intAttr.getValue().getZExtValue() - 1);
1634 if (downTo)
1635 os << ":0";
1636 os << ']';
1637 }
1638 return;
1639 }
1640
1641 // Otherwise it must be a parameterized dimension. Shove the "-1" into the
1642 // attribute so it gets printed in canonical form.
1643 auto typedAttr = dyn_cast<TypedAttr>(width);
1644 if (!typedAttr) {
1645 emitter.emitError(loc, "untyped dimension attribute ") << width;
1646 return;
1647 }
1648 auto negOne =
1649 getIntAttr(loc.getContext(), typedAttr.getType(),
1650 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L, true));
1651 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1652 os << '[';
1653 if (!downTo)
1654 os << "0:";
1655 emitter.printParamValue(width, os, [loc, &emitter]() {
1656 return emitter.emitError(loc, "invalid parameter in type");
1657 });
1658 if (downTo)
1659 os << ":0";
1660 os << ']';
1661}
1662
1663/// Emit a list of packed dimensions.
1664static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1665 ModuleEmitter &emitter) {
1666 for (Attribute width : dims) {
1667 emitDim(width, os, loc, emitter, /*downTo=*/true);
1668 }
1669}
1670
1671/// Emit a type's packed dimensions.
1672void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1673 SmallVector<Attribute, 4> dims;
1674 getTypeDims(dims, type, loc,
1675 [&](Location loc) { return this->emitError(loc); });
1676 emitDims(dims, os, loc, *this);
1677}
1678
1679/// Return a 2-state integer atom type name if the width matches. See Spec 6.8
1680/// Variable declarations.
1681static StringRef getTwoStateIntegerAtomType(size_t width) {
1682 switch (width) {
1683 case 8:
1684 return "byte";
1685 case 16:
1686 return "shortint";
1687 case 32:
1688 return "int";
1689 case 64:
1690 return "longint";
1691 default:
1692 return "";
1693 }
1694}
1695
1696/// Output the basic type that consists of packed and primitive types. This is
1697/// those to the left of the name in verilog. implicitIntType controls whether
1698/// to print a base type for (logic) for inteters or whether the caller will
1699/// have handled this (with logic, wire, reg, etc).
1700/// optionalAliasType can be provided to perform any necessary alias-aware
1701/// printing of 'type'.
1702///
1703/// Returns true when anything was printed out.
1704// NOLINTBEGIN(misc-no-recursion)
1705static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc,
1706 SmallVectorImpl<Attribute> &dims,
1707 bool implicitIntType, bool singleBitDefaultType,
1708 ModuleEmitter &emitter,
1709 Type optionalAliasType = {},
1710 bool emitAsTwoStateType = false) {
1711 return TypeSwitch<Type, bool>(type)
1712 .Case<IntegerType>([&](IntegerType integerType) -> bool {
1713 if (emitAsTwoStateType && dims.empty()) {
1714 auto typeName = getTwoStateIntegerAtomType(integerType.getWidth());
1715 if (!typeName.empty()) {
1716 os << typeName;
1717 return true;
1718 }
1719 }
1720 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1721 dims.push_back(
1722 getInt32Attr(type.getContext(), integerType.getWidth()));
1723
1724 StringRef typeName =
1725 (emitAsTwoStateType ? "bit" : (implicitIntType ? "" : "logic"));
1726 if (!typeName.empty()) {
1727 os << typeName;
1728 if (!dims.empty())
1729 os << ' ';
1730 }
1731
1732 emitDims(dims, os, loc, emitter);
1733 return !dims.empty() || !implicitIntType;
1734 })
1735 .Case<IntType>([&](IntType intType) {
1736 if (!implicitIntType)
1737 os << "logic ";
1738 dims.push_back(intType.getWidth());
1739 emitDims(dims, os, loc, emitter);
1740 return true;
1741 })
1742 .Case<ArrayType>([&](ArrayType arrayType) {
1743 dims.push_back(arrayType.getSizeAttr());
1744 return printPackedTypeImpl(arrayType.getElementType(), os, loc, dims,
1745 implicitIntType, singleBitDefaultType,
1746 emitter, /*optionalAliasType=*/{},
1747 emitAsTwoStateType);
1748 })
1749 .Case<InOutType>([&](InOutType inoutType) {
1750 return printPackedTypeImpl(inoutType.getElementType(), os, loc, dims,
1751 implicitIntType, singleBitDefaultType,
1752 emitter, /*optionalAliasType=*/{},
1753 emitAsTwoStateType);
1754 })
1755 .Case<EnumType>([&](EnumType enumType) {
1756 assert(enumType.getBitWidth().has_value() &&
1757 "enum type must have bitwidth");
1758 os << "enum ";
1759 if (enumType.getBitWidth() != 32)
1760 os << "bit [" << *enumType.getBitWidth() - 1 << ":0] ";
1761 os << "{";
1762 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1763 llvm::interleaveComma(
1764 enumType.getFields().getAsRange<StringAttr>(), os,
1765 [&](auto enumerator) {
1766 os << emitter.fieldNameResolver.getEnumFieldName(
1767 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1768 });
1769 os << "}";
1770 return true;
1771 })
1772 .Case<StructType>([&](StructType structType) {
1773 if (structType.getElements().empty() || isZeroBitType(structType)) {
1774 os << "/*Zero Width*/";
1775 return true;
1776 }
1777 os << "struct packed {";
1778 for (auto &element : structType.getElements()) {
1779 if (isZeroBitType(element.type)) {
1780 os << "/*" << emitter.getVerilogStructFieldName(element.name)
1781 << ": Zero Width;*/ ";
1782 continue;
1783 }
1784 SmallVector<Attribute, 8> structDims;
1785 printPackedTypeImpl(stripUnpackedTypes(element.type), os, loc,
1786 structDims,
1787 /*implicitIntType=*/false,
1788 /*singleBitDefaultType=*/true, emitter,
1789 /*optionalAliasType=*/{}, emitAsTwoStateType);
1790 os << ' ' << emitter.getVerilogStructFieldName(element.name);
1791 emitter.printUnpackedTypePostfix(element.type, os);
1792 os << "; ";
1793 }
1794 os << '}';
1795 emitDims(dims, os, loc, emitter);
1796 return true;
1797 })
1798 .Case<UnionType>([&](UnionType unionType) {
1799 if (unionType.getElements().empty() || isZeroBitType(unionType)) {
1800 os << "/*Zero Width*/";
1801 return true;
1802 }
1803
1804 int64_t unionWidth = hw::getBitWidth(unionType);
1805 os << "union packed {";
1806 for (auto &element : unionType.getElements()) {
1807 if (isZeroBitType(element.type)) {
1808 os << "/*" << emitter.getVerilogStructFieldName(element.name)
1809 << ": Zero Width;*/ ";
1810 continue;
1811 }
1812 int64_t elementWidth = hw::getBitWidth(element.type);
1813 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1814 if (needsPadding) {
1815 os << " struct packed {";
1816 if (element.offset) {
1817 os << (emitAsTwoStateType ? "bit" : "logic") << " ["
1818 << element.offset - 1 << ":0] "
1819 << "__pre_padding_" << element.name.getValue() << "; ";
1820 }
1821 }
1822
1823 SmallVector<Attribute, 8> structDims;
1825 stripUnpackedTypes(element.type), os, loc, structDims,
1826 /*implicitIntType=*/false,
1827 /*singleBitDefaultType=*/true, emitter, {}, emitAsTwoStateType);
1828 os << ' ' << emitter.getVerilogStructFieldName(element.name);
1829 emitter.printUnpackedTypePostfix(element.type, os);
1830 os << ";";
1831
1832 if (needsPadding) {
1833 if (elementWidth + (int64_t)element.offset < unionWidth) {
1834 os << " " << (emitAsTwoStateType ? "bit" : "logic") << " ["
1835 << unionWidth - (elementWidth + element.offset) - 1 << ":0] "
1836 << "__post_padding_" << element.name.getValue() << ";";
1837 }
1838 os << "} " << emitter.getVerilogStructFieldName(element.name)
1839 << ";";
1840 }
1841 }
1842 os << '}';
1843 emitDims(dims, os, loc, emitter);
1844 return true;
1845 })
1846
1847 .Case<InterfaceType>([](InterfaceType ifaceType) { return false; })
1848 .Case<ModportType>([&](ModportType modportType) {
1849 auto modportAttr = modportType.getModport();
1850 os << modportAttr.getRootReference().getValue() << "."
1851 << modportAttr.getNestedReferences().front().getValue();
1852 return true;
1853 })
1854 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1855 os << "<<unexpected unpacked array>>";
1856 emitter.emitError(loc, "Unexpected unpacked array in packed type ")
1857 << arrayType;
1858 return true;
1859 })
1860 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1861 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1862 if (!typedecl) {
1863 emitter.emitError(loc, "unresolvable type reference");
1864 return false;
1865 }
1866 if (typedecl.getType() != typeRef.getInnerType()) {
1867 emitter.emitError(loc, "declared type did not match aliased type");
1868 return false;
1869 }
1870
1871 os << typedecl.getPreferredName();
1872 emitDims(dims, os, typedecl->getLoc(), emitter);
1873 return true;
1874 })
1875 .Default([&](Type type) {
1876 os << "<<invalid type '" << type << "'>>";
1877 emitter.emitError(loc, "value has an unsupported verilog type ")
1878 << type;
1879 return true;
1880 });
1881}
1882// NOLINTEND(misc-no-recursion)
1883
1884/// Print the specified packed portion of the type to the specified stream,
1885///
1886/// * When `implicitIntType` is false, a "logic" is printed. This is used in
1887/// struct fields and typedefs.
1888/// * When `singleBitDefaultType` is false, single bit values are printed as
1889/// `[0:0]`. This is used in parameter lists.
1890/// * When `emitAsTwoStateType` is true, a "bit" is printed. This is used in
1891/// DPI function import statement.
1892///
1893/// This returns true if anything was printed.
1894bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1895 Type optionalAliasType,
1896 bool implicitIntType,
1897 bool singleBitDefaultType,
1898 bool emitAsTwoStateType) {
1899 SmallVector<Attribute, 8> packedDimensions;
1900 return printPackedTypeImpl(type, os, loc, packedDimensions, implicitIntType,
1901 singleBitDefaultType, *this, optionalAliasType,
1902 emitAsTwoStateType);
1903}
1904
1905/// Output the unpacked array dimensions. This is the part of the type that is
1906/// to the right of the name.
1907// NOLINTBEGIN(misc-no-recursion)
1908void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1909 TypeSwitch<Type, void>(type)
1910 .Case<InOutType>([&](InOutType inoutType) {
1911 printUnpackedTypePostfix(inoutType.getElementType(), os);
1912 })
1913 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1914 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1915 : state.designOp->getLoc();
1916 emitDim(arrayType.getSizeAttr(), os, loc, *this,
1917 /*downTo=*/false);
1918 printUnpackedTypePostfix(arrayType.getElementType(), os);
1919 })
1920 .Case<sv::UnpackedOpenArrayType>([&](auto arrayType) {
1921 os << "[]";
1922 printUnpackedTypePostfix(arrayType.getElementType(), os);
1923 })
1924 .Case<InterfaceType>([&](auto) {
1925 // Interface instantiations have parentheses like a module with no
1926 // ports.
1927 os << "()";
1928 });
1929}
1930// NOLINTEND(misc-no-recursion)
1931
1932//===----------------------------------------------------------------------===//
1933// Methods for formatting parameters.
1934
1935/// Prints a parameter attribute expression in a Verilog compatible way to the
1936/// specified stream. This returns the precedence of the generated string.
1937SubExprInfo
1938ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1939 function_ref<InFlightDiagnostic()> emitError) {
1940 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1941 emitError);
1942}
1943
1944/// Helper that prints a parameter constant value in a Verilog compatible way.
1945/// This returns the precedence of the generated string.
1946// NOLINTBEGIN(misc-no-recursion)
1947SubExprInfo
1948ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1949 VerilogPrecedence parenthesizeIfLooserThan,
1950 function_ref<InFlightDiagnostic()> emitError) {
1951 if (auto intAttr = dyn_cast<IntegerAttr>(value)) {
1952 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1953 APInt value = intAttr.getValue();
1954
1955 // We omit the width specifier if the value is <= 32-bits in size, which
1956 // makes this more compatible with unknown width extmodules.
1957 if (intTy.getWidth() > 32) {
1958 // Sign comes out before any width specifier.
1959 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1960 os << '-';
1961 value = -value;
1962 }
1963 if (intTy.isSigned())
1964 os << intTy.getWidth() << "'sd";
1965 else
1966 os << intTy.getWidth() << "'d";
1967 }
1968 value.print(os, intTy.isSigned());
1969 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1970 }
1971 if (auto strAttr = dyn_cast<StringAttr>(value)) {
1972 os << '"';
1973 os.write_escaped(strAttr.getValue());
1974 os << '"';
1975 return {Symbol, IsUnsigned};
1976 }
1977 if (auto fpAttr = dyn_cast<FloatAttr>(value)) {
1978 // TODO: relying on float printing to be precise is not a good idea.
1979 os << fpAttr.getValueAsDouble();
1980 return {Symbol, IsUnsigned};
1981 }
1982 if (auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1983 os << verbatimParam.getValue().getValue();
1984 return {Symbol, IsUnsigned};
1985 }
1986 if (auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1987 // Get the name of this parameter (in case it got renamed).
1988 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1989 parameterRef.getName());
1990
1991 // TODO: Should we support signed parameters?
1992 return {Symbol, IsUnsigned};
1993 }
1994
1995 // Handle nested expressions.
1996 auto expr = dyn_cast<ParamExprAttr>(value);
1997 if (!expr) {
1998 os << "<<UNKNOWN MLIRATTR: " << value << ">>";
1999 emitError() << " = " << value;
2000 return {LowestPrecedence, IsUnsigned};
2001 }
2002
2003 StringRef operatorStr;
2004 StringRef openStr, closeStr;
2005 VerilogPrecedence subprecedence = LowestPrecedence;
2006 VerilogPrecedence prec; // precedence of the emitted expression.
2007 std::optional<SubExprSignResult> operandSign;
2008 bool isUnary = false;
2009 bool hasOpenClose = false;
2010
2011 switch (expr.getOpcode()) {
2012 case PEO::Add:
2013 operatorStr = " + ";
2014 subprecedence = Addition;
2015 break;
2016 case PEO::Mul:
2017 operatorStr = " * ";
2018 subprecedence = Multiply;
2019 break;
2020 case PEO::And:
2021 operatorStr = " & ";
2022 subprecedence = And;
2023 break;
2024 case PEO::Or:
2025 operatorStr = " | ";
2026 subprecedence = Or;
2027 break;
2028 case PEO::Xor:
2029 operatorStr = " ^ ";
2030 subprecedence = Xor;
2031 break;
2032 case PEO::Shl:
2033 operatorStr = " << ";
2034 subprecedence = Shift;
2035 break;
2036 case PEO::ShrU:
2037 // >> in verilog is always a logical shift even if operands are signed.
2038 operatorStr = " >> ";
2039 subprecedence = Shift;
2040 break;
2041 case PEO::ShrS:
2042 // >>> in verilog is an arithmetic shift if both operands are signed.
2043 operatorStr = " >>> ";
2044 subprecedence = Shift;
2045 operandSign = IsSigned;
2046 break;
2047 case PEO::DivU:
2048 operatorStr = " / ";
2049 subprecedence = Multiply;
2050 operandSign = IsUnsigned;
2051 break;
2052 case PEO::DivS:
2053 operatorStr = " / ";
2054 subprecedence = Multiply;
2055 operandSign = IsSigned;
2056 break;
2057 case PEO::ModU:
2058 operatorStr = " % ";
2059 subprecedence = Multiply;
2060 operandSign = IsUnsigned;
2061 break;
2062 case PEO::ModS:
2063 operatorStr = " % ";
2064 subprecedence = Multiply;
2065 operandSign = IsSigned;
2066 break;
2067 case PEO::CLog2:
2068 openStr = "$clog2(";
2069 closeStr = ")";
2070 operandSign = IsUnsigned;
2071 hasOpenClose = true;
2072 prec = Symbol;
2073 break;
2074 case PEO::StrConcat:
2075 openStr = "{";
2076 closeStr = "}";
2077 hasOpenClose = true;
2078 operatorStr = ", ";
2079 // We don't have Concat precedence, but it's lowest anyway. (SV Table 11-2).
2080 subprecedence = LowestPrecedence;
2081 prec = Symbol;
2082 break;
2083 }
2084 if (!hasOpenClose)
2085 prec = subprecedence;
2086
2087 // unary -> one element.
2088 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2089 // one element -> {unary || open/close}.
2090 assert(isUnary || hasOpenClose ||
2091 !llvm::hasSingleElement(expr.getOperands()));
2092
2093 // Emit the specified operand with a $signed() or $unsigned() wrapper around
2094 // it if context requires a specific signedness to compute the right value.
2095 // This returns true if the operand is signed.
2096 // TODO: This could try harder to omit redundant casts like the mainline
2097 // expression emitter.
2098 auto emitOperand = [&](Attribute operand) -> bool {
2099 // If surrounding with signed/unsigned, inner expr doesn't need parens.
2100 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2101 if (operandSign.has_value())
2102 os << (*operandSign == IsSigned ? "$signed(" : "$unsigned(");
2103 auto signedness =
2104 printParamValue(operand, os, subprec, emitError).signedness;
2105 if (operandSign.has_value()) {
2106 os << ')';
2107 signedness = *operandSign;
2108 }
2109 return signedness == IsSigned;
2110 };
2111
2112 // Check outer precedence, wrap in parentheses if needed.
2113 if (prec > parenthesizeIfLooserThan)
2114 os << '(';
2115
2116 // Emit opening portion of the operation.
2117 if (hasOpenClose)
2118 os << openStr;
2119 else if (isUnary)
2120 os << operatorStr;
2121
2122 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2123 for (auto op : expr.getOperands().drop_front()) {
2124 // Handle the special case of (a + b + -42) as (a + b - 42).
2125 // TODO: Also handle (a + b + x*-1).
2126 if (expr.getOpcode() == PEO::Add) {
2127 if (auto integer = dyn_cast<IntegerAttr>(op)) {
2128 const APInt &value = integer.getValue();
2129 if (value.isNegative() && !value.isMinSignedValue()) {
2130 os << " - ";
2131 allOperandsSigned &=
2132 emitOperand(IntegerAttr::get(op.getType(), -value));
2133 continue;
2134 }
2135 }
2136 }
2137
2138 os << operatorStr;
2139 allOperandsSigned &= emitOperand(op);
2140 }
2141 if (hasOpenClose)
2142 os << closeStr;
2143 if (prec > parenthesizeIfLooserThan) {
2144 os << ')';
2145 prec = Selection;
2146 }
2147 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2148}
2149// NOLINTEND(misc-no-recursion)
2150
2151//===----------------------------------------------------------------------===//
2152// Expression Emission
2153//===----------------------------------------------------------------------===//
2154
2155namespace {
2156/// This builds a recursively nested expression from an SSA use-def graph. This
2157/// uses a post-order walk, but it needs to obey precedence and signedness
2158/// constraints that depend on the behavior of the child nodes.
2159/// To handle this, we must buffer all output so we can insert parentheses
2160/// and other things if we find out that it was needed later.
2161// NOLINTBEGIN(misc-no-recursion)
2162class ExprEmitter : public EmitterBase,
2163 public TypeOpVisitor<ExprEmitter, SubExprInfo>,
2164 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2165 public sv::Visitor<ExprEmitter, SubExprInfo> {
2166public:
2167 /// Create an ExprEmitter for the specified module emitter, and keeping track
2168 /// of any emitted expressions in the specified set.
2169 ExprEmitter(ModuleEmitter &emitter,
2170 SmallPtrSetImpl<Operation *> &emittedExprs)
2171 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2172
2173 ExprEmitter(ModuleEmitter &emitter,
2174 SmallPtrSetImpl<Operation *> &emittedExprs,
2175 BufferingPP::BufferVec &tokens)
2176 : EmitterBase(emitter.state), emitter(emitter),
2177 emittedExprs(emittedExprs), buffer(tokens),
2178 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2179 assert(state.pp.getListener() == &state.saver);
2180 }
2181
2182 /// Emit the specified value as an expression. If this is an inline-emitted
2183 /// expression, we emit that expression, otherwise we emit a reference to the
2184 /// already computed name.
2185 ///
2186 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2187 bool isAssignmentLikeContext) {
2188 assert(localTokens.empty());
2189 // Wrap to this column.
2190 ps.scopedBox(PP::ibox0, [&]() {
2191 // Require unsigned in an assignment context since every wire is
2192 // declared as unsigned.
2193 emitSubExpr(exp, parenthesizeIfLooserThan,
2194 /*signRequirement*/
2195 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2196 /*isSelfDeterminedUnsignedValue*/ false,
2197 isAssignmentLikeContext);
2198 });
2199 // If we are not using an external token buffer provided through the
2200 // constructor, but we're using the default `ExprEmitter`-scoped buffer,
2201 // flush it.
2202 if (&buffer.tokens == &localTokens)
2203 buffer.flush(state.pp);
2204 }
2205
2206private:
2207 friend class TypeOpVisitor<ExprEmitter, SubExprInfo>;
2208 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2209 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2210
2211 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2212
2213 /// Emit the specified value `exp` as a subexpression to the stream. The
2214 /// `parenthesizeIfLooserThan` parameter indicates when parentheses should be
2215 /// added aroun the subexpression. The `signReq` flag can cause emitSubExpr
2216 /// to emit a subexpression that is guaranteed to be signed or unsigned, and
2217 /// the `isSelfDeterminedUnsignedValue` flag indicates whether the value is
2218 /// known to be have "self determined" width, allowing us to omit extensions.
2219 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2220 SubExprSignRequirement signReq = NoRequirement,
2221 bool isSelfDeterminedUnsignedValue = false,
2222 bool isAssignmentLikeContext = false);
2223
2224 /// Emit SystemVerilog attributes attached to the expression op as dialect
2225 /// attributes.
2226 void emitSVAttributes(Operation *op);
2227
2228 SubExprInfo visitUnhandledExpr(Operation *op);
2229 SubExprInfo visitInvalidComb(Operation *op) {
2230 return dispatchTypeOpVisitor(op);
2231 }
2232 SubExprInfo visitUnhandledComb(Operation *op) {
2233 return visitUnhandledExpr(op);
2234 }
2235 SubExprInfo visitInvalidTypeOp(Operation *op) {
2236 return dispatchSVVisitor(op);
2237 }
2238 SubExprInfo visitUnhandledTypeOp(Operation *op) {
2239 return visitUnhandledExpr(op);
2240 }
2241 SubExprInfo visitUnhandledSV(Operation *op) { return visitUnhandledExpr(op); }
2242
2243 /// These are flags that control `emitBinary`.
2244 enum EmitBinaryFlags {
2245 EB_RequireSignedOperands = RequireSigned, /* 0x1*/
2246 EB_RequireUnsignedOperands = RequireUnsigned, /* 0x2*/
2247 EB_OperandSignRequirementMask = 0x3,
2248
2249 /// This flag indicates that the RHS operand is an unsigned value that has
2250 /// "self determined" width. This means that we can omit explicit zero
2251 /// extensions from it, and don't impose a sign on it.
2252 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2253
2254 /// This flag indicates that the result should be wrapped in a $signed(x)
2255 /// expression to force the result to signed.
2256 EB_ForceResultSigned = 0x8,
2257 };
2258
2259 /// Emit a binary expression. The "emitBinaryFlags" are a bitset from
2260 /// EmitBinaryFlags.
2261 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2262 const char *syntax, unsigned emitBinaryFlags = 0);
2263
2264 SubExprInfo emitUnary(Operation *op, const char *syntax,
2265 bool resultAlwaysUnsigned = false);
2266
2267 /// Emit the specified value as a subexpression, wrapping in an ibox2.
2268 void emitSubExprIBox2(
2269 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2270 ps.scopedBox(PP::ibox2,
2271 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2272 }
2273
2274 /// Emit a range of values separated by commas and a breakable space.
2275 /// Each value is emitted by invoking `eachFn`.
2276 template <typename Container, typename EachFn>
2277 void interleaveComma(const Container &c, EachFn eachFn) {
2278 llvm::interleave(c, eachFn, [&]() { ps << "," << PP::space; });
2279 }
2280
2281 /// Emit a range of values separated by commas and a breakable space.
2282 /// Each value is emitted in an ibox2.
2283 void interleaveComma(ValueRange ops) {
2284 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2285 }
2286
2287 /// Emit an array-literal-like structure, separated by commas.
2288 /// Use callbacks to emit open tokens, closing tokens, and handle each value.
2289 /// If it fits, will be emitted on a single line with no space between
2290 /// list and surrounding open and close.
2291 /// Otherwise, each item is placed on its own line.
2292 /// This has property that if any element requires breaking, all elements
2293 /// are emitted on separate lines (with open/close attached to first/last).
2294 /// `{a + b, x + y, c}`
2295 /// OR
2296 /// ```
2297 /// {a + b,
2298 /// x + y,
2299 /// c}
2300 /// ```
2301 template <typename Container, typename OpenFunc, typename CloseFunc,
2302 typename EachFunc>
2303 void emitBracedList(const Container &c, OpenFunc openFn, EachFunc eachFn,
2304 CloseFunc closeFn) {
2305 openFn();
2306 ps.scopedBox(PP::cbox0, [&]() {
2307 interleaveComma(c, eachFn);
2308 closeFn();
2309 });
2310 }
2311
2312 /// Emit braced list of values surrounded by specified open/close.
2313 template <typename OpenFunc, typename CloseFunc>
2314 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2315 return emitBracedList(
2316 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2317 }
2318
2319 /// Emit braced list of values surrounded by `{` and `}`.
2320 void emitBracedList(ValueRange ops) {
2321 return emitBracedList(
2322 ops, [&]() { ps << "{"; }, [&]() { ps << "}"; });
2323 }
2324
2325 /// Print an APInt constant.
2326 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2327
2328 /// Print a constant array.
2329 void printConstantArray(ArrayAttr elementValues, Type elementType,
2330 bool printAsPattern, Operation *op);
2331 /// Print a constant struct.
2332 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2333 ArrayAttr fieldValues, bool printAsPattern,
2334 Operation *op);
2335 /// Print an aggregate array or struct constant as the given type.
2336 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2337
2338 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2339 SubExprInfo visitSV(GetModportOp op);
2340 SubExprInfo visitSV(SystemFunctionOp op);
2341 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2342 SubExprInfo visitSV(XMROp op);
2343 SubExprInfo visitSV(SFormatFOp op);
2344 SubExprInfo visitSV(XMRRefOp op);
2345 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2346 SubExprInfo visitSV(VerbatimExprOp op) {
2347 return visitVerbatimExprOp(op, op.getSymbols());
2348 }
2349 SubExprInfo visitSV(VerbatimExprSEOp op) {
2350 return visitVerbatimExprOp(op, op.getSymbols());
2351 }
2352 SubExprInfo visitSV(MacroRefExprOp op);
2353 SubExprInfo visitSV(MacroRefExprSEOp op);
2354 template <typename MacroTy>
2355 SubExprInfo emitMacroCall(MacroTy op);
2356
2357 SubExprInfo visitSV(ConstantXOp op);
2358 SubExprInfo visitSV(ConstantZOp op);
2359 SubExprInfo visitSV(ConstantStrOp op);
2360 SubExprInfo visitSV(ConcatStrOp op);
2361
2362 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2363 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2364 // Cast op is noop.
2365 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2366 }
2367
2368 // Noop cast operators.
2369 SubExprInfo visitSV(ReadInOutOp op) {
2370 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2371 emitSVAttributes(op);
2372 return result;
2373 }
2374 SubExprInfo visitSV(ArrayIndexInOutOp op);
2375 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2376 SubExprInfo visitSV(IndexedPartSelectOp op);
2377 SubExprInfo visitSV(StructFieldInOutOp op);
2378
2379 // Sampled value functions
2380 SubExprInfo visitSV(SampledOp op);
2381
2382 // Time system functions
2383 SubExprInfo visitSV(TimeOp op);
2384 SubExprInfo visitSV(STimeOp op);
2385
2386 // Other
2387 using TypeOpVisitor::visitTypeOp;
2388 SubExprInfo visitTypeOp(ConstantOp op);
2389 SubExprInfo visitTypeOp(AggregateConstantOp op);
2390 SubExprInfo visitTypeOp(BitcastOp op);
2391 SubExprInfo visitTypeOp(ParamValueOp op);
2392 SubExprInfo visitTypeOp(ArraySliceOp op);
2393 SubExprInfo visitTypeOp(ArrayGetOp op);
2394 SubExprInfo visitTypeOp(ArrayCreateOp op);
2395 SubExprInfo visitTypeOp(ArrayConcatOp op);
2396 SubExprInfo visitTypeOp(StructCreateOp op);
2397 SubExprInfo visitTypeOp(StructExtractOp op);
2398 SubExprInfo visitTypeOp(StructInjectOp op);
2399 SubExprInfo visitTypeOp(UnionCreateOp op);
2400 SubExprInfo visitTypeOp(UnionExtractOp op);
2401 SubExprInfo visitTypeOp(EnumCmpOp op);
2402 SubExprInfo visitTypeOp(EnumConstantOp op);
2403
2404 // Comb Dialect Operations
2405 using CombinationalVisitor::visitComb;
2406 SubExprInfo visitComb(MuxOp op);
2407 SubExprInfo visitComb(ReverseOp op);
2408 SubExprInfo visitComb(AddOp op) {
2409 assert(op.getNumOperands() == 2 && "prelowering should handle variadics");
2410 return emitBinary(op, Addition, "+");
2411 }
2412 SubExprInfo visitComb(SubOp op) { return emitBinary(op, Addition, "-"); }
2413 SubExprInfo visitComb(MulOp op) {
2414 assert(op.getNumOperands() == 2 && "prelowering should handle variadics");
2415 return emitBinary(op, Multiply, "*");
2416 }
2417 SubExprInfo visitComb(DivUOp op) {
2418 return emitBinary(op, Multiply, "/", EB_RequireUnsignedOperands);
2419 }
2420 SubExprInfo visitComb(DivSOp op) {
2421 return emitBinary(op, Multiply, "/",
2422 EB_RequireSignedOperands | EB_ForceResultSigned);
2423 }
2424 SubExprInfo visitComb(ModUOp op) {
2425 return emitBinary(op, Multiply, "%", EB_RequireUnsignedOperands);
2426 }
2427 SubExprInfo visitComb(ModSOp op) {
2428 return emitBinary(op, Multiply, "%",
2429 EB_RequireSignedOperands | EB_ForceResultSigned);
2430 }
2431 SubExprInfo visitComb(ShlOp op) {
2432 return emitBinary(op, Shift, "<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2433 }
2434 SubExprInfo visitComb(ShrUOp op) {
2435 // >> in Verilog is always an unsigned right shift.
2436 return emitBinary(op, Shift, ">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2437 }
2438 SubExprInfo visitComb(ShrSOp op) {
2439 // >>> is only an arithmetic shift right when both operands are signed.
2440 // Otherwise it does a logical shift.
2441 return emitBinary(op, Shift, ">>>",
2442 EB_RequireSignedOperands | EB_ForceResultSigned |
2443 EB_RHS_UnsignedWithSelfDeterminedWidth);
2444 }
2445 SubExprInfo visitComb(AndOp op) {
2446 assert(op.getNumOperands() == 2 && "prelowering should handle variadics");
2447 return emitBinary(op, And, "&");
2448 }
2449 SubExprInfo visitComb(OrOp op) {
2450 assert(op.getNumOperands() == 2 && "prelowering should handle variadics");
2451 return emitBinary(op, Or, "|");
2452 }
2453 SubExprInfo visitComb(XorOp op) {
2454 if (op.isBinaryNot())
2455 return emitUnary(op, "~");
2456 assert(op.getNumOperands() == 2 && "prelowering should handle variadics");
2457 return emitBinary(op, Xor, "^");
2458 }
2459
2460 // SystemVerilog spec 11.8.1: "Reduction operator results are unsigned,
2461 // regardless of the operands."
2462 SubExprInfo visitComb(ParityOp op) { return emitUnary(op, "^", true); }
2463
2464 SubExprInfo visitComb(ReplicateOp op);
2465 SubExprInfo visitComb(ConcatOp op);
2466 SubExprInfo visitComb(ExtractOp op);
2467 SubExprInfo visitComb(ICmpOp op);
2468
2469 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2470 auto d = emitOpError(op, "must be printed as assignment pattern, but is "
2471 "not printed within an assignment-like context");
2472 d.attachNote() << "this is likely a bug in PrepareForEmission, which is "
2473 "supposed to spill such expressions";
2474 return d;
2475 }
2476
2477 SubExprInfo printStructCreate(
2478 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2479 llvm::function_ref<void(const hw::detail::FieldInfo &, unsigned)> fieldFn,
2480 bool printAsPattern, Operation *op);
2481
2482public:
2483 ModuleEmitter &emitter;
2484
2485private:
2486 /// This is set (before a visit method is called) if emitSubExpr would
2487 /// prefer to get an output of a specific sign. This is a hint to cause the
2488 /// visitor to change its emission strategy, but the visit method can ignore
2489 /// it without a correctness problem.
2490 SubExprSignRequirement signPreference = NoRequirement;
2491
2492 /// Keep track of all operations emitted within this subexpression for
2493 /// location information tracking.
2494 SmallPtrSetImpl<Operation *> &emittedExprs;
2495
2496 /// Tokens buffered for inserting casts/parens after emitting children.
2497 SmallVector<Token> localTokens;
2498
2499 /// Stores tokens until told to flush. Uses provided buffer (tokens).
2500 BufferingPP buffer;
2501
2502 /// Stream to emit expressions into, will add to buffer.
2504
2505 /// Tracks whether the expression being emitted is currently within an
2506 /// assignment-like context. Certain constructs such as `'{...}` assignment
2507 /// patterns are restricted to only appear in assignment-like contexts.
2508 /// Others, like packed struct and array constants, can be printed as either
2509 /// `{...}` concatenation or `'{...}` assignment pattern, depending on whether
2510 /// they appear within an assignment-like context or not.
2511 bool isAssignmentLikeContext = false;
2512};
2513} // end anonymous namespace
2514
2515SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2516 const char *syntax,
2517 unsigned emitBinaryFlags) {
2518 if (hasSVAttributes(op))
2519 emitError(op, "SV attributes emission is unimplemented for the op");
2520
2521 // It's tempting to wrap expressions in groups as we emit them,
2522 // but that can cause bad wrapping as-is:
2523 // add(a, add(b, add(c, add(d, e))))
2524 // ->
2525 // group(a + (group(b + group(c + group(d + e)))))
2526 // Which will break after 'a +' first.
2527 // TODO: Build tree capturing precedence/fixity at same level, group those!
2528 // Maybe like: https://www.tweag.io/blog/2022-02-10-ormolu-and-operators/ .
2529 // For now, only group within punctuation, such as parens + braces.
2530 if (emitBinaryFlags & EB_ForceResultSigned)
2531 ps << "$signed(" << PP::ibox0;
2532 auto operandSignReq =
2533 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2534 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2535 // Bit of a kludge: if this is a comparison or equality, don't break on either
2536 // side.
2537 auto lhsSpace = (prec == VerilogPrecedence::Comparison ||
2538 prec == VerilogPrecedence::Equality)
2539 ? PP::nbsp
2540 : PP::space;
2541 // Use non-breaking space between op and RHS so breaking is consistent.
2542 ps << lhsSpace << syntax << PP::nbsp; // PP::space;
2543
2544 // Right associative operators are already generally variadic, we need to
2545 // handle things like: (a<4> == b<4>) == (c<3> == d<3>). When processing the
2546 // top operation of the tree, the rhs needs parens. When processing
2547 // known-reassociative operators like +, ^, etc we don't need parens.
2548 // TODO: MLIR should have general "Associative" trait.
2549 auto rhsPrec = prec;
2550 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2551 rhsPrec = VerilogPrecedence(prec - 1);
2552
2553 // If the RHS operand has self-determined width and always treated as
2554 // unsigned, inform emitSubExpr of this. This is true for the shift amount in
2555 // a shift operation.
2556 bool rhsIsUnsignedValueWithSelfDeterminedWidth = false;
2557 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2558 rhsIsUnsignedValueWithSelfDeterminedWidth = true;
2559 operandSignReq = NoRequirement;
2560 }
2561
2562 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2563 rhsIsUnsignedValueWithSelfDeterminedWidth);
2564
2565 // SystemVerilog 11.8.1 says that the result of a binary expression is signed
2566 // only if both operands are signed.
2567 SubExprSignResult signedness = IsUnsigned;
2568 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2569 signedness = IsSigned;
2570
2571 if (emitBinaryFlags & EB_ForceResultSigned) {
2572 ps << PP::end << ")";
2573 signedness = IsSigned;
2574 prec = Selection;
2575 }
2576
2577 return {prec, signedness};
2578}
2579
2580SubExprInfo ExprEmitter::emitUnary(Operation *op, const char *syntax,
2581 bool resultAlwaysUnsigned) {
2582 if (hasSVAttributes(op))
2583 emitError(op, "SV attributes emission is unimplemented for the op");
2584
2585 ps << syntax;
2586 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2587 // For reduction operators "&" and "|", make precedence lowest to avoid
2588 // emitting an expression like `a & &b`, which is syntactically valid but some
2589 // tools produce LINT warnings.
2590 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2591 resultAlwaysUnsigned ? IsUnsigned : signedness};
2592}
2593
2594/// Emit SystemVerilog attributes attached to the expression op as dialect
2595/// attributes.
2596void ExprEmitter::emitSVAttributes(Operation *op) {
2597 // SystemVerilog 2017 Section 5.12.
2598 auto svAttrs = getSVAttributes(op);
2599 if (!svAttrs)
2600 return;
2601
2602 // For now, no breaks for attributes.
2603 ps << PP::nbsp;
2604 emitSVAttributesImpl(ps, svAttrs, /*mayBreak=*/false);
2605}
2606
2607/// If the specified extension is a zero extended version of another value,
2608/// return the shorter value, otherwise return null.
2609static Value isZeroExtension(Value value) {
2610 auto concat = value.getDefiningOp<ConcatOp>();
2611 if (!concat || concat.getNumOperands() != 2)
2612 return {};
2613
2614 auto constant = concat.getOperand(0).getDefiningOp<ConstantOp>();
2615 if (constant && constant.getValue().isZero())
2616 return concat.getOperand(1);
2617 return {};
2618}
2619
2620/// Emit the specified value `exp` as a subexpression to the stream. The
2621/// `parenthesizeIfLooserThan` parameter indicates when parentheses should be
2622/// added aroun the subexpression. The `signReq` flag can cause emitSubExpr
2623/// to emit a subexpression that is guaranteed to be signed or unsigned, and
2624/// the `isSelfDeterminedUnsignedValue` flag indicates whether the value is
2625/// known to be have "self determined" width, allowing us to omit extensions.
2626SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2627 VerilogPrecedence parenthesizeIfLooserThan,
2628 SubExprSignRequirement signRequirement,
2629 bool isSelfDeterminedUnsignedValue,
2630 bool isAssignmentLikeContext) {
2631 // `verif.contract` ops act as no-ops.
2632 if (auto result = dyn_cast<OpResult>(exp))
2633 if (auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2634 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2635 parenthesizeIfLooserThan, signRequirement,
2636 isSelfDeterminedUnsignedValue,
2637 isAssignmentLikeContext);
2638
2639 // If this is a self-determined unsigned value, look through any inline zero
2640 // extensions. This occurs on the RHS of a shift operation for example.
2641 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2642 if (auto smaller = isZeroExtension(exp))
2643 exp = smaller;
2644 }
2645
2646 auto *op = exp.getDefiningOp();
2647 bool shouldEmitInlineExpr = op && isVerilogExpression(op);
2648
2649 // If this is a non-expr or shouldn't be done inline, just refer to its name.
2650 if (!shouldEmitInlineExpr) {
2651 // All wires are declared as unsigned, so if the client needed it signed,
2652 // emit a conversion.
2653 if (signRequirement == RequireSigned) {
2654 ps << "$signed(" << PPExtString(getVerilogValueName(exp)) << ")";
2655 return {Symbol, IsSigned};
2656 }
2657
2658 ps << PPExtString(getVerilogValueName(exp));
2659 return {Symbol, IsUnsigned};
2660 }
2661
2662 unsigned subExprStartIndex = buffer.tokens.size();
2663 if (op)
2664 ps.addCallback({op, true});
2665 llvm::scope_exit done([&]() {
2666 if (op)
2667 ps.addCallback({op, false});
2668 });
2669
2670 // Inform the visit method about the preferred sign we want from the result.
2671 // It may choose to ignore this, but some emitters can change behavior based
2672 // on contextual desired sign.
2673 signPreference = signRequirement;
2674
2675 bool bitCastAdded = false;
2676 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2677 if (auto inType =
2678 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2679 ps.addAsString(inType.getWidth());
2680 ps << "'(" << PP::ibox0;
2681 bitCastAdded = true;
2682 }
2683 // Okay, this is an expression we should emit inline. Do this through our
2684 // visitor.
2685 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2686 isAssignmentLikeContext);
2687 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2688
2689 // Check cases where we have to insert things before the expression now that
2690 // we know things about it.
2691 auto addPrefix = [&](StringToken &&t) {
2692 // insert {Prefix, ibox0}.
2693 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2694 BeginToken(0));
2695 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2696 };
2697 auto closeBoxAndParen = [&]() { ps << PP::end << ")"; };
2698 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2699 addPrefix(StringToken("$signed("));
2700 closeBoxAndParen();
2701 expInfo.signedness = IsSigned;
2702 expInfo.precedence = Selection;
2703 } else if (signRequirement == RequireUnsigned &&
2704 expInfo.signedness == IsSigned) {
2705 addPrefix(StringToken("$unsigned("));
2706 closeBoxAndParen();
2707 expInfo.signedness = IsUnsigned;
2708 expInfo.precedence = Selection;
2709 } else if (expInfo.precedence > parenthesizeIfLooserThan) {
2710 // If this subexpression would bind looser than the expression it is bound
2711 // into, then we need to parenthesize it. Insert the parentheses
2712 // retroactively.
2713 addPrefix(StringToken("("));
2714 closeBoxAndParen();
2715 // Reset the precedence to the () level.
2716 expInfo.precedence = Selection;
2717 }
2718 if (bitCastAdded) {
2719 closeBoxAndParen();
2720 }
2721
2722 // Remember that we emitted this.
2723 emittedExprs.insert(exp.getDefiningOp());
2724 return expInfo;
2725}
2726
2727SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2728 auto openFn = [&]() {
2729 ps << "{";
2730 ps.addAsString(op.getMultiple());
2731 ps << "{";
2732 };
2733 auto closeFn = [&]() { ps << "}}"; };
2734
2735 // If the subexpression is an inline concat, we can emit it as part of the
2736 // replicate.
2737 if (auto concatOp = op.getOperand().getDefiningOp<ConcatOp>()) {
2738 if (op.getOperand().hasOneUse()) {
2739 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2740 return {Symbol, IsUnsigned};
2741 }
2742 }
2743 emitBracedList(op.getOperand(), openFn, closeFn);
2744 return {Symbol, IsUnsigned};
2745}
2746
2747SubExprInfo ExprEmitter::visitComb(ConcatOp op) {
2748 emitBracedList(op.getOperands());
2749 return {Symbol, IsUnsigned};
2750}
2751
2752SubExprInfo ExprEmitter::visitTypeOp(BitcastOp op) {
2753 // NOTE: Bitcasts are emitted out-of-line with their own wire declaration when
2754 // their dimensions don't match. SystemVerilog uses the wire declaration to
2755 // know what type this value is being casted to.
2756 Type toType = op.getType();
2757 if (!haveMatchingDims(
2758 toType, op.getInput().getType(), op.getLoc(),
2759 [&](Location loc) { return emitter.emitError(loc, ""); })) {
2760 ps << "/*cast(bit";
2761 ps.invokeWithStringOS(
2762 [&](auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2763 ps << ")*/";
2764 }
2765 return emitSubExpr(op.getInput(), LowestPrecedence);
2766}
2767
2768SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2769 const char *symop[] = {"==", "!=", "<", "<=", ">", ">=", "<",
2770 "<=", ">", ">=", "===", "!==", "==?", "!=?"};
2771 SubExprSignRequirement signop[] = {
2772 // Equality
2773 NoRequirement, NoRequirement,
2774 // Signed Comparisons
2775 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2776 // Unsigned Comparisons
2777 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2778 // Weird Comparisons
2779 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2780
2781 auto pred = static_cast<uint64_t>(op.getPredicate());
2782 assert(pred < sizeof(symop) / sizeof(symop[0]));
2783
2784 // Lower "== -1" to Reduction And.
2785 if (op.isEqualAllOnes())
2786 return emitUnary(op, "&", true);
2787
2788 // Lower "!= 0" to Reduction Or.
2789 if (op.isNotEqualZero())
2790 return emitUnary(op, "|", true);
2791
2792 VerilogPrecedence precedence = Comparison;
2793 switch (op.getPredicate()) {
2794 case ICmpPredicate::eq:
2795 case ICmpPredicate::ne:
2796 case ICmpPredicate::ceq:
2797 case ICmpPredicate::cne:
2798 case ICmpPredicate::weq:
2799 case ICmpPredicate::wne:
2800 precedence = Equality;
2801 break;
2802 default:
2803 precedence = Comparison;
2804 break;
2805 }
2806 auto result = emitBinary(op, precedence, symop[pred], signop[pred]);
2807
2808 // SystemVerilog 11.8.1: "Comparison... operator results are unsigned,
2809 // regardless of the operands".
2810 result.signedness = IsUnsigned;
2811 return result;
2812}
2813
2814SubExprInfo ExprEmitter::visitComb(ExtractOp op) {
2815 if (hasSVAttributes(op))
2816 emitError(op, "SV attributes emission is unimplemented for the op");
2817
2818 unsigned loBit = op.getLowBit();
2819 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2820
2821 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2822 assert((x.precedence == Symbol ||
2823 (x.precedence == Selection && isOkToBitSelectFrom(op.getInput()))) &&
2824 "should be handled by isExpressionUnableToInline");
2825
2826 // If we're extracting the whole input, just return it. This is valid but
2827 // non-canonical IR, and we don't want to generate invalid Verilog.
2828 if (loBit == 0 &&
2829 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2830 return x;
2831
2832 ps << "[";
2833 ps.addAsString(hiBit);
2834 if (hiBit != loBit) { // Emit x[4] instead of x[4:4].
2835 ps << ":";
2836 ps.addAsString(loBit);
2837 }
2838 ps << "]";
2839 return {Unary, IsUnsigned};
2840}
2841
2842SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2843 if (hasSVAttributes(op))
2844 emitError(op, "SV attributes emission is unimplemented for the op");
2845
2846 auto decl = op.getReferencedDecl(state.symbolCache);
2847 ps << PPExtString(getVerilogValueName(op.getIface())) << "."
2848 << PPExtString(getSymOpName(decl));
2849 return {Selection, IsUnsigned};
2850}
2851
2852SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2853 if (hasSVAttributes(op))
2854 emitError(op, "SV attributes emission is unimplemented for the op");
2855
2856 ps << "$" << PPExtString(op.getFnName()) << "(";
2857 ps.scopedBox(PP::ibox0, [&]() {
2858 llvm::interleave(
2859 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2860 [&]() { ps << "," << PP::space; });
2861 ps << ")";
2862 });
2863 return {Symbol, IsUnsigned};
2864}
2865
2866SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2867 if (hasSVAttributes(op))
2868 emitError(op, "SV attributes emission is unimplemented for the op");
2869
2870 auto decl = op.getReferencedDecl(state.symbolCache);
2871
2872 ps << PPExtString(getVerilogValueName(op.getIface())) << "."
2873 << PPExtString(getSymOpName(decl));
2874 return {Selection, IsUnsigned};
2875}
2876
2877SubExprInfo ExprEmitter::visitSV(XMROp op) {
2878 if (hasSVAttributes(op))
2879 emitError(op, "SV attributes emission is unimplemented for the op");
2880
2881 if (op.getIsRooted())
2882 ps << "$root.";
2883 for (auto s : op.getPath())
2884 ps << PPExtString(cast<StringAttr>(s).getValue()) << ".";
2885 ps << PPExtString(op.getTerminal());
2886 return {Selection, IsUnsigned};
2887}
2888
2889// TODO: This shares a lot of code with the getNameRemotely mtehod. Combine
2890// these to share logic.
2891SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2892 if (hasSVAttributes(op))
2893 emitError(op, "SV attributes emission is unimplemented for the op");
2894
2895 // The XMR is pointing at a GlobalRef.
2896 auto globalRef = op.getReferencedPath(&state.symbolCache);
2897 auto namepath = globalRef.getNamepathAttr().getValue();
2898 auto *module = state.symbolCache.getDefinition(
2899 cast<InnerRefAttr>(namepath.front()).getModule());
2900 ps << PPExtString(getSymOpName(module));
2901 for (auto sym : namepath) {
2902 ps << ".";
2903 auto innerRef = cast<InnerRefAttr>(sym);
2904 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2905 innerRef.getName());
2906 if (ref.hasPort()) {
2907 ps << PPExtString(getPortVerilogName(ref.getOp(), ref.getPort()));
2908 continue;
2909 }
2910 ps << PPExtString(getSymOpName(ref.getOp()));
2911 }
2912 auto leaf = op.getVerbatimSuffixAttr();
2913 if (leaf && leaf.size())
2914 ps << PPExtString(leaf);
2915 return {Selection, IsUnsigned};
2916}
2917
2918SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2919 if (hasSVAttributes(op))
2920 emitError(op, "SV attributes emission is unimplemented for the op");
2921
2922 emitTextWithSubstitutions(
2923 ps, op->getAttrOfType<StringAttr>("format_string").getValue(), op,
2924 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2925
2926 return {Unary, IsUnsigned};
2927}
2928
2929template <typename MacroTy>
2930SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2931 if (hasSVAttributes(op))
2932 emitError(op, "SV attributes emission is unimplemented for the op");
2933
2934 // Use the specified name or the symbol name as appropriate.
2935 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2936 assert(macroOp && "Invalid IR");
2937 StringRef name =
2938 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2939 ps << "`" << PPExtString(name);
2940 if (!op.getInputs().empty()) {
2941 ps << "(";
2942 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2943 emitExpression(val, LowestPrecedence, /*isAssignmentLikeContext=*/false);
2944 });
2945 ps << ")";
2946 }
2947 return {LowestPrecedence, IsUnsigned};
2948}
2949
2950SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2951 return emitMacroCall(op);
2952}
2953
2954SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2955 return emitMacroCall(op);
2956}
2957
2958SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2959 if (hasSVAttributes(op))
2960 emitError(op, "SV attributes emission is unimplemented for the op");
2961
2962 ps.addAsString(op.getWidth());
2963 ps << "'bx";
2964 return {Unary, IsUnsigned};
2965}
2966
2967SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2968 if (hasSVAttributes(op))
2969 emitError(op, "SV attributes emission is unimplemented for the op");
2970
2971 ps.writeQuotedEscaped(op.getStr());
2972 return {Symbol, IsUnsigned}; // is a string unsigned? Yes! SV 5.9
2973}
2974
2975SubExprInfo ExprEmitter::visitSV(ConcatStrOp op) {
2976 if (hasSVAttributes(op))
2977 emitError(op, "SV attributes emission is unimplemented for the op");
2978
2979 // Emits the SystemVerilog concatenation `{a, b, ...}`. Strings are unsigned
2980 // (SV 5.9) and braces bind at the primary/Symbol level.
2981 emitBracedList(op.getInputs());
2982 return {Symbol, IsUnsigned};
2983}
2984
2985SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2986 if (hasSVAttributes(op))
2987 emitError(op, "SV attributes emission is unimplemented for the op");
2988
2989 ps.addAsString(op.getWidth());
2990 ps << "'bz";
2991 return {Unary, IsUnsigned};
2992}
2993
2994SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2995 bool isNegated = false;
2996 // If this is a negative signed number and not MININT (e.g. -128), then print
2997 // it as a negated positive number.
2998 if (signPreference == RequireSigned && value.isNegative() &&
2999 !value.isMinSignedValue()) {
3000 ps << "-";
3001 isNegated = true;
3002 }
3003
3004 ps.addAsString(type.getWidth());
3005 ps << "'";
3006
3007 // Emit this as a signed constant if the caller would prefer that.
3008 if (signPreference == RequireSigned)
3009 ps << "sh";
3010 else
3011 ps << "h";
3012
3013 // Print negated if required.
3014 SmallString<32> valueStr;
3015 if (isNegated) {
3016 (-value).toStringUnsigned(valueStr, 16);
3017 } else {
3018 value.toStringUnsigned(valueStr, 16);
3019 }
3020 ps << valueStr;
3021 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
3022}
3023
3024SubExprInfo ExprEmitter::visitTypeOp(ConstantOp op) {
3025 if (hasSVAttributes(op))
3026 emitError(op, "SV attributes emission is unimplemented for the op");
3027
3028 auto value = op.getValue();
3029 // We currently only allow zero width values to be handled as special cases in
3030 // the various operations that may come across them. If we reached this point
3031 // in the emitter, the value should be considered illegal to emit.
3032 if (value.getBitWidth() == 0) {
3033 emitOpError(op, "will not emit zero width constants in the general case");
3034 ps << "<<unsupported zero width constant: "
3035 << PPExtString(op->getName().getStringRef()) << ">>";
3036 return {Unary, IsUnsigned};
3037 }
3038
3039 return printConstantScalar(value, cast<IntegerType>(op.getType()));
3040}
3041
3042void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type elementType,
3043 bool printAsPattern, Operation *op) {
3044 if (printAsPattern && !isAssignmentLikeContext)
3045 emitAssignmentPatternContextError(op);
3046 StringRef openDelim = printAsPattern ? "'{" : "{";
3047
3048 emitBracedList(
3049 elementValues, [&]() { ps << openDelim; },
3050 [&](Attribute elementValue) {
3051 printConstantAggregate(elementValue, elementType, op);
3052 },
3053 [&]() { ps << "}"; });
3054}
3055
3056void ExprEmitter::printConstantStruct(
3057 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3058 bool printAsPattern, Operation *op) {
3059 if (printAsPattern && !isAssignmentLikeContext)
3060 emitAssignmentPatternContextError(op);
3061
3062 // Only emit elements with non-zero bit width.
3063 // TODO: Ideally we should emit zero bit values as comments, e.g. `{/*a:
3064 // ZeroBit,*/ b: foo, /* c: ZeroBit*/ d: bar}`. However it's tedious to
3065 // nicely emit all edge cases hence currently we just elide zero bit
3066 // values.
3067 auto fieldRange = llvm::make_filter_range(
3068 llvm::zip(fieldInfos, fieldValues), [](const auto &fieldAndValue) {
3069 // Elide zero bit elements.
3070 return !isZeroBitType(std::get<0>(fieldAndValue).type);
3071 });
3072
3073 if (printAsPattern) {
3074 emitBracedList(
3075 fieldRange, [&]() { ps << "'{"; },
3076 [&](const auto &fieldAndValue) {
3077 ps.scopedBox(PP::ibox2, [&]() {
3078 const auto &[field, value] = fieldAndValue;
3079 ps << PPExtString(emitter.getVerilogStructFieldName(field.name))
3080 << ":" << PP::space;
3081 printConstantAggregate(value, field.type, op);
3082 });
3083 },
3084 [&]() { ps << "}"; });
3085 } else {
3086 emitBracedList(
3087 fieldRange, [&]() { ps << "{"; },
3088 [&](const auto &fieldAndValue) {
3089 ps.scopedBox(PP::ibox2, [&]() {
3090 const auto &[field, value] = fieldAndValue;
3091 printConstantAggregate(value, field.type, op);
3092 });
3093 },
3094 [&]() { ps << "}"; });
3095 }
3096}
3097
3098void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3099 Operation *op) {
3100 // Packed arrays can be printed as concatenation or pattern.
3101 if (auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3102 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3103 isAssignmentLikeContext, op);
3104
3105 // Unpacked arrays must be printed as pattern.
3106 if (auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3107 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3108 true, op);
3109
3110 // Packed structs can be printed as concatenation or pattern.
3111 if (auto structType = hw::type_dyn_cast<StructType>(type))
3112 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3113 isAssignmentLikeContext, op);
3114
3115 if (auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3116 auto value = cast<IntegerAttr>(attr).getValue();
3117 printConstantScalar(value, intType);
3118 return;
3119 }
3120
3121 emitOpError(op, "contains constant of type ")
3122 << type << " which cannot be emitted as Verilog";
3123}
3124
3125SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3126 if (hasSVAttributes(op))
3127 emitError(op, "SV attributes emission is unimplemented for the op");
3128
3129 // If the constant op as a whole is zero-width, it is an error.
3130 assert(!isZeroBitType(op.getType()) &&
3131 "zero-bit types not allowed at this point");
3132
3133 printConstantAggregate(op.getFields(), op.getType(), op);
3134 return {Symbol, IsUnsigned};
3135}
3136
3137SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3138 if (hasSVAttributes(op))
3139 emitError(op, "SV attributes emission is unimplemented for the op");
3140
3141 return ps.invokeWithStringOS([&](auto &os) {
3142 return emitter.printParamValue(op.getValue(), os, [&]() {
3143 return op->emitOpError("invalid parameter use");
3144 });
3145 });
3146}
3147
3148// 11.5.1 "Vector bit-select and part-select addressing" allows a '+:' syntax
3149// for slicing operations.
3150SubExprInfo ExprEmitter::visitTypeOp(ArraySliceOp op) {
3151 if (hasSVAttributes(op))
3152 emitError(op, "SV attributes emission is unimplemented for the op");
3153
3154 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3155
3156 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3157 ps << "[";
3158 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3159 ps << " +: ";
3160 ps.addAsString(dstWidth);
3161 ps << "]";
3162 return {Selection, arrayPrec.signedness};
3163}
3164
3165SubExprInfo ExprEmitter::visitTypeOp(ArrayGetOp op) {
3166 emitSubExpr(op.getInput(), Selection);
3167 ps << "[";
3168 if (isZeroBitType(op.getIndex().getType()))
3170 else
3171 emitSubExpr(op.getIndex(), LowestPrecedence);
3172 ps << "]";
3173 emitSVAttributes(op);
3174 return {Selection, IsUnsigned};
3175}
3176
3177// Syntax from: section 5.11 "Array literals".
3178SubExprInfo ExprEmitter::visitTypeOp(ArrayCreateOp op) {
3179 if (hasSVAttributes(op))
3180 emitError(op, "SV attributes emission is unimplemented for the op");
3181
3182 if (op.isUniform()) {
3183 ps << "{";
3184 ps.addAsString(op.getInputs().size());
3185 ps << "{";
3186 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3187 ps << "}}";
3188 } else {
3189 emitBracedList(
3190 op.getInputs(), [&]() { ps << "{"; },
3191 [&](Value v) {
3192 ps << "{";
3193 emitSubExprIBox2(v);
3194 ps << "}";
3195 },
3196 [&]() { ps << "}"; });
3197 }
3198 return {Unary, IsUnsigned};
3199}
3200
3201SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3202 if (hasSVAttributes(op))
3203 emitError(op, "SV attributes emission is unimplemented for the op");
3204
3205 emitBracedList(
3206 llvm::reverse(op.getInputs()), [&]() { ps << "'{"; },
3207 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps << "}"; });
3208 return {Unary, IsUnsigned};
3209}
3210
3211SubExprInfo ExprEmitter::visitTypeOp(ArrayConcatOp op) {
3212 if (hasSVAttributes(op))
3213 emitError(op, "SV attributes emission is unimplemented for the op");
3214
3215 emitBracedList(op.getOperands());
3216 return {Unary, IsUnsigned};
3217}
3218
3219SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3220 if (hasSVAttributes(op))
3221 emitError(op, "SV attributes emission is unimplemented for the op");
3222
3223 auto index = op.getIndex();
3224 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3225 ps << "[";
3226 if (isZeroBitType(index.getType()))
3228 else
3229 emitSubExpr(index, LowestPrecedence);
3230 ps << "]";
3231 return {Selection, arrayPrec.signedness};
3232}
3233
3234SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3235 if (hasSVAttributes(op))
3236 emitError(op, "SV attributes emission is unimplemented for the op");
3237
3238 auto prec = emitSubExpr(op.getInput(), Selection);
3239 ps << "[";
3240 emitSubExpr(op.getBase(), LowestPrecedence);
3241 if (op.getDecrement())
3242 ps << " -: ";
3243 else
3244 ps << " +: ";
3245 ps.addAsString(op.getWidth());
3246 ps << "]";
3247 return {Selection, prec.signedness};
3248}
3249
3250SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3251 if (hasSVAttributes(op))
3252 emitError(op, "SV attributes emission is unimplemented for the op");
3253
3254 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3255 ps << "[";
3256 emitSubExpr(op.getBase(), LowestPrecedence);
3257 if (op.getDecrement())
3258 ps << " -: ";
3259 else
3260 ps << " +: ";
3261 ps.addAsString(op.getWidth());
3262 ps << "]";
3263 return info;
3264}
3265
3266SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3267 if (hasSVAttributes(op))
3268 emitError(op, "SV attributes emission is unimplemented for the op");
3269
3270 auto prec = emitSubExpr(op.getInput(), Selection);
3271 ps << "."
3272 << PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3273 return {Selection, prec.signedness};
3274}
3275
3276SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3277 if (hasSVAttributes(op))
3278 emitError(op, "SV attributes emission is unimplemented for the op");
3279
3280 ps << "$sampled(";
3281 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3282 ps << ")";
3283 return info;
3284}
3285
3286SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3287 if (hasSVAttributes(op))
3288 emitError(op, "SV attributes emission is unimplemented for the op");
3289
3290 ps << "$sformatf(";
3291 ps.scopedBox(PP::ibox0, [&]() {
3292 ps.writeQuotedEscaped(op.getFormatString());
3293 // TODO: if any of these breaks, it'd be "nice" to break
3294 // after the comma, instead of:
3295 // $sformatf("...", a + b,
3296 // longexpr_goes
3297 // + here, c);
3298 // (without forcing breaking between all elements, like braced list)
3299 for (auto operand : op.getSubstitutions()) {
3300 ps << "," << PP::space;
3301 emitSubExpr(operand, LowestPrecedence);
3302 }
3303 });
3304 ps << ")";
3305 return {Symbol, IsUnsigned};
3306}
3307
3308SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3309 if (hasSVAttributes(op))
3310 emitError(op, "SV attributes emission is unimplemented for the op");
3311
3312 ps << "$time";
3313 return {Symbol, IsUnsigned};
3314}
3315
3316SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3317 if (hasSVAttributes(op))
3318 emitError(op, "SV attributes emission is unimplemented for the op");
3319
3320 ps << "$stime";
3321 return {Symbol, IsUnsigned};
3322}
3323
3324SubExprInfo ExprEmitter::visitComb(MuxOp op) {
3325 // The ?: operator is right associative.
3326
3327 // Layout:
3328 // cond ? a : b
3329 // (long
3330 // + cond) ? a : b
3331 // long
3332 // + cond
3333 // ? a : b
3334 // long
3335 // + cond
3336 // ? a
3337 // : b
3338 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3339 ps.scopedBox(PP::ibox0, [&]() {
3340 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3341 });
3342 ps << BreakToken(1, 2);
3343 ps << "?";
3344 emitSVAttributes(op);
3345 ps << " ";
3346 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3347 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3348 });
3349 ps << BreakToken(1, 2) << ": ";
3350
3351 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3352 return emitSubExpr(op.getFalseValue(), Conditional);
3353 });
3354
3355 SubExprSignResult signedness = IsUnsigned;
3356 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3357 signedness = IsSigned;
3358
3359 return {Conditional, signedness};
3360 });
3361}
3362
3363SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3364 if (hasSVAttributes(op))
3365 emitError(op, "SV attributes emission is unimplemented for the op");
3366
3367 ps << "{<<{";
3368 emitSubExpr(op.getInput(), LowestPrecedence);
3369 ps << "}}";
3370
3371 return {Symbol, IsUnsigned};
3372}
3373
3374SubExprInfo ExprEmitter::printStructCreate(
3375 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3376 llvm::function_ref<void(const hw::detail::FieldInfo &, unsigned)> fieldFn,
3377 bool printAsPattern, Operation *op) {
3378 if (printAsPattern && !isAssignmentLikeContext)
3379 emitAssignmentPatternContextError(op);
3380
3381 // Elide zero bit elements.
3382 auto filteredFields = llvm::make_filter_range(
3383 llvm::enumerate(fieldInfos),
3384 [](const auto &field) { return !isZeroBitType(field.value().type); });
3385
3386 if (printAsPattern) {
3387 emitBracedList(
3388 filteredFields, [&]() { ps << "'{"; },
3389 [&](const auto &field) {
3390 ps.scopedBox(PP::ibox2, [&]() {
3391 ps << PPExtString(
3392 emitter.getVerilogStructFieldName(field.value().name))
3393 << ":" << PP::space;
3394 fieldFn(field.value(), field.index());
3395 });
3396 },
3397 [&]() { ps << "}"; });
3398 } else {
3399 emitBracedList(
3400 filteredFields, [&]() { ps << "{"; },
3401 [&](const auto &field) {
3402 ps.scopedBox(PP::ibox2,
3403 [&]() { fieldFn(field.value(), field.index()); });
3404 },
3405 [&]() { ps << "}"; });
3406 }
3407
3408 return {Selection, IsUnsigned};
3409}
3410
3411SubExprInfo ExprEmitter::visitTypeOp(StructCreateOp op) {
3412 if (hasSVAttributes(op))
3413 emitError(op, "SV attributes emission is unimplemented for the op");
3414
3415 // TODO: For unpacked structs, once we have support for them, `printAsPattern`
3416 // should be set to true.
3417 bool printAsPattern = isAssignmentLikeContext;
3418 StructType structType = op.getType();
3419 return printStructCreate(
3420 structType.getElements(),
3421 [&](const auto &field, auto index) {
3422 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3423 /*isSelfDeterminedUnsignedValue=*/false,
3424 /*isAssignmentLikeContext=*/isAssignmentLikeContext);
3425 },
3426 printAsPattern, op);
3427}
3428
3429SubExprInfo ExprEmitter::visitTypeOp(StructExtractOp op) {
3430 if (hasSVAttributes(op))
3431 emitError(op, "SV attributes emission is unimplemented for the op");
3432
3433 emitSubExpr(op.getInput(), Selection);
3434 ps << "."
3435 << PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3436 return {Selection, IsUnsigned};
3437}
3438
3439SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3440 if (hasSVAttributes(op))
3441 emitError(op, "SV attributes emission is unimplemented for the op");
3442
3443 // TODO: For unpacked structs, once we have support for them, `printAsPattern`
3444 // should be set to true.
3445 bool printAsPattern = isAssignmentLikeContext;
3446 StructType structType = op.getType();
3447 return printStructCreate(
3448 structType.getElements(),
3449 [&](const auto &field, auto index) {
3450 if (field.name == op.getFieldNameAttr()) {
3451 emitSubExpr(op.getNewValue(), Selection);
3452 } else {
3453 emitSubExpr(op.getInput(), Selection);
3454 ps << "."
3455 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3456 }
3457 },
3458 printAsPattern, op);
3459}
3460
3461SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3462 ps << PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3463 return {Selection, IsUnsigned};
3464}
3465
3466SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3467 if (hasSVAttributes(op))
3468 emitError(op, "SV attributes emission is unimplemented for the op");
3469 auto result = emitBinary(op, Comparison, "==", NoRequirement);
3470 // SystemVerilog 11.8.1: "Comparison... operator results are unsigned,
3471 // regardless of the operands".
3472 result.signedness = IsUnsigned;
3473 return result;
3474}
3475
3476SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3477 if (hasSVAttributes(op))
3478 emitError(op, "SV attributes emission is unimplemented for the op");
3479
3480 // Check if this union type has been padded.
3481 auto unionType = cast<UnionType>(getCanonicalType(op.getType()));
3482 auto unionWidth = hw::getBitWidth(unionType);
3483 auto &element = unionType.getElements()[op.getFieldIndex()];
3484 auto elementWidth = hw::getBitWidth(element.type);
3485
3486 // If the element is 0 width, just fill the union with 0s.
3487 if (!elementWidth) {
3488 ps.addAsString(unionWidth);
3489 ps << "'h0";
3490 return {Unary, IsUnsigned};
3491 }
3492
3493 // If the element has no padding, emit it directly.
3494 if (elementWidth == unionWidth) {
3495 emitSubExpr(op.getInput(), LowestPrecedence);
3496 return {Unary, IsUnsigned};
3497 }
3498
3499 // Emit the value as a bitconcat, supplying 0 for the padding bits.
3500 ps << "{";
3501 ps.scopedBox(PP::ibox0, [&]() {
3502 if (auto prePadding = element.offset) {
3503 ps.addAsString(prePadding);
3504 ps << "'h0," << PP::space;
3505 }
3506 emitSubExpr(op.getInput(), Selection);
3507 if (auto postPadding = unionWidth - elementWidth - element.offset) {
3508 ps << "," << PP::space;
3509 ps.addAsString(postPadding);
3510 ps << "'h0";
3511 }
3512 ps << "}";
3513 });
3514
3515 return {Unary, IsUnsigned};
3516}
3517
3518SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3519 if (hasSVAttributes(op))
3520 emitError(op, "SV attributes emission is unimplemented for the op");
3521 emitSubExpr(op.getInput(), Selection);
3522
3523 // Check if this union type has been padded.
3524 auto unionType = cast<UnionType>(getCanonicalType(op.getInput().getType()));
3525 auto unionWidth = hw::getBitWidth(unionType);
3526 auto &element = unionType.getElements()[op.getFieldIndex()];
3527 auto elementWidth = hw::getBitWidth(element.type);
3528 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3529 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3530
3531 // If the element needs padding then we need to get the actual element out
3532 // of an anonymous structure.
3533 if (needsPadding)
3534 ps << "." << PPExtString(verilogFieldName);
3535
3536 // Get the correct member from the union.
3537 ps << "." << PPExtString(verilogFieldName);
3538 return {Selection, IsUnsigned};
3539}
3540
3541SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3542 emitOpError(op, "cannot emit this expression to Verilog");
3543 ps << "<<unsupported expr: " << PPExtString(op->getName().getStringRef())
3544 << ">>";
3545 return {Symbol, IsUnsigned};
3546}
3547// NOLINTEND(misc-no-recursion)
3548
3549//===----------------------------------------------------------------------===//
3550// Property Emission
3551//===----------------------------------------------------------------------===//
3552
3553// NOLINTBEGIN(misc-no-recursion)
3554
3555namespace {
3556/// Precedence level of various property and sequence expressions. Lower numbers
3557/// bind tighter.
3558///
3559/// See IEEE 1800-2017 section 16.12 "Declaring properties", specifically table
3560/// 16-3 on "Sequence and property operator precedence and associativity".
3561enum class PropertyPrecedence {
3562 Symbol, // Atomic symbol like `foo` and regular boolean expressions
3563 Repeat, // Sequence `[*]`, `[=]`, `[->]`
3564 Concat, // Sequence `##`
3565 Throughout, // Sequence `throughout`
3566 Within, // Sequence `within`
3567 Intersect, // Sequence `intersect`
3568 Unary, // Property `not`, `nexttime`-like
3569 And, // Sequence and property `and`
3570 Or, // Sequence and property `or`
3571 Iff, // Property `iff`
3572 Until, // Property `until`-like, `implies`
3573 Implication, // Property `|->`, `|=>`, `#-#`, `#=#`
3574 Qualifier, // Property `always`-like, `eventually`-like, `if`, `case`,
3575 // `accept`-like, `reject`-like
3576 Clocking, // `@(...)`, `disable iff` (not specified in the standard)
3577 Lowest, // Sentinel which is always the lowest precedence.
3578};
3579
3580/// Additional information on emitted property and sequence expressions.
3581struct EmittedProperty {
3582 /// The precedence of this expression.
3583 PropertyPrecedence precedence;
3584};
3585
3586/// A helper to emit recursively nested property and sequence expressions for
3587/// SystemVerilog assertions.
3588class PropertyEmitter : public EmitterBase,
3589 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3590public:
3591 /// Create a PropertyEmitter for the specified module emitter, and keeping
3592 /// track of any emitted expressions in the specified set.
3593 PropertyEmitter(ModuleEmitter &emitter,
3594 SmallPtrSetImpl<Operation *> &emittedOps)
3595 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3596 PropertyEmitter(ModuleEmitter &emitter,
3597 SmallPtrSetImpl<Operation *> &emittedOps,
3598 BufferingPP::BufferVec &tokens)
3599 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3600 buffer(tokens),
3601 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3602 assert(state.pp.getListener() == &state.saver);
3603 }
3604
3605 void emitAssertPropertyDisable(
3606 Value property, Value disable,
3607 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3608
3609 void emitAssertPropertyBody(
3610 Value property, Value disable,
3611 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3612
3613 void emitAssertPropertyBody(
3614 Value property, sv::EventControl event, Value clock, Value disable,
3615 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3616
3617private:
3618 /// Emit the specified value as an SVA property or sequence.
3619 EmittedProperty
3620 emitNestedProperty(Value property,
3621 PropertyPrecedence parenthesizeIfLooserThan);
3622 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3623 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3624
3625 EmittedProperty visitUnhandledLTL(Operation *op);
3626 EmittedProperty visitLTL(ltl::BooleanConstantOp op);
3627 EmittedProperty visitLTL(ltl::AndOp op);
3628 EmittedProperty visitLTL(ltl::OrOp op);
3629 EmittedProperty visitLTL(ltl::IntersectOp op);
3630 EmittedProperty visitLTL(ltl::DelayOp op);
3631 EmittedProperty visitLTL(ltl::ClockedDelayOp op);
3632 EmittedProperty visitLTL(ltl::ConcatOp op);
3633 EmittedProperty visitLTL(ltl::RepeatOp op);
3634 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3635 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3636 EmittedProperty visitLTL(ltl::NotOp op);
3637 EmittedProperty visitLTL(ltl::ImplicationOp op);
3638 EmittedProperty visitLTL(ltl::UntilOp op);
3639 EmittedProperty visitLTL(ltl::EventuallyOp op);
3640 EmittedProperty visitLTL(ltl::ClockOp op);
3641
3642 void emitLTLDelay(int64_t delay, std::optional<int64_t> length);
3643 void emitLTLClockingEvent(ltl::ClockEdge edge, Value clock);
3644 void emitLTLConcat(ValueRange inputs);
3645
3646public:
3647 ModuleEmitter &emitter;
3648
3649private:
3650 /// Keep track of all operations emitted within this subexpression for
3651 /// location information tracking.
3652 SmallPtrSetImpl<Operation *> &emittedOps;
3653
3654 /// Tokens buffered for inserting casts/parens after emitting children.
3655 SmallVector<Token> localTokens;
3656
3657 /// Stores tokens until told to flush. Uses provided buffer (tokens).
3658 BufferingPP buffer;
3659
3660 /// Stream to emit expressions into, will add to buffer.
3662};
3663} // end anonymous namespace
3664
3665// Emits a disable signal and its containing property.
3666// This function can be called from withing another emission process in which
3667// case we don't need to check that the local tokens are empty.
3668void PropertyEmitter::emitAssertPropertyDisable(
3669 Value property, Value disable,
3670 PropertyPrecedence parenthesizeIfLooserThan) {
3671 // If the property is tied to a disable, emit that.
3672 if (disable) {
3673 ps << "disable iff" << PP::nbsp << "(";
3674 ps.scopedBox(PP::ibox2, [&] {
3675 emitNestedProperty(disable, PropertyPrecedence::Unary);
3676 ps << ")";
3677 });
3678 ps << PP::space;
3679 }
3680
3681 ps.scopedBox(PP::ibox0,
3682 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3683}
3684
3685// Emits a disable signal and its containing property.
3686// This function can be called from withing another emission process in which
3687// case we don't need to check that the local tokens are empty.
3688void PropertyEmitter::emitAssertPropertyBody(
3689 Value property, Value disable,
3690 PropertyPrecedence parenthesizeIfLooserThan) {
3691 assert(localTokens.empty());
3692
3693 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3694
3695 // If we are not using an external token buffer provided through the
3696 // constructor, but we're using the default `PropertyEmitter`-scoped buffer,
3697 // flush it.
3698 if (&buffer.tokens == &localTokens)
3699 buffer.flush(state.pp);
3700}
3701
3702void PropertyEmitter::emitAssertPropertyBody(
3703 Value property, sv::EventControl event, Value clock, Value disable,
3704 PropertyPrecedence parenthesizeIfLooserThan) {
3705 assert(localTokens.empty());
3706 // Wrap to this column.
3707 ps << "@(";
3708 ps.scopedBox(PP::ibox2, [&] {
3709 ps << PPExtString(stringifyEventControl(event)) << PP::space;
3710 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3711 ps << ")";
3712 });
3713 ps << PP::space;
3714
3715 // Emit the rest of the body
3716 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3717
3718 // If we are not using an external token buffer provided through the
3719 // constructor, but we're using the default `PropertyEmitter`-scoped buffer,
3720 // flush it.
3721 if (&buffer.tokens == &localTokens)
3722 buffer.flush(state.pp);
3723}
3724
3725EmittedProperty PropertyEmitter::emitNestedProperty(
3726 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3727 // Emit the property as a plain expression if it doesn't have a property or
3728 // sequence type, in which case it is just a boolean expression.
3729 //
3730 // We use the `LowestPrecedence` for the boolean expression such that it never
3731 // gets parenthesized. According to IEEE 1800-2017, "the operators described
3732 // in Table 11-2 have higher precedence than the sequence and property
3733 // operators". Therefore any boolean expression behaves just like a
3734 // `PropertyPrecedence::Symbol` and needs no parantheses, which is equivalent
3735 // to `VerilogPrecedence::LowestPrecedence`.
3736 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3737 ExprEmitter(emitter, emittedOps, buffer.tokens)
3738 .emitExpression(property, LowestPrecedence,
3739 /*isAssignmentLikeContext=*/false);
3740 return {PropertyPrecedence::Symbol};
3741 }
3742
3743 unsigned startIndex = buffer.tokens.size();
3744 auto info = dispatchLTLVisitor(property.getDefiningOp());
3745
3746 // If this subexpression would bind looser than the expression it is bound
3747 // into, then we need to parenthesize it. Insert the parentheses
3748 // retroactively.
3749 if (info.precedence > parenthesizeIfLooserThan) {
3750 // Insert {"(", ibox0} before the subexpression.
3751 buffer.tokens.insert(buffer.tokens.begin() + startIndex, BeginToken(0));
3752 buffer.tokens.insert(buffer.tokens.begin() + startIndex, StringToken("("));
3753 // Insert {end, ")" } after the subexpression.
3754 ps << PP::end << ")";
3755 // Reset the precedence level.
3756 info.precedence = PropertyPrecedence::Symbol;
3757 }
3758
3759 // Remember that we emitted this.
3760 emittedOps.insert(property.getDefiningOp());
3761 return info;
3762}
3763
3764EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3765 emitOpError(op, "emission as Verilog property or sequence not supported");
3766 ps << "<<unsupported: " << PPExtString(op->getName().getStringRef()) << ">>";
3767 return {PropertyPrecedence::Symbol};
3768}
3769
3770EmittedProperty PropertyEmitter::visitLTL(ltl::BooleanConstantOp op) {
3771 // Emit the boolean constant value as a literal.
3772 ps << (op.getValueAttr().getValue() ? "1'h1" : "1'h0");
3773 return {PropertyPrecedence::Symbol};
3774}
3775
3776EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3777 llvm::interleave(
3778 op.getInputs(),
3779 [&](auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3780 [&]() { ps << PP::space << "and" << PP::nbsp; });
3781 return {PropertyPrecedence::And};
3782}
3783
3784EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3785 llvm::interleave(
3786 op.getInputs(),
3787 [&](auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3788 [&]() { ps << PP::space << "or" << PP::nbsp; });
3789 return {PropertyPrecedence::Or};
3790}
3791
3792EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3793 llvm::interleave(
3794 op.getInputs(),
3795 [&](auto input) {
3796 emitNestedProperty(input, PropertyPrecedence::Intersect);
3797 },
3798 [&]() { ps << PP::space << "intersect" << PP::nbsp; });
3799 return {PropertyPrecedence::Intersect};
3800}
3801
3802void PropertyEmitter::emitLTLDelay(int64_t delay,
3803 std::optional<int64_t> length) {
3804 ps << "##";
3805 if (length) {
3806 if (*length == 0) {
3807 ps.addAsString(delay);
3808 } else {
3809 ps << "[";
3810 ps.addAsString(delay);
3811 ps << ":";
3812 ps.addAsString(delay + *length);
3813 ps << "]";
3814 }
3815 } else {
3816 if (delay == 0) {
3817 ps << "[*]";
3818 } else if (delay == 1) {
3819 ps << "[+]";
3820 } else {
3821 ps << "[";
3822 ps.addAsString(delay);
3823 ps << ":$]";
3824 }
3825 }
3826}
3827
3828void PropertyEmitter::emitLTLClockingEvent(ltl::ClockEdge edge, Value clock) {
3829 ps << "@(";
3830 ps.scopedBox(PP::ibox2, [&] {
3831 ps << PPExtString(stringifyClockEdge(edge)) << PP::space;
3832 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3833 ps << ")";
3834 });
3835}
3836
3837EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3838 emitLTLDelay(op.getDelay(), op.getLength());
3839 ps << PP::space;
3840 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3841 return {PropertyPrecedence::Concat};
3842}
3843
3844EmittedProperty PropertyEmitter::visitLTL(ltl::ClockedDelayOp op) {
3845 emitLTLClockingEvent(op.getEdge(), op.getClock());
3846 ps << PP::space;
3847 emitLTLDelay(op.getDelay(), op.getLength());
3848 ps << PP::space;
3849 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3850 return {PropertyPrecedence::Clocking};
3851}
3852
3853void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3854 bool addSeparator = false;
3855 for (auto input : inputs) {
3856 if (addSeparator) {
3857 ps << PP::space;
3858 if (!input.getDefiningOp<ltl::DelayOp>())
3859 ps << "##0" << PP::space;
3860 }
3861 addSeparator = true;
3862 emitNestedProperty(input, PropertyPrecedence::Concat);
3863 }
3864}
3865
3866EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3867 emitLTLConcat(op.getInputs());
3868 return {PropertyPrecedence::Concat};
3869}
3870
3871EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3872 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3873 if (auto more = op.getMore()) {
3874 ps << "[*";
3875 ps.addAsString(op.getBase());
3876 if (*more != 0) {
3877 ps << ":";
3878 ps.addAsString(op.getBase() + *more);
3879 }
3880 ps << "]";
3881 } else {
3882 if (op.getBase() == 0) {
3883 ps << "[*]";
3884 } else if (op.getBase() == 1) {
3885 ps << "[+]";
3886 } else {
3887 ps << "[*";
3888 ps.addAsString(op.getBase());
3889 ps << ":$]";
3890 }
3891 }
3892 return {PropertyPrecedence::Repeat};
3893}
3894
3895EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3896 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3897 // More always exists
3898 auto more = op.getMore();
3899 ps << "[->";
3900 ps.addAsString(op.getBase());
3901 if (more != 0) {
3902 ps << ":";
3903 ps.addAsString(op.getBase() + more);
3904 }
3905 ps << "]";
3906
3907 return {PropertyPrecedence::Repeat};
3908}
3909
3910EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3911 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3912 // More always exists
3913 auto more = op.getMore();
3914 ps << "[=";
3915 ps.addAsString(op.getBase());
3916 if (more != 0) {
3917 ps << ":";
3918 ps.addAsString(op.getBase() + more);
3919 }
3920 ps << "]";
3921
3922 return {PropertyPrecedence::Repeat};
3923}
3924
3925EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3926 // Emit `not (s_eventually X)` as `always ...` by duality, pulling the
3927 // quantifier to the top and cancelling any inner negation.
3928 if (auto ev = op.getInput().getDefiningOp<ltl::EventuallyOp>()) {
3929 ps << "always" << PP::space;
3930 if (auto innerNot = ev.getInput().getDefiningOp<ltl::NotOp>()) {
3931 // `not(strong_eventually(not(X)))` -> `always X`.
3932 emitNestedProperty(innerNot.getInput(), PropertyPrecedence::Qualifier);
3933 } else {
3934 // `not(strong_eventually(X))` -> `always (not X)`.
3935 ps << "not" << PP::space;
3936 emitNestedProperty(ev.getInput(), PropertyPrecedence::Unary);
3937 }
3938 return {PropertyPrecedence::Qualifier};
3939 }
3940 ps << "not" << PP::space;
3941 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3942 return {PropertyPrecedence::Unary};
3943}
3944
3945/// For a value `concat(..., delay(const(true), 1, 0))`, return `...`. This is
3946/// useful for emitting `(seq ##1 true) |-> prop` as `seq |=> prop`.
3947static ValueRange getNonOverlappingConcatSubrange(Value value) {
3948 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3949 if (!concatOp || concatOp.getInputs().size() < 2)
3950 return {};
3951 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3952 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3953 return {};
3954 auto constOp = delayOp.getInput().getDefiningOp<ConstantOp>();
3955 if (!constOp || !constOp.getValue().isOne())
3956 return {};
3957 return concatOp.getInputs().drop_back();
3958}
3959
3960EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3961 // Emit `(seq ##1 true) |-> prop` as `seq |=> prop`.
3962 if (auto range = getNonOverlappingConcatSubrange(op.getAntecedent());
3963 !range.empty()) {
3964 emitLTLConcat(range);
3965 ps << PP::space << "|=>" << PP::nbsp;
3966 } else {
3967 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3968 ps << PP::space << "|->" << PP::nbsp;
3969 }
3970 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3971 return {PropertyPrecedence::Implication};
3972}
3973
3974EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3975 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3976 ps << PP::space << "until" << PP::space;
3977 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3978 return {PropertyPrecedence::Until};
3979}
3980
3981EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3982 ps << "s_eventually" << PP::space;
3983 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3984 return {PropertyPrecedence::Qualifier};
3985}
3986
3987EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3988 emitLTLClockingEvent(op.getEdge(), op.getClock());
3989 ps << PP::space;
3990 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3991 return {PropertyPrecedence::Clocking};
3992}
3993
3994// NOLINTEND(misc-no-recursion)
3995
3996//===----------------------------------------------------------------------===//
3997// NameCollector
3998//===----------------------------------------------------------------------===//
3999
4000namespace {
4001class NameCollector {
4002public:
4003 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
4004
4005 // Scan operations in the specified block, collecting information about
4006 // those that need to be emitted as declarations.
4007 void collectNames(Block &block);
4008
4009 size_t getMaxDeclNameWidth() const { return maxDeclNameWidth; }
4010 size_t getMaxTypeWidth() const { return maxTypeWidth; }
4011
4012private:
4013 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
4014 ModuleEmitter &moduleEmitter;
4015
4016 /// Types that are longer than `maxTypeWidthBound` are not added to the
4017 /// `maxTypeWidth` to prevent one single huge type from messing up the
4018 /// alignment of all other declarations.
4019 static constexpr size_t maxTypeWidthBound = 32;
4020};
4021} // namespace
4022
4023// NOLINTNEXTLINE(misc-no-recursion)
4024void NameCollector::collectNames(Block &block) {
4025 // Loop over all of the results of all of the ops. Anything that defines a
4026 // value needs to be noticed.
4027 for (auto &op : block) {
4028 // Instances have an instance name to recognize but we don't need to look
4029 // at the result values since wires used by instances should be traversed
4030 // anyway.
4031 if (isa<InstanceOp, InterfaceInstanceOp, FuncCallProceduralOp, FuncCallOp>(
4032 op))
4033 continue;
4034 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
4035 continue;
4036
4037 if (!isVerilogExpression(&op)) {
4038 for (auto result : op.getResults()) {
4039 StringRef declName = getVerilogDeclWord(&op, moduleEmitter);
4040 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
4041 SmallString<16> typeString;
4042
4043 // Convert the port's type to a string and measure it.
4044 {
4045 llvm::raw_svector_ostream stringStream(typeString);
4046 moduleEmitter.printPackedType(stripUnpackedTypes(result.getType()),
4047 stringStream, op.getLoc());
4048 }
4049 if (typeString.size() <= maxTypeWidthBound)
4050 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
4051 }
4052 }
4053
4054 // Recursively process any regions under the op iff this is a procedural
4055 // #ifdef region: we need to emit automatic logic values at the top of the
4056 // enclosing region.
4057 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
4058 for (auto &region : op.getRegions()) {
4059 if (!region.empty())
4060 collectNames(region.front());
4061 }
4062 continue;
4063 }
4064 }
4065}
4066
4067//===----------------------------------------------------------------------===//
4068// StmtEmitter
4069//===----------------------------------------------------------------------===//
4070
4071namespace {
4072/// This emits statement-related operations.
4073// NOLINTBEGIN(misc-no-recursion)
4074class StmtEmitter : public EmitterBase,
4075 public hw::StmtVisitor<StmtEmitter, LogicalResult>,
4076 public sv::Visitor<StmtEmitter, LogicalResult>,
4077 public verif::Visitor<StmtEmitter, LogicalResult> {
4078public:
4079 /// Create an ExprEmitter for the specified module emitter, and keeping track
4080 /// of any emitted expressions in the specified set.
4081 StmtEmitter(ModuleEmitter &emitter, const LoweringOptions &options)
4082 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
4083
4084 void emitStatement(Operation *op);
4085 void emitStatementBlock(Block &body);
4086
4087 /// Emit a declaration.
4088 LogicalResult emitDeclaration(Operation *op);
4089
4090private:
4091 void collectNamesAndCalculateDeclarationWidths(Block &block);
4092
4093 void
4094 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4095 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4096 bool isAssignmentLikeContext = false);
4097 void emitSVAttributes(Operation *op);
4098
4099 using hw::StmtVisitor<StmtEmitter, LogicalResult>::visitStmt;
4100 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4101 using verif::Visitor<StmtEmitter, LogicalResult>::visitVerif;
4102 friend class hw::StmtVisitor<StmtEmitter, LogicalResult>;
4103 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4104 friend class verif::Visitor<StmtEmitter, LogicalResult>;
4105
4106 // Visitor methods.
4107 LogicalResult visitUnhandledStmt(Operation *op) { return failure(); }
4108 LogicalResult visitInvalidStmt(Operation *op) { return failure(); }
4109 LogicalResult visitUnhandledSV(Operation *op) { return failure(); }
4110 LogicalResult visitInvalidSV(Operation *op) { return failure(); }
4111 LogicalResult visitUnhandledVerif(Operation *op) { return failure(); }
4112 LogicalResult visitInvalidVerif(Operation *op) { return failure(); }
4113
4114 LogicalResult visitSV(sv::WireOp op) { return emitDeclaration(op); }
4115 LogicalResult visitSV(RegOp op) { return emitDeclaration(op); }
4116 LogicalResult visitSV(LogicOp op) { return emitDeclaration(op); }
4117 LogicalResult visitSV(LocalParamOp op) { return emitDeclaration(op); }
4118 template <typename Op>
4119 LogicalResult
4120 emitAssignLike(Op op, PPExtString syntax,
4121 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4122 void emitAssignLike(llvm::function_ref<void()> emitLHS,
4123 llvm::function_ref<void()> emitRHS, PPExtString syntax,
4124 PPExtString postSyntax = PPExtString(";"),
4125 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4126 LogicalResult visitSV(AssignOp op);
4127 LogicalResult visitSV(BPAssignOp op);
4128 LogicalResult visitSV(PAssignOp op);
4129 LogicalResult visitSV(ForceOp op);
4130 LogicalResult visitSV(ReleaseOp op);
4131 LogicalResult visitSV(AliasOp op);
4132 LogicalResult visitSV(InterfaceInstanceOp op);
4133 LogicalResult emitOutputLikeOp(Operation *op, const ModulePortInfo &ports);
4134 LogicalResult visitStmt(OutputOp op);
4135
4136 LogicalResult visitStmt(InstanceOp op);
4137 void emitInstancePortList(Operation *op, ModulePortInfo &modPortInfo,
4138 ArrayRef<Value> instPortValues);
4139
4140 LogicalResult visitStmt(TypeScopeOp op);
4141 LogicalResult visitStmt(TypedeclOp op);
4142
4143 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4144 LogicalResult visitSV(OrderedOutputOp op);
4145 LogicalResult visitSV(IfDefOp op) { return emitIfDef(op, op.getCond()); }
4146 LogicalResult visitSV(IfDefProceduralOp op) {
4147 return emitIfDef(op, op.getCond());
4148 }
4149 LogicalResult visitSV(IfOp op);
4150 LogicalResult visitSV(AlwaysOp op);
4151 LogicalResult visitSV(AlwaysCombOp op);
4152 LogicalResult visitSV(AlwaysFFOp op);
4153 LogicalResult visitSV(InitialOp op);
4154 LogicalResult visitSV(CaseOp op);
4155 template <typename OpTy, typename EmitPrefixFn>
4156 LogicalResult
4157 emitFormattedWriteLikeOp(OpTy op, StringRef callee, StringRef formatString,
4158 ValueRange substitutions, EmitPrefixFn emitPrefix);
4159 LogicalResult visitSV(WriteOp op);
4160 LogicalResult visitSV(FWriteOp op);
4161 LogicalResult visitSV(FFlushOp op);
4162 LogicalResult visitSV(FCloseOp op);
4163 LogicalResult visitSV(VerbatimOp op);
4164 LogicalResult visitSV(MacroRefOp op);
4165
4166 LogicalResult emitSimulationControlTask(Operation *op, PPExtString taskName,
4167 std::optional<unsigned> verbosity);
4168 LogicalResult visitSV(StopOp op);
4169 LogicalResult visitSV(FinishOp op);
4170 LogicalResult visitSV(ExitOp op);
4171
4172 LogicalResult emitSeverityMessageTask(Operation *op, PPExtString taskName,
4173 std::optional<unsigned> verbosity,
4174 StringAttr message,
4175 ValueRange operands);
4176
4177 // Helper template for nonfatal message operations
4178 template <typename OpTy>
4179 LogicalResult emitNonfatalMessageOp(OpTy op, const char *taskName) {
4180 return emitSeverityMessageTask(op, PPExtString(taskName), {},
4181 op.getMessageAttr(), op.getSubstitutions());
4182 }
4183
4184 // Helper template for fatal message operations
4185 template <typename OpTy>
4186 LogicalResult emitFatalMessageOp(OpTy op) {
4187 return emitSeverityMessageTask(op, PPExtString("$fatal"), op.getVerbosity(),
4188 op.getMessageAttr(), op.getSubstitutions());
4189 }
4190
4191 LogicalResult visitSV(FatalProceduralOp op);
4192 LogicalResult visitSV(FatalOp op);
4193 LogicalResult visitSV(ErrorProceduralOp op);
4194 LogicalResult visitSV(WarningProceduralOp op);
4195 LogicalResult visitSV(InfoProceduralOp op);
4196 LogicalResult visitSV(ErrorOp op);
4197 LogicalResult visitSV(WarningOp op);
4198 LogicalResult visitSV(InfoOp op);
4199
4200 LogicalResult visitSV(ReadMemOp op);
4201
4202 LogicalResult visitSV(GenerateOp op);
4203 LogicalResult visitSV(GenerateCaseOp op);
4204 LogicalResult visitSV(GenerateForOp op);
4205
4206 LogicalResult visitSV(ForOp op);
4207
4208 void emitAssertionLabel(Operation *op);
4209 void emitAssertionMessage(StringAttr message, ValueRange args,
4210 SmallPtrSetImpl<Operation *> &ops,
4211 bool isConcurrent);
4212 template <typename Op>
4213 LogicalResult emitImmediateAssertion(Op op, PPExtString opName);
4214 LogicalResult visitSV(AssertOp op);
4215 LogicalResult visitSV(AssumeOp op);
4216 LogicalResult visitSV(CoverOp op);
4217 template <typename Op>
4218 LogicalResult emitConcurrentAssertion(Op op, PPExtString opName);
4219 LogicalResult visitSV(AssertConcurrentOp op);
4220 LogicalResult visitSV(AssumeConcurrentOp op);
4221 LogicalResult visitSV(CoverConcurrentOp op);
4222 template <typename Op>
4223 LogicalResult emitPropertyAssertion(Op op, PPExtString opName);
4224 LogicalResult visitSV(AssertPropertyOp op);
4225 LogicalResult visitSV(AssumePropertyOp op);
4226 LogicalResult visitSV(CoverPropertyOp op);
4227
4228 LogicalResult visitSV(BindOp op);
4229 LogicalResult visitSV(InterfaceOp op);
4230 LogicalResult visitSV(sv::SVVerbatimSourceOp op);
4231 LogicalResult visitSV(InterfaceSignalOp op);
4232 LogicalResult visitSV(InterfaceModportOp op);
4233 LogicalResult visitSV(AssignInterfaceSignalOp op);
4234 LogicalResult visitSV(MacroErrorOp op);
4235 LogicalResult visitSV(MacroDefOp op);
4236
4237 void emitBlockAsStatement(Block *block,
4238 const SmallPtrSetImpl<Operation *> &locationOps,
4239 StringRef multiLineComment = StringRef());
4240
4241 LogicalResult visitSV(FuncDPIImportOp op);
4242 template <typename CallOp>
4243 LogicalResult emitFunctionCall(CallOp callOp);
4244 LogicalResult visitSV(FuncCallProceduralOp op);
4245 LogicalResult visitSV(FuncCallOp op);
4246 LogicalResult visitSV(ReturnOp op);
4247 LogicalResult visitSV(IncludeOp op);
4248
4249public:
4250 ModuleEmitter &emitter;
4251
4252private:
4253 /// These keep track of the maximum length of name width and type width in the
4254 /// current statement scope.
4255 size_t maxDeclNameWidth = 0;
4256 size_t maxTypeWidth = 0;
4257
4258 const LoweringOptions &options;
4259};
4260
4261} // end anonymous namespace
4262
4263/// Emit the specified value as an expression. If this is an inline-emitted
4264/// expression, we emit that expression, otherwise we emit a reference to the
4265/// already computed name.
4266///
4267void StmtEmitter::emitExpression(Value exp,
4268 SmallPtrSetImpl<Operation *> &emittedExprs,
4269 VerilogPrecedence parenthesizeIfLooserThan,
4270 bool isAssignmentLikeContext) {
4271 ExprEmitter(emitter, emittedExprs)
4272 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4273}
4274
4275/// Emit SystemVerilog attributes attached to the statement op as dialect
4276/// attributes.
4277void StmtEmitter::emitSVAttributes(Operation *op) {
4278 // SystemVerilog 2017 Section 5.12.
4279 auto svAttrs = getSVAttributes(op);
4280 if (!svAttrs)
4281 return;
4282
4283 startStatement(); // For attributes.
4284 emitSVAttributesImpl(ps, svAttrs, /*mayBreak=*/true);
4285 setPendingNewline();
4286}
4287
4288void StmtEmitter::emitAssignLike(llvm::function_ref<void()> emitLHS,
4289 llvm::function_ref<void()> emitRHS,
4290 PPExtString syntax, PPExtString postSyntax,
4291 std::optional<PPExtString> wordBeforeLHS) {
4292 // If wraps, indent.
4293 ps.scopedBox(PP::ibox2, [&]() {
4294 if (wordBeforeLHS) {
4295 ps << *wordBeforeLHS << PP::space;
4296 }
4297 emitLHS();
4298 // Allow breaking before 'syntax' (e.g., '=') if long assignment.
4299 ps << PP::space << syntax << PP::space;
4300 // RHS is boxed to right of the syntax.
4301 ps.scopedBox(PP::ibox0, [&]() {
4302 emitRHS();
4303 ps << postSyntax;
4304 });
4305 });
4306}
4307
4308template <typename Op>
4309LogicalResult
4310StmtEmitter::emitAssignLike(Op op, PPExtString syntax,
4311 std::optional<PPExtString> wordBeforeLHS) {
4312 SmallPtrSet<Operation *, 8> ops;
4313 ops.insert(op);
4314
4315 startStatement();
4316 ps.addCallback({op, true});
4317 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4318 [&]() {
4319 emitExpression(op.getSrc(), ops, LowestPrecedence,
4320 /*isAssignmentLikeContext=*/true);
4321 },
4322 syntax, PPExtString(";"), wordBeforeLHS);
4323
4324 ps.addCallback({op, false});
4325 emitLocationInfoAndNewLine(ops);
4326 return success();
4327}
4328
4329LogicalResult StmtEmitter::visitSV(AssignOp op) {
4330 // prepare assigns wires to instance outputs and function results, but these
4331 // are logically handled in the port binding list when outputing an instance.
4332 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4333 return success();
4334
4335 if (emitter.assignsInlined.count(op))
4336 return success();
4337
4338 // Emit SV attributes. See Spec 12.3.
4339 emitSVAttributes(op);
4340
4341 return emitAssignLike(op, PPExtString("="), PPExtString("assign"));
4342}
4343
4344LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4345 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4346 return success();
4347
4348 // If the assign is emitted into logic declaration, we must not emit again.
4349 if (emitter.assignsInlined.count(op))
4350 return success();
4351
4352 // Emit SV attributes. See Spec 12.3.
4353 emitSVAttributes(op);
4354
4355 return emitAssignLike(op, PPExtString("="));
4356}
4357
4358LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4359 // Emit SV attributes. See Spec 12.3.
4360 emitSVAttributes(op);
4361
4362 return emitAssignLike(op, PPExtString("<="));
4363}
4364
4365LogicalResult StmtEmitter::visitSV(ForceOp op) {
4366 if (hasSVAttributes(op))
4367 emitError(op, "SV attributes emission is unimplemented for the op");
4368
4369 return emitAssignLike(op, PPExtString("="), PPExtString("force"));
4370}
4371
4372LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4373 if (hasSVAttributes(op))
4374 emitError(op, "SV attributes emission is unimplemented for the op");
4375
4376 startStatement();
4377 SmallPtrSet<Operation *, 8> ops;
4378 ops.insert(op);
4379 ps.addCallback({op, true});
4380 ps.scopedBox(PP::ibox2, [&]() {
4381 ps << "release" << PP::space;
4382 emitExpression(op.getDest(), ops);
4383 ps << ";";
4384 });
4385 ps.addCallback({op, false});
4386 emitLocationInfoAndNewLine(ops);
4387 return success();
4388}
4389
4390LogicalResult StmtEmitter::visitSV(AliasOp op) {
4391 if (hasSVAttributes(op))
4392 emitError(op, "SV attributes emission is unimplemented for the op");
4393
4394 startStatement();
4395 SmallPtrSet<Operation *, 8> ops;
4396 ops.insert(op);
4397 ps.addCallback({op, true});
4398 ps.scopedBox(PP::ibox2, [&]() {
4399 ps << "alias" << PP::space;
4400 ps.scopedBox(PP::cbox0, [&]() { // If any breaks, all break.
4401 llvm::interleave(
4402 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4403 [&]() { ps << PP::nbsp << "=" << PP::space; });
4404 ps << ";";
4405 });
4406 });
4407 ps.addCallback({op, false});
4408 emitLocationInfoAndNewLine(ops);
4409 return success();
4410}
4411
4412LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4413 auto doNotPrint = op.getDoNotPrint();
4414 if (doNotPrint && !state.options.emitBindComments)
4415 return success();
4416
4417 if (hasSVAttributes(op))
4418 emitError(op, "SV attributes emission is unimplemented for the op");
4419
4420 startStatement();
4421 StringRef prefix = "";
4422 ps.addCallback({op, true});
4423 if (doNotPrint) {
4424 prefix = "// ";
4425 ps << "// This interface is elsewhere emitted as a bind statement."
4426 << PP::newline;
4427 }
4428
4429 SmallPtrSet<Operation *, 8> ops;
4430 ops.insert(op);
4431
4432 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4433 assert(interfaceOp && "InterfaceInstanceOp has invalid symbol that does not "
4434 "point to an interface");
4435
4436 auto verilogName = getSymOpName(interfaceOp);
4437 if (!prefix.empty())
4438 ps << PPExtString(prefix);
4439 ps << PPExtString(verilogName)
4440 << PP::nbsp /* don't break, may be comment line */
4441 << PPExtString(op.getName()) << "();";
4442
4443 ps.addCallback({op, false});
4444 emitLocationInfoAndNewLine(ops);
4445
4446 return success();
4447}
4448
4449/// For OutputOp and ReturnOp we put "assign" statements at the end of the
4450/// Verilog module or function respectively to assign outputs to intermediate
4451/// wires.
4452LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4453 const ModulePortInfo &ports) {
4454 SmallPtrSet<Operation *, 8> ops;
4455 size_t operandIndex = 0;
4456 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4457 for (PortInfo port : ports.getOutputs()) {
4458 auto operand = op->getOperand(operandIndex);
4459 // Outputs that are set by the output port of an instance are handled
4460 // directly when the instance is emitted.
4461 // Keep synced with countStatements() and visitStmt(InstanceOp).
4462 if (operand.hasOneUse() && operand.getDefiningOp() &&
4463 isa<InstanceOp>(operand.getDefiningOp())) {
4464 ++operandIndex;
4465 continue;
4466 }
4467
4468 ops.clear();
4469 ops.insert(op);
4470
4471 startStatement();
4472 ps.addCallback({op, true});
4473 bool isZeroBit = isZeroBitType(port.type);
4474 ps.scopedBox(isZeroBit ? PP::neverbox : PP::ibox2, [&]() {
4475 if (isZeroBit)
4476 ps << "// Zero width: ";
4477 // Emit "assign" only in a non-procedural region.
4478 if (!isProcedural)
4479 ps << "assign" << PP::space;
4480 ps << PPExtString(port.getVerilogName());
4481 ps << PP::space << "=" << PP::space;
4482 ps.scopedBox(PP::ibox0, [&]() {
4483 // If this is a zero-width constant then don't emit it (illegal). Else,
4484 // emit the expression - even for zero width - for traceability.
4485 if (isZeroBit &&
4486 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4487 ps << "/*Zero width*/";
4488 else
4489 emitExpression(operand, ops, LowestPrecedence,
4490 /*isAssignmentLikeContext=*/true);
4491 ps << ";";
4492 });
4493 });
4494 ps.addCallback({op, false});
4495 emitLocationInfoAndNewLine(ops);
4496
4497 ++operandIndex;
4498 }
4499 return success();
4500}
4501
4502LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4503 auto parent = op->getParentOfType<PortList>();
4504 ModulePortInfo ports(parent.getPortList());
4505 return emitOutputLikeOp(op, ports);
4506}
4507
4508LogicalResult StmtEmitter::visitStmt(TypeScopeOp op) {
4509 startStatement();
4510 auto typescopeDef = ("_TYPESCOPE_" + op.getSymName()).str();
4511 ps << "`ifndef " << typescopeDef << PP::newline;
4512 ps << "`define " << typescopeDef;
4513 setPendingNewline();
4514 emitStatementBlock(*op.getBodyBlock());
4515 startStatement();
4516 ps << "`endif // " << typescopeDef;
4517 setPendingNewline();
4518 return success();
4519}
4520
4521LogicalResult StmtEmitter::visitStmt(TypedeclOp op) {
4522 if (hasSVAttributes(op))
4523 emitError(op, "SV attributes emission is unimplemented for the op");
4524
4525 startStatement();
4526 auto zeroBitType = isZeroBitType(op.getType());
4527 if (zeroBitType)
4528 ps << PP::neverbox << "// ";
4529
4530 SmallPtrSet<Operation *, 8> ops;
4531 ops.insert(op);
4532 ps.scopedBox(PP::ibox2, [&]() {
4533 ps << "typedef" << PP::space;
4534 ps.invokeWithStringOS([&](auto &os) {
4535 emitter.printPackedType(stripUnpackedTypes(op.getType()), os, op.getLoc(),
4536 op.getAliasType(), false);
4537 });
4538 ps << PP::space << PPExtString(op.getPreferredName());
4539 ps.invokeWithStringOS(
4540 [&](auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4541 ps << ";";
4542 });
4543 if (zeroBitType)
4544 ps << PP::end;
4545 emitLocationInfoAndNewLine(ops);
4546 return success();
4547}
4548
4549template <typename CallOpTy>
4550LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4551 startStatement();
4552
4553 auto callee =
4554 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4555
4556 SmallPtrSet<Operation *, 8> ops;
4557 ops.insert(op);
4558 assert(callee);
4559
4560 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4561 if (explicitReturn) {
4562 assert(explicitReturn.hasOneUse());
4563 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4564 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4565 emitExpression(bpassignOp.getDest(), ops);
4566 } else {
4567 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4568 ps << "assign" << PP::nbsp;
4569 emitExpression(assignOp.getDest(), ops);
4570 }
4571 ps << PP::nbsp << "=" << PP::nbsp;
4572 }
4573
4574 auto arguments = callee.getPortList(true);
4575
4576 ps << PPExtString(getSymOpName(callee)) << "(";
4577
4578 bool needsComma = false;
4579 auto printArg = [&](Value value) {
4580 if (needsComma)
4581 ps << "," << PP::space;
4582 emitExpression(value, ops);
4583 needsComma = true;
4584 };
4585
4586 ps.scopedBox(PP::ibox0, [&] {
4587 unsigned inputIndex = 0, outputIndex = 0;
4588 for (auto arg : arguments) {
4589 if (arg.dir == hw::ModulePort::Output)
4590 printArg(
4591 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4592 else
4593 printArg(op.getInputs()[inputIndex++]);
4594 }
4595 });
4596
4597 ps << ");";
4598 emitLocationInfoAndNewLine(ops);
4599 return success();
4600}
4601
4602LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4603 return emitFunctionCall(op);
4604}
4605
4606LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4607 return emitFunctionCall(op);
4608}
4609
4610template <typename PPS>
4611void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op,
4612 bool isAutomatic = false,
4613 bool emitAsTwoStateType = false) {
4614 ps << "function" << PP::nbsp;
4615 if (isAutomatic)
4616 ps << "automatic" << PP::nbsp;
4617 auto retType = op.getExplicitlyReturnedType();
4618 if (retType) {
4619 ps.invokeWithStringOS([&](auto &os) {
4620 emitter.printPackedType(retType, os, op->getLoc(), {}, false, true,
4621 emitAsTwoStateType);
4622 });
4623 } else
4624 ps << "void";
4625 ps << PP::nbsp << PPExtString(getSymOpName(op));
4626
4627 emitter.emitPortList(
4628 op, ModulePortInfo(op.getPortList(/*excludeExplicitReturn=*/true)), true);
4629}
4630
4631LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4632 auto parent = op->getParentOfType<sv::FuncOp>();
4633 ModulePortInfo ports(parent.getPortList(false));
4634 return emitOutputLikeOp(op, ports);
4635}
4636
4637LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4638 startStatement();
4639 ps << "`include" << PP::nbsp;
4640
4641 if (op.getStyle() == IncludeStyle::System)
4642 ps << "<" << op.getTarget() << ">";
4643 else
4644 ps << "\"" << op.getTarget() << "\"";
4645
4646 emitLocationInfo(op.getLoc());
4647 setPendingNewline();
4648 return success();
4649}
4650
4651LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4652 startStatement();
4653
4654 ps << "import" << PP::nbsp << "\"DPI-C\"" << PP::nbsp << "context"
4655 << PP::nbsp;
4656
4657 // Emit a linkage name if provided.
4658 if (auto linkageName = importOp.getLinkageName())
4659 ps << *linkageName << PP::nbsp << "=" << PP::nbsp;
4660 auto op =
4661 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4662 assert(op.isDeclaration() && "function must be a declaration");
4663 emitFunctionSignature(emitter, ps, op, /*isAutomatic=*/false,
4664 /*emitAsTwoStateType=*/true);
4665 assert(state.pendingNewline);
4666 ps << PP::newline;
4667
4668 return success();
4669}
4670
4671LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4672 if (hasSVAttributes(op))
4673 emitError(op, "SV attributes emission is unimplemented for the op");
4674
4675 startStatement();
4676 SmallPtrSet<Operation *, 8> ops;
4677 ops.insert(op);
4678
4679 ps.addCallback({op, true});
4680 ps << "$fflush(";
4681 if (auto fd = op.getFd())
4682 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4683
4684 ps << ");";
4685 ps.addCallback({op, false});
4686 emitLocationInfoAndNewLine(ops);
4687 return success();
4688}
4689
4690LogicalResult StmtEmitter::visitSV(FCloseOp op) {
4691 if (hasSVAttributes(op))
4692 emitError(op, "SV attributes emission is unimplemented for the op");
4693
4694 startStatement();
4695 SmallPtrSet<Operation *, 8> ops;
4696 ops.insert(op);
4697
4698 ps.addCallback({op, true});
4699 ps << "$fclose(";
4700 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4701 ps << ");";
4702 ps.addCallback({op, false});
4703 emitLocationInfoAndNewLine(ops);
4704 return success();
4705}
4706
4707template <typename OpTy, typename EmitPrefixFn>
4708LogicalResult StmtEmitter::emitFormattedWriteLikeOp(OpTy op, StringRef callee,
4709 StringRef formatString,
4710 ValueRange substitutions,
4711 EmitPrefixFn emitPrefix) {
4712 if (hasSVAttributes(op))
4713 emitError(op, "SV attributes emission is unimplemented for the op");
4714
4715 startStatement();
4716 SmallPtrSet<Operation *, 8> ops;
4717 ops.insert(op);
4718
4719 ps.addCallback({op, true});
4720 ps << callee;
4721 ps.scopedBox(PP::ibox0, [&]() {
4722 emitPrefix(ops);
4723 ps.writeQuotedEscaped(formatString);
4724 // TODO: if any of these breaks, it'd be "nice" to break
4725 // after the comma, instead of:
4726 // $fwrite(5, "...", a + b,
4727 // longexpr_goes
4728 // + here, c);
4729 // (without forcing breaking between all elements, like braced list)
4730 for (auto operand : substitutions) {
4731 ps << "," << PP::space;
4732 emitExpression(operand, ops);
4733 }
4734 ps << ");";
4735 });
4736 ps.addCallback({op, false});
4737 emitLocationInfoAndNewLine(ops);
4738 return success();
4739}
4740
4741LogicalResult StmtEmitter::visitSV(WriteOp op) {
4742 return emitFormattedWriteLikeOp(op, "$write(", op.getFormatString(),
4743 op.getSubstitutions(),
4744 [&](SmallPtrSetImpl<Operation *> &) {});
4745}
4746
4747LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4748 return emitFormattedWriteLikeOp(op, "$fwrite(", op.getFormatString(),
4749 op.getSubstitutions(),
4750 [&](SmallPtrSetImpl<Operation *> &ops) {
4751 emitExpression(op.getFd(), ops);
4752 ps << "," << PP::space;
4753 });
4754}
4755
4756LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4757 if (hasSVAttributes(op))
4758 emitError(op, "SV attributes emission is unimplemented for the op");
4759
4760 startStatement();
4761 SmallPtrSet<Operation *, 8> ops;
4762 ops.insert(op);
4763 ps << PP::neverbox;
4764
4765 // Drop an extraneous \n off the end of the string if present.
4766 StringRef string = op.getFormatString();
4767 if (string.ends_with("\n"))
4768 string = string.drop_back();
4769
4770 // Emit each \n separated piece of the string with each piece properly
4771 // indented. The convention is to not emit the \n so
4772 // emitLocationInfoAndNewLine can do that for the last line.
4773 bool isFirst = true;
4774
4775 // Emit each line of the string at a time.
4776 while (!string.empty()) {
4777 auto lhsRhs = string.split('\n');
4778 if (isFirst)
4779 isFirst = false;
4780 else {
4781 ps << PP::end << PP::newline << PP::neverbox;
4782 }
4783
4784 // Emit each chunk of the line.
4785 emitTextWithSubstitutions(
4786 ps, lhsRhs.first, op,
4787 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4788 string = lhsRhs.second;
4789 }
4790
4791 ps << PP::end;
4792
4793 emitLocationInfoAndNewLine(ops);
4794 return success();
4795}
4796
4797// Emit macro as a statement.
4798LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4799 if (hasSVAttributes(op)) {
4800 emitError(op, "SV attributes emission is unimplemented for the op");
4801 return failure();
4802 }
4803 startStatement();
4804 SmallPtrSet<Operation *, 8> ops;
4805 ops.insert(op);
4806 ps << PP::neverbox;
4807
4808 // Use the specified name or the symbol name as appropriate.
4809 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4810 assert(macroOp && "Invalid IR");
4811 StringRef name =
4812 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4813 ps << "`" << PPExtString(name);
4814 if (!op.getInputs().empty()) {
4815 ps << "(";
4816 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4817 emitExpression(val, ops, LowestPrecedence,
4818 /*isAssignmentLikeContext=*/false);
4819 });
4820 ps << ")";
4821 }
4822 ps << PP::end;
4823 emitLocationInfoAndNewLine(ops);
4824 return success();
4825}
4826
4827/// Emit one of the simulation control tasks `$stop`, `$finish`, or `$exit`.
4828LogicalResult
4829StmtEmitter::emitSimulationControlTask(Operation *op, PPExtString taskName,
4830 std::optional<unsigned> verbosity) {
4831 if (hasSVAttributes(op))
4832 emitError(op, "SV attributes emission is unimplemented for the op");
4833
4834 startStatement();
4835 SmallPtrSet<Operation *, 8> ops;
4836 ops.insert(op);
4837 ps.addCallback({op, true});
4838 ps << taskName;
4839 if (verbosity && *verbosity != 1) {
4840 ps << "(";
4841 ps.addAsString(*verbosity);
4842 ps << ")";
4843 }
4844 ps << ";";
4845 ps.addCallback({op, false});
4846 emitLocationInfoAndNewLine(ops);
4847 return success();
4848}
4849
4850LogicalResult StmtEmitter::visitSV(StopOp op) {
4851 return emitSimulationControlTask(op, PPExtString("$stop"), op.getVerbosity());
4852}
4853
4854LogicalResult StmtEmitter::visitSV(FinishOp op) {
4855 return emitSimulationControlTask(op, PPExtString("$finish"),
4856 op.getVerbosity());
4857}
4858
4859LogicalResult StmtEmitter::visitSV(ExitOp op) {
4860 return emitSimulationControlTask(op, PPExtString("$exit"), {});
4861}
4862
4863/// Emit one of the severity message tasks `$fatal`, `$error`, `$warning`, or
4864/// `$info`.
4865LogicalResult
4866StmtEmitter::emitSeverityMessageTask(Operation *op, PPExtString taskName,
4867 std::optional<unsigned> verbosity,
4868 StringAttr message, ValueRange operands) {
4869 if (hasSVAttributes(op))
4870 emitError(op, "SV attributes emission is unimplemented for the op");
4871
4872 startStatement();
4873 SmallPtrSet<Operation *, 8> ops;
4874 ops.insert(op);
4875 ps.addCallback({op, true});
4876 ps << taskName;
4877
4878 // In case we have a message to print, or the operation has an optional
4879 // verbosity and that verbosity is present, print the parenthesized parameter
4880 // list.
4881 if ((verbosity && *verbosity != 1) || message) {
4882 ps << "(";
4883 ps.scopedBox(PP::ibox0, [&]() {
4884 // If the operation takes a verbosity, print it if it is set, or print the
4885 // default "1".
4886 if (verbosity)
4887 ps.addAsString(*verbosity);
4888
4889 // Print the message and interpolation operands if present.
4890 if (message) {
4891 if (verbosity)
4892 ps << "," << PP::space;
4893 ps.writeQuotedEscaped(message.getValue());
4894 // TODO: good comma/wrapping behavior as elsewhere.
4895 for (auto operand : operands) {
4896 ps << "," << PP::space;
4897 emitExpression(operand, ops);
4898 }
4899 }
4900
4901 ps << ")";
4902 });
4903 }
4904
4905 ps << ";";
4906 ps.addCallback({op, false});
4907 emitLocationInfoAndNewLine(ops);
4908 return success();
4909}
4910
4911LogicalResult StmtEmitter::visitSV(FatalProceduralOp op) {
4912 return emitFatalMessageOp(op);
4913}
4914
4915LogicalResult StmtEmitter::visitSV(FatalOp op) {
4916 return emitFatalMessageOp(op);
4917}
4918
4919LogicalResult StmtEmitter::visitSV(ErrorProceduralOp op) {
4920 return emitNonfatalMessageOp(op, "$error");
4921}
4922
4923LogicalResult StmtEmitter::visitSV(WarningProceduralOp op) {
4924 return emitNonfatalMessageOp(op, "$warning");
4925}
4926
4927LogicalResult StmtEmitter::visitSV(InfoProceduralOp op) {
4928 return emitNonfatalMessageOp(op, "$info");
4929}
4930
4931LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4932 return emitNonfatalMessageOp(op, "$error");
4933}
4934
4935LogicalResult StmtEmitter::visitSV(WarningOp op) {
4936 return emitNonfatalMessageOp(op, "$warning");
4937}
4938
4939LogicalResult StmtEmitter::visitSV(InfoOp op) {
4940 return emitNonfatalMessageOp(op, "$info");
4941}
4942
4943LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4944 SmallPtrSet<Operation *, 8> ops({op});
4945
4946 startStatement();
4947 ps.addCallback({op, true});
4948 ps << "$readmem";
4949 switch (op.getBaseAttr().getValue()) {
4950 case MemBaseTypeAttr::MemBaseBin:
4951 ps << "b";
4952 break;
4953 case MemBaseTypeAttr::MemBaseHex:
4954 ps << "h";
4955 break;
4956 }
4957 ps << "(";
4958 ps.scopedBox(PP::ibox0, [&]() {
4959 ps.writeQuotedEscaped(op.getFilename());
4960 ps << "," << PP::space;
4961 emitExpression(op.getDest(), ops);
4962 });
4963
4964 ps << ");";
4965 ps.addCallback({op, false});
4966 emitLocationInfoAndNewLine(ops);
4967 return success();
4968}
4969
4970LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4971 emitSVAttributes(op);
4972 // TODO: location info?
4973 startStatement();
4974 ps.addCallback({op, true});
4975 ps << "generate" << PP::newline;
4976 ps << "begin: " << PPExtString(getSymOpName(op));
4977 setPendingNewline();
4978 emitStatementBlock(op.getBody().getBlocks().front());
4979 startStatement();
4980 ps << "end: " << PPExtString(getSymOpName(op)) << PP::newline;
4981 ps << "endgenerate";
4982 ps.addCallback({op, false});
4983 setPendingNewline();
4984 return success();
4985}
4986
4987LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4988 emitSVAttributes(op);
4989 // TODO: location info?
4990 startStatement();
4991 ps.addCallback({op, true});
4992 ps << "case (";
4993 ps.invokeWithStringOS([&](auto &os) {
4994 emitter.printParamValue(
4995 op.getCond(), os, VerilogPrecedence::Selection,
4996 [&]() { return op->emitOpError("invalid case parameter"); });
4997 });
4998 ps << ")";
4999 setPendingNewline();
5000
5001 // Ensure that all of the per-case arrays are the same length.
5002 ArrayAttr patterns = op.getCasePatterns();
5003 ArrayAttr caseNames = op.getCaseNames();
5004 MutableArrayRef<Region> regions = op.getCaseRegions();
5005 assert(patterns.size() == regions.size());
5006 assert(patterns.size() == caseNames.size());
5007
5008 // TODO: We'll probably need to store the legalized names somewhere for
5009 // `verbose` formatting. Set up the infra for storing names recursively. Just
5010 // store this locally for now.
5011 llvm::StringMap<size_t> nextGenIds;
5012 ps.scopedBox(PP::bbox2, [&]() {
5013 // Emit each case.
5014 for (size_t i = 0, e = patterns.size(); i < e; ++i) {
5015 auto &region = regions[i];
5016 assert(region.hasOneBlock());
5017 Attribute patternAttr = patterns[i];
5018
5019 startStatement();
5020 if (!isa<mlir::TypedAttr>(patternAttr))
5021 ps << "default";
5022 else
5023 ps.invokeWithStringOS([&](auto &os) {
5024 emitter.printParamValue(
5025 patternAttr, os, VerilogPrecedence::LowestPrecedence,
5026 [&]() { return op->emitOpError("invalid case value"); });
5027 });
5028
5029 StringRef legalName =
5030 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
5031 options.caseInsensitiveKeywords);
5032 ps << ": begin: " << PPExtString(legalName);
5033 setPendingNewline();
5034 emitStatementBlock(region.getBlocks().front());
5035 startStatement();
5036 ps << "end: " << PPExtString(legalName);
5037 setPendingNewline();
5038 }
5039 });
5040
5041 startStatement();
5042 ps << "endcase";
5043 ps.addCallback({op, false});
5044 setPendingNewline();
5045 return success();
5046}
5047
5048LogicalResult StmtEmitter::visitSV(GenerateForOp op) {
5049 emitSVAttributes(op);
5050 llvm::SmallPtrSet<Operation *, 8> ops;
5051 ps.addCallback({op, true});
5052 startStatement();
5053
5054 StringRef inductionVarName = op->getAttrOfType<StringAttr>("hw.verilogName");
5055
5056 ps << "for (";
5057 ps.scopedBox(PP::cbox0, [&]() {
5058 emitAssignLike(
5059 [&]() { ps << "genvar" << PP::nbsp << PPExtString(inductionVarName); },
5060 [&]() {
5061 ps.invokeWithStringOS([&](auto &os) {
5062 emitter.printParamValue(
5063 op.getLowerBound(), os, VerilogPrecedence::LowestPrecedence,
5064 [&]() { return op->emitOpError("invalid lower bound"); });
5065 });
5066 },
5067 PPExtString("="));
5068 ps << PP::space;
5069
5070 emitAssignLike(
5071 [&]() { ps << PPExtString(inductionVarName); },
5072 [&]() {
5073 ps.invokeWithStringOS([&](auto &os) {
5074 emitter.printParamValue(
5075 op.getUpperBound(), os, VerilogPrecedence::LowestPrecedence,
5076 [&]() { return op->emitOpError("invalid upper bound"); });
5077 });
5078 },
5079 PPExtString("<"));
5080 ps << PP::space;
5081
5082 ps << PPExtString(inductionVarName) << PP::nbsp << "+=" << PP::nbsp;
5083 ps.invokeWithStringOS([&](auto &os) {
5084 emitter.printParamValue(
5085 op.getStep(), os, VerilogPrecedence::LowestPrecedence,
5086 [&]() { return op->emitOpError("invalid step"); });
5087 });
5088 ps << ") begin";
5089 StringRef blockName = op.getGenBlockName();
5090 if (!blockName.empty())
5091 ps << " : " << PPExtString(blockName);
5092 });
5093
5094 ps << PP::neverbreak;
5095 setPendingNewline();
5096 emitStatementBlock(op.getBody().getBlocks().front());
5097 startStatement();
5098 ps << "end";
5099 if (StringRef blockName = op.getGenBlockName(); !blockName.empty())
5100 ps << " // " << PPExtString(blockName);
5101 ps.addCallback({op, false});
5102 setPendingNewline();
5103 return success();
5104}
5105
5106LogicalResult StmtEmitter::visitSV(ForOp op) {
5107 emitSVAttributes(op);
5108 llvm::SmallPtrSet<Operation *, 8> ops;
5109 ps.addCallback({op, true});
5110 startStatement();
5111 auto inductionVarName = op->getAttrOfType<StringAttr>("hw.verilogName");
5112 ps << "for (";
5113 // Emit statements on same line if possible, or put each on own line.
5114 ps.scopedBox(PP::cbox0, [&]() {
5115 // Emit initialization assignment.
5116 emitAssignLike(
5117 [&]() {
5118 ps << "logic" << PP::nbsp;
5119 ps.invokeWithStringOS([&](auto &os) {
5120 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
5121 os);
5122 });
5123 ps << PP::nbsp << PPExtString(inductionVarName);
5124 },
5125 [&]() { emitExpression(op.getLowerBound(), ops); }, PPExtString("="));
5126 // Break between statements.
5127 ps << PP::space;
5128
5129 // Emit bounds-check statement.
5130 emitAssignLike([&]() { ps << PPExtString(inductionVarName); },
5131 [&]() { emitExpression(op.getUpperBound(), ops); },
5132 PPExtString("<"));
5133 // Break between statements.
5134 ps << PP::space;
5135
5136 // Emit update statement and trailing syntax.
5137 emitAssignLike([&]() { ps << PPExtString(inductionVarName); },
5138 [&]() { emitExpression(op.getStep(), ops); },
5139 PPExtString("+="), PPExtString(") begin"));
5140 });
5141 // Don't break for because of newline.
5142 ps << PP::neverbreak;
5143 setPendingNewline();
5144 emitStatementBlock(op.getBody().getBlocks().front());
5145 startStatement();
5146 ps << "end";
5147 ps.addCallback({op, false});
5148 emitLocationInfoAndNewLine(ops);
5149 return success();
5150}
5151
5152/// Emit the `<label>:` portion of a verification operation.
5153void StmtEmitter::emitAssertionLabel(Operation *op) {
5154 if (auto label = op->getAttrOfType<StringAttr>("hw.verilogName"))
5155 ps << PPExtString(label) << ":" << PP::space;
5156}
5157
5158/// Emit the optional ` else $error(...)` portion of an immediate or concurrent
5159/// verification operation.
5160void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
5161 SmallPtrSetImpl<Operation *> &ops,
5162 bool isConcurrent = false) {
5163 if (!message)
5164 return;
5165 ps << PP::space << "else" << PP::nbsp << "$error(";
5166 ps.scopedBox(PP::ibox0, [&]() {
5167 ps.writeQuotedEscaped(message.getValue());
5168 // TODO: box, break/wrap behavior!
5169 for (auto arg : args) {
5170 ps << "," << PP::space;
5171 emitExpression(arg, ops);
5172 }
5173 ps << ")";
5174 });
5175}
5176
5177template <typename Op>
5178LogicalResult StmtEmitter::emitImmediateAssertion(Op op, PPExtString opName) {
5179 if (hasSVAttributes(op))
5180 emitError(op, "SV attributes emission is unimplemented for the op");
5181
5182 startStatement();
5183 SmallPtrSet<Operation *, 8> ops;
5184 ops.insert(op);
5185 ps.addCallback({op, true});
5186 ps.scopedBox(PP::ibox2, [&]() {
5187 emitAssertionLabel(op);
5188 ps.scopedBox(PP::cbox0, [&]() {
5189 ps << opName;
5190 switch (op.getDefer()) {
5191 case DeferAssert::Immediate:
5192 break;
5193 case DeferAssert::Observed:
5194 ps << " #0 ";
5195 break;
5196 case DeferAssert::Final:
5197 ps << " final ";
5198 break;
5199 }
5200 ps << "(";
5201 ps.scopedBox(PP::ibox0, [&]() {
5202 emitExpression(op.getExpression(), ops);
5203 ps << ")";
5204 });
5205 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
5206 ps << ";";
5207 });
5208 });
5209 ps.addCallback({op, false});
5210 emitLocationInfoAndNewLine(ops);
5211 return success();
5212}
5213
5214LogicalResult StmtEmitter::visitSV(AssertOp op) {
5215 return emitImmediateAssertion(op, PPExtString("assert"));
5216}
5217
5218LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5219 return emitImmediateAssertion(op, PPExtString("assume"));
5220}
5221
5222LogicalResult StmtEmitter::visitSV(CoverOp op) {
5223 return emitImmediateAssertion(op, PPExtString("cover"));
5224}
5225
5226template <typename Op>
5227LogicalResult StmtEmitter::emitConcurrentAssertion(Op op, PPExtString opName) {
5228 if (hasSVAttributes(op))
5229 emitError(op, "SV attributes emission is unimplemented for the op");
5230
5231 startStatement();
5232 SmallPtrSet<Operation *, 8> ops;
5233 ops.insert(op);
5234 ps.addCallback({op, true});
5235 ps.scopedBox(PP::ibox2, [&]() {
5236 emitAssertionLabel(op);
5237 ps.scopedBox(PP::cbox0, [&]() {
5238 ps << opName << PP::nbsp << "property (";
5239 ps.scopedBox(PP::ibox0, [&]() {
5240 ps << "@(" << PPExtString(stringifyEventControl(op.getEvent()))
5241 << PP::nbsp;
5242 emitExpression(op.getClock(), ops);
5243 ps << ")" << PP::space;
5244 emitExpression(op.getProperty(), ops);
5245 ps << ")";
5246 });
5247 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5248 true);
5249 ps << ";";
5250 });
5251 });
5252 ps.addCallback({op, false});
5253 emitLocationInfoAndNewLine(ops);
5254 return success();
5255}
5256
5257LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5258 return emitConcurrentAssertion(op, PPExtString("assert"));
5259}
5260
5261LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5262 return emitConcurrentAssertion(op, PPExtString("assume"));
5263}
5264
5265LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5266 return emitConcurrentAssertion(op, PPExtString("cover"));
5267}
5268
5269// Property assertions are what gets emitted if the user want to combine
5270// concurrent assertions with a disable signal, a clock and an ltl property.
5271template <typename Op>
5272LogicalResult StmtEmitter::emitPropertyAssertion(Op op, PPExtString opName) {
5273 if (hasSVAttributes(op))
5274 emitError(op, "SV attributes emission is unimplemented for the op");
5275
5276 // If we are inside a procedural region we have the option of emitting either
5277 // an `assert` or `assert property`. If we are in a non-procedural region,
5278 // e.g., the body of a module, we have to use the concurrent form `assert
5279 // property` (which also supports plain booleans).
5280 //
5281 // See IEEE 1800-2017 section 16.14.5 "Using concurrent assertion statements
5282 // outside procedural code" and 16.14.6 "Embedding concurrent assertions in
5283 // procedural code".
5284 Operation *parent = op->getParentOp();
5285 Value property = op.getProperty();
5286 bool isTemporal = !property.getType().isSignlessInteger(1);
5287 bool isProcedural = parent->hasTrait<ProceduralRegion>();
5288 bool emitAsImmediate = !isTemporal && isProcedural;
5289
5290 startStatement();
5291 SmallPtrSet<Operation *, 8> ops;
5292 ops.insert(op);
5293 ps.addCallback({op, true});
5294 ps.scopedBox(PP::ibox2, [&]() {
5295 // Check for a label and emit it if necessary
5296 emitAssertionLabel(op);
5297 // Emit the assertion
5298 ps.scopedBox(PP::cbox0, [&]() {
5299 if (emitAsImmediate)
5300 ps << opName << "(";
5301 else
5302 ps << opName << PP::nbsp << "property" << PP::nbsp << "(";
5303 // Event only exists if the clock exists
5304 Value clock = op.getClock();
5305 auto event = op.getEvent();
5306 if (clock)
5307 ps.scopedBox(PP::ibox2, [&]() {
5308 PropertyEmitter(emitter, ops)
5309 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5310 });
5311 else
5312 ps.scopedBox(PP::ibox2, [&]() {
5313 PropertyEmitter(emitter, ops)
5314 .emitAssertPropertyBody(property, op.getDisable());
5315 });
5316 ps << ");";
5317 });
5318 });
5319 ps.addCallback({op, false});
5320 emitLocationInfoAndNewLine(ops);
5321 return success();
5322}
5323
5324LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5325 return emitPropertyAssertion(op, PPExtString("assert"));
5326}
5327
5328LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5329 return emitPropertyAssertion(op, PPExtString("assume"));
5330}
5331
5332LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5333 return emitPropertyAssertion(op, PPExtString("cover"));
5334}
5335
5336LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5337 if (hasSVAttributes(op))
5338 emitError(op, "SV attributes emission is unimplemented for the op");
5339
5340 auto ident = PPExtString(
5341 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5342 .getMacroIdentifier());
5343
5344 startStatement();
5345 bool hasEmptyThen = op->getRegion(0).front().empty();
5346 if (hasEmptyThen)
5347 ps << "`ifndef " << ident;
5348 else
5349 ps << "`ifdef " << ident;
5350
5351 SmallPtrSet<Operation *, 8> ops;
5352 ops.insert(op);
5353 emitLocationInfoAndNewLine(ops);
5354
5355 if (!hasEmptyThen)
5356 emitStatementBlock(op->getRegion(0).front());
5357
5358 if (!op->getRegion(1).empty()) {
5359 if (!hasEmptyThen) {
5360 startStatement();
5361 ps << "`else // " << ident;
5362 setPendingNewline();
5363 }
5364 emitStatementBlock(op->getRegion(1).front());
5365 }
5366 startStatement();
5367 ps << "`endif // ";
5368 if (hasEmptyThen)
5369 ps << "not def ";
5370 ps << ident;
5371 setPendingNewline();
5372 return success();
5373}
5374
5375/// Emit the body of a control flow statement that is surrounded by begin/end
5376/// markers if non-singular. If the control flow construct is multi-line and
5377/// if multiLineComment is non-null, the string is included in a comment after
5378/// the 'end' to make it easier to associate.
5379void StmtEmitter::emitBlockAsStatement(
5380 Block *block, const SmallPtrSetImpl<Operation *> &locationOps,
5381 StringRef multiLineComment) {
5382
5383 // Determine if we need begin/end by scanning the block.
5384 auto count = countStatements(*block);
5385 auto needsBeginEnd =
5386 count != BlockStatementCount::One || state.options.alwaysEmitBeginEnd;
5387 if (needsBeginEnd)
5388 ps << " begin";
5389 emitLocationInfoAndNewLine(locationOps);
5390
5391 if (count != BlockStatementCount::Zero)
5392 emitStatementBlock(*block);
5393
5394 if (needsBeginEnd) {
5395 startStatement();
5396 ps << "end";
5397 // Emit comment if there's an 'end', regardless of line count.
5398 if (!multiLineComment.empty())
5399 ps << " // " << multiLineComment;
5400 setPendingNewline();
5401 }
5402}
5403
5404LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5405 // Emit the body.
5406 for (auto &op : ooop.getBody().front())
5407 emitStatement(&op);
5408 return success();
5409}
5410
5411LogicalResult StmtEmitter::visitSV(IfOp op) {
5412 SmallPtrSet<Operation *, 8> ops;
5413
5414 auto ifcondBox = PP::ibox2;
5415
5416 emitSVAttributes(op);
5417 startStatement();
5418 ps.addCallback({op, true});
5419 ps << "if (" << ifcondBox;
5420
5421 // In the loop, emit an if statement assuming the keyword introducing
5422 // it (either "if (" or "else if (") was printed already.
5423 IfOp ifOp = op;
5424 for (;;) {
5425 ops.clear();
5426 ops.insert(ifOp);
5427
5428 // Emit the condition and the then block.
5429 emitExpression(ifOp.getCond(), ops);
5430 ps << PP::end << ")";
5431 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5432
5433 if (!ifOp.hasElse())
5434 break;
5435
5436 startStatement();
5437 Block *elseBlock = ifOp.getElseBlock();
5438 auto nestedElseIfOp = findNestedElseIf(elseBlock);
5439 if (!nestedElseIfOp) {
5440 // The else block does not contain an if-else that can be flattened.
5441 ops.clear();
5442 ops.insert(ifOp);
5443 ps << "else";
5444 emitBlockAsStatement(elseBlock, ops);
5445 break;
5446 }
5447
5448 // Introduce the 'else if', and iteratively continue unfolding any if-else
5449 // statements inside of it.
5450 ifOp = nestedElseIfOp;
5451 ps << "else if (" << ifcondBox;
5452 }
5453 ps.addCallback({op, false});
5454
5455 return success();
5456}
5457
5458LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5459 emitSVAttributes(op);
5460 SmallPtrSet<Operation *, 8> ops;
5461 ops.insert(op);
5462 startStatement();
5463
5464 auto printEvent = [&](AlwaysOp::Condition cond) {
5465 ps << PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5466 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5467 };
5468 ps.addCallback({op, true});
5469
5470 switch (op.getNumConditions()) {
5471 case 0:
5472 ps << "always @*";
5473 break;
5474 case 1:
5475 ps << "always @(";
5476 printEvent(op.getCondition(0));
5477 ps << ")";
5478 break;
5479 default:
5480 ps << "always @(";
5481 ps.scopedBox(PP::cbox0, [&]() {
5482 printEvent(op.getCondition(0));
5483 for (size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5484 ps << PP::space << "or" << PP::space;
5485 printEvent(op.getCondition(i));
5486 }
5487 ps << ")";
5488 });
5489 break;
5490 }
5491
5492 // Build the comment string, leave out the signal expressions (since they
5493 // can be large).
5494 std::string comment;
5495 if (op.getNumConditions() == 0) {
5496 comment = "always @*";
5497 } else {
5498 comment = "always @(";
5499 llvm::interleave(
5500 op.getEvents(),
5501 [&](Attribute eventAttr) {
5502 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5503 comment += stringifyEventControl(event);
5504 },
5505 [&]() { comment += ", "; });
5506 comment += ')';
5507 }
5508
5509 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5510 ps.addCallback({op, false});
5511 return success();
5512}
5513
5514LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5515 emitSVAttributes(op);
5516 SmallPtrSet<Operation *, 8> ops;
5517 ops.insert(op);
5518 startStatement();
5519
5520 ps.addCallback({op, true});
5521 StringRef opString = "always_comb";
5522 if (state.options.noAlwaysComb)
5523 opString = "always @(*)";
5524
5525 ps << PPExtString(opString);
5526 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5527 ps.addCallback({op, false});
5528 return success();
5529}
5530
5531LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5532 emitSVAttributes(op);
5533
5534 SmallPtrSet<Operation *, 8> ops;
5535 ops.insert(op);
5536 startStatement();
5537
5538 ps.addCallback({op, true});
5539 ps << "always_ff @(";
5540 ps.scopedBox(PP::cbox0, [&]() {
5541 ps << PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5542 emitExpression(op.getClock(), ops);
5543 if (op.getResetStyle() == ResetType::AsyncReset) {
5544 ps << PP::nbsp << "or" << PP::space
5545 << PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5546 emitExpression(op.getReset(), ops);
5547 }
5548 ps << ")";
5549 });
5550
5551 // Build the comment string, leave out the signal expressions (since they
5552 // can be large).
5553 std::string comment;
5554 comment += "always_ff @(";
5555 comment += stringifyEventControl(op.getClockEdge());
5556 if (op.getResetStyle() == ResetType::AsyncReset) {
5557 comment += " or ";
5558 comment += stringifyEventControl(*op.getResetEdge());
5559 }
5560 comment += ')';
5561
5562 if (op.getResetStyle() == ResetType::NoReset)
5563 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5564 else {
5565 ps << " begin";
5566 emitLocationInfoAndNewLine(ops);
5567 ps.scopedBox(PP::bbox2, [&]() {
5568 startStatement();
5569 ps << "if (";
5570 // TODO: group, like normal 'if'.
5571 // Negative edge async resets need to invert the reset condition. This
5572 // is noted in the op description.
5573 if (op.getResetStyle() == ResetType::AsyncReset &&
5574 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5575 ps << "!";
5576 emitExpression(op.getReset(), ops);
5577 ps << ")";
5578 emitBlockAsStatement(op.getResetBlock(), ops);
5579 startStatement();
5580 ps << "else";
5581 emitBlockAsStatement(op.getBodyBlock(), ops);
5582 });
5583
5584 startStatement();
5585 ps << "end";
5586 ps << " // " << comment;
5587 setPendingNewline();
5588 }
5589 ps.addCallback({op, false});
5590 return success();
5591}
5592
5593LogicalResult StmtEmitter::visitSV(InitialOp op) {
5594 emitSVAttributes(op);
5595 SmallPtrSet<Operation *, 8> ops;
5596 ops.insert(op);
5597 startStatement();
5598 ps.addCallback({op, true});
5599 ps << "initial";
5600 emitBlockAsStatement(op.getBodyBlock(), ops, "initial");
5601 ps.addCallback({op, false});
5602 return success();
5603}
5604
5605LogicalResult StmtEmitter::visitSV(CaseOp op) {
5606 emitSVAttributes(op);
5607 SmallPtrSet<Operation *, 8> ops, emptyOps;
5608 ops.insert(op);
5609 startStatement();
5610 ps.addCallback({op, true});
5611 if (op.getValidationQualifier() !=
5612 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5613 ps << PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5614 op.getValidationQualifier()))
5615 << PP::nbsp;
5616 const char *opname = nullptr;
5617 switch (op.getCaseStyle()) {
5618 case CaseStmtType::CaseStmt:
5619 opname = "case";
5620 break;
5621 case CaseStmtType::CaseXStmt:
5622 opname = "casex";
5623 break;
5624 case CaseStmtType::CaseZStmt:
5625 opname = "casez";
5626 break;
5627 }
5628 ps << opname << " (";
5629 ps.scopedBox(PP::ibox0, [&]() {
5630 emitExpression(op.getCond(), ops);
5631 ps << ")";
5632 });
5633 emitLocationInfoAndNewLine(ops);
5634
5635 size_t caseValueIndex = 0;
5636 ps.scopedBox(PP::bbox2, [&]() {
5637 for (auto &caseInfo : op.getCases()) {
5638 startStatement();
5639 auto &pattern = caseInfo.pattern;
5640
5641 llvm::TypeSwitch<CasePattern *>(pattern.get())
5642 .Case<CaseBitPattern>([&](auto bitPattern) {
5643 // TODO: We could emit in hex if/when the size is a multiple of
5644 // 4 and there are no x's crossing nibble boundaries.
5645 ps.invokeWithStringOS([&](auto &os) {
5646 os << bitPattern->getWidth() << "'b";
5647 for (size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5648 os << getLetter(bitPattern->getBit(e - bit - 1));
5649 });
5650 })
5651 .Case<CaseEnumPattern>([&](auto enumPattern) {
5652 ps << PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5653 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5654 })
5655 .Case<CaseExprPattern>([&](auto) {
5656 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5657 })
5658 .Case<CaseDefaultPattern>([&](auto) { ps << "default"; })
5659 .Default([&](auto) { assert(false && "unhandled case pattern"); });
5660
5661 ps << ":";
5662 emitBlockAsStatement(caseInfo.block, emptyOps);
5663 }
5664 });
5665
5666 startStatement();
5667 ps << "endcase";
5668 ps.addCallback({op, false});
5669 emitLocationInfoAndNewLine(ops);
5670 return success();
5671}
5672
5673LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5674 bool doNotPrint = op.getDoNotPrint();
5675 if (doNotPrint && !state.options.emitBindComments)
5676 return success();
5677
5678 // Emit SV attributes if the op is not emitted as a bind statement.
5679 if (!doNotPrint)
5680 emitSVAttributes(op);
5681 startStatement();
5682 ps.addCallback({op, true});
5683 if (doNotPrint) {
5684 ps << PP::ibox2
5685 << "/* This instance is elsewhere emitted as a bind statement."
5686 << PP::newline;
5687 if (hasSVAttributes(op))
5688 op->emitWarning() << "is emitted as a bind statement but has SV "
5689 "attributes. The attributes will not be emitted.";
5690 }
5691
5692 SmallPtrSet<Operation *, 8> ops;
5693 ops.insert(op);
5694
5695 // Use the specified name or the symbol name as appropriate.
5696 auto *moduleOp =
5697 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5698 assert(moduleOp && "Invalid IR");
5699 ps << PPExtString(getVerilogModuleName(moduleOp));
5700
5701 // If this is a parameterized module, then emit the parameters.
5702 if (!op.getParameters().empty()) {
5703 // All the parameters may be defaulted -- don't print out an empty list if
5704 // so.
5705 bool printed = false;
5706 for (auto params :
5707 llvm::zip(op.getParameters(),
5708 moduleOp->getAttrOfType<ArrayAttr>("parameters"))) {
5709 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5710 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5711 // Ignore values that line up with their default.
5712 if (param.getValue() == modParam.getValue())
5713 continue;
5714
5715 // Handle # if this is the first parameter we're printing.
5716 if (!printed) {
5717 ps << " #(" << PP::bbox2 << PP::newline;
5718 printed = true;
5719 } else {
5720 ps << "," << PP::newline;
5721 }
5722 ps << ".";
5723 ps << PPExtString(
5724 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5725 ps << "(";
5726 ps.invokeWithStringOS([&](auto &os) {
5727 emitter.printParamValue(param.getValue(), os, [&]() {
5728 return op->emitOpError("invalid instance parameter '")
5729 << param.getName().getValue() << "' value";
5730 });
5731 });
5732 ps << ")";
5733 }
5734 if (printed) {
5735 ps << PP::end << PP::newline << ")";
5736 }
5737 }
5738
5739 ps << PP::nbsp << PPExtString(getSymOpName(op));
5740
5741 ModulePortInfo modPortInfo(cast<PortList>(moduleOp).getPortList());
5742 SmallVector<Value> instPortValues(modPortInfo.size());
5743 op.getValues(instPortValues, modPortInfo);
5744 emitInstancePortList(op, modPortInfo, instPortValues);
5745
5746 ps.addCallback({op, false});
5747 emitLocationInfoAndNewLine(ops);
5748 if (doNotPrint) {
5749 ps << PP::end;
5750 startStatement();
5751 ps << "*/";
5752 setPendingNewline();
5753 }
5754 return success();
5755}
5756
5757void StmtEmitter::emitInstancePortList(Operation *op,
5758 ModulePortInfo &modPortInfo,
5759 ArrayRef<Value> instPortValues) {
5760 SmallPtrSet<Operation *, 8> ops;
5761 ops.insert(op);
5762
5763 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5764 ModulePortInfo containingPortList(containingModule.getPortList());
5765
5766 ps << " (";
5767
5768 // Get the max port name length so we can align the '('.
5769 // Exclude outlier names that span the whole line from the alignment column.
5770 size_t maxNameLength = 0;
5771 for (auto &elt : modPortInfo) {
5772 size_t nameLength = elt.getVerilogName().size();
5773 if (nameLength <= state.options.emittedLineLength / 3)
5774 maxNameLength = std::max(maxNameLength, nameLength);
5775 }
5776
5777 auto getWireForValue = [&](Value result) {
5778 return result.getUsers().begin()->getOperand(0);
5779 };
5780
5781 // Emit the argument and result ports.
5782 bool isFirst = true; // True until we print a port.
5783 bool isZeroWidth = false;
5784
5785 for (size_t portNum = 0, portEnd = modPortInfo.size(); portNum < portEnd;
5786 ++portNum) {
5787 auto &modPort = modPortInfo.at(portNum);
5788 isZeroWidth = isZeroBitType(modPort.type);
5789 Value portVal = instPortValues[portNum];
5790
5791 // Decide if we should print a comma. We can't do this if we're the first
5792 // port or if all the subsequent ports are zero width.
5793 if (!isFirst) {
5794 bool shouldPrintComma = true;
5795 if (isZeroWidth) {
5796 shouldPrintComma = false;
5797 for (size_t i = portNum + 1, e = modPortInfo.size(); i != e; ++i)
5798 if (!isZeroBitType(modPortInfo.at(i).type)) {
5799 shouldPrintComma = true;
5800 break;
5801 }
5802 }
5803
5804 if (shouldPrintComma)
5805 ps << ",";
5806 }
5807 emitLocationInfoAndNewLine(ops);
5808
5809 // Emit the port's name.
5810 startStatement();
5811 if (!isZeroWidth) {
5812 // If this is a real port we're printing, then it isn't the first one. Any
5813 // subsequent ones will need a comma.
5814 isFirst = false;
5815 ps << " ";
5816 } else {
5817 // We comment out zero width ports, so their presence and initializer
5818 // expressions are still emitted textually.
5819 ps << "//";
5820 }
5821
5822 ps.scopedBox(isZeroWidth ? PP::neverbox : PP::ibox2, [&]() {
5823 auto modPortName = modPort.getVerilogName();
5824 ps << "." << PPExtString(modPortName);
5825 // Align to the column if fits, else no-break and accept possible overrun.
5826 if (modPortName.size() <= maxNameLength)
5827 ps.spaces(maxNameLength - modPortName.size() + 1);
5828 else
5829 ps.nbsp();
5830 ps << "(";
5831 ps.scopedBox(PP::ibox0, [&]() {
5832 // Emit the value as an expression.
5833 ops.clear();
5834
5835 // Output ports that are not connected to single use output ports were
5836 // lowered to wire.
5837 OutputOp output;
5838 if (!modPort.isOutput()) {
5839 if (isZeroWidth &&
5840 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5841 ps << "/* Zero width */";
5842 else
5843 emitExpression(portVal, ops, LowestPrecedence);
5844 } else if (portVal.use_empty()) {
5845 ps << "/* unused */";
5846 } else if (portVal.hasOneUse() &&
5847 (output = dyn_cast_or_null<OutputOp>(
5848 portVal.getUses().begin()->getOwner()))) {
5849 // If this is directly using the output port of the containing module,
5850 // just specify that directly so we avoid a temporary wire.
5851 // Keep this synchronized with countStatements() and
5852 // visitStmt(OutputOp).
5853 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5854 ps << PPExtString(
5855 containingPortList.atOutput(outputPortNo).getVerilogName());
5856 } else {
5857 portVal = getWireForValue(portVal);
5858 emitExpression(portVal, ops);
5859 }
5860 ps << ")";
5861 });
5862 });
5863 }
5864 if (!isFirst || isZeroWidth) {
5865 emitLocationInfoAndNewLine(ops);
5866 ops.clear();
5867 startStatement();
5868 }
5869 ps << ");";
5870}
5871
5872// This may be called in the top-level, not just in an hw.module. Thus we can't
5873// use the name map to find expression names for arguments to the instance, nor
5874// do we need to emit subexpressions. Prepare pass, which has run for all
5875// modules prior to this, has ensured that all arguments are bound to wires,
5876// regs, or ports, with legalized names, so we can lookup up the names through
5877// the IR.
5878LogicalResult StmtEmitter::visitSV(BindOp op) {
5879 emitter.emitBind(op);
5880 assert(state.pendingNewline);
5881 return success();
5882}
5883
5884LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5885 emitComment(op.getCommentAttr());
5886 // Emit SV attributes.
5887 emitSVAttributes(op);
5888 // TODO: source info!
5889 startStatement();
5890 ps.addCallback({op, true});
5891 ps << "interface " << PPExtString(getSymOpName(op)) << ";";
5892 setPendingNewline();
5893 // FIXME: Don't emit the body of this as general statements, they aren't!
5894 emitStatementBlock(*op.getBodyBlock());
5895 startStatement();
5896 ps << "endinterface" << PP::newline;
5897 ps.addCallback({op, false});
5898 setPendingNewline();
5899 return success();
5900}
5901
5902LogicalResult StmtEmitter::visitSV(sv::SVVerbatimSourceOp op) {
5903 emitSVAttributes(op);
5904 startStatement();
5905 ps.addCallback({op, true});
5906
5907 ps << op.getContent();
5908
5909 ps.addCallback({op, false});
5910 setPendingNewline();
5911 return success();
5912}
5913
5914LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5915 // Emit SV attributes.
5916 emitSVAttributes(op);
5917 startStatement();
5918 ps.addCallback({op, true});
5919 if (isZeroBitType(op.getType()))
5920 ps << PP::neverbox << "// ";
5921 ps.invokeWithStringOS([&](auto &os) {
5922 emitter.printPackedType(stripUnpackedTypes(op.getType()), os, op->getLoc(),
5923 Type(), false);
5924 });
5925 ps << PP::nbsp << PPExtString(getSymOpName(op));
5926 ps.invokeWithStringOS(
5927 [&](auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5928 ps << ";";
5929 if (isZeroBitType(op.getType()))
5930 ps << PP::end; // Close never-break group.
5931 ps.addCallback({op, false});
5932 setPendingNewline();
5933 return success();
5934}
5935
5936LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5937 startStatement();
5938 ps.addCallback({op, true});
5939 ps << "modport " << PPExtString(getSymOpName(op)) << "(";
5940
5941 // TODO: revisit, better breaks/grouping.
5942 llvm::interleaveComma(op.getPorts(), ps, [&](const Attribute &portAttr) {
5943 auto port = cast<ModportStructAttr>(portAttr);
5944 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) << " ";
5945 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5946 ps << PPExtString(getSymOpName(signalDecl));
5947 });
5948
5949 ps << ");";
5950 ps.addCallback({op, false});
5951 setPendingNewline();
5952 return success();
5953}
5954
5955LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5956 startStatement();
5957 ps.addCallback({op, true});
5958 SmallPtrSet<Operation *, 8> emitted;
5959 // TODO: emit like emitAssignLike does, maybe refactor.
5960 ps << "assign ";
5961 emitExpression(op.getIface(), emitted);
5962 ps << "." << PPExtString(op.getSignalName()) << " = ";
5963 emitExpression(op.getRhs(), emitted);
5964 ps << ";";
5965 ps.addCallback({op, false});
5966 setPendingNewline();
5967 return success();
5968}
5969
5970LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5971 startStatement();
5972 ps << "`" << op.getMacroIdentifier();
5973 setPendingNewline();
5974 return success();
5975}
5976
5977LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5978 auto decl = op.getReferencedMacro(&state.symbolCache);
5979 // TODO: source info!
5980 startStatement();
5981 ps.addCallback({op, true});
5982 ps << "`define " << PPExtString(getSymOpName(decl));
5983 if (decl.getArgs()) {
5984 ps << "(";
5985 llvm::interleaveComma(*decl.getArgs(), ps, [&](const Attribute &name) {
5986 ps << cast<StringAttr>(name);
5987 });
5988 ps << ")";
5989 }
5990 if (!op.getFormatString().empty()) {
5991 ps << " ";
5992 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5993 op.getSymbols());
5994 }
5995 ps.addCallback({op, false});
5996 setPendingNewline();
5997 return success();
5998}
5999
6000void StmtEmitter::emitStatement(Operation *op) {
6001 // Expressions may either be ignored or emitted as an expression statements.
6002 if (isVerilogExpression(op))
6003 return;
6004
6005 // Ignore LTL expressions as they are emitted as part of verification
6006 // statements. Ignore debug ops as they are emitted as part of debug info.
6007 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
6008 return;
6009
6010 // Handle HW statements, SV statements.
6011 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
6012 succeeded(dispatchVerifVisitor(op)))
6013 return;
6014
6015 emitOpError(op, "emission to Verilog not supported");
6016 emitPendingNewlineIfNeeded();
6017 ps << "unknown MLIR operation " << PPExtString(op->getName().getStringRef());
6018 setPendingNewline();
6019}
6020
6021/// Given an operation corresponding to a VerilogExpression, determine whether
6022/// it is safe to emit inline into a 'localparam' or 'automatic logic' varaible
6023/// initializer in a procedural region.
6024///
6025/// We can't emit exprs inline when they refer to something else that can't be
6026/// emitted inline, when they're in a general #ifdef region,
6027static bool
6029 StmtEmitter &stmtEmitter) {
6030 if (!isVerilogExpression(op))
6031 return false;
6032
6033 // If the expression exists in an #ifdef region, then bail. Emitting it
6034 // inline would cause it to be executed unconditionally, because the
6035 // declarations are outside the #ifdef.
6036 if (isa<IfDefProceduralOp>(op->getParentOp()))
6037 return false;
6038
6039 // This expression tree can be emitted into the initializer if all leaf
6040 // references are safe to refer to from here. They are only safe if they are
6041 // defined in an enclosing scope (guaranteed to already be live by now) or if
6042 // they are defined in this block and already emitted to an inline automatic
6043 // logic variable.
6044 SmallVector<Value, 8> exprsToScan(op->getOperands());
6045
6046 // This loop is guaranteed to terminate because we're only scanning up
6047 // single-use expressions and other things that 'isExpressionEmittedInline'
6048 // returns success for. Cycles won't get in here.
6049 while (!exprsToScan.empty()) {
6050 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
6051 if (!expr)
6052 continue; // Ports are always safe to reference.
6053
6054 // If this is an inout op, check that its inout op has no blocking
6055 // assignment. A register or logic might be mutated by a blocking assignment
6056 // so it is not always safe to inline.
6057 if (auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
6058 auto *defOp = readInout.getOperand().getDefiningOp();
6059
6060 // If it is a read from an inout port, it's unsafe to inline in general.
6061 if (!defOp)
6062 return false;
6063
6064 // If the operand is a wire, it's OK to inline the read.
6065 if (isa<sv::WireOp>(defOp))
6066 continue;
6067
6068 // Reject struct_field_inout/array_index_inout for now because it's
6069 // necessary to consider aliasing inout operations.
6070 if (!isa<RegOp, LogicOp>(defOp))
6071 return false;
6072
6073 // It's safe to inline if all users are read op, passign or assign.
6074 // If the op is a logic op whose single assignment is inlined into
6075 // declaration, we can inline the read.
6076 if (isa<LogicOp>(defOp) &&
6077 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
6078 continue;
6079
6080 // Check that it's safe for all users to be inlined.
6081 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
6082 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
6083 }))
6084 continue;
6085 return false;
6086 }
6087
6088 // If this is an internal node in the expression tree, process its operands.
6089 if (isExpressionEmittedInline(expr, stmtEmitter.state.options)) {
6090 exprsToScan.append(expr->getOperands().begin(),
6091 expr->getOperands().end());
6092 continue;
6093 }
6094
6095 // Otherwise, this isn't an inlinable expression. If it is defined outside
6096 // this block, then it is live-in.
6097 if (expr->getBlock() != op->getBlock())
6098 continue;
6099
6100 // Otherwise, if it is defined in this block then it is only ok to reference
6101 // if it has already been emitted into an automatic logic.
6102 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
6103 return false;
6104 }
6105
6106 return true;
6107}
6108
6109template <class AssignTy>
6110static AssignTy getSingleAssignAndCheckUsers(Operation *op) {
6111 AssignTy singleAssign;
6112 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
6113 if (hasSVAttributes(user))
6114 return false;
6115
6116 if (auto assign = dyn_cast<AssignTy>(user)) {
6117 if (singleAssign)
6118 return false;
6119 singleAssign = assign;
6120 return true;
6121 }
6122
6123 return isa<ReadInOutOp>(user);
6124 }))
6125 return singleAssign;
6126 return {};
6127}
6128
6129/// Return true if `op1` dominates users of `op2`.
6130static bool checkDominanceOfUsers(Operation *op1, Operation *op2) {
6131 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
6132 /// TODO: Use MLIR DominanceInfo.
6133
6134 // If the op1 and op2 are in different blocks, conservatively return false.
6135 if (op1->getBlock() != user->getBlock())
6136 return false;
6137
6138 if (op1 == user)
6139 return true;
6140
6141 return op1->isBeforeInBlock(user);
6142 });
6143}
6144
6145LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
6146 emitSVAttributes(op);
6147 auto value = op->getResult(0);
6148 SmallPtrSet<Operation *, 8> opsForLocation;
6149 opsForLocation.insert(op);
6150 startStatement();
6151 ps.addCallback({op, true});
6152
6153 // Emit the leading word, like 'wire', 'reg' or 'logic'.
6154 auto type = value.getType();
6155 auto word = getVerilogDeclWord(op, emitter);
6156 auto isZeroBit = isZeroBitType(type);
6157
6158 // LocalParams always need the bitwidth, otherwise they are considered to have
6159 // an unknown size.
6160 bool singleBitDefaultType = !isa<LocalParamOp>(op);
6161
6162 ps.scopedBox(isZeroBit ? PP::neverbox : PP::ibox2, [&]() {
6163 unsigned targetColumn = 0;
6164 unsigned column = 0;
6165
6166 // Emit the declaration keyword.
6167 if (maxDeclNameWidth > 0)
6168 targetColumn += maxDeclNameWidth + 1;
6169
6170 if (isZeroBit) {
6171 ps << "// Zero width: " << PPExtString(word) << PP::space;
6172 } else if (!word.empty()) {
6173 ps << PPExtString(word);
6174 column += word.size();
6175 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
6176 ps.spaces(numSpaces);
6177 column += numSpaces;
6178 }
6179
6180 SmallString<8> typeString;
6181 // Convert the port's type to a string and measure it.
6182 {
6183 llvm::raw_svector_ostream stringStream(typeString);
6184 emitter.printPackedType(stripUnpackedTypes(type), stringStream,
6185 op->getLoc(), /*optionalAliasType=*/{},
6186 /*implicitIntType=*/true, singleBitDefaultType);
6187 }
6188 // Emit the type.
6189 if (maxTypeWidth > 0)
6190 targetColumn += maxTypeWidth + 1;
6191 unsigned numSpaces = 0;
6192 if (!typeString.empty()) {
6193 ps << typeString;
6194 column += typeString.size();
6195 ++numSpaces;
6196 }
6197 if (targetColumn > column)
6198 numSpaces = targetColumn - column;
6199 ps.spaces(numSpaces);
6200 column += numSpaces;
6201
6202 // Emit the name.
6203 ps << PPExtString(getSymOpName(op));
6204
6205 // Print out any array subscripts or other post-name stuff.
6206 ps.invokeWithStringOS(
6207 [&](auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6208
6209 // Print debug info.
6210 if (state.options.printDebugInfo) {
6211 if (auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6212 auto innerSym = innerSymOp.getInnerSymAttr();
6213 if (innerSym && !innerSym.empty()) {
6214 ps << " /* ";
6215 ps.invokeWithStringOS([&](auto &os) { os << innerSym; });
6216 ps << " */";
6217 }
6218 }
6219 }
6220
6221 if (auto localparam = dyn_cast<LocalParamOp>(op)) {
6222 ps << PP::space << "=" << PP::space;
6223 ps.invokeWithStringOS([&](auto &os) {
6224 emitter.printParamValue(localparam.getValue(), os, [&]() {
6225 return op->emitOpError("invalid localparam value");
6226 });
6227 });
6228 }
6229
6230 if (auto regOp = dyn_cast<RegOp>(op)) {
6231 if (auto initValue = regOp.getInit()) {
6232 ps << PP::space << "=" << PP::space;
6233 ps.scopedBox(PP::ibox0, [&]() {
6234 emitExpression(initValue, opsForLocation, LowestPrecedence,
6235 /*isAssignmentLikeContext=*/true);
6236 });
6237 }
6238 }
6239
6240 // Try inlining an assignment into declarations.
6241 // FIXME: Unpacked array is not inlined since several tools doesn't support
6242 // that syntax. See Issue 6363.
6243 if (!state.options.disallowDeclAssignments && isa<sv::WireOp>(op) &&
6244 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
6245 !hasLeadingUnpackedType(op->getResult(0).getType())) {
6246 // Get a single assignments if any.
6247 if (auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6248 auto *source = singleAssign.getSrc().getDefiningOp();
6249 // Check that the source value is OK to inline in the current emission
6250 // point. A port or constant is fine, otherwise check that the assign is
6251 // next to the operation.
6252 if (!source || isa<ConstantOp>(source) ||
6253 op->getNextNode() == singleAssign) {
6254 ps << PP::space << "=" << PP::space;
6255 ps.scopedBox(PP::ibox0, [&]() {
6256 emitExpression(singleAssign.getSrc(), opsForLocation,
6257 LowestPrecedence,
6258 /*isAssignmentLikeContext=*/true);
6259 });
6260 emitter.assignsInlined.insert(singleAssign);
6261 }
6262 }
6263 }
6264
6265 // Try inlining a blocking assignment to logic op declaration.
6266 // FIXME: Unpacked array is not inlined since several tools doesn't support
6267 // that syntax. See Issue 6363.
6268 if (!state.options.disallowDeclAssignments && isa<LogicOp>(op) &&
6269 op->getParentOp()->hasTrait<ProceduralRegion>() &&
6270 !hasLeadingUnpackedType(op->getResult(0).getType())) {
6271 // Get a single assignment which might be possible to inline.
6272 if (auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6273 // It is necessary for the assignment to dominate users of the op.
6274 if (checkDominanceOfUsers(singleAssign, op)) {
6275 auto *source = singleAssign.getSrc().getDefiningOp();
6276 // A port or constant can be inlined at everywhere. Otherwise, check
6277 // the validity by
6278 // `isExpressionEmittedInlineIntoProceduralDeclaration`.
6279 if (!source || isa<ConstantOp>(source) ||
6281 *this)) {
6282 ps << PP::space << "=" << PP::space;
6283 ps.scopedBox(PP::ibox0, [&]() {
6284 emitExpression(singleAssign.getSrc(), opsForLocation,
6285 LowestPrecedence,
6286 /*isAssignmentLikeContext=*/true);
6287 });
6288 // Remember that the assignment and logic op are emitted into decl.
6289 emitter.assignsInlined.insert(singleAssign);
6290 emitter.expressionsEmittedIntoDecl.insert(op);
6291 }
6292 }
6293 }
6294 }
6295 ps << ";";
6296 });
6297 ps.addCallback({op, false});
6298 emitLocationInfoAndNewLine(opsForLocation);
6299 return success();
6300}
6301
6302void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6303 // In the first pass, we fill in the symbol table, calculate the max width
6304 // of the declaration words and the max type width.
6305 NameCollector collector(emitter);
6306 collector.collectNames(block);
6307
6308 // Record maxDeclNameWidth and maxTypeWidth in the current scope.
6309 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6310 maxTypeWidth = collector.getMaxTypeWidth();
6311}
6312
6313void StmtEmitter::emitStatementBlock(Block &body) {
6314 ps.scopedBox(PP::bbox2, [&]() {
6315 // Ensure decl alignment values are preserved after the block is emitted.
6316 // These values were computed for and from all declarations in the current
6317 // block (before/after this nested block), so be sure they're restored
6318 // and not overwritten by the declaration alignment within the block.
6319 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6320 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6321
6322 // Build up the symbol table for all of the values that need names in the
6323 // module. #ifdef's in procedural regions are special because local
6324 // variables are all emitted at the top of their enclosing blocks.
6325 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6326 collectNamesAndCalculateDeclarationWidths(body);
6327
6328 // Emit the body.
6329 for (auto &op : body) {
6330 emitStatement(&op);
6331 }
6332 });
6333}
6334// NOLINTEND(misc-no-recursion)
6335
6336void ModuleEmitter::emitStatement(Operation *op) {
6337 StmtEmitter(*this, state.options).emitStatement(op);
6338}
6339
6340/// Emit SystemVerilog attributes attached to the expression op as dialect
6341/// attributes.
6342void ModuleEmitter::emitSVAttributes(Operation *op) {
6343 // SystemVerilog 2017 Section 5.12.
6344 auto svAttrs = getSVAttributes(op);
6345 if (!svAttrs)
6346 return;
6347
6348 startStatement(); // For attributes.
6349 emitSVAttributesImpl(ps, svAttrs, /*mayBreak=*/true);
6350 setPendingNewline();
6351}
6352
6353//===----------------------------------------------------------------------===//
6354// Module Driver
6355//===----------------------------------------------------------------------===//
6356
6357void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6358 auto verilogName = module.getVerilogModuleNameAttr();
6359 startStatement();
6360 ps << "// external generated module " << PPExtString(verilogName.getValue())
6361 << PP::newline;
6362 setPendingNewline();
6363}
6364
6365// This may be called in the top-level, not just in an hw.module. Thus we can't
6366// use the name map to find expression names for arguments to the instance, nor
6367// do we need to emit subexpressions. Prepare pass, which has run for all
6368// modules prior to this, has ensured that all arguments are bound to wires,
6369// regs, or ports, with legalized names, so we can lookup up the names through
6370// the IR.
6371void ModuleEmitter::emitBind(BindOp op) {
6372 if (hasSVAttributes(op))
6373 emitError(op, "SV attributes emission is unimplemented for the op");
6374 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6375
6376 HWModuleOp parentMod = inst->getParentOfType<hw::HWModuleOp>();
6377 ModulePortInfo parentPortList(parentMod.getPortList());
6378 auto parentVerilogName = getVerilogModuleNameAttr(parentMod);
6379
6380 Operation *childMod =
6381 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6382 auto childVerilogName = getVerilogModuleNameAttr(childMod);
6383
6384 startStatement();
6385 ps.addCallback({op, true});
6386 ps << "bind " << PPExtString(parentVerilogName.getValue()) << PP::nbsp
6387 << PPExtString(childVerilogName.getValue()) << PP::nbsp
6388 << PPExtString(getSymOpName(inst)) << " (";
6389 bool isFirst = true; // True until we print a port.
6390 ps.scopedBox(PP::bbox2, [&]() {
6391 auto parentPortInfo = parentMod.getPortList();
6392 ModulePortInfo childPortInfo(cast<PortList>(childMod).getPortList());
6393
6394 // Get the max port name length so we can align the '('.
6395 // Exclude outlier names longer than the line.
6396 size_t maxNameLength = 0;
6397 for (auto &elt : childPortInfo) {
6398 auto portName = elt.getVerilogName();
6399 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6400 size_t nameLength = elt.getName().size();
6401 if (nameLength <= state.options.emittedLineLength / 3)
6402 maxNameLength = std::max(maxNameLength, nameLength);
6403 }
6404
6405 SmallVector<Value> instPortValues(childPortInfo.size());
6406 inst.getValues(instPortValues, childPortInfo);
6407 // Emit the argument and result ports.
6408 for (auto [idx, elt] : llvm::enumerate(childPortInfo)) {
6409 // Figure out which value we are emitting.
6410 Value portVal = instPortValues[idx];
6411 bool isZeroWidth = isZeroBitType(elt.type);
6412
6413 // Decide if we should print a comma. We can't do this if we're the
6414 // first port or if all the subsequent ports are zero width.
6415 if (!isFirst) {
6416 bool shouldPrintComma = true;
6417 if (isZeroWidth) {
6418 shouldPrintComma = false;
6419 for (size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6420 if (!isZeroBitType(childPortInfo.at(i).type)) {
6421 shouldPrintComma = true;
6422 break;
6423 }
6424 }
6425
6426 if (shouldPrintComma)
6427 ps << ",";
6428 }
6429 ps << PP::newline;
6430
6431 // Emit the port's name.
6432 if (!isZeroWidth) {
6433 // If this is a real port we're printing, then it isn't the first
6434 // one. Any subsequent ones will need a comma.
6435 isFirst = false;
6436 } else {
6437 // We comment out zero width ports, so their presence and
6438 // initializer expressions are still emitted textually.
6439 ps << PP::neverbox << "//";
6440 }
6441
6442 ps << "." << PPExtString(elt.getName());
6443 // Align to the column if fits, else no-break and accept possible overrun.
6444 if (elt.getName().size() <= maxNameLength)
6445 ps.nbsp(maxNameLength - elt.getName().size());
6446 ps << " (";
6447 llvm::SmallPtrSet<Operation *, 4> ops;
6448 if (elt.isOutput()) {
6449 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6450 "output port must have either single or no use");
6451 if (portVal.use_empty()) {
6452 ps << "/* unused */";
6453 } else if (auto output = dyn_cast_or_null<OutputOp>(
6454 portVal.getUses().begin()->getOwner())) {
6455 // If this is directly using the output port of the containing
6456 // module, just specify that directly.
6457 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6458 ps << PPExtString(
6459 parentPortList.atOutput(outputPortNo).getVerilogName());
6460 } else {
6461 portVal = portVal.getUsers().begin()->getOperand(0);
6462 ExprEmitter(*this, ops)
6463 .emitExpression(portVal, LowestPrecedence,
6464 /*isAssignmentLikeContext=*/false);
6465 }
6466 } else {
6467 ExprEmitter(*this, ops)
6468 .emitExpression(portVal, LowestPrecedence,
6469 /*isAssignmentLikeContext=*/false);
6470 }
6471
6472 ps << ")";
6473
6474 if (isZeroWidth)
6475 ps << PP::end; // Close never-break group.
6476 }
6477 });
6478 if (!isFirst)
6479 ps << PP::newline;
6480 ps << ");";
6481 ps.addCallback({op, false});
6482 setPendingNewline();
6483}
6484
6485void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6486 if (hasSVAttributes(op))
6487 emitError(op, "SV attributes emission is unimplemented for the op");
6488
6489 auto instance = op.getReferencedInstance(&state.symbolCache);
6490 auto instantiator = instance->getParentOfType<HWModuleOp>().getName();
6491 auto *interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6492 instance.getInterfaceType().getInterface());
6493 startStatement();
6494 ps.addCallback({op, true});
6495 ps << "bind " << PPExtString(instantiator) << PP::nbsp
6496 << PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6497 << PPExtString(getSymOpName(instance)) << " (.*);" << PP::newline;
6498 ps.addCallback({op, false});
6499 setPendingNewline();
6500}
6501
6502void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6503 if (params.empty())
6504 return;
6505
6506 auto printParamType = [&](Type type, Attribute defaultValue,
6507 SmallString<8> &result) {
6508 result.clear();
6509 llvm::raw_svector_ostream sstream(result);
6510
6511 // If there is a default value like "32" then just print without type at
6512 // all.
6513 if (defaultValue) {
6514 if (auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6515 if (intAttr.getValue().getBitWidth() == 32)
6516 return;
6517 if (auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6518 if (fpAttr.getType().isF64())
6519 return;
6520 }
6521 if (isa<NoneType>(type))
6522 return;
6523
6524 // Classic Verilog parser don't allow a type in the parameter declaration.
6525 // For compatibility with them, we omit the type when it is implicit based
6526 // on its initializer value, and print the type commented out when it is
6527 // a 32-bit "integer" parameter.
6528 if (auto intType = type_dyn_cast<IntegerType>(type))
6529 if (intType.getWidth() == 32) {
6530 sstream << "/*integer*/";
6531 return;
6532 }
6533
6534 printPackedType(type, sstream, module->getLoc(),
6535 /*optionalAliasType=*/Type(),
6536 /*implicitIntType=*/true,
6537 // Print single-bit values as explicit `[0:0]` type.
6538 /*singleBitDefaultType=*/false);
6539 };
6540
6541 // Determine the max width of the parameter types so things are lined up.
6542 size_t maxTypeWidth = 0;
6543 SmallString<8> scratch;
6544 for (auto param : params) {
6545 auto paramAttr = cast<ParamDeclAttr>(param);
6546 // Measure the type length by printing it to a temporary string.
6547 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6548 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6549 }
6550
6551 if (maxTypeWidth > 0) // add a space if any type exists.
6552 maxTypeWidth += 1;
6553
6554 ps.scopedBox(PP::bbox2, [&]() {
6555 ps << PP::newline << "#(";
6556 ps.scopedBox(PP::cbox0, [&]() {
6557 llvm::interleave(
6558 params,
6559 [&](Attribute param) {
6560 auto paramAttr = cast<ParamDeclAttr>(param);
6561 auto defaultValue = paramAttr.getValue(); // may be null if absent.
6562 ps << "parameter ";
6563 printParamType(paramAttr.getType(), defaultValue, scratch);
6564 if (!scratch.empty())
6565 ps << scratch;
6566 if (scratch.size() < maxTypeWidth)
6567 ps.nbsp(maxTypeWidth - scratch.size());
6568
6569 ps << PPExtString(state.globalNames.getParameterVerilogName(
6570 module, paramAttr.getName()));
6571
6572 if (defaultValue) {
6573 ps << " = ";
6574 ps.invokeWithStringOS([&](auto &os) {
6575 printParamValue(defaultValue, os, [&]() {
6576 return module->emitError("parameter '")
6577 << paramAttr.getName().getValue()
6578 << "' has invalid value";
6579 });
6580 });
6581 }
6582 },
6583 [&]() { ps << "," << PP::newline; });
6584 ps << ") ";
6585 });
6586 });
6587}
6588
6589void ModuleEmitter::emitPortList(Operation *module,
6590 const ModulePortInfo &portInfo,
6591 bool emitAsTwoStateType) {
6592 ps << "(";
6593 if (portInfo.size())
6594 emitLocationInfo(module->getLoc());
6595
6596 // Determine the width of the widest type we have to print so everything
6597 // lines up nicely.
6598 bool hasOutputs = false, hasZeroWidth = false;
6599 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6600 SmallVector<SmallString<8>, 16> portTypeStrings;
6601
6602 for (size_t i = 0, e = portInfo.size(); i < e; ++i) {
6603 auto port = portInfo.at(i);
6604 hasOutputs |= port.isOutput();
6605 hasZeroWidth |= isZeroBitType(port.type);
6606 if (!isZeroBitType(port.type))
6607 lastNonZeroPort = i;
6608
6609 // Convert the port's type to a string and measure it.
6610 portTypeStrings.push_back({});
6611 {
6612 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6613 printPackedType(stripUnpackedTypes(port.type), stringStream,
6614 module->getLoc(), {}, true, true, emitAsTwoStateType);
6615 }
6616
6617 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6618 }
6619
6620 if (maxTypeWidth > 0) // add a space if any type exists
6621 maxTypeWidth += 1;
6622
6623 // Emit the port list.
6624 ps.scopedBox(PP::bbox2, [&]() {
6625 for (size_t portIdx = 0, e = portInfo.size(); portIdx != e;) {
6626 auto lastPort = e - 1;
6627
6628 ps << PP::newline;
6629 auto portType = portInfo.at(portIdx).type;
6630
6631 // If this is a zero width type, emit the port as a comment and create a
6632 // neverbox to ensure we don't insert a line break.
6633 bool isZeroWidth = false;
6634 if (hasZeroWidth) {
6635 isZeroWidth = isZeroBitType(portType);
6636 if (isZeroWidth)
6637 ps << PP::neverbox;
6638 ps << (isZeroWidth ? "// " : " ");
6639 }
6640
6641 // Emit the port direction and optional wire keyword.
6642 auto thisPortDirection = portInfo.at(portIdx).dir;
6643 size_t startOfNamePos = (hasOutputs ? 7 : 6) +
6644 (state.options.emitWireInPorts ? 5 : 0) +
6645 maxTypeWidth;
6646 // Modport-typed ports (e.g., MyBundle.sink) already encode their
6647 // direction in the interface modport definition, so we suppress the
6648 // direction and wire keywords for them.
6649 if (!isa<ModportType>(portType)) {
6650 switch (thisPortDirection) {
6651 case ModulePort::Direction::Output:
6652 ps << "output ";
6653 break;
6654 case ModulePort::Direction::Input:
6655 ps << (hasOutputs ? "input " : "input ");
6656 break;
6657 case ModulePort::Direction::InOut:
6658 ps << (hasOutputs ? "inout " : "inout ");
6659 break;
6660 }
6661 if (state.options.emitWireInPorts)
6662 ps << "wire ";
6663 if (!portTypeStrings[portIdx].empty())
6664 ps << portTypeStrings[portIdx];
6665 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6666 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6667 } else {
6668 ps << portTypeStrings[portIdx];
6669 if (portTypeStrings[portIdx].size() < startOfNamePos)
6670 ps.nbsp(startOfNamePos - portTypeStrings[portIdx].size());
6671 }
6672
6673 // Emit the name.
6674 ps << PPExtString(portInfo.at(portIdx).getVerilogName());
6675
6676 // Emit array dimensions.
6677 ps.invokeWithStringOS(
6678 [&](auto &os) { printUnpackedTypePostfix(portType, os); });
6679
6680 // Emit the symbol.
6681 auto innerSym = portInfo.at(portIdx).getSym();
6682 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6683 ps << " /* ";
6684 ps.invokeWithStringOS([&](auto &os) { os << innerSym; });
6685 ps << " */";
6686 }
6687
6688 // Emit the comma if this is not the last real port.
6689 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6690 ps << ",";
6691
6692 // Emit the location.
6693 if (auto loc = portInfo.at(portIdx).loc)
6694 emitLocationInfo(loc);
6695
6696 if (isZeroWidth)
6697 ps << PP::end; // Close never-break group.
6698
6699 ++portIdx;
6700
6701 // If we have any more ports with the same types and the same
6702 // direction, emit them in a list one per line. Optionally skip this
6703 // behavior when requested by user.
6704 if (!state.options.disallowPortDeclSharing) {
6705 while (portIdx != e && portInfo.at(portIdx).dir == thisPortDirection &&
6706 stripUnpackedTypes(portType) ==
6707 stripUnpackedTypes(portInfo.at(portIdx).type)) {
6708 auto port = portInfo.at(portIdx);
6709 // Append this to the running port decl.
6710 ps << PP::newline;
6711
6712 bool isZeroWidth = false;
6713 if (hasZeroWidth) {
6714 isZeroWidth = isZeroBitType(portType);
6715 if (isZeroWidth)
6716 ps << PP::neverbox;
6717 ps << (isZeroWidth ? "// " : " ");
6718 }
6719
6720 ps.nbsp(startOfNamePos);
6721
6722 // Emit the name.
6723 StringRef name = port.getVerilogName();
6724 ps << PPExtString(name);
6725
6726 // Emit array dimensions.
6727 ps.invokeWithStringOS(
6728 [&](auto &os) { printUnpackedTypePostfix(port.type, os); });
6729
6730 // Emit the symbol.
6731 auto sym = port.getSym();
6732 if (state.options.printDebugInfo && sym && !sym.empty())
6733 ps << " /* inner_sym: " << PPExtString(sym.getSymName().getValue())
6734 << " */";
6735
6736 // Emit the comma if this is not the last real port.
6737 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6738 ps << ",";
6739
6740 // Emit the location.
6741 if (auto loc = port.loc)
6742 emitLocationInfo(loc);
6743
6744 if (isZeroWidth)
6745 ps << PP::end; // Close never-break group.
6746
6747 ++portIdx;
6748 }
6749 }
6750 }
6751 });
6752
6753 if (!portInfo.size()) {
6754 ps << ");";
6755 SmallPtrSet<Operation *, 8> moduleOpSet;
6756 moduleOpSet.insert(module);
6757 emitLocationInfoAndNewLine(moduleOpSet);
6758 } else {
6759 ps << PP::newline;
6760 ps << ");" << PP::newline;
6761 setPendingNewline();
6762 }
6763}
6764
6765void ModuleEmitter::emitHWModule(HWModuleOp module) {
6766 currentModuleOp = module;
6767
6768 emitComment(module.getCommentAttr());
6769 emitSVAttributes(module);
6770 startStatement();
6771 ps.addCallback({module, true});
6772 ps << "module " << PPExtString(getVerilogModuleName(module));
6773
6774 // If we have any parameters, print them on their own line.
6775 emitParameters(module, module.getParameters());
6776
6777 emitPortList(module, ModulePortInfo(module.getPortList()));
6778
6779 assert(state.pendingNewline);
6780
6781 // Emit the body of the module.
6782 StmtEmitter(*this, state.options).emitStatementBlock(*module.getBodyBlock());
6783 startStatement();
6784 ps << "endmodule";
6785 ps.addCallback({module, false});
6786 ps << PP::newline;
6787 setPendingNewline();
6788
6789 currentModuleOp = nullptr;
6790}
6791
6792void ModuleEmitter::emitFunc(FuncOp func) {
6793 // Nothing to emit for a declaration.
6794 if (func.isDeclaration())
6795 return;
6796
6797 currentModuleOp = func;
6798 startStatement();
6799 ps.addCallback({func, true});
6800 // A function is moduled as an automatic function.
6801 emitFunctionSignature(*this, ps, func, /*isAutomatic=*/true);
6802 // Emit the body of the module.
6803 StmtEmitter(*this, state.options).emitStatementBlock(*func.getBodyBlock());
6804 startStatement();
6805 ps << "endfunction";
6806 ps << PP::newline;
6807 currentModuleOp = nullptr;
6808}
6809
6810//===----------------------------------------------------------------------===//
6811// Emitter for files & file lists.
6812//===----------------------------------------------------------------------===//
6813
6814class FileEmitter : public EmitterBase {
6815public:
6816 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6817
6818 void emit(emit::FileOp op) {
6819 emit(op.getBody());
6820 ps.eof();
6821 }
6822 void emit(emit::FragmentOp op) { emit(op.getBody()); }
6823 void emit(emit::FileListOp op);
6824
6825private:
6826 void emit(Block *block);
6827
6828 void emitOp(emit::RefOp op);
6829 void emitOp(emit::VerbatimOp op);
6830};
6831
6832void FileEmitter::emit(Block *block) {
6833 for (Operation &op : *block) {
6834 TypeSwitch<Operation *>(&op)
6835 .Case<emit::VerbatimOp, emit::RefOp>([&](auto op) { emitOp(op); })
6836 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6837 [&](auto op) { ModuleEmitter(state).emitStatement(op); })
6838 .Case<BindOp>([&](auto op) { ModuleEmitter(state).emitBind(op); })
6839 .Case<BindInterfaceOp>(
6840 [&](auto op) { ModuleEmitter(state).emitBindInterface(op); })
6841 .Case<TypeScopeOp>([&](auto typedecls) {
6842 ModuleEmitter(state).emitStatement(typedecls);
6843 })
6844 .Default(
6845 [&](auto op) { emitOpError(op, "cannot be emitted to a file"); });
6846 }
6847}
6848
6849void FileEmitter::emit(emit::FileListOp op) {
6850 // Find the associated file ops and write the paths on individual lines.
6851 for (auto sym : op.getFiles()) {
6852 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6853
6854 auto it = state.fileMapping.find(fileName);
6855 if (it == state.fileMapping.end()) {
6856 emitOpError(op, " references an invalid file: ") << sym;
6857 continue;
6858 }
6859
6860 auto file = cast<emit::FileOp>(it->second);
6861 ps << PP::neverbox << PPExtString(file.getFileName()) << PP::end
6862 << PP::newline;
6863 }
6864 ps.eof();
6865}
6866
6867void FileEmitter::emitOp(emit::RefOp op) {
6868 StringAttr target = op.getTargetAttr().getAttr();
6869 auto *targetOp = state.symbolCache.getDefinition(target);
6870 assert(isa<emit::Emittable>(targetOp) && "target must be emittable");
6871
6872 TypeSwitch<Operation *>(targetOp)
6873 .Case<sv::FuncOp>([&](auto func) { ModuleEmitter(state).emitFunc(func); })
6874 .Case<hw::HWModuleOp>(
6875 [&](auto module) { ModuleEmitter(state).emitHWModule(module); })
6876 .Case<TypeScopeOp>([&](auto typedecls) {
6877 ModuleEmitter(state).emitStatement(typedecls);
6878 })
6879 .Default(
6880 [&](auto op) { emitOpError(op, "cannot be emitted to a file"); });
6881}
6882
6883void FileEmitter::emitOp(emit::VerbatimOp op) {
6884 startStatement();
6885
6886 SmallPtrSet<Operation *, 8> ops;
6887 ops.insert(op);
6888
6889 // Emit each line of the string at a time, emitting the
6890 // location comment after the last emitted line.
6891 StringRef text = op.getText();
6892
6893 ps << PP::neverbox;
6894 do {
6895 const auto &[lhs, rhs] = text.split('\n');
6896 if (!lhs.empty())
6897 ps << PPExtString(lhs);
6898 if (!rhs.empty())
6899 ps << PP::end << PP::newline << PP::neverbox;
6900 text = rhs;
6901 } while (!text.empty());
6902 ps << PP::end;
6903
6904 emitLocationInfoAndNewLine(ops);
6905}
6906
6907//===----------------------------------------------------------------------===//
6908// Top level "file" emitter logic
6909//===----------------------------------------------------------------------===//
6910
6911/// Organize the operations in the root MLIR module into output files to be
6912/// generated. If `separateModules` is true, a handful of top-level
6913/// declarations will be split into separate output files even in the absence
6914/// of an explicit output file attribute.
6915void SharedEmitterState::gatherFiles(bool separateModules) {
6916
6917 /// Collect all the inner names from the specified module and add them to the
6918 /// IRCache. Declarations (named things) only exist at the top level of the
6919 /// module. Also keep track of any modules that contain bind operations.
6920 /// These are non-hierarchical references which we need to be careful about
6921 /// during emission.
6922 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6923 moduleOp->walk([&](Operation *op) {
6924 // Populate the symbolCache with all operations that can define a symbol.
6925 if (auto name = op->getAttrOfType<InnerSymAttr>(
6927 symbolCache.addDefinition(moduleOp->getAttrOfType<StringAttr>(
6928 SymbolTable::getSymbolAttrName()),
6929 name.getSymName(), op);
6930 if (isa<BindOp>(op))
6931 modulesContainingBinds.insert(moduleOp);
6932 });
6933 };
6934
6935 /// Collect any port marked as being referenced via symbol.
6936 auto collectPorts = [&](auto moduleOp) {
6937 auto portInfo = moduleOp.getPortList();
6938 for (auto [i, p] : llvm::enumerate(portInfo)) {
6939 if (!p.attrs || p.attrs.empty())
6940 continue;
6941 for (NamedAttribute portAttr : p.attrs) {
6942 if (auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6943 symbolCache.addDefinition(moduleOp.getNameAttr(), sym.getSymName(),
6944 moduleOp, i);
6945 }
6946 }
6947 }
6948 };
6949
6950 // Create a mapping identifying the files each symbol is emitted to.
6951 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6952 for (auto file : designOp.getOps<emit::FileOp>())
6953 for (auto refs : file.getOps<emit::RefOp>())
6954 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6955
6956 SmallString<32> outputPath;
6957 for (auto &op : *designOp.getBody()) {
6958 auto info = OpFileInfo{&op, replicatedOps.size()};
6959
6960 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6961
6962 bool hasFileName = false;
6963 bool emitReplicatedOps = !isFileOp;
6964 bool addToFilelist = !isFileOp;
6965
6966 outputPath.clear();
6967
6968 // Check if the operation has an explicit `output_file` attribute set. If
6969 // it does, extract the information from the attribute.
6970 auto attr = op.getAttrOfType<hw::OutputFileAttr>("output_file");
6971 if (attr) {
6972 LLVM_DEBUG(llvm::dbgs() << "Found output_file attribute " << attr
6973 << " on " << op << "\n";);
6974 if (!attr.isDirectory())
6975 hasFileName = true;
6976 appendPossiblyAbsolutePath(outputPath, attr.getFilename().getValue());
6977 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6978 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6979 }
6980
6981 auto separateFile = [&](Operation *op, Twine defaultFileName = "") {
6982 // If we're emitting to a separate file and the output_file attribute
6983 // didn't specify a filename, take the default one if present or emit an
6984 // error if not.
6985 if (!hasFileName) {
6986 if (!defaultFileName.isTriviallyEmpty()) {
6987 llvm::sys::path::append(outputPath, defaultFileName);
6988 } else {
6989 op->emitError("file name unspecified");
6990 encounteredError = true;
6991 llvm::sys::path::append(outputPath, "error.out");
6992 }
6993 }
6994
6995 auto destFile = StringAttr::get(op->getContext(), outputPath);
6996 auto &file = files[destFile];
6997 file.ops.push_back(info);
6998 file.emitReplicatedOps = emitReplicatedOps;
6999 file.addToFilelist = addToFilelist;
7000 file.isVerilog = outputPath.ends_with(".sv");
7001
7002 // Back-annotate the op with an OutputFileAttr if there wasn't one. If it
7003 // was a directory, back-annotate the final file path. This is so output
7004 // files are explicit in the final MLIR after export.
7005 if (!attr || attr.isDirectory()) {
7006 auto excludeFromFileListAttr =
7007 BoolAttr::get(op->getContext(), !addToFilelist);
7008 auto includeReplicatedOpsAttr =
7009 BoolAttr::get(op->getContext(), emitReplicatedOps);
7010 auto outputFileAttr = hw::OutputFileAttr::get(
7011 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
7012 op->setAttr("output_file", outputFileAttr);
7013 }
7014 };
7015
7016 // Separate the operation into dedicated output file, or emit into the
7017 // root file, or replicate in all output files.
7018 TypeSwitch<Operation *>(&op)
7019 .Case<emit::FileOp, emit::FileListOp>([&](auto file) {
7020 // Emit file ops to their respective files.
7021 fileMapping.try_emplace(file.getSymNameAttr(), file);
7022 separateFile(file, file.getFileName());
7023 })
7024 .Case<emit::FragmentOp>([&](auto fragment) {
7025 fragmentMapping.try_emplace(fragment.getSymNameAttr(), fragment);
7026 })
7027 .Case<HWModuleOp>([&](auto mod) {
7028 // Build the IR cache.
7029 auto sym = mod.getNameAttr();
7030 symbolCache.addDefinition(sym, mod);
7031 collectPorts(mod);
7032 collectInstanceSymbolsAndBinds(mod);
7033
7034 if (auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
7035 if (it->second.size() != 1 || attr) {
7036 // This is a temporary check, present as long as both
7037 // output_file and file operations are used.
7038 op.emitError("modules can be emitted to a single file");
7039 encounteredError = true;
7040 } else {
7041 // The op is not separated into a file as it will be
7042 // pulled into the unique file operation it references.
7043 }
7044 } else {
7045 // Emit into a separate file named after the module.
7046 if (attr || separateModules)
7047 separateFile(mod, getVerilogModuleName(mod) + ".sv");
7048 else
7049 rootFile.ops.push_back(info);
7050 }
7051 })
7052 .Case<InterfaceOp>([&](InterfaceOp intf) {
7053 // Build the IR cache.
7054 symbolCache.addDefinition(intf.getNameAttr(), intf);
7055 // Populate the symbolCache with all operations that can define a
7056 // symbol.
7057 for (auto &op : *intf.getBodyBlock())
7058 if (auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
7059 if (auto name = symOp.getNameAttr())
7060 symbolCache.addDefinition(name, symOp);
7061
7062 // Emit into a separate file named after the interface.
7063 if (attr || separateModules)
7064 separateFile(intf, intf.getSymName() + ".sv");
7065 else
7066 rootFile.ops.push_back(info);
7067 })
7068 .Case<sv::SVVerbatimSourceOp>([&](sv::SVVerbatimSourceOp op) {
7069 symbolCache.addDefinition(op.getNameAttr(), op);
7070 separateFile(op, op.getOutputFile().getFilename().getValue());
7071 })
7072 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](auto op) {
7073 // Build the IR cache.
7074 symbolCache.addDefinition(op.getNameAttr(), op);
7075 collectPorts(op);
7076 // External modules are _not_ emitted.
7077 })
7078 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
7079 [&](Operation *op) {
7080 // Emit into a separate file using the specified file name or
7081 // replicate the operation in each outputfile.
7082 if (!attr) {
7083 replicatedOps.push_back(op);
7084 } else
7085 separateFile(op, "");
7086 })
7087 .Case<FuncOp>([&](auto op) {
7088 // Emit into a separate file using the specified file name or
7089 // replicate the operation in each outputfile.
7090 if (!attr) {
7091 replicatedOps.push_back(op);
7092 } else
7093 separateFile(op, "");
7094
7095 symbolCache.addDefinition(op.getSymNameAttr(), op);
7096 })
7097 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
7098 symbolCache.addDefinition(schemaOp.getNameAttr(), schemaOp);
7099 })
7100 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
7101 symbolCache.addDefinition(hierPathOp.getSymNameAttr(), hierPathOp);
7102 })
7103 .Case<TypeScopeOp>([&](TypeScopeOp op) {
7104 symbolCache.addDefinition(op.getNameAttr(), op);
7105 // TODO: How do we want to handle typedefs in a split output?
7106 if (!attr) {
7107 replicatedOps.push_back(op);
7108 } else
7109 separateFile(op, "");
7110 })
7111 .Case<BindOp>([&](auto op) {
7112 if (!attr) {
7113 separateFile(op, "bindfile.sv");
7114 } else {
7115 separateFile(op);
7116 }
7117 })
7118 .Case<MacroErrorOp>([&](auto op) { replicatedOps.push_back(op); })
7119 .Case<MacroDeclOp>([&](auto op) {
7120 symbolCache.addDefinition(op.getSymNameAttr(), op);
7121 })
7122 .Case<sv::ReserveNamesOp>([](auto op) {
7123 // This op was already used in gathering used names.
7124 })
7125 .Case<om::ClassLike>([&](auto op) {
7126 symbolCache.addDefinition(op.getSymNameAttr(), op);
7127 })
7128 .Case<om::ConstantOp>([&](auto op) {
7129 // Constant ops might reference symbols, skip them.
7130 })
7131 .Default([&](auto *) {
7132 op.emitError("unknown operation (SharedEmitterState::gatherFiles)");
7133 encounteredError = true;
7134 });
7135 }
7136
7137 // We've built the whole symbol cache. Freeze it so things can start
7138 // querying it (potentially concurrently).
7140}
7141
7142/// Given a FileInfo, collect all the replicated and designated operations
7143/// that go into it and append them to "thingsToEmit".
7145 EmissionList &thingsToEmit,
7146 bool emitHeader) {
7147 // Include the version string comment when the file is verilog.
7149 thingsToEmit.emplace_back(circt::getCirctVersionComment());
7150
7151 // If we're emitting replicated ops, keep track of where we are in the list.
7152 size_t lastReplicatedOp = 0;
7153
7154 bool emitHeaderInclude =
7155 emitHeader && file.emitReplicatedOps && !file.isHeader;
7156
7157 if (emitHeaderInclude)
7158 thingsToEmit.emplace_back(circtHeaderInclude);
7159
7160 size_t numReplicatedOps =
7161 file.emitReplicatedOps && !emitHeaderInclude ? replicatedOps.size() : 0;
7162
7163 // Emit each operation in the file preceded by the replicated ops not yet
7164 // printed.
7165 DenseSet<emit::FragmentOp> includedFragments;
7166 for (const auto &opInfo : file.ops) {
7167 Operation *op = opInfo.op;
7168
7169 // Emit the replicated per-file operations before the main operation's
7170 // position (if enabled).
7171 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
7172 ++lastReplicatedOp)
7173 thingsToEmit.emplace_back(replicatedOps[lastReplicatedOp]);
7174
7175 // Pull in the fragments that the op references. In one file, each
7176 // fragment is emitted only once.
7177 if (auto fragments =
7178 op->getAttrOfType<ArrayAttr>(emit::getFragmentsAttrName())) {
7179 for (auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
7180 auto it = fragmentMapping.find(sym.getAttr());
7181 if (it == fragmentMapping.end()) {
7182 encounteredError = true;
7183 op->emitError("cannot find referenced fragment ") << sym;
7184 continue;
7185 }
7186 emit::FragmentOp fragment = it->second;
7187 if (includedFragments.insert(fragment).second) {
7188 thingsToEmit.emplace_back(it->second);
7189 }
7190 }
7191 }
7192
7193 // Emit the operation itself.
7194 thingsToEmit.emplace_back(op);
7195 }
7196
7197 // Emit the replicated per-file operations after the last operation (if
7198 // enabled).
7199 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
7200 thingsToEmit.emplace_back(replicatedOps[lastReplicatedOp]);
7201}
7202
7203static void emitOperation(VerilogEmitterState &state, Operation *op) {
7204 TypeSwitch<Operation *>(op)
7205 .Case<HWModuleOp>([&](auto op) { ModuleEmitter(state).emitHWModule(op); })
7206 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](auto op) {
7207 // External modules are _not_ emitted.
7208 })
7209 .Case<HWModuleGeneratedOp>(
7210 [&](auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
7211 .Case<HWGeneratorSchemaOp>([&](auto op) { /* Empty */ })
7212 .Case<BindOp>([&](auto op) { ModuleEmitter(state).emitBind(op); })
7213 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7214 [&](auto op) { ModuleEmitter(state).emitStatement(op); })
7215 .Case<TypeScopeOp>([&](auto typedecls) {
7216 ModuleEmitter(state).emitStatement(typedecls);
7217 })
7218 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7219 [&](auto op) { FileEmitter(state).emit(op); })
7220 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7221 [&](auto op) { ModuleEmitter(state).emitStatement(op); })
7222 .Case<FuncOp>([&](auto op) { ModuleEmitter(state).emitFunc(op); })
7223 .Case<IncludeOp>([&](auto op) { ModuleEmitter(state).emitStatement(op); })
7224 .Default([&](auto *op) {
7225 state.encounteredError = true;
7226 op->emitError("unknown operation (ExportVerilog::emitOperation)");
7227 });
7228}
7229
7230/// Actually emit the collected list of operations and strings to the
7231/// specified file.
7233 llvm::formatted_raw_ostream &os,
7234 StringAttr fileName, bool parallelize) {
7235 MLIRContext *context = designOp->getContext();
7236
7237 // Disable parallelization overhead if MLIR threading is disabled.
7238 if (parallelize)
7239 parallelize &= context->isMultithreadingEnabled();
7240
7241 // If we aren't parallelizing output, directly output each operation to the
7242 // specified stream.
7243 if (!parallelize) {
7244 // All the modules share the same map to store the verilog output location
7245 // on the stream.
7246 OpLocMap verilogLocMap(os);
7247 VerilogEmitterState state(designOp, *this, options, symbolCache,
7248 globalNames, fileMapping, os, fileName,
7249 verilogLocMap);
7250 size_t lineOffset = 0;
7251 for (auto &entry : thingsToEmit) {
7252 entry.verilogLocs.setStream(os);
7253 if (auto *op = entry.getOperation()) {
7254 emitOperation(state, op);
7255 // Since the modules are exported sequentially, update all the ops with
7256 // the verilog location. This also clears the map, so that the map only
7257 // contains the current iteration's ops.
7258 state.addVerilogLocToOps(lineOffset, fileName);
7259 } else {
7260 os << entry.getStringData();
7261 ++lineOffset;
7262 }
7263 }
7264
7265 if (state.encounteredError)
7266 encounteredError = true;
7267 return;
7268 }
7269
7270 // If we are parallelizing emission, we emit each independent operation to a
7271 // string buffer in parallel, then concat at the end.
7272 parallelForEach(context, thingsToEmit, [&](StringOrOpToEmit &stringOrOp) {
7273 auto *op = stringOrOp.getOperation();
7274 if (!op)
7275 return; // Ignore things that are already strings.
7276
7277 // BindOp emission reaches into the hw.module of the instance, and that
7278 // body may be being transformed by its own emission. Defer their
7279 // emission to the serial phase. They are speedy to emit anyway.
7280 if (isa<BindOp>(op) || modulesContainingBinds.count(op))
7281 return;
7282
7283 SmallString<256> buffer;
7284 llvm::raw_svector_ostream tmpStream(buffer);
7285 llvm::formatted_raw_ostream rs(tmpStream);
7286 // Each `thingToEmit` (op) uses a unique map to store verilog locations.
7287 stringOrOp.verilogLocs.setStream(rs);
7288 VerilogEmitterState state(designOp, *this, options, symbolCache,
7289 globalNames, fileMapping, rs, fileName,
7290 stringOrOp.verilogLocs);
7291 emitOperation(state, op);
7292 stringOrOp.setString(buffer);
7293 if (state.encounteredError)
7294 encounteredError = true;
7295 });
7296
7297 // Finally emit each entry now that we know it is a string.
7298 for (auto &entry : thingsToEmit) {
7299 // Almost everything is lowered to a string, just concat the strings onto
7300 // the output stream.
7301 auto *op = entry.getOperation();
7302 if (!op) {
7303 auto lineOffset = os.getLine() + 1;
7304 os << entry.getStringData();
7305 // Ensure the line numbers are offset properly in the map. Each `entry`
7306 // was exported in parallel onto independent string streams, hence the
7307 // line numbers need to be updated with the offset in the current stream.
7308 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
7309 continue;
7310 }
7311 entry.verilogLocs.setStream(os);
7312
7313 // If this wasn't emitted to a string (e.g. it is a bind) do so now.
7314 VerilogEmitterState state(designOp, *this, options, symbolCache,
7315 globalNames, fileMapping, os, fileName,
7316 entry.verilogLocs);
7317 emitOperation(state, op);
7318 state.addVerilogLocToOps(0, fileName);
7319 if (state.encounteredError) {
7320 encounteredError = true;
7321 return;
7322 }
7323 }
7324}
7325
7326//===----------------------------------------------------------------------===//
7327// Unified Emitter
7328//===----------------------------------------------------------------------===//
7329
7330static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os) {
7331 LoweringOptions options(module);
7332 GlobalNameTable globalNames = legalizeGlobalNames(module, options);
7333
7334 SharedEmitterState emitter(module, options, std::move(globalNames));
7335 emitter.gatherFiles(false);
7336
7338 module.emitWarning()
7339 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7340 "created only at SplitExportVerilog";
7341
7343
7344 // Collect the contents of the main file. This is a container for anything
7345 // not explicitly split out into a separate file.
7346 emitter.collectOpsForFile(emitter.rootFile, list);
7347
7348 // Emit the separate files.
7349 for (const auto &it : emitter.files) {
7350 list.emplace_back("\n// ----- 8< ----- FILE \"" + it.first.str() +
7351 "\" ----- 8< -----\n\n");
7352 emitter.collectOpsForFile(it.second, list);
7353 }
7354
7355 // Emit the filelists.
7356 for (auto &it : emitter.fileLists) {
7357 std::string contents("\n// ----- 8< ----- FILE \"" + it.first().str() +
7358 "\" ----- 8< -----\n\n");
7359 for (auto &name : it.second)
7360 contents += name.str() + "\n";
7361 list.emplace_back(contents);
7362 }
7363
7364 llvm::formatted_raw_ostream rs(os);
7365 // Finally, emit all the ops we collected.
7366 // output file name is not known, it can be specified as command line
7367 // argument.
7368 emitter.emitOps(list, rs, StringAttr::get(module.getContext(), ""),
7369 /*parallelize=*/true);
7370 return failure(emitter.encounteredError);
7371}
7372
7373LogicalResult circt::exportVerilog(ModuleOp module, llvm::raw_ostream &os) {
7374 LoweringOptions options(module);
7375 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7376 module.walk(
7377 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7378 if (failed(failableParallelForEach(
7379 module->getContext(), modulesToPrepare,
7380 [&](auto op) { return prepareHWModule(op, options); })))
7381 return failure();
7382 return exportVerilogImpl(module, os);
7383}
7384
7385namespace {
7386
7387struct ExportVerilogPass
7388 : public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7389 ExportVerilogPass(raw_ostream &os) : os(os) {}
7390 void runOnOperation() override {
7391 // Prepare the ops in the module for emission.
7392 mlir::OpPassManager preparePM("builtin.module");
7393 preparePM.addPass(createLegalizeAnonEnums());
7394 auto &modulePM = preparePM.nestAny();
7395 modulePM.addPass(createPrepareForEmission());
7396 if (failed(runPipeline(preparePM, getOperation())))
7397 return signalPassFailure();
7398
7399 if (failed(exportVerilogImpl(getOperation(), os)))
7400 return signalPassFailure();
7401 }
7402
7403private:
7404 raw_ostream &os;
7405};
7406
7407struct ExportVerilogStreamOwnedPass : public ExportVerilogPass {
7408 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7409 : ExportVerilogPass{*os} {
7410 owned = std::move(os);
7411 }
7412
7413private:
7414 std::unique_ptr<llvm::raw_ostream> owned;
7415};
7416} // end anonymous namespace
7417
7418std::unique_ptr<mlir::Pass>
7419circt::createExportVerilogPass(std::unique_ptr<llvm::raw_ostream> os) {
7420 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7421}
7422
7423std::unique_ptr<mlir::Pass>
7424circt::createExportVerilogPass(llvm::raw_ostream &os) {
7425 return std::make_unique<ExportVerilogPass>(os);
7426}
7427
7428std::unique_ptr<mlir::Pass> circt::createExportVerilogPass() {
7429 return createExportVerilogPass(llvm::outs());
7430}
7431
7432//===----------------------------------------------------------------------===//
7433// Split Emitter
7434//===----------------------------------------------------------------------===//
7435
7436static std::unique_ptr<llvm::ToolOutputFile>
7437createOutputFile(StringRef fileName, StringRef dirname,
7438 SharedEmitterState &emitter) {
7439 // Determine the output path from the output directory and filename.
7440 SmallString<128> outputFilename(dirname);
7441 appendPossiblyAbsolutePath(outputFilename, fileName);
7442 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7443
7444 // Create the output directory if needed.
7445 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7446 if (error) {
7447 emitter.designOp.emitError("cannot create output directory \"")
7448 << outputDir << "\": " << error.message();
7449 emitter.encounteredError = true;
7450 return {};
7451 }
7452
7453 // Open the output file.
7454 std::string errorMessage;
7455 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7456 if (!output) {
7457 emitter.designOp.emitError(errorMessage);
7458 emitter.encounteredError = true;
7459 }
7460 return output;
7461}
7462
7463static void createSplitOutputFile(StringAttr fileName, FileInfo &file,
7464 StringRef dirname,
7465 SharedEmitterState &emitter) {
7466 auto output = createOutputFile(fileName, dirname, emitter);
7467 if (!output)
7468 return;
7469
7471 emitter.collectOpsForFile(file, list,
7473
7474 llvm::formatted_raw_ostream rs(output->os());
7475 // Emit the file, copying the global options into the individual module
7476 // state. Don't parallelize emission of the ops within this file - we
7477 // already parallelize per-file emission and we pay a string copy overhead
7478 // for parallelization.
7479 emitter.emitOps(list, rs,
7480 StringAttr::get(fileName.getContext(), output->getFilename()),
7481 /*parallelize=*/false);
7482 output->keep();
7483}
7484
7485static LogicalResult exportSplitVerilogImpl(ModuleOp module,
7486 StringRef dirname) {
7487 // Prepare the ops in the module for emission and legalize the names that will
7488 // end up in the output.
7489 LoweringOptions options(module);
7490 GlobalNameTable globalNames = legalizeGlobalNames(module, options);
7491
7492 SharedEmitterState emitter(module, options, std::move(globalNames));
7493 emitter.gatherFiles(true);
7494
7495 if (emitter.options.emitReplicatedOpsToHeader) {
7496 // Add a header to the file list.
7497 bool insertSuccess =
7498 emitter.files
7499 .insert({StringAttr::get(module.getContext(), circtHeader),
7500 FileInfo{/*ops*/ {},
7501 /*emitReplicatedOps*/ true,
7502 /*addToFilelist*/ true,
7503 /*isHeader*/ true}})
7504 .second;
7505 if (!insertSuccess) {
7506 module.emitError() << "tried to emit a heder to " << circtHeader
7507 << ", but the file is used as an output too.";
7508 return failure();
7509 }
7510 }
7511
7512 // Emit each file in parallel if context enables it.
7513 parallelForEach(module->getContext(), emitter.files.begin(),
7514 emitter.files.end(), [&](auto &it) {
7515 createSplitOutputFile(it.first, it.second, dirname,
7516 emitter);
7517 });
7518
7519 // Write the file list.
7520 SmallString<128> filelistPath(dirname);
7521 llvm::sys::path::append(filelistPath, "filelist.f");
7522
7523 std::string errorMessage;
7524 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7525 if (!output) {
7526 module->emitError(errorMessage);
7527 return failure();
7528 }
7529
7530 for (const auto &it : emitter.files) {
7531 if (it.second.addToFilelist)
7532 output->os() << it.first.str() << "\n";
7533 }
7534 output->keep();
7535
7536 // Emit the filelists.
7537 for (auto &it : emitter.fileLists) {
7538 auto output = createOutputFile(it.first(), dirname, emitter);
7539 if (!output)
7540 continue;
7541 for (auto &name : it.second)
7542 output->os() << name.str() << "\n";
7543 output->keep();
7544 }
7545
7546 return failure(emitter.encounteredError);
7547}
7548
7549LogicalResult circt::exportSplitVerilog(ModuleOp module, StringRef dirname) {
7550 LoweringOptions options(module);
7551 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7552 module.walk(
7553 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7554 if (failed(failableParallelForEach(
7555 module->getContext(), modulesToPrepare,
7556 [&](auto op) { return prepareHWModule(op, options); })))
7557 return failure();
7558
7559 return exportSplitVerilogImpl(module, dirname);
7560}
7561
7562namespace {
7563
7564struct ExportSplitVerilogPass
7565 : public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7566 ExportSplitVerilogPass(StringRef directory) {
7567 directoryName = directory.str();
7568 }
7569 void runOnOperation() override {
7570 // Prepare the ops in the module for emission.
7571 mlir::OpPassManager preparePM("builtin.module");
7572
7573 auto &modulePM = preparePM.nest<hw::HWModuleOp>();
7574 modulePM.addPass(createPrepareForEmission());
7575 if (failed(runPipeline(preparePM, getOperation())))
7576 return signalPassFailure();
7577
7578 if (failed(exportSplitVerilogImpl(getOperation(), directoryName)))
7579 return signalPassFailure();
7580 }
7581};
7582} // end anonymous namespace
7583
7584std::unique_ptr<mlir::Pass>
7585circt::createExportSplitVerilogPass(StringRef directory) {
7586 return std::make_unique<ExportSplitVerilogPass>(directory);
7587}
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
static bool hasSVAttributes(Operation *op)
Definition CombFolds.cpp:67
static void emitOperation(VerilogEmitterState &state, Operation *op)
static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os)
static void emitDim(Attribute width, raw_ostream &os, Location loc, ModuleEmitter &emitter, bool downTo)
Emit a single dimension.
static int compareLocs(Location lhs, Location rhs)
static bool isDuplicatableExpression(Operation *op)
static TypedAttr getInt32Attr(MLIRContext *ctx, uint32_t value)
StringRef getVerilogValueName(Value val)
Retrieve value's verilog name from IR.
static void sortLocationVector(TVector &vec)
static bool hasStructType(Type type)
Return true if type has a struct type as a subtype.
static StringRef getVerilogDeclWord(Operation *op, const ModuleEmitter &emitter)
Return the word (e.g.
static bool isOkToBitSelectFrom(Value v)
Most expressions are invalid to bit-select from in Verilog, but some things are ok.
static LogicalResult exportSplitVerilogImpl(ModuleOp module, StringRef dirname)
static int compareLocsImpl(mlir::NameLoc lhs, mlir::NameLoc rhs)
static void emitZeroWidthIndexingValue(PPS &os)
Emits a known-safe token that is legal when indexing into singleton arrays.
static bool checkDominanceOfUsers(Operation *op1, Operation *op2)
Return true if op1 dominates users of op2.
static void emitDims(ArrayRef< Attribute > dims, raw_ostream &os, Location loc, ModuleEmitter &emitter)
Emit a list of packed dimensions.
static bool isExpressionEmittedInlineIntoProceduralDeclaration(Operation *op, StmtEmitter &stmtEmitter)
Given an operation corresponding to a VerilogExpression, determine whether it is safe to emit inline ...
StringRef circtHeader
static StringRef getPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
BlockStatementCount
static void collectAndUniqueLocations(Location loc, SmallPtrSetImpl< Attribute > &locationSet)
Pull apart any fused locations into the location set, such that they are uniqued.
static Value isZeroExtension(Value value)
If the specified extension is a zero extended version of another value, return the shorter value,...
static void createSplitOutputFile(StringAttr fileName, FileInfo &file, StringRef dirname, SharedEmitterState &emitter)
static StringRef getInputPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static StringRef getTwoStateIntegerAtomType(size_t width)
Return a 2-state integer atom type name if the width matches.
static TypedAttr getIntAttr(MLIRContext *ctx, Type t, const APInt &value)
static BlockStatementCount countStatements(Block &block)
Compute how many statements are within this block, for begin/end markers.
static Type stripUnpackedTypes(Type type)
Given a set of known nested types (those supported by this pass), strip off leading unpacked types.
FailureOr< int > dispatchCompareLocations(Location lhs, Location rhs)
static bool haveMatchingDims(Type a, Type b, Location loc, llvm::function_ref< mlir::InFlightDiagnostic(Location)> errorHandler)
True iff 'a' and 'b' have the same wire dims.
static void getTypeDims(SmallVectorImpl< Attribute > &dims, Type type, Location loc, llvm::function_ref< mlir::InFlightDiagnostic(Location)> errorHandler)
Push this type's dimension into a vector.
static bool isExpressionUnableToInline(Operation *op, const LoweringOptions &options)
Return true if we are unable to ever inline the specified operation.
void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op, bool isAutomatic=false, bool emitAsTwoStateType=false)
static AssignTy getSingleAssignAndCheckUsers(Operation *op)
static bool hasLeadingUnpackedType(Type type)
Return true if the type has a leading unpacked type.
static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, SmallVectorImpl< Attribute > &dims, bool implicitIntType, bool singleBitDefaultType, ModuleEmitter &emitter, Type optionalAliasType={}, bool emitAsTwoStateType=false)
Output the basic type that consists of packed and primitive types.
static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak)
Emit SystemVerilog attributes.
static bool isDuplicatableNullaryExpression(Operation *op)
Return true for nullary operations that are better emitted multiple times as inline expression (when ...
static IfOp findNestedElseIf(Block *elseBlock)
Find a nested IfOp in an else block that can be printed as else if instead of nesting it into a new b...
StringRef circtHeaderInclude
static ValueRange getNonOverlappingConcatSubrange(Value value)
For a value concat(..., delay(const(true), 1, 0)), return ....
static std::unique_ptr< Context > context
static StringRef legalizeName(StringRef name, llvm::StringMap< size_t > &nextGeneratedNameIDs)
Legalize the given name such that it only consists of valid identifier characters in Verilog and does...
#define isdigit(x)
Definition FIRLexer.cpp:26
static void printParamValue(OpAsmPrinter &p, Operation *, Attribute value, Type resultType)
Definition HWOps.cpp:505
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
Definition HWOps.cpp:1438
RewritePatternSet pattern
static InstancePath empty
void emit(emit::FragmentOp op)
FileEmitter(VerilogEmitterState &state)
void emit(emit::FileOp op)
void emitOp(emit::RefOp op)
LocationEmitter(LoweringOptions::LocationInfoStyle style, Location loc)
void emitLocationSetInfo(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
LocationEmitter(LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Operation * > &ops)
Track the output verilog line,column number information for every op.
void setStream(llvm::formatted_raw_ostream &f)
Set the output stream.
void updateIRWithLoc(unsigned lineOffset, StringAttr fileName, MLIRContext *context)
Called after the verilog has been exported and the corresponding locations are recorded in the map.
This class wraps an operation or a fixed string that should be emitted.
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.
Signals that an operation's regions are procedural.
This stores lookup tables to make manipulating and working with the IR more efficient.
Definition HWSymCache.h:28
void freeze()
Mark the cache as frozen, which allows it to be shared across threads.
Definition HWSymCache.h:76
void addDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name, mlir::Operation *op, size_t port=invalidPort)
Definition HWSymCache.h:44
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
This helps visit TypeOp nodes.
Definition HWVisitors.h:89
This helps visit TypeOp nodes.
Definition HWVisitors.h:25
ResultType dispatchTypeOpVisitor(Operation *op, ExtraArgs... args)
Definition HWVisitors.h:27
ResultType visitUnhandledTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any combinational operations that are not handled by the concrete visitor...
Definition HWVisitors.h:57
ResultType visitInvalidTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any non-expression operations.
Definition HWVisitors.h:50
Note: Callable class must implement a callable with signature: void (Data)
Wrap the TokenStream with a helper for CallbackTokens, to record the print events on the stream.
auto scopedBox(T &&t, Callable &&c, Token close=EndToken())
Open a box, invoke the lambda, and close it after.
Definition sv.py:70
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.
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.
StringRef getSymOpName(Operation *symOp)
Return the verilog name of the operations that can define a symbol.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
Definition EmitOps.h:30
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
Definition HWOps.cpp:59
StringRef getVerilogModuleName(Operation *module)
Definition HWOps.h:56
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
Definition HWOps.cpp:551
mlir::Type getCanonicalType(mlir::Type type)
Definition HWTypes.cpp:49
void info(Twine message)
Definition LSPUtils.cpp:20
PP
Send one of these to TokenStream to add the corresponding token.
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
char getLetter(CasePatternBit bit)
Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
Definition SVOps.cpp:875
circt::hw::InOutType InOutType
Definition SVTypes.h:25
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createExportSplitVerilogPass(llvm::StringRef directory="./")
mlir::LogicalResult exportVerilog(mlir::ModuleOp module, llvm::raw_ostream &os)
Export a module containing HW, and SV dialect code.
mlir::LogicalResult exportSplitVerilog(mlir::ModuleOp module, llvm::StringRef dirname)
Export a module containing HW, and SV dialect code, as one file per SV module.
const char * getCirctVersionComment()
std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef filename, StringRef dirname, function_ref< InFlightDiagnostic()> emitError)
Creates an output file with the given filename in the specified directory.
Definition Path.cpp:55
std::unique_ptr< mlir::Pass > createExportVerilogPass()
void appendPossiblyAbsolutePath(llvm::SmallVectorImpl< char > &base, const llvm::Twine &suffix)
Append a path to an existing path, replacing it if the other path is absolute.
Definition Path.cpp:26
Definition comb.py:1
Definition emit.py:1
Definition hw.py:1
Definition sv.py:1
llvm::raw_string_ostream & os
void emitLocationInfo(Location loc)
Return the location information in the specified style.
Impl(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
void emitLocationInfo(FileLineColLoc loc)
void emitLocationSetInfoImpl(const SmallPtrSetImpl< Attribute > &locationSet)
Emit the location information of locationSet to sstr.
void emitLocationInfo(mlir::NameLoc loc)
LoweringOptions::LocationInfoStyle style
void emitLocationInfo(mlir::CallSiteLoc loc)
void printFileLineColSetInfo(llvm::SmallVector< FileLineColLoc, 8 > locVector)
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.
This class keeps track of global names at the module/interface level.
Information to control the emission of a single operation into a file.
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.
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.
bool omitVersionComment
If true, do not emit a version comment at the top of each verilog file.
LocationInfoStyle
This option controls emitted location information style.
bool disallowMuxInlining
If true, every mux expression is spilled to a wire.
bool caseInsensitiveKeywords
If true, then unique names that collide with keywords case insensitively.
bool emitReplicatedOpsToHeader
If true, replicated ops are emitted to a header file.
bool allowExprInEventControl
If true, expressions are allowed in the sensitivity list of always statements, otherwise they are for...
This holds a decoded list of input/inout and output ports for a module or instance.
PortInfo & at(size_t idx)
mlir::Type type
Definition HWTypes.h:32
This holds the name, type, direction of a module's ports.
StringRef getVerilogName() const
InnerSymAttr getSym() const
Struct defining a field. Used in structs.
Definition HWTypes.h:93
Buffer tokens for clients that need to adjust things.
SmallVectorImpl< Token > BufferVec
String wrapper to indicate string has external storage.
String wrapper to indicate string needs to be saved.