From 72e89c3c097eb8d3efbaa060df851a48aecd1703 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Mon, 10 Aug 2026 15:45:13 +0100 Subject: [PATCH 01/16] ABI for F16B, the BFloat lang item, the ADT-to-backend layout bridge, and backend handling. GCC and Cranelift explicitly unsupported --- compiler/rustc_abi/src/lib.rs | 5 ++++- compiler/rustc_attr_ir/src/lang_items.rs | 1 + compiler/rustc_codegen_cranelift/src/common.rs | 1 + compiler/rustc_codegen_gcc/src/type_.rs | 4 ++++ compiler/rustc_codegen_llvm/src/abi.rs | 1 + compiler/rustc_codegen_llvm/src/intrinsic.rs | 3 +++ compiler/rustc_codegen_llvm/src/type_.rs | 4 ++++ compiler/rustc_codegen_llvm/src/va_arg.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/naked_asm.rs | 2 +- compiler/rustc_codegen_ssa/src/traits/type_.rs | 2 ++ compiler/rustc_middle/src/ty/layout.rs | 5 +++++ compiler/rustc_public/src/abi.rs | 3 ++- .../rustc_public/src/unstable/convert/stable/abi.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/callconv/mips64.rs | 4 ++-- compiler/rustc_target/src/callconv/sparc64.rs | 4 ++-- compiler/rustc_ty_utils/src/layout.rs | 10 +++++++++- 17 files changed, 44 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 589c5a5cb1229..ff5402ba58300 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1404,6 +1404,7 @@ impl Integer { #[cfg_attr(feature = "nightly", derive(StableHash))] pub enum Float { F16, + F16B, F32, F64, F128, @@ -1415,6 +1416,7 @@ impl Float { match self { F16 => Size::from_bits(16), + F16B => Size::from_bits(16), F32 => Size::from_bits(32), F64 => Size::from_bits(64), F128 => Size::from_bits(128), @@ -1426,7 +1428,7 @@ impl Float { let dl = cx.data_layout(); AbiAlign::new(match self { - F16 => dl.f16_align, + F16 | F16B => dl.f16_align, F32 => dl.f32_align, F64 => dl.f64_align, F128 => dl.f128_align, @@ -1438,6 +1440,7 @@ impl Float { match self { F16 => "f16", + F16B => "f16b", F32 => "f32", F64 => "f64", F128 => "f128", diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index e45621689558e..e2e83e17b765b 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -276,6 +276,7 @@ language_item_table! { PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; + BFloat, sym::bfloat, bfloat_type, Target::Struct, GenericRequirement::Exact(0); Type, sym::type_info, type_struct, Target::Struct, GenericRequirement::None; TypeGeneric, sym::type_info_generic, type_generic, Target::Enum, GenericRequirement::None; diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 1bdb3efefa1aa..2a3fe11e9c820 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -34,6 +34,7 @@ pub(crate) fn scalar_to_clif_type(tcx: TyCtxt<'_>, scalar: Scalar) -> Type { }, Primitive::Float(float) => match float { Float::F16 => types::F16, + Float::F16B => bug!("f16b is not supported by the Cranelift codegen backend"), Float::F32 => types::F32, Float::F64 => types::F64, Float::F128 => types::F128, diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index 5252f93a92ebe..4d51f60ffb386 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -156,6 +156,10 @@ impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> { bug!("unsupported float width 16") } + fn type_f16b(&self) -> Type<'gcc> { + bug!("f16b is not supported by the GCC codegen backend") + } + fn type_f32(&self) -> Type<'gcc> { #[cfg(feature = "master")] if self.supports_f32_type { diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 65bb32ee666f2..eb6f927db15c8 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -158,6 +158,7 @@ impl LlvmType for Reg { }, Primitive::Float(float) => match float { Float::F16 => cx.type_f16(), + Float::F16B => cx.type_f16b(), Float::F32 => cx.type_f32(), Float::F64 => cx.type_f64(), Float::F128 => cx.type_f128(), diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index ba11ef29fb536..a5d0e8af05e91 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -332,6 +332,9 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { Primitive::Float(Float::F16) => { bug!("the va_arg intrinsic does not support `f16`") } + Primitive::Float(Float::F16B) => { + bug!("the va_arg intrinsic does not support `f16b`") + } Primitive::Float(Float::F32) => { // c_double is actually f32 on avr. if self.cx().sess().target.arch != Arch::Avr { diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 22d43f22e24a4..7af0a0be1b2dc 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -214,6 +214,10 @@ impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { unsafe { llvm::LLVMHalfTypeInContext(self.llcx()) } } + fn type_f16b(&self) -> &'ll Type { + unsafe { llvm::LLVMBFloatTypeInContext(self.llcx()) } + } + fn type_f32(&self) -> &'ll Type { unsafe { llvm::LLVMFloatTypeInContext(self.llcx()) } } diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index a64452dbc5a7e..8511dc33bd69d 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -95,7 +95,7 @@ fn get_param_type_alignment<'ll, 'tcx>( Integer::I128 => return Align::EIGHT, }, Primitive::Float(float) => match float { - Float::F16 | Float::F32 => unreachable!(), + Float::F16 | Float::F16B | Float::F32 => unreachable!(), Float::F64 => { /* fall through */ } Float::F128 => return Align::from_bytes(16).unwrap(), }, diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 33cc321ea6d32..2fa45dd98504d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -506,7 +506,7 @@ fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str Integer::I128 => "i64, i64", }, Primitive::Float(float) => match float { - Float::F16 | Float::F32 => "f32", + Float::F16 | Float::F16B | Float::F32 => "f32", Float::F64 => "f64", Float::F128 => "i64, i64", }, diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 707eb3a6ee85d..772330afa701b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -18,6 +18,7 @@ pub trait BaseTypeCodegenMethods: BackendTypes { fn type_isize(&self) -> Self::Type; fn type_f16(&self) -> Self::Type; + fn type_f16b(&self) -> Self::Type; fn type_f32(&self) -> Self::Type; fn type_f64(&self) -> Self::Type; fn type_f128(&self) -> Self::Type; @@ -67,6 +68,7 @@ pub trait DerivedTypeCodegenMethods<'tcx>: use Float::*; match f { F16 => self.type_f16(), + F16B => self.type_f16b(), F32 => self.type_f32(), F64 => self.type_f64(), F128 => self.type_f128(), diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index af2481f47c9ba..772257e3933cd 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -139,6 +139,11 @@ impl abi::Float { use abi::Float::*; match *self { F16 => tcx.types.f16, + F16B => Ty::new_adt( + tcx, + tcx.adt_def(tcx.require_lang_item(LangItem::BFloat, DUMMY_SP)), + ty::List::empty(), + ), F32 => tcx.types.f32, F64 => tcx.types.f64, F128 => tcx.types.f128, diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 02674e4107c77..d0482ffc58020 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -353,6 +353,7 @@ pub enum IntegerLength { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize)] pub enum FloatLength { F16, + F16B, F32, F64, F128, @@ -373,7 +374,7 @@ impl IntegerLength { impl FloatLength { pub fn bits(self) -> usize { match self { - FloatLength::F16 => 16, + FloatLength::F16 | FloatLength::F16B => 16, FloatLength::F32 => 32, FloatLength::F64 => 64, FloatLength::F128 => 128, diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index 31104ce897ffb..20caacabd0912 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -388,6 +388,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Float { fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { match self { rustc_abi::Float::F16 => FloatLength::F16, + rustc_abi::Float::F16B => FloatLength::F16B, rustc_abi::Float::F32 => FloatLength::F32, rustc_abi::Float::F64 => FloatLength::F64, rustc_abi::Float::F128 => FloatLength::F128, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 45df107bf7469..9c07bdbdd6eba 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -522,6 +522,7 @@ symbols! { begin_panic, bench, bevy_ecs, + bfloat, bikeshed, bikeshed_guaranteed_no_drop, bin, diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index a9d5ec958889f..ba5820bb19eeb 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -29,8 +29,8 @@ where BackendRepr::Scalar(scalar) => match scalar.primitive() { Primitive::Float(float) => { match float { - // C does not have the f16 type - Float::F16 => None, + // C does not have the f16 or f16b type + Float::F16 | Float::F16B => None, Float::F32 => Some(Reg::f32()), Float::F64 => Some(Reg::f64()), Float::F128 => Some(Reg::f128()), diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index 6b19f8ebd76ce..a73bbeb79536a 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -55,8 +55,8 @@ fn classify<'a, Ty, C>( } _ => unreachable!(), }, - Float::F16 => { - // Match LLVM by passing `f16` in integer registers. + Float::F16 | Float::F16B => { + // Match LLVM by passing `f16` and `f16b` in integer registers. } } } else { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 9e9aed008bf31..319143bd1920b 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -12,6 +12,7 @@ use rustc_abi::{ use rustc_data_structures::Limit; use rustc_hashes::Hash64; use rustc_hir as hir; +use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::find_attr; use rustc_index::{Idx as _, IndexVec}; use rustc_middle::bug; @@ -738,7 +739,7 @@ fn layout_of_uncached<'tcx>( .is_sized(tcx, typing_env) }); - let layout = cx + let mut layout = cx .calc .layout_of_struct_or_enum( &def.repr(), @@ -805,6 +806,13 @@ fn layout_of_uncached<'tcx>( } } + if tcx.is_lang_item(def.did(), LangItem::BFloat) { + let bfloat = scalar_unit(Primitive::Float(abi::Float::F16B)); + assert_eq!(layout.size, abi::Float::F16B.size()); + assert_eq!(layout.align, abi::Float::F16B.align(cx)); + layout.backend_repr = BackendRepr::Scalar(bfloat); + } + tcx.mk_layout(layout) } From 86f0b31f10fd831f3d1d82eea034490e786dbbbf Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Mon, 10 Aug 2026 15:45:43 +0100 Subject: [PATCH 02/16] Add `#[lang = "bfloat"]` and `pub f16b struct` (as a `u16` wrapper) along with traits agreed on in the RFC --- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_span/src/symbol.rs | 1 + library/core/src/num/f16b.rs | 108 ++++++++++++++++++ library/core/src/num/mod.rs | 4 + .../src/language-features/f16b.md | 11 ++ 5 files changed, 126 insertions(+) create mode 100644 library/core/src/num/f16b.rs create mode 100644 src/doc/unstable-book/src/language-features/f16b.md diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 3a80145e2897d..ebe486f0adbbf 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -547,6 +547,8 @@ declare_features! ( (unstable, f128, "1.78.0", Some(116909)), /// Allow using 16-bit (half precision) floating point numbers. (unstable, f16, "1.78.0", Some(116909)), + /// Allow using bfloat16 floating point numbers. + (unstable, f16b, "CURRENT_RUSTC_VERSION", Some(160630)), /// Allows the use of `#[ffi_const]` on foreign functions. (unstable, ffi_const, "1.45.0", Some(58328)), /// Allows the use of `#[ffi_pure]` on foreign functions. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9c07bdbdd6eba..e241165f5080a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -945,6 +945,7 @@ symbols! { external_doc, f16, f16_nan, + f16b, f16c_target_feature, f32, f32_nan, diff --git a/library/core/src/num/f16b.rs b/library/core/src/num/f16b.rs new file mode 100644 index 0000000000000..dea03c025cdd1 --- /dev/null +++ b/library/core/src/num/f16b.rs @@ -0,0 +1,108 @@ +//! The 16-bit brain floating-point type. + +#![unstable(feature = "f16b", issue = "160630")] + +use crate::{fmt, mem}; + +/// A 16-bit brain floating-point value. +/// +/// This type stores values using the IEEE bfloat16 encoding. It deliberately +/// exposes only raw-bit construction, comparison, formatting, and lossless +/// widening to [`f32`]. +#[lang = "bfloat"] +#[allow(non_camel_case_types)] +#[repr(transparent)] +#[unstable(feature = "f16b", issue = "160630")] +pub struct f16b(u16); + +impl f16b { + /// Creates a bfloat16 value from its raw representation. + #[inline] + #[must_use] + #[unstable(feature = "f16b", issue = "160630")] + pub const fn from_bits(bits: u16) -> Self { + // SAFETY: `f16b` and `u16` have the same size, and every bit pattern is valid. + unsafe { mem::transmute(bits) } + } + + /// Returns the raw representation of this value. + #[inline] + #[must_use = "this returns the result of the operation, without modifying the original"] + #[unstable(feature = "f16b", issue = "160630")] + pub const fn to_bits(self) -> u16 { + // SAFETY: `f16b` and `u16` have the same size, and every bit pattern is valid. + unsafe { mem::transmute(self) } + } +} + +#[inline] +const fn widen(value: f16b) -> f32 { + f32::from_bits((value.to_bits() as u32) << 16) +} + +#[unstable(feature = "f16b", issue = "160630")] +impl Copy for f16b {} + +#[unstable(feature = "f16b", issue = "160630")] +impl Clone for f16b { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +#[unstable(feature = "f16b", issue = "160630")] +impl Default for f16b { + #[inline] + fn default() -> Self { + Self::from_bits(0) + } +} + +#[unstable(feature = "f16b", issue = "160630")] +impl PartialEq for f16b { + #[inline] + fn eq(&self, other: &Self) -> bool { + widen(*self).eq(&widen(*other)) + } +} + +#[unstable(feature = "f16b", issue = "160630")] +impl PartialOrd for f16b { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + widen(*self).partial_cmp(&widen(*other)) + } +} + +#[unstable(feature = "f16b", issue = "160630")] +impl From for f32 { + #[inline] + fn from(value: f16b) -> Self { + widen(value) + } +} + +#[unstable(feature = "f16b", issue = "160630")] +impl fmt::Debug for f16b { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&widen(*self), formatter) + } +} + +#[unstable(feature = "f16b", issue = "160630")] +impl fmt::LowerExp for f16b { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerExp::fmt(&widen(*self), formatter) + } +} + +#[unstable(feature = "f16b", issue = "160630")] +impl fmt::UpperExp for f16b { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperExp::fmt(&widen(*self), formatter) + } +} diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 3fe7b95283446..6988869414ad1 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -42,6 +42,8 @@ mod int_macros; // import int_impl! #[macro_use] mod uint_macros; // import uint_impl! +#[path = "f16b.rs"] +mod bfloat; mod complex; mod error; #[cfg(not(no_fp_fmt_parse))] @@ -55,6 +57,8 @@ mod wrapping; #[doc(hidden)] pub mod niche_types; +#[unstable(feature = "f16b", issue = "160630")] +pub use bfloat::f16b; #[unstable(feature = "complex_numbers", issue = "154023")] pub use complex::Complex; #[stable(feature = "int_error_matching", since = "1.55.0")] diff --git a/src/doc/unstable-book/src/language-features/f16b.md b/src/doc/unstable-book/src/language-features/f16b.md new file mode 100644 index 0000000000000..ae57ebd4a10e4 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/f16b.md @@ -0,0 +1,11 @@ +# `f16b` + +The tracking issue for this feature is: [#160630] + +[#160630]: https://github.com/rust-lang/rust/issues/160630 + +--- + +Enable the experimental `core::num::f16b` type for values stored in the IEEE bfloat16 format. + +`f16b` is a nominal library type, not a primitive floating-point type: it has no literal suffix, arithmetic operators, or numeric `as` casts. A compiler lang item connects its layout to the native LLVM `bfloat` representation. From ce95daf2361ea1ae33b2312e02a0b6c89a9fcd42 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Mon, 10 Aug 2026 15:47:27 +0100 Subject: [PATCH 03/16] f16b API and LLVM codegen tests --- tests/codegen-llvm/float/f16b.rs | 41 ++++++++++++++++++ tests/ui/feature-gates/feature-gate-f16b.rs | 10 +++++ .../ui/feature-gates/feature-gate-f16b.stderr | 33 ++++++++++++++ tests/ui/float/f16b-restrictions.rs | 20 +++++++++ tests/ui/float/f16b-restrictions.stderr | 43 +++++++++++++++++++ tests/ui/float/f16b.rs | 37 ++++++++++++++++ tests/ui/parser/f16b.rs | 14 ++++++ tests/ui/parser/f16b.stderr | 10 +++++ 8 files changed, 208 insertions(+) create mode 100644 tests/codegen-llvm/float/f16b.rs create mode 100644 tests/ui/feature-gates/feature-gate-f16b.rs create mode 100644 tests/ui/feature-gates/feature-gate-f16b.stderr create mode 100644 tests/ui/float/f16b-restrictions.rs create mode 100644 tests/ui/float/f16b-restrictions.stderr create mode 100644 tests/ui/float/f16b.rs create mode 100644 tests/ui/parser/f16b.rs create mode 100644 tests/ui/parser/f16b.stderr diff --git a/tests/codegen-llvm/float/f16b.rs b/tests/codegen-llvm/float/f16b.rs new file mode 100644 index 0000000000000..a82b15a21110e --- /dev/null +++ b/tests/codegen-llvm/float/f16b.rs @@ -0,0 +1,41 @@ +//@ only-x86_64 +//@ compile-flags: -O + +#![crate_type = "lib"] +#![feature(f16b)] +#![allow(improper_ctypes_definitions)] + +extern crate core; + +use core::num::f16b; + +// CHECK-LABEL: define{{.*}} bfloat @identity_f16b(bfloat +#[no_mangle] +pub extern "C" fn identity_f16b(value: f16b) -> f16b { + // CHECK: ret bfloat + value +} + +// CHECK-LABEL: define{{.*}} i16 @f16b_to_bits(bfloat +#[no_mangle] +pub extern "C" fn f16b_to_bits(value: f16b) -> u16 { + // CHECK: bitcast bfloat %value to i16 + value.to_bits() +} + +// CHECK-LABEL: define{{.*}} bfloat @f16b_from_bits(i16 +#[no_mangle] +pub extern "C" fn f16b_from_bits(bits: u16) -> f16b { + // CHECK: bitcast i16 %bits to bfloat + f16b::from_bits(bits) +} + +// CHECK-LABEL: define{{.*}} float @widen_f16b(bfloat +#[no_mangle] +pub extern "C" fn widen_f16b(value: f16b) -> f32 { + // CHECK: bitcast bfloat %value to i16 + // CHECK: zext i16 + // CHECK: shl nuw i32 {{.*}}, 16 + // CHECK: bitcast i32 {{.*}} to float + f32::from(value) +} diff --git a/tests/ui/feature-gates/feature-gate-f16b.rs b/tests/ui/feature-gates/feature-gate-f16b.rs new file mode 100644 index 0000000000000..23b631d7bb58a --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-f16b.rs @@ -0,0 +1,10 @@ +extern crate core; + +use core::num::f16b; +//~^ ERROR use of unstable library feature `f16b` + +fn main() { + let _ = f16b::from_bits(0); + //~^ ERROR use of unstable library feature `f16b` + //~| ERROR use of unstable library feature `f16b` +} diff --git a/tests/ui/feature-gates/feature-gate-f16b.stderr b/tests/ui/feature-gates/feature-gate-f16b.stderr new file mode 100644 index 0000000000000..7654c3b726389 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-f16b.stderr @@ -0,0 +1,33 @@ +error[E0658]: use of unstable library feature `f16b` + --> $DIR/feature-gate-f16b.rs:3:5 + | +LL | use core::num::f16b; + | ^^^^^^^^^^^^^^^ + | + = note: see issue #160630 for more information + = help: add `#![feature(f16b)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `f16b` + --> $DIR/feature-gate-f16b.rs:7:13 + | +LL | let _ = f16b::from_bits(0); + | ^^^^ + | + = note: see issue #160630 for more information + = help: add `#![feature(f16b)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `f16b` + --> $DIR/feature-gate-f16b.rs:7:13 + | +LL | let _ = f16b::from_bits(0); + | ^^^^^^^^^^^^^^^ + | + = note: see issue #160630 for more information + = help: add `#![feature(f16b)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/float/f16b-restrictions.rs b/tests/ui/float/f16b-restrictions.rs new file mode 100644 index 0000000000000..b2dbf1a734be3 --- /dev/null +++ b/tests/ui/float/f16b-restrictions.rs @@ -0,0 +1,20 @@ +#![feature(f16b)] + +extern crate core; + +use core::num::f16b; + +fn main() { + let _: f16b = 1.0; + //~^ ERROR mismatched types + + let x = f16b::from_bits(0x3f80); + let _ = x + x; + //~^ ERROR cannot add `f16b` to `f16b` + + let _ = 1u16 as f16b; + //~^ ERROR non-primitive cast + + let _ = x as f32; + //~^ ERROR non-primitive cast +} diff --git a/tests/ui/float/f16b-restrictions.stderr b/tests/ui/float/f16b-restrictions.stderr new file mode 100644 index 0000000000000..02ffd07e1a464 --- /dev/null +++ b/tests/ui/float/f16b-restrictions.stderr @@ -0,0 +1,43 @@ +error[E0308]: mismatched types + --> $DIR/f16b-restrictions.rs:8:19 + | +LL | let _: f16b = 1.0; + | ---- ^^^ expected `f16b`, found floating-point number + | | + | expected due to this + +error[E0369]: cannot add `f16b` to `f16b` + --> $DIR/f16b-restrictions.rs:12:15 + | +LL | let _ = x + x; + | - ^ - f16b + | | + | f16b + | +note: `f16b` does not implement `Add` + --> $SRC_DIR/core/src/num/f16b.rs:LL:COL + | + = note: `f16b` is defined in another crate + +error[E0605]: non-primitive cast: `u16` as `f16b` + --> $DIR/f16b-restrictions.rs:15:13 + | +LL | let _ = 1u16 as f16b; + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + +error[E0605]: non-primitive cast: `f16b` as `f32` + --> $DIR/f16b-restrictions.rs:18:13 + | +LL | let _ = x as f32; + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + | +help: consider using the `From` trait instead + | +LL - let _ = x as f32; +LL + let _ = f32::from(x); + | + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0308, E0369, E0605. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/float/f16b.rs b/tests/ui/float/f16b.rs new file mode 100644 index 0000000000000..cdd4dfebd291d --- /dev/null +++ b/tests/ui/float/f16b.rs @@ -0,0 +1,37 @@ +//@ run-pass + +#![feature(f16b)] + +extern crate core; + +use core::num::f16b; +use std::fmt::{Debug, LowerExp, UpperExp}; + +const ONE: f16b = f16b::from_bits(0x3f80); +const ONE_BITS: u16 = ONE.to_bits(); + +fn assert_traits() +where + T: Default + Copy + Clone + Debug + LowerExp + UpperExp + PartialEq + PartialOrd, +{ +} + +fn main() { + assert_traits::(); + + assert_eq!(size_of::(), 2); + assert_eq!(align_of::(), 2); + assert_eq!(f16b::default().to_bits(), 0); + assert_eq!(ONE_BITS, 0x3f80); + assert_eq!(f32::from(ONE).to_bits(), 0x3f80_0000); + + let two = f16b::from_bits(0x4000); + let negative_zero = f16b::from_bits(0x8000); + let nan = f16b::from_bits(0x7fc0); + assert!(ONE < two); + assert_eq!(f16b::from_bits(0), negative_zero); + assert!(nan != nan); + assert_eq!(format!("{ONE:?}"), "1.0"); + assert_eq!(format!("{ONE:e}"), "1e0"); + assert_eq!(format!("{ONE:E}"), "1E0"); +} diff --git a/tests/ui/parser/f16b.rs b/tests/ui/parser/f16b.rs new file mode 100644 index 0000000000000..bb6f3737a33d5 --- /dev/null +++ b/tests/ui/parser/f16b.rs @@ -0,0 +1,14 @@ +#![feature(f16b)] + +extern crate core; + +use core::num::f16b; + +// `f16b` is a nominal core type, not a floating-point literal type. +fn main() { + let value = f16b::from_bits(0x3f80); + let _: f16b = value; + + let _ = 0.0f16b; + //~^ ERROR invalid suffix `f16b` for float literal +} diff --git a/tests/ui/parser/f16b.stderr b/tests/ui/parser/f16b.stderr new file mode 100644 index 0000000000000..2d89bbe4d8776 --- /dev/null +++ b/tests/ui/parser/f16b.stderr @@ -0,0 +1,10 @@ +error: invalid suffix `f16b` for float literal + --> $DIR/f16b.rs:12:13 + | +LL | let _ = 0.0f16b; + | ^^^^^^^ invalid suffix `f16b` + | + = help: valid suffixes are `f32` and `f64` + +error: aborting due to 1 previous error + From a8baf6e2b24a92cab5ac6ea51ac2cc03a10226af Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Mon, 10 Aug 2026 15:47:46 +0100 Subject: [PATCH 04/16] Add test for an AArch64 `bf16` intrinsics, fixed code to allow an `f16b` to be treated as a primitive scalar --- .../rustc_hir_analysis/src/check/check.rs | 2 ++ .../scalable-vectors/bf16-intrinsic.rs | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 tests/codegen-llvm/scalable-vectors/bf16-intrinsic.rs diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 32960bd370172..f0b5bba05a64a 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1560,6 +1560,8 @@ fn check_scalable_vector(tcx: TyCtxt<'_>, span: Span, def_id: LocalDefId, scalab // bools match element_ty.kind() { ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Bool => (), + // We need to treat a `bfloat` (`f16b`) as a primitive scalar + ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::BFloat) => (), _ => { let mut err = tcx.dcx().struct_span_err( span, diff --git a/tests/codegen-llvm/scalable-vectors/bf16-intrinsic.rs b/tests/codegen-llvm/scalable-vectors/bf16-intrinsic.rs new file mode 100644 index 0000000000000..d8f344320195e --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/bf16-intrinsic.rs @@ -0,0 +1,35 @@ +//@ edition: 2021 +//@ only-aarch64 + +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features)] +#![feature(f16b, link_llvm_intrinsics, rustc_attrs, simd_ffi)] + +use core::num::f16b; + +#[allow(non_camel_case_types)] +type bfloat16_t = f16b; + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +pub struct svbfloat16_t(bfloat16_t); + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +pub struct svbool_t(bool); + +#[no_mangle] +#[target_feature(enable = "sve,sve2,bf16")] +// CHECK-LABEL: define @svadd_bf16_n_x( +// CHECK-SAME: %pg, %zdn, bfloat{{.*}} %zm) +pub unsafe fn svadd_bf16_n_x(pg: svbool_t, zdn: svbfloat16_t, zm: bfloat16_t) -> svbfloat16_t { + extern "C" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fadd.u.nxv8bf16")] + fn _svadd_bf16_n_x(pg: svbool_t, zdn: svbfloat16_t, zm: bfloat16_t) -> svbfloat16_t; + } + // CHECK: [[RESULT:%.*]] = {{.*}}call @llvm.aarch64.sve.fadd.u.nxv8bf16( + // CHECK-SAME: %pg, %zdn, bfloat %zm) + // CHECK: ret [[RESULT]] + unsafe { _svadd_bf16_n_x(pg, zdn, zm) } +} From b840b82493f40d80f5610658a5cbcd3c5059b828 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Wed, 12 Aug 2026 08:59:26 +0100 Subject: [PATCH 05/16] Pr Feedback; - Add documentation aliases to `f16b` - Add Wikipedia link to `bfloat16` - Remove `@ only-x86_64` flag from test --- library/core/src/num/f16b.rs | 14 +++++++++++++- tests/codegen-llvm/float/f16b.rs | 1 - 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/library/core/src/num/f16b.rs b/library/core/src/num/f16b.rs index dea03c025cdd1..6cbc4a68242d4 100644 --- a/library/core/src/num/f16b.rs +++ b/library/core/src/num/f16b.rs @@ -6,10 +6,22 @@ use crate::{fmt, mem}; /// A 16-bit brain floating-point value. /// -/// This type stores values using the IEEE bfloat16 encoding. It deliberately +/// This type stores values using the bfloat16 encoding. It deliberately /// exposes only raw-bit construction, comparison, formatting, and lossless /// widening to [`f32`]. +/// +/// The 16-bit brain floating-point intends to preserve the dynamic range of +/// a 32-bit floating-point value while using half the storage. It does +/// this by using the same number of exponent bits as and `f32`, 8-bits, but only +/// using 7-bits for the mantissa see [Wikipedia on bfloat16][wikipedia] for +/// more information. +/// +/// [wikipedia]: https://en.wikipedia.org/wiki/Bfloat16_floating-point_format #[lang = "bfloat"] +#[doc(alias = "bf16")] // what hardware often names it +#[doc(alias = "bfloat")] // LLVM's name +#[doc(alias = "bfloat16")] // Wikipedia's name +#[doc(alias = "bfloat16_t")] // The C++ `stdfloat` name #[allow(non_camel_case_types)] #[repr(transparent)] #[unstable(feature = "f16b", issue = "160630")] diff --git a/tests/codegen-llvm/float/f16b.rs b/tests/codegen-llvm/float/f16b.rs index a82b15a21110e..f1eb6298cdf62 100644 --- a/tests/codegen-llvm/float/f16b.rs +++ b/tests/codegen-llvm/float/f16b.rs @@ -1,4 +1,3 @@ -//@ only-x86_64 //@ compile-flags: -O #![crate_type = "lib"] From 0d109593224d871c4fe8ba534818b916fe6ca0f3 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Wed, 12 Aug 2026 09:10:13 +0100 Subject: [PATCH 06/16] Pr Feedback - ICE on `sparc64`, `mips64` & WASM --- compiler/rustc_codegen_ssa/src/mir/naked_asm.rs | 3 ++- compiler/rustc_target/src/callconv/mips64.rs | 6 ++++-- compiler/rustc_target/src/callconv/sparc64.rs | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 2fa45dd98504d..3ef2fb36bc65f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -506,7 +506,8 @@ fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str Integer::I128 => "i64, i64", }, Primitive::Float(float) => match float { - Float::F16 | Float::F16B | Float::F32 => "f32", + Float::F16B => panic!("`f16b` unsupported"), + Float::F16 | Float::F32 => "f32", Float::F64 => "f64", Float::F128 => "i64, i64", }, diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index ba5820bb19eeb..ca4d278710d6d 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -29,8 +29,10 @@ where BackendRepr::Scalar(scalar) => match scalar.primitive() { Primitive::Float(float) => { match float { - // C does not have the f16 or f16b type - Float::F16 | Float::F16B => None, + // C does not have the f16 + Float::F16 => None, + // No `f16b` type + Float::F16B => panic!("`f16b` unsupported"), Float::F32 => Some(Reg::f32()), Float::F64 => Some(Reg::f64()), Float::F128 => Some(Reg::f128()), diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index a73bbeb79536a..bf8682c27b695 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -55,9 +55,10 @@ fn classify<'a, Ty, C>( } _ => unreachable!(), }, - Float::F16 | Float::F16B => { + Float::F16 => { // Match LLVM by passing `f16` and `f16b` in integer registers. } + Float::F16B => panic!("`f16b` unsupported"), } } else { /* pass unaligned floats in integer registers */ From bcb05c30ff6d399702a69aece2ebcd5356b54a1f Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Wed, 12 Aug 2026 11:07:32 +0100 Subject: [PATCH 07/16] Skip GCC in tests & add `f16b` to minicore --- tests/auxiliary/minicore.rs | 1 + tests/codegen-llvm/float/f16b.rs | 1 + tests/ui/feature-gates/feature-gate-f16b.rs | 2 ++ tests/ui/feature-gates/feature-gate-f16b.stderr | 6 +++--- tests/ui/float/f16b-restrictions.rs | 2 ++ tests/ui/float/f16b-restrictions.stderr | 8 ++++---- tests/ui/float/f16b.rs | 1 + tests/ui/parser/f16b.rs | 2 ++ tests/ui/parser/f16b.stderr | 2 +- 9 files changed, 17 insertions(+), 8 deletions(-) diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index b732088553bb3..5928938527d84 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -29,6 +29,7 @@ rustc_attrs, decl_macro, f16, + f16b, f128, repr_simd, transparent_unions, diff --git a/tests/codegen-llvm/float/f16b.rs b/tests/codegen-llvm/float/f16b.rs index f1eb6298cdf62..7fbb76977b691 100644 --- a/tests/codegen-llvm/float/f16b.rs +++ b/tests/codegen-llvm/float/f16b.rs @@ -1,4 +1,5 @@ //@ compile-flags: -O +//@ ignore-backends: gcc #![crate_type = "lib"] #![feature(f16b)] diff --git a/tests/ui/feature-gates/feature-gate-f16b.rs b/tests/ui/feature-gates/feature-gate-f16b.rs index 23b631d7bb58a..5f73ce9594e51 100644 --- a/tests/ui/feature-gates/feature-gate-f16b.rs +++ b/tests/ui/feature-gates/feature-gate-f16b.rs @@ -1,3 +1,5 @@ +//@ ignore-backends: gcc + extern crate core; use core::num::f16b; diff --git a/tests/ui/feature-gates/feature-gate-f16b.stderr b/tests/ui/feature-gates/feature-gate-f16b.stderr index 7654c3b726389..3764d6073b191 100644 --- a/tests/ui/feature-gates/feature-gate-f16b.stderr +++ b/tests/ui/feature-gates/feature-gate-f16b.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `f16b` - --> $DIR/feature-gate-f16b.rs:3:5 + --> $DIR/feature-gate-f16b.rs:5:5 | LL | use core::num::f16b; | ^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | use core::num::f16b; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `f16b` - --> $DIR/feature-gate-f16b.rs:7:13 + --> $DIR/feature-gate-f16b.rs:9:13 | LL | let _ = f16b::from_bits(0); | ^^^^ @@ -19,7 +19,7 @@ LL | let _ = f16b::from_bits(0); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `f16b` - --> $DIR/feature-gate-f16b.rs:7:13 + --> $DIR/feature-gate-f16b.rs:9:13 | LL | let _ = f16b::from_bits(0); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/float/f16b-restrictions.rs b/tests/ui/float/f16b-restrictions.rs index b2dbf1a734be3..d05646c25026e 100644 --- a/tests/ui/float/f16b-restrictions.rs +++ b/tests/ui/float/f16b-restrictions.rs @@ -1,3 +1,5 @@ +//@ ignore-backends: gcc + #![feature(f16b)] extern crate core; diff --git a/tests/ui/float/f16b-restrictions.stderr b/tests/ui/float/f16b-restrictions.stderr index 02ffd07e1a464..62de32a86c776 100644 --- a/tests/ui/float/f16b-restrictions.stderr +++ b/tests/ui/float/f16b-restrictions.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/f16b-restrictions.rs:8:19 + --> $DIR/f16b-restrictions.rs:10:19 | LL | let _: f16b = 1.0; | ---- ^^^ expected `f16b`, found floating-point number @@ -7,7 +7,7 @@ LL | let _: f16b = 1.0; | expected due to this error[E0369]: cannot add `f16b` to `f16b` - --> $DIR/f16b-restrictions.rs:12:15 + --> $DIR/f16b-restrictions.rs:14:15 | LL | let _ = x + x; | - ^ - f16b @@ -20,13 +20,13 @@ note: `f16b` does not implement `Add` = note: `f16b` is defined in another crate error[E0605]: non-primitive cast: `u16` as `f16b` - --> $DIR/f16b-restrictions.rs:15:13 + --> $DIR/f16b-restrictions.rs:17:13 | LL | let _ = 1u16 as f16b; | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `f16b` as `f32` - --> $DIR/f16b-restrictions.rs:18:13 + --> $DIR/f16b-restrictions.rs:20:13 | LL | let _ = x as f32; | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object diff --git a/tests/ui/float/f16b.rs b/tests/ui/float/f16b.rs index cdd4dfebd291d..da3a1a03c8036 100644 --- a/tests/ui/float/f16b.rs +++ b/tests/ui/float/f16b.rs @@ -1,4 +1,5 @@ //@ run-pass +//@ ignore-backends: gcc #![feature(f16b)] diff --git a/tests/ui/parser/f16b.rs b/tests/ui/parser/f16b.rs index bb6f3737a33d5..8c7816272dfcb 100644 --- a/tests/ui/parser/f16b.rs +++ b/tests/ui/parser/f16b.rs @@ -1,3 +1,5 @@ +//@ ignore-backends: gcc + #![feature(f16b)] extern crate core; diff --git a/tests/ui/parser/f16b.stderr b/tests/ui/parser/f16b.stderr index 2d89bbe4d8776..c01c98c77d652 100644 --- a/tests/ui/parser/f16b.stderr +++ b/tests/ui/parser/f16b.stderr @@ -1,5 +1,5 @@ error: invalid suffix `f16b` for float literal - --> $DIR/f16b.rs:12:13 + --> $DIR/f16b.rs:14:13 | LL | let _ = 0.0f16b; | ^^^^^^^ invalid suffix `f16b` From 84787b4da8d3f5e436f1be8a6d95a0ad764bd2ac Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Wed, 12 Aug 2026 13:03:42 +0100 Subject: [PATCH 08/16] weave in `target_has_reliable_f16b` --- compiler/rustc_codegen_cranelift/src/lib.rs | 1 + compiler/rustc_codegen_gcc/src/lib.rs | 1 + compiler/rustc_codegen_llvm/src/llvm_util.rs | 21 +++++++++++++++++++ compiler/rustc_codegen_ssa/src/lib.rs | 3 +++ .../rustc_codegen_ssa/src/traits/backend.rs | 1 + compiler/rustc_feature/src/builtin_attrs.rs | 5 +++++ compiler/rustc_feature/src/unstable.rs | 2 ++ compiler/rustc_interface/src/util.rs | 4 ++++ compiler/rustc_session/src/config/cfg.rs | 1 + compiler/rustc_span/src/symbol.rs | 2 ++ src/tools/miri/src/bin/miri.rs | 1 + 11 files changed, 42 insertions(+) diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 8b0ca770ec067..3ffa23010d8cf 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -190,6 +190,7 @@ impl CodegenBackend for CraneliftCodegenBackend { // available in Cranelift. has_reliable_f16: has_reliable_f16_f128, has_reliable_f16_math: has_reliable_f16_f128, + has_reliable_f16b: false, has_reliable_f128: has_reliable_f16_f128, has_reliable_f128_math, } diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index cbc7db8e9e23f..bd23c119857e9 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -560,6 +560,7 @@ fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig // There are no known bugs with GCC support for f16 or f128 has_reliable_f16, has_reliable_f16_math: has_reliable_f16, + has_reliable_f16b: false, has_reliable_f128, has_reliable_f128_math: has_reliable_f128, } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 298b58dd0007f..b75e9339f7cff 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -353,6 +353,7 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { internal_target_features, has_reliable_f16: true, has_reliable_f16_math: true, + has_reliable_f16b: true, has_reliable_f128: true, has_reliable_f128_math: true, }; @@ -390,6 +391,26 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { _ => true, }; + cfg.has_reliable_f16b = match (target_arch, target_os) { + // Unsupported . While this issue is for + // `f16` and `f128`, the same problem is observable for `bfloat` (fixed in llvm22) + (Arch::Arm64EC, _) if major < 22 => false, + // MinGW ABI bugs , while the issue + // is marked for `_Float16` the same observation can be made for `__bf16` + (Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != CfgAbi::Llvm => { + false + } + // These platforms do not have support for `f16b` + (Arch::CSky, _) => false, + (Arch::PowerPC | Arch::PowerPC64, _) => false, + (Arch::Sparc | Arch::Sparc64, _) => false, + (Arch::Wasm32 | Arch::Wasm64, _) => false, + // `f16b`, presently, only requires that it can be converted to a `f32`. + // Compiler builtins should have the required conversions to widen to + // an `f32` if need be. + _ => true, + }; + cfg.has_reliable_f128 = match (target_arch, target_os) { // Unsupported https://github.com/llvm/llvm-project/issues/121122 (Arch::AmdGpu, _) => false, diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index d62c5c713f9ea..8acfff7f0bba4 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -316,6 +316,9 @@ pub struct TargetConfig { pub has_reliable_f16: bool, /// Option for `cfg(target_has_reliable_f16_math)`, true if `f16` math calls work. pub has_reliable_f16_math: bool, + /// Option for `cfg(target_has_reliable_f16b)`, presently true if both the operating + /// system and the LLVM version supports `f16b`. + pub has_reliable_f16b: bool, /// Option for `cfg(target_has_reliable_f128)`, true if `f128` basic arithmetic works. pub has_reliable_f128: bool, /// Option for `cfg(target_has_reliable_f128_math)`, true if `f128` math calls work. diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 36f4d858d0be5..3d5c68c438cb9 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -49,6 +49,7 @@ pub trait CodegenBackend { // support the float types, rather than accidentally quietly skipping all tests. has_reliable_f16: true, has_reliable_f16_math: true, + has_reliable_f16b: true, has_reliable_f128: true, has_reliable_f128_math: true, } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index bc6f87a2a7f17..2b64a959cd3b3 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -40,6 +40,11 @@ const GATED_CFGS: &[GatedCfg] = &[ sym::cfg_target_has_reliable_f16_f128, Features::cfg_target_has_reliable_f16_f128, ), + ( + sym::target_has_reliable_f16b, + sym::cfg_target_has_reliable_f16b, + Features::cfg_target_has_reliable_f16b, + ), ( sym::target_has_reliable_f128, sym::cfg_target_has_reliable_f16_f128, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index ebe486f0adbbf..c198e77ca8f5d 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -254,6 +254,8 @@ declare_features! ( (unstable, anonymous_lifetime_in_impl_trait, "1.63.0", None), /// Allows checking whether or not the backend correctly supports unstable float types. (internal, cfg_target_has_reliable_f16_f128, "1.88.0", None), + /// Allows checking whether or not the backend correctly supports the unstable `f16b` type. + (internal, cfg_target_has_reliable_f16b, "CURRENT_RUSTC_VERSION", None), /// Allows checking whether or not the target might have thread support. (internal, cfg_target_has_threads, "CURRENT_RUSTC_VERSION", None), /// Allows identifying the `compiler_builtins` crate. diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index f6ea8ca19ce69..783179d9f82b4 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -78,6 +78,9 @@ pub(crate) fn add_configuration( if tf_cfg.has_reliable_f16_math { cfg.insert((sym::target_has_reliable_f16_math, None)); } + if tf_cfg.has_reliable_f16b { + cfg.insert((sym::target_has_reliable_f16b, None)); + } if tf_cfg.has_reliable_f128 { cfg.insert((sym::target_has_reliable_f128, None)); } @@ -407,6 +410,7 @@ impl CodegenBackend for DummyCodegenBackend { internal_target_features, has_reliable_f16: true, has_reliable_f16_math: true, + has_reliable_f16b: true, has_reliable_f128: true, has_reliable_f128_math: true, } diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index a6decd5898689..05e3c346cd767 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -154,6 +154,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { | (sym::target_has_atomic_load_store, Some(_)) | (sym::target_has_reliable_f16, None | Some(_)) | (sym::target_has_reliable_f16_math, None | Some(_)) + | (sym::target_has_reliable_f16b, None | Some(_)) | (sym::target_has_reliable_f128, None | Some(_)) | (sym::target_has_reliable_f128_math, None | Some(_)) | (sym::target_thread_local, None) => disallow(cfg, "--target"), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e241165f5080a..b0f251bd89393 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -601,6 +601,7 @@ symbols! { cfg_target_has_atomic, cfg_target_has_atomic_equal_alignment, cfg_target_has_reliable_f16_f128, + cfg_target_has_reliable_f16b, cfg_target_has_threads, cfg_target_object_format, cfg_target_thread_local, @@ -2105,6 +2106,7 @@ symbols! { target_has_atomic_primitive_alignment, target_has_reliable_f16, target_has_reliable_f16_math, + target_has_reliable_f16b, target_has_reliable_f128, target_has_reliable_f128_math, target_has_threads, diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 5a5acc53de766..2a2587bc255dc 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -115,6 +115,7 @@ fn make_miri_codegen_backend(sess: &Session) -> Box { let mut cfg = target_config_backend.target_config(sess); // The basic types and ABI always work. cfg.has_reliable_f16 = true; + cfg.has_reliable_f16b = true; cfg.has_reliable_f128 = true; // We always provide the f16 intrinsics, but some are provided via the host, // so forward its reliability. From 3b3e8ae79b7ffbc199d28658718e43ad98eb49c3 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Thu, 13 Aug 2026 11:29:37 +0100 Subject: [PATCH 09/16] update condition for `cfg.has_reliable_f16b = ...`, along with comment updates --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 28 ++++++++----------- compiler/rustc_codegen_ssa/src/lib.rs | 4 +-- compiler/rustc_target/src/callconv/mips64.rs | 2 +- compiler/rustc_target/src/callconv/sparc64.rs | 2 +- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index b75e9339f7cff..358724a537635 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -392,23 +392,19 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { }; cfg.has_reliable_f16b = match (target_arch, target_os) { - // Unsupported . While this issue is for - // `f16` and `f128`, the same problem is observable for `bfloat` (fixed in llvm22) - (Arch::Arm64EC, _) if major < 22 => false, - // MinGW ABI bugs , while the issue - // is marked for `_Float16` the same observation can be made for `__bf16` - (Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != CfgAbi::Llvm => { - false + // This is similar to , however + // does not work until LLVM 23 on Windows. + (Arch::Arm64EC, _) => { + if target_os == Os::Windows { + major >= 23 + } else { + major >= 19 + } } - // These platforms do not have support for `f16b` - (Arch::CSky, _) => false, - (Arch::PowerPC | Arch::PowerPC64, _) => false, - (Arch::Sparc | Arch::Sparc64, _) => false, - (Arch::Wasm32 | Arch::Wasm64, _) => false, - // `f16b`, presently, only requires that it can be converted to a `f32`. - // Compiler builtins should have the required conversions to widen to - // an `f32` if need be. - _ => true, + (Arch::AArch64, _) => major >= 19, + (Arch::X86_64, _) => major >= 15, + (Arch::RiscV64, _) => major >= 17, + _ => false, }; cfg.has_reliable_f128 = match (target_arch, target_os) { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 8acfff7f0bba4..4e8476dc8cc96 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -316,8 +316,8 @@ pub struct TargetConfig { pub has_reliable_f16: bool, /// Option for `cfg(target_has_reliable_f16_math)`, true if `f16` math calls work. pub has_reliable_f16_math: bool, - /// Option for `cfg(target_has_reliable_f16b)`, presently true if both the operating - /// system and the LLVM version supports `f16b`. + /// Option for `cfg(target_has_reliable_f16b)`, presently true if both the ABI + /// and LLVM version supports `f16b`. pub has_reliable_f16b: bool, /// Option for `cfg(target_has_reliable_f128)`, true if `f128` basic arithmetic works. pub has_reliable_f128: bool, diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index ca4d278710d6d..e1563c7acb236 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -29,7 +29,7 @@ where BackendRepr::Scalar(scalar) => match scalar.primitive() { Primitive::Float(float) => { match float { - // C does not have the f16 + // C does not have the f16 type Float::F16 => None, // No `f16b` type Float::F16B => panic!("`f16b` unsupported"), diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index bf8682c27b695..6304291546297 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -56,7 +56,7 @@ fn classify<'a, Ty, C>( _ => unreachable!(), }, Float::F16 => { - // Match LLVM by passing `f16` and `f16b` in integer registers. + // Match LLVM by passing `f16` in integer registers. } Float::F16B => panic!("`f16b` unsupported"), } From 1f9f414f419eb1bfc2aa93113c20bdab0ec0ae15 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Thu, 13 Aug 2026 14:49:26 +0100 Subject: [PATCH 10/16] documentation feedback --- library/core/src/num/f16b.rs | 59 ++++++++++++++++++- .../src/language-features/f16b.md | 4 +- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/library/core/src/num/f16b.rs b/library/core/src/num/f16b.rs index 6cbc4a68242d4..ea03fcc1a0757 100644 --- a/library/core/src/num/f16b.rs +++ b/library/core/src/num/f16b.rs @@ -28,7 +28,44 @@ use crate::{fmt, mem}; pub struct f16b(u16); impl f16b { - /// Creates a bfloat16 value from its raw representation. + /// Raw transmutation from `u16`. + /// + /// This is currently identical to `transmute::(v)` on all platforms. + /// It turns out this is incredibly portable, for two reasons: + /// + /// * Floats and Ints have the same endianness on all supported platforms. + /// * IEEE 754 very precisely specifies the bit layout of floats. + /// + /// However there is one caveat: prior to the 2008 version of IEEE 754, how + /// to interpret the NaN signaling bit wasn't actually specified. Most platforms + /// (notably x86 and ARM) picked the interpretation that was ultimately + /// standardized in 2008, but some didn't (notably MIPS). As a result, all + /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. + /// + /// Rather than trying to preserve signaling-ness cross-platform, this + /// implementation favors preserving the exact bits. This means that + /// any payloads encoded in NaNs will be preserved even if the result of + /// this method is sent over the network from an x86 machine to a MIPS one. + /// + /// If the results of this method are only manipulated by the same + /// architecture that produced them, then there is no portability concern. + /// + /// If the input isn't NaN, then there is no portability concern. + /// + /// If you don't care about signalingness (very likely), then there is no + /// portability concern. + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + /// + /// ``` + /// #![feature(f16b)] + /// # #[cfg(target_has_reliable_f16b)] { + /// + /// let v = f16b::from_bits(0x4a40); + /// assert_eq!(v, 12.5); + /// # } + /// ``` #[inline] #[must_use] #[unstable(feature = "f16b", issue = "160630")] @@ -37,10 +74,26 @@ impl f16b { unsafe { mem::transmute(bits) } } - /// Returns the raw representation of this value. + /// Raw transmutation to `u16`. + /// + /// This is currently identical to `transmute::(self)` on all platforms. + /// + /// See [`from_bits`](#method.from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + /// + /// ``` + /// #![feature(f16b)] + /// # #[cfg(target_has_reliable_f16)] { + /// + /// assert_eq!(f16b::from_bits(0x4a40)).to_bits(), 0x4a40); + /// # } + /// ``` #[inline] - #[must_use = "this returns the result of the operation, without modifying the original"] #[unstable(feature = "f16b", issue = "160630")] + #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_bits(self) -> u16 { // SAFETY: `f16b` and `u16` have the same size, and every bit pattern is valid. unsafe { mem::transmute(self) } diff --git a/src/doc/unstable-book/src/language-features/f16b.md b/src/doc/unstable-book/src/language-features/f16b.md index ae57ebd4a10e4..ada81e26c4f8a 100644 --- a/src/doc/unstable-book/src/language-features/f16b.md +++ b/src/doc/unstable-book/src/language-features/f16b.md @@ -6,6 +6,6 @@ The tracking issue for this feature is: [#160630] --- -Enable the experimental `core::num::f16b` type for values stored in the IEEE bfloat16 format. +Enable the `core::num::f16b` type for values stored in the bfloat16 format. -`f16b` is a nominal library type, not a primitive floating-point type: it has no literal suffix, arithmetic operators, or numeric `as` casts. A compiler lang item connects its layout to the native LLVM `bfloat` representation. +`f16b` is a nominal library type, not a primitive floating-point type: it has no literal suffix, arithmetic operators, or numeric `as` casts. A compiler lang item connects its layout to the chosen backends representation. From 7938bd469110ec9a36e0e29f2d6de034d0c080c6 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Fri, 14 Aug 2026 14:36:50 +0100 Subject: [PATCH 11/16] version >= 21 for AArch64, x86_64 and RISC-V, along with improved documentation --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 358724a537635..410b94a8c7f40 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -391,19 +391,16 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { _ => true, }; + // The heuristic for evaluating to true is twofold, namely; + // + // 1. Can LLVM compile an IR snippet containing `fpext bfloat % to float` + // 2. Does the documentation indicate `bf16` support, can be seen in the + // tracking issue; cfg.has_reliable_f16b = match (target_arch, target_os) { // This is similar to , however // does not work until LLVM 23 on Windows. - (Arch::Arm64EC, _) => { - if target_os == Os::Windows { - major >= 23 - } else { - major >= 19 - } - } - (Arch::AArch64, _) => major >= 19, - (Arch::X86_64, _) => major >= 15, - (Arch::RiscV64, _) => major >= 17, + (Arch::Arm64EC, _) => major >= 23, + (Arch::AArch64, _) | (Arch::X86_64, _) | (Arch::RiscV64, _) => major >= 21, _ => false, }; From c2162ecf3dc6fc4586d4d915afc83fe2b9d709e5 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Fri, 14 Aug 2026 15:27:50 +0100 Subject: [PATCH 12/16] Add tests checking assembly --- tests/assembly-llvm/f16b/aarch64.rs | 65 ++++++++++++++++++++++ tests/assembly-llvm/f16b/riscv.rs | 57 +++++++++++++++++++ tests/assembly-llvm/f16b/x86_64-linux.rs | 51 +++++++++++++++++ tests/assembly-llvm/f16b/x86_64-windows.rs | 55 ++++++++++++++++++ tests/auxiliary/minicore.rs | 43 +++++++++++++- 5 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 tests/assembly-llvm/f16b/aarch64.rs create mode 100644 tests/assembly-llvm/f16b/riscv.rs create mode 100644 tests/assembly-llvm/f16b/x86_64-linux.rs create mode 100644 tests/assembly-llvm/f16b/x86_64-windows.rs diff --git a/tests/assembly-llvm/f16b/aarch64.rs b/tests/assembly-llvm/f16b/aarch64.rs new file mode 100644 index 0000000000000..d602a236899aa --- /dev/null +++ b/tests/assembly-llvm/f16b/aarch64.rs @@ -0,0 +1,65 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: AARCH64_LINUX AARCH64_DARWIN AARCH64_BE AARCH64_MSVC ARM64EC_MSVC +//@ [AARCH64_LINUX] compile-flags: -Copt-level=3 --target aarch64-unknown-linux-gnu +//@ [AARCH64_LINUX] needs-llvm-components: aarch64 +//@ [AARCH64_BE] compile-flags: -Copt-level=3 --target aarch64_be-unknown-linux-gnu +//@ [AARCH64_BE] needs-llvm-components: aarch64 +//@ [AARCH64_DARWIN] compile-flags: -Copt-level=3 --target aarch64-apple-darwin +//@ [AARCH64_DARWIN] needs-llvm-components: aarch64 +//@ [AARCH64_MSVC] compile-flags: -Copt-level=3 --target aarch64-pc-windows-msvc +//@ [AARCH64_MSVC] needs-llvm-components: aarch64 +//@ [ARM64EC_MSVC] compile-flags: -Copt-level=3 --target arm64ec-pc-windows-msvc +//@ [ARM64EC_MSVC] needs-llvm-components: aarch64 +//@ [ARM64EC_MSVC] min-llvm-version: 23 + +#![feature(f16b, no_core)] +#![no_core] +#![crate_type = "lib"] +#![allow(improper_ctypes_definitions)] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; + +use minicore::From; +use minicore::num::f16b; + +// CHECK-LABEL: {{^"?[#_]?identity_f16b"?:}} +// CHECK: ret +#[unsafe(no_mangle)] +pub extern "C" fn identity_f16b(value: f16b) -> f16b { + value +} + +// CHECK-LABEL: {{^"?[#_]?f16b_to_bits"?:}} +// AARCH64_LINUX: fmov w0, s0 +// AARCH64_BE: fmov w0, s0 +// AARCH64_DARWIN: fmov w8, s0 +// AARCH64_DARWIN-NEXT: and w0, w8, #0xffff +// AARCH64_MSVC: fmov w0, s0 +// ARM64EC_MSVC: fmov w0, s0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn f16b_to_bits(value: f16b) -> u16 { + value.to_bits() +} + +// CHECK-LABEL: {{^"?[#_]?f16b_from_bits"?:}} +// CHECK: fmov s0, w0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn f16b_from_bits(bits: u16) -> f16b { + f16b::from_bits(bits) +} + +// CHECK-LABEL: {{^"?[#_]?widen_f16b"?:}} +// CHECK: fmov w8, s0 +// CHECK-NEXT: lsl w8, w8, #16 +// CHECK-NEXT: fmov s0, w8 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn widen_f16b(value: f16b) -> f32 { + f32::from(value) +} diff --git a/tests/assembly-llvm/f16b/riscv.rs b/tests/assembly-llvm/f16b/riscv.rs new file mode 100644 index 0000000000000..8459148975c50 --- /dev/null +++ b/tests/assembly-llvm/f16b/riscv.rs @@ -0,0 +1,57 @@ +//@ add-minicore +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=3 --target riscv64gc-unknown-linux-gnu +//@ needs-llvm-components: riscv + +#![feature(f16b, no_core)] +#![no_core] +#![crate_type = "lib"] +#![allow(improper_ctypes_definitions)] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; + +use minicore::From; +use minicore::num::f16b; + +// CHECK-LABEL: identity_f16b: +// CHECK: fmv.x.w a0, fa0 +// CHECK-NEXT: lui a1, 1048560 +// CHECK-NEXT: or a0, a0, a1 +// CHECK-NEXT: fmv.w.x fa0, a0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn identity_f16b(value: f16b) -> f16b { + value +} + +// CHECK-LABEL: f16b_to_bits: +// CHECK: fmv.x.w a0, fa0 +// CHECK-NEXT: slli a0, a0, 48 +// CHECK-NEXT: srli a0, a0, 48 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn f16b_to_bits(value: f16b) -> u16 { + value.to_bits() +} + +// CHECK-LABEL: f16b_from_bits: +// CHECK: lui a1, 1048560 +// CHECK-NEXT: or a0, a0, a1 +// CHECK-NEXT: fmv.w.x fa0, a0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn f16b_from_bits(bits: u16) -> f16b { + f16b::from_bits(bits) +} + +// CHECK-LABEL: widen_f16b: +// CHECK: fmv.x.w a0, fa0 +// CHECK-NEXT: slli a0, a0, 16 +// CHECK-NEXT: fmv.w.x fa0, a0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn widen_f16b(value: f16b) -> f32 { + f32::from(value) +} diff --git a/tests/assembly-llvm/f16b/x86_64-linux.rs b/tests/assembly-llvm/f16b/x86_64-linux.rs new file mode 100644 index 0000000000000..c8fe49b11d911 --- /dev/null +++ b/tests/assembly-llvm/f16b/x86_64-linux.rs @@ -0,0 +1,51 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ compile-flags: --target x86_64-unknown-linux-gnu +//@ needs-llvm-components: x86 + +#![feature(f16b, no_core)] +#![no_core] +#![crate_type = "lib"] +#![allow(improper_ctypes_definitions)] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; + +use minicore::From; +use minicore::num::f16b; + +// CHECK-LABEL: identity_f16b: +// CHECK: ret +#[unsafe(no_mangle)] +pub extern "C" fn identity_f16b(value: f16b) -> f16b { + value +} + +// CHECK-LABEL: f16b_to_bits: +// CHECK: pextrw eax, xmm0, 0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn f16b_to_bits(value: f16b) -> u16 { + value.to_bits() +} + +// CHECK-LABEL: f16b_from_bits: +// CHECK: pinsrw xmm0, edi, 0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn f16b_from_bits(bits: u16) -> f16b { + f16b::from_bits(bits) +} + +// CHECK-LABEL: widen_f16b: +// CHECK: pextrw eax, xmm0, 0 +// CHECK-NEXT: shl eax, 16 +// CHECK-NEXT: movd xmm0, eax +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn widen_f16b(value: f16b) -> f32 { + f32::from(value) +} diff --git a/tests/assembly-llvm/f16b/x86_64-windows.rs b/tests/assembly-llvm/f16b/x86_64-windows.rs new file mode 100644 index 0000000000000..43af6c3a5f516 --- /dev/null +++ b/tests/assembly-llvm/f16b/x86_64-windows.rs @@ -0,0 +1,55 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: WINDOWS_GNU WINDOWS_MSVC +//@ [WINDOWS_GNU] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ [WINDOWS_GNU] compile-flags: --target x86_64-pc-windows-gnu +//@ [WINDOWS_GNU] needs-llvm-components: x86 +//@ [WINDOWS_MSVC] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ [WINDOWS_MSVC] compile-flags: --target x86_64-pc-windows-msvc +//@ [WINDOWS_MSVC] needs-llvm-components: x86 + +#![feature(f16b, no_core)] +#![no_core] +#![crate_type = "lib"] +#![allow(improper_ctypes_definitions)] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; + +use minicore::From; +use minicore::num::f16b; + +// CHECK-LABEL: identity_f16b: +// CHECK: ret +#[unsafe(no_mangle)] +pub extern "C" fn identity_f16b(value: f16b) -> f16b { + value +} + +// CHECK-LABEL: f16b_to_bits: +// CHECK: pextrw eax, xmm0, 0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn f16b_to_bits(value: f16b) -> u16 { + value.to_bits() +} + +// CHECK-LABEL: f16b_from_bits: +// CHECK: pinsrw xmm0, ecx, 0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn f16b_from_bits(bits: u16) -> f16b { + f16b::from_bits(bits) +} + +// CHECK-LABEL: widen_f16b: +// CHECK: pextrw eax, xmm0, 0 +// CHECK-NEXT: shl eax, 16 +// CHECK-NEXT: movd xmm0, eax +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn widen_f16b(value: f16b) -> f32 { + f32::from(value) +} diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index 5928938527d84..a530388493e28 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -30,6 +30,7 @@ decl_macro, f16, f16b, + cfg_target_has_reliable_f16b, f128, repr_simd, transparent_unions, @@ -81,6 +82,10 @@ impl LegacyReceiver for &mut T {} #[lang = "copy"] pub trait Copy: Sized {} +pub trait From: Sized { + fn from(value: T) -> Self; +} + #[lang = "bikeshed_guaranteed_no_drop"] pub trait BikeshedGuaranteedNoDrop {} @@ -361,7 +366,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us pub mod mem { #[rustc_nounwind] #[rustc_intrinsic] - pub unsafe fn transmute(src: Src) -> Dst; + pub const unsafe fn transmute(src: Src) -> Dst; #[rustc_nounwind] #[rustc_intrinsic] @@ -393,7 +398,41 @@ pub mod hint { } pub mod num { - use super::Copy; + use super::{Copy, From, mem}; + + #[cfg(target_has_reliable_f16b)] + #[rustc_intrinsic] + const unsafe fn unchecked_shl(value: T, shift: U) -> T; + + #[cfg(target_has_reliable_f16b)] + #[allow(non_camel_case_types)] + #[lang = "bfloat"] + #[repr(transparent)] + pub struct f16b(u16); + + #[cfg(target_has_reliable_f16b)] + impl f16b { + #[inline] + pub const fn from_bits(bits: u16) -> Self { + unsafe { mem::transmute(bits) } + } + + #[inline] + pub const fn to_bits(self) -> u16 { + unsafe { mem::transmute(self) } + } + } + + #[cfg(target_has_reliable_f16b)] + impl Copy for f16b {} + + #[cfg(target_has_reliable_f16b)] + impl From for f32 { + #[inline] + fn from(value: f16b) -> Self { + unsafe { mem::transmute(unchecked_shl(value.to_bits() as u32, 16u32)) } + } + } #[repr(C)] #[lang = "complex"] From 6cec2289f03ea1e6ffd33a5a53ab4110caa04804 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Fri, 14 Aug 2026 16:01:18 +0100 Subject: [PATCH 13/16] more similar tests to the `f16` for feature gating --- ...ature-gate-cfg-target-has-reliable-f16b.rs | 6 ++ ...e-gate-cfg-target-has-reliable-f16b.stderr | 12 +++ tests/ui/feature-gates/feature-gate-f16b.rs | 20 ++++ .../ui/feature-gates/feature-gate-f16b.stderr | 92 ++++++++++++++++++- 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16b.rs create mode 100644 tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16b.stderr diff --git a/tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16b.rs b/tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16b.rs new file mode 100644 index 0000000000000..a1c2f22782c4c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16b.rs @@ -0,0 +1,6 @@ +//@ compile-flags: --check-cfg=cfg(target_has_reliable_f16b) + +fn main() { + cfg!(target_has_reliable_f16b); + //~^ ERROR `cfg(target_has_reliable_f16b)` is experimental and subject to change +} diff --git a/tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16b.stderr b/tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16b.stderr new file mode 100644 index 0000000000000..46106f61ad3ae --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-target-has-reliable-f16b.stderr @@ -0,0 +1,12 @@ +error[E0658]: `cfg(target_has_reliable_f16b)` is experimental and subject to change + --> $DIR/feature-gate-cfg-target-has-reliable-f16b.rs:4:10 + | +LL | cfg!(target_has_reliable_f16b); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(cfg_target_has_reliable_f16b)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-f16b.rs b/tests/ui/feature-gates/feature-gate-f16b.rs index 5f73ce9594e51..2be7f7a556227 100644 --- a/tests/ui/feature-gates/feature-gate-f16b.rs +++ b/tests/ui/feature-gates/feature-gate-f16b.rs @@ -9,4 +9,24 @@ fn main() { let _ = f16b::from_bits(0); //~^ ERROR use of unstable library feature `f16b` //~| ERROR use of unstable library feature `f16b` + + let a = 0.0f16b; + //~^ ERROR invalid suffix `f16b` + + let _: f16b = 1.0; + //~^ ERROR use of unstable library feature `f16b` + //~| ERROR mismatched types + + let x = f16b::from_bits(0x3f80); + //~^ ERROR use of unstable library feature `f16b` + //~| ERROR use of unstable library feature `f16b` + let _ = x + x; + //~^ ERROR cannot add `f16b` to `f16b` + + let _ = 1u16 as f16b; + //~^ ERROR use of unstable library feature `f16b` + //~| ERROR non-primitive cast + + let _ = x as f32; + //~^ ERROR non-primitive cast } diff --git a/tests/ui/feature-gates/feature-gate-f16b.stderr b/tests/ui/feature-gates/feature-gate-f16b.stderr index 3764d6073b191..21b537430c295 100644 --- a/tests/ui/feature-gates/feature-gate-f16b.stderr +++ b/tests/ui/feature-gates/feature-gate-f16b.stderr @@ -1,3 +1,11 @@ +error: invalid suffix `f16b` for float literal + --> $DIR/feature-gate-f16b.rs:13:13 + | +LL | let a = 0.0f16b; + | ^^^^^^^ invalid suffix `f16b` + | + = help: valid suffixes are `f32` and `f64` + error[E0658]: use of unstable library feature `f16b` --> $DIR/feature-gate-f16b.rs:5:5 | @@ -18,6 +26,36 @@ LL | let _ = f16b::from_bits(0); = help: add `#![feature(f16b)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: use of unstable library feature `f16b` + --> $DIR/feature-gate-f16b.rs:16:12 + | +LL | let _: f16b = 1.0; + | ^^^^ + | + = note: see issue #160630 for more information + = help: add `#![feature(f16b)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `f16b` + --> $DIR/feature-gate-f16b.rs:20:13 + | +LL | let x = f16b::from_bits(0x3f80); + | ^^^^ + | + = note: see issue #160630 for more information + = help: add `#![feature(f16b)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `f16b` + --> $DIR/feature-gate-f16b.rs:26:21 + | +LL | let _ = 1u16 as f16b; + | ^^^^ + | + = note: see issue #160630 for more information + = help: add `#![feature(f16b)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: use of unstable library feature `f16b` --> $DIR/feature-gate-f16b.rs:9:13 | @@ -28,6 +66,56 @@ LL | let _ = f16b::from_bits(0); = help: add `#![feature(f16b)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 3 previous errors +error[E0308]: mismatched types + --> $DIR/feature-gate-f16b.rs:16:19 + | +LL | let _: f16b = 1.0; + | ---- ^^^ expected `f16b`, found floating-point number + | | + | expected due to this + +error[E0658]: use of unstable library feature `f16b` + --> $DIR/feature-gate-f16b.rs:20:13 + | +LL | let x = f16b::from_bits(0x3f80); + | ^^^^^^^^^^^^^^^ + | + = note: see issue #160630 for more information + = help: add `#![feature(f16b)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0369]: cannot add `f16b` to `f16b` + --> $DIR/feature-gate-f16b.rs:23:15 + | +LL | let _ = x + x; + | - ^ - f16b + | | + | f16b + | +note: `f16b` does not implement `Add` + --> $SRC_DIR/core/src/num/f16b.rs:LL:COL + | + = note: `f16b` is defined in another crate + +error[E0605]: non-primitive cast: `u16` as `f16b` + --> $DIR/feature-gate-f16b.rs:26:13 + | +LL | let _ = 1u16 as f16b; + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + +error[E0605]: non-primitive cast: `f16b` as `f32` + --> $DIR/feature-gate-f16b.rs:30:13 + | +LL | let _ = x as f32; + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + | +help: consider using the `From` trait instead + | +LL - let _ = x as f32; +LL + let _ = f32::from(x); + | + +error: aborting due to 12 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0308, E0369, E0605, E0658. +For more information about an error, try `rustc --explain E0308`. From 669cd12c0088365e62087cea25f28cef9b238bad Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Fri, 14 Aug 2026 16:03:52 +0100 Subject: [PATCH 14/16] update documentation on struct --- library/core/src/num/f16b.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/num/f16b.rs b/library/core/src/num/f16b.rs index ea03fcc1a0757..4b5d00935b880 100644 --- a/library/core/src/num/f16b.rs +++ b/library/core/src/num/f16b.rs @@ -12,8 +12,8 @@ use crate::{fmt, mem}; /// /// The 16-bit brain floating-point intends to preserve the dynamic range of /// a 32-bit floating-point value while using half the storage. It does -/// this by using the same number of exponent bits as and `f32`, 8-bits, but only -/// using 7-bits for the mantissa see [Wikipedia on bfloat16][wikipedia] for +/// this by using 8 bits for the exponent, the same as `f32`, but only +/// using 7 bits for the mantissa. See [Wikipedia on bfloat16][wikipedia] for /// more information. /// /// [wikipedia]: https://en.wikipedia.org/wiki/Bfloat16_floating-point_format From ed74711ff09403d91caa831886cd42bc1e4d28cb Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Fri, 14 Aug 2026 16:05:30 +0100 Subject: [PATCH 15/16] update error message on WASM --- compiler/rustc_codegen_ssa/src/mir/naked_asm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 3ef2fb36bc65f..7aae6dec131b7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -506,7 +506,7 @@ fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str Integer::I128 => "i64, i64", }, Primitive::Float(float) => match float { - Float::F16B => panic!("`f16b` unsupported"), + Float::F16B => panic!("`f16b` unsupported on wasm"), Float::F16 | Float::F32 => "f32", Float::F64 => "f64", Float::F128 => "i64, i64", From 62a7f72873629dcab99f473bb072ab29b94565d8 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Fri, 14 Aug 2026 16:16:28 +0100 Subject: [PATCH 16/16] add loongarch64 & tests --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 4 +- tests/assembly-llvm/f16b/loongarch64.rs | 56 ++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 tests/assembly-llvm/f16b/loongarch64.rs diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 410b94a8c7f40..9c4b8e21109ac 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -400,7 +400,9 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { // This is similar to , however // does not work until LLVM 23 on Windows. (Arch::Arm64EC, _) => major >= 23, - (Arch::AArch64, _) | (Arch::X86_64, _) | (Arch::RiscV64, _) => major >= 21, + (Arch::AArch64, _) | (Arch::X86_64, _) | (Arch::RiscV64, _) | (Arch::LoongArch64, _) => { + major >= 21 + } _ => false, }; diff --git a/tests/assembly-llvm/f16b/loongarch64.rs b/tests/assembly-llvm/f16b/loongarch64.rs new file mode 100644 index 0000000000000..966c2ebfafc89 --- /dev/null +++ b/tests/assembly-llvm/f16b/loongarch64.rs @@ -0,0 +1,56 @@ +//@ add-minicore +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=3 --target loongarch64-unknown-linux-gnu +//@ needs-llvm-components: loongarch + +#![feature(f16b, no_core)] +#![no_core] +#![crate_type = "lib"] +#![allow(improper_ctypes_definitions)] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; + +use minicore::From; +use minicore::num::f16b; + +// CHECK-LABEL: identity_f16b: +// CHECK: movfr2gr.s $a0, $fa0 +// CHECK-NEXT: lu12i.w $a1, -16 +// CHECK-NEXT: or $a0, $a0, $a1 +// CHECK-NEXT: movgr2fr.w $fa0, $a0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn identity_f16b(value: f16b) -> f16b { + value +} + +// CHECK-LABEL: f16b_to_bits: +// CHECK: movfr2gr.s $a0, $fa0 +// CHECK-NEXT: bstrpick.d $a0, $a0, 15, 0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn f16b_to_bits(value: f16b) -> u16 { + value.to_bits() +} + +// CHECK-LABEL: f16b_from_bits: +// CHECK: lu12i.w $a1, -16 +// CHECK-NEXT: or $a0, $a0, $a1 +// CHECK-NEXT: movgr2fr.w $fa0, $a0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn f16b_from_bits(bits: u16) -> f16b { + f16b::from_bits(bits) +} + +// CHECK-LABEL: widen_f16b: +// CHECK: movfr2gr.s $a0, $fa0 +// CHECK-NEXT: slli.d $a0, $a0, 16 +// CHECK-NEXT: movgr2fr.w $fa0, $a0 +// CHECK-NEXT: ret +#[unsafe(no_mangle)] +pub extern "C" fn widen_f16b(value: f16b) -> f32 { + f32::from(value) +}