From a29477c9ca059112ab8da087976b1d43a8556410 Mon Sep 17 00:00:00 2001 From: Petr Sumbera Date: Mon, 21 Feb 2022 13:49:36 +0100 Subject: [PATCH 1/3] more complete sparc64 ABI fix for aggregates with floating point members Previous fix didn't handle nested structures at all. --- compiler/rustc_target/src/abi/call/sparc64.rs | 292 +++++++++++------- 1 file changed, 187 insertions(+), 105 deletions(-) diff --git a/compiler/rustc_target/src/abi/call/sparc64.rs b/compiler/rustc_target/src/abi/call/sparc64.rs index 39d80c4c7e7..822db6d05bb 100644 --- a/compiler/rustc_target/src/abi/call/sparc64.rs +++ b/compiler/rustc_target/src/abi/call/sparc64.rs @@ -1,29 +1,135 @@ // FIXME: This needs an audit for correctness and completeness. use crate::abi::call::{ - ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Reg, RegKind, Uniform, + ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Reg, Uniform, }; -use crate::abi::{self, HasDataLayout, Size, TyAbiInterface}; +use crate::abi::{self, HasDataLayout, Scalar, Size, TyAbiInterface, TyAndLayout}; -fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option +#[derive(Clone, Debug)] +pub struct Sdata { + pub prefix: [Option; 8], + pub prefix_index: usize, + pub last_offset: Size, + pub has_float: bool, + pub arg_attribute: ArgAttribute, +} + +fn arg_scalar(cx: &C, scalar: &Scalar, offset: Size, mut data: Sdata) -> Sdata +where + C: HasDataLayout, +{ + let dl = cx.data_layout(); + + if scalar.value != abi::F32 && scalar.value != abi::F64 { + return data; + } + + data.has_float = true; + + if !data.last_offset.is_aligned(dl.f64_align.abi) && data.last_offset < offset { + if data.prefix_index == data.prefix.len() { + return data; + } + data.prefix[data.prefix_index] = Some(Reg::i32()); + data.prefix_index += 1; + data.last_offset = data.last_offset + Reg::i32().size; + } + + for _ in 0..((offset - data.last_offset).bits() / 64) + .min((data.prefix.len() - data.prefix_index) as u64) + { + data.prefix[data.prefix_index] = Some(Reg::i64()); + data.prefix_index += 1; + data.last_offset = data.last_offset + Reg::i64().size; + } + + if data.last_offset < offset { + if data.prefix_index == data.prefix.len() { + return data; + } + data.prefix[data.prefix_index] = Some(Reg::i32()); + data.prefix_index += 1; + data.last_offset = data.last_offset + Reg::i32().size; + } + + if data.prefix_index == data.prefix.len() { + return data; + } + + if scalar.value == abi::F32 { + data.arg_attribute = ArgAttribute::InReg; + data.prefix[data.prefix_index] = Some(Reg::f32()); + data.last_offset = offset + Reg::f32().size; + } else { + data.prefix[data.prefix_index] = Some(Reg::f64()); + data.last_offset = offset + Reg::f64().size; + } + data.prefix_index += 1; + return data; +} + +fn arg_scalar_pair( + cx: &C, + scalar1: &Scalar, + scalar2: &Scalar, + mut offset: Size, + mut data: Sdata, +) -> Sdata +where + C: HasDataLayout, +{ + data = arg_scalar(cx, &scalar1, offset, data); + if scalar1.value == abi::F32 { + offset += Reg::f32().size; + } else if scalar2.value == abi::F64 { + offset += Reg::f64().size; + } else if let abi::Int(i, _signed) = scalar1.value { + offset += i.size(); + } else if scalar1.value == abi::Pointer { + offset = offset + Reg::i64().size; + } + + if (offset.raw % 4) != 0 && (scalar2.value == abi::F32 || scalar2.value == abi::F64) { + offset.raw += 4 - (offset.raw % 4); + } + data = arg_scalar(cx, &scalar2, offset, data); + return data; +} + +fn parse_structure<'a, Ty, C>( + cx: &C, + layout: TyAndLayout<'a, Ty>, + mut data: Sdata, + mut offset: Size, +) -> Sdata where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { - // Ensure we have at most eight uniquely addressable members. - if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() { - return None; + if let abi::FieldsShape::Union(_) = layout.fields { + return data; + } + + match layout.abi { + abi::Abi::Scalar(scalar) => { + data = arg_scalar(cx, &scalar, offset, data); } + abi::Abi::Aggregate { .. } => { + for i in 0..layout.fields.count().clone() { + if offset < layout.fields.offset(i) { + offset = layout.fields.offset(i); + } + data = parse_structure(cx, layout.field(cx, i).clone(), data.clone(), offset); + } + } + _ => { + if let abi::Abi::ScalarPair(scalar1, scalar2) = &layout.abi { + data = arg_scalar_pair(cx, scalar1, scalar2, offset, data); + } + } + } - let valid_unit = match unit.kind { - RegKind::Integer => false, - RegKind::Float => false, - RegKind::Vector => arg.layout.size.bits() == 128, - }; - - valid_unit.then_some(Uniform { unit, total: arg.layout.size }) - }) + return data; } fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, in_registers_max: Size) @@ -36,102 +142,78 @@ where return; } - // This doesn't intentionally handle structures with floats which needs - // special care below. - if let Some(uniform) = is_homogeneous_aggregate(cx, arg) { - arg.cast_to(uniform); - return; - } - - if let abi::FieldsShape::Arbitrary { .. } = arg.layout.fields { - let dl = cx.data_layout(); - let size = arg.layout.size; - let mut prefix = [None; 8]; - let mut prefix_index = 0; - let mut last_offset = Size::ZERO; - let mut has_float = false; - let mut arg_attribute = ArgAttribute::default(); - - for i in 0..arg.layout.fields.count() { - let field = arg.layout.field(cx, i); - let offset = arg.layout.fields.offset(i); - - if let abi::Abi::Scalar(scalar) = &field.abi { - if scalar.value == abi::F32 || scalar.value == abi::F64 { - has_float = true; - - if !last_offset.is_aligned(dl.f64_align.abi) && last_offset < offset { - if prefix_index == prefix.len() { - break; - } - prefix[prefix_index] = Some(Reg::i32()); - prefix_index += 1; - last_offset = last_offset + Reg::i32().size; - } - - for _ in 0..((offset - last_offset).bits() / 64) - .min((prefix.len() - prefix_index) as u64) - { - prefix[prefix_index] = Some(Reg::i64()); - prefix_index += 1; - last_offset = last_offset + Reg::i64().size; - } - - if last_offset < offset { - if prefix_index == prefix.len() { - break; - } - prefix[prefix_index] = Some(Reg::i32()); - prefix_index += 1; - last_offset = last_offset + Reg::i32().size; - } - - if prefix_index == prefix.len() { - break; - } - - if scalar.value == abi::F32 { - arg_attribute = ArgAttribute::InReg; - prefix[prefix_index] = Some(Reg::f32()); - last_offset = offset + Reg::f32().size; - } else { - prefix[prefix_index] = Some(Reg::f64()); - last_offset = offset + Reg::f64().size; - } - prefix_index += 1; - } - } - } - - if has_float && arg.layout.size <= in_registers_max { - let mut rest_size = size - last_offset; - - if (rest_size.raw % 8) != 0 && prefix_index < prefix.len() { - prefix[prefix_index] = Some(Reg::i32()); - rest_size = rest_size - Reg::i32().size; - } - - arg.cast_to(CastTarget { - prefix, - rest: Uniform { unit: Reg::i64(), total: rest_size }, - attrs: ArgAttributes { - regular: arg_attribute, - arg_ext: ArgExtension::None, - pointee_size: Size::ZERO, - pointee_align: None, - }, - }); - return; - } - } - let total = arg.layout.size; if total > in_registers_max { arg.make_indirect(); return; } - arg.cast_to(Uniform { unit: Reg::i64(), total }); + match arg.layout.fields { + abi::FieldsShape::Primitive => unreachable!(), + abi::FieldsShape::Array { .. } => { + // Arrays are passed indirectly + arg.make_indirect(); + return; + } + abi::FieldsShape::Union(_) => { + // Unions and are always treated as a series of 64-bit integer chunks + } + abi::FieldsShape::Arbitrary { .. } => { + // Stuctures with floating point numbers need special care. + + let mut data = parse_structure( + cx, + arg.layout.clone(), + Sdata { + prefix: [None; 8], + prefix_index: 0, + last_offset: Size::ZERO, + has_float: false, + arg_attribute: ArgAttribute::default(), + }, + Size { raw: 0 }, + ); + + if data.has_float { + // Structure { float, int, int } doesn't like to be handled like + // { float, long int }. Other way around it doesn't mind. + if data.last_offset < arg.layout.size + && (data.last_offset.raw % 8) != 0 + && data.prefix_index < data.prefix.len() + { + data.prefix[data.prefix_index] = Some(Reg::i32()); + data.prefix_index += 1; + data.last_offset += Reg::i32().size; + } + + let mut rest_size = arg.layout.size - data.last_offset; + if (rest_size.raw % 8) != 0 && data.prefix_index < data.prefix.len() { + data.prefix[data.prefix_index] = Some(Reg::i32()); + rest_size = rest_size - Reg::i32().size; + } + + arg.cast_to(CastTarget { + prefix: data.prefix, + rest: Uniform { + unit: Reg::i64(), + total: rest_size, + }, + attrs: ArgAttributes { + regular: data.arg_attribute, + arg_ext: ArgExtension::None, + pointee_size: Size::ZERO, + pointee_align: None, + }, + }); + return; + } + } + } + + arg.cast_to(Uniform { + unit: Reg::i64(), + total, + }); } pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) From 0d200116d3e8188ee7c8c997f31f54cb4d978612 Mon Sep 17 00:00:00 2001 From: Petr Sumbera Date: Mon, 21 Feb 2022 14:23:22 +0100 Subject: [PATCH 2/3] formatting fixes --- compiler/rustc_target/src/abi/call/sparc64.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_target/src/abi/call/sparc64.rs b/compiler/rustc_target/src/abi/call/sparc64.rs index 822db6d05bb..601b3e7d33b 100644 --- a/compiler/rustc_target/src/abi/call/sparc64.rs +++ b/compiler/rustc_target/src/abi/call/sparc64.rs @@ -194,10 +194,7 @@ where arg.cast_to(CastTarget { prefix: data.prefix, - rest: Uniform { - unit: Reg::i64(), - total: rest_size, - }, + rest: Uniform { unit: Reg::i64(), total: rest_size }, attrs: ArgAttributes { regular: data.arg_attribute, arg_ext: ArgExtension::None, @@ -210,10 +207,7 @@ where } } - arg.cast_to(Uniform { - unit: Reg::i64(), - total, - }); + arg.cast_to(Uniform { unit: Reg::i64(), total }); } pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) From 992c27c601db490d4b1f76a6e0e51475b871d315 Mon Sep 17 00:00:00 2001 From: Petr Sumbera Date: Thu, 24 Feb 2022 13:46:34 +0100 Subject: [PATCH 3/3] Add test for nested structures. --- src/test/codegen/sparc-struct-abi.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/codegen/sparc-struct-abi.rs b/src/test/codegen/sparc-struct-abi.rs index b531dba4607..e8816e4f303 100644 --- a/src/test/codegen/sparc-struct-abi.rs +++ b/src/test/codegen/sparc-struct-abi.rs @@ -81,3 +81,23 @@ pub struct FloatLongFloat { pub extern "C" fn structfloatlongfloat() -> FloatLongFloat { FloatLongFloat { f: 0.1, i: 123, g: 3.14 } } + +#[repr(C)] +pub struct FloatFloat { + f: f32, + g: f32, +} + +#[repr(C)] +pub struct NestedStructs { + a: FloatFloat, + b: FloatFloat, +} + +// CHECK: define inreg { float, float, float, float } @structnestestructs() +// CHECK-NEXT: start: +// CHECK-NEXT: ret { float, float, float, float } { float 0x3FB99999A0000000, float 0x3FF19999A0000000, float 0x40019999A0000000, float 0x400A666660000000 } +#[no_mangle] +pub extern "C" fn structnestestructs() -> NestedStructs { + NestedStructs { a: FloatFloat { f: 0.1, g: 1.1 }, b: FloatFloat { f: 2.2, g: 3.3 } } +}