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