Skip to content
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

[FIRRTL] Fix sign bit truncation in constant parser #6794

Merged
merged 1 commit into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/Dialect/FIRRTL/FIRRTLOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3933,8 +3933,10 @@ ParseResult ConstantOp::parse(OpAsmParser &parser, OperationState &result) {
} else if (width < value.getBitWidth()) {
// The parser can return an unnecessarily wide result with leading
// zeros. This isn't a problem, but truncating off bits is bad.
if (value.getNumSignBits() < value.getBitWidth() - width)
return parser.emitError(loc, "constant too large for result type ")
unsigned neededBits = value.isNegative() ? value.getSignificantBits()
: value.getActiveBits();
if (width < neededBits)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we actually allowed to remove the leading zero if it is a positive value (which would happen here because getActiveBits == bitWidth - numLeadingZeros)? I think the APInt is just used as a signless bitvector and the return type actually determines how this bitvector should be interpreted. If the type is sint then it's two's complement. E.g., we have the binary number 01 (1 in decimal) and we truncate it to 1 then it's -1 in decimal. 1 (decimal) cannot be represented in a 1-bit signed integer. If the APInt is interpreted as unsigned, then we can truncate to 1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to look at the parsed value and see if that value as it was parsed can be truncated to the bit width. If it's positive it has a leading 0 bit (the binary 0b10 actually parses into APInt 010), and if it's negative it has a leading 1 bit. That means we can always check if the user typed a negative or positive value. Then this check ensures that by cutting off leading bits we don't change the interpretation of the number, or rather, that there is no overflow.

return parser.emitError(loc, "constant out of range for result type ")
<< resultType;
value = value.trunc(width);
}
Expand Down
13 changes: 11 additions & 2 deletions test/Dialect/FIRRTL/errors.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ firrtl.module @Foo() {

firrtl.circuit "Foo" {
firrtl.module @Foo() {
// expected-error @+1 {{constant too large for result type}}
// expected-error @+1 {{constant out of range for result type}}
firrtl.constant 100 : !firrtl.uint<4>
}
}
Expand All @@ -148,13 +148,22 @@ firrtl.module @Foo() {

firrtl.circuit "Foo" {
firrtl.module @Foo() {
// expected-error @+1 {{constant too large for result type}}
// expected-error @+1 {{constant out of range for result type}}
firrtl.constant -100 : !firrtl.sint<4>
}
}

// -----

firrtl.circuit "Foo" {
firrtl.module @Foo() {
// expected-error @+1 {{constant out of range for result type}}
firrtl.constant -2 : !firrtl.sint<1>
}
}

// -----

firrtl.circuit "Foo" {
firrtl.module @Foo() {
// expected-error @+1 {{special constants can only be 0 or 1}}
Expand Down
Loading