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