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).
This commit is contained in:
parent
c56a3f8a6e
commit
8e161a791a
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -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
|
65
.travis.yml
65
.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:
|
||||
|
@ -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"]
|
||||
|
@ -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
|
||||
|
@ -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
build.rs
1
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
ci/docker/aarch64-unknown-linux-gnu/Dockerfile
Normal file
10
ci/docker/aarch64-unknown-linux-gnu/Dockerfile
Normal file
@ -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
ci/docker/arm-unknown-linux-gnueabi/Dockerfile
Normal file
10
ci/docker/arm-unknown-linux-gnueabi/Dockerfile
Normal file
@ -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
ci/docker/arm-unknown-linux-gnueabihf/Dockerfile
Normal file
9
ci/docker/arm-unknown-linux-gnueabihf/Dockerfile
Normal file
@ -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
ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile
Normal file
9
ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile
Normal file
@ -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
ci/docker/i586-unknown-linux-gnu/Dockerfile
Normal file
5
ci/docker/i586-unknown-linux-gnu/Dockerfile
Normal file
@ -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
ci/docker/i686-unknown-linux-gnu/Dockerfile
Normal file
5
ci/docker/i686-unknown-linux-gnu/Dockerfile
Normal file
@ -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
ci/docker/mips-unknown-linux-gnu/Dockerfile
Normal file
12
ci/docker/mips-unknown-linux-gnu/Dockerfile
Normal file
@ -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
ci/docker/mipsel-unknown-linux-gnu/Dockerfile
Normal file
12
ci/docker/mipsel-unknown-linux-gnu/Dockerfile
Normal file
@ -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
ci/docker/powerpc-unknown-linux-gnu/Dockerfile
Normal file
12
ci/docker/powerpc-unknown-linux-gnu/Dockerfile
Normal file
@ -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
ci/docker/powerpc64-unknown-linux-gnu/Dockerfile
Normal file
13
ci/docker/powerpc64-unknown-linux-gnu/Dockerfile
Normal file
@ -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
ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile
Normal file
13
ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile
Normal file
@ -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
ci/docker/x86_64-unknown-linux-gnu/Dockerfile
Normal file
6
ci/docker/x86_64-unknown-linux-gnu/Dockerfile
Normal file
@ -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
|
||||
|
51
ci/env.sh
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
|
@ -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
ci/run-docker.sh
Normal file
29
ci/run-docker.sh
Normal file
@ -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
|
80
ci/script.sh
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
compiler-rt/Cargo.toml
Normal file
8
compiler-rt/Cargo.toml
Normal file
@ -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
compiler-rt/compiler-rt-cdylib/Cargo.toml
Normal file
11
compiler-rt/compiler-rt-cdylib/Cargo.toml
Normal file
@ -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
compiler-rt/compiler-rt-cdylib/build.rs
Normal file
68
compiler-rt/compiler-rt-cdylib/build.rs
Normal file
@ -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
compiler-rt/compiler-rt-cdylib/compiler-rt
Submodule
1
compiler-rt/compiler-rt-cdylib/compiler-rt
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 515106ebc07227b85336816ef77bc39506b8fd26
|
60
compiler-rt/compiler-rt-cdylib/src/lib.rs
Normal file
60
compiler-rt/compiler-rt-cdylib/src/lib.rs
Normal file
@ -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
compiler-rt/src/lib.rs
Normal file
35
compiler-rt/src/lib.rs
Normal file
@ -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()
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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>(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)
|
||||
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));
|
||||
Some(FRepr(f(a, b)))
|
||||
}
|
||||
|
||||
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));
|
||||
Some(FRepr(f(a, b)))
|
||||
}
|
||||
}
|
||||
|
||||
// More tests for special float values
|
||||
|
||||
#[test]
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
104
src/int/sdiv.rs
104
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
100
src/int/udiv.rs
100
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
src/lib.rs
11
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
src/qc.rs
121
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)
|
||||
}
|
||||
)*
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use core::intrinsics;
|
||||
|
||||
// NOTE These functions are implemented using assembly because they using a custom
|
||||
|
Loading…
Reference in New Issue
Block a user