From 8e161a791a895a63adbd6e292e2ddaf082a2b35c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 26 Sep 2016 22:22:10 -0700 Subject: [PATCH] Expand and refactor teting infrastructure This commit moves over most of the testing infrastructure to in-tree docker images that are all dispatched to from Travis (no other test configuration). This allows versioning modifications to the test infrastructure as well as the code itself. Additionally separate docker images allows for easy modification of one without worrying about tampering of others as well as easy addition of new targets by simply adding a new `Dockerfile`. Additionally this commit bundles the master version of the `compiler-rt` source repository from `llvm-mirror/compiler-rt` to test against. The compiler-rt library itself is compiled as a `cdylib` which is then dynamically located at runtime and we look for symbols in. There's a few hoops here, but they currently get the job done. All tests now execute against both gcc_s and compiler-rt, and this testing strategy is now all hidden behind a macro as well (refactoring all existing tests along the way). --- .gitmodules | 3 + .travis.yml | 65 +++++----- Cargo.toml | 5 +- README.md | 6 +- appveyor.yml | 1 + build.rs | 1 + .../aarch64-unknown-linux-gnu/Dockerfile | 10 ++ .../arm-unknown-linux-gnueabi/Dockerfile | 10 ++ .../arm-unknown-linux-gnueabihf/Dockerfile | 9 ++ .../armv7-unknown-linux-gnueabihf/Dockerfile | 9 ++ ci/docker/i586-unknown-linux-gnu/Dockerfile | 5 + ci/docker/i686-unknown-linux-gnu/Dockerfile | 5 + ci/docker/mips-unknown-linux-gnu/Dockerfile | 12 ++ ci/docker/mipsel-unknown-linux-gnu/Dockerfile | 12 ++ .../powerpc-unknown-linux-gnu/Dockerfile | 12 ++ .../powerpc64-unknown-linux-gnu/Dockerfile | 13 ++ .../powerpc64le-unknown-linux-gnu/Dockerfile | 13 ++ ci/docker/x86_64-unknown-linux-gnu/Dockerfile | 6 + ci/env.sh | 51 -------- ci/install.sh | 53 -------- ci/run-docker.sh | 29 +++++ ci/script.sh | 80 ------------ compiler-rt/Cargo.toml | 8 ++ compiler-rt/compiler-rt-cdylib/Cargo.toml | 11 ++ compiler-rt/compiler-rt-cdylib/build.rs | 68 ++++++++++ compiler-rt/compiler-rt-cdylib/compiler-rt | 1 + compiler-rt/compiler-rt-cdylib/src/lib.rs | 60 +++++++++ compiler-rt/src/lib.rs | 35 +++++ gcc_s/src/lib.rs | 39 +----- src/float/add.rs | 71 +++++----- src/float/mod.rs | 6 +- src/int/mul.rs | 58 +++------ src/int/sdiv.rs | 104 +++++---------- src/int/shift.rs | 45 ++----- src/int/udiv.rs | 100 ++++----------- src/lib.rs | 11 +- src/qc.rs | 121 ++++++++++++++++++ src/x86_64.rs | 2 + 38 files changed, 624 insertions(+), 526 deletions(-) create mode 100644 .gitmodules create mode 100644 ci/docker/aarch64-unknown-linux-gnu/Dockerfile create mode 100644 ci/docker/arm-unknown-linux-gnueabi/Dockerfile create mode 100644 ci/docker/arm-unknown-linux-gnueabihf/Dockerfile create mode 100644 ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile create mode 100644 ci/docker/i586-unknown-linux-gnu/Dockerfile create mode 100644 ci/docker/i686-unknown-linux-gnu/Dockerfile create mode 100644 ci/docker/mips-unknown-linux-gnu/Dockerfile create mode 100644 ci/docker/mipsel-unknown-linux-gnu/Dockerfile create mode 100644 ci/docker/powerpc-unknown-linux-gnu/Dockerfile create mode 100644 ci/docker/powerpc64-unknown-linux-gnu/Dockerfile create mode 100644 ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile create mode 100644 ci/docker/x86_64-unknown-linux-gnu/Dockerfile delete mode 100644 ci/env.sh delete mode 100644 ci/install.sh create mode 100644 ci/run-docker.sh delete mode 100644 ci/script.sh create mode 100644 compiler-rt/Cargo.toml create mode 100644 compiler-rt/compiler-rt-cdylib/Cargo.toml create mode 100644 compiler-rt/compiler-rt-cdylib/build.rs create mode 160000 compiler-rt/compiler-rt-cdylib/compiler-rt create mode 100644 compiler-rt/compiler-rt-cdylib/src/lib.rs create mode 100644 compiler-rt/src/lib.rs diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..fe501bb --- /dev/null +++ b/.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 diff --git a/.travis.yml b/.travis.yml index 74f3a08..47252a4 100644 --- a/.travis.yml +++ b/.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: diff --git a/Cargo.toml b/Cargo.toml index ae28057..6e75a11 100644 --- a/Cargo.toml +++ b/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"] diff --git a/README.md b/README.md index edccd30..28d8604 100644 --- a/README.md +++ b/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 diff --git a/appveyor.yml b/appveyor.yml index b34efc2..0e64977 100644 --- a/appveyor.yml +++ b/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 diff --git a/build.rs b/build.rs index 298b656..3ef6c28 100644 --- a/build.rs +++ b/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"); } diff --git a/ci/docker/aarch64-unknown-linux-gnu/Dockerfile b/ci/docker/aarch64-unknown-linux-gnu/Dockerfile new file mode 100644 index 0000000..6a68154 --- /dev/null +++ b/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 diff --git a/ci/docker/arm-unknown-linux-gnueabi/Dockerfile b/ci/docker/arm-unknown-linux-gnueabi/Dockerfile new file mode 100644 index 0000000..36efcd7 --- /dev/null +++ b/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 + diff --git a/ci/docker/arm-unknown-linux-gnueabihf/Dockerfile b/ci/docker/arm-unknown-linux-gnueabihf/Dockerfile new file mode 100644 index 0000000..83e7e52 --- /dev/null +++ b/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 diff --git a/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile b/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile new file mode 100644 index 0000000..adc15a5 --- /dev/null +++ b/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 diff --git a/ci/docker/i586-unknown-linux-gnu/Dockerfile b/ci/docker/i586-unknown-linux-gnu/Dockerfile new file mode 100644 index 0000000..63450ff --- /dev/null +++ b/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 diff --git a/ci/docker/i686-unknown-linux-gnu/Dockerfile b/ci/docker/i686-unknown-linux-gnu/Dockerfile new file mode 100644 index 0000000..63450ff --- /dev/null +++ b/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 diff --git a/ci/docker/mips-unknown-linux-gnu/Dockerfile b/ci/docker/mips-unknown-linux-gnu/Dockerfile new file mode 100644 index 0000000..3281524 --- /dev/null +++ b/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 diff --git a/ci/docker/mipsel-unknown-linux-gnu/Dockerfile b/ci/docker/mipsel-unknown-linux-gnu/Dockerfile new file mode 100644 index 0000000..088c45f --- /dev/null +++ b/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 diff --git a/ci/docker/powerpc-unknown-linux-gnu/Dockerfile b/ci/docker/powerpc-unknown-linux-gnu/Dockerfile new file mode 100644 index 0000000..542afa2 --- /dev/null +++ b/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 diff --git a/ci/docker/powerpc64-unknown-linux-gnu/Dockerfile b/ci/docker/powerpc64-unknown-linux-gnu/Dockerfile new file mode 100644 index 0000000..f4559c7 --- /dev/null +++ b/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 diff --git a/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile b/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile new file mode 100644 index 0000000..9a77788 --- /dev/null +++ b/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 diff --git a/ci/docker/x86_64-unknown-linux-gnu/Dockerfile b/ci/docker/x86_64-unknown-linux-gnu/Dockerfile new file mode 100644 index 0000000..a5704bb --- /dev/null +++ b/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 + diff --git a/ci/env.sh b/ci/env.sh deleted file mode 100644 index 6275107..0000000 --- a/ci/env.sh +++ /dev/null @@ -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 diff --git a/ci/install.sh b/ci/install.sh deleted file mode 100644 index 86095e3..0000000 --- a/ci/install.sh +++ /dev/null @@ -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 diff --git a/ci/run-docker.sh b/ci/run-docker.sh new file mode 100644 index 0000000..8ef024e --- /dev/null +++ b/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 diff --git a/ci/script.sh b/ci/script.sh deleted file mode 100644 index 9dffac5..0000000 --- a/ci/script.sh +++ /dev/null @@ -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 diff --git a/compiler-rt/Cargo.toml b/compiler-rt/Cargo.toml new file mode 100644 index 0000000..cac8773 --- /dev/null +++ b/compiler-rt/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "compiler-rt" +version = "0.1.0" +authors = ["Alex Crichton "] + +[dependencies] +compiler-rt-cdylib = { path = "compiler-rt-cdylib" } +libloading = "0.3" diff --git a/compiler-rt/compiler-rt-cdylib/Cargo.toml b/compiler-rt/compiler-rt-cdylib/Cargo.toml new file mode 100644 index 0000000..3c64d23 --- /dev/null +++ b/compiler-rt/compiler-rt-cdylib/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "compiler-rt-cdylib" +version = "0.1.0" +authors = ["Alex Crichton "] +build = "build.rs" + +[lib] +crate-type = ["cdylib"] + +[build-dependencies] +gcc = "0.3" diff --git a/compiler-rt/compiler-rt-cdylib/build.rs b/compiler-rt/compiler-rt-cdylib/build.rs new file mode 100644 index 0000000..b9b56a6 --- /dev/null +++ b/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"); +} diff --git a/compiler-rt/compiler-rt-cdylib/compiler-rt b/compiler-rt/compiler-rt-cdylib/compiler-rt new file mode 160000 index 0000000..515106e --- /dev/null +++ b/compiler-rt/compiler-rt-cdylib/compiler-rt @@ -0,0 +1 @@ +Subproject commit 515106ebc07227b85336816ef77bc39506b8fd26 diff --git a/compiler-rt/compiler-rt-cdylib/src/lib.rs b/compiler-rt/compiler-rt-cdylib/src/lib.rs new file mode 100644 index 0000000..5758483 --- /dev/null +++ b/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() {} diff --git a/compiler-rt/src/lib.rs b/compiler-rt/src/lib.rs new file mode 100644 index 0000000..ca99f39 --- /dev/null +++ b/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 = 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() + } +} diff --git a/gcc_s/src/lib.rs b/gcc_s/src/lib.rs index fa034b0..e3417ff 100644 --- a/gcc_s/src/lib.rs +++ b/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 $o> { - None - } - } +pub fn get(_sym: &str) -> Option { + None } #[cfg(not(windows))] -macro_rules! declare { - ($symbol:ident: fn($($i:ty),+) -> $o:ty) => { - pub fn $symbol() -> Option $o> { - unsafe { - gcc_s().get(concat!("__", stringify!($symbol)).as_bytes()).ok().map(|s| *s) - } - } +pub fn get(sym: &str) -> Option { + 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); diff --git a/src/float/add.rs b/src/float/add.rs index 7c3856a..1e7a651 100644 --- a/src/float/add.rs +++ b/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. - // 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 { - 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), - } - } - - fn adddf3(a: U64, b: U64) -> bool { - 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), + struct FRepr(F); + impl PartialEq for FRepr { + fn eq(&self, other: &FRepr) -> 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) + check! { + fn __addsf3(f: extern fn(f32, f32) -> f32, + a: U32, + b: U32) + -> Option > { + let (a, b) = (f32::from_repr(a.0), f32::from_repr(b.0)); + Some(FRepr(f(a, b))) + } + + fn __adddf3(f: extern fn(f64, f64) -> f64, + a: U64, + b: U64) -> Option > { + let (a, b) = (f64::from_repr(a.0), f64::from_repr(b.0)); + Some(FRepr(f(a, b))) + } + } + // More tests for special float values #[test] diff --git a/src/float/mod.rs b/src/float/mod.rs index bd9194b..cee2b73 100644 --- a/src/float/mod.rs +++ b/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) diff --git a/src/int/mul.rs b/src/int/mul.rs index dea3499..ea48ca9 100644 --- a/src/int/mul.rs +++ b/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 { + 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)) } } } diff --git a/src/int/sdiv.rs b/src/int/sdiv.rs index fcb9ed9..7a8e38e 100644 --- a/src/int/sdiv.rs +++ b/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 { 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 { 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 { 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 { 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)) } } } diff --git a/src/int/shift.rs b/src/int/shift.rs index bafc9db..4fc2391 100644 --- a/src/int/shift.rs +++ b/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 { 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 { 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 { 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)) } } } diff --git a/src/int/udiv.rs b/src/int/udiv.rs index 89793c8..2c2089a 100644 --- a/src/int/udiv.rs +++ b/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 { 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 { 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 { 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 { 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)) } } } diff --git a/src/lib.rs b/src/lib.rs index 7f2d391..bb51e40 100644 --- a/src/lib.rs +++ b/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; - diff --git a/src/qc.rs b/src/qc.rs index 8f102c0..1450a4e 100644 --- a/src/qc.rs +++ b/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) + } + )* + } + ) +} diff --git a/src/x86_64.rs b/src/x86_64.rs index 8bc3309..e38147a 100644 --- a/src/x86_64.rs +++ b/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