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