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