Browse Source

Merge pull request #76 from alexcrichton/more-test

Prep for and expand test infrastructure
Jorge Aparicio 8 năm trước cách đây
mục cha
commit
52383edf3e

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "compiler-rt/compiler-rt-cdylib/compiler-rt"]
+	path = compiler-rt/compiler-rt-cdylib/compiler-rt
+	url = https://github.com/llvm-mirror/compiler-rt

+ 33 - 32
.travis.yml

@@ -1,62 +1,63 @@
 dist: trusty
-language: generic
+language: rust
 services: docker
 sudo: required
+rust: nightly
+cache: cargo
 
 matrix:
   include:
     - env: TARGET=aarch64-unknown-linux-gnu
-      os: linux
     - env: TARGET=arm-unknown-linux-gnueabi
-      os: linux
+      # FIXME(rust-lang/rust#36518)
+      rust: nightly-2016-09-21
     - env: TARGET=arm-unknown-linux-gnueabihf
-      os: linux
     - env: TARGET=armv7-unknown-linux-gnueabihf
-      os: linux
     - env: TARGET=i586-unknown-linux-gnu
-      os: linux
     - env: TARGET=i686-apple-darwin
-      language: ruby
+      # FIXME(rust-lang/rust#36793)
+      rust: nightly-2016-09-10
       os: osx
     - env: TARGET=i686-unknown-linux-gnu
-      os: linux
+      # FIXME(rust-lang/rust#36793)
+      rust: nightly-2016-09-10
     - env: TARGET=mips-unknown-linux-gnu
-      os: linux
     - env: TARGET=mipsel-unknown-linux-gnu
-      os: linux
     - env: TARGET=powerpc-unknown-linux-gnu
-      os: linux
     - env: TARGET=powerpc64-unknown-linux-gnu
-      os: linux
-    - env: TARGET=powerpc64le-unknown-linux-gnu
-      os: linux
+    # QEMU crashes even when executing the simplest cross compiled C program:
+    # `int main() { return 0; }`
+    - env: TARGET=powerpc64le-unknown-linux-gnu NO_RUN=1
     - env: TARGET=thumbv6m-none-eabi
-      os: linux
-    - env: TARGET=thumbv6m-none-eabi WEAK=true
-      os: linux
+      install: cargo install xargo --debug -f
+      script: $HOME/.cargo/bin/xargo build --target $TARGET
     - env: TARGET=thumbv7em-none-eabi
-      os: linux
-    - env: TARGET=thumbv7em-none-eabi WEAK=true
-      os: linux
+      install: cargo install xargo --debug -f
+      script: $HOME/.cargo/bin/xargo build --target $TARGET
     - env: TARGET=thumbv7em-none-eabihf
-      os: linux
-    - env: TARGET=thumbv7em-none-eabihf WEAK=true
-      os: linux
-    - env: TARGET=thumbv7m-none-eabi
-      os: linux
-    - env: TARGET=thumbv7m-none-eabi WEAK=true
-      os: linux
+      install: cargo install xargo --debug -f
+      script: $HOME/.cargo/bin/xargo build --target $TARGET
     - env: TARGET=x86_64-apple-darwin
-      language: ruby
       os: osx
-    - env: TARGET=x86_64-unknown-linux-gnu
-      os: linux
+env: TARGET=x86_64-unknown-linux-gnu
+
+before_install:
+  - test "$TRAVIS_OS_NAME" = "osx" || docker run --rm --privileged multiarch/qemu-user-static:register
 
 install:
-  - bash ci/install.sh
+  - curl https://static.rust-lang.org/rustup.sh |
+    sh -s -- --add-target=$TARGET --disable-sudo -y --prefix=`rustc --print sysroot`
 
 script:
-  - bash ci/script.sh
+  - cargo generate-lockfile
+  - if [[ $TRAVIS_OS_NAME = "linux" ]]; then
+      sudo apt-get remove -y qemu-user-static &&
+      sudo apt-get install -y qemu-user-static &&
+      sh ci/run-docker.sh $TARGET;
+    else
+      cargo test --target $TARGET &&
+      cargo test --target $TARGET --release;
+    fi
 
 branches:
   only:

+ 2 - 3
Cargo.toml

@@ -13,9 +13,8 @@ optional = true
 [dev-dependencies]
 quickcheck = "0.3.1"
 rand = "0.3.14"
-
-[dev-dependencies.gcc_s]
-path = "gcc_s"
+gcc_s = { path = "gcc_s" }
+compiler-rt = { path = "compiler-rt" }
 
 [features]
 weak = ["rlibc/weak"]

+ 3 - 3
README.md

@@ -16,8 +16,8 @@ See [rust-lang/rust#35437][0].
 
 If you are working with a target that doesn't have binary releases of std available via rustup (this
 probably means you are building the core crate yourself) and need compiler-rt intrinsics (i.e. you
-are probably getting linker errors when building an executable: "undefined reference to
-__aeabi_memcpy"), you can use this crate to get those intrinsics and solve the linker errors. To do
+are probably getting linker errors when building an executable: `undefined reference to
+__aeabi_memcpy`), you can use this crate to get those intrinsics and solve the linker errors. To do
 that, simply add this crate as a Cargo dependency (it doesn't matter where in the dependency graph
 this crate ends up, as long as it's there):
 
@@ -52,7 +52,7 @@ porting that particular intrinsic.
 1. [Rust][4] and [C][5] have slightly different operator precedence. C evaluates comparisons (`== !=`) before bitwise operations (`& | ^`), while Rust evaluates the other way.
 2. C assumes wrapping operations everywhere. Rust panics on overflow when in debug mode. Consider using the [Wrapping][6] type or the explicit [wrapping_*][7] functions where applicable.
 3. Note [C implicit casts][8], especially integer promotion. Rust is much more explicit about casting, so be sure that any cast which affects the output is ported to the Rust implementation.
-4. Rust has [many functions][9] for integer or floating point manipulation in the standard library. Consider using one of these functions rather than porting a new one. 
+4. Rust has [many functions][9] for integer or floating point manipulation in the standard library. Consider using one of these functions rather than porting a new one.
 
 [4]: https://doc.rust-lang.org/reference.html#operator-precedence
 [5]: http://en.cppreference.com/w/c/language/operator_precedence

+ 1 - 0
appveyor.yml

@@ -4,6 +4,7 @@ environment:
     - TARGET: x86_64-pc-windows-msvc
 
 install:
+  - git submodule update --init
   - curl -sSf -o rustup-init.exe https://win.rustup.rs
   - rustup-init.exe --default-host %TARGET% --default-toolchain nightly -y
   - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin

+ 1 - 0
build.rs

@@ -4,4 +4,5 @@ fn main() {
     if env::var("TARGET").unwrap().ends_with("gnueabihf") {
         println!("cargo:rustc-cfg=gnueabihf")
     }
+    println!("cargo:rerun-if-changed=build.rs");
 }

+ 10 - 0
ci/docker/aarch64-unknown-linux-gnu/Dockerfile

@@ -0,0 +1,10 @@
+FROM ubuntu:16.04
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends \
+  gcc libc6-dev ca-certificates \
+  gcc-aarch64-linux-gnu libc6-dev-arm64-cross \
+  qemu-user-static
+ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
+    PATH=$PATH:/rust/bin \
+    QEMU_LD_PREFIX=/usr/aarch64-linux-gnu \
+    RUST_TEST_THREADS=1

+ 10 - 0
ci/docker/arm-unknown-linux-gnueabi/Dockerfile

@@ -0,0 +1,10 @@
+FROM ubuntu:16.04
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends \
+  gcc libc6-dev ca-certificates \
+  gcc-arm-linux-gnueabi libc6-dev-armel-cross qemu-user-static
+ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_LINKER=arm-linux-gnueabi-gcc \
+    PATH=$PATH:/rust/bin \
+    QEMU_LD_PREFIX=/usr/arm-linux-gnueabi \
+    RUST_TEST_THREADS=1
+

+ 9 - 0
ci/docker/arm-unknown-linux-gnueabihf/Dockerfile

@@ -0,0 +1,9 @@
+FROM ubuntu:16.04
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends \
+  gcc libc6-dev ca-certificates \
+  gcc-arm-linux-gnueabihf libc6-dev-armhf-cross qemu-user-static
+ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \
+    PATH=$PATH:/rust/bin \
+    QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \
+    RUST_TEST_THREADS=1

+ 9 - 0
ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile

@@ -0,0 +1,9 @@
+FROM ubuntu:16.04
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends \
+  gcc libc6-dev ca-certificates \
+  gcc-arm-linux-gnueabihf libc6-dev-armhf-cross qemu-user-static
+ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \
+    PATH=$PATH:/rust/bin \
+    QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \
+    RUST_TEST_THREADS=1

+ 5 - 0
ci/docker/i586-unknown-linux-gnu/Dockerfile

@@ -0,0 +1,5 @@
+FROM ubuntu:16.04
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends \
+  gcc-multilib libc6-dev ca-certificates
+ENV PATH=$PATH:/rust/bin

+ 5 - 0
ci/docker/i686-unknown-linux-gnu/Dockerfile

@@ -0,0 +1,5 @@
+FROM ubuntu:16.04
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends \
+  gcc-multilib libc6-dev ca-certificates
+ENV PATH=$PATH:/rust/bin

+ 12 - 0
ci/docker/mips-unknown-linux-gnu/Dockerfile

@@ -0,0 +1,12 @@
+FROM ubuntu:16.04
+
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends \
+        gcc libc6-dev ca-certificates \
+        gcc-mips-linux-gnu libc6-dev-mips-cross \
+        binfmt-support qemu-user-static qemu-system-mips
+
+ENV CARGO_TARGET_MIPS_UNKNOWN_LINUX_GNU_LINKER=mips-linux-gnu-gcc \
+    PATH=$PATH:/rust/bin \
+    QEMU_LD_PREFIX=/usr/mips-linux-gnu \
+    RUST_TEST_THREADS=1

+ 12 - 0
ci/docker/mipsel-unknown-linux-gnu/Dockerfile

@@ -0,0 +1,12 @@
+FROM ubuntu:16.04
+
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends \
+        gcc libc6-dev ca-certificates \
+        gcc-mipsel-linux-gnu libc6-dev-mipsel-cross \
+        binfmt-support qemu-user-static
+
+ENV CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_GNU_LINKER=mipsel-linux-gnu-gcc \
+    PATH=$PATH:/rust/bin \
+    QEMU_LD_PREFIX=/usr/mipsel-linux-gnu \
+    RUST_TEST_THREADS=1

+ 12 - 0
ci/docker/powerpc-unknown-linux-gnu/Dockerfile

@@ -0,0 +1,12 @@
+FROM ubuntu:16.04
+
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends \
+        gcc libc6-dev qemu-user-static ca-certificates \
+        gcc-powerpc-linux-gnu libc6-dev-powerpc-cross \
+        qemu-system-ppc
+
+ENV CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_LINKER=powerpc-linux-gnu-gcc \
+    PATH=$PATH:/rust/bin \
+    QEMU_LD_PREFIX=/usr/powerpc-linux-gnu \
+    RUST_TEST_THREADS=1

+ 13 - 0
ci/docker/powerpc64-unknown-linux-gnu/Dockerfile

@@ -0,0 +1,13 @@
+FROM ubuntu:16.04
+
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends \
+        gcc libc6-dev ca-certificates \
+        gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross \
+        binfmt-support qemu-user-static qemu-system-ppc
+
+ENV CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_LINKER=powerpc64-linux-gnu-gcc \
+    CC_powerpc64_unknown_linux_gnu=powerpc64-linux-gnu-gcc \
+    PATH=$PATH:/rust/bin \
+    QEMU_LD_PREFIX=/usr/powerpc64-linux-gnu \
+    RUST_TEST_THREADS=1

+ 13 - 0
ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile

@@ -0,0 +1,13 @@
+FROM ubuntu:16.04
+
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends \
+        gcc libc6-dev qemu-user-static ca-certificates \
+        gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross \
+        qemu-system-ppc
+
+ENV CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_LINKER=powerpc64le-linux-gnu-gcc \
+    CC_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-gcc \
+    PATH=$PATH:/rust/bin \
+    QEMU_LD_PREFIX=/usr/powerpc64le-linux-gnu \
+    RUST_TEST_THREADS=1

+ 6 - 0
ci/docker/x86_64-unknown-linux-gnu/Dockerfile

@@ -0,0 +1,6 @@
+FROM ubuntu:16.04
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends \
+  gcc libc6-dev ca-certificates
+ENV PATH=$PATH:/rust/bin
+

+ 0 - 51
ci/env.sh

@@ -1,51 +0,0 @@
-case $TRAVIS_OS_NAME in
-    linux)
-        HOST=x86_64-unknown-linux-gnu
-        NM=nm
-        OBJDUMP=objdump
-        LINUX=y
-        ;;
-    osx)
-        HOST=x86_64-apple-darwin
-        NM=gnm
-        OBJDUMP=gobjdump
-        OSX=y
-        ;;
-esac
-
-# NOTE For rustup
-export PATH="$HOME/.cargo/bin:$PATH"
-
-CARGO=cargo
-RUN_TESTS=y
-
-# NOTE For the host and its 32-bit variants we don't need prefixed tools or QEMU
-if [[ $TARGET != $HOST && ! $TARGET =~ ^i.86- ]]; then
-    GCC_TRIPLE=${TARGET//unknown-/}
-
-    case $TARGET in
-        armv7-unknown-linux-gnueabihf)
-            GCC_TRIPLE=arm-linux-gnueabihf
-            ;;
-        powerpc64le-unknown-linux-gnu)
-            # QEMU crashes even when executing the simplest cross compiled C program:
-            # `int main() { return 0; }`
-            RUN_TESTS=n
-            ;;
-        thumbv*-none-eabi*)
-            CARGO=xargo
-            GCC_TRIPLE=arm-none-eabi
-            # Bare metal targets. No `std` or `test` crates for these targets.
-            RUN_TESTS=n
-            ;;
-    esac
-
-    export CARGO_TARGET_$(echo $TARGET | tr a-z- A-Z_)_LINKER=$GCC_TRIPLE-gcc
-
-    if [[ $RUN_TESTS == y ]]; then
-        # NOTE(export) so this can reach the processes that `cargo test` spawns
-        export QEMU_LD_PREFIX=/usr/$GCC_TRIPLE
-    fi
-
-    PREFIX=$GCC_TRIPLE-
-fi

+ 0 - 53
ci/install.sh

@@ -1,53 +0,0 @@
-set -ex
-
-. $(dirname $0)/env.sh
-
-install_qemu() {
-    if [[ $QEMU_LD_PREFIX ]]; then
-        apt-get update
-        apt-get install -y --no-install-recommends \
-                binfmt-support qemu-user-static
-    fi
-}
-
-install_gist() {
-    if [[ $OSX ]]; then
-        gem install gist -v 4.5.0
-    fi
-}
-
-install_binutils() {
-    if [[ $OSX ]]; then
-        brew install binutils
-    fi
-}
-
-install_rust() {
-    if [[ $OSX ]]; then
-        curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain=nightly
-    else
-        rustup default nightly
-    fi
-
-    rustup -V
-    rustc -V
-    cargo -V
-}
-
-add_rustup_target() {
-    if [[ $TARGET != $HOST && $CARGO == cargo ]]; then
-        rustup target add $TARGET
-    fi
-}
-
-main() {
-    if [[ $OSX || ${IN_DOCKER_CONTAINER:-n} == y ]]; then
-        install_qemu
-        install_gist
-        install_binutils
-        install_rust
-        add_rustup_target
-    fi
-}
-
-main

+ 29 - 0
ci/run-docker.sh

@@ -0,0 +1,29 @@
+# Small script to run tests for a target (or all targets) inside all the
+# respective docker images.
+
+set -ex
+
+run() {
+    echo $1
+    CMD="cargo test --target $1"
+    if [ "$NO_RUN" = "1" ]; then
+        CMD="$CMD --no-run"
+    fi
+    docker build -t libc ci/docker/$1
+    docker run \
+      -v `rustc --print sysroot`:/rust:ro \
+      -v `pwd`:/checkout:ro \
+      -e CARGO_TARGET_DIR=/tmp/target \
+      -w /checkout \
+      --privileged \
+      -it libc \
+      bash -c "$CMD && $CMD --release"
+}
+
+if [ -z "$1" ]; then
+  for d in `ls ci/docker/`; do
+    run $d
+  done
+else
+  run $1
+fi

+ 0 - 80
ci/script.sh

@@ -1,80 +0,0 @@
-set -ex
-
-. $(dirname $0)/env.sh
-
-gist_it() {
-    gist -d "'$TARGET/rustc-builtins.rlib' from commit '$TRAVIS_COMMIT' on branch '$TRAVIS_BRANCH'"
-    echo "Disassembly available at the above URL."
-}
-
-build() {
-    if [[ $WEAK ]]; then
-        $CARGO build --features weak --target $TARGET
-        $CARGO build --features weak --target $TARGET --release
-    else
-        $CARGO build --target $TARGET
-        $CARGO build --target $TARGET --release
-    fi
-}
-
-inspect() {
-    $PREFIX$NM -g --defined-only target/**/debug/*.rlib
-
-    set +e
-    $PREFIX$OBJDUMP -Cd target/**/release/*.rlib | gist_it
-    set -e
-
-    # Check presence/absence of weak symbols
-    if [[ $WEAK ]]; then
-        local symbols=( memcmp memcpy memmove memset )
-        for symbol in "${symbols[@]}"; do
-            $PREFIX$NM target/$TARGET/debug/deps/librlibc-*.rlib | grep -q "W $symbol"
-        done
-    else
-        set +e
-        ls target/$TARGET/debug/deps/librlibc-*.rlib
-
-        if [[ $? == 0 ]]; then
-            exit 1
-        fi
-
-        set -e
-    fi
-
-}
-
-run_tests() {
-    if [[ $QEMU_LD_PREFIX ]]; then
-        export RUST_TEST_THREADS=1
-    fi
-
-    if [[ $RUN_TESTS == y ]]; then
-        cargo test --target $TARGET
-        cargo test --target $TARGET --release
-    fi
-}
-
-main() {
-    if [[ $LINUX && ${IN_DOCKER_CONTAINER:-n} == n ]]; then
-        # NOTE The Dockerfile of this image is in the docker branch of this repository
-        docker run \
-               --privileged \
-               -e IN_DOCKER_CONTAINER=y \
-               -e TARGET=$TARGET \
-               -e TRAVIS_BRANCH=$TRAVIS_BRANCH \
-               -e TRAVIS_COMMIT=$TRAVIS_COMMIT \
-               -e TRAVIS_OS_NAME=$TRAVIS_OS_NAME \
-               -e WEAK=$WEAK \
-               -v $(pwd):/mnt \
-               japaric/rustc-builtins \
-               sh -c 'cd /mnt;
-                      bash ci/install.sh;
-                      bash ci/script.sh'
-    else
-        build
-        inspect
-        run_tests
-    fi
-}
-
-main

+ 8 - 0
compiler-rt/Cargo.toml

@@ -0,0 +1,8 @@
+[package]
+name = "compiler-rt"
+version = "0.1.0"
+authors = ["Alex Crichton <alex@alexcrichton.com>"]
+
+[dependencies]
+compiler-rt-cdylib = { path = "compiler-rt-cdylib" }
+libloading = "0.3"

+ 11 - 0
compiler-rt/compiler-rt-cdylib/Cargo.toml

@@ -0,0 +1,11 @@
+[package]
+name = "compiler-rt-cdylib"
+version = "0.1.0"
+authors = ["Alex Crichton <alex@alexcrichton.com>"]
+build = "build.rs"
+
+[lib]
+crate-type = ["cdylib"]
+
+[build-dependencies]
+gcc = "0.3"

+ 68 - 0
compiler-rt/compiler-rt-cdylib/build.rs

@@ -0,0 +1,68 @@
+extern crate gcc;
+
+use std::env;
+use std::path::Path;
+use std::process::Command;
+
+struct Sources {
+    files: Vec<&'static str>,
+}
+
+impl Sources {
+    fn new() -> Sources {
+        Sources { files: Vec::new() }
+    }
+
+    fn extend(&mut self, sources: &[&'static str]) {
+        self.files.extend(sources);
+    }
+}
+
+fn main() {
+    if !Path::new("compiler-rt/.git").exists() {
+        let _ = Command::new("git").args(&["submodule", "update", "--init"])
+                                   .status();
+    }
+
+    let target = env::var("TARGET").expect("TARGET was not set");
+    let cfg = &mut gcc::Config::new();
+
+    if target.contains("msvc") {
+        cfg.define("__func__", Some("__FUNCTION__"));
+    } else {
+        cfg.flag("-fno-builtin");
+        cfg.flag("-fomit-frame-pointer");
+        cfg.flag("-ffreestanding");
+    }
+
+    let mut sources = Sources::new();
+
+    sources.extend(&[
+        "muldi3.c",
+        "mulosi4.c",
+        "mulodi4.c",
+        "divsi3.c",
+        "divdi3.c",
+        "modsi3.c",
+        "moddi3.c",
+        "divmodsi4.c",
+        "divmoddi4.c",
+        "ashldi3.c",
+        "ashrdi3.c",
+        "lshrdi3.c",
+        "udivdi3.c",
+        "umoddi3.c",
+        "udivmoddi4.c",
+        "udivsi3.c",
+        "umodsi3.c",
+        "udivmodsi4.c",
+        "adddf3.c",
+        "addsf3.c",
+    ]);
+
+    for src in sources.files.iter() {
+        cfg.file(Path::new("compiler-rt/lib/builtins").join(src));
+    }
+
+    cfg.compile("libcompiler-rt.a");
+}

+ 1 - 0
compiler-rt/compiler-rt-cdylib/compiler-rt

@@ -0,0 +1 @@
+Subproject commit 515106ebc07227b85336816ef77bc39506b8fd26

+ 60 - 0
compiler-rt/compiler-rt-cdylib/src/lib.rs

@@ -0,0 +1,60 @@
+#![feature(lang_items)]
+#![no_std]
+
+extern {
+    fn __ashldi3();
+    fn __ashrdi3();
+    fn __divdi3();
+    fn __divmoddi4();
+    fn __divmodsi4();
+    fn __divsi3();
+    fn __lshrdi3();
+    fn __moddi3();
+    fn __modsi3();
+    fn __muldi3();
+    fn __mulodi4();
+    fn __mulosi4();
+    fn __udivdi3();
+    fn __udivmoddi4();
+    fn __udivmodsi4();
+    fn __udivsi3();
+    fn __umoddi3();
+    fn __umodsi3();
+    fn __addsf3();
+    fn __adddf3();
+}
+
+macro_rules! declare {
+    ($func:ident, $sym:ident) => {
+        #[no_mangle]
+        pub extern fn $func() -> usize {
+            $sym as usize
+        }
+    }
+}
+
+declare!(___ashldi3, __ashldi3);
+declare!(___ashrdi3, __ashrdi3);
+declare!(___divdi3, __divdi3);
+declare!(___divmoddi4, __divmoddi4);
+declare!(___divmodsi4, __divmodsi4);
+declare!(___divsi3, __divsi3);
+declare!(___lshrdi3, __lshrdi3);
+declare!(___moddi3, __moddi3);
+declare!(___modsi3, __modsi3);
+declare!(___muldi3, __muldi3);
+declare!(___mulodi4, __mulodi4);
+declare!(___mulosi4, __mulosi4);
+declare!(___udivdi3, __udivdi3);
+declare!(___udivmoddi4, __udivmoddi4);
+declare!(___udivmodsi4, __udivmodsi4);
+declare!(___udivsi3, __udivsi3);
+declare!(___umoddi3, __umoddi3);
+declare!(___umodsi3, __umodsi3);
+declare!(___addsf3, __addsf3);
+declare!(___adddf3, __adddf3);
+
+#[lang = "eh_personality"]
+fn eh_personality() {}
+#[lang = "panic_fmt"]
+fn panic_fmt() {}

+ 35 - 0
compiler-rt/src/lib.rs

@@ -0,0 +1,35 @@
+#![feature(drop_types_in_const)]
+
+extern crate libloading;
+
+use std::sync::{Once, ONCE_INIT};
+use std::env;
+
+use libloading::Library;
+
+fn compiler_rt() -> &'static Library {
+    let dir = env::current_exe().unwrap();
+    let cdylib = dir.parent().unwrap().read_dir().unwrap().map(|c| {
+        c.unwrap().path()
+    }).find(|path| {
+        path.file_name().unwrap().to_str().unwrap().contains("compiler_rt_cdylib")
+    }).unwrap();
+
+    unsafe {
+        static mut COMPILER_RT: Option<Library> = None;
+        static INIT: Once = ONCE_INIT;
+
+        INIT.call_once(|| {
+            COMPILER_RT = Some(Library::new(&cdylib).unwrap());
+        });
+        COMPILER_RT.as_ref().unwrap()
+    }
+}
+
+pub fn get(sym: &str) -> usize {
+    unsafe {
+        let sym = format!("_{}", sym);
+        let f: fn() -> usize = *compiler_rt().get(sym.as_bytes()).unwrap();
+        f()
+    }
+}

+ 5 - 34
gcc_s/src/lib.rs

@@ -27,42 +27,13 @@ fn gcc_s() -> &'static Library {
 }
 
 #[cfg(windows)]
-macro_rules! declare {
-    ($symbol:ident: fn($($i:ty),+) -> $o:ty) => {
-        pub fn $symbol() -> Option<unsafe extern fn($($i),+) -> $o> {
-            None
-        }
-    }
+pub fn get(_sym: &str) -> Option<usize> {
+    None
 }
 
 #[cfg(not(windows))]
-macro_rules! declare {
-    ($symbol:ident: fn($($i:ty),+) -> $o:ty) => {
-        pub fn $symbol() -> Option<unsafe extern fn($($i),+) -> $o> {
-            unsafe {
-                gcc_s().get(concat!("__", stringify!($symbol)).as_bytes()).ok().map(|s| *s)
-            }
-        }
+pub fn get(sym: &str) -> Option<usize> {
+    unsafe {
+        gcc_s().get(sym.as_bytes()).ok().map(|s| *s)
     }
 }
-
-declare!(ashldi3: fn(u64, u32) -> u64);
-declare!(ashrdi3: fn(i64, u32) -> i64);
-declare!(divdi3: fn(i64, i64) -> i64);
-declare!(divmoddi4: fn(i64, i64, &mut i64) -> i64);
-declare!(divmodsi4: fn(i32, i32, &mut i32) -> i32);
-declare!(divsi3: fn(i32, i32) -> i32);
-declare!(lshrdi3: fn(u64, u32) -> u64);
-declare!(moddi3: fn(i64, i64) -> i64);
-declare!(modsi3: fn(i32, i32) -> i32);
-declare!(muldi3: fn(u64, u64) -> u64);
-declare!(mulodi4: fn(i64, i64, &mut i32) -> i64);
-declare!(mulosi4: fn(i32, i32, &mut i32) -> i32);
-declare!(udivdi3: fn(u64, u64) -> u64);
-declare!(udivmoddi4: fn(u64, u64, Option<&mut u64>) -> u64);
-declare!(udivmodsi4: fn(u32, u32, Option<&mut u32>) -> u32);
-declare!(udivsi3: fn(u32, u32) -> u32);
-declare!(umoddi3: fn(u64, u64) -> u64);
-declare!(umodsi3: fn(u32, u32) -> u32);
-declare!(addsf3: fn(f32, f32) -> f32);
-declare!(adddf3: fn(f64, f64) -> f64);

+ 32 - 33
src/float/add.rs

@@ -89,12 +89,12 @@ macro_rules! add {
             if a_exponent.0 == 0 {
                 let (exponent, significand) = <$ty>::normalize(a_significand.0);
                 a_exponent = Wrapping(exponent);
-                a_significand = Wrapping(significand); 
+                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); 
+                b_significand = Wrapping(significand);
             }
 
             // The sign of the result is the sign of the larger operand, a.  If they
@@ -123,8 +123,8 @@ macro_rules! add {
             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_significand.0 == 0 {
+                    return (<$ty as Float>::from_repr(0));
                 }
 
                 // If partial cancellation occured, we need to left-shift the result
@@ -148,7 +148,7 @@ macro_rules! add {
             }
 
             // If we have overflowed the type, return +/- infinity:
-            if a_exponent >= Wrapping(max_exponent.0 as i32) { 
+            if a_exponent >= Wrapping(max_exponent.0 as i32) {
                 return (<$ty>::from_repr((inf_rep | result_sign).0));
             }
 
@@ -190,47 +190,46 @@ mod tests {
     use float::Float;
     use qc::{U32, U64};
 
-    use gcc_s;
-    use rand;
-
     // NOTE The tests below have special handing for NaN values.
     // Because NaN != NaN, the floating-point representations must be used
     // Because there are many diffferent values of NaN, and the implementation
     // doesn't care about calculating the 'correct' one, if both values are NaN
     // the values are considered equivalent.
 
+    struct FRepr<F>(F);
+
+    impl<F: Float> PartialEq for FRepr<F> {
+        fn eq(&self, other: &FRepr<F>) -> bool {
+            // NOTE(cfg) for some reason, on hard float targets, our implementation doesn't
+            // match the output of its gcc_s counterpart. Until we investigate further, we'll
+            // just avoid testing against gcc_s on those targets. Do note that our
+            // implementation matches the output of the FPU instruction on *hard* float targets
+            // and matches its gcc_s counterpart on *soft* float targets.
+            if cfg!(gnueabihf) {
+                return true
+            }
+            self.0.eq_repr(other.0)
+        }
+    }
+
     // TODO: Add F32/F64 to qc so that they print the right values (at the very least)
-    quickcheck! {
-        fn addsf3(a: U32, b: U32) -> bool {
+    check! {
+        fn __addsf3(f: extern fn(f32, f32) -> f32,
+                    a: U32,
+                    b: U32)
+                    -> Option<FRepr<f32> > {
             let (a, b) = (f32::from_repr(a.0), f32::from_repr(b.0));
-            let x = super::__addsf3(a, b);
-
-            match gcc_s::addsf3() {
-                // NOTE(cfg) for some reason, on hard float targets, our implementation doesn't
-                // match the output of its gcc_s counterpart. Until we investigate further, we'll
-                // just avoid testing against gcc_s on those targets. Do note that our
-                // implementation matches the output of the FPU instruction on *hard* float targets
-                // and matches its gcc_s counterpart on *soft* float targets.
-                #[cfg(not(gnueabihf))]
-                Some(addsf3) if rand::random() => x.eq_repr(unsafe { addsf3(a, b) }),
-                _ => x.eq_repr(a + b),
-            }
+            Some(FRepr(f(a, b)))
         }
 
-        fn adddf3(a: U64, b: U64) -> bool {
+        fn __adddf3(f: extern fn(f64, f64) -> f64,
+                    a: U64,
+                    b: U64) -> Option<FRepr<f64> > {
             let (a, b) = (f64::from_repr(a.0), f64::from_repr(b.0));
-            let x = super::__adddf3(a, b);
-
-            match gcc_s::adddf3() {
-                // NOTE(cfg) See NOTE above
-                #[cfg(not(gnueabihf))]
-                Some(adddf3) if rand::random() => x.eq_repr(unsafe { adddf3(a, b) }),
-                _ => x.eq_repr(a + b),
-
-            }
+            Some(FRepr(f(a, b)))
         }
     }
-    
+
     // More tests for special float values
 
     #[test]

+ 3 - 3
src/float/mod.rs

@@ -3,10 +3,10 @@ use core::mem;
 pub mod add;
 
 /// Trait for some basic operations on floats
-pub trait Float: Sized {
+pub trait Float: Sized + Copy {
     /// A uint of the same with as the float
     type Int;
-    
+
     /// Returns the bitwidth of the float type
     fn bits() -> u32;
 
@@ -22,7 +22,7 @@ pub trait Float: Sized {
     /// compared.
     fn eq_repr(self, rhs: Self) -> bool;
 
-    /// Returns a `Self::Int` transmuted back to `Self` 
+    /// Returns a `Self::Int` transmuted back to `Self`
     fn from_repr(a: Self::Int) -> Self;
 
     /// Returns (normalized exponent, normalized significand)

+ 16 - 42
src/int/mul.rs

@@ -74,60 +74,34 @@ mulo!(__mulodi4: i64);
 mod tests {
     use qc::{I32, I64, U64};
 
-    use gcc_s;
-    use rand;
-
-    quickcheck! {
-        fn muldi(a: U64, b: U64) -> bool {
-            let (a, b) = (a.0, b.0);
-            let r = super::__muldi3(a, b);
-
-            match gcc_s::muldi3() {
-                Some(muldi3) if rand::random() => r == unsafe { muldi3(a, b) },
-                _ => r == a.wrapping_mul(b),
-            }
+    check! {
+        fn __muldi3(f: extern fn(u64, u64) -> u64, a: U64, b: U64)
+                    -> Option<u64> {
+            Some(f(a.0, b.0))
         }
 
-        fn mulosi(a: I32, b: I32) -> bool {
+        fn __mulosi4(f: extern fn(i32, i32, &mut i32) -> i32,
+                     a: I32,
+                     b: I32) -> Option<(i32, i32)> {
             let (a, b) = (a.0, b.0);
             let mut overflow = 2;
-            let r = super::__mulosi4(a, b, &mut overflow);
+            let r = f(a, b, &mut overflow);
             if overflow != 0 && overflow != 1 {
-                return false;
-            }
-
-            match gcc_s::mulosi4() {
-                Some(mulosi4) if rand::random() => {
-                    let mut gcc_s_overflow = 2;
-                    let gcc_s_r = unsafe {
-                        mulosi4(a, b, &mut gcc_s_overflow)
-                    };
-
-                    (r, overflow) == (gcc_s_r, gcc_s_overflow)
-                },
-                _ => (r, overflow != 0) == a.overflowing_mul(b),
+                return None
             }
+            Some((r, overflow))
         }
 
-        fn mulodi(a: I64, b: I64) -> bool {
+        fn __mulodi4(f: extern fn(i64, i64, &mut i32) -> i64,
+                     a: I64,
+                     b: I64) -> Option<(i64, i32)> {
             let (a, b) = (a.0, b.0);
             let mut overflow = 2;
-            let r = super::__mulodi4(a, b, &mut overflow);
+            let r = f(a, b, &mut overflow);
             if overflow != 0 && overflow != 1 {
-                return false;
-            }
-
-            match gcc_s::mulodi4() {
-                Some(mulodi4) if rand::random() => {
-                    let mut gcc_s_overflow = 2;
-                    let gcc_s_r = unsafe {
-                        mulodi4(a, b, &mut gcc_s_overflow)
-                    };
-
-                    (r, overflow) == (gcc_s_r, gcc_s_overflow)
-                },
-                _ => (r, overflow != 0) == a.overflowing_mul(b),
+                return None
             }
+            Some((r, overflow))
         }
     }
 }

+ 29 - 75
src/int/sdiv.rs

@@ -54,116 +54,70 @@ divmod!(__divmoddi4, __divdi3: i64);
 mod tests {
     use qc::{U32, U64};
 
-    use gcc_s;
-    use quickcheck::TestResult;
-    use rand;
-
-    quickcheck!{
-        fn divdi3(n: U64, d: U64) -> TestResult {
+    check! {
+        fn __divdi3(f: extern fn(i64, i64) -> i64, n: U64, d: U64) -> Option<i64> {
             let (n, d) = (n.0 as i64, d.0 as i64);
             if d == 0 {
-                TestResult::discard()
+                None
             } else {
-                let q = super::__divdi3(n, d);
-
-                match gcc_s::divdi3() {
-                    Some(divdi3) if rand::random() => {
-                        TestResult::from_bool(q == unsafe { divdi3(n, d) })
-                    },
-                    _ => TestResult::from_bool(q == n / d),
-                }
+                Some(f(n, d))
             }
         }
 
-        fn moddi3(n: U64, d: U64) -> TestResult {
+        fn __moddi3(f: extern fn(i64, i64) -> i64, n: U64, d: U64) -> Option<i64> {
             let (n, d) = (n.0 as i64, d.0 as i64);
             if d == 0 {
-                TestResult::discard()
+                None
             } else {
-                let r = super::__moddi3(n, d);
-
-                match gcc_s::moddi3() {
-                    Some(moddi3) if rand::random() => {
-                        TestResult::from_bool(r == unsafe { moddi3(n, d) })
-                    },
-                    _ => TestResult::from_bool(r == n % d),
-                }
+                Some(f(n, d))
             }
         }
 
-        fn divmoddi4(n: U64, d: U64) -> TestResult {
+        fn __divmoddi4(f: extern fn(i64, i64, &mut i64) -> i64,
+                       n: U64,
+                       d: U64) -> Option<(i64, i64)> {
             let (n, d) = (n.0 as i64, d.0 as i64);
             if d == 0 {
-                TestResult::discard()
+                None
             } else {
                 let mut r = 0;
-                let q = super::__divmoddi4(n, d, &mut r);
-
-                match gcc_s::divmoddi4() {
-                    Some(divmoddi4) if rand::random() => {
-                        let mut gcc_s_r = 0;
-                        let gcc_s_q = unsafe {
-                            divmoddi4(n, d, &mut gcc_s_r)
-                        };
-
-                        TestResult::from_bool(q == gcc_s_q && r == gcc_s_r)
-                    },
-                    _ => TestResult::from_bool(q == n / d && r == n % d),
-                }
+                let q = f(n, d, &mut r);
+                Some((q, r))
             }
         }
 
-        fn divsi3(n: U32, d: U32) -> TestResult {
+        fn __divsi3(f: extern fn(i32, i32) -> i32,
+                    n: U32,
+                    d: U32) -> Option<i32> {
             let (n, d) = (n.0 as i32, d.0 as i32);
             if d == 0 {
-                TestResult::discard()
+                None
             } else {
-                let q = super::__divsi3(n, d);
-
-                match gcc_s::divsi3() {
-                    Some(divsi3) if rand::random() => {
-                        TestResult::from_bool(q == unsafe { divsi3(n, d)})
-                    },
-                    _ => TestResult::from_bool(q == n / d),
-                }
+                Some(f(n, d))
             }
         }
 
-        fn modsi3(n: U32, d: U32) -> TestResult {
+        fn __modsi3(f: extern fn(i32, i32) -> i32,
+                    n: U32,
+                    d: U32) -> Option<i32> {
             let (n, d) = (n.0 as i32, d.0 as i32);
             if d == 0 {
-                TestResult::discard()
+                None
             } else {
-                let r = super::__modsi3(n, d);
-
-                match gcc_s::modsi3() {
-                    Some(modsi3) if rand::random() => {
-                        TestResult::from_bool(r == unsafe { modsi3(n, d) })
-                    },
-                    _ => TestResult::from_bool(r == n % d),
-                }
+                Some(f(n, d))
             }
         }
 
-        fn divmodsi4(n: U32, d: U32) -> TestResult {
+        fn __divmodsi4(f: extern fn(i32, i32, &mut i32) -> i32,
+                       n: U32,
+                       d: U32) -> Option<(i32, i32)> {
             let (n, d) = (n.0 as i32, d.0 as i32);
             if d == 0 {
-                TestResult::discard()
+                None
             } else {
                 let mut r = 0;
-                let q = super::__divmodsi4(n, d, &mut r);
-
-                match gcc_s::divmodsi4() {
-                    Some(divmodsi4) if rand::random() => {
-                        let mut gcc_s_r = 0;
-                        let gcc_s_q = unsafe {
-                            divmodsi4(n, d, &mut gcc_s_r)
-                        };
-
-                        TestResult::from_bool(q == gcc_s_q && r == gcc_s_r)
-                    },
-                    _ => TestResult::from_bool(q == n / d && r == n % d),
-                }
+                let q = f(n, d, &mut r);
+                Some((q, r))
             }
         }
     }

+ 10 - 35
src/int/shift.rs

@@ -62,57 +62,32 @@ lshr!(__lshrdi3: u64);
 mod tests {
     use qc::{I64, U64};
 
-    use gcc_s;
-    use quickcheck::TestResult;
-    use rand;
-
     // NOTE We purposefully stick to `u32` for `b` here because we want "small" values (b < 64)
-    quickcheck! {
-        fn ashldi(a: U64, b: u32) -> TestResult {
+    check! {
+        fn __ashldi3(f: extern fn(u64, u32) -> u64, a: U64, b: u32) -> Option<u64> {
             let a = a.0;
             if b >= 64 {
-                TestResult::discard()
+                None
             } else {
-                let r = super::__ashldi3(a, b);
-
-                match gcc_s::ashldi3() {
-                    Some(ashldi3) if rand::random() => {
-                        TestResult::from_bool(r == unsafe { ashldi3(a, b) })
-                    },
-                    _ => TestResult::from_bool(r == a << b),
-                }
+                Some(f(a, b))
             }
         }
 
-        fn ashrdi(a: I64, b: u32) -> TestResult {
+        fn __ashrdi3(f: extern fn(i64, u32) -> i64, a: I64, b: u32) -> Option<i64> {
             let a = a.0;
             if b >= 64 {
-                TestResult::discard()
+                None
             } else {
-                let r = super::__ashrdi3(a, b);
-
-                match gcc_s::ashrdi3() {
-                    Some(ashrdi3) if rand::random() => {
-                        TestResult::from_bool(r == unsafe { ashrdi3(a, b) })
-                    },
-                    _ => TestResult::from_bool(r == a >> b),
-                }
+                Some(f(a, b))
             }
         }
 
-        fn lshrdi(a: U64, b: u32) -> TestResult {
+        fn __lshrdi3(f: extern fn(u64, u32) -> u64, a: U64, b: u32) -> Option<u64> {
             let a = a.0;
             if b >= 64 {
-                TestResult::discard()
+                None
             } else {
-                let r = super::__lshrdi3(a, b);
-
-                match gcc_s::lshrdi3() {
-                    Some(lshrdi3) if rand::random() => {
-                        TestResult::from_bool(r == unsafe { lshrdi3(a, b) })
-                    },
-                    _ => TestResult::from_bool(r == a >> b),
-                }
+                Some(f(a, b))
             }
         }
     }

+ 25 - 75
src/int/udiv.rs

@@ -230,116 +230,66 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
 mod tests {
     use qc::{U32, U64};
 
-    use gcc_s;
-    use quickcheck::TestResult;
-    use rand;
-
-    quickcheck!{
-        fn udivdi3(n: U64, d: U64) -> TestResult {
+    check! {
+        fn __udivdi3(f: extern fn(u64, u64) -> u64, n: U64, d: U64) -> Option<u64> {
             let (n, d) = (n.0, d.0);
             if d == 0 {
-                TestResult::discard()
+                None
             } else {
-                let q = super::__udivdi3(n, d);
-
-                match gcc_s::udivdi3() {
-                    Some(udivdi3) if rand::random() => {
-                        TestResult::from_bool(q == unsafe { udivdi3(n, d) })
-                    },
-                    _ => TestResult::from_bool(q == n / d),
-                }
+                Some(f(n, d))
             }
         }
 
-        fn umoddi3(n: U64, d: U64) -> TestResult {
+        fn __umoddi3(f: extern fn(u64, u64) -> u64, n: U64, d: U64) -> Option<u64> {
             let (n, d) = (n.0, d.0);
             if d == 0 {
-                TestResult::discard()
+                None
             } else {
-                let r = super::__umoddi3(n, d);
-
-                match gcc_s::umoddi3() {
-                    Some(umoddi3) if rand::random() => {
-                        TestResult::from_bool(r == unsafe { umoddi3(n, d) })
-                    },
-                    _ => TestResult::from_bool(r == n % d),
-                }
+                Some(f(n, d))
             }
         }
 
-        fn udivmoddi4(n: U64, d: U64) -> TestResult {
+        fn __udivmoddi4(f: extern fn(u64, u64, Option<&mut u64>) -> u64,
+                        n: U64,
+                        d: U64) -> Option<(u64, u64)> {
             let (n, d) = (n.0, d.0);
             if d == 0 {
-                TestResult::discard()
+                None
             } else {
                 let mut r = 0;
-                let q = super::__udivmoddi4(n, d, Some(&mut r));
-
-                match gcc_s::udivmoddi4() {
-                    Some(udivmoddi4) if rand::random() => {
-                        let mut gcc_s_r = 0;
-                        let gcc_s_q = unsafe {
-                            udivmoddi4(n, d, Some(&mut gcc_s_r))
-                        };
-
-                        TestResult::from_bool(q == gcc_s_q && r == gcc_s_r)
-                    },
-                    _ => TestResult::from_bool(q == n / d && r == n % d),
-                }
+                let q = f(n, d, Some(&mut r));
+                Some((q, r))
             }
         }
 
-        fn udivsi3(n: U32, d: U32) -> TestResult {
+        fn __udivsi3(f: extern fn(u32, u32) -> u32, n: U32, d: U32) -> Option<u32> {
             let (n, d) = (n.0, d.0);
             if d == 0 {
-                TestResult::discard()
+                None
             } else {
-                let q = super::__udivsi3(n, d);
-
-                match gcc_s::udivsi3() {
-                    Some(udivsi3) if rand::random() => {
-                        TestResult::from_bool(q == unsafe { udivsi3(n, d) })
-                    },
-                    _ => TestResult::from_bool(q == n / d),
-                }
+                Some(f(n, d))
             }
         }
 
-        fn umodsi3(n: U32, d: U32) -> TestResult {
+        fn __umodsi3(f: extern fn(u32, u32) -> u32, n: U32, d: U32) -> Option<u32> {
             let (n, d) = (n.0, d.0);
             if d == 0 {
-                TestResult::discard()
+                None
             } else {
-                let r = super::__umodsi3(n, d);
-
-                match gcc_s::umodsi3() {
-                    Some(umodsi3) if rand::random() => {
-                        TestResult::from_bool(r == unsafe { umodsi3(n, d) })
-                    },
-                    _ => TestResult::from_bool(r == n % d),
-                }
+                Some(f(n, d))
             }
         }
 
-        fn udivmodsi4(n: U32, d: U32) -> TestResult {
+        fn __udivmodsi4(f: extern fn(u32, u32, Option<&mut u32>) -> u32,
+                        n: U32,
+                        d: U32) -> Option<(u32, u32)> {
             let (n, d) = (n.0, d.0);
             if d == 0 {
-                TestResult::discard()
+                None
             } else {
                 let mut r = 0;
-                let q = super::__udivmodsi4(n, d, Some(&mut r));
-
-                match gcc_s::udivmodsi4() {
-                    Some(udivmodsi4) if rand::random() => {
-                        let mut gcc_s_r = 0;
-                        let gcc_s_q = unsafe {
-                            udivmodsi4(n, d, Some(&mut gcc_s_r))
-                        };
-
-                        TestResult::from_bool(q == gcc_s_q && r == gcc_s_r)
-                    },
-                    _ => TestResult::from_bool(q == n / d && r == n % d),
-                }
+                let q = f(n, d, Some(&mut r));
+                Some((q, r))
             }
         }
     }

+ 7 - 4
src/lib.rs

@@ -20,12 +20,19 @@ extern crate core;
 #[cfg(test)]
 extern crate gcc_s;
 
+#[cfg(test)]
+extern crate compiler_rt;
+
 #[cfg(test)]
 extern crate rand;
 
 #[cfg(feature = "weak")]
 extern crate rlibc;
 
+#[cfg(test)]
+#[macro_use]
+mod qc;
+
 pub mod int;
 pub mod float;
 
@@ -34,7 +41,3 @@ pub mod arm;
 
 #[cfg(target_arch = "x86_64")]
 pub mod x86_64;
-
-#[cfg(test)]
-mod qc;
-

+ 121 - 0
src/qc.rs

@@ -142,3 +142,124 @@ macro_rules! arbitrary_large {
 
 arbitrary_large!(I64: i64);
 arbitrary_large!(U64: u64);
+
+// 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 {
+    ($(
+        fn $name:ident($f:ident: extern fn($($farg:ty),*) -> $fret:ty,
+                       $($arg:ident: $t:ty),*)
+                       -> Option<$ret:ty>
+        {
+            $($code:tt)*
+        }
+    )*) => (
+        $(
+            fn $name($f: extern fn($($farg),*) -> $fret,
+                     $($arg: $t),*) -> Option<$ret> {
+                $($code)*
+            }
+        )*
+
+        mod _compiler_rt {
+            use qc::*;
+            use std::mem;
+            use quickcheck::TestResult;
+
+            $(
+                #[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));
+                        unsafe {
+                            let compiler_rt_answer =
+                                super::$name(mem::transmute(compiler_rt_fn),
+                                             $($arg),*);
+                            match (my_answer, compiler_rt_answer) {
+                                (None, _) | (_, None) => TestResult::discard(),
+                                (Some(a), Some(b)) => {
+                                    TestResult::from_bool(a == b)
+                                }
+                            }
+                        }
+                    }
+
+                    ::quickcheck::quickcheck(my_check as fn($($t),*) -> TestResult)
+                }
+            )*
+        }
+
+        mod _gcc_s {
+            use qc::*;
+            use std::mem;
+            use quickcheck::TestResult;
+
+            $(
+                #[test]
+                fn $name() {
+                    fn my_check($($arg:$t),*) -> TestResult {
+                        let my_answer = super::$name(super::super::$name,
+                                                     $($arg),*);
+                        let gcc_s_fn = ::gcc_s::get(stringify!($name)).unwrap();
+                        unsafe {
+                            let gcc_s_answer =
+                                super::$name(mem::transmute(gcc_s_fn),
+                                             $($arg),*);
+                            match (my_answer, gcc_s_answer) {
+                                (None, _) | (_, None) => TestResult::discard(),
+                                (Some(a), Some(b)) => {
+                                    TestResult::from_bool(a == b)
+                                }
+                            }
+                        }
+                    }
+
+                    // If it's not in libgcc, or we couldn't find libgcc, then
+                    // just ignore this. We should have tests through
+                    // compiler-rt in any case
+                    if ::gcc_s::get(stringify!($name)).is_none() {
+                        return
+                    }
+
+                    ::quickcheck::quickcheck(my_check as fn($($t),*) -> TestResult)
+                }
+            )*
+        }
+    )
+}

+ 2 - 0
src/x86_64.rs

@@ -1,3 +1,5 @@
+#![allow(unused_imports)]
+
 use core::intrinsics;
 
 // NOTE These functions are implemented using assembly because they using a custom