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 unsafe 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"), unsafe(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}
487