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        // ELFv1 and AIX only passes one-member aggregates transparently.
64        // ELFv2 passes up to eight uniquely addressable members.
65        if ((abi == ELFv1 || abi == AIX)
66            && (arg.layout.size > unit.size || is_or_contains_union(cx, arg.layout)))
67            || arg.layout.size > unit.size.checked_mul(8, cx).unwrap()
68        {
69            return None;
70        }
71
72        let valid_unit = match unit.kind {
73            RegKind::Integer => false,
74            RegKind::Float => true,
75            RegKind::Vector { .. } => unit.size.bits() == 128,
76        };
77
78        valid_unit.then_some(Uniform::consecutive(unit, arg.layout.size))
79    })
80}
81
82fn classify<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, abi: ABI, is_ret: bool)
83where
84    Ty: TyAbiInterface<'a, C> + Copy,
85    C: HasDataLayout,
86{
87    if arg.is_ignore() || !arg.layout.is_sized() {
88        // Not touching this...
89        return;
90    }
91    if !is_ret && arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
92        arg.make_indirect();
93        return;
94    }
95    if !arg.layout.is_aggregate() {
96        arg.extend_integer_width_to(64);
97        return;
98    }
99    if let Some(component) = arg.layout.complex_number(cx) {
100        if let Numeric::Int(Integer::I16, _) = component {
101            // FIXME: use `PassMode::Cast` here. In LLVM 23 doing so would hit
102            // https://github.com/llvm/llvm-project/issues/218676.
103            return;
104        }
105
106        let reg = Reg { kind: component.reg_kind(), size: component.size() };
107        arg.cast_to(CastTarget::pair(reg, reg));
108        return;
109    }
110
111    // The AIX ABI expect byval for aggregates
112    // See https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/Targets/PPC.cpp.
113    // The incoming parameter is represented as a pointer in the IR,
114    // the alignment is associated with the size of the register. (align 8 for 64bit)
115    if !is_ret && abi == AIX {
116        arg.pass_by_stack_offset(Some(Align::from_bytes(8).unwrap()));
117        return;
118    }
119
120    // The ELFv1 ABI doesn't return aggregates in registers
121    if is_ret && (abi == ELFv1 || abi == AIX) {
122        arg.make_indirect();
123        return;
124    }
125
126    if let Some(uniform) = is_homogeneous_aggregate(cx, arg, abi) {
127        arg.cast_to(uniform);
128        return;
129    }
130
131    let size = arg.layout.size;
132    if is_ret && size.bits() > 128 {
133        // Non-homogeneous aggregates larger than two doublewords are returned indirectly.
134        arg.make_indirect();
135    } else if size.bits() <= 64 {
136        // Aggregates smaller than a doubleword should appear in
137        // the least-significant bits of the parameter doubleword.
138        arg.cast_to(Reg { kind: RegKind::Integer, size })
139    } else {
140        // Aggregates larger than i64 should be padded at the tail to fill out a whole number
141        // of i64s or i128s, depending on the aggregate alignment. Always use an array for
142        // this, even if there is only a single element.
143        let reg = if arg.layout.align.bytes() > 8 { Reg::i128() } else { Reg::i64() };
144        arg.cast_to(Uniform::consecutive(
145            reg,
146            size.align_to(Align::from_bytes(reg.size.bytes()).unwrap()),
147        ))
148    };
149}
150
151pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
152where
153    Ty: TyAbiInterface<'a, C> + Copy,
154    C: HasDataLayout + HasTargetSpec,
155{
156    let abi = match cx.target_spec().options.llvm_abiname {
157        LlvmAbi::ElfV1 => ELFv1,
158        LlvmAbi::ElfV2 => ELFv2,
159        LlvmAbi::Unspecified if cx.target_spec().os == Os::Aix => AIX,
160        // Target::check_consistency enforces that every target except AIX
161        // sets llvm_abiname to either ElfV1 or ElfV2
162        _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
163    };
164
165    classify(cx, &mut fn_abi.ret, abi, true);
166
167    for arg in fn_abi.args.iter_mut() {
168        classify(cx, arg, abi, false);
169    }
170}