diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index 55b65fb1caaaf..be1fc0c034ce4 100644 --- a/compiler/rustc_target/src/callconv/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs @@ -1,5 +1,10 @@ +use std::iter; + +use rustc_abi::{BackendRepr, Primitive}; + use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; use crate::abi::{HasDataLayout, TyAbiInterface}; +use crate::spec::{HasTargetSpec, Target}; /// Indicates the variant of the AArch64 ABI we are compiling for. /// Used to accommodate Apple and Microsoft's deviations from the usual AAPCS ABI. @@ -15,7 +20,7 @@ pub(crate) enum AbiKind { fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option where Ty: TyAbiInterface<'a, C> + Copy, - C: HasDataLayout, + C: HasDataLayout + HasTargetSpec, { arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { let size = arg.layout.size; @@ -27,7 +32,7 @@ where let valid_unit = match unit.kind { RegKind::Integer => false, - RegKind::Float => true, + RegKind::Float => cx.target_spec().abi != "softfloat", RegKind::Vector => size.bits() == 64 || size.bits() == 128, }; @@ -35,10 +40,32 @@ where }) } +fn softfloat_float_abi(target: &Target, arg: &mut ArgAbi<'_, Ty>) { + if target.abi != "softfloat" { + return; + } + if let BackendRepr::Scalar(s) = arg.layout.backend_repr + && let Primitive::Float(f) = s.primitive() + { + // Do *not* use the floag registers for passing arguments, as that would make + // the ABI depend on whether `neon` instructions are enabled. + // Apparently there is no standard ABI here [1], so we can do whatever we want. + // We choose to pass floats via equal-sized integer registers. + // [1]: https://github.com/rust-lang/rust/issues/131058#issuecomment-2384960972 + arg.cast_to(Reg { kind: RegKind::Integer, size: f.size() }); + } else if let BackendRepr::ScalarPair(s1, s2) = arg.layout.backend_repr + && (matches!(s1.primitive(), Primitive::Float(_)) + || matches!(s2.primitive(), Primitive::Float(_))) + { + // For now just pass this indirectly, that definitely avoids all trouble. + arg.make_indirect(); + } +} + fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, kind: AbiKind) where Ty: TyAbiInterface<'a, C> + Copy, - C: HasDataLayout, + C: HasDataLayout + HasTargetSpec, { if !ret.layout.is_sized() { // Not touching this... @@ -51,6 +78,7 @@ where // See also: ret.extend_integer_width_to(32) } + softfloat_float_abi(cx.target_spec(), ret); return; } if let Some(uniform) = is_homogeneous_aggregate(cx, ret) { @@ -69,7 +97,7 @@ where fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, kind: AbiKind) where Ty: TyAbiInterface<'a, C> + Copy, - C: HasDataLayout, + C: HasDataLayout + HasTargetSpec, { if !arg.layout.is_sized() { // Not touching this... @@ -82,6 +110,8 @@ where // See also: arg.extend_integer_width_to(32); } + softfloat_float_abi(cx.target_spec(), arg); + return; } if let Some(uniform) = is_homogeneous_aggregate(cx, arg) { @@ -112,7 +142,7 @@ where pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, kind: AbiKind) where Ty: TyAbiInterface<'a, C> + Copy, - C: HasDataLayout, + C: HasDataLayout + HasTargetSpec, { if !fn_abi.ret.is_ignore() { classify_ret(cx, &mut fn_abi.ret, kind); @@ -125,3 +155,13 @@ where classify_arg(cx, arg, kind); } } + +pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + for arg in fn_abi.args.iter_mut().chain(iter::once(&mut fn_abi.ret)) { + softfloat_float_abi(cx.target_spec(), arg); + } +} diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index aa639f1624f42..fb0fe4029348e 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -738,6 +738,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { "x86" => x86::compute_rust_abi_info(cx, self, abi), "riscv32" | "riscv64" => riscv::compute_rust_abi_info(cx, self, abi), "loongarch64" => loongarch::compute_rust_abi_info(cx, self, abi), + "aarch64" => aarch64::compute_rust_abi_info(cx, self), _ => {} }; diff --git a/tests/codegen/aarch64-softfloat.rs b/tests/codegen/aarch64-softfloat.rs new file mode 100644 index 0000000000000..f5306a247c76e --- /dev/null +++ b/tests/codegen/aarch64-softfloat.rs @@ -0,0 +1,35 @@ +//@ compile-flags: --target aarch64-unknown-none-softfloat +//@ needs-llvm-components: aarch64 +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} +impl Copy for f64 {} + +// CHECK: i64 @pass_f64_C(i64 {{[^,]*}}) +#[no_mangle] +extern "C" fn pass_f64_C(x: f64) -> f64 { + x +} + +// CHECK: [2 x i64] @pass_f64_pair_C([2 x i64] {{[^,]*}}) +#[no_mangle] +extern "C" fn pass_f64_pair_C(x: (f64, f64)) -> (f64, f64) { + x +} + +// CHECK: i64 @pass_f64_Rust(i64 {{[^,]*}}, {{[^,]*}} %_dont_merge_with_other_fn) +#[no_mangle] +fn pass_f64_Rust(x: f64, _dont_merge_with_other_fn: bool) -> f64 { + x +} + +// CHECK: void @pass_f64_pair_Rust(ptr {{[^,]*}}, ptr {{[^,]*}}, {{[^,]*}} %_dont_merge_with_other_fn) +#[no_mangle] +fn pass_f64_pair_Rust(x: (f64, f64), _dont_merge_with_other_fn: bool) -> (f64, f64) { + x +}