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