Skip to main content

rustc_target/callconv/
powerpc.rs

1use rustc_abi::{BackendRepr, Float, Primitive, Reg, RegKind, TyAbiInterface};
2use rustc_span::bug;
3
4use crate::callconv::{ArgAbi, CastTarget, FnAbi, Uniform};
5use crate::spec::{Env, HasTargetSpec, Os, RustcAbi};
6
7const NUM_ARG_GPRS: u64 = 8;
8
9/// How to cast `Complex<T>` so that we match the GCC ABI.
10fn complex_cast_target<Ty>(arg: &ArgAbi<'_, Ty>) -> CastTarget {
11    let size = arg.layout.size;
12
13    if size.bytes() <= 4 {
14        // Coerce to an integer for `Complex<i8>` and `Complex<i16>`.
15        CastTarget::from(Reg { kind: RegKind::Integer, size })
16    } else if size.bytes() == 8 {
17        // Coerce to a single `i64` for `Complex<f32>` and `Complex<i32>`, which has the correct
18        // register alignment of 8 bytes.
19        CastTarget::from(Reg::i64())
20    } else {
21        // Coerce to an array `[N x i32]` for everything wider. An array of i32 gives the correct
22        // 4-byte register alignment.
23        CastTarget::from(Uniform::new(Reg::i32(), size))
24    }
25}
26
27fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, gprs_in_use: &mut u64)
28where
29    Ty: TyAbiInterface<'a, C> + Copy,
30{
31    if ret.layout.is_complex_number(cx) {
32        ret.cast_to(complex_cast_target(ret));
33    } else if ret.layout.is_aggregate() {
34        *gprs_in_use += 1;
35        ret.make_indirect();
36    } else {
37        ret.extend_integer_width_to(32);
38    }
39}
40
41fn classify_arg<'a, Ty, C: HasTargetSpec>(cx: &C, arg: &mut ArgAbi<'a, Ty>, gprs_in_use: &mut u64)
42where
43    Ty: TyAbiInterface<'a, C> + Copy,
44{
45    if arg.is_ignore() {
46        // powerpc-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs.
47        if cx.target_spec().os == Os::Linux
48            && #[allow(non_exhaustive_omitted_patterns)] match cx.target_spec().env {
    Env::Gnu | Env::Musl | Env::Uclibc => true,
    _ => false,
}matches!(cx.target_spec().env, Env::Gnu | Env::Musl | Env::Uclibc)
49            && arg.layout.is_repr_c()
50        {
51            *gprs_in_use += 1;
52            arg.make_indirect_from_ignore();
53        }
54        return;
55    }
56
57    // The powerpc ABI in GCC hardcodes a special rule for values of size 8. It remarks
58    //
59    // > V.4 wants long longs and doubles to be double word aligned. Just
60    // > testing the mode size is a boneheaded way to do this as it means
61    // > that other types such as complex int are also double word aligned.
62    // > However, we're stuck with this because changing the ABI might break
63    // > existing library interfaces.
64    //
65    // LLVM aligns every value that it splits over multiple GPRs, but a `Complex<T>` is only
66    // aligned when it is cast to an `i64` (i.e. has a size of exactly 8).
67
68    if arg.layout.is_complex_number(cx) {
69        let cast_target = complex_cast_target(arg);
70
71        // Complex values are only register aligned when the size is exactly 8.
72        let is_aligned_pair = arg.layout.size.bytes() == 8;
73        if is_aligned_pair && !gprs_in_use.is_multiple_of(2) {
74            *gprs_in_use += 1;
75        }
76
77        let registers_used = arg.layout.size.bytes().div_ceil(4);
78        if *gprs_in_use + registers_used > NUM_ARG_GPRS {
79            let pad_i32_count = u8::try_from(NUM_ARG_GPRS.saturating_sub(*gprs_in_use)).unwrap();
80            arg.cast_to_and_pad_i32(cast_target, pad_i32_count);
81        } else {
82            arg.cast_to(cast_target);
83        }
84
85        // A value that does not fit uses up the remaining GPRs.
86        *gprs_in_use += registers_used;
87    } else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) || arg.layout.is_aggregate() {
88        *gprs_in_use += 1;
89        arg.make_indirect();
90    } else {
91        if backend_repr_uses_gprs(cx, arg) {
92            // Other types (i64, i128, etc.) are register aligned when >= 8.
93            let is_aligned_pair = arg.layout.size.bytes() >= 8;
94            if is_aligned_pair && !gprs_in_use.is_multiple_of(2) {
95                *gprs_in_use += 1;
96            }
97
98            *gprs_in_use += arg.layout.size.bytes().div_ceil(4)
99        }
100
101        arg.extend_integer_width_to(32);
102    }
103}
104
105/// Are GPRs used to pass this argument (assuming it is passed directly)?
106fn backend_repr_uses_gprs<'a, Ty, C: HasTargetSpec>(cx: &C, arg: &ArgAbi<'a, Ty>) -> bool {
107    let is_softfloat = match cx.target_spec().rustc_abi {
108        Some(RustcAbi::Softfloat) => ::rustc_span::macros::bug_impl(None,
    format_args!("there are currently no powerpc softfloat targets"),
    Location::caller())bug!("there are currently no powerpc softfloat targets"),
109        Some(RustcAbi::PowerPcSpe) => true, // SPE is like softfloat in terms of ABI
110        None => false,
111        Some(other) => ::rustc_span::macros::bug_impl(None,
    format_args!("unexpected powerpc abi {0:?}", other), Location::caller())bug!("unexpected powerpc abi {other:?}"),
112    };
113
114    match arg.layout.backend_repr {
115        BackendRepr::ScalarPair { .. } | BackendRepr::SimdScalableVector { .. } => {
116            ::rustc_span::macros::bug_impl(None,
    format_args!("unexpected backend_repr for 32-bit powerpc: {0:?}",
        arg.layout.backend_repr), Location::caller())bug!("unexpected backend_repr for 32-bit powerpc: {:?}", arg.layout.backend_repr)
117        }
118        BackendRepr::Memory { .. } => {
119            ::rustc_span::macros::bug_impl(None,
    format_args!("unexpected backend_repr for PassMode::Direct: {0:?}",
        arg.layout.backend_repr), Location::caller())bug!("unexpected backend_repr for PassMode::Direct: {:?}", arg.layout.backend_repr)
120        }
121        BackendRepr::Scalar(scalar) => match scalar.primitive() {
122            Primitive::Float(Float::F32 | Float::F64 | Float::F128 | Float::PpcF128) => {
123                is_softfloat
124            }
125
126            _ => true,
127        },
128        BackendRepr::SimdVector { .. } => {
129            // When vector types are legal, they are passed in vector registers.
130            false
131        }
132    }
133}
134
135pub(crate) fn compute_abi_info<'a, Ty, C: HasTargetSpec>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
136where
137    Ty: TyAbiInterface<'a, C> + Copy,
138{
139    // Keeps track of how many GPRs are in use so far to pass arguments. This is used to exhaust
140    // the remaining argument registers when a complex value, that doesn't fit fully in the
141    // remaining registers, is passed via the stack. Subsequent arguments must then also be passed
142    // via the stack.
143    let mut gprs_in_use: u64 = 0;
144
145    if !fn_abi.ret.is_ignore() {
146        classify_ret(cx, &mut fn_abi.ret, &mut gprs_in_use);
147    }
148
149    for arg in fn_abi.args.iter_mut() {
150        classify_arg(cx, arg, &mut gprs_in_use);
151    }
152}