Auto merge of #166 - alexcrichton:test-c, r=japaric
Test with the 'c' feature enabled on CI
This commit is contained in:
commit
6024080570
@ -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:
|
||||
|
10
Cargo.toml
10
Cargo.toml
@ -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]
|
||||
|
36
appveyor.yml
36
appveyor.yml
@ -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%
|
||||
|
64
build.rs
64
build.rs
@ -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
101
ci/run.sh
@ -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
|
||||
|
@ -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
92
src/arm.rs
Executable file → Normal 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);
|
||||
}
|
||||
|
344
src/float/add.rs
344
src/float/add.rs
@ -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
263
src/float/conv.rs
Executable file → Normal 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);
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
147
src/int/mod.rs
Executable file → Normal 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)
|
||||
}
|
||||
|
160
src/int/mul.rs
160
src/int/mul.rs
@ -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)
|
||||
}
|
||||
}
|
||||
|
158
src/int/sdiv.rs
158
src/int/sdiv.rs
@ -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))
|
||||
}
|
||||
}
|
||||
|
133
src/int/shift.rs
133
src/int/shift.rs
@ -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)
|
||||
}
|
||||
}
|
||||
|
285
src/int/udiv.rs
285
src/int/udiv.rs
@ -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 });
|
||||
|
71
src/lib.rs
71
src/lib.rs
@ -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
282
src/macros.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
}
|
@ -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
311
src/qc.rs
@ -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)
|
||||
}
|
||||
)*
|
||||
}
|
||||
)
|
||||
}
|
@ -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"));
|
||||
|
@ -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"));
|
||||
|
@ -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"));
|
||||
|
@ -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"));
|
||||
|
@ -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"));
|
||||
|
Loading…
Reference in New Issue
Block a user