-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Jump threading MIR opt unsoundly uses bitpattern equality for floats #128243
Comments
To minimize this, you should try to inline the logic from However, what is the reason you believe this to be a compiler/std bug rather than inari? The return |
I tried but inlining the code, the program behaves correctly: use std::{arch::x86_64::*, mem::transmute};
#[derive(Copy, Clone)]
struct I {
rep: __m128d,
}
impl I {
fn new(a: f64, b: f64) -> Self {
Self { rep: unsafe { transmute([-a, b]) } }
}
fn inf(self) -> f64 {
unsafe { transmute::<_, [f64; 2]>(self.rep)[0] }
}
}
fn f(u: I) -> f64 {
let t = u.inf() == 0.;
if t { 1. }
else { println!("{} {}", u.inf(), u.inf() == 0.);
u.inf() }
}
fn main() {
println!("{}", f(I::new(0., 0.)));
} |
If I am understanding the code correctly then |
You are correct, I oversimplified the inlining. The bug can be reproduced with: use std::{arch::x86_64::*, mem::transmute};
#[derive(Copy, Clone)]
struct I {
rep: __m128d,
}
impl I {
fn new(a: f64, b: f64) -> Self {
Self { rep: unsafe { transmute([-a, b]) } }
}
pub(crate) fn inf_raw(self) -> f64 {
unsafe { - transmute::<_, [f64; 2]>(self.rep)[0] }
}
fn inf(self) -> f64 {
let x = self.inf_raw();
if x.is_nan() {
// The empty interval.
f64::INFINITY
} else if x == 0.0 {
-0.0
} else {
x
}
}
}
fn f(u: I) -> f64 {
let t = u.inf() == 0.;
if t { 1. }
else { println!("{} {}", u.inf(), u.inf() == 0.);
u.inf() }
}
fn main() {
println!("{}", f(I::new(0., 0.)));
} |
Reduced some #![allow(unused)]
use std::{arch::x86_64::*, mem::transmute};
use std::hint::black_box;
fn check(x: f64) -> f64 {
if x == 0.0 {
-0.0
} else {
1234.0
}
}
fn main() {
let f = 0.0;
let simty: __m128d = unsafe { transmute([-f, f]) };
let as_f = unsafe { -transmute::<_, [f64; 2]>(simty)[0] };
if check(as_f) == 0.0 {
println!("ok");
} else {
println!("bug")
}
} |
What, why is this code fragile?? No simd types required. pub fn main() {
let f: f64 = 0.0;
let as_f = -f;
println!("{:#018x}", as_f.to_bits());
let tmp = if -0.0 == 0.0 {
-0.0
} else {
0.0
};
if tmp == 0.0 {
println!("ok");
} else {
println!("bug")
}
} Prints "ok" by default, "bug" with Spot checking some versions it seems to show up at 1.78, which aligns with the LLVM18 version bump. Still reproduces on nightly. This might be a known issue and it might be fixed with the LLVM19 update. Needs some more digging but I'm going to guess LLVM for now. |
LLVM IR already includes an unconditional bug print. Also reproduces with |
This is a bug in JumpThreading. The bad codegen reproduces with -// MIR for `main` before JumpThreading
+// MIR for `main` after JumpThreading
fn main() -> () {
let mut _0: ();
@@ -165,7 +165,7 @@ fn main() -> () {
bb7: {
_29 = const -0f64;
- goto -> bb9;
+ goto -> bb17;
}
bb8: {
@@ -179,7 +179,7 @@ fn main() -> () {
StorageLive(_32);
_32 = _29;
_31 = Eq(move _32, const 0f64);
- switchInt(move _31) -> [0: bb13, otherwise: bb10];
+ goto -> bb10;
}
bb10: {
@@ -242,4 +242,13 @@ fn main() -> () {
StorageDead(_1);
return;
}
+
+ bb17: {
+ StorageDead(_30);
+ StorageLive(_31);
+ StorageLive(_32);
+ _32 = _29;
+ _31 = Eq(move _32, const 0f64);
+ goto -> bb13;
+ }
} |
|
Well, #117206. Must have been a preexisting bug |
Ah. You always need to stabilize the MIR-opt pipeline by carefully selecting specifically the passes you need when bisecting. You should bisect with the flags I mentioned above, |
Ah, thanks.
Seems like it was from the original implementation at #107009. Cc @cjgillot |
Minimized: fn main() {
let tmp = if true { -0.0 } else { 1.0 };
if tmp == 0.0 {
println!("ok");
} else {
println!("bug")
}
} This incorrectly prints "bug" in release mode. |
It looks like the bug here is that jump threading deals in fn main() {
let tmp = if true { f32::NAN } else { 1.0 };
if tmp == f32::NAN {
println!("true");
} else {
println!("false");
}
} same problem here, this prints |
f64
comparisonRollup merge of rust-lang#128271 - Nilstrieb:jump-into-a-can-of-worms-called-float-equality, r=compiler-errors Disable jump threading of float equality Jump threading stores values as `u128` (`ScalarInt`) and does its comparisons for equality as integer comparisons. This works great for integers. Sadly, not everything is an integer. Floats famously have wonky equality semantcs, with `NaN!=NaN` and `0.0 == -0.0`. This does not match our beautiful integer bitpattern equality and therefore causes things to go horribly wrong. While jump threading could be extended to support floats by remembering that they're floats in the value state and handling them properly, it's signficantly easier to just disable it for now. fixes rust-lang#128243
reopen to track potential backports |
WG-prioritization assigning priority (Zulip discussion). @rustbot label -I-prioritize +P-critical |
Closing as this was fixed in the 1.80.1 release this morning. Thanks to @Chris00 for reporting and @Noratrieb for the fix! |
Rust previously had a bug related to comparison of floats, see rust-lang/rust#128243. ### Motivation Upgrades Rust to pickup fix ### Checklist - [x] This PR has adequate test coverage / QA involvement has been duly considered. ([trigger-ci for additional test/nightly runs](https://trigger-ci.dev.materialize.com/)) - [x] This PR has an associated up-to-date [design doc](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/design/README.md), is a design doc ([template](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/design/00000000_template.md)), or is sufficiently small to not require a design. <!-- Reference the design in the description. --> - [x] If this PR evolves [an existing `$T ⇔ Proto$T` mapping](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/command-and-response-binary-encoding.md) (possibly in a backwards-incompatible way), then it is tagged with a `T-proto` label. - [x] If this PR will require changes to cloud orchestration or tests, there is a companion cloud PR to account for those changes that is tagged with the release-blocker label ([example](MaterializeInc/cloud#5021)). <!-- Ask in #team-cloud on Slack if you need help preparing the cloud PR. --> - [x] If this PR includes major [user-facing behavior changes](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/guide-changes.md#what-changes-require-a-release-note), I have pinged the relevant PM to schedule a changelog post.
I tried this code (sorry, I cannot reproduce it without
inari
ATM):I expected to see this happen:
1
Instead, in
--release
mode, I got:If I uncomment one of the above commented lines, the code executes correctly.
Meta
This happens with both
rustc --version --verbose
:and
The text was updated successfully, but these errors were encountered: