Skip to content

Commit 197dc38

Browse files
[Sim][MooreToCore] Added width, alignment and padding support for moore.fmt.int (#9390)
The standard wasn't super clear about the semantics, so here's what I figured out after twiddling with some test cases using Icarus: 1. For Octal, Hexadecimal, and Binary numbers, they are padded with 0s upto the minimum width that a number of that datatype would fit into (eg `i22` would fit into `(22 + 2)/3 = 8` octal bytes, so 106 in octal would be printed as `00000152`), and then any additional bytes are padded with spaces (so `$write("%10o",val)` would be ` 00000152`). Even contraction would have the whole 0-padded value be printed(`$write ("%3o",val)` would _still_ be `00000152`). A similar logic follows for hexadecimal and binary 2. For decimal numbers, the default padding is spaces themselves, but they can contract too. `$write("%4d",val)` (where val is an `i22` with value 106) would cause ` 106` to be printed (an extra space to fill up the padding of 4). Expansion is pretty straightforward, and similar to Octals. Signed numbers can also require an extra character to be printed sometimes. This PR implements the above, and adds some test cases to substantiate them.
1 parent ca9ed6b commit 197dc38

File tree

11 files changed

+377
-217
lines changed

11 files changed

+377
-217
lines changed

include/circt/Dialect/Moore/MooreOps.td

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,16 +1997,18 @@ def FormatIntOp : MooreOp<"fmt.int", [Pure]> {
19971997
let arguments = (ins
19981998
IntType:$value,
19991999
IntFormatAttr:$format,
2000-
I32Attr:$width,
20012000
IntAlignAttr:$alignment,
2002-
IntPaddingAttr:$padding
2001+
IntPaddingAttr:$padding,
2002+
OptionalAttr<I32Attr>:$specifierWidth,
2003+
UnitAttr:$isSigned
20032004
);
20042005
let results = (outs FormatStringType:$result);
20052006
let assemblyFormat = [{
2006-
$format $value `,`
2007-
`width` $width `,`
2007+
$format $value `,`
20082008
`align` $alignment `,`
20092009
`pad` $padding
2010+
(`width` $specifierWidth^)?
2011+
(`signed` $isSigned^)?
20102012
attr-dict `:` type($value)
20112013
}];
20122014
}

include/circt/Dialect/Sim/SimOps.td

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,23 @@ def FormatHexOp : SimOp<"fmt.hex", [Pure]> {
181181
}];
182182

183183

184-
let arguments = (ins AnyInteger:$value);
184+
let arguments = (ins AnyInteger:$value,
185+
BoolAttr:$isHexUppercase,
186+
DefaultValuedAttr<BoolAttr,"false">:$isLeftAligned,
187+
DefaultValuedAttr<I8Attr,"48">:$paddingChar,
188+
OptionalAttr<I32Attr>:$specifierWidth);
185189
let results = (outs FormatStringType:$result);
186190

187191
let hasFolder = true;
188192

189-
let assemblyFormat = "$value attr-dict `:` qualified(type($value))";
193+
let assemblyFormat = [{
194+
$value `,`
195+
`isUpper` $isHexUppercase
196+
(`isLeftAligned` $isLeftAligned^)?
197+
(`paddingChar` $paddingChar^)?
198+
(`specifierWidth` $specifierWidth^)?
199+
attr-dict `:` qualified(type($value))
200+
}];
190201
}
191202

192203
def FormatOctOp : SimOp<"fmt.oct", [Pure]> {
@@ -201,12 +212,22 @@ def FormatOctOp : SimOp<"fmt.oct", [Pure]> {
201212
}];
202213

203214

204-
let arguments = (ins AnyInteger:$value);
215+
let arguments = (ins AnyInteger:$value,
216+
DefaultValuedAttr<BoolAttr,"false">:$isLeftAligned,
217+
DefaultValuedAttr<I8Attr,"48">:$paddingChar,
218+
OptionalAttr<I32Attr>:$specifierWidth);
219+
205220
let results = (outs FormatStringType:$result);
206221

207222
let hasFolder = true;
208223

209-
let assemblyFormat = "$value attr-dict `:` qualified(type($value))";
224+
let assemblyFormat = [{
225+
$value
226+
(`isLeftAligned` $isLeftAligned^)?
227+
(`paddingChar` $paddingChar^)?
228+
(`specifierWidth` $specifierWidth^)?
229+
attr-dict `:` qualified(type($value))
230+
}];
210231
}
211232

212233
def FormatBinOp : SimOp<"fmt.bin", [Pure]> {
@@ -219,12 +240,21 @@ def FormatBinOp : SimOp<"fmt.bin", [Pure]> {
219240
the empty string. No further prefix will be added.
220241
}];
221242

222-
let arguments = (ins AnyInteger:$value);
243+
let arguments = (ins AnyInteger:$value,
244+
DefaultValuedAttr<BoolAttr,"false">:$isLeftAligned,
245+
DefaultValuedAttr<I8Attr,"48">:$paddingChar,
246+
OptionalAttr<I32Attr>:$specifierWidth);
223247
let results = (outs FormatStringType:$result);
224248

225249
let hasFolder = true;
226250

227-
let assemblyFormat = "$value attr-dict `:` qualified(type($value))";
251+
let assemblyFormat = [{
252+
$value
253+
(`isLeftAligned` $isLeftAligned^)?
254+
(`paddingChar` $paddingChar^)?
255+
(`specifierWidth` $specifierWidth^)?
256+
attr-dict `:` qualified(type($value))
257+
}];
228258
}
229259

230260

@@ -252,13 +282,22 @@ def FormatDecOp : SimOp<"fmt.dec", [Pure]> {
252282
Backends are recommended to not exceed the required amount of padding.
253283
}];
254284

255-
let arguments = (ins AnyInteger:$value, UnitAttr:$isSigned);
285+
let arguments = (ins AnyInteger:$value,
286+
DefaultValuedAttr<BoolAttr,"false">:$isLeftAligned,
287+
DefaultValuedAttr<I8Attr,"32">:$paddingChar,
288+
OptionalAttr<I32Attr>:$specifierWidth,
289+
UnitAttr:$isSigned);
256290
let results = (outs FormatStringType:$result);
257291

258292
let hasFolder = true;
259293

260294
let assemblyFormat = [{
261-
(`signed` $isSigned^)? $value attr-dict `:` qualified(type($value))
295+
$value
296+
(`isLeftAligned` $isLeftAligned^)?
297+
(`paddingChar` $paddingChar^)?
298+
(`specifierWidth` $specifierWidth^)?
299+
(`signed` $isSigned^)?
300+
attr-dict `:` qualified(type($value))
262301
}];
263302

264303
let extraClassDeclaration = [{

lib/Conversion/ImportVerilog/FormatStrings.cpp

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ struct FormatStringParser {
153153
Type intTy = {};
154154
Value val;
155155
auto rVal = context.convertRvalueExpression(arg);
156+
// To infer whether or not the value is signed while printing as a decimal
157+
// Since it only matters if it's a decimal, we add `format ==
158+
// IntFormat::Decimal`
159+
bool isSigned = arg.type->isSigned() && format == IntFormat::Decimal;
156160
if (!rVal)
157161
return failure();
158162

@@ -184,30 +188,17 @@ struct FormatStringParser {
184188
if (!value)
185189
return failure();
186190

187-
// Determine the width to which the formatted integer should be padded.
188-
unsigned width;
189-
if (options.width) {
190-
width = *options.width;
191-
} else {
192-
width = cast<moore::IntType>(value.getType()).getWidth();
193-
if (format == IntFormat::Octal)
194-
// 3 bits per octal digit
195-
width = (width + 2) / 3;
196-
else if (format == IntFormat::HexLower || format == IntFormat::HexUpper)
197-
// 4 bits per hex digit
198-
width = (width + 3) / 4;
199-
else if (format == IntFormat::Decimal)
200-
// ca. 3.322 bits per decimal digit (ln(10)/ln(2))
201-
width = std::ceil(width * std::log(2) / std::log(10));
202-
}
203-
204191
// Determine the alignment and padding.
205192
auto alignment = options.leftJustify ? IntAlign::Left : IntAlign::Right;
206193
auto padding =
207194
format == IntFormat::Decimal ? IntPadding::Space : IntPadding::Zero;
195+
IntegerAttr widthAttr = nullptr;
196+
if (options.width) {
197+
widthAttr = builder.getI32IntegerAttr(*options.width);
198+
}
208199

209-
fragments.push_back(moore::FormatIntOp::create(builder, loc, value, format,
210-
width, alignment, padding));
200+
fragments.push_back(moore::FormatIntOp::create(
201+
builder, loc, value, format, alignment, padding, widthAttr, isSigned));
211202
return success();
212203
}
213204

@@ -250,7 +241,8 @@ struct FormatStringParser {
250241
auto alignment = options.leftJustify ? IntAlign::Left : IntAlign::Right;
251242
auto padding = options.zeroPad ? IntPadding::Zero : IntPadding::Space;
252243
fragments.push_back(moore::FormatIntOp::create(
253-
builder, loc, value, IntFormat::Decimal, width, alignment, padding));
244+
builder, loc, value, IntFormat::Decimal, alignment, padding,
245+
builder.getI32IntegerAttr(width)));
254246
return success();
255247
}
256248

lib/Conversion/MooreToCore/MooreToCore.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,20 +2009,37 @@ struct FormatIntOpConversion : public OpConversionPattern<FormatIntOp> {
20092009
LogicalResult
20102010
matchAndRewrite(FormatIntOp op, OpAdaptor adaptor,
20112011
ConversionPatternRewriter &rewriter) const override {
2012-
// TODO: These should honor the width, alignment, and padding.
2012+
2013+
char padChar = adaptor.getPadding() == IntPadding::Space ? 32 : 48;
2014+
IntegerAttr padCharAttr = rewriter.getI8IntegerAttr(padChar);
2015+
auto widthAttr = adaptor.getSpecifierWidthAttr();
2016+
2017+
bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2018+
BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2019+
20132020
switch (op.getFormat()) {
20142021
case IntFormat::Decimal:
2015-
rewriter.replaceOpWithNewOp<sim::FormatDecOp>(op, adaptor.getValue());
2022+
rewriter.replaceOpWithNewOp<sim::FormatDecOp>(
2023+
op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr,
2024+
adaptor.getIsSignedAttr());
20162025
return success();
20172026
case IntFormat::Binary:
2018-
rewriter.replaceOpWithNewOp<sim::FormatBinOp>(op, adaptor.getValue());
2027+
rewriter.replaceOpWithNewOp<sim::FormatBinOp>(
2028+
op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
20192029
return success();
20202030
case IntFormat::Octal:
2021-
rewriter.replaceOpWithNewOp<sim::FormatOctOp>(op, adaptor.getValue());
2031+
rewriter.replaceOpWithNewOp<sim::FormatOctOp>(
2032+
op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
20222033
return success();
20232034
case IntFormat::HexLower:
2035+
rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2036+
op, adaptor.getValue(), rewriter.getBoolAttr(false),
2037+
isLeftAlignedAttr, padCharAttr, widthAttr);
2038+
return success();
20242039
case IntFormat::HexUpper:
2025-
rewriter.replaceOpWithNewOp<sim::FormatHexOp>(op, adaptor.getValue());
2040+
rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2041+
op, adaptor.getValue(), rewriter.getBoolAttr(true), isLeftAlignedAttr,
2042+
padCharAttr, widthAttr);
20262043
return success();
20272044
default:
20282045
return rewriter.notifyMatchFailure(op, "unsupported int format");

lib/Dialect/Sim/SimOps.cpp

Lines changed: 73 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,57 @@ using namespace mlir;
2323
using namespace circt;
2424
using namespace sim;
2525

26+
static StringAttr formatIntegersByRadix(MLIRContext *ctx, unsigned radix,
27+
const Attribute &value,
28+
bool isUpperCase, bool isLeftAligned,
29+
char paddingChar,
30+
std::optional<int> specifierWidth,
31+
bool isSigned = false) {
32+
33+
if (auto intAttr = llvm::dyn_cast_or_null<IntegerAttr>(value)) {
34+
SmallVector<char, 32> strBuf;
35+
intAttr.getValue().toString(strBuf, radix, isSigned, false, isUpperCase);
36+
unsigned width = intAttr.getType().getIntOrFloatBitWidth();
37+
unsigned padWidth;
38+
switch (radix) {
39+
case 2:
40+
padWidth = width;
41+
break;
42+
case 8:
43+
padWidth = (width + 2) / 3;
44+
break;
45+
case 16:
46+
padWidth = (width + 3) / 4;
47+
break;
48+
default:
49+
padWidth = width;
50+
break;
51+
}
52+
53+
unsigned numSpaces = 0;
54+
if (specifierWidth.has_value() &&
55+
(specifierWidth.value() >
56+
std::max(padWidth, static_cast<unsigned>(strBuf.size())))) {
57+
numSpaces = std::max(
58+
0U, specifierWidth.value() -
59+
std::max(padWidth, static_cast<unsigned>(strBuf.size())));
60+
}
61+
62+
SmallVector<char, 1> spacePadding(numSpaces, ' ');
63+
64+
padWidth = padWidth > strBuf.size() ? padWidth - strBuf.size() : 0;
65+
66+
SmallVector<char, 32> padding(padWidth, paddingChar);
67+
if (isLeftAligned) {
68+
return StringAttr::get(ctx, Twine(padding) + Twine(strBuf) +
69+
Twine(spacePadding));
70+
}
71+
return StringAttr::get(ctx, Twine(spacePadding) + Twine(padding) +
72+
Twine(strBuf));
73+
}
74+
return {};
75+
}
76+
2677
ParseResult DPIFuncOp::parse(OpAsmParser &parser, OperationState &result) {
2778
auto builder = parser.getBuilder();
2879
// Parse visibility.
@@ -120,13 +171,21 @@ OpFoldResult FormatDecOp::fold(FoldAdaptor adaptor) {
120171

121172
if (auto intAttr = llvm::dyn_cast_or_null<IntegerAttr>(adaptor.getValue())) {
122173
SmallVector<char, 16> strBuf;
123-
intAttr.getValue().toString(strBuf, 10U, getIsSigned());
174+
intAttr.getValue().toString(strBuf, 10U, adaptor.getIsSigned());
175+
unsigned padWidth;
176+
if (adaptor.getSpecifierWidth().has_value()) {
177+
padWidth = adaptor.getSpecifierWidth().value();
178+
} else {
179+
unsigned width = intAttr.getType().getIntOrFloatBitWidth();
180+
padWidth = FormatDecOp::getDecimalWidth(width, adaptor.getIsSigned());
181+
}
124182

125-
unsigned width = intAttr.getType().getIntOrFloatBitWidth();
126-
unsigned padWidth = FormatDecOp::getDecimalWidth(width, getIsSigned());
127183
padWidth = padWidth > strBuf.size() ? padWidth - strBuf.size() : 0;
128184

129-
SmallVector<char, 8> padding(padWidth, ' ');
185+
SmallVector<char, 10> padding(padWidth, adaptor.getPaddingChar());
186+
if (adaptor.getIsLeftAligned()) {
187+
return StringAttr::get(getContext(), Twine(strBuf) + Twine(padding));
188+
}
130189
return StringAttr::get(getContext(), Twine(padding) + Twine(strBuf));
131190
}
132191
return {};
@@ -136,61 +195,28 @@ OpFoldResult FormatHexOp::fold(FoldAdaptor adaptor) {
136195
if (getValue().getType() == IntegerType::get(getContext(), 0U))
137196
return StringAttr::get(getContext(), "");
138197

139-
if (auto intAttr = llvm::dyn_cast_or_null<IntegerAttr>(adaptor.getValue())) {
140-
SmallVector<char, 8> strBuf;
141-
intAttr.getValue().toString(strBuf, 16U, /*Signed*/ false,
142-
/*formatAsCLiteral*/ false,
143-
/*UpperCase*/ false);
144-
145-
unsigned width = intAttr.getType().getIntOrFloatBitWidth();
146-
unsigned padWidth = width / 4;
147-
if (width % 4 != 0)
148-
padWidth++;
149-
padWidth = padWidth > strBuf.size() ? padWidth - strBuf.size() : 0;
150-
151-
SmallVector<char, 8> padding(padWidth, '0');
152-
return StringAttr::get(getContext(), Twine(padding) + Twine(strBuf));
153-
}
154-
return {};
198+
return formatIntegersByRadix(
199+
getContext(), 16U, adaptor.getValue(), adaptor.getIsHexUppercase(),
200+
adaptor.getIsLeftAligned(), adaptor.getPaddingChar(),
201+
adaptor.getSpecifierWidth());
155202
}
156203

157204
OpFoldResult FormatOctOp::fold(FoldAdaptor adaptor) {
158205
if (getValue().getType() == IntegerType::get(getContext(), 0U))
159206
return StringAttr::get(getContext(), "");
160207

161-
if (auto intAttr = llvm::dyn_cast_or_null<IntegerAttr>(adaptor.getValue())) {
162-
SmallVector<char, 11> strBuf;
163-
intAttr.getValue().toString(strBuf, 8U, /*Signed*/ false,
164-
/*formatAsCLiteral*/ false,
165-
/*UpperCase*/ false);
166-
167-
unsigned width = intAttr.getType().getIntOrFloatBitWidth();
168-
unsigned padWidth = width / 3;
169-
if (width % 3 != 0)
170-
padWidth++;
171-
padWidth = padWidth > strBuf.size() ? padWidth - strBuf.size() : 0;
172-
173-
SmallVector<char, 11> padding(padWidth, '0');
174-
return StringAttr::get(getContext(), Twine(padding) + Twine(strBuf));
175-
}
176-
return {};
208+
return formatIntegersByRadix(
209+
getContext(), 8U, adaptor.getValue(), false, adaptor.getIsLeftAligned(),
210+
adaptor.getPaddingChar(), adaptor.getSpecifierWidth());
177211
}
178212

179213
OpFoldResult FormatBinOp::fold(FoldAdaptor adaptor) {
180214
if (getValue().getType() == IntegerType::get(getContext(), 0U))
181215
return StringAttr::get(getContext(), "");
182216

183-
if (auto intAttr = llvm::dyn_cast_or_null<IntegerAttr>(adaptor.getValue())) {
184-
SmallVector<char, 32> strBuf;
185-
intAttr.getValue().toString(strBuf, 2U, false);
186-
187-
unsigned width = intAttr.getType().getIntOrFloatBitWidth();
188-
unsigned padWidth = width > strBuf.size() ? width - strBuf.size() : 0;
189-
190-
SmallVector<char, 32> padding(padWidth, '0');
191-
return StringAttr::get(getContext(), Twine(padding) + Twine(strBuf));
192-
}
193-
return {};
217+
return formatIntegersByRadix(
218+
getContext(), 2U, adaptor.getValue(), false, adaptor.getIsLeftAligned(),
219+
adaptor.getPaddingChar(), adaptor.getSpecifierWidth());
194220
}
195221

196222
OpFoldResult FormatCharOp::fold(FoldAdaptor adaptor) {

0 commit comments

Comments
 (0)