Skip to main content

rustc_target/callconv/
powerpc64.rs

1// FIXME:
2// Alignment of 128 bit types is not currently handled, this will
3// need to be fixed when PowerPC vector support is added.
4
5use rustc_abi::{HasDataLayout, Integer, Numeric, TyAbiInterface};
6
7use crate::callconv::{Align, ArgAbi, CastTarget, FnAbi, Reg, RegKind, Uniform};
8use crate::spec::{HasTargetSpec, LlvmAbi, Os};
9
10#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ABI {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                ABI::ELFv1 => "ELFv1",
                ABI::ELFv2 => "ELFv2",
                ABI::AIX => "AIX",
            })
    }
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ABI { }
#[automatically_derived]
impl ::core::clone::Clone for ABI {
    #[inline]
    fn clone(&self) -> ABI { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ABI { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for ABI { }
#[automatically_derived]
impl ::core::cmp::PartialEq for ABI {
    #[inline]
    fn eq(&self, other: &ABI) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq)]
11enum ABI {
12    ELFv1, // original ABI used for powerpc64 (big-endian)
13    ELFv2, // newer ABI used for powerpc64le and musl (both endians)
14    AIX,   // used by AIX OS, big-endian only
15}
16use ABI::*;
17
18fn is_homogeneous_aggregate<'a, Ty, C>(
19    cx: &C,
20    arg: &mut ArgAbi<'a, Ty>,
21    abi: ABI,
22) -> Option<Uniform>
23where
24    Ty: TyAbiInterface<'a, C> + Copy,
25    C: HasDataLayout,
26{
27    arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
28        // ELFv1 and AIX only passes one-member aggregates transparently.
29        // ELFv2 passes up to eight uniquely addressable members.
30        if ((abi == ELFv1 || abi == AIX) && arg.layout.size > unit.size)
31            || arg.layout.size > unit.size.checked_mul(8, cx).unwrap()
32        {
33            return None;
34        }
35
36        let valid_unit = match unit.kind {
37            RegKind::Integer => false,
38            RegKind::Float => true,
39            RegKind::Vector { .. } => unit.size.bits() == 128,
40        };
41
42        valid_unit.then_some(Uniform::consecutive(unit, arg.layout.size))
43    })
44}
45
46fn classify<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, abi: ABI, is_ret: bool)
47where
48    Ty: TyAbiInterface<'a, C> + Copy,
49    C: HasDataLayout,
50{
51    if arg.is_ignore() || !arg.layout.is_sized() {
52        // Not touching this...
53        return;
54    }
55    if !is_ret && arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
56        arg.make_indirect();
57        return;
58    }
59    if !arg.layout.is_aggregate() {
60        arg.extend_integer_width_to(64);
61        return;
62    }
63    if let Some(component) = arg.layout.complex_number(cx) {
64        if let Numeric::Int(Integer::I16, _) = component {
65            // FIXME: use `PassMode::Cast` here. In LLVM 23 doing so would hit
66            // https://github.com/llvm/llvm-project/issues/218676.
67            return;
68        }
69
70        let reg = Reg { kind: component.reg_kind(), size: component.size() };
71        arg.cast_to(CastTarget::pair(reg, reg));
72        return;
73    }
74
75    // The AIX ABI expect byval for aggregates
76    // See https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/Targets/PPC.cpp.
77    // The incoming parameter is represented as a pointer in the IR,
78    // the alignment is associated with the size of the register. (align 8 for 64bit)
79    if !is_ret && abi == AIX {
80        arg.pass_by_stack_offset(Some(Align::from_bytes(8).unwrap()));
81        return;
82    }
83
84    // The ELFv1 ABI doesn't return aggregates in registers
85    if is_ret && (abi == ELFv1 || abi == AIX) {
86        arg.make_indirect();
87        return;
88    }
89
90    if let Some(uniform) = is_homogeneous_aggregate(cx, arg, abi) {
91        arg.cast_to(uniform);
92        return;
93    }
94
95    let size = arg.layout.size;
96    if is_ret && size.bits() > 128 {
97        // Non-homogeneous aggregates larger than two doublewords are returned indirectly.
98        arg.make_indirect();
99    } else if size.bits() <= 64 {
100        // Aggregates smaller than a doubleword should appear in
101        // the least-significant bits of the parameter doubleword.
102        arg.cast_to(Reg { kind: RegKind::Integer, size })
103    } else {
104        // Aggregates larger than i64 should be padded at the tail to fill out a whole number
105        // of i64s or i128s, depending on the aggregate alignment. Always use an array for
106        // this, even if there is only a single element.
107        let reg = if arg.layout.align.bytes() > 8 { Reg::i128() } else { Reg::i64() };
108        arg.cast_to(Uniform::consecutive(
109            reg,
110            size.align_to(Align::from_bytes(reg.size.bytes()).unwrap()),
111        ))
112    };
113}
114
115pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
116where
117    Ty: TyAbiInterface<'a, C> + Copy,
118    C: HasDataLayout + HasTargetSpec,
119{
120    let abi = match cx.target_spec().options.llvm_abiname {
121        LlvmAbi::ElfV1 => ELFv1,
122        LlvmAbi::ElfV2 => ELFv2,
123        LlvmAbi::Unspecified if cx.target_spec().os == Os::Aix => AIX,
124        // Target::check_consistency enforces that every target except AIX
125        // sets llvm_abiname to either ElfV1 or ElfV2
126        _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
127    };
128
129    classify(cx, &mut fn_abi.ret, abi, true);
130
131    for arg in fn_abi.args.iter_mut() {
132        classify(cx, arg, abi, false);
133    }
134}