diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2015-08-17 17:00:45 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-08-17 23:32:30 -0700 |
| commit | 8a7b0fad531f2cbb40aace2bdfb5f03060a61d33 (patch) | |
| tree | 99e9e41afaf66e95fcd789d1da50ef83de859b6e /src | |
| parent | 47ea0cfb6bd250c970e3a61d62bfa1b1c7bb27d4 (diff) | |
| download | rust-8a7b0fad531f2cbb40aace2bdfb5f03060a61d33.tar.gz rust-8a7b0fad531f2cbb40aace2bdfb5f03060a61d33.zip | |
trans: Call `fmod` manually for 32-bit float rem
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
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustc_trans/trans/expr.rs | 39 | ||||
| -rw-r--r-- | src/test/run-pass/issue-27859.rs | 27 |
2 files changed, 65 insertions, 1 deletions
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index c5043f867de..9e9e65c398c 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -65,6 +65,7 @@ use trans::cleanup::{self, CleanupMethods, DropHintMethods}; 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 index 00000000000..900614be612 --- /dev/null +++ b/src/test/run-pass/issue-27859.rs @@ -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)); +} |
