//! Macros shared throughout the compiler-builtins implementation /// The "main macro" used for defining intrinsics. /// /// The compiler-builtins library is super platform-specific with tons of crazy /// little tweaks for various platforms. As a result it *could* involve a lot fo /// #[cfg] and macro soup, but the intention is that this macro alleviates a lof /// of that complexity. Ideally this macro has all the weird ABI things /// platforms need and elsewhere in this library it just looks like normal Rust /// code. /// /// This macro is structured to be invoked with a bunch of functions that looks /// like: /// /// intrinsics! { /// pub extern "C" fn foo(a: i32) -> u32 { /// // ... /// } /// /// #[nonstandard_attribute] /// pub extern "C" fn bar(a: i32) -> u32 { /// // ... /// } /// } /// /// Each function is defined in a manner that looks like a normal Rust function. /// The macro then accepts a few nonstandard attributes that can decorate /// various functions. Each of the attributes is documented below with what it /// can do, and each of them slightly tweaks how further expansion happens. /// /// A quick overview of attributes supported right now are: /// /// * `use_c_shim_if` - takes a #[cfg] directive and falls back to the /// C-compiled version if `feature = "c"` is specified. /// * `aapcs_on_arm` - forces the ABI of the function to be `"aapcs"` on ARM and /// the specified ABI everywhere else. /// * `unadjusted_on_win64` - like `aapcs_on_arm` this switches to the /// `"unadjusted"` abi on Win64 and the specified abi elsewhere. /// * `win64_128bit_abi_hack` - this attribute is used for 128-bit integer /// intrinsics where the ABI is slightly tweaked on Windows platforms, but /// it's a normal ABI elsewhere for returning a 128 bit integer. /// * `arm_aeabi_alias` - handles the "aliasing" of various intrinsics on ARM /// their otherwise typical names to other prefixed ones. /// macro_rules! intrinsics { () => (); // Right now there's a bunch of architecture-optimized intrinsics in the // stock compiler-rt implementation. Not all of these have been ported over // to Rust yet so when the `c` feature of this crate is enabled we fall back // to the architecture-specific versions which should be more optimized. The // purpose of this macro is to easily allow specifying this. // // The argument to `use_c_shim_if` is a `#[cfg]` directive which, when true, // will cause this crate's exported version of `$name` to just redirect to // the C implementation. No symbol named `$name` will be in the object file // for this crate itself. // // When the `#[cfg]` directive is false, or when the `c` feature is // disabled, the provided implementation is used instead. ( #[use_c_shim_if($($cfg_clause:tt)*)] $(#[$($attr:tt)*])* pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { $($body:tt)* } $($rest:tt)* ) => ( #[cfg(all(feature = "c", $($cfg_clause)*))] pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { extern $abi { fn $name($($argname: $ty),*) -> $ret; } unsafe { $name($($argname),*) } } #[cfg(not(all(feature = "c", $($cfg_clause)*)))] intrinsics! { $(#[$($attr)*])* pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } } intrinsics!($($rest)*); ); // We recognize the `#[aapcs_on_arm]` attribute here and generate the // same intrinsic but force it to have the `"aapcs"` calling convention on // ARM and `"C"` elsewhere. ( #[aapcs_on_arm] $(#[$($attr:tt)*])* pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { $($body:tt)* } $($rest:tt)* ) => ( #[cfg(target_arch = "arm")] intrinsics! { $(#[$($attr)*])* pub extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret { $($body)* } } #[cfg(not(target_arch = "arm"))] intrinsics! { $(#[$($attr)*])* pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } } intrinsics!($($rest)*); ); // Like aapcs above we recognize an attribute for the "unadjusted" abi on // win64 for some methods. ( #[unadjusted_on_win64] $(#[$($attr:tt)*])* pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { $($body:tt)* } $($rest:tt)* ) => ( #[cfg(all(windows, target_pointer_width = "64"))] intrinsics! { $(#[$($attr)*])* pub extern "unadjusted" fn $name( $($argname: $ty),* ) -> $ret { $($body)* } } #[cfg(not(all(windows, target_pointer_width = "64")))] intrinsics! { $(#[$($attr)*])* pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } } intrinsics!($($rest)*); ); // Some intrinsics on win64 which return a 128-bit integer have an.. unusual // calling convention. That's managed here with this "abi hack" which alters // the generated symbol's ABI. // // This will still define a function in this crate with the given name and // signature, but the actual symbol for the intrinsic may have a slightly // different ABI on win64. ( #[win64_128bit_abi_hack] $(#[$($attr:tt)*])* pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { $($body:tt)* } $($rest:tt)* ) => ( #[cfg(all(windows, target_pointer_width = "64"))] $(#[$($attr)*])* pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } #[cfg(all(windows, target_pointer_width = "64"))] pub mod $name { intrinsics! { pub extern $abi fn $name( $($argname: $ty),* ) -> ::macros::win64_128bit_abi_hack::U64x2 { let e: $ret = super::$name($($argname),*); ::macros::win64_128bit_abi_hack::U64x2::from(e) } } } #[cfg(not(all(windows, target_pointer_width = "64")))] intrinsics! { $(#[$($attr)*])* pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } } intrinsics!($($rest)*); ); // A bunch of intrinsics on ARM are aliased in the standard compiler-rt // build under `__aeabi_*` aliases, and LLVM will call these instead of the // original function. The aliasing here is used to generate these symbols in // the object file. ( #[arm_aeabi_alias = $alias:ident] $(#[$($attr:tt)*])* pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { $($body:tt)* } $($rest:tt)* ) => ( #[cfg(target_arch = "arm")] pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } #[cfg(target_arch = "arm")] pub mod $name { intrinsics! { pub extern "aapcs" fn $alias( $($argname: $ty),* ) -> $ret { super::$name($($argname),*) } pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { super::$name($($argname),*) } } } #[cfg(not(target_arch = "arm"))] intrinsics! { $(#[$($attr)*])* pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } } intrinsics!($($rest)*); ); // This is the final catch-all rule. At this point we just generate an // intrinsic with a conditional `#[no_mangle]` directive to avoid // interfereing with duplicate symbols and whatnot during testing. // // After the intrinsic is defined we just continue with the rest of the // input we were given. ( $(#[$($attr:tt)*])* pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { $($body:tt)* } $($rest:tt)* ) => ( $(#[$($attr)*])* #[cfg_attr(not(feature = "gen-tests"), no_mangle)] pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } intrinsics!($($rest)*); ); } // Hack for LLVM expectations for ABI on windows. This is used by the // `#[win64_128bit_abi_hack]` attribute recognized above #[cfg(all(windows, target_pointer_width="64"))] pub mod win64_128bit_abi_hack { #[repr(simd)] pub struct U64x2(u64, u64); impl From for U64x2 { fn from(i: i128) -> U64x2 { use int::LargeInt; let j = i as u128; U64x2(j.low(), j.high()) } } impl From for U64x2 { fn from(i: u128) -> U64x2 { use int::LargeInt; U64x2(i.low(), i.high()) } } }