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