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