• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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