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