]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | use std::fmt; |
2 | ||
60c5eb7d | 3 | use rustc_macros::HashStable_Generic; |
2b03887a FG |
4 | use rustc_span::symbol::sym; |
5 | use rustc_span::{Span, Symbol}; | |
60c5eb7d | 6 | |
416331ca XL |
7 | #[cfg(test)] |
8 | mod tests; | |
9 | ||
3dfed10e XL |
10 | #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)] |
11 | #[derive(HashStable_Generic, Encodable, Decodable)] | |
223e47cc | 12 | pub enum Abi { |
136023e0 XL |
13 | // Some of the ABIs come first because every time we add a new ABI, we have to re-bless all the |
14 | // hashing tests. These are used in many places, so giving them stable values reduces test | |
15 | // churn. The specific values are meaningless. | |
6a06907d XL |
16 | Rust, |
17 | C { unwind: bool }, | |
5099ac24 | 18 | Cdecl { unwind: bool }, |
6a06907d | 19 | Stdcall { unwind: bool }, |
5099ac24 FG |
20 | Fastcall { unwind: bool }, |
21 | Vectorcall { unwind: bool }, | |
6a06907d | 22 | Thiscall { unwind: bool }, |
5099ac24 FG |
23 | Aapcs { unwind: bool }, |
24 | Win64 { unwind: bool }, | |
25 | SysV64 { unwind: bool }, | |
32a655c1 SL |
26 | PtxKernel, |
27 | Msp430Interrupt, | |
8bb4bdeb | 28 | X86Interrupt, |
8faf50e0 | 29 | AmdGpuKernel, |
e74abb32 | 30 | EfiApi, |
f035d41b XL |
31 | AvrInterrupt, |
32 | AvrNonBlockingInterrupt, | |
5869c6ff | 33 | CCmseNonSecureCall, |
cdc7bbd5 | 34 | Wasm, |
6a06907d | 35 | System { unwind: bool }, |
223e47cc | 36 | RustIntrinsic, |
1a4d82fc | 37 | RustCall, |
e9174d1e | 38 | PlatformIntrinsic, |
dfeec247 | 39 | Unadjusted, |
923072b8 | 40 | RustCold, |
223e47cc LB |
41 | } |
42 | ||
487cf647 FG |
43 | impl Abi { |
44 | pub fn supports_varargs(self) -> bool { | |
45 | // * C and Cdecl obviously support varargs. | |
46 | // * C can be based on SysV64 or Win64, so they must support varargs. | |
47 | // * EfiApi is based on Win64 or C, so it also supports it. | |
48 | // | |
49 | // * Stdcall does not, because it would be impossible for the callee to clean | |
50 | // up the arguments. (callee doesn't know how many arguments are there) | |
51 | // * Same for Fastcall, Vectorcall and Thiscall. | |
52 | // * System can become Stdcall, so is also a no-no. | |
53 | // * Other calling conventions are related to hardware or the compiler itself. | |
54 | match self { | |
55 | Self::C { .. } | |
56 | | Self::Cdecl { .. } | |
57 | | Self::Win64 { .. } | |
58 | | Self::SysV64 { .. } | |
59 | | Self::EfiApi => true, | |
60 | _ => false, | |
61 | } | |
62 | } | |
63 | } | |
64 | ||
c34b1796 | 65 | #[derive(Copy, Clone)] |
1a4d82fc | 66 | pub struct AbiData { |
223e47cc LB |
67 | abi: Abi, |
68 | ||
c30ab7b3 | 69 | /// Name of this ABI as we like it called. |
223e47cc | 70 | name: &'static str, |
223e47cc LB |
71 | } |
72 | ||
1a4d82fc | 73 | #[allow(non_upper_case_globals)] |
b7449926 | 74 | const AbiDatas: &[AbiData] = &[ |
136023e0 XL |
75 | AbiData { abi: Abi::Rust, name: "Rust" }, |
76 | AbiData { abi: Abi::C { unwind: false }, name: "C" }, | |
77 | AbiData { abi: Abi::C { unwind: true }, name: "C-unwind" }, | |
5099ac24 FG |
78 | AbiData { abi: Abi::Cdecl { unwind: false }, name: "cdecl" }, |
79 | AbiData { abi: Abi::Cdecl { unwind: true }, name: "cdecl-unwind" }, | |
136023e0 XL |
80 | AbiData { abi: Abi::Stdcall { unwind: false }, name: "stdcall" }, |
81 | AbiData { abi: Abi::Stdcall { unwind: true }, name: "stdcall-unwind" }, | |
5099ac24 FG |
82 | AbiData { abi: Abi::Fastcall { unwind: false }, name: "fastcall" }, |
83 | AbiData { abi: Abi::Fastcall { unwind: true }, name: "fastcall-unwind" }, | |
84 | AbiData { abi: Abi::Vectorcall { unwind: false }, name: "vectorcall" }, | |
85 | AbiData { abi: Abi::Vectorcall { unwind: true }, name: "vectorcall-unwind" }, | |
136023e0 XL |
86 | AbiData { abi: Abi::Thiscall { unwind: false }, name: "thiscall" }, |
87 | AbiData { abi: Abi::Thiscall { unwind: true }, name: "thiscall-unwind" }, | |
5099ac24 FG |
88 | AbiData { abi: Abi::Aapcs { unwind: false }, name: "aapcs" }, |
89 | AbiData { abi: Abi::Aapcs { unwind: true }, name: "aapcs-unwind" }, | |
90 | AbiData { abi: Abi::Win64 { unwind: false }, name: "win64" }, | |
91 | AbiData { abi: Abi::Win64 { unwind: true }, name: "win64-unwind" }, | |
92 | AbiData { abi: Abi::SysV64 { unwind: false }, name: "sysv64" }, | |
93 | AbiData { abi: Abi::SysV64 { unwind: true }, name: "sysv64-unwind" }, | |
136023e0 XL |
94 | AbiData { abi: Abi::PtxKernel, name: "ptx-kernel" }, |
95 | AbiData { abi: Abi::Msp430Interrupt, name: "msp430-interrupt" }, | |
96 | AbiData { abi: Abi::X86Interrupt, name: "x86-interrupt" }, | |
97 | AbiData { abi: Abi::AmdGpuKernel, name: "amdgpu-kernel" }, | |
98 | AbiData { abi: Abi::EfiApi, name: "efiapi" }, | |
99 | AbiData { abi: Abi::AvrInterrupt, name: "avr-interrupt" }, | |
100 | AbiData { abi: Abi::AvrNonBlockingInterrupt, name: "avr-non-blocking-interrupt" }, | |
101 | AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call" }, | |
102 | AbiData { abi: Abi::Wasm, name: "wasm" }, | |
103 | AbiData { abi: Abi::System { unwind: false }, name: "system" }, | |
104 | AbiData { abi: Abi::System { unwind: true }, name: "system-unwind" }, | |
105 | AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic" }, | |
106 | AbiData { abi: Abi::RustCall, name: "rust-call" }, | |
107 | AbiData { abi: Abi::PlatformIntrinsic, name: "platform-intrinsic" }, | |
108 | AbiData { abi: Abi::Unadjusted, name: "unadjusted" }, | |
923072b8 | 109 | AbiData { abi: Abi::RustCold, name: "rust-cold" }, |
223e47cc LB |
110 | ]; |
111 | ||
1a4d82fc | 112 | /// Returns the ABI with the given name (if any). |
223e47cc | 113 | pub fn lookup(name: &str) -> Option<Abi> { |
1a4d82fc | 114 | AbiDatas.iter().find(|abi_data| name == abi_data.name).map(|&x| x.abi) |
223e47cc LB |
115 | } |
116 | ||
1a4d82fc JJ |
117 | pub fn all_names() -> Vec<&'static str> { |
118 | AbiDatas.iter().map(|d| d.name).collect() | |
223e47cc LB |
119 | } |
120 | ||
2b03887a FG |
121 | pub fn enabled_names(features: &rustc_feature::Features, span: Span) -> Vec<&'static str> { |
122 | AbiDatas | |
123 | .iter() | |
124 | .map(|d| d.name) | |
125 | .filter(|name| is_enabled(features, span, name).is_ok()) | |
126 | .collect() | |
127 | } | |
128 | ||
129 | pub enum AbiDisabled { | |
130 | Unstable { feature: Symbol, explain: &'static str }, | |
131 | Unrecognized, | |
132 | } | |
133 | ||
134 | pub fn is_enabled( | |
135 | features: &rustc_feature::Features, | |
136 | span: Span, | |
137 | name: &str, | |
138 | ) -> Result<(), AbiDisabled> { | |
139 | let s = is_stable(name); | |
140 | if let Err(AbiDisabled::Unstable { feature, .. }) = s { | |
141 | if features.enabled(feature) || span.allows_unstable(feature) { | |
142 | return Ok(()); | |
143 | } | |
144 | } | |
145 | s | |
146 | } | |
147 | ||
148 | pub fn is_stable(name: &str) -> Result<(), AbiDisabled> { | |
149 | match name { | |
150 | // Stable | |
151 | "Rust" | "C" | "cdecl" | "stdcall" | "fastcall" | "aapcs" | "win64" | "sysv64" | |
9c376795 | 152 | | "system" | "efiapi" => Ok(()), |
2b03887a FG |
153 | "rust-intrinsic" => Err(AbiDisabled::Unstable { |
154 | feature: sym::intrinsics, | |
155 | explain: "intrinsics are subject to change", | |
156 | }), | |
157 | "platform-intrinsic" => Err(AbiDisabled::Unstable { | |
158 | feature: sym::platform_intrinsics, | |
159 | explain: "platform intrinsics are experimental and possibly buggy", | |
160 | }), | |
161 | "vectorcall" => Err(AbiDisabled::Unstable { | |
162 | feature: sym::abi_vectorcall, | |
163 | explain: "vectorcall is experimental and subject to change", | |
164 | }), | |
165 | "thiscall" => Err(AbiDisabled::Unstable { | |
166 | feature: sym::abi_thiscall, | |
167 | explain: "thiscall is experimental and subject to change", | |
168 | }), | |
169 | "rust-call" => Err(AbiDisabled::Unstable { | |
170 | feature: sym::unboxed_closures, | |
171 | explain: "rust-call ABI is subject to change", | |
172 | }), | |
173 | "rust-cold" => Err(AbiDisabled::Unstable { | |
174 | feature: sym::rust_cold_cc, | |
175 | explain: "rust-cold is experimental and subject to change", | |
176 | }), | |
177 | "ptx-kernel" => Err(AbiDisabled::Unstable { | |
178 | feature: sym::abi_ptx, | |
179 | explain: "PTX ABIs are experimental and subject to change", | |
180 | }), | |
181 | "unadjusted" => Err(AbiDisabled::Unstable { | |
182 | feature: sym::abi_unadjusted, | |
183 | explain: "unadjusted ABI is an implementation detail and perma-unstable", | |
184 | }), | |
185 | "msp430-interrupt" => Err(AbiDisabled::Unstable { | |
186 | feature: sym::abi_msp430_interrupt, | |
187 | explain: "msp430-interrupt ABI is experimental and subject to change", | |
188 | }), | |
189 | "x86-interrupt" => Err(AbiDisabled::Unstable { | |
190 | feature: sym::abi_x86_interrupt, | |
191 | explain: "x86-interrupt ABI is experimental and subject to change", | |
192 | }), | |
193 | "amdgpu-kernel" => Err(AbiDisabled::Unstable { | |
194 | feature: sym::abi_amdgpu_kernel, | |
195 | explain: "amdgpu-kernel ABI is experimental and subject to change", | |
196 | }), | |
197 | "avr-interrupt" | "avr-non-blocking-interrupt" => Err(AbiDisabled::Unstable { | |
198 | feature: sym::abi_avr_interrupt, | |
199 | explain: "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change", | |
200 | }), | |
2b03887a FG |
201 | "C-cmse-nonsecure-call" => Err(AbiDisabled::Unstable { |
202 | feature: sym::abi_c_cmse_nonsecure_call, | |
203 | explain: "C-cmse-nonsecure-call ABI is experimental and subject to change", | |
204 | }), | |
205 | "C-unwind" => Err(AbiDisabled::Unstable { | |
206 | feature: sym::c_unwind, | |
207 | explain: "C-unwind ABI is experimental and subject to change", | |
208 | }), | |
209 | "stdcall-unwind" => Err(AbiDisabled::Unstable { | |
210 | feature: sym::c_unwind, | |
211 | explain: "stdcall-unwind ABI is experimental and subject to change", | |
212 | }), | |
213 | "system-unwind" => Err(AbiDisabled::Unstable { | |
214 | feature: sym::c_unwind, | |
215 | explain: "system-unwind ABI is experimental and subject to change", | |
216 | }), | |
217 | "thiscall-unwind" => Err(AbiDisabled::Unstable { | |
218 | feature: sym::c_unwind, | |
219 | explain: "thiscall-unwind ABI is experimental and subject to change", | |
220 | }), | |
221 | "cdecl-unwind" => Err(AbiDisabled::Unstable { | |
222 | feature: sym::c_unwind, | |
223 | explain: "cdecl-unwind ABI is experimental and subject to change", | |
224 | }), | |
225 | "fastcall-unwind" => Err(AbiDisabled::Unstable { | |
226 | feature: sym::c_unwind, | |
227 | explain: "fastcall-unwind ABI is experimental and subject to change", | |
228 | }), | |
229 | "vectorcall-unwind" => Err(AbiDisabled::Unstable { | |
230 | feature: sym::c_unwind, | |
231 | explain: "vectorcall-unwind ABI is experimental and subject to change", | |
232 | }), | |
233 | "aapcs-unwind" => Err(AbiDisabled::Unstable { | |
234 | feature: sym::c_unwind, | |
235 | explain: "aapcs-unwind ABI is experimental and subject to change", | |
236 | }), | |
237 | "win64-unwind" => Err(AbiDisabled::Unstable { | |
238 | feature: sym::c_unwind, | |
239 | explain: "win64-unwind ABI is experimental and subject to change", | |
240 | }), | |
241 | "sysv64-unwind" => Err(AbiDisabled::Unstable { | |
242 | feature: sym::c_unwind, | |
243 | explain: "sysv64-unwind ABI is experimental and subject to change", | |
244 | }), | |
245 | "wasm" => Err(AbiDisabled::Unstable { | |
246 | feature: sym::wasm_abi, | |
247 | explain: "wasm ABI is experimental and subject to change", | |
248 | }), | |
249 | _ => Err(AbiDisabled::Unrecognized), | |
250 | } | |
251 | } | |
252 | ||
970d7e83 | 253 | impl Abi { |
94222f64 XL |
254 | /// Default ABI chosen for `extern fn` declarations without an explicit ABI. |
255 | pub const FALLBACK: Abi = Abi::C { unwind: false }; | |
256 | ||
223e47cc | 257 | #[inline] |
b7449926 | 258 | pub fn index(self) -> usize { |
6a06907d XL |
259 | // N.B., this ordering MUST match the AbiDatas array above. |
260 | // (This is ensured by the test indices_are_correct().) | |
261 | use Abi::*; | |
262 | let i = match self { | |
263 | // Cross-platform ABIs | |
264 | Rust => 0, | |
265 | C { unwind: false } => 1, | |
266 | C { unwind: true } => 2, | |
267 | // Platform-specific ABIs | |
5099ac24 FG |
268 | Cdecl { unwind: false } => 3, |
269 | Cdecl { unwind: true } => 4, | |
270 | Stdcall { unwind: false } => 5, | |
271 | Stdcall { unwind: true } => 6, | |
272 | Fastcall { unwind: false } => 7, | |
273 | Fastcall { unwind: true } => 8, | |
274 | Vectorcall { unwind: false } => 9, | |
275 | Vectorcall { unwind: true } => 10, | |
276 | Thiscall { unwind: false } => 11, | |
277 | Thiscall { unwind: true } => 12, | |
278 | Aapcs { unwind: false } => 13, | |
279 | Aapcs { unwind: true } => 14, | |
280 | Win64 { unwind: false } => 15, | |
281 | Win64 { unwind: true } => 16, | |
282 | SysV64 { unwind: false } => 17, | |
283 | SysV64 { unwind: true } => 18, | |
284 | PtxKernel => 19, | |
285 | Msp430Interrupt => 20, | |
286 | X86Interrupt => 21, | |
287 | AmdGpuKernel => 22, | |
288 | EfiApi => 23, | |
289 | AvrInterrupt => 24, | |
290 | AvrNonBlockingInterrupt => 25, | |
291 | CCmseNonSecureCall => 26, | |
292 | Wasm => 27, | |
6a06907d | 293 | // Cross-platform ABIs |
5099ac24 FG |
294 | System { unwind: false } => 28, |
295 | System { unwind: true } => 29, | |
296 | RustIntrinsic => 30, | |
297 | RustCall => 31, | |
298 | PlatformIntrinsic => 32, | |
299 | Unadjusted => 33, | |
923072b8 | 300 | RustCold => 34, |
6a06907d XL |
301 | }; |
302 | debug_assert!( | |
303 | AbiDatas | |
304 | .iter() | |
305 | .enumerate() | |
306 | .find(|(_, AbiData { abi, .. })| *abi == self) | |
307 | .map(|(index, _)| index) | |
308 | .expect("abi variant has associated data") | |
309 | == i, | |
310 | "Abi index did not match `AbiDatas` ordering" | |
311 | ); | |
312 | i | |
223e47cc LB |
313 | } |
314 | ||
315 | #[inline] | |
b7449926 | 316 | pub fn data(self) -> &'static AbiData { |
223e47cc LB |
317 | &AbiDatas[self.index()] |
318 | } | |
319 | ||
b7449926 | 320 | pub fn name(self) -> &'static str { |
223e47cc LB |
321 | self.data().name |
322 | } | |
323 | } | |
324 | ||
85aaf69f | 325 | impl fmt::Display for Abi { |
9fa01778 | 326 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
6a06907d XL |
327 | match self { |
328 | abi => write!(f, "\"{}\"", abi.name()), | |
329 | } | |
223e47cc LB |
330 | } |
331 | } |