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