Bladeren bron

Auto merge of #166 - alexcrichton:test-c, r=japaric

Test with the 'c' feature enabled on CI
bors 7 jaren geleden
bovenliggende
commit
6024080570
25 gewijzigde bestanden met toevoegingen van 1325 en 1261 verwijderingen
  1. 1 4
      .travis.yml
  2. 9 1
      Cargo.toml
  3. 30 6
      appveyor.yml
  4. 14 50
      build.rs
  5. 68 33
      ci/run.sh
  6. 27 21
      examples/intrinsics.rs
  7. 17 75
      src/arm.rs
  8. 163 163
      src/float/add.rs
  9. 167 96
      src/float/conv.rs
  10. 30 24
      src/float/pow.rs
  11. 9 16
      src/float/sub.rs
  12. 141 6
      src/int/mod.rs
  13. 78 76
      src/int/mul.rs
  14. 81 83
      src/int/sdiv.rs
  15. 78 55
      src/int/shift.rs
  16. 115 164
      src/int/udiv.rs
  17. 6 63
      src/lib.rs
  18. 282 0
      src/macros.rs
  19. 4 4
      src/mem.rs
  20. 0 311
      src/qc.rs
  21. 1 2
      tests/divti3.rs
  22. 1 2
      tests/modti3.rs
  23. 1 2
      tests/udivmodti4.rs
  24. 1 2
      tests/udivti3.rs
  25. 1 2
      tests/umodti3.rs

+ 1 - 4
.travis.yml

@@ -1,4 +1,3 @@
-cache: cargo
 dist: trusty
 language: rust
 rust: nightly
@@ -28,7 +27,7 @@ matrix:
     - env: TARGET=thumbv7m-linux-eabi
     - env: TARGET=x86_64-apple-darwin
       os: osx
-env: TARGET=x86_64-unknown-linux-gnu
+    - env: TARGET=x86_64-unknown-linux-gnu
 
 before_install:
   - test "$TRAVIS_OS_NAME" = "osx" || docker run --rm --privileged multiarch/qemu-user-static:register
@@ -52,8 +51,6 @@ script:
     else
       sh ci/run.sh $TARGET;
     fi
-  # Travis can't cache files that are not readable by "others"
-  - chmod -R a+r $HOME/.cargo
 
 notifications:
   email:

+ 9 - 1
Cargo.toml

@@ -18,7 +18,13 @@ compiler-builtins = []
 default = ["compiler-builtins"]
 mem = []
 rustbuild = ["compiler-builtins"]
+mangled-names = []
+
 # generate tests
+#
+# Note that this is an internal-only feature used in testing, this should not
+# be relied on with crates.io! Enabling this may expose you to breaking
+# changes.
 gen-tests = ["cast", "rand"]
 
 [target.'cfg(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")), target_os = "linux"))'.dev-dependencies]
@@ -26,6 +32,8 @@ test = { git = "https://github.com/japaric/utest" }
 utest-cortex-m-qemu = { default-features = false, git = "https://github.com/japaric/utest" }
 utest-macros = { git = "https://github.com/japaric/utest" }
 
-
+[[example]]
+name = "intrinsics"
+required-features = ["c", "compiler-builtins"]
 
 [workspace]

+ 30 - 6
appveyor.yml

@@ -1,21 +1,45 @@
 environment:
+  # It's... a little unclear why the memcpy symbols clash on linux but not on
+  # other platforms. Would be great to not differ on this though!
+  INTRINSICS_FAILS_WITH_MEM_FEATURE: 1
+
   matrix:
     - TARGET: i686-pc-windows-msvc
     - TARGET: x86_64-pc-windows-msvc
 
+    # Ensure MinGW works, but we need to download the 32-bit MinGW compiler from a
+    # custom location.
+    #
+    # Note that the MinGW builds have tons of references to
+    # `rust_eh_unwind_resume` in the debug LTO builds that aren't optimized out,
+    # so we skip that test for now. Would be great to not skip it!
+    - TARGET: i686-pc-windows-gnu
+      MINGW_URL: https://s3.amazonaws.com/rust-lang-ci
+      MINGW_ARCHIVE: i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z
+      MINGW_DIR: mingw32
+      DEBUG_LTO_BUILD_DOESNT_WORK: 1
+    - TARGET: x86_64-pc-windows-gnu
+      DEBUG_LTO_BUILD_DOESNT_WORK: 1
+
 install:
   - git submodule update --init
-  - curl -sSf -o rustup-init.exe https://win.rustup.rs
+  - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
   - rustup-init.exe --default-host x86_64-pc-windows-msvc --default-toolchain nightly -y
   - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
-  - if "%TARGET%"=="i686-pc-windows-msvc" ( rustup target add %TARGET% )
+  - if NOT "%TARGET%" == "x86_64-pc-windows-msvc" rustup target add %TARGET%
+
+  # Use the system msys
+  - set PATH=C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%
+
+  # download a custom compiler otherwise
+  - if defined MINGW_URL appveyor DownloadFile %MINGW_URL%/%MINGW_ARCHIVE%
+  - if defined MINGW_URL 7z x -y %MINGW_ARCHIVE% > nul
+  - if defined MINGW_URL set PATH=C:\Python27;%CD%\%MINGW_DIR%\bin;C:\msys64\usr\bin;%PATH%
+
   - rustc -Vv
   - cargo -V
 
 build: false
 
 test_script:
-  - cargo build --target %TARGET%
-  - cargo build --release --target %TARGET%
-  - cargo test --no-default-features --features gen-tests --target %TARGET%
-  - cargo test --no-default-features --features gen-tests --release --target %TARGET%
+  - sh ci/run.sh %TARGET%

+ 14 - 50
build.rs

@@ -4066,28 +4066,8 @@ mod c {
                 "divxc3.c",
                 "extendsfdf2.c",
                 "extendhfsf2.c",
-                "ffsdi2.c",
-                "fixdfdi.c",
-                "fixdfsi.c",
-                "fixsfdi.c",
-                "fixsfsi.c",
-                "fixunsdfdi.c",
-                "fixunsdfsi.c",
-                "fixunssfdi.c",
-                "fixunssfsi.c",
-                "fixunsxfdi.c",
-                "fixunsxfsi.c",
-                "fixxfdi.c",
-                "floatdidf.c",
                 "floatdisf.c",
-                "floatdixf.c",
-                "floatsidf.c",
-                "floatsisf.c",
-                "floatundidf.c",
                 "floatundisf.c",
-                "floatundixf.c",
-                "floatunsidf.c",
-                "floatunsisf.c",
                 "int_util.c",
                 "muldc3.c",
                 "muldf3.c",
@@ -4124,18 +4104,6 @@ mod c {
                     "cmpti2.c",
                     "ctzti2.c",
                     "ffsti2.c",
-                    "fixdfti.c",
-                    "fixsfti.c",
-                    "fixunsdfti.c",
-                    "fixunssfti.c",
-                    "fixunsxfti.c",
-                    "fixxfti.c",
-                    "floattidf.c",
-                    "floattisf.c",
-                    "floattixf.c",
-                    "floatuntidf.c",
-                    "floatuntisf.c",
-                    "floatuntixf.c",
                     "mulvti3.c",
                     "negti2.c",
                     "negvti2.c",
@@ -4164,30 +4132,26 @@ mod c {
             if target_arch == "x86_64" {
                 sources.extend(
                     &[
-                        "x86_64/floatdidf.c",
                         "x86_64/floatdisf.c",
                         "x86_64/floatdixf.c",
                     ],
                 );
             }
         } else {
-            if target_os != "freebsd" && target_os != "netbsd" {
-                sources.extend(&["gcc_personality_v0.c"]);
-            }
-
-            if target_arch == "x86_64" {
-                sources.extend(
-                    &[
-                        "x86_64/chkstk.S",
-                        "x86_64/chkstk2.S",
-                        "x86_64/floatdidf.c",
-                        "x86_64/floatdisf.c",
-                        "x86_64/floatdixf.c",
-                        "x86_64/floatundidf.S",
-                        "x86_64/floatundisf.S",
-                        "x86_64/floatundixf.S",
-                    ],
-                );
+            // None of these seem to be used on x86_64 windows, and they've all
+            // got the wrong ABI anyway, so we want to avoid them.
+            if target_os != "windows" {
+                if target_arch == "x86_64" {
+                    sources.extend(
+                        &[
+                            "x86_64/floatdisf.c",
+                            "x86_64/floatdixf.c",
+                            "x86_64/floatundidf.S",
+                            "x86_64/floatundisf.S",
+                            "x86_64/floatundixf.S",
+                        ],
+                    );
+                }
             }
 
             if target_arch == "x86" {

+ 68 - 33
ci/run.sh

@@ -1,5 +1,23 @@
 set -ex
 
+case $1 in
+    thumb*)
+        cargo=xargo
+        ;;
+    *)
+        cargo=cargo
+        ;;
+esac
+
+INTRINSICS_FEATURES="c"
+
+# Some architectures like ARM apparently seem to require the `mem` feature
+# enabled to successfully compile the `intrinsics` example, and... we're not
+# sure why!
+if [ -z "$INTRINSICS_FAILS_WITH_MEM_FEATURE" ]; then
+  INTRINSICS_FEATURES="$INTRINSICS_FEATURES mem"
+fi
+
 # Test our implementation
 case $1 in
     thumb*)
@@ -33,35 +51,14 @@ case $1 in
         done
         ;;
     *)
-        cargo test --no-default-features --features gen-tests --target $1
-        cargo test --no-default-features --features gen-tests --target $1 --release
+        run="cargo test --no-default-features --target $1"
+        $run --features 'gen-tests mangled-names'
+        $run --features 'gen-tests mangled-names' --release
+        $run --features 'gen-tests mangled-names c'
+        $run --features 'gen-tests mangled-names c' --release
         ;;
 esac
 
-# Verify that we haven't drop any intrinsic/symbol
-case $1 in
-    thumb*)
-        xargo build --features c --target $1 --example intrinsics
-        ;;
-    *)
-        cargo build --no-default-features --features c --target $1 --example intrinsics
-        ;;
-esac
-
-# Verify that there are no undefined symbols to `panic` within our implementations
-# TODO(#79) fix the undefined references problem for debug-assertions+lto
-case $1 in
-    thumb*)
-        RUSTFLAGS="-C debug-assertions=no" xargo rustc --no-default-features --features c --target $1 --example intrinsics -- -C lto -C link-arg=-nostartfiles
-        xargo rustc --no-default-features --features c --target $1 --example intrinsics --release -- -C lto
-        ;;
-    *)
-        RUSTFLAGS="-C debug-assertions=no" cargo rustc --no-default-features --features c --target $1 --example intrinsics -- -C lto
-        cargo rustc --no-default-features --features c --target $1 --example intrinsics --release -- -C lto
-        ;;
-esac
-
-# Look out for duplicated symbols when we include the compiler-rt (C) implementation
 PREFIX=$(echo $1 | sed -e 's/unknown-//')-
 case $1 in
     armv7-*)
@@ -75,7 +72,7 @@ case $1 in
         ;;
 esac
 
-case $TRAVIS_OS_NAME in
+case "$TRAVIS_OS_NAME" in
     osx)
         # NOTE OSx's nm doesn't accept the `--defined-only` or provide an equivalent.
         # Use GNU nm instead
@@ -87,22 +84,60 @@ case $TRAVIS_OS_NAME in
         ;;
 esac
 
-if [ $TRAVIS_OS_NAME = osx ]; then
-    path=target/${1}/debug/deps/libcompiler_builtins-*.rlib
-else
+if [ -d /target ]; then
     path=/target/${1}/debug/deps/libcompiler_builtins-*.rlib
+else
+    path=target/${1}/debug/deps/libcompiler_builtins-*.rlib
 fi
 
+# Look out for duplicated symbols when we include the compiler-rt (C) implementation
 for rlib in $(echo $path); do
-    stdout=$($PREFIX$NM -g --defined-only $rlib)
+    set +x
+    stdout=$($PREFIX$NM -g --defined-only $rlib 2>&1)
 
-    # NOTE On i586, It's normal that the get_pc_thunk symbol appears several times so ignore it
+    # NOTE On i586, It's normal that the get_pc_thunk symbol appears several
+    # times so ignore it
+    #
+    # FIXME(#167) - we shouldn't ignore `__builtin_cl` style symbols here.
     set +e
-    echo "$stdout" | sort | uniq -d | grep -v __x86.get_pc_thunk | grep 'T __'
+    echo "$stdout" | \
+      sort | \
+      uniq -d | \
+      grep -v __x86.get_pc_thunk | \
+      grep -v __builtin_cl | \
+      grep 'T __'
+
+    if test $? = 0; then
+        exit 1
+    fi
+    set -ex
+done
+
+rm -f $path
+
+# Verify that we haven't drop any intrinsic/symbol
+RUSTFLAGS="-C debug-assertions=no" \
+  $cargo build --features "$INTRINSICS_FEATURES" --target $1 --example intrinsics -v
+
+# Verify that there are no undefined symbols to `panic` within our
+# implementations
+#
+# TODO(#79) fix the undefined references problem for debug-assertions+lto
+if [ -z "$DEBUG_LTO_BUILD_DOESNT_WORK" ]; then
+  RUSTFLAGS="-C debug-assertions=no" \
+    $cargo rustc --features "$INTRINSICS_FEATURES" --target $1 --example intrinsics -- -C lto
+fi
+$cargo rustc --features "$INTRINSICS_FEATURES" --target $1 --example intrinsics --release -- -C lto
+
+# Ensure no references to a panicking function
+for rlib in $(echo $path); do
+    set +ex
+    $PREFIX$NM -u $rlib 2>&1 | grep panicking
 
     if test $? = 0; then
         exit 1
     fi
+    set -ex
 done
 
 true

+ 27 - 21
examples/intrinsics.rs

@@ -6,18 +6,21 @@
 #![allow(unused_features)]
 #![cfg_attr(thumb, no_main)]
 #![deny(dead_code)]
+#![feature(alloc_system)]
 #![feature(asm)]
 #![feature(compiler_builtins_lib)]
 #![feature(core_float)]
 #![feature(lang_items)]
-#![feature(libc)]
 #![feature(start)]
 #![feature(i128_type)]
+#![cfg_attr(windows, feature(panic_unwind))]
 #![no_std]
 
 #[cfg(not(thumb))]
-extern crate libc;
+extern crate alloc_system;
 extern crate compiler_builtins;
+#[cfg(windows)]
+extern crate panic_unwind;
 
 // NOTE cfg(not(thumbv6m)) means that the operation is not supported on ARMv6-M at all. Not even
 // compiler-rt provides a C/assembly implementation.
@@ -27,7 +30,6 @@ extern crate compiler_builtins;
 // convention for its intrinsics that's different from other architectures; that's why some function
 // have an additional comment: the function name is the ARM name for the intrinsic and the comment
 // in the non-ARM name for the intrinsic.
-#[cfg(feature = "c")]
 mod intrinsics {
     use core::num::Float;
 
@@ -339,7 +341,6 @@ mod intrinsics {
     }
 }
 
-#[cfg(feature = "c")]
 fn run() {
     use intrinsics::*;
 
@@ -402,34 +403,40 @@ fn run() {
     bb(umodti3(bb(2), bb(2)));
     bb(divti3(bb(2), bb(2)));
     bb(modti3(bb(2), bb(2)));
+
+    something_with_a_dtor(&|| assert_eq!(bb(1), 1));
 }
 
-#[cfg(all(feature = "c", not(thumb)))]
-#[start]
-fn main(_: isize, _: *const *const u8) -> isize {
-    run();
+fn something_with_a_dtor(f: &Fn()) {
+    struct A<'a>(&'a (Fn() + 'a));
 
-    0
+    impl<'a> Drop for A<'a> {
+        fn drop(&mut self) {
+            (self.0)();
+        }
+    }
+    let _a = A(f);
+    f();
 }
 
-#[cfg(all(not(feature = "c"), not(thumb)))]
+#[cfg(not(thumb))]
 #[start]
 fn main(_: isize, _: *const *const u8) -> isize {
+    run();
     0
 }
 
-#[cfg(all(feature = "c", thumb))]
+#[cfg(thumb)]
 #[no_mangle]
 pub fn _start() -> ! {
     run();
     loop {}
 }
 
-#[cfg(all(not(feature = "c"), thumb))]
-#[no_mangle]
-pub fn _start() -> ! {
-    loop {}
-}
+#[cfg(windows)]
+#[link(name = "kernel32")]
+#[link(name = "msvcrt")]
+extern {}
 
 // ARM targets need these symbols
 #[no_mangle]
@@ -438,18 +445,17 @@ pub fn __aeabi_unwind_cpp_pr0() {}
 #[no_mangle]
 pub fn __aeabi_unwind_cpp_pr1() {}
 
-// Avoid "undefined reference to `_Unwind_Resume`" errors
+#[cfg(not(windows))]
 #[allow(non_snake_case)]
 #[no_mangle]
 pub fn _Unwind_Resume() {}
 
-// Lang items
-#[cfg(not(test))]
+#[cfg(not(windows))]
 #[lang = "eh_personality"]
 #[no_mangle]
-extern "C" fn eh_personality() {}
+pub extern "C" fn eh_personality() {}
 
-#[cfg(not(test))]
 #[lang = "panic_fmt"]
 #[no_mangle]
+#[allow(private_no_mangle_fns)]
 extern "C" fn panic_fmt() {}

+ 17 - 75
src/arm.rs

@@ -6,7 +6,7 @@ use mem::{memcpy, memmove, memset};
 // NOTE This function and the ones below are implemented using assembly because they using a custom
 // calling convention which can't be implemented using a normal Rust function
 #[naked]
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe fn __aeabi_uidivmod() {
     asm!("push {lr}
           sub sp, sp, #4
@@ -19,7 +19,7 @@ pub unsafe fn __aeabi_uidivmod() {
 }
 
 #[naked]
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe fn __aeabi_uldivmod() {
     asm!("push {r4, lr}
           sub sp, sp, #16
@@ -34,10 +34,10 @@ pub unsafe fn __aeabi_uldivmod() {
 }
 
 #[naked]
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe fn __aeabi_idivmod() {
     asm!("push {r0, r1, r4, lr}
-          bl __divsi3
+          bl __aeabi_idiv
           pop {r1, r2}
           muls r2, r2, r0
           subs r1, r1, r2
@@ -46,7 +46,7 @@ pub unsafe fn __aeabi_idivmod() {
 }
 
 #[naked]
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe fn __aeabi_ldivmod() {
     asm!("push {r4, lr}
           sub sp, sp, #16
@@ -60,64 +60,6 @@ pub unsafe fn __aeabi_ldivmod() {
     intrinsics::unreachable();
 }
 
-#[cfg_attr(not(test), no_mangle)]
-pub extern "aapcs" fn __aeabi_dadd(a: f64, b: f64) -> f64 {
-    ::float::add::__adddf3(a, b)
-}
-
-#[cfg_attr(not(test), no_mangle)]
-pub extern "aapcs" fn __aeabi_fadd(a: f32, b: f32) -> f32 {
-    ::float::add::__addsf3(a, b)
-}
-
-#[cfg_attr(not(test), no_mangle)]
-pub extern "aapcs" fn __aeabi_dsub(a: f64, b: f64) -> f64 {
-    ::float::sub::__subdf3(a, b)
-}
-
-#[cfg_attr(not(test), no_mangle)]
-pub extern "aapcs" fn __aeabi_fsub(a: f32, b: f32) -> f32 {
-    ::float::sub::__subsf3(a, b)
-}
-
-#[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"), not(thumbv6m))))]
-#[cfg_attr(not(test), no_mangle)]
-pub extern "aapcs" fn __aeabi_idiv(a: i32, b: i32) -> i32 {
-    ::int::sdiv::__divsi3(a, b)
-}
-
-#[cfg_attr(not(test), no_mangle)]
-pub extern "aapcs" fn __aeabi_lasr(a: i64, b: u32) -> i64 {
-    ::int::shift::__ashrdi3(a, b)
-}
-
-#[cfg_attr(not(test), no_mangle)]
-pub extern "aapcs" fn __aeabi_llsl(a: u64, b: u32) -> u64 {
-    ::int::shift::__ashldi3(a, b)
-}
-
-#[cfg_attr(not(test), no_mangle)]
-pub extern "aapcs" fn __aeabi_llsr(a: u64, b: u32) -> u64 {
-    ::int::shift::__lshrdi3(a, b)
-}
-
-#[cfg_attr(not(test), no_mangle)]
-pub extern "aapcs" fn __aeabi_lmul(a: u64, b: u64) -> u64 {
-    ::int::mul::__muldi3(a, b)
-}
-
-#[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"), not(thumbv6m))))]
-#[cfg_attr(not(test), no_mangle)]
-pub extern "aapcs" 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)
-}
-
 // TODO: These aeabi_* functions should be defined as aliases
 #[cfg(not(feature = "mem"))]
 extern "C" {
@@ -128,55 +70,55 @@ extern "C" {
 
 // FIXME: The `*4` and `*8` variants should be defined as aliases.
 
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) {
     memcpy(dest, src, n);
 }
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) {
     memcpy(dest, src, n);
 }
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "aapcs" fn __aeabi_memcpy8(dest: *mut u8, src: *const u8, n: usize) {
     memcpy(dest, src, n);
 }
 
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "aapcs" fn __aeabi_memmove(dest: *mut u8, src: *const u8, n: usize) {
     memmove(dest, src, n);
 }
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "aapcs" fn __aeabi_memmove4(dest: *mut u8, src: *const u8, n: usize) {
     memmove(dest, src, n);
 }
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "aapcs" fn __aeabi_memmove8(dest: *mut u8, src: *const u8, n: usize) {
     memmove(dest, src, n);
 }
 
 // Note the different argument order
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) {
     memset(dest, c, n);
 }
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) {
     memset(dest, c, n);
 }
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "aapcs" fn __aeabi_memset8(dest: *mut u8, n: usize, c: i32) {
     memset(dest, c, n);
 }
 
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) {
     memset(dest, 0, n);
 }
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) {
     memset(dest, 0, n);
 }
-#[cfg_attr(not(test), no_mangle)]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "aapcs" fn __aeabi_memclr8(dest: *mut u8, n: usize) {
     memset(dest, 0, n);
 }

+ 163 - 163
src/float/add.rs

@@ -3,192 +3,192 @@ use core::num::Wrapping;
 
 use float::Float;
 
+/// Returns `a + b`
 macro_rules! add {
-    ($abi:tt, $intrinsic:ident: $ty:ty) => {
-        /// Returns `a + b`
-        #[allow(unused_parens)]
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern $abi fn $intrinsic(a: $ty, b: $ty) -> $ty {
-            let one = Wrapping(1 as <$ty as Float>::Int);
-            let zero = Wrapping(0 as <$ty as Float>::Int);
-
-            let bits =             Wrapping(<$ty>::bits() as <$ty as Float>::Int);
-            let significand_bits = Wrapping(<$ty>::significand_bits() as <$ty as Float>::Int);
-            let exponent_bits =    bits - significand_bits - one;
-            let max_exponent =     (one << exponent_bits.0 as usize) - one;
-
-            let implicit_bit =     one << significand_bits.0 as usize;
-            let significand_mask = implicit_bit - one;
-            let sign_bit =         one << (significand_bits + exponent_bits).0 as usize;
-            let abs_mask =         sign_bit - one;
-            let exponent_mask =    abs_mask ^ significand_mask;
-            let inf_rep =          exponent_mask;
-            let quiet_bit =        implicit_bit >> 1;
-            let qnan_rep =         exponent_mask | quiet_bit;
-
-            let mut a_rep = Wrapping(a.repr());
-            let mut b_rep = Wrapping(b.repr());
-            let a_abs = a_rep & abs_mask;
-            let b_abs = b_rep & abs_mask;
-
-            // Detect if a or b is zero, infinity, or NaN.
-            if a_abs - one >= inf_rep - one ||
-                b_abs - one >= inf_rep - one {
-                // NaN + anything = qNaN
-                if a_abs > inf_rep {
-                    return (<$ty as Float>::from_repr((a_abs | quiet_bit).0));
-                }
-                // anything + NaN = qNaN
-                if b_abs > inf_rep {
-                    return (<$ty as Float>::from_repr((b_abs | quiet_bit).0));
-                }
-
-                if a_abs == inf_rep {
-                    // +/-infinity + -/+infinity = qNaN
-                    if (a.repr() ^ b.repr()) == sign_bit.0 {
-                        return (<$ty as Float>::from_repr(qnan_rep.0));
-                    } else {
-                        // +/-infinity + anything remaining = +/- infinity
-                        return a;
-                    }
-                }
+    ($a:expr, $b:expr, $ty:ty) => ({
+        let a = $a;
+        let b = $b;
+        let one = Wrapping(1 as <$ty as Float>::Int);
+        let zero = Wrapping(0 as <$ty as Float>::Int);
+
+        let bits =             Wrapping(<$ty>::bits() as <$ty as Float>::Int);
+        let significand_bits = Wrapping(<$ty>::significand_bits() as <$ty as Float>::Int);
+        let exponent_bits =    bits - significand_bits - one;
+        let max_exponent =     (one << exponent_bits.0 as usize) - one;
+
+        let implicit_bit =     one << significand_bits.0 as usize;
+        let significand_mask = implicit_bit - one;
+        let sign_bit =         one << (significand_bits + exponent_bits).0 as usize;
+        let abs_mask =         sign_bit - one;
+        let exponent_mask =    abs_mask ^ significand_mask;
+        let inf_rep =          exponent_mask;
+        let quiet_bit =        implicit_bit >> 1;
+        let qnan_rep =         exponent_mask | quiet_bit;
+
+        let mut a_rep = Wrapping(a.repr());
+        let mut b_rep = Wrapping(b.repr());
+        let a_abs = a_rep & abs_mask;
+        let b_abs = b_rep & abs_mask;
+
+        // Detect if a or b is zero, infinity, or NaN.
+        if a_abs - one >= inf_rep - one ||
+            b_abs - one >= inf_rep - one {
+            // NaN + anything = qNaN
+            if a_abs > inf_rep {
+                return <$ty as Float>::from_repr((a_abs | quiet_bit).0);
+            }
+            // anything + NaN = qNaN
+            if b_abs > inf_rep {
+                return <$ty as Float>::from_repr((b_abs | quiet_bit).0);
+            }
 
-                // anything remaining + +/-infinity = +/-infinity
-                if b_abs == inf_rep {
-                    return b;
+            if a_abs == inf_rep {
+                // +/-infinity + -/+infinity = qNaN
+                if (a.repr() ^ b.repr()) == sign_bit.0 {
+                    return <$ty as Float>::from_repr(qnan_rep.0);
+                } else {
+                    // +/-infinity + anything remaining = +/- infinity
+                    return a;
                 }
+            }
 
-                // zero + anything = anything
-                if a_abs.0 == 0 {
-                    // but we need to get the sign right for zero + zero
-                    if b_abs.0 == 0 {
-                        return (<$ty as Float>::from_repr(a.repr() & b.repr()));
-                    } else {
-                        return b;
-                    }
-                }
+            // anything remaining + +/-infinity = +/-infinity
+            if b_abs == inf_rep {
+                return b;
+            }
 
-                // anything + zero = anything
+            // zero + anything = anything
+            if a_abs.0 == 0 {
+                // but we need to get the sign right for zero + zero
                 if b_abs.0 == 0 {
-                     return a;
+                    return <$ty as Float>::from_repr(a.repr() & b.repr());
+                } else {
+                    return b;
                 }
             }
 
-            // Swap a and b if necessary so that a has the larger absolute value.
-            if b_abs > a_abs {
-                mem::swap(&mut a_rep, &mut b_rep);
+            // anything + zero = anything
+            if b_abs.0 == 0 {
+                 return a;
             }
+        }
 
-            // Extract the exponent and significand from the (possibly swapped) a and b.
-            let mut a_exponent = Wrapping((a_rep >> significand_bits.0 as usize & max_exponent).0 as i32);
-            let mut b_exponent = Wrapping((b_rep >> significand_bits.0 as usize & max_exponent).0 as i32);
-            let mut a_significand = a_rep & significand_mask;
-            let mut b_significand = b_rep & significand_mask;
-
-            // normalize any denormals, and adjust the exponent accordingly.
-            if a_exponent.0 == 0 {
-                let (exponent, significand) = <$ty>::normalize(a_significand.0);
-                a_exponent = Wrapping(exponent);
-                a_significand = Wrapping(significand);
-            }
-            if b_exponent.0 == 0 {
-                let (exponent, significand) = <$ty>::normalize(b_significand.0);
-                b_exponent = Wrapping(exponent);
-                b_significand = Wrapping(significand);
-            }
+        // Swap a and b if necessary so that a has the larger absolute value.
+        if b_abs > a_abs {
+            mem::swap(&mut a_rep, &mut b_rep);
+        }
 
-            // The sign of the result is the sign of the larger operand, a.  If they
-            // have opposite signs, we are performing a subtraction; otherwise addition.
-            let result_sign = a_rep & sign_bit;
-            let subtraction = ((a_rep ^ b_rep) & sign_bit) != zero;
-
-            // Shift the significands to give us round, guard and sticky, and or in the
-            // implicit significand bit.  (If we fell through from the denormal path it
-            // was already set by normalize(), but setting it twice won't hurt
-            // anything.)
-            a_significand = (a_significand | implicit_bit) << 3;
-            b_significand = (b_significand | implicit_bit) << 3;
-
-            // Shift the significand of b by the difference in exponents, with a sticky
-            // bottom bit to get rounding correct.
-            let align = Wrapping((a_exponent - b_exponent).0 as <$ty as Float>::Int);
-            if align.0 != 0 {
-                if align < bits {
-                    let sticky = ((b_significand << (bits - align).0 as usize).0 != 0) as <$ty as Float>::Int;
-                    b_significand = (b_significand >> align.0 as usize) | Wrapping(sticky);
-                } else {
-                    b_significand = one; // sticky; b is known to be non-zero.
-                }
-            }
-            if subtraction {
-                a_significand -= b_significand;
-                // If a == -b, return +zero.
-                if a_significand.0 == 0 {
-                    return (<$ty as Float>::from_repr(0));
-                }
+        // Extract the exponent and significand from the (possibly swapped) a and b.
+        let mut a_exponent = Wrapping((a_rep >> significand_bits.0 as usize & max_exponent).0 as i32);
+        let mut b_exponent = Wrapping((b_rep >> significand_bits.0 as usize & max_exponent).0 as i32);
+        let mut a_significand = a_rep & significand_mask;
+        let mut b_significand = b_rep & significand_mask;
+
+        // normalize any denormals, and adjust the exponent accordingly.
+        if a_exponent.0 == 0 {
+            let (exponent, significand) = <$ty>::normalize(a_significand.0);
+            a_exponent = Wrapping(exponent);
+            a_significand = Wrapping(significand);
+        }
+        if b_exponent.0 == 0 {
+            let (exponent, significand) = <$ty>::normalize(b_significand.0);
+            b_exponent = Wrapping(exponent);
+            b_significand = Wrapping(significand);
+        }
 
-                // If partial cancellation occured, we need to left-shift the result
-                // and adjust the exponent:
-                if a_significand < implicit_bit << 3 {
-                    let shift = a_significand.0.leading_zeros() as i32
-                        - (implicit_bit << 3).0.leading_zeros() as i32;
-                    a_significand <<= shift as usize;
-                    a_exponent -= Wrapping(shift);
-                }
-            } else /* addition */ {
-                a_significand += b_significand;
-
-                // If the addition carried up, we need to right-shift the result and
-                // adjust the exponent:
-                if (a_significand & implicit_bit << 4).0 != 0 {
-                    let sticky = ((a_significand & one).0 != 0) as <$ty as Float>::Int;
-                    a_significand = a_significand >> 1 | Wrapping(sticky);
-                    a_exponent += Wrapping(1);
-                }
+        // The sign of the result is the sign of the larger operand, a.  If they
+        // have opposite signs, we are performing a subtraction; otherwise addition.
+        let result_sign = a_rep & sign_bit;
+        let subtraction = ((a_rep ^ b_rep) & sign_bit) != zero;
+
+        // Shift the significands to give us round, guard and sticky, and or in the
+        // implicit significand bit.  (If we fell through from the denormal path it
+        // was already set by normalize(), but setting it twice won't hurt
+        // anything.)
+        a_significand = (a_significand | implicit_bit) << 3;
+        b_significand = (b_significand | implicit_bit) << 3;
+
+        // Shift the significand of b by the difference in exponents, with a sticky
+        // bottom bit to get rounding correct.
+        let align = Wrapping((a_exponent - b_exponent).0 as <$ty as Float>::Int);
+        if align.0 != 0 {
+            if align < bits {
+                let sticky = ((b_significand << (bits - align).0 as usize).0 != 0) as <$ty as Float>::Int;
+                b_significand = (b_significand >> align.0 as usize) | Wrapping(sticky);
+            } else {
+                b_significand = one; // sticky; b is known to be non-zero.
             }
-
-            // If we have overflowed the type, return +/- infinity:
-            if a_exponent >= Wrapping(max_exponent.0 as i32) {
-                return (<$ty>::from_repr((inf_rep | result_sign).0));
+        }
+        if subtraction {
+            a_significand -= b_significand;
+            // If a == -b, return +zero.
+            if a_significand.0 == 0 {
+                return <$ty as Float>::from_repr(0);
             }
 
-            if a_exponent.0 <= 0 {
-                // Result is denormal before rounding; the exponent is zero and we
-                // need to shift the significand.
-                let shift = Wrapping((Wrapping(1) - a_exponent).0 as <$ty as Float>::Int);
-                let sticky = ((a_significand << (bits - shift).0 as usize).0 != 0) as <$ty as Float>::Int;
-                a_significand = a_significand >> shift.0 as usize | Wrapping(sticky);
-                a_exponent = Wrapping(0);
+            // If partial cancellation occured, we need to left-shift the result
+            // and adjust the exponent:
+            if a_significand < implicit_bit << 3 {
+                let shift = a_significand.0.leading_zeros() as i32
+                    - (implicit_bit << 3).0.leading_zeros() as i32;
+                a_significand <<= shift as usize;
+                a_exponent -= Wrapping(shift);
             }
+        } else /* addition */ {
+            a_significand += b_significand;
+
+            // If the addition carried up, we need to right-shift the result and
+            // adjust the exponent:
+            if (a_significand & implicit_bit << 4).0 != 0 {
+                let sticky = ((a_significand & one).0 != 0) as <$ty as Float>::Int;
+                a_significand = a_significand >> 1 | Wrapping(sticky);
+                a_exponent += Wrapping(1);
+            }
+        }
 
-            // Low three bits are round, guard, and sticky.
-            let round_guard_sticky: i32 = (a_significand.0 & 0x7) as i32;
+        // If we have overflowed the type, return +/- infinity:
+        if a_exponent >= Wrapping(max_exponent.0 as i32) {
+            return <$ty>::from_repr((inf_rep | result_sign).0);
+        }
 
-            // Shift the significand into place, and mask off the implicit bit.
-            let mut result = a_significand >> 3 & significand_mask;
+        if a_exponent.0 <= 0 {
+            // Result is denormal before rounding; the exponent is zero and we
+            // need to shift the significand.
+            let shift = Wrapping((Wrapping(1) - a_exponent).0 as <$ty as Float>::Int);
+            let sticky = ((a_significand << (bits - shift).0 as usize).0 != 0) as <$ty as Float>::Int;
+            a_significand = a_significand >> shift.0 as usize | Wrapping(sticky);
+            a_exponent = Wrapping(0);
+        }
 
-            // Insert the exponent and sign.
-            result |= Wrapping(a_exponent.0 as <$ty as Float>::Int) << significand_bits.0 as usize;
-            result |= result_sign;
+        // Low three bits are round, guard, and sticky.
+        let round_guard_sticky: i32 = (a_significand.0 & 0x7) as i32;
 
-            // Final rounding.  The result may overflow to infinity, but that is the
-            // correct result in that case.
-            if round_guard_sticky > 0x4 { result += one; }
-            if round_guard_sticky == 0x4 { result += result & one; }
+        // Shift the significand into place, and mask off the implicit bit.
+        let mut result = a_significand >> 3 & significand_mask;
 
-            <$ty>::from_repr(result.0)
-        }
-    }
-}
+        // Insert the exponent and sign.
+        result |= Wrapping(a_exponent.0 as <$ty as Float>::Int) << significand_bits.0 as usize;
+        result |= result_sign;
 
-#[cfg(target_arch = "arm")]
-add!("aapcs", __addsf3: f32);
+        // Final rounding.  The result may overflow to infinity, but that is the
+        // correct result in that case.
+        if round_guard_sticky > 0x4 { result += one; }
+        if round_guard_sticky == 0x4 { result += result & one; }
 
-#[cfg(not(target_arch = "arm"))]
-add!("C", __addsf3: f32);
+        <$ty>::from_repr(result.0)
+    })
+}
 
-#[cfg(target_arch = "arm")]
-add!("aapcs", __adddf3: f64);
+intrinsics! {
+    #[aapcs_on_arm]
+    #[arm_aeabi_alias = __aeabi_fadd]
+    pub extern "C" fn __addsf3(a: f32, b: f32) -> f32 {
+        add!(a, b, f32)
+    }
 
-#[cfg(not(target_arch = "arm"))]
-add!("C", __adddf3: f64);
+    #[aapcs_on_arm]
+    #[arm_aeabi_alias = __aeabi_dadd]
+    pub extern "C" fn __adddf3(a: f64, b: f64) -> f64 {
+        add!(a, b, f64)
+    }
+}

+ 167 - 96
src/float/conv.rs

@@ -2,12 +2,8 @@ use float::Float;
 use int::Int;
 
 macro_rules! int_to_float {
-    ($intrinsic:ident: $ity:ty, $fty:ty) => {
-        int_to_float!($intrinsic: $ity, $fty, "C");
-    };
-    ($intrinsic:ident: $ity:ty, $fty:ty, $abi:tt) => {
-
-    pub extern $abi fn $intrinsic(i: $ity) -> $fty {
+    ($i:expr, $ity:ty, $fty:ty) => ({
+        let i = $i;
         if i == 0 {
             return 0.0
         }
@@ -70,110 +66,185 @@ macro_rules! int_to_float {
         <$fty>::from_parts(s,
             (e + exponent_bias) as <$fty as Float>::Int,
             a as <$fty as Float>::Int)
+    })
+}
+
+intrinsics! {
+    #[arm_aeabi_alias = __aeabi_i2f]
+    pub extern "C" fn __floatsisf(i: i32) -> f32 {
+        int_to_float!(i, i32, f32)
     }
+
+    #[arm_aeabi_alias = __aeabi_i2d]
+    pub extern "C" fn __floatsidf(i: i32) -> f64 {
+        int_to_float!(i, i32, f64)
+    }
+
+    #[use_c_shim_if(all(target_arch = "x86", not(target_env = "msvc")))]
+    #[arm_aeabi_alias = __aeabi_l2d]
+    pub extern "C" fn __floatdidf(i: i64) -> f64 {
+        // On x86_64 LLVM will use native instructions for this conversion, we
+        // can just do it directly
+        if cfg!(target_arch = "x86_64") {
+            i as f64
+        } else {
+            int_to_float!(i, i64, f64)
+        }
+    }
+
+    #[unadjusted_on_win64]
+    pub extern "C" fn __floattisf(i: i128) -> f32 {
+        int_to_float!(i, i128, f32)
+    }
+
+    #[unadjusted_on_win64]
+    pub extern "C" fn __floattidf(i: i128) -> f64 {
+        int_to_float!(i, i128, f64)
+    }
+
+    #[arm_aeabi_alias = __aeabi_ui2f]
+    pub extern "C" fn __floatunsisf(i: u32) -> f32 {
+        int_to_float!(i, u32, f32)
     }
-}
 
-macro_rules! int_to_float_unadj_on_win {
-    ($intrinsic:ident: $ity:ty, $fty:ty) => {
-        #[cfg(all(windows, target_pointer_width="64"))]
-        int_to_float!($intrinsic: $ity, $fty, "unadjusted");
-        #[cfg(not(all(windows, target_pointer_width="64")))]
-        int_to_float!($intrinsic: $ity, $fty, "C");
-    };
+    #[arm_aeabi_alias = __aeabi_ui2d]
+    pub extern "C" fn __floatunsidf(i: u32) -> f64 {
+        int_to_float!(i, u32, f64)
+    }
+
+    #[use_c_shim_if(all(any(target_arch = "x86", target_arch = "x86_64"),
+                        not(windows)))]
+    #[arm_aeabi_alias = __aeabi_ul2d]
+    pub extern "C" fn __floatundidf(i: u64) -> f64 {
+        int_to_float!(i, u64, f64)
+    }
+
+    #[unadjusted_on_win64]
+    pub extern "C" fn __floatuntisf(i: u128) -> f32 {
+        int_to_float!(i, u128, f32)
+    }
+
+    #[unadjusted_on_win64]
+    pub extern "C" fn __floatuntidf(i: u128) -> f64 {
+        int_to_float!(i, u128, f64)
+    }
 }
 
-int_to_float!(__floatsisf: i32, f32);
-int_to_float!(__floatsidf: i32, f64);
-int_to_float!(__floatdidf: i64, f64);
-int_to_float_unadj_on_win!(__floattisf: i128, f32);
-int_to_float_unadj_on_win!(__floattidf: i128, f64);
-int_to_float!(__floatunsisf: u32, f32);
-int_to_float!(__floatunsidf: u32, f64);
-int_to_float!(__floatundidf: u64, f64);
-int_to_float_unadj_on_win!(__floatuntisf: u128, f32);
-int_to_float_unadj_on_win!(__floatuntidf: u128, f64);
-
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
 enum Sign {
     Positive,
     Negative
 }
 
 macro_rules! float_to_int {
-    ($intrinsic:ident: $fty:ty, $ity:ty) => {
-        float_to_int!($intrinsic: $fty, $ity, "C");
-    };
-    ($intrinsic:ident: $fty:ty, $ity:ty, $abi:tt) => {
-        pub extern $abi fn $intrinsic(f: $fty) -> $ity {
-            let fixint_min = <$ity>::min_value();
-            let fixint_max = <$ity>::max_value();
-            let fixint_bits = <$ity>::bits() as usize;
-            let fixint_unsigned = fixint_min == 0;
-
-            let sign_bit = <$fty>::sign_mask();
-            let significand_bits = <$fty>::significand_bits() as usize;
-            let exponent_bias = <$fty>::exponent_bias() as usize;
-            //let exponent_max = <$fty>::exponent_max() as usize;
-
-            // Break a into sign, exponent, significand
-            let a_rep = <$fty>::repr(f);
-            let a_abs = a_rep & !sign_bit;
-
-            // this is used to work around -1 not being available for unsigned
-            let sign = if (a_rep & sign_bit) == 0 { Sign::Positive } else { Sign::Negative };
-            let mut exponent = (a_abs >> significand_bits) as usize;
-            let significand = (a_abs & <$fty>::significand_mask()) | <$fty>::implicit_bit();
-
-            // if < 1 or unsigned & negative
-            if  exponent < exponent_bias ||
-                fixint_unsigned && sign == Sign::Negative {
-                return 0
-            }
-            exponent -= exponent_bias;
+    ($f:expr, $fty:ty, $ity:ty) => ({
+        let f = $f;
+        let fixint_min = <$ity>::min_value();
+        let fixint_max = <$ity>::max_value();
+        let fixint_bits = <$ity>::bits() as usize;
+        let fixint_unsigned = fixint_min == 0;
+
+        let sign_bit = <$fty>::sign_mask();
+        let significand_bits = <$fty>::significand_bits() as usize;
+        let exponent_bias = <$fty>::exponent_bias() as usize;
+        //let exponent_max = <$fty>::exponent_max() as usize;
+
+        // Break a into sign, exponent, significand
+        let a_rep = <$fty>::repr(f);
+        let a_abs = a_rep & !sign_bit;
+
+        // this is used to work around -1 not being available for unsigned
+        let sign = if (a_rep & sign_bit) == 0 { Sign::Positive } else { Sign::Negative };
+        let mut exponent = (a_abs >> significand_bits) as usize;
+        let significand = (a_abs & <$fty>::significand_mask()) | <$fty>::implicit_bit();
+
+        // if < 1 or unsigned & negative
+        if  exponent < exponent_bias ||
+            fixint_unsigned && sign == Sign::Negative {
+            return 0
+        }
+        exponent -= exponent_bias;
 
-            // If the value is infinity, saturate.
-            // If the value is too large for the integer type, 0.
-            if exponent >= (if fixint_unsigned {fixint_bits} else {fixint_bits -1}) {
-                return if sign == Sign::Positive {fixint_max} else {fixint_min}
-            }
-            // If 0 <= exponent < significand_bits, right shift to get the result.
-            // Otherwise, shift left.
-            // (sign - 1) will never overflow as negative signs are already returned as 0 for unsigned
-            let r = if exponent < significand_bits {
-                (significand >> (significand_bits - exponent)) as $ity
-            } else {
-                (significand as $ity) << (exponent - significand_bits)
-            };
+        // If the value is infinity, saturate.
+        // If the value is too large for the integer type, 0.
+        if exponent >= (if fixint_unsigned {fixint_bits} else {fixint_bits -1}) {
+            return if sign == Sign::Positive {fixint_max} else {fixint_min}
+        }
+        // If 0 <= exponent < significand_bits, right shift to get the result.
+        // Otherwise, shift left.
+        // (sign - 1) will never overflow as negative signs are already returned as 0 for unsigned
+        let r = if exponent < significand_bits {
+            (significand >> (significand_bits - exponent)) as $ity
+        } else {
+            (significand as $ity) << (exponent - significand_bits)
+        };
 
-            if sign == Sign::Negative {
-                (!r).wrapping_add(1)
-            } else {
-                r
-            }
+        if sign == Sign::Negative {
+            (!r).wrapping_add(1)
+        } else {
+            r
         }
-    }
+    })
 }
 
-macro_rules! float_to_int_unadj_on_win {
-    ($intrinsic:ident: $fty:ty, $ity:ty) => {
-        #[cfg(all(windows, target_pointer_width="64"))]
-        float_to_int!($intrinsic: $fty, $ity, "unadjusted");
-        #[cfg(not(all(windows, target_pointer_width="64")))]
-        float_to_int!($intrinsic: $fty, $ity, "C");
-    };
-}
+intrinsics! {
+    #[arm_aeabi_alias = __aeabi_f2iz]
+    pub extern "C" fn __fixsfsi(f: f32) -> i32 {
+        float_to_int!(f, f32, i32)
+    }
+
+    #[arm_aeabi_alias = __aeabi_f2lz]
+    pub extern "C" fn __fixsfdi(f: f32) -> i64 {
+        float_to_int!(f, f32, i64)
+    }
+
+    #[unadjusted_on_win64]
+    pub extern "C" fn __fixsfti(f: f32) -> i128 {
+        float_to_int!(f, f32, i128)
+    }
+
+    #[arm_aeabi_alias = __aeabi_d2iz]
+    pub extern "C" fn __fixdfsi(f: f64) -> i32 {
+        float_to_int!(f, f64, i32)
+    }
+
+    #[arm_aeabi_alias = __aeabi_d2lz]
+    pub extern "C" fn __fixdfdi(f: f64) -> i64 {
+        float_to_int!(f, f64, i64)
+    }
 
-float_to_int!(__fixsfsi: f32, i32);
-float_to_int!(__fixsfdi: f32, i64);
-float_to_int_unadj_on_win!(__fixsfti: f32, i128);
-float_to_int!(__fixdfsi: f64, i32);
-float_to_int!(__fixdfdi: f64, i64);
-float_to_int_unadj_on_win!(__fixdfti: f64, i128);
-
-float_to_int!(__fixunssfsi: f32, u32);
-float_to_int!(__fixunssfdi: f32, u64);
-float_to_int_unadj_on_win!(__fixunssfti: f32, u128);
-float_to_int!(__fixunsdfsi: f64, u32);
-float_to_int!(__fixunsdfdi: f64, u64);
-float_to_int_unadj_on_win!(__fixunsdfti: f64, u128);
+    #[unadjusted_on_win64]
+    pub extern "C" fn __fixdfti(f: f64) -> i128 {
+        float_to_int!(f, f64, i128)
+    }
+
+    #[arm_aeabi_alias = __aeabi_f2uiz]
+    pub extern "C" fn __fixunssfsi(f: f32) -> u32 {
+        float_to_int!(f, f32, u32)
+    }
+
+    #[arm_aeabi_alias = __aeabi_f2ulz]
+    pub extern "C" fn __fixunssfdi(f: f32) -> u64 {
+        float_to_int!(f, f32, u64)
+    }
+
+    #[unadjusted_on_win64]
+    pub extern "C" fn __fixunssfti(f: f32) -> u128 {
+        float_to_int!(f, f32, u128)
+    }
+
+    #[arm_aeabi_alias = __aeabi_d2uiz]
+    pub extern "C" fn __fixunsdfsi(f: f64) -> u32 {
+        float_to_int!(f, f64, u32)
+    }
+
+    #[arm_aeabi_alias = __aeabi_d2ulz]
+    pub extern "C" fn __fixunsdfdi(f: f64) -> u64 {
+        float_to_int!(f, f64, u64)
+    }
+
+    #[unadjusted_on_win64]
+    pub extern "C" fn __fixunsdfti(f: f64) -> u128 {
+        float_to_int!(f, f64, u128)
+    }
+}

+ 30 - 24
src/float/pow.rs

@@ -1,30 +1,36 @@
+use int::Int;
+
+/// Returns `a` raised to the power `b`
 macro_rules! pow {
-    ($intrinsic:ident: $fty:ty, $ity:ident) => {
-        /// Returns `a` raised to the power `b`
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern "C" fn $intrinsic(a: $fty, b: $ity) -> $fty {
-            let (mut a, mut b) = (a, b);
-            let recip = b < 0;
-            let mut r: $fty = 1.0;
-            loop {
-                if (b & 1) != 0 {
-                    r *= a;
-                }
-                b = sdiv!($ity, b, 2);
-                if b == 0 {
-                    break;
-                }
-                a *= a;
+    ($a: expr, $b: expr) => ({
+        let (mut a, mut b) = ($a, $b);
+        let recip = b < 0;
+        let mut r = 1.0;
+        loop {
+            if (b & 1) != 0 {
+                r *= a;
             }
-
-            if recip {
-                1.0 / r
-            } else {
-                r
+            b = b.aborting_div(2);
+            if b == 0 {
+                break;
             }
+            a *= a;
         }
-    }
+
+        if recip {
+            1.0 / r
+        } else {
+            r
+        }
+    })
 }
 
-pow!(__powisf2: f32, i32);
-pow!(__powidf2: f64, i32);
+intrinsics! {
+    pub extern "C" fn __powisf2(a: f32, b: i32) -> f32 {
+        pow!(a, b)
+    }
+
+    pub extern "C" fn __powidf2(a: f64, b: i32) -> f64 {
+        pow!(a, b)
+    }
+}

+ 9 - 16
src/float/sub.rs

@@ -1,20 +1,13 @@
 use float::Float;
 
-macro_rules! sub {
-    ($(#[$attr:meta])*
-     | $intrinsic:ident: $ty:ty) => {
-        /// Returns `a - b`
-        $(#[$attr])*
-        pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $ty {
-            a + <$ty>::from_repr(b.repr() ^ <$ty>::sign_mask())
-        }
+intrinsics! {
+    #[arm_aeabi_alias = __aeabi_fsub]
+    pub extern "C" fn __subsf3(a: f32, b: f32) -> f32 {
+        a + f32::from_repr(b.repr() ^ f32::sign_mask())
     }
-}
-
-sub!(#[cfg_attr(all(not(test), not(target_arch = "arm")), no_mangle)]
-     #[cfg_attr(all(not(test), target_arch = "arm"), inline(always))]
-     | __subsf3: f32);
 
-sub!(#[cfg_attr(all(not(test), not(target_arch = "arm")), no_mangle)]
-     #[cfg_attr(all(not(test), target_arch = "arm"), inline(always))]
-     | __subdf3: f64);
+    #[arm_aeabi_alias = __aeabi_dsub]
+    pub extern "C" fn __subdf3(a: f64, b: f64) -> f64 {
+        a + f64::from_repr(b.repr() ^ f64::sign_mask())
+    }
+}

+ 141 - 6
src/int/mod.rs

@@ -1,3 +1,5 @@
+use core::ops;
+
 macro_rules! hty {
     ($ty:ty) => {
         <$ty as LargeInt>::HighHalf
@@ -16,15 +18,33 @@ pub mod shift;
 pub mod udiv;
 
 /// Trait for some basic operations on integers
-pub trait Int {
+pub trait Int:
+    Copy +
+    PartialEq +
+    PartialOrd +
+    ops::AddAssign +
+    ops::Add<Output = Self> +
+    ops::Sub<Output = Self> +
+    ops::Div<Output = Self> +
+    ops::Shl<u32, Output = Self> +
+    ops::Shr<u32, Output = Self> +
+    ops::BitOr<Output = Self> +
+    ops::BitXor<Output = Self> +
+    ops::BitAnd<Output = Self> +
+    ops::BitAndAssign +
+    ops::Not<Output = Self> +
+{
     /// Type with the same width but other signedness
-    type OtherSign;
+    type OtherSign: Int;
     /// Unsigned version of Self
-    type UnsignedInt;
+    type UnsignedInt: Int;
 
     /// Returns the bitwidth of the int type
     fn bits() -> u32;
 
+    fn zero() -> Self;
+    fn one() -> Self;
+
     /// Extracts the sign from self and returns a tuple.
     ///
     /// # Examples
@@ -36,6 +56,25 @@ pub trait Int {
     /// assert_eq!(u, 25_u32);
     /// ```
     fn extract_sign(self) -> (bool, Self::UnsignedInt);
+
+    fn unsigned(self) -> Self::UnsignedInt;
+    fn from_unsigned(unsigned: Self::UnsignedInt) -> Self;
+
+    // copied from primitive integers, but put in a trait
+    fn max_value() -> Self;
+    fn min_value() -> Self;
+    fn wrapping_add(self, other: Self) -> Self;
+    fn wrapping_mul(self, other: Self) -> Self;
+    fn wrapping_sub(self, other: Self) -> Self;
+    fn aborting_div(self, other: Self) -> Self;
+    fn aborting_rem(self, other: Self) -> Self;
+}
+
+fn unwrap<T>(t: Option<T>) -> T {
+    match t {
+        Some(t) => t,
+        None => ::abort(),
+    }
 }
 
 macro_rules! int_impl {
@@ -44,6 +83,14 @@ macro_rules! int_impl {
             type OtherSign = $ity;
             type UnsignedInt = $uty;
 
+            fn zero() -> Self {
+                0
+            }
+
+            fn one() -> Self {
+                1
+            }
+
             fn bits() -> u32 {
                 $bits
             }
@@ -51,6 +98,42 @@ macro_rules! int_impl {
             fn extract_sign(self) -> (bool, $uty) {
                 (false, self)
             }
+
+            fn unsigned(self) -> $uty {
+                self
+            }
+
+            fn from_unsigned(me: $uty) -> Self {
+                me
+            }
+
+            fn max_value() -> Self {
+                <Self>::max_value()
+            }
+
+            fn min_value() -> Self {
+                <Self>::min_value()
+            }
+
+            fn wrapping_add(self, other: Self) -> Self {
+                <Self>::wrapping_add(self, other)
+            }
+
+            fn wrapping_mul(self, other: Self) -> Self {
+                <Self>::wrapping_mul(self, other)
+            }
+
+            fn wrapping_sub(self, other: Self) -> Self {
+                <Self>::wrapping_sub(self, other)
+            }
+
+            fn aborting_div(self, other: Self) -> Self {
+                unwrap(<Self>::checked_div(self, other))
+            }
+
+            fn aborting_rem(self, other: Self) -> Self {
+                unwrap(<Self>::checked_rem(self, other))
+            }
         }
 
         impl Int for $ity {
@@ -61,6 +144,14 @@ macro_rules! int_impl {
                 $bits
             }
 
+            fn zero() -> Self {
+                0
+            }
+
+            fn one() -> Self {
+                1
+            }
+
             fn extract_sign(self) -> (bool, $uty) {
                 if self < 0 {
                     (true, (!(self as $uty)).wrapping_add(1))
@@ -68,6 +159,42 @@ macro_rules! int_impl {
                     (false, self as $uty)
                 }
             }
+
+            fn unsigned(self) -> $uty {
+                self as $uty
+            }
+
+            fn from_unsigned(me: $uty) -> Self {
+                me as $ity
+            }
+
+            fn max_value() -> Self {
+                <Self>::max_value()
+            }
+
+            fn min_value() -> Self {
+                <Self>::min_value()
+            }
+
+            fn wrapping_add(self, other: Self) -> Self {
+                <Self>::wrapping_add(self, other)
+            }
+
+            fn wrapping_mul(self, other: Self) -> Self {
+                <Self>::wrapping_mul(self, other)
+            }
+
+            fn wrapping_sub(self, other: Self) -> Self {
+                <Self>::wrapping_sub(self, other)
+            }
+
+            fn aborting_div(self, other: Self) -> Self {
+                unwrap(<Self>::checked_div(self, other))
+            }
+
+            fn aborting_rem(self, other: Self) -> Self {
+                unwrap(<Self>::checked_rem(self, other))
+            }
         }
     }
 }
@@ -77,12 +204,14 @@ int_impl!(i64, u64, 64);
 int_impl!(i128, u128, 128);
 
 /// Trait to convert an integer to/from smaller parts
-pub trait LargeInt {
-    type LowHalf;
-    type HighHalf;
+pub trait LargeInt: Int {
+    type LowHalf: Int;
+    type HighHalf: Int;
 
     fn low(self) -> Self::LowHalf;
+    fn low_as_high(low: Self::LowHalf) -> Self::HighHalf;
     fn high(self) -> Self::HighHalf;
+    fn high_as_low(low: Self::HighHalf) -> Self::LowHalf;
     fn from_parts(low: Self::LowHalf, high: Self::HighHalf) -> Self;
 }
 
@@ -95,9 +224,15 @@ macro_rules! large_int {
             fn low(self) -> $tylow {
                 self as $tylow
             }
+            fn low_as_high(low: $tylow) -> $tyhigh {
+                low as $tyhigh
+            }
             fn high(self) -> $tyhigh {
                 (self >> $halfbits) as $tyhigh
             }
+            fn high_as_low(high: $tyhigh) -> $tylow {
+                high as $tylow
+            }
             fn from_parts(low: $tylow, high: $tyhigh) -> $ty {
                 low as $ty | ((high as $ty) << $halfbits)
             }

+ 78 - 76
src/int/mul.rs

@@ -1,95 +1,97 @@
+use core::ops;
+
 use int::LargeInt;
 use int::Int;
 
-macro_rules! mul {
-    ($(#[$attr:meta])+ |
-     $abi:tt, $intrinsic:ident: $ty:ty) => {
-        /// Returns `a * b`
-        $(#[$attr])+
-        pub extern $abi fn $intrinsic(a: $ty, b: $ty) -> $ty {
-            let half_bits = <$ty>::bits() / 4;
-            let lower_mask = !0 >> half_bits;
-            let mut low = (a.low() & lower_mask).wrapping_mul(b.low() & lower_mask);
-            let mut t = low >> half_bits;
-            low &= lower_mask;
-            t += (a.low() >> half_bits).wrapping_mul(b.low() & lower_mask);
-            low += (t & lower_mask) << half_bits;
-            let mut high = (t >> half_bits) as hty!($ty);
-            t = low >> half_bits;
-            low &= lower_mask;
-            t += (b.low() >> half_bits).wrapping_mul(a.low() & lower_mask);
-            low += (t & lower_mask) << half_bits;
-            high += (t >> half_bits) as hty!($ty);
-            high += (a.low() >> half_bits).wrapping_mul(b.low() >> half_bits) as hty!($ty);
-            high = high.wrapping_add(a.high().wrapping_mul(b.low() as hty!($ty)))
-                       .wrapping_add((a.low() as hty!($ty)).wrapping_mul(b.high()));
-            <$ty>::from_parts(low, high)
-        }
+trait Mul: LargeInt {
+    fn mul(self, other: Self) -> Self {
+        let half_bits = Self::bits() / 4;
+        let lower_mask = !<<Self as LargeInt>::LowHalf>::zero() >> half_bits;
+        let mut low = (self.low() & lower_mask).wrapping_mul(other.low() & lower_mask);
+        let mut t = low >> half_bits;
+        low &= lower_mask;
+        t += (self.low() >> half_bits).wrapping_mul(other.low() & lower_mask);
+        low += (t & lower_mask) << half_bits;
+        let mut high = Self::low_as_high(t >> half_bits);
+        t = low >> half_bits;
+        low &= lower_mask;
+        t += (other.low() >> half_bits).wrapping_mul(self.low() & lower_mask);
+        low += (t & lower_mask) << half_bits;
+        high += Self::low_as_high(t >> half_bits);
+        high += Self::low_as_high((self.low() >> half_bits).wrapping_mul(other.low() >> half_bits));
+        high = high.wrapping_add(self.high().wrapping_mul(Self::low_as_high(other.low())))
+                   .wrapping_add(Self::low_as_high(self.low()).wrapping_mul(other.high()));
+        Self::from_parts(low, high)
     }
 }
 
-macro_rules! mulo {
-    ($intrinsic:ident: $ty:ty) => {
-        // Default is "C" ABI
-        mulo!($intrinsic: $ty, "C");
-    };
-    ($intrinsic:ident: $ty:ty, $abi:tt) => {
-        /// Returns `a * b` and sets `*overflow = 1` if `a * b` overflows
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern $abi fn $intrinsic(a: $ty, b: $ty, overflow: &mut i32) -> $ty {
-            *overflow = 0;
-            let result = a.wrapping_mul(b);
-            if a == <$ty>::min_value() {
-                if b != 0 && b != 1 {
-                    *overflow = 1;
-                }
-                return result;
+impl Mul for u64 {}
+impl Mul for i128 {}
+
+trait Mulo: Int + ops::Neg<Output = Self> {
+    fn mulo(self, other: Self, overflow: &mut i32) -> Self {
+        *overflow = 0;
+        let result = self.wrapping_mul(other);
+        if self == Self::min_value() {
+            if other != Self::zero() && other != Self::one() {
+                *overflow = 1;
             }
-            if b == <$ty>::min_value() {
-                if a != 0 && a != 1 {
-                    *overflow = 1;
-                }
-                return result;
+            return result;
+        }
+        if other == Self::min_value() {
+            if self != Self::zero() && self != Self::one() {
+                *overflow = 1;
             }
+            return result;
+        }
 
-            let sa = a >> (<$ty>::bits() - 1);
-            let abs_a = (a ^ sa) - sa;
-            let sb = b >> (<$ty>::bits() - 1);
-            let abs_b = (b ^ sb) - sb;
-            if abs_a < 2 || abs_b < 2 {
-                return result;
+        let sa = self >> (Self::bits() - 1);
+        let abs_a = (self ^ sa) - sa;
+        let sb = other >> (Self::bits() - 1);
+        let abs_b = (other ^ sb) - sb;
+        let two = Self::one() + Self::one();
+        if abs_a < two || abs_b < two {
+            return result;
+        }
+        if sa == sb {
+            if abs_a > Self::max_value().aborting_div(abs_b) {
+                *overflow = 1;
             }
-            if sa == sb {
-                if abs_a > <$ty>::max_value() / abs_b {
-                    *overflow = 1;
-                }
-            } else {
-                if abs_a > <$ty>::min_value() / -abs_b {
-                    *overflow = 1;
-                }
+        } else {
+            if abs_a > Self::min_value().aborting_div(-abs_b) {
+                *overflow = 1;
             }
-            result
         }
+        result
     }
 }
 
-#[cfg(not(all(feature = "c", target_arch = "x86")))]
-mul!(#[cfg_attr(all(not(test), not(target_arch = "arm")), no_mangle)]
-     #[cfg_attr(all(not(test), target_arch = "arm"), inline(always))]
-     | "C", __muldi3: u64);
+impl Mulo for i32 {}
+impl Mulo for i64 {}
+impl Mulo for i128 {}
 
-#[cfg(not(target_arch = "arm"))]
-mul!(#[cfg_attr(not(test), no_mangle)]
-     | "C", __multi3: i128);
+intrinsics! {
+    #[use_c_shim_if(all(target_arch = "x86", not(target_env = "msvc")))]
+    #[arm_aeabi_alias = __aeabi_lmul]
+    pub extern "C" fn __muldi3(a: u64, b: u64) -> u64 {
+        a.mul(b)
+    }
 
-#[cfg(target_arch = "arm")]
-mul!(#[cfg_attr(not(test), no_mangle)]
-     | "aapcs", __multi3: i128);
+    #[aapcs_on_arm]
+    pub extern "C" fn __multi3(a: i128, b: i128) -> i128 {
+        a.mul(b)
+    }
+
+    pub extern "C" fn __mulosi4(a: i32, b: i32, oflow: &mut i32) -> i32 {
+        a.mulo(b, oflow)
+    }
 
-mulo!(__mulosi4: i32);
-mulo!(__mulodi4: i64);
+    pub extern "C" fn __mulodi4(a: i64, b: i64, oflow: &mut i32) -> i64 {
+        a.mulo(b, oflow)
+    }
 
-#[cfg(all(windows, target_pointer_width="64"))]
-mulo!(__muloti4: i128, "unadjusted");
-#[cfg(not(all(windows, target_pointer_width="64")))]
-mulo!(__muloti4: i128);
+    #[unadjusted_on_win64]
+    pub extern "C" fn __muloti4(a: i128, b: i128, oflow: &mut i32) -> i128 {
+        a.mulo(b, oflow)
+    }
+}

+ 81 - 83
src/int/sdiv.rs

@@ -1,102 +1,100 @@
 use int::Int;
 
-macro_rules! div {
-    ($intrinsic:ident: $ty:ty, $uty:ty) => {
-        div!($intrinsic: $ty, $uty, $ty, |i| {i});
-    };
-    ($intrinsic:ident: $ty:ty, $uty:ty, $tyret:ty, $conv:expr) => {
-        /// Returns `a / b`
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $tyret {
-            let s_a = a >> (<$ty>::bits() - 1);
-            let s_b = b >> (<$ty>::bits() - 1);
-            // NOTE it's OK to overflow here because of the `as $uty` cast below
-            // This whole operation is computing the absolute value of the inputs
-            // So some overflow will happen when dealing with e.g. `i64::MIN`
-            // where the absolute value is `(-i64::MIN) as u64`
-            let a = (a ^ s_a).wrapping_sub(s_a);
-            let b = (b ^ s_b).wrapping_sub(s_b);
-            let s = s_a ^ s_b;
-
-            let r = udiv!(a as $uty, b as $uty);
-            ($conv)((r as $ty ^ s) - s)
-        }
+trait Div: Int {
+    /// Returns `a / b`
+    fn div(self, other: Self) -> Self {
+        let s_a = self >> (Self::bits() - 1);
+        let s_b = other >> (Self::bits() - 1);
+        // NOTE it's OK to overflow here because of the `as $uty` cast below
+        // This whole operation is computing the absolute value of the inputs
+        // So some overflow will happen when dealing with e.g. `i64::MIN`
+        // where the absolute value is `(-i64::MIN) as u64`
+        let a = (self ^ s_a).wrapping_sub(s_a);
+        let b = (other ^ s_b).wrapping_sub(s_b);
+        let s = s_a ^ s_b;
+
+        let r = a.unsigned().aborting_div(b.unsigned());
+        (Self::from_unsigned(r) ^ s) - s
     }
 }
 
-macro_rules! mod_ {
-    ($intrinsic:ident: $ty:ty, $uty:ty) => {
-        mod_!($intrinsic: $ty, $uty, $ty, |i| {i});
-    };
-    ($intrinsic:ident: $ty:ty, $uty:ty, $tyret:ty, $conv:expr) => {
-        /// Returns `a % b`
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $tyret {
-            let s = b >> (<$ty>::bits() - 1);
-            // NOTE(wrapping_sub) see comment in the `div` macro
-            let b = (b ^ s).wrapping_sub(s);
-            let s = a >> (<$ty>::bits() - 1);
-            let a = (a ^ s).wrapping_sub(s);
-
-            let r = urem!(a as $uty, b as $uty);
-            ($conv)((r as $ty ^ s) - s)
-        }
+impl Div for i32 {}
+impl Div for i64 {}
+impl Div for i128 {}
+
+trait Mod: Int {
+    /// Returns `a % b`
+    fn mod_(self, other: Self) -> Self {
+        let s = other >> (Self::bits() - 1);
+        // NOTE(wrapping_sub) see comment in the `div`
+        let b = (other ^ s).wrapping_sub(s);
+        let s = self >> (Self::bits() - 1);
+        let a = (self ^ s).wrapping_sub(s);
+
+        let r = a.unsigned().aborting_rem(b.unsigned());
+        (Self::from_unsigned(r) ^ s) - s
     }
 }
 
-macro_rules! divmod {
-    ($abi:tt, $intrinsic:ident, $div:ident: $ty:ty) => {
-        /// Returns `a / b` and sets `*rem = n % d`
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern $abi fn $intrinsic(a: $ty, b: $ty, rem: &mut $ty) -> $ty {
-            #[cfg(all(feature = "c", any(target_arch = "x86")))]
-            extern {
-                fn $div(a: $ty, b: $ty) -> $ty;
-            }
-
-            let r = match () {
-                #[cfg(not(all(feature = "c", any(target_arch = "x86"))))]
-                () => $div(a, b),
-                #[cfg(all(feature = "c", any(target_arch = "x86")))]
-                () => unsafe { $div(a, b) },
-            };
-            // NOTE won't overflow because it's using the result from the
-            // previous division
-            *rem = a - r.wrapping_mul(b);
-            r
-        }
+impl Mod for i32 {}
+impl Mod for i64 {}
+impl Mod for i128 {}
+
+trait Divmod: Int {
+    /// Returns `a / b` and sets `*rem = n % d`
+    fn divmod<F>(self, other: Self, rem: &mut Self, div: F) -> Self
+        where F: Fn(Self, Self) -> Self,
+    {
+        let r = div(self, other);
+        // NOTE won't overflow because it's using the result from the
+        // previous division
+        *rem = self - r.wrapping_mul(other);
+        r
     }
 }
 
-#[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"), not(thumbv6m))))]
-div!(__divsi3: i32, u32);
+impl Divmod for i32 {}
+impl Divmod for i64 {}
 
-#[cfg(not(all(feature = "c", target_arch = "x86")))]
-div!(__divdi3: i64, u64);
-
-#[cfg(not(all(windows, target_pointer_width="64")))]
-div!(__divti3: i128, u128);
-
-#[cfg(all(windows, target_pointer_width="64"))]
-div!(__divti3: i128, u128, ::U64x2, ::sconv);
+intrinsics! {
+    #[use_c_shim_if(all(target_arch = "arm", not(target_os = "ios"), not(thumbv6m)))]
+    #[arm_aeabi_alias = __aeabi_idiv]
+    pub extern "C" fn __divsi3(a: i32, b: i32) -> i32 {
+        a.div(b)
+    }
 
-#[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"))))]
-mod_!(__modsi3: i32, u32);
+    #[use_c_shim_if(all(target_arch = "x86", not(target_env = "msvc")))]
+    pub extern "C" fn __divdi3(a: i64, b: i64) -> i64 {
+        a.div(b)
+    }
 
-#[cfg(not(all(feature = "c", target_arch = "x86")))]
-mod_!(__moddi3: i64, u64);
+    #[win64_128bit_abi_hack]
+    pub extern "C" fn __divti3(a: i128, b: i128) -> i128 {
+        a.div(b)
+    }
 
-#[cfg(not(all(windows, target_pointer_width="64")))]
-mod_!(__modti3: i128, u128);
+    #[use_c_shim_if(all(target_arch = "arm", not(target_os = "ios")))]
+    pub extern "C" fn __modsi3(a: i32, b: i32) -> i32 {
+        a.mod_(b)
+    }
 
-#[cfg(all(windows, target_pointer_width="64"))]
-mod_!(__modti3: i128, u128, ::U64x2, ::sconv);
+    #[use_c_shim_if(all(target_arch = "x86", not(target_env = "msvc")))]
+    pub extern "C" fn __moddi3(a: i64, b: i64) -> i64 {
+        a.mod_(b)
+    }
 
-#[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"))))]
-divmod!("C", __divmodsi4, __divsi3: i32);
+    #[win64_128bit_abi_hack]
+    pub extern "C" fn __modti3(a: i128, b: i128) -> i128 {
+        a.mod_(b)
+    }
 
-#[cfg(target_arch = "arm")]
-divmod!("aapcs", __divmoddi4, __divdi3: i64);
+    #[use_c_shim_if(all(target_arch = "arm", not(target_os = "ios")))]
+    pub extern "C" fn __divmodsi4(a: i32, b: i32, rem: &mut i32) -> i32 {
+        a.divmod(b, rem, |a, b| __divsi3(a, b))
+    }
 
-#[cfg(not(target_arch = "arm"))]
-divmod!("C", __divmoddi4, __divdi3: i64);
+    #[aapcs_on_arm]
+    pub extern "C" fn __divmoddi4(a: i64, b: i64, rem: &mut i64) -> i64 {
+        a.divmod(b, rem, |a, b| __divdi3(a, b))
+    }
+}

+ 78 - 55
src/int/shift.rs

@@ -1,74 +1,97 @@
 use int::{Int, LargeInt};
 
-macro_rules! ashl {
-    ($intrinsic:ident: $ty:ty) => {
-        /// Returns `a << b`, requires `b < $ty::bits()`
-        #[cfg_attr(not(test), no_mangle)]
-        #[cfg_attr(all(not(test), not(target_arch = "arm")), no_mangle)]
-        #[cfg_attr(all(not(test), target_arch = "arm"), inline(always))]
-        pub extern "C" fn $intrinsic(a: $ty, b: u32) -> $ty {
-            let half_bits = <$ty>::bits() / 2;
-            if b & half_bits != 0 {
-                <$ty>::from_parts(0, a.low() << (b - half_bits))
-            } else if b == 0 {
-                a
-            } else {
-                <$ty>::from_parts(a.low() << b, (a.high() << b) | (a.low() >> (half_bits - b)))
-            }
+trait Ashl: Int + LargeInt {
+    /// Returns `a << b`, requires `b < $ty::bits()`
+    fn ashl(self, offset: u32) -> Self
+        where Self: LargeInt<HighHalf = <Self as LargeInt>::LowHalf>,
+    {
+        let half_bits = Self::bits() / 2;
+        if offset & half_bits != 0 {
+            Self::from_parts(Int::zero(), self.low() << (offset - half_bits))
+        } else if offset == 0 {
+            self
+        } else {
+            Self::from_parts(self.low() << offset,
+                             (self.high() << offset) |
+                                (self.low() >> (half_bits - offset)))
         }
     }
 }
 
-macro_rules! ashr {
-    ($intrinsic:ident: $ty:ty) => {
-        /// Returns arithmetic `a >> b`, requires `b < $ty::bits()`
-        #[cfg_attr(not(test), no_mangle)]
-        #[cfg_attr(all(not(test), not(target_arch = "arm")), no_mangle)]
-        #[cfg_attr(all(not(test), target_arch = "arm"), inline(always))]
-        pub extern "C" fn $intrinsic(a: $ty, b: u32) -> $ty {
-            let half_bits = <$ty>::bits() / 2;
-            if b & half_bits != 0 {
-                <$ty>::from_parts((a.high() >> (b - half_bits)) as <$ty as LargeInt>::LowHalf,
-                                  a.high() >> (half_bits - 1))
-            } else if b == 0 {
-                a
-            } else {
-                let high_unsigned = a.high() as <$ty as LargeInt>::LowHalf;
-                <$ty>::from_parts((high_unsigned << (half_bits - b)) | (a.low() >> b),
-                                  a.high() >> b)
-            }
+impl Ashl for u64 {}
+impl Ashl for u128 {}
+
+trait Ashr: Int + LargeInt {
+    /// Returns arithmetic `a >> b`, requires `b < $ty::bits()`
+    fn ashr(self, offset: u32) -> Self
+        where Self: LargeInt<LowHalf = <<Self as LargeInt>::HighHalf as Int>::UnsignedInt>,
+    {
+        let half_bits = Self::bits() / 2;
+        if offset & half_bits != 0 {
+            Self::from_parts((self.high() >> (offset - half_bits)).unsigned(),
+                              self.high() >> (half_bits - 1))
+        } else if offset == 0 {
+            self
+        } else {
+            let high_unsigned = self.high().unsigned();
+            Self::from_parts((high_unsigned << (half_bits - offset)) | (self.low() >> offset),
+                              self.high() >> offset)
         }
     }
 }
 
-macro_rules! lshr {
-    ($intrinsic:ident: $ty:ty) => {
-        /// Returns logical `a >> b`, requires `b < $ty::bits()`
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern "C" fn $intrinsic(a: $ty, b: u32) -> $ty {
-            let half_bits = <$ty>::bits() / 2;
-            if b & half_bits != 0 {
-                <$ty>::from_parts(a.high() >> (b - half_bits), 0)
-            } else if b == 0 {
-                a
-            } else {
-                <$ty>::from_parts((a.high() << (half_bits - b)) | (a.low() >> b), a.high() >> b)
-            }
+impl Ashr for i64 {}
+impl Ashr for i128 {}
+
+trait Lshr: Int + LargeInt {
+    /// Returns logical `a >> b`, requires `b < $ty::bits()`
+    fn lshr(self, offset: u32) -> Self
+        where Self: LargeInt<HighHalf = <Self as LargeInt>::LowHalf>,
+    {
+        let half_bits = Self::bits() / 2;
+        if offset & half_bits != 0 {
+            Self::from_parts(self.high() >> (offset - half_bits), Int::zero())
+        } else if offset == 0 {
+            self
+        } else {
+            Self::from_parts((self.high() << (half_bits - offset)) |
+                                (self.low() >> offset),
+                             self.high() >> offset)
         }
     }
 }
 
-#[cfg(not(all(feature = "c", target_arch = "x86")))]
-ashl!(__ashldi3: u64);
+impl Lshr for u64 {}
+impl Lshr for u128 {}
 
-ashl!(__ashlti3: u128);
+intrinsics! {
+    #[use_c_shim_if(all(target_arch = "x86", not(target_env = "msvc")))]
+    #[arm_aeabi_alias = __aeabi_llsl]
+    pub extern "C" fn __ashldi3(a: u64, b: u32) -> u64 {
+        a.ashl(b)
+    }
+
+    pub extern "C" fn __ashlti3(a: u128, b: u32) -> u128 {
+        a.ashl(b)
+    }
 
-#[cfg(not(all(feature = "c", target_arch = "x86")))]
-ashr!(__ashrdi3: i64);
+    #[use_c_shim_if(all(target_arch = "x86", not(target_env = "msvc")))]
+    #[arm_aeabi_alias = __aeabi_lasr]
+    pub extern "C" fn __ashrdi3(a: i64, b: u32) -> i64 {
+        a.ashr(b)
+    }
 
-ashr!(__ashrti3: i128);
+    pub extern "C" fn __ashrti3(a: i128, b: u32) -> i128 {
+        a.ashr(b)
+    }
 
-#[cfg(not(all(feature = "c", target_arch = "x86")))]
-lshr!(__lshrdi3: u64);
+    #[use_c_shim_if(all(target_arch = "x86", not(target_env = "msvc")))]
+    #[arm_aeabi_alias = __aeabi_llsr]
+    pub extern "C" fn __lshrdi3(a: u64, b: u32) -> u64 {
+        a.lshr(b)
+    }
 
-lshr!(__lshrti3: u128);
+    pub extern "C" fn __lshrti3(a: u128, b: u32) -> u128 {
+        a.lshr(b)
+    }
+}

+ 115 - 164
src/int/udiv.rs

@@ -1,141 +1,5 @@
-use core::intrinsics;
 use int::{Int, LargeInt};
 
-/// Returns `n / d`
-#[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"), not(thumbv6m))))]
-#[cfg_attr(all(not(test), not(target_arch = "arm")), no_mangle)]
-#[cfg_attr(all(not(test), target_arch = "arm"), inline(always))]
-pub extern "C" fn __udivsi3(n: u32, d: u32) -> u32 {
-    // Special cases
-    if d == 0 {
-        // NOTE This should be unreachable in safe Rust because the program will panic before
-        // this intrinsic is called
-        unsafe {
-            intrinsics::abort()
-        }
-    }
-
-    if n == 0 {
-        return 0;
-    }
-
-    let mut sr = d.leading_zeros().wrapping_sub(n.leading_zeros());
-
-    // d > n
-    if sr > u32::bits() - 1 {
-        return 0;
-    }
-
-    // d == 1
-    if sr == u32::bits() - 1 {
-        return n;
-    }
-
-    sr += 1;
-
-    // 1 <= sr <= u32::bits() - 1
-    let mut q = n << (u32::bits() - sr);
-    let mut r = n >> sr;
-
-    let mut carry = 0;
-    for _ in 0..sr {
-        // r:q = ((r:q) << 1) | carry
-        r = (r << 1) | (q >> (u32::bits() - 1));
-        q = (q << 1) | carry;
-
-        // carry = 0;
-        // if r > d {
-        //     r -= d;
-        //     carry = 1;
-        // }
-
-        let s = (d.wrapping_sub(r).wrapping_sub(1)) as i32 >> (u32::bits() - 1);
-        carry = (s & 1) as u32;
-        r -= d & s as u32;
-    }
-
-    (q << 1) | carry
-}
-
-/// Returns `n % d`
-#[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"))))]
-#[cfg_attr(not(test), no_mangle)]
-pub extern "C" fn __umodsi3(n: u32, d: u32) -> u32 {
-    #[cfg(all(feature = "c", target_arch = "arm", not(target_os = "ios")))]
-    extern "C" {
-        fn __udivsi3(n: u32, d: u32) -> u32;
-    }
-
-    let q = match () {
-        #[cfg(all(feature = "c", target_arch = "arm", not(target_os = "ios")))]
-        () => unsafe { __udivsi3(n, d) },
-        #[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"))))]
-        () => __udivsi3(n, d),
-    };
-
-    n - q * d
-}
-
-/// Returns `n / d` and sets `*rem = n % d`
-#[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"), not(thumbv6m))))]
-#[cfg_attr(not(test), no_mangle)]
-pub extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 {
-    #[cfg(all(feature = "c", target_arch = "arm", not(target_os = "ios"), not(thumbv6m)))]
-    extern "C" {
-        fn __udivsi3(n: u32, d: u32) -> u32;
-    }
-
-    let q = match () {
-        #[cfg(all(feature = "c", target_arch = "arm", not(target_os = "ios"), not(thumbv6m)))]
-        () => unsafe { __udivsi3(n, d) },
-        #[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"), not(thumbv6m))))]
-        () => __udivsi3(n, d),
-    };
-    if let Some(rem) = rem {
-        *rem = n - (q * d);
-    }
-    q
-}
-
-macro_rules! div_mod_intrinsics {
-    ($udiv_intr:ident, $umod_intr:ident : $ty:ty) => {
-        div_mod_intrinsics!($udiv_intr, $umod_intr : $ty,
-                            __udivmoddi4);
-    };
-    ($udiv_intr:ident, $umod_intr:ident : $ty:ty, $divmod_intr:expr) => {
-        div_mod_intrinsics!($udiv_intr, $umod_intr : $ty,
-                            $divmod_intr, $ty, |i|{ i });
-    };
-    ($udiv_intr:ident, $umod_intr:ident : $ty:ty, $divmod_intr:expr,
-     $tyret:ty, $conv:expr) => {
-        /// Returns `n / d`
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern "C" fn $udiv_intr(n: $ty, d: $ty) -> $tyret {
-            let r = $divmod_intr(n, d, None);
-            ($conv)(r)
-        }
-
-        /// Returns `n % d`
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern "C" fn $umod_intr(a: $ty, b: $ty) -> $tyret {
-            use core::mem;
-
-            let mut rem = unsafe { mem::uninitialized() };
-            $divmod_intr(a, b, Some(&mut rem));
-            ($conv)(rem)
-        }
-    }
-}
-
-#[cfg(not(all(feature = "c", target_arch = "x86")))]
-div_mod_intrinsics!(__udivdi3, __umoddi3: u64);
-
-#[cfg(not(all(windows, target_pointer_width="64")))]
-div_mod_intrinsics!(__udivti3, __umodti3: u128, u128_div_mod);
-
-#[cfg(all(windows, target_pointer_width="64"))]
-div_mod_intrinsics!(__udivti3, __umodti3: u128, u128_div_mod, ::U64x2, ::conv);
-
 macro_rules! udivmod_inner {
     ($n:expr, $d:expr, $rem:expr, $ty:ty) => {{
         let (n, d, rem) = ($n, $d, $rem);
@@ -147,9 +11,9 @@ macro_rules! udivmod_inner {
                 // 0 X
 
                 if let Some(rem) = rem {
-                    *rem = <$ty>::from(urem!(n.low(), d.low()));
+                    *rem = <$ty>::from(n.low().aborting_rem(d.low()));
                 }
-                return <$ty>::from(udiv!(n.low(), d.low()));
+                return <$ty>::from(n.low().aborting_div(d.low()))
             } else {
                 // 0 X
                 // ---
@@ -172,9 +36,7 @@ macro_rules! udivmod_inner {
                 // 0 0
                 // NOTE This should be unreachable in safe Rust because the program will panic before
                 // this intrinsic is called
-                unsafe {
-                    intrinsics::abort()
-                }
+                ::abort();
             }
 
             if n.low() == 0 {
@@ -182,9 +44,9 @@ macro_rules! udivmod_inner {
                 // ---
                 // K 0
                 if let Some(rem) = rem {
-                    *rem = <$ty>::from_parts(0, urem!(n.high(), d.high()));
+                    *rem = <$ty>::from_parts(0, n.high().aborting_rem(d.high()));
                 }
-                return <$ty>::from(udiv!(n.high(), d.high()));
+                return <$ty>::from(n.high().aborting_div(d.high()))
             }
 
             // K K
@@ -285,30 +147,119 @@ macro_rules! udivmod_inner {
     }}
 }
 
-/// Returns `n / d` and sets `*rem = n % d`
-#[cfg_attr(not(test), no_mangle)]
-pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
-    udivmod_inner!(n, d, rem, u64)
-}
+intrinsics! {
+    #[use_c_shim_if(all(target_arch = "arm",
+                        not(target_os = "ios"),
+                        not(thumbv6m)))]
+    #[arm_aeabi_alias = __aeabi_uidiv]
+    /// Returns `n / d`
+    pub extern "C" fn __udivsi3(n: u32, d: u32) -> u32 {
+        // Special cases
+        if d == 0 {
+            // NOTE This should be unreachable in safe Rust because the program will panic before
+            // this intrinsic is called
+            ::abort();
+        }
+
+        if n == 0 {
+            return 0;
+        }
+
+        let mut sr = d.leading_zeros().wrapping_sub(n.leading_zeros());
+
+        // d > n
+        if sr > u32::bits() - 1 {
+            return 0;
+        }
+
+        // d == 1
+        if sr == u32::bits() - 1 {
+            return n;
+        }
+
+        sr += 1;
+
+        // 1 <= sr <= u32::bits() - 1
+        let mut q = n << (u32::bits() - sr);
+        let mut r = n >> sr;
+
+        let mut carry = 0;
+        for _ in 0..sr {
+            // r:q = ((r:q) << 1) | carry
+            r = (r << 1) | (q >> (u32::bits() - 1));
+            q = (q << 1) | carry;
+
+            // carry = 0;
+            // if r > d {
+            //     r -= d;
+            //     carry = 1;
+            // }
 
-macro_rules! udivmodti4 {
-    ($tyret:ty, $conv:expr) => {
-        /// Returns `n / d` and sets `*rem = n % d`
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern "C" fn __udivmodti4(n: u128, d: u128, rem: Option<&mut u128>) -> $tyret {
-            let r = u128_div_mod(n, d, rem);
-            ($conv)(r)
+            let s = (d.wrapping_sub(r).wrapping_sub(1)) as i32 >> (u32::bits() - 1);
+            carry = (s & 1) as u32;
+            r -= d & s as u32;
         }
+
+        (q << 1) | carry
     }
-}
 
-/// Returns `n / d` and sets `*rem = n % d`
-fn u128_div_mod(n: u128, d: u128, rem: Option<&mut u128>) -> u128 {
-    udivmod_inner!(n, d, rem, u128)
-}
+    #[use_c_shim_if(all(target_arch = "arm", not(target_os = "ios")))]
+    /// Returns `n % d`
+    pub extern "C" fn __umodsi3(n: u32, d: u32) -> u32 {
+        let q = __udivsi3(n, d);
+        n - q * d
+    }
+
+    #[use_c_shim_if(all(target_arch = "arm",
+                        not(target_os = "ios"),
+                        not(thumbv6m)))]
+    /// Returns `n / d` and sets `*rem = n % d`
+    pub extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 {
+        let q = __udivsi3(n, d);
+        if let Some(rem) = rem {
+            *rem = n - (q * d);
+        }
+        q
+    }
 
-#[cfg(all(windows, target_pointer_width="64"))]
-udivmodti4!(::U64x2, ::conv);
+    #[use_c_shim_if(all(target_arch = "x86", not(target_env = "msvc")))]
+    /// Returns `n / d`
+    pub extern "C" fn __udivdi3(n: u64, d: u64) -> u64 {
+        __udivmoddi4(n, d, None)
+    }
 
-#[cfg(not(all(windows, target_pointer_width="64")))]
-udivmodti4!(u128, |i|{ i });
+    #[use_c_shim_if(all(target_arch = "x86", not(target_env = "msvc")))]
+    /// Returns `n % d`
+    pub extern "C" fn __umoddi3(n: u64, d: u64) -> u64 {
+        let mut rem = 0;
+        __udivmoddi4(n, d, Some(&mut rem));
+        rem
+    }
+
+    #[win64_128bit_abi_hack]
+    /// Returns `n / d`
+    pub extern "C" fn __udivti3(n: u128, d: u128) -> u128 {
+        __udivmodti4(n, d, None)
+    }
+
+    #[win64_128bit_abi_hack]
+    /// Returns `n % d`
+    pub extern "C" fn __umodti3(n: u128, d: u128) -> u128 {
+        let mut rem = 0;
+        __udivmodti4(n, d, Some(&mut rem));
+        rem
+    }
+
+    /// Returns `n / d` and sets `*rem = n % d`
+    pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
+        udivmod_inner!(n, d, rem, u64)
+    }
+
+    #[win64_128bit_abi_hack]
+    /// Returns `n / d` and sets `*rem = n % d`
+    pub extern "C" fn __udivmodti4(n: u128,
+                                   d: u128,
+                                   rem: Option<&mut u128>) -> u128 {
+        udivmod_inner!(n, d, rem, u128)
+    }
+}

+ 6 - 63
src/lib.rs

@@ -32,72 +32,15 @@
 // that follow "x86 naming convention" (e.g. addsf3). Those aeabi intrinsics must adhere to the
 // AAPCS calling convention (`extern "aapcs"`) because that's how LLVM will call them.
 
-// TODO(rust-lang/rust#37029) use e.g. checked_div(_).unwrap_or_else(|| abort())
-macro_rules! udiv {
-    ($a:expr, $b:expr) => {
-        unsafe {
-            let a = $a;
-            let b = $b;
-
-            if b == 0 {
-                ::core::intrinsics::abort()
-            } else {
-                ::core::intrinsics::unchecked_div(a, b)
-            }
-        }
-    }
-}
-
-macro_rules! sdiv {
-    ($sty:ident, $a:expr, $b:expr) => {
-        unsafe {
-            let a = $a;
-            let b = $b;
-
-            if b == 0 || (b == -1 && a == $sty::min_value()) {
-                ::core::intrinsics::abort()
-            } else {
-                ::core::intrinsics::unchecked_div(a, b)
-            }
-        }
-    }
-}
-
-macro_rules! urem {
-    ($a:expr, $b:expr) => {
-        unsafe {
-            let a = $a;
-            let b = $b;
-
-            if b == 0 {
-                ::core::intrinsics::abort()
-            } else {
-                ::core::intrinsics::unchecked_rem(a, b)
-            }
-        }
-    }
-}
-
-// Hack for LLVM expectations for ABI on windows
-#[cfg(all(windows, target_pointer_width="64"))]
-#[repr(simd)]
-pub struct U64x2(u64, u64);
-
-#[cfg(all(windows, target_pointer_width="64"))]
-fn conv(i: u128) -> U64x2 {
-    use int::LargeInt;
-    U64x2(i.low(), i.high())
-}
+#[cfg(test)]
+extern crate core;
 
-#[cfg(all(windows, target_pointer_width="64"))]
-fn sconv(i: i128) -> U64x2 {
-    use int::LargeInt;
-    let j = i as u128;
-    U64x2(j.low(), j.high())
+fn abort() -> ! {
+    unsafe { core::intrinsics::abort() }
 }
 
-#[cfg(test)]
-extern crate core;
+#[macro_use]
+mod macros;
 
 pub mod int;
 pub mod float;

+ 282 - 0
src/macros.rs

@@ -0,0 +1,282 @@
+//! Macros shared throughout the compiler-builtins implementation
+
+/// The "main macro" used for defining intrinsics.
+///
+/// The compiler-builtins library is super platform-specific with tons of crazy
+/// little tweaks for various platforms. As a result it *could* involve a lot of
+/// #[cfg] and macro soup, but the intention is that this macro alleviates a lot
+/// of that complexity. Ideally this macro has all the weird ABI things
+/// platforms need and elsewhere in this library it just looks like normal Rust
+/// code.
+///
+/// This macro is structured to be invoked with a bunch of functions that looks
+/// like:
+///
+///     intrinsics! {
+///         pub extern "C" fn foo(a: i32) -> u32 {
+///             // ...
+///         }
+///
+///         #[nonstandard_attribute]
+///         pub extern "C" fn bar(a: i32) -> u32 {
+///             // ...
+///         }
+///     }
+///
+/// Each function is defined in a manner that looks like a normal Rust function.
+/// The macro then accepts a few nonstandard attributes that can decorate
+/// various functions. Each of the attributes is documented below with what it
+/// can do, and each of them slightly tweaks how further expansion happens.
+///
+/// A quick overview of attributes supported right now are:
+///
+/// * `use_c_shim_if` - takes a #[cfg] directive and falls back to the
+///   C-compiled version if `feature = "c"` is specified.
+/// * `aapcs_on_arm` - forces the ABI of the function to be `"aapcs"` on ARM and
+///   the specified ABI everywhere else.
+/// * `unadjusted_on_win64` - like `aapcs_on_arm` this switches to the
+///   `"unadjusted"` abi on Win64 and the specified abi elsewhere.
+/// * `win64_128bit_abi_hack` - this attribute is used for 128-bit integer
+///   intrinsics where the ABI is slightly tweaked on Windows platforms, but
+///   it's a normal ABI elsewhere for returning a 128 bit integer.
+/// * `arm_aeabi_alias` - handles the "aliasing" of various intrinsics on ARM
+///   their otherwise typical names to other prefixed ones.
+///
+macro_rules! intrinsics {
+    () => ();
+
+    // Right now there's a bunch of architecture-optimized intrinsics in the
+    // stock compiler-rt implementation. Not all of these have been ported over
+    // to Rust yet so when the `c` feature of this crate is enabled we fall back
+    // to the architecture-specific versions which should be more optimized. The
+    // purpose of this macro is to easily allow specifying this.
+    //
+    // The argument to `use_c_shim_if` is a `#[cfg]` directive which, when true,
+    // will cause this crate's exported version of `$name` to just redirect to
+    // the C implementation. No symbol named `$name` will be in the object file
+    // for this crate itself.
+    //
+    // When the `#[cfg]` directive is false, or when the `c` feature is
+    // disabled, the provided implementation is used instead.
+    (
+        #[use_c_shim_if($($cfg_clause:tt)*)]
+        $(#[$($attr:tt)*])*
+        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
+            $($body:tt)*
+        }
+
+        $($rest:tt)*
+    ) => (
+
+        #[cfg(all(feature = "c", $($cfg_clause)*))]
+        pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
+            extern $abi {
+                fn $name($($argname: $ty),*) -> $ret;
+            }
+            unsafe {
+                $name($($argname),*)
+            }
+        }
+
+        #[cfg(not(all(feature = "c", $($cfg_clause)*)))]
+        intrinsics! {
+            $(#[$($attr)*])*
+            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
+                $($body)*
+            }
+        }
+
+        intrinsics!($($rest)*);
+    );
+
+    // We recognize the `#[aapcs_on_arm]` attribute here and generate the
+    // same intrinsic but force it to have the `"aapcs"` calling convention on
+    // ARM and `"C"` elsewhere.
+    (
+        #[aapcs_on_arm]
+        $(#[$($attr:tt)*])*
+        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
+            $($body:tt)*
+        }
+
+        $($rest:tt)*
+    ) => (
+        #[cfg(target_arch = "arm")]
+        intrinsics! {
+            $(#[$($attr)*])*
+            pub extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret {
+                $($body)*
+            }
+        }
+
+        #[cfg(not(target_arch = "arm"))]
+        intrinsics! {
+            $(#[$($attr)*])*
+            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
+                $($body)*
+            }
+        }
+
+        intrinsics!($($rest)*);
+    );
+
+    // Like aapcs above we recognize an attribute for the "unadjusted" abi on
+    // win64 for some methods.
+    (
+        #[unadjusted_on_win64]
+        $(#[$($attr:tt)*])*
+        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
+            $($body:tt)*
+        }
+
+        $($rest:tt)*
+    ) => (
+        #[cfg(all(windows, target_pointer_width = "64"))]
+        intrinsics! {
+            $(#[$($attr)*])*
+            pub extern "unadjusted" fn $name( $($argname: $ty),* ) -> $ret {
+                $($body)*
+            }
+        }
+
+        #[cfg(not(all(windows, target_pointer_width = "64")))]
+        intrinsics! {
+            $(#[$($attr)*])*
+            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
+                $($body)*
+            }
+        }
+
+        intrinsics!($($rest)*);
+    );
+
+    // Some intrinsics on win64 which return a 128-bit integer have an.. unusual
+    // calling convention. That's managed here with this "abi hack" which alters
+    // the generated symbol's ABI.
+    //
+    // This will still define a function in this crate with the given name and
+    // signature, but the actual symbol for the intrinsic may have a slightly
+    // different ABI on win64.
+    (
+        #[win64_128bit_abi_hack]
+        $(#[$($attr:tt)*])*
+        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
+            $($body:tt)*
+        }
+
+        $($rest:tt)*
+    ) => (
+        #[cfg(all(windows, target_pointer_width = "64"))]
+        $(#[$($attr)*])*
+        pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
+            $($body)*
+        }
+
+        #[cfg(all(windows, target_pointer_width = "64"))]
+        pub mod $name {
+
+            intrinsics! {
+                pub extern $abi fn $name( $($argname: $ty),* )
+                    -> ::macros::win64_128bit_abi_hack::U64x2
+                {
+                    let e: $ret = super::$name($($argname),*);
+                    ::macros::win64_128bit_abi_hack::U64x2::from(e)
+                }
+            }
+        }
+
+        #[cfg(not(all(windows, target_pointer_width = "64")))]
+        intrinsics! {
+            $(#[$($attr)*])*
+            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
+                $($body)*
+            }
+        }
+
+        intrinsics!($($rest)*);
+    );
+
+    // A bunch of intrinsics on ARM are aliased in the standard compiler-rt
+    // build under `__aeabi_*` aliases, and LLVM will call these instead of the
+    // original function. The aliasing here is used to generate these symbols in
+    // the object file.
+    (
+        #[arm_aeabi_alias = $alias:ident]
+        $(#[$($attr:tt)*])*
+        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
+            $($body:tt)*
+        }
+
+        $($rest:tt)*
+    ) => (
+        #[cfg(target_arch = "arm")]
+        pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
+            $($body)*
+        }
+
+        #[cfg(target_arch = "arm")]
+        pub mod $name {
+            intrinsics! {
+                pub extern "aapcs" fn $alias( $($argname: $ty),* ) -> $ret {
+                    super::$name($($argname),*)
+                }
+            }
+        }
+
+        #[cfg(not(target_arch = "arm"))]
+        intrinsics! {
+            $(#[$($attr)*])*
+            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
+                $($body)*
+            }
+        }
+
+        intrinsics!($($rest)*);
+    );
+
+    // This is the final catch-all rule. At this point we just generate an
+    // intrinsic with a conditional `#[no_mangle]` directive to avoid
+    // interfereing with duplicate symbols and whatnot during testing.
+    //
+    // After the intrinsic is defined we just continue with the rest of the
+    // input we were given.
+    (
+        $(#[$($attr:tt)*])*
+        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
+            $($body:tt)*
+        }
+
+        $($rest:tt)*
+    ) => (
+        $(#[$($attr)*])*
+        #[cfg_attr(not(feature = "mangled-names"), no_mangle)]
+        pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
+            $($body)*
+        }
+
+        intrinsics!($($rest)*);
+    );
+}
+
+// Hack for LLVM expectations for ABI on windows. This is used by the
+// `#[win64_128bit_abi_hack]` attribute recognized above
+#[cfg(all(windows, target_pointer_width="64"))]
+pub mod win64_128bit_abi_hack {
+    #[repr(simd)]
+    pub struct U64x2(u64, u64);
+
+    impl From<i128> for U64x2 {
+        fn from(i: i128) -> U64x2 {
+            use int::LargeInt;
+            let j = i as u128;
+            U64x2(j.low(), j.high())
+        }
+    }
+
+    impl From<u128> for U64x2 {
+        fn from(i: u128) -> U64x2 {
+            use int::LargeInt;
+            U64x2(i.low(), i.high())
+        }
+    }
+}

+ 4 - 4
src/mem.rs

@@ -5,7 +5,7 @@ type c_int = i16;
 #[cfg(not(target_pointer_width = "16"))]
 type c_int = i32;
 
-#[no_mangle]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "C" fn memcpy(dest: *mut u8,
                                 src: *const u8,
                                 n: usize)
@@ -18,7 +18,7 @@ pub unsafe extern "C" fn memcpy(dest: *mut u8,
     dest
 }
 
-#[no_mangle]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "C" fn memmove(dest: *mut u8,
                                  src: *const u8,
                                  n: usize)
@@ -41,7 +41,7 @@ pub unsafe extern "C" fn memmove(dest: *mut u8,
     dest
 }
 
-#[no_mangle]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "C" fn memset(s: *mut u8, c: c_int, n: usize) -> *mut u8 {
     let mut i = 0;
     while i < n {
@@ -51,7 +51,7 @@ pub unsafe extern "C" fn memset(s: *mut u8, c: c_int, n: usize) -> *mut u8 {
     s
 }
 
-#[no_mangle]
+#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
 pub unsafe extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 {
     let mut i = 0;
     while i < n {

+ 0 - 311
src/qc.rs

@@ -1,311 +0,0 @@
-//  When testing functions, QuickCheck (QC) uses small values for integer (`u*`/`i*`) arguments
-// (~ `[-100, 100]`), but these values don't stress all the code paths in our intrinsics. Here we
-// create newtypes over the primitive integer types with the goal of having full control over the
-// random values that will be used to test our intrinsics.
-
-use std::boxed::Box;
-use std::fmt;
-use core::{f32, f64};
-
-use quickcheck::{Arbitrary, Gen};
-
-use int::LargeInt;
-use float::Float;
-
-// Generates values in the full range of the integer type
-macro_rules! arbitrary {
-    ($TY:ident : $ty:ident) => {
-        #[derive(Clone, Copy, PartialEq)]
-        pub struct $TY(pub $ty);
-
-        impl Arbitrary for $TY {
-            fn arbitrary<G>(g: &mut G) -> $TY
-                where G: Gen
-            {
-                // NOTE Generate edge cases with a 10% chance
-                let t = if g.gen_weighted_bool(10) {
-                    *g.choose(&[
-                        $ty::min_value(),
-                        0,
-                        $ty::max_value(),
-                    ]).unwrap()
-                } else {
-                    g.gen()
-                };
-
-                $TY(t)
-            }
-
-            fn shrink(&self) -> Box<Iterator<Item=$TY>> {
-                struct Shrinker {
-                    x: $ty,
-                }
-
-                impl Iterator for Shrinker {
-                    type Item = $TY;
-
-                    fn next(&mut self) -> Option<$TY> {
-                        self.x /= 2;
-                        if self.x == 0 {
-                            None
-                        } else {
-                            Some($TY(self.x))
-                        }
-                    }
-                }
-
-                if self.0 == 0 {
-                    ::quickcheck::empty_shrinker()
-                } else {
-                    Box::new(Shrinker { x: self.0 })
-                }
-            }
-        }
-
-        impl fmt::Debug for $TY {
-            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-                fmt::Debug::fmt(&self.0, f)
-            }
-        }
-    }
-}
-
-arbitrary!(I32: i32);
-arbitrary!(U32: u32);
-
-// These integers are "too large". If we generate e.g. `u64` values in the full range then there's
-// only `1 / 2^32` chance of seeing a value smaller than `2^32` (i.e. whose higher "word" (32-bits)
-// is `0`)! But this is an important group of values to tests because we have special code paths for
-// them. Instead we'll generate e.g. `u64` integers this way: uniformly pick between (a) setting the
-// low word to 0 and generating a random high word, (b) vice versa: high word to 0 and random low
-// word or (c) generate both words randomly. This let's cover better the code paths in our
-// intrinsics.
-macro_rules! arbitrary_large {
-    ($TY:ident : $ty:ident) => {
-        #[derive(Clone, Copy, PartialEq)]
-        pub struct $TY(pub $ty);
-
-        impl Arbitrary for $TY {
-            fn arbitrary<G>(g: &mut G) -> $TY
-                where G: Gen
-            {
-                // NOTE Generate edge cases with a 10% chance
-                let t = if g.gen_weighted_bool(10) {
-                    *g.choose(&[
-                        $ty::min_value(),
-                        0,
-                        $ty::max_value(),
-                    ]).unwrap()
-                } else {
-                    match g.gen_range(0, 3) {
-                        0 => $ty::from_parts(g.gen(), g.gen()),
-                        1 => $ty::from_parts(0, g.gen()),
-                        2 => $ty::from_parts(g.gen(), 0),
-                        _ => unreachable!(),
-                    }
-                };
-
-                $TY(t)
-            }
-
-            fn shrink(&self) -> Box<Iterator<Item=$TY>> {
-                struct Shrinker {
-                    x: $ty,
-                }
-
-                impl Iterator for Shrinker {
-                    type Item = $TY;
-
-                    fn next(&mut self) -> Option<$TY> {
-                        self.x /= 2;
-                        if self.x == 0 {
-                            None
-                        } else {
-                            Some($TY(self.x))
-                        }
-                    }
-                }
-
-                if self.0 == 0 {
-                    ::quickcheck::empty_shrinker()
-                } else {
-                    Box::new(Shrinker { x: self.0 })
-                }
-            }
-        }
-
-        impl fmt::Debug for $TY {
-            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-                fmt::Debug::fmt(&self.0, f)
-            }
-        }
-    }
-}
-
-arbitrary_large!(I64: i64);
-arbitrary_large!(U64: u64);
-arbitrary_large!(I128: i128);
-arbitrary_large!(U128: u128);
-
-macro_rules! arbitrary_float {
-    ($TY:ident : $ty:ident) => {
-        #[derive(Clone, Copy)]
-        pub struct $TY(pub $ty);
-
-        impl Arbitrary for $TY {
-            fn arbitrary<G>(g: &mut G) -> $TY
-                where G: Gen
-            {
-                let special = [
-                    -0.0, 0.0, $ty::NAN, $ty::INFINITY, -$ty::INFINITY
-                ];
-
-                if g.gen_weighted_bool(10) { // Random special case
-                    $TY(*g.choose(&special).unwrap())
-                } else if g.gen_weighted_bool(10) { // NaN variants
-                    let sign: bool = g.gen();
-                    let exponent: <$ty as Float>::Int = g.gen();
-                    let significand: <$ty as Float>::Int = 0;
-                    $TY($ty::from_parts(sign, exponent, significand))
-                } else if g.gen() { // Denormalized
-                    let sign: bool = g.gen();
-                    let exponent: <$ty as Float>::Int = 0;
-                    let significand: <$ty as Float>::Int = g.gen();
-                    $TY($ty::from_parts(sign, exponent, significand))
-                } else { // Random anything
-                    let sign: bool = g.gen();
-                    let exponent: <$ty as Float>::Int = g.gen();
-                    let significand: <$ty as Float>::Int = g.gen();
-                    $TY($ty::from_parts(sign, exponent, significand))
-                }
-            }
-
-            fn shrink(&self) -> Box<Iterator<Item=$TY>> {
-                ::quickcheck::empty_shrinker()
-            }
-        }
-
-        impl fmt::Debug for $TY {
-            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-                fmt::Debug::fmt(&self.0, f)
-            }
-        }
-
-        impl PartialEq for $TY {
-            fn eq(&self, other: &$TY) -> bool {
-                self.0.eq_repr(other.0)
-            }
-        }
-    }
-}
-
-arbitrary_float!(F32: f32);
-arbitrary_float!(F64: f64);
-
-// Convenience macro to test intrinsics against their reference implementations.
-//
-// Each intrinsic is tested against both the `gcc_s` library as well as
-// `compiler-rt`. These libraries are defined in the `gcc_s` crate as well as
-// the `compiler-rt` crate in this repository. Both load a dynamic library and
-// lookup symbols through that dynamic library to ensure that we're using the
-// right intrinsic.
-//
-// This macro hopefully allows you to define a bare minimum of how to test an
-// intrinsic without worrying about these implementation details. A sample
-// invocation looks like:
-//
-//
-//    check! {
-//        // First argument is the function we're testing (either from this lib
-//        // or a dynamically loaded one. Further arguments are all generated by
-//        // quickcheck.
-//        fn __my_intrinsic(f: extern fn(i32) -> i32,
-//                          a: I32)
-//                          -> Option<(i32, i64)> {
-//
-//            // Discard tests by returning Some
-//            if a.0 == 0 {
-//                return None
-//            }
-//
-//            // Return the result via `Some` if the test can run
-//            let mut other_result = 0;
-//            let result = f(a.0, &mut other_result);
-//            Some((result, other_result))
-//        }
-//    }
-//
-// If anything returns `None` then the test is discarded, otherwise the two
-// results are compared for equality and the test fails if this equality check
-// fails.
-macro_rules! check {
-    ($(
-        $(#[$cfg:meta])*
-        fn $name:ident($f:ident: extern $abi:tt fn($($farg:ty),*) -> $fret:ty,
-                       $($arg:ident: $t:ty),*)
-                       -> Option<$ret:ty>
-        {
-            $($code:tt)*
-        }
-    )*) => (
-        $(
-            $(#[$cfg])*
-            fn $name($f: extern $abi fn($($farg),*) -> $fret,
-                     $($arg: $t),*) -> Option<$ret> {
-                $($code)*
-            }
-        )*
-
-        mod _test {
-            use qc::*;
-            use std::mem;
-            use quickcheck::TestResult;
-
-            $(
-                $(#[$cfg])*
-                #[test]
-                fn $name() {
-                    fn my_check($($arg:$t),*) -> TestResult {
-                        let my_answer = super::$name(super::super::$name,
-                                                     $($arg),*);
-                        let compiler_rt_fn = ::compiler_rt::get(stringify!($name));
-                        let compiler_rt_answer = unsafe {
-                            super::$name(mem::transmute(compiler_rt_fn),
-                                            $($arg),*)
-                        };
-                        let gcc_s_answer = 
-                        match ::gcc_s::get(stringify!($name)) {
-                            Some(f) => unsafe {
-                                Some(super::$name(mem::transmute(f), 
-                                                  $($arg),*))
-                            },
-                            None => None,
-                        };
-
-                        let print_values = || {
-                            print!("{} - Args: ", stringify!($name));
-                            $(print!("{:?} ", $arg);)*
-                            print!("\n");
-                            println!(" compiler-builtins: {:?}", my_answer);
-                            println!(" compiler_rt:       {:?}", compiler_rt_answer);
-                            println!(" gcc_s:             {:?}", gcc_s_answer);
-                        };
-
-                        if my_answer != compiler_rt_answer {
-                            print_values();
-                            TestResult::from_bool(false)
-                        } else if gcc_s_answer.is_some() && 
-                                  my_answer != gcc_s_answer.unwrap() {
-                            print_values();
-                            TestResult::from_bool(false)
-                        } else {
-                            TestResult::from_bool(true)
-                        }
-                    }
-
-                    ::quickcheck::quickcheck(my_check as fn($($t),*) -> TestResult)
-                }
-            )*
-        }
-    )
-}

+ 1 - 2
tests/divti3.rs

@@ -6,6 +6,5 @@
                 test), no_std)]
 
 // FIXME(#137)
-// FIXME(#158)
-#[cfg(not(any(target_arch = "mips", windows)))]
+#[cfg(not(target_arch = "mips"))]
 include!(concat!(env!("OUT_DIR"), "/divti3.rs"));

+ 1 - 2
tests/modti3.rs

@@ -6,6 +6,5 @@
                 test), no_std)]
 
 // FIXME(#137)
-// FIXME(#158)
-#[cfg(not(any(target_arch = "mips", windows)))]
+#[cfg(not(target_arch = "mips"))]
 include!(concat!(env!("OUT_DIR"), "/modti3.rs"));

+ 1 - 2
tests/udivmodti4.rs

@@ -6,6 +6,5 @@
                 test), no_std)]
 
 // FIXME(#137)
-// FIXME(#158)
-#[cfg(not(any(target_arch = "mips", windows)))]
+#[cfg(not(target_arch = "mips"))]
 include!(concat!(env!("OUT_DIR"), "/udivmodti4.rs"));

+ 1 - 2
tests/udivti3.rs

@@ -6,6 +6,5 @@
                 test), no_std)]
 
 // FIXME(#137)
-// FIXME(#158)
-#[cfg(not(any(target_arch = "mips", windows)))]
+#[cfg(not(target_arch = "mips"))]
 include!(concat!(env!("OUT_DIR"), "/udivti3.rs"));

+ 1 - 2
tests/umodti3.rs

@@ -6,6 +6,5 @@
                 test), no_std)]
 
 // FIXME(#137)
-// FIXME(#158)
-#[cfg(not(any(target_arch = "mips", windows)))]
+#[cfg(not(target_arch = "mips"))]
 include!(concat!(env!("OUT_DIR"), "/umodti3.rs"));