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::{FieldsShape, HasDataLayout, Integer, Numeric, TyAbiInterface, TyAndLayout};
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) -> Self { *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: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}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
18/// Whether `layout` is or contains a union.
19///
20/// `homogeneous_aggregate` merges the fields of a union, so it cannot tell a union from a
21/// structure. This walks the layout a second time; keep the array handling here in sync with
22/// `homogeneous_aggregate`. ZSTs are ignored entirely, as both GCC and Clang do.
23///
24/// This does not look at enums that are represented as unions at the ABI level (e.g. a
25/// `#[repr(C)]` enum with fields). That does not matter here: enums can only have integer
26/// discriminants, and `is_homogeneous_aggregate` rejects anything containing an integer, so an
27/// enum can never be a float or vector homogeneous aggregate.
28fn is_or_contains_union<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool
29where
30    Ty: TyAbiInterface<'a, C> + Copy,
31    C: HasDataLayout,
32{
33    if layout.is_zst() {
34        return false;
35    }
36    match layout.fields {
37        FieldsShape::Primitive => false,
38        // A `repr(transparent)` union is guaranteed to be ABI-compatible with its single
39        // non-1-ZST field, so look through it instead of rejecting it.
40        FieldsShape::Union(_) => {
41            match layout.non_1zst_field(cx).filter(|_| layout.is_transparent()) {
42                Some((_, field)) => is_or_contains_union(cx, field),
43                None => true,
44            }
45        }
46        FieldsShape::Array { .. } => is_or_contains_union(cx, layout.field(cx, 0)),
47        FieldsShape::Arbitrary { .. } => {
48            (0..layout.fields.count()).any(|i| is_or_contains_union(cx, layout.field(cx, i)))
49        }
50    }
51}
52
53fn is_homogeneous_aggregate<'a, Ty, C>(
54    cx: &C,
55    arg: &mut ArgAbi<'a, Ty>,
56    abi: ABI,
57) -> Option<Uniform>
58where
59    Ty: TyAbiInterface<'a, C> + Copy,
60    C: HasDataLayout,
61{
62    arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
63        match abi {
64            ELFv1 | AIX => {
65                // Pass only one-member aggregates transparently.
66                if arg.layout.size > unit.size || is_or_contains_union(cx, arg.layout) {
67                    return None;
68                }
69            }
70            ELFv2 => {
71                // A `ppcf128` occupies two floating-point registers, so only four of them fit.
72                let max_members = if unit.kind == RegKind::PpcF128 { 4 } else { 8 };
73
74                // Pass up to max_members uniquely addressable members.
75                if arg.layout.size > unit.size.checked_mul(max_members, cx).unwrap() {
76                    return None;
77                }
78            }
79        }
80
81        let valid_unit = match unit.kind {
82            RegKind::Integer => false,
83            RegKind::Float | RegKind::PpcF128 => true,
84            RegKind::Vector { .. } => unit.size.bits() == 128,
85        };
86
87        valid_unit.then_some(Uniform::new(unit, arg.layout.size))
88    })
89}
90
91fn classify<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, abi: ABI, is_ret: bool)
92where
93    Ty: TyAbiInterface<'a, C> + Copy,
94    C: HasDataLayout,
95{
96    if arg.is_ignore() || !arg.layout.is_sized() {
97        // Not touching this...
98        return;
99    }
100    if !is_ret && arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
101        arg.make_indirect();
102        return;
103    }
104    if !arg.layout.is_aggregate() {
105        arg.extend_integer_width_to(64);
106        return;
107    }
108    if let Some(component) = arg.layout.complex_number(cx) {
109        if let Numeric::Int(Integer::I16, _) = component {
110            // FIXME: use `PassMode::Cast` here. In LLVM 23 doing so would hit
111            // https://github.com/llvm/llvm-project/issues/218676.
112            return;
113        }
114
115        let reg = Reg { kind: component.reg_kind(), size: component.size() };
116        arg.cast_to(CastTarget::pair(reg, reg));
117        return;
118    }
119
120    // The AIX ABI expect byval for aggregates
121    // See https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/Targets/PPC.cpp.
122    // The incoming parameter is represented as a pointer in the IR,
123    // the alignment is associated with the size of the register. (align 8 for 64bit)
124    if !is_ret && abi == AIX {
125        arg.pass_by_stack_offset(Some(Align::from_bytes(8).unwrap()));
126        return;
127    }
128
129    // The ELFv1 ABI doesn't return aggregates in registers
130    if is_ret && (abi == ELFv1 || abi == AIX) {
131        arg.make_indirect();
132        return;
133    }
134
135    if let Some(uniform) = is_homogeneous_aggregate(cx, arg, abi) {
136        arg.cast_to(uniform);
137        return;
138    }
139
140    let size = arg.layout.size;
141    if is_ret && size.bits() > 128 {
142        // Non-homogeneous aggregates larger than two doublewords are returned indirectly.
143        arg.make_indirect();
144    } else if size.bits() <= 64 {
145        // Aggregates smaller than a doubleword should appear in
146        // the least-significant bits of the parameter doubleword.
147        arg.cast_to(Reg { kind: RegKind::Integer, size })
148    } else {
149        // Aggregates larger than i64 should be padded at the tail to fill out a whole number
150        // of i64s or i128s, depending on the aggregate alignment. Always use an array for
151        // this, even if there is only a single element.
152        let reg = if arg.layout.align.bytes() > 8 { Reg::i128() } else { Reg::i64() };
153        arg.cast_to(Uniform::consecutive(
154            reg,
155            size.align_to(Align::from_bytes(reg.size.bytes()).unwrap()),
156        ))
157    };
158}
159
160pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
161where
162    Ty: TyAbiInterface<'a, C> + Copy,
163    C: HasDataLayout + HasTargetSpec,
164{
165    let abi = match cx.target_spec().options.llvm_abiname {
166        LlvmAbi::ElfV1 => ELFv1,
167        LlvmAbi::ElfV2 => ELFv2,
168        LlvmAbi::Unspecified if cx.target_spec().os == Os::Aix => AIX,
169        // Target::check_consistency enforces that every target except AIX
170        // sets llvm_abiname to either ElfV1 or ElfV2
171        _ => ::core::panicking::unreachable_default_msg()unreachable!(),
172    };
173
174    classify(cx, &mut fn_abi.ret, abi, true);
175
176    for arg in fn_abi.args.iter_mut() {
177        classify(cx, arg, abi, false);
178    }
179}