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
14 changes: 10 additions & 4 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 @@ -70,7 +71,8 @@ impl Reg {
}

impl Reg {
pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
/// Alignment used for the scratch space when this type is part of a `PassMode::Cast`.
pub fn scratch_align<C: HasDataLayout>(&self, cx: &C) -> Align {
let dl = cx.data_layout();
match self.kind {
RegKind::Integer => match self.size.bits() {
Expand All @@ -86,6 +88,10 @@ impl Reg {
16 => dl.f16_align,
32 => dl.f32_align,
64 => dl.f64_align,
// This alignment is not a multiple of 80 bits, but the alignment
// here is only used to align the alloca of a cast, and there the
// full alignment of x87_f80 should be used.
80 => dl.x87_f80_align,
Comment thread
folkertdev marked this conversation as resolved.
Resolved
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.
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
28 changes: 24 additions & 4 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 @@ -1387,10 +1390,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 @@ -1403,6 +1418,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 @@ -1416,6 +1432,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,
Comment thread
folkertdev marked this conversation as resolved.
Resolved
F128 => dl.f128_align,
PpcF128 => dl.f128_align,
}
Expand All @@ -1429,6 +1448,7 @@ impl Float {
F16B => "f16b",
F32 => "f32",
F64 => "f64",
X87F80 => "x87_f80",
F128 => "f128",
PpcF128 => "ppcf128",
}
Expand All @@ -1439,7 +1459,7 @@ impl Float {

match self {
F16 | F32 | F64 | F128 => true,
F16B | PpcF128 => false,
F16B | X87F80 | PpcF128 => false,
}
}
}
Expand All @@ -1465,9 +1485,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: 3 additions & 3 deletions compiler/rustc_codegen_gcc/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
);
return self.context.new_call(self.location, fmodf128, &[a, b]);
}
Some(float @ (abi::Float::F16B | abi::Float::PpcF128)) => {
Some(float @ (abi::Float::F16B | abi::Float::X87F80 | abi::Float::PpcF128)) => {
bug!("frem unsupported on {float:?}")
}
None => (),
Expand Down Expand Up @@ -2028,7 +2028,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
abi::Float::F32 => bx.cx().const_u32(bits as u32),
abi::Float::F64 => bx.cx().const_u64(bits as u64),
abi::Float::F128 => bx.cx().const_u128(bits),
abi::Float::F16B | abi::Float::PpcF128 => {
abi::Float::F16B | abi::Float::X87F80 | abi::Float::PpcF128 => {
bug!("unsupported float width {float_type:?}")
}
};
Expand All @@ -2039,7 +2039,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
abi::Float::F32 => compute_clamp_bounds::<ieee::Single>(signed, int_width),
abi::Float::F64 => compute_clamp_bounds::<ieee::Double>(signed, int_width),
abi::Float::F128 => compute_clamp_bounds::<ieee::Quad>(signed, int_width),
abi::Float::F16B | abi::Float::PpcF128 => {
abi::Float::F16B | abi::Float::X87F80 | abi::Float::PpcF128 => {
bug!("unsupported float width {float_type:?}")
}
};
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_codegen_gcc/src/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
Some(Float::F32) => "tisf",
Some(Float::F64) => "tidf",
Some(Float::F128) => "titf",
None | Some(Float::F16B | Float::PpcF128) => {
None | Some(Float::F16B | Float::X87F80 | Float::PpcF128) => {
panic!("cannot cast a non-native integer to type {dest_typ:?}")
}
};
Expand Down Expand Up @@ -1037,7 +1037,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
Some(Float::F16 | Float::F32) => "sfti",
Some(Float::F64) => "dfti",
Some(Float::F128) => "tfti",
None | Some(Float::F16B | Float::PpcF128) => {
None | Some(Float::F16B | Float::X87F80 | Float::PpcF128) => {
bug!("cannot cast a {value_type:?} to non-native integer")
}
};
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 @@ -304,6 +304,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
11 changes: 11 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 Expand Up @@ -210,13 +212,22 @@ impl LlvmType for CastTarget {
return rest_ll_unit;
}

// The x87_f80 type has a store size of 10 bytes, but an allocation size of 12 or 16
// bytes. Ensure we don't cast to it, because that could have unintened behavior.
assert!(rest_ll_unit != cx.type_x87_f80());

return cx.type_array(rest_ll_unit, rest_count);
}

// Generate a struct type with the prefix and the "rest" arguments.
let prefix_args = self.prefix.iter().map(|reg| reg.llvm_type(cx));
let rest_args = (0..rest_count).map(|_| rest_ll_unit);
let args: Vec<_> = prefix_args.chain(rest_args).collect();

// The x87_f80 type has a store size of 10 bytes, but an allocation size of 12 or 16 bytes.
// Ensure we don't cast to it, because that could have unintened behavior.
assert!(args.iter().all(|a| *a != cx.type_x87_f80()));

cx.type_struct(&args)
}
}
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 @@ -950,6 +950,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 @@ -244,6 +244,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
31 changes: 25 additions & 6 deletions compiler/rustc_codegen_llvm/src/type_of.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fmt::Write;

use rustc_abi::Primitive::{Float, Int, Pointer};
use rustc_abi::Primitive::{self, Float, Int, Pointer};
use rustc_abi::{Align, BackendRepr, FieldsShape, Scalar, Size, Variants};
use rustc_codegen_ssa::traits::*;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
Expand Down Expand Up @@ -85,7 +85,16 @@ fn uncached_llvm_type<'a, 'tcx>(
}
}
}
FieldsShape::Array { count, .. } => cx.type_array(layout.field(cx, 0).llvm_type(cx), count),
FieldsShape::Array { count, .. } => {
let element_ty = layout.field(cx, 0);
if let BackendRepr::Scalar(scalar) = element_ty.backend_repr
&& scalar.primitive() == Primitive::Float(rustc_abi::Float::X87F80)
{
cx.type_padding_filler(element_ty.size * count, element_ty.align)
} else {
cx.type_array(element_ty.llvm_type(cx), count)
}

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.

This is a bit ugly. We could have element_ty.is_x87_f80_raw I guess, is that worth it?

}
FieldsShape::Arbitrary { .. } => match name {
None => {
let (llfields, packed) = struct_llfields(cx, layout);
Expand Down Expand Up @@ -135,7 +144,15 @@ fn struct_llfields<'a, 'tcx>(
result.push(cx.type_padding_filler(padding, padding_align));
debug!(" padding before: {:?}", padding);
}
result.push(field.llvm_type(cx));

if let BackendRepr::Scalar(scalar) = field.backend_repr
&& scalar.primitive() == Primitive::Float(rustc_abi::Float::X87F80)
{
result.push(cx.type_padding_filler(field.size, field.align));
} else {
result.push(field.llvm_type(cx));
}

offset = target_offset + field.size;
prev_effective_align = effective_field_align;
}
Expand Down Expand Up @@ -192,12 +209,14 @@ pub(crate) trait LayoutLlvmExt<'tcx> {

impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
/// Gets the LLVM type corresponding to a Rust type, i.e., `rustc_middle::ty::Ty`.
/// The pointee type of the pointer in `PlaceRef` is always this type.
/// For sized types, it is also the right LLVM type for an `alloca`
/// containing a value of that type, and most immediates (except `bool`).
///
/// For sized types, this is the in-memory type that is used for stores and loads.
/// The type used for immediates may be different, see [`Self::immediate_llvm_type`].
Comment on lines +213 to +214

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.

We can workshop this some more. I did just remove the line about the pointer type, it's blatantly wrong and didn't really fit in the reformatted text.

///
/// Unsized types, however, are represented by a "minimal unit", e.g.
/// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this
/// is useful for indexing slices, as `&[T]`'s data pointer is `T*`.
///
/// If the type is an unsized struct, the regular layout is generated,
/// with the innermost trailing unsized field using the "minimal unit"
/// of that field's type - this is useful for taking the address of
Expand Down
Loading
Loading