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