Skip to content

core: Add float-semantics to support low-precision - #6254

Merged
n-io merged 23 commits into
mainfrom
nicolai/float-semantics
Jul 21, 2026
Merged

core: Add float-semantics to support low-precision#6254
n-io merged 23 commits into
mainfrom
nicolai/float-semantics

Conversation

@n-io

@n-io n-io commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Extends support for reduced-precision floating point values by adding support for reduced-precision literals, and a central mechanism to implement a variety of different flavours. The types itself were added in #6250. This PR adds support for literals, by introducing the FloatSemantics class based on MLIR's fltSemantics concept (alongside fltNonfiniteBehavior and fltNanEncoding).

Stack:

@n-io n-io self-assigned this Jul 14, 2026
@n-io n-io added the core xDSL core (ir, textual format, ...) label Jul 14, 2026
@codecov

codecov Bot commented Jul 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.60140% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.97%. Comparing base (999233d) to head (5c22de5).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
xdsl/dialects/builtin.py 98.60% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6254      +/-   ##
==========================================
+ Coverage   86.94%   86.97%   +0.02%     
==========================================
  Files         431      431              
  Lines       64924    65064     +140     
  Branches     7419     7451      +32     
==========================================
+ Hits        56450    56587     +137     
- Misses       6903     6904       +1     
- Partials     1571     1573       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@n-io
n-io requested a review from superlopuh July 14, 2026 18:02
Comment thread tests/dialects/test_builtin.py Outdated

@superlopuh superlopuh left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is quite a lot of code to review at a time, let's split this up? I feel like the first thing to add is the overall structure, and none of the methods on _ReducedPrecisionFloatType (which I would make public). Then the next PRs can one-by-one replace the existing methods on the subclasses, like bitwidth could be one PR, and then format could be another PR. Does this sound like a reasonable plan?

@n-io n-io changed the title core: Add float-semantics to support low-precision [WIP] core: Add float-semantics to support low-precision Jul 15, 2026
@n-io

n-io commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

Definitely happy to split off the FloatSemantics + enums, a basically empty ReducedPrecisionFloatType (now public), and the rewiring into the classes. It helps reduce the diff, but it's mostly optical and its harder to split up the logic. Separate PR raised, this is WIP PR for now.

@superlopuh
superlopuh marked this pull request as draft July 15, 2026 10:49
n-io added a commit that referenced this pull request Jul 15, 2026
This PR introduces the FloatSemantics class based on MLIR's
`fltSemantics` concept (alongside `fltNonfiniteBehavior` and
`fltNanEncoding`). This is used to calculate the `bitwidth` of
reduced-precision floats.

WIP:
* #6254
n-io added 3 commits July 15, 2026 18:03
# Conflicts:
#	tests/dialects/test_builtin.py
#	xdsl/dialects/builtin.py
@n-io
n-io marked this pull request as ready for review July 15, 2026 16:48
@n-io
n-io requested a review from superlopuh July 15, 2026 16:49
Comment thread xdsl/dialects/builtin.py
n-io added a commit that referenced this pull request Jul 16, 2026
Adding helpers to `FloatSemantics`.

WIP:
* #6254
@n-io
n-io requested a review from superlopuh July 16, 2026 09:42
@n-io n-io changed the title [WIP] core: Add float-semantics to support low-precision core: Add float-semantics to support low-precision Jul 16, 2026
Comment thread xdsl/dialects/builtin.py Outdated
Comment thread xdsl/dialects/builtin.py Outdated
Comment thread xdsl/dialects/builtin.py Outdated
@n-io
n-io requested a review from superlopuh July 16, 2026 13:13
Comment thread tests/dialects/test_builtin.py Outdated
float_type.format
# Reduced-precision floats pack via the reduced-float codec, not a struct
# format string, so (like bf16) they have no `format` attribute.
assert not hasattr(tf32, "format")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This feels off to me, what are we really testing here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

My gut tells me to just delete this line, I'm not sure what benefit there is to it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It's mirroring the same (pre-existing) check for bf16 just above. Either way is fine.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Moved these next to each other, removed comment. Share the general gut feeling though.

Comment thread xdsl/dialects/builtin.py Outdated

@superlopuh superlopuh left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TYVM

@superlopuh

Copy link
Copy Markdown
Member

If you have the motivation it feels to me like it would be great to support the larger floats also

@Samielakkad Samielakkad left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice addition overall. One edge case I’d want covered before this lands: iter_unpack currently walks the buffer in size chunks and decodes the last slice even if it is shorter than size.

For the reduced-float types with odd byte widths, that means a truncated buffer can silently decode as if the missing high bytes were zero. That is a little different from struct.iter_unpack, which requires an exact multiple of the element size.

It may be worth either checking len(buffer) % self.size == 0 in iter_unpack / unpack, or adding an explicit test that this silent partial decode is the intended behavior. I would lean toward rejecting the partial chunk so malformed dense data does not quietly round-trip to a different value.

@n-io

n-io commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks @Samielakkad, I think bf16 rejects too so it makes sense to replicate that.

@n-io
n-io requested a review from Samielakkad July 17, 2026 10:08
Comment thread xdsl/dialects/builtin.py
Comment on lines +1414 to +1417
raise ValueError(
f"buffer of {len(mv)} bytes is not a multiple of the {size}-byte "
f"element size of {self.name}"
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No, this is also not the right check, as it should be OK to unpack lazily just the prefix. The error should be raised only if the last element cannot be read in full

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm also not sure about the comment, the old code was reading potentially past the end of the buffer, not silently truncating? I would have expected an error to be raised there from an out of bounds access for the last element.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reading the struct documentation, it says that the bytes buffer should be a multiple of the struct size but not what error is raised if it isn't...

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think the OOB error didn't happen because of the slicing.

@n-io
n-io requested a review from superlopuh July 17, 2026 10:35
Comment thread xdsl/dialects/builtin.py Outdated
f"buffer ends with a partial {self.name} value: "
f"{len(chunk)} of {size} bytes"
)
yield self.decode_bits(int.from_bytes(chunk, "little"))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for updating this, but I still think it would be good to be consistent across implementations. Could you please add f64 to your test for this, and check that the error type is the same for both the struct and this manual implementation, and that the errors are consistent on whether the whole buffer needs to have size multiple of this element's size or just the last element?

@n-io n-io Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

If we're going for consistency, we should throw a struct.error. However, to do so I'd like to inherit from StructPackableType. Although we're not using any of its code, we do provide the same methods, so it's somewhat cleaner. I've also parameterized

@pytest.mark.parametrize("type_", [f64, bf16, tf32])
def test_float_rejects_truncated_buffer(type_: AnyFloat):

to demonstrate uniform rejection errors across these types.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's iterate on the PR I just opened (#6273) to decide what errors we want, feels like a good order of solving these issues to me.

@n-io
n-io requested a review from superlopuh July 17, 2026 12:44
Comment thread tests/dialects/test_builtin.py Outdated
assert type_.unpack(packed, 2) == (1.5, 2.0)
truncated = packed[:-1] # ends part-way through the final element
with pytest.raises(struct.error):
list(type_.iter_unpack(truncated))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems important to me, does it raise when you fetch the first element or all the elements

Suggested change
list(type_.iter_unpack(truncated))
next(type_.iter_unpack(truncated))

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, with a small fix for bf16 to match others.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the PR I just opened it actually raises before you fetch the first element

@n-io
n-io requested a review from superlopuh July 17, 2026 15:14

@Samielakkad Samielakkad left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Found one edge case in f8E8M0FNU: negative inputs lose their sign before rounding, so -4.0 is encoded as +4.0 (0x81). LLVM rejects signed values for this format, while ml_dtypes maps them to 0xff/NaN.

We should pick one of those behaviors rather than silently taking the absolute value. Tests for -0.0 and one negative finite value would catch it.

@superlopuh superlopuh left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

@n-io

n-io commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks @superlopuh, and good spot @Samielakkad! It's what one gets for sweeping the hexadecimal spectrum (:

@n-io
n-io merged commit 701c481 into main Jul 21, 2026
24 checks passed
@n-io
n-io deleted the nicolai/float-semantics branch July 21, 2026 08:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core xDSL core (ir, textual format, ...)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants