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