]> git.lizzy.rs Git - rust.git/blob - src/librustc_target/abi/call/x86.rs
Auto merge of #68414 - michaelwoerister:share-drop-glue, r=alexcrichton
[rust.git] / src / librustc_target / abi / call / x86.rs
1 use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind};
2 use crate::abi::{self, HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods};
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: TyLayout<'a, Ty>) -> bool
12 where
13     Ty: TyLayoutMethods<'a, C> + Copy,
14     C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'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: TyLayoutMethods<'a, C> + Copy,
32     C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'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             // http://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.options.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.options.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 | PassMode::Indirect(_, None) => continue,
96                 PassMode::Direct(ref mut attrs) => attrs,
97                 PassMode::Pair(..) | PassMode::Indirect(_, Some(_)) | PassMode::Cast(_) => {
98                     unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
99                 }
100             };
101
102             // At this point we know this must be a primitive of sorts.
103             let unit = arg.layout.homogeneous_aggregate(cx).unit().unwrap();
104             assert_eq!(unit.size, arg.layout.size);
105             if unit.kind == RegKind::Float {
106                 continue;
107             }
108
109             let size_in_regs = (arg.layout.size.bits() + 31) / 32;
110
111             if size_in_regs == 0 {
112                 continue;
113             }
114
115             if size_in_regs > free_regs {
116                 break;
117             }
118
119             free_regs -= size_in_regs;
120
121             if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer {
122                 attrs.set(ArgAttribute::InReg);
123             }
124
125             if free_regs == 0 {
126                 break;
127             }
128         }
129     }
130 }