5

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.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Houss_gc
  • 727
  • 5
  • 27
  • I am afraid that this information may very much depends on the version of LLVM (which is free to add/remove passes), and as a result tied to the version of `rustc`. Which version of `rustc` are you using? Do you mind if this breaks when upgrading? – Matthieu M. Apr 22 '16 at 10:03
  • I am using Rust 1.10 nightly. I don't mind if it breaks. If I understand what is causing this behavior I can with a little bit of hard work make some workaround. – Houss_gc Apr 22 '16 at 10:33
  • 1
    After some reading i think that there is some scheduling passes that are moving the divide instruction before the upward() function call. (just a guess), correct me if I am wrong. – Houss_gc Apr 22 '16 at 10:36
  • Please include the exact code you're testing with: if by "one third" you mean literally the expression `1.0/3.0`, then this will be evaluated *at compile time* (i.e. using whatever rounding mode LLVM chooses to use at compile time) when optimisations are on. The question becomes harder to answer if you are computing "one third" in a way the optimiser can't see through (e.g. parsing command line arguments). – huon Apr 23 '16 at 07:34
  • @huon This is the [code](http://pastebin.com/p4EVk6wV) that I am using to test. Can I force llvm to use some rounding mode at compile time ??. – Houss_gc Apr 23 '16 at 12:39

1 Answers1

5

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).

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Eli Friedman
  • 2,343
  • 1
  • 13
  • 11
  • If I understand you right, when using the inline assembly the rounding mode set at the mxcsr or the fctrl registers is taken into account by the computation in the asm! macro? – Houss_gc Apr 23 '16 at 12:43
  • 2
    Yes. At that point, you're basically just writing raw assembly, so the semantics of Rust or LLVM IR doesn't matter. – Eli Friedman Apr 23 '16 at 20:52
  • Yes ofc, LLVM optimization are made on the IR, thank you. – Houss_gc Apr 23 '16 at 21:05