From 930297791a208077f6ebecd11e2013f13b1f224a Mon Sep 17 00:00:00 2001 From: Qiu Chaofan Date: Tue, 11 Jul 2023 00:36:07 +0800 Subject: [PATCH 1/9] Initial draft from #3451 This revision also contains comments addressed from reviewers in RFC #3451. --- text/add-bf16-f64f64-and-f80-type.md | 121 +++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 text/add-bf16-f64f64-and-f80-type.md diff --git a/text/add-bf16-f64f64-and-f80-type.md b/text/add-bf16-f64f64-and-f80-type.md new file mode 100644 index 00000000000..36298726327 --- /dev/null +++ b/text/add-bf16-f64f64-and-f80-type.md @@ -0,0 +1,121 @@ +- Feature Name: `add-bf16-f64f64-and-f80-type` +- Start Date: 2023-7-10 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#2629](https://github.com/rust-lang/rfcs/issues/2629) + +# Summary +[summary]: #summary + +This RFC proposes new floating point types to enhance FFI with specific targets: + +- `bf16` as builtin type for 'Brain floating format', widely used in machine learning, different from IEEE-754 standard `binary16` representation +- `f64f64` into `core::arch` for the legacy extended float format used in PowerPC architecture +- `f80` into `core::arch` for the extended float format used in x86 and x86_64 architecture + +Also, this proposal introduces `c_longdouble` in `core::ffi` to represent correct format for 'long double' in C. + +# Motivation +[motivation]: #motivation + +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. + +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. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +`bf16` is available on all targets. The operators and constants defined for `f32` are also available for `bf16`. + +For `f64f64` and `f80`, their availability is limited into following targets, but may change over time: + +- `f64f64` is supported on `powerpc-*` and `powerpc64(le)-*`, available in `core::arch::{powerpc, powerpc64}` +- `f80` is supported on `i[356]86-*` and `x86_64-*`, available in `core::arch::{x86, x86_64}` + +The operators and constants defined for `f32` or `f64` are available for `f64f64` and `f80` in their respective arch-specific modules. + +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. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +## `bf16` type + +`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`. + +`bf16` will generate `bfloat` type in LLVM IR. + +## `f64f64` type + +`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. + +The following `From` traits are implemented in `core::arch::{powerpc, powerpc64}` for conversion between `f64f64` and other floating types: + +```rust +impl From for f64f64 { /* ... */ } +impl From for f64f64 { /* ... */ } +impl From for f64f64 { /* ... */ } +``` + +`f64f64` will generate `ppc_fp128` type in LLVM IR. + +## `f80` type + +`f80` represents the extended precision floating type on x86 targets, with 1 sign bit, 15 bits of exponent and 63 bits of mantissa. + +The following `From` traits are implemented in `core::arch::{x86, x86_64}` for conversion between `f64f64` and other floating types: + +```rust +impl From for f80 { /* ... */ } +impl From for f80 { /* ... */ } +impl From for f80 { /* ... */ } +``` + +`f80` will generate `x86_fp80` type in LLVM IR. + +## `c_longdouble` type in FFI + +`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: + +- 80-bit extended precision (f80) on `x86` and `x86_64`: +- `f64` double precision with MSVC +- `f128` quadruple precision on AArch64 +- `f64f64` on PowerPC + +# Drawbacks +[drawbacks]: #drawbacks + +`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. + +`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. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +[half](https://github.com/starkat99/half-rs) crate provides implementation of binary16 and bfloat16 types. + +However, besides the disadvantage of usage inconsistency between primitive type and type from crate, there are still issues around those bindings. + +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. + +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. + +And since third party tools also relies on Rust internal code, implementing additional float types in compiler also help the tools to recognize them. + +# Prior art +[prior-art]: #prior-art + +There is a previous proposal on `f16b` type to represent `bfloat16`: https://github.com/rust-lang/rfcs/pull/2690. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +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). + +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). + +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. + +# Future possibilities +[future-possibilities]: #future-possibilities + +[LLVM reference for floating types]: https://llvm.org/docs/LangRef.html#floating-point-types From f34867e1a3a36e55a04dd3de159eff715df1bff7 Mon Sep 17 00:00:00 2001 From: Qiu Chaofan Date: Tue, 11 Jul 2023 00:49:33 +0800 Subject: [PATCH 2/9] Give RFC number --- text/add-bf16-f64f64-and-f80-type.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/add-bf16-f64f64-and-f80-type.md b/text/add-bf16-f64f64-and-f80-type.md index 36298726327..3c00fb675b5 100644 --- a/text/add-bf16-f64f64-and-f80-type.md +++ b/text/add-bf16-f64f64-and-f80-type.md @@ -1,6 +1,6 @@ - Feature Name: `add-bf16-f64f64-and-f80-type` - Start Date: 2023-7-10 -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- RFC PR: [rust-lang/rfcs#3456](https://github.com/rust-lang/rfcs/pull/3456) - Rust Issue: [rust-lang/rust#2629](https://github.com/rust-lang/rfcs/issues/2629) # Summary From 26436fac78d19b12107b8390c0d60c4da2711282 Mon Sep 17 00:00:00 2001 From: Qiu Chaofan Date: Tue, 17 Oct 2023 17:13:35 +0800 Subject: [PATCH 3/9] Fix mention of SSE to AVX Co-authored-by: Jacob Lifshay --- text/add-bf16-f64f64-and-f80-type.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/add-bf16-f64f64-and-f80-type.md b/text/add-bf16-f64f64-and-f80-type.md index 3c00fb675b5..68ec19a2888 100644 --- a/text/add-bf16-f64f64-and-f80-type.md +++ b/text/add-bf16-f64f64-and-f80-type.md @@ -111,7 +111,7 @@ There is a previous proposal on `f16b` type to represent `bfloat16`: https://git 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). -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). +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 AVX). 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. From 2117dda8d97002d06e2174d5e77f00b3d39713e9 Mon Sep 17 00:00:00 2001 From: Qiu Chaofan Date: Tue, 17 Oct 2023 17:16:14 +0800 Subject: [PATCH 4/9] Fix typo of f80 Co-authored-by: teor --- text/add-bf16-f64f64-and-f80-type.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/add-bf16-f64f64-and-f80-type.md b/text/add-bf16-f64f64-and-f80-type.md index 68ec19a2888..be2c32ace11 100644 --- a/text/add-bf16-f64f64-and-f80-type.md +++ b/text/add-bf16-f64f64-and-f80-type.md @@ -62,7 +62,7 @@ impl From for f64f64 { /* ... */ } `f80` represents the extended precision floating type on x86 targets, with 1 sign bit, 15 bits of exponent and 63 bits of mantissa. -The following `From` traits are implemented in `core::arch::{x86, x86_64}` for conversion between `f64f64` and other floating types: +The following `From` traits are implemented in `core::arch::{x86, x86_64}` for conversion between `f80` and other floating types: ```rust impl From for f80 { /* ... */ } From cb883464f95becbe8492fe8fb8e8e6418987ef3c Mon Sep 17 00:00:00 2001 From: Qiu Chaofan Date: Tue, 17 Oct 2023 17:18:37 +0800 Subject: [PATCH 5/9] Update text/add-bf16-f64f64-and-f80-type.md Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- text/add-bf16-f64f64-and-f80-type.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text/add-bf16-f64f64-and-f80-type.md b/text/add-bf16-f64f64-and-f80-type.md index be2c32ace11..7369406c7b9 100644 --- a/text/add-bf16-f64f64-and-f80-type.md +++ b/text/add-bf16-f64f64-and-f80-type.md @@ -8,11 +8,11 @@ This RFC proposes new floating point types to enhance FFI with specific targets: -- `bf16` as builtin type for 'Brain floating format', widely used in machine learning, different from IEEE-754 standard `binary16` representation -- `f64f64` into `core::arch` for the legacy extended float format used in PowerPC architecture -- `f80` into `core::arch` for the extended float format used in x86 and x86_64 architecture +- `bf16` as builtin type for the 'brain floating point' format, widely used in machine learning, different from the IEEE 754 standard `binary16` representation +- `f64f64` in `core::arch` for the legacy extended float format used in the PowerPC architecture +- `f80` in `core::arch` for the extended float format used in the x86 and x86_64 architectures -Also, this proposal introduces `c_longdouble` in `core::ffi` to represent correct format for 'long double' in C. +Also, this proposal introduces `c_longdouble` in `core::ffi` to represent the correct format for 'long double' in C. # Motivation [motivation]: #motivation From bdbf8fec105ae20023bd7e6ad24bdf4a3a32dbaf Mon Sep 17 00:00:00 2001 From: Qiu Chaofan Date: Tue, 17 Oct 2023 17:19:05 +0800 Subject: [PATCH 6/9] Update text/add-bf16-f64f64-and-f80-type.md Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- text/add-bf16-f64f64-and-f80-type.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/add-bf16-f64f64-and-f80-type.md b/text/add-bf16-f64f64-and-f80-type.md index 7369406c7b9..890c4fca420 100644 --- a/text/add-bf16-f64f64-and-f80-type.md +++ b/text/add-bf16-f64f64-and-f80-type.md @@ -17,9 +17,9 @@ Also, this proposal introduces `c_longdouble` in `core::ffi` to represent the co # Motivation [motivation]: #motivation -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. +The types listed above may be widely used in existing native code, but are not available on all targets. Their underlying representations are quite different from 16-bit and 128-bit binary floating format defined in IEEE 754. -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. +In respective targets (namely PowerPC and x86), the target-specific extended types are referenced by `long double`, which makes `long double` ambiguous in the context of FFI. Thus defining `c_longdouble` should help interoperating with C code using the `long double` type. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From c57d867bad16fc8b9fc64773354c527f233d5277 Mon Sep 17 00:00:00 2001 From: Qiu Chaofan Date: Tue, 17 Oct 2023 17:19:17 +0800 Subject: [PATCH 7/9] Update text/add-bf16-f64f64-and-f80-type.md Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- text/add-bf16-f64f64-and-f80-type.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/add-bf16-f64f64-and-f80-type.md b/text/add-bf16-f64f64-and-f80-type.md index 890c4fca420..f106dfcb94f 100644 --- a/text/add-bf16-f64f64-and-f80-type.md +++ b/text/add-bf16-f64f64-and-f80-type.md @@ -84,7 +84,7 @@ impl From for f80 { /* ... */ } # Drawbacks [drawbacks]: #drawbacks -`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. +`bf16` is not an IEEE 754 standard type, so adding it as primitive type may break existing consistency for builtin float types. The truncation after calculations on targets not supporting `bf16` natively also breaks how Rust treats precision loss in other cases. `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. From 8fdcc60f461b3ade5998e2944c456136a4da05ee Mon Sep 17 00:00:00 2001 From: Qiu Chaofan Date: Tue, 17 Oct 2023 17:30:09 +0800 Subject: [PATCH 8/9] Syntax and typo fix from @konsumlamm Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- text/add-bf16-f64f64-and-f80-type.md | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/text/add-bf16-f64f64-and-f80-type.md b/text/add-bf16-f64f64-and-f80-type.md index f106dfcb94f..3cea1de2bb6 100644 --- a/text/add-bf16-f64f64-and-f80-type.md +++ b/text/add-bf16-f64f64-and-f80-type.md @@ -26,14 +26,14 @@ In respective targets (namely PowerPC and x86), the target-specific extended typ `bf16` is available on all targets. The operators and constants defined for `f32` are also available for `bf16`. -For `f64f64` and `f80`, their availability is limited into following targets, but may change over time: +For `f64f64` and `f80`, their availability is limited to the following targets, but this may change over time: - `f64f64` is supported on `powerpc-*` and `powerpc64(le)-*`, available in `core::arch::{powerpc, powerpc64}` - `f80` is supported on `i[356]86-*` and `x86_64-*`, available in `core::arch::{x86, x86_64}` -The operators and constants defined for `f32` or `f64` are available for `f64f64` and `f80` in their respective arch-specific modules. +The operators and constants defined for `f32` and `f64` are available for `f64f64` and `f80` in their respective arch-specific modules. -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. +All proposed types do not have literal representation. Instead, they can be converted to or from IEEE 754 compliant types. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -42,13 +42,13 @@ All the proposed types, including `bf16`, `f64f64` and `f80`, do not have litera `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`. -`bf16` will generate `bfloat` type in LLVM IR. +`bf16` will generate the `bfloat` type in LLVM IR. ## `f64f64` type -`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. +`f64f64` is the legacy extended floating point format used on PowerPC targets. It consists of two `f64`s, with the former acting as a normal `f64` and the latter for an extended mantissa. -The following `From` traits are implemented in `core::arch::{powerpc, powerpc64}` for conversion between `f64f64` and other floating types: +The following `From` traits are implemented in `core::arch::{powerpc, powerpc64}` for conversion between `f64f64` and other floating point types: ```rust impl From for f64f64 { /* ... */ } @@ -60,9 +60,9 @@ impl From for f64f64 { /* ... */ } ## `f80` type -`f80` represents the extended precision floating type on x86 targets, with 1 sign bit, 15 bits of exponent and 63 bits of mantissa. +`f80` represents the extended precision floating point type on x86 targets, with 1 sign bit, 15 bits of exponent and 63 bits of mantissa. -The following `From` traits are implemented in `core::arch::{x86, x86_64}` for conversion between `f80` and other floating types: +The following `From` traits are implemented in `core::arch::{x86, x86_64}` for conversion between `f80` and other floating point types: ```rust impl From for f80 { /* ... */ } @@ -70,13 +70,13 @@ impl From for f80 { /* ... */ } impl From for f80 { /* ... */ } ``` -`f80` will generate `x86_fp80` type in LLVM IR. +`f80` will generate the `x86_fp80` type in LLVM IR. ## `c_longdouble` type in FFI `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: -- 80-bit extended precision (f80) on `x86` and `x86_64`: +- `f80` extended precision on `x86` and `x86_64` - `f64` double precision with MSVC - `f128` quadruple precision on AArch64 - `f64f64` on PowerPC @@ -91,15 +91,15 @@ impl From for f80 { /* ... */ } # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -[half](https://github.com/starkat99/half-rs) crate provides implementation of binary16 and bfloat16 types. +The [half](https://github.com/starkat99/half-rs) crate provides an implementation of the binary16 and bfloat16 formats. -However, besides the disadvantage of usage inconsistency between primitive type and type from crate, there are still issues around those bindings. +However, besides the disadvantage of usage inconsistency between primitive types and types from crates, there are still issues around those bindings. -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. +The availablity of additional float types heavily depends on CPU/OS/ABI/features of different targets. Evolution of LLVM may also unlock the possibility of the types on new targets. Implementing them in the compiler handles the stuff at the best location. -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. +Most of such crates define their type on top of C bindings. However the extended float type definition in C is complex and confusing. The meaning of `long double` and `_Float128` varies by targets or compiler options. Implementing them in the Rust compiler helps to maintain a stable codegen interface. -And since third party tools also relies on Rust internal code, implementing additional float types in compiler also help the tools to recognize them. +And since third party tools also rely on Rust internal code, implementing additional float types in the compiler also helps the tools to recognize them. # Prior art [prior-art]: #prior-art @@ -113,7 +113,7 @@ This proposal does not contain information for FFI with C's `_Float128` and `__f 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 AVX). -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. +Representation of `long double` in C may depend on some compiler options. For example, 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. # Future possibilities [future-possibilities]: #future-possibilities From 892334b3fa74b0f8a3496c0db37c171e835153fc Mon Sep 17 00:00:00 2001 From: Qiu Chaofan Date: Tue, 17 Oct 2023 17:48:10 +0800 Subject: [PATCH 9/9] Explain why C _Float128 not mentioned --- text/add-bf16-f64f64-and-f80-type.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/add-bf16-f64f64-and-f80-type.md b/text/add-bf16-f64f64-and-f80-type.md index 3cea1de2bb6..09adc466fbb 100644 --- a/text/add-bf16-f64f64-and-f80-type.md +++ b/text/add-bf16-f64f64-and-f80-type.md @@ -109,7 +109,7 @@ There is a previous proposal on `f16b` type to represent `bfloat16`: https://git # Unresolved questions [unresolved-questions]: #unresolved-questions -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). +This proposal does not contain information for FFI with C's `_Float128` and `__float128` type. [RFC #3453](https://github.com/rust-lang/rfcs/pull/3453) focuses on type conforming to IEEE 754 `binary128`. 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 AVX).