Skip to content

Commit 9302977

Browse files
committed
Initial draft from rust-lang#3451
This revision also contains comments addressed from reviewers in RFC rust-lang#3451.
1 parent ed4c592 commit 9302977

File tree

1 file changed

+121
-0
lines changed

1 file changed

+121
-0
lines changed

text/add-bf16-f64f64-and-f80-type.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
- Feature Name: `add-bf16-f64f64-and-f80-type`
2+
- Start Date: 2023-7-10
3+
- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)
4+
- Rust Issue: [rust-lang/rust#2629](https://github.com/rust-lang/rfcs/issues/2629)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
This RFC proposes new floating point types to enhance FFI with specific targets:
10+
11+
- `bf16` as builtin type for 'Brain floating format', widely used in machine learning, different from IEEE-754 standard `binary16` representation
12+
- `f64f64` into `core::arch` for the legacy extended float format used in PowerPC architecture
13+
- `f80` into `core::arch` for the extended float format used in x86 and x86_64 architecture
14+
15+
Also, this proposal introduces `c_longdouble` in `core::ffi` to represent correct format for 'long double' in C.
16+
17+
# Motivation
18+
[motivation]: #motivation
19+
20+
The types listed above may be widely used in existing native code, but not available on all targets. Their underlying representations are quite different from 16-bit and 128-bit binary floating format defined in IEEE-754.
21+
22+
In respective targets (namely PowerPC and x86), the target-specific extended types are referenced by `long double`, which makes `long double` ambiguous in context of FFI. Thus defining `c_longdouble` should help interoperating with C code with `long double` type.
23+
24+
# Guide-level explanation
25+
[guide-level-explanation]: #guide-level-explanation
26+
27+
`bf16` is available on all targets. The operators and constants defined for `f32` are also available for `bf16`.
28+
29+
For `f64f64` and `f80`, their availability is limited into following targets, but may change over time:
30+
31+
- `f64f64` is supported on `powerpc-*` and `powerpc64(le)-*`, available in `core::arch::{powerpc, powerpc64}`
32+
- `f80` is supported on `i[356]86-*` and `x86_64-*`, available in `core::arch::{x86, x86_64}`
33+
34+
The operators and constants defined for `f32` or `f64` are available for `f64f64` and `f80` in their respective arch-specific modules.
35+
36+
All the proposed types, including `bf16`, `f64f64` and `f80`, do not have literal representation. Instead, they can be converted to or from IEEE-754 compliant types.
37+
38+
# Reference-level explanation
39+
[reference-level-explanation]: #reference-level-explanation
40+
41+
## `bf16` type
42+
43+
`bf16` consists of 1 sign bit, 8 bits of exponent, 7 bits of mantissa. Some ARM, AArch64, x86 and x86_64 targets support `bf16` operations natively. For other targets, they will be promoted into `f32` before computation and truncated back into `bf16`.
44+
45+
`bf16` will generate `bfloat` type in LLVM IR.
46+
47+
## `f64f64` type
48+
49+
`f64f64` is the legacy format of extended floating format used on PowerPC target. It consists of two `f64`s, with the former as normal `f64` and the latter for extended mantissa.
50+
51+
The following `From` traits are implemented in `core::arch::{powerpc, powerpc64}` for conversion between `f64f64` and other floating types:
52+
53+
```rust
54+
impl From<bf16> for f64f64 { /* ... */ }
55+
impl From<f32> for f64f64 { /* ... */ }
56+
impl From<f64> for f64f64 { /* ... */ }
57+
```
58+
59+
`f64f64` will generate `ppc_fp128` type in LLVM IR.
60+
61+
## `f80` type
62+
63+
`f80` represents the extended precision floating type on x86 targets, with 1 sign bit, 15 bits of exponent and 63 bits of mantissa.
64+
65+
The following `From` traits are implemented in `core::arch::{x86, x86_64}` for conversion between `f64f64` and other floating types:
66+
67+
```rust
68+
impl From<bf16> for f80 { /* ... */ }
69+
impl From<f32> for f80 { /* ... */ }
70+
impl From<f64> for f80 { /* ... */ }
71+
```
72+
73+
`f80` will generate `x86_fp80` type in LLVM IR.
74+
75+
## `c_longdouble` type in FFI
76+
77+
`core::ffi::c_longdouble` will always represent whatever `long double` does in C. Rust will defer to the compiler backend (LLVM) for what exactly this represents, but it will approximately be:
78+
79+
- 80-bit extended precision (f80) on `x86` and `x86_64`:
80+
- `f64` double precision with MSVC
81+
- `f128` quadruple precision on AArch64
82+
- `f64f64` on PowerPC
83+
84+
# Drawbacks
85+
[drawbacks]: #drawbacks
86+
87+
`bf16` is not a IEEE-754 standard type, so adding it as primitive type may break existing consistency for builtin float types. The truncation after calculation on targets not supporting `bf16` natively also breaks how Rust treats precision loss in other cases.
88+
89+
`c_longdouble` are not uniquely determined by architecture, OS and ABI. On the same target, C compiler options may change what representation `long double` uses.
90+
91+
# Rationale and alternatives
92+
[rationale-and-alternatives]: #rationale-and-alternatives
93+
94+
[half](https://github.com/starkat99/half-rs) crate provides implementation of binary16 and bfloat16 types.
95+
96+
However, besides the disadvantage of usage inconsistency between primitive type and type from crate, there are still issues around those bindings.
97+
98+
The availablity of additional float types depends on CPU/OS/ABI/features of different targets heavily. Evolution of LLVM may also unlock possibility of the types on new targets. Implementing them in compiler handles the stuff at the best location.
99+
100+
Most of such crates defines their type on top of C binding. But extended float type definition in C is complex and confusing. The meaning of `long double`, `_Float128` varies by targets or compiler options. Implementing in Rust compiler helps to maintain a stable codegen interface.
101+
102+
And since third party tools also relies on Rust internal code, implementing additional float types in compiler also help the tools to recognize them.
103+
104+
# Prior art
105+
[prior-art]: #prior-art
106+
107+
There is a previous proposal on `f16b` type to represent `bfloat16`: https://github.com/rust-lang/rfcs/pull/2690.
108+
109+
# Unresolved questions
110+
[unresolved-questions]: #unresolved-questions
111+
112+
This proposal does not contain information for FFI with C's `_Float128` and `__float128` type. Because they are not so commonly used compared to `long double`, and they are even more complex than the situation of `c_longdouble` (for example, their semantics are different under C or C++ mode).
113+
114+
Although statements like `X target supports A type` is used in above text, some target may only support some type when some target features are enabled. Such features are assumed to be enabled, with precedents like `core::arch::x86_64::__m256d` (which is part of SSE).
115+
116+
Representation of `long double` in C may depend on some compiler options. For example, in Clang on `powerpc64le-*`, `-mabi=ieeelongdouble`/`-mabi=ibmlongdouble`/`-mlong-double-64` will set `long double` as `fp128`/`ppc_fp128`/`double` in LLVM. Currently, the default option is assumed.
117+
118+
# Future possibilities
119+
[future-possibilities]: #future-possibilities
120+
121+
[LLVM reference for floating types]: https://llvm.org/docs/LangRef.html#floating-point-types

0 commit comments

Comments
 (0)