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

Test with the 'c' feature enabled on CI
master
bors 2017-06-25 17:10:13 +00:00
commit 6024080570
25 changed files with 1341 additions and 1277 deletions

View File

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

View File

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

View File

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

View File

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

101
ci/run.sh
View File

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

View File

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

92
src/arm.rs Executable file → Normal file
View File

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

View File

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

263
src/float/conv.rs Executable file → Normal file
View File

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

View File

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

View File

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

147
src/int/mod.rs Executable file → Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

282
src/macros.rs Normal file
View File

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

View File

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

311
src/qc.rs
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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