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