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