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