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