Skip to content

Undefined Behavior: Changing MXCSR to _MM_ROUND_TOWARD_ZERO on x86 and x86_64. #776

Closed

Description

@herabit

In Rust, modifying the rounding mode in x86 and x86_64 by modifying the MXCSR register (through _mm_setcsr or inline assembly) is considered immediate undefined behavior.

See here for more info.

Activity

  1. ThierryBerger commented on Dec 20, 2024

    @ThierryBerger
    Contributor

    Thanks for the issue ; I admit I'm not sure how to research "using inline assembly instead".

  2. Lokathor commented on Dec 20, 2024

    @Lokathor

    The key element of using inline assembly is that you can't ever leave an inline assembly block without restoring MXCSR to the default state, or you hit the same UB problem.

    You'd have to do the entire round-toward-zero operation using only inline assembly.

  3. ThierryBerger commented on Jan 6, 2025

    @ThierryBerger
    Contributor

    A bit more context: this operation occurs when using enable_flush_to_zero, for example:

    enable_flush_to_zero!();

    This "flush to zero" leads to important performance improvements.

    Brainstorming further:

    Could the entire round-toward-zero operation can be summarized as a function call, so the assembly would end up quite small (function declaration, arguments prep, function call) ?

  4. Lokathor commented on Jan 6, 2025

    @Lokathor

    You cannot leave flush to zero active while executing rust code, it's simply not allowed.

    So you can't have functions to toggle the effect on/off, and you also can't have a function that takes a fn pointer to run with flushing set which changes the state and calls back into rust while the state is changed.

    This applies equally to all floating point environment settings, though flush to zero is usually the only one people try to change.

  5. Ralith commented on Feb 15, 2025

    @Ralith
    Contributor

    This "flush to zero" leads to important performance improvements.

    Can you elaborate on these? From @Lokathor's comments, it sounds like these just aren't available in sound Rust today, and it might be best to do without.

  6. im-0 commented on Sep 20, 2025

    @im-0

    There is no benchmark specifically for subnormal floating point values for Rapier. And I am not sure whether it will be easy to construct one or not.

    However, I made a generic benchmark with just a random arithmetic operations: https://github.com/im-0/rust-denormals-bench
    There is a 50x performance degradation on modern Intel CPUs when subnormal values are encountered.

  7. herabit commented on Sep 21, 2025

    @herabit
    Author

    I totally forgot about this. So a large portion of the issue is the fact that Rust guarantees that all of its floating point arithmetic matches that of IEE-754-whatever-the-current-number-is. There is no sound way, in stable Rust anyways, to get the behavior of -ffast-math without there being a risk of UB. This is because, well, the compiler makes this assumption, everywhere.

    Potential solutions? None, really, besides writing these routines utilizing inline assembly. With the recent addition of... Raw functions I think they're called, this may not be so bad, but still, it is an optimization barrier, and error prone.

    Now, if you're willing to utilize intrinsics in nightly, this could be solved I think potentially with those. See fadd_fast for one such example. It would be nice if these intrinsics were provided with a clean API on stable, but currently they're not, and this is the only likely to be sound way to achieve this behavior.

    I've been meaning to contribute to the standard library for quite some time now, so perhaps this is the first thing I'll try to propose: making these APIs accessible, safely, or, safer.

    Though that may require some, additional analysis on behalf of rustc... Maybe... I'll have to check godbolt.

  8. herabit commented on Sep 21, 2025

    @herabit
    Author

    Oh, yeah, I forgot about this blog post Someone's Been Messing With My Subnormals! that, introduces a related issue.

    Starting to believe that behavior detailing the behavior of some instruction should probably not be defined by some global mutable state anyone can modify... Oh well. Thanks x86!

  9. herabit commented on Sep 21, 2025

    @herabit
    Author

    Or, just... Avoid -ffast-math (see here)... Why is this shit so hard??????

    Guess that explains why there's no stable API for this... It's just fucked.

  10. im-0 commented on Sep 21, 2025

    @im-0

    @herabit the usage of MXCSR in Rapier has nothing to do with rounding. Current code enables "Flush [denormals] to Zero" and "Denormals Are Zero" flags (still UB in Rust).

    And... could you please elaborate on how -ffast-math is relevant here?

  11. herabit commented on Sep 21, 2025

    @herabit
    Author

    @im-0 I made this issue nearly a year ago. It may not have to do with rounding, or I may have copied the wrong flag as I was trying to figure out how to use MXCSR in Rust properly, was reading the standard library, and then came across its problematic usage here while searching GitHub for examples on how to do it correctly.

    Point is, currently, there is zero way of changing these flags without incurring undefined behavior.

    This is related to -ffast-math, because one of the major features of -ffast-math, at least on x86 and x86_64, is flushing denormals to zero. Handling denormals properly according to IEEE-754 is extremely expensive, even in hardware, so the "flush to zero" flag is a part of -ffast-math.

    If there was safe-r ways of accessing fast-math arithmetic within Rust, that would be the preferred way of accomplishing these tasks, without requiring the utilization of inline assembly.

    I hope this makes, sense.

  12. Morgane55440 commented on Jun 8, 2026

    @Morgane55440

    @sebcrozet sorry for the ping, but it seems it was you who introduced these calls in #909 .
    i have been looking at the problem for the last couple days and the prospects for a fix do not seem great.
    i have no clue how to measure this reliably, so given you made the PR and the blogpost i was wondering if you had insight on how much this specific guard impacts performance on the relevant architectures.

  13. herabit commented on Jun 21, 2026

    @herabit
    Author

    At this point it'd just be better as an unstable feature flag, that's opt-in. If you really need the performance and do not give half a shit about, well, soundness... It's a good medium until these things are, well, able to be made more sound.

  14. Lokathor commented on Jun 24, 2026

    @Lokathor

    I think it would be a poor idea to have a crate feature to enable unsoundness in the code

  15. sebcrozet commented on Aug 7, 2026

    @sebcrozet
    Member

    The MXCSR register modifications have been removed entirely in the latest version of rapier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions