1 | //! Macros shared throughout the compiler-builtins implementation |
2 | |
3 | /// The "main macro" used for defining intrinsics. |
4 | /// |
5 | /// The compiler-builtins library is super platform-specific with tons of crazy |
6 | /// little tweaks for various platforms. As a result it *could* involve a lot of |
7 | /// #[cfg] and macro soup, but the intention is that this macro alleviates a lot |
8 | /// of that complexity. Ideally this macro has all the weird ABI things |
9 | /// platforms need and elsewhere in this library it just looks like normal Rust |
10 | /// code. |
11 | /// |
12 | /// All intrinsics functions are marked with #[linkage = "weak"] when |
13 | /// `not(windows) and not(target_vendor = "apple")`. |
14 | /// `weak` linkage attribute is used so that these functions can be replaced |
15 | /// by another implementation at link time. This is particularly useful for mixed |
16 | /// Rust/C++ binaries that want to use the C++ intrinsics, otherwise linking against |
17 | /// the Rust stdlib will replace those from the compiler-rt library. |
18 | /// |
19 | /// This macro is structured to be invoked with a bunch of functions that looks |
20 | /// like: |
21 | /// ```ignore |
22 | /// intrinsics! { |
23 | /// pub extern "C" fn foo(a: i32) -> u32 { |
24 | /// // ... |
25 | /// } |
26 | /// |
27 | /// #[nonstandard_attribute] |
28 | /// pub extern "C" fn bar(a: i32) -> u32 { |
29 | /// // ... |
30 | /// } |
31 | /// } |
32 | /// ``` |
33 | /// |
34 | /// Each function is defined in a manner that looks like a normal Rust function. |
35 | /// The macro then accepts a few nonstandard attributes that can decorate |
36 | /// various functions. Each of the attributes is documented below with what it |
37 | /// can do, and each of them slightly tweaks how further expansion happens. |
38 | /// |
39 | /// A quick overview of attributes supported right now are: |
40 | /// |
41 | /// * `maybe_use_optimized_c_shim` - indicates that the Rust implementation is |
42 | /// ignored if an optimized C version was compiled. |
43 | /// * `aapcs_on_arm` - forces the ABI of the function to be `"aapcs"` on ARM and |
44 | /// the specified ABI everywhere else. |
45 | /// * `unadjusted_on_win64` - like `aapcs_on_arm` this switches to the |
46 | /// `"unadjusted"` abi on Win64 and the specified abi elsewhere. |
47 | /// * `arm_aeabi_alias` - handles the "aliasing" of various intrinsics on ARM |
48 | /// their otherwise typical names to other prefixed ones. |
49 | /// * `ppc_alias` - changes the name of the symbol on PowerPC platforms without |
50 | /// changing any other behavior. This is mostly for `f128`, which is `tf` on |
51 | /// most platforms but `kf` on PowerPC. |
52 | macro_rules! intrinsics { |
53 | () => (); |
54 | |
55 | // Support cfg_attr: |
56 | ( |
57 | #[cfg_attr($e:meta, $($attr:tt)*)] |
58 | $(#[$($attrs:tt)*])* |
59 | pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
60 | $($body:tt)* |
61 | } |
62 | $($rest:tt)* |
63 | ) => ( |
64 | #[cfg($e)] |
65 | intrinsics! { |
66 | #[$($attr)*] |
67 | $(#[$($attrs)*])* |
68 | pub extern $abi fn $name($($argname: $ty),*) $(-> $ret)? { |
69 | $($body)* |
70 | } |
71 | } |
72 | |
73 | #[cfg(not($e))] |
74 | intrinsics! { |
75 | $(#[$($attrs)*])* |
76 | pub extern $abi fn $name($($argname: $ty),*) $(-> $ret)? { |
77 | $($body)* |
78 | } |
79 | } |
80 | |
81 | intrinsics!($($rest)*); |
82 | ); |
83 | // Same as above but for unsafe. |
84 | ( |
85 | #[cfg_attr($e:meta, $($attr:tt)*)] |
86 | $(#[$($attrs:tt)*])* |
87 | pub unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
88 | $($body:tt)* |
89 | } |
90 | $($rest:tt)* |
91 | ) => ( |
92 | #[cfg($e)] |
93 | intrinsics! { |
94 | #[$($attr)*] |
95 | $(#[$($attrs)*])* |
96 | pub unsafe extern $abi fn $name($($argname: $ty),*) $(-> $ret)? { |
97 | $($body)* |
98 | } |
99 | } |
100 | |
101 | #[cfg(not($e))] |
102 | intrinsics! { |
103 | $(#[$($attrs)*])* |
104 | pub unsafe extern $abi fn $name($($argname: $ty),*) $(-> $ret)? { |
105 | $($body)* |
106 | } |
107 | } |
108 | |
109 | intrinsics!($($rest)*); |
110 | ); |
111 | |
112 | // Right now there's a bunch of architecture-optimized intrinsics in the |
113 | // stock compiler-rt implementation. Not all of these have been ported over |
114 | // to Rust yet so when the `c` feature of this crate is enabled we fall back |
115 | // to the architecture-specific versions which should be more optimized. The |
116 | // purpose of this macro is to easily allow specifying this. |
117 | // |
118 | // The `#[maybe_use_optimized_c_shim]` attribute indicates that this |
119 | // intrinsic may have an optimized C version. In these situations the build |
120 | // script, if the C code is enabled and compiled, will emit a cfg directive |
121 | // to get passed to rustc for our compilation. If that cfg is set we skip |
122 | // the Rust implementation, but if the attribute is not enabled then we |
123 | // compile in the Rust implementation. |
124 | ( |
125 | #[maybe_use_optimized_c_shim] |
126 | $(#[$($attr:tt)*])* |
127 | pub $(unsafe $(@ $empty:tt)? )? extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
128 | $($body:tt)* |
129 | } |
130 | |
131 | $($rest:tt)* |
132 | ) => ( |
133 | #[cfg($name = "optimized-c" )] |
134 | pub $(unsafe $($empty)? )? extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
135 | extern $abi { |
136 | fn $name($($argname: $ty),*) $(-> $ret)?; |
137 | } |
138 | unsafe { |
139 | $name($($argname),*) |
140 | } |
141 | } |
142 | |
143 | #[cfg(not($name = "optimized-c" ))] |
144 | intrinsics! { |
145 | $(#[$($attr)*])* |
146 | pub $(unsafe $($empty)? )? extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
147 | $($body)* |
148 | } |
149 | } |
150 | |
151 | intrinsics!($($rest)*); |
152 | ); |
153 | |
154 | // We recognize the `#[aapcs_on_arm]` attribute here and generate the |
155 | // same intrinsic but force it to have the `"aapcs"` calling convention on |
156 | // ARM and `"C"` elsewhere. |
157 | ( |
158 | #[aapcs_on_arm] |
159 | $(#[$($attr:tt)*])* |
160 | pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
161 | $($body:tt)* |
162 | } |
163 | |
164 | $($rest:tt)* |
165 | ) => ( |
166 | #[cfg(target_arch = "arm" )] |
167 | intrinsics! { |
168 | $(#[$($attr)*])* |
169 | pub extern "aapcs" fn $name( $($argname: $ty),* ) $(-> $ret)? { |
170 | $($body)* |
171 | } |
172 | } |
173 | |
174 | #[cfg(not(target_arch = "arm" ))] |
175 | intrinsics! { |
176 | $(#[$($attr)*])* |
177 | pub extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
178 | $($body)* |
179 | } |
180 | } |
181 | |
182 | intrinsics!($($rest)*); |
183 | ); |
184 | |
185 | // Like aapcs above we recognize an attribute for the "unadjusted" abi on |
186 | // win64 for some methods. |
187 | ( |
188 | #[unadjusted_on_win64] |
189 | $(#[$($attr:tt)*])* |
190 | pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
191 | $($body:tt)* |
192 | } |
193 | |
194 | $($rest:tt)* |
195 | ) => ( |
196 | #[cfg(all(any(windows, target_os = "cygwin" , all(target_os = "uefi" , target_arch = "x86_64" )), target_pointer_width = "64" ))] |
197 | intrinsics! { |
198 | $(#[$($attr)*])* |
199 | pub extern "unadjusted" fn $name( $($argname: $ty),* ) $(-> $ret)? { |
200 | $($body)* |
201 | } |
202 | } |
203 | |
204 | #[cfg(not(all(any(windows, target_os = "cygwin" , all(target_os = "uefi" , target_arch = "x86_64" )), target_pointer_width = "64" )))] |
205 | intrinsics! { |
206 | $(#[$($attr)*])* |
207 | pub extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
208 | $($body)* |
209 | } |
210 | } |
211 | |
212 | intrinsics!($($rest)*); |
213 | ); |
214 | |
215 | // `arm_aeabi_alias` would conflict with `f16_apple_{arg,ret}_abi` not handled here. Avoid macro ambiguity by combining in a |
216 | // single `#[]`. |
217 | ( |
218 | #[apple_f16_arg_abi] |
219 | #[arm_aeabi_alias = $alias:ident] |
220 | $($t:tt)* |
221 | ) => { |
222 | intrinsics! { |
223 | #[apple_f16_arg_abi, arm_aeabi_alias = $alias] |
224 | $($t)* |
225 | } |
226 | }; |
227 | ( |
228 | #[apple_f16_ret_abi] |
229 | #[arm_aeabi_alias = $alias:ident] |
230 | $($t:tt)* |
231 | ) => { |
232 | intrinsics! { |
233 | #[apple_f16_ret_abi, arm_aeabi_alias = $alias] |
234 | $($t)* |
235 | } |
236 | }; |
237 | |
238 | // On x86 (32-bit and 64-bit) Apple platforms, `f16` is passed and returned like a `u16` unless |
239 | // the builtin involves `f128`. |
240 | ( |
241 | // `arm_aeabi_alias` would conflict if not handled here. Avoid macro ambiguity by combining |
242 | // in a single `#[]`. |
243 | #[apple_f16_arg_abi $(, arm_aeabi_alias = $alias:ident)?] |
244 | $(#[$($attr:tt)*])* |
245 | pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
246 | $($body:tt)* |
247 | } |
248 | |
249 | $($rest:tt)* |
250 | ) => ( |
251 | #[cfg(all(target_vendor = "apple" , any(target_arch = "x86" , target_arch = "x86_64" )))] |
252 | $(#[$($attr)*])* |
253 | pub extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
254 | $($body)* |
255 | } |
256 | |
257 | #[cfg(all(target_vendor = "apple" , any(target_arch = "x86" , target_arch = "x86_64" ), not(feature = "mangled-names" )))] |
258 | mod $name { |
259 | #[no_mangle] |
260 | #[cfg_attr(not(any(all(windows, target_env = "gnu" ), target_os = "cygwin" )), linkage = "weak" )] |
261 | $(#[$($attr)*])* |
262 | extern $abi fn $name( $($argname: u16),* ) $(-> $ret)? { |
263 | super::$name($(f16::from_bits($argname)),*) |
264 | } |
265 | } |
266 | |
267 | #[cfg(not(all(target_vendor = "apple" , any(target_arch = "x86" , target_arch = "x86_64" ))))] |
268 | intrinsics! { |
269 | $(#[arm_aeabi_alias = $alias])? |
270 | $(#[$($attr)*])* |
271 | pub extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
272 | $($body)* |
273 | } |
274 | } |
275 | |
276 | intrinsics!($($rest)*); |
277 | ); |
278 | ( |
279 | #[apple_f16_ret_abi $(, arm_aeabi_alias = $alias:ident)?] |
280 | $(#[$($attr:tt)*])* |
281 | pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
282 | $($body:tt)* |
283 | } |
284 | |
285 | $($rest:tt)* |
286 | ) => ( |
287 | #[cfg(all(target_vendor = "apple" , any(target_arch = "x86" , target_arch = "x86_64" )))] |
288 | $(#[$($attr)*])* |
289 | pub extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
290 | $($body)* |
291 | } |
292 | |
293 | #[cfg(all(target_vendor = "apple" , any(target_arch = "x86" , target_arch = "x86_64" ), not(feature = "mangled-names" )))] |
294 | mod $name { |
295 | #[no_mangle] |
296 | #[cfg_attr(not(any(all(windows, target_env = "gnu" ), target_os = "cygwin" )), linkage = "weak" )] |
297 | $(#[$($attr)*])* |
298 | extern $abi fn $name( $($argname: $ty),* ) -> u16 { |
299 | super::$name($($argname),*).to_bits() |
300 | } |
301 | } |
302 | |
303 | #[cfg(not(all(target_vendor = "apple" , any(target_arch = "x86" , target_arch = "x86_64" ))))] |
304 | intrinsics! { |
305 | $(#[arm_aeabi_alias = $alias])? |
306 | $(#[$($attr)*])* |
307 | pub extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
308 | $($body)* |
309 | } |
310 | } |
311 | |
312 | intrinsics!($($rest)*); |
313 | ); |
314 | |
315 | // A bunch of intrinsics on ARM are aliased in the standard compiler-rt |
316 | // build under `__aeabi_*` aliases, and LLVM will call these instead of the |
317 | // original function. The aliasing here is used to generate these symbols in |
318 | // the object file. |
319 | ( |
320 | #[arm_aeabi_alias = $alias:ident] |
321 | $(#[$($attr:tt)*])* |
322 | pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
323 | $($body:tt)* |
324 | } |
325 | |
326 | $($rest:tt)* |
327 | ) => ( |
328 | #[cfg(target_arch = "arm" )] |
329 | $(#[$($attr)*])* |
330 | pub extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
331 | $($body)* |
332 | } |
333 | |
334 | #[cfg(all(target_arch = "arm" , not(feature = "mangled-names" )))] |
335 | mod $name { |
336 | #[no_mangle] |
337 | #[cfg_attr(not(any(all(windows, target_env = "gnu" ), target_os = "cygwin" )), linkage = "weak" )] |
338 | $(#[$($attr)*])* |
339 | extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
340 | super::$name($($argname),*) |
341 | } |
342 | } |
343 | |
344 | #[cfg(all(target_arch = "arm" , not(feature = "mangled-names" )))] |
345 | mod $alias { |
346 | #[no_mangle] |
347 | #[cfg_attr(not(any(all(windows, target_env = "gnu" ), target_os = "cygwin" )), linkage = "weak" )] |
348 | $(#[$($attr)*])* |
349 | extern "aapcs" fn $alias( $($argname: $ty),* ) $(-> $ret)? { |
350 | super::$name($($argname),*) |
351 | } |
352 | } |
353 | |
354 | #[cfg(not(target_arch = "arm" ))] |
355 | intrinsics! { |
356 | $(#[$($attr)*])* |
357 | pub extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
358 | $($body)* |
359 | } |
360 | } |
361 | |
362 | intrinsics!($($rest)*); |
363 | ); |
364 | |
365 | // PowerPC usually uses `kf` rather than `tf` for `f128`. This is just an easy |
366 | // way to add an alias on those targets. |
367 | ( |
368 | #[ppc_alias = $alias:ident] |
369 | $(#[$($attr:tt)*])* |
370 | pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
371 | $($body:tt)* |
372 | } |
373 | |
374 | $($rest:tt)* |
375 | ) => ( |
376 | #[cfg(not(any(target_arch = "powerpc" , target_arch = "powerpc64" )))] |
377 | intrinsics! { |
378 | $(#[$($attr)*])* |
379 | pub extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
380 | $($body)* |
381 | } |
382 | } |
383 | |
384 | #[cfg(any(target_arch = "powerpc" , target_arch = "powerpc64" ))] |
385 | intrinsics! { |
386 | $(#[$($attr)*])* |
387 | pub extern $abi fn $alias( $($argname: $ty),* ) $(-> $ret)? { |
388 | $($body)* |
389 | } |
390 | } |
391 | |
392 | intrinsics!($($rest)*); |
393 | ); |
394 | |
395 | // C mem* functions are only generated when the "mem" feature is enabled. |
396 | ( |
397 | #[mem_builtin] |
398 | $(#[$($attr:tt)*])* |
399 | pub unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
400 | $($body:tt)* |
401 | } |
402 | |
403 | $($rest:tt)* |
404 | ) => ( |
405 | $(#[$($attr)*])* |
406 | pub unsafe extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
407 | $($body)* |
408 | } |
409 | |
410 | #[cfg(all(feature = "mem" , not(feature = "mangled-names" )))] |
411 | mod $name { |
412 | $(#[$($attr)*])* |
413 | #[no_mangle] |
414 | #[cfg_attr(not(any(all(windows, target_env = "gnu" ), target_os = "cygwin" )), linkage = "weak" )] |
415 | unsafe extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
416 | super::$name($($argname),*) |
417 | } |
418 | } |
419 | |
420 | intrinsics!($($rest)*); |
421 | ); |
422 | |
423 | // Naked functions are special: we can't generate wrappers for them since |
424 | // they use a custom calling convention. |
425 | ( |
426 | #[naked] |
427 | $(#[$($attr:tt)*])* |
428 | pub unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
429 | $($body:tt)* |
430 | } |
431 | |
432 | $($rest:tt)* |
433 | ) => ( |
434 | // `#[naked]` definitions are referenced by other places, so we can't use `cfg` like the others |
435 | pub mod $name { |
436 | #[naked] |
437 | $(#[$($attr)*])* |
438 | #[cfg_attr(not(feature = "mangled-names" ), no_mangle)] |
439 | #[cfg_attr(not(any(all(windows, target_env = "gnu" ), target_os = "cygwin" )), linkage = "weak" )] |
440 | pub unsafe extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
441 | $($body)* |
442 | } |
443 | } |
444 | |
445 | intrinsics!($($rest)*); |
446 | ); |
447 | |
448 | // For some intrinsics, AVR uses a custom calling convention¹ that does not |
449 | // match our definitions here. Ideally we would just use hand-written naked |
450 | // functions, but that's quite a lot of code to port² - so for the time |
451 | // being we are just ignoring the problematic functions, letting avr-gcc |
452 | // (which is required to compile to AVR anyway) link them from libgcc. |
453 | // |
454 | // ¹ https://gcc.gnu.org/wiki/avr-gcc (see "Exceptions to the Calling |
455 | // Convention") |
456 | // ² https://github.com/gcc-mirror/gcc/blob/31048012db98f5ec9c2ba537bfd850374bdd771f/libgcc/config/avr/lib1funcs.S |
457 | ( |
458 | #[avr_skip] |
459 | $(#[$($attr:tt)*])* |
460 | pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
461 | $($body:tt)* |
462 | } |
463 | |
464 | $($rest:tt)* |
465 | ) => ( |
466 | #[cfg(not(target_arch = "avr" ))] |
467 | intrinsics! { |
468 | $(#[$($attr)*])* |
469 | pub extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
470 | $($body)* |
471 | } |
472 | } |
473 | |
474 | intrinsics!($($rest)*); |
475 | ); |
476 | |
477 | // This is the final catch-all rule. At this point we generate an |
478 | // intrinsic with a conditional `#[no_mangle]` directive to avoid |
479 | // interfering with duplicate symbols and whatnot during testing. |
480 | // |
481 | // The implementation is placed in a separate module, to take advantage |
482 | // of the fact that rustc partitions functions into code generation |
483 | // units based on module they are defined in. As a result we will have |
484 | // a separate object file for each intrinsic. For further details see |
485 | // corresponding PR in rustc https://github.com/rust-lang/rust/pull/70846 |
486 | // |
487 | // After the intrinsic is defined we just continue with the rest of the |
488 | // input we were given. |
489 | ( |
490 | $(#[$($attr:tt)*])* |
491 | pub $(unsafe $(@ $empty:tt)?)? extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) $(-> $ret:ty)? { |
492 | $($body:tt)* |
493 | } |
494 | |
495 | $($rest:tt)* |
496 | ) => ( |
497 | $(#[$($attr)*])* |
498 | pub $(unsafe $($empty)?)? extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
499 | $($body)* |
500 | } |
501 | |
502 | #[cfg(not(feature = "mangled-names" ))] |
503 | mod $name { |
504 | $(#[$($attr)*])* |
505 | #[no_mangle] |
506 | #[cfg_attr(not(any(all(windows, target_env = "gnu" ), target_os = "cygwin" )), linkage = "weak" )] |
507 | $(unsafe $($empty)?)? extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? { |
508 | super::$name($($argname),*) |
509 | } |
510 | } |
511 | |
512 | intrinsics!($($rest)*); |
513 | ); |
514 | } |
515 | |