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 ps.scopedBox(PP::bbox2, [&]() {
5413 for (auto &caseInfo : op.getCases()) {
5414 startStatement();
5415 auto &pattern = caseInfo.pattern;
5416
5417 llvm::TypeSwitch<CasePattern *>(pattern.get())
5418 .Case<CaseBitPattern>([&](auto bitPattern) {
5419 // TODO: We could emit in hex if/when the size is a multiple of
5420 // 4 and there are no x's crossing nibble boundaries.
5421 ps.invokeWithStringOS([&](auto &os) {
5422 os << bitPattern->getWidth() << "'b";
5423 for (size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5424 os << getLetter(bitPattern->getBit(e - bit - 1));
5425 });
5426 })
5427 .Case<CaseEnumPattern>([&](auto enumPattern) {
5428 ps << PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5429 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5430 })
5431 .Case<CaseDefaultPattern>([&](auto) { ps << "default"; })
5432 .Default([&](auto) { assert(false && "unhandled case pattern"); });
5433
5434 ps << ":";
5435 emitBlockAsStatement(caseInfo.block, emptyOps);
5436 }
5437 });
5438
5439 startStatement();
5440 ps << "endcase";
5441 ps.addCallback({op, false});
5442 emitLocationInfoAndNewLine(ops);
5443 return success();
5444}
5445
5446LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5447 bool doNotPrint = op.getDoNotPrint();
5448 if (doNotPrint && !state.options.emitBindComments)
5449 return success();
5450
5451 // Emit SV attributes if the op is not emitted as a bind statement.
5452 if (!doNotPrint)
5453 emitSVAttributes(op);
5454 startStatement();
5455 ps.addCallback({op, true});
5456 if (doNotPrint) {
5457 ps << PP::ibox2
5458 << "/* This instance is elsewhere emitted as a bind statement."
5459 << PP::newline;
5460 if (hasSVAttributes(op))
5461 op->emitWarning() << "is emitted as a bind statement but has SV "
5462 "attributes. The attributes will not be emitted.";
5463 }
5464
5465 SmallPtrSet<Operation *, 8> ops;
5466 ops.insert(op);
5467
5468 // Use the specified name or the symbol name as appropriate.
5469 auto *moduleOp =
5470 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5471 assert(moduleOp && "Invalid IR");
5472 ps << PPExtString(getVerilogModuleName(moduleOp));
5473
5474 // If this is a parameterized module, then emit the parameters.
5475 if (!op.getParameters().empty()) {
5476 // All the parameters may be defaulted -- don't print out an empty list if
5477 // so.
5478 bool printed = false;
5479 for (auto params :
5480 llvm::zip(op.getParameters(),
5481 moduleOp->getAttrOfType<ArrayAttr>("parameters"))) {
5482 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5483 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5484 // Ignore values that line up with their default.
5485 if (param.getValue() == modParam.getValue())
5486 continue;
5487
5488 // Handle # if this is the first parameter we're printing.
5489 if (!printed) {
5490 ps << " #(" << PP::bbox2 << PP::newline;
5491 printed = true;
5492 } else {
5493 ps << "," << PP::newline;
5494 }
5495 ps << ".";
5496 ps << PPExtString(
5497 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5498 ps << "(";
5499 ps.invokeWithStringOS([&](auto &os) {
5500 emitter.printParamValue(param.getValue(), os, [&]() {
5501 return op->emitOpError("invalid instance parameter '")
5502 << param.getName().getValue() << "' value";
5503 });
5504 });
5505 ps << ")";
5506 }
5507 if (printed) {
5508 ps << PP::end << PP::newline << ")";
5509 }
5510 }
5511
5512 ps << PP::nbsp << PPExtString(getSymOpName(op));
5513
5514 ModulePortInfo modPortInfo(cast<PortList>(moduleOp).getPortList());
5515 SmallVector<Value> instPortValues(modPortInfo.size());
5516 op.getValues(instPortValues, modPortInfo);
5517 emitInstancePortList(op, modPortInfo, instPortValues);
5518
5519 ps.addCallback({op, false});
5520 emitLocationInfoAndNewLine(ops);
5521 if (doNotPrint) {
5522 ps << PP::end;
5523 startStatement();
5524 ps << "*/";
5525 setPendingNewline();
5526 }
5527 return success();
5528}
5529
5530LogicalResult StmtEmitter::visitStmt(InstanceChoiceOp op) {
5531 startStatement();
5532 Operation *choiceMacroDeclOp = state.symbolCache.getDefinition(
5533 op->getAttrOfType<FlatSymbolRefAttr>("hw.choiceTarget"));
5534
5535 ps << "`" << PPExtString(getSymOpName(choiceMacroDeclOp)) << PP::nbsp
5536 << PPExtString(getSymOpName(op));
5537
5538 Operation *defaultModuleOp =
5539 state.symbolCache.getDefinition(op.getDefaultModuleNameAttr());
5540 ModulePortInfo modPortInfo(cast<PortList>(defaultModuleOp).getPortList());
5541 SmallVector<Value> instPortValues(modPortInfo.size());
5542 op.getValues(instPortValues, modPortInfo);
5543 emitInstancePortList(op, modPortInfo, instPortValues);
5544
5545 SmallPtrSet<Operation *, 8> ops;
5546 ops.insert(op);
5547 ps.addCallback({op, false});
5548 emitLocationInfoAndNewLine(ops);
5549
5550 return success();
5551}
5552
5553void StmtEmitter::emitInstancePortList(Operation *op,
5554 ModulePortInfo &modPortInfo,
5555 ArrayRef<Value> instPortValues) {
5556 SmallPtrSet<Operation *, 8> ops;
5557 ops.insert(op);
5558
5559 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5560 ModulePortInfo containingPortList(containingModule.getPortList());
5561
5562 ps << " (";
5563
5564 // Get the max port name length so we can align the '('.
5565 size_t maxNameLength = 0;
5566 for (auto &elt : modPortInfo) {
5567 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5568 }
5569
5570 auto getWireForValue = [&](Value result) {
5571 return result.getUsers().begin()->getOperand(0);
5572 };
5573
5574 // Emit the argument and result ports.
5575 bool isFirst = true; // True until we print a port.
5576 bool isZeroWidth = false;
5577
5578 for (size_t portNum = 0, portEnd = modPortInfo.size(); portNum < portEnd;
5579 ++portNum) {
5580 auto &modPort = modPortInfo.at(portNum);
5581 isZeroWidth = isZeroBitType(modPort.type);
5582 Value portVal = instPortValues[portNum];
5583
5584 // Decide if we should print a comma. We can't do this if we're the first
5585 // port or if all the subsequent ports are zero width.
5586 if (!isFirst) {
5587 bool shouldPrintComma = true;
5588 if (isZeroWidth) {
5589 shouldPrintComma = false;
5590 for (size_t i = portNum + 1, e = modPortInfo.size(); i != e; ++i)
5591 if (!isZeroBitType(modPortInfo.at(i).type)) {
5592 shouldPrintComma = true;
5593 break;
5594 }
5595 }
5596
5597 if (shouldPrintComma)
5598 ps << ",";
5599 }
5600 emitLocationInfoAndNewLine(ops);
5601
5602 // Emit the port's name.
5603 startStatement();
5604 if (!isZeroWidth) {
5605 // If this is a real port we're printing, then it isn't the first one. Any
5606 // subsequent ones will need a comma.
5607 isFirst = false;
5608 ps << " ";
5609 } else {
5610 // We comment out zero width ports, so their presence and initializer
5611 // expressions are still emitted textually.
5612 ps << "//";
5613 }
5614
5615 ps.scopedBox(isZeroWidth ? PP::neverbox : PP::ibox2, [&]() {
5616 auto modPortName = modPort.getVerilogName();
5617 ps << "." << PPExtString(modPortName);
5618 ps.spaces(maxNameLength - modPortName.size() + 1);
5619 ps << "(";
5620 ps.scopedBox(PP::ibox0, [&]() {
5621 // Emit the value as an expression.
5622 ops.clear();
5623
5624 // Output ports that are not connected to single use output ports were
5625 // lowered to wire.
5626 OutputOp output;
5627 if (!modPort.isOutput()) {
5628 if (isZeroWidth &&
5629 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5630 ps << "/* Zero width */";
5631 else
5632 emitExpression(portVal, ops, LowestPrecedence);
5633 } else if (portVal.use_empty()) {
5634 ps << "/* unused */";
5635 } else if (portVal.hasOneUse() &&
5636 (output = dyn_cast_or_null<OutputOp>(
5637 portVal.getUses().begin()->getOwner()))) {
5638 // If this is directly using the output port of the containing module,
5639 // just specify that directly so we avoid a temporary wire.
5640 // Keep this synchronized with countStatements() and
5641 // visitStmt(OutputOp).
5642 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5643 ps << PPExtString(
5644 containingPortList.atOutput(outputPortNo).getVerilogName());
5645 } else {
5646 portVal = getWireForValue(portVal);
5647 emitExpression(portVal, ops);
5648 }
5649 ps << ")";
5650 });
5651 });
5652 }
5653 if (!isFirst || isZeroWidth) {
5654 emitLocationInfoAndNewLine(ops);
5655 ops.clear();
5656 startStatement();
5657 }
5658 ps << ");";
5659}
5660
5661// This may be called in the top-level, not just in an hw.module. Thus we can't
5662// use the name map to find expression names for arguments to the instance, nor
5663// do we need to emit subexpressions. Prepare pass, which has run for all
5664// modules prior to this, has ensured that all arguments are bound to wires,
5665// regs, or ports, with legalized names, so we can lookup up the names through
5666// the IR.
5667LogicalResult StmtEmitter::visitSV(BindOp op) {
5668 emitter.emitBind(op);
5669 assert(state.pendingNewline);
5670 return success();
5671}
5672
5673LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5674 emitComment(op.getCommentAttr());
5675 // Emit SV attributes.
5676 emitSVAttributes(op);
5677 // TODO: source info!
5678 startStatement();
5679 ps.addCallback({op, true});
5680 ps << "interface " << PPExtString(getSymOpName(op)) << ";";
5681 setPendingNewline();
5682 // FIXME: Don't emit the body of this as general statements, they aren't!
5683 emitStatementBlock(*op.getBodyBlock());
5684 startStatement();
5685 ps << "endinterface" << PP::newline;
5686 ps.addCallback({op, false});
5687 setPendingNewline();
5688 return success();
5689}
5690
5691LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5692 // Emit SV attributes.
5693 emitSVAttributes(op);
5694 startStatement();
5695 ps.addCallback({op, true});
5696 if (isZeroBitType(op.getType()))
5697 ps << PP::neverbox << "// ";
5698 ps.invokeWithStringOS([&](auto &os) {
5699 emitter.printPackedType(stripUnpackedTypes(op.getType()), os, op->getLoc(),
5700 Type(), false);
5701 });
5702 ps << PP::nbsp << PPExtString(getSymOpName(op));
5703 ps.invokeWithStringOS(
5704 [&](auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5705 ps << ";";
5706 if (isZeroBitType(op.getType()))
5707 ps << PP::end; // Close never-break group.
5708 ps.addCallback({op, false});
5709 setPendingNewline();
5710 return success();
5711}
5712
5713LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5714 startStatement();
5715 ps.addCallback({op, true});
5716 ps << "modport " << PPExtString(getSymOpName(op)) << "(";
5717
5718 // TODO: revisit, better breaks/grouping.
5719 llvm::interleaveComma(op.getPorts(), ps, [&](const Attribute &portAttr) {
5720 auto port = cast<ModportStructAttr>(portAttr);
5721 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) << " ";
5722 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5723 ps << PPExtString(getSymOpName(signalDecl));
5724 });
5725
5726 ps << ");";
5727 ps.addCallback({op, false});
5728 setPendingNewline();
5729 return success();
5730}
5731
5732LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5733 startStatement();
5734 ps.addCallback({op, true});
5735 SmallPtrSet<Operation *, 8> emitted;
5736 // TODO: emit like emitAssignLike does, maybe refactor.
5737 ps << "assign ";
5738 emitExpression(op.getIface(), emitted);
5739 ps << "." << PPExtString(op.getSignalName()) << " = ";
5740 emitExpression(op.getRhs(), emitted);
5741 ps << ";";
5742 ps.addCallback({op, false});
5743 setPendingNewline();
5744 return success();
5745}
5746
5747LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5748 startStatement();
5749 ps << "`" << op.getMacroIdentifier();
5750 setPendingNewline();
5751 return success();
5752}
5753
5754LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5755 auto decl = op.getReferencedMacro(&state.symbolCache);
5756 // TODO: source info!
5757 startStatement();
5758 ps.addCallback({op, true});
5759 ps << "`define " << PPExtString(getSymOpName(decl));
5760 if (decl.getArgs()) {
5761 ps << "(";
5762 llvm::interleaveComma(*decl.getArgs(), ps, [&](const Attribute &name) {
5763 ps << cast<StringAttr>(name);
5764 });
5765 ps << ")";
5766 }
5767 if (!op.getFormatString().empty()) {
5768 ps << " ";
5769 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5770 op.getSymbols());
5771 }
5772 ps.addCallback({op, false});
5773 setPendingNewline();
5774 return success();
5775}
5776
5777void StmtEmitter::emitStatement(Operation *op) {
5778 // Expressions may either be ignored or emitted as an expression statements.
5779 if (isVerilogExpression(op))
5780 return;
5781
5782 // Ignore LTL expressions as they are emitted as part of verification
5783 // statements. Ignore debug ops as they are emitted as part of debug info.
5784 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5785 return;
5786
5787 // Handle HW statements, SV statements.
5788 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5789 succeeded(dispatchVerifVisitor(op)))
5790 return;
5791
5792 emitOpError(op, "emission to Verilog not supported");
5793 emitPendingNewlineIfNeeded();
5794 ps << "unknown MLIR operation " << PPExtString(op->getName().getStringRef());
5795 setPendingNewline();
5796}
5797
5798/// Given an operation corresponding to a VerilogExpression, determine whether
5799/// it is safe to emit inline into a 'localparam' or 'automatic logic' varaible
5800/// initializer in a procedural region.
5801///
5802/// We can't emit exprs inline when they refer to something else that can't be
5803/// emitted inline, when they're in a general #ifdef region,
5804static bool
5806 StmtEmitter &stmtEmitter) {
5807 if (!isVerilogExpression(op))
5808 return false;
5809
5810 // If the expression exists in an #ifdef region, then bail. Emitting it
5811 // inline would cause it to be executed unconditionally, because the
5812 // declarations are outside the #ifdef.
5813 if (isa<IfDefProceduralOp>(op->getParentOp()))
5814 return false;
5815
5816 // This expression tree can be emitted into the initializer if all leaf
5817 // references are safe to refer to from here. They are only safe if they are
5818 // defined in an enclosing scope (guaranteed to already be live by now) or if
5819 // they are defined in this block and already emitted to an inline automatic
5820 // logic variable.
5821 SmallVector<Value, 8> exprsToScan(op->getOperands());
5822
5823 // This loop is guaranteed to terminate because we're only scanning up
5824 // single-use expressions and other things that 'isExpressionEmittedInline'
5825 // returns success for. Cycles won't get in here.
5826 while (!exprsToScan.empty()) {
5827 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5828 if (!expr)
5829 continue; // Ports are always safe to reference.
5830
5831 // If this is an inout op, check that its inout op has no blocking
5832 // assignment. A register or logic might be mutated by a blocking assignment
5833 // so it is not always safe to inline.
5834 if (auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5835 auto *defOp = readInout.getOperand().getDefiningOp();
5836
5837 // If it is a read from an inout port, it's unsafe to inline in general.
5838 if (!defOp)
5839 return false;
5840
5841 // If the operand is a wire, it's OK to inline the read.
5842 if (isa<sv::WireOp>(defOp))
5843 continue;
5844
5845 // Reject struct_field_inout/array_index_inout for now because it's
5846 // necessary to consider aliasing inout operations.
5847 if (!isa<RegOp, LogicOp>(defOp))
5848 return false;
5849
5850 // It's safe to inline if all users are read op, passign or assign.
5851 // If the op is a logic op whose single assignment is inlined into
5852 // declaration, we can inline the read.
5853 if (isa<LogicOp>(defOp) &&
5854 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5855 continue;
5856
5857 // Check that it's safe for all users to be inlined.
5858 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5859 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5860 }))
5861 continue;
5862 return false;
5863 }
5864
5865 // If this is an internal node in the expression tree, process its operands.
5866 if (isExpressionEmittedInline(expr, stmtEmitter.state.options)) {
5867 exprsToScan.append(expr->getOperands().begin(),
5868 expr->getOperands().end());
5869 continue;
5870 }
5871
5872 // Otherwise, this isn't an inlinable expression. If it is defined outside
5873 // this block, then it is live-in.
5874 if (expr->getBlock() != op->getBlock())
5875 continue;
5876
5877 // Otherwise, if it is defined in this block then it is only ok to reference
5878 // if it has already been emitted into an automatic logic.
5879 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5880 return false;
5881 }
5882
5883 return true;
5884}
5885
5886template <class AssignTy>
5887static AssignTy getSingleAssignAndCheckUsers(Operation *op) {
5888 AssignTy singleAssign;
5889 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5890 if (hasSVAttributes(user))
5891 return false;
5892
5893 if (auto assign = dyn_cast<AssignTy>(user)) {
5894 if (singleAssign)
5895 return false;
5896 singleAssign = assign;
5897 return true;
5898 }
5899
5900 return isa<ReadInOutOp>(user);
5901 }))
5902 return singleAssign;
5903 return {};
5904}
5905
5906/// Return true if `op1` dominates users of `op2`.
5907static bool checkDominanceOfUsers(Operation *op1, Operation *op2) {
5908 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5909 /// TODO: Use MLIR DominanceInfo.
5910
5911 // If the op1 and op2 are in different blocks, conservatively return false.
5912 if (op1->getBlock() != user->getBlock())
5913 return false;
5914
5915 if (op1 == user)
5916 return true;
5917
5918 return op1->isBeforeInBlock(user);
5919 });
5920}
5921
5922LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5923 emitSVAttributes(op);
5924 auto value = op->getResult(0);
5925 SmallPtrSet<Operation *, 8> opsForLocation;
5926 opsForLocation.insert(op);
5927 startStatement();
5928 ps.addCallback({op, true});
5929
5930 // Emit the leading word, like 'wire', 'reg' or 'logic'.
5931 auto type = value.getType();
5932 auto word = getVerilogDeclWord(op, emitter);
5933 auto isZeroBit = isZeroBitType(type);
5934
5935 // LocalParams always need the bitwidth, otherwise they are considered to have
5936 // an unknown size.
5937 bool singleBitDefaultType = !isa<LocalParamOp>(op);
5938
5939 ps.scopedBox(isZeroBit ? PP::neverbox : PP::ibox2, [&]() {
5940 unsigned targetColumn = 0;
5941 unsigned column = 0;
5942
5943 // Emit the declaration keyword.
5944 if (maxDeclNameWidth > 0)
5945 targetColumn += maxDeclNameWidth + 1;
5946
5947 if (isZeroBit) {
5948 ps << "// Zero width: " << PPExtString(word) << PP::space;
5949 } else if (!word.empty()) {
5950 ps << PPExtString(word);
5951 column += word.size();
5952 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
5953 ps.spaces(numSpaces);
5954 column += numSpaces;
5955 }
5956
5957 SmallString<8> typeString;
5958 // Convert the port's type to a string and measure it.
5959 {
5960 llvm::raw_svector_ostream stringStream(typeString);
5961 emitter.printPackedType(stripUnpackedTypes(type), stringStream,
5962 op->getLoc(), /*optionalAliasType=*/{},
5963 /*implicitIntType=*/true, singleBitDefaultType);
5964 }
5965 // Emit the type.
5966 if (maxTypeWidth > 0)
5967 targetColumn += maxTypeWidth + 1;
5968 unsigned numSpaces = 0;
5969 if (!typeString.empty()) {
5970 ps << typeString;
5971 column += typeString.size();
5972 ++numSpaces;
5973 }
5974 if (targetColumn > column)
5975 numSpaces = targetColumn - column;
5976 ps.spaces(numSpaces);
5977 column += numSpaces;
5978
5979 // Emit the name.
5980 ps << PPExtString(getSymOpName(op));
5981
5982 // Print out any array subscripts or other post-name stuff.
5983 ps.invokeWithStringOS(
5984 [&](auto &os) { emitter.printUnpackedTypePostfix(type, os); });
5985
5986 // Print debug info.
5987 if (state.options.printDebugInfo) {
5988 if (auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
5989 auto innerSym = innerSymOp.getInnerSymAttr();
5990 if (innerSym && !innerSym.empty()) {
5991 ps << " /* ";
5992 ps.invokeWithStringOS([&](auto &os) { os << innerSym; });
5993 ps << " */";
5994 }
5995 }
5996 }
5997
5998 if (auto localparam = dyn_cast<LocalParamOp>(op)) {
5999 ps << PP::space << "=" << PP::space;
6000 ps.invokeWithStringOS([&](auto &os) {
6001 emitter.printParamValue(localparam.getValue(), os, [&]() {
6002 return op->emitOpError("invalid localparam value");
6003 });
6004 });
6005 }
6006
6007 if (auto regOp = dyn_cast<RegOp>(op)) {
6008 if (auto initValue = regOp.getInit()) {
6009 ps << PP::space << "=" << PP::space;
6010 ps.scopedBox(PP::ibox0, [&]() {
6011 emitExpression(initValue, opsForLocation, LowestPrecedence,
6012 /*isAssignmentLikeContext=*/true);
6013 });
6014 }
6015 }
6016
6017 // Try inlining an assignment into declarations.
6018 // FIXME: Unpacked array is not inlined since several tools doesn't support
6019 // that syntax. See Issue 6363.
6020 if (isa<sv::WireOp>(op) &&
6021 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
6022 !hasLeadingUnpackedType(op->getResult(0).getType())) {
6023 // Get a single assignments if any.
6024 if (auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6025 auto *source = singleAssign.getSrc().getDefiningOp();
6026 // Check that the source value is OK to inline in the current emission
6027 // point. A port or constant is fine, otherwise check that the assign is
6028 // next to the operation.
6029 if (!source || isa<ConstantOp>(source) ||
6030 op->getNextNode() == singleAssign) {
6031 ps << PP::space << "=" << PP::space;
6032 ps.scopedBox(PP::ibox0, [&]() {
6033 emitExpression(singleAssign.getSrc(), opsForLocation,
6034 LowestPrecedence,
6035 /*isAssignmentLikeContext=*/true);
6036 });
6037 emitter.assignsInlined.insert(singleAssign);
6038 }
6039 }
6040 }
6041
6042 // Try inlining a blocking assignment to logic op declaration.
6043 // FIXME: Unpacked array is not inlined since several tools doesn't support
6044 // that syntax. See Issue 6363.
6045 if (isa<LogicOp>(op) && op->getParentOp()->hasTrait<ProceduralRegion>() &&
6046 !hasLeadingUnpackedType(op->getResult(0).getType())) {
6047 // Get a single assignment which might be possible to inline.
6048 if (auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6049 // It is necessary for the assignment to dominate users of the op.
6050 if (checkDominanceOfUsers(singleAssign, op)) {
6051 auto *source = singleAssign.getSrc().getDefiningOp();
6052 // A port or constant can be inlined at everywhere. Otherwise, check
6053 // the validity by
6054 // `isExpressionEmittedInlineIntoProceduralDeclaration`.
6055 if (!source || isa<ConstantOp>(source) ||
6057 *this)) {
6058 ps << PP::space << "=" << PP::space;
6059 ps.scopedBox(PP::ibox0, [&]() {
6060 emitExpression(singleAssign.getSrc(), opsForLocation,
6061 LowestPrecedence,
6062 /*isAssignmentLikeContext=*/true);
6063 });
6064 // Remember that the assignment and logic op are emitted into decl.
6065 emitter.assignsInlined.insert(singleAssign);
6066 emitter.expressionsEmittedIntoDecl.insert(op);
6067 }
6068 }
6069 }
6070 }
6071 ps << ";";
6072 });
6073 ps.addCallback({op, false});
6074 emitLocationInfoAndNewLine(opsForLocation);
6075 return success();
6076}
6077
6078void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6079 // In the first pass, we fill in the symbol table, calculate the max width
6080 // of the declaration words and the max type width.
6081 NameCollector collector(emitter);
6082 collector.collectNames(block);
6083
6084 // Record maxDeclNameWidth and maxTypeWidth in the current scope.
6085 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6086 maxTypeWidth = collector.getMaxTypeWidth();
6087}
6088
6089void StmtEmitter::emitStatementBlock(Block &body) {
6090 ps.scopedBox(PP::bbox2, [&]() {
6091 // Ensure decl alignment values are preserved after the block is emitted.
6092 // These values were computed for and from all declarations in the current
6093 // block (before/after this nested block), so be sure they're restored
6094 // and not overwritten by the declaration alignment within the block.
6095 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6096 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6097
6098 // Build up the symbol table for all of the values that need names in the
6099 // module. #ifdef's in procedural regions are special because local
6100 // variables are all emitted at the top of their enclosing blocks.
6101 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6102 collectNamesAndCalculateDeclarationWidths(body);
6103
6104 // Emit the body.
6105 for (auto &op : body) {
6106 emitStatement(&op);
6107 }
6108 });
6109}
6110// NOLINTEND(misc-no-recursion)
6111
6112void ModuleEmitter::emitStatement(Operation *op) {
6113 StmtEmitter(*this, state.options).emitStatement(op);
6114}
6115
6116/// Emit SystemVerilog attributes attached to the expression op as dialect
6117/// attributes.
6118void ModuleEmitter::emitSVAttributes(Operation *op) {
6119 // SystemVerilog 2017 Section 5.12.
6120 auto svAttrs = getSVAttributes(op);
6121 if (!svAttrs)
6122 return;
6123
6124 startStatement(); // For attributes.
6125 emitSVAttributesImpl(ps, svAttrs, /*mayBreak=*/true);
6126 setPendingNewline();
6127}
6128
6129//===----------------------------------------------------------------------===//
6130// Module Driver
6131//===----------------------------------------------------------------------===//
6132
6133void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6134 auto verilogName = module.getVerilogModuleNameAttr();
6135 startStatement();
6136 ps << "// external generated module " << PPExtString(verilogName.getValue())
6137 << PP::newline;
6138 setPendingNewline();
6139}
6140
6141// This may be called in the top-level, not just in an hw.module. Thus we can't
6142// use the name map to find expression names for arguments to the instance, nor
6143// do we need to emit subexpressions. Prepare pass, which has run for all
6144// modules prior to this, has ensured that all arguments are bound to wires,
6145// regs, or ports, with legalized names, so we can lookup up the names through
6146// the IR.
6147void ModuleEmitter::emitBind(BindOp op) {
6148 if (hasSVAttributes(op))
6149 emitError(op, "SV attributes emission is unimplemented for the op");
6150 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6151
6152 HWModuleOp parentMod = inst->getParentOfType<hw::HWModuleOp>();
6153 ModulePortInfo parentPortList(parentMod.getPortList());
6154 auto parentVerilogName = getVerilogModuleNameAttr(parentMod);
6155
6156 Operation *childMod =
6157 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6158 auto childVerilogName = getVerilogModuleNameAttr(childMod);
6159
6160 startStatement();
6161 ps.addCallback({op, true});
6162 ps << "bind " << PPExtString(parentVerilogName.getValue()) << PP::nbsp
6163 << PPExtString(childVerilogName.getValue()) << PP::nbsp
6164 << PPExtString(getSymOpName(inst)) << " (";
6165 bool isFirst = true; // True until we print a port.
6166 ps.scopedBox(PP::bbox2, [&]() {
6167 auto parentPortInfo = parentMod.getPortList();
6168 ModulePortInfo childPortInfo(cast<PortList>(childMod).getPortList());
6169
6170 // Get the max port name length so we can align the '('.
6171 size_t maxNameLength = 0;
6172 for (auto &elt : childPortInfo) {
6173 auto portName = elt.getVerilogName();
6174 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6175 maxNameLength = std::max(maxNameLength, elt.getName().size());
6176 }
6177
6178 SmallVector<Value> instPortValues(childPortInfo.size());
6179 inst.getValues(instPortValues, childPortInfo);
6180 // Emit the argument and result ports.
6181 for (auto [idx, elt] : llvm::enumerate(childPortInfo)) {
6182 // Figure out which value we are emitting.
6183 Value portVal = instPortValues[idx];
6184 bool isZeroWidth = isZeroBitType(elt.type);
6185
6186 // Decide if we should print a comma. We can't do this if we're the
6187 // first port or if all the subsequent ports are zero width.
6188 if (!isFirst) {
6189 bool shouldPrintComma = true;
6190 if (isZeroWidth) {
6191 shouldPrintComma = false;
6192 for (size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6193 if (!isZeroBitType(childPortInfo.at(i).type)) {
6194 shouldPrintComma = true;
6195 break;
6196 }
6197 }
6198
6199 if (shouldPrintComma)
6200 ps << ",";
6201 }
6202 ps << PP::newline;
6203
6204 // Emit the port's name.
6205 if (!isZeroWidth) {
6206 // If this is a real port we're printing, then it isn't the first
6207 // one. Any subsequent ones will need a comma.
6208 isFirst = false;
6209 } else {
6210 // We comment out zero width ports, so their presence and
6211 // initializer expressions are still emitted textually.
6212 ps << PP::neverbox << "//";
6213 }
6214
6215 ps << "." << PPExtString(elt.getName());
6216 ps.nbsp(maxNameLength - elt.getName().size());
6217 ps << " (";
6218 llvm::SmallPtrSet<Operation *, 4> ops;
6219 if (elt.isOutput()) {
6220 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6221 "output port must have either single or no use");
6222 if (portVal.use_empty()) {
6223 ps << "/* unused */";
6224 } else if (auto output = dyn_cast_or_null<OutputOp>(
6225 portVal.getUses().begin()->getOwner())) {
6226 // If this is directly using the output port of the containing
6227 // module, just specify that directly.
6228 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6229 ps << PPExtString(
6230 parentPortList.atOutput(outputPortNo).getVerilogName());
6231 } else {
6232 portVal = portVal.getUsers().begin()->getOperand(0);
6233 ExprEmitter(*this, ops)
6234 .emitExpression(portVal, LowestPrecedence,
6235 /*isAssignmentLikeContext=*/false);
6236 }
6237 } else {
6238 ExprEmitter(*this, ops)
6239 .emitExpression(portVal, LowestPrecedence,
6240 /*isAssignmentLikeContext=*/false);
6241 }
6242
6243 ps << ")";
6244
6245 if (isZeroWidth)
6246 ps << PP::end; // Close never-break group.
6247 }
6248 });
6249 if (!isFirst)
6250 ps << PP::newline;
6251 ps << ");";
6252 ps.addCallback({op, false});
6253 setPendingNewline();
6254}
6255
6256void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6257 if (hasSVAttributes(op))
6258 emitError(op, "SV attributes emission is unimplemented for the op");
6259
6260 auto instance = op.getReferencedInstance(&state.symbolCache);
6261 auto instantiator = instance->getParentOfType<HWModuleOp>().getName();
6262 auto *interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6263 instance.getInterfaceType().getInterface());
6264 startStatement();
6265 ps.addCallback({op, true});
6266 ps << "bind " << PPExtString(instantiator) << PP::nbsp
6267 << PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6268 << PPExtString(getSymOpName(instance)) << " (.*);" << PP::newline;
6269 ps.addCallback({op, false});
6270 setPendingNewline();
6271}
6272
6273void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6274 if (params.empty())
6275 return;
6276
6277 auto printParamType = [&](Type type, Attribute defaultValue,
6278 SmallString<8> &result) {
6279 result.clear();
6280 llvm::raw_svector_ostream sstream(result);
6281
6282 // If there is a default value like "32" then just print without type at
6283 // all.
6284 if (defaultValue) {
6285 if (auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6286 if (intAttr.getValue().getBitWidth() == 32)
6287 return;
6288 if (auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6289 if (fpAttr.getType().isF64())
6290 return;
6291 }
6292 if (isa<NoneType>(type))
6293 return;
6294
6295 // Classic Verilog parser don't allow a type in the parameter declaration.
6296 // For compatibility with them, we omit the type when it is implicit based
6297 // on its initializer value, and print the type commented out when it is
6298 // a 32-bit "integer" parameter.
6299 if (auto intType = type_dyn_cast<IntegerType>(type))
6300 if (intType.getWidth() == 32) {
6301 sstream << "/*integer*/";
6302 return;
6303 }
6304
6305 printPackedType(type, sstream, module->getLoc(),
6306 /*optionalAliasType=*/Type(),
6307 /*implicitIntType=*/true,
6308 // Print single-bit values as explicit `[0:0]` type.
6309 /*singleBitDefaultType=*/false);
6310 };
6311
6312 // Determine the max width of the parameter types so things are lined up.
6313 size_t maxTypeWidth = 0;
6314 SmallString<8> scratch;
6315 for (auto param : params) {
6316 auto paramAttr = cast<ParamDeclAttr>(param);
6317 // Measure the type length by printing it to a temporary string.
6318 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6319 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6320 }
6321
6322 if (maxTypeWidth > 0) // add a space if any type exists.
6323 maxTypeWidth += 1;
6324
6325 ps.scopedBox(PP::bbox2, [&]() {
6326 ps << PP::newline << "#(";
6327 ps.scopedBox(PP::cbox0, [&]() {
6328 llvm::interleave(
6329 params,
6330 [&](Attribute param) {
6331 auto paramAttr = cast<ParamDeclAttr>(param);
6332 auto defaultValue = paramAttr.getValue(); // may be null if absent.
6333 ps << "parameter ";
6334 printParamType(paramAttr.getType(), defaultValue, scratch);
6335 if (!scratch.empty())
6336 ps << scratch;
6337 if (scratch.size() < maxTypeWidth)
6338 ps.nbsp(maxTypeWidth - scratch.size());
6339
6340 ps << PPExtString(state.globalNames.getParameterVerilogName(
6341 module, paramAttr.getName()));
6342
6343 if (defaultValue) {
6344 ps << " = ";
6345 ps.invokeWithStringOS([&](auto &os) {
6346 printParamValue(defaultValue, os, [&]() {
6347 return module->emitError("parameter '")
6348 << paramAttr.getName().getValue()
6349 << "' has invalid value";
6350 });
6351 });
6352 }
6353 },
6354 [&]() { ps << "," << PP::newline; });
6355 ps << ") ";
6356 });
6357 });
6358}
6359
6360void ModuleEmitter::emitPortList(Operation *module,
6361 const ModulePortInfo &portInfo,
6362 bool emitAsTwoStateType) {
6363 ps << "(";
6364 if (portInfo.size())
6365 emitLocationInfo(module->getLoc());
6366
6367 // Determine the width of the widest type we have to print so everything
6368 // lines up nicely.
6369 bool hasOutputs = false, hasZeroWidth = false;
6370 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6371 SmallVector<SmallString<8>, 16> portTypeStrings;
6372
6373 for (size_t i = 0, e = portInfo.size(); i < e; ++i) {
6374 auto port = portInfo.at(i);
6375 hasOutputs |= port.isOutput();
6376 hasZeroWidth |= isZeroBitType(port.type);
6377 if (!isZeroBitType(port.type))
6378 lastNonZeroPort = i;
6379
6380 // Convert the port's type to a string and measure it.
6381 portTypeStrings.push_back({});
6382 {
6383 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6384 printPackedType(stripUnpackedTypes(port.type), stringStream,
6385 module->getLoc(), {}, true, true, emitAsTwoStateType);
6386 }
6387
6388 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6389 }
6390
6391 if (maxTypeWidth > 0) // add a space if any type exists
6392 maxTypeWidth += 1;
6393
6394 // Emit the port list.
6395 ps.scopedBox(PP::bbox2, [&]() {
6396 for (size_t portIdx = 0, e = portInfo.size(); portIdx != e;) {
6397 auto lastPort = e - 1;
6398
6399 ps << PP::newline;
6400 auto portType = portInfo.at(portIdx).type;
6401
6402 // If this is a zero width type, emit the port as a comment and create a
6403 // neverbox to ensure we don't insert a line break.
6404 bool isZeroWidth = false;
6405 if (hasZeroWidth) {
6406 isZeroWidth = isZeroBitType(portType);
6407 if (isZeroWidth)
6408 ps << PP::neverbox;
6409 ps << (isZeroWidth ? "// " : " ");
6410 }
6411
6412 // Emit the port direction.
6413 auto thisPortDirection = portInfo.at(portIdx).dir;
6414 switch (thisPortDirection) {
6415 case ModulePort::Direction::Output:
6416 ps << "output ";
6417 break;
6418 case ModulePort::Direction::Input:
6419 ps << (hasOutputs ? "input " : "input ");
6420 break;
6421 case ModulePort::Direction::InOut:
6422 ps << (hasOutputs ? "inout " : "inout ");
6423 break;
6424 }
6425 bool emitWireInPorts = state.options.emitWireInPorts;
6426 if (emitWireInPorts)
6427 ps << "wire ";
6428
6429 // Emit the type.
6430 if (!portTypeStrings[portIdx].empty())
6431 ps << portTypeStrings[portIdx];
6432 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6433 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6434
6435 size_t startOfNamePos =
6436 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
6437
6438 // Emit the name.
6439 ps << PPExtString(portInfo.at(portIdx).getVerilogName());
6440
6441 // Emit array dimensions.
6442 ps.invokeWithStringOS(
6443 [&](auto &os) { printUnpackedTypePostfix(portType, os); });
6444
6445 // Emit the symbol.
6446 auto innerSym = portInfo.at(portIdx).getSym();
6447 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6448 ps << " /* ";
6449 ps.invokeWithStringOS([&](auto &os) { os << innerSym; });
6450 ps << " */";
6451 }
6452
6453 // Emit the comma if this is not the last real port.
6454 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6455 ps << ",";
6456
6457 // Emit the location.
6458 if (auto loc = portInfo.at(portIdx).loc)
6459 emitLocationInfo(loc);
6460
6461 if (isZeroWidth)
6462 ps << PP::end; // Close never-break group.
6463
6464 ++portIdx;
6465
6466 // If we have any more ports with the same types and the same
6467 // direction, emit them in a list one per line. Optionally skip this
6468 // behavior when requested by user.
6469 if (!state.options.disallowPortDeclSharing) {
6470 while (portIdx != e && portInfo.at(portIdx).dir == thisPortDirection &&
6471 stripUnpackedTypes(portType) ==
6472 stripUnpackedTypes(portInfo.at(portIdx).type)) {
6473 auto port = portInfo.at(portIdx);
6474 // Append this to the running port decl.
6475 ps << PP::newline;
6476
6477 bool isZeroWidth = false;
6478 if (hasZeroWidth) {
6479 isZeroWidth = isZeroBitType(portType);
6480 if (isZeroWidth)
6481 ps << PP::neverbox;
6482 ps << (isZeroWidth ? "// " : " ");
6483 }
6484
6485 ps.nbsp(startOfNamePos);
6486
6487 // Emit the name.
6488 StringRef name = port.getVerilogName();
6489 ps << PPExtString(name);
6490
6491 // Emit array dimensions.
6492 ps.invokeWithStringOS(
6493 [&](auto &os) { printUnpackedTypePostfix(port.type, os); });
6494
6495 // Emit the symbol.
6496 auto sym = port.getSym();
6497 if (state.options.printDebugInfo && sym && !sym.empty())
6498 ps << " /* inner_sym: " << PPExtString(sym.getSymName().getValue())
6499 << " */";
6500
6501 // Emit the comma if this is not the last real port.
6502 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6503 ps << ",";
6504
6505 // Emit the location.
6506 if (auto loc = port.loc)
6507 emitLocationInfo(loc);
6508
6509 if (isZeroWidth)
6510 ps << PP::end; // Close never-break group.
6511
6512 ++portIdx;
6513 }
6514 }
6515 }
6516 });
6517
6518 if (!portInfo.size()) {
6519 ps << ");";
6520 SmallPtrSet<Operation *, 8> moduleOpSet;
6521 moduleOpSet.insert(module);
6522 emitLocationInfoAndNewLine(moduleOpSet);
6523 } else {
6524 ps << PP::newline;
6525 ps << ");" << PP::newline;
6526 setPendingNewline();
6527 }
6528}
6529
6530void ModuleEmitter::emitHWModule(HWModuleOp module) {
6531 currentModuleOp = module;
6532
6533 emitComment(module.getCommentAttr());
6534 emitSVAttributes(module);
6535 startStatement();
6536 ps.addCallback({module, true});
6537 ps << "module " << PPExtString(getVerilogModuleName(module));
6538
6539 // If we have any parameters, print them on their own line.
6540 emitParameters(module, module.getParameters());
6541
6542 emitPortList(module, ModulePortInfo(module.getPortList()));
6543
6544 assert(state.pendingNewline);
6545
6546 // Emit the body of the module.
6547 StmtEmitter(*this, state.options).emitStatementBlock(*module.getBodyBlock());
6548 startStatement();
6549 ps << "endmodule";
6550 ps.addCallback({module, false});
6551 ps << PP::newline;
6552 setPendingNewline();
6553
6554 currentModuleOp = nullptr;
6555}
6556
6557void ModuleEmitter::emitFunc(FuncOp func) {
6558 // Nothing to emit for a declaration.
6559 if (func.isDeclaration())
6560 return;
6561
6562 currentModuleOp = func;
6563 startStatement();
6564 ps.addCallback({func, true});
6565 // A function is moduled as an automatic function.
6566 emitFunctionSignature(*this, ps, func, /*isAutomatic=*/true);
6567 // Emit the body of the module.
6568 StmtEmitter(*this, state.options).emitStatementBlock(*func.getBodyBlock());
6569 startStatement();
6570 ps << "endfunction";
6571 ps << PP::newline;
6572 currentModuleOp = nullptr;
6573}
6574
6575//===----------------------------------------------------------------------===//
6576// Emitter for files & file lists.
6577//===----------------------------------------------------------------------===//
6578
6579class FileEmitter : public EmitterBase {
6580public:
6581 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6582
6583 void emit(emit::FileOp op) {
6584 emit(op.getBody());
6585 ps.eof();
6586 }
6587 void emit(emit::FragmentOp op) { emit(op.getBody()); }
6588 void emit(emit::FileListOp op);
6589
6590private:
6591 void emit(Block *block);
6592
6593 void emitOp(emit::RefOp op);
6594 void emitOp(emit::VerbatimOp op);
6595};
6596
6597void FileEmitter::emit(Block *block) {
6598 for (Operation &op : *block) {
6599 TypeSwitch<Operation *>(&op)
6600 .Case<emit::VerbatimOp, emit::RefOp>([&](auto op) { emitOp(op); })
6601 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6602 [&](auto op) { ModuleEmitter(state).emitStatement(op); })
6603 .Case<BindOp>([&](auto op) { ModuleEmitter(state).emitBind(op); })
6604 .Case<BindInterfaceOp>(
6605 [&](auto op) { ModuleEmitter(state).emitBindInterface(op); })
6606 .Case<TypeScopeOp>([&](auto typedecls) {
6607 ModuleEmitter(state).emitStatement(typedecls);
6608 })
6609 .Default(
6610 [&](auto op) { emitOpError(op, "cannot be emitted to a file"); });
6611 }
6612}
6613
6614void FileEmitter::emit(emit::FileListOp op) {
6615 // Find the associated file ops and write the paths on individual lines.
6616 for (auto sym : op.getFiles()) {
6617 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6618
6619 auto it = state.fileMapping.find(fileName);
6620 if (it == state.fileMapping.end()) {
6621 emitOpError(op, " references an invalid file: ") << sym;
6622 continue;
6623 }
6624
6625 auto file = cast<emit::FileOp>(it->second);
6626 ps << PP::neverbox << PPExtString(file.getFileName()) << PP::end
6627 << PP::newline;
6628 }
6629 ps.eof();
6630}
6631
6632void FileEmitter::emitOp(emit::RefOp op) {
6633 StringAttr target = op.getTargetAttr().getAttr();
6634 auto *targetOp = state.symbolCache.getDefinition(target);
6635 assert(isa<emit::Emittable>(targetOp) && "target must be emittable");
6636
6637 TypeSwitch<Operation *>(targetOp)
6638 .Case<sv::FuncOp>([&](auto func) { ModuleEmitter(state).emitFunc(func); })
6639 .Case<hw::HWModuleOp>(
6640 [&](auto module) { ModuleEmitter(state).emitHWModule(module); })
6641 .Case<TypeScopeOp>([&](auto typedecls) {
6642 ModuleEmitter(state).emitStatement(typedecls);
6643 })
6644 .Default(
6645 [&](auto op) { emitOpError(op, "cannot be emitted to a file"); });
6646}
6647
6648void FileEmitter::emitOp(emit::VerbatimOp op) {
6649 startStatement();
6650
6651 SmallPtrSet<Operation *, 8> ops;
6652 ops.insert(op);
6653
6654 // Emit each line of the string at a time, emitting the
6655 // location comment after the last emitted line.
6656 StringRef text = op.getText();
6657
6658 ps << PP::neverbox;
6659 do {
6660 const auto &[lhs, rhs] = text.split('\n');
6661 if (!lhs.empty())
6662 ps << PPExtString(lhs);
6663 if (!rhs.empty())
6664 ps << PP::end << PP::newline << PP::neverbox;
6665 text = rhs;
6666 } while (!text.empty());
6667 ps << PP::end;
6668
6669 emitLocationInfoAndNewLine(ops);
6670}
6671
6672//===----------------------------------------------------------------------===//
6673// Top level "file" emitter logic
6674//===----------------------------------------------------------------------===//
6675
6676/// Organize the operations in the root MLIR module into output files to be
6677/// generated. If `separateModules` is true, a handful of top-level
6678/// declarations will be split into separate output files even in the absence
6679/// of an explicit output file attribute.
6680void SharedEmitterState::gatherFiles(bool separateModules) {
6681
6682 /// Collect all the inner names from the specified module and add them to the
6683 /// IRCache. Declarations (named things) only exist at the top level of the
6684 /// module. Also keep track of any modules that contain bind operations.
6685 /// These are non-hierarchical references which we need to be careful about
6686 /// during emission.
6687 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6688 moduleOp->walk([&](Operation *op) {
6689 // Populate the symbolCache with all operations that can define a symbol.
6690 if (auto name = op->getAttrOfType<InnerSymAttr>(
6692 symbolCache.addDefinition(moduleOp->getAttrOfType<StringAttr>(
6693 SymbolTable::getSymbolAttrName()),
6694 name.getSymName(), op);
6695 if (isa<BindOp>(op))
6696 modulesContainingBinds.insert(moduleOp);
6697 });
6698 };
6699
6700 /// Collect any port marked as being referenced via symbol.
6701 auto collectPorts = [&](auto moduleOp) {
6702 auto portInfo = moduleOp.getPortList();
6703 for (auto [i, p] : llvm::enumerate(portInfo)) {
6704 if (!p.attrs || p.attrs.empty())
6705 continue;
6706 for (NamedAttribute portAttr : p.attrs) {
6707 if (auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6708 symbolCache.addDefinition(moduleOp.getNameAttr(), sym.getSymName(),
6709 moduleOp, i);
6710 }
6711 }
6712 }
6713 };
6714
6715 // Create a mapping identifying the files each symbol is emitted to.
6716 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6717 for (auto file : designOp.getOps<emit::FileOp>())
6718 for (auto refs : file.getOps<emit::RefOp>())
6719 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6720
6721 SmallString<32> outputPath;
6722 for (auto &op : *designOp.getBody()) {
6723 auto info = OpFileInfo{&op, replicatedOps.size()};
6724
6725 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6726
6727 bool hasFileName = false;
6728 bool emitReplicatedOps = !isFileOp;
6729 bool addToFilelist = !isFileOp;
6730
6731 outputPath.clear();
6732
6733 // Check if the operation has an explicit `output_file` attribute set. If
6734 // it does, extract the information from the attribute.
6735 auto attr = op.getAttrOfType<hw::OutputFileAttr>("output_file");
6736 if (attr) {
6737 LLVM_DEBUG(llvm::dbgs() << "Found output_file attribute " << attr
6738 << " on " << op << "\n";);
6739 if (!attr.isDirectory())
6740 hasFileName = true;
6741 appendPossiblyAbsolutePath(outputPath, attr.getFilename().getValue());
6742 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6743 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6744 }
6745
6746 auto separateFile = [&](Operation *op, Twine defaultFileName = "") {
6747 // If we're emitting to a separate file and the output_file attribute
6748 // didn't specify a filename, take the default one if present or emit an
6749 // error if not.
6750 if (!hasFileName) {
6751 if (!defaultFileName.isTriviallyEmpty()) {
6752 llvm::sys::path::append(outputPath, defaultFileName);
6753 } else {
6754 op->emitError("file name unspecified");
6755 encounteredError = true;
6756 llvm::sys::path::append(outputPath, "error.out");
6757 }
6758 }
6759
6760 auto destFile = StringAttr::get(op->getContext(), outputPath);
6761 auto &file = files[destFile];
6762 file.ops.push_back(info);
6763 file.emitReplicatedOps = emitReplicatedOps;
6764 file.addToFilelist = addToFilelist;
6765 file.isVerilog = outputPath.ends_with(".sv");
6766
6767 // Back-annotate the op with an OutputFileAttr if there wasn't one. If it
6768 // was a directory, back-annotate the final file path. This is so output
6769 // files are explicit in the final MLIR after export.
6770 if (!attr || attr.isDirectory()) {
6771 auto excludeFromFileListAttr =
6772 BoolAttr::get(op->getContext(), !addToFilelist);
6773 auto includeReplicatedOpsAttr =
6774 BoolAttr::get(op->getContext(), emitReplicatedOps);
6775 auto outputFileAttr = hw::OutputFileAttr::get(
6776 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6777 op->setAttr("output_file", outputFileAttr);
6778 }
6779 };
6780
6781 // Separate the operation into dedicated output file, or emit into the
6782 // root file, or replicate in all output files.
6783 TypeSwitch<Operation *>(&op)
6784 .Case<emit::FileOp, emit::FileListOp>([&](auto file) {
6785 // Emit file ops to their respective files.
6786 fileMapping.try_emplace(file.getSymNameAttr(), file);
6787 separateFile(file, file.getFileName());
6788 })
6789 .Case<emit::FragmentOp>([&](auto fragment) {
6790 fragmentMapping.try_emplace(fragment.getSymNameAttr(), fragment);
6791 })
6792 .Case<HWModuleOp>([&](auto mod) {
6793 // Build the IR cache.
6794 auto sym = mod.getNameAttr();
6795 symbolCache.addDefinition(sym, mod);
6796 collectPorts(mod);
6797 collectInstanceSymbolsAndBinds(mod);
6798
6799 if (auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6800 if (it->second.size() != 1 || attr) {
6801 // This is a temporary check, present as long as both
6802 // output_file and file operations are used.
6803 op.emitError("modules can be emitted to a single file");
6804 encounteredError = true;
6805 } else {
6806 // The op is not separated into a file as it will be
6807 // pulled into the unique file operation it references.
6808 }
6809 } else {
6810 // Emit into a separate file named after the module.
6811 if (attr || separateModules)
6812 separateFile(mod, getVerilogModuleName(mod) + ".sv");
6813 else
6814 rootFile.ops.push_back(info);
6815 }
6816 })
6817 .Case<InterfaceOp>([&](InterfaceOp intf) {
6818 // Build the IR cache.
6819 symbolCache.addDefinition(intf.getNameAttr(), intf);
6820 // Populate the symbolCache with all operations that can define a
6821 // symbol.
6822 for (auto &op : *intf.getBodyBlock())
6823 if (auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6824 if (auto name = symOp.getNameAttr())
6825 symbolCache.addDefinition(name, symOp);
6826
6827 // Emit into a separate file named after the interface.
6828 if (attr || separateModules)
6829 separateFile(intf, intf.getSymName() + ".sv");
6830 else
6831 rootFile.ops.push_back(info);
6832 })
6833 .Case<HWModuleExternOp>([&](HWModuleExternOp op) {
6834 // Build the IR cache.
6835 symbolCache.addDefinition(op.getNameAttr(), op);
6836 collectPorts(op);
6837 // External modules are _not_ emitted.
6838 })
6839 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
6840 [&](Operation *op) {
6841 // Emit into a separate file using the specified file name or
6842 // replicate the operation in each outputfile.
6843 if (!attr) {
6844 replicatedOps.push_back(op);
6845 } else
6846 separateFile(op, "");
6847 })
6848 .Case<FuncOp>([&](auto op) {
6849 // Emit into a separate file using the specified file name or
6850 // replicate the operation in each outputfile.
6851 if (!attr) {
6852 replicatedOps.push_back(op);
6853 } else
6854 separateFile(op, "");
6855
6856 symbolCache.addDefinition(op.getSymNameAttr(), op);
6857 })
6858 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6859 symbolCache.addDefinition(schemaOp.getNameAttr(), schemaOp);
6860 })
6861 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6862 symbolCache.addDefinition(hierPathOp.getSymNameAttr(), hierPathOp);
6863 })
6864 .Case<TypeScopeOp>([&](TypeScopeOp op) {
6865 symbolCache.addDefinition(op.getNameAttr(), op);
6866 // TODO: How do we want to handle typedefs in a split output?
6867 if (!attr) {
6868 replicatedOps.push_back(op);
6869 } else
6870 separateFile(op, "");
6871 })
6872 .Case<BindOp>([&](auto op) {
6873 if (!attr) {
6874 separateFile(op, "bindfile.sv");
6875 } else {
6876 separateFile(op);
6877 }
6878 })
6879 .Case<MacroErrorOp>([&](auto op) { replicatedOps.push_back(op); })
6880 .Case<MacroDeclOp>([&](auto op) {
6881 symbolCache.addDefinition(op.getSymNameAttr(), op);
6882 })
6883 .Case<sv::ReserveNamesOp>([](auto op) {
6884 // This op was already used in gathering used names.
6885 })
6886 .Case<om::ClassLike>([&](auto op) {
6887 symbolCache.addDefinition(op.getSymNameAttr(), op);
6888 })
6889 .Case<om::ConstantOp>([&](auto op) {
6890 // Constant ops might reference symbols, skip them.
6891 })
6892 .Default([&](auto *) {
6893 op.emitError("unknown operation (SharedEmitterState::gatherFiles)");
6894 encounteredError = true;
6895 });
6896 }
6897
6898 // We've built the whole symbol cache. Freeze it so things can start
6899 // querying it (potentially concurrently).
6901}
6902
6903/// Given a FileInfo, collect all the replicated and designated operations
6904/// that go into it and append them to "thingsToEmit".
6906 EmissionList &thingsToEmit,
6907 bool emitHeader) {
6908 // Include the version string comment when the file is verilog.
6910 thingsToEmit.emplace_back(circt::getCirctVersionComment());
6911
6912 // If we're emitting replicated ops, keep track of where we are in the list.
6913 size_t lastReplicatedOp = 0;
6914
6915 bool emitHeaderInclude =
6916 emitHeader && file.emitReplicatedOps && !file.isHeader;
6917
6918 if (emitHeaderInclude)
6919 thingsToEmit.emplace_back(circtHeaderInclude);
6920
6921 size_t numReplicatedOps =
6922 file.emitReplicatedOps && !emitHeaderInclude ? replicatedOps.size() : 0;
6923
6924 // Emit each operation in the file preceded by the replicated ops not yet
6925 // printed.
6926 DenseSet<emit::FragmentOp> includedFragments;
6927 for (const auto &opInfo : file.ops) {
6928 Operation *op = opInfo.op;
6929
6930 // Emit the replicated per-file operations before the main operation's
6931 // position (if enabled).
6932 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
6933 ++lastReplicatedOp)
6934 thingsToEmit.emplace_back(replicatedOps[lastReplicatedOp]);
6935
6936 // Pull in the fragments that the op references. In one file, each
6937 // fragment is emitted only once.
6938 if (auto fragments =
6939 op->getAttrOfType<ArrayAttr>(emit::getFragmentsAttrName())) {
6940 for (auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
6941 auto it = fragmentMapping.find(sym.getAttr());
6942 if (it == fragmentMapping.end()) {
6943 encounteredError = true;
6944 op->emitError("cannot find referenced fragment ") << sym;
6945 continue;
6946 }
6947 emit::FragmentOp fragment = it->second;
6948 if (includedFragments.insert(fragment).second) {
6949 thingsToEmit.emplace_back(it->second);
6950 }
6951 }
6952 }
6953
6954 // Emit the operation itself.
6955 thingsToEmit.emplace_back(op);
6956 }
6957
6958 // Emit the replicated per-file operations after the last operation (if
6959 // enabled).
6960 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
6961 thingsToEmit.emplace_back(replicatedOps[lastReplicatedOp]);
6962}
6963
6964static void emitOperation(VerilogEmitterState &state, Operation *op) {
6965 TypeSwitch<Operation *>(op)
6966 .Case<HWModuleOp>([&](auto op) { ModuleEmitter(state).emitHWModule(op); })
6967 .Case<HWModuleExternOp>([&](auto op) {
6968 // External modules are _not_ emitted.
6969 })
6970 .Case<HWModuleGeneratedOp>(
6971 [&](auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
6972 .Case<HWGeneratorSchemaOp>([&](auto op) { /* Empty */ })
6973 .Case<BindOp>([&](auto op) { ModuleEmitter(state).emitBind(op); })
6974 .Case<InterfaceOp, VerbatimOp, IfDefOp>(
6975 [&](auto op) { ModuleEmitter(state).emitStatement(op); })
6976 .Case<TypeScopeOp>([&](auto typedecls) {
6977 ModuleEmitter(state).emitStatement(typedecls);
6978 })
6979 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
6980 [&](auto op) { FileEmitter(state).emit(op); })
6981 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
6982 [&](auto op) { ModuleEmitter(state).emitStatement(op); })
6983 .Case<FuncOp>([&](auto op) { ModuleEmitter(state).emitFunc(op); })
6984 .Case<IncludeOp>([&](auto op) { ModuleEmitter(state).emitStatement(op); })
6985 .Default([&](auto *op) {
6986 state.encounteredError = true;
6987 op->emitError("unknown operation (ExportVerilog::emitOperation)");
6988 });
6989}
6990
6991/// Actually emit the collected list of operations and strings to the
6992/// specified file.
6994 llvm::formatted_raw_ostream &os,
6995 StringAttr fileName, bool parallelize) {
6996 MLIRContext *context = designOp->getContext();
6997
6998 // Disable parallelization overhead if MLIR threading is disabled.
6999 if (parallelize)
7000 parallelize &= context->isMultithreadingEnabled();
7001
7002 // If we aren't parallelizing output, directly output each operation to the
7003 // specified stream.
7004 if (!parallelize) {
7005 // All the modules share the same map to store the verilog output location
7006 // on the stream.
7007 OpLocMap verilogLocMap(os);
7008 VerilogEmitterState state(designOp, *this, options, symbolCache,
7009 globalNames, fileMapping, os, fileName,
7010 verilogLocMap);
7011 size_t lineOffset = 0;
7012 for (auto &entry : thingsToEmit) {
7013 entry.verilogLocs.setStream(os);
7014 if (auto *op = entry.getOperation()) {
7015 emitOperation(state, op);
7016 // Since the modules are exported sequentially, update all the ops with
7017 // the verilog location. This also clears the map, so that the map only
7018 // contains the current iteration's ops.
7019 state.addVerilogLocToOps(lineOffset, fileName);
7020 } else {
7021 os << entry.getStringData();
7022 ++lineOffset;
7023 }
7024 }
7025
7026 if (state.encounteredError)
7027 encounteredError = true;
7028 return;
7029 }
7030
7031 // If we are parallelizing emission, we emit each independent operation to a
7032 // string buffer in parallel, then concat at the end.
7033 parallelForEach(context, thingsToEmit, [&](StringOrOpToEmit &stringOrOp) {
7034 auto *op = stringOrOp.getOperation();
7035 if (!op)
7036 return; // Ignore things that are already strings.
7037
7038 // BindOp emission reaches into the hw.module of the instance, and that
7039 // body may be being transformed by its own emission. Defer their
7040 // emission to the serial phase. They are speedy to emit anyway.
7041 if (isa<BindOp>(op) || modulesContainingBinds.count(op))
7042 return;
7043
7044 SmallString<256> buffer;
7045 llvm::raw_svector_ostream tmpStream(buffer);
7046 llvm::formatted_raw_ostream rs(tmpStream);
7047 // Each `thingToEmit` (op) uses a unique map to store verilog locations.
7048 stringOrOp.verilogLocs.setStream(rs);
7049 VerilogEmitterState state(designOp, *this, options, symbolCache,
7050 globalNames, fileMapping, rs, fileName,
7051 stringOrOp.verilogLocs);
7052 emitOperation(state, op);
7053 stringOrOp.setString(buffer);
7054 });
7055
7056 // Finally emit each entry now that we know it is a string.
7057 for (auto &entry : thingsToEmit) {
7058 // Almost everything is lowered to a string, just concat the strings onto
7059 // the output stream.
7060 auto *op = entry.getOperation();
7061 if (!op) {
7062 auto lineOffset = os.getLine() + 1;
7063 os << entry.getStringData();
7064 // Ensure the line numbers are offset properly in the map. Each `entry`
7065 // was exported in parallel onto independent string streams, hence the
7066 // line numbers need to be updated with the offset in the current stream.
7067 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
7068 continue;
7069 }
7070 entry.verilogLocs.setStream(os);
7071
7072 // If this wasn't emitted to a string (e.g. it is a bind) do so now.
7073 VerilogEmitterState state(designOp, *this, options, symbolCache,
7074 globalNames, fileMapping, os, fileName,
7075 entry.verilogLocs);
7076 emitOperation(state, op);
7077 state.addVerilogLocToOps(0, fileName);
7078 }
7079}
7080
7081//===----------------------------------------------------------------------===//
7082// Unified Emitter
7083//===----------------------------------------------------------------------===//
7084
7085static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os) {
7086 LoweringOptions options(module);
7087 GlobalNameTable globalNames = legalizeGlobalNames(module, options);
7088
7089 SharedEmitterState emitter(module, options, std::move(globalNames));
7090 emitter.gatherFiles(false);
7091
7093 module.emitWarning()
7094 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7095 "created only at SplitExportVerilog";
7096
7098
7099 // Collect the contents of the main file. This is a container for anything
7100 // not explicitly split out into a separate file.
7101 emitter.collectOpsForFile(emitter.rootFile, list);
7102
7103 // Emit the separate files.
7104 for (const auto &it : emitter.files) {
7105 list.emplace_back("\n// ----- 8< ----- FILE \"" + it.first.str() +
7106 "\" ----- 8< -----\n\n");
7107 emitter.collectOpsForFile(it.second, list);
7108 }
7109
7110 // Emit the filelists.
7111 for (auto &it : emitter.fileLists) {
7112 std::string contents("\n// ----- 8< ----- FILE \"" + it.first().str() +
7113 "\" ----- 8< -----\n\n");
7114 for (auto &name : it.second)
7115 contents += name.str() + "\n";
7116 list.emplace_back(contents);
7117 }
7118
7119 llvm::formatted_raw_ostream rs(os);
7120 // Finally, emit all the ops we collected.
7121 // output file name is not known, it can be specified as command line
7122 // argument.
7123 emitter.emitOps(list, rs, StringAttr::get(module.getContext(), ""),
7124 /*parallelize=*/true);
7125 return failure(emitter.encounteredError);
7126}
7127
7128LogicalResult circt::exportVerilog(ModuleOp module, llvm::raw_ostream &os) {
7129 LoweringOptions options(module);
7130 if (failed(lowerHWInstanceChoices(module)))
7131 return failure();
7132 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7133 module.walk(
7134 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7135 if (failed(failableParallelForEach(
7136 module->getContext(), modulesToPrepare,
7137 [&](auto op) { return prepareHWModule(op, options); })))
7138 return failure();
7139 return exportVerilogImpl(module, os);
7140}
7141
7142namespace {
7143
7144struct ExportVerilogPass
7145 : public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7146 ExportVerilogPass(raw_ostream &os) : os(os) {}
7147 void runOnOperation() override {
7148 // Prepare the ops in the module for emission.
7149 mlir::OpPassManager preparePM("builtin.module");
7150 preparePM.addPass(createLegalizeAnonEnums());
7151 preparePM.addPass(createHWLowerInstanceChoices());
7152 auto &modulePM = preparePM.nestAny();
7153 modulePM.addPass(createPrepareForEmission());
7154 if (failed(runPipeline(preparePM, getOperation())))
7155 return signalPassFailure();
7156
7157 if (failed(exportVerilogImpl(getOperation(), os)))
7158 return signalPassFailure();
7159 }
7160
7161private:
7162 raw_ostream &os;
7163};
7164
7165struct ExportVerilogStreamOwnedPass : public ExportVerilogPass {
7166 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7167 : ExportVerilogPass{*os} {
7168 owned = std::move(os);
7169 }
7170
7171private:
7172 std::unique_ptr<llvm::raw_ostream> owned;
7173};
7174} // end anonymous namespace
7175
7176std::unique_ptr<mlir::Pass>
7177circt::createExportVerilogPass(std::unique_ptr<llvm::raw_ostream> os) {
7178 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7179}
7180
7181std::unique_ptr<mlir::Pass>
7182circt::createExportVerilogPass(llvm::raw_ostream &os) {
7183 return std::make_unique<ExportVerilogPass>(os);
7184}
7185
7186std::unique_ptr<mlir::Pass> circt::createExportVerilogPass() {
7187 return createExportVerilogPass(llvm::outs());
7188}
7189
7190//===----------------------------------------------------------------------===//
7191// Split Emitter
7192//===----------------------------------------------------------------------===//
7193
7194static std::unique_ptr<llvm::ToolOutputFile>
7195createOutputFile(StringRef fileName, StringRef dirname,
7196 SharedEmitterState &emitter) {
7197 // Determine the output path from the output directory and filename.
7198 SmallString<128> outputFilename(dirname);
7199 appendPossiblyAbsolutePath(outputFilename, fileName);
7200 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7201
7202 // Create the output directory if needed.
7203 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7204 if (error) {
7205 emitter.designOp.emitError("cannot create output directory \"")
7206 << outputDir << "\": " << error.message();
7207 emitter.encounteredError = true;
7208 return {};
7209 }
7210
7211 // Open the output file.
7212 std::string errorMessage;
7213 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7214 if (!output) {
7215 emitter.designOp.emitError(errorMessage);
7216 emitter.encounteredError = true;
7217 }
7218 return output;
7219}
7220
7221static void createSplitOutputFile(StringAttr fileName, FileInfo &file,
7222 StringRef dirname,
7223 SharedEmitterState &emitter) {
7224 auto output = createOutputFile(fileName, dirname, emitter);
7225 if (!output)
7226 return;
7227
7229 emitter.collectOpsForFile(file, list,
7231
7232 llvm::formatted_raw_ostream rs(output->os());
7233 // Emit the file, copying the global options into the individual module
7234 // state. Don't parallelize emission of the ops within this file - we
7235 // already parallelize per-file emission and we pay a string copy overhead
7236 // for parallelization.
7237 emitter.emitOps(list, rs,
7238 StringAttr::get(fileName.getContext(), output->getFilename()),
7239 /*parallelize=*/false);
7240 output->keep();
7241}
7242
7243static LogicalResult exportSplitVerilogImpl(ModuleOp module,
7244 StringRef dirname) {
7245 // Prepare the ops in the module for emission and legalize the names that will
7246 // end up in the output.
7247 LoweringOptions options(module);
7248 GlobalNameTable globalNames = legalizeGlobalNames(module, options);
7249
7250 SharedEmitterState emitter(module, options, std::move(globalNames));
7251 emitter.gatherFiles(true);
7252
7253 if (emitter.options.emitReplicatedOpsToHeader) {
7254 // Add a header to the file list.
7255 bool insertSuccess =
7256 emitter.files
7257 .insert({StringAttr::get(module.getContext(), circtHeader),
7258 FileInfo{/*ops*/ {},
7259 /*emitReplicatedOps*/ true,
7260 /*addToFilelist*/ true,
7261 /*isHeader*/ true}})
7262 .second;
7263 if (!insertSuccess) {
7264 module.emitError() << "tried to emit a heder to " << circtHeader
7265 << ", but the file is used as an output too.";
7266 return failure();
7267 }
7268 }
7269
7270 // Emit each file in parallel if context enables it.
7271 parallelForEach(module->getContext(), emitter.files.begin(),
7272 emitter.files.end(), [&](auto &it) {
7273 createSplitOutputFile(it.first, it.second, dirname,
7274 emitter);
7275 });
7276
7277 // Write the file list.
7278 SmallString<128> filelistPath(dirname);
7279 llvm::sys::path::append(filelistPath, "filelist.f");
7280
7281 std::string errorMessage;
7282 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7283 if (!output) {
7284 module->emitError(errorMessage);
7285 return failure();
7286 }
7287
7288 for (const auto &it : emitter.files) {
7289 if (it.second.addToFilelist)
7290 output->os() << it.first.str() << "\n";
7291 }
7292 output->keep();
7293
7294 // Emit the filelists.
7295 for (auto &it : emitter.fileLists) {
7296 auto output = createOutputFile(it.first(), dirname, emitter);
7297 if (!output)
7298 continue;
7299 for (auto &name : it.second)
7300 output->os() << name.str() << "\n";
7301 output->keep();
7302 }
7303
7304 return failure(emitter.encounteredError);
7305}
7306
7307LogicalResult circt::exportSplitVerilog(ModuleOp module, StringRef dirname) {
7308 LoweringOptions options(module);
7309 if (failed(lowerHWInstanceChoices(module)))
7310 return failure();
7311 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7312 module.walk(
7313 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7314 if (failed(failableParallelForEach(
7315 module->getContext(), modulesToPrepare,
7316 [&](auto op) { return prepareHWModule(op, options); })))
7317 return failure();
7318
7319 return exportSplitVerilogImpl(module, dirname);
7320}
7321
7322namespace {
7323
7324struct ExportSplitVerilogPass
7325 : public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7326 ExportSplitVerilogPass(StringRef directory) {
7327 directoryName = directory.str();
7328 }
7329 void runOnOperation() override {
7330 // Prepare the ops in the module for emission.
7331 mlir::OpPassManager preparePM("builtin.module");
7332 preparePM.addPass(createHWLowerInstanceChoices());
7333
7334 auto &modulePM = preparePM.nest<hw::HWModuleOp>();
7335 modulePM.addPass(createPrepareForEmission());
7336 if (failed(runPipeline(preparePM, getOperation())))
7337 return signalPassFailure();
7338
7339 if (failed(exportSplitVerilogImpl(getOperation(), directoryName)))
7340 return signalPassFailure();
7341 }
7342};
7343} // end anonymous namespace
7344
7345std::unique_ptr<mlir::Pass>
7346circt::createExportSplitVerilogPass(StringRef directory) {
7347 return std::make_unique<ExportSplitVerilogPass>(directory);
7348}
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:66
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.