CIRCT  20.0.0git
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 
12 using namespace mlir;
13 using namespace circt;
14 using namespace ImportVerilog;
15 using moore::IntAlign;
16 using moore::IntFormat;
17 using moore::IntPadding;
18 using slang::SFormat::FormatOptions;
19 
20 namespace {
21 struct 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 
193 FailureOr<Value> Context::convertFormatString(
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")
static void log(char *epId, bool toClient, const MessageData &msg)
Emit the contents of 'msg' to the log file in hex.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
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.