1 use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind};
2 use crate::abi::{HasDataLayout, TyAbiInterface};
3 use crate::spec::HasTargetSpec;
4
5 #[derive(PartialEq)]
6 pub enum Flavor {
7 General,
8 FastcallOrVectorcall,
9 }
10
compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, flavor: Flavor) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec,11 pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, flavor: Flavor)
12 where
13 Ty: TyAbiInterface<'a, C> + Copy,
14 C: HasDataLayout + HasTargetSpec,
15 {
16 if !fn_abi.ret.is_ignore() {
17 if fn_abi.ret.layout.is_aggregate() {
18 // Returning a structure. Most often, this will use
19 // a hidden first argument. On some platforms, though,
20 // small structs are returned as integers.
21 //
22 // Some links:
23 // https://www.angelcode.com/dev/callconv/callconv.html
24 // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp
25 let t = cx.target_spec();
26 if t.abi_return_struct_as_int {
27 // According to Clang, everyone but MSVC returns single-element
28 // float aggregates directly in a floating-point register.
29 if !t.is_like_msvc && fn_abi.ret.layout.is_single_fp_element(cx) {
30 match fn_abi.ret.layout.size.bytes() {
31 4 => fn_abi.ret.cast_to(Reg::f32()),
32 8 => fn_abi.ret.cast_to(Reg::f64()),
33 _ => fn_abi.ret.make_indirect(),
34 }
35 } else {
36 match fn_abi.ret.layout.size.bytes() {
37 1 => fn_abi.ret.cast_to(Reg::i8()),
38 2 => fn_abi.ret.cast_to(Reg::i16()),
39 4 => fn_abi.ret.cast_to(Reg::i32()),
40 8 => fn_abi.ret.cast_to(Reg::i64()),
41 _ => fn_abi.ret.make_indirect(),
42 }
43 }
44 } else {
45 fn_abi.ret.make_indirect();
46 }
47 } else {
48 fn_abi.ret.extend_integer_width_to(32);
49 }
50 }
51
52 for arg in fn_abi.args.iter_mut() {
53 if arg.is_ignore() {
54 continue;
55 }
56 if arg.layout.is_aggregate() {
57 arg.make_indirect_byval();
58 } else {
59 arg.extend_integer_width_to(32);
60 }
61 }
62
63 if flavor == Flavor::FastcallOrVectorcall {
64 // Mark arguments as InReg like clang does it,
65 // so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall.
66
67 // Clang reference: lib/CodeGen/TargetInfo.cpp
68 // See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs()
69
70 // IsSoftFloatABI is only set to true on ARM platforms,
71 // which in turn can't be x86?
72
73 let mut free_regs = 2;
74
75 for arg in fn_abi.args.iter_mut() {
76 let attrs = match arg.mode {
77 PassMode::Ignore
78 | PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => {
79 continue;
80 }
81 PassMode::Direct(ref mut attrs) => attrs,
82 PassMode::Pair(..)
83 | PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ }
84 | PassMode::Cast(..) => {
85 unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
86 }
87 };
88
89 // At this point we know this must be a primitive of sorts.
90 let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap();
91 assert_eq!(unit.size, arg.layout.size);
92 if unit.kind == RegKind::Float {
93 continue;
94 }
95
96 let size_in_regs = (arg.layout.size.bits() + 31) / 32;
97
98 if size_in_regs == 0 {
99 continue;
100 }
101
102 if size_in_regs > free_regs {
103 break;
104 }
105
106 free_regs -= size_in_regs;
107
108 if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer {
109 attrs.set(ArgAttribute::InReg);
110 }
111
112 if free_regs == 0 {
113 break;
114 }
115 }
116 }
117 }
118