2525#include " mlir/IR/Threading.h"
2626#include " mlir/Pass/Pass.h"
2727#include " mlir/Transforms/DialectConversion.h"
28+ #include " mlir/Transforms/RegionUtils.h"
29+ #include " llvm/ADT/SmallPtrSet.h"
2830
2931#define DEBUG_TYPE " lower-sim-to-sv"
3032
@@ -55,6 +57,162 @@ static std::pair<Value, Value> needsClockAndConditionWrapper(Operation *op) {
5557
5658namespace {
5759
60+ static void appendLiteralToFWriteFormat (SmallString<128 > &formatString,
61+ StringRef literal) {
62+ for (char ch : literal) {
63+ if (ch == ' %' )
64+ formatString += " %%" ;
65+ else
66+ formatString.push_back (ch);
67+ }
68+ }
69+
70+ static LogicalResult appendIntegerSpecifier (SmallString<128 > &formatString,
71+ bool isLeftAligned,
72+ uint8_t paddingChar,
73+ std::optional<int32_t > width,
74+ char spec) {
75+ formatString.push_back (' %' );
76+ if (isLeftAligned)
77+ formatString.push_back (' -' );
78+
79+ // SystemVerilog formatting only has built-in support for '0' and ' '. Keep
80+ // this lowering strict to avoid silently changing formatting semantics.
81+ if (paddingChar == ' 0' ) {
82+ formatString.push_back (' 0' );
83+ } else if (paddingChar != ' ' ) {
84+ return failure ();
85+ }
86+
87+ if (width.has_value ())
88+ formatString += std::to_string (width.value ());
89+
90+ formatString.push_back (spec);
91+ return success ();
92+ }
93+
94+ static void appendFloatSpecifier (SmallString<128 > &formatString,
95+ bool isLeftAligned,
96+ std::optional<int32_t > fieldWidth,
97+ int32_t fracDigits, char spec) {
98+ formatString.push_back (' %' );
99+ if (isLeftAligned)
100+ formatString.push_back (' -' );
101+ if (fieldWidth.has_value ())
102+ formatString += std::to_string (fieldWidth.value ());
103+ formatString.push_back (' .' );
104+ formatString += std::to_string (fracDigits);
105+ formatString.push_back (spec);
106+ }
107+
108+ static LogicalResult foldFormatStringToFWrite (
109+ Value input, SmallString<128 > &formatString, SmallVectorImpl<Value> &args,
110+ llvm::SmallPtrSetImpl<Operation *> &visited, std::string &failureReason) {
111+ Operation *op = input.getDefiningOp ();
112+ if (!op) {
113+ failureReason =
114+ " block argument format strings are unsupported as sim.proc.print input" ;
115+ return failure ();
116+ }
117+
118+ if (auto concat = dyn_cast<FormatStringConcatOp>(op)) {
119+ if (!visited.insert (op).second ) {
120+ failureReason = " cyclic sim.fmt.concat is unsupported" ;
121+ return failure ();
122+ }
123+ for (auto operand : concat.getInputs ())
124+ if (failed (foldFormatStringToFWrite (operand, formatString, args, visited,
125+ failureReason)))
126+ return failure ();
127+ visited.erase (op);
128+ return success ();
129+ }
130+
131+ return TypeSwitch<Operation *, LogicalResult>(op)
132+ .Case <FormatLiteralOp>([&](auto literal) -> LogicalResult {
133+ appendLiteralToFWriteFormat (formatString, literal.getLiteral ());
134+ return success ();
135+ })
136+ .Case <FormatHierPathOp>([&](auto hierPath) -> LogicalResult {
137+ formatString += hierPath.getUseEscapes () ? " %M" : " %m" ;
138+ return success ();
139+ })
140+ .Case <FormatCharOp>([&](auto fmt) -> LogicalResult {
141+ formatString += " %c" ;
142+ args.push_back (fmt.getValue ());
143+ return success ();
144+ })
145+ .Case <FormatDecOp>([&](auto fmt) -> LogicalResult {
146+ if (failed (appendIntegerSpecifier (formatString, fmt.getIsLeftAligned (),
147+ fmt.getPaddingChar (),
148+ fmt.getSpecifierWidth (), ' d' ))) {
149+ failureReason = " sim.fmt.dec only supports paddingChar 32 (' ') or "
150+ " 48 ('0') for SystemVerilog lowering" ;
151+ return failure ();
152+ }
153+ args.push_back (fmt.getValue ());
154+ return success ();
155+ })
156+ .Case <FormatHexOp>([&](auto fmt) -> LogicalResult {
157+ if (failed (appendIntegerSpecifier (
158+ formatString, fmt.getIsLeftAligned (), fmt.getPaddingChar (),
159+ fmt.getSpecifierWidth (),
160+ fmt.getIsHexUppercase () ? ' X' : ' x' ))) {
161+ failureReason = " sim.fmt.hex only supports paddingChar 32 (' ') or "
162+ " 48 ('0') for SystemVerilog lowering" ;
163+ return failure ();
164+ }
165+ args.push_back (fmt.getValue ());
166+ return success ();
167+ })
168+ .Case <FormatOctOp>([&](auto fmt) -> LogicalResult {
169+ if (failed (appendIntegerSpecifier (formatString, fmt.getIsLeftAligned (),
170+ fmt.getPaddingChar (),
171+ fmt.getSpecifierWidth (), ' o' ))) {
172+ failureReason = " sim.fmt.oct only supports paddingChar 32 (' ') or "
173+ " 48 ('0') for SystemVerilog lowering" ;
174+ return failure ();
175+ }
176+ args.push_back (fmt.getValue ());
177+ return success ();
178+ })
179+ .Case <FormatBinOp>([&](auto fmt) -> LogicalResult {
180+ if (failed (appendIntegerSpecifier (formatString, fmt.getIsLeftAligned (),
181+ fmt.getPaddingChar (),
182+ fmt.getSpecifierWidth (), ' b' ))) {
183+ failureReason = " sim.fmt.bin only supports paddingChar 32 (' ') or "
184+ " 48 ('0') for SystemVerilog lowering" ;
185+ return failure ();
186+ }
187+ args.push_back (fmt.getValue ());
188+ return success ();
189+ })
190+ .Case <FormatScientificOp>([&](auto fmt) -> LogicalResult {
191+ appendFloatSpecifier (formatString, fmt.getIsLeftAligned (),
192+ fmt.getFieldWidth (), fmt.getFracDigits (), ' e' );
193+ args.push_back (fmt.getValue ());
194+ return success ();
195+ })
196+ .Case <FormatFloatOp>([&](auto fmt) -> LogicalResult {
197+ appendFloatSpecifier (formatString, fmt.getIsLeftAligned (),
198+ fmt.getFieldWidth (), fmt.getFracDigits (), ' f' );
199+ args.push_back (fmt.getValue ());
200+ return success ();
201+ })
202+ .Case <FormatGeneralOp>([&](auto fmt) -> LogicalResult {
203+ appendFloatSpecifier (formatString, fmt.getIsLeftAligned (),
204+ fmt.getFieldWidth (), fmt.getFracDigits (), ' g' );
205+ args.push_back (fmt.getValue ());
206+ return success ();
207+ })
208+ .Default ([&](auto unsupportedOp) {
209+ failureReason = (Twine (" unsupported format fragment '" ) +
210+ unsupportedOp->getName ().getStringRef () + " '" )
211+ .str ();
212+ return failure ();
213+ });
214+ }
215+
58216struct SimConversionState {
59217 hw::HWModuleOp module ;
60218 bool usedSynthesisMacro = false ;
@@ -281,6 +439,62 @@ class DPICallLowering : public SimConversionPattern<DPICallOp> {
281439 }
282440};
283441
442+ namespace {
443+ class PrintFormattedProcLowering
444+ : public SimConversionPattern<PrintFormattedProcOp> {
445+ public:
446+ using SimConversionPattern<PrintFormattedProcOp>::SimConversionPattern;
447+
448+ LogicalResult
449+ matchAndRewrite (PrintFormattedProcOp op, OpAdaptor adaptor,
450+ ConversionPatternRewriter &rewriter) const final {
451+ SmallString<128 > formatString;
452+ SmallVector<Value> args;
453+ std::string failureReason;
454+ llvm::SmallPtrSet<Operation *, 8 > visited;
455+ if (failed (foldFormatStringToFWrite (op.getInput (), formatString, args,
456+ visited, failureReason))) {
457+ auto diag = op.emitError (" cannot lower 'sim.proc.print' to sv.fwrite: " );
458+ diag << failureReason;
459+ return failure ();
460+ }
461+
462+ // Align with FIRRTLToHW: default to writing to stderr.
463+ // Specifying an output stream is not currently supported.
464+ auto fd =
465+ hw::ConstantOp::create (rewriter, op.getLoc (), APInt (32 , 0x80000002 ));
466+ sv::FWriteOp::create (rewriter, op.getLoc (), fd, formatString, args);
467+
468+ rewriter.eraseOp (op);
469+ return success ();
470+ }
471+ };
472+
473+ static LogicalResult validatePrintLowering (hw::HWModuleOp module ) {
474+ LogicalResult result = success ();
475+ module .walk ([&](PrintFormattedOp op) {
476+ op.emitError (" cannot lower 'sim.print' directly to SystemVerilog; run "
477+ " --sim-proceduralize first to convert it to "
478+ " 'sim.proc.print'" );
479+ result = failure ();
480+ });
481+
482+ module .walk ([&](PrintFormattedProcOp op) {
483+ SmallString<128 > formatString;
484+ SmallVector<Value> args;
485+ std::string failureReason;
486+ llvm::SmallPtrSet<Operation *, 8 > visited;
487+ if (failed (foldFormatStringToFWrite (op.getInput (), formatString, args,
488+ visited, failureReason))) {
489+ auto diag = op.emitError (" cannot lower 'sim.proc.print' to sv.fwrite: " );
490+ diag << failureReason;
491+ result = failure ();
492+ }
493+ });
494+ return result;
495+ }
496+ }; // namespace
497+
284498// A helper struct to lower DPI function/call.
285499struct LowerDPIFunc {
286500 llvm::DenseMap<StringAttr, StringAttr> symbolToFragment;
@@ -480,9 +694,16 @@ struct SimToSVPass : public circt::impl::LowerSimToSVBase<SimToSVPass> {
480694 if (moveOpsIntoIfdefGuardsAndProcesses (module ))
481695 usedSynthesisMacro = true ;
482696
697+ if (failed (validatePrintLowering (module )))
698+ return failure ();
699+
483700 SimConversionState state;
484701 ConversionTarget target (*context);
485702 target.addIllegalDialect <SimDialect>();
703+ target.addLegalOp <FormatLiteralOp, FormatHierPathOp, FormatCharOp,
704+ FormatDecOp, FormatHexOp, FormatOctOp, FormatBinOp,
705+ FormatScientificOp, FormatFloatOp, FormatGeneralOp,
706+ FormatStringConcatOp>();
486707 target.addLegalDialect <sv::SVDialect>();
487708 target.addLegalDialect <hw::HWDialect>();
488709 target.addLegalDialect <seq::SeqDialect>();
@@ -496,11 +717,15 @@ struct SimToSVPass : public circt::impl::LowerSimToSVBase<SimToSVPass> {
496717 patterns.add <TerminateOp>(convert);
497718 patterns.add <PauseOp>(convert);
498719 patterns.add <DPICallLowering>(context, state);
720+ patterns.add <PrintFormattedProcLowering>(context, state);
499721 auto result = applyPartialConversion (module , target, std::move (patterns));
500722
501723 if (failed (result))
502724 return result;
503725
726+ mlir::IRRewriter rewriter (module );
727+ (void )mlir::runRegionDCE (rewriter, module ->getRegions ());
728+
504729 // Set the emit fragment.
505730 lowerDPIFunc.addFragments (module , state.dpiCallees .takeVector ());
506731
0 commit comments