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::{Endian, HasDataLayout, TyAbiInterface};
6
7use crate::callconv::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform};
8use crate::spec::HasTargetSpec;
9
10#[derive(Debug, Clone, Copy, 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 => arg.layout.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
64    // The AIX ABI expect byval for aggregates
65    // See https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/Targets/PPC.cpp.
66    // The incoming parameter is represented as a pointer in the IR,
67    // the alignment is associated with the size of the register. (align 8 for 64bit)
68    if !is_ret && abi == AIX {
69        arg.pass_by_stack_offset(Some(Align::from_bytes(8).unwrap()));
70        return;
71    }
72
73    // The ELFv1 ABI doesn't return aggregates in registers
74    if is_ret && (abi == ELFv1 || abi == AIX) {
75        arg.make_indirect();
76        return;
77    }
78
79    if let Some(uniform) = is_homogeneous_aggregate(cx, arg, abi) {
80        arg.cast_to(uniform);
81        return;
82    }
83
84    let size = arg.layout.size;
85    if is_ret && size.bits() > 128 {
86        // Non-homogeneous aggregates larger than two doublewords are returned indirectly.
87        arg.make_indirect();
88    } else if size.bits() <= 64 {
89        // Aggregates smaller than a doubleword should appear in
90        // the least-significant bits of the parameter doubleword.
91        arg.cast_to(Reg { kind: RegKind::Integer, size })
92    } else {
93        // Aggregates larger than i64 should be padded at the tail to fill out a whole number
94        // of i64s or i128s, depending on the aggregate alignment. Always use an array for
95        // this, even if there is only a single element.
96        let reg = if arg.layout.align.bytes() > 8 { Reg::i128() } else { Reg::i64() };
97        arg.cast_to(Uniform::consecutive(
98            reg,
99            size.align_to(Align::from_bytes(reg.size.bytes()).unwrap()),
100        ))
101    };
102}
103
104pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
105where
106    Ty: TyAbiInterface<'a, C> + Copy,
107    C: HasDataLayout + HasTargetSpec,
108{
109    let abi = if cx.target_spec().env == "musl" || cx.target_spec().os == "freebsd" {
110        ELFv2
111    } else if cx.target_spec().os == "aix" {
112        AIX
113    } else {
114        match cx.data_layout().endian {
115            Endian::Big => ELFv1,
116            Endian::Little => ELFv2,
117        }
118    };
119
120    classify(cx, &mut fn_abi.ret, abi, true);
121
122    for arg in fn_abi.args.iter_mut() {
123        classify(cx, arg, abi, false);
124    }
125}