about summary refs log tree commit diff
path: root/compiler/rustc_target/src/callconv/powerpc64.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_target/src/callconv/powerpc64.rs')
-rw-r--r--compiler/rustc_target/src/callconv/powerpc64.rs118
1 files changed, 118 insertions, 0 deletions
diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs
new file mode 100644
index 00000000000..71e533b8cc5
--- /dev/null
+++ b/compiler/rustc_target/src/callconv/powerpc64.rs
@@ -0,0 +1,118 @@
+// FIXME:
+// Alignment of 128 bit types is not currently handled, this will
+// need to be fixed when PowerPC vector support is added.
+
+use crate::abi::call::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform};
+use crate::abi::{Endian, HasDataLayout, TyAbiInterface};
+use crate::spec::HasTargetSpec;
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+enum ABI {
+    ELFv1, // original ABI used for powerpc64 (big-endian)
+    ELFv2, // newer ABI used for powerpc64le and musl (both endians)
+    AIX,   // used by AIX OS, big-endian only
+}
+use ABI::*;
+
+fn is_homogeneous_aggregate<'a, Ty, C>(
+    cx: &C,
+    arg: &mut ArgAbi<'a, Ty>,
+    abi: ABI,
+) -> Option<Uniform>
+where
+    Ty: TyAbiInterface<'a, C> + Copy,
+    C: HasDataLayout,
+{
+    arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
+        // ELFv1 and AIX only passes one-member aggregates transparently.
+        // ELFv2 passes up to eight uniquely addressable members.
+        if ((abi == ELFv1 || abi == AIX) && arg.layout.size > unit.size)
+            || arg.layout.size > unit.size.checked_mul(8, cx).unwrap()
+        {
+            return None;
+        }
+
+        let valid_unit = match unit.kind {
+            RegKind::Integer => false,
+            RegKind::Float => true,
+            RegKind::Vector => arg.layout.size.bits() == 128,
+        };
+
+        valid_unit.then_some(Uniform::consecutive(unit, arg.layout.size))
+    })
+}
+
+fn classify<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, abi: ABI, is_ret: bool)
+where
+    Ty: TyAbiInterface<'a, C> + Copy,
+    C: HasDataLayout,
+{
+    if arg.is_ignore() || !arg.layout.is_sized() {
+        // Not touching this...
+        return;
+    }
+    if !arg.layout.is_aggregate() {
+        arg.extend_integer_width_to(64);
+        return;
+    }
+
+    // The AIX ABI expect byval for aggregates
+    // See https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/Targets/PPC.cpp.
+    if !is_ret && abi == AIX {
+        arg.pass_by_stack_offset(None);
+        return;
+    }
+
+    // The ELFv1 ABI doesn't return aggregates in registers
+    if is_ret && (abi == ELFv1 || abi == AIX) {
+        arg.make_indirect();
+        return;
+    }
+
+    if let Some(uniform) = is_homogeneous_aggregate(cx, arg, abi) {
+        arg.cast_to(uniform);
+        return;
+    }
+
+    let size = arg.layout.size;
+    if is_ret && size.bits() > 128 {
+        // Non-homogeneous aggregates larger than two doublewords are returned indirectly.
+        arg.make_indirect();
+    } else if size.bits() <= 64 {
+        // Aggregates smaller than a doubleword should appear in
+        // the least-significant bits of the parameter doubleword.
+        arg.cast_to(Reg { kind: RegKind::Integer, size })
+    } else {
+        // Aggregates larger than i64 should be padded at the tail to fill out a whole number
+        // of i64s or i128s, depending on the aggregate alignment. Always use an array for
+        // this, even if there is only a single element.
+        let reg = if arg.layout.align.abi.bytes() > 8 { Reg::i128() } else { Reg::i64() };
+        arg.cast_to(Uniform::consecutive(
+            reg,
+            size.align_to(Align::from_bytes(reg.size.bytes()).unwrap()),
+        ))
+    };
+}
+
+pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+    Ty: TyAbiInterface<'a, C> + Copy,
+    C: HasDataLayout + HasTargetSpec,
+{
+    let abi = if cx.target_spec().env == "musl" {
+        ELFv2
+    } else if cx.target_spec().os == "aix" {
+        AIX
+    } else {
+        match cx.data_layout().endian {
+            Endian::Big => ELFv1,
+            Endian::Little => ELFv2,
+        }
+    };
+
+    classify(cx, &mut fn_abi.ret, abi, true);
+
+    for arg in fn_abi.args.iter_mut() {
+        classify(cx, arg, abi, false);
+    }
+}