]> git.lizzy.rs Git - rust.git/commitdiff
trans: Call `fmod` manually for 32-bit float rem
authorAlex Crichton <alex@alexcrichton.com>
Tue, 18 Aug 2015 00:00:45 +0000 (17:00 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Tue, 18 Aug 2015 06:32:30 +0000 (23:32 -0700)
Currently `f32 % f32` will generate a link error on 32-bit MSVC because LLVM
will lower the operation to a call to the nonexistent function `fmodf`. Work
around in this in the backend by lowering to a call to `fmod` instead with
necessary extension/truncation between floats/doubles.

Closes #27859

src/librustc_trans/trans/expr.rs
src/test/run-pass/issue-27859.rs [new file with mode: 0644]

index c5043f867ded09d3e939e697eb9c1ddd84760d7c..9e9e65c398cc236de51249dfa06a33ec8b647a8a 100644 (file)
@@ -65,6 +65,7 @@
 use trans::common::*;
 use trans::datum::*;
 use trans::debuginfo::{self, DebugLoc, ToDebugLoc};
+use trans::declare;
 use trans::glue;
 use trans::machine;
 use trans::meth;
@@ -1767,7 +1768,43 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
       }
       ast::BiRem => {
         if is_float {
-            FRem(bcx, lhs, rhs, binop_debug_loc)
+            // LLVM currently always lowers the `frem` instructions appropriate
+            // library calls typically found in libm. Notably f64 gets wired up
+            // to `fmod` and f32 gets wired up to `fmodf`. Inconveniently for
+            // us, 32-bit MSVC does not actually have a `fmodf` symbol, it's
+            // instead just an inline function in a header that goes up to a
+            // f64, uses `fmod`, and then comes back down to a f32.
+            //
+            // Although LLVM knows that `fmodf` doesn't exist on MSVC, it will
+            // still unconditionally lower frem instructions over 32-bit floats
+            // to a call to `fmodf`. To work around this we special case MSVC
+            // 32-bit float rem instructions and instead do the call out to
+            // `fmod` ourselves.
+            //
+            // Note that this is currently duplicated with src/libcore/ops.rs
+            // which does the same thing, and it would be nice to perhaps unify
+            // these two implementations on day! Also note that we call `fmod`
+            // for both 32 and 64-bit floats because if we emit any FRem
+            // instruction at all then LLVM is capable of optimizing it into a
+            // 32-bit FRem (which we're trying to avoid).
+            let use_fmod = tcx.sess.target.target.options.is_like_msvc &&
+                           tcx.sess.target.target.arch == "x86";
+            if use_fmod {
+                let f64t = Type::f64(bcx.ccx());
+                let fty = Type::func(&[f64t, f64t], &f64t);
+                let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty,
+                                                tcx.types.f64);
+                if lhs_t == tcx.types.f32 {
+                    let lhs = FPExt(bcx, lhs, f64t);
+                    let rhs = FPExt(bcx, rhs, f64t);
+                    let res = Call(bcx, llfn, &[lhs, rhs], None, binop_debug_loc);
+                    FPTrunc(bcx, res, Type::f32(bcx.ccx()))
+                } else {
+                    Call(bcx, llfn, &[lhs, rhs], None, binop_debug_loc)
+                }
+            } else {
+                FRem(bcx, lhs, rhs, binop_debug_loc)
+            }
         } else {
             // Only zero-check integers; fp %0 is NaN
             bcx = base::fail_if_zero_or_overflows(bcx,
diff --git a/src/test/run-pass/issue-27859.rs b/src/test/run-pass/issue-27859.rs
new file mode 100644 (file)
index 0000000..900614b
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[inline(never)]
+fn foo(a: f32, b: f32) -> f32 {
+    a % b
+}
+
+#[inline(never)]
+fn bar(a: f32, b: f32) -> f32 {
+    ((a as f64) % (b as f64)) as f32
+}
+
+fn main() {
+    let unknown_float = std::env::args().len();
+    println!("{}", foo(4.0, unknown_float as f32));
+    println!("{}", foo(5.0, (unknown_float as f32) + 1.0));
+    println!("{}", bar(6.0, (unknown_float as f32) + 2.0));
+    println!("{}", bar(7.0, (unknown_float as f32) + 3.0));
+}