From 8f018562ca05dfbc8f392c5431a419c5128fe0cd Mon Sep 17 00:00:00 2001 From: Paolo Teti Date: Mon, 29 Jan 2018 19:52:55 +0100 Subject: [PATCH 1/4] Add support for mul[s/d]f3vfp and div[s/d]f3vfp Here using `"C"` the compiler will use `"aapcs"` or `"aapcs-vfp"` depending on target configuration. Of course this translates in a call to `__aeabi_fdiv` / `__aeabi_fmul` on non-HF targets. On `eabi` targets with +vfpv2/vfpv3 LLVM generate: vmov s0, r1 vmov s2, r0 vdiv.f32 s0, s2, s0 vmov r0, s0 bx lr On `eabihf` targets with +vfpv3-d16/d32/f32 +fp-only-sp LLVM generate: vdiv.f32 s0, s0, s1 bx lr That's exactly what We need for [div/mul][s/d]f3vfp.S --- README.md | 8 ++++---- build.rs | 4 ---- src/float/div.rs | 7 +++++++ src/float/mul.rs | 8 ++++++++ 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a7dabe7..cc4852a 100644 --- a/README.md +++ b/README.md @@ -96,9 +96,9 @@ features = ["c"] - [x] arm/aeabi_memset.S - [x] arm/aeabi_uidivmod.S - [x] arm/aeabi_uldivmod.S -- [ ] arm/divdf3vfp.S +- [x] arm/divdf3vfp.S - [ ] arm/divmodsi4.S (generic version is done) -- [ ] arm/divsf3vfp.S +- [x] arm/divsf3vfp.S - [ ] arm/divsi3.S (generic version is done) - [ ] arm/eqdf2vfp.S - [ ] arm/eqsf2vfp.S @@ -120,8 +120,8 @@ features = ["c"] - [ ] arm/ltdf2vfp.S - [ ] arm/ltsf2vfp.S - [ ] arm/modsi3.S (generic version is done) -- [ ] arm/muldf3vfp.S -- [ ] arm/mulsf3vfp.S +- [x] arm/muldf3vfp.S +- [x] arm/mulsf3vfp.S - [ ] arm/nedf2vfp.S - [ ] arm/negdf2vfp.S - [ ] arm/negsf2vfp.S diff --git a/build.rs b/build.rs index a7351cc..1bb2b0f 100644 --- a/build.rs +++ b/build.rs @@ -5511,8 +5511,6 @@ mod c { &[ "arm/adddf3vfp.S", "arm/addsf3vfp.S", - "arm/divdf3vfp.S", - "arm/divsf3vfp.S", "arm/eqdf2vfp.S", "arm/eqsf2vfp.S", "arm/extendsfdf2vfp.S", @@ -5532,8 +5530,6 @@ mod c { "arm/lesf2vfp.S", "arm/ltdf2vfp.S", "arm/ltsf2vfp.S", - "arm/muldf3vfp.S", - "arm/mulsf3vfp.S", "arm/nedf2vfp.S", "arm/nesf2vfp.S", "arm/restore_vfp_d8_d15_regs.S", diff --git a/src/float/div.rs b/src/float/div.rs index e0e287a..94be4f2 100644 --- a/src/float/div.rs +++ b/src/float/div.rs @@ -454,4 +454,11 @@ intrinsics! { div64(a, b) } + pub extern "C" fn __divsf3vfp(a: f32, b: f32) -> f32 { + a / b + } + + pub extern "C" fn __divdf3vfp(a: f64, b: f64) -> f64 { + a / b + } } diff --git a/src/float/mul.rs b/src/float/mul.rs index 696adea..c5c5403 100644 --- a/src/float/mul.rs +++ b/src/float/mul.rs @@ -188,4 +188,12 @@ intrinsics! { pub extern "C" fn __muldf3(a: f64, b: f64) -> f64 { mul(a, b) } + + pub extern "C" fn __mulsf3vfp(a: f32, b: f32) -> f32 { + a * b + } + + pub extern "C" fn __muldf3vfp(a: f64, b: f64) -> f64 { + a * b + } } From f42b9e0bade14ed4fdda35e03477cb7ecefec00e Mon Sep 17 00:00:00 2001 From: Paolo Teti Date: Tue, 30 Jan 2018 18:48:20 +0100 Subject: [PATCH 2/4] __[mul/div]sf3vfp and __[mul/div]df3vfp only on ARM --- src/float/div.rs | 2 ++ src/float/mul.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/float/div.rs b/src/float/div.rs index 94be4f2..ae12bb3 100644 --- a/src/float/div.rs +++ b/src/float/div.rs @@ -454,10 +454,12 @@ intrinsics! { div64(a, b) } + #[cfg(target_arch = "arm")] pub extern "C" fn __divsf3vfp(a: f32, b: f32) -> f32 { a / b } + #[cfg(target_arch = "arm")] pub extern "C" fn __divdf3vfp(a: f64, b: f64) -> f64 { a / b } diff --git a/src/float/mul.rs b/src/float/mul.rs index c5c5403..d014bbc 100644 --- a/src/float/mul.rs +++ b/src/float/mul.rs @@ -189,10 +189,12 @@ intrinsics! { mul(a, b) } + #[cfg(target_arch = "arm")] pub extern "C" fn __mulsf3vfp(a: f32, b: f32) -> f32 { a * b } + #[cfg(target_arch = "arm")] pub extern "C" fn __muldf3vfp(a: f64, b: f64) -> f64 { a * b } From 9e0472381279005a45edfee50333988bf8c71fd5 Mon Sep 17 00:00:00 2001 From: Paolo Teti Date: Tue, 30 Jan 2018 20:15:37 +0100 Subject: [PATCH 3/4] Add test cases for Mul[s/d]f3vfp and Div[s/d]f3vfp for ARM targets only --- build.rs | 339 +++++++++++++++++++++++++++++++++++++++++++++ tests/divdf3vfp.rs | 8 ++ tests/divsf3vfp.rs | 8 ++ tests/muldf3vfp.rs | 8 ++ tests/mulsf3vfp.rs | 8 ++ 5 files changed, 371 insertions(+) create mode 100644 tests/divdf3vfp.rs create mode 100644 tests/divsf3vfp.rs create mode 100644 tests/muldf3vfp.rs create mode 100644 tests/mulsf3vfp.rs diff --git a/build.rs b/build.rs index 1bb2b0f..a8407b2 100644 --- a/build.rs +++ b/build.rs @@ -130,10 +130,14 @@ mod tests { // float/mul.rs Mulsf3, Muldf3, + Mulsf3vfp, + Muldf3vfp, // float/div.rs Divsf3, Divdf3, + Divsf3vfp, + Divdf3vfp, // int/addsub.rs AddU128, @@ -4203,6 +4207,171 @@ fn muldf3() { } } + #[derive(Eq, Hash, PartialEq)] + pub struct Mulsf3vfp { + a: u32, // f32 + b: u32, // f32 + c: u32, // f32 + } + + impl TestCase for Mulsf3vfp { + fn name() -> &'static str { + "mulsf3vfp" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_large_f32(rng); + let b = gen_large_f32(rng); + let c = a * b; + // TODO accept NaNs. We don't do that right now because we can't check + // for NaN-ness on the thumb targets (due to missing intrinsics) + if a.is_nan() || b.is_nan() || c.is_nan() { + return None; + } + + Some( + Mulsf3vfp { + a: to_u32(a), + b: to_u32(b), + c: to_u32(c), + }, + ) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), {c}),", + a = self.a, + b = self.b, + c = self.c + ) + .unwrap(); + } + + fn prologue() -> &'static str { + r#" +#[cfg(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test))] +use core::mem; +use compiler_builtins::float::mul::__mulsf3vfp; + +fn mk_f32(x: u32) -> f32 { + unsafe { mem::transmute(x) } +} + +fn to_u32(x: f32) -> u32 { + unsafe { mem::transmute(x) } +} + +static TEST_CASES: &[((u32, u32), u32)] = &[ +"# + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn mulsf3vfp() { + for &((a, b), c) in TEST_CASES { + let c_ = __mulsf3vfp(mk_f32(a), mk_f32(b)); + assert_eq!(((a, b), c), ((a, b), to_u32(c_))); + } +} +" + } + } + + #[derive(Eq, Hash, PartialEq)] + pub struct Muldf3vfp { + a: u64, // f64 + b: u64, // f64 + c: u64, // f64 + } + + impl TestCase for Muldf3vfp { + fn name() -> &'static str { + "muldf3vfp" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_large_f64(rng); + let b = gen_large_f64(rng); + let c = a * b; + // TODO accept NaNs. We don't do that right now because we can't check + // for NaN-ness on the thumb targets (due to missing intrinsics) + if a.is_nan() || b.is_nan() || c.is_nan() { + return None; + } + + Some( + Muldf3vfp { + a: to_u64(a), + b: to_u64(b), + c: to_u64(c), + }, + ) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), {c}),", + a = self.a, + b = self.b, + c = self.c + ) + .unwrap(); + } + + fn prologue() -> &'static str { + r#" +#[cfg(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test))] +use core::mem; +use compiler_builtins::float::mul::__muldf3vfp; + +fn mk_f64(x: u64) -> f64 { + unsafe { mem::transmute(x) } +} + +fn to_u64(x: f64) -> u64 { + unsafe { mem::transmute(x) } +} + +static TEST_CASES: &[((u64, u64), u64)] = &[ +"# + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn muldf3vfp() { + for &((a, b), c) in TEST_CASES { + let c_ = __muldf3vfp(mk_f64(a), mk_f64(b)); + assert_eq!(((a, b), c), ((a, b), to_u64(c_))); + } +} +" + } + } + + #[derive(Eq, Hash, PartialEq)] pub struct Divsf3 { a: u32, // f32 @@ -4384,6 +4553,176 @@ fn divdf3() { } } + #[derive(Eq, Hash, PartialEq)] + pub struct Divsf3vfp { + a: u32, // f32 + b: u32, // f32 + c: u32, // f32 + } + + impl TestCase for Divsf3vfp { + fn name() -> &'static str { + "divsf3vfp" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_large_f32(rng); + let b = gen_large_f32(rng); + if b == 0.0 { + return None; + } + let c = a / b; + // TODO accept NaNs. We don't do that right now because we can't check + // for NaN-ness on the thumb targets (due to missing intrinsics) + if a.is_nan() || b.is_nan() || c.is_nan()|| c.abs() <= unsafe { mem::transmute(16777215u32) } { + return None; + } + + Some( + Divsf3vfp { + a: to_u32(a), + b: to_u32(b), + c: to_u32(c), + }, + ) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), {c}),", + a = self.a, + b = self.b, + c = self.c + ) + .unwrap(); + } + + fn prologue() -> &'static str { + r#" +#[cfg(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test))] +use core::mem; +use compiler_builtins::float::div::__divsf3vfp; + +fn mk_f32(x: u32) -> f32 { + unsafe { mem::transmute(x) } +} + +fn to_u32(x: f32) -> u32 { + unsafe { mem::transmute(x) } +} + +static TEST_CASES: &[((u32, u32), u32)] = &[ +"# + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn divsf3vfp() { + for &((a, b), c) in TEST_CASES { + let c_ = __divsf3vfp(mk_f32(a), mk_f32(b)); + assert_eq!(((a, b), c), ((a, b), to_u32(c_))); + } +} +" + } + } + + #[derive(Eq, Hash, PartialEq)] + pub struct Divdf3vfp { + a: u64, // f64 + b: u64, // f64 + c: u64, // f64 + } + + impl TestCase for Divdf3vfp { + fn name() -> &'static str { + "divdf3vfp" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_large_f64(rng); + let b = gen_large_f64(rng); + if b == 0.0 { + return None; + } + let c = a / b; + // TODO accept NaNs. We don't do that right now because we can't check + // for NaN-ness on the thumb targets (due to missing intrinsics) + if a.is_nan() || b.is_nan() || c.is_nan() + || c.abs() <= unsafe { mem::transmute(4503599627370495u64) } { + return None; + } + + Some( + Divdf3vfp { + a: to_u64(a), + b: to_u64(b), + c: to_u64(c), + }, + ) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), {c}),", + a = self.a, + b = self.b, + c = self.c + ) + .unwrap(); + } + + fn prologue() -> &'static str { + r#" +#[cfg(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test))] +use core::mem; +use compiler_builtins::float::div::__divdf3vfp; + +fn mk_f64(x: u64) -> f64 { + unsafe { mem::transmute(x) } +} + +fn to_u64(x: f64) -> u64 { + unsafe { mem::transmute(x) } +} + +static TEST_CASES: &[((u64, u64), u64)] = &[ +"# + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn divdf3vfp() { + for &((a, b), c) in TEST_CASES { + let c_ = __divdf3vfp(mk_f64(a), mk_f64(b)); + assert_eq!(((a, b), c), ((a, b), to_u64(c_))); + } +} +" + } + } #[derive(Eq, Hash, PartialEq)] pub struct Udivdi3 { diff --git a/tests/divdf3vfp.rs b/tests/divdf3vfp.rs new file mode 100644 index 0000000..9225a5c --- /dev/null +++ b/tests/divdf3vfp.rs @@ -0,0 +1,8 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", test), + no_std)] + +#[cfg(target_arch = "arm")] +include!(concat!(env!("OUT_DIR"), "/divdf3vfp.rs")); diff --git a/tests/divsf3vfp.rs b/tests/divsf3vfp.rs new file mode 100644 index 0000000..0ecc8fe --- /dev/null +++ b/tests/divsf3vfp.rs @@ -0,0 +1,8 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", test), + no_std)] + +#[cfg(target_arch = "arm")] +include!(concat!(env!("OUT_DIR"), "/divsf3vfp.rs")); diff --git a/tests/muldf3vfp.rs b/tests/muldf3vfp.rs new file mode 100644 index 0000000..8a71715 --- /dev/null +++ b/tests/muldf3vfp.rs @@ -0,0 +1,8 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", test), + no_std)] + +#[cfg(target_arch = "arm")] +include!(concat!(env!("OUT_DIR"), "/muldf3vfp.rs")); diff --git a/tests/mulsf3vfp.rs b/tests/mulsf3vfp.rs new file mode 100644 index 0000000..4d7f288 --- /dev/null +++ b/tests/mulsf3vfp.rs @@ -0,0 +1,8 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", test), + no_std)] + +#[cfg(target_arch = "arm")] +include!(concat!(env!("OUT_DIR"), "/mulsf3vfp.rs")); From 1269ad14382842ace65e4cac27f9c579a07ab3c5 Mon Sep 17 00:00:00 2001 From: Paolo Teti Date: Tue, 30 Jan 2018 23:10:56 +0100 Subject: [PATCH 4/4] Fix build errors on arm*-linux-gnueabi I removed std::mem, but We need it. --- build.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/build.rs b/build.rs index a8407b2..12ed31f 100644 --- a/build.rs +++ b/build.rs @@ -4260,6 +4260,11 @@ fn muldf3() { target_os = "linux", test))] use core::mem; +#[cfg(not(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test)))] +use std::mem; use compiler_builtins::float::mul::__mulsf3vfp; fn mk_f32(x: u32) -> f32 { @@ -4342,6 +4347,11 @@ fn mulsf3vfp() { target_os = "linux", test))] use core::mem; +#[cfg(not(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test)))] +use std::mem; use compiler_builtins::float::mul::__muldf3vfp; fn mk_f64(x: u64) -> f64 { @@ -4609,6 +4619,11 @@ fn divdf3() { target_os = "linux", test))] use core::mem; +#[cfg(not(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test)))] +use std::mem; use compiler_builtins::float::div::__divsf3vfp; fn mk_f32(x: u32) -> f32 { @@ -4695,6 +4710,11 @@ fn divsf3vfp() { target_os = "linux", test))] use core::mem; +#[cfg(not(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test)))] +use std::mem; use compiler_builtins::float::div::__divdf3vfp; fn mk_f64(x: u64) -> f64 {