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