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