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