diff --git a/compiler-rt/compiler-rt-cdylib/build.rs b/compiler-rt/compiler-rt-cdylib/build.rs index 4eb76389..21608afe 100644 --- a/compiler-rt/compiler-rt-cdylib/build.rs +++ b/compiler-rt/compiler-rt-cdylib/build.rs @@ -60,6 +60,13 @@ fn main() { "addsf3.c", "powidf2.c", "powisf2.c", + "fixsfsi.c", + "floatsisf.c", + "floatsidf.c", + "floatdidf.c", + "floatunsisf.c", + "floatunsidf.c", + "floatundidf.c", ]); for src in sources.files.iter() { diff --git a/compiler-rt/compiler-rt-cdylib/src/lib.rs b/compiler-rt/compiler-rt-cdylib/src/lib.rs index 81affa24..d1eede2c 100644 --- a/compiler-rt/compiler-rt-cdylib/src/lib.rs +++ b/compiler-rt/compiler-rt-cdylib/src/lib.rs @@ -24,6 +24,12 @@ extern { fn __adddf3(); fn __powisf2(); fn __powidf2(); + fn __floatsisf(); + fn __floatsidf(); + fn __floatdidf(); + fn __floatunsisf(); + fn __floatunsidf(); + fn __floatundidf(); } macro_rules! declare { @@ -57,6 +63,12 @@ declare!(___addsf3, __addsf3); declare!(___adddf3, __adddf3); declare!(___powisf2, __powisf2); declare!(___powidf2, __powidf2); +declare!(___floatsisf, __floatsisf); +declare!(___floatsidf, __floatsidf); +declare!(___floatdidf, __floatdidf); +declare!(___floatunsisf, __floatunsisf); +declare!(___floatunsidf, __floatunsidf); +declare!(___floatundidf, __floatundidf); #[lang = "eh_personality"] fn eh_personality() {} diff --git a/src/arm.rs b/src/arm.rs index b74458f1..0b05e9e6 100644 --- a/src/arm.rs +++ b/src/arm.rs @@ -100,6 +100,12 @@ pub extern "C" fn __aeabi_uidiv(a: u32, b: u32) -> u32 { ::int::udiv::__udivsi3(a, b) } +#[cfg(not(feature = "c"))] +#[cfg_attr(not(test), no_mangle)] +pub extern "C" fn __aeabi_ui2d(a: u32) -> f64 { + ::float::conv::__floatunsidf(a) +} + extern "C" { fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; @@ -161,7 +167,6 @@ pub unsafe extern "C" fn __aeabi_memclr8(dest: *mut u8, n: usize) { memset(dest, 0, n); } - #[cfg(test)] mod tests { use quickcheck::TestResult; diff --git a/src/float/conv.rs b/src/float/conv.rs new file mode 100755 index 00000000..30237edb --- /dev/null +++ b/src/float/conv.rs @@ -0,0 +1,126 @@ +use float::Float; +use int::Int; + +macro_rules! fp_overflow { + (infinity, $fty:ty, $sign: expr) => { + return { + <$fty as Float>::from_parts( + $sign, + <$fty as Float>::exponent_max() as <$fty as Float>::Int, + 0 as <$fty as Float>::Int) + } + } +} + +macro_rules! fp_convert { + ($intrinsic:ident: $ity:ty, $fty:ty) => { + + pub extern "C" fn $intrinsic(i: $ity) -> $fty { + let work_bits = 3; + let work_round = 1 << (work_bits - 1); + let work_mask = (1 << (work_bits + 1)) - 1; + let exponent_bias = <$fty>::exponent_bias(); + let exponent_max = <$fty>::exponent_max(); + let significand_bits = <$fty>::significand_bits(); + let significand_wbits = significand_bits + work_bits + 1; + let integer_bits = <$fty>::bits(); + + if i == 0 { + return <$fty>::from_parts(false,0,0) + } + + // work register. + let (sign, i) = i.init_float(); + let mut wr = i as <$fty as Float>::Int; + + let payload_len = integer_bits - wr.leading_zeros(); + let mut exp = exponent_bias + payload_len - 1; + + if exp >= exponent_max { + // overflow to infinity + fp_overflow!(infinity, $fty, sign); + } + + if payload_len < significand_wbits { + let left_shift = significand_wbits - payload_len; + wr = wr.wrapping_shl(left_shift); + } else { + let right_shift = payload_len - significand_wbits; // this is also the number of unused bits from the i + let has_spare_bits = (if right_shift == 0 { + 0 + } else { + wr.wrapping_shl(integer_bits - right_shift) + } != 0) as <$fty as Float>::Int; + // shift and if there is any dropped bit to 1, raise the least significant bit. + wr = (wr >> right_shift) | has_spare_bits; + } + + wr &= (<$fty>::significand_mask() << work_bits) | work_mask; + + if (wr & work_mask) > work_round { + wr += work_round; + } + + if wr >= (1<< (significand_wbits - 1)) { + exp += 1; + + if exp >= exponent_max { + fp_overflow!(infinity, $fty, sign); + } + } + + let frac = wr >> work_bits; + <$fty>::from_parts(sign, exp as <$fty as Float>::Int, frac) + } + } +} + +fp_convert!(__floatsisf: i32, f32); +fp_convert!(__floatsidf: i32, f64); +fp_convert!(__floatdidf: i64, f64); +fp_convert!(__floatunsisf: u32, f32); +fp_convert!(__floatunsidf: u32, f64); +fp_convert!(__floatundidf: u64, f64); + +// NOTE(cfg) for some reason, on arm*-unknown-linux-gnueabihf, our implementation doesn't +// match the output of its gcc_s or compiler-rt counterpart. Until we investigate further, we'll +// just avoid testing against them on those targets. Do note that our implementation gives the +// correct answer; gcc_s and compiler-rt are incorrect in this case. +// +#[cfg(all(test, not(arm_linux)))] +mod tests { + use qc::{I32, U32, I64, U64, F32, F64}; + + check! { + fn __floatsisf(f: extern fn(i32) -> f32, + a: I32) + -> Option { + Some(F32(f(a.0))) + } + fn __floatsidf(f: extern fn(i32) -> f64, + a: I32) + -> Option { + Some(F64(f(a.0))) + } + fn __floatdidf(f: extern fn(i64) -> f64, + a: I64) + -> Option { + Some(F64(f(a.0))) + } + fn __floatunsisf(f: extern fn(u32) -> f32, + a: U32) + -> Option { + Some(F32(f(a.0))) + } + fn __floatunsidf(f: extern fn(u32) -> f64, + a: U32) + -> Option { + Some(F64(f(a.0))) + } + fn __floatundidf(f: extern fn(u64) -> f64, + a: U64) + -> Option { + Some(F64(f(a.0))) + } + } +} diff --git a/src/float/mod.rs b/src/float/mod.rs index 134c32dc..a6a76271 100644 --- a/src/float/mod.rs +++ b/src/float/mod.rs @@ -2,6 +2,7 @@ use core::mem; pub mod add; pub mod pow; +pub mod conv; /// Trait for some basic operations on floats pub trait Float: Sized + Copy { @@ -19,6 +20,16 @@ pub trait Float: Sized + Copy { Self::bits() - Self::significand_bits() - 1 } + /// Returns the maximum value of the exponent + fn exponent_max() -> u32 { + (1 << Self::exponent_bits()) - 1 + } + + /// Returns the exponent bias value + fn exponent_bias() -> u32 { + Self::exponent_max() >> 1 + } + /// Returns a mask for the sign bit fn sign_mask() -> Self::Int; diff --git a/src/int/mod.rs b/src/int/mod.rs index 37e05377..c40bc0d4 100644 --- a/src/int/mod.rs +++ b/src/int/mod.rs @@ -6,30 +6,78 @@ pub mod udiv; /// Trait for some basic operations on integers pub trait Int { + /// Unsigned version of Self + type UnsignedInt; /// Returns the bitwidth of the int type fn bits() -> u32; + + /// Extracts the sign from self and returns a tuple. + /// + /// This is used by the module float to prepare conversions. + /// This is needed by the generic code supporting signed and unsigned conversions. + /// + /// # Examples + /// + /// ```rust,ignore + /// let i = -25; + /// let (sign, u) = i.init_float(); + /// assert_eq!(sign, true); + /// assert_eq!(u, 25); + /// ``` + fn init_float(self) -> (bool, Self::UnsignedInt); } // TODO: Once i128/u128 support lands, we'll want to add impls for those as well impl Int for u32 { + type UnsignedInt = u32; fn bits() -> u32 { 32 } + + fn init_float(self) -> (bool, u32) { + (false, self) + } } impl Int for i32 { + type UnsignedInt = u32; + fn bits() -> u32 { 32 } + + fn init_float(self) -> (bool, u32) { + if self < 0 { + (true, !(self as u32) + 1) + } else { + (false, self as u32) + } + } } impl Int for u64 { + type UnsignedInt = u64; + fn bits() -> u32 { 64 } + + fn init_float(self) -> (bool, u64) { + (false, self) + } } impl Int for i64 { + type UnsignedInt = u64; + fn bits() -> u32 { 64 } + + fn init_float(self) -> (bool, u64) { + if self < 0 { + (true, !(self as u64) + 1) + } else { + (false, self as u64) + } + } } /// Trait to convert an integer to/from smaller parts