From 8a7b0fad531f2cbb40aace2bdfb5f03060a61d33 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 17 Aug 2015 17:00:45 -0700 Subject: [PATCH] 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 --- src/librustc_trans/trans/expr.rs | 39 +++++++++++++++++++++++++++++++- src/test/run-pass/issue-27859.rs | 27 ++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/test/run-pass/issue-27859.rs diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index c5043f867ded0..9e9e65c398cc2 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 0000000000000..900614be612f6 --- /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 or the MIT license +// , 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)); +}