Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting a non-default rounding mode with Rust inline asm isn't respected by the LLVM optimizer?

I am working on a Rust crate which changes the rounding mode (+inf, -inf, nearest, or truncate).

The functions that change the rounding mode are written using inline assembly:

fn upward() {
    let cw: u32 = 0;
    unsafe {
    asm!("stmxcsr $0;
          mov $0, %eax;
          or $$0x4000, %eax;
          mov %eax, $0;
          ldmxcsr $0;"
          : "=*m"(&cw)
          : "*m"(&cw)
          : "{eax}"
        );
    }
}

When I compile the code in debug mode it works as intended, I get 0.3333333333337 for one-third when rounding toward positive infinity, but when I compile in release mode I get the same result no matter what rounding mode I set. I guess this behavior is due to the optimizations that the LLVM backend does.

If I knew which LLVM passes are responsible for this optimization, I can disable them as I don't see any other workaround at the moment.

like image 479
Houss_gc Avatar asked Apr 22 '16 09:04

Houss_gc


1 Answers

Basically, you can't do this. LLVM assumes that all floating point operations use the default rounding mode, and that the floating-point control register is never read or modified.

There's been some discussion of this issue recently on the LLVM-dev mailing list, if you're interested.

In the meantime, the only reliable workaround is to use inline assembly, like asm!("addsd $0, $1".

Rust's standard library also assumes that you don't modify the rounding mode (in particular, the code for converting between floating-point and strings is sensitive to this).

like image 148
Eli Friedman Avatar answered Sep 22 '22 11:09

Eli Friedman