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