CIRCT 20.0.0git
Loading...
Searching...
No Matches
FormatStrings.cpp
Go to the documentation of this file.
1//===- FormatStrings.cpp - Verilog format string conversion ---------------===//
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
10#include "slang/text/SFormat.h"
11
12using namespace mlir;
13using namespace circt;
14using namespace ImportVerilog;
15using moore::IntAlign;
16using moore::IntFormat;
17using moore::IntPadding;
18using slang::SFormat::FormatOptions;
19
20namespace {
21struct FormatStringParser {
22 Context &context;
23 OpBuilder &builder;
24 /// The remaining arguments to be parsed.
25 ArrayRef<const slang::ast::Expression *> arguments;
26 /// The current location to use for ops and diagnostics.
27 Location loc;
28 /// The default format for integer arguments not covered by a format string
29 /// literal.
30 IntFormat defaultFormat;
31 /// The interpolated string fragments that will be concatenated using a
32 /// `moore.fmt.concat` op.
33 SmallVector<Value> fragments;
34
35 FormatStringParser(Context &context,
36 ArrayRef<const slang::ast::Expression *> arguments,
37 Location loc, IntFormat defaultFormat)
38 : context(context), builder(context.builder), arguments(arguments),
39 loc(loc), defaultFormat(defaultFormat) {}
40
41 /// Entry point to the format string parser.
42 FailureOr<Value> parse(bool appendNewline) {
43 while (!arguments.empty()) {
44 const auto &arg = *arguments[0];
45 arguments = arguments.drop_front();
46 if (arg.kind == slang::ast::ExpressionKind::EmptyArgument)
47 continue;
48 loc = context.convertLocation(arg.sourceRange);
49 if (auto *lit = arg.as_if<slang::ast::StringLiteral>()) {
50 if (failed(parseFormat(lit->getValue())))
51 return failure();
52 } else {
53 if (failed(emitDefault(arg)))
54 return failure();
55 }
56 }
57
58 // Append the optional newline.
59 if (appendNewline)
60 emitLiteral("\n");
61
62 // Concatenate all string fragments into one formatted string, or return an
63 // empty literal if no fragments were generated.
64 if (fragments.empty())
65 return Value{};
66 if (fragments.size() == 1)
67 return fragments[0];
68 return builder.create<moore::FormatConcatOp>(loc, fragments).getResult();
69 }
70
71 /// Parse a format string literal and consume and format the arguments
72 /// corresponding to the format specifiers it contains.
73 LogicalResult parseFormat(StringRef format) {
74 bool anyFailure = false;
75 auto onText = [&](auto text) {
76 if (anyFailure)
77 return;
78 emitLiteral(text);
79 };
80 auto onArg = [&](auto specifier, auto offset, auto len,
81 const auto &options) {
82 if (anyFailure)
83 return;
84 if (failed(emitArgument(specifier, format.substr(offset, len), options)))
85 anyFailure = true;
86 };
87 auto onError = [&](auto, auto, auto, auto) {
88 assert(false && "Slang should have already reported all errors");
89 };
90 slang::SFormat::parse(format, onText, onArg, onError);
91 return failure(anyFailure);
92 }
93
94 /// Emit a string literal that requires no additional formatting.
95 void emitLiteral(StringRef literal) {
96 fragments.push_back(builder.create<moore::FormatLiteralOp>(loc, literal));
97 }
98
99 /// Consume the next argument from the list and emit it according to the given
100 /// format specifier.
101 LogicalResult emitArgument(char specifier, StringRef fullSpecifier,
102 const FormatOptions &options) {
103 auto specifierLower = std::tolower(specifier);
104
105 // Special handling for format specifiers that consume no argument.
106 if (specifierLower == 'm' || specifierLower == 'l')
107 return mlir::emitError(loc)
108 << "unsupported format specifier `" << fullSpecifier << "`";
109
110 // Consume the next argument, which will provide the value to be
111 // formatted.
112 assert(!arguments.empty() && "Slang guarantees correct arg count");
113 const auto &arg = *arguments[0];
114 arguments = arguments.drop_front();
115 auto argLoc = context.convertLocation(arg.sourceRange);
116
117 // Handle the different formatting options.
118 // See IEEE 1800-2017 ยง 21.2.1.2 "Format specifications".
119 switch (specifierLower) {
120 case 'b':
121 return emitInteger(arg, options, IntFormat::Binary);
122 case 'o':
123 return emitInteger(arg, options, IntFormat::Octal);
124 case 'd':
125 return emitInteger(arg, options, IntFormat::Decimal);
126 case 'h':
127 case 'x':
128 return emitInteger(arg, options,
129 std::isupper(specifier) ? IntFormat::HexUpper
130 : IntFormat::HexLower);
131
132 case 's':
133 // Simplified handling for literals.
134 if (auto *lit = arg.as_if<slang::ast::StringLiteral>()) {
135 if (options.width)
136 return mlir::emitError(loc)
137 << "string format specifier with width not supported";
138 emitLiteral(lit->getValue());
139 return success();
140 }
141 return mlir::emitError(argLoc)
142 << "expression cannot be formatted as string";
143
144 default:
145 return mlir::emitError(loc)
146 << "unsupported format specifier `" << fullSpecifier << "`";
147 }
148 }
149
150 /// Emit an integer value with the given format.
151 LogicalResult emitInteger(const slang::ast::Expression &arg,
152 const FormatOptions &options, IntFormat format) {
153 auto value =
155 if (!value)
156 return failure();
157
158 // Determine the width to which the formatted integer should be padded.
159 unsigned width;
160 if (options.width) {
161 width = *options.width;
162 } else {
163 width = cast<moore::IntType>(value.getType()).getWidth();
164 if (format == IntFormat::Octal)
165 // 3 bits per octal digit
166 width = (width + 2) / 3;
167 else if (format == IntFormat::HexLower || format == IntFormat::HexUpper)
168 // 4 bits per hex digit
169 width = (width + 3) / 4;
170 else if (format == IntFormat::Decimal)
171 // ca. 3.322 bits per decimal digit (ln(10)/ln(2))
172 width = std::ceil(width * std::log(2) / std::log(10));
173 }
174
175 // Determine the alignment and padding.
176 auto alignment = options.leftJustify ? IntAlign::Left : IntAlign::Right;
177 auto padding =
178 format == IntFormat::Decimal ? IntPadding::Space : IntPadding::Zero;
179
180 fragments.push_back(builder.create<moore::FormatIntOp>(
181 loc, value, format, width, alignment, padding));
182 return success();
183 }
184
185 /// Emit an expression argument with the appropriate default formatting.
186 LogicalResult emitDefault(const slang::ast::Expression &expr) {
187 FormatOptions options;
188 return emitInteger(expr, options, defaultFormat);
189 }
190};
191} // namespace
192
194 slang::span<const slang::ast::Expression *const> arguments, Location loc,
195 IntFormat defaultFormat, bool appendNewline) {
196 FormatStringParser parser(*this, ArrayRef(arguments.data(), arguments.size()),
197 loc, defaultFormat);
198 return parser.parse(appendNewline);
199}
assert(baseType &&"element must be base type")
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
FailureOr< Value > convertFormatString(slang::span< const slang::ast::Expression *const > arguments, Location loc, moore::IntFormat defaultFormat=moore::IntFormat::Decimal, bool appendNewline=false)
Convert a list of string literal arguments with formatting specifiers and arguments to be interpolate...
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
Value convertToSimpleBitVector(Value value)
Helper function to convert a value to its simple bit vector representation, if it has one.
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.