Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Codegen and const eval evaluates -1.0 % -1.0 to 0 with different signs #109567

Closed
cbeuw opened this issue Mar 24, 2023 · 8 comments
Closed

Codegen and const eval evaluates -1.0 % -1.0 to 0 with different signs #109567

cbeuw opened this issue Mar 24, 2023 · 8 comments
Assignees
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) A-floating-point Area: Floating point numbers and arithmetic C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@cbeuw
Copy link
Contributor

cbeuw commented Mar 24, 2023

pub fn f() -> f64 {
    std::hint::black_box(-1.0) % std::hint::black_box(-1.0)
}

pub fn g() -> f64 {
    -1.0 % -1.0
}

pub fn main() {
    println!("with black box: {}", f());
    println!("without black box: {}", g());
    assert_eq!(f().signum(), g().signum());
}
% rustc repro.rs && ./repro
with black box: -0
without black box: 0
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `-1.0`,
 right: `1.0`', repro.rs:12:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Not sure whether -1.0 % -1.0 should be 0 or -0 according to IEEE 754, but codegen (with black box) and const eval (without blackbox) should produce the same result

Meta

rustc --version --verbose:

rustc 1.70.0-nightly (1459b3128 2023-03-23)
binary: rustc
commit-hash: 1459b3128e288a85fcc4dd1fee7ada2cdcf28794
commit-date: 2023-03-23
host: aarch64-apple-darwin
release: 1.70.0-nightly
LLVM version: 15.0.7
@cbeuw cbeuw added the C-bug Category: This is a bug. label Mar 24, 2023
@cbeuw
Copy link
Contributor Author

cbeuw commented Mar 24, 2023

IEEE 754-2019 §5.3.1 says

When y ≠ 0, the remainder r = remainder(x, y) is defined for finite x and y [...] If r = 0, its sign shall be that of x. [...]

So -1.0 % -1.0 should be -0.0, so codegen is correct.

Looking at const eval code, this is handled correctly, so the sign somehow got dropped later on? (nvm this function isn't used anywhere)

if v.is_zero() {
status.and(v.copy_sign(self)) // IEEE754 requires this
} else {
status.and(v)
}

@chenyukang
Copy link
Member

I think it's optimized, https://rust.godbolt.org/z/Eqae9xPKE

example::f:
        sub     rsp, 24
        mov     rax, qword ptr [rip + core::hint::black_box@GOTPCREL]
        mov     qword ptr [rsp], rax
        movsd   xmm0, qword ptr [rip + .LCPI7_0]
        movsd   qword ptr [rsp + 8], xmm0
        call    rax
        mov     rax, qword ptr [rsp]
        movaps  xmm1, xmm0
        movsd   xmm0, qword ptr [rsp + 8]
        movsd   qword ptr [rsp + 16], xmm1
        call    rax
        movaps  xmm1, xmm0
        movsd   xmm0, qword ptr [rsp + 16]
        mov     rax, qword ptr [rip + fmod@GOTPCREL]
        call    rax
        add     rsp, 24
        ret

example::g:
        xorps   xmm0, xmm0
        ret

@cbeuw
Copy link
Contributor Author

cbeuw commented Mar 24, 2023

it's optimized

Not by LLVM. g is already 0 in MIR

// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn f() -> f64 {
    let mut _0: f64;                     // return place in scope 0 at repro.rs:1:15: 1:18
    let mut _1: f64;                     // in scope 0 at repro.rs:2:5: 2:31
    let mut _2: f64;                     // in scope 0 at repro.rs:2:34: 2:60

    bb0: {
        _1 = std::hint::black_box::<f64>(const -1f64) -> bb1; // scope 0 at repro.rs:2:5: 2:31
                                         // mir::Constant
                                         // + span: repro.rs:2:5: 2:25
                                         // + literal: Const { ty: fn(f64) -> f64 {std::hint::black_box::<f64>}, val: Value(<ZST>) }
    }

    bb1: {
        _2 = std::hint::black_box::<f64>(const -1f64) -> bb2; // scope 0 at repro.rs:2:34: 2:60
                                         // mir::Constant
                                         // + span: repro.rs:2:34: 2:54
                                         // + literal: Const { ty: fn(f64) -> f64 {std::hint::black_box::<f64>}, val: Value(<ZST>) }
    }

    bb2: {
        _0 = Rem(move _1, move _2);      // scope 0 at repro.rs:2:5: 2:60
        return;                          // scope 0 at repro.rs:3:2: 3:2
    }
}

fn g() -> f64 {
    let mut _0: f64;                     // return place in scope 0 at repro.rs:5:15: 5:18

    bb0: {
        _0 = const 0f64;                 // scope 0 at repro.rs:6:5: 6:16
        return;                          // scope 0 at repro.rs:7:2: 7:2
    }
}

@chenyukang
Copy link
Member

it's done by mir-opt, if we disable it the result will be same:

rustc +dev --edition 2021 ./p/debug3.rs -Z mir-opt-level=0 

I'm still investigating the specific line of code, but it seems in part of compiler/rustc_mir_transform.

@chenyukang
Copy link
Member

The line of code is here:

self.binary_float_op(bin_op, ty, left.to_f64()?, right.to_f64()?)

@RalfJung
Copy link
Member

This looks to me like a duplicate of #102403?

@cbeuw
Copy link
Contributor Author

cbeuw commented Mar 25, 2023

This looks to me like a duplicate of #102403?

Yes it's the same root cause. Although the reproduction here requires only stable rust

@jyn514 jyn514 added A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) A-floating-point Area: Floating point numbers and arithmetic labels Mar 27, 2023
@Noratrieb Noratrieb added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Apr 5, 2023
@RalfJung
Copy link
Member

RalfJung commented Jul 6, 2023

Closing as a duplicate of #102403, then.

@RalfJung RalfJung closed this as completed Jul 6, 2023
wesleywiser pushed a commit to wesleywiser/rust that referenced this issue Jul 26, 2023
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) A-floating-point Area: Floating point numbers and arithmetic C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants