mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Auto merge of #153866 - tgross35:update-builtins, r=tgross35
compiler-builtins subtree update Subtree update of `compiler-builtins` to https://github.com/rust-lang/compiler-builtins/commit/8173070e590c04a0d36ee9f3e0397c6006596976. Created using https://github.com/rust-lang/josh-sync. r? @ghost
This commit is contained in:
+9
-9
@@ -13,7 +13,7 @@ env:
|
||||
RUSTDOCFLAGS: -Dwarnings
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: full
|
||||
BENCHMARK_RUSTC: nightly-2025-12-01 # Pin the toolchain for reproducable results
|
||||
BENCHMARK_RUSTC: nightly-2026-02-10 # Pin the toolchain for reproducable results
|
||||
|
||||
jobs:
|
||||
# Determine which tests should be run based on changed files.
|
||||
@@ -70,14 +70,12 @@ jobs:
|
||||
os: ubuntu-24.04
|
||||
- target: powerpc64le-unknown-linux-gnu
|
||||
os: ubuntu-24.04
|
||||
# - target: powerpc64le-unknown-linux-gnu
|
||||
# os: ubuntu-24.04-ppc64le
|
||||
# # FIXME(rust#151807): remove once PPC builds work again.
|
||||
# channel: nightly-2026-01-23
|
||||
- target: powerpc64le-unknown-linux-gnu
|
||||
os: ubuntu-24.04-ppc64le
|
||||
- target: riscv64gc-unknown-linux-gnu
|
||||
os: ubuntu-24.04
|
||||
# - target: s390x-unknown-linux-gnu
|
||||
# os: ubuntu-24.04-s390x
|
||||
- target: s390x-unknown-linux-gnu
|
||||
os: ubuntu-24.04-s390x
|
||||
- target: thumbv6m-none-eabi
|
||||
os: ubuntu-24.04
|
||||
- target: thumbv7em-none-eabi
|
||||
@@ -289,7 +287,7 @@ jobs:
|
||||
path: ${{ env.BASELINE_NAME }}.tar.xz
|
||||
|
||||
- name: Run wall time benchmarks
|
||||
run: ./ci/bench-runtime.sh
|
||||
run: ./ci/bench-walltime.sh
|
||||
|
||||
- name: Print test logs if available
|
||||
if: always()
|
||||
@@ -303,7 +301,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust (rustup)
|
||||
run: rustup update nightly --no-self-update && rustup default nightly
|
||||
# FIXME(ci): not working in the 2026-02-11 nightly
|
||||
# https://rust-lang.zulipchat.com/#narrow/channel/269128-miri/topic/build-script-build.20contains.20outdated.20or.20invalid.20JSON/with/573426109
|
||||
run: rustup update nightly-2026-02-10 --no-self-update && rustup default nightly-2026-02-10
|
||||
shell: bash
|
||||
- run: rustup component add miri
|
||||
- run: cargo miri setup
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
# Rust files
|
||||
Cargo.lock
|
||||
target
|
||||
|
||||
# We have a couple of potential Cargo.lock files, but we only the root
|
||||
# workspace should have dependencies other than (optional) `cc`.
|
||||
Cargo.lock
|
||||
!/Cargo.lock
|
||||
|
||||
# Sources for external files
|
||||
compiler-rt
|
||||
*.tar.gz
|
||||
|
||||
@@ -0,0 +1,1655 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloca"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anes"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
|
||||
|
||||
[[package]]
|
||||
name = "assert_cmd"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c5bcfa8749ac45dd12cb11055aeeb6b27a3895560d60d71e3c23bf979e60514"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"bstr",
|
||||
"libc",
|
||||
"predicates",
|
||||
"predicates-core",
|
||||
"predicates-tree",
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "az"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be5eb007b7cacc6c660343e96f650fedf4b5a77512399eb952ca6642cf8d13f7"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "builtins-test"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"criterion",
|
||||
"gungraun",
|
||||
"paste",
|
||||
"rand_xoshiro",
|
||||
"rustc_apfloat",
|
||||
"test",
|
||||
"utest-cortex-m-qemu",
|
||||
"utest-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ciborium"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"ciborium-ll",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-io"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-ll"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"half",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.160"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3"
|
||||
dependencies = [
|
||||
"alloca",
|
||||
"anes",
|
||||
"cast",
|
||||
"ciborium",
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"itertools",
|
||||
"num-traits",
|
||||
"oorandom",
|
||||
"page_size",
|
||||
"plotters",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "difflib"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "escape8259"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"rand_core",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gmp-mpfr-sys"
|
||||
version = "1.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60f8970a75c006bb2f8ae79c6768a116dd215fa8346a87aed99bf9d82ca43394"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gungraun"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c1bbe46f51c63bc08a1fac0ee0c530a77c961613a86ecf828ab1b0ffc6687a"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"derive_more",
|
||||
"gungraun-macros",
|
||||
"gungraun-runner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gungraun-macros"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdccd089c36fb2ee66ef0eb7b1baa3ce7e7878a8eae682d9c8c368869ff6eca1"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gungraun-runner"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6da6487203fa53ae6b1c8fead642fe79a3199464b0dd1337635594d675a9ac05"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.18.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb"
|
||||
dependencies = [
|
||||
"console",
|
||||
"portable-atomic",
|
||||
"unit-prefix",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.16"
|
||||
dependencies = [
|
||||
"no-panic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||
|
||||
[[package]]
|
||||
name = "libm-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm-test"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"criterion",
|
||||
"getrandom",
|
||||
"gmp-mpfr-sys",
|
||||
"gungraun",
|
||||
"indicatif",
|
||||
"libm 0.2.16",
|
||||
"libm-macros",
|
||||
"libtest-mimic",
|
||||
"musl-math-sys",
|
||||
"paste",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
"rayon",
|
||||
"rug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtest-mimic"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5297962ef19edda4ce33aaa484386e0a5b3d7f2f4e037cbeee00503ef6b29d33"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap",
|
||||
"escape8259",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "musl-math-sys"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libm 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "no-panic"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f967505aabc8af5752d098c34146544a43684817cdba8f9725b292530cabbf53"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.38.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271638cd5fa9cca89c4c304675ca658efc4e64a66c716b7cfe1afb4b9611dbbc"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"memchr",
|
||||
"ruzstd",
|
||||
"wasmparser 0.243.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
||||
|
||||
[[package]]
|
||||
name = "page_size"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "panic-handler"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"plotters-backend",
|
||||
"plotters-svg",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plotters-backend"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
|
||||
|
||||
[[package]]
|
||||
name = "plotters-svg"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
|
||||
dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "3.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"difflib",
|
||||
"predicates-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "predicates-core"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144"
|
||||
|
||||
[[package]]
|
||||
name = "predicates-tree"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2"
|
||||
dependencies = [
|
||||
"predicates-core",
|
||||
"termtree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
|
||||
dependencies = [
|
||||
"chacha20",
|
||||
"getrandom",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e6af7f3e25ded52c41df4e0b1af2d047e45896c2f3281792ed68a1c243daedb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
|
||||
|
||||
[[package]]
|
||||
name = "rand_xoshiro"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f0b2cc7bfeef8f0320ca45f88b00157a03c67137022d59393614352d6bf4312"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
|
||||
|
||||
[[package]]
|
||||
name = "rug"
|
||||
version = "1.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de190ec858987c79cad4da30e19e546139b3339331282832af004d0ea7829639"
|
||||
dependencies = [
|
||||
"az",
|
||||
"gmp-mpfr-sys",
|
||||
"libc",
|
||||
"libm 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_apfloat"
|
||||
version = "0.2.3+llvm-462a31f5a5ab"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "486c2179b4796f65bfe2ee33679acf0927ac83ecf583ad6c91c3b4570911b9ad"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "ruzstd"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ff0cc5e135c8870a775d3320910cd9b564ec036b4dc0b8741629020be63f01"
|
||||
dependencies = [
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sc"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "010e18bd3bfd1d45a7e666b236c78720df0d9a7698ebaa9c1c559961eb60a38b"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "symbol-check"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"cc",
|
||||
"getopts",
|
||||
"object",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.116"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termtree"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
|
||||
|
||||
[[package]]
|
||||
name = "test"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/japaric/utest#e32073e2b078e3bee46001c13ae4c1acf368d762"
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "unit-prefix"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
|
||||
|
||||
[[package]]
|
||||
name = "utest-cortex-m-qemu"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/japaric/utest#e32073e2b078e3bee46001c13ae4c1acf368d762"
|
||||
dependencies = [
|
||||
"sc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utest-macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/japaric/utest#e32073e2b078e3bee46001c13ae4c1acf368d762"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "util"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libm 0.2.16",
|
||||
"libm-macros",
|
||||
"libm-test",
|
||||
"musl-math-sys",
|
||||
"rug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.2+wasi-0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip3"
|
||||
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser 0.244.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser 0.244.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.243.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6d8db401b0528ec316dfbe579e6ab4152d61739cfe076706d2009127970159d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-time"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||
dependencies = [
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser 0.244.0",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser 0.244.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
@@ -34,12 +34,14 @@ exclude = [
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.101"
|
||||
assert_cmd = "2.1.2"
|
||||
cc = "1.2.55"
|
||||
cc = "1.2.56"
|
||||
cfg-if = "1.0.4"
|
||||
compiler_builtins = { path = "builtins-shim", default-features = false }
|
||||
criterion = { version = "0.6.0", default-features = false, features = ["cargo_bench_support"] }
|
||||
getrandom = "0.3.4"
|
||||
criterion = { version = "0.8.2", default-features = false, features = ["cargo_bench_support"] }
|
||||
getopts = "0.2.24"
|
||||
getrandom = "0.4.1"
|
||||
gmp-mpfr-sys = { version = "1.6.8", default-features = false }
|
||||
gungraun = "0.17.0"
|
||||
gungraun = "0.17.2"
|
||||
heck = "0.5.0"
|
||||
indicatif = { version = "0.18.3", default-features = false }
|
||||
libm = { path = "libm", default-features = false }
|
||||
@@ -47,22 +49,22 @@ libm-macros = { path = "crates/libm-macros" }
|
||||
libm-test = { path = "libm-test", default-features = false }
|
||||
libtest-mimic = "0.8.1"
|
||||
musl-math-sys = { path = "crates/musl-math-sys" }
|
||||
no-panic = "0.1.35"
|
||||
object = { version = "0.37.3", features = ["wasm"] }
|
||||
no-panic = "0.1.36"
|
||||
object = { version = "0.38.1", features = ["wasm"] }
|
||||
panic-handler = { path = "crates/panic-handler" }
|
||||
paste = "1.0.15"
|
||||
proc-macro2 = "1.0.106"
|
||||
quote = "1.0.44"
|
||||
rand = "0.9.2"
|
||||
rand_chacha = "0.9.0"
|
||||
rand_xoshiro = "0.7"
|
||||
rand = "0.10.0"
|
||||
rand_chacha = "0.10.0"
|
||||
rand_xoshiro = "0.8"
|
||||
rayon = "1.11.0"
|
||||
regex = "1.12.3"
|
||||
rug = { version = "1.28.1", default-features = false, features = ["float", "integer", "std"] }
|
||||
rustc_apfloat = "0.2.3"
|
||||
serde_json = "1.0.149"
|
||||
syn = "2.0.114"
|
||||
tempfile = "3.24.0"
|
||||
syn = "2.0.115"
|
||||
tempfile = "3.25.0"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
|
||||
@@ -3,10 +3,8 @@
|
||||
// compiling a C implementation and forget to implement that intrinsic in Rust, this file will fail
|
||||
// to link due to the missing intrinsic (symbol).
|
||||
|
||||
#![allow(unused_features)]
|
||||
#![allow(internal_features)]
|
||||
#![allow(internal_features, unused_features)]
|
||||
#![deny(dead_code)]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(f128)]
|
||||
#![feature(f16)]
|
||||
#![feature(lang_items)]
|
||||
|
||||
@@ -16,11 +16,11 @@ rand_xoshiro.workspace = true
|
||||
# To compare float builtins against
|
||||
rustc_apfloat.workspace = true
|
||||
|
||||
# Really a dev dependency, but dev dependencies can't be optional
|
||||
# Really dev dependencies, but those can't be optional
|
||||
criterion = { workspace = true, optional = true }
|
||||
gungraun = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion.workspace = true
|
||||
paste.workspace = true
|
||||
|
||||
[target.'cfg(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")), target_os = "linux"))'.dev-dependencies]
|
||||
@@ -46,8 +46,10 @@ no-sys-f16 = ["no-sys-f16-f64-convert"]
|
||||
# Enable icount benchmarks (requires gungraun-runner and valgrind locally)
|
||||
icount = ["dep:gungraun"]
|
||||
|
||||
# Enable report generation without bringing in more dependencies by default
|
||||
benchmarking-reports = ["criterion/plotters", "criterion/html_reports"]
|
||||
# Config for wall time benchmarks. Plotters and html_reports bring in extra
|
||||
# deps so are off by default for CI.
|
||||
benchmarking-reports = ["walltime", "criterion/plotters", "criterion/html_reports"]
|
||||
walltime = ["dep:criterion"]
|
||||
|
||||
# NOTE: benchmarks must be run with `--no-default-features` or with
|
||||
# `-p builtins-test`, otherwise the default `compiler-builtins` feature
|
||||
@@ -57,38 +59,47 @@ benchmarking-reports = ["criterion/plotters", "criterion/html_reports"]
|
||||
[[bench]]
|
||||
name = "float_add"
|
||||
harness = false
|
||||
required-features = ["walltime"]
|
||||
|
||||
[[bench]]
|
||||
name = "float_sub"
|
||||
harness = false
|
||||
required-features = ["walltime"]
|
||||
|
||||
[[bench]]
|
||||
name = "float_mul"
|
||||
harness = false
|
||||
required-features = ["walltime"]
|
||||
|
||||
[[bench]]
|
||||
name = "float_div"
|
||||
harness = false
|
||||
required-features = ["walltime"]
|
||||
|
||||
[[bench]]
|
||||
name = "float_cmp"
|
||||
harness = false
|
||||
required-features = ["walltime"]
|
||||
|
||||
[[bench]]
|
||||
name = "float_conv"
|
||||
harness = false
|
||||
required-features = ["walltime"]
|
||||
|
||||
[[bench]]
|
||||
name = "float_extend"
|
||||
harness = false
|
||||
required-features = ["walltime"]
|
||||
|
||||
[[bench]]
|
||||
name = "float_trunc"
|
||||
harness = false
|
||||
required-features = ["walltime"]
|
||||
|
||||
[[bench]]
|
||||
name = "float_pow"
|
||||
harness = false
|
||||
required-features = ["walltime"]
|
||||
|
||||
[[bench]]
|
||||
name = "mem_icount"
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
use compiler_builtins::float::Float;
|
||||
use compiler_builtins::int::{Int, MinInt};
|
||||
use rand_xoshiro::Xoshiro128StarStar;
|
||||
use rand_xoshiro::rand_core::{RngCore, SeedableRng};
|
||||
use rand_xoshiro::rand_core::{Rng, SeedableRng};
|
||||
|
||||
/// Sets the number of fuzz iterations run for most tests. In practice, the vast majority of bugs
|
||||
/// are caught by the edge case testers. Most of the remaining bugs triggered by more complex
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused_macros, unused_features)]
|
||||
#![cfg_attr(f16_enabled, feature(f16))]
|
||||
#![cfg_attr(f128_enabled, feature(f128))]
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#![cfg_attr(f128_enabled, feature(f128))]
|
||||
#![cfg_attr(f16_enabled, feature(f16))]
|
||||
// makes configuration easier
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused_features)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_macros)]
|
||||
|
||||
use builtins_test::*;
|
||||
use compiler_builtins::float::Float;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![feature(f128)]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused_macros, unused_features)]
|
||||
|
||||
use builtins_test::*;
|
||||
use compiler_builtins::int::sdiv::{__divmoddi4, __divmodsi4, __divmodti4};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused_macros, unused_features)]
|
||||
#![cfg_attr(f128_enabled, feature(f128))]
|
||||
|
||||
#[cfg_attr(x86_no_sse, allow(unused))]
|
||||
|
||||
@@ -1,30 +1,71 @@
|
||||
#![allow(unused_features)]
|
||||
#![feature(decl_macro)] // so we can use pub(super)
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![cfg(all(target_arch = "aarch64", target_os = "linux"))]
|
||||
#![cfg(all(target_arch = "aarch64", feature = "mangled-names"))]
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
use compiler_builtins::aarch64_outline_atomics::{get_have_lse_atomics, set_have_lse_atomics};
|
||||
use compiler_builtins::int::{Int, MinInt};
|
||||
use compiler_builtins::{foreach_bytes, foreach_ordering};
|
||||
|
||||
#[track_caller]
|
||||
fn with_maybe_lse_atomics(use_lse: bool, f: impl FnOnce()) {
|
||||
// Ensure tests run in parallel don't interleave global settings
|
||||
static LOCK: Mutex<()> = Mutex::new(());
|
||||
let _g = LOCK.lock().unwrap();
|
||||
let old = get_have_lse_atomics();
|
||||
// safety: as the caller of the unsafe fn `set_have_lse_atomics`, we
|
||||
// have to ensure the CPU supports LSE. This is why we make this assertion.
|
||||
if use_lse || old {
|
||||
assert!(std::arch::is_aarch64_feature_detected!("lse"));
|
||||
}
|
||||
unsafe { set_have_lse_atomics(use_lse) };
|
||||
f();
|
||||
unsafe { set_have_lse_atomics(old) };
|
||||
}
|
||||
|
||||
pub fn run_fuzz_tests_with_lse_variants<I: Int, F: Fn(I, I) + Copy>(n: u32, f: F)
|
||||
where
|
||||
<I as MinInt>::Unsigned: Int,
|
||||
{
|
||||
// We use `fuzz_2` because our subject function `f` requires two inputs
|
||||
let test_fn = || {
|
||||
builtins_test::fuzz_2(n, f);
|
||||
};
|
||||
// Always run without LSE
|
||||
with_maybe_lse_atomics(false, test_fn);
|
||||
|
||||
// Conditionally run with LSE
|
||||
if std::arch::is_aarch64_feature_detected!("lse") {
|
||||
with_maybe_lse_atomics(true, test_fn);
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate a byte size to a Rust type.
|
||||
macro int_ty {
|
||||
(1) => { i8 },
|
||||
(2) => { i16 },
|
||||
(4) => { i32 },
|
||||
(8) => { i64 },
|
||||
(16) => { i128 }
|
||||
(1) => { u8 },
|
||||
(2) => { u16 },
|
||||
(4) => { u32 },
|
||||
(8) => { u64 },
|
||||
(16) => { u128 }
|
||||
}
|
||||
|
||||
mod cas {
|
||||
pub(super) macro test($_ordering:ident, $bytes:tt, $name:ident) {
|
||||
#[test]
|
||||
fn $name() {
|
||||
builtins_test::fuzz_2(10000, |expected: super::int_ty!($bytes), new| {
|
||||
crate::run_fuzz_tests_with_lse_variants(10000, |expected: super::int_ty!($bytes), new| {
|
||||
let mut target = expected.wrapping_add(10);
|
||||
let ret: super::int_ty!($bytes) = unsafe {
|
||||
compiler_builtins::aarch64_outline_atomics::$name::$name(
|
||||
expected,
|
||||
new,
|
||||
&mut target,
|
||||
)
|
||||
};
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
compiler_builtins::aarch64_outline_atomics::$name::$name(
|
||||
expected,
|
||||
new,
|
||||
&mut target,
|
||||
)
|
||||
},
|
||||
ret,
|
||||
expected.wrapping_add(10),
|
||||
"return value should always be the previous value",
|
||||
);
|
||||
@@ -35,15 +76,17 @@ fn $name() {
|
||||
);
|
||||
|
||||
target = expected;
|
||||
let ret: super::int_ty!($bytes) = unsafe {
|
||||
compiler_builtins::aarch64_outline_atomics::$name::$name(
|
||||
expected,
|
||||
new,
|
||||
&mut target,
|
||||
)
|
||||
};
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
compiler_builtins::aarch64_outline_atomics::$name::$name(
|
||||
expected,
|
||||
new,
|
||||
&mut target,
|
||||
)
|
||||
},
|
||||
expected
|
||||
ret,
|
||||
expected,
|
||||
"the new return value should always be the previous value (i.e. the first parameter passed to the function)",
|
||||
);
|
||||
assert_eq!(target, new, "should have updated target");
|
||||
});
|
||||
@@ -59,16 +102,21 @@ mod swap {
|
||||
pub(super) macro test($_ordering:ident, $bytes:tt, $name:ident) {
|
||||
#[test]
|
||||
fn $name() {
|
||||
builtins_test::fuzz_2(10000, |left: super::int_ty!($bytes), mut right| {
|
||||
let orig_right = right;
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
compiler_builtins::aarch64_outline_atomics::$name::$name(left, &mut right)
|
||||
},
|
||||
orig_right
|
||||
);
|
||||
assert_eq!(left, right);
|
||||
});
|
||||
crate::run_fuzz_tests_with_lse_variants(
|
||||
10000,
|
||||
|left: super::int_ty!($bytes), mut right| {
|
||||
let orig_right = right;
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
compiler_builtins::aarch64_outline_atomics::$name::$name(
|
||||
left, &mut right,
|
||||
)
|
||||
},
|
||||
orig_right
|
||||
);
|
||||
assert_eq!(left, right);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,7 +128,7 @@ mod $mod {
|
||||
($_ordering:ident, $bytes:tt, $name:ident) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
builtins_test::fuzz_2(10000, |old, val| {
|
||||
crate::run_fuzz_tests_with_lse_variants(10000, |old, val| {
|
||||
let mut target = old;
|
||||
let op: fn(super::int_ty!($bytes), super::int_ty!($bytes)) -> _ = $($op)*;
|
||||
let expected = op(old, val);
|
||||
@@ -98,7 +146,6 @@ fn $name() {
|
||||
test_op!(clr, |left, right| left & !right);
|
||||
test_op!(xor, std::ops::BitXor::bitxor);
|
||||
test_op!(or, std::ops::BitOr::bitor);
|
||||
use compiler_builtins::{foreach_bytes, foreach_ordering};
|
||||
compiler_builtins::foreach_cas!(cas::test);
|
||||
compiler_builtins::foreach_cas16!(test_cas16);
|
||||
compiler_builtins::foreach_swp!(swap::test);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![cfg_attr(f16_enabled, feature(f16))]
|
||||
#![cfg_attr(f128_enabled, feature(f128))]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused_macros, unused_features)]
|
||||
|
||||
use builtins_test::*;
|
||||
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@
|
||||
export LIBM_SEED=benchesbenchesbenchesbencheswoo!
|
||||
cargo bench --package libm-test \
|
||||
--no-default-features \
|
||||
--features short-benchmarks,build-musl,libm/force-soft-floats
|
||||
--features walltime,short-benchmarks,build-musl,libm/force-soft-floats
|
||||
@@ -11,7 +11,7 @@ import pprint
|
||||
import re
|
||||
import subprocess as sp
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from functools import cache
|
||||
from glob import glob
|
||||
from inspect import cleandoc
|
||||
@@ -19,8 +19,7 @@ from os import getenv
|
||||
from pathlib import Path
|
||||
from typing import TypedDict, Self
|
||||
|
||||
USAGE = cleandoc(
|
||||
"""
|
||||
USAGE = cleandoc("""
|
||||
usage:
|
||||
|
||||
./ci/ci-util.py <COMMAND> [flags]
|
||||
@@ -44,8 +43,7 @@ USAGE = cleandoc(
|
||||
Exit with success if the pull request contains a line starting with
|
||||
`ci: allow-regressions`, indicating that regressions in benchmarks should
|
||||
be accepted. Otherwise, exit 1.
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
REPO_ROOT = Path(__file__).parent.parent
|
||||
GIT = ["git", "-C", REPO_ROOT]
|
||||
@@ -73,17 +71,21 @@ def eprint(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
@dataclass(kw_only=True)
|
||||
class PrCfg:
|
||||
"""Directives that we allow in the commit body to control test behavior.
|
||||
|
||||
These are of the form `ci: foo`, at the start of a line.
|
||||
"""
|
||||
|
||||
# The PR body
|
||||
body: str
|
||||
# Skip regression checks (must be at the start of a line).
|
||||
allow_regressions: bool = False
|
||||
# Don't run extensive tests
|
||||
skip_extensive: bool = False
|
||||
# Add these extensive tests to the list
|
||||
extra_extensive: list[str] = field(default_factory=list, init=False)
|
||||
|
||||
# Allow running a large number of extensive tests. If not set, this script
|
||||
# will error out if a threshold is exceeded in order to avoid accidentally
|
||||
@@ -101,11 +103,17 @@ class PrCfg:
|
||||
DIR_SKIP_EXTENSIVE: str = "skip-extensive"
|
||||
DIR_ALLOW_MANY_EXTENSIVE: str = "allow-many-extensive"
|
||||
DIR_TEST_LIBM: str = "test-libm"
|
||||
DIR_EXTRA_EXTENSIVE: str = "extra-extensive"
|
||||
|
||||
def __init__(self, body: str):
|
||||
directives = re.finditer(r"^\s*ci:\s*(?P<dir_name>\S*)", body, re.MULTILINE)
|
||||
def __post_init__(self):
|
||||
directives = re.finditer(
|
||||
r"^\s*ci:\s*(?P<dir_name>[^\s=]*)(?:\s*=\s*(?P<args>.*))?",
|
||||
self.body,
|
||||
re.MULTILINE,
|
||||
)
|
||||
for dir in directives:
|
||||
name = dir.group("dir_name")
|
||||
args = dir.group("args")
|
||||
if name == self.DIR_ALLOW_REGRESSIONS:
|
||||
self.allow_regressions = True
|
||||
elif name == self.DIR_SKIP_EXTENSIVE:
|
||||
@@ -114,11 +122,18 @@ class PrCfg:
|
||||
self.allow_many_extensive = True
|
||||
elif name == self.DIR_TEST_LIBM:
|
||||
self.always_test_libm = True
|
||||
elif name == self.DIR_EXTRA_EXTENSIVE:
|
||||
self.extra_extensive = [x.strip() for x in args.split(",")]
|
||||
args = None
|
||||
else:
|
||||
eprint(f"Found unexpected directive `{name}`")
|
||||
exit(1)
|
||||
|
||||
pprint.pp(self)
|
||||
if args is not None:
|
||||
eprint("Found arguments where not expected")
|
||||
exit(1)
|
||||
|
||||
eprint(pprint.pformat(self))
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -158,7 +173,7 @@ class PrInfo:
|
||||
)
|
||||
pr_json = json.loads(pr_info)
|
||||
eprint("PR info:", json.dumps(pr_json, indent=4))
|
||||
return cls(**json.loads(pr_info), cfg=PrCfg(pr_json["body"]))
|
||||
return cls(**pr_json, cfg=PrCfg(body=pr_json["body"]))
|
||||
|
||||
|
||||
class FunctionDef(TypedDict):
|
||||
@@ -276,29 +291,35 @@ class Context:
|
||||
|
||||
skip_tests = False
|
||||
error_on_many_tests = False
|
||||
extra_tests = {}
|
||||
|
||||
pr = PrInfo.from_env()
|
||||
if pr is not None:
|
||||
skip_tests = pr.cfg.skip_extensive
|
||||
error_on_many_tests = not pr.cfg.allow_many_extensive
|
||||
for fn_name in pr.cfg.extra_extensive:
|
||||
extra_tests.setdefault(base_name(fn_name)[1], []).append(fn_name)
|
||||
|
||||
if skip_tests:
|
||||
eprint("Skipping all extensive tests")
|
||||
|
||||
changed = self.changed_routines()
|
||||
eprint(f"Changed: {changed}")
|
||||
|
||||
matrix = []
|
||||
total_to_test = 0
|
||||
|
||||
# Figure out which extensive tests need to run
|
||||
for ty in TYPES:
|
||||
ty_changed = changed.get(ty, [])
|
||||
ty_to_test = [] if skip_tests else ty_changed
|
||||
ty_to_test = [] if skip_tests else ty_changed.copy()
|
||||
ty_to_test.extend(extra_tests.get(ty, []))
|
||||
total_to_test += len(ty_to_test)
|
||||
|
||||
item = {
|
||||
"ty": ty,
|
||||
"changed": ",".join(ty_changed),
|
||||
"to_test": ",".join(ty_to_test),
|
||||
"changed": ",".join(sorted(set(ty_changed))),
|
||||
"to_test": ",".join(sorted(set(ty_to_test))),
|
||||
}
|
||||
|
||||
matrix.append(item)
|
||||
@@ -319,6 +340,33 @@ class Context:
|
||||
exit(1)
|
||||
|
||||
|
||||
def base_name(name: str) -> tuple[str, str]:
|
||||
"""Return the basename and type from a full function name. Keep in sync with Rust's
|
||||
`fn base_name`.
|
||||
"""
|
||||
known_mappings = [
|
||||
("erff", ("erf", "f32")),
|
||||
("erf", ("erf", "f64")),
|
||||
("modff", ("modf", "f32")),
|
||||
("modf", ("modf", "f64")),
|
||||
("lgammaf_r", ("lgamma_r", "f32")),
|
||||
("lgamma_r", ("lgamma_r", "f64")),
|
||||
]
|
||||
|
||||
found = next((base for (full, base) in known_mappings if full == name), None)
|
||||
if found is not None:
|
||||
return found
|
||||
|
||||
if name.endswith("f"):
|
||||
return (name.rstrip("f"), "f32")
|
||||
elif name.endswith("f16"):
|
||||
return (name.rstrip("f16"), "f16")
|
||||
elif name.endswith("f128"):
|
||||
return (name.rstrip("f128"), "f128")
|
||||
|
||||
return (name, "f64")
|
||||
|
||||
|
||||
def locate_baseline(flags: list[str]) -> None:
|
||||
"""Find the most recent baseline from CI, download it if specified.
|
||||
|
||||
|
||||
@@ -14,5 +14,9 @@ targets=(
|
||||
)
|
||||
for target in "${targets[@]}"; do
|
||||
# Only run the `mem` tests to avoid this taking too long.
|
||||
cargo miri test --manifest-path builtins-test/Cargo.toml --features no-asm --target "$target" -- mem
|
||||
cargo miri test \
|
||||
--manifest-path builtins-test/Cargo.toml \
|
||||
--features no-asm \
|
||||
--target "$target" \
|
||||
-- mem
|
||||
done
|
||||
|
||||
@@ -48,21 +48,24 @@ fi
|
||||
# build with the arguments we provide it, then validates the built artifacts.
|
||||
SYMCHECK_TEST_TARGET="$target" cargo test -p symbol-check --release
|
||||
symcheck=(cargo run -p symbol-check --release)
|
||||
symcheck+=(-- build-and-check)
|
||||
symcheck+=(-- --build-and-check --target "$target")
|
||||
|
||||
"${symcheck[@]}" "$target" -- -p compiler_builtins
|
||||
"${symcheck[@]}" "$target" -- -p compiler_builtins --release
|
||||
"${symcheck[@]}" "$target" -- -p compiler_builtins --features c
|
||||
"${symcheck[@]}" "$target" -- -p compiler_builtins --features c --release
|
||||
"${symcheck[@]}" "$target" -- -p compiler_builtins --features no-asm
|
||||
"${symcheck[@]}" "$target" -- -p compiler_builtins --features no-asm --release
|
||||
# Executable section checks are meaningless on no-std targets
|
||||
[[ "$target" == *"-none"* ]] && symcheck+=(--no-os)
|
||||
|
||||
"${symcheck[@]}" -- -p compiler_builtins
|
||||
"${symcheck[@]}" -- -p compiler_builtins --release
|
||||
"${symcheck[@]}" -- -p compiler_builtins --features c
|
||||
"${symcheck[@]}" -- -p compiler_builtins --features c --release
|
||||
"${symcheck[@]}" -- -p compiler_builtins --features no-asm
|
||||
"${symcheck[@]}" -- -p compiler_builtins --features no-asm --release
|
||||
|
||||
run_intrinsics_test() {
|
||||
build_args=(--verbose --manifest-path builtins-test-intrinsics/Cargo.toml)
|
||||
build_args+=("$@")
|
||||
|
||||
# symcheck also checks the results of builtins-test-intrinsics
|
||||
"${symcheck[@]}" "$target" -- "${build_args[@]}"
|
||||
"${symcheck[@]}" -- "${build_args[@]}"
|
||||
|
||||
# FIXME: we get access violations on Windows, our entrypoint may need to
|
||||
# be tweaked.
|
||||
|
||||
@@ -22,7 +22,7 @@ See `build.rs` for details.
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
See [CONTRIBUTING.md](../CONTRIBUTING.md).
|
||||
|
||||
## Progress
|
||||
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use core::intrinsics;
|
||||
|
||||
intrinsics! {
|
||||
#[unsafe(naked)]
|
||||
#[cfg(any(all(windows, target_env = "gnu"), target_os = "uefi"))]
|
||||
|
||||
@@ -34,14 +34,27 @@ pub extern "C" fn __rust_enable_lse() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Function to enable/disable LSE. To be used only for testing purposes.
|
||||
#[cfg(feature = "mangled-names")]
|
||||
pub unsafe fn set_have_lse_atomics(has_lse: bool) {
|
||||
let lse_flag = if has_lse { 1 } else { 0 };
|
||||
HAVE_LSE_ATOMICS.store(lse_flag, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Function to obtain whether LSE is enabled or not. To be used only for testing purposes.
|
||||
#[cfg(feature = "mangled-names")]
|
||||
pub fn get_have_lse_atomics() -> bool {
|
||||
HAVE_LSE_ATOMICS.load(Ordering::Relaxed) != 0
|
||||
}
|
||||
|
||||
/// Translate a byte size to a Rust type.
|
||||
#[rustfmt::skip]
|
||||
macro_rules! int_ty {
|
||||
(1) => { i8 };
|
||||
(2) => { i16 };
|
||||
(4) => { i32 };
|
||||
(8) => { i64 };
|
||||
(16) => { i128 };
|
||||
(1) => { u8 };
|
||||
(2) => { u16 };
|
||||
(4) => { u32 };
|
||||
(8) => { u64 };
|
||||
(16) => { u128 };
|
||||
}
|
||||
|
||||
/// Given a byte size and a register number, return a register of the appropriate size.
|
||||
@@ -135,18 +148,73 @@ macro_rules! stxp {
|
||||
};
|
||||
}
|
||||
|
||||
// The AArch64 assembly syntax for relocation specifiers
|
||||
// when accessing symbols changes depending on the target executable format.
|
||||
// In ELF (used in Linux), we have a prefix notation surrounded by colons (:specifier:sym),
|
||||
// while in Mach-O object files (used in MacOS), a postfix notation is used (sym@specifier).
|
||||
|
||||
/// AArch64 ELF position-independent addressing:
|
||||
///
|
||||
/// adrp xN, symbol
|
||||
/// add xN, xN, :lo12:symbol
|
||||
///
|
||||
/// The :lo12: modifier selects the low 12 bits of the symbol address
|
||||
/// and emits an ELF relocation such as R_AARCH64_ADD_ABS_LO12_NC.
|
||||
///
|
||||
/// Defined by the AArch64 ELF psABI.
|
||||
/// See: <https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst#static-miscellaneous-relocations>.
|
||||
#[cfg(not(target_vendor = "apple"))]
|
||||
macro_rules! sym {
|
||||
($sym:literal) => {
|
||||
$sym
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(target_vendor = "apple"))]
|
||||
macro_rules! sym_off {
|
||||
($sym:literal) => {
|
||||
concat!(":lo12:", $sym)
|
||||
};
|
||||
}
|
||||
|
||||
/// Mach-O ARM64 relocation types:
|
||||
/// ARM64_RELOC_PAGE21
|
||||
/// ARM64_RELOC_PAGEOFF12
|
||||
///
|
||||
/// These relocations implement the @PAGE / @PAGEOFF split used by
|
||||
/// adrp + add sequences on Apple platforms.
|
||||
///
|
||||
/// adrp xN, symbol@PAGE -> ARM64_RELOC_PAGE21
|
||||
/// add xN, xN, symbol@PAGEOFF -> ARM64_RELOC_PAGEOFF12
|
||||
///
|
||||
/// Relocation types defined by Apple in XNU: <mach-o/arm64/reloc.h>.
|
||||
/// See: <https://github.com/apple-oss-distributions/xnu/blob/f6217f891ac0bb64f3d375211650a4c1ff8ca1ea/EXTERNAL_HEADERS/mach-o/arm64/reloc.h>.
|
||||
#[cfg(target_vendor = "apple")]
|
||||
macro_rules! sym {
|
||||
($sym:literal) => {
|
||||
concat!($sym, "@PAGE")
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "apple")]
|
||||
macro_rules! sym_off {
|
||||
($sym:literal) => {
|
||||
concat!($sym, "@PAGEOFF")
|
||||
};
|
||||
}
|
||||
|
||||
// If supported, perform the requested LSE op and return, or fallthrough.
|
||||
macro_rules! try_lse_op {
|
||||
($op: literal, $ordering:ident, $bytes:tt, $($reg:literal,)* [ $mem:ident ] ) => {
|
||||
concat!(
|
||||
".arch_extension lse; ",
|
||||
"adrp x16, {have_lse}; ",
|
||||
"ldrb w16, [x16, :lo12:{have_lse}]; ",
|
||||
"cbz w16, 8f; ",
|
||||
".arch_extension lse\n",
|
||||
concat!("adrp x16, ", sym!("{have_lse}"), "\n"),
|
||||
concat!("ldrb w16, [x16, ", sym_off!("{have_lse}"), "]\n"),
|
||||
"cbz w16, 8f\n",
|
||||
// LSE_OP s(reg),* [$mem]
|
||||
concat!(lse!($op, $ordering, $bytes), $( " ", reg!($bytes, $reg), ", " ,)* "[", stringify!($mem), "]; ",),
|
||||
"ret; ",
|
||||
"8:"
|
||||
concat!(lse!($op, $ordering, $bytes), $( " ", reg!($bytes, $reg), ", " ,)* "[", stringify!($mem), "]\n",),
|
||||
"ret
|
||||
8:"
|
||||
)
|
||||
};
|
||||
}
|
||||
@@ -203,15 +271,15 @@ macro_rules! compare_and_swap {
|
||||
};
|
||||
}
|
||||
|
||||
// i128 uses a completely different impl, so it has its own macro.
|
||||
macro_rules! compare_and_swap_i128 {
|
||||
// u128 uses a completely different impl, so it has its own macro.
|
||||
macro_rules! compare_and_swap_u128 {
|
||||
($ordering:ident, $name:ident) => {
|
||||
intrinsics! {
|
||||
#[maybe_use_optimized_c_shim]
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn $name (
|
||||
expected: i128, desired: i128, ptr: *mut i128
|
||||
) -> i128 {
|
||||
expected: u128, desired: u128, ptr: *mut u128
|
||||
) -> u128 {
|
||||
core::arch::naked_asm! {
|
||||
// CASP x0, x1, x2, x3, [x4]; if LSE supported.
|
||||
try_lse_op!("cas", $ordering, 16, 0, 1, 2, 3, [x4]),
|
||||
@@ -391,7 +459,7 @@ macro_rules! foreach_ldset {
|
||||
}
|
||||
|
||||
foreach_cas!(compare_and_swap);
|
||||
foreach_cas16!(compare_and_swap_i128);
|
||||
foreach_cas16!(compare_and_swap_u128);
|
||||
foreach_swp!(swap);
|
||||
foreach_ldadd!(add);
|
||||
foreach_ldclr!(and);
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#![feature(compiler_builtins)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(linkage)]
|
||||
#![feature(naked_functions)]
|
||||
#![feature(repr_simd)]
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![feature(rustc_attrs)]
|
||||
@@ -57,7 +56,12 @@
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))]
|
||||
pub mod aarch64;
|
||||
|
||||
#[cfg(all(target_arch = "aarch64", target_feature = "outline-atomics"))]
|
||||
// Note that we enable the module on "mangled-names" because that is the default feature
|
||||
// in the builtins-test tests. So this is a way of enabling the module during testing.
|
||||
#[cfg(all(
|
||||
target_arch = "aarch64",
|
||||
any(target_feature = "outline-atomics", feature = "mangled-names")
|
||||
))]
|
||||
pub mod aarch64_outline_atomics;
|
||||
|
||||
#[cfg(target_arch = "avr")]
|
||||
|
||||
@@ -28,8 +28,10 @@ pub mod full_availability {
|
||||
fn fdimf16(x: f16, y: f16) -> f16;
|
||||
fn floorf16(x: f16) -> f16;
|
||||
fn fmaxf16(x: f16, y: f16) -> f16;
|
||||
fn fmaximum_numf16(x: f16, y: f16) -> f16;
|
||||
fn fmaximumf16(x: f16, y: f16) -> f16;
|
||||
fn fminf16(x: f16, y: f16) -> f16;
|
||||
fn fminimum_numf16(x: f16, y: f16) -> f16;
|
||||
fn fminimumf16(x: f16, y: f16) -> f16;
|
||||
fn fmodf16(x: f16, y: f16) -> f16;
|
||||
fn rintf16(x: f16) -> f16;
|
||||
@@ -81,8 +83,12 @@ pub mod full_availability {
|
||||
// however, so we still provide a fallback.
|
||||
libm_intrinsics! {
|
||||
fn fmaximum(x: f64, y: f64) -> f64;
|
||||
fn fmaximum_num(x: f64, y: f64) -> f64;
|
||||
fn fmaximum_numf(x: f32, y: f32) -> f32;
|
||||
fn fmaximumf(x: f32, y: f32) -> f32;
|
||||
fn fminimum(x: f64, y: f64) -> f64;
|
||||
fn fminimum_num(x: f64, y: f64) -> f64;
|
||||
fn fminimum_numf(x: f32, y: f32) -> f32;
|
||||
fn fminimumf(x: f32, y: f32) -> f32;
|
||||
fn roundeven(x: f64) -> f64;
|
||||
fn roundevenf(x: f32) -> f32;
|
||||
@@ -97,8 +103,10 @@ pub mod full_availability {
|
||||
fn floorf128(x: f128) -> f128;
|
||||
fn fmaf128(x: f128, y: f128, z: f128) -> f128;
|
||||
fn fmaxf128(x: f128, y: f128) -> f128;
|
||||
fn fmaximum_numf128(x: f128, y: f128) -> f128;
|
||||
fn fmaximumf128(x: f128, y: f128) -> f128;
|
||||
fn fminf128(x: f128, y: f128) -> f128;
|
||||
fn fminimum_numf128(x: f128, y: f128) -> f128;
|
||||
fn fminimumf128(x: f128, y: f128) -> f128;
|
||||
fn fmodf128(x: f128, y: f128) -> f128;
|
||||
fn rintf128(x: f128) -> f128;
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
// crate doing wrapping pointer arithmetic with a method that must not wrap won't be the problem if
|
||||
// something does go wrong at runtime.
|
||||
use core::ffi::c_int;
|
||||
use core::intrinsics::likely;
|
||||
|
||||
use crate::support::cold_path;
|
||||
|
||||
const WORD_SIZE: usize = core::mem::size_of::<usize>();
|
||||
const WORD_MASK: usize = WORD_SIZE - 1;
|
||||
@@ -209,9 +210,10 @@ unsafe fn copy_forward_misaligned_words(dest: *mut u8, src: *const u8, n: usize)
|
||||
|
||||
let n_words = n & !WORD_MASK;
|
||||
let src_misalignment = src as usize & WORD_MASK;
|
||||
if likely(src_misalignment == 0) {
|
||||
if src_misalignment == 0 {
|
||||
copy_forward_aligned_words(dest, src, n_words);
|
||||
} else {
|
||||
cold_path();
|
||||
copy_forward_misaligned_words(dest, src, n_words);
|
||||
}
|
||||
dest = dest.wrapping_add(n_words);
|
||||
@@ -327,9 +329,10 @@ unsafe fn copy_backward_misaligned_words(dest: *mut u8, src: *const u8, n: usize
|
||||
|
||||
let n_words = n & !WORD_MASK;
|
||||
let src_misalignment = src as usize & WORD_MASK;
|
||||
if likely(src_misalignment == 0) {
|
||||
if src_misalignment == 0 {
|
||||
copy_backward_aligned_words(dest, src, n_words);
|
||||
} else {
|
||||
cold_path();
|
||||
copy_backward_misaligned_words(dest, src, n_words);
|
||||
}
|
||||
dest = dest.wrapping_sub(n_words);
|
||||
@@ -368,7 +371,7 @@ pub unsafe fn set_bytes_words(s: *mut u8, c: u8, n: usize) {
|
||||
}
|
||||
}
|
||||
|
||||
if likely(n >= WORD_COPY_THRESHOLD) {
|
||||
if n >= WORD_COPY_THRESHOLD {
|
||||
// Align s
|
||||
// Because of n >= 2 * WORD_SIZE, dst_misalignment < n
|
||||
let misalignment = (s as usize).wrapping_neg() & WORD_MASK;
|
||||
@@ -380,6 +383,8 @@ pub unsafe fn set_bytes_words(s: *mut u8, c: u8, n: usize) {
|
||||
set_bytes_words(s, c, n_words);
|
||||
s = s.wrapping_add(n_words);
|
||||
n -= n_words;
|
||||
} else {
|
||||
cold_path();
|
||||
}
|
||||
set_bytes_bytes(s, c, n);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use core::intrinsics;
|
||||
|
||||
// NOTE These functions are implemented using assembly because they use a custom
|
||||
// calling convention which can't be implemented using a normal Rust function
|
||||
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use core::intrinsics;
|
||||
|
||||
// NOTE These functions are implemented using assembly because they use a custom
|
||||
// calling convention which can't be implemented using a normal Rust function
|
||||
|
||||
|
||||
@@ -279,6 +279,17 @@ struct NestedOp {
|
||||
fn_list: &["fmaf128"],
|
||||
public: true,
|
||||
},
|
||||
NestedOp {
|
||||
// `(f16) -> i32`
|
||||
float_ty: FloatTy::F16,
|
||||
rust_sig: Signature {
|
||||
args: &[Ty::F16],
|
||||
returns: &[Ty::I32],
|
||||
},
|
||||
c_sig: None,
|
||||
fn_list: &["ilogbf16"],
|
||||
public: true,
|
||||
},
|
||||
NestedOp {
|
||||
// `(f32) -> i32`
|
||||
float_ty: FloatTy::F32,
|
||||
@@ -301,6 +312,17 @@ struct NestedOp {
|
||||
fn_list: &["ilogb"],
|
||||
public: true,
|
||||
},
|
||||
NestedOp {
|
||||
// `(f128) -> i32`
|
||||
float_ty: FloatTy::F128,
|
||||
rust_sig: Signature {
|
||||
args: &[Ty::F128],
|
||||
returns: &[Ty::I32],
|
||||
},
|
||||
c_sig: None,
|
||||
fn_list: &["ilogbf128"],
|
||||
public: true,
|
||||
},
|
||||
NestedOp {
|
||||
// `(i32, f32) -> f32`
|
||||
float_ty: FloatTy::F32,
|
||||
@@ -395,6 +417,20 @@ struct NestedOp {
|
||||
fn_list: &["modf"],
|
||||
public: true,
|
||||
},
|
||||
NestedOp {
|
||||
// `(f16, &mut c_int) -> f16` as `(f16) -> (f16, i32)`
|
||||
float_ty: FloatTy::F16,
|
||||
rust_sig: Signature {
|
||||
args: &[Ty::F16],
|
||||
returns: &[Ty::F16, Ty::I32],
|
||||
},
|
||||
c_sig: Some(Signature {
|
||||
args: &[Ty::F16, Ty::MutCInt],
|
||||
returns: &[Ty::F16],
|
||||
}),
|
||||
fn_list: &["frexpf16"],
|
||||
public: true,
|
||||
},
|
||||
NestedOp {
|
||||
// `(f32, &mut c_int) -> f32` as `(f32) -> (f32, i32)`
|
||||
float_ty: FloatTy::F32,
|
||||
@@ -423,6 +459,20 @@ struct NestedOp {
|
||||
fn_list: &["frexp", "lgamma_r"],
|
||||
public: true,
|
||||
},
|
||||
NestedOp {
|
||||
// `(f128, &mut c_int) -> f128` as `(f128) -> (f128, i32)`
|
||||
float_ty: FloatTy::F128,
|
||||
rust_sig: Signature {
|
||||
args: &[Ty::F128],
|
||||
returns: &[Ty::F128, Ty::I32],
|
||||
},
|
||||
c_sig: Some(Signature {
|
||||
args: &[Ty::F128, Ty::MutCInt],
|
||||
returns: &[Ty::F128],
|
||||
}),
|
||||
fn_list: &["frexpf128"],
|
||||
public: true,
|
||||
},
|
||||
NestedOp {
|
||||
// `(f32, f32, &mut c_int) -> f32` as `(f32, f32) -> (f32, i32)`
|
||||
float_ty: FloatTy::F32,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#![allow(unused_features)]
|
||||
#![feature(f16)]
|
||||
#![feature(f128)]
|
||||
// `STATUS_DLL_NOT_FOUND` on i686 MinGW, not worth looking into.
|
||||
|
||||
@@ -5,6 +5,7 @@ edition = "2024"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
getopts.workspace = true
|
||||
object.workspace = true
|
||||
regex.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
@@ -5,75 +5,100 @@
|
||||
//! actual target is cross compiled.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||
use std::fs;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::process::{Command, Stdio, exit};
|
||||
use std::sync::LazyLock;
|
||||
use std::{env, fs};
|
||||
|
||||
use object::read::archive::ArchiveFile;
|
||||
use object::{
|
||||
File as ObjFile, Object, ObjectSection, ObjectSymbol, Result as ObjResult, Symbol, SymbolKind,
|
||||
SymbolScope,
|
||||
Architecture, BinaryFormat, Endianness, File as ObjFile, Object, ObjectSection, ObjectSymbol,
|
||||
Result as ObjResult, SectionFlags, Symbol, SymbolKind, SymbolScope, U32, elf,
|
||||
};
|
||||
use regex::Regex;
|
||||
use serde_json::Value;
|
||||
|
||||
const CHECK_LIBRARIES: &[&str] = &["compiler_builtins", "builtins_test_intrinsics"];
|
||||
const CHECK_EXTENSIONS: &[Option<&str>] = &[Some("rlib"), Some("a"), Some("exe"), None];
|
||||
const GNU_STACK: &str = ".note.GNU-stack";
|
||||
|
||||
const USAGE: &str = "Usage:
|
||||
|
||||
symbol-check build-and-check [TARGET] -- CARGO_BUILD_ARGS ...
|
||||
|
||||
Cargo will get invoked with `CARGO_ARGS` and the specified target. All output
|
||||
`compiler_builtins*.rlib` files will be checked.
|
||||
|
||||
If TARGET is not specified, the host target is used.
|
||||
|
||||
check PATHS ...
|
||||
|
||||
Run the same checks on the given set of paths, without invoking Cargo. Paths
|
||||
may be either archives or object files.
|
||||
symbol-check --build-and-check [--target TARGET] [--no-os] -- CARGO_BUILD_ARGS ...
|
||||
symbol-check --check PATHS ...\
|
||||
";
|
||||
|
||||
fn main() {
|
||||
// Create a `&str` vec so we can match on it.
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
let args_ref = args.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
let mut opts = getopts::Options::new();
|
||||
|
||||
match &args_ref[1..] {
|
||||
["build-and-check", target, "--", args @ ..] if !args.is_empty() => {
|
||||
run_build_and_check(target, args);
|
||||
}
|
||||
["build-and-check", "--", args @ ..] if !args.is_empty() => {
|
||||
run_build_and_check(env!("HOST"), args);
|
||||
}
|
||||
["check", paths @ ..] if !paths.is_empty() => {
|
||||
check_paths(paths);
|
||||
}
|
||||
_ => {
|
||||
println!("{USAGE}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
// Ideally these would be subcommands but that isn't supported.
|
||||
opts.optflag("h", "help", "Print this help message");
|
||||
opts.optflag(
|
||||
"",
|
||||
"build-and-check",
|
||||
"Cargo will get invoked with `CARGO_BUILD_ARGS` and the specified target. All output \
|
||||
`compiler_builtins*.rlib` files will be checked.",
|
||||
);
|
||||
opts.optopt(
|
||||
"",
|
||||
"target",
|
||||
"Set the target for build-and-check. Falls back to the host target otherwise.",
|
||||
"TARGET",
|
||||
);
|
||||
opts.optflag(
|
||||
"",
|
||||
"check",
|
||||
"Run checks on the given set of paths, without invoking Cargo. Paths \
|
||||
may be either archives or object files.",
|
||||
);
|
||||
opts.optflag(
|
||||
"",
|
||||
"no-os",
|
||||
"The binaries will not be checked for executable stacks. Used for embedded targets which \
|
||||
don't set `.note.GNU-stack` since there is no protection.",
|
||||
);
|
||||
opts.optflag("", "no-visibility", "Don't check visibility.");
|
||||
|
||||
let print_usage_and_exit = |code: i32| -> ! {
|
||||
eprintln!("{}", opts.usage(USAGE));
|
||||
exit(code);
|
||||
};
|
||||
|
||||
let m = opts.parse(std::env::args().skip(1)).unwrap_or_else(|e| {
|
||||
eprintln!("{e}");
|
||||
print_usage_and_exit(1);
|
||||
});
|
||||
|
||||
if m.opt_present("help") {
|
||||
print_usage_and_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_build_and_check(target: &str, args: &[&str]) {
|
||||
// Make sure `--target` isn't passed to avoid confusion (since it should be
|
||||
// proivded only once, positionally).
|
||||
for arg in args {
|
||||
let no_os_target = m.opt_present("no-os");
|
||||
let check_visibility = !m.opt_present("no-visibility");
|
||||
let free_args = m.free.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
for arg in &free_args {
|
||||
assert!(
|
||||
!arg.contains("--target"),
|
||||
"target must be passed positionally. {USAGE}"
|
||||
"target must be passed to symbol-check"
|
||||
);
|
||||
}
|
||||
|
||||
let paths = exec_cargo_with_args(target, args);
|
||||
check_paths(&paths);
|
||||
if m.opt_present("build-and-check") {
|
||||
let target = m.opt_str("target").unwrap_or(env!("HOST").to_string());
|
||||
let paths = exec_cargo_with_args(&target, &free_args);
|
||||
check_paths(&paths, no_os_target, check_visibility);
|
||||
} else if m.opt_present("check") {
|
||||
if free_args.is_empty() {
|
||||
print_usage_and_exit(1);
|
||||
}
|
||||
check_paths(&free_args, no_os_target, check_visibility);
|
||||
} else {
|
||||
print_usage_and_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_paths<P: AsRef<Path>>(paths: &[P]) {
|
||||
fn check_paths<P: AsRef<Path>>(paths: &[P], no_os_target: bool, check_visibility: bool) {
|
||||
for path in paths {
|
||||
let path = path.as_ref();
|
||||
println!("Checking {}", path.display());
|
||||
@@ -81,6 +106,10 @@ fn check_paths<P: AsRef<Path>>(paths: &[P]) {
|
||||
|
||||
verify_no_duplicates(&archive);
|
||||
verify_core_symbols(&archive);
|
||||
verify_no_exec_stack(&archive, no_os_target);
|
||||
if check_visibility {
|
||||
verify_hidden_visibility(&archive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +273,9 @@ fn verify_no_duplicates(archive: &BinFile) {
|
||||
found_any = true;
|
||||
});
|
||||
|
||||
assert!(found_any, "no symbols found");
|
||||
if archive.has_symbol_tables() {
|
||||
assert!(found_any, "no symbols found");
|
||||
}
|
||||
|
||||
if !dups.is_empty() {
|
||||
let count = dups.iter().map(|x| &x.name).collect::<HashSet<_>>().len();
|
||||
@@ -285,7 +316,9 @@ fn verify_core_symbols(archive: &BinFile) {
|
||||
}
|
||||
});
|
||||
|
||||
assert!(has_symbols, "no symbols found");
|
||||
if archive.has_symbol_tables() {
|
||||
assert!(has_symbols, "no symbols found");
|
||||
}
|
||||
|
||||
// Discard any symbols that are defined somewhere in the archive
|
||||
undefined.retain(|sym| !defined.contains(&sym.name));
|
||||
@@ -301,6 +334,209 @@ fn verify_core_symbols(archive: &BinFile) {
|
||||
println!(" success: no undefined references to core found");
|
||||
}
|
||||
|
||||
/// Check for symbols with default visibility.
|
||||
fn verify_hidden_visibility(archive: &BinFile) {
|
||||
let mut visible = Vec::new();
|
||||
let mut found_any = false;
|
||||
|
||||
archive.for_each_symbol(|symbol, obj, member| {
|
||||
// Only check defined globals.
|
||||
if !symbol.is_global() || symbol.is_undefined() {
|
||||
return;
|
||||
}
|
||||
|
||||
let sym = SymInfo::new(&symbol, obj, member);
|
||||
if sym.scope == SymbolScope::Dynamic {
|
||||
visible.push(sym);
|
||||
}
|
||||
|
||||
found_any = true
|
||||
});
|
||||
|
||||
if archive.has_symbol_tables() {
|
||||
assert!(found_any, "no symbols found");
|
||||
}
|
||||
|
||||
if !visible.is_empty() {
|
||||
visible.sort_unstable_by(|a, b| a.name.cmp(&b.name));
|
||||
let num = visible.len();
|
||||
panic!("found {num:#?} visible symbols: {visible:#?}");
|
||||
}
|
||||
|
||||
println!(" success: no visible symbols found");
|
||||
}
|
||||
|
||||
/// Reasons a binary is considered to have an executable stack.
|
||||
enum ExeStack {
|
||||
MissingGnuStackSec,
|
||||
ExeGnuStackSec,
|
||||
ExePtGnuStack,
|
||||
}
|
||||
|
||||
/// Ensure that the object/archive will not require an executable stack.
|
||||
fn verify_no_exec_stack(archive: &BinFile, no_os_target: bool) {
|
||||
if no_os_target {
|
||||
// We don't really have a good way of knowing whether or not an elf file is for a
|
||||
// no-os environment so we rely on a CLI arg (note.GNU-stack doesn't get emitted if
|
||||
// there is no OS to protect the stack).
|
||||
println!(" skipping check for writeable+executable stack on no-os target");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut problem_objfiles = Vec::new();
|
||||
|
||||
archive.for_each_object(|obj, obj_path| match check_obj_exe_stack(&obj) {
|
||||
Ok(()) => (),
|
||||
Err(exe) => problem_objfiles.push((obj_path.to_owned(), exe)),
|
||||
});
|
||||
|
||||
if problem_objfiles.is_empty() {
|
||||
println!(" success: no writeable+executable stack indicators found");
|
||||
return;
|
||||
}
|
||||
|
||||
eprintln!("the following object files require an executable stack:");
|
||||
|
||||
for (obj, exe) in problem_objfiles {
|
||||
let reason = match exe {
|
||||
ExeStack::MissingGnuStackSec => "no .note.GNU-stack section",
|
||||
ExeStack::ExeGnuStackSec => ".note.GNU-stack section marked SHF_EXECINSTR",
|
||||
ExeStack::ExePtGnuStack => "PT_GNU_STACK program header marked PF_X",
|
||||
};
|
||||
eprintln!(" {obj} ({reason})");
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/// `Err` if the section/flag combination indicates that the object file should be linked with an
|
||||
/// executable stack.
|
||||
fn check_obj_exe_stack(obj: &ObjFile) -> Result<(), ExeStack> {
|
||||
match obj.format() {
|
||||
BinaryFormat::Elf => check_elf_exe_stack(obj),
|
||||
// Technically has the `MH_ALLOW_STACK_EXECUTION` flag but I can't get the compiler to
|
||||
// emit it (`-allow_stack_execute` doesn't seem to work in recent versions).
|
||||
BinaryFormat::MachO => Ok(()),
|
||||
// Can't find much information about Windows stack executability.
|
||||
BinaryFormat::Coff | BinaryFormat::Pe => Ok(()),
|
||||
// Also not sure about wasm.
|
||||
BinaryFormat::Wasm => Ok(()),
|
||||
BinaryFormat::Xcoff | _ => {
|
||||
unimplemented!("binary format {:?} is not supported", obj.format())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for an executable stack in elf binaries.
|
||||
///
|
||||
/// If the `PT_GNU_STACK` header on a binary is present and marked executable, the binary will
|
||||
/// have an executable stack (RWE rather than the desired RW). If any object file has the right
|
||||
/// `.note.GNU-stack` logic, the final binary will get `PT_GNU_STACK`.
|
||||
///
|
||||
/// Individual object file logic is as follows, paraphrased from [1]:
|
||||
///
|
||||
/// - A `.note.GNU-stack` section with the exe flag means this needs an executable stack
|
||||
/// - A `.note.GNU-stack` section without the exe flag means there is no executable stack needed
|
||||
/// - Without the section, behavior is target-specific. Historically it usually means an executable
|
||||
/// stack is required.
|
||||
///
|
||||
/// Per [2], it is now deprecated behavior for a missing `.note.GNU-stack` section to imply an
|
||||
/// executable stack. However, we shouldn't assume that tooling has caught up to this.
|
||||
///
|
||||
/// [1]: https://www.man7.org/linux/man-pages/man1/ld.1.html
|
||||
/// [2]: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=0d38576a34ec64a1b4500c9277a8e9d0f07e6774>
|
||||
fn check_elf_exe_stack(obj: &ObjFile) -> Result<(), ExeStack> {
|
||||
let end = obj.endianness();
|
||||
|
||||
// Check for PT_GNU_STACK marked executable
|
||||
let mut is_obj_exe = false;
|
||||
let mut found_gnu_stack = false;
|
||||
let mut check_ph = |p_type: U32<Endianness>, p_flags: U32<Endianness>| {
|
||||
let ty = p_type.get(end);
|
||||
let flags = p_flags.get(end);
|
||||
|
||||
// Presence of PT_INTERP indicates that this is an executable rather than a standalone
|
||||
// object file.
|
||||
if ty == elf::PT_INTERP {
|
||||
is_obj_exe = true;
|
||||
}
|
||||
|
||||
if ty == elf::PT_GNU_STACK {
|
||||
assert!(!found_gnu_stack, "multiple PT_GNU_STACK sections");
|
||||
found_gnu_stack = true;
|
||||
if flags & elf::PF_X != 0 {
|
||||
return Err(ExeStack::ExePtGnuStack);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
match obj {
|
||||
ObjFile::Elf32(f) => {
|
||||
for ph in f.elf_program_headers() {
|
||||
check_ph(ph.p_type, ph.p_flags)?;
|
||||
}
|
||||
}
|
||||
ObjFile::Elf64(f) => {
|
||||
for ph in f.elf_program_headers() {
|
||||
check_ph(ph.p_type, ph.p_flags)?;
|
||||
}
|
||||
}
|
||||
_ => panic!("should only be called with elf objects"),
|
||||
}
|
||||
|
||||
if is_obj_exe {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// The remaining are checks for individual object files, which wind up controlling PT_GNU_STACK
|
||||
// in the final binary.
|
||||
let mut gnu_stack_exe = None;
|
||||
let mut has_exe_sections = false;
|
||||
for sec in obj.sections() {
|
||||
let SectionFlags::Elf { sh_flags } = sec.flags() else {
|
||||
unreachable!("only elf files are being checked");
|
||||
};
|
||||
|
||||
let is_sec_exe = sh_flags & u64::from(elf::SHF_EXECINSTR) != 0;
|
||||
|
||||
// If the magic section is present, its exe bit tells us whether or not the object
|
||||
// file requires an executable stack.
|
||||
if sec.name().unwrap_or_default() == GNU_STACK {
|
||||
assert!(gnu_stack_exe.is_none(), "multiple {GNU_STACK} sections");
|
||||
if is_sec_exe {
|
||||
gnu_stack_exe = Some(Err(ExeStack::ExeGnuStackSec));
|
||||
} else {
|
||||
gnu_stack_exe = Some(Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, just keep track of whether or not we have exeuctable sections
|
||||
has_exe_sections |= is_sec_exe;
|
||||
}
|
||||
|
||||
// GNU_STACK sets the executability if specified.
|
||||
if let Some(exe) = gnu_stack_exe {
|
||||
return exe;
|
||||
}
|
||||
|
||||
// Ignore object files that have no executable sections, like rmeta.
|
||||
if !has_exe_sections {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// If there is no `.note.GNU-stack` and no executable sections, behavior differs by platform.
|
||||
match obj.architecture() {
|
||||
// PPC64 doesn't set `.note.GNU-stack` since GNU nested functions don't need a trampoline,
|
||||
// <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=21098>. From experimentation, it seems
|
||||
// like this only applies to big endian.
|
||||
Architecture::PowerPc64 if obj.endianness() == Endianness::Big => Ok(()),
|
||||
|
||||
_ => Err(ExeStack::MissingGnuStackSec),
|
||||
}
|
||||
}
|
||||
|
||||
/// Thin wrapper for owning data used by `object`.
|
||||
struct BinFile {
|
||||
path: PathBuf,
|
||||
@@ -361,4 +597,23 @@ fn for_each_symbol(&self, mut f: impl FnMut(Symbol, &ObjFile, &str)) {
|
||||
obj.symbols().for_each(|sym| f(sym, &obj, obj_path));
|
||||
});
|
||||
}
|
||||
|
||||
/// PE executable files don't have the same kind of symbol tables. This isn't a perfectly
|
||||
/// accurate check, but at least tells us whether we can skip erroring if we don't find any
|
||||
/// symbols.
|
||||
fn has_symbol_tables(&self) -> bool {
|
||||
let mut empty = true;
|
||||
let mut ret = false;
|
||||
|
||||
self.for_each_object(|obj, _obj_path| {
|
||||
if !matches!(obj.format(), BinaryFormat::Pe) {
|
||||
// Any non-PE objects should have symbol tables.
|
||||
ret = true;
|
||||
}
|
||||
empty = false;
|
||||
});
|
||||
|
||||
// If empty, assume there should be tables.
|
||||
empty || ret
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
use std::ffi::OsString;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use assert_cmd::assert::Assert;
|
||||
use assert_cmd::cargo::cargo_bin_cmd;
|
||||
use object::BinaryFormat;
|
||||
use tempfile::tempdir;
|
||||
|
||||
trait AssertExt {
|
||||
@@ -13,6 +13,7 @@ trait AssertExt {
|
||||
}
|
||||
|
||||
impl AssertExt for Assert {
|
||||
#[track_caller]
|
||||
fn stderr_contains(self, s: &str) -> Self {
|
||||
let out = String::from_utf8_lossy(&self.get_output().stderr);
|
||||
assert!(out.contains(s), "looking for: `{s}`\nout:\n```\n{out}\n```");
|
||||
@@ -22,18 +23,19 @@ fn stderr_contains(self, s: &str) -> Self {
|
||||
|
||||
#[test]
|
||||
fn test_duplicates() {
|
||||
let t = TestTarget::from_env();
|
||||
let dir = tempdir().unwrap();
|
||||
let dup_out = dir.path().join("dup.o");
|
||||
let lib_out = dir.path().join("libfoo.rlib");
|
||||
|
||||
// For the "bad" file, we need duplicate symbols from different object files in the archive. Do
|
||||
// this reliably by building an archive and a separate object file then merging them.
|
||||
rustc_build(&input_dir().join("duplicates.rs"), &lib_out, |cmd| cmd);
|
||||
rustc_build(&input_dir().join("duplicates.rs"), &dup_out, |cmd| {
|
||||
t.rustc_build(&input_dir().join("duplicates.rs"), &lib_out, |cmd| cmd);
|
||||
t.rustc_build(&input_dir().join("duplicates.rs"), &dup_out, |cmd| {
|
||||
cmd.arg("--emit=obj")
|
||||
});
|
||||
|
||||
let mut ar = cc_build().get_archiver();
|
||||
let mut ar = t.cc_build().get_archiver();
|
||||
|
||||
if ar.get_program().to_string_lossy().contains("lib.exe") {
|
||||
let mut out_arg = OsString::from("-out:");
|
||||
@@ -48,10 +50,10 @@ fn test_duplicates() {
|
||||
.stderr(Stdio::null())
|
||||
.arg(&lib_out);
|
||||
}
|
||||
let status = ar.arg(&dup_out).status().unwrap();
|
||||
assert!(status.success());
|
||||
|
||||
let assert = cargo_bin_cmd!().arg("check").arg(&lib_out).assert();
|
||||
run(ar.arg(&dup_out));
|
||||
|
||||
let assert = t.symcheck_exe().arg(&lib_out).assert();
|
||||
assert
|
||||
.failure()
|
||||
.stderr_contains("duplicate symbols")
|
||||
@@ -62,10 +64,11 @@ fn test_duplicates() {
|
||||
|
||||
#[test]
|
||||
fn test_core_symbols() {
|
||||
let t = TestTarget::from_env();
|
||||
let dir = tempdir().unwrap();
|
||||
let lib_out = dir.path().join("libfoo.rlib");
|
||||
rustc_build(&input_dir().join("core_symbols.rs"), &lib_out, |cmd| cmd);
|
||||
let assert = cargo_bin_cmd!().arg("check").arg(&lib_out).assert();
|
||||
t.rustc_build(&input_dir().join("core_symbols.rs"), &lib_out, |cmd| cmd);
|
||||
let assert = t.symcheck_exe().arg(&lib_out).assert();
|
||||
assert
|
||||
.failure()
|
||||
.stderr_contains("found 1 undefined symbols from core")
|
||||
@@ -73,40 +76,160 @@ fn test_core_symbols() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_good() {
|
||||
fn test_visible_symbols() {
|
||||
let t = TestTarget::from_env();
|
||||
if t.is_windows() {
|
||||
eprintln!("windows does not have visibility, skipping");
|
||||
return;
|
||||
}
|
||||
let dir = tempdir().unwrap();
|
||||
let lib_out = dir.path().join("libfoo.rlib");
|
||||
rustc_build(&input_dir().join("good.rs"), &lib_out, |cmd| cmd);
|
||||
let assert = cargo_bin_cmd!().arg("check").arg(&lib_out).assert();
|
||||
t.rustc_build(&input_dir().join("good_lib.rs"), &lib_out, |cmd| cmd);
|
||||
let assert = t.symcheck_exe().arg(&lib_out).assert();
|
||||
assert.failure().stderr_contains("found 1 visible symbols"); // good is visible.
|
||||
}
|
||||
|
||||
mod exe_stack {
|
||||
use super::*;
|
||||
|
||||
/// Check with an object that has no `.note.GNU-stack` section, indicating platform-default stack
|
||||
/// writeability (usually enabled).
|
||||
#[test]
|
||||
fn test_missing_gnu_stack_section() {
|
||||
let t = TestTarget::from_env();
|
||||
if t.is_msvc() {
|
||||
// Can't easily build asm via cc with cl.exe / masm.exe
|
||||
eprintln!("assembly on windows, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let src = input_dir().join("missing_gnu_stack_section.S");
|
||||
|
||||
let objs = t.cc_build().file(src).out_dir(&dir).compile_intermediates();
|
||||
let [obj] = objs.as_slice() else { panic!() };
|
||||
|
||||
let assert = t.symcheck_exe().arg(obj).arg("--no-visibility").assert();
|
||||
|
||||
if t.is_ppc64be() || t.no_os() || t.binary_obj_format() != BinaryFormat::Elf {
|
||||
// Ppc64be doesn't emit `.note.GNU-stack`, not relevant without an OS, and non-elf
|
||||
// targets don't use `.note.GNU-stack`.
|
||||
assert.success();
|
||||
return;
|
||||
}
|
||||
|
||||
assert
|
||||
.failure()
|
||||
.stderr_contains("the following object files require an executable stack")
|
||||
.stderr_contains("missing_gnu_stack_section.o (no .note.GNU-stack section)");
|
||||
}
|
||||
|
||||
/// Check with an object that has a `.note.GNU-stack` section with the executable flag set.
|
||||
#[test]
|
||||
fn test_exe_gnu_stack_section() {
|
||||
let t = TestTarget::from_env();
|
||||
let mut build = t.cc_build();
|
||||
if !build.get_compiler().is_like_gnu() || t.is_windows() {
|
||||
eprintln!("unsupported compiler for nested functions, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let objs = build
|
||||
.file(input_dir().join("has_exe_gnu_stack_section.c"))
|
||||
.out_dir(&dir)
|
||||
.compile_intermediates();
|
||||
let [obj] = objs.as_slice() else { panic!() };
|
||||
|
||||
let assert = t.symcheck_exe().arg(obj).arg("--no-visibility").assert();
|
||||
|
||||
if t.is_ppc64be() || t.no_os() {
|
||||
// Ppc64be doesn't emit `.note.GNU-stack`, not relevant without an OS.
|
||||
assert.success();
|
||||
return;
|
||||
}
|
||||
|
||||
assert
|
||||
.failure()
|
||||
.stderr_contains("the following object files require an executable stack")
|
||||
.stderr_contains(
|
||||
"has_exe_gnu_stack_section.o (.note.GNU-stack section marked SHF_EXECINSTR)",
|
||||
);
|
||||
}
|
||||
|
||||
/// Check a final binary with `PT_GNU_STACK`.
|
||||
#[test]
|
||||
fn test_execstack_bin() {
|
||||
let t = TestTarget::from_env();
|
||||
if t.binary_obj_format() != BinaryFormat::Elf || !t.supports_executables() {
|
||||
// Mac's Clang rejects `-z execstack`. `-allow_stack_execute` should work per the ld
|
||||
// manpage, at least on x86, but it doesn't seem to., not relevant without an OS.
|
||||
eprintln!("non-elf or no-executable target, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let out = dir.path().join("execstack.out");
|
||||
|
||||
let mut cmd = t.cc_build().get_compiler().to_command();
|
||||
t.set_bin_out_path(&mut cmd, &out);
|
||||
|
||||
run(cmd
|
||||
.arg("-z")
|
||||
.arg("execstack")
|
||||
.arg(input_dir().join("good_bin.c")));
|
||||
|
||||
let assert = t.symcheck_exe().arg(&out).assert();
|
||||
assert
|
||||
.failure()
|
||||
.stderr_contains("the following object files require an executable stack")
|
||||
.stderr_contains("execstack.out (PT_GNU_STACK program header marked PF_X)");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_good_lib() {
|
||||
let t = TestTarget::from_env();
|
||||
let dir = tempdir().unwrap();
|
||||
let lib_out = dir.path().join("libfoo.rlib");
|
||||
t.rustc_build(&input_dir().join("good_lib.rs"), &lib_out, |cmd| cmd);
|
||||
let assert = t
|
||||
.symcheck_exe()
|
||||
.arg(&lib_out)
|
||||
.arg("--no-visibility")
|
||||
.assert();
|
||||
assert.success();
|
||||
}
|
||||
|
||||
/// Build i -> o with optional additional configuration.
|
||||
fn rustc_build(i: &Path, o: &Path, mut f: impl FnMut(&mut Command) -> &mut Command) {
|
||||
let mut cmd = Command::new("rustc");
|
||||
cmd.arg(i)
|
||||
.arg("--target")
|
||||
.arg(target())
|
||||
.arg("--crate-type=lib")
|
||||
.arg("-o")
|
||||
.arg(o);
|
||||
f(&mut cmd);
|
||||
let status = cmd.status().unwrap();
|
||||
assert!(status.success());
|
||||
#[test]
|
||||
fn test_good_bin() {
|
||||
let t = TestTarget::from_env();
|
||||
// Nothing to test if we can't build a binary.
|
||||
if !t.supports_executables() {
|
||||
eprintln!("no-exe target, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let out = dir.path().join("good_bin.out");
|
||||
|
||||
let mut cmd = t.cc_build().get_compiler().to_command();
|
||||
t.set_bin_out_path(&mut cmd, &out);
|
||||
run(cmd.arg(input_dir().join("good_bin.c")));
|
||||
|
||||
let assert = t.symcheck_exe().arg(&out).arg("--no-visibility").assert();
|
||||
assert.success();
|
||||
}
|
||||
|
||||
/// Configure `cc` with the host and target.
|
||||
fn cc_build() -> cc::Build {
|
||||
let mut b = cc::Build::new();
|
||||
b.host(env!("HOST")).target(&target());
|
||||
b
|
||||
/// Since symcheck is a hostprog, the target we want to build and test symcheck for may not be the
|
||||
/// same as the host target.
|
||||
struct TestTarget {
|
||||
triple: String,
|
||||
}
|
||||
|
||||
/// Symcheck runs on the host but we want to verify that we find issues on all targets, so
|
||||
/// the cross target may be specified.
|
||||
fn target() -> String {
|
||||
static TARGET: LazyLock<String> = LazyLock::new(|| {
|
||||
let target = match env::var("SYMCHECK_TEST_TARGET") {
|
||||
impl TestTarget {
|
||||
fn from_env() -> Self {
|
||||
let triple = match env::var("SYMCHECK_TEST_TARGET") {
|
||||
Ok(t) => t,
|
||||
// Require on CI so we don't accidentally always test the native target
|
||||
_ if env::var("CI").is_ok() => panic!("SYMCHECK_TEST_TARGET must be set in CI"),
|
||||
@@ -114,13 +237,108 @@ fn target() -> String {
|
||||
Err(_) => env!("HOST").to_string(),
|
||||
};
|
||||
|
||||
println!("using target {target}");
|
||||
target
|
||||
});
|
||||
println!("using target {triple}");
|
||||
Self { triple }
|
||||
}
|
||||
|
||||
TARGET.clone()
|
||||
/// Build i -> o with optional additional configuration.
|
||||
fn rustc_build(&self, i: &Path, o: &Path, mut f: impl FnMut(&mut Command) -> &mut Command) {
|
||||
let mut cmd = Command::new("rustc");
|
||||
cmd.arg(i)
|
||||
.arg("--target")
|
||||
.arg(&self.triple)
|
||||
.arg("--crate-type=lib")
|
||||
.arg("-o")
|
||||
.arg(o);
|
||||
f(&mut cmd);
|
||||
run(&mut cmd);
|
||||
}
|
||||
|
||||
/// Configure `cc` with the host and target.
|
||||
fn cc_build(&self) -> cc::Build {
|
||||
let mut b = cc::Build::new();
|
||||
b.host(env!("HOST"))
|
||||
.target(&self.triple)
|
||||
.opt_level(0)
|
||||
.cargo_debug(true)
|
||||
.cargo_metadata(false);
|
||||
b
|
||||
}
|
||||
|
||||
fn symcheck_exe(&self) -> assert_cmd::Command {
|
||||
let mut cmd = cargo_bin_cmd!();
|
||||
cmd.arg("--check");
|
||||
if self.no_os() {
|
||||
cmd.arg("--no-os");
|
||||
}
|
||||
cmd
|
||||
}
|
||||
|
||||
/// MSVC requires different flags for setting output path, account for that here.
|
||||
fn set_bin_out_path<'a>(&self, cmd: &'a mut Command, out: &Path) -> &'a mut Command {
|
||||
if self.cc_build().get_compiler().is_like_msvc() {
|
||||
let mut exe_arg = OsString::from("/Fe");
|
||||
let mut obj_arg = OsString::from("/Fo");
|
||||
exe_arg.push(out);
|
||||
obj_arg.push(out.with_extension("o"));
|
||||
cmd.arg(exe_arg).arg(obj_arg)
|
||||
} else {
|
||||
cmd.arg("-o").arg(out)
|
||||
}
|
||||
}
|
||||
|
||||
/// Based on `rustc_target`.
|
||||
fn binary_obj_format(&self) -> BinaryFormat {
|
||||
let t = &self.triple;
|
||||
if t.contains("-windows-") || t.contains("-cygwin") {
|
||||
// Coff for libraries, PE for executables.
|
||||
BinaryFormat::Coff
|
||||
} else if t.starts_with("wasm") {
|
||||
BinaryFormat::Wasm
|
||||
} else if t.contains("-aix") {
|
||||
BinaryFormat::Xcoff
|
||||
} else if t.contains("-apple-") {
|
||||
BinaryFormat::MachO
|
||||
} else {
|
||||
BinaryFormat::Elf
|
||||
}
|
||||
}
|
||||
|
||||
fn is_windows(&self) -> bool {
|
||||
self.triple.contains("-windows-")
|
||||
}
|
||||
|
||||
fn is_msvc(&self) -> bool {
|
||||
self.triple.contains("-windows-msvc")
|
||||
}
|
||||
|
||||
fn is_ppc64be(&self) -> bool {
|
||||
self.triple.starts_with("powerpc64-")
|
||||
}
|
||||
|
||||
/// True if the target needs `--no-os` passed to symcheck.
|
||||
fn no_os(&self) -> bool {
|
||||
self.triple.contains("-none")
|
||||
}
|
||||
|
||||
/// True if the target supports (easily) building to a final executable.
|
||||
fn supports_executables(&self) -> bool {
|
||||
// Technically i686-pc-windows-gnu should work but it has nontrivial setup in CI.
|
||||
!(self.no_os()
|
||||
|| self.triple == "wasm32-unknown-unknown"
|
||||
|| self.triple == "i686-pc-windows-gnu")
|
||||
}
|
||||
}
|
||||
|
||||
fn input_dir() -> PathBuf {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/input")
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn run(cmd: &mut Command) {
|
||||
eprintln!("+ {cmd:?}");
|
||||
let out = cmd.output().unwrap();
|
||||
println!("{}", String::from_utf8_lossy(&out.stdout));
|
||||
eprintln!("{}", String::from_utf8_lossy(&out.stderr));
|
||||
assert!(out.status.success(), "{:?}", out.status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
/* empty main used to test binaries with compiler options */
|
||||
|
||||
int main() {}
|
||||
@@ -0,0 +1,16 @@
|
||||
/* A file that requires an executable stack and thus will have a
|
||||
* `.note.GNU-stack` section with the executable bit set.
|
||||
*
|
||||
* GNU nested functions are the only way I could find to force an explicitly
|
||||
* executable stack. Supported by GCC only, not Clang.
|
||||
*/
|
||||
|
||||
void intermediate(void (*)(int, int), int);
|
||||
|
||||
void hack(int *array, int size) {
|
||||
void store (int index, int value) {
|
||||
array[index] = value;
|
||||
}
|
||||
|
||||
intermediate(store, size);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* Create an object file with no `.note.GNU-stack` section.
|
||||
*
|
||||
* Assembly files do not get that section, meaning platform-default stack
|
||||
* executability is implied (usually yes on Linux).
|
||||
*/
|
||||
|
||||
.global func
|
||||
|
||||
#ifdef __wasm__
|
||||
.functype func () -> ()
|
||||
.type func, @function
|
||||
#endif
|
||||
|
||||
func:
|
||||
nop
|
||||
|
||||
#ifdef __wasm__
|
||||
end_function
|
||||
#endif
|
||||
@@ -6,6 +6,7 @@ publish = false
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
cfg-if.workspace = true
|
||||
libm.workspace = true
|
||||
libm-macros.workspace = true
|
||||
libm-test.workspace = true
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
use std::num::ParseIntError;
|
||||
use std::str::FromStr;
|
||||
|
||||
use libm::support::{Hexf, hf32, hf64};
|
||||
use cfg_if::cfg_if;
|
||||
use libm::support::{Float, Hexf, hf32, hf64};
|
||||
#[cfg(feature = "build-mpfr")]
|
||||
use libm_test::mpfloat::MpOp;
|
||||
use libm_test::{MathOp, TupleCall};
|
||||
use libm_test::{Hex, MathOp, TupleCall};
|
||||
#[cfg(feature = "build-mpfr")]
|
||||
use rug::az::{self, Az};
|
||||
|
||||
@@ -22,10 +23,16 @@
|
||||
|
||||
SUBCOMMAND:
|
||||
eval <BASIS> <OP> inputs...
|
||||
x <BASIS> <OP> inputs...
|
||||
Evaulate the expression with a given basis. This can be useful for
|
||||
running routines with a debugger, or quickly checking input. Examples:
|
||||
* eval musl sinf 1.234 # print the results of musl sinf(1.234f32)
|
||||
* eval mpfr pow 1.234 2.432 # print the results of mpfr pow(1.234, 2.432)
|
||||
|
||||
print inputs...
|
||||
p inputs...
|
||||
For each input, print it in different formats with various floating
|
||||
point properties (normal, infinite, etc).
|
||||
";
|
||||
|
||||
fn main() {
|
||||
@@ -33,7 +40,8 @@ fn main() {
|
||||
let str_args = args.iter().map(|s| s.as_str()).collect::<Vec<_>>();
|
||||
|
||||
match &str_args.as_slice()[1..] {
|
||||
["eval", basis, op, inputs @ ..] => do_eval(basis, op, inputs),
|
||||
["eval" | "x", basis, op, inputs @ ..] => do_eval(basis, op, inputs),
|
||||
["print" | "p", inputs @ ..] => do_classify(inputs),
|
||||
_ => {
|
||||
println!("{USAGE}\nunrecognized input `{str_args:?}`");
|
||||
std::process::exit(1);
|
||||
@@ -106,6 +114,66 @@ fn do_eval(basis: &str, op: &str, inputs: &[&str]) {
|
||||
panic!("no operation matching {op}");
|
||||
}
|
||||
|
||||
/// Print basic float information to stdout.
|
||||
fn do_classify(inputs: &[&str]) {
|
||||
for s in inputs {
|
||||
if let Some(s) = s.strip_suffix("f16") {
|
||||
cfg_if! {
|
||||
if #[cfg(f16_enabled)] {
|
||||
let s = s.trim_end_matches("_");
|
||||
let x: f16 = parse(&[s], 0);
|
||||
classify_print(x);
|
||||
continue;
|
||||
} else {
|
||||
panic!("parsing this type requires f16 support: `{s}`");
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Some(s) = s.strip_suffix("f32") {
|
||||
let s = s.trim_end_matches("_");
|
||||
let x: f32 = parse(&[s], 0);
|
||||
classify_print(x);
|
||||
continue;
|
||||
} else if let Some(s) = s.strip_suffix("f64") {
|
||||
let s = s.trim_end_matches("_");
|
||||
let x: f64 = parse(&[s], 0);
|
||||
classify_print(x);
|
||||
continue;
|
||||
}
|
||||
if let Some(s) = s.strip_suffix("f128") {
|
||||
cfg_if! {
|
||||
if #[cfg(all(f128_enabled, feature = "build-mpfr"))] {
|
||||
let s = s.trim_end_matches("_");
|
||||
let x: f128 = parse_rug(&[s], 0);
|
||||
classify_print(x);
|
||||
continue;
|
||||
} else {
|
||||
panic!("parsing this type requires f128 support and \
|
||||
the `build-mpfr` feature: `{s}`");
|
||||
}
|
||||
}
|
||||
};
|
||||
panic!("float type must be specified with a `f*` suffix: `{s}`");
|
||||
}
|
||||
}
|
||||
|
||||
fn classify_print<F>(x: F)
|
||||
where
|
||||
F: Float,
|
||||
F::Int: Hex,
|
||||
{
|
||||
println!("{x:?}");
|
||||
println!(" hex: {}", Hexf(x));
|
||||
println!(" bits: {}", x.to_bits().hex());
|
||||
println!(" nan: {}", x.is_nan());
|
||||
println!(" inf: {}", x.is_infinite());
|
||||
println!(" normal: {}", !x.is_subnormal());
|
||||
println!(" pos: {}", x.is_sign_positive());
|
||||
println!(" exp: {} {}", x.ex(), x.ex().hex());
|
||||
println!(" exp unbiased: {}", x.exp_unbiased());
|
||||
println!(" frac: {} {}", x.frac(), x.frac().hex());
|
||||
}
|
||||
|
||||
/// Parse a tuple from a space-delimited string.
|
||||
trait ParseTuple {
|
||||
fn parse(input: &[&str]) -> Self;
|
||||
@@ -232,11 +300,11 @@ fn parse(_input: &[&str]) -> Self {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(f16_enabled)]
|
||||
impl_parse_tuple!(f16);
|
||||
impl_parse_tuple!(f32);
|
||||
impl_parse_tuple!(f64);
|
||||
|
||||
#[cfg(f16_enabled)]
|
||||
impl_parse_tuple_via_rug!(f16);
|
||||
#[cfg(f128_enabled)]
|
||||
impl_parse_tuple_via_rug!(f128);
|
||||
|
||||
|
||||
@@ -560,16 +560,32 @@
|
||||
},
|
||||
"frexp": {
|
||||
"sources": [
|
||||
"libm/src/math/frexp.rs"
|
||||
"libm/src/math/frexp.rs",
|
||||
"libm/src/math/generic/frexp.rs"
|
||||
],
|
||||
"type": "f64"
|
||||
},
|
||||
"frexpf": {
|
||||
"sources": [
|
||||
"libm/src/math/frexpf.rs"
|
||||
"libm/src/math/frexp.rs",
|
||||
"libm/src/math/generic/frexp.rs"
|
||||
],
|
||||
"type": "f32"
|
||||
},
|
||||
"frexpf128": {
|
||||
"sources": [
|
||||
"libm/src/math/frexp.rs",
|
||||
"libm/src/math/generic/frexp.rs"
|
||||
],
|
||||
"type": "f128"
|
||||
},
|
||||
"frexpf16": {
|
||||
"sources": [
|
||||
"libm/src/math/frexp.rs",
|
||||
"libm/src/math/generic/frexp.rs"
|
||||
],
|
||||
"type": "f16"
|
||||
},
|
||||
"hypot": {
|
||||
"sources": [
|
||||
"libm/src/math/hypot.rs"
|
||||
@@ -584,16 +600,32 @@
|
||||
},
|
||||
"ilogb": {
|
||||
"sources": [
|
||||
"libm/src/math/generic/ilogb.rs",
|
||||
"libm/src/math/ilogb.rs"
|
||||
],
|
||||
"type": "f64"
|
||||
},
|
||||
"ilogbf": {
|
||||
"sources": [
|
||||
"libm/src/math/ilogbf.rs"
|
||||
"libm/src/math/generic/ilogb.rs",
|
||||
"libm/src/math/ilogb.rs"
|
||||
],
|
||||
"type": "f32"
|
||||
},
|
||||
"ilogbf128": {
|
||||
"sources": [
|
||||
"libm/src/math/generic/ilogb.rs",
|
||||
"libm/src/math/ilogb.rs"
|
||||
],
|
||||
"type": "f128"
|
||||
},
|
||||
"ilogbf16": {
|
||||
"sources": [
|
||||
"libm/src/math/generic/ilogb.rs",
|
||||
"libm/src/math/ilogb.rs"
|
||||
],
|
||||
"type": "f16"
|
||||
},
|
||||
"j0": {
|
||||
"sources": [
|
||||
"libm/src/math/j0.rs"
|
||||
|
||||
@@ -84,10 +84,14 @@ fmodf128
|
||||
fmodf16
|
||||
frexp
|
||||
frexpf
|
||||
frexpf128
|
||||
frexpf16
|
||||
hypot
|
||||
hypotf
|
||||
ilogb
|
||||
ilogbf
|
||||
ilogbf128
|
||||
ilogbf16
|
||||
j0
|
||||
j0f
|
||||
j1
|
||||
|
||||
@@ -9,7 +9,6 @@ license = "MIT OR Apache-2.0"
|
||||
anyhow.workspace = true
|
||||
# This is not directly used but is required so we can enable `gmp-mpfr-sys/force-cross`.
|
||||
gmp-mpfr-sys = { workspace = true, optional = true }
|
||||
gungraun = { workspace = true, optional = true }
|
||||
indicatif.workspace = true
|
||||
libm = { workspace = true, default-features = true, features = ["unstable-public-internals"] }
|
||||
libm-macros.workspace = true
|
||||
@@ -20,14 +19,18 @@ rand_chacha.workspace = true
|
||||
rayon.workspace = true
|
||||
rug = { workspace = true, optional = true }
|
||||
|
||||
# Really dev dependencies, but those can't be optional
|
||||
criterion = { workspace = true, optional = true }
|
||||
gungraun = { workspace = true, optional = true }
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
getrandom = { workspace = true, features = ["wasm_js"] }
|
||||
indicatif = { workspace = true, features = ["wasmbind"] }
|
||||
|
||||
[build-dependencies]
|
||||
rand = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion.workspace = true
|
||||
libtest-mimic.workspace = true
|
||||
|
||||
[features]
|
||||
@@ -43,8 +46,10 @@ build-mpfr = ["dep:rug", "dep:gmp-mpfr-sys"]
|
||||
# Build our own musl for testing and benchmarks
|
||||
build-musl = ["dep:musl-math-sys"]
|
||||
|
||||
# Enable report generation without bringing in more dependencies by default
|
||||
benchmarking-reports = ["criterion/plotters", "criterion/html_reports"]
|
||||
# Config for wall time benchmarks. Plotters and html_reports bring in extra
|
||||
# deps so are off by default for CI.
|
||||
benchmarking-reports = ["walltime", "criterion/plotters", "criterion/html_reports"]
|
||||
walltime = ["dep:criterion"]
|
||||
|
||||
# Enable icount benchmarks (requires gungraun-runner and valgrind locally)
|
||||
icount = ["dep:gungraun"]
|
||||
@@ -60,6 +65,7 @@ required-features = ["icount"]
|
||||
[[bench]]
|
||||
name = "random"
|
||||
harness = false
|
||||
required-features = ["walltime"]
|
||||
|
||||
[[test]]
|
||||
# No harness so that we can skip tests at runtime based on env. Prefixed with
|
||||
|
||||
@@ -331,10 +331,14 @@ fn icount_bench_print_hf128(x: f128) -> String {
|
||||
icount_bench_fmodf16_group,
|
||||
icount_bench_fmodf_group,
|
||||
icount_bench_frexp_group,
|
||||
icount_bench_frexpf128_group,
|
||||
icount_bench_frexpf16_group,
|
||||
icount_bench_frexpf_group,
|
||||
icount_bench_hypot_group,
|
||||
icount_bench_hypotf_group,
|
||||
icount_bench_ilogb_group,
|
||||
icount_bench_ilogbf128_group,
|
||||
icount_bench_ilogbf16_group,
|
||||
icount_bench_ilogbf_group,
|
||||
icount_bench_j0_group,
|
||||
icount_bench_j0f_group,
|
||||
|
||||
@@ -25,12 +25,16 @@ impl Float for f8 {
|
||||
const NEG_ZERO: Self = Self(0b1_0000_000);
|
||||
const ONE: Self = Self(0b0_0111_000);
|
||||
const NEG_ONE: Self = Self(0b1_0111_000);
|
||||
const MAX: Self = Self(0b0_1110_111);
|
||||
const MIN: Self = Self(0b1_1110_111);
|
||||
const INFINITY: Self = Self(0b0_1111_000);
|
||||
const NEG_INFINITY: Self = Self(0b1_1111_000);
|
||||
const MAX: Self = Self(0b0_1110_111);
|
||||
const MIN: Self = Self(0b1_1110_111);
|
||||
|
||||
const NAN: Self = Self(0b0_1111_100);
|
||||
const SNAN: Self = Self(0b0_1111_001);
|
||||
const NEG_NAN: Self = Self(0b1_1111_100);
|
||||
const NEG_SNAN: Self = Self(0b1_1111_001);
|
||||
|
||||
const MIN_POSITIVE_NORMAL: Self = Self(1 << Self::SIG_BITS);
|
||||
// FIXME: incorrect values
|
||||
const EPSILON: Self = Self::ZERO;
|
||||
@@ -44,6 +48,7 @@ impl Float for f8 {
|
||||
const SIG_MASK: Self::Int = 0b0_0000_111;
|
||||
const EXP_MASK: Self::Int = 0b0_1111_000;
|
||||
const IMPLICIT_BIT: Self::Int = 0b0_0001_000;
|
||||
const SIG_TOP_BIT: Self::Int = Self::IMPLICIT_BIT >> 1;
|
||||
|
||||
fn to_bits(self) -> Self::Int {
|
||||
self.0
|
||||
|
||||
@@ -460,7 +460,8 @@ fn fmodf16_cases() -> Vec<TestCase<op::fmodf16::Routine>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn frexp_cases() -> Vec<TestCase<op::frexp::Routine>> {
|
||||
#[cfg(f16_enabled)]
|
||||
fn frexpf16_cases() -> Vec<TestCase<op::frexpf16::Routine>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
@@ -468,6 +469,15 @@ fn frexpf_cases() -> Vec<TestCase<op::frexpf::Routine>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn frexp_cases() -> Vec<TestCase<op::frexp::Routine>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
#[cfg(f128_enabled)]
|
||||
fn frexpf128_cases() -> Vec<TestCase<op::frexpf128::Routine>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn hypot_cases() -> Vec<TestCase<op::hypot::Routine>> {
|
||||
vec![]
|
||||
}
|
||||
@@ -476,7 +486,8 @@ fn hypotf_cases() -> Vec<TestCase<op::hypotf::Routine>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn ilogb_cases() -> Vec<TestCase<op::ilogb::Routine>> {
|
||||
#[cfg(f16_enabled)]
|
||||
fn ilogbf16_cases() -> Vec<TestCase<op::ilogbf16::Routine>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
@@ -484,6 +495,15 @@ fn ilogbf_cases() -> Vec<TestCase<op::ilogbf::Routine>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn ilogb_cases() -> Vec<TestCase<op::ilogb::Routine>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
#[cfg(f128_enabled)]
|
||||
fn ilogbf128_cases() -> Vec<TestCase<op::ilogbf128::Routine>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn j0_cases() -> Vec<TestCase<op::j0::Routine>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
use libm::support::Float;
|
||||
use rand::distr::{Alphanumeric, StandardUniform};
|
||||
use rand::prelude::Distribution;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand::{RngExt, SeedableRng};
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
|
||||
use super::KnownSize;
|
||||
|
||||
@@ -162,8 +162,12 @@ fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
|
||||
fmodf16,
|
||||
frexp,
|
||||
frexpf,
|
||||
frexpf128,
|
||||
frexpf16,
|
||||
ilogb,
|
||||
ilogbf,
|
||||
ilogbf128,
|
||||
ilogbf16,
|
||||
jn,
|
||||
jnf,
|
||||
ldexp,
|
||||
@@ -324,43 +328,6 @@ fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
|
||||
}
|
||||
}
|
||||
|
||||
impl MpOp for crate::op::[<frexp $suffix>]::Routine {
|
||||
type MpTy = MpFloat;
|
||||
|
||||
fn new_mp() -> Self::MpTy {
|
||||
new_mpfloat::<Self::FTy>()
|
||||
}
|
||||
|
||||
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
|
||||
this.assign(input.0);
|
||||
let exp = this.frexp_mut();
|
||||
(prep_retval::<Self::FTy>(this, Ordering::Equal), exp)
|
||||
}
|
||||
}
|
||||
|
||||
impl MpOp for crate::op::[<ilogb $suffix>]::Routine {
|
||||
type MpTy = MpFloat;
|
||||
|
||||
fn new_mp() -> Self::MpTy {
|
||||
new_mpfloat::<Self::FTy>()
|
||||
}
|
||||
|
||||
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
|
||||
this.assign(input.0);
|
||||
|
||||
// `get_exp` follows `frexp` for `0.5 <= |m| < 1.0`. Adjust the exponent by
|
||||
// one to scale the significand to `1.0 <= |m| < 2.0`.
|
||||
this.get_exp().map(|v| v - 1).unwrap_or_else(|| {
|
||||
if this.is_infinite() {
|
||||
i32::MAX
|
||||
} else {
|
||||
// Zero or NaN
|
||||
i32::MIN
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl MpOp for crate::op::[<jn $suffix>]::Routine {
|
||||
type MpTy = MpFloat;
|
||||
|
||||
@@ -505,6 +472,43 @@ fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
|
||||
}
|
||||
}
|
||||
|
||||
impl MpOp for crate::op::[<frexp $suffix>]::Routine {
|
||||
type MpTy = MpFloat;
|
||||
|
||||
fn new_mp() -> Self::MpTy {
|
||||
new_mpfloat::<Self::FTy>()
|
||||
}
|
||||
|
||||
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
|
||||
this.assign(input.0);
|
||||
let exp = this.frexp_mut();
|
||||
(prep_retval::<Self::FTy>(this, Ordering::Equal), exp)
|
||||
}
|
||||
}
|
||||
|
||||
impl MpOp for crate::op::[<ilogb $suffix>]::Routine {
|
||||
type MpTy = MpFloat;
|
||||
|
||||
fn new_mp() -> Self::MpTy {
|
||||
new_mpfloat::<Self::FTy>()
|
||||
}
|
||||
|
||||
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
|
||||
this.assign(input.0);
|
||||
|
||||
// `get_exp` follows `frexp` for `0.5 <= |m| < 1.0`. Adjust the exponent by
|
||||
// one to scale the significand to `1.0 <= |m| < 2.0`.
|
||||
this.get_exp().map(|v| v - 1).unwrap_or_else(|| {
|
||||
if this.is_infinite() {
|
||||
i32::MAX
|
||||
} else {
|
||||
// Zero or NaN
|
||||
i32::MIN
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// `ldexp` and `scalbn` are the same for binary floating point, so just forward all
|
||||
// methods.
|
||||
impl MpOp for crate::op::[<ldexp $suffix>]::Routine {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
//! Configuration for skipping or changing the result for individual test cases (inputs) rather
|
||||
//! than ignoring entire tests.
|
||||
|
||||
use BaseName as Bn;
|
||||
use CheckBasis::{Mpfr, Musl};
|
||||
use Identifier as Id;
|
||||
use libm::support::CastFrom;
|
||||
use {BaseName as Bn, Identifier as Id};
|
||||
|
||||
use crate::{BaseName, CheckBasis, CheckCtx, Float, Identifier, Int, TestResult};
|
||||
|
||||
@@ -222,6 +223,15 @@ fn check_float<F: Float>(input: (f32,), actual: F, expected: F, ctx: &CheckCtx)
|
||||
return XFAIL_NOCHECK;
|
||||
}
|
||||
|
||||
// the testing infrastructure doesn't account for allowed ulp in the case of overflow
|
||||
if matches!(ctx.base_name, BaseName::Lgamma | BaseName::LgammaR)
|
||||
&& input.0 == 4.0850034e36
|
||||
&& expected.is_infinite()
|
||||
&& actual == F::MAX
|
||||
{
|
||||
return XFAIL_NOCHECK;
|
||||
}
|
||||
|
||||
// FIXME(correctness): lgammaf has high relative inaccuracy near its zeroes
|
||||
if matches!(ctx.base_name, BaseName::Lgamma | BaseName::LgammaR)
|
||||
&& input.0 > -13.0625
|
||||
|
||||
@@ -260,7 +260,7 @@ fn validate_int<I, Input>(actual: I, expected: I, input: Input, ctx: &CheckCtx)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl_int!(u32, i32, u64, i64);
|
||||
impl_int!(u16, i16, u32, i32, u64, i64, u128, i128);
|
||||
|
||||
/* trait implementations for floats */
|
||||
|
||||
@@ -456,3 +456,15 @@ fn validate<'a>(
|
||||
(f32, f32);
|
||||
(f64, f64);
|
||||
);
|
||||
|
||||
#[cfg(f16_enabled)]
|
||||
impl_tuples!(
|
||||
(f16, i32);
|
||||
(f16, f16);
|
||||
);
|
||||
|
||||
#[cfg(f128_enabled)]
|
||||
impl_tuples!(
|
||||
(f128, i32);
|
||||
(f128, f128);
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
use libm_test::bigint_fuzz_iteration_count;
|
||||
use libm_test::generate::random::SEED;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand::{RngExt, SeedableRng};
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
use rug::Assign;
|
||||
use rug::integer::Order;
|
||||
|
||||
@@ -17,7 +17,7 @@ rust-version = "1.67"
|
||||
|
||||
[dev-dependencies]
|
||||
# FIXME(msrv): switch to `no-panic.workspace` when possible
|
||||
no-panic = "0.1.35"
|
||||
no-panic = "0.1.36"
|
||||
|
||||
[features]
|
||||
default = ["arch"]
|
||||
|
||||
@@ -202,6 +202,8 @@ pub fn $func($($arg: $arg_typ),*) -> ($($ret_typ),*) {
|
||||
(fn fminimum(x: f16, y: f16) -> (f16); => fminimumf16);
|
||||
(fn fminimum_num(x: f16, y: f16) -> (f16); => fminimum_numf16);
|
||||
(fn fmod(x: f16, y: f16) -> (f16); => fmodf16);
|
||||
(fn frexp(x: f16) -> (f16, i32); => frexpf16);
|
||||
(fn ilogb(x: f16) -> (i32); => ilogbf16);
|
||||
(fn ldexp(x: f16, n: i32) -> (f16); => ldexpf16);
|
||||
(fn rint(x: f16) -> (f16); => rintf16);
|
||||
(fn round(x: f16) -> (f16); => roundf16);
|
||||
@@ -231,6 +233,8 @@ pub fn $func($($arg: $arg_typ),*) -> ($($ret_typ),*) {
|
||||
(fn fminimum(x: f128, y: f128) -> (f128); => fminimumf128);
|
||||
(fn fminimum_num(x: f128, y: f128) -> (f128); => fminimum_numf128);
|
||||
(fn fmod(x: f128, y: f128) -> (f128); => fmodf128);
|
||||
(fn frexp(x: f128) -> (f128, i32); => frexpf128);
|
||||
(fn ilogb(x: f128) -> (i32); => ilogbf128);
|
||||
(fn ldexp(x: f128, n: i32) -> (f128); => ldexpf128);
|
||||
(fn rint(x: f128) -> (f128); => rintf128);
|
||||
(fn round(x: f128) -> (f128); => roundf128);
|
||||
|
||||
@@ -380,7 +380,7 @@ pub fn exp2(mut x: f64) -> f64 {
|
||||
let mut i0 = ui as u32;
|
||||
i0 = i0.wrapping_add(TBLSIZE as u32 / 2);
|
||||
let ku = i0 / TBLSIZE as u32 * TBLSIZE as u32;
|
||||
let ki = div!(ku as i32, TBLSIZE as i32);
|
||||
let ki = (ku as i32) / TBLSIZE as i32;
|
||||
i0 %= TBLSIZE as u32;
|
||||
let uf = f64::from_bits(ui) - redux;
|
||||
let mut z = x - uf;
|
||||
|
||||
@@ -77,9 +77,12 @@ pub fn fmaxf128(x: f128, y: f128) -> f128 {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::hex_float::Hexi;
|
||||
use crate::support::{Float, Hexf};
|
||||
|
||||
fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
// Note that (YaN, sNaN) and (sNaN, YaN) results differ from 754-2008. This is intentional,
|
||||
// see comments in the generic implementations.
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::ONE, F::ZERO),
|
||||
@@ -88,6 +91,8 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::ZERO, F::SNAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_SNAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
@@ -95,6 +100,8 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::SNAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_SNAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
@@ -103,6 +110,8 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::ONE, F::SNAN, F::ONE),
|
||||
(F::ONE, F::NEG_SNAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ONE, F::NEG_ONE),
|
||||
@@ -111,6 +120,8 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::SNAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_SNAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::INFINITY, F::ONE, F::ONE),
|
||||
@@ -119,6 +130,8 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::INFINITY, F::SNAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_SNAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
|
||||
@@ -127,6 +140,8 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::SNAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_SNAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
@@ -140,6 +155,18 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::SNAN, F::ZERO, F::ZERO),
|
||||
(F::SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::SNAN, F::ONE, F::ONE),
|
||||
(F::SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_SNAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_SNAN, F::ONE, F::ONE),
|
||||
(F::NEG_SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
@@ -147,12 +174,29 @@ fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
assert_biteq!(val, res, "fmin({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between zeros and NaNs does not matter
|
||||
// Ordering between zeros does not matter
|
||||
assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
|
||||
assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO);
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
|
||||
// Selection between NaNs does not matter, it just must be quiet
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
|
||||
|
||||
// These operations should technically return a qnan, but LLVM optimizes out our
|
||||
// `* 1.0` canonicalization.
|
||||
assert!(f(F::NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_SNAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -186,6 +230,8 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ZERO, F::NEG_INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::ZERO, F::SNAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_SNAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::ONE),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
|
||||
@@ -193,6 +239,8 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::SNAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_SNAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ONE, F::NEG_ZERO, F::ONE),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
@@ -201,6 +249,8 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ONE, F::NEG_INFINITY, F::ONE),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::ONE, F::SNAN, F::ONE),
|
||||
(F::ONE, F::NEG_SNAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ONE, F::ONE, F::ONE),
|
||||
@@ -209,6 +259,8 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::SNAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_SNAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::ONE, F::INFINITY),
|
||||
@@ -217,6 +269,8 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::INFINITY, F::NEG_INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::INFINITY, F::SNAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_SNAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_INFINITY, F::ONE, F::ONE),
|
||||
@@ -225,6 +279,8 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::SNAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_SNAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
@@ -238,19 +294,54 @@ fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::SNAN, F::ZERO, F::ZERO),
|
||||
(F::SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::SNAN, F::ONE, F::ONE),
|
||||
(F::SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_SNAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_SNAN, F::ONE, F::ONE),
|
||||
(F::NEG_SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fmax({}, {})", Hexf(x), Hexf(y));
|
||||
assert_biteq!(
|
||||
val,
|
||||
res,
|
||||
"fmax({}, {}) ({}, {})",
|
||||
Hexf(x),
|
||||
Hexf(y),
|
||||
Hexi(x.to_bits()),
|
||||
Hexi(y.to_bits()),
|
||||
);
|
||||
}
|
||||
|
||||
// Ordering between zeros and NaNs does not matter
|
||||
// Ordering between zeros
|
||||
assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
|
||||
assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO);
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
|
||||
// Selection between NaNs does not matter, it just must be quiet
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
|
||||
|
||||
assert!(f(F::NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::SNAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -69,6 +69,7 @@ pub fn fmaximumf128(x: f128, y: f128) -> f128 {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::hex_float::Hexi;
|
||||
use crate::support::{Float, Hexf};
|
||||
|
||||
fn fminimum_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
@@ -122,29 +123,63 @@ fn fminimum_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NAN, F::INFINITY, F::NAN),
|
||||
(F::NAN, F::NEG_INFINITY, F::NAN),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::NAN, F::SNAN, F::NAN),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fminimum({}, {})", Hexf(x), Hexf(y));
|
||||
assert_biteq!(
|
||||
val,
|
||||
res,
|
||||
"fminimum({}, {}) ({}, {})",
|
||||
Hexf(x),
|
||||
Hexf(y),
|
||||
Hexi(x.to_bits()),
|
||||
Hexi(y.to_bits()),
|
||||
);
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
// On platforms where operations only return a single canonical NaN (e.g. RISC-V), the
|
||||
// result may not exactly match one of the inputs which is fine.
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
|
||||
assert!(f(F::ZERO, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_ZERO, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::ONE, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_ONE, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::INFINITY, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::ZERO).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ZERO).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::ONE).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ONE).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::INFINITY).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
|
||||
|
||||
// These operations should technically return a qnan, but LLVM optimizes out our
|
||||
// `* 1.0` canonicalization.
|
||||
assert!(f(F::INFINITY, F::SNAN,).is_nan());
|
||||
assert!(f(F::NEG_INFINITY, F::SNAN,).is_nan());
|
||||
assert!(f(F::NEG_ONE, F::SNAN,).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::INFINITY).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_INFINITY).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_ONE).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_ZERO).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::ONE).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::ZERO).is_nan());
|
||||
assert!(f(F::NEG_ZERO, F::SNAN,).is_nan());
|
||||
assert!(f(F::ONE, F::SNAN,).is_nan());
|
||||
assert!(f(F::SNAN, F::INFINITY,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_INFINITY,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_ONE,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_SNAN,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_ZERO,).is_nan());
|
||||
assert!(f(F::SNAN, F::ONE,).is_nan());
|
||||
assert!(f(F::SNAN, F::SNAN,).is_nan());
|
||||
assert!(f(F::SNAN, F::ZERO,).is_nan());
|
||||
assert!(f(F::ZERO, F::SNAN,).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -220,29 +255,63 @@ fn fmaximum_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NAN, F::INFINITY, F::NAN),
|
||||
(F::NAN, F::NEG_INFINITY, F::NAN),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::NAN, F::SNAN, F::NAN),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fmaximum({}, {})", Hexf(x), Hexf(y));
|
||||
assert_biteq!(
|
||||
val,
|
||||
res,
|
||||
"fmaximum({}, {}) ({}, {})",
|
||||
Hexf(x),
|
||||
Hexf(y),
|
||||
Hexi(x.to_bits()),
|
||||
Hexi(y.to_bits()),
|
||||
);
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
// On platforms where operations only return a single canonical NaN (e.g. RISC-V), the
|
||||
// result may not exactly match one of the inputs which is fine.
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
|
||||
assert!(f(F::ZERO, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_ZERO, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::ONE, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_ONE, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::INFINITY, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::ZERO).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ZERO).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::ONE).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ONE).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::INFINITY).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
|
||||
|
||||
// These operations should technically return a qnan, but LLVM optimizes out our
|
||||
// `* 1.0` canonicalization.
|
||||
assert!(f(F::INFINITY, F::SNAN,).is_nan());
|
||||
assert!(f(F::NEG_INFINITY, F::SNAN,).is_nan());
|
||||
assert!(f(F::NEG_ONE, F::SNAN,).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::INFINITY).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_INFINITY).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_ONE).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_ZERO).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::ONE).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::ZERO).is_nan());
|
||||
assert!(f(F::NEG_ZERO, F::SNAN,).is_nan());
|
||||
assert!(f(F::ONE, F::SNAN,).is_nan());
|
||||
assert!(f(F::SNAN, F::INFINITY,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_INFINITY,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_ONE,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_SNAN,).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_ZERO,).is_nan());
|
||||
assert!(f(F::SNAN, F::ONE,).is_nan());
|
||||
assert!(f(F::SNAN, F::SNAN,).is_nan());
|
||||
assert!(f(F::SNAN, F::ZERO,).is_nan());
|
||||
assert!(f(F::ZERO, F::SNAN,).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -69,6 +69,7 @@ pub fn fmaximum_numf128(x: f128, y: f128) -> f128 {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::hex_float::Hexi;
|
||||
use crate::support::{Float, Hexf};
|
||||
|
||||
fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
@@ -81,6 +82,8 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::ZERO, F::SNAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_SNAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::NEG_ZERO),
|
||||
@@ -89,6 +92,8 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::SNAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_SNAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
@@ -97,6 +102,8 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::ONE, F::SNAN, F::ONE),
|
||||
(F::ONE, F::NEG_SNAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ONE, F::NEG_ONE),
|
||||
@@ -105,6 +112,8 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::SNAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_SNAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::INFINITY, F::ONE, F::ONE),
|
||||
@@ -113,6 +122,8 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::INFINITY, F::SNAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_SNAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
|
||||
@@ -121,6 +132,8 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::SNAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_SNAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
@@ -134,17 +147,52 @@ fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::SNAN, F::ZERO, F::ZERO),
|
||||
(F::SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::SNAN, F::ONE, F::ONE),
|
||||
(F::SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_SNAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_SNAN, F::ONE, F::ONE),
|
||||
(F::NEG_SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, expected) in cases {
|
||||
let actual = f(x, y);
|
||||
assert_biteq!(actual, expected, "fminimum_num({}, {})", Hexf(x), Hexf(y));
|
||||
assert_biteq!(
|
||||
actual,
|
||||
expected,
|
||||
"fminimum_num({}, {}) ({}, {})",
|
||||
Hexf(x),
|
||||
Hexf(y),
|
||||
Hexi(x.to_bits()),
|
||||
Hexi(y.to_bits()),
|
||||
);
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
// Selection between NaNs does not matter, it just must be quiet
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
|
||||
|
||||
// These operations should technically return a qnan, but LLVM optimizes out our
|
||||
// `* 1.0` canonicalization.
|
||||
assert!(f(F::NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::SNAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -179,6 +227,8 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ZERO, F::NEG_INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::ZERO, F::SNAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_SNAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::ZERO, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::ONE),
|
||||
@@ -187,6 +237,8 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::SNAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_SNAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ONE, F::NEG_ZERO, F::ONE),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
@@ -195,6 +247,8 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::ONE, F::NEG_INFINITY, F::ONE),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::ONE, F::SNAN, F::ONE),
|
||||
(F::ONE, F::NEG_SNAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ONE, F::ONE, F::ONE),
|
||||
@@ -203,6 +257,8 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::SNAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_SNAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::ONE, F::INFINITY),
|
||||
@@ -211,6 +267,8 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::INFINITY, F::NEG_INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::INFINITY, F::SNAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_SNAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_INFINITY, F::ONE, F::ONE),
|
||||
@@ -219,6 +277,8 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::SNAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_SNAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
@@ -232,17 +292,52 @@ fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::SNAN, F::ZERO, F::ZERO),
|
||||
(F::SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::SNAN, F::ONE, F::ONE),
|
||||
(F::SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_SNAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_SNAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_SNAN, F::ONE, F::ONE),
|
||||
(F::NEG_SNAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_SNAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, expected) in cases {
|
||||
let actual = f(x, y);
|
||||
assert_biteq!(actual, expected, "fmaximum_num({}, {})", Hexf(x), Hexf(y));
|
||||
assert_biteq!(
|
||||
actual,
|
||||
expected,
|
||||
"fmaximum_num({}, {}) ({}, {})",
|
||||
Hexf(x),
|
||||
Hexf(y),
|
||||
Hexi(x.to_bits()),
|
||||
Hexi(y.to_bits()),
|
||||
);
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
// Selection between NaNs does not matter, it just must be quiet
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
|
||||
|
||||
// These operations should technically return a qnan, but LLVM optimizes out our
|
||||
// `* 1.0` canonicalization.
|
||||
assert!(f(F::NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::NEG_SNAN, F::SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::SNAN, F::NEG_SNAN).is_nan());
|
||||
assert!(f(F::SNAN, F::SNAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,21 +1,34 @@
|
||||
/// Decompose a float into a normalized value within the range `[0.5, 1)`, and a power of 2.
|
||||
///
|
||||
/// That is, `x * 2^p` will represent the input value.
|
||||
// Placeholder so we can have `frexpf16` in the `Float` trait.
|
||||
#[cfg(f16_enabled)]
|
||||
#[cfg_attr(assert_no_panic, no_panic::no_panic)]
|
||||
pub fn frexpf16(x: f16) -> (f16, i32) {
|
||||
super::generic::frexp(x)
|
||||
}
|
||||
|
||||
/// Decompose a float into a normalized value within the range `[0.5, 1)`, and a power of 2.
|
||||
///
|
||||
/// That is, `x * 2^p` will represent the input value.
|
||||
#[cfg_attr(assert_no_panic, no_panic::no_panic)]
|
||||
pub fn frexpf(x: f32) -> (f32, i32) {
|
||||
super::generic::frexp(x)
|
||||
}
|
||||
|
||||
/// Decompose a float into a normalized value within the range `[0.5, 1)`, and a power of 2.
|
||||
///
|
||||
/// That is, `x * 2^p` will represent the input value.
|
||||
#[cfg_attr(assert_no_panic, no_panic::no_panic)]
|
||||
pub fn frexp(x: f64) -> (f64, i32) {
|
||||
let mut y = x.to_bits();
|
||||
let ee = ((y >> 52) & 0x7ff) as i32;
|
||||
|
||||
if ee == 0 {
|
||||
if x != 0.0 {
|
||||
let x1p64 = f64::from_bits(0x43f0000000000000);
|
||||
let (x, e) = frexp(x * x1p64);
|
||||
return (x, e - 64);
|
||||
}
|
||||
return (x, 0);
|
||||
} else if ee == 0x7ff {
|
||||
return (x, 0);
|
||||
}
|
||||
|
||||
let e = ee - 0x3fe;
|
||||
y &= 0x800fffffffffffff;
|
||||
y |= 0x3fe0000000000000;
|
||||
return (f64::from_bits(y), e);
|
||||
super::generic::frexp(x)
|
||||
}
|
||||
|
||||
/// Decompose a float into a normalized value within the range `[0.5, 1)`, and a power of 2.
|
||||
///
|
||||
/// That is, `x * 2^p` will represent the input value.
|
||||
#[cfg(f128_enabled)]
|
||||
#[cfg_attr(assert_no_panic, no_panic::no_panic)]
|
||||
pub fn frexpf128(x: f128) -> (f128, i32) {
|
||||
super::generic::frexp(x)
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#[cfg_attr(assert_no_panic, no_panic::no_panic)]
|
||||
pub fn frexpf(x: f32) -> (f32, i32) {
|
||||
let mut y = x.to_bits();
|
||||
let ee: i32 = ((y >> 23) & 0xff) as i32;
|
||||
|
||||
if ee == 0 {
|
||||
if x != 0.0 {
|
||||
let x1p64 = f32::from_bits(0x5f800000);
|
||||
let (x, e) = frexpf(x * x1p64);
|
||||
return (x, e - 64);
|
||||
} else {
|
||||
return (x, 0);
|
||||
}
|
||||
} else if ee == 0xff {
|
||||
return (x, 0);
|
||||
}
|
||||
|
||||
let e = ee - 0x7e;
|
||||
y &= 0x807fffff;
|
||||
y |= 0x3f000000;
|
||||
(f32::from_bits(y), e)
|
||||
}
|
||||
@@ -46,7 +46,7 @@ pub fn ceil_status<F: Float>(x: F) -> FpResult<F> {
|
||||
F::from_bits(ix)
|
||||
} else {
|
||||
// |x| < 1.0, raise an inexact exception since truncation will happen (unless x == 0).
|
||||
if ix & F::SIG_MASK == F::Int::ZERO {
|
||||
if ix & !F::SIGN_MASK == F::Int::ZERO {
|
||||
status = Status::OK;
|
||||
} else {
|
||||
status = Status::INEXACT;
|
||||
@@ -72,103 +72,83 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::support::Hexf;
|
||||
|
||||
/// Test against https://en.cppreference.com/w/cpp/numeric/math/ceil
|
||||
fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
|
||||
let roundtrip = [
|
||||
F::ZERO,
|
||||
F::ONE,
|
||||
F::NEG_ONE,
|
||||
F::NEG_ZERO,
|
||||
F::INFINITY,
|
||||
F::NEG_INFINITY,
|
||||
];
|
||||
|
||||
for x in roundtrip {
|
||||
let FpResult { val, status } = ceil_status(x);
|
||||
assert_biteq!(val, x, "{}", Hexf(x));
|
||||
assert_eq!(status, Status::OK, "{}", Hexf(x));
|
||||
}
|
||||
|
||||
for &(x, res, res_stat) in cases {
|
||||
let FpResult { val, status } = ceil_status(x);
|
||||
assert_biteq!(val, res, "{}", Hexf(x));
|
||||
assert_eq!(status, res_stat, "{}", Hexf(x));
|
||||
}
|
||||
macro_rules! cases {
|
||||
($f:ty) => {
|
||||
[
|
||||
// roundtrip
|
||||
(0.0, 0.0, Status::OK),
|
||||
(-0.0, -0.0, Status::OK),
|
||||
(1.0, 1.0, Status::OK),
|
||||
(-1.0, -1.0, Status::OK),
|
||||
(<$f>::INFINITY, <$f>::INFINITY, Status::OK),
|
||||
(<$f>::NEG_INFINITY, <$f>::NEG_INFINITY, Status::OK),
|
||||
// with rounding
|
||||
(0.1, 1.0, Status::INEXACT),
|
||||
(-0.1, -0.0, Status::INEXACT),
|
||||
(0.5, 1.0, Status::INEXACT),
|
||||
(-0.5, -0.0, Status::INEXACT),
|
||||
(0.9, 1.0, Status::INEXACT),
|
||||
(-0.9, -0.0, Status::INEXACT),
|
||||
(1.1, 2.0, Status::INEXACT),
|
||||
(-1.1, -1.0, Status::INEXACT),
|
||||
(1.5, 2.0, Status::INEXACT),
|
||||
(-1.5, -1.0, Status::INEXACT),
|
||||
(1.9, 2.0, Status::INEXACT),
|
||||
(-1.9, -1.0, Status::INEXACT),
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/* Skipping f16 / f128 "sanity_check"s due to rejected literal lexing at MSRV */
|
||||
#[track_caller]
|
||||
fn check<F: Float>(cases: &[(F, F, Status)]) {
|
||||
for &(x, exp_res, exp_stat) in cases {
|
||||
let FpResult { val, status } = ceil_status(x);
|
||||
assert_biteq!(val, exp_res, "{x:?} {}", Hexf(x));
|
||||
assert_eq!(
|
||||
status,
|
||||
exp_stat,
|
||||
"{x:?} {} -> {exp_res:?} {}",
|
||||
Hexf(x),
|
||||
Hexf(exp_res)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(f16_enabled)]
|
||||
fn spec_tests_f16() {
|
||||
let cases = [
|
||||
(0.1, 1.0, Status::INEXACT),
|
||||
(-0.1, -0.0, Status::INEXACT),
|
||||
(0.9, 1.0, Status::INEXACT),
|
||||
(-0.9, -0.0, Status::INEXACT),
|
||||
(1.1, 2.0, Status::INEXACT),
|
||||
(-1.1, -1.0, Status::INEXACT),
|
||||
(1.9, 2.0, Status::INEXACT),
|
||||
(-1.9, -1.0, Status::INEXACT),
|
||||
];
|
||||
spec_test::<f16>(&cases);
|
||||
fn check_f16() {
|
||||
check::<f16>(&cases!(f16));
|
||||
check::<f16>(&[
|
||||
(hf16!("0x1p10"), hf16!("0x1p10"), Status::OK),
|
||||
(hf16!("-0x1p10"), hf16!("-0x1p10"), Status::OK),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_check_f32() {
|
||||
assert_eq!(ceil(1.1f32), 2.0);
|
||||
assert_eq!(ceil(2.9f32), 3.0);
|
||||
fn check_f32() {
|
||||
check::<f32>(&cases!(f32));
|
||||
check::<f32>(&[
|
||||
(hf32!("0x1p23"), hf32!("0x1p23"), Status::OK),
|
||||
(hf32!("-0x1p23"), hf32!("-0x1p23"), Status::OK),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spec_tests_f32() {
|
||||
let cases = [
|
||||
(0.1, 1.0, Status::INEXACT),
|
||||
(-0.1, -0.0, Status::INEXACT),
|
||||
(0.9, 1.0, Status::INEXACT),
|
||||
(-0.9, -0.0, Status::INEXACT),
|
||||
(1.1, 2.0, Status::INEXACT),
|
||||
(-1.1, -1.0, Status::INEXACT),
|
||||
(1.9, 2.0, Status::INEXACT),
|
||||
(-1.9, -1.0, Status::INEXACT),
|
||||
];
|
||||
spec_test::<f32>(&cases);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_check_f64() {
|
||||
assert_eq!(ceil(1.1f64), 2.0);
|
||||
assert_eq!(ceil(2.9f64), 3.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spec_tests_f64() {
|
||||
let cases = [
|
||||
(0.1, 1.0, Status::INEXACT),
|
||||
(-0.1, -0.0, Status::INEXACT),
|
||||
(0.9, 1.0, Status::INEXACT),
|
||||
(-0.9, -0.0, Status::INEXACT),
|
||||
(1.1, 2.0, Status::INEXACT),
|
||||
(-1.1, -1.0, Status::INEXACT),
|
||||
(1.9, 2.0, Status::INEXACT),
|
||||
(-1.9, -1.0, Status::INEXACT),
|
||||
];
|
||||
spec_test::<f64>(&cases);
|
||||
fn check_f64() {
|
||||
check::<f64>(&cases!(f64));
|
||||
check::<f64>(&[
|
||||
(hf64!("0x1p52"), hf64!("0x1p52"), Status::OK),
|
||||
(hf64!("-0x1p52"), hf64!("-0x1p52"), Status::OK),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(f128_enabled)]
|
||||
fn spec_tests_f128() {
|
||||
let cases = [
|
||||
(0.1, 1.0, Status::INEXACT),
|
||||
(-0.1, -0.0, Status::INEXACT),
|
||||
(0.9, 1.0, Status::INEXACT),
|
||||
(-0.9, -0.0, Status::INEXACT),
|
||||
(1.1, 2.0, Status::INEXACT),
|
||||
(-1.1, -1.0, Status::INEXACT),
|
||||
(1.9, 2.0, Status::INEXACT),
|
||||
(-1.9, -1.0, Status::INEXACT),
|
||||
];
|
||||
spec_test::<f128>(&cases);
|
||||
check::<f128>(&cases!(f128));
|
||||
check::<f128>(&[
|
||||
(hf128!("0x1p112"), hf128!("0x1p112"), Status::OK),
|
||||
(hf128!("-0x1p112"), hf128!("-0x1p112"), Status::OK),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ pub fn floor_status<F: Float>(x: F) -> FpResult<F> {
|
||||
return FpResult::ok(x);
|
||||
}
|
||||
|
||||
let status;
|
||||
let res = if e >= 0 {
|
||||
// |x| >= 1.0
|
||||
let m = F::SIG_MASK >> e.unsigned();
|
||||
@@ -35,9 +34,6 @@ pub fn floor_status<F: Float>(x: F) -> FpResult<F> {
|
||||
return FpResult::ok(x);
|
||||
}
|
||||
|
||||
// Otherwise, raise an inexact exception.
|
||||
status = Status::INEXACT;
|
||||
|
||||
if x.is_sign_negative() {
|
||||
ix += m;
|
||||
}
|
||||
@@ -45,26 +41,22 @@ pub fn floor_status<F: Float>(x: F) -> FpResult<F> {
|
||||
ix &= !m;
|
||||
F::from_bits(ix)
|
||||
} else {
|
||||
// |x| < 1.0, raise an inexact exception since truncation will happen.
|
||||
if ix & F::SIG_MASK == F::Int::ZERO {
|
||||
status = Status::OK;
|
||||
} else {
|
||||
status = Status::INEXACT;
|
||||
// |x| < 1.0, zero or inexact with truncation
|
||||
|
||||
if (ix & !F::SIGN_MASK) == F::Int::ZERO {
|
||||
return FpResult::ok(x);
|
||||
}
|
||||
|
||||
if x.is_sign_positive() {
|
||||
// 0.0 <= x < 1.0; rounding down goes toward +0.0.
|
||||
F::ZERO
|
||||
} else if ix << 1 != zero {
|
||||
} else {
|
||||
// -1.0 < x < 0.0; rounding down goes toward -1.0.
|
||||
F::NEG_ONE
|
||||
} else {
|
||||
// -0.0 remains unchanged
|
||||
x
|
||||
}
|
||||
};
|
||||
|
||||
FpResult::new(res, status)
|
||||
FpResult::new(res, Status::INEXACT)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -72,86 +64,83 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::support::Hexf;
|
||||
|
||||
/// Test against https://en.cppreference.com/w/cpp/numeric/math/floor
|
||||
fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
|
||||
let roundtrip = [
|
||||
F::ZERO,
|
||||
F::ONE,
|
||||
F::NEG_ONE,
|
||||
F::NEG_ZERO,
|
||||
F::INFINITY,
|
||||
F::NEG_INFINITY,
|
||||
];
|
||||
|
||||
for x in roundtrip {
|
||||
let FpResult { val, status } = floor_status(x);
|
||||
assert_biteq!(val, x, "{}", Hexf(x));
|
||||
assert_eq!(status, Status::OK, "{}", Hexf(x));
|
||||
}
|
||||
|
||||
for &(x, res, res_stat) in cases {
|
||||
let FpResult { val, status } = floor_status(x);
|
||||
assert_biteq!(val, res, "{}", Hexf(x));
|
||||
assert_eq!(status, res_stat, "{}", Hexf(x));
|
||||
}
|
||||
macro_rules! cases {
|
||||
($f:ty) => {
|
||||
[
|
||||
// roundtrip
|
||||
(0.0, 0.0, Status::OK),
|
||||
(-0.0, -0.0, Status::OK),
|
||||
(1.0, 1.0, Status::OK),
|
||||
(-1.0, -1.0, Status::OK),
|
||||
(<$f>::INFINITY, <$f>::INFINITY, Status::OK),
|
||||
(<$f>::NEG_INFINITY, <$f>::NEG_INFINITY, Status::OK),
|
||||
// with rounding
|
||||
(0.1, 0.0, Status::INEXACT),
|
||||
(-0.1, -1.0, Status::INEXACT),
|
||||
(0.5, 0.0, Status::INEXACT),
|
||||
(-0.5, -1.0, Status::INEXACT),
|
||||
(0.9, 0.0, Status::INEXACT),
|
||||
(-0.9, -1.0, Status::INEXACT),
|
||||
(1.1, 1.0, Status::INEXACT),
|
||||
(-1.1, -2.0, Status::INEXACT),
|
||||
(1.5, 1.0, Status::INEXACT),
|
||||
(-1.5, -2.0, Status::INEXACT),
|
||||
(1.9, 1.0, Status::INEXACT),
|
||||
(-1.9, -2.0, Status::INEXACT),
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/* Skipping f16 / f128 "sanity_check"s and spec cases due to rejected literal lexing at MSRV */
|
||||
#[track_caller]
|
||||
fn check<F: Float>(cases: &[(F, F, Status)]) {
|
||||
for &(x, exp_res, exp_stat) in cases {
|
||||
let FpResult { val, status } = floor_status(x);
|
||||
assert_biteq!(val, exp_res, "{x:?} {}", Hexf(x));
|
||||
assert_eq!(
|
||||
status,
|
||||
exp_stat,
|
||||
"{x:?} {} -> {exp_res:?} {}",
|
||||
Hexf(x),
|
||||
Hexf(exp_res)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(f16_enabled)]
|
||||
fn spec_tests_f16() {
|
||||
let cases = [];
|
||||
spec_test::<f16>(&cases);
|
||||
fn check_f16() {
|
||||
check::<f16>(&cases!(f16));
|
||||
check::<f16>(&[
|
||||
(hf16!("0x1p10"), hf16!("0x1p10"), Status::OK),
|
||||
(hf16!("-0x1p10"), hf16!("-0x1p10"), Status::OK),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_check_f32() {
|
||||
assert_eq!(floor(0.5f32), 0.0);
|
||||
assert_eq!(floor(1.1f32), 1.0);
|
||||
assert_eq!(floor(2.9f32), 2.0);
|
||||
fn check_f32() {
|
||||
check::<f32>(&cases!(f32));
|
||||
check::<f32>(&[
|
||||
(hf32!("0x1p23"), hf32!("0x1p23"), Status::OK),
|
||||
(hf32!("-0x1p23"), hf32!("-0x1p23"), Status::OK),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spec_tests_f32() {
|
||||
let cases = [
|
||||
(0.1, 0.0, Status::INEXACT),
|
||||
(-0.1, -1.0, Status::INEXACT),
|
||||
(0.9, 0.0, Status::INEXACT),
|
||||
(-0.9, -1.0, Status::INEXACT),
|
||||
(1.1, 1.0, Status::INEXACT),
|
||||
(-1.1, -2.0, Status::INEXACT),
|
||||
(1.9, 1.0, Status::INEXACT),
|
||||
(-1.9, -2.0, Status::INEXACT),
|
||||
];
|
||||
spec_test::<f32>(&cases);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_check_f64() {
|
||||
assert_eq!(floor(1.1f64), 1.0);
|
||||
assert_eq!(floor(2.9f64), 2.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spec_tests_f64() {
|
||||
let cases = [
|
||||
(0.1, 0.0, Status::INEXACT),
|
||||
(-0.1, -1.0, Status::INEXACT),
|
||||
(0.9, 0.0, Status::INEXACT),
|
||||
(-0.9, -1.0, Status::INEXACT),
|
||||
(1.1, 1.0, Status::INEXACT),
|
||||
(-1.1, -2.0, Status::INEXACT),
|
||||
(1.9, 1.0, Status::INEXACT),
|
||||
(-1.9, -2.0, Status::INEXACT),
|
||||
];
|
||||
spec_test::<f64>(&cases);
|
||||
fn check_f64() {
|
||||
check::<f64>(&cases!(f64));
|
||||
check::<f64>(&[
|
||||
(hf64!("0x1p52"), hf64!("0x1p52"), Status::OK),
|
||||
(hf64!("-0x1p52"), hf64!("-0x1p52"), Status::OK),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(f128_enabled)]
|
||||
fn spec_tests_f128() {
|
||||
let cases = [];
|
||||
spec_test::<f128>(&cases);
|
||||
check::<f128>(&cases!(f128));
|
||||
check::<f128>(&[
|
||||
(hf128!("0x1p112"), hf128!("0x1p112"), Status::OK),
|
||||
(hf128!("-0x1p112"), hf128!("-0x1p112"), Status::OK),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
//! - Otherwise, either `x` or `y`, canonicalized
|
||||
//! - -0.0 and +0.0 may be disregarded (unlike newer operations)
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
//! We do not treat sNaN and qNaN differently; even though IEEE technically requires this, (a call
|
||||
//! with sNaN should return qNaN rather than the other result), it breaks associativity so isn't
|
||||
//! desired behavior. C23 does not differentiate between sNaN and qNaN, so we do not either. More
|
||||
//! on the problems with `minNum` [here][minnum-removal].
|
||||
//!
|
||||
//! More on the differences: [link].
|
||||
//! IEEE also specifies that a sNaN in either argument should signal invalid, but we do not
|
||||
//! implement this.
|
||||
//!
|
||||
//! [link]: https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf
|
||||
//! [minnum-removal]: https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf
|
||||
|
||||
use crate::support::Float;
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
//! - +0.0 if x and y are zero with opposite signs
|
||||
//! - qNaN if either operation is NaN
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
//! Note that the IEEE 754-2019 specifies that a sNaN in either argument should signal invalid,
|
||||
//! but we do not implement this.
|
||||
|
||||
use crate::support::Float;
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
//! - Non-NaN if one operand is NaN
|
||||
//! - qNaN if both operands are NaNx
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
//! Note that the IEEE 754-2019 specifies that a sNaN in either argument should signal invalid,
|
||||
//! but we do not implement this.
|
||||
|
||||
use crate::support::Float;
|
||||
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
//! - Otherwise, either `x` or `y`, canonicalized
|
||||
//! - -0.0 and +0.0 may be disregarded (unlike newer operations)
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
//! We do not treat sNaN and qNaN differently; even though IEEE technically requires this, (a call
|
||||
//! with sNaN should return qNaN rather than the other result), it breaks associativity so isn't
|
||||
//! desired behavior. C23 does not differentiate between sNaN and qNaN, so we do not either. More
|
||||
//! on the problems with `minNum` [here][minnum-removal].
|
||||
//!
|
||||
//! More on the differences: [link].
|
||||
//! IEEE also specifies that a sNaN in either argument should signal invalid, but we do not
|
||||
//! implement this.
|
||||
//!
|
||||
//! [link]: https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf
|
||||
//! [minnum-removal]: https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf
|
||||
|
||||
use crate::support::Float;
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
//! - -0.0 if x and y are zero with opposite signs
|
||||
//! - qNaN if either operation is NaN
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
//! Note that the IEEE 754-2019 specifies that a sNaN in either argument should signal invalid,
|
||||
//! but we do not implement this.
|
||||
|
||||
use crate::support::Float;
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
//! - Non-NaN if one operand is NaN
|
||||
//! - qNaN if both operands are NaNx
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
//! Note that the IEEE 754-2019 specifies that a sNaN in either argument should signal invalid,
|
||||
//! but we do not implement this.
|
||||
|
||||
use crate::support::Float;
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
use super::super::{CastFrom, Float};
|
||||
|
||||
#[inline]
|
||||
pub fn frexp<F: Float>(x: F) -> (F, i32) {
|
||||
let mut ix = x.to_bits();
|
||||
let mut ee = x.ex() as i32;
|
||||
|
||||
if ee == 0 {
|
||||
if x == F::ZERO {
|
||||
return (x, 0);
|
||||
}
|
||||
|
||||
// Subnormals, needs to be normalized first
|
||||
ix &= F::SIG_MASK;
|
||||
(ee, ix) = F::normalize(ix);
|
||||
ix |= x.to_bits() & F::SIGN_MASK;
|
||||
} else if ee == F::EXP_SAT as i32 {
|
||||
// inf or NaN
|
||||
return (x, 0);
|
||||
}
|
||||
|
||||
let e = ee - (F::EXP_BIAS as i32 - 1);
|
||||
ix &= F::SIGN_MASK | F::SIG_MASK;
|
||||
ix |= F::Int::cast_from(F::EXP_BIAS - 1) << F::SIG_BITS;
|
||||
(F::from_bits(ix), e)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use super::super::{Float, MinInt};
|
||||
|
||||
const FP_ILOGBNAN: i32 = i32::MIN;
|
||||
const FP_ILOGB0: i32 = FP_ILOGBNAN;
|
||||
|
||||
#[inline]
|
||||
pub fn ilogb<F: Float>(x: F) -> i32 {
|
||||
let zero = F::Int::ZERO;
|
||||
let mut i = x.to_bits();
|
||||
let e = x.ex() as i32;
|
||||
|
||||
if e == 0 {
|
||||
i <<= F::EXP_BITS + 1;
|
||||
if i == F::Int::ZERO {
|
||||
force_eval!(0.0 / 0.0);
|
||||
return FP_ILOGB0;
|
||||
}
|
||||
/* subnormal x */
|
||||
let mut e = -(F::EXP_BIAS as i32);
|
||||
while i >> (F::BITS - 1) == zero {
|
||||
e -= 1;
|
||||
i <<= 1;
|
||||
}
|
||||
e
|
||||
} else if e == F::EXP_SAT as i32 {
|
||||
force_eval!(0.0 / 0.0);
|
||||
if i << (F::EXP_BITS + 1) != zero {
|
||||
FP_ILOGBNAN
|
||||
} else {
|
||||
i32::MAX
|
||||
}
|
||||
} else {
|
||||
e - F::EXP_BIAS as i32
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
mod fminimum;
|
||||
mod fminimum_num;
|
||||
mod fmod;
|
||||
mod frexp;
|
||||
mod ilogb;
|
||||
mod rint;
|
||||
mod round;
|
||||
mod scalbn;
|
||||
@@ -35,6 +37,8 @@
|
||||
pub use fminimum::fminimum;
|
||||
pub use fminimum_num::fminimum_num;
|
||||
pub use fmod::fmod;
|
||||
pub use frexp::frexp;
|
||||
pub use ilogb::ilogb;
|
||||
pub use rint::rint_round;
|
||||
pub use round::round;
|
||||
pub use scalbn::scalbn;
|
||||
|
||||
@@ -10,38 +10,34 @@ pub fn trunc<F: Float>(x: F) -> F {
|
||||
|
||||
#[inline]
|
||||
pub fn trunc_status<F: Float>(x: F) -> FpResult<F> {
|
||||
let mut xi: F::Int = x.to_bits();
|
||||
let xi: F::Int = x.to_bits();
|
||||
let e: i32 = x.exp_unbiased();
|
||||
|
||||
// C1: The represented value has no fractional part, so no truncation is needed
|
||||
// The represented value has no fractional part, so no truncation is needed
|
||||
if e >= F::SIG_BITS as i32 {
|
||||
return FpResult::ok(x);
|
||||
}
|
||||
|
||||
let mask = if e < 0 {
|
||||
// C2: If the exponent is negative, the result will be zero so we mask out everything
|
||||
let clear_mask = if e < 0 {
|
||||
// If the exponent is negative, the result will be zero so we clear everything
|
||||
// except the sign.
|
||||
F::SIGN_MASK
|
||||
!F::SIGN_MASK
|
||||
} else {
|
||||
// C3: Otherwise, we mask out the last `e` bits of the significand.
|
||||
!(F::SIG_MASK >> e.unsigned())
|
||||
// Otherwise, we keep `e` fractional bits and clear the rest.
|
||||
F::SIG_MASK >> e.unsigned()
|
||||
};
|
||||
|
||||
// C4: If the to-be-masked-out portion is already zero, we have an exact result
|
||||
if (xi & !mask) == IntTy::<F>::ZERO {
|
||||
return FpResult::ok(x);
|
||||
}
|
||||
|
||||
// C5: Otherwise the result is inexact and we will truncate. Raise `FE_INEXACT`, mask the
|
||||
// result, and return.
|
||||
|
||||
let status = if xi & F::SIG_MASK == F::Int::ZERO {
|
||||
let cleared = xi & clear_mask;
|
||||
let status = if cleared == IntTy::<F>::ZERO {
|
||||
// If the to-be-zeroed portion is already zero, we have an exact result.
|
||||
Status::OK
|
||||
} else {
|
||||
// Otherwise the result is inexact and we will truncate, so indicate `FE_INEXACT`.
|
||||
Status::INEXACT
|
||||
};
|
||||
xi &= mask;
|
||||
FpResult::new(F::from_bits(xi), status)
|
||||
|
||||
// Now zero the bits we need to truncate and return.
|
||||
FpResult::new(F::from_bits(xi ^ cleared), status)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -49,100 +45,83 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::support::Hexf;
|
||||
|
||||
fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
|
||||
let roundtrip = [
|
||||
F::ZERO,
|
||||
F::ONE,
|
||||
F::NEG_ONE,
|
||||
F::NEG_ZERO,
|
||||
F::INFINITY,
|
||||
F::NEG_INFINITY,
|
||||
];
|
||||
|
||||
for x in roundtrip {
|
||||
let FpResult { val, status } = trunc_status(x);
|
||||
assert_biteq!(val, x, "{}", Hexf(x));
|
||||
assert_eq!(status, Status::OK, "{}", Hexf(x));
|
||||
}
|
||||
|
||||
for &(x, res, res_stat) in cases {
|
||||
let FpResult { val, status } = trunc_status(x);
|
||||
assert_biteq!(val, res, "{}", Hexf(x));
|
||||
assert_eq!(status, res_stat, "{}", Hexf(x));
|
||||
}
|
||||
macro_rules! cases {
|
||||
($f:ty) => {
|
||||
[
|
||||
// roundtrip
|
||||
(0.0, 0.0, Status::OK),
|
||||
(-0.0, -0.0, Status::OK),
|
||||
(1.0, 1.0, Status::OK),
|
||||
(-1.0, -1.0, Status::OK),
|
||||
(<$f>::INFINITY, <$f>::INFINITY, Status::OK),
|
||||
(<$f>::NEG_INFINITY, <$f>::NEG_INFINITY, Status::OK),
|
||||
// with rounding
|
||||
(0.1, 0.0, Status::INEXACT),
|
||||
(-0.1, -0.0, Status::INEXACT),
|
||||
(0.5, 0.0, Status::INEXACT),
|
||||
(-0.5, -0.0, Status::INEXACT),
|
||||
(0.9, 0.0, Status::INEXACT),
|
||||
(-0.9, -0.0, Status::INEXACT),
|
||||
(1.1, 1.0, Status::INEXACT),
|
||||
(-1.1, -1.0, Status::INEXACT),
|
||||
(1.5, 1.0, Status::INEXACT),
|
||||
(-1.5, -1.0, Status::INEXACT),
|
||||
(1.9, 1.0, Status::INEXACT),
|
||||
(-1.9, -1.0, Status::INEXACT),
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/* Skipping f16 / f128 "sanity_check"s and spec cases due to rejected literal lexing at MSRV */
|
||||
#[track_caller]
|
||||
fn check<F: Float>(cases: &[(F, F, Status)]) {
|
||||
for &(x, exp_res, exp_stat) in cases {
|
||||
let FpResult { val, status } = trunc_status(x);
|
||||
assert_biteq!(val, exp_res, "{x:?} {}", Hexf(x));
|
||||
assert_eq!(
|
||||
status,
|
||||
exp_stat,
|
||||
"{x:?} {} -> {exp_res:?} {}",
|
||||
Hexf(x),
|
||||
Hexf(exp_res)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(f16_enabled)]
|
||||
fn spec_tests_f16() {
|
||||
let cases = [];
|
||||
spec_test::<f16>(&cases);
|
||||
fn check_f16() {
|
||||
check::<f16>(&cases!(f16));
|
||||
check::<f16>(&[
|
||||
(hf16!("0x1p10"), hf16!("0x1p10"), Status::OK),
|
||||
(hf16!("-0x1p10"), hf16!("-0x1p10"), Status::OK),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_check_f32() {
|
||||
assert_eq!(trunc(0.5f32), 0.0);
|
||||
assert_eq!(trunc(1.1f32), 1.0);
|
||||
assert_eq!(trunc(2.9f32), 2.0);
|
||||
fn check_f32() {
|
||||
check::<f32>(&cases!(f32));
|
||||
check::<f32>(&[
|
||||
(hf32!("0x1p23"), hf32!("0x1p23"), Status::OK),
|
||||
(hf32!("-0x1p23"), hf32!("-0x1p23"), Status::OK),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spec_tests_f32() {
|
||||
let cases = [
|
||||
(0.1, 0.0, Status::INEXACT),
|
||||
(-0.1, -0.0, Status::INEXACT),
|
||||
(0.9, 0.0, Status::INEXACT),
|
||||
(-0.9, -0.0, Status::INEXACT),
|
||||
(1.1, 1.0, Status::INEXACT),
|
||||
(-1.1, -1.0, Status::INEXACT),
|
||||
(1.9, 1.0, Status::INEXACT),
|
||||
(-1.9, -1.0, Status::INEXACT),
|
||||
];
|
||||
spec_test::<f32>(&cases);
|
||||
|
||||
assert_biteq!(trunc(1.1f32), 1.0);
|
||||
assert_biteq!(trunc(1.1f64), 1.0);
|
||||
|
||||
// C1
|
||||
assert_biteq!(trunc(hf32!("0x1p23")), hf32!("0x1p23"));
|
||||
assert_biteq!(trunc(hf64!("0x1p52")), hf64!("0x1p52"));
|
||||
assert_biteq!(trunc(hf32!("-0x1p23")), hf32!("-0x1p23"));
|
||||
assert_biteq!(trunc(hf64!("-0x1p52")), hf64!("-0x1p52"));
|
||||
|
||||
// C2
|
||||
assert_biteq!(trunc(hf32!("0x1p-1")), 0.0);
|
||||
assert_biteq!(trunc(hf64!("0x1p-1")), 0.0);
|
||||
assert_biteq!(trunc(hf32!("-0x1p-1")), -0.0);
|
||||
assert_biteq!(trunc(hf64!("-0x1p-1")), -0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_check_f64() {
|
||||
assert_eq!(trunc(1.1f64), 1.0);
|
||||
assert_eq!(trunc(2.9f64), 2.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spec_tests_f64() {
|
||||
let cases = [
|
||||
(0.1, 0.0, Status::INEXACT),
|
||||
(-0.1, -0.0, Status::INEXACT),
|
||||
(0.9, 0.0, Status::INEXACT),
|
||||
(-0.9, -0.0, Status::INEXACT),
|
||||
(1.1, 1.0, Status::INEXACT),
|
||||
(-1.1, -1.0, Status::INEXACT),
|
||||
(1.9, 1.0, Status::INEXACT),
|
||||
(-1.9, -1.0, Status::INEXACT),
|
||||
];
|
||||
spec_test::<f64>(&cases);
|
||||
fn check_f64() {
|
||||
check::<f64>(&cases!(f64));
|
||||
check::<f64>(&[
|
||||
(hf64!("0x1p52"), hf64!("0x1p52"), Status::OK),
|
||||
(hf64!("-0x1p52"), hf64!("-0x1p52"), Status::OK),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(f128_enabled)]
|
||||
fn spec_tests_f128() {
|
||||
let cases = [];
|
||||
spec_test::<f128>(&cases);
|
||||
check::<f128>(&cases!(f128));
|
||||
check::<f128>(&[
|
||||
(hf128!("0x1p112"), hf128!("0x1p112"), Status::OK),
|
||||
(hf128!("-0x1p112"), hf128!("-0x1p112"), Status::OK),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,25 @@
|
||||
const FP_ILOGBNAN: i32 = -1 - 0x7fffffff;
|
||||
const FP_ILOGB0: i32 = FP_ILOGBNAN;
|
||||
/// Extract the binary exponent of `x`.
|
||||
#[cfg(f16_enabled)]
|
||||
#[cfg_attr(assert_no_panic, no_panic::no_panic)]
|
||||
pub fn ilogbf16(x: f16) -> i32 {
|
||||
super::generic::ilogb(x)
|
||||
}
|
||||
|
||||
/// Extract the binary exponent of `x`.
|
||||
#[cfg_attr(assert_no_panic, no_panic::no_panic)]
|
||||
pub fn ilogbf(x: f32) -> i32 {
|
||||
super::generic::ilogb(x)
|
||||
}
|
||||
|
||||
/// Extract the binary exponent of `x`.
|
||||
#[cfg_attr(assert_no_panic, no_panic::no_panic)]
|
||||
pub fn ilogb(x: f64) -> i32 {
|
||||
let mut i: u64 = x.to_bits();
|
||||
let e = ((i >> 52) & 0x7ff) as i32;
|
||||
|
||||
if e == 0 {
|
||||
i <<= 12;
|
||||
if i == 0 {
|
||||
force_eval!(0.0 / 0.0);
|
||||
return FP_ILOGB0;
|
||||
}
|
||||
/* subnormal x */
|
||||
let mut e = -0x3ff;
|
||||
while (i >> 63) == 0 {
|
||||
e -= 1;
|
||||
i <<= 1;
|
||||
}
|
||||
e
|
||||
} else if e == 0x7ff {
|
||||
force_eval!(0.0 / 0.0);
|
||||
if (i << 12) != 0 {
|
||||
FP_ILOGBNAN
|
||||
} else {
|
||||
i32::MAX
|
||||
}
|
||||
} else {
|
||||
e - 0x3ff
|
||||
}
|
||||
super::generic::ilogb(x)
|
||||
}
|
||||
|
||||
/// Extract the binary exponent of `x`.
|
||||
#[cfg(f128_enabled)]
|
||||
#[cfg_attr(assert_no_panic, no_panic::no_panic)]
|
||||
pub fn ilogbf128(x: f128) -> i32 {
|
||||
super::generic::ilogb(x)
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
const FP_ILOGBNAN: i32 = -1 - 0x7fffffff;
|
||||
const FP_ILOGB0: i32 = FP_ILOGBNAN;
|
||||
|
||||
#[cfg_attr(assert_no_panic, no_panic::no_panic)]
|
||||
pub fn ilogbf(x: f32) -> i32 {
|
||||
let mut i = x.to_bits();
|
||||
let e = ((i >> 23) & 0xff) as i32;
|
||||
|
||||
if e == 0 {
|
||||
i <<= 9;
|
||||
if i == 0 {
|
||||
force_eval!(0.0 / 0.0);
|
||||
return FP_ILOGB0;
|
||||
}
|
||||
/* subnormal x */
|
||||
let mut e = -0x7f;
|
||||
while (i >> 31) == 0 {
|
||||
e -= 1;
|
||||
i <<= 1;
|
||||
}
|
||||
e
|
||||
} else if e == 0xff {
|
||||
force_eval!(0.0 / 0.0);
|
||||
if (i << 9) != 0 { FP_ILOGBNAN } else { i32::MAX }
|
||||
} else {
|
||||
e - 0x7f
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,7 @@
|
||||
*/
|
||||
|
||||
use super::{floor, k_cos, k_sin, log};
|
||||
use crate::support::unchecked_div_i32;
|
||||
|
||||
const PI: f64 = 3.14159265358979311600e+00; /* 0x400921FB, 0x54442D18 */
|
||||
const A0: f64 = 7.72156649015328655494e-02; /* 0x3FB3C467, 0xE37DB0C8 */
|
||||
@@ -152,7 +153,8 @@ fn sin_pi(mut x: f64) -> f64 {
|
||||
x = 2.0 * (x * 0.5 - floor(x * 0.5)); /* x mod 2.0 */
|
||||
|
||||
n = (x * 4.0) as i32;
|
||||
n = div!(n + 1, 2);
|
||||
// SAFETY: nonzero divisor, nonnegative dividend (`n < 8`).
|
||||
n = unsafe { unchecked_div_i32(n + 1, 2) };
|
||||
x -= (n as f64) * 0.5;
|
||||
x *= PI;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
use super::{floorf, k_cosf, k_sinf, logf};
|
||||
use crate::support::unchecked_div_isize;
|
||||
|
||||
const PI: f32 = 3.1415927410e+00; /* 0x40490fdb */
|
||||
const A0: f32 = 7.7215664089e-02; /* 0x3d9e233f */
|
||||
@@ -88,7 +89,8 @@ fn sin_pi(mut x: f32) -> f32 {
|
||||
x = 2.0 * (x * 0.5 - floorf(x * 0.5)); /* x mod 2.0 */
|
||||
|
||||
n = (x * 4.0) as isize;
|
||||
n = div!(n + 1, 2);
|
||||
// SAFETY: nonzero divisor, nonnegative dividend (`n < 8`).
|
||||
n = unsafe { unchecked_div_isize(n + 1, 2) };
|
||||
y = (x as f64) - (n as f64) * 0.5;
|
||||
y *= 3.14159265358979323846;
|
||||
match n {
|
||||
|
||||
@@ -58,24 +58,6 @@ macro_rules! i {
|
||||
};
|
||||
}
|
||||
|
||||
// Temporary macro to avoid panic codegen for division (in debug mode too). At
|
||||
// the time of this writing this is only used in a few places, and once
|
||||
// rust-lang/rust#72751 is fixed then this macro will no longer be necessary and
|
||||
// the native `/` operator can be used and panics won't be codegen'd.
|
||||
#[cfg(any(debug_assertions, not(intrinsics_enabled)))]
|
||||
macro_rules! div {
|
||||
($a:expr, $b:expr) => {
|
||||
$a / $b
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(all(not(debug_assertions), intrinsics_enabled))]
|
||||
macro_rules! div {
|
||||
($a:expr, $b:expr) => {
|
||||
unsafe { core::intrinsics::unchecked_div($a, $b) }
|
||||
};
|
||||
}
|
||||
|
||||
// `support` may be public for testing
|
||||
#[macro_use]
|
||||
#[cfg(feature = "unstable-public-internals")]
|
||||
@@ -166,11 +148,9 @@ macro_rules! div {
|
||||
mod fminimum_fmaximum_num;
|
||||
mod fmod;
|
||||
mod frexp;
|
||||
mod frexpf;
|
||||
mod hypot;
|
||||
mod hypotf;
|
||||
mod ilogb;
|
||||
mod ilogbf;
|
||||
mod j0;
|
||||
mod j0f;
|
||||
mod j1;
|
||||
@@ -260,12 +240,10 @@ macro_rules! div {
|
||||
pub use self::fminimum_fmaximum::{fmaximum, fmaximumf, fminimum, fminimumf};
|
||||
pub use self::fminimum_fmaximum_num::{fmaximum_num, fmaximum_numf, fminimum_num, fminimum_numf};
|
||||
pub use self::fmod::{fmod, fmodf};
|
||||
pub use self::frexp::frexp;
|
||||
pub use self::frexpf::frexpf;
|
||||
pub use self::frexp::{frexp, frexpf};
|
||||
pub use self::hypot::hypot;
|
||||
pub use self::hypotf::hypotf;
|
||||
pub use self::ilogb::ilogb;
|
||||
pub use self::ilogbf::ilogbf;
|
||||
pub use self::ilogb::{ilogb, ilogbf};
|
||||
pub use self::j0::{j0, y0};
|
||||
pub use self::j0f::{j0f, y0f};
|
||||
pub use self::j1::{j1, y1};
|
||||
@@ -326,6 +304,8 @@ macro_rules! div {
|
||||
pub use self::fminimum_fmaximum::{fmaximumf16, fminimumf16};
|
||||
pub use self::fminimum_fmaximum_num::{fmaximum_numf16, fminimum_numf16};
|
||||
pub use self::fmod::fmodf16;
|
||||
pub use self::frexp::frexpf16;
|
||||
pub use self::ilogb::ilogbf16;
|
||||
pub use self::ldexp::ldexpf16;
|
||||
pub use self::rint::rintf16;
|
||||
pub use self::round::roundf16;
|
||||
@@ -353,6 +333,8 @@ macro_rules! div {
|
||||
pub use self::fminimum_fmaximum::{fmaximumf128, fminimumf128};
|
||||
pub use self::fminimum_fmaximum_num::{fmaximum_numf128, fminimum_numf128};
|
||||
pub use self::fmod::fmodf128;
|
||||
pub use self::frexp::frexpf128;
|
||||
pub use self::ilogb::ilogbf128;
|
||||
pub use self::ldexp::ldexpf128;
|
||||
pub use self::rint::rintf128;
|
||||
pub use self::round::roundf128;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#![allow(unused_unsafe)]
|
||||
// FIXME(clippy): the suggested fix is bad but the code as-written could be better
|
||||
#![allow(clippy::explicit_counter_loop)]
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/k_rem_pio2.c */
|
||||
/*
|
||||
* ====================================================
|
||||
@@ -255,7 +257,7 @@ extern "C" fn floor(x: f64) -> f64 {
|
||||
|
||||
/* determine jx,jv,q0, note that 3>q0 */
|
||||
let jx = nx - 1;
|
||||
let mut jv = div!(e0 - 3, 24);
|
||||
let mut jv = (e0 - 3) / 24;
|
||||
if jv < 0 {
|
||||
jv = 0;
|
||||
}
|
||||
|
||||
@@ -122,8 +122,6 @@ pub fn sincosf(x: f32) -> (f32, f32) {
|
||||
}
|
||||
}
|
||||
|
||||
// PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520
|
||||
#[cfg(not(target_arch = "powerpc64"))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::sincosf;
|
||||
|
||||
@@ -261,8 +261,6 @@ fn shr_u256() {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[cfg(debug_assertions)]
|
||||
// FIXME(ppc): ppc64le seems to have issues with `should_panic` tests.
|
||||
#[cfg(not(all(target_arch = "powerpc64", target_endian = "little")))]
|
||||
fn shr_u256_overflow() {
|
||||
// Like regular shr, panic on overflow with debug assertions
|
||||
let _ = u256::MAX >> 256;
|
||||
|
||||
@@ -49,10 +49,14 @@ pub enum Round {
|
||||
}
|
||||
|
||||
/// IEEE 754 exception status flags.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Status(u8);
|
||||
|
||||
impl Status {
|
||||
/* Note that if we ever store/load this to/from floating point control status registers, it
|
||||
* may be worth making these values platform-dependent to line up with register layout
|
||||
* to avoid bit swapping. For the time being, this isn't a concern. */
|
||||
|
||||
/// Default status indicating no errors.
|
||||
pub const OK: Self = Self(0);
|
||||
|
||||
@@ -74,7 +78,7 @@ impl Status {
|
||||
/// result is -inf.
|
||||
/// `x / y` when `x != 0.0` and `y == 0.0`,
|
||||
#[cfg_attr(not(feature = "unstable-public-internals"), allow(dead_code))]
|
||||
pub const DIVIDE_BY_ZERO: Self = Self(1 << 2);
|
||||
pub const DIVIDE_BY_ZERO: Self = Self(1 << 1);
|
||||
|
||||
/// The result exceeds the maximum finite value.
|
||||
///
|
||||
@@ -82,14 +86,14 @@ impl Status {
|
||||
/// on the intermediate result. `Zero` rounds to the signed maximum finite. `Positive` and
|
||||
/// `Negative` round to signed maximum finite in one direction, signed infinity in the other.
|
||||
#[cfg_attr(not(feature = "unstable-public-internals"), allow(dead_code))]
|
||||
pub const OVERFLOW: Self = Self(1 << 3);
|
||||
pub const OVERFLOW: Self = Self(1 << 2);
|
||||
|
||||
/// The result is subnormal and lost precision.
|
||||
pub const UNDERFLOW: Self = Self(1 << 4);
|
||||
pub const UNDERFLOW: Self = Self(1 << 3);
|
||||
|
||||
/// The finite-precision result does not match that of infinite precision, and the reason
|
||||
/// is not represented by one of the other flags.
|
||||
pub const INEXACT: Self = Self(1 << 5);
|
||||
pub const INEXACT: Self = Self(1 << 4);
|
||||
|
||||
/// True if `UNDERFLOW` is set.
|
||||
#[cfg_attr(not(feature = "unstable-public-internals"), allow(dead_code))]
|
||||
@@ -128,3 +132,38 @@ pub(crate) const fn with(self, rhs: Self) -> Self {
|
||||
Self(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "unstable-public-internals"))]
|
||||
impl core::fmt::Debug for Status {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
// shift -> flag map
|
||||
let names = &[
|
||||
"INVALID",
|
||||
"DIVIDE_BY_ZERO",
|
||||
"OVERFLOW",
|
||||
"UNDERFLOW",
|
||||
"INEXACT",
|
||||
];
|
||||
|
||||
write!(f, "Status(")?;
|
||||
let mut any = false;
|
||||
for shift in 0..u8::BITS {
|
||||
if self.0 & (1 << shift) != 0 {
|
||||
if any {
|
||||
write!(f, " | ")?;
|
||||
}
|
||||
match names.get(shift as usize) {
|
||||
Some(name) => write!(f, "{name}")?,
|
||||
None => write!(f, "UNKNOWN(1 << {shift})")?,
|
||||
}
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
|
||||
if !any {
|
||||
write!(f, "OK")?;
|
||||
}
|
||||
write!(f, ")")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
use super::int_traits::{CastFrom, Int, MinInt};
|
||||
|
||||
/// Whether MIPS sNaN/qNaNs should be used.
|
||||
///
|
||||
/// Note that MIPS is doing some general migration here, though this is only available on (rare)
|
||||
/// modern MIPS hardware per discussion at <https://github.com/WebAssembly/design/issues/976>.
|
||||
const MIPS_NAN: bool = cfg!(target_arch = "mips") || cfg!(target_arch = "mips64");
|
||||
|
||||
/// Trait for some basic operations on floats
|
||||
// #[allow(dead_code)]
|
||||
#[allow(dead_code)] // Some constants are only used with tests
|
||||
@@ -34,10 +40,16 @@ pub trait Float:
|
||||
const NEG_ONE: Self;
|
||||
const INFINITY: Self;
|
||||
const NEG_INFINITY: Self;
|
||||
const NAN: Self;
|
||||
const NEG_NAN: Self;
|
||||
const MAX: Self;
|
||||
const MIN: Self;
|
||||
|
||||
/// Quiet NaN.
|
||||
const NAN: Self;
|
||||
/// Signaling NaN.
|
||||
const SNAN: Self;
|
||||
const NEG_NAN: Self;
|
||||
const NEG_SNAN: Self;
|
||||
|
||||
const EPSILON: Self;
|
||||
const PI: Self;
|
||||
const NEG_PI: Self;
|
||||
@@ -84,6 +96,9 @@ pub trait Float:
|
||||
/// The implicit bit of the float format
|
||||
const IMPLICIT_BIT: Self::Int;
|
||||
|
||||
/// A mask for the top bit of the significand, useful for NaN ops.
|
||||
const SIG_TOP_BIT: Self::Int;
|
||||
|
||||
/// Returns `self` transmuted to `Self::Int`
|
||||
fn to_bits(self) -> Self::Int;
|
||||
|
||||
@@ -116,6 +131,24 @@ fn eq_repr(self, rhs: Self) -> bool {
|
||||
/// Returns true if the value is NaN.
|
||||
fn is_nan(self) -> bool;
|
||||
|
||||
fn is_qnan(self) -> bool {
|
||||
if !self.is_nan() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let top = self.to_bits() & Self::SIG_TOP_BIT;
|
||||
|
||||
if MIPS_NAN {
|
||||
top == Self::Int::ZERO
|
||||
} else {
|
||||
top != Self::Int::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
fn is_snan(self) -> bool {
|
||||
self.is_nan() && !self.is_qnan()
|
||||
}
|
||||
|
||||
/// Returns true if the value is +inf or -inf.
|
||||
fn is_infinite(self) -> bool;
|
||||
|
||||
@@ -176,7 +209,6 @@ fn from_parts(negative: bool, exponent: u32, significand: Self::Int) -> Self {
|
||||
fn fma(self, y: Self, z: Self) -> Self;
|
||||
|
||||
/// Returns (normalized exponent, normalized significand)
|
||||
#[allow(dead_code)]
|
||||
fn normalize(significand: Self::Int) -> (i32, Self::Int);
|
||||
|
||||
/// Returns a number that represents the sign of self.
|
||||
@@ -224,13 +256,28 @@ impl Float for $ty {
|
||||
const NEG_ONE: Self = -1.0;
|
||||
const INFINITY: Self = Self::INFINITY;
|
||||
const NEG_INFINITY: Self = Self::NEG_INFINITY;
|
||||
const NAN: Self = Self::NAN;
|
||||
// NAN isn't guaranteed to be positive but it usually is. We only use this for
|
||||
// tests.
|
||||
const NEG_NAN: Self = $from_bits($to_bits(Self::NAN) | Self::SIGN_MASK);
|
||||
const MAX: Self = -Self::MIN;
|
||||
// Sign bit set, saturated mantissa, saturated exponent with last bit zeroed
|
||||
const MIN: Self = $from_bits(Self::Int::MAX & !(1 << Self::SIG_BITS));
|
||||
|
||||
// The default NaN seems to set one of the top two significand bits on most
|
||||
// platforms (sNaN vs. qNaN). For mips, the significand is all 1s (with the
|
||||
// exception of the signaling bit).
|
||||
const NAN: Self = $from_bits(if MIPS_NAN {
|
||||
Self::EXP_MASK | (Self::SIG_TOP_BIT - 1)
|
||||
} else {
|
||||
Self::EXP_MASK | Self::SIG_TOP_BIT
|
||||
});
|
||||
const SNAN: Self = $from_bits(if MIPS_NAN {
|
||||
Self::EXP_MASK | Self::SIG_MASK
|
||||
} else {
|
||||
Self::EXP_MASK | (Self::SIG_TOP_BIT >> 1)
|
||||
});
|
||||
// NAN isn't guaranteed to be positive but it usually is. We only use these for
|
||||
// tests.
|
||||
const NEG_NAN: Self = $from_bits($to_bits(Self::NAN) | Self::SIGN_MASK);
|
||||
const NEG_SNAN: Self = $from_bits($to_bits(Self::SNAN) | Self::SIGN_MASK);
|
||||
|
||||
const EPSILON: Self = <$ty>::EPSILON;
|
||||
|
||||
// Exponent is a 1 in the LSB
|
||||
@@ -247,6 +294,7 @@ impl Float for $ty {
|
||||
const SIG_MASK: Self::Int = (1 << Self::SIG_BITS) - 1;
|
||||
const EXP_MASK: Self::Int = !(Self::SIGN_MASK | Self::SIG_MASK);
|
||||
const IMPLICIT_BIT: Self::Int = 1 << Self::SIG_BITS;
|
||||
const SIG_TOP_BIT: Self::Int = Self::IMPLICIT_BIT >> 1;
|
||||
|
||||
fn to_bits(self) -> Self::Int {
|
||||
self.to_bits()
|
||||
@@ -295,10 +343,7 @@ fn fma(self, y: Self, z: Self) -> Self {
|
||||
}
|
||||
fn normalize(significand: Self::Int) -> (i32, Self::Int) {
|
||||
let shift = significand.leading_zeros().wrapping_sub(Self::EXP_BITS);
|
||||
(
|
||||
1i32.wrapping_sub(shift as i32),
|
||||
significand << shift as Self::Int,
|
||||
)
|
||||
(1i32.wrapping_sub(shift as i32), significand << shift)
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -454,6 +499,17 @@ fn check_f16() {
|
||||
assert_eq!(f16::EXP_MAX, 15);
|
||||
assert_eq!(f16::EXP_MIN, -14);
|
||||
assert_eq!(f16::EXP_MIN_SUBNORM, -24);
|
||||
assert_biteq!(f16::NAN, <f16 as Float>::NAN);
|
||||
// Value of NAN and FLT16_SNAN in C. We don't strictly need to match up, but it is good to
|
||||
// be aware if there are platforms where we don't.
|
||||
if MIPS_NAN {
|
||||
assert_biteq!(f16::NAN, f16::from_bits(0x7fbf));
|
||||
assert_biteq!(f16::SNAN, f16::from_bits(0x7fff));
|
||||
} else {
|
||||
assert_biteq!(f16::NAN, f16::from_bits(0x7e00));
|
||||
assert_biteq!(f16::SNAN, f16::from_bits(0x7d00));
|
||||
}
|
||||
assert!(f16::NAN.is_qnan());
|
||||
|
||||
// `exp_unbiased`
|
||||
assert_eq!(f16::FRAC_PI_2.exp_unbiased(), 0);
|
||||
@@ -480,6 +536,21 @@ fn check_f32() {
|
||||
assert_eq!(f32::EXP_MAX, 127);
|
||||
assert_eq!(f32::EXP_MIN, -126);
|
||||
assert_eq!(f32::EXP_MIN_SUBNORM, -149);
|
||||
assert_biteq!(f32::NAN, <f32 as Float>::NAN);
|
||||
// Value of NAN and FLT_SNAN in C. We don't strictly need to match up, but it is good to
|
||||
// be aware if there are platforms where we don't.
|
||||
if MIPS_NAN {
|
||||
assert_biteq!(f32::NAN, f32::from_bits(0x7fbfffff));
|
||||
assert_biteq!(f32::SNAN, f32::from_bits(0x7fffffff));
|
||||
} else {
|
||||
assert_biteq!(f32::NAN, f32::from_bits(0x7fc00000));
|
||||
assert_biteq!(f32::SNAN, f32::from_bits(0x7fa00000));
|
||||
}
|
||||
assert!(f32::NAN.is_qnan());
|
||||
// FIXME(rust-lang/rust#115567): x87 use in `is_snan` quiets the sNaN
|
||||
if !cfg!(x86_no_sse) {
|
||||
assert!(f32::SNAN.is_snan());
|
||||
}
|
||||
|
||||
// `exp_unbiased`
|
||||
assert_eq!(f32::FRAC_PI_2.exp_unbiased(), 0);
|
||||
@@ -510,6 +581,21 @@ fn check_f64() {
|
||||
assert_eq!(f64::EXP_MAX, 1023);
|
||||
assert_eq!(f64::EXP_MIN, -1022);
|
||||
assert_eq!(f64::EXP_MIN_SUBNORM, -1074);
|
||||
assert_biteq!(f64::NAN, <f64 as Float>::NAN);
|
||||
// Value of NAN and DBL_SNAN in C. We don't strictly need to match up, but it is good to
|
||||
// be aware if there are platforms where we don't.
|
||||
if MIPS_NAN {
|
||||
assert_biteq!(f64::NAN, f64::from_bits(0x7ff7ffffffffffff));
|
||||
assert_biteq!(f64::SNAN, f64::from_bits(0x7fffffffffffffff));
|
||||
} else {
|
||||
assert_biteq!(f64::NAN, f64::from_bits(0x7ff8000000000000));
|
||||
assert_biteq!(f64::SNAN, f64::from_bits(0x7ff4000000000000));
|
||||
}
|
||||
assert!(f64::NAN.is_qnan());
|
||||
// FIXME(rust-lang/rust#115567): x87 use in `is_snan` quiets the sNaN
|
||||
if !cfg!(x86_no_sse) {
|
||||
assert!(f64::SNAN.is_snan());
|
||||
}
|
||||
|
||||
// `exp_unbiased`
|
||||
assert_eq!(f64::FRAC_PI_2.exp_unbiased(), 0);
|
||||
@@ -541,6 +627,30 @@ fn check_f128() {
|
||||
assert_eq!(f128::EXP_MAX, 16383);
|
||||
assert_eq!(f128::EXP_MIN, -16382);
|
||||
assert_eq!(f128::EXP_MIN_SUBNORM, -16494);
|
||||
assert_biteq!(f128::NAN, <f128 as Float>::NAN);
|
||||
// Value of NAN and FLT128_SNAN in C. We don't strictly need to match up, but it is good to
|
||||
// be aware if there are platforms where we don't.
|
||||
if MIPS_NAN {
|
||||
assert_biteq!(
|
||||
f128::NAN,
|
||||
f128::from_bits(0x7fff7fffffffffffffffffffffffffff)
|
||||
);
|
||||
assert_biteq!(
|
||||
f128::SNAN,
|
||||
f128::from_bits(0x7fffffffffffffffffffffffffffffff)
|
||||
);
|
||||
} else {
|
||||
assert_biteq!(
|
||||
f128::NAN,
|
||||
f128::from_bits(0x7fff8000000000000000000000000000)
|
||||
);
|
||||
assert_biteq!(
|
||||
f128::SNAN,
|
||||
f128::from_bits(0x7fff4000000000000000000000000000)
|
||||
);
|
||||
}
|
||||
assert!(f128::NAN.is_qnan());
|
||||
assert!(f128::SNAN.is_snan());
|
||||
|
||||
// `exp_unbiased`
|
||||
assert_eq!(f128::FRAC_PI_2.exp_unbiased(), 0);
|
||||
|
||||
@@ -328,7 +328,7 @@ const fn hex_digit(c: u8) -> Option<u8> {
|
||||
mod hex_fmt {
|
||||
use core::fmt;
|
||||
|
||||
use crate::support::Float;
|
||||
use crate::support::{Float, Int};
|
||||
|
||||
/// Format a floating point number as its IEEE hex (`%a`) representation.
|
||||
pub struct Hexf<F>(pub F);
|
||||
@@ -340,8 +340,10 @@ pub(super) fn fmt_any_hex<F: Float>(x: &F, f: &mut fmt::Formatter<'_>) -> fmt::R
|
||||
write!(f, "-")?;
|
||||
}
|
||||
|
||||
if x.is_nan() {
|
||||
return write!(f, "NaN");
|
||||
if x.is_snan() {
|
||||
return write!(f, "sNaN");
|
||||
} else if x.is_nan() {
|
||||
return write!(f, "qNaN");
|
||||
} else if x.is_infinite() {
|
||||
return write!(f, "inf");
|
||||
} else if *x == F::ZERO {
|
||||
@@ -419,7 +421,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let _ = f;
|
||||
unimplemented!()
|
||||
} else {
|
||||
fmt::LowerHex::fmt(&self.0, f)
|
||||
write!(f, "{:#010x}", self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -456,6 +458,53 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Hexi<F>(pub F);
|
||||
|
||||
impl<I: Int> fmt::LowerHex for Hexi<I> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "compiler-builtins")] {
|
||||
let _ = f;
|
||||
unimplemented!()
|
||||
} else {
|
||||
write!(f, "{:#0width$x}", self.0, width = ((I::BITS / 4) + 2) as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Hexi<T>
|
||||
where
|
||||
Hexi<T>: fmt::LowerHex,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "compiler-builtins")] {
|
||||
let _ = f;
|
||||
unimplemented!()
|
||||
} else {
|
||||
fmt::LowerHex::fmt(self, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Display for Hexi<T>
|
||||
where
|
||||
Hexi<T>: fmt::LowerHex,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "compiler-builtins")] {
|
||||
let _ = f;
|
||||
unimplemented!()
|
||||
} else {
|
||||
fmt::LowerHex::fmt(self, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "unstable-public-internals"))]
|
||||
@@ -464,6 +513,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
#[cfg(test)]
|
||||
mod parse_tests {
|
||||
extern crate std;
|
||||
use std::string::String;
|
||||
use std::{format, println};
|
||||
|
||||
use super::*;
|
||||
@@ -512,6 +562,16 @@ fn rounding_properties(s: &str) -> Result<(), HexFloatParseError> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(not(f16_enabled), expect(unused))]
|
||||
pub fn canonicalize_snan_str(s: String) -> String {
|
||||
if s.contains("sNaN") || s.contains("qNaN") {
|
||||
s.replace("sNaN", "NaN").replace("qNaN", "NaN")
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(f16_enabled)]
|
||||
fn test_rounding() {
|
||||
@@ -519,7 +579,11 @@ fn test_rounding() {
|
||||
for i in -n..n {
|
||||
let u = i.rotate_right(11) as u32;
|
||||
let s = format!("{}", Hexf(f32::from_bits(u)));
|
||||
assert!(rounding_properties(&s).is_ok());
|
||||
let s = canonicalize_snan_str(s);
|
||||
match rounding_properties(&s) {
|
||||
Ok(()) => (),
|
||||
Err(e) => panic!("failed rounding properties for `{s}`: {e:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,8 +910,6 @@ fn test_macros() {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
// FIXME(ppc): something with `should_panic` tests cause a SIGILL with ppc64le
|
||||
#[cfg(not(all(target_arch = "powerpc64", target_endian = "little")))]
|
||||
mod tests_panicking {
|
||||
extern crate std;
|
||||
use super::*;
|
||||
@@ -1054,8 +1116,11 @@ fn test_f16() {
|
||||
use std::format;
|
||||
// Exhaustively check that `f16` roundtrips.
|
||||
for x in 0..=u16::MAX {
|
||||
use super::parse_tests::canonicalize_snan_str;
|
||||
|
||||
let f = f16::from_bits(x);
|
||||
let s = format!("{}", Hexf(f));
|
||||
let s = canonicalize_snan_str(s);
|
||||
let from_s = hf16(&s);
|
||||
|
||||
if f.is_nan() && from_s.is_nan() {
|
||||
@@ -1074,6 +1139,8 @@ fn test_f16() {
|
||||
#[cfg(f16_enabled)]
|
||||
fn test_f16_to_f32() {
|
||||
use std::format;
|
||||
|
||||
use super::parse_tests::canonicalize_snan_str;
|
||||
// Exhaustively check that these are equivalent for all `f16`:
|
||||
// - `f16 -> f32`
|
||||
// - `f16 -> str -> f32`
|
||||
@@ -1082,8 +1149,10 @@ fn test_f16_to_f32() {
|
||||
for x in 0..=u16::MAX {
|
||||
let f16 = f16::from_bits(x);
|
||||
let s16 = format!("{}", Hexf(f16));
|
||||
let s16 = canonicalize_snan_str(s16);
|
||||
let f32 = f16 as f32;
|
||||
let s32 = format!("{}", Hexf(f32));
|
||||
let s32 = canonicalize_snan_str(s32);
|
||||
|
||||
let a = hf32(&s16);
|
||||
let b = hf32(&s32);
|
||||
@@ -1124,8 +1193,17 @@ fn spot_checks() {
|
||||
assert_eq!(Hexf(f32::NEG_ZERO).to_string(), "-0x0p+0");
|
||||
assert_eq!(Hexf(f64::NEG_ZERO).to_string(), "-0x0p+0");
|
||||
|
||||
assert_eq!(Hexf(f32::NAN).to_string(), "NaN");
|
||||
assert_eq!(Hexf(f64::NAN).to_string(), "NaN");
|
||||
assert_eq!(Hexf(f32::NAN).to_string(), "qNaN");
|
||||
assert_eq!(Hexf(f64::NAN).to_string(), "qNaN");
|
||||
assert_eq!(Hexf(f32::NEG_NAN).to_string(), "-qNaN");
|
||||
assert_eq!(Hexf(f64::NEG_NAN).to_string(), "-qNaN");
|
||||
if !cfg!(x86_no_sse) {
|
||||
// FIXME(rust-lang/rust#115567): calls quiet the sNaN
|
||||
assert_eq!(Hexf(f32::SNAN).to_string(), "sNaN");
|
||||
assert_eq!(Hexf(f64::SNAN).to_string(), "sNaN");
|
||||
assert_eq!(Hexf(f32::NEG_SNAN).to_string(), "-sNaN");
|
||||
assert_eq!(Hexf(f64::NEG_SNAN).to_string(), "-sNaN");
|
||||
}
|
||||
|
||||
assert_eq!(Hexf(f32::INFINITY).to_string(), "inf");
|
||||
assert_eq!(Hexf(f64::INFINITY).to_string(), "inf");
|
||||
@@ -1139,7 +1217,9 @@ fn spot_checks() {
|
||||
assert_eq!(Hexf(f16::MIN).to_string(), "-0x1.ffcp+15");
|
||||
assert_eq!(Hexf(f16::ZERO).to_string(), "0x0p+0");
|
||||
assert_eq!(Hexf(f16::NEG_ZERO).to_string(), "-0x0p+0");
|
||||
assert_eq!(Hexf(f16::NAN).to_string(), "NaN");
|
||||
assert_eq!(Hexf(f16::NAN).to_string(), "qNaN");
|
||||
assert_eq!(Hexf(f16::SNAN).to_string(), "sNaN");
|
||||
assert_eq!(Hexf(f16::NEG_NAN).to_string(), "-qNaN");
|
||||
assert_eq!(Hexf(f16::INFINITY).to_string(), "inf");
|
||||
assert_eq!(Hexf(f16::NEG_INFINITY).to_string(), "-inf");
|
||||
}
|
||||
@@ -1156,7 +1236,9 @@ fn spot_checks() {
|
||||
);
|
||||
assert_eq!(Hexf(f128::ZERO).to_string(), "0x0p+0");
|
||||
assert_eq!(Hexf(f128::NEG_ZERO).to_string(), "-0x0p+0");
|
||||
assert_eq!(Hexf(f128::NAN).to_string(), "NaN");
|
||||
assert_eq!(Hexf(f128::NAN).to_string(), "qNaN");
|
||||
assert_eq!(Hexf(f128::SNAN).to_string(), "sNaN");
|
||||
assert_eq!(Hexf(f128::NEG_NAN).to_string(), "-qNaN");
|
||||
assert_eq!(Hexf(f128::INFINITY).to_string(), "inf");
|
||||
assert_eq!(Hexf(f128::NEG_INFINITY).to_string(), "-inf");
|
||||
}
|
||||
|
||||
@@ -35,5 +35,38 @@
|
||||
/// Hint to the compiler that the current path is cold.
|
||||
pub fn cold_path() {
|
||||
#[cfg(intrinsics_enabled)]
|
||||
core::intrinsics::cold_path();
|
||||
core::hint::cold_path();
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `y` must not be zero and the result must not overflow (`x > i32::MIN`).
|
||||
pub unsafe fn unchecked_div_i32(x: i32, y: i32) -> i32 {
|
||||
cfg_if! {
|
||||
if #[cfg(all(not(debug_assertions), intrinsics_enabled))] {
|
||||
// Temporary macro to avoid panic codegen for division (in debug mode too). At
|
||||
// the time of this writing this is only used in a few places, and once
|
||||
// rust-lang/rust#72751 is fixed then this macro will no longer be necessary and
|
||||
// the native `/` operator can be used and panics won't be codegen'd.
|
||||
//
|
||||
// Note: I am not sure whether the above comment is still up to date, we need
|
||||
// to double check whether panics are elided where we use this.
|
||||
unsafe { core::intrinsics::unchecked_div(x, y) }
|
||||
} else {
|
||||
x / y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `y` must not be zero and the result must not overflow (`x > isize::MIN`).
|
||||
pub unsafe fn unchecked_div_isize(x: isize, y: isize) -> isize {
|
||||
cfg_if! {
|
||||
if #[cfg(all(not(debug_assertions), intrinsics_enabled))] {
|
||||
unsafe { core::intrinsics::unchecked_div(x, y) }
|
||||
} else {
|
||||
x / y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
most ideas and constants are from boost and python
|
||||
*/
|
||||
use super::{exp, floor, k_cos, k_sin, pow};
|
||||
use crate::support::unchecked_div_isize;
|
||||
|
||||
const PI: f64 = 3.141592653589793238462643383279502884;
|
||||
|
||||
@@ -37,7 +38,8 @@ fn sinpi(mut x: f64) -> f64 {
|
||||
|
||||
/* reduce x into [-.25,.25] */
|
||||
n = (4.0 * x) as isize;
|
||||
n = div!(n + 1, 2);
|
||||
// SAFETY: nonzero divisor, nonnegative dividend (`n < 8`).
|
||||
n = unsafe { unchecked_div_isize(n + 1, 2) };
|
||||
x -= (n as f64) * 0.5;
|
||||
|
||||
x *= PI;
|
||||
|
||||
Reference in New Issue
Block a user