-
Notifications
You must be signed in to change notification settings - Fork 417
[Sim][MooreToCore] Added width, alignment and padding support for moore.fmt.int
#9390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
fabianschuiki
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for reworking this and bringing it up to par with the SV spec 🥳👍. Just a few minor nits and potential to reduce code duplication a bit.
| mlir::IntegerAttr widthAttr = nullptr; | ||
| if (options.width) { | ||
| widthAttr = builder.getI32IntegerAttr(*options.width); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit:
| mlir::IntegerAttr widthAttr = nullptr; | |
| if (options.width) { | |
| widthAttr = builder.getI32IntegerAttr(*options.width); | |
| } | |
| IntegerAttr widthAttr = nullptr; | |
| if (options.width) | |
| widthAttr = builder.getI32IntegerAttr(*options.width); |
| // TODO: These should honor the width, alignment, and padding. | ||
|
|
||
| char padChar = adaptor.getPadding() == IntPadding::Space ? 32 : 48; | ||
| mlir::IntegerAttr padCharAttr = rewriter.getI8IntegerAttr(padChar); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| mlir::IntegerAttr padCharAttr = rewriter.getI8IntegerAttr(padChar); | |
| IntegerAttr padCharAttr = rewriter.getI8IntegerAttr(padChar); |
| auto widthAttr = adaptor.getSpecifierWidthAttr(); | ||
|
|
||
| bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left; | ||
| mlir::BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| mlir::BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned); | |
| BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned); |
lib/Dialect/Sim/SimOps.cpp
Outdated
| SmallVector<char, 8> strBuf; | ||
| intAttr.getValue().toString(strBuf, 16U, /*Signed*/ false, | ||
| /*formatAsCLiteral*/ false, | ||
| /*UpperCase*/ false); | ||
| /*UpperCase*/ adaptor.getIsHexUppercase()); | ||
|
|
||
| unsigned width = intAttr.getType().getIntOrFloatBitWidth(); | ||
| unsigned padWidth = width / 4; | ||
| if (width % 4 != 0) | ||
| padWidth++; | ||
|
|
||
| unsigned numSpaces; | ||
| if (adaptor.getSpecifierWidth().has_value()) { | ||
| numSpaces = std::max( | ||
| 0U, adaptor.getSpecifierWidth().value() - | ||
| std::max(padWidth, static_cast<unsigned>(strBuf.size()))); | ||
| } else { | ||
| numSpaces = 0; | ||
| } | ||
|
|
||
| SmallVector<char, 1> spacePadding(numSpaces, ' '); | ||
| padWidth = padWidth > strBuf.size() ? padWidth - strBuf.size() : 0; | ||
|
|
||
| SmallVector<char, 8> padding(padWidth, '0'); | ||
| return StringAttr::get(getContext(), Twine(padding) + Twine(strBuf)); | ||
| SmallVector<char, 8> padding(padWidth, adaptor.getPaddingChar()); | ||
| if (adaptor.getIsLeftAligned()) { | ||
| return StringAttr::get(getContext(), Twine(padding) + Twine(strBuf) + | ||
| Twine(spacePadding)); | ||
| } | ||
| return StringAttr::get(getContext(), Twine(spacePadding) + Twine(padding) + | ||
| Twine(strBuf)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any chance you could factor this code out into a helper function that unifies formatting for binary, octal, hexadecimal, and decimal? I think this only differs by the base passed to the int's toString().
| // CHECK: sim.fmt.hex %arg0 : i42 | ||
| moore.fmt.int hex_upper %arg0, width 42, align right, pad space : i42 | ||
| // CHECK: sim.fmt.dec %arg0 {specifierWidth = 42 : i32} : i42 | ||
| moore.fmt.int decimal %arg0, align right, pad space, width 42 : i42 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test for signed but no width after this?
// CHECK: sim.fmt.dec %arg0 {isSigned = true} : i42
moore.fmt.int decimal %arg0, align right, pad space, signed : i42| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Stray whitespace change. Keep a newline at the end of the file.
test/Dialect/Moore/basic.mlir
Outdated
| // CHECK: moore.fmt.int decimal %arg1, align left, pad zero, width 42, signed : i42 | ||
| moore.fmt.int decimal %arg1, align left, pad zero, width 42, signed : i42 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you check that the parser also eats the following? I suspect it might not 😬:
// CHECK: moore.fmt.int decimal %arg1, align left, pad zero, signed : i42
moore.fmt.int decimal %arg1, align left, pad zero, signed : i42| %wrd = sim.fmt.dec %1 : i16 | ||
| %wld = sim.fmt.dec %1 {isLeftAligned = true}: i16 | ||
| %wrds = sim.fmt.dec %1 {isSigned} : i16 | ||
| %wlds = sim.fmt.dec %1 {isLeftAligned = true, isSigned}: i16 | ||
| %wrdp = sim.fmt.dec %1 {specifierWidth = 10 : i32}: i16 | ||
| %wldp = sim.fmt.dec %1 {isLeftAligned = true, specifierWidth = 10 : i32}: i16 | ||
| %wrdps = sim.fmt.dec %1 {specifierWidth = 10 : i32, isSigned}: i16 | ||
| %wldps = sim.fmt.dec %1 {isLeftAligned = true, specifierWidth = 10 : i32, isSigned}: i16 | ||
| %dec = sim.fmt.concat (%wrd, %comma, %wld, %comma, %wrds, %comma, %wlds, %comma, %wrdp, %comma, %wldp, %comma, %wrdps, %comma, %wldps) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for testing all this! 🖐️
| // CHECK-DAG: %[[FD:.*]] = sim.fmt.dec %[[ARG]] : i32 | ||
| // CHECK-DAG: %[[FH:.*]] = sim.fmt.hex %[[ARG]] : i32 | ||
| // CHECK-DAG: %[[FO:.*]] = sim.fmt.oct %[[ARG]] : i32 | ||
| // CHECK-DAG: %[[FB:.*]] = sim.fmt.bin %[[ARG]] {specifierWidth = 32 : i32} : i32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This makes me wonder if we should just call the optional width width instead of specifierWidth. No need to change anything in the PR 😁 Just a thought for a later refactoring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My rationale to go with specifierWidth was to disambiguate the datatype width (which is used when you don't add anything to the specifier, eg in %d and is inferred in SimOps.cpp) vs the specifier width (which is used when you explicitly specify the width you want, eg %10d, which we inherit from the input file itself). Should we still rename it to width?
b8dfbad to
4314042
Compare
fabianschuiki
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all the tweaks and factoring out the code 🥳 🙌. I've rebased this onto main and fixed a few breakages with the time formatting that landed on main in the meantime. Going to merge this as soon as CI is happy!
The standard wasn't super clear about the semantics, so here's what I figured out after twiddling with some test cases using Icarus:
i22would fit into(22 + 2)/3 = 8octal bytes, so 106 in octal would be printed as00000152), and then any additional bytes are padded with spaces (so$write("%10o",val)would be00000152). Even contraction would have the whole 0-padded value be printed ($write("%3o",val)would still be00000152). A similar logic follows for hexadecimal and binary$write("%4d",val)(where val is ani22with value 106) would cause106to 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. Happy to add more tests if needed as well