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