Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions compiler/rustc_abi/src/callconv/reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ impl RegKind {
match primitive {
Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer,
Primitive::Float(Float::PpcF128) => RegKind::PpcF128,
Primitive::Float(Float::F16 | Float::F16B | Float::F32 | Float::F64 | Float::F128) => {
RegKind::Float
}
Primitive::Float(
Float::F16 | Float::F16B | Float::F32 | Float::F64 | Float::X87F80 | Float::F128,
) => RegKind::Float,
}
}
}
Expand Down Expand Up @@ -60,6 +60,7 @@ impl Reg {
reg_ctor!(f16, Float, 16);
reg_ctor!(f32, Float, 32);
reg_ctor!(f64, Float, 64);
reg_ctor!(x87_f80, Float, 80);
reg_ctor!(f128, Float, 128);

/// A vector of the given size with an unknown (and irrelevant) element type.
Expand All @@ -86,6 +87,7 @@ impl Reg {
16 => dl.f16_align,
32 => dl.f32_align,
64 => dl.f64_align,
80 => dl.x87_f80_align,

@beetrees beetrees Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that (80 / 8) % dl.x87_f80_align != 0, could you add a comment here explaining why this is correct (I think it is as the align is only used to affect the overall alignment of the cast alloca, but we should document that fact somewhere to avoid confusion).

View changes since the review

128 => dl.f128_align,
_ => panic!("unsupported float: {self:?}"),
},
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_abi/src/layout/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug + std::fmt::Display {
fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
fn is_transparent(this: TyAndLayout<'a, Self>) -> bool;
fn is_complex_number_lang_item(this: TyAndLayout<'a, Self>, cx: &C) -> bool;
fn is_x87_f80_lang_item(this: TyAndLayout<'a, Self>, cx: &C) -> bool;
fn is_scalable_vector(this: TyAndLayout<'a, Self>) -> bool;
/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'a, Self>) -> bool;
Expand Down Expand Up @@ -232,6 +233,17 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
Ty::is_scalable_vector(self)
}

/// Returns `true` if this type is the `x87_f80` lang item.
///
/// Note that this function returns `false` for the `x87_f80_raw` lang item.
pub fn is_x87_f80<C>(self, cx: &C) -> bool
Comment thread
folkertdev marked this conversation as resolved.
where
Ty: TyAbiInterface<'a, C> + Copy,
{
// A 1-ZST is never x87_f80.
Ty::is_x87_f80_lang_item(self.peel_transparent_wrappers_from_non_1zst(cx), cx)
}

/// If this method returns `true`, then this type should always have a `PassMode` of
/// `Indirect { mode: IndirectMode::Pointer, .. }` when being used as the argument type of a
/// function with a non-Rustic ABI (this is true for structs annotated with the
Expand Down Expand Up @@ -339,6 +351,10 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {

let component = complex.field(cx, 0).peel_transparent_wrappers_from_non_1zst(cx);

if component.is_x87_f80(cx) {
return Some(Numeric::Float(Float::X87F80));
}

let BackendRepr::Scalar(scalar) = component.backend_repr else {
return None;
};
Expand All @@ -353,6 +369,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
Primitive::Float(
float @ (Float::F16 | Float::F32 | Float::F64 | Float::F128 | Float::PpcF128),
) => Some(Numeric::Float(float)),
Primitive::Float(Float::X87F80) => None, // x87_f80_raw has incorrect alignment.
Primitive::Pointer(..) | Primitive::Float(Float::F16B) => None,
}
}
Expand Down
26 changes: 23 additions & 3 deletions compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ pub struct TargetDataLayout {
pub f16_align: Align,
pub f32_align: Align,
pub f64_align: Align,
pub x87_f80_align: Align,
pub f128_align: Align,
pub aggregate_align: Align,

Expand Down Expand Up @@ -367,6 +368,7 @@ impl Default for TargetDataLayout {
f16_align: align(16),
f32_align: align(32),
f64_align: align(64),
x87_f80_align: align(128),
f128_align: align(128),
aggregate_align: align(8),
vector_align: vec![
Expand Down Expand Up @@ -510,6 +512,7 @@ impl TargetDataLayout {
["f16", a @ ..] => dl.f16_align = parse_align_seq(a, "f16")?,
["f32", a @ ..] => dl.f32_align = parse_align_seq(a, "f32")?,
["f64", a @ ..] => dl.f64_align = parse_align_seq(a, "f64")?,
["f80", a @ ..] => dl.x87_f80_align = parse_align_seq(a, "f80")?,
["f128", a @ ..] => dl.f128_align = parse_align_seq(a, "f128")?,
[p, s, a @ ..] if p.starts_with("p") => {
let mut p = p.strip_prefix('p').unwrap();
Expand Down Expand Up @@ -1427,10 +1430,22 @@ pub enum Float {
F16B,
F32,
F64,
/// `x87_f80`. This is not a builtin type in Rust (it is exposed as a lang item),
/// but it is a builtin type in LLVM so needs to be explicitly represented
/// in the backend.
///
/// Here we define the 80-bit scalar value that corresponds to the `x87_f80_raw` lang item.
/// In `core::arch::{x86, x86_64}` this type is wrapped by the `x87_f80` lang item that adds
/// the proper alignment and bumps the size to either 12 or 16 bytes depending on the target.
///
/// Only used on x86 and x86_64 targets.
X87F80,
F128,
/// `ppcf128`. This is not a builtin type in Rust (it is exposed as a lang item),
/// but it is a builtin type in LLVM so needs to be explicitly represented
/// in the backend.
///
/// Only used on powerpc and powerpc64 targets.
PpcF128,
}

Expand All @@ -1443,6 +1458,7 @@ impl Float {
F16B => Size::from_bits(16),
F32 => Size::from_bits(32),
F64 => Size::from_bits(64),
X87F80 => Size::from_bits(80),
F128 => Size::from_bits(128),
PpcF128 => Size::from_bits(128),
}
Expand All @@ -1456,6 +1472,9 @@ impl Float {
F16 | F16B => dl.f16_align,
F32 => dl.f32_align,
F64 => dl.f64_align,
// Use an alignment of 1 for x87_f80_raw,
// so that its size really is 80 bits in memory.
X87F80 => Align::ONE,

@beetrees beetrees Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth a short comment here explaining why this isn't dl.x87_f80_align?

View changes since the review

F128 => dl.f128_align,
PpcF128 => dl.f128_align,
})
Expand All @@ -1469,6 +1488,7 @@ impl Float {
F16B => "f16b",
F32 => "f32",
F64 => "f64",
X87F80 => "x87_f80",
F128 => "f128",
PpcF128 => "ppcf128",
}
Expand Down Expand Up @@ -1496,9 +1516,9 @@ impl Numeric {
match self {
Numeric::Int(_, _) => RegKind::Integer,
Numeric::Float(Float::PpcF128) => RegKind::PpcF128,
Numeric::Float(Float::F16 | Float::F16B | Float::F32 | Float::F64 | Float::F128) => {
RegKind::Float
}
Numeric::Float(
Float::F16 | Float::F16B | Float::F32 | Float::F64 | Float::X87F80 | Float::F128,
) => RegKind::Float,
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_attr_ir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ language_item_table! {

Complex, sym::complex, complex, Target::Struct, GenericRequirement::Exact(1);
PpcF128, sym::ppcf128, ppcf128, Target::Struct, GenericRequirement::None;
X87F80, sym::x87_f80, x87_f80, Target::Struct, GenericRequirement::None;
X87F80Raw, sym::x87_f80_raw, x87_f80_raw, Target::Struct, GenericRequirement::None;

Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_cranelift/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub(crate) fn scalar_to_clif_type(tcx: TyCtxt<'_>, scalar: Scalar) -> Type {
Float::F16B => bug!("f16b is not supported by the Cranelift codegen backend"),
Float::F32 => types::F32,
Float::F64 => types::F64,
Float::X87F80 => bug!("cranelift does not support x87 floats"),
Float::F128 => types::F128,
Float::PpcF128 => bug!("cranelift does not support powerpc"),
},
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_codegen_gcc/src/type_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,12 @@ impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> {
bug!("unsupported ppcf128 type")
}

fn type_x87_f80(&self) -> Type<'gcc> {
// FIXME(x87_f80): The GCC gcc_jit_types enum (which CType is based on) does not
// have __float80. It does have long double but it may not correspond to x87 f80.
bug!("unsupported x87_f80 type")
}

fn type_func(&self, params: &[Type<'gcc>], return_type: Type<'gcc>) -> Type<'gcc> {
self.context.new_function_pointer_type(None, return_type, params, false)
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_codegen_llvm/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ impl LlvmType for Reg {
16 => cx.type_f16(),
32 => cx.type_f32(),
64 => cx.type_f64(),
80 => cx.type_x87_f80(),
128 => cx.type_f128(),
_ => bug!("unsupported float: {:?}", self),
},
Expand All @@ -167,6 +168,7 @@ impl LlvmType for Reg {
Float::F64 => cx.type_f64(),
Float::F128 => cx.type_f128(),
Float::PpcF128 => bug!("ppcf128 is not a valid vector element type"),
Float::X87F80 => bug!("x87_f80 is not a valid vector element type"),
},
Primitive::Pointer(_) => cx.type_ptr(),
};
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
Primitive::Float(Float::PpcF128) => {
// Supported on powerpc and powerpc64 targets.
}
Primitive::Float(Float::X87F80) => {
// FIXME(x87_f80) we should support this.
bug!("the va_arg intrinsic does not currently support `x87_f80`")
}
}

emit_va_arg(self, args[0], result_layout)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,7 @@ unsafe extern "C" {

// Operations on non-IEEE real types
pub(crate) fn LLVMBFloatTypeInContext<'ll>(C: Context<'ll>) -> &'ll Type;
pub(crate) fn LLVMX86FP80TypeInContext<'ll>(C: Context<'ll>) -> &'ll Type;
pub(crate) fn LLVMPPCFP128TypeInContext<'ll>(C: Context<'ll>) -> &'ll Type;

// Operations on function types
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_codegen_llvm/src/type_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> BaseTypeCodegenMethods for GenericCx<'ll, CX> {
unsafe { llvm::LLVMPPCFP128TypeInContext(self.llcx()) }
}

fn type_x87_f80(&self) -> &'ll Type {
unsafe { llvm::LLVMX86FP80TypeInContext(self.llcx()) }
}

fn type_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type {
unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, FALSE) }
}
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_codegen_llvm/src/type_of.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::fmt::Write;

use rustc_abi::Primitive::{Float, Int, Pointer};
use rustc_abi::{Align, BackendRepr, FieldsShape, Scalar, Size, Variants};
use rustc_abi::{Align, BackendRepr, FieldsShape, Scalar, Size, TyAbiInterface, Variants};
use rustc_codegen_ssa::traits::*;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
Expand Down Expand Up @@ -75,6 +75,11 @@ fn uncached_llvm_type<'a, 'tcx>(
BackendRepr::Memory { .. } | BackendRepr::ScalarPair { .. } => {}
}

// Map `core::arch::{x86, x86_64}::x87_f80` to LLVM's `x86_fp80`.
if Ty::is_x87_f80_lang_item(layout, cx) {
return cx.type_x87_f80();
}
Comment on lines +78 to +81

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this is really all it takes in the LLVM backend to make this type work. it gets the LLVM store size of 10 bytes (touched by load/store) and target-dependent alloc size of 12 or 16.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x87_f80 is BackendRepr::Memory, so we should never be loading or storing its LLVM type. Instead, when uncached_llvm_type is fetching the type of the field of a struct or array, it should replace any occurrences of x87_f80_raw with [10 x i8]. This will ensure the returned struct always has the right size, which I AFAICT is the problem this is here to solve. Additionally, could you add an assert with FIXME to CastTarget::llvm_type ensuring it never returns an LLVM struct/array containing an LLVM x86_fp80 - load_cast/store_cast currently load/store the LLVM cast target aggregate directly (which is not generally a good idea for reasons like this, but that can be fixed in a separate PR).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the LLVM type just behaves correctly though? It has a store size of 10 but an allocation size of 12/16, and LLVM uses the right value in the right context as far as I can tell. Hence, we don't need to manually convert to the array?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

phrased differently: what sort of code do you think fails with the current implementation, because we should test its behavior either way.


let name = match layout.ty.kind() {
// FIXME(eddyb) producing readable type names for trait objects can result
// in problematically distinct types due to HRTB and subtyping (see #47638).
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_codegen_llvm/src/va_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ fn get_param_type_alignment<'ll, 'tcx>(
Integer::I128 => return Align::EIGHT,
},
Primitive::Float(float) => match float {
Float::F16 | Float::F16B | Float::F32 => unreachable!(),
Float::F16 | Float::F16B | Float::F32 | Float::X87F80 => unreachable!(),
Float::F64 | Float::F128 => { /* fall through */ }
Float::PpcF128 => {
return if is_powerpc_softfloat_abi(&bx.cx.tcx.sess.target) {
Expand Down Expand Up @@ -491,6 +491,9 @@ fn emit_s390x_va_arg<'ll, 'tcx>(
Primitive::Float(Float::PpcF128) => {
bug!("`ppcf128` use in varadics unsupported on s390x")
}
Primitive::Float(Float::X87F80) => {
bug!("`x87_f80` use in varadics unsupported on s390x")
}
},

_ => false,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str
Float::F16B => bug!("`f16b` unsupported on wasm"),
Float::F16 | Float::F32 => "f32",
Float::F64 => "f64",
Float::X87F80 => bug!("`x87_f80` unsupported on wasm"),
Float::F128 => "i64, i64",
Float::PpcF128 => bug!("`ppcf128` unsupported on wasm"),
},
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_codegen_ssa/src/traits/type_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub trait BaseTypeCodegenMethods: BackendTypes {
fn type_f64(&self) -> Self::Type;
fn type_f128(&self) -> Self::Type;
fn type_ppcf128(&self) -> Self::Type;
fn type_x87_f80(&self) -> Self::Type;

fn type_func(&self, args: &[Self::Type], ret: Self::Type) -> Self::FunctionSignature;
fn type_kind(&self, ty: Self::Type) -> TypeKind;
Expand Down Expand Up @@ -71,6 +72,7 @@ pub trait DerivedTypeCodegenMethods<'tcx>:
F16B => self.type_f16b(),
F32 => self.type_f32(),
F64 => self.type_f64(),
X87F80 => self.type_x87_f80(),
F128 => self.type_f128(),
PpcF128 => self.type_ppcf128(),
}
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ impl abi::Float {
),
F32 => tcx.types.f32,
F64 => tcx.types.f64,
X87F80 => Ty::new_adt(
tcx,
tcx.adt_def(tcx.require_lang_item(LangItem::X87F80Raw, DUMMY_SP)),
ty::List::empty(),
),
F128 => tcx.types.f128,
PpcF128 => Ty::new_adt(
tcx,
Expand Down Expand Up @@ -1255,6 +1260,12 @@ where
cx.tcx().is_lang_item(def.did(), LangItem::Complex)
}

/// Is this type `core::arch::{x86, x86_64}::x87_f80`?
fn is_x87_f80_lang_item(this: TyAndLayout<'tcx>, cx: &C) -> bool {
let Some(def) = this.ty.ty_adt_def() else { return false };
cx.tcx().is_lang_item(def.did(), LangItem::X87F80)
}

fn is_scalable_vector(this: TyAndLayout<'tcx>) -> bool {
this.ty.is_scalable_vector()
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_public/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ pub enum FloatLength {
F16B,
F32,
F64,
X87F80,
F128,
PpcF128,
}
Expand All @@ -560,6 +561,7 @@ impl FloatLength {
FloatLength::F16 | FloatLength::F16B => 16,
FloatLength::F32 => 32,
FloatLength::F64 => 64,
FloatLength::X87F80 => 80,
FloatLength::F128 => 128,
FloatLength::PpcF128 => 128,
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_public/src/unstable/convert/stable/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Float {
rustc_abi::Float::F16B => FloatLength::F16B,
rustc_abi::Float::F32 => FloatLength::F32,
rustc_abi::Float::F64 => FloatLength::F64,
rustc_abi::Float::X87F80 => FloatLength::X87F80,
rustc_abi::Float::F128 => FloatLength::F128,
rustc_abi::Float::PpcF128 => FloatLength::PpcF128,
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2433,6 +2433,8 @@ symbols! {
x86,
x86_64,
x86_amx_intrinsics,
x87_f80,
x87_f80_raw,
x87_reg,
x87_target_feature,
xcoff,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_target/src/callconv/mips.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ where
Numeric::Int(Integer::I128, _) => false,
Numeric::Float(Float::F32 | Float::F64) => true,
Numeric::Float(Float::F16 | Float::F128) => false,
Numeric::Float(Float::F16B | Float::PpcF128) => bug!("unsupported on mips"),
Numeric::Float(Float::F16B | Float::X87F80 | Float::PpcF128) => bug!("unsupported on mips"),
};

if let Some(component) = ret.layout.complex_number(cx)
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_target/src/callconv/mips64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ where
Float::F64 => Some(Reg::f64()),
Float::F128 => Some(Reg::f128()),
Float::F16B => unreachable!("`f16b` unsupported on mips64"),
Float::X87F80 => unreachable!("`x87_f80` unsupported on mips64"),
Float::PpcF128 => unreachable!("`ppcf128` unsupported on mips64"),
}
}
Expand Down Expand Up @@ -142,8 +143,9 @@ where

match component {
Numeric::Float(Float::F16B) => unreachable!("Complex<f16b> is not C-compatible"),
Numeric::Float(Float::F16) => unreachable!("not supported on mips64"),
Numeric::Float(Float::PpcF128) => unreachable!("not supported on mips64"),
Numeric::Float(Float::F16 | Float::X87F80 | Float::PpcF128) => {
unreachable!("not supported on mips64")
}
Numeric::Float(Float::F32 | Float::F64) => {
// Only pass a Complex<f32>/Complex<f64> in FPRs when two argument slots are free.
if curr_offset.bytes() / slot.bytes() + 2 <= NUM_ARG_SLOTS {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/callconv/sparc64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ fn classify<'a, Ty, C>(
// Match LLVM by passing `f16` in integer registers.
}
Float::F16B => unreachable!("`f16b` unsupported on sparc64"),
Float::X87F80 => unreachable!("`x87_f80` unsupported on sparc64"),
Float::PpcF128 => unreachable!("`ppcf128` unsupported on sparc64"),
}
} else {
Expand Down
Loading
Loading