LLVM's APFloat (aka llvm::APFloat) software floating-point (or "softfloat")
library was first ported to Rust (and named rustc_apfloat) back in 2017,
in the Rust pull request rust-lang/rust#43554,
as part of an effort to expand Rust compile-time capabilities without sacrificing
determinism (and therefore soundness, if the type-system was involved).
Note: while using the original C++ llvm::APFloat directly would've been an option,
certain high-level API design differences made in the Rust port, without behavioral impact
(C++ raw pointers and dynamic allocations vs Rust generics, traits and #![no_std]),
made the Rust port more appealing from a determinism standpoint (mostly thanks to
lacking all 3 of: unsafe code, host floating-point use, std access - and only
allocating to handle the arbitrary precision needed for conversions to/from decimal),
even though there was a chance it had correctness issues unique to it.
However, that port had a fatal flaw: it was added to the rust-lang/rust repository
without its unique licensing status (as a port of a C++ library with its own license)
being properly tracked, communicated, taken into account, etc.
The end result was years of limbo, mostly chronicled in the Rust issue
rust-lang/rust#55993, in which
the in-tree port couldn't really receive proper updated or even maintenance, due
due to its unclear status.
This repository (rust-lang/rustc_apfloat) is the result of a 2022 plan on
the relevant Zulip topic, fully put into motion during 2023:
- the
githistory of the in-treecompiler/rustc_apfloatlibrary was extracted
(see the separaterustc_apfloat-git-history-extractionrepository for more details) - only commits that were both necessary and had clear copyright status, were kept
- any missing functionality or bug fixes, would have to be either be re-contributed,
or rebuilt from the ground up (mostly the latter ended up being done, see below)
Most changes since the original port had been aesthetic (e.g. spell-checking, rustfmt),
so little was lost in the process.
Starting from that much smaller "trusted" base:
- everything could use LLVM's new (since 2019) license, "
Apache-2.0 WITH LLVM-exception"
(see the "Licensing" section below and/or LICENSE-DETAILS.md for more details) - new facilities were built (benchmarks, and a fuzzer comparing Rust/C++/hardware)
- excessive testing was performed (via a combination of fuzzing and bruteforce search)
- latent bugs were discovered (e.g. LLVM issues #63895 and #63938)
- the port has been forwarded in time, to include upstream (
llvm/llvm-project) changes
tollvm::APFloatover the years (since 2017), removing the need for selective backports
As this is, for the time being, a "living port", tracking upstream (llvm/llvm-project)
llvm::APFloat changes, the rustc_apfloat crate will have versions of the form:
0.X.Y+llvm-ZZZZZZZZZZZZ
Xis always bumped after semver-incompatible API changes,
or when updating the upstream (llvm/llvm-project) commit the port is based onYis only bumped when other parts of the version don't need to be (e.g. for bug fixes)+llvm-ZZZZZZZZZZZZis "version metadata" (which Cargo itself ignores),
andZZZZZZZZZZZZalways holds the first 12 hexadecimal digits of
the upstream (llvm/llvm-project)gitcommit hash the port is based on
rustc_apfloat contains ports of all the tests from the C++ llvm::APFloat code,
in tests/ieee.rs and tests/ppc.rs.
For tests specific to rustc_apfloat (without C++ equivalents), tests/downstream.rs
is used (which mainly contains tests for now-fixed bugs, found by fuzzing).
As llvm::APFloat tests are far from comprehensive, the only option for in-depth
comparisons between the original C++ code and the Rust port (and between them and
hardware floating-point behavior) is to employ fuzzing.
The fuzzing infrastructure lives in fuzz/ and requires cargo-afl, but also
involves an automated build of the original C++ llvm::APFloat code with clang
(to be able to instrument it via LLVM, in the same way cargo-afl does for the
Rust code), and has been prototyped and tested on Linux (and is unlikely to work
on other platforms, or even some Linux distros, though it mostly assumes UNIX).
Example usage:
(TODO: maybe move this to fuzz/README.md and/or expand on it)
# Install `cargo-afl` (used below to build/run the fuzzing binary).
cargo install cargo-afl
# Build the fuzzing binary (`target/release/rustc_apfloat-fuzz`).
cargo afl build -p rustc_apfloat-fuzz --release
# Seed the inputs for a run `foo` (while not ideal, even this one minimal input works).
mkdir fuzz/in-foo && echo > fuzz/in-foo/empty
# Start the fuzzing run `foo`, which should bring up the AFL++ progress TUI
# (see also `cargo run -p rustc_apfloat-fuzz` for extra flags available).
cargo afl fuzz -i fuzz/in-foo -o fuzz/out-foo target/release/rustc_apfloat-fuzzTo visualize the fuzzing testcases, you can use the decode subcommand:
cargo run -p rustc_apfloat-fuzz decode fuzz/out-foo/default/crashes/*(this will work even while cargo afl fuzz, i.e. AFL++, is running)
This project is licensed under the Apache 2 with LLVM exception license. For a more complete discussion of this project's licensing, see LICENSE-DETAILS.md.