compiler_builtins/
macros.rs

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.
52macro_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            #[unsafe(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            #[unsafe(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            #[unsafe(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            #[unsafe(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            #[unsafe(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        #[unsafe(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            #[unsafe(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    // This is the final catch-all rule. At this point we generate an
449    // intrinsic with a conditional `#[no_mangle]` directive to avoid
450    // interfering with duplicate symbols and whatnot during testing.
451    //
452    // The implementation is placed in a separate module, to take advantage
453    // of the fact that rustc partitions functions into code generation
454    // units based on module they are defined in. As a result we will have
455    // a separate object file for each intrinsic. For further details see
456    // corresponding PR in rustc https://github.com/rust-lang/rust/pull/70846
457    //
458    // After the intrinsic is defined we just continue with the rest of the
459    // input we were given.
460    (
461        $(#[$($attr:tt)*])*
462        pub $(unsafe $(@ $empty:tt)?)? extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) $(-> $ret:ty)? {
463            $($body:tt)*
464        }
465
466        $($rest:tt)*
467    ) => (
468        $(#[$($attr)*])*
469        pub $(unsafe $($empty)?)? extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? {
470            $($body)*
471        }
472
473        #[cfg(not(feature = "mangled-names"))]
474        mod $name {
475            $(#[$($attr)*])*
476            #[unsafe(no_mangle)]
477            #[cfg_attr(not(any(all(windows, target_env = "gnu"), target_os = "cygwin")), linkage = "weak")]
478            $(unsafe $($empty)?)? extern $abi fn $name( $($argname: $ty),* ) $(-> $ret)? {
479                // SAFETY: same preconditions.
480                $(unsafe $($empty)?)? { super::$name($($argname),*) }
481            }
482        }
483
484        intrinsics!($($rest)*);
485    );
486}